package createuser import ( "crypto/rand" "encoding/base64" "errors" "fmt" "net/http" "strings" "git.artlef.fr/bibliomane/internal/appcontext" "git.artlef.fr/bibliomane/internal/i18nresource" "git.artlef.fr/bibliomane/internal/model" "git.artlef.fr/bibliomane/internal/myvalidator" "golang.org/x/crypto/bcrypt" "gorm.io/gorm" ) func CreateUserToActivate(ac appcontext.AppContext, username string) (model.User, error) { token := generateRandomToken() user := model.User{ Name: username, Admin: false, InviteToken: token, Activated: false, } err := CreateUserWithHashedPassword(ac, &user) return user, err } func generateRandomToken() string { tokenByte := make([]byte, 64) var tokenBuilder strings.Builder rand.Read(tokenByte) encoder := base64.NewEncoder(base64.StdEncoding, &tokenBuilder) encoder.Write(tokenByte) // Must close the encoder when finished to flush any partial blocks. // If you comment out the following line, the last partial block "r" // won't be encoded. encoder.Close() return tokenBuilder.String() } // this method will hash the password func CreateUser(ac appcontext.AppContext, username string, password string) error { hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { return err } user := model.User{ Name: username, Password: string(hashedPassword), Admin: false, Activated: true, } return CreateUserWithHashedPassword(ac, &user) } // only call this method with hashed password func CreateUserWithHashedPassword(ac appcontext.AppContext, userToCreate *model.User) error { var existingUser model.User err := ac.Db.Where("name = ?", userToCreate.Name).First(&existingUser).Error if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { return err } if err == nil { return myvalidator.HttpError{ StatusCode: http.StatusInternalServerError, Err: errors.New(i18nresource.GetTranslatedMessage(&ac, "UserAlreadyExists")), } } return ac.Db.Model(&model.User{}).Save(&userToCreate).Error } func CreateDefaultUsers(ac appcontext.AppContext) error { usersPasswordMap, err := createNormalUsersMap(ac) if err != nil { return err } err = createDefaultUsersFromMap(ac, usersPasswordMap, false) if err != nil { return err } adminUsersPasswordMap, err := createDefaultUsersMap(ac, ac.Config.AddAdminUser) if err != nil { return err } return createDefaultUsersFromMap(ac, adminUsersPasswordMap, true) } func createNormalUsersMap(ac appcontext.AppContext) (map[string]string, error) { usersPasswordMap, err := createDefaultUsersMap(ac, ac.Config.AddUser) if err != nil { return usersPasswordMap, err } _, ok := usersPasswordMap[ac.Config.DemoUsername] if !ok { usersPasswordMap[ac.Config.DemoUsername] = "" } return usersPasswordMap, nil } func createDefaultUsersMap(ac appcontext.AppContext, adduser []string) (map[string]string, error) { usersPasswordMap := make(map[string]string) for _, s := range adduser { splittedString := strings.Split(s, ":") if len(splittedString) < 2 { return usersPasswordMap, fmt.Errorf(i18nresource.GetTranslatedMessage(&ac, "ErrorWhenCreatingUserFromStr"), s) } usersPasswordMap[splittedString[0]] = splittedString[1] } return usersPasswordMap, nil } func createDefaultUsersFromMap( ac appcontext.AppContext, usersPasswordMap map[string]string, isAdmin bool) error { var existingUsers []model.User err := ac.Db.Where("name IN ?", mapToArrayKey(usersPasswordMap)).Find(&existingUsers).Error if err != nil { return err } for username, password := range usersPasswordMap { if isInExistingUsers(username, existingUsers) { continue } user := model.User{ Name: username, Password: password, Admin: isAdmin, Activated: true, } err = CreateUserWithHashedPassword(ac, &user) if err != nil { return err } } return nil } func isInExistingUsers(username string, existingUsers []model.User) bool { for _, existingUser := range existingUsers { if username == existingUser.Name { return true } } return false } func mapToArrayKey(m map[string]string) []string { var a []string for k := range m { a = append(a, k) } return a }