You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

password.go 2.1KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package password
  5. import (
  6. "crypto/rand"
  7. "math/big"
  8. "strings"
  9. "sync"
  10. "code.gitea.io/gitea/modules/setting"
  11. )
  12. var (
  13. matchComplexityOnce sync.Once
  14. validChars string
  15. requiredChars []string
  16. charComplexities = map[string]string{
  17. "lower": `abcdefghijklmnopqrstuvwxyz`,
  18. "upper": `ABCDEFGHIJKLMNOPQRSTUVWXYZ`,
  19. "digit": `0123456789`,
  20. "spec": ` !"#$%&'()*+,-./:;<=>?@[\]^_{|}~` + "`",
  21. }
  22. )
  23. // NewComplexity for preparation
  24. func NewComplexity() {
  25. matchComplexityOnce.Do(func() {
  26. setupComplexity(setting.PasswordComplexity)
  27. })
  28. }
  29. func setupComplexity(values []string) {
  30. if len(values) != 1 || values[0] != "off" {
  31. for _, val := range values {
  32. if chars, ok := charComplexities[val]; ok {
  33. validChars += chars
  34. requiredChars = append(requiredChars, chars)
  35. }
  36. }
  37. if len(requiredChars) == 0 {
  38. // No valid character classes found; use all classes as default
  39. for _, chars := range charComplexities {
  40. validChars += chars
  41. requiredChars = append(requiredChars, chars)
  42. }
  43. }
  44. }
  45. if validChars == "" {
  46. // No complexities to check; provide a sensible default for password generation
  47. validChars = charComplexities["lower"] + charComplexities["upper"] + charComplexities["digit"]
  48. }
  49. }
  50. // IsComplexEnough return True if password meets complexity settings
  51. func IsComplexEnough(pwd string) bool {
  52. NewComplexity()
  53. if len(validChars) > 0 {
  54. for _, req := range requiredChars {
  55. if !strings.ContainsAny(req, pwd) {
  56. return false
  57. }
  58. }
  59. }
  60. return true
  61. }
  62. // Generate a random password
  63. func Generate(n int) (string, error) {
  64. NewComplexity()
  65. buffer := make([]byte, n)
  66. max := big.NewInt(int64(len(validChars)))
  67. for {
  68. for j := 0; j < n; j++ {
  69. rnd, err := rand.Int(rand.Reader, max)
  70. if err != nil {
  71. return "", err
  72. }
  73. buffer[j] = validChars[rnd.Int64()]
  74. }
  75. if IsComplexEnough(string(buffer)) && string(buffer[0]) != " " && string(buffer[n-1]) != " " {
  76. return string(buffer), nil
  77. }
  78. }
  79. }