package data import ( "crypto/subtle" "errors" "fmt" "time" "github.com/oklog/ulid/v2" "golang.org/x/crypto/scrypt" ) const ( scryptN = 32768 scryptR = 8 scryptP = 2 ) type User struct { ID string Created int64 User string Password []byte Salt []byte Roles []string } // HasRole checks if the user has a specific role func (u *User) HasRole(role string) bool { for _, r := range u.Roles { if r == role { return true } } return false } // HasPermission checks if the user has a specific permission using the authorization service func (u *User) HasPermission(authService *AuthorizationService, resource, action string) (bool, error) { return authService.UserHasPermission(u.ID, resource, action) } // GetPermissions returns all permissions for the user using the authorization service func (u *User) GetPermissions(authService *AuthorizationService) ([]Permission, error) { return authService.GetUserPermissions(u.ID) } type Login struct { User string `json:"user"` Password string `json:"password,omitzero"` Token string `json:"token,omitzero"` } func derive(password string, salt []byte) ([]byte, error) { return scrypt.Key([]byte(password), salt, scryptN, scryptR, scryptP, 32) } func (u *User) Check(login *Login) bool { if u.User != login.User { return false } derived, err := derive(login.Password, u.Salt) if err != nil { return false } if subtle.ConstantTimeCompare(derived, u.Password) != 1 { return false } return true } func (u *User) Register(login *Login) error { var err error if u.User != "" && u.User != login.User { return errors.New("invalid user") } if u.ID == "" { u.ID = ulid.Make().String() } u.User = login.User u.Salt, err = Salt() if err != nil { return fmt.Errorf("failed to register user: %w", err) } u.Password, err = derive(login.Password, u.Salt) if err != nil { return fmt.Errorf("key derivation failed: %w", err) } u.Created = time.Now().Unix() return nil }