2019-04-24 21:52:08 +00:00
package validator
import (
"errors"
2019-12-06 08:15:54 +00:00
"fmt"
"net/url"
2019-12-08 22:21:55 +00:00
"strings"
2019-04-24 21:52:08 +00:00
2019-12-24 02:14:52 +00:00
"github.com/authelia/authelia/internal/configuration/schema"
2020-05-04 19:39:25 +00:00
"github.com/authelia/authelia/internal/utils"
2019-04-24 21:52:08 +00:00
)
2021-08-03 09:55:21 +00:00
// ValidateAuthenticationBackend validates and updates the authentication backend configuration.
2021-04-16 01:44:37 +00:00
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 )
}
if configuration . RefreshInterval == "" {
configuration . RefreshInterval = schema . RefreshIntervalDefault
} else {
_ , err := utils . ParseDurationString ( configuration . RefreshInterval )
if err != nil && configuration . RefreshInterval != schema . ProfileRefreshDisabled && configuration . RefreshInterval != schema . ProfileRefreshAlways {
validator . Push ( fmt . Errorf ( "Auth Backend `refresh_interval` is configured to '%s' but it must be either a duration notation or one of 'disable', or 'always'. Error from parser: %s" , configuration . RefreshInterval , err ) )
}
}
}
2021-08-03 09:55:21 +00:00
// validateFileAuthenticationBackend validates and updates the file authentication backend configuration.
2019-04-24 21:52:08 +00:00
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`" ) )
}
2020-03-06 01:38:02 +00:00
2020-04-11 03:54:18 +00:00
if configuration . Password == nil {
configuration . Password = & schema . DefaultPasswordConfiguration
2020-03-06 01:38:02 +00:00
} else {
2020-09-18 12:05:43 +00:00
// Salt Length
2020-05-06 00:52:06 +00:00
switch {
case configuration . Password . SaltLength == 0 :
2020-04-11 03:54:18 +00:00
configuration . Password . SaltLength = schema . DefaultPasswordConfiguration . SaltLength
2020-05-14 05:55:03 +00:00
case configuration . Password . SaltLength < 8 :
2020-04-11 03:54:18 +00:00
validator . Push ( fmt . Errorf ( "The salt length must be 2 or more, you configured %d" , configuration . Password . SaltLength ) )
2020-03-06 01:38:02 +00:00
}
2021-08-03 09:55:21 +00:00
switch configuration . Password . Algorithm {
case "" :
configuration . Password . Algorithm = schema . DefaultPasswordConfiguration . Algorithm
fallthrough
case hashArgon2id :
validateFileAuthenticationBackendArgon2id ( configuration , validator )
case hashSHA512 :
validateFileAuthenticationBackendSHA512 ( configuration )
default :
validator . Push ( fmt . Errorf ( "Unknown hashing algorithm supplied, valid values are argon2id and sha512, you configured '%s'" , configuration . Password . Algorithm ) )
}
2020-03-06 01:38:02 +00:00
2021-08-03 09:55:21 +00:00
if configuration . Password . Iterations < 1 {
validator . Push ( fmt . Errorf ( "The number of iterations specified is invalid, must be 1 or more, you configured %d" , configuration . Password . Iterations ) )
2020-03-06 01:38:02 +00:00
}
}
2019-04-24 21:52:08 +00:00
}
2021-08-03 09:55:21 +00:00
func validateFileAuthenticationBackendSHA512 ( configuration * schema . FileAuthenticationBackendConfiguration ) {
// Iterations (time)
if configuration . Password . Iterations == 0 {
configuration . Password . Iterations = schema . DefaultPasswordSHA512Configuration . Iterations
}
}
func validateFileAuthenticationBackendArgon2id ( configuration * schema . FileAuthenticationBackendConfiguration , validator * schema . StructValidator ) {
// Iterations (time)
if configuration . Password . Iterations == 0 {
configuration . Password . Iterations = schema . DefaultPasswordConfiguration . Iterations
}
// Parallelism
if configuration . Password . Parallelism == 0 {
configuration . Password . Parallelism = schema . DefaultPasswordConfiguration . Parallelism
} else if configuration . Password . Parallelism < 1 {
validator . Push ( fmt . Errorf ( "Parallelism for argon2id must be 1 or more, you configured %d" , configuration . Password . Parallelism ) )
}
// Memory
if configuration . Password . Memory == 0 {
configuration . Password . Memory = schema . DefaultPasswordConfiguration . Memory
} else if configuration . Password . Memory < configuration . Password . 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 . Password . Parallelism * 8 , configuration . Password . Memory , configuration . Password . Parallelism ) )
}
// Key Length
if configuration . Password . KeyLength == 0 {
configuration . Password . KeyLength = schema . DefaultPasswordConfiguration . KeyLength
} else if configuration . Password . KeyLength < 16 {
validator . Push ( fmt . Errorf ( "Key length for argon2id must be 16, you configured %d" , configuration . Password . KeyLength ) )
}
}
2021-04-16 01:44:37 +00:00
func validateLDAPAuthenticationBackend ( configuration * schema . LDAPAuthenticationBackendConfiguration , validator * schema . StructValidator ) {
2020-11-27 09:59:22 +00:00
if configuration . Implementation == "" {
configuration . Implementation = schema . DefaultLDAPAuthenticationBackendConfiguration . Implementation
}
2021-04-16 01:44:37 +00:00
if configuration . TLS == nil {
2021-01-04 10:28:55 +00:00
configuration . TLS = schema . DefaultLDAPAuthenticationBackendConfiguration . TLS
}
if configuration . TLS . MinimumVersion == "" {
configuration . TLS . MinimumVersion = schema . DefaultLDAPAuthenticationBackendConfiguration . TLS . MinimumVersion
}
if _ , err := utils . TLSStringToTLSConfigVersion ( configuration . TLS . MinimumVersion ) ; err != nil {
validator . Push ( fmt . Errorf ( "error occurred validating the LDAP minimum_tls_version key with value %s: %v" , configuration . TLS . MinimumVersion , err ) )
2020-12-03 05:23:52 +00:00
}
2020-11-27 09:59:22 +00:00
switch configuration . Implementation {
case schema . LDAPImplementationCustom :
2021-04-16 01:44:37 +00:00
setDefaultImplementationCustomLDAPAuthenticationBackend ( configuration )
2020-11-27 09:59:22 +00:00
case schema . LDAPImplementationActiveDirectory :
2021-04-16 01:44:37 +00:00
setDefaultImplementationActiveDirectoryLDAPAuthenticationBackend ( configuration )
2020-11-27 09:59:22 +00:00
default :
validator . Push ( fmt . Errorf ( "authentication backend ldap implementation must be blank or one of the following values `%s`, `%s`" , schema . LDAPImplementationCustom , schema . LDAPImplementationActiveDirectory ) )
}
2021-04-16 01:44:37 +00:00
if strings . Contains ( configuration . UsersFilter , "{0}" ) {
validator . Push ( fmt . Errorf ( "authentication backend ldap users filter must not contain removed placeholders" +
", {0} has been replaced with {input}" ) )
}
if strings . Contains ( configuration . GroupsFilter , "{0}" ) ||
strings . Contains ( configuration . GroupsFilter , "{1}" ) {
validator . Push ( fmt . Errorf ( "authentication backend ldap groups filter must not contain removed " +
"placeholders, {0} has been replaced with {input} and {1} has been replaced with {username}" ) )
}
2019-04-24 21:52:08 +00:00
if configuration . URL == "" {
validator . Push ( errors . New ( "Please provide a URL to the LDAP server" ) )
2019-10-29 20:16:38 +00:00
} else {
2021-04-16 01:44:37 +00:00
ldapURL , serverName := validateLDAPURL ( configuration . URL , validator )
2021-01-04 10:28:55 +00:00
configuration . URL = ldapURL
if configuration . TLS . ServerName == "" {
configuration . TLS . ServerName = serverName
}
2019-04-24 21:52:08 +00:00
}
2021-04-16 01:44:37 +00:00
validateLDAPRequiredParameters ( configuration , validator )
}
// Wrapper for test purposes to exclude the hostname from the return.
func validateLDAPURLSimple ( ldapURL string , validator * schema . StructValidator ) ( finalURL string ) {
finalURL , _ = validateLDAPURL ( ldapURL , validator )
return finalURL
}
func validateLDAPURL ( ldapURL string , validator * schema . StructValidator ) ( finalURL string , hostname string ) {
parsedURL , 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 ! ( parsedURL . Scheme == schemeLDAP || parsedURL . Scheme == schemeLDAPS ) {
validator . Push ( errors . New ( "Unknown scheme for ldap url, should be ldap:// or ldaps://" ) )
return "" , ""
}
return parsedURL . String ( ) , parsedURL . Hostname ( )
}
func validateLDAPRequiredParameters ( configuration * schema . LDAPAuthenticationBackendConfiguration , validator * schema . StructValidator ) {
2020-04-04 23:28:09 +00:00
// TODO: see if it's possible to disable this check if disable_reset_password is set and when anonymous/user binding is supported (#101 and #387)
2019-04-24 21:52:08 +00:00
if configuration . User == "" {
validator . Push ( errors . New ( "Please provide a user name to connect to the LDAP server" ) )
}
2020-04-04 23:28:09 +00:00
// TODO: see if it's possible to disable this check if disable_reset_password is set and when anonymous/user binding is supported (#101 and #387)
2019-04-24 21:52:08 +00:00
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" ) )
}
2020-03-30 22:36:04 +00:00
if configuration . UsersFilter == "" {
validator . Push ( errors . New ( "Please provide a users filter with `users_filter` attribute" ) )
} else {
2020-03-15 07:10:25 +00:00
if ! strings . HasPrefix ( configuration . UsersFilter , "(" ) || ! strings . HasSuffix ( configuration . UsersFilter , ")" ) {
2020-11-27 13:30:27 +00:00
validator . Push ( errors . New ( "The users filter should contain enclosing parenthesis. For instance {username_attribute}={input} should be ({username_attribute}={input})" ) )
}
if ! strings . Contains ( configuration . UsersFilter , "{username_attribute}" ) {
validator . Push ( errors . New ( "Unable to detect {username_attribute} placeholder in users_filter, your configuration is broken. " +
2021-04-11 11:25:03 +00:00
"Please review configuration options listed at https://www.authelia.com/docs/configuration/authentication/ldap.html" ) )
2020-03-30 22:36:04 +00:00
}
// This test helps the user know that users_filter is broken after the breaking change induced by this commit.
if ! strings . Contains ( configuration . UsersFilter , "{0}" ) && ! strings . Contains ( configuration . UsersFilter , "{input}" ) {
validator . Push ( errors . New ( "Unable to detect {input} placeholder in users_filter, your configuration might be broken. " +
2021-04-11 11:25:03 +00:00
"Please review configuration options listed at https://www.authelia.com/docs/configuration/authentication/ldap.html" ) )
2020-03-15 07:10:25 +00:00
}
2019-12-08 22:21:55 +00:00
}
2019-04-24 21:52:08 +00:00
if configuration . GroupsFilter == "" {
2020-03-15 07:10:25 +00:00
validator . Push ( errors . New ( "Please provide a groups filter with `groups_filter` attribute" ) )
2020-05-06 00:52:06 +00:00
} else if ! strings . HasPrefix ( configuration . GroupsFilter , "(" ) || ! strings . HasSuffix ( configuration . GroupsFilter , ")" ) {
validator . Push ( errors . New ( "The groups filter should contain enclosing parenthesis. For instance cn={input} should be (cn={input})" ) )
2019-04-24 21:52:08 +00:00
}
2020-11-27 09:59:22 +00:00
}
2021-04-16 01:44:37 +00:00
func setDefaultImplementationActiveDirectoryLDAPAuthenticationBackend ( configuration * schema . LDAPAuthenticationBackendConfiguration ) {
2020-11-27 09:59:22 +00:00
if configuration . UsersFilter == "" {
configuration . UsersFilter = schema . DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration . UsersFilter
}
if configuration . UsernameAttribute == "" {
configuration . UsernameAttribute = schema . DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration . UsernameAttribute
}
if configuration . DisplayNameAttribute == "" {
configuration . DisplayNameAttribute = schema . DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration . DisplayNameAttribute
}
if configuration . MailAttribute == "" {
configuration . MailAttribute = schema . DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration . MailAttribute
}
if configuration . GroupsFilter == "" {
configuration . GroupsFilter = schema . DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration . GroupsFilter
}
if configuration . GroupNameAttribute == "" {
configuration . GroupNameAttribute = schema . DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration . GroupNameAttribute
}
}
2021-04-16 01:44:37 +00:00
func setDefaultImplementationCustomLDAPAuthenticationBackend ( configuration * schema . LDAPAuthenticationBackendConfiguration ) {
2020-11-27 09:59:22 +00:00
if configuration . UsernameAttribute == "" {
configuration . UsernameAttribute = schema . DefaultLDAPAuthenticationBackendConfiguration . UsernameAttribute
}
2019-12-08 22:21:55 +00:00
2019-04-24 21:52:08 +00:00
if configuration . GroupNameAttribute == "" {
2020-05-04 19:39:25 +00:00
configuration . GroupNameAttribute = schema . DefaultLDAPAuthenticationBackendConfiguration . GroupNameAttribute
2019-04-24 21:52:08 +00:00
}
if configuration . MailAttribute == "" {
2020-05-04 19:39:25 +00:00
configuration . MailAttribute = schema . DefaultLDAPAuthenticationBackendConfiguration . MailAttribute
2019-04-24 21:52:08 +00:00
}
2020-06-19 10:50:21 +00:00
if configuration . DisplayNameAttribute == "" {
configuration . DisplayNameAttribute = schema . DefaultLDAPAuthenticationBackendConfiguration . DisplayNameAttribute
}
2019-04-24 21:52:08 +00:00
}