diff options
Diffstat (limited to 'internal/authentication/ldap_user_provider.go')
| -rw-r--r-- | internal/authentication/ldap_user_provider.go | 85 |
1 files changed, 52 insertions, 33 deletions
diff --git a/internal/authentication/ldap_user_provider.go b/internal/authentication/ldap_user_provider.go index 190ed4976..eb6a76096 100644 --- a/internal/authentication/ldap_user_provider.go +++ b/internal/authentication/ldap_user_provider.go @@ -11,47 +11,76 @@ import ( "github.com/authelia/authelia/internal/configuration/schema" "github.com/authelia/authelia/internal/logging" + "github.com/authelia/authelia/internal/utils" ) // LDAPUserProvider is a provider using a LDAP or AD as a user database. type LDAPUserProvider struct { configuration schema.LDAPAuthenticationBackendConfiguration + tlsConfig *tls.Config connectionFactory LDAPConnectionFactory } // NewLDAPUserProvider creates a new instance of LDAPUserProvider. func NewLDAPUserProvider(configuration schema.LDAPAuthenticationBackendConfiguration) *LDAPUserProvider { + minimumTLSVersion, _ := utils.TLSStringToTLSConfigVersion(configuration.MinimumTLSVersion) + + // TODO: RELEASE-4.27.0 Deprecated Completely in this release. + logger := logging.Logger() + + if strings.Contains(configuration.UsersFilter, "{0}") { + logger.Warnf("DEPRECATION NOTICE: LDAP Users Filter will no longer support replacing `{0}` in 4.27.0. Please use `{input}` instead.") + + configuration.UsersFilter = strings.ReplaceAll(configuration.UsersFilter, "{0}", "{input}") + } + + if strings.Contains(configuration.GroupsFilter, "{0}") { + logger.Warnf("DEPRECATION NOTICE: LDAP Groups Filter will no longer support replacing `{0}` in 4.27.0. Please use `{input}` instead.") + + configuration.GroupsFilter = strings.ReplaceAll(configuration.GroupsFilter, "{0}", "{input}") + } + + if strings.Contains(configuration.GroupsFilter, "{1}") { + logger.Warnf("DEPRECATION NOTICE: LDAP Groups Filter will no longer support replacing `{1}` in 4.27.0. Please use `{username}` instead.") + + configuration.GroupsFilter = strings.ReplaceAll(configuration.GroupsFilter, "{1}", "{username}") + } + // TODO: RELEASE-4.27.0 Deprecated Completely in this release. + + configuration.UsersFilter = strings.ReplaceAll(configuration.UsersFilter, "{username_attribute}", configuration.UsernameAttribute) + configuration.UsersFilter = strings.ReplaceAll(configuration.UsersFilter, "{mail_attribute}", configuration.MailAttribute) + configuration.UsersFilter = strings.ReplaceAll(configuration.UsersFilter, "{display_name_attribute}", configuration.DisplayNameAttribute) + return &LDAPUserProvider{ - configuration: configuration, + configuration: configuration, + tlsConfig: &tls.Config{InsecureSkipVerify: configuration.SkipVerify, MinVersion: minimumTLSVersion}, //nolint:gosec // Disabling InsecureSkipVerify is an informed choice by users. + connectionFactory: NewLDAPConnectionFactoryImpl(), } } // NewLDAPUserProviderWithFactory creates a new instance of LDAPUserProvider with existing factory. -func NewLDAPUserProviderWithFactory(configuration schema.LDAPAuthenticationBackendConfiguration, - connectionFactory LDAPConnectionFactory) *LDAPUserProvider { - return &LDAPUserProvider{ - configuration: configuration, - connectionFactory: connectionFactory, - } +func NewLDAPUserProviderWithFactory(configuration schema.LDAPAuthenticationBackendConfiguration, connectionFactory LDAPConnectionFactory) *LDAPUserProvider { + provider := NewLDAPUserProvider(configuration) + provider.connectionFactory = connectionFactory + + return provider } func (p *LDAPUserProvider) connect(userDN string, password string) (LDAPConnection, error) { var newConnection LDAPConnection - url, err := url.Parse(p.configuration.URL) + ldapURL, err := url.Parse(p.configuration.URL) if err != nil { - return nil, fmt.Errorf("Unable to parse URL to LDAP: %s", url) + return nil, fmt.Errorf("Unable to parse URL to LDAP: %s", ldapURL) } - if url.Scheme == "ldaps" { + if ldapURL.Scheme == "ldaps" { logging.Logger().Trace("LDAP client starts a TLS session") - conn, err := p.connectionFactory.DialTLS("tcp", url.Host, &tls.Config{ - InsecureSkipVerify: p.configuration.SkipVerify, //nolint:gosec // This is a configurable option, is desirable in some situations and is off by default. - }) + conn, err := p.connectionFactory.DialTLS("tcp", ldapURL.Host, p.tlsConfig) if err != nil { return nil, err } @@ -59,13 +88,19 @@ func (p *LDAPUserProvider) connect(userDN string, password string) (LDAPConnecti newConnection = conn } else { logging.Logger().Trace("LDAP client starts a session over raw TCP") - conn, err := p.connectionFactory.Dial("tcp", url.Host) + conn, err := p.connectionFactory.Dial("tcp", ldapURL.Host) if err != nil { return nil, err } newConnection = conn } + if p.configuration.StartTLS { + if err := newConnection.StartTLS(p.tlsConfig); err != nil { + return nil, err + } + } + if err := newConnection.Bind(userDN, password); err != nil { return nil, err } @@ -95,10 +130,6 @@ func (p *LDAPUserProvider) CheckUserPassword(inputUsername string, password stri return true, nil } -// OWASP recommends to escape some special characters. -// https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/LDAP_Injection_Prevention_Cheat_Sheet.md -const specialLDAPRunes = ",#+<>;\"=" - func (p *LDAPUserProvider) ldapEscape(inputUsername string) string { inputUsername = ldap.EscapeFilter(inputUsername) for _, c := range specialLDAPRunes { @@ -118,18 +149,9 @@ type ldapUserProfile struct { func (p *LDAPUserProvider) resolveUsersFilter(userFilter string, inputUsername string) string { inputUsername = p.ldapEscape(inputUsername) - // We temporarily keep placeholder {0} for backward compatibility. - userFilter = strings.ReplaceAll(userFilter, "{0}", inputUsername) - - // The {username} placeholder is equivalent to {0}, it's the new way, a named placeholder. + // The {input} placeholder is replaced by the users username input. userFilter = strings.ReplaceAll(userFilter, "{input}", inputUsername) - // {username_attribute} and {mail_attribute} are replaced by the content of the attribute defined - // in configuration. - userFilter = strings.ReplaceAll(userFilter, "{username_attribute}", p.configuration.UsernameAttribute) - userFilter = strings.ReplaceAll(userFilter, "{mail_attribute}", p.configuration.MailAttribute) - userFilter = strings.ReplaceAll(userFilter, "{display_name_attribute}", p.configuration.DisplayNameAttribute) - return userFilter } @@ -199,13 +221,10 @@ func (p *LDAPUserProvider) getUserProfile(conn LDAPConnection, inputUsername str func (p *LDAPUserProvider) resolveGroupsFilter(inputUsername string, profile *ldapUserProfile) (string, error) { //nolint:unparam inputUsername = p.ldapEscape(inputUsername) - // We temporarily keep placeholder {0} for backward compatibility. - groupFilter := strings.ReplaceAll(p.configuration.GroupsFilter, "{0}", inputUsername) - groupFilter = strings.ReplaceAll(groupFilter, "{input}", inputUsername) + // The {input} placeholder is replaced by the users username input. + groupFilter := strings.ReplaceAll(p.configuration.GroupsFilter, "{input}", inputUsername) if profile != nil { - // We temporarily keep placeholder {1} for backward compatibility. - groupFilter = strings.ReplaceAll(groupFilter, "{1}", ldap.EscapeFilter(profile.Username)) groupFilter = strings.ReplaceAll(groupFilter, "{username}", ldap.EscapeFilter(profile.Username)) groupFilter = strings.ReplaceAll(groupFilter, "{dn}", ldap.EscapeFilter(profile.DN)) } |
