util.go 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. package user
  2. import (
  3. "golang.org/x/crypto/bcrypt"
  4. "heckel.io/ntfy/v2/util"
  5. "regexp"
  6. "strings"
  7. )
  8. var (
  9. allowedUsernameRegex = regexp.MustCompile(`^[-_.+@a-zA-Z0-9]+$`) // Does not include Everyone (*)
  10. allowedTopicRegex = regexp.MustCompile(`^[-_A-Za-z0-9]{1,64}$`) // No '*'
  11. allowedTopicPatternRegex = regexp.MustCompile(`^[-_*A-Za-z0-9]{1,64}$`) // Adds '*' for wildcards!
  12. allowedTierRegex = regexp.MustCompile(`^[-_A-Za-z0-9]{1,64}$`)
  13. allowedTokenRegex = regexp.MustCompile(`^tk_[-_A-Za-z0-9]{29}$`) // Must be tokenLength-len(tokenPrefix)
  14. )
  15. // AllowedRole returns true if the given role can be used for new users
  16. func AllowedRole(role Role) bool {
  17. return role == RoleUser || role == RoleAdmin
  18. }
  19. // AllowedUsername returns true if the given username is valid
  20. func AllowedUsername(username string) bool {
  21. return allowedUsernameRegex.MatchString(username)
  22. }
  23. // AllowedTopic returns true if the given topic name is valid
  24. func AllowedTopic(topic string) bool {
  25. return allowedTopicRegex.MatchString(topic)
  26. }
  27. // AllowedTopicPattern returns true if the given topic pattern is valid; this includes the wildcard character (*)
  28. func AllowedTopicPattern(topic string) bool {
  29. return allowedTopicPatternRegex.MatchString(topic)
  30. }
  31. // AllowedTier returns true if the given tier name is valid
  32. func AllowedTier(tier string) bool {
  33. return allowedTierRegex.MatchString(tier)
  34. }
  35. // ValidPasswordHash checks if the given password hash is a valid bcrypt hash
  36. func ValidPasswordHash(hash string) error {
  37. if !strings.HasPrefix(hash, "$2a$") && !strings.HasPrefix(hash, "$2b$") && !strings.HasPrefix(hash, "$2y$") {
  38. return ErrPasswordHashInvalid
  39. }
  40. return nil
  41. }
  42. // ValidToken returns true if the given token matches the naming convention
  43. func ValidToken(token string) bool {
  44. return allowedTokenRegex.MatchString(token)
  45. }
  46. // GenerateToken generates a new token with a prefix and a fixed length
  47. // Lowercase only to support "<topic>+<token>@<domain>" email addresses
  48. func GenerateToken() string {
  49. return util.RandomLowerStringPrefix(tokenPrefix, tokenLength)
  50. }
  51. // HashPassword hashes the given password using bcrypt with the configured cost
  52. func HashPassword(password string) (string, error) {
  53. return hashPassword(password, DefaultUserPasswordBcryptCost)
  54. }
  55. func hashPassword(password string, cost int) (string, error) {
  56. hash, err := bcrypt.GenerateFromPassword([]byte(password), cost)
  57. if err != nil {
  58. return "", err
  59. }
  60. return string(hash), nil
  61. }