newdrive/internal/domain/identity.go
Elijah 715423ab8e
Some checks failed
CI / Backend (push) Failing after 1s
CI / Frontend (push) Successful in 13s
CI / Contracts and repository policy (push) Failing after 3s
CI / Container (push) Has been skipped
Implement owner setup and browser authentication sessions
2026-07-16 19:45:40 -07:00

73 lines
1.7 KiB
Go

package domain
import (
"errors"
"strings"
"unicode"
"unicode/utf8"
"golang.org/x/text/cases"
"golang.org/x/text/unicode/norm"
)
const (
MinimumPasswordRunes = 12
MaximumPasswordBytes = 1024
MaximumIdentityRunes = 128
)
var (
ErrInvalidUsername = errors.New("invalid username")
ErrInvalidDisplayName = errors.New("invalid display name")
ErrInvalidPassword = errors.New("invalid password")
)
type OwnerIdentity struct {
Username string
UsernameKey string
DisplayName string
}
func NewOwnerIdentity(username, displayName string) (OwnerIdentity, error) {
username, usernameKey, err := NormalizeUsername(username)
if err != nil {
return OwnerIdentity{}, err
}
displayName = norm.NFC.String(strings.TrimSpace(displayName))
if !validIdentityText(displayName) {
return OwnerIdentity{}, ErrInvalidDisplayName
}
return OwnerIdentity{
Username: username,
UsernameKey: usernameKey,
DisplayName: displayName,
}, nil
}
func NormalizeUsername(username string) (string, string, error) {
username = norm.NFC.String(strings.TrimSpace(username))
if !validIdentityText(username) {
return "", "", ErrInvalidUsername
}
return username, cases.Fold().String(username), nil
}
func ValidatePassword(password string) error {
if !utf8.ValidString(password) || len(password) > MaximumPasswordBytes || utf8.RuneCountInString(password) < MinimumPasswordRunes {
return ErrInvalidPassword
}
return nil
}
func validIdentityText(value string) bool {
count := utf8.RuneCountInString(value)
if count == 0 || count > MaximumIdentityRunes {
return false
}
for _, character := range value {
if unicode.IsControl(character) {
return false
}
}
return true
}