fix: removed deprecated smtp/ldap options (#1912)

This removes the deprecated options from 4.25. This includes the LDAP filters which allow {0} or {1} placeholders. The new aliases are documented. Additionally it refactors the keys validator to use uniform messages for most replaced keys.
pull/1924/head
James Elliott 2021-04-16 11:44:37 +10:00 committed by GitHub
parent 168dbf7265
commit cc4f47f21c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 305 additions and 436 deletions

View File

@ -36,7 +36,7 @@ func startServer() {
os.Exit(1) os.Exit(1)
} }
autheliaCertPool, errs, nonFatalErrs := utils.NewX509CertPool(config.CertificatesDirectory, config) autheliaCertPool, errs, nonFatalErrs := utils.NewX509CertPool(config.CertificatesDirectory)
if len(errs) > 0 { if len(errs) > 0 {
for _, err := range errs { for _, err := range errs {
logger.Error(err) logger.Error(err)
@ -89,8 +89,8 @@ func startServer() {
switch { switch {
case config.AuthenticationBackend.File != nil: case config.AuthenticationBackend.File != nil:
userProvider = authentication.NewFileUserProvider(config.AuthenticationBackend.File) userProvider = authentication.NewFileUserProvider(config.AuthenticationBackend.File)
case config.AuthenticationBackend.Ldap != nil: case config.AuthenticationBackend.LDAP != nil:
userProvider = authentication.NewLDAPUserProvider(*config.AuthenticationBackend.Ldap, autheliaCertPool) userProvider = authentication.NewLDAPUserProvider(*config.AuthenticationBackend.LDAP, autheliaCertPool)
default: default:
logger.Fatalf("Unrecognized authentication backend") logger.Fatalf("Unrecognized authentication backend")
} }

View File

@ -162,8 +162,6 @@ authentication_backend:
## - {input} is a placeholder replaced by what the user inputs in the login form. ## - {input} is a placeholder replaced by what the user inputs in the login form.
## - {username_attribute} is a mandatory placeholder replaced by what is configured in `username_attribute`. ## - {username_attribute} is a mandatory placeholder replaced by what is configured in `username_attribute`.
## - {mail_attribute} is a placeholder replaced by what is configured in `mail_attribute`. ## - {mail_attribute} is a placeholder replaced by what is configured in `mail_attribute`.
## - DON'T USE - {0} is an alias for {input} supported for backward compatibility but it will be deprecated in later
## versions, so please don't use it.
## ##
## Recommended settings are as follows: ## Recommended settings are as follows:
## - Microsoft Active Directory: (&({username_attribute}={input})(objectCategory=person)(objectClass=user)) ## - Microsoft Active Directory: (&({username_attribute}={input})(objectCategory=person)(objectClass=user))
@ -185,10 +183,6 @@ authentication_backend:
## - {dn} is a matcher replaced by the user distinguished name, aka, user DN. ## - {dn} is a matcher replaced by the user distinguished name, aka, user DN.
## - {username_attribute} is a placeholder replaced by what is configured in `username_attribute`. ## - {username_attribute} is a placeholder replaced by what is configured in `username_attribute`.
## - {mail_attribute} is a placeholder replaced by what is configured in `mail_attribute`. ## - {mail_attribute} is a placeholder replaced by what is configured in `mail_attribute`.
## - DON'T USE - {0} is an alias for {input} supported for backward compatibility but it will be deprecated in later
## versions, so please don't use it.
## - DON'T USE - {1} is an alias for {username} supported for backward compatibility but it will be deprecated in
## later version, so please don't use it.
## ##
## If your groups use the `groupOfUniqueNames` structure use this instead: ## If your groups use the `groupOfUniqueNames` structure use this instead:
## (&(uniquemember={dn})(objectclass=groupOfUniqueNames)) ## (&(uniquemember={dn})(objectclass=groupOfUniqueNames))

View File

@ -148,7 +148,11 @@ Similar to [additional_users_dn](#additional_users_dn) but it applies to group s
### groups_filter ### groups_filter
Similar to [users_filter](#users_filter) but it applies to group searches. Similar to [users_filter](#users_filter) but it applies to group searches. In order to include groups the memeber is not
a direct member of, but is a member of another group that is a member of those (i.e. recursive groups), you may try
using the following filter which is currently only tested against Microsoft Active Directory:
`(&(member:1.2.840.113556.1.4.1941:={dn})(objectClass=group)(objectCategory=group))`
### mail_attribute ### mail_attribute

View File

@ -62,27 +62,6 @@ func NewLDAPUserProviderWithFactory(configuration schema.LDAPAuthenticationBacke
} }
func (p *LDAPUserProvider) parseDynamicConfiguration() { func (p *LDAPUserProvider) parseDynamicConfiguration() {
// Deprecated: This is temporary for deprecation notice purposes. TODO: Remove in 4.28.
if strings.Contains(p.configuration.UsersFilter, "{0}") {
p.logger.Warnf("DEPRECATION NOTICE: LDAP Users Filter will no longer support replacing `{0}` in 4.28.0. Please use `{input}` instead.")
p.configuration.UsersFilter = strings.ReplaceAll(p.configuration.UsersFilter, "{0}", "{input}")
}
// Deprecated: This is temporary for deprecation notice purposes. TODO: Remove in 4.28.
if strings.Contains(p.configuration.GroupsFilter, "{0}") {
p.logger.Warnf("DEPRECATION NOTICE: LDAP Groups Filter will no longer support replacing `{0}` in 4.28.0. Please use `{input}` instead.")
p.configuration.GroupsFilter = strings.ReplaceAll(p.configuration.GroupsFilter, "{0}", "{input}")
}
// Deprecated: This is temporary for deprecation notice purposes. TODO: Remove in 4.28.
if strings.Contains(p.configuration.GroupsFilter, "{1}") {
p.logger.Warnf("DEPRECATION NOTICE: LDAP Groups Filter will no longer support replacing `{1}` in 4.28.0. Please use `{username}` instead.")
p.configuration.GroupsFilter = strings.ReplaceAll(p.configuration.GroupsFilter, "{1}", "{username}")
}
p.configuration.UsersFilter = strings.ReplaceAll(p.configuration.UsersFilter, "{username_attribute}", p.configuration.UsernameAttribute) p.configuration.UsersFilter = strings.ReplaceAll(p.configuration.UsersFilter, "{username_attribute}", p.configuration.UsernameAttribute)
p.configuration.UsersFilter = strings.ReplaceAll(p.configuration.UsersFilter, "{mail_attribute}", p.configuration.MailAttribute) p.configuration.UsersFilter = strings.ReplaceAll(p.configuration.UsersFilter, "{mail_attribute}", p.configuration.MailAttribute)
p.configuration.UsersFilter = strings.ReplaceAll(p.configuration.UsersFilter, "{display_name_attribute}", p.configuration.DisplayNameAttribute) p.configuration.UsersFilter = strings.ReplaceAll(p.configuration.UsersFilter, "{display_name_attribute}", p.configuration.DisplayNameAttribute)

View File

@ -713,8 +713,8 @@ func TestShouldParseDynamicConfiguration(t *testing.T) {
UsernameAttribute: "uid", UsernameAttribute: "uid",
MailAttribute: "mail", MailAttribute: "mail",
DisplayNameAttribute: "displayname", DisplayNameAttribute: "displayname",
UsersFilter: "(&(|({username_attribute}={0})({mail_attribute}={0})({display_name_attribute}={0}))(objectCategory=person)(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=2)(!pwdLastSet=0))", UsersFilter: "(&(|({username_attribute}={input})({mail_attribute}={input})({display_name_attribute}={input}))(objectCategory=person)(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=2)(!pwdLastSet=0))",
GroupsFilter: "(&(|(member={dn})(member={0})(member={1}))(objectClass=group))", GroupsFilter: "(&(|(member={dn})(member={input})(member={username}))(objectClass=group))",
AdditionalUsersDN: "ou=users", AdditionalUsersDN: "ou=users",
AdditionalGroupsDN: "ou=groups", AdditionalGroupsDN: "ou=groups",
BaseDN: "dc=example,dc=com", BaseDN: "dc=example,dc=com",

View File

@ -162,8 +162,6 @@ authentication_backend:
## - {input} is a placeholder replaced by what the user inputs in the login form. ## - {input} is a placeholder replaced by what the user inputs in the login form.
## - {username_attribute} is a mandatory placeholder replaced by what is configured in `username_attribute`. ## - {username_attribute} is a mandatory placeholder replaced by what is configured in `username_attribute`.
## - {mail_attribute} is a placeholder replaced by what is configured in `mail_attribute`. ## - {mail_attribute} is a placeholder replaced by what is configured in `mail_attribute`.
## - DON'T USE - {0} is an alias for {input} supported for backward compatibility but it will be deprecated in later
## versions, so please don't use it.
## ##
## Recommended settings are as follows: ## Recommended settings are as follows:
## - Microsoft Active Directory: (&({username_attribute}={input})(objectCategory=person)(objectClass=user)) ## - Microsoft Active Directory: (&({username_attribute}={input})(objectCategory=person)(objectClass=user))
@ -185,10 +183,6 @@ authentication_backend:
## - {dn} is a matcher replaced by the user distinguished name, aka, user DN. ## - {dn} is a matcher replaced by the user distinguished name, aka, user DN.
## - {username_attribute} is a placeholder replaced by what is configured in `username_attribute`. ## - {username_attribute} is a placeholder replaced by what is configured in `username_attribute`.
## - {mail_attribute} is a placeholder replaced by what is configured in `mail_attribute`. ## - {mail_attribute} is a placeholder replaced by what is configured in `mail_attribute`.
## - DON'T USE - {0} is an alias for {input} supported for backward compatibility but it will be deprecated in later
## versions, so please don't use it.
## - DON'T USE - {1} is an alias for {username} supported for backward compatibility but it will be deprecated in
## later version, so please don't use it.
## ##
## If your groups use the `groupOfUniqueNames` structure use this instead: ## If your groups use the `groupOfUniqueNames` structure use this instead:
## (&(uniquemember={dn})(objectclass=groupOfUniqueNames)) ## (&(uniquemember={dn})(objectclass=groupOfUniqueNames))

View File

@ -195,7 +195,7 @@ func TestShouldParseConfigFile(t *testing.T) {
assert.Equal(t, "duo_secret_from_env", config.DuoAPI.SecretKey) assert.Equal(t, "duo_secret_from_env", config.DuoAPI.SecretKey)
assert.Equal(t, "session_secret_from_env", config.Session.Secret) assert.Equal(t, "session_secret_from_env", config.Session.Secret)
assert.Equal(t, "ldap_secret_from_env", config.AuthenticationBackend.Ldap.Password) assert.Equal(t, "ldap_secret_from_env", config.AuthenticationBackend.LDAP.Password)
assert.Equal(t, "smtp_secret_from_env", config.Notifier.SMTP.Password) assert.Equal(t, "smtp_secret_from_env", config.Notifier.SMTP.Password)
assert.Equal(t, "redis_secret_from_env", config.Session.Redis.Password) assert.Equal(t, "redis_secret_from_env", config.Session.Redis.Password)
assert.Equal(t, "redis-sentinel_secret_from_env", config.Session.Redis.HighAvailability.SentinelPassword) assert.Equal(t, "redis-sentinel_secret_from_env", config.Session.Redis.HighAvailability.SentinelPassword)
@ -253,7 +253,7 @@ func TestShouldNotParseConfigFileWithOldOrUnexpectedKeys(t *testing.T) {
return errors[i].Error() < errors[j].Error() return errors[i].Error() < errors[j].Error()
}) })
assert.EqualError(t, errors[0], "config key not expected: loggy_file") assert.EqualError(t, errors[0], "config key not expected: loggy_file")
assert.EqualError(t, errors[1], "config key replaced: logs_level is now log_level") assert.EqualError(t, errors[1], "invalid configuration key 'logs_level' was replaced by 'log_level'")
} }
func TestShouldValidateConfigurationTemplate(t *testing.T) { func TestShouldValidateConfigurationTemplate(t *testing.T) {

View File

@ -17,8 +17,6 @@ type LDAPAuthenticationBackendConfiguration struct {
Password string `mapstructure:"password"` Password string `mapstructure:"password"`
StartTLS bool `mapstructure:"start_tls"` StartTLS bool `mapstructure:"start_tls"`
TLS *TLSConfig `mapstructure:"tls"` TLS *TLSConfig `mapstructure:"tls"`
SkipVerify *bool `mapstructure:"skip_verify"` // Deprecated: Replaced with LDAPAuthenticationBackendConfiguration.TLS.SkipVerify. TODO: Remove in 4.28.
MinimumTLSVersion string `mapstructure:"minimum_tls_version"` // Deprecated: Replaced with LDAPAuthenticationBackendConfiguration.TLS.MinimumVersion. TODO: Remove in 4.28.
} }
// FileAuthenticationBackendConfiguration represents the configuration related to file-based backend. // FileAuthenticationBackendConfiguration represents the configuration related to file-based backend.
@ -41,7 +39,7 @@ type PasswordConfiguration struct {
type AuthenticationBackendConfiguration struct { type AuthenticationBackendConfiguration struct {
DisableResetPassword bool `mapstructure:"disable_reset_password"` DisableResetPassword bool `mapstructure:"disable_reset_password"`
RefreshInterval string `mapstructure:"refresh_interval"` RefreshInterval string `mapstructure:"refresh_interval"`
Ldap *LDAPAuthenticationBackendConfiguration `mapstructure:"ldap"` LDAP *LDAPAuthenticationBackendConfiguration `mapstructure:"ldap"`
File *FileAuthenticationBackendConfiguration `mapstructure:"file"` File *FileAuthenticationBackendConfiguration `mapstructure:"file"`
} }

View File

@ -18,8 +18,6 @@ type SMTPNotifierConfiguration struct {
DisableRequireTLS bool `mapstructure:"disable_require_tls"` DisableRequireTLS bool `mapstructure:"disable_require_tls"`
DisableHTMLEmails bool `mapstructure:"disable_html_emails"` DisableHTMLEmails bool `mapstructure:"disable_html_emails"`
TLS *TLSConfig `mapstructure:"tls"` TLS *TLSConfig `mapstructure:"tls"`
TrustedCert string `mapstructure:"trusted_cert"` // Deprecated: Replaced with Global Option CertificatesDirectory. TODO: Remove in 4.28.
DisableVerifyCert *bool `mapstructure:"disable_verify_cert"` // Deprecated: Replaced with LDAPAuthenticationBackendConfiguration.TLS.SkipVerify. TODO: Remove in 4.28.
} }
// NotifierConfiguration represents the configuration of the notifier to use when sending notifications to users. // NotifierConfiguration represents the configuration of the notifier to use when sending notifications to users.

View File

@ -10,6 +10,32 @@ import (
"github.com/authelia/authelia/internal/utils" "github.com/authelia/authelia/internal/utils"
) )
// 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)
}
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))
}
}
}
//nolint:gocyclo // TODO: Consider refactoring/simplifying, time permitting. //nolint:gocyclo // TODO: Consider refactoring/simplifying, time permitting.
func validateFileAuthenticationBackend(configuration *schema.FileAuthenticationBackendConfiguration, validator *schema.StructValidator) { func validateFileAuthenticationBackend(configuration *schema.FileAuthenticationBackendConfiguration, validator *schema.StructValidator) {
if configuration.Path == "" { if configuration.Path == "" {
@ -72,14 +98,66 @@ func validateFileAuthenticationBackend(configuration *schema.FileAuthenticationB
} }
} }
func validateLDAPAuthenticationBackend(configuration *schema.LDAPAuthenticationBackendConfiguration, validator *schema.StructValidator) {
if configuration.Implementation == "" {
configuration.Implementation = schema.DefaultLDAPAuthenticationBackendConfiguration.Implementation
}
if configuration.TLS == nil {
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))
}
switch configuration.Implementation {
case schema.LDAPImplementationCustom:
setDefaultImplementationCustomLDAPAuthenticationBackend(configuration)
case schema.LDAPImplementationActiveDirectory:
setDefaultImplementationActiveDirectoryLDAPAuthenticationBackend(configuration)
default:
validator.Push(fmt.Errorf("authentication backend ldap implementation must be blank or one of the following values `%s`, `%s`", schema.LDAPImplementationCustom, schema.LDAPImplementationActiveDirectory))
}
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}"))
}
if configuration.URL == "" {
validator.Push(errors.New("Please provide a URL to the LDAP server"))
} else {
ldapURL, serverName := validateLDAPURL(configuration.URL, validator)
configuration.URL = ldapURL
if configuration.TLS.ServerName == "" {
configuration.TLS.ServerName = serverName
}
}
validateLDAPRequiredParameters(configuration, validator)
}
// Wrapper for test purposes to exclude the hostname from the return. // Wrapper for test purposes to exclude the hostname from the return.
func validateLdapURLSimple(ldapURL string, validator *schema.StructValidator) (finalURL string) { func validateLDAPURLSimple(ldapURL string, validator *schema.StructValidator) (finalURL string) {
finalURL, _ = validateLdapURL(ldapURL, validator) finalURL, _ = validateLDAPURL(ldapURL, validator)
return finalURL return finalURL
} }
func validateLdapURL(ldapURL string, validator *schema.StructValidator) (finalURL string, hostname string) { func validateLDAPURL(ldapURL string, validator *schema.StructValidator) (finalURL string, hostname string) {
parsedURL, err := url.Parse(ldapURL) parsedURL, err := url.Parse(ldapURL)
if err != nil { if err != nil {
@ -95,64 +173,7 @@ func validateLdapURL(ldapURL string, validator *schema.StructValidator) (finalUR
return parsedURL.String(), parsedURL.Hostname() return parsedURL.String(), parsedURL.Hostname()
} }
//nolint:gocyclo // TODO: Consider refactoring/simplifying, time permitting. func validateLDAPRequiredParameters(configuration *schema.LDAPAuthenticationBackendConfiguration, validator *schema.StructValidator) {
func validateLdapAuthenticationBackend(configuration *schema.LDAPAuthenticationBackendConfiguration, validator *schema.StructValidator) {
if configuration.Implementation == "" {
configuration.Implementation = schema.DefaultLDAPAuthenticationBackendConfiguration.Implementation
}
nilTLS := configuration.TLS == nil
if nilTLS {
configuration.TLS = schema.DefaultLDAPAuthenticationBackendConfiguration.TLS
}
// Deprecated. Maps deprecated values to the new ones. TODO: Remove in 4.28 (if block).
if configuration.SkipVerify != nil {
validator.PushWarning(errors.New("DEPRECATED: LDAP Auth Backend `skip_verify` option has been replaced by `authentication_backend.ldap.tls.skip_verify` (will be removed in 4.28.0)"))
if nilTLS {
configuration.TLS.SkipVerify = *configuration.SkipVerify
}
}
// Deprecated. Maps deprecated values to the new ones. TODO: Remove in 4.28 (if block).
if configuration.MinimumTLSVersion != "" {
validator.PushWarning(errors.New("DEPRECATED: LDAP Auth Backend `minimum_tls_version` option has been replaced by `authentication_backend.ldap.tls.minimum_version` (will be removed in 4.28.0)"))
if nilTLS {
configuration.TLS.MinimumVersion = configuration.MinimumTLSVersion
}
}
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))
}
switch configuration.Implementation {
case schema.LDAPImplementationCustom:
setDefaultImplementationCustomLdapAuthenticationBackend(configuration)
case schema.LDAPImplementationActiveDirectory:
setDefaultImplementationActiveDirectoryLdapAuthenticationBackend(configuration)
default:
validator.Push(fmt.Errorf("authentication backend ldap implementation must be blank or one of the following values `%s`, `%s`", schema.LDAPImplementationCustom, schema.LDAPImplementationActiveDirectory))
}
if configuration.URL == "" {
validator.Push(errors.New("Please provide a URL to the LDAP server"))
} else {
ldapURL, serverName := validateLdapURL(configuration.URL, validator)
configuration.URL = ldapURL
if configuration.TLS.ServerName == "" {
configuration.TLS.ServerName = serverName
}
}
// 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) // 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)
if configuration.User == "" { if configuration.User == "" {
validator.Push(errors.New("Please provide a user name to connect to the LDAP server")) validator.Push(errors.New("Please provide a user name to connect to the LDAP server"))
@ -193,7 +214,7 @@ func validateLdapAuthenticationBackend(configuration *schema.LDAPAuthenticationB
} }
} }
func setDefaultImplementationActiveDirectoryLdapAuthenticationBackend(configuration *schema.LDAPAuthenticationBackendConfiguration) { func setDefaultImplementationActiveDirectoryLDAPAuthenticationBackend(configuration *schema.LDAPAuthenticationBackendConfiguration) {
if configuration.UsersFilter == "" { if configuration.UsersFilter == "" {
configuration.UsersFilter = schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.UsersFilter configuration.UsersFilter = schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.UsersFilter
} }
@ -219,7 +240,7 @@ func setDefaultImplementationActiveDirectoryLdapAuthenticationBackend(configurat
} }
} }
func setDefaultImplementationCustomLdapAuthenticationBackend(configuration *schema.LDAPAuthenticationBackendConfiguration) { func setDefaultImplementationCustomLDAPAuthenticationBackend(configuration *schema.LDAPAuthenticationBackendConfiguration) {
if configuration.UsernameAttribute == "" { if configuration.UsernameAttribute == "" {
configuration.UsernameAttribute = schema.DefaultLDAPAuthenticationBackendConfiguration.UsernameAttribute configuration.UsernameAttribute = schema.DefaultLDAPAuthenticationBackendConfiguration.UsernameAttribute
} }
@ -236,29 +257,3 @@ func setDefaultImplementationCustomLdapAuthenticationBackend(configuration *sche
configuration.DisplayNameAttribute = schema.DefaultLDAPAuthenticationBackendConfiguration.DisplayNameAttribute configuration.DisplayNameAttribute = schema.DefaultLDAPAuthenticationBackendConfiguration.DisplayNameAttribute
} }
} }
// 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)
}
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))
}
}
}

