From a3b14871baeca9ebfbaded981bebb6f9c36b0311 Mon Sep 17 00:00:00 2001 From: James Elliott Date: Thu, 5 Aug 2021 14:17:07 +1000 Subject: [PATCH] perf(authentication): improve ldap dynamic replacement performance (#2239) This change means we only check the filters for the existence of placeholders that cannot be replaced at startup. We then utilized cached results of that lookup for subsequent replacements. --- config.template.yml | 21 +-- docs/configuration/authentication/ldap.md | 34 +++- internal/authentication/const.go | 6 + internal/authentication/ldap_user_provider.go | 154 ++++++------------ .../ldap_user_provider_startup.go | 103 ++++++++++++ .../authentication/ldap_user_provider_test.go | 60 ++++--- internal/configuration/config.template.yml | 21 +-- .../configuration/schema/authentication.go | 2 +- .../configuration/test_resources/config.yml | 2 +- .../test_resources/config_alt.yml | 2 +- .../test_resources/config_bad_keys.yml | 2 +- .../test_resources/config_with_secret.yml | 2 +- .../validator/authentication_test.go | 2 +- .../suites/HighAvailability/configuration.yml | 2 +- internal/suites/LDAP/configuration.yml | 2 +- .../example/compose/ldap/ldif/03-base.ldif | 54 +++--- .../kube/authelia/configs/configuration.yml | 2 +- internal/suites/example/kube/ldap/base.ldif | 46 +++--- internal/utils/const.go | 12 +- 19 files changed, 312 insertions(+), 217 deletions(-) create mode 100644 internal/authentication/ldap_user_provider_startup.go diff --git a/config.template.yml b/config.template.yml index 356e909dd..933a03eac 100644 --- a/config.template.yml +++ b/config.template.yml @@ -183,10 +183,8 @@ authentication_backend: additional_users_dn: ou=users ## The users filter used in search queries to find the user profile based on input filled in login form. - ## Various placeholders are available in the user filter: - ## - {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`. + ## Various placeholders are available in the user filter which you can read about in the documentation which can + ## be found at: https://www.authelia.com/docs/configuration/authentication/ldap.html#users-filter-replacements ## ## Recommended settings are as follows: ## - Microsoft Active Directory: (&({username_attribute}={input})(objectCategory=person)(objectClass=user)) @@ -202,16 +200,13 @@ authentication_backend: ## i.e. with this set to OU=Groups and base_dn set to DC=a,DC=com; OU=Groups,DC=a,DC=com is searched for groups. additional_groups_dn: ou=groups - ## The groups filter used in search queries to find the groups of the user. - ## - {input} is a placeholder replaced by what the user inputs in the login form. - ## - {username} is a placeholder replace by the username stored in LDAP (based on `username_attribute`). - ## - {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`. + ## The groups filter used in search queries to find the groups based on relevant authenticated user. + ## Various placeholders are available in the groups filter which you can read about in the documentation which can + ## be found at: https://www.authelia.com/docs/configuration/authentication/ldap.html#groups-filter-replacements ## ## If your groups use the `groupOfUniqueNames` structure use this instead: - ## (&(uniquemember={dn})(objectclass=groupOfUniqueNames)) - groups_filter: (&(member={dn})(objectclass=groupOfNames)) + ## (&(uniqueMember={dn})(objectClass=groupOfUniqueNames)) + groups_filter: (&(member={dn})(objectClass=groupOfNames)) ## The attribute holding the name of the group. # group_name_attribute: cn @@ -221,7 +216,7 @@ authentication_backend: # mail_attribute: mail ## The attribute holding the display name of the user. This will be used to greet an authenticated user. - # display_name_attribute: displayname + # display_name_attribute: displayName ## The username and password of the admin user. user: cn=admin,dc=example,dc=com diff --git a/docs/configuration/authentication/ldap.md b/docs/configuration/authentication/ldap.md index e4ee8f966..fc0bc81bf 100644 --- a/docs/configuration/authentication/ldap.md +++ b/docs/configuration/authentication/ldap.md @@ -29,10 +29,10 @@ authentication_backend: additional_users_dn: ou=users users_filter: (&({username_attribute}={input})(objectClass=person)) additional_groups_dn: ou=groups - groups_filter: (&(member={dn})(objectclass=groupOfNames)) + groups_filter: (&(member={dn})(objectClass=groupOfNames)) group_name_attribute: cn mail_attribute: mail - display_name_attribute: displayname + display_name_attribute: displayName user: cn=admin,dc=example,dc=com password: password ``` @@ -182,6 +182,28 @@ must be used if you wish to allow users to change or reset their password as Act uses a custom attribute for this, and an input format other implementations do not use. The long term intention of this is to have logical defaults for various RFC implementations of LDAP. +### Filter replacements + +Various replacements occur in the user and groups filter. The replacements either occur at startup or upon an LDAP +search. + +#### Users filter replacements + +|Placeholder |Phase |Replacement | +|:----------------------:|:-----:|:--------------------------------------------------------------:| +|{username_attribute} |startup|The [username attribute](#username_attribute) configured | +|{mail_attribute} |startup|The [mail attribute](#mail_attribute) configured | +|{display_name_attribute}|startup|The [display name attribute](#display_name_attribute) configured| +|{input} |search |The input into the username field | + +#### Groups filter replacements + +|Placeholder |Phase |Replacement | +|:----------------------:|:-----:|:-------------------------------------------------------------------------:| +|{input} |search |The input into the username field | +|{username} |search |The username from the profile lookup obtained from the [username attribute]| +|{dn} |search |The distinguished name from the profile lookup | + ### Defaults The below tables describes the current attribute defaults for each implementation. @@ -192,8 +214,8 @@ described by the Username column. |Implementation |Username |Display Name|Mail|Group Name| |:-------------:|:------------:|:----------:|:--:|:--------:| -|custom |n/a |displayname |mail|cn | -|activedirectory|sAMAccountName|displayname |mail|cn | +|custom |n/a |displayName |mail|cn | +|activedirectory|sAMAccountName|displayName |mail|cn | #### Filter defaults @@ -245,3 +267,7 @@ As of versions > `4.24.0` the `users_filter` must include the `username_attribut result in Authelia throwing an error. In versions <= `4.24.0` not including the `username_attribute` placeholder will cause issues with the session refresh and will result in session resets when the refresh interval has expired, default of 5 minutes. + +[LDAP GeneralizedTime]: https://ldapwiki.com/wiki/GeneralizedTime +[username attribute]: #username_attribute +[TechNet wiki]: https://social.technet.microsoft.com/wiki/contents/articles/5392.active-directory-ldap-syntax-filters.aspx \ No newline at end of file diff --git a/internal/authentication/const.go b/internal/authentication/const.go index 371466ab7..21a1669fa 100644 --- a/internal/authentication/const.go +++ b/internal/authentication/const.go @@ -30,6 +30,12 @@ const ( ldapOIDPasswdModifyExtension = "1.3.6.1.4.1.4203.1.11.1" // http://oidref.com/1.3.6.1.4.1.4203.1.11.1 ) +const ( + ldapPlaceholderInput = "{input}" + ldapPlaceholderDistinguishedName = "{dn}" + ldapPlaceholderUsername = "{username}" +) + // PossibleMethods is the set of all possible 2FA methods. var PossibleMethods = []string{TOTP, U2F, Push} diff --git a/internal/authentication/ldap_user_provider.go b/internal/authentication/ldap_user_provider.go index 3c38926dd..15e51df56 100644 --- a/internal/authentication/ldap_user_provider.go +++ b/internal/authentication/ldap_user_provider.go @@ -15,17 +15,28 @@ import ( "github.com/authelia/authelia/internal/utils" ) -// LDAPUserProvider is a provider using a LDAP or AD as a user database. +// LDAPUserProvider is a UserProvider that connects to LDAP servers like ActiveDirectory, OpenLDAP, OpenDJ, FreeIPA, etc. type LDAPUserProvider struct { configuration schema.LDAPAuthenticationBackendConfiguration tlsConfig *tls.Config dialOpts ldap.DialOpt logger *logrus.Logger connectionFactory LDAPConnectionFactory - usersBaseDN string - groupsBaseDN string + // Automatically detected ldap features. supportExtensionPasswdModify bool + + // Dynamically generated users values. + usersBaseDN string + usersAttributes []string + usersFilterReplacementInput bool + + // Dynamically generated groups values. + groupsBaseDN string + groupsAttributes []string + groupsFilterReplacementInput bool + groupsFilterReplacementUsername bool + groupsFilterReplacementDN bool } // NewLDAPUserProvider creates a new instance of LDAPUserProvider. @@ -72,74 +83,12 @@ func newLDAPUserProvider(configuration schema.LDAPAuthenticationBackendConfigura connectionFactory: factory, } - provider.parseDynamicConfiguration() + provider.parseDynamicUsersConfiguration() + provider.parseDynamicGroupsConfiguration() return provider } -func (p *LDAPUserProvider) parseDynamicConfiguration() { - 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) - - p.logger.Tracef("Dynamically generated users filter is %s", p.configuration.UsersFilter) - - if p.configuration.AdditionalUsersDN != "" { - p.usersBaseDN = p.configuration.AdditionalUsersDN + "," + p.configuration.BaseDN - } else { - p.usersBaseDN = p.configuration.BaseDN - } - - p.logger.Tracef("Dynamically generated users BaseDN is %s", p.usersBaseDN) - - if p.configuration.AdditionalGroupsDN != "" { - p.groupsBaseDN = ldap.EscapeFilter(p.configuration.AdditionalGroupsDN + "," + p.configuration.BaseDN) - } else { - p.groupsBaseDN = p.configuration.BaseDN - } - - p.logger.Tracef("Dynamically generated groups BaseDN is %s", p.groupsBaseDN) -} - -func (p *LDAPUserProvider) checkServer() (err error) { - conn, err := p.connect(p.configuration.User, p.configuration.Password) - if err != nil { - return err - } - - defer conn.Close() - - searchRequest := ldap.NewSearchRequest("", ldap.ScopeBaseObject, ldap.NeverDerefAliases, - 1, 0, false, "(objectClass=*)", []string{ldapSupportedExtensionAttribute}, nil) - - sr, err := conn.Search(searchRequest) - if err != nil { - return err - } - - if len(sr.Entries) != 1 { - return nil - } - - // Iterate the attribute values to see what the server supports. - for _, attr := range sr.Entries[0].Attributes { - if attr.Name == ldapSupportedExtensionAttribute { - p.logger.Tracef("LDAP Supported Extension OIDs: %s", strings.Join(attr.Values, ", ")) - - for _, oid := range attr.Values { - if oid == ldapOIDPasswdModifyExtension { - p.supportExtensionPasswdModify = true - break - } - } - - break - } - } - - return nil -} - func (p *LDAPUserProvider) connect(userDN string, password string) (LDAPConnection, error) { conn, err := p.connectionFactory.DialURL(p.configuration.URL, p.dialOpts) if err != nil { @@ -197,34 +146,31 @@ type ldapUserProfile struct { Username string } -func (p *LDAPUserProvider) resolveUsersFilter(userFilter string, inputUsername string) string { - inputUsername = p.ldapEscape(inputUsername) +func (p *LDAPUserProvider) resolveUsersFilter(inputUsername string) (filter string) { + filter = p.configuration.UsersFilter - // The {input} placeholder is replaced by the users username input. - userFilter = strings.ReplaceAll(userFilter, "{input}", inputUsername) + if p.usersFilterReplacementInput { + // The {input} placeholder is replaced by the users username input. + filter = strings.ReplaceAll(filter, ldapPlaceholderInput, p.ldapEscape(inputUsername)) + } - p.logger.Tracef("Computed user filter is %s", userFilter) + p.logger.Tracef("Computed user filter is %s", filter) - return userFilter + return filter } func (p *LDAPUserProvider) getUserProfile(conn LDAPConnection, inputUsername string) (*ldapUserProfile, error) { - userFilter := p.resolveUsersFilter(p.configuration.UsersFilter, inputUsername) - - attributes := []string{"dn", - p.configuration.DisplayNameAttribute, - p.configuration.MailAttribute, - p.configuration.UsernameAttribute} + userFilter := p.resolveUsersFilter(inputUsername) // Search for the given username. searchRequest := ldap.NewSearchRequest( p.usersBaseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, - 1, 0, false, userFilter, attributes, nil, + 1, 0, false, userFilter, p.usersAttributes, nil, ) sr, err := conn.Search(searchRequest) if err != nil { - return nil, fmt.Errorf("Cannot find user DN of user %s. Cause: %s", inputUsername, err) + return nil, fmt.Errorf("cannot find user DN of user '%s'. Cause: %w", inputUsername, err) } if len(sr.Entries) == 0 { @@ -232,7 +178,7 @@ func (p *LDAPUserProvider) getUserProfile(conn LDAPConnection, inputUsername str } if len(sr.Entries) > 1 { - return nil, fmt.Errorf("Multiple users %s found", inputUsername) + return nil, fmt.Errorf("multiple users %s found", inputUsername) } userProfile := ldapUserProfile{ @@ -250,7 +196,7 @@ func (p *LDAPUserProvider) getUserProfile(conn LDAPConnection, inputUsername str if attr.Name == p.configuration.UsernameAttribute { if len(attr.Values) != 1 { - return nil, fmt.Errorf("User %s cannot have multiple value for attribute %s", + return nil, fmt.Errorf("user '%s' cannot have multiple value for attribute '%s'", inputUsername, p.configuration.UsernameAttribute) } @@ -259,26 +205,33 @@ func (p *LDAPUserProvider) getUserProfile(conn LDAPConnection, inputUsername str } if userProfile.DN == "" { - return nil, fmt.Errorf("No DN has been found for user %s", inputUsername) + return nil, fmt.Errorf("no DN has been found for user %s", inputUsername) } return &userProfile, nil } -func (p *LDAPUserProvider) resolveGroupsFilter(inputUsername string, profile *ldapUserProfile) (string, error) { //nolint:unparam - inputUsername = p.ldapEscape(inputUsername) +func (p *LDAPUserProvider) resolveGroupsFilter(inputUsername string, profile *ldapUserProfile) (filter string, err error) { //nolint:unparam + filter = p.configuration.GroupsFilter - // The {input} placeholder is replaced by the users username input. - groupFilter := strings.ReplaceAll(p.configuration.GroupsFilter, "{input}", inputUsername) - - if profile != nil { - groupFilter = strings.ReplaceAll(groupFilter, "{username}", ldap.EscapeFilter(profile.Username)) - groupFilter = strings.ReplaceAll(groupFilter, "{dn}", ldap.EscapeFilter(profile.DN)) + if p.groupsFilterReplacementInput { + // The {input} placeholder is replaced by the users username input. + filter = strings.ReplaceAll(p.configuration.GroupsFilter, ldapPlaceholderInput, p.ldapEscape(inputUsername)) } - p.logger.Tracef("Computed groups filter is %s", groupFilter) + if profile != nil { + if p.groupsFilterReplacementUsername { + filter = strings.ReplaceAll(filter, ldapPlaceholderUsername, ldap.EscapeFilter(profile.Username)) + } - return groupFilter, nil + if p.groupsFilterReplacementDN { + filter = strings.ReplaceAll(filter, ldapPlaceholderDistinguishedName, ldap.EscapeFilter(profile.DN)) + } + } + + p.logger.Tracef("Computed groups filter is %s", filter) + + return filter, nil } // GetDetails retrieve the groups a user belongs to. @@ -296,19 +249,19 @@ func (p *LDAPUserProvider) GetDetails(inputUsername string) (*UserDetails, error groupsFilter, err := p.resolveGroupsFilter(inputUsername, profile) if err != nil { - return nil, fmt.Errorf("Unable to create group filter for user %s. Cause: %s", inputUsername, err) + return nil, fmt.Errorf("unable to create group filter for user '%s'. Cause: %w", inputUsername, err) } // Search for the given username. searchGroupRequest := ldap.NewSearchRequest( p.groupsBaseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, - 0, 0, false, groupsFilter, []string{p.configuration.GroupNameAttribute}, nil, + 0, 0, false, groupsFilter, p.groupsAttributes, nil, ) sr, err := conn.Search(searchGroupRequest) if err != nil { - return nil, fmt.Errorf("Unable to retrieve groups of user %s. Cause: %s", inputUsername, err) + return nil, fmt.Errorf("unable to retrieve groups of user '%s'. Cause: %w", inputUsername, err) } groups := make([]string, 0) @@ -318,6 +271,7 @@ func (p *LDAPUserProvider) GetDetails(inputUsername string) (*UserDetails, error p.logger.Warningf("No groups retrieved from LDAP for user %s", inputUsername) break } + // Append all values of the document. Normally there should be only one per document. groups = append(groups, res.Attributes[0].Values...) } @@ -334,14 +288,14 @@ func (p *LDAPUserProvider) GetDetails(inputUsername string) (*UserDetails, error func (p *LDAPUserProvider) UpdatePassword(inputUsername string, newPassword string) error { conn, err := p.connect(p.configuration.User, p.configuration.Password) if err != nil { - return fmt.Errorf("Unable to update password. Cause: %s", err) + return fmt.Errorf("unable to update password. Cause: %w", err) } defer conn.Close() profile, err := p.getUserProfile(conn, inputUsername) if err != nil { - return fmt.Errorf("Unable to update password. Cause: %s", err) + return fmt.Errorf("unable to update password. Cause: %w", err) } switch { @@ -370,7 +324,7 @@ func (p *LDAPUserProvider) UpdatePassword(inputUsername string, newPassword stri } if err != nil { - return fmt.Errorf("Unable to update password. Cause: %s", err) + return fmt.Errorf("unable to update password. Cause: %w", err) } return nil diff --git a/internal/authentication/ldap_user_provider_startup.go b/internal/authentication/ldap_user_provider_startup.go new file mode 100644 index 000000000..f06985335 --- /dev/null +++ b/internal/authentication/ldap_user_provider_startup.go @@ -0,0 +1,103 @@ +package authentication + +import ( + "strings" + + "github.com/go-ldap/ldap/v3" +) + +func (p *LDAPUserProvider) checkServer() (err error) { + conn, err := p.connect(p.configuration.User, p.configuration.Password) + if err != nil { + return err + } + + defer conn.Close() + + searchRequest := ldap.NewSearchRequest("", ldap.ScopeBaseObject, ldap.NeverDerefAliases, + 1, 0, false, "(objectClass=*)", []string{ldapSupportedExtensionAttribute}, nil) + + sr, err := conn.Search(searchRequest) + if err != nil { + return err + } + + if len(sr.Entries) != 1 { + return nil + } + + // Iterate the attribute values to see what the server supports. + for _, attr := range sr.Entries[0].Attributes { + if attr.Name == ldapSupportedExtensionAttribute { + p.logger.Tracef("LDAP Supported Extension OIDs: %s", strings.Join(attr.Values, ", ")) + + for _, oid := range attr.Values { + if oid == ldapOIDPasswdModifyExtension { + p.supportExtensionPasswdModify = true + break + } + } + + break + } + } + + return nil +} + +func (p *LDAPUserProvider) parseDynamicUsersConfiguration() { + 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) + + p.logger.Tracef("Dynamically generated users filter is %s", p.configuration.UsersFilter) + + p.usersAttributes = []string{ + p.configuration.DisplayNameAttribute, + p.configuration.MailAttribute, + p.configuration.UsernameAttribute, + } + + if p.configuration.AdditionalUsersDN != "" { + p.usersBaseDN = p.configuration.AdditionalUsersDN + "," + p.configuration.BaseDN + } else { + p.usersBaseDN = p.configuration.BaseDN + } + + p.logger.Tracef("Dynamically generated users BaseDN is %s", p.usersBaseDN) + + if strings.Contains(p.configuration.UsersFilter, ldapPlaceholderInput) { + p.usersFilterReplacementInput = true + } + + p.logger.Tracef("Detected user filter replacements that need to be resolved per lookup are: %s=%v", + ldapPlaceholderInput, p.usersFilterReplacementInput) +} + +func (p *LDAPUserProvider) parseDynamicGroupsConfiguration() { + p.groupsAttributes = []string{ + p.configuration.GroupNameAttribute, + } + + if p.configuration.AdditionalGroupsDN != "" { + p.groupsBaseDN = ldap.EscapeFilter(p.configuration.AdditionalGroupsDN + "," + p.configuration.BaseDN) + } else { + p.groupsBaseDN = p.configuration.BaseDN + } + + p.logger.Tracef("Dynamically generated groups BaseDN is %s", p.groupsBaseDN) + + if strings.Contains(p.configuration.GroupsFilter, ldapPlaceholderInput) { + p.groupsFilterReplacementInput = true + } + + if strings.Contains(p.configuration.GroupsFilter, ldapPlaceholderUsername) { + p.groupsFilterReplacementUsername = true + } + + if strings.Contains(p.configuration.GroupsFilter, ldapPlaceholderDistinguishedName) { + p.groupsFilterReplacementDN = true + } + + p.logger.Tracef("Detected group filter replacements that need to be resolved per lookup are: input=%v, username=%v, dn=%v", p.groupsFilterReplacementInput, p.groupsFilterReplacementUsername, p.groupsFilterReplacementDN) +} diff --git a/internal/authentication/ldap_user_provider_test.go b/internal/authentication/ldap_user_provider_test.go index 9415bb72c..93d2829d0 100644 --- a/internal/authentication/ldap_user_provider_test.go +++ b/internal/authentication/ldap_user_provider_test.go @@ -174,7 +174,7 @@ func TestShouldCheckLDAPServerExtensions(t *testing.T) { UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", UsernameAttribute: "uid", MailAttribute: "mail", - DisplayNameAttribute: "displayname", + DisplayNameAttribute: "displayName", Password: "password", AdditionalUsersDN: "ou=users", BaseDN: "dc=example,dc=com", @@ -230,7 +230,7 @@ func TestShouldNotEnablePasswdModifyExtension(t *testing.T) { UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", UsernameAttribute: "uid", MailAttribute: "mail", - DisplayNameAttribute: "displayname", + DisplayNameAttribute: "displayName", Password: "password", AdditionalUsersDN: "ou=users", BaseDN: "dc=example,dc=com", @@ -286,7 +286,7 @@ func TestShouldReturnCheckServerConnectError(t *testing.T) { UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", UsernameAttribute: "uid", MailAttribute: "mail", - DisplayNameAttribute: "displayname", + DisplayNameAttribute: "displayName", Password: "password", AdditionalUsersDN: "ou=users", BaseDN: "dc=example,dc=com", @@ -318,7 +318,7 @@ func TestShouldReturnCheckServerSearchError(t *testing.T) { UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", UsernameAttribute: "uid", MailAttribute: "mail", - DisplayNameAttribute: "displayname", + DisplayNameAttribute: "displayName", Password: "password", AdditionalUsersDN: "ou=users", BaseDN: "dc=example,dc=com", @@ -379,7 +379,7 @@ func TestShouldEscapeUserInput(t *testing.T) { UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", UsernameAttribute: "uid", MailAttribute: "mail", - DisplayNameAttribute: "displayname", + DisplayNameAttribute: "displayName", Password: "password", AdditionalUsersDN: "ou=users", BaseDN: "dc=example,dc=com", @@ -414,11 +414,13 @@ func TestShouldCombineUsernameFilterAndUsersFilter(t *testing.T) { AdditionalUsersDN: "ou=users", BaseDN: "dc=example,dc=com", MailAttribute: "mail", - DisplayNameAttribute: "displayname", + DisplayNameAttribute: "displayName", }, nil, mockFactory) + assert.True(t, ldapClient.usersFilterReplacementInput) + mockConn.EXPECT(). Search(NewSearchRequestMatcher("(&(uid=john)(&(objectCategory=person)(objectClass=user)))")). Return(&ldap.SearchResult{}, nil) @@ -456,7 +458,7 @@ func TestShouldNotCrashWhenGroupsAreNotRetrievedFromLDAP(t *testing.T) { Password: "password", UsernameAttribute: "uid", MailAttribute: "mail", - DisplayNameAttribute: "displayname", + DisplayNameAttribute: "displayName", UsersFilter: "uid={input}", AdditionalUsersDN: "ou=users", BaseDN: "dc=example,dc=com", @@ -486,7 +488,7 @@ func TestShouldNotCrashWhenGroupsAreNotRetrievedFromLDAP(t *testing.T) { DN: "uid=test,dc=example,dc=com", Attributes: []*ldap.EntryAttribute{ { - Name: "displayname", + Name: "displayName", Values: []string{"John Doe"}, }, { @@ -587,7 +589,7 @@ func TestShouldReturnUsernameFromLDAP(t *testing.T) { Password: "password", UsernameAttribute: "uid", MailAttribute: "mail", - DisplayNameAttribute: "displayname", + DisplayNameAttribute: "displayName", UsersFilter: "uid={input}", AdditionalUsersDN: "ou=users", BaseDN: "dc=example,dc=com", @@ -617,7 +619,7 @@ func TestShouldReturnUsernameFromLDAP(t *testing.T) { DN: "uid=test,dc=example,dc=com", Attributes: []*ldap.EntryAttribute{ { - Name: "displayname", + Name: "displayName", Values: []string{"John Doe"}, }, { @@ -658,7 +660,7 @@ func TestShouldUpdateUserPasswordPasswdModifyExtension(t *testing.T) { Password: "password", UsernameAttribute: "uid", MailAttribute: "mail", - DisplayNameAttribute: "displayname", + DisplayNameAttribute: "displayName", UsersFilter: "uid={input}", AdditionalUsersDN: "ou=users", BaseDN: "dc=example,dc=com", @@ -716,7 +718,7 @@ func TestShouldUpdateUserPasswordPasswdModifyExtension(t *testing.T) { DN: "uid=test,dc=example,dc=com", Attributes: []*ldap.EntryAttribute{ { - Name: "displayname", + Name: "displayName", Values: []string{"John Doe"}, }, { @@ -822,7 +824,7 @@ func TestShouldUpdateUserPasswordActiveDirectory(t *testing.T) { DN: "cn=test,dc=example,dc=com", Attributes: []*ldap.EntryAttribute{ { - Name: "displayname", + Name: "displayName", Values: []string{"John Doe"}, }, { @@ -968,7 +970,7 @@ func TestShouldCheckValidUserPassword(t *testing.T) { Password: "password", UsernameAttribute: "uid", MailAttribute: "mail", - DisplayNameAttribute: "displayname", + DisplayNameAttribute: "displayName", UsersFilter: "uid={input}", AdditionalUsersDN: "ou=users", BaseDN: "dc=example,dc=com", @@ -991,7 +993,7 @@ func TestShouldCheckValidUserPassword(t *testing.T) { DN: "uid=test,dc=example,dc=com", Attributes: []*ldap.EntryAttribute{ { - Name: "displayname", + Name: "displayName", Values: []string{"John Doe"}, }, { @@ -1035,7 +1037,7 @@ func TestShouldCheckInvalidUserPassword(t *testing.T) { Password: "password", UsernameAttribute: "uid", MailAttribute: "mail", - DisplayNameAttribute: "displayname", + DisplayNameAttribute: "displayName", UsersFilter: "uid={input}", AdditionalUsersDN: "ou=users", BaseDN: "dc=example,dc=com", @@ -1058,7 +1060,7 @@ func TestShouldCheckInvalidUserPassword(t *testing.T) { DN: "uid=test,dc=example,dc=com", Attributes: []*ldap.EntryAttribute{ { - Name: "displayname", + Name: "displayName", Values: []string{"John Doe"}, }, { @@ -1102,7 +1104,7 @@ func TestShouldCallStartTLSWhenEnabled(t *testing.T) { Password: "password", UsernameAttribute: "uid", MailAttribute: "mail", - DisplayNameAttribute: "displayname", + DisplayNameAttribute: "displayName", UsersFilter: "uid={input}", AdditionalUsersDN: "ou=users", BaseDN: "dc=example,dc=com", @@ -1136,7 +1138,7 @@ func TestShouldCallStartTLSWhenEnabled(t *testing.T) { DN: "uid=test,dc=example,dc=com", Attributes: []*ldap.EntryAttribute{ { - Name: "displayname", + Name: "displayName", Values: []string{"John Doe"}, }, { @@ -1176,7 +1178,7 @@ func TestShouldParseDynamicConfiguration(t *testing.T) { Password: "password", UsernameAttribute: "uid", MailAttribute: "mail", - DisplayNameAttribute: "displayname", + DisplayNameAttribute: "displayName", 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", @@ -1187,7 +1189,13 @@ func TestShouldParseDynamicConfiguration(t *testing.T) { nil, mockFactory) - assert.Equal(t, "(&(|(uid={input})(mail={input})(displayname={input}))(objectCategory=person)(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=2)(!pwdLastSet=0))", ldapClient.configuration.UsersFilter) + assert.True(t, ldapClient.groupsFilterReplacementInput) + assert.True(t, ldapClient.groupsFilterReplacementUsername) + assert.True(t, ldapClient.groupsFilterReplacementDN) + + assert.True(t, ldapClient.usersFilterReplacementInput) + + assert.Equal(t, "(&(|(uid={input})(mail={input})(displayName={input}))(objectCategory=person)(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=2)(!pwdLastSet=0))", ldapClient.configuration.UsersFilter) assert.Equal(t, "(&(|(member={dn})(member={input})(member={username}))(objectClass=group))", ldapClient.configuration.GroupsFilter) assert.Equal(t, "ou=users,dc=example,dc=com", ldapClient.usersBaseDN) assert.Equal(t, "ou=groups,dc=example,dc=com", ldapClient.groupsBaseDN) @@ -1207,7 +1215,7 @@ func TestShouldCallStartTLSWithInsecureSkipVerifyWhenSkipVerifyTrue(t *testing.T Password: "password", UsernameAttribute: "uid", MailAttribute: "mail", - DisplayNameAttribute: "displayname", + DisplayNameAttribute: "displayName", UsersFilter: "uid={input}", AdditionalUsersDN: "ou=users", BaseDN: "dc=example,dc=com", @@ -1219,6 +1227,10 @@ func TestShouldCallStartTLSWithInsecureSkipVerifyWhenSkipVerifyTrue(t *testing.T nil, mockFactory) + assert.False(t, ldapClient.groupsFilterReplacementInput) + assert.False(t, ldapClient.groupsFilterReplacementUsername) + assert.False(t, ldapClient.groupsFilterReplacementDN) + dialURL := mockFactory.EXPECT(). DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). Return(mockConn, nil) @@ -1244,7 +1256,7 @@ func TestShouldCallStartTLSWithInsecureSkipVerifyWhenSkipVerifyTrue(t *testing.T DN: "uid=test,dc=example,dc=com", Attributes: []*ldap.EntryAttribute{ { - Name: "displayname", + Name: "displayName", Values: []string{"John Doe"}, }, { @@ -1285,7 +1297,7 @@ func TestShouldReturnLDAPSAlreadySecuredWhenStartTLSAttempted(t *testing.T) { Password: "password", UsernameAttribute: "uid", MailAttribute: "mail", - DisplayNameAttribute: "displayname", + DisplayNameAttribute: "displayName", UsersFilter: "uid={input}", AdditionalUsersDN: "ou=users", BaseDN: "dc=example,dc=com", diff --git a/internal/configuration/config.template.yml b/internal/configuration/config.template.yml index 356e909dd..933a03eac 100644 --- a/internal/configuration/config.template.yml +++ b/internal/configuration/config.template.yml @@ -183,10 +183,8 @@ authentication_backend: additional_users_dn: ou=users ## The users filter used in search queries to find the user profile based on input filled in login form. - ## Various placeholders are available in the user filter: - ## - {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`. + ## Various placeholders are available in the user filter which you can read about in the documentation which can + ## be found at: https://www.authelia.com/docs/configuration/authentication/ldap.html#users-filter-replacements ## ## Recommended settings are as follows: ## - Microsoft Active Directory: (&({username_attribute}={input})(objectCategory=person)(objectClass=user)) @@ -202,16 +200,13 @@ authentication_backend: ## i.e. with this set to OU=Groups and base_dn set to DC=a,DC=com; OU=Groups,DC=a,DC=com is searched for groups. additional_groups_dn: ou=groups - ## The groups filter used in search queries to find the groups of the user. - ## - {input} is a placeholder replaced by what the user inputs in the login form. - ## - {username} is a placeholder replace by the username stored in LDAP (based on `username_attribute`). - ## - {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`. + ## The groups filter used in search queries to find the groups based on relevant authenticated user. + ## Various placeholders are available in the groups filter which you can read about in the documentation which can + ## be found at: https://www.authelia.com/docs/configuration/authentication/ldap.html#groups-filter-replacements ## ## If your groups use the `groupOfUniqueNames` structure use this instead: - ## (&(uniquemember={dn})(objectclass=groupOfUniqueNames)) - groups_filter: (&(member={dn})(objectclass=groupOfNames)) + ## (&(uniqueMember={dn})(objectClass=groupOfUniqueNames)) + groups_filter: (&(member={dn})(objectClass=groupOfNames)) ## The attribute holding the name of the group. # group_name_attribute: cn @@ -221,7 +216,7 @@ authentication_backend: # mail_attribute: mail ## The attribute holding the display name of the user. This will be used to greet an authenticated user. - # display_name_attribute: displayname + # display_name_attribute: displayName ## The username and password of the admin user. user: cn=admin,dc=example,dc=com diff --git a/internal/configuration/schema/authentication.go b/internal/configuration/schema/authentication.go index c5d72a1b4..a4727d60f 100644 --- a/internal/configuration/schema/authentication.go +++ b/internal/configuration/schema/authentication.go @@ -75,7 +75,7 @@ var DefaultLDAPAuthenticationBackendConfiguration = LDAPAuthenticationBackendCon Implementation: LDAPImplementationCustom, UsernameAttribute: "uid", MailAttribute: "mail", - DisplayNameAttribute: "displayname", + DisplayNameAttribute: "displayName", GroupNameAttribute: "cn", TLS: &TLSConfig{ MinimumVersion: "TLS1.2", diff --git a/internal/configuration/test_resources/config.yml b/internal/configuration/test_resources/config.yml index 8c72bc6f3..fbe2eca0a 100644 --- a/internal/configuration/test_resources/config.yml +++ b/internal/configuration/test_resources/config.yml @@ -23,7 +23,7 @@ authentication_backend: additional_users_dn: ou=users users_filter: (&({username_attribute}={input})(objectCategory=person)(objectClass=user)) additional_groups_dn: ou=groups - groups_filter: (&(member={dn})(objectclass=groupOfNames)) + groups_filter: (&(member={dn})(objectClass=groupOfNames)) group_name_attribute: cn mail_attribute: mail user: cn=admin,dc=example,dc=com diff --git a/internal/configuration/test_resources/config_alt.yml b/internal/configuration/test_resources/config_alt.yml index 69a5884a7..c97198439 100644 --- a/internal/configuration/test_resources/config_alt.yml +++ b/internal/configuration/test_resources/config_alt.yml @@ -23,7 +23,7 @@ authentication_backend: additional_users_dn: ou=users users_filter: (&({username_attribute}={input})(objectCategory=person)(objectClass=user)) additional_groups_dn: ou=groups - groups_filter: (&(member={dn})(objectclass=groupOfNames)) + groups_filter: (&(member={dn})(objectClass=groupOfNames)) group_name_attribute: cn mail_attribute: mail user: cn=admin,dc=example,dc=com diff --git a/internal/configuration/test_resources/config_bad_keys.yml b/internal/configuration/test_resources/config_bad_keys.yml index f7bed7693..d0bcacd1e 100644 --- a/internal/configuration/test_resources/config_bad_keys.yml +++ b/internal/configuration/test_resources/config_bad_keys.yml @@ -24,7 +24,7 @@ authentication_backend: additional_users_dn: ou=users users_filter: (&({username_attribute}={input})(objectCategory=person)(objectClass=user)) additional_groups_dn: ou=groups - groups_filter: (&(member={dn})(objectclass=groupOfNames)) + groups_filter: (&(member={dn})(objectClass=groupOfNames)) group_name_attribute: cn mail_attribute: mail user: cn=admin,dc=example,dc=com diff --git a/internal/configuration/test_resources/config_with_secret.yml b/internal/configuration/test_resources/config_with_secret.yml index 95497479a..e97d9391c 100644 --- a/internal/configuration/test_resources/config_with_secret.yml +++ b/internal/configuration/test_resources/config_with_secret.yml @@ -25,7 +25,7 @@ authentication_backend: additional_users_dn: ou=users users_filter: (&({username_attribute}={input})(objectCategory=person)(objectClass=user)) additional_groups_dn: ou=groups - groups_filter: (&(member={dn})(objectclass=groupOfNames)) + groups_filter: (&(member={dn})(objectClass=groupOfNames)) group_name_attribute: cn mail_attribute: mail user: cn=admin,dc=example,dc=com diff --git a/internal/configuration/validator/authentication_test.go b/internal/configuration/validator/authentication_test.go index d135b8112..479db7071 100644 --- a/internal/configuration/validator/authentication_test.go +++ b/internal/configuration/validator/authentication_test.go @@ -387,7 +387,7 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultDisplayNameAttr 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() { diff --git a/internal/suites/HighAvailability/configuration.yml b/internal/suites/HighAvailability/configuration.yml index 4447cb43e..e9cd1a52b 100644 --- a/internal/suites/HighAvailability/configuration.yml +++ b/internal/suites/HighAvailability/configuration.yml @@ -25,7 +25,7 @@ authentication_backend: additional_users_dn: ou=users users_filter: (&({username_attribute}={input})(objectClass=person)) additional_groups_dn: ou=groups - groups_filter: (&(member={dn})(objectclass=groupOfNames)) + groups_filter: (&(member={dn})(objectClass=groupOfNames)) group_name_attribute: cn mail_attribute: mail display_name_attribute: displayName diff --git a/internal/suites/LDAP/configuration.yml b/internal/suites/LDAP/configuration.yml index 94538f78b..432efaf00 100644 --- a/internal/suites/LDAP/configuration.yml +++ b/internal/suites/LDAP/configuration.yml @@ -26,7 +26,7 @@ authentication_backend: additional_users_dn: ou=users users_filter: (&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=person)(objectClass=inetOrgPerson)) # yamllint disable-line rule:line-length additional_groups_dn: ou=groups - groups_filter: (&(member={dn})(objectclass=groupOfNames)) + groups_filter: (&(member={dn})(objectClass=groupOfNames)) group_name_attribute: cn mail_attribute: mail display_name_attribute: displayName diff --git a/internal/suites/example/compose/ldap/ldif/03-base.ldif b/internal/suites/example/compose/ldap/ldif/03-base.ldif index 4a56821f8..ac838a4e5 100644 --- a/internal/suites/example/compose/ldap/ldif/03-base.ldif +++ b/internal/suites/example/compose/ldap/ldif/03-base.ldif @@ -1,78 +1,78 @@ dn: cn=pwmanager,{{ LDAP_BASE_DN }} cn: Password Manager -displayname: Password Manager +displayName: Password Manager givenName: Password -objectclass: inetOrgPerson -objectclass: top +objectClass: inetOrgPerson +objectClass: top mail: password.manager@authelia.com sn: Manager uid: pwmanager userPassword: {CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/ dn: ou=groups,{{ LDAP_BASE_DN }} -objectclass: organizationalUnit -objectclass: top +objectClass: organizationalUnit +objectClass: top ou: groups dn: ou=users,{{ LDAP_BASE_DN }} -objectclass: organizationalUnit -objectclass: top +objectClass: organizationalUnit +objectClass: top ou: users dn: cn=dev,ou=groups,{{ LDAP_BASE_DN }} cn: dev member: cn=John Doe (external),ou=users,{{ LDAP_BASE_DN }} member: cn=Bob Dylan,ou=users,{{ LDAP_BASE_DN }} -objectclass: groupOfNames -objectclass: top +objectClass: groupOfNames +objectClass: top dn: cn=admins,ou=groups,{{ LDAP_BASE_DN }} cn: admins member: cn=John Doe (external),ou=users,{{ LDAP_BASE_DN }} -objectclass: groupOfNames -objectclass: top +objectClass: groupOfNames +objectClass: top dn: cn=John Doe (external),ou=users,{{ LDAP_BASE_DN }} cn: John Doe (external) -displayname: John Doe +displayName: John Doe givenName: John -objectclass: inetOrgPerson -objectclass: top +objectClass: inetOrgPerson +objectClass: top mail: john.doe@authelia.com sn: Doe uid: john -userpassword: {CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/ +userPassword: {CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/ dn: cn=Harry Potter,ou=users,{{ LDAP_BASE_DN }} cn: Harry Potter -displayname: Harry Potter +displayName: Harry Potter givenName: Harry -objectclass: inetOrgPerson -objectclass: top +objectClass: inetOrgPerson +objectClass: top mail: harry.potter@authelia.com sn: Potter uid: harry -userpassword: {CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/ +userPassword: {CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/ dn: cn=Bob Dylan,ou=users,{{ LDAP_BASE_DN }} cn: Bob Dylan -displayname: Bob Dylan +displayName: Bob Dylan givenName: Bob -objectclass: inetOrgPerson -objectclass: top +objectClass: inetOrgPerson +objectClass: top mail: bob.dylan@authelia.com sn: Dylan uid: bob -userpassword: {CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/ +userPassword: {CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/ dn: cn=James Dean,ou=users,{{ LDAP_BASE_DN }} cn: James Dean -displayname: James Dean +displayName: James Dean givenName: James -objectclass: inetOrgPerson -objectclass: top +objectClass: inetOrgPerson +objectClass: top mail: james.dean@authelia.com sn: Dean uid: james -userpassword: {CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/ +userPassword: {CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/ diff --git a/internal/suites/example/kube/authelia/configs/configuration.yml b/internal/suites/example/kube/authelia/configs/configuration.yml index 4045b9946..b4d3ece18 100644 --- a/internal/suites/example/kube/authelia/configs/configuration.yml +++ b/internal/suites/example/kube/authelia/configs/configuration.yml @@ -24,7 +24,7 @@ authentication_backend: additional_users_dn: ou=users users_filter: (&({username_attribute}={input})(objectClass=person)) additional_groups_dn: ou=groups - groups_filter: (&(member={dn})(objectclass=groupOfNames)) + groups_filter: (&(member={dn})(objectClass=groupOfNames)) group_name_attribute: cn mail_attribute: mail display_name_attribute: displayName diff --git a/internal/suites/example/kube/ldap/base.ldif b/internal/suites/example/kube/ldap/base.ldif index aa2d95746..e1a198e8a 100644 --- a/internal/suites/example/kube/ldap/base.ldif +++ b/internal/suites/example/kube/ldap/base.ldif @@ -1,67 +1,67 @@ dn: ou=groups,dc=example,dc=com -objectclass: organizationalUnit -objectclass: top +objectClass: organizationalUnit +objectClass: top ou: groups dn: ou=users,dc=example,dc=com -objectclass: organizationalUnit -objectclass: top +objectClass: organizationalUnit +objectClass: top ou: users dn: cn=dev,ou=groups,dc=example,dc=com cn: dev member: uid=john,ou=users,dc=example,dc=com member: uid=bob,ou=users,dc=example,dc=com -objectclass: groupOfNames -objectclass: top +objectClass: groupOfNames +objectClass: top dn: cn=admins,ou=groups,dc=example,dc=com cn: admins member: uid=john,ou=users,dc=example,dc=com -objectclass: groupOfNames -objectclass: top +objectClass: groupOfNames +objectClass: top dn: uid=john,ou=users,dc=example,dc=com uid: john cn: john -objectclass: inetOrgPerson -objectclass: top +objectClass: inetOrgPerson +objectClass: top mail: john.doe@authelia.com sn: John Doe -userpassword: {CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/ +userPassword: {CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/ dn: uid=harry,ou=users,dc=example,dc=com uid: harry cn: harry -objectclass: inetOrgPerson -objectclass: top +objectClass: inetOrgPerson +objectClass: top mail: harry.potter@authelia.com sn: Harry Potter -userpassword: {CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/ +userPassword: {CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/ dn: uid=bob,ou=users,dc=example,dc=com uid: bob cn: bob -objectclass: inetOrgPerson -objectclass: top +objectClass: inetOrgPerson +objectClass: top mail: bob.dylan@authelia.com sn: Bob Dylan -userpassword: {CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/ +userPassword: {CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/ dn: uid=james,ou=users,dc=example,dc=com uid: james cn: james -objectclass: inetOrgPerson -objectclass: top +objectClass: inetOrgPerson +objectClass: top mail: james.dean@authelia.com sn: James Dean -userpassword: {CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/ +userPassword: {CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/ dn: uid=blackhat,ou=users,dc=example,dc=com uid: blackhat cn: blackhat -objectclass: inetOrgPerson -objectclass: top +objectClass: inetOrgPerson +objectClass: top mail: billy.blackhat@authelia.com sn: Billy BlackHat -userpassword: {CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/ +userPassword: {CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/ diff --git a/internal/utils/const.go b/internal/utils/const.go index 56ca197ea..21c1619b2 100644 --- a/internal/utils/const.go +++ b/internal/utils/const.go @@ -26,6 +26,12 @@ const ( // TLS10 is the textual representation of TLS 1.0. TLS10 = "1.0" + clean = "clean" + tagged = "tagged" + unknown = "unknown" +) + +const ( // Hour is an int based representation of the time unit. Hour = time.Minute * 60 @@ -40,11 +46,9 @@ const ( // Month is an int based representation of the time unit. Month = Year / 12 +) - clean = "clean" - tagged = "tagged" - unknown = "unknown" - +const ( errFmtLinuxNotFound = "open %s: no such file or directory" )