2020-10-28 14:35:41 +01:00
|
|
|
package auth
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"golang.org/x/crypto/bcrypt"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
PasswordMaximumLength = 64
|
|
|
|
PasswordSpecialChars = "!\"\\#$%&'()*+,-./:;<=>?@[]^_`|~"
|
|
|
|
PasswordNumbers = "0123456789"
|
|
|
|
PasswordUpperCaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
|
|
PasswordLowerCaseLetters = "abcdefghijklmnopqrstuvwxyz"
|
|
|
|
PasswordAllChars = PasswordSpecialChars + PasswordNumbers + PasswordUpperCaseLetters + PasswordLowerCaseLetters
|
|
|
|
|
|
|
|
InvalidLowercasePassword = "lowercase"
|
|
|
|
InvalidMinLengthPassword = "min-length"
|
|
|
|
InvalidMaxLengthPassword = "max-length"
|
|
|
|
InvalidNumberPassword = "number"
|
|
|
|
InvalidUppercasePassword = "uppercase"
|
|
|
|
InvalidSymbolPassword = "symbol"
|
|
|
|
)
|
|
|
|
|
|
|
|
// HashPassword generates a hash using the bcrypt.GenerateFromPassword
|
|
|
|
func HashPassword(password string) string {
|
|
|
|
hash, err := bcrypt.GenerateFromPassword([]byte(password), 10)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return string(hash)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ComparePassword compares the hash
|
2021-03-21 09:28:26 +01:00
|
|
|
func ComparePassword(hash, password string) bool {
|
2020-10-28 14:35:41 +01:00
|
|
|
if len(password) == 0 || len(hash) == 0 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
|
|
|
return err == nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type InvalidPasswordError struct {
|
|
|
|
FailingCriterias []string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ipe *InvalidPasswordError) Error() string {
|
|
|
|
return fmt.Sprintf("invalid password, failing criterias: %s", strings.Join(ipe.FailingCriterias, ", "))
|
|
|
|
}
|
|
|
|
|
|
|
|
type PasswordSettings struct {
|
|
|
|
MinimumLength int
|
|
|
|
Lowercase bool
|
|
|
|
Number bool
|
|
|
|
Uppercase bool
|
|
|
|
Symbol bool
|
|
|
|
}
|
|
|
|
|
2020-11-06 16:46:35 +01:00
|
|
|
func IsPasswordValid(password string, settings PasswordSettings) error {
|
2020-10-28 14:35:41 +01:00
|
|
|
err := &InvalidPasswordError{
|
|
|
|
FailingCriterias: []string{},
|
|
|
|
}
|
|
|
|
|
2020-11-06 16:46:35 +01:00
|
|
|
if len(password) < settings.MinimumLength {
|
2020-10-28 14:35:41 +01:00
|
|
|
err.FailingCriterias = append(err.FailingCriterias, InvalidMinLengthPassword)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(password) > PasswordMaximumLength {
|
|
|
|
err.FailingCriterias = append(err.FailingCriterias, InvalidMaxLengthPassword)
|
|
|
|
}
|
|
|
|
|
2020-11-06 16:46:35 +01:00
|
|
|
if settings.Lowercase {
|
2020-10-28 14:35:41 +01:00
|
|
|
if !strings.ContainsAny(password, PasswordLowerCaseLetters) {
|
|
|
|
err.FailingCriterias = append(err.FailingCriterias, InvalidLowercasePassword)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-06 16:46:35 +01:00
|
|
|
if settings.Uppercase {
|
2020-10-28 14:35:41 +01:00
|
|
|
if !strings.ContainsAny(password, PasswordUpperCaseLetters) {
|
|
|
|
err.FailingCriterias = append(err.FailingCriterias, InvalidUppercasePassword)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-06 16:46:35 +01:00
|
|
|
if settings.Number {
|
2020-10-28 14:35:41 +01:00
|
|
|
if !strings.ContainsAny(password, PasswordNumbers) {
|
|
|
|
err.FailingCriterias = append(err.FailingCriterias, InvalidNumberPassword)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-06 16:46:35 +01:00
|
|
|
if settings.Symbol {
|
2020-10-28 14:35:41 +01:00
|
|
|
if !strings.ContainsAny(password, PasswordSpecialChars) {
|
|
|
|
err.FailingCriterias = append(err.FailingCriterias, InvalidSymbolPassword)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(err.FailingCriterias) > 0 {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|