View File

@ -14,7 +14,7 @@ func TestShouldRaiseErrorWhenBothBackendsProvided(t *testing.T) {
validator := schema.NewStructValidator() validator := schema.NewStructValidator()
backendConfig := schema.AuthenticationBackendConfiguration{} backendConfig := schema.AuthenticationBackendConfiguration{}
backendConfig.Ldap = &schema.LDAPAuthenticationBackendConfiguration{} backendConfig.LDAP = &schema.LDAPAuthenticationBackendConfiguration{}
backendConfig.File = &schema.FileAuthenticationBackendConfiguration{ backendConfig.File = &schema.FileAuthenticationBackendConfiguration{
Path: "/tmp", Path: "/tmp",
} }
@ -202,47 +202,47 @@ func TestFileBasedAuthenticationBackend(t *testing.T) {
suite.Run(t, new(FileBasedAuthenticationBackend)) suite.Run(t, new(FileBasedAuthenticationBackend))
} }
type LdapAuthenticationBackendSuite struct { type LDAPAuthenticationBackendSuite struct {
suite.Suite suite.Suite
configuration schema.AuthenticationBackendConfiguration configuration schema.AuthenticationBackendConfiguration
validator *schema.StructValidator validator *schema.StructValidator
} }
func (suite *LdapAuthenticationBackendSuite) SetupTest() { func (suite *LDAPAuthenticationBackendSuite) SetupTest() {
suite.validator = schema.NewStructValidator() suite.validator = schema.NewStructValidator()
suite.configuration = schema.AuthenticationBackendConfiguration{} suite.configuration = schema.AuthenticationBackendConfiguration{}
suite.configuration.Ldap = &schema.LDAPAuthenticationBackendConfiguration{} suite.configuration.LDAP = &schema.LDAPAuthenticationBackendConfiguration{}
suite.configuration.Ldap.Implementation = schema.LDAPImplementationCustom suite.configuration.LDAP.Implementation = schema.LDAPImplementationCustom
suite.configuration.Ldap.URL = testLDAPURL suite.configuration.LDAP.URL = testLDAPURL
suite.configuration.Ldap.User = testLDAPUser suite.configuration.LDAP.User = testLDAPUser
suite.configuration.Ldap.Password = testLDAPPassword suite.configuration.LDAP.Password = testLDAPPassword
suite.configuration.Ldap.BaseDN = testLDAPBaseDN suite.configuration.LDAP.BaseDN = testLDAPBaseDN
suite.configuration.Ldap.UsernameAttribute = "uid" suite.configuration.LDAP.UsernameAttribute = "uid"
suite.configuration.Ldap.UsersFilter = "({username_attribute}={input})" suite.configuration.LDAP.UsersFilter = "({username_attribute}={input})"
suite.configuration.Ldap.GroupsFilter = "(cn={input})" suite.configuration.LDAP.GroupsFilter = "(cn={input})"
} }
func (suite *LdapAuthenticationBackendSuite) TestShouldValidateCompleteConfiguration() { func (suite *LDAPAuthenticationBackendSuite) TestShouldValidateCompleteConfiguration() {
ValidateAuthenticationBackend(&suite.configuration, suite.validator) ValidateAuthenticationBackend(&suite.configuration, suite.validator)
suite.Assert().False(suite.validator.HasWarnings()) suite.Assert().False(suite.validator.HasWarnings())
suite.Assert().False(suite.validator.HasErrors()) suite.Assert().False(suite.validator.HasErrors())
} }
func (suite *LdapAuthenticationBackendSuite) TestShouldValidateDefaultImplementationAndUsernameAttribute() { func (suite *LDAPAuthenticationBackendSuite) TestShouldValidateDefaultImplementationAndUsernameAttribute() {
suite.configuration.Ldap.Implementation = "" suite.configuration.LDAP.Implementation = ""
suite.configuration.Ldap.UsernameAttribute = "" suite.configuration.LDAP.UsernameAttribute = ""
ValidateAuthenticationBackend(&suite.configuration, suite.validator) ValidateAuthenticationBackend(&suite.configuration, suite.validator)
suite.Assert().Equal(schema.LDAPImplementationCustom, suite.configuration.Ldap.Implementation) suite.Assert().Equal(schema.LDAPImplementationCustom, suite.configuration.LDAP.Implementation)
suite.Assert().Equal(suite.configuration.Ldap.UsernameAttribute, schema.DefaultLDAPAuthenticationBackendConfiguration.UsernameAttribute) suite.Assert().Equal(suite.configuration.LDAP.UsernameAttribute, schema.DefaultLDAPAuthenticationBackendConfiguration.UsernameAttribute)
suite.Assert().False(suite.validator.HasWarnings()) suite.Assert().False(suite.validator.HasWarnings())
suite.Assert().False(suite.validator.HasErrors()) suite.Assert().False(suite.validator.HasErrors())
} }
func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseErrorWhenImplementationIsInvalidMSAD() { func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorWhenImplementationIsInvalidMSAD() {
suite.configuration.Ldap.Implementation = "masd" suite.configuration.LDAP.Implementation = "masd"
ValidateAuthenticationBackend(&suite.configuration, suite.validator) ValidateAuthenticationBackend(&suite.configuration, suite.validator)
@ -252,8 +252,8 @@ func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseErrorWhenImplementat
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication backend ldap implementation must be blank or one of the following values `custom`, `activedirectory`") suite.Assert().EqualError(suite.validator.Errors()[0], "authentication backend ldap implementation must be blank or one of the following values `custom`, `activedirectory`")
} }
func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseErrorWhenURLNotProvided() { func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorWhenURLNotProvided() {
suite.configuration.Ldap.URL = "" suite.configuration.LDAP.URL = ""
ValidateAuthenticationBackend(&suite.configuration, suite.validator) ValidateAuthenticationBackend(&suite.configuration, suite.validator)
suite.Assert().False(suite.validator.HasWarnings()) suite.Assert().False(suite.validator.HasWarnings())
@ -262,8 +262,8 @@ func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseErrorWhenURLNotProvi
suite.Assert().EqualError(suite.validator.Errors()[0], "Please provide a URL to the LDAP server") suite.Assert().EqualError(suite.validator.Errors()[0], "Please provide a URL to the LDAP server")
} }
func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseErrorWhenUserNotProvided() { func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorWhenUserNotProvided() {
suite.configuration.Ldap.User = "" suite.configuration.LDAP.User = ""
ValidateAuthenticationBackend(&suite.configuration, suite.validator) ValidateAuthenticationBackend(&suite.configuration, suite.validator)
@ -273,8 +273,8 @@ func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseErrorWhenUserNotProv
suite.Assert().EqualError(suite.validator.Errors()[0], "Please provide a user name to connect to the LDAP server") suite.Assert().EqualError(suite.validator.Errors()[0], "Please provide a user name to connect to the LDAP server")
} }
func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseErrorWhenPasswordNotProvided() { func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorWhenPasswordNotProvided() {
suite.configuration.Ldap.Password = "" suite.configuration.LDAP.Password = ""
ValidateAuthenticationBackend(&suite.configuration, suite.validator) ValidateAuthenticationBackend(&suite.configuration, suite.validator)
@ -284,8 +284,8 @@ func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseErrorWhenPasswordNot
suite.Assert().EqualError(suite.validator.Errors()[0], "Please provide a password to connect to the LDAP server") suite.Assert().EqualError(suite.validator.Errors()[0], "Please provide a password to connect to the LDAP server")
} }
func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseErrorWhenBaseDNNotProvided() { func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorWhenBaseDNNotProvided() {
suite.configuration.Ldap.BaseDN = "" suite.configuration.LDAP.BaseDN = ""
ValidateAuthenticationBackend(&suite.configuration, suite.validator) ValidateAuthenticationBackend(&suite.configuration, suite.validator)
@ -295,8 +295,8 @@ func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseErrorWhenBaseDNNotPr
suite.Assert().EqualError(suite.validator.Errors()[0], "Please provide a base DN to connect to the LDAP server") suite.Assert().EqualError(suite.validator.Errors()[0], "Please provide a base DN to connect to the LDAP server")
} }
func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseOnEmptyGroupsFilter() { func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseOnEmptyGroupsFilter() {
suite.configuration.Ldap.GroupsFilter = "" suite.configuration.LDAP.GroupsFilter = ""
ValidateAuthenticationBackend(&suite.configuration, suite.validator) ValidateAuthenticationBackend(&suite.configuration, suite.validator)
@ -306,8 +306,8 @@ func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseOnEmptyGroupsFilter(
suite.Assert().EqualError(suite.validator.Errors()[0], "Please provide a groups filter with `groups_filter` attribute") suite.Assert().EqualError(suite.validator.Errors()[0], "Please provide a groups filter with `groups_filter` attribute")
} }
func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseOnEmptyUsersFilter() { func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseOnEmptyUsersFilter() {
suite.configuration.Ldap.UsersFilter = "" suite.configuration.LDAP.UsersFilter = ""
ValidateAuthenticationBackend(&suite.configuration, suite.validator) ValidateAuthenticationBackend(&suite.configuration, suite.validator)
@ -317,8 +317,8 @@ func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseOnEmptyUsersFilter()
suite.Assert().EqualError(suite.validator.Errors()[0], "Please provide a users filter with `users_filter` attribute") suite.Assert().EqualError(suite.validator.Errors()[0], "Please provide a users filter with `users_filter` attribute")
} }
func (suite *LdapAuthenticationBackendSuite) TestShouldNotRaiseOnEmptyUsernameAttribute() { func (suite *LDAPAuthenticationBackendSuite) TestShouldNotRaiseOnEmptyUsernameAttribute() {
suite.configuration.Ldap.UsernameAttribute = "" suite.configuration.LDAP.UsernameAttribute = ""
ValidateAuthenticationBackend(&suite.configuration, suite.validator) ValidateAuthenticationBackend(&suite.configuration, suite.validator)
@ -326,7 +326,7 @@ func (suite *LdapAuthenticationBackendSuite) TestShouldNotRaiseOnEmptyUsernameAt
suite.Assert().False(suite.validator.HasErrors()) suite.Assert().False(suite.validator.HasErrors())
} }
func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseOnBadRefreshInterval() { func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseOnBadRefreshInterval() {
suite.configuration.RefreshInterval = "blah" suite.configuration.RefreshInterval = "blah"
ValidateAuthenticationBackend(&suite.configuration, suite.validator) ValidateAuthenticationBackend(&suite.configuration, suite.validator)
@ -337,43 +337,60 @@ func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseOnBadRefreshInterval
suite.Assert().EqualError(suite.validator.Errors()[0], "Auth Backend `refresh_interval` is configured to 'blah' but it must be either a duration notation or one of 'disable', or 'always'. Error from parser: Could not convert the input string of blah into a duration") suite.Assert().EqualError(suite.validator.Errors()[0], "Auth Backend `refresh_interval` is configured to 'blah' but it must be either a duration notation or one of 'disable', or 'always'. Error from parser: Could not convert the input string of blah into a duration")
} }
func (suite *LdapAuthenticationBackendSuite) TestShouldSetDefaultImplementation() { func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultImplementation() {
ValidateAuthenticationBackend(&suite.configuration, suite.validator) ValidateAuthenticationBackend(&suite.configuration, suite.validator)
suite.Assert().False(suite.validator.HasWarnings()) suite.Assert().False(suite.validator.HasWarnings())
suite.Assert().False(suite.validator.HasErrors()) suite.Assert().False(suite.validator.HasErrors())
suite.Assert().Equal(schema.LDAPImplementationCustom, suite.configuration.Ldap.Implementation) suite.Assert().Equal(schema.LDAPImplementationCustom, suite.configuration.LDAP.Implementation)
} }
func (suite *LdapAuthenticationBackendSuite) TestShouldSetDefaultGroupNameAttribute() { func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorOnBadFilterPlaceholders() {
suite.configuration.LDAP.UsersFilter = "(&({username_attribute}={0})(objectCategory=person)(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=2))"
suite.configuration.LDAP.GroupsFilter = "(&(member={0})(objectClass=group)(objectCategory=group))"
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
suite.Assert().False(suite.validator.HasWarnings())
suite.Assert().True(suite.validator.HasErrors())
suite.Require().Len(suite.validator.Errors(), 2)
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication backend ldap users filter must "+
"not contain removed placeholders, {0} has been replaced with {input}")
suite.Assert().EqualError(suite.validator.Errors()[1], "authentication backend ldap groups filter must "+
"not contain removed placeholders, "+
"{0} has been replaced with {input} and {1} has been replaced with {username}")
}
func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultGroupNameAttribute() {
ValidateAuthenticationBackend(&suite.configuration, suite.validator) ValidateAuthenticationBackend(&suite.configuration, suite.validator)
suite.Assert().False(suite.validator.HasWarnings()) suite.Assert().False(suite.validator.HasWarnings())
suite.Assert().False(suite.validator.HasErrors()) suite.Assert().False(suite.validator.HasErrors())
suite.Assert().Equal("cn", suite.configuration.Ldap.GroupNameAttribute) suite.Assert().Equal("cn", suite.configuration.LDAP.GroupNameAttribute)
} }
func (suite *LdapAuthenticationBackendSuite) TestShouldSetDefaultMailAttribute() { func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultMailAttribute() {
ValidateAuthenticationBackend(&suite.configuration, suite.validator) ValidateAuthenticationBackend(&suite.configuration, suite.validator)
suite.Assert().False(suite.validator.HasWarnings()) suite.Assert().False(suite.validator.HasWarnings())
suite.Assert().False(suite.validator.HasErrors()) suite.Assert().False(suite.validator.HasErrors())
suite.Assert().Equal("mail", suite.configuration.Ldap.MailAttribute) suite.Assert().Equal("mail", suite.configuration.LDAP.MailAttribute)
} }
func (suite *LdapAuthenticationBackendSuite) TestShouldSetDefaultDisplayNameAttribute() { func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultDisplayNameAttribute() {
ValidateAuthenticationBackend(&suite.configuration, suite.validator) ValidateAuthenticationBackend(&suite.configuration, suite.validator)
suite.Assert().False(suite.validator.HasWarnings()) suite.Assert().False(suite.validator.HasWarnings())
suite.Assert().False(suite.validator.HasErrors()) suite.Assert().False(suite.validator.HasErrors())
suite.Assert().Equal("displayname", suite.configuration.Ldap.DisplayNameAttribute) suite.Assert().Equal("displayname", suite.configuration.LDAP.DisplayNameAttribute)
} }
func (suite *LdapAuthenticationBackendSuite) TestShouldSetDefaultRefreshInterval() { func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultRefreshInterval() {
ValidateAuthenticationBackend(&suite.configuration, suite.validator) ValidateAuthenticationBackend(&suite.configuration, suite.validator)
suite.Assert().False(suite.validator.HasWarnings()) suite.Assert().False(suite.validator.HasWarnings())
@ -382,8 +399,8 @@ func (suite *LdapAuthenticationBackendSuite) TestShouldSetDefaultRefreshInterval
suite.Assert().Equal("5m", suite.configuration.RefreshInterval) suite.Assert().Equal("5m", suite.configuration.RefreshInterval)
} }
func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseWhenUsersFilterDoesNotContainEnclosingParenthesis() { func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseWhenUsersFilterDoesNotContainEnclosingParenthesis() {
suite.configuration.Ldap.UsersFilter = "{username_attribute}={input}" suite.configuration.LDAP.UsersFilter = "{username_attribute}={input}"
ValidateAuthenticationBackend(&suite.configuration, suite.validator) ValidateAuthenticationBackend(&suite.configuration, suite.validator)
@ -393,8 +410,8 @@ func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseWhenUsersFilterDoesN
suite.Assert().EqualError(suite.validator.Errors()[0], "The users filter should contain enclosing parenthesis. For instance {username_attribute}={input} should be ({username_attribute}={input})") suite.Assert().EqualError(suite.validator.Errors()[0], "The users filter should contain enclosing parenthesis. For instance {username_attribute}={input} should be ({username_attribute}={input})")
} }
func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseWhenGroupsFilterDoesNotContainEnclosingParenthesis() { func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseWhenGroupsFilterDoesNotContainEnclosingParenthesis() {
suite.configuration.Ldap.GroupsFilter = "cn={input}" suite.configuration.LDAP.GroupsFilter = "cn={input}"
ValidateAuthenticationBackend(&suite.configuration, suite.validator) ValidateAuthenticationBackend(&suite.configuration, suite.validator)
@ -404,8 +421,8 @@ func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseWhenGroupsFilterDoes
suite.Assert().EqualError(suite.validator.Errors()[0], "The groups filter should contain enclosing parenthesis. For instance cn={input} should be (cn={input})") suite.Assert().EqualError(suite.validator.Errors()[0], "The groups filter should contain enclosing parenthesis. For instance cn={input} should be (cn={input})")
} }
func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseWhenUsersFilterDoesNotContainUsernameAttribute() { func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseWhenUsersFilterDoesNotContainUsernameAttribute() {
suite.configuration.Ldap.UsersFilter = "(&({mail_attribute}={input})(objectClass=person))" suite.configuration.LDAP.UsersFilter = "(&({mail_attribute}={input})(objectClass=person))"
ValidateAuthenticationBackend(&suite.configuration, suite.validator) ValidateAuthenticationBackend(&suite.configuration, suite.validator)
suite.Assert().False(suite.validator.HasWarnings()) suite.Assert().False(suite.validator.HasWarnings())
@ -414,8 +431,8 @@ func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseWhenUsersFilterDoesN
suite.Assert().EqualError(suite.validator.Errors()[0], "Unable to detect {username_attribute} placeholder in users_filter, your configuration is broken. Please review configuration options listed at https://www.authelia.com/docs/configuration/authentication/ldap.html") suite.Assert().EqualError(suite.validator.Errors()[0], "Unable to detect {username_attribute} placeholder in users_filter, your configuration is broken. Please review configuration options listed at https://www.authelia.com/docs/configuration/authentication/ldap.html")
} }
func (suite *LdapAuthenticationBackendSuite) TestShouldHelpDetectNoInputPlaceholder() { func (suite *LDAPAuthenticationBackendSuite) TestShouldHelpDetectNoInputPlaceholder() {
suite.configuration.Ldap.UsersFilter = "(&({username_attribute}={mail_attribute})(objectClass=person))" suite.configuration.LDAP.UsersFilter = "(&({username_attribute}={mail_attribute})(objectClass=person))"
ValidateAuthenticationBackend(&suite.configuration, suite.validator) ValidateAuthenticationBackend(&suite.configuration, suite.validator)
@ -425,40 +442,42 @@ func (suite *LdapAuthenticationBackendSuite) TestShouldHelpDetectNoInputPlacehol
suite.Assert().EqualError(suite.validator.Errors()[0], "Unable to detect {input} placeholder in users_filter, your configuration might be broken. Please review configuration options listed at https://www.authelia.com/docs/configuration/authentication/ldap.html") suite.Assert().EqualError(suite.validator.Errors()[0], "Unable to detect {input} placeholder in users_filter, your configuration might be broken. Please review configuration options listed at https://www.authelia.com/docs/configuration/authentication/ldap.html")
} }
func (suite *LdapAuthenticationBackendSuite) TestShouldAdaptLDAPURL() { func (suite *LDAPAuthenticationBackendSuite) TestShouldAdaptLDAPURL() {
suite.Assert().Equal("", validateLdapURLSimple("127.0.0.1", suite.validator)) suite.Assert().Equal("", validateLDAPURLSimple("127.0.0.1", suite.validator))
suite.Assert().False(suite.validator.HasWarnings()) suite.Assert().False(suite.validator.HasWarnings())
suite.Require().Len(suite.validator.Errors(), 1) suite.Require().Len(suite.validator.Errors(), 1)
suite.Assert().EqualError(suite.validator.Errors()[0], "Unknown scheme for ldap url, should be ldap:// or ldaps://") suite.Assert().EqualError(suite.validator.Errors()[0], "Unknown scheme for ldap url, should be ldap:// or ldaps://")
suite.Assert().Equal("", validateLdapURLSimple("127.0.0.1:636", suite.validator)) suite.Assert().Equal("", validateLDAPURLSimple("127.0.0.1:636", suite.validator))
suite.Assert().False(suite.validator.HasWarnings()) suite.Assert().False(suite.validator.HasWarnings())
suite.Require().Len(suite.validator.Errors(), 2) suite.Require().Len(suite.validator.Errors(), 2)
suite.Assert().EqualError(suite.validator.Errors()[1], "Unable to parse URL to ldap server. The scheme is probably missing: ldap:// or ldaps://") suite.Assert().EqualError(suite.validator.Errors()[1], "Unable to parse URL to ldap server. The scheme is probably missing: ldap:// or ldaps://")
suite.Assert().Equal("ldap://127.0.0.1", validateLdapURLSimple("ldap://127.0.0.1", suite.validator)) suite.Assert().Equal("ldap://127.0.0.1", validateLDAPURLSimple("ldap://127.0.0.1", suite.validator))
suite.Assert().Equal("ldap://127.0.0.1:390", validateLdapURLSimple("ldap://127.0.0.1:390", suite.validator)) suite.Assert().Equal("ldap://127.0.0.1:390", validateLDAPURLSimple("ldap://127.0.0.1:390", suite.validator))
suite.Assert().Equal("ldap://127.0.0.1/abc", validateLdapURLSimple("ldap://127.0.0.1/abc", suite.validator)) suite.Assert().Equal("ldap://127.0.0.1/abc", validateLDAPURLSimple("ldap://127.0.0.1/abc", suite.validator))
suite.Assert().Equal("ldap://127.0.0.1/abc?test=abc&x=y", validateLdapURLSimple("ldap://127.0.0.1/abc?test=abc&x=y", suite.validator)) suite.Assert().Equal("ldap://127.0.0.1/abc?test=abc&x=y", validateLDAPURLSimple("ldap://127.0.0.1/abc?test=abc&x=y", suite.validator))
suite.Assert().Equal("ldaps://127.0.0.1:390", validateLdapURLSimple("ldaps://127.0.0.1:390", suite.validator)) suite.Assert().Equal("ldaps://127.0.0.1:390", validateLDAPURLSimple("ldaps://127.0.0.1:390", suite.validator))
suite.Assert().Equal("ldaps://127.0.0.1", validateLdapURLSimple("ldaps://127.0.0.1", suite.validator)) suite.Assert().Equal("ldaps://127.0.0.1", validateLDAPURLSimple("ldaps://127.0.0.1", suite.validator))
} }
func (suite *LdapAuthenticationBackendSuite) TestShouldDefaultTLS12() { func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultTLSMinimumVersion() {
suite.configuration.LDAP.TLS = &schema.TLSConfig{MinimumVersion: ""}
ValidateAuthenticationBackend(&suite.configuration, suite.validator) ValidateAuthenticationBackend(&suite.configuration, suite.validator)
suite.Assert().False(suite.validator.HasWarnings()) suite.Assert().False(suite.validator.HasWarnings())
suite.Assert().False(suite.validator.HasErrors()) suite.Assert().False(suite.validator.HasErrors())
suite.Assert().Equal(schema.DefaultLDAPAuthenticationBackendConfiguration.MinimumTLSVersion, suite.configuration.Ldap.MinimumTLSVersion) suite.Assert().Equal(schema.DefaultLDAPAuthenticationBackendConfiguration.TLS.MinimumVersion, suite.configuration.LDAP.TLS.MinimumVersion)
} }
func (suite *LdapAuthenticationBackendSuite) TestShouldNotAllowInvalidTLSValue() { func (suite *LDAPAuthenticationBackendSuite) TestShouldNotAllowInvalidTLSValue() {
suite.configuration.Ldap.TLS = &schema.TLSConfig{ suite.configuration.LDAP.TLS = &schema.TLSConfig{
MinimumVersion: "SSL2.0", MinimumVersion: "SSL2.0",
} }
@ -470,59 +489,8 @@ func (suite *LdapAuthenticationBackendSuite) TestShouldNotAllowInvalidTLSValue()
suite.Assert().EqualError(suite.validator.Errors()[0], "error occurred validating the LDAP minimum_tls_version key with value SSL2.0: supplied TLS version isn't supported") suite.Assert().EqualError(suite.validator.Errors()[0], "error occurred validating the LDAP minimum_tls_version key with value SSL2.0: supplied TLS version isn't supported")
} }
// Deprecated: Temporary Test. TODO: Remove in 4.28 (Whole Test).
func (suite *LdapAuthenticationBackendSuite) TestShouldReturnDeprecationWarningsAndNoMappingFor428() {
var skipVerify = true
suite.configuration.Ldap.MinimumTLSVersion = "TLS1.0"
suite.configuration.Ldap.SkipVerify = &skipVerify
suite.configuration.Ldap.TLS = nil
suite.configuration.Ldap.TLS = &schema.TLSConfig{
ServerName: "golang.org",
MinimumVersion: "",
}
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
// Should not override since TLS schema is defined
suite.Assert().Equal(false, suite.configuration.Ldap.TLS.SkipVerify)
suite.Assert().Equal(schema.DefaultLDAPAuthenticationBackendConfiguration.TLS.MinimumVersion, suite.configuration.Ldap.TLS.MinimumVersion)
suite.Assert().False(suite.validator.HasErrors())
suite.Require().Len(suite.validator.Warnings(), 2)
warnings := suite.validator.Warnings()
suite.Assert().EqualError(warnings[0], "DEPRECATED: LDAP Auth Backend `skip_verify` option has been replaced by `authentication_backend.ldap.tls.skip_verify` (will be removed in 4.28.0)")
suite.Assert().EqualError(warnings[1], "DEPRECATED: LDAP Auth Backend `minimum_tls_version` option has been replaced by `authentication_backend.ldap.tls.minimum_version` (will be removed in 4.28.0)")
}
// Deprecated: Temporary Test. TODO: Remove in 4.28 (Whole Test).
func (suite *LdapAuthenticationBackendSuite) TestShouldReturnDeprecationWarningsAndMappingFor428() {
var skipVerify = true
tlsVersion := "TLS1.1"
suite.configuration.Ldap.MinimumTLSVersion = tlsVersion
suite.configuration.Ldap.SkipVerify = &skipVerify
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
// Should override since TLS schema is not defined
suite.Assert().Equal(true, suite.configuration.Ldap.TLS.SkipVerify)
suite.Assert().Equal(tlsVersion, suite.configuration.Ldap.TLS.MinimumVersion)
suite.Assert().False(suite.validator.HasErrors())
suite.Require().Len(suite.validator.Warnings(), 2)
warnings := suite.validator.Warnings()
suite.Assert().EqualError(warnings[0], "DEPRECATED: LDAP Auth Backend `skip_verify` option has been replaced by `authentication_backend.ldap.tls.skip_verify` (will be removed in 4.28.0)")
suite.Assert().EqualError(warnings[1], "DEPRECATED: LDAP Auth Backend `minimum_tls_version` option has been replaced by `authentication_backend.ldap.tls.minimum_version` (will be removed in 4.28.0)")
}
func TestLdapAuthenticationBackend(t *testing.T) { func TestLdapAuthenticationBackend(t *testing.T) {
suite.Run(t, new(LdapAuthenticationBackendSuite)) suite.Run(t, new(LDAPAuthenticationBackendSuite))
} }
type ActiveDirectoryAuthenticationBackendSuite struct { type ActiveDirectoryAuthenticationBackendSuite struct {
@ -534,13 +502,13 @@ type ActiveDirectoryAuthenticationBackendSuite struct {
func (suite *ActiveDirectoryAuthenticationBackendSuite) SetupTest() { func (suite *ActiveDirectoryAuthenticationBackendSuite) SetupTest() {
suite.validator = schema.NewStructValidator() suite.validator = schema.NewStructValidator()
suite.configuration = schema.AuthenticationBackendConfiguration{} suite.configuration = schema.AuthenticationBackendConfiguration{}
suite.configuration.Ldap = &schema.LDAPAuthenticationBackendConfiguration{} suite.configuration.LDAP = &schema.LDAPAuthenticationBackendConfiguration{}
suite.configuration.Ldap.Implementation = schema.LDAPImplementationActiveDirectory suite.configuration.LDAP.Implementation = schema.LDAPImplementationActiveDirectory
suite.configuration.Ldap.URL = testLDAPURL suite.configuration.LDAP.URL = testLDAPURL
suite.configuration.Ldap.User = testLDAPUser suite.configuration.LDAP.User = testLDAPUser
suite.configuration.Ldap.Password = testLDAPPassword suite.configuration.LDAP.Password = testLDAPPassword
suite.configuration.Ldap.BaseDN = testLDAPBaseDN suite.configuration.LDAP.BaseDN = testLDAPBaseDN
suite.configuration.Ldap.TLS = schema.DefaultLDAPAuthenticationBackendConfiguration.TLS suite.configuration.LDAP.TLS = schema.DefaultLDAPAuthenticationBackendConfiguration.TLS
} }
func (suite *ActiveDirectoryAuthenticationBackendSuite) TestShouldSetActiveDirectoryDefaults() { func (suite *ActiveDirectoryAuthenticationBackendSuite) TestShouldSetActiveDirectoryDefaults() {
@ -550,52 +518,52 @@ func (suite *ActiveDirectoryAuthenticationBackendSuite) TestShouldSetActiveDirec
suite.Assert().False(suite.validator.HasErrors()) suite.Assert().False(suite.validator.HasErrors())
suite.Assert().Equal( suite.Assert().Equal(
suite.configuration.Ldap.UsersFilter, suite.configuration.LDAP.UsersFilter,
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.UsersFilter) schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.UsersFilter)
suite.Assert().Equal( suite.Assert().Equal(
suite.configuration.Ldap.UsernameAttribute, suite.configuration.LDAP.UsernameAttribute,
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.UsernameAttribute) schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.UsernameAttribute)
suite.Assert().Equal( suite.Assert().Equal(
suite.configuration.Ldap.DisplayNameAttribute, suite.configuration.LDAP.DisplayNameAttribute,
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.DisplayNameAttribute) schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.DisplayNameAttribute)
suite.Assert().Equal( suite.Assert().Equal(
suite.configuration.Ldap.MailAttribute, suite.configuration.LDAP.MailAttribute,
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.MailAttribute) schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.MailAttribute)
suite.Assert().Equal( suite.Assert().Equal(
suite.configuration.Ldap.GroupsFilter, suite.configuration.LDAP.GroupsFilter,
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.GroupsFilter) schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.GroupsFilter)
suite.Assert().Equal( suite.Assert().Equal(
suite.configuration.Ldap.GroupNameAttribute, suite.configuration.LDAP.GroupNameAttribute,
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.GroupNameAttribute) schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.GroupNameAttribute)
} }
func (suite *ActiveDirectoryAuthenticationBackendSuite) TestShouldOnlySetDefaultsIfNotManuallyConfigured() { func (suite *ActiveDirectoryAuthenticationBackendSuite) TestShouldOnlySetDefaultsIfNotManuallyConfigured() {
suite.configuration.Ldap.UsersFilter = "(&({username_attribute}={input})(objectCategory=person)(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=2))" suite.configuration.LDAP.UsersFilter = "(&({username_attribute}={input})(objectCategory=person)(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=2))"
suite.configuration.Ldap.UsernameAttribute = "cn" suite.configuration.LDAP.UsernameAttribute = "cn"
suite.configuration.Ldap.MailAttribute = "userPrincipalName" suite.configuration.LDAP.MailAttribute = "userPrincipalName"
suite.configuration.Ldap.DisplayNameAttribute = "name" suite.configuration.LDAP.DisplayNameAttribute = "name"
suite.configuration.Ldap.GroupsFilter = "(&(member={dn})(objectClass=group)(objectCategory=group))" suite.configuration.LDAP.GroupsFilter = "(&(member={dn})(objectClass=group)(objectCategory=group))"
suite.configuration.Ldap.GroupNameAttribute = "distinguishedName" suite.configuration.LDAP.GroupNameAttribute = "distinguishedName"
ValidateAuthenticationBackend(&suite.configuration, suite.validator) ValidateAuthenticationBackend(&suite.configuration, suite.validator)
suite.Assert().NotEqual( suite.Assert().NotEqual(
suite.configuration.Ldap.UsersFilter, suite.configuration.LDAP.UsersFilter,
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.UsersFilter) schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.UsersFilter)
suite.Assert().NotEqual( suite.Assert().NotEqual(
suite.configuration.Ldap.UsernameAttribute, suite.configuration.LDAP.UsernameAttribute,
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.UsernameAttribute) schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.UsernameAttribute)
suite.Assert().NotEqual( suite.Assert().NotEqual(
suite.configuration.Ldap.DisplayNameAttribute, suite.configuration.LDAP.DisplayNameAttribute,
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.DisplayNameAttribute) schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.DisplayNameAttribute)
suite.Assert().NotEqual( suite.Assert().NotEqual(
suite.configuration.Ldap.MailAttribute, suite.configuration.LDAP.MailAttribute,
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.MailAttribute) schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.MailAttribute)
suite.Assert().NotEqual( suite.Assert().NotEqual(
suite.configuration.Ldap.GroupsFilter, suite.configuration.LDAP.GroupsFilter,
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.GroupsFilter) schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.GroupsFilter)
suite.Assert().NotEqual( suite.Assert().NotEqual(
suite.configuration.Ldap.GroupNameAttribute, suite.configuration.LDAP.GroupNameAttribute,
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.GroupNameAttribute) schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.GroupNameAttribute)
} }

View File

@ -5,6 +5,7 @@ const (
errFmtSessionRedisPortRange = "The port must be between 1 and 65535 for the %s session provider" errFmtSessionRedisPortRange = "The port must be between 1 and 65535 for the %s session provider"
errFmtSessionRedisHostRequired = "The host must be provided when using the %s session provider" errFmtSessionRedisHostRequired = "The host must be provided when using the %s session provider"
errFmtSessionRedisHostOrNodesRequired = "Either the host or a node must be provided when using the %s session provider" errFmtSessionRedisHostOrNodesRequired = "Either the host or a node must be provided when using the %s session provider"
errFmtReplacedConfigurationKey = "invalid configuration key '%s' was replaced by '%s'"
errFileHashing = "config key incorrect: authentication_backend.file.hashing should be authentication_backend.file.password" errFileHashing = "config key incorrect: authentication_backend.file.hashing should be authentication_backend.file.password"
errFilePHashing = "config key incorrect: authentication_backend.file.password_hashing should be authentication_backend.file.password" errFilePHashing = "config key incorrect: authentication_backend.file.password_hashing should be authentication_backend.file.password"
@ -138,12 +139,10 @@ var validKeys = []string{
"notifier.smtp.subject", "notifier.smtp.subject",
"notifier.smtp.startup_check_address", "notifier.smtp.startup_check_address",
"notifier.smtp.disable_require_tls", "notifier.smtp.disable_require_tls",
"notifier.smtp.trusted_cert", // TODO: Deprecated: Remove in 4.28.
"notifier.smtp.disable_html_emails", "notifier.smtp.disable_html_emails",
"notifier.smtp.tls.minimum_version", "notifier.smtp.tls.minimum_version",
"notifier.smtp.tls.skip_verify", "notifier.smtp.tls.skip_verify",
"notifier.smtp.tls.server_name", "notifier.smtp.tls.server_name",
"notifier.smtp.disable_verify_cert", // TODO: Deprecated: Remove in 4.28.
// Regulation Keys. // Regulation Keys.
"regulation.max_retries", "regulation.max_retries",
@ -175,8 +174,6 @@ var validKeys = []string{
"authentication_backend.ldap.tls.minimum_version", "authentication_backend.ldap.tls.minimum_version",
"authentication_backend.ldap.tls.skip_verify", "authentication_backend.ldap.tls.skip_verify",
"authentication_backend.ldap.tls.server_name", "authentication_backend.ldap.tls.server_name",
"authentication_backend.ldap.skip_verify", // TODO: Deprecated: Remove in 4.28.
"authentication_backend.ldap.minimum_tls_version", // TODO: Deprecated: Remove in 4.28.
// File Authentication Backend Keys. // File Authentication Backend Keys.
"authentication_backend.file.path", "authentication_backend.file.path",
@ -188,10 +185,19 @@ var validKeys = []string{
"authentication_backend.file.password.parallelism", "authentication_backend.file.password.parallelism",
} }
var replacedKeys = map[string]string{
"authentication_backend.ldap.skip_verify": "authentication_backend.ldap.tls.skip_verify",
"authentication_backend.ldap.minimum_tls_version": "authentication_backend.ldap.tls.minimum_version",
"notifier.smtp.disable_verify_cert": "notifier.smtp.tls.skip_verify",
"logs_file_path": "log_file",
"logs_level": "log_level",
}
var specificErrorKeys = map[string]string{ var specificErrorKeys = map[string]string{
"logs_file_path": "config key replaced: logs_file is now log_file",
"logs_level": "config key replaced: logs_level is now log_level",
"google_analytics": "config key removed: google_analytics - this functionality has been deprecated", "google_analytics": "config key removed: google_analytics - this functionality has been deprecated",
"notifier.smtp.trusted_cert": "invalid configuration key `notifier.smtp.trusted_cert` it has been removed, " +
"option has been replaced by the global option `certificates_directory`",
"authentication_backend.file.password_options.algorithm": errFilePOptions, "authentication_backend.file.password_options.algorithm": errFilePOptions,
"authentication_backend.file.password_options.iterations": errFilePOptions, "authentication_backend.file.password_options.iterations": errFilePOptions,
"authentication_backend.file.password_options.key_length": errFilePOptions, "authentication_backend.file.password_options.key_length": errFilePOptions,

View File

@ -21,6 +21,11 @@ func ValidateKeys(validator *schema.StructValidator, keys []string) {
continue continue
} }
if newKey, ok := replacedKeys[key]; ok {
validator.Push(fmt.Errorf(errFmtReplacedConfigurationKey, key, newKey))
continue
}
if err, ok := specificErrorKeys[key]; ok { if err, ok := specificErrorKeys[key]; ok {
if !utils.IsStringInSlice(err, errStrings) { if !utils.IsStringInSlice(err, errStrings) {
errStrings = append(errStrings, err) errStrings = append(errStrings, err)

View File

@ -1,6 +1,7 @@
package validator package validator
import ( import (
"fmt"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -62,8 +63,8 @@ func TestAllSpecificErrorKeys(t *testing.T) {
func TestSpecificErrorKeys(t *testing.T) { func TestSpecificErrorKeys(t *testing.T) {
configKeys := []string{ configKeys := []string{
"logs_level", "notifier.smtp.trusted_cert",
"logs_file_path", "google_analytics",
"authentication_backend.file.password_options.algorithm", "authentication_backend.file.password_options.algorithm",
"authentication_backend.file.password_options.iterations", // This should not show another error since our target for the specific error is password_options. "authentication_backend.file.password_options.iterations", // This should not show another error since our target for the specific error is password_options.
"authentication_backend.file.password_hashing.algorithm", "authentication_backend.file.password_hashing.algorithm",
@ -77,9 +78,49 @@ func TestSpecificErrorKeys(t *testing.T) {
require.Len(t, errs, 5) require.Len(t, errs, 5)
assert.EqualError(t, errs[0], specificErrorKeys["logs_level"]) assert.EqualError(t, errs[0], specificErrorKeys["notifier.smtp.trusted_cert"])
assert.EqualError(t, errs[1], specificErrorKeys["logs_file_path"]) assert.EqualError(t, errs[1], specificErrorKeys["google_analytics"])
assert.EqualError(t, errs[2], specificErrorKeys["authentication_backend.file.password_options.iterations"]) assert.EqualError(t, errs[2], specificErrorKeys["authentication_backend.file.password_options.iterations"])
assert.EqualError(t, errs[3], specificErrorKeys["authentication_backend.file.password_hashing.algorithm"]) assert.EqualError(t, errs[3], specificErrorKeys["authentication_backend.file.password_hashing.algorithm"])
assert.EqualError(t, errs[4], specificErrorKeys["authentication_backend.file.hashing.algorithm"]) assert.EqualError(t, errs[4], specificErrorKeys["authentication_backend.file.hashing.algorithm"])
} }
func TestReplacedErrors(t *testing.T) {
configKeys := []string{
"authentication_backend.ldap.skip_verify",
"authentication_backend.ldap.minimum_tls_version",
"notifier.smtp.disable_verify_cert",
"logs_file_path",
"logs_level",
}
val := schema.NewStructValidator()
ValidateKeys(val, configKeys)
warns := val.Warnings()
errs := val.Errors()
assert.Len(t, warns, 0)
require.Len(t, errs, 5)
assert.EqualError(t, errs[0], fmt.Sprintf(errFmtReplacedConfigurationKey, "authentication_backend.ldap.skip_verify", "authentication_backend.ldap.tls.skip_verify"))
assert.EqualError(t, errs[1], fmt.Sprintf(errFmtReplacedConfigurationKey, "authentication_backend.ldap.minimum_tls_version", "authentication_backend.ldap.tls.minimum_version"))
assert.EqualError(t, errs[2], fmt.Sprintf(errFmtReplacedConfigurationKey, "notifier.smtp.disable_verify_cert", "notifier.smtp.tls.skip_verify"))
assert.EqualError(t, errs[3], fmt.Sprintf(errFmtReplacedConfigurationKey, "logs_file_path", "log_file"))
assert.EqualError(t, errs[4], fmt.Sprintf(errFmtReplacedConfigurationKey, "logs_level", "log_level"))
}
func TestSecretKeysDontRaiseErrors(t *testing.T) {
configKeys := []string{}
for _, key := range SecretNames {
configKeys = append(configKeys, SecretNameToEnvName(key))
configKeys = append(configKeys, key)
}
val := schema.NewStructValidator()
ValidateKeys(val, configKeys)
assert.Len(t, val.Warnings(), 0)
assert.Len(t, val.Errors(), 0)
}

View File

@ -1,22 +1,17 @@
package validator package validator
import ( import (
"errors"
"fmt" "fmt"
"github.com/authelia/authelia/internal/configuration/schema" "github.com/authelia/authelia/internal/configuration/schema"
) )
// ValidateNotifier validates and update notifier configuration. // ValidateNotifier validates and update notifier configuration.
//nolint:gocyclo // TODO: Remove in 4.28. Should be able to remove this during the removal of deprecated config.
func ValidateNotifier(configuration *schema.NotifierConfiguration, validator *schema.StructValidator) { func ValidateNotifier(configuration *schema.NotifierConfiguration, validator *schema.StructValidator) {
if configuration.SMTP == nil && configuration.FileSystem == nil { if configuration.SMTP == nil && configuration.FileSystem == nil ||
configuration.SMTP != nil && configuration.FileSystem != nil {
validator.Push(fmt.Errorf("Notifier should be either `smtp` or `filesystem`")) validator.Push(fmt.Errorf("Notifier should be either `smtp` or `filesystem`"))
return
}
if configuration.SMTP != nil && configuration.FileSystem != nil {
validator.Push(fmt.Errorf("Notifier should be either `smtp` or `filesystem`"))
return return
} }
@ -28,51 +23,39 @@ func ValidateNotifier(configuration *schema.NotifierConfiguration, validator *sc
return return
} }
if configuration.SMTP != nil { validateSMTPNotifier(configuration.SMTP, validator)
if configuration.SMTP.StartupCheckAddress == "" { }
configuration.SMTP.StartupCheckAddress = "test@authelia.com"
}
if configuration.SMTP.Host == "" { func validateSMTPNotifier(configuration *schema.SMTPNotifierConfiguration, validator *schema.StructValidator) {
validator.Push(fmt.Errorf("Host of SMTP notifier must be provided")) if configuration.StartupCheckAddress == "" {
} configuration.StartupCheckAddress = "test@authelia.com"
}
if configuration.SMTP.Port == 0 { if configuration.Host == "" {
validator.Push(fmt.Errorf("Port of SMTP notifier must be provided")) validator.Push(fmt.Errorf("Host of SMTP notifier must be provided"))
} }
if configuration.SMTP.Sender == "" { if configuration.Port == 0 {
validator.Push(fmt.Errorf("Sender of SMTP notifier must be provided")) validator.Push(fmt.Errorf("Port of SMTP notifier must be provided"))
} }
if configuration.SMTP.Subject == "" { if configuration.Sender == "" {
configuration.SMTP.Subject = schema.DefaultSMTPNotifierConfiguration.Subject validator.Push(fmt.Errorf("Sender of SMTP notifier must be provided"))
} }
if configuration.SMTP.Identifier == "" { if configuration.Subject == "" {
configuration.SMTP.Identifier = schema.DefaultSMTPNotifierConfiguration.Identifier configuration.Subject = schema.DefaultSMTPNotifierConfiguration.Subject
} }
if configuration.SMTP.TLS == nil { if configuration.Identifier == "" {
configuration.SMTP.TLS = schema.DefaultSMTPNotifierConfiguration.TLS configuration.Identifier = schema.DefaultSMTPNotifierConfiguration.Identifier
}
// Deprecated. Maps deprecated values to the new ones. TODO: Remove in 4.28. if configuration.TLS == nil {
if configuration.SMTP.DisableVerifyCert != nil { configuration.TLS = schema.DefaultSMTPNotifierConfiguration.TLS
validator.PushWarning(errors.New("DEPRECATED: SMTP Notifier `disable_verify_cert` option has been replaced by `notifier.smtp.tls.skip_verify` (will be removed in 4.28.0)")) }
configuration.SMTP.TLS.SkipVerify = *configuration.SMTP.DisableVerifyCert if configuration.TLS.ServerName == "" {
} configuration.TLS.ServerName = configuration.Host
}
// Deprecated. Maps deprecated values to the new ones. TODO: Remove in 4.28.
if configuration.SMTP.TrustedCert != "" {
validator.PushWarning(errors.New("DEPRECATED: SMTP Notifier `trusted_cert` option has been replaced by the global option `certificates_directory` (will be removed in 4.28.0)"))
}
if configuration.SMTP.TLS.ServerName == "" {
configuration.SMTP.TLS.ServerName = configuration.SMTP.Host
}
return
} }
} }

View File

@ -127,29 +127,6 @@ func (suite *NotifierSuite) TestShouldEnsureSenderOfSMTPNotifierAreProvided() {
suite.Assert().EqualError(suite.validator.Errors()[0], "Sender of SMTP notifier must be provided") suite.Assert().EqualError(suite.validator.Errors()[0], "Sender of SMTP notifier must be provided")
} }
// Deprecated: Temporary Test. TODO: Remove in 4.28 (Whole Test).
func (suite *NotifierSuite) TestShouldReturnDeprecationWarningsFor428() {
var disableVerifyCert = true
suite.configuration.SMTP.TrustedCert = "/tmp"
suite.configuration.SMTP.DisableVerifyCert = &disableVerifyCert
ValidateNotifier(&suite.configuration, suite.validator)
suite.Require().True(suite.validator.HasWarnings())
suite.Assert().False(suite.validator.HasErrors())
warnings := suite.validator.Warnings()
suite.Require().Len(warnings, 2)
suite.Assert().EqualError(warnings[0], "DEPRECATED: SMTP Notifier `disable_verify_cert` option has been replaced by `notifier.smtp.tls.skip_verify` (will be removed in 4.28.0)")
suite.Assert().EqualError(warnings[1], "DEPRECATED: SMTP Notifier `trusted_cert` option has been replaced by the global option `certificates_directory` (will be removed in 4.28.0)")
// Should override since TLS schema is not defined
suite.Assert().Equal(true, suite.configuration.SMTP.TLS.SkipVerify)
}
func TestNotifierSuite(t *testing.T) { func TestNotifierSuite(t *testing.T) {
suite.Run(t, new(NotifierSuite)) suite.Run(t, new(NotifierSuite))
} }

View File

@ -43,8 +43,8 @@ func ValidateSecrets(configuration *schema.Configuration, validator *schema.Stru
} }
} }
if configuration.AuthenticationBackend.Ldap != nil { if configuration.AuthenticationBackend.LDAP != nil {
configuration.AuthenticationBackend.Ldap.Password = getSecretValue(SecretNames["LDAPPassword"], validator, viper) configuration.AuthenticationBackend.LDAP.Password = getSecretValue(SecretNames["LDAPPassword"], validator, viper)
} }
if configuration.Notifier != nil && configuration.Notifier.SMTP != nil { if configuration.Notifier != nil && configuration.Notifier.SMTP != nil {

View File

@ -402,7 +402,7 @@ func verifySessionHasUpToDateProfile(ctx *middlewares.AutheliaCtx, targetURL *ur
} }
func getProfileRefreshSettings(cfg schema.AuthenticationBackendConfiguration) (refresh bool, refreshInterval time.Duration) { func getProfileRefreshSettings(cfg schema.AuthenticationBackendConfiguration) (refresh bool, refreshInterval time.Duration) {
if cfg.Ldap != nil { if cfg.LDAP != nil {
if cfg.RefreshInterval == schema.ProfileRefreshDisabled { if cfg.RefreshInterval == schema.ProfileRefreshDisabled {
refresh = false refresh = false
refreshInterval = 0 refreshInterval = 0

View File

@ -23,7 +23,7 @@ import (
var verifyGetCfg = schema.AuthenticationBackendConfiguration{ var verifyGetCfg = schema.AuthenticationBackendConfiguration{
RefreshInterval: schema.RefreshIntervalDefault, RefreshInterval: schema.RefreshIntervalDefault,
Ldap: &schema.LDAPAuthenticationBackendConfiguration{}, LDAP: &schema.LDAPAuthenticationBackendConfiguration{},
} }
// Test getOriginalURL. // Test getOriginalURL.

View File

@ -27,9 +27,8 @@ func NewTLSConfig(config *schema.TLSConfig, defaultMinVersion uint16, certPool *
} }
} }
//nolint:gocyclo // TODO: Remove in 4.28. Should be able to remove the nolint during the removal of deprecated config.
// NewX509CertPool generates a x509.CertPool from the system PKI and the directory specified. // NewX509CertPool generates a x509.CertPool from the system PKI and the directory specified.
func NewX509CertPool(directory string, config *schema.Configuration) (certPool *x509.CertPool, errors []error, nonFatalErrors []error) { func NewX509CertPool(directory string) (certPool *x509.CertPool, errors []error, nonFatalErrors []error) {
certPool, err := x509.SystemCertPool() certPool, err := x509.SystemCertPool()
if err != nil { if err != nil {
nonFatalErrors = append(nonFatalErrors, fmt.Errorf("could not load system certificate pool which may result in untrusted certificate issues: %v", err)) nonFatalErrors = append(nonFatalErrors, fmt.Errorf("could not load system certificate pool which may result in untrusted certificate issues: %v", err))
@ -66,22 +65,6 @@ func NewX509CertPool(directory string, config *schema.Configuration) (certPool *
logger.Tracef("Finished scan of directory %s for certificates", directory) logger.Tracef("Finished scan of directory %s for certificates", directory)
// Deprecated. Maps deprecated values to the new ones. TODO: Remove in 4.28.
if config != nil && config.Notifier != nil && config.Notifier.SMTP != nil && config.Notifier.SMTP.TrustedCert != "" {
nonFatalErrors = append(nonFatalErrors, fmt.Errorf("defining the trusted cert in the SMTP notifier is deprecated and will be removed in 4.28.0, please use the global certificates_directory instead"))
if exists, _ := FileExists(config.Notifier.SMTP.TrustedCert); exists {
pem, err := ioutil.ReadFile(config.Notifier.SMTP.TrustedCert)
if err != nil {
errors = append(errors, fmt.Errorf("failed to read legacy SMTP trusted_cert (see the new certificates_directory option) certificate %s with error: %s", config.Notifier.SMTP.TrustedCert, err))
} else if ok := certPool.AppendCertsFromPEM(pem); !ok {
errors = append(errors, fmt.Errorf("could not import legacy SMTP trusted_cert (see the new certificates_directory option) certificate %s", config.Notifier.SMTP.TrustedCert))
}
} else {
errors = append(errors, fmt.Errorf("could not import legacy SMTP trusted_cert (see the new certificates_directory option) certificate %s (file does not exist)", config.Notifier.SMTP.TrustedCert))
}
}
return certPool, errors, nonFatalErrors return certPool, errors, nonFatalErrors
} }

View File

@ -77,7 +77,7 @@ func TestShouldReturnZeroAndErrorOnInvalidTLSVersions(t *testing.T) {
} }
func TestShouldReturnErrWhenX509DirectoryNotExist(t *testing.T) { func TestShouldReturnErrWhenX509DirectoryNotExist(t *testing.T) {
pool, errs, nonFatalErrs := NewX509CertPool("/tmp/asdfzyxabc123/not/a/real/dir", nil) pool, errs, nonFatalErrs := NewX509CertPool("/tmp/asdfzyxabc123/not/a/real/dir")
assert.NotNil(t, pool) assert.NotNil(t, pool)
if runtime.GOOS == windows { if runtime.GOOS == windows {
@ -97,7 +97,7 @@ func TestShouldReturnErrWhenX509DirectoryNotExist(t *testing.T) {
} }
func TestShouldNotReturnErrWhenX509DirectoryExist(t *testing.T) { func TestShouldNotReturnErrWhenX509DirectoryExist(t *testing.T) {
pool, errs, nonFatalErrs := NewX509CertPool("/tmp", nil) pool, errs, nonFatalErrs := NewX509CertPool("/tmp")
assert.NotNil(t, pool) assert.NotNil(t, pool)
if runtime.GOOS == windows { if runtime.GOOS == windows {
@ -110,64 +110,8 @@ func TestShouldNotReturnErrWhenX509DirectoryExist(t *testing.T) {
assert.Len(t, errs, 0) assert.Len(t, errs, 0)
} }
func TestShouldRaiseNonFatalErrWhenNotifierTrustedCertConfigured(t *testing.T) {
config := &schema.Configuration{
Notifier: &schema.NotifierConfiguration{
SMTP: &schema.SMTPNotifierConfiguration{
TrustedCert: "../suites/common/ssl/cert.pem",
},
},
}
pool, errs, nonFatalErrs := NewX509CertPool("/tmp", config)
assert.NotNil(t, pool)
index := 0
if runtime.GOOS == windows {
require.Len(t, nonFatalErrs, 2)
assert.EqualError(t, nonFatalErrs[0], "could not load system certificate pool which may result in untrusted certificate issues: crypto/x509: system root pool is not available on Windows")
index = 1
} else {
require.Len(t, nonFatalErrs, 1)
}
assert.Len(t, errs, 0)
assert.EqualError(t, nonFatalErrs[index], "defining the trusted cert in the SMTP notifier is deprecated and will be removed in 4.28.0, please use the global certificates_directory instead")
}
func TestShouldRaiseErrAndNonFatalErrWhenNotifierTrustedCertConfiguredAndNotExist(t *testing.T) {
config := &schema.Configuration{
Notifier: &schema.NotifierConfiguration{
SMTP: &schema.SMTPNotifierConfiguration{
TrustedCert: "/tmp/asdfzyxabc123/not/a/real/cert.pem",
},
},
}
pool, errs, nonFatalErrs := NewX509CertPool("/tmp", config)
assert.NotNil(t, pool)
index := 0
if runtime.GOOS == windows {
require.Len(t, nonFatalErrs, 2)
assert.EqualError(t, nonFatalErrs[0], "could not load system certificate pool which may result in untrusted certificate issues: crypto/x509: system root pool is not available on Windows")
index = 1
} else {
require.Len(t, nonFatalErrs, 1)
}
require.Len(t, errs, 1)
assert.EqualError(t, errs[0], "could not import legacy SMTP trusted_cert (see the new certificates_directory option) certificate /tmp/asdfzyxabc123/not/a/real/cert.pem (file does not exist)")
assert.EqualError(t, nonFatalErrs[index], "defining the trusted cert in the SMTP notifier is deprecated and will be removed in 4.28.0, please use the global certificates_directory instead")
}
func TestShouldReadCertsFromDirectoryButNotKeys(t *testing.T) { func TestShouldReadCertsFromDirectoryButNotKeys(t *testing.T) {
pool, errs, nonFatalErrs := NewX509CertPool("../suites/common/ssl/", nil) pool, errs, nonFatalErrs := NewX509CertPool("../suites/common/ssl/")
assert.NotNil(t, pool) assert.NotNil(t, pool)
require.Len(t, errs, 1) require.Len(t, errs, 1)