internal/auth/register.go (view raw)
1package auth
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "strings"
8
9 "github.com/mjensen/weimar/internal/db"
10 "golang.org/x/crypto/bcrypt"
11)
12
13var (
14 ErrUsernameTaken = errors.New("username already taken")
15 ErrPasswordTooShort = errors.New("password must be at least 8 characters")
16 ErrUsernameTooShort = errors.New("username must be at least 3 characters")
17 ErrRegistrationClosed = errors.New("registration is closed")
18)
19
20const (
21 MinPasswordLength = 8
22 MinUsernameLength = 3
23 bcryptCost = 12
24)
25
26// Register creates a new user account. If allowRegistration is false it
27// returns ErrRegistrationClosed.
28func Register(ctx context.Context, database *db.DB, username, password string, isAdmin bool) (*db.User, error) {
29 if len(username) < MinUsernameLength {
30 return nil, ErrUsernameTooShort
31 }
32 if len(password) < MinPasswordLength {
33 return nil, ErrPasswordTooShort
34 }
35
36 hash, err := bcrypt.GenerateFromPassword([]byte(password), bcryptCost)
37 if err != nil {
38 return nil, fmt.Errorf("hash password: %w", err)
39 }
40
41 user, err := database.CreateUser(ctx, username, string(hash), isAdmin)
42 if err != nil {
43 if isUniqueViolation(err) {
44 return nil, ErrUsernameTaken
45 }
46 return nil, fmt.Errorf("create user: %w", err)
47 }
48 return user, nil
49}
50
51func isUniqueViolation(err error) bool {
52 return err != nil && strings.Contains(err.Error(), "UNIQUE constraint failed")
53}