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.
This commit is contained in:
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)
}
autheliaCertPool, errs, nonFatalErrs := utils.NewX509CertPool(config.CertificatesDirectory, config)
autheliaCertPool, errs, nonFatalErrs := utils.NewX509CertPool(config.CertificatesDirectory)
if len(errs) > 0 {
for _, err := range errs {
logger.Error(err)
@ -89,8 +89,8 @@ func startServer() {
switch {
case config.AuthenticationBackend.File != nil:
userProvider = authentication.NewFileUserProvider(config.AuthenticationBackend.File)
case config.AuthenticationBackend.Ldap != nil:
userProvider = authentication.NewLDAPUserProvider(*config.AuthenticationBackend.Ldap, autheliaCertPool)
case config.AuthenticationBackend.LDAP != nil:
userProvider = authentication.NewLDAPUserProvider(*config.AuthenticationBackend.LDAP, autheliaCertPool)
default:
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.
## - {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`.
## - 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:
## - 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.
## - {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`.
## - 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:
## (&(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
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

View File

@ -62,27 +62,6 @@ func NewLDAPUserProviderWithFactory(configuration schema.LDAPAuthenticationBacke
}
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, "{mail_attribute}", p.configuration.MailAttribute)
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",
MailAttribute: "mail",
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))",
GroupsFilter: "(&(|(member={dn})(member={0})(member={1}))(objectClass=group))",
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={input})(member={username}))(objectClass=group))",
AdditionalUsersDN: "ou=users",
AdditionalGroupsDN: "ou=groups",
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.
## - {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`.
## - 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:
## - 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.
## - {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`.
## - 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:
## (&(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, "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, "redis_secret_from_env", config.Session.Redis.Password)
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()
})
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) {

View File

@ -17,8 +17,6 @@ type LDAPAuthenticationBackendConfiguration struct {
Password string `mapstructure:"password"`
StartTLS bool `mapstructure:"start_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.
@ -41,7 +39,7 @@ type PasswordConfiguration struct {
type AuthenticationBackendConfiguration struct {
DisableResetPassword bool `mapstructure:"disable_reset_password"`
RefreshInterval string `mapstructure:"refresh_interval"`
Ldap *LDAPAuthenticationBackendConfiguration `mapstructure:"ldap"`
LDAP *LDAPAuthenticationBackendConfiguration `mapstructure:"ldap"`
File *FileAuthenticationBackendConfiguration `mapstructure:"file"`
}

View File

@ -18,8 +18,6 @@ type SMTPNotifierConfiguration struct {
DisableRequireTLS bool `mapstructure:"disable_require_tls"`
DisableHTMLEmails bool `mapstructure:"disable_html_emails"`
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.

View File

@ -10,6 +10,32 @@ import (
"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.
func validateFileAuthenticationBackend(configuration *schema.FileAuthenticationBackendConfiguration, validator *schema.StructValidator) {
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.
func validateLdapURLSimple(ldapURL string, validator *schema.StructValidator) (finalURL string) {
finalURL, _ = validateLdapURL(ldapURL, validator)
func validateLDAPURLSimple(ldapURL string, validator *schema.StructValidator) (finalURL string) {
finalURL, _ = validateLDAPURL(ldapURL, validator)
return finalURL
}
func validateLdapURL(ldapURL string, validator *schema.StructValidator) (finalURL string, hostname string) {
func validateLDAPURL(ldapURL string, validator *schema.StructValidator) (finalURL string, hostname string) {
parsedURL, err := url.Parse(ldapURL)
if err != nil {
@ -95,64 +173,7 @@ func validateLdapURL(ldapURL string, validator *schema.StructValidator) (finalUR
return parsedURL.String(), parsedURL.Hostname()
}
//nolint:gocyclo // TODO: Consider refactoring/simplifying, time permitting.
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
}
}
func validateLDAPRequiredParameters(configuration *schema.LDAPAuthenticationBackendConfiguration, validator *schema.StructValidator) {
// 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 == "" {
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 == "" {
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 == "" {
configuration.UsernameAttribute = schema.DefaultLDAPAuthenticationBackendConfiguration.UsernameAttribute
}
@ -236,29 +257,3 @@ func setDefaultImplementationCustomLdapAuthenticationBackend(configuration *sche
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()
backendConfig := schema.AuthenticationBackendConfiguration{}
backendConfig.Ldap = &schema.LDAPAuthenticationBackendConfiguration{}
backendConfig.LDAP = &schema.LDAPAuthenticationBackendConfiguration{}
backendConfig.File = &schema.FileAuthenticationBackendConfiguration{
Path: "/tmp",
}
@ -202,47 +202,47 @@ func TestFileBasedAuthenticationBackend(t *testing.T) {
suite.Run(t, new(FileBasedAuthenticationBackend))
}
type LdapAuthenticationBackendSuite struct {
type LDAPAuthenticationBackendSuite struct {
suite.Suite
configuration schema.AuthenticationBackendConfiguration
validator *schema.StructValidator
}
func (suite *LdapAuthenticationBackendSuite) SetupTest() {
func (suite *LDAPAuthenticationBackendSuite) SetupTest() {
suite.validator = schema.NewStructValidator()
suite.configuration = schema.AuthenticationBackendConfiguration{}
suite.configuration.Ldap = &schema.LDAPAuthenticationBackendConfiguration{}
suite.configuration.Ldap.Implementation = schema.LDAPImplementationCustom
suite.configuration.Ldap.URL = testLDAPURL
suite.configuration.Ldap.User = testLDAPUser
suite.configuration.Ldap.Password = testLDAPPassword
suite.configuration.Ldap.BaseDN = testLDAPBaseDN
suite.configuration.Ldap.UsernameAttribute = "uid"
suite.configuration.Ldap.UsersFilter = "({username_attribute}={input})"
suite.configuration.Ldap.GroupsFilter = "(cn={input})"
suite.configuration.LDAP = &schema.LDAPAuthenticationBackendConfiguration{}
suite.configuration.LDAP.Implementation = schema.LDAPImplementationCustom
suite.configuration.LDAP.URL = testLDAPURL
suite.configuration.LDAP.User = testLDAPUser
suite.configuration.LDAP.Password = testLDAPPassword
suite.configuration.LDAP.BaseDN = testLDAPBaseDN
suite.configuration.LDAP.UsernameAttribute = "uid"
suite.configuration.LDAP.UsersFilter = "({username_attribute}={input})"
suite.configuration.LDAP.GroupsFilter = "(cn={input})"
}
func (suite *LdapAuthenticationBackendSuite) TestShouldValidateCompleteConfiguration() {
func (suite *LDAPAuthenticationBackendSuite) TestShouldValidateCompleteConfiguration() {
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
suite.Assert().False(suite.validator.HasWarnings())
suite.Assert().False(suite.validator.HasErrors())
}
func (suite *LdapAuthenticationBackendSuite) TestShouldValidateDefaultImplementationAndUsernameAttribute() {
suite.configuration.Ldap.Implementation = ""
suite.configuration.Ldap.UsernameAttribute = ""
func (suite *LDAPAuthenticationBackendSuite) TestShouldValidateDefaultImplementationAndUsernameAttribute() {
suite.configuration.LDAP.Implementation = ""
suite.configuration.LDAP.UsernameAttribute = ""
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.HasErrors())
}
func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseErrorWhenImplementationIsInvalidMSAD() {
suite.configuration.Ldap.Implementation = "masd"
func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorWhenImplementationIsInvalidMSAD() {
suite.configuration.LDAP.Implementation = "masd"
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`")
}
func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseErrorWhenURLNotProvided() {
suite.configuration.Ldap.URL = ""
func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorWhenURLNotProvided() {
suite.configuration.LDAP.URL = ""
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
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")
}
func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseErrorWhenUserNotProvided() {
suite.configuration.Ldap.User = ""
func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorWhenUserNotProvided() {
suite.configuration.LDAP.User = ""
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")
}
func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseErrorWhenPasswordNotProvided() {
suite.configuration.Ldap.Password = ""
func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorWhenPasswordNotProvided() {
suite.configuration.LDAP.Password = ""
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")
}
func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseErrorWhenBaseDNNotProvided() {
suite.configuration.Ldap.BaseDN = ""
func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorWhenBaseDNNotProvided() {
suite.configuration.LDAP.BaseDN = ""
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")
}
func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseOnEmptyGroupsFilter() {
suite.configuration.Ldap.GroupsFilter = ""
func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseOnEmptyGroupsFilter() {
suite.configuration.LDAP.GroupsFilter = ""
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")
}
func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseOnEmptyUsersFilter() {
suite.configuration.Ldap.UsersFilter = ""
func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseOnEmptyUsersFilter() {
suite.configuration.LDAP.UsersFilter = ""
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")
}
func (suite *LdapAuthenticationBackendSuite) TestShouldNotRaiseOnEmptyUsernameAttribute() {
suite.configuration.Ldap.UsernameAttribute = ""
func (suite *LDAPAuthenticationBackendSuite) TestShouldNotRaiseOnEmptyUsernameAttribute() {
suite.configuration.LDAP.UsernameAttribute = ""
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
@ -326,7 +326,7 @@ func (suite *LdapAuthenticationBackendSuite) TestShouldNotRaiseOnEmptyUsernameAt
suite.Assert().False(suite.validator.HasErrors())
}
func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseOnBadRefreshInterval() {
func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseOnBadRefreshInterval() {
suite.configuration.RefreshInterval = "blah"
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")
}
func (suite *LdapAuthenticationBackendSuite) TestShouldSetDefaultImplementation() {
func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultImplementation() {
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
suite.Assert().False(suite.validator.HasWarnings())
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)
suite.Assert().False(suite.validator.HasWarnings())
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)
suite.Assert().False(suite.validator.HasWarnings())
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)
suite.Assert().False(suite.validator.HasWarnings())
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)
suite.Assert().False(suite.validator.HasWarnings())
@ -382,8 +399,8 @@ func (suite *LdapAuthenticationBackendSuite) TestShouldSetDefaultRefreshInterval
suite.Assert().Equal("5m", suite.configuration.RefreshInterval)
}
func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseWhenUsersFilterDoesNotContainEnclosingParenthesis() {
suite.configuration.Ldap.UsersFilter = "{username_attribute}={input}"
func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseWhenUsersFilterDoesNotContainEnclosingParenthesis() {
suite.configuration.LDAP.UsersFilter = "{username_attribute}={input}"
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})")
}
func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseWhenGroupsFilterDoesNotContainEnclosingParenthesis() {
suite.configuration.Ldap.GroupsFilter = "cn={input}"
func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseWhenGroupsFilterDoesNotContainEnclosingParenthesis() {
suite.configuration.LDAP.GroupsFilter = "cn={input}"
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})")
}
func (suite *LdapAuthenticationBackendSuite) TestShouldRaiseWhenUsersFilterDoesNotContainUsernameAttribute() {
suite.configuration.Ldap.UsersFilter = "(&({mail_attribute}={input})(objectClass=person))"
func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseWhenUsersFilterDoesNotContainUsernameAttribute() {
suite.configuration.LDAP.UsersFilter = "(&({mail_attribute}={input})(objectClass=person))"
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
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")
}
func (suite *LdapAuthenticationBackendSuite) TestShouldHelpDetectNoInputPlaceholder() {
suite.configuration.Ldap.UsersFilter = "(&({username_attribute}={mail_attribute})(objectClass=person))"
func (suite *LDAPAuthenticationBackendSuite) TestShouldHelpDetectNoInputPlaceholder() {
suite.configuration.LDAP.UsersFilter = "(&({username_attribute}={mail_attribute})(objectClass=person))"
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")
}
func (suite *LdapAuthenticationBackendSuite) TestShouldAdaptLDAPURL() {
suite.Assert().Equal("", validateLdapURLSimple("127.0.0.1", suite.validator))
func (suite *LDAPAuthenticationBackendSuite) TestShouldAdaptLDAPURL() {
suite.Assert().Equal("", validateLDAPURLSimple("127.0.0.1", suite.validator))
suite.Assert().False(suite.validator.HasWarnings())
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().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.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().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/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", 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/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("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: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))
}
func (suite *LdapAuthenticationBackendSuite) TestShouldDefaultTLS12() {
func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultTLSMinimumVersion() {
suite.configuration.LDAP.TLS = &schema.TLSConfig{MinimumVersion: ""}
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
suite.Assert().False(suite.validator.HasWarnings())
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() {
suite.configuration.Ldap.TLS = &schema.TLSConfig{
func (suite *LDAPAuthenticationBackendSuite) TestShouldNotAllowInvalidTLSValue() {
suite.configuration.LDAP.TLS = &schema.TLSConfig{
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")
}
// 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) {
suite.Run(t, new(LdapAuthenticationBackendSuite))
suite.Run(t, new(LDAPAuthenticationBackendSuite))
}
type ActiveDirectoryAuthenticationBackendSuite struct {
@ -534,13 +502,13 @@ type ActiveDirectoryAuthenticationBackendSuite struct {
func (suite *ActiveDirectoryAuthenticationBackendSuite) SetupTest() {
suite.validator = schema.NewStructValidator()
suite.configuration = schema.AuthenticationBackendConfiguration{}
suite.configuration.Ldap = &schema.LDAPAuthenticationBackendConfiguration{}
suite.configuration.Ldap.Implementation = schema.LDAPImplementationActiveDirectory
suite.configuration.Ldap.URL = testLDAPURL
suite.configuration.Ldap.User = testLDAPUser
suite.configuration.Ldap.Password = testLDAPPassword
suite.configuration.Ldap.BaseDN = testLDAPBaseDN
suite.configuration.Ldap.TLS = schema.DefaultLDAPAuthenticationBackendConfiguration.TLS
suite.configuration.LDAP = &schema.LDAPAuthenticationBackendConfiguration{}
suite.configuration.LDAP.Implementation = schema.LDAPImplementationActiveDirectory
suite.configuration.LDAP.URL = testLDAPURL
suite.configuration.LDAP.User = testLDAPUser
suite.configuration.LDAP.Password = testLDAPPassword
suite.configuration.LDAP.BaseDN = testLDAPBaseDN
suite.configuration.LDAP.TLS = schema.DefaultLDAPAuthenticationBackendConfiguration.TLS
}
func (suite *ActiveDirectoryAuthenticationBackendSuite) TestShouldSetActiveDirectoryDefaults() {
@ -550,52 +518,52 @@ func (suite *ActiveDirectoryAuthenticationBackendSuite) TestShouldSetActiveDirec
suite.Assert().False(suite.validator.HasErrors())
suite.Assert().Equal(
suite.configuration.Ldap.UsersFilter,
suite.configuration.LDAP.UsersFilter,
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.UsersFilter)
suite.Assert().Equal(
suite.configuration.Ldap.UsernameAttribute,
suite.configuration.LDAP.UsernameAttribute,
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.UsernameAttribute)
suite.Assert().Equal(
suite.configuration.Ldap.DisplayNameAttribute,
suite.configuration.LDAP.DisplayNameAttribute,
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.DisplayNameAttribute)
suite.Assert().Equal(
suite.configuration.Ldap.MailAttribute,
suite.configuration.LDAP.MailAttribute,
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.MailAttribute)
suite.Assert().Equal(
suite.configuration.Ldap.GroupsFilter,
suite.configuration.LDAP.GroupsFilter,
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.GroupsFilter)
suite.Assert().Equal(
suite.configuration.Ldap.GroupNameAttribute,
suite.configuration.LDAP.GroupNameAttribute,
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.GroupNameAttribute)
}
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.UsernameAttribute = "cn"
suite.configuration.Ldap.MailAttribute = "userPrincipalName"
suite.configuration.Ldap.DisplayNameAttribute = "name"
suite.configuration.Ldap.GroupsFilter = "(&(member={dn})(objectClass=group)(objectCategory=group))"
suite.configuration.Ldap.GroupNameAttribute = "distinguishedName"
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.MailAttribute = "userPrincipalName"
suite.configuration.LDAP.DisplayNameAttribute = "name"
suite.configuration.LDAP.GroupsFilter = "(&(member={dn})(objectClass=group)(objectCategory=group))"
suite.configuration.LDAP.GroupNameAttribute = "distinguishedName"
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
suite.Assert().NotEqual(
suite.configuration.Ldap.UsersFilter,
suite.configuration.LDAP.UsersFilter,
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.UsersFilter)
suite.Assert().NotEqual(
suite.configuration.Ldap.UsernameAttribute,
suite.configuration.LDAP.UsernameAttribute,
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.UsernameAttribute)
suite.Assert().NotEqual(
suite.configuration.Ldap.DisplayNameAttribute,
suite.configuration.LDAP.DisplayNameAttribute,
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.DisplayNameAttribute)
suite.Assert().NotEqual(
suite.configuration.Ldap.MailAttribute,
suite.configuration.LDAP.MailAttribute,
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.MailAttribute)
suite.Assert().NotEqual(
suite.configuration.Ldap.GroupsFilter,
suite.configuration.LDAP.GroupsFilter,
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.GroupsFilter)
suite.Assert().NotEqual(
suite.configuration.Ldap.GroupNameAttribute,
suite.configuration.LDAP.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"
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"
errFmtReplacedConfigurationKey = "invalid configuration key '%s' was replaced by '%s'"
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"
@ -138,12 +139,10 @@ var validKeys = []string{
"notifier.smtp.subject",
"notifier.smtp.startup_check_address",
"notifier.smtp.disable_require_tls",
"notifier.smtp.trusted_cert", // TODO: Deprecated: Remove in 4.28.
"notifier.smtp.disable_html_emails",
"notifier.smtp.tls.minimum_version",
"notifier.smtp.tls.skip_verify",
"notifier.smtp.tls.server_name",
"notifier.smtp.disable_verify_cert", // TODO: Deprecated: Remove in 4.28.
// Regulation Keys.
"regulation.max_retries",
@ -175,8 +174,6 @@ var validKeys = []string{
"authentication_backend.ldap.tls.minimum_version",
"authentication_backend.ldap.tls.skip_verify",
"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.
"authentication_backend.file.path",
@ -188,10 +185,19 @@ var validKeys = []string{
"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{
"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",
"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.iterations": errFilePOptions,
"authentication_backend.file.password_options.key_length": errFilePOptions,

View File

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

View File

@ -1,6 +1,7 @@
package validator
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
@ -62,8 +63,8 @@ func TestAllSpecificErrorKeys(t *testing.T) {
func TestSpecificErrorKeys(t *testing.T) {
configKeys := []string{
"logs_level",
"logs_file_path",
"notifier.smtp.trusted_cert",
"google_analytics",
"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_hashing.algorithm",
@ -77,9 +78,49 @@ func TestSpecificErrorKeys(t *testing.T) {
require.Len(t, errs, 5)
assert.EqualError(t, errs[0], specificErrorKeys["logs_level"])
assert.EqualError(t, errs[1], specificErrorKeys["logs_file_path"])
assert.EqualError(t, errs[0], specificErrorKeys["notifier.smtp.trusted_cert"])
assert.EqualError(t, errs[1], specificErrorKeys["google_analytics"])
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[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
import (
"errors"
"fmt"
"github.com/authelia/authelia/internal/configuration/schema"
)
// 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) {
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`"))
return
}
if configuration.SMTP != nil && configuration.FileSystem != nil {
validator.Push(fmt.Errorf("Notifier should be either `smtp` or `filesystem`"))
return
}
@ -28,51 +23,39 @@ func ValidateNotifier(configuration *schema.NotifierConfiguration, validator *sc
return
}
if configuration.SMTP != nil {
if configuration.SMTP.StartupCheckAddress == "" {
configuration.SMTP.StartupCheckAddress = "test@authelia.com"
validateSMTPNotifier(configuration.SMTP, validator)
}
if configuration.SMTP.Host == "" {
func validateSMTPNotifier(configuration *schema.SMTPNotifierConfiguration, validator *schema.StructValidator) {
if configuration.StartupCheckAddress == "" {
configuration.StartupCheckAddress = "test@authelia.com"
}
if configuration.Host == "" {
validator.Push(fmt.Errorf("Host of SMTP notifier must be provided"))
}
if configuration.SMTP.Port == 0 {
if configuration.Port == 0 {
validator.Push(fmt.Errorf("Port of SMTP notifier must be provided"))
}
if configuration.SMTP.Sender == "" {
if configuration.Sender == "" {
validator.Push(fmt.Errorf("Sender of SMTP notifier must be provided"))
}
if configuration.SMTP.Subject == "" {
configuration.SMTP.Subject = schema.DefaultSMTPNotifierConfiguration.Subject
if configuration.Subject == "" {
configuration.Subject = schema.DefaultSMTPNotifierConfiguration.Subject
}
if configuration.SMTP.Identifier == "" {
configuration.SMTP.Identifier = schema.DefaultSMTPNotifierConfiguration.Identifier
if configuration.Identifier == "" {
configuration.Identifier = schema.DefaultSMTPNotifierConfiguration.Identifier
}
if configuration.SMTP.TLS == nil {
configuration.SMTP.TLS = schema.DefaultSMTPNotifierConfiguration.TLS
// Deprecated. Maps deprecated values to the new ones. TODO: Remove in 4.28.
if configuration.SMTP.DisableVerifyCert != nil {
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 == nil {
configuration.TLS = schema.DefaultSMTPNotifierConfiguration.TLS
}
// 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
if configuration.TLS.ServerName == "" {
configuration.TLS.ServerName = configuration.Host
}
}

View File

@ -127,29 +127,6 @@ func (suite *NotifierSuite) TestShouldEnsureSenderOfSMTPNotifierAreProvided() {
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) {
suite.Run(t, new(NotifierSuite))
}

View File

@ -43,8 +43,8 @@ func ValidateSecrets(configuration *schema.Configuration, validator *schema.Stru
}
}
if configuration.AuthenticationBackend.Ldap != nil {
configuration.AuthenticationBackend.Ldap.Password = getSecretValue(SecretNames["LDAPPassword"], validator, viper)
if configuration.AuthenticationBackend.LDAP != nil {
configuration.AuthenticationBackend.LDAP.Password = getSecretValue(SecretNames["LDAPPassword"], validator, viper)
}
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) {
if cfg.Ldap != nil {
if cfg.LDAP != nil {
if cfg.RefreshInterval == schema.ProfileRefreshDisabled {
refresh = false
refreshInterval = 0

View File

@ -23,7 +23,7 @@ import (
var verifyGetCfg = schema.AuthenticationBackendConfiguration{
RefreshInterval: schema.RefreshIntervalDefault,
Ldap: &schema.LDAPAuthenticationBackendConfiguration{},
LDAP: &schema.LDAPAuthenticationBackendConfiguration{},
}
// 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.
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()
if err != nil {
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)
// 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
}

View File

@ -77,7 +77,7 @@ func TestShouldReturnZeroAndErrorOnInvalidTLSVersions(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)
if runtime.GOOS == windows {
@ -97,7 +97,7 @@ func TestShouldReturnErrWhenX509DirectoryNotExist(t *testing.T) {
}
func TestShouldNotReturnErrWhenX509DirectoryExist(t *testing.T) {
pool, errs, nonFatalErrs := NewX509CertPool("/tmp", nil)
pool, errs, nonFatalErrs := NewX509CertPool("/tmp")
assert.NotNil(t, pool)
if runtime.GOOS == windows {
@ -110,64 +110,8 @@ func TestShouldNotReturnErrWhenX509DirectoryExist(t *testing.T) {
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) {
pool, errs, nonFatalErrs := NewX509CertPool("../suites/common/ssl/", nil)
pool, errs, nonFatalErrs := NewX509CertPool("../suites/common/ssl/")
assert.NotNil(t, pool)
require.Len(t, errs, 1)