2019-10-14 18:24:26 +03:00
// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package password
import (
"crypto/rand"
"math/big"
2019-10-16 06:09:58 +03:00
"strings"
2019-10-14 18:24:26 +03:00
"sync"
"code.gitea.io/gitea/modules/setting"
)
2019-10-16 06:09:58 +03:00
var (
matchComplexityOnce sync . Once
validChars string
requiredChars [ ] string
charComplexities = map [ string ] string {
"lower" : ` abcdefghijklmnopqrstuvwxyz ` ,
"upper" : ` ABCDEFGHIJKLMNOPQRSTUVWXYZ ` ,
"digit" : ` 0123456789 ` ,
"spec" : ` !"#$%&'()*+,-./:;<=>?@[\]^_ { |}~ ` + "`" ,
}
)
2019-10-14 18:24:26 +03:00
// NewComplexity for preparation
func NewComplexity ( ) {
matchComplexityOnce . Do ( func ( ) {
2019-10-16 06:09:58 +03:00
setupComplexity ( setting . PasswordComplexity )
} )
}
func setupComplexity ( values [ ] string ) {
if len ( values ) != 1 || values [ 0 ] != "off" {
for _ , val := range values {
if chars , ok := charComplexities [ val ] ; ok {
validChars += chars
requiredChars = append ( requiredChars , chars )
2019-10-14 18:24:26 +03:00
}
2019-10-16 06:09:58 +03:00
}
if len ( requiredChars ) == 0 {
// No valid character classes found; use all classes as default
for _ , chars := range charComplexities {
validChars += chars
requiredChars = append ( requiredChars , chars )
2019-10-14 18:24:26 +03:00
}
}
2019-10-16 06:09:58 +03:00
}
if validChars == "" {
// No complexities to check; provide a sensible default for password generation
validChars = charComplexities [ "lower" ] + charComplexities [ "upper" ] + charComplexities [ "digit" ]
}
2019-10-14 18:24:26 +03:00
}
2019-10-16 06:09:58 +03:00
// IsComplexEnough return True if password meets complexity settings
2019-10-14 18:24:26 +03:00
func IsComplexEnough ( pwd string ) bool {
2019-10-16 06:09:58 +03:00
NewComplexity ( )
if len ( validChars ) > 0 {
for _ , req := range requiredChars {
if ! strings . ContainsAny ( req , pwd ) {
2019-10-14 18:24:26 +03:00
return false
}
}
}
return true
}
// Generate a random password
func Generate ( n int ) ( string , error ) {
NewComplexity ( )
buffer := make ( [ ] byte , n )
max := big . NewInt ( int64 ( len ( validChars ) ) )
for {
for j := 0 ; j < n ; j ++ {
rnd , err := rand . Int ( rand . Reader , max )
if err != nil {
return "" , err
}
buffer [ j ] = validChars [ rnd . Int64 ( ) ]
}
if IsComplexEnough ( string ( buffer ) ) && string ( buffer [ 0 ] ) != " " && string ( buffer [ n - 1 ] ) != " " {
return string ( buffer ) , nil
}
}
}