您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

password.go 3.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package password
  4. import (
  5. "bytes"
  6. "context"
  7. "crypto/rand"
  8. "errors"
  9. "html/template"
  10. "math/big"
  11. "strings"
  12. "sync"
  13. "code.gitea.io/gitea/modules/setting"
  14. "code.gitea.io/gitea/modules/translation"
  15. )
  16. var (
  17. ErrComplexity = errors.New("password not complex enough")
  18. ErrMinLength = errors.New("password not long enough")
  19. )
  20. // complexity contains information about a particular kind of password complexity
  21. type complexity struct {
  22. ValidChars string
  23. TrNameOne string
  24. }
  25. var (
  26. matchComplexityOnce sync.Once
  27. validChars string
  28. requiredList []complexity
  29. charComplexities = map[string]complexity{
  30. "lower": {
  31. `abcdefghijklmnopqrstuvwxyz`,
  32. "form.password_lowercase_one",
  33. },
  34. "upper": {
  35. `ABCDEFGHIJKLMNOPQRSTUVWXYZ`,
  36. "form.password_uppercase_one",
  37. },
  38. "digit": {
  39. `0123456789`,
  40. "form.password_digit_one",
  41. },
  42. "spec": {
  43. ` !"#$%&'()*+,-./:;<=>?@[\]^_{|}~` + "`",
  44. "form.password_special_one",
  45. },
  46. }
  47. )
  48. // NewComplexity for preparation
  49. func NewComplexity() {
  50. matchComplexityOnce.Do(func() {
  51. setupComplexity(setting.PasswordComplexity)
  52. })
  53. }
  54. func setupComplexity(values []string) {
  55. if len(values) != 1 || values[0] != "off" {
  56. for _, val := range values {
  57. if complexity, ok := charComplexities[val]; ok {
  58. validChars += complexity.ValidChars
  59. requiredList = append(requiredList, complexity)
  60. }
  61. }
  62. if len(requiredList) == 0 {
  63. // No valid character classes found; use all classes as default
  64. for _, complexity := range charComplexities {
  65. validChars += complexity.ValidChars
  66. requiredList = append(requiredList, complexity)
  67. }
  68. }
  69. }
  70. if validChars == "" {
  71. // No complexities to check; provide a sensible default for password generation
  72. validChars = charComplexities["lower"].ValidChars + charComplexities["upper"].ValidChars + charComplexities["digit"].ValidChars
  73. }
  74. }
  75. // IsComplexEnough return True if password meets complexity settings
  76. func IsComplexEnough(pwd string) bool {
  77. NewComplexity()
  78. if len(validChars) > 0 {
  79. for _, req := range requiredList {
  80. if !strings.ContainsAny(req.ValidChars, pwd) {
  81. return false
  82. }
  83. }
  84. }
  85. return true
  86. }
  87. // Generate a random password
  88. func Generate(n int) (string, error) {
  89. NewComplexity()
  90. buffer := make([]byte, n)
  91. max := big.NewInt(int64(len(validChars)))
  92. for {
  93. for j := 0; j < n; j++ {
  94. rnd, err := rand.Int(rand.Reader, max)
  95. if err != nil {
  96. return "", err
  97. }
  98. buffer[j] = validChars[rnd.Int64()]
  99. }
  100. if err := IsPwned(context.Background(), string(buffer)); err != nil {
  101. if errors.Is(err, ErrIsPwned) {
  102. continue
  103. }
  104. return "", err
  105. }
  106. if IsComplexEnough(string(buffer)) && string(buffer[0]) != " " && string(buffer[n-1]) != " " {
  107. return string(buffer), nil
  108. }
  109. }
  110. }
  111. // BuildComplexityError builds the error message when password complexity checks fail
  112. func BuildComplexityError(locale translation.Locale) template.HTML {
  113. var buffer bytes.Buffer
  114. buffer.WriteString(locale.TrString("form.password_complexity"))
  115. buffer.WriteString("<ul>")
  116. for _, c := range requiredList {
  117. buffer.WriteString("<li>")
  118. buffer.WriteString(locale.TrString(c.TrNameOne))
  119. buffer.WriteString("</li>")
  120. }
  121. buffer.WriteString("</ul>")
  122. return template.HTML(buffer.String())
  123. }