Move stuff to util.go

This commit is contained in:
binwiederhier
2025-07-31 07:33:11 +02:00
parent 23ec7702fc
commit b91ff5f0b5
6 changed files with 82 additions and 76 deletions

View File

@@ -1015,11 +1015,11 @@ func (a *Manager) addUserTx(tx *sql.Tx, username, password string, role Role, ha
var err error = nil
if hashed {
hash = password
if err := AllowedPasswordHash(hash); err != nil {
if err := ValidPasswordHash(hash); err != nil {
return err
}
} else {
hash, err = a.HashPassword(password)
hash, err = hashPassword(password, a.config.BcryptCost)
if err != nil {
return err
}
@@ -1365,11 +1365,11 @@ func (a *Manager) changePasswordTx(tx *sql.Tx, username, password string, hashed
var err error
if hashed {
hash = password
if err := AllowedPasswordHash(hash); err != nil {
if err := ValidPasswordHash(hash); err != nil {
return err
}
} else {
hash, err = a.HashPassword(password)
hash, err = hashPassword(password, a.config.BcryptCost)
if err != nil {
return err
}
@@ -1697,15 +1697,6 @@ func (a *Manager) readTier(rows *sql.Rows) (*Tier, error) {
}, nil
}
// HashPassword hashes the given password using bcrypt with the configured cost
func (a *Manager) HashPassword(password string) (string, error) {
hash, err := bcrypt.GenerateFromPassword([]byte(password), a.config.BcryptCost)
if err != nil {
return "", err
}
return string(hash), nil
}
// Close closes the underlying database
func (a *Manager) Close() error {
return a.db.Close()

View File

@@ -4,9 +4,7 @@ import (
"errors"
"github.com/stripe/stripe-go/v74"
"heckel.io/ntfy/v2/log"
"heckel.io/ntfy/v2/util"
"net/netip"
"regexp"
"strings"
"time"
)
@@ -244,58 +242,6 @@ const (
everyoneID = "u_everyone"
)
var (
allowedUsernameRegex = regexp.MustCompile(`^[-_.+@a-zA-Z0-9]+$`) // Does not include Everyone (*)
allowedTopicRegex = regexp.MustCompile(`^[-_A-Za-z0-9]{1,64}$`) // No '*'
allowedTopicPatternRegex = regexp.MustCompile(`^[-_*A-Za-z0-9]{1,64}$`) // Adds '*' for wildcards!
allowedTierRegex = regexp.MustCompile(`^[-_A-Za-z0-9]{1,64}$`)
allowedTokenRegex = regexp.MustCompile(`^tk_[-_A-Za-z0-9]{29}$`) // Must be tokenLength-len(tokenPrefix)
)
// AllowedRole returns true if the given role can be used for new users
func AllowedRole(role Role) bool {
return role == RoleUser || role == RoleAdmin
}
// AllowedUsername returns true if the given username is valid
func AllowedUsername(username string) bool {
return allowedUsernameRegex.MatchString(username)
}
// AllowedTopic returns true if the given topic name is valid
func AllowedTopic(topic string) bool {
return allowedTopicRegex.MatchString(topic)
}
// AllowedTopicPattern returns true if the given topic pattern is valid; this includes the wildcard character (*)
func AllowedTopicPattern(topic string) bool {
return allowedTopicPatternRegex.MatchString(topic)
}
// AllowedTier returns true if the given tier name is valid
func AllowedTier(tier string) bool {
return allowedTierRegex.MatchString(tier)
}
// AllowedPasswordHash checks if the given password hash is a valid bcrypt hash
func AllowedPasswordHash(hash string) error {
if !strings.HasPrefix(hash, "$2a$") && !strings.HasPrefix(hash, "$2b$") && !strings.HasPrefix(hash, "$2y$") {
return ErrPasswordHashInvalid
}
return nil
}
// AllowedToken returns true if the given token matches the naming convention
func AllowedToken(token string) bool {
return allowedTokenRegex.MatchString(token)
}
// GenerateToken generates a new token with a prefix and a fixed length
// Lowercase only to support "<topic>+<token>@<domain>" email addresses
func GenerateToken() string {
return util.RandomLowerStringPrefix(tokenPrefix, tokenLength)
}
// Error constants used by the package
var (
ErrUnauthenticated = errors.New("unauthenticated")

73
user/util.go Normal file
View File

@@ -0,0 +1,73 @@
package user
import (
"golang.org/x/crypto/bcrypt"
"heckel.io/ntfy/v2/util"
"regexp"
"strings"
)
var (
allowedUsernameRegex = regexp.MustCompile(`^[-_.+@a-zA-Z0-9]+$`) // Does not include Everyone (*)
allowedTopicRegex = regexp.MustCompile(`^[-_A-Za-z0-9]{1,64}$`) // No '*'
allowedTopicPatternRegex = regexp.MustCompile(`^[-_*A-Za-z0-9]{1,64}$`) // Adds '*' for wildcards!
allowedTierRegex = regexp.MustCompile(`^[-_A-Za-z0-9]{1,64}$`)
allowedTokenRegex = regexp.MustCompile(`^tk_[-_A-Za-z0-9]{29}$`) // Must be tokenLength-len(tokenPrefix)
)
// AllowedRole returns true if the given role can be used for new users
func AllowedRole(role Role) bool {
return role == RoleUser || role == RoleAdmin
}
// AllowedUsername returns true if the given username is valid
func AllowedUsername(username string) bool {
return allowedUsernameRegex.MatchString(username)
}
// AllowedTopic returns true if the given topic name is valid
func AllowedTopic(topic string) bool {
return allowedTopicRegex.MatchString(topic)
}
// AllowedTopicPattern returns true if the given topic pattern is valid; this includes the wildcard character (*)
func AllowedTopicPattern(topic string) bool {
return allowedTopicPatternRegex.MatchString(topic)
}
// AllowedTier returns true if the given tier name is valid
func AllowedTier(tier string) bool {
return allowedTierRegex.MatchString(tier)
}
// ValidPasswordHash checks if the given password hash is a valid bcrypt hash
func ValidPasswordHash(hash string) error {
if !strings.HasPrefix(hash, "$2a$") && !strings.HasPrefix(hash, "$2b$") && !strings.HasPrefix(hash, "$2y$") {
return ErrPasswordHashInvalid
}
return nil
}
// ValidToken returns true if the given token matches the naming convention
func ValidToken(token string) bool {
return allowedTokenRegex.MatchString(token)
}
// GenerateToken generates a new token with a prefix and a fixed length
// Lowercase only to support "<topic>+<token>@<domain>" email addresses
func GenerateToken() string {
return util.RandomLowerStringPrefix(tokenPrefix, tokenLength)
}
// HashPassword hashes the given password using bcrypt with the configured cost
func HashPassword(password string) (string, error) {
return hashPassword(password, DefaultUserPasswordBcryptCost)
}
func hashPassword(password string, cost int) (string, error) {
hash, err := bcrypt.GenerateFromPassword([]byte(password), cost)
if err != nil {
return "", err
}
return string(hash), nil
}