2019-04-24 21:52:08 +00:00
|
|
|
package validator
|
|
|
|
|
|
|
|
import (
|
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
|
|
|
|
2021-08-11 01:04:35 +00:00
|
|
|
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
|
|
|
"github.com/authelia/authelia/v4/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.
|
2022-02-28 03:15:01 +00:00
|
|
|
func ValidateAuthenticationBackend(config *schema.AuthenticationBackendConfiguration, validator *schema.StructValidator) {
|
|
|
|
if config.LDAP == nil && config.File == nil {
|
|
|
|
validator.Push(fmt.Errorf(errFmtAuthBackendNotConfigured))
|
2021-04-16 01:44:37 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
if config.LDAP != nil && config.File != nil {
|
|
|
|
validator.Push(fmt.Errorf(errFmtAuthBackendMultipleConfigured))
|
2021-04-16 01:44:37 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
if config.File != nil {
|
|
|
|
validateFileAuthenticationBackend(config.File, validator)
|
|
|
|
} else if config.LDAP != nil {
|
|
|
|
validateLDAPAuthenticationBackend(config.LDAP, validator)
|
2021-04-16 01:44:37 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
if config.RefreshInterval == "" {
|
|
|
|
config.RefreshInterval = schema.RefreshIntervalDefault
|
2021-04-16 01:44:37 +00:00
|
|
|
} else {
|
2022-02-28 03:15:01 +00:00
|
|
|
_, err := utils.ParseDurationString(config.RefreshInterval)
|
|
|
|
if err != nil && config.RefreshInterval != schema.ProfileRefreshDisabled && config.RefreshInterval != schema.ProfileRefreshAlways {
|
|
|
|
validator.Push(fmt.Errorf(errFmtAuthBackendRefreshInterval, config.RefreshInterval, err))
|
2021-04-16 01:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
2022-04-04 07:46:55 +00:00
|
|
|
|
|
|
|
if config.PasswordReset.CustomURL.String() != "" {
|
|
|
|
switch config.PasswordReset.CustomURL.Scheme {
|
|
|
|
case schemeHTTP, schemeHTTPS:
|
|
|
|
config.DisableResetPassword = false
|
|
|
|
default:
|
|
|
|
validator.Push(fmt.Errorf(errFmtAuthBackendPasswordResetCustomURLScheme, config.PasswordReset.CustomURL.String(), config.PasswordReset.CustomURL.Scheme))
|
|
|
|
}
|
|
|
|
}
|
2021-04-16 01:44:37 +00:00
|
|
|
}
|
|
|
|
|
2021-08-03 09:55:21 +00:00
|
|
|
// validateFileAuthenticationBackend validates and updates the file authentication backend configuration.
|
2022-02-28 03:15:01 +00:00
|
|
|
func validateFileAuthenticationBackend(config *schema.FileAuthenticationBackendConfiguration, validator *schema.StructValidator) {
|
|
|
|
if config.Path == "" {
|
|
|
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPathNotConfigured))
|
2019-04-24 21:52:08 +00:00
|
|
|
}
|
2020-03-06 01:38:02 +00:00
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
if config.Password == nil {
|
|
|
|
config.Password = &schema.DefaultPasswordConfiguration
|
2020-03-06 01:38:02 +00:00
|
|
|
} else {
|
2022-01-31 05:25:15 +00:00
|
|
|
// Salt Length.
|
2020-05-06 00:52:06 +00:00
|
|
|
switch {
|
2022-02-28 03:15:01 +00:00
|
|
|
case config.Password.SaltLength == 0:
|
|
|
|
config.Password.SaltLength = schema.DefaultPasswordConfiguration.SaltLength
|
|
|
|
case config.Password.SaltLength < 8:
|
|
|
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordSaltLength, config.Password.SaltLength))
|
2020-03-06 01:38:02 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
switch config.Password.Algorithm {
|
2021-08-03 09:55:21 +00:00
|
|
|
case "":
|
2022-02-28 03:15:01 +00:00
|
|
|
config.Password.Algorithm = schema.DefaultPasswordConfiguration.Algorithm
|
2021-08-03 09:55:21 +00:00
|
|
|
fallthrough
|
|
|
|
case hashArgon2id:
|
2022-02-28 03:15:01 +00:00
|
|
|
validateFileAuthenticationBackendArgon2id(config, validator)
|
2021-08-03 09:55:21 +00:00
|
|
|
case hashSHA512:
|
2022-02-28 03:15:01 +00:00
|
|
|
validateFileAuthenticationBackendSHA512(config)
|
2021-08-03 09:55:21 +00:00
|
|
|
default:
|
2022-02-28 03:15:01 +00:00
|
|
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordUnknownAlg, config.Password.Algorithm))
|
2021-08-03 09:55:21 +00:00
|
|
|
}
|
2020-03-06 01:38:02 +00:00
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
if config.Password.Iterations < 1 {
|
|
|
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordInvalidIterations, config.Password.Iterations))
|
2020-03-06 01:38:02 +00:00
|
|
|
}
|
|
|
|
}
|
2019-04-24 21:52:08 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
func validateFileAuthenticationBackendSHA512(config *schema.FileAuthenticationBackendConfiguration) {
|
2022-01-31 05:25:15 +00:00
|
|
|
// Iterations (time).
|
2022-02-28 03:15:01 +00:00
|
|
|
if config.Password.Iterations == 0 {
|
|
|
|
config.Password.Iterations = schema.DefaultPasswordSHA512Configuration.Iterations
|
2021-08-03 09:55:21 +00:00
|
|
|
}
|
|
|
|
}
|
2022-02-28 03:15:01 +00:00
|
|
|
func validateFileAuthenticationBackendArgon2id(config *schema.FileAuthenticationBackendConfiguration, validator *schema.StructValidator) {
|
2022-01-31 05:25:15 +00:00
|
|
|
// Iterations (time).
|
2022-02-28 03:15:01 +00:00
|
|
|
if config.Password.Iterations == 0 {
|
|
|
|
config.Password.Iterations = schema.DefaultPasswordConfiguration.Iterations
|
2021-08-03 09:55:21 +00:00
|
|
|
}
|
|
|
|
|
2022-01-31 05:25:15 +00:00
|
|
|
// Parallelism.
|
2022-02-28 03:15:01 +00:00
|
|
|
if config.Password.Parallelism == 0 {
|
|
|
|
config.Password.Parallelism = schema.DefaultPasswordConfiguration.Parallelism
|
|
|
|
} else if config.Password.Parallelism < 1 {
|
|
|
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordArgon2idInvalidParallelism, config.Password.Parallelism))
|
2021-08-03 09:55:21 +00:00
|
|
|
}
|
|
|
|
|
2022-01-31 05:25:15 +00:00
|
|
|
// Memory.
|
2022-02-28 03:15:01 +00:00
|
|
|
if config.Password.Memory == 0 {
|
|
|
|
config.Password.Memory = schema.DefaultPasswordConfiguration.Memory
|
|
|
|
} else if config.Password.Memory < config.Password.Parallelism*8 {
|
|
|
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordArgon2idInvalidMemory, config.Password.Parallelism, config.Password.Parallelism*8, config.Password.Memory))
|
2021-08-03 09:55:21 +00:00
|
|
|
}
|
|
|
|
|
2022-01-31 05:25:15 +00:00
|
|
|
// Key Length.
|
2022-02-28 03:15:01 +00:00
|
|
|
if config.Password.KeyLength == 0 {
|
|
|
|
config.Password.KeyLength = schema.DefaultPasswordConfiguration.KeyLength
|
|
|
|
} else if config.Password.KeyLength < 16 {
|
|
|
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordArgon2idInvalidKeyLength, config.Password.KeyLength))
|
2021-08-03 09:55:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
func validateLDAPAuthenticationBackend(config *schema.LDAPAuthenticationBackendConfiguration, validator *schema.StructValidator) {
|
|
|
|
if config.Timeout == 0 {
|
|
|
|
config.Timeout = schema.DefaultLDAPAuthenticationBackendConfiguration.Timeout
|
2021-08-05 04:30:00 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
if config.Implementation == "" {
|
|
|
|
config.Implementation = schema.DefaultLDAPAuthenticationBackendConfiguration.Implementation
|
2020-11-27 09:59:22 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
if config.TLS == nil {
|
|
|
|
config.TLS = schema.DefaultLDAPAuthenticationBackendConfiguration.TLS
|
2022-05-02 01:51:38 +00:00
|
|
|
} else if config.TLS.MinimumVersion == "" {
|
2022-02-28 03:15:01 +00:00
|
|
|
config.TLS.MinimumVersion = schema.DefaultLDAPAuthenticationBackendConfiguration.TLS.MinimumVersion
|
2021-01-04 10:28:55 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
if _, err := utils.TLSStringToTLSConfigVersion(config.TLS.MinimumVersion); err != nil {
|
|
|
|
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendTLSMinVersion, config.TLS.MinimumVersion, err))
|
2020-12-03 05:23:52 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
switch config.Implementation {
|
2020-11-27 09:59:22 +00:00
|
|
|
case schema.LDAPImplementationCustom:
|
2022-02-28 03:15:01 +00:00
|
|
|
setDefaultImplementationCustomLDAPAuthenticationBackend(config)
|
2020-11-27 09:59:22 +00:00
|
|
|
case schema.LDAPImplementationActiveDirectory:
|
2022-02-28 03:15:01 +00:00
|
|
|
setDefaultImplementationActiveDirectoryLDAPAuthenticationBackend(config)
|
2020-11-27 09:59:22 +00:00
|
|
|
default:
|
2022-02-28 03:15:01 +00:00
|
|
|
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendImplementation, config.Implementation, strings.Join([]string{schema.LDAPImplementationCustom, schema.LDAPImplementationActiveDirectory}, "', '")))
|
2020-11-27 09:59:22 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
if strings.Contains(config.UsersFilter, "{0}") {
|
|
|
|
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendFilterReplacedPlaceholders, "users_filter", "{0}", "{input}"))
|
2021-04-16 01:44:37 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
if strings.Contains(config.GroupsFilter, "{0}") {
|
|
|
|
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendFilterReplacedPlaceholders, "groups_filter", "{0}", "{input}"))
|
2021-04-16 01:44:37 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
if strings.Contains(config.GroupsFilter, "{1}") {
|
|
|
|
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendFilterReplacedPlaceholders, "groups_filter", "{1}", "{username}"))
|
|
|
|
}
|
2021-01-04 10:28:55 +00:00
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
if config.URL == "" {
|
|
|
|
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendMissingOption, "url"))
|
|
|
|
} else {
|
|
|
|
validateLDAPAuthenticationBackendURL(config, validator)
|
2019-04-24 21:52:08 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
validateLDAPRequiredParameters(config, validator)
|
2021-04-16 01:44:37 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
func validateLDAPAuthenticationBackendURL(config *schema.LDAPAuthenticationBackendConfiguration, validator *schema.StructValidator) {
|
|
|
|
var (
|
|
|
|
parsedURL *url.URL
|
|
|
|
err error
|
|
|
|
)
|
2021-04-16 01:44:37 +00:00
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
if parsedURL, err = url.Parse(config.URL); err != nil {
|
|
|
|
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendURLNotParsable, err))
|
2021-04-16 01:44:37 +00:00
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
return
|
2021-04-16 01:44:37 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
if parsedURL.Scheme != schemeLDAP && parsedURL.Scheme != schemeLDAPS {
|
|
|
|
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendURLInvalidScheme, parsedURL.Scheme))
|
|
|
|
|
|
|
|
return
|
2021-04-16 01:44:37 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
config.URL = parsedURL.String()
|
|
|
|
if config.TLS.ServerName == "" {
|
|
|
|
config.TLS.ServerName = parsedURL.Hostname()
|
|
|
|
}
|
2021-04-16 01:44:37 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
func validateLDAPRequiredParameters(config *schema.LDAPAuthenticationBackendConfiguration, validator *schema.StructValidator) {
|
2022-01-31 05:25:15 +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).
|
2022-02-28 03:15:01 +00:00
|
|
|
if config.User == "" {
|
|
|
|
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendMissingOption, "user"))
|
2019-04-24 21:52:08 +00:00
|
|
|
}
|
|
|
|
|
2022-01-31 05:25:15 +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).
|
2022-02-28 03:15:01 +00:00
|
|
|
if config.Password == "" {
|
|
|
|
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendMissingOption, "password"))
|
2019-04-24 21:52:08 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
if config.BaseDN == "" {
|
|
|
|
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendMissingOption, "base_dn"))
|
2019-04-24 21:52:08 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
if config.UsersFilter == "" {
|
|
|
|
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendMissingOption, "users_filter"))
|
2020-03-30 22:36:04 +00:00
|
|
|
} else {
|
2022-02-28 03:15:01 +00:00
|
|
|
if !strings.HasPrefix(config.UsersFilter, "(") || !strings.HasSuffix(config.UsersFilter, ")") {
|
|
|
|
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendFilterEnclosingParenthesis, "users_filter", config.UsersFilter, config.UsersFilter))
|
2020-11-27 13:30:27 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
if !strings.Contains(config.UsersFilter, "{username_attribute}") {
|
|
|
|
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendFilterMissingPlaceholder, "users_filter", "username_attribute"))
|
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.
|
2022-02-28 03:15:01 +00:00
|
|
|
if !strings.Contains(config.UsersFilter, "{input}") {
|
|
|
|
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendFilterMissingPlaceholder, "users_filter", "input"))
|
2020-03-15 07:10:25 +00:00
|
|
|
}
|
2019-12-08 22:21:55 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
if config.GroupsFilter == "" {
|
|
|
|
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendMissingOption, "groups_filter"))
|
|
|
|
} else if !strings.HasPrefix(config.GroupsFilter, "(") || !strings.HasSuffix(config.GroupsFilter, ")") {
|
|
|
|
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendFilterEnclosingParenthesis, "groups_filter", config.GroupsFilter, config.GroupsFilter))
|
2019-04-24 21:52:08 +00:00
|
|
|
}
|
2020-11-27 09:59:22 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
func setDefaultImplementationActiveDirectoryLDAPAuthenticationBackend(config *schema.LDAPAuthenticationBackendConfiguration) {
|
|
|
|
if config.UsersFilter == "" {
|
|
|
|
config.UsersFilter = schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.UsersFilter
|
2020-11-27 09:59:22 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
if config.UsernameAttribute == "" {
|
|
|
|
config.UsernameAttribute = schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.UsernameAttribute
|
2020-11-27 09:59:22 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
if config.DisplayNameAttribute == "" {
|
|
|
|
config.DisplayNameAttribute = schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.DisplayNameAttribute
|
2020-11-27 09:59:22 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
if config.MailAttribute == "" {
|
|
|
|
config.MailAttribute = schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.MailAttribute
|
2020-11-27 09:59:22 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
if config.GroupsFilter == "" {
|
|
|
|
config.GroupsFilter = schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.GroupsFilter
|
2020-11-27 09:59:22 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
if config.GroupNameAttribute == "" {
|
|
|
|
config.GroupNameAttribute = schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.GroupNameAttribute
|
2020-11-27 09:59:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
func setDefaultImplementationCustomLDAPAuthenticationBackend(config *schema.LDAPAuthenticationBackendConfiguration) {
|
|
|
|
if config.UsernameAttribute == "" {
|
|
|
|
config.UsernameAttribute = schema.DefaultLDAPAuthenticationBackendConfiguration.UsernameAttribute
|
2020-11-27 09:59:22 +00:00
|
|
|
}
|
2019-12-08 22:21:55 +00:00
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
if config.GroupNameAttribute == "" {
|
|
|
|
config.GroupNameAttribute = schema.DefaultLDAPAuthenticationBackendConfiguration.GroupNameAttribute
|
2019-04-24 21:52:08 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
if config.MailAttribute == "" {
|
|
|
|
config.MailAttribute = schema.DefaultLDAPAuthenticationBackendConfiguration.MailAttribute
|
2019-04-24 21:52:08 +00:00
|
|
|
}
|
2020-06-19 10:50:21 +00:00
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
if config.DisplayNameAttribute == "" {
|
|
|
|
config.DisplayNameAttribute = schema.DefaultLDAPAuthenticationBackendConfiguration.DisplayNameAttribute
|
2020-06-19 10:50:21 +00:00
|
|
|
}
|
2019-04-24 21:52:08 +00:00
|
|
|
}
|