/
passwordgeneration.go
109 lines (95 loc) · 3.4 KB
/
passwordgeneration.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package keycloakb
import (
crand "crypto/rand"
"math/big"
mrand "math/rand"
"strconv"
"strings"
"unicode"
)
const (
lowerCase = "abcdefghjkmnpqrtuvwxyz"
upperCase = "ABCDEFGHJKLMNPQRTUVWXYZ"
specialChars = "?!#%$"
digits = "2346789"
alphabet = "abcdefghjkmnpqrtuvwxyzABCDEFGHJKLMNPQRTUVWXYZ2346789"
)
// appendCharacters appends a number of characters from a certain alphabet to a string array
func appendCharacters(pwdElems []string, alphabet string, length int) []string {
for j := 0; j < length; j++ {
nBig, _ := crand.Int(crand.Reader, big.NewInt(int64(len(alphabet))))
index := int(nBig.Int64())
pwdElems = append(pwdElems, string(alphabet[index]))
}
return pwdElems
}
// GeneratePassword generates a password accoring to the policy or minimum length imposed
func GeneratePassword(policy *string, minLength int, userID string) (string, error) {
var pwd string
var err error
// generate a pwd != userID
for {
if policy != nil {
pwd, err = GeneratePasswordFromKeycloakPolicy(*policy)
} else {
pwd = GeneratePasswordNoKeycloakPolicy(minLength)
}
if pwd != userID {
break
}
}
return pwd, err
}
// GeneratePasswordNoKeycloakPolicy generates a password of a given length
func GeneratePasswordNoKeycloakPolicy(minLength int) string {
var pwdElems []string
pwdElems = appendCharacters(pwdElems, alphabet, minLength)
pwd := strings.Join(pwdElems, "")
return pwd
}
// GeneratePasswordFromKeycloakPolicy generates a random password respecting the keycloak password policy
func GeneratePasswordFromKeycloakPolicy(policy string) (string, error) {
// Keycloak password policy is a string of the form
// "forceExpiredPasswordChange(365) and specialChars(1) and upperCase(1) and lowerCase(1) and length(4) and digits(1) and notUsername(undefined)"
var pwdElems = make([]string, 0)
policyItems := strings.Split(policy, "and")
// generate a random password that corresponds to the password policy
//reg := regexp.MustCompile(`[a-zA-z]+[(]{1}[0-9]+[)]{1}`)
//pwdReq := string(reg.Find([]byte()))
f := func(c rune) bool {
return !unicode.IsLetter(c) && !unicode.IsNumber(c)
}
for i := 0; i < len(policyItems); i++ {
keyValueItem := strings.FieldsFunc(policyItems[i], f)
minRequired, err := strconv.Atoi(keyValueItem[1])
switch keyValueItem[0] {
case "length", "lowerCase":
// make sure that the password has the minimum length required by choosing random lower case letters
pwdElems = appendCharacters(pwdElems, lowerCase, minRequired)
case "specialChars":
// pick randomly special characters from ?!#%$
pwdElems = appendCharacters(pwdElems, specialChars, minRequired)
case "upperCase":
pwdElems = appendCharacters(pwdElems, upperCase, minRequired)
case "digits":
pwdElems = appendCharacters(pwdElems, digits, minRequired)
default:
err = nil
}
if err != nil {
return "", err
}
}
mrand.Shuffle(len(pwdElems), func(i, j int) { pwdElems[i], pwdElems[j] = pwdElems[j], pwdElems[i] })
pwd := strings.Join(pwdElems, "")
return pwd, nil
}
// GenerateInitialCode generates a code of the format UpperCase + digits + LowerCase
func GenerateInitialCode(nbUpperCase int, nbDigits int, nbLowerCase int) string {
var pwdElems []string
pwdElems = appendCharacters(pwdElems, upperCase, nbUpperCase)
pwdElems = appendCharacters(pwdElems, digits, nbDigits)
pwdElems = appendCharacters(pwdElems, lowerCase, nbLowerCase)
pwd := strings.Join(pwdElems, "")
return pwd
}