mirror of
https://github.com/0rangebananaspy/authelia.git
synced 2024-09-14 22:47:21 +07:00
26369fff3d
* [FEATURE] Support Argon2id Passwords - Updated go module github.com/simia-tech/crypt - Added Argon2id support for file based authentication backend - Made it the default method - Made it so backwards compatibility with SHA512 exists - Force seeding of the random string generator used for salts to ensure they are all different - Added command params to the authelia hash-password command - Automatically remove {CRYPT} from hashes as they are updated - Automatically change hashes when they are updated to the configured algorithm - Made the hashing algorithm parameters completely configurable - Added reasonably comprehensive test suites - Updated docs - Updated config template * Adjust error output * Fix unit test * Add unit tests and argon2 version check * Fix new unit tests * Update docs, added tests * Implement configurable values and more comprehensive testing * Added cmd params to hash_password, updated docs, misc fixes * More detailed error for cmd, fixed a typo * Fixed cmd flag error, minor refactoring * Requested Changes and Minor refactoring * Increase entropy * Update docs for entropy changes * Refactor to reduce nesting and easier code maintenance * Cleanup Errors (uniformity for the function call) * Check salt length, fix docs * Add Base64 string validation for argon2id * Cleanup and Finalization - Moved RandomString function from ./internal/authentication/password_hash.go to ./internal/utils/strings.go - Added SplitStringToArrayOfStrings func that splits strings into an array with a fixed max string len - Fixed an error in validator that would allow a zero salt length - Added a test to verify the upstream crypt module supports our defined random salt chars - Updated docs - Removed unused "HashingAlgorithm" string type * Update crypt go mod, support argon2id key length and major refactor * Config Template Update, Final Tests * Use schema defaults for hash-password cmd * Iterations check * Docs requested changes * Test Coverage, suggested edits * Wording edit * Doc changes * Default sanity changes * Default sanity changes - docs * CI Sanity changes * Memory in MB
164 lines
6.5 KiB
Go
164 lines
6.5 KiB
Go
package validator
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"github.com/authelia/authelia/internal/configuration/schema"
|
|
)
|
|
|
|
var ldapProtocolPrefix = "ldap://"
|
|
var ldapsProtocolPrefix = "ldaps://"
|
|
|
|
func validateFileAuthenticationBackend(configuration *schema.FileAuthenticationBackendConfiguration, validator *schema.StructValidator) {
|
|
if configuration.Path == "" {
|
|
validator.Push(errors.New("Please provide a `path` for the users database in `authentication_backend`"))
|
|
}
|
|
|
|
if configuration.PasswordHashing == nil {
|
|
configuration.PasswordHashing = &schema.DefaultPasswordOptionsConfiguration
|
|
} else {
|
|
if configuration.PasswordHashing.Algorithm == "" {
|
|
configuration.PasswordHashing.Algorithm = schema.DefaultPasswordOptionsConfiguration.Algorithm
|
|
} else {
|
|
configuration.PasswordHashing.Algorithm = strings.ToLower(configuration.PasswordHashing.Algorithm)
|
|
if configuration.PasswordHashing.Algorithm != "argon2id" && configuration.PasswordHashing.Algorithm != "sha512" {
|
|
validator.Push(fmt.Errorf("Unknown hashing algorithm supplied, valid values are argon2id and sha512, you configured '%s'", configuration.PasswordHashing.Algorithm))
|
|
}
|
|
}
|
|
|
|
// Iterations (time)
|
|
if configuration.PasswordHashing.Iterations == 0 {
|
|
if configuration.PasswordHashing.Algorithm == "argon2id" {
|
|
configuration.PasswordHashing.Iterations = schema.DefaultPasswordOptionsConfiguration.Iterations
|
|
} else {
|
|
configuration.PasswordHashing.Iterations = schema.DefaultPasswordOptionsSHA512Configuration.Iterations
|
|
}
|
|
} else if configuration.PasswordHashing.Iterations < 1 {
|
|
validator.Push(fmt.Errorf("The number of iterations specified is invalid, must be 1 or more, you configured %d", configuration.PasswordHashing.Iterations))
|
|
}
|
|
|
|
//Salt Length
|
|
if configuration.PasswordHashing.SaltLength == 0 {
|
|
configuration.PasswordHashing.SaltLength = schema.DefaultPasswordOptionsConfiguration.SaltLength
|
|
} else if configuration.PasswordHashing.SaltLength < 2 {
|
|
validator.Push(fmt.Errorf("The salt length must be 2 or more, you configured %d", configuration.PasswordHashing.SaltLength))
|
|
} else if configuration.PasswordHashing.SaltLength > 16 {
|
|
validator.Push(fmt.Errorf("The salt length must be 16 or less, you configured %d", configuration.PasswordHashing.SaltLength))
|
|
}
|
|
|
|
if configuration.PasswordHashing.Algorithm == "argon2id" {
|
|
|
|
// Parallelism
|
|
if configuration.PasswordHashing.Parallelism == 0 {
|
|
configuration.PasswordHashing.Parallelism = schema.DefaultPasswordOptionsConfiguration.Parallelism
|
|
} else if configuration.PasswordHashing.Parallelism < 1 {
|
|
validator.Push(fmt.Errorf("Parallelism for argon2id must be 1 or more, you configured %d", configuration.PasswordHashing.Parallelism))
|
|
}
|
|
|
|
// Memory
|
|
if configuration.PasswordHashing.Memory == 0 {
|
|
configuration.PasswordHashing.Memory = schema.DefaultPasswordOptionsConfiguration.Memory
|
|
} else if configuration.PasswordHashing.Memory < configuration.PasswordHashing.Parallelism*8 {
|
|
validator.Push(fmt.Errorf("Memory for argon2id must be %d or more (parallelism * 8), you configured memory as %d and parallelism as %d", configuration.PasswordHashing.Parallelism*8, configuration.PasswordHashing.Memory, configuration.PasswordHashing.Parallelism))
|
|
}
|
|
|
|
// Key Length
|
|
if configuration.PasswordHashing.KeyLength == 0 {
|
|
configuration.PasswordHashing.KeyLength = schema.DefaultPasswordOptionsConfiguration.KeyLength
|
|
} else if configuration.PasswordHashing.KeyLength < 16 {
|
|
validator.Push(fmt.Errorf("Key length for argon2id must be 16, you configured %d", configuration.PasswordHashing.KeyLength))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func validateLdapURL(ldapURL string, validator *schema.StructValidator) string {
|
|
u, err := url.Parse(ldapURL)
|
|
|
|
if err != nil {
|
|
validator.Push(errors.New("Unable to parse URL to ldap server. The scheme is probably missing: ldap:// or ldaps://"))
|
|
return ""
|
|
}
|
|
|
|
if !(u.Scheme == "ldap" || u.Scheme == "ldaps") {
|
|
validator.Push(errors.New("Unknown scheme for ldap url, should be ldap:// or ldaps://"))
|
|
return ""
|
|
}
|
|
|
|
if u.Scheme == "ldap" && u.Port() == "" {
|
|
u.Host += ":389"
|
|
} else if u.Scheme == "ldaps" && u.Port() == "" {
|
|
u.Host += ":636"
|
|
}
|
|
|
|
if !u.IsAbs() {
|
|
validator.Push(fmt.Errorf("URL to LDAP %s is still not absolute, it should be something like ldap://127.0.0.1:389", u.String()))
|
|
}
|
|
|
|
return u.String()
|
|
}
|
|
|
|
func validateLdapAuthenticationBackend(configuration *schema.LDAPAuthenticationBackendConfiguration, validator *schema.StructValidator) {
|
|
if configuration.URL == "" {
|
|
validator.Push(errors.New("Please provide a URL to the LDAP server"))
|
|
} else {
|
|
configuration.URL = validateLdapURL(configuration.URL, validator)
|
|
}
|
|
|
|
if configuration.User == "" {
|
|
validator.Push(errors.New("Please provide a user name to connect to the LDAP server"))
|
|
}
|
|
|
|
if configuration.Password == "" {
|
|
validator.Push(errors.New("Please provide a password to connect to the LDAP server"))
|
|
}
|
|
|
|
if configuration.BaseDN == "" {
|
|
validator.Push(errors.New("Please provide a base DN to connect to the LDAP server"))
|
|
}
|
|
|
|
if configuration.UsersFilter == "" {
|
|
configuration.UsersFilter = "(cn={0})"
|
|
}
|
|
|
|
if !strings.HasPrefix(configuration.UsersFilter, "(") || !strings.HasSuffix(configuration.UsersFilter, ")") {
|
|
validator.Push(errors.New("The users filter should contain enclosing parenthesis. For instance cn={0} should be (cn={0})"))
|
|
}
|
|
|
|
if configuration.GroupsFilter == "" {
|
|
configuration.GroupsFilter = "(member={dn})"
|
|
}
|
|
|
|
if !strings.HasPrefix(configuration.GroupsFilter, "(") || !strings.HasSuffix(configuration.GroupsFilter, ")") {
|
|
validator.Push(errors.New("The groups filter should contain enclosing parenthesis. For instance cn={0} should be (cn={0})"))
|
|
}
|
|
|
|
if configuration.GroupNameAttribute == "" {
|
|
configuration.GroupNameAttribute = "cn"
|
|
}
|
|
|
|
if configuration.MailAttribute == "" {
|
|
configuration.MailAttribute = "mail"
|
|
}
|
|
}
|
|
|
|
// ValidateAuthenticationBackend validates and update authentication backend configuration.
|
|
func ValidateAuthenticationBackend(configuration *schema.AuthenticationBackendConfiguration, validator *schema.StructValidator) {
|
|
if configuration.Ldap == nil && configuration.File == nil {
|
|
validator.Push(errors.New("Please provide `ldap` or `file` object in `authentication_backend`"))
|
|
}
|
|
|
|
if configuration.Ldap != nil && configuration.File != nil {
|
|
validator.Push(errors.New("You cannot provide both `ldap` and `file` objects in `authentication_backend`"))
|
|
}
|
|
|
|
if configuration.File != nil {
|
|
validateFileAuthenticationBackend(configuration.File, validator)
|
|
} else if configuration.Ldap != nil {
|
|
validateLdapAuthenticationBackend(configuration.Ldap, validator)
|
|
}
|
|
}
|