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.
pull/2240/head^2
James Elliott 2021-08-05 14:17:07 +10:00 committed by GitHub
parent c5c6bda8b0
commit a3b14871ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 312 additions and 217 deletions

View File

@ -183,10 +183,8 @@ authentication_backend:
additional_users_dn: ou=users additional_users_dn: ou=users
## The users filter used in search queries to find the user profile based on input filled in login form. ## 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: ## Various placeholders are available in the user filter which you can read about in the documentation which can
## - {input} is a placeholder replaced by what the user inputs in the login form. ## be found at: https://www.authelia.com/docs/configuration/authentication/ldap.html#users-filter-replacements
## - {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`.
## ##
## Recommended settings are as follows: ## Recommended settings are as follows:
## - Microsoft Active Directory: (&({username_attribute}={input})(objectCategory=person)(objectClass=user)) ## - Microsoft Active Directory: (&({username_attribute}={input})(objectCategory=person)(objectClass=user))
@ -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. ## 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 additional_groups_dn: ou=groups
## The groups filter used in search queries to find the groups of the user. ## The groups filter used in search queries to find the groups based on relevant authenticated user.
## - {input} is a placeholder replaced by what the user inputs in the login form. ## Various placeholders are available in the groups filter which you can read about in the documentation which can
## - {username} is a placeholder replace by the username stored in LDAP (based on `username_attribute`). ## be found at: https://www.authelia.com/docs/configuration/authentication/ldap.html#groups-filter-replacements
## - {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`.
## ##
## If your groups use the `groupOfUniqueNames` structure use this instead: ## If your groups use the `groupOfUniqueNames` structure use this instead:
## (&(uniquemember={dn})(objectclass=groupOfUniqueNames)) ## (&(uniqueMember={dn})(objectClass=groupOfUniqueNames))
groups_filter: (&(member={dn})(objectclass=groupOfNames)) groups_filter: (&(member={dn})(objectClass=groupOfNames))
## The attribute holding the name of the group. ## The attribute holding the name of the group.
# group_name_attribute: cn # group_name_attribute: cn
@ -221,7 +216,7 @@ authentication_backend:
# mail_attribute: mail # mail_attribute: mail
## The attribute holding the display name of the user. This will be used to greet an authenticated user. ## 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. ## The username and password of the admin user.
user: cn=admin,dc=example,dc=com user: cn=admin,dc=example,dc=com

View File

@ -29,10 +29,10 @@ authentication_backend:
additional_users_dn: ou=users additional_users_dn: ou=users
users_filter: (&({username_attribute}={input})(objectClass=person)) users_filter: (&({username_attribute}={input})(objectClass=person))
additional_groups_dn: ou=groups additional_groups_dn: ou=groups
groups_filter: (&(member={dn})(objectclass=groupOfNames)) groups_filter: (&(member={dn})(objectClass=groupOfNames))
group_name_attribute: cn group_name_attribute: cn
mail_attribute: mail mail_attribute: mail
display_name_attribute: displayname display_name_attribute: displayName
user: cn=admin,dc=example,dc=com user: cn=admin,dc=example,dc=com
password: password 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 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. 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 ### Defaults
The below tables describes the current attribute defaults for each implementation. 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| |Implementation |Username |Display Name|Mail|Group Name|
|:-------------:|:------------:|:----------:|:--:|:--------:| |:-------------:|:------------:|:----------:|:--:|:--------:|
|custom |n/a |displayname |mail|cn | |custom |n/a |displayName |mail|cn |
|activedirectory|sAMAccountName|displayname |mail|cn | |activedirectory|sAMAccountName|displayName |mail|cn |
#### Filter defaults #### 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. result in Authelia throwing an error.
In versions <= `4.24.0` not including the `username_attribute` placeholder will cause issues with the session refresh 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. 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

View File

@ -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 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. // PossibleMethods is the set of all possible 2FA methods.
var PossibleMethods = []string{TOTP, U2F, Push} var PossibleMethods = []string{TOTP, U2F, Push}

View File

@ -15,17 +15,28 @@ import (
"github.com/authelia/authelia/internal/utils" "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 { type LDAPUserProvider struct {
configuration schema.LDAPAuthenticationBackendConfiguration configuration schema.LDAPAuthenticationBackendConfiguration
tlsConfig *tls.Config tlsConfig *tls.Config
dialOpts ldap.DialOpt dialOpts ldap.DialOpt
logger *logrus.Logger logger *logrus.Logger
connectionFactory LDAPConnectionFactory connectionFactory LDAPConnectionFactory
usersBaseDN string
groupsBaseDN string
// Automatically detected ldap features.
supportExtensionPasswdModify bool 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. // NewLDAPUserProvider creates a new instance of LDAPUserProvider.
@ -72,74 +83,12 @@ func newLDAPUserProvider(configuration schema.LDAPAuthenticationBackendConfigura
connectionFactory: factory, connectionFactory: factory,
} }
provider.parseDynamicConfiguration() provider.parseDynamicUsersConfiguration()
provider.parseDynamicGroupsConfiguration()
return provider 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) { func (p *LDAPUserProvider) connect(userDN string, password string) (LDAPConnection, error) {
conn, err := p.connectionFactory.DialURL(p.configuration.URL, p.dialOpts) conn, err := p.connectionFactory.DialURL(p.configuration.URL, p.dialOpts)
if err != nil { if err != nil {
@ -197,34 +146,31 @@ type ldapUserProfile struct {
Username string Username string
} }
func (p *LDAPUserProvider) resolveUsersFilter(userFilter string, inputUsername string) string { func (p *LDAPUserProvider) resolveUsersFilter(inputUsername string) (filter string) {
inputUsername = p.ldapEscape(inputUsername) filter = p.configuration.UsersFilter
if p.usersFilterReplacementInput {
// The {input} placeholder is replaced by the users username input. // The {input} placeholder is replaced by the users username input.
userFilter = strings.ReplaceAll(userFilter, "{input}", inputUsername) 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) { func (p *LDAPUserProvider) getUserProfile(conn LDAPConnection, inputUsername string) (*ldapUserProfile, error) {
userFilter := p.resolveUsersFilter(p.configuration.UsersFilter, inputUsername) userFilter := p.resolveUsersFilter(inputUsername)
attributes := []string{"dn",
p.configuration.DisplayNameAttribute,
p.configuration.MailAttribute,
p.configuration.UsernameAttribute}
// Search for the given username. // Search for the given username.
searchRequest := ldap.NewSearchRequest( searchRequest := ldap.NewSearchRequest(
p.usersBaseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, p.usersBaseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases,
1, 0, false, userFilter, attributes, nil, 1, 0, false, userFilter, p.usersAttributes, nil,
) )
sr, err := conn.Search(searchRequest) sr, err := conn.Search(searchRequest)
if err != nil { 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 { if len(sr.Entries) == 0 {
@ -232,7 +178,7 @@ func (p *LDAPUserProvider) getUserProfile(conn LDAPConnection, inputUsername str
} }
if len(sr.Entries) > 1 { 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{ userProfile := ldapUserProfile{
@ -250,7 +196,7 @@ func (p *LDAPUserProvider) getUserProfile(conn LDAPConnection, inputUsername str
if attr.Name == p.configuration.UsernameAttribute { if attr.Name == p.configuration.UsernameAttribute {
if len(attr.Values) != 1 { 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) inputUsername, p.configuration.UsernameAttribute)
} }
@ -259,26 +205,33 @@ func (p *LDAPUserProvider) getUserProfile(conn LDAPConnection, inputUsername str
} }
if userProfile.DN == "" { 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 return &userProfile, nil
} }
func (p *LDAPUserProvider) resolveGroupsFilter(inputUsername string, profile *ldapUserProfile) (string, error) { //nolint:unparam func (p *LDAPUserProvider) resolveGroupsFilter(inputUsername string, profile *ldapUserProfile) (filter string, err error) { //nolint:unparam
inputUsername = p.ldapEscape(inputUsername) filter = p.configuration.GroupsFilter
if p.groupsFilterReplacementInput {
// The {input} placeholder is replaced by the users username input. // The {input} placeholder is replaced by the users username input.
groupFilter := strings.ReplaceAll(p.configuration.GroupsFilter, "{input}", inputUsername) filter = strings.ReplaceAll(p.configuration.GroupsFilter, ldapPlaceholderInput, p.ldapEscape(inputUsername))
if profile != nil {
groupFilter = strings.ReplaceAll(groupFilter, "{username}", ldap.EscapeFilter(profile.Username))
groupFilter = strings.ReplaceAll(groupFilter, "{dn}", ldap.EscapeFilter(profile.DN))
} }
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. // 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) groupsFilter, err := p.resolveGroupsFilter(inputUsername, profile)
if err != nil { 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. // Search for the given username.
searchGroupRequest := ldap.NewSearchRequest( searchGroupRequest := ldap.NewSearchRequest(
p.groupsBaseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 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) sr, err := conn.Search(searchGroupRequest)
if err != nil { 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) 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) p.logger.Warningf("No groups retrieved from LDAP for user %s", inputUsername)
break break
} }
// Append all values of the document. Normally there should be only one per document. // Append all values of the document. Normally there should be only one per document.
groups = append(groups, res.Attributes[0].Values...) 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 { func (p *LDAPUserProvider) UpdatePassword(inputUsername string, newPassword string) error {
conn, err := p.connect(p.configuration.User, p.configuration.Password) conn, err := p.connect(p.configuration.User, p.configuration.Password)
if err != nil { 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() defer conn.Close()
profile, err := p.getUserProfile(conn, inputUsername) profile, err := p.getUserProfile(conn, inputUsername)
if err != nil { if err != nil {
return fmt.Errorf("Unable to update password. Cause: %s", err) return fmt.Errorf("unable to update password. Cause: %w", err)
} }
switch { switch {
@ -370,7 +324,7 @@ func (p *LDAPUserProvider) UpdatePassword(inputUsername string, newPassword stri
} }
if err != nil { 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 return nil

View File

@ -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)
}

View File

@ -174,7 +174,7 @@ func TestShouldCheckLDAPServerExtensions(t *testing.T) {
UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))",
UsernameAttribute: "uid", UsernameAttribute: "uid",
MailAttribute: "mail", MailAttribute: "mail",
DisplayNameAttribute: "displayname", DisplayNameAttribute: "displayName",
Password: "password", Password: "password",
AdditionalUsersDN: "ou=users", AdditionalUsersDN: "ou=users",
BaseDN: "dc=example,dc=com", BaseDN: "dc=example,dc=com",
@ -230,7 +230,7 @@ func TestShouldNotEnablePasswdModifyExtension(t *testing.T) {
UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))",
UsernameAttribute: "uid", UsernameAttribute: "uid",
MailAttribute: "mail", MailAttribute: "mail",
DisplayNameAttribute: "displayname", DisplayNameAttribute: "displayName",
Password: "password", Password: "password",
AdditionalUsersDN: "ou=users", AdditionalUsersDN: "ou=users",
BaseDN: "dc=example,dc=com", BaseDN: "dc=example,dc=com",
@ -286,7 +286,7 @@ func TestShouldReturnCheckServerConnectError(t *testing.T) {
UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))",
UsernameAttribute: "uid", UsernameAttribute: "uid",
MailAttribute: "mail", MailAttribute: "mail",
DisplayNameAttribute: "displayname", DisplayNameAttribute: "displayName",
Password: "password", Password: "password",
AdditionalUsersDN: "ou=users", AdditionalUsersDN: "ou=users",
BaseDN: "dc=example,dc=com", BaseDN: "dc=example,dc=com",
@ -318,7 +318,7 @@ func TestShouldReturnCheckServerSearchError(t *testing.T) {
UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))",
UsernameAttribute: "uid", UsernameAttribute: "uid",
MailAttribute: "mail", MailAttribute: "mail",
DisplayNameAttribute: "displayname", DisplayNameAttribute: "displayName",
Password: "password", Password: "password",
AdditionalUsersDN: "ou=users", AdditionalUsersDN: "ou=users",
BaseDN: "dc=example,dc=com", BaseDN: "dc=example,dc=com",
@ -379,7 +379,7 @@ func TestShouldEscapeUserInput(t *testing.T) {
UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))",
UsernameAttribute: "uid", UsernameAttribute: "uid",
MailAttribute: "mail", MailAttribute: "mail",
DisplayNameAttribute: "displayname", DisplayNameAttribute: "displayName",
Password: "password", Password: "password",
AdditionalUsersDN: "ou=users", AdditionalUsersDN: "ou=users",
BaseDN: "dc=example,dc=com", BaseDN: "dc=example,dc=com",
@ -414,11 +414,13 @@ func TestShouldCombineUsernameFilterAndUsersFilter(t *testing.T) {
AdditionalUsersDN: "ou=users", AdditionalUsersDN: "ou=users",
BaseDN: "dc=example,dc=com", BaseDN: "dc=example,dc=com",
MailAttribute: "mail", MailAttribute: "mail",
DisplayNameAttribute: "displayname", DisplayNameAttribute: "displayName",
}, },
nil, nil,
mockFactory) mockFactory)
assert.True(t, ldapClient.usersFilterReplacementInput)
mockConn.EXPECT(). mockConn.EXPECT().
Search(NewSearchRequestMatcher("(&(uid=john)(&(objectCategory=person)(objectClass=user)))")). Search(NewSearchRequestMatcher("(&(uid=john)(&(objectCategory=person)(objectClass=user)))")).
Return(&ldap.SearchResult{}, nil) Return(&ldap.SearchResult{}, nil)
@ -456,7 +458,7 @@ func TestShouldNotCrashWhenGroupsAreNotRetrievedFromLDAP(t *testing.T) {
Password: "password", Password: "password",
UsernameAttribute: "uid", UsernameAttribute: "uid",
MailAttribute: "mail", MailAttribute: "mail",
DisplayNameAttribute: "displayname", DisplayNameAttribute: "displayName",
UsersFilter: "uid={input}", UsersFilter: "uid={input}",
AdditionalUsersDN: "ou=users", AdditionalUsersDN: "ou=users",
BaseDN: "dc=example,dc=com", BaseDN: "dc=example,dc=com",
@ -486,7 +488,7 @@ func TestShouldNotCrashWhenGroupsAreNotRetrievedFromLDAP(t *testing.T) {
DN: "uid=test,dc=example,dc=com", DN: "uid=test,dc=example,dc=com",
Attributes: []*ldap.EntryAttribute{ Attributes: []*ldap.EntryAttribute{
{ {
Name: "displayname", Name: "displayName",
Values: []string{"John Doe"}, Values: []string{"John Doe"},
}, },
{ {
@ -587,7 +589,7 @@ func TestShouldReturnUsernameFromLDAP(t *testing.T) {
Password: "password", Password: "password",
UsernameAttribute: "uid", UsernameAttribute: "uid",
MailAttribute: "mail", MailAttribute: "mail",
DisplayNameAttribute: "displayname", DisplayNameAttribute: "displayName",
UsersFilter: "uid={input}", UsersFilter: "uid={input}",
AdditionalUsersDN: "ou=users", AdditionalUsersDN: "ou=users",
BaseDN: "dc=example,dc=com", BaseDN: "dc=example,dc=com",
@ -617,7 +619,7 @@ func TestShouldReturnUsernameFromLDAP(t *testing.T) {
DN: "uid=test,dc=example,dc=com", DN: "uid=test,dc=example,dc=com",
Attributes: []*ldap.EntryAttribute{ Attributes: []*ldap.EntryAttribute{
{ {
Name: "displayname", Name: "displayName",
Values: []string{"John Doe"}, Values: []string{"John Doe"},
}, },
{ {
@ -658,7 +660,7 @@ func TestShouldUpdateUserPasswordPasswdModifyExtension(t *testing.T) {
Password: "password", Password: "password",
UsernameAttribute: "uid", UsernameAttribute: "uid",
MailAttribute: "mail", MailAttribute: "mail",
DisplayNameAttribute: "displayname", DisplayNameAttribute: "displayName",
UsersFilter: "uid={input}", UsersFilter: "uid={input}",
AdditionalUsersDN: "ou=users", AdditionalUsersDN: "ou=users",
BaseDN: "dc=example,dc=com", BaseDN: "dc=example,dc=com",
@ -716,7 +718,7 @@ func TestShouldUpdateUserPasswordPasswdModifyExtension(t *testing.T) {
DN: "uid=test,dc=example,dc=com", DN: "uid=test,dc=example,dc=com",
Attributes: []*ldap.EntryAttribute{ Attributes: []*ldap.EntryAttribute{
{ {
Name: "displayname", Name: "displayName",
Values: []string{"John Doe"}, Values: []string{"John Doe"},
}, },
{ {
@ -822,7 +824,7 @@ func TestShouldUpdateUserPasswordActiveDirectory(t *testing.T) {
DN: "cn=test,dc=example,dc=com", DN: "cn=test,dc=example,dc=com",
Attributes: []*ldap.EntryAttribute{ Attributes: []*ldap.EntryAttribute{
{ {
Name: "displayname", Name: "displayName",
Values: []string{"John Doe"}, Values: []string{"John Doe"},
}, },
{ {
@ -968,7 +970,7 @@ func TestShouldCheckValidUserPassword(t *testing.T) {
Password: "password", Password: "password",
UsernameAttribute: "uid", UsernameAttribute: "uid",
MailAttribute: "mail", MailAttribute: "mail",
DisplayNameAttribute: "displayname", DisplayNameAttribute: "displayName",
UsersFilter: "uid={input}", UsersFilter: "uid={input}",
AdditionalUsersDN: "ou=users", AdditionalUsersDN: "ou=users",
BaseDN: "dc=example,dc=com", BaseDN: "dc=example,dc=com",
@ -991,7 +993,7 @@ func TestShouldCheckValidUserPassword(t *testing.T) {
DN: "uid=test,dc=example,dc=com", DN: "uid=test,dc=example,dc=com",
Attributes: []*ldap.EntryAttribute{ Attributes: []*ldap.EntryAttribute{
{ {
Name: "displayname", Name: "displayName",
Values: []string{"John Doe"}, Values: []string{"John Doe"},
}, },
{ {
@ -1035,7 +1037,7 @@ func TestShouldCheckInvalidUserPassword(t *testing.T) {
Password: "password", Password: "password",
UsernameAttribute: "uid", UsernameAttribute: "uid",
MailAttribute: "mail", MailAttribute: "mail",
DisplayNameAttribute: "displayname", DisplayNameAttribute: "displayName",
UsersFilter: "uid={input}", UsersFilter: "uid={input}",
AdditionalUsersDN: "ou=users", AdditionalUsersDN: "ou=users",
BaseDN: "dc=example,dc=com", BaseDN: "dc=example,dc=com",
@ -1058,7 +1060,7 @@ func TestShouldCheckInvalidUserPassword(t *testing.T) {
DN: "uid=test,dc=example,dc=com", DN: "uid=test,dc=example,dc=com",
Attributes: []*ldap.EntryAttribute{ Attributes: []*ldap.EntryAttribute{
{ {
Name: "displayname", Name: "displayName",
Values: []string{"John Doe"}, Values: []string{"John Doe"},
}, },
{ {
@ -1102,7 +1104,7 @@ func TestShouldCallStartTLSWhenEnabled(t *testing.T) {
Password: "password", Password: "password",
UsernameAttribute: "uid", UsernameAttribute: "uid",
MailAttribute: "mail", MailAttribute: "mail",
DisplayNameAttribute: "displayname", DisplayNameAttribute: "displayName",
UsersFilter: "uid={input}", UsersFilter: "uid={input}",
AdditionalUsersDN: "ou=users", AdditionalUsersDN: "ou=users",
BaseDN: "dc=example,dc=com", BaseDN: "dc=example,dc=com",
@ -1136,7 +1138,7 @@ func TestShouldCallStartTLSWhenEnabled(t *testing.T) {
DN: "uid=test,dc=example,dc=com", DN: "uid=test,dc=example,dc=com",
Attributes: []*ldap.EntryAttribute{ Attributes: []*ldap.EntryAttribute{
{ {
Name: "displayname", Name: "displayName",
Values: []string{"John Doe"}, Values: []string{"John Doe"},
}, },
{ {
@ -1176,7 +1178,7 @@ func TestShouldParseDynamicConfiguration(t *testing.T) {
Password: "password", Password: "password",
UsernameAttribute: "uid", UsernameAttribute: "uid",
MailAttribute: "mail", 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))", 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))", GroupsFilter: "(&(|(member={dn})(member={input})(member={username}))(objectClass=group))",
AdditionalUsersDN: "ou=users", AdditionalUsersDN: "ou=users",
@ -1187,7 +1189,13 @@ func TestShouldParseDynamicConfiguration(t *testing.T) {
nil, nil,
mockFactory) 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, "(&(|(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=users,dc=example,dc=com", ldapClient.usersBaseDN)
assert.Equal(t, "ou=groups,dc=example,dc=com", ldapClient.groupsBaseDN) assert.Equal(t, "ou=groups,dc=example,dc=com", ldapClient.groupsBaseDN)
@ -1207,7 +1215,7 @@ func TestShouldCallStartTLSWithInsecureSkipVerifyWhenSkipVerifyTrue(t *testing.T
Password: "password", Password: "password",
UsernameAttribute: "uid", UsernameAttribute: "uid",
MailAttribute: "mail", MailAttribute: "mail",
DisplayNameAttribute: "displayname", DisplayNameAttribute: "displayName",
UsersFilter: "uid={input}", UsersFilter: "uid={input}",
AdditionalUsersDN: "ou=users", AdditionalUsersDN: "ou=users",
BaseDN: "dc=example,dc=com", BaseDN: "dc=example,dc=com",
@ -1219,6 +1227,10 @@ func TestShouldCallStartTLSWithInsecureSkipVerifyWhenSkipVerifyTrue(t *testing.T
nil, nil,
mockFactory) mockFactory)
assert.False(t, ldapClient.groupsFilterReplacementInput)
assert.False(t, ldapClient.groupsFilterReplacementUsername)
assert.False(t, ldapClient.groupsFilterReplacementDN)
dialURL := mockFactory.EXPECT(). dialURL := mockFactory.EXPECT().
DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()).
Return(mockConn, nil) Return(mockConn, nil)
@ -1244,7 +1256,7 @@ func TestShouldCallStartTLSWithInsecureSkipVerifyWhenSkipVerifyTrue(t *testing.T
DN: "uid=test,dc=example,dc=com", DN: "uid=test,dc=example,dc=com",
Attributes: []*ldap.EntryAttribute{ Attributes: []*ldap.EntryAttribute{
{ {
Name: "displayname", Name: "displayName",
Values: []string{"John Doe"}, Values: []string{"John Doe"},
}, },
{ {
@ -1285,7 +1297,7 @@ func TestShouldReturnLDAPSAlreadySecuredWhenStartTLSAttempted(t *testing.T) {
Password: "password", Password: "password",
UsernameAttribute: "uid", UsernameAttribute: "uid",
MailAttribute: "mail", MailAttribute: "mail",
DisplayNameAttribute: "displayname", DisplayNameAttribute: "displayName",
UsersFilter: "uid={input}", UsersFilter: "uid={input}",
AdditionalUsersDN: "ou=users", AdditionalUsersDN: "ou=users",
BaseDN: "dc=example,dc=com", BaseDN: "dc=example,dc=com",

View File

@ -183,10 +183,8 @@ authentication_backend:
additional_users_dn: ou=users additional_users_dn: ou=users
## The users filter used in search queries to find the user profile based on input filled in login form. ## 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: ## Various placeholders are available in the user filter which you can read about in the documentation which can
## - {input} is a placeholder replaced by what the user inputs in the login form. ## be found at: https://www.authelia.com/docs/configuration/authentication/ldap.html#users-filter-replacements
## - {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`.
## ##
## Recommended settings are as follows: ## Recommended settings are as follows:
## - Microsoft Active Directory: (&({username_attribute}={input})(objectCategory=person)(objectClass=user)) ## - Microsoft Active Directory: (&({username_attribute}={input})(objectCategory=person)(objectClass=user))
@ -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. ## 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 additional_groups_dn: ou=groups
## The groups filter used in search queries to find the groups of the user. ## The groups filter used in search queries to find the groups based on relevant authenticated user.
## - {input} is a placeholder replaced by what the user inputs in the login form. ## Various placeholders are available in the groups filter which you can read about in the documentation which can
## - {username} is a placeholder replace by the username stored in LDAP (based on `username_attribute`). ## be found at: https://www.authelia.com/docs/configuration/authentication/ldap.html#groups-filter-replacements
## - {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`.
## ##
## If your groups use the `groupOfUniqueNames` structure use this instead: ## If your groups use the `groupOfUniqueNames` structure use this instead:
## (&(uniquemember={dn})(objectclass=groupOfUniqueNames)) ## (&(uniqueMember={dn})(objectClass=groupOfUniqueNames))
groups_filter: (&(member={dn})(objectclass=groupOfNames)) groups_filter: (&(member={dn})(objectClass=groupOfNames))
## The attribute holding the name of the group. ## The attribute holding the name of the group.
# group_name_attribute: cn # group_name_attribute: cn
@ -221,7 +216,7 @@ authentication_backend:
# mail_attribute: mail # mail_attribute: mail
## The attribute holding the display name of the user. This will be used to greet an authenticated user. ## 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. ## The username and password of the admin user.
user: cn=admin,dc=example,dc=com user: cn=admin,dc=example,dc=com

View File

@ -75,7 +75,7 @@ var DefaultLDAPAuthenticationBackendConfiguration = LDAPAuthenticationBackendCon
Implementation: LDAPImplementationCustom, Implementation: LDAPImplementationCustom,
UsernameAttribute: "uid", UsernameAttribute: "uid",
MailAttribute: "mail", MailAttribute: "mail",
DisplayNameAttribute: "displayname", DisplayNameAttribute: "displayName",
GroupNameAttribute: "cn", GroupNameAttribute: "cn",
TLS: &TLSConfig{ TLS: &TLSConfig{
MinimumVersion: "TLS1.2", MinimumVersion: "TLS1.2",

View File

@ -23,7 +23,7 @@ authentication_backend:
additional_users_dn: ou=users additional_users_dn: ou=users
users_filter: (&({username_attribute}={input})(objectCategory=person)(objectClass=user)) users_filter: (&({username_attribute}={input})(objectCategory=person)(objectClass=user))
additional_groups_dn: ou=groups additional_groups_dn: ou=groups
groups_filter: (&(member={dn})(objectclass=groupOfNames)) groups_filter: (&(member={dn})(objectClass=groupOfNames))
group_name_attribute: cn group_name_attribute: cn
mail_attribute: mail mail_attribute: mail
user: cn=admin,dc=example,dc=com user: cn=admin,dc=example,dc=com

View File

@ -23,7 +23,7 @@ authentication_backend:
additional_users_dn: ou=users additional_users_dn: ou=users
users_filter: (&({username_attribute}={input})(objectCategory=person)(objectClass=user)) users_filter: (&({username_attribute}={input})(objectCategory=person)(objectClass=user))
additional_groups_dn: ou=groups additional_groups_dn: ou=groups
groups_filter: (&(member={dn})(objectclass=groupOfNames)) groups_filter: (&(member={dn})(objectClass=groupOfNames))
group_name_attribute: cn group_name_attribute: cn
mail_attribute: mail mail_attribute: mail
user: cn=admin,dc=example,dc=com user: cn=admin,dc=example,dc=com

View File

@ -24,7 +24,7 @@ authentication_backend:
additional_users_dn: ou=users additional_users_dn: ou=users
users_filter: (&({username_attribute}={input})(objectCategory=person)(objectClass=user)) users_filter: (&({username_attribute}={input})(objectCategory=person)(objectClass=user))
additional_groups_dn: ou=groups additional_groups_dn: ou=groups
groups_filter: (&(member={dn})(objectclass=groupOfNames)) groups_filter: (&(member={dn})(objectClass=groupOfNames))
group_name_attribute: cn group_name_attribute: cn
mail_attribute: mail mail_attribute: mail
user: cn=admin,dc=example,dc=com user: cn=admin,dc=example,dc=com

View File

@ -25,7 +25,7 @@ authentication_backend:
additional_users_dn: ou=users additional_users_dn: ou=users
users_filter: (&({username_attribute}={input})(objectCategory=person)(objectClass=user)) users_filter: (&({username_attribute}={input})(objectCategory=person)(objectClass=user))
additional_groups_dn: ou=groups additional_groups_dn: ou=groups
groups_filter: (&(member={dn})(objectclass=groupOfNames)) groups_filter: (&(member={dn})(objectClass=groupOfNames))
group_name_attribute: cn group_name_attribute: cn
mail_attribute: mail mail_attribute: mail
user: cn=admin,dc=example,dc=com user: cn=admin,dc=example,dc=com

View File

@ -387,7 +387,7 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultDisplayNameAttr
suite.Assert().False(suite.validator.HasWarnings()) suite.Assert().False(suite.validator.HasWarnings())
suite.Assert().False(suite.validator.HasErrors()) suite.Assert().False(suite.validator.HasErrors())
suite.Assert().Equal("displayname", suite.configuration.LDAP.DisplayNameAttribute) suite.Assert().Equal("displayName", suite.configuration.LDAP.DisplayNameAttribute)
} }
func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultRefreshInterval() { func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultRefreshInterval() {

View File

@ -25,7 +25,7 @@ authentication_backend:
additional_users_dn: ou=users additional_users_dn: ou=users
users_filter: (&({username_attribute}={input})(objectClass=person)) users_filter: (&({username_attribute}={input})(objectClass=person))
additional_groups_dn: ou=groups additional_groups_dn: ou=groups
groups_filter: (&(member={dn})(objectclass=groupOfNames)) groups_filter: (&(member={dn})(objectClass=groupOfNames))
group_name_attribute: cn group_name_attribute: cn
mail_attribute: mail mail_attribute: mail
display_name_attribute: displayName display_name_attribute: displayName

View File

@ -26,7 +26,7 @@ authentication_backend:
additional_users_dn: ou=users additional_users_dn: ou=users
users_filter: (&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=person)(objectClass=inetOrgPerson)) # yamllint disable-line rule:line-length users_filter: (&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=person)(objectClass=inetOrgPerson)) # yamllint disable-line rule:line-length
additional_groups_dn: ou=groups additional_groups_dn: ou=groups
groups_filter: (&(member={dn})(objectclass=groupOfNames)) groups_filter: (&(member={dn})(objectClass=groupOfNames))
group_name_attribute: cn group_name_attribute: cn
mail_attribute: mail mail_attribute: mail
display_name_attribute: displayName display_name_attribute: displayName

View File

@ -1,78 +1,78 @@
dn: cn=pwmanager,{{ LDAP_BASE_DN }} dn: cn=pwmanager,{{ LDAP_BASE_DN }}
cn: Password Manager cn: Password Manager
displayname: Password Manager displayName: Password Manager
givenName: Password givenName: Password
objectclass: inetOrgPerson objectClass: inetOrgPerson
objectclass: top objectClass: top
mail: password.manager@authelia.com mail: password.manager@authelia.com
sn: Manager sn: Manager
uid: pwmanager uid: pwmanager
userPassword: {CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/ userPassword: {CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/
dn: ou=groups,{{ LDAP_BASE_DN }} dn: ou=groups,{{ LDAP_BASE_DN }}
objectclass: organizationalUnit objectClass: organizationalUnit
objectclass: top objectClass: top
ou: groups ou: groups
dn: ou=users,{{ LDAP_BASE_DN }} dn: ou=users,{{ LDAP_BASE_DN }}
objectclass: organizationalUnit objectClass: organizationalUnit
objectclass: top objectClass: top
ou: users ou: users
dn: cn=dev,ou=groups,{{ LDAP_BASE_DN }} dn: cn=dev,ou=groups,{{ LDAP_BASE_DN }}
cn: dev cn: dev
member: cn=John Doe (external),ou=users,{{ LDAP_BASE_DN }} member: cn=John Doe (external),ou=users,{{ LDAP_BASE_DN }}
member: cn=Bob Dylan,ou=users,{{ LDAP_BASE_DN }} member: cn=Bob Dylan,ou=users,{{ LDAP_BASE_DN }}
objectclass: groupOfNames objectClass: groupOfNames
objectclass: top objectClass: top
dn: cn=admins,ou=groups,{{ LDAP_BASE_DN }} dn: cn=admins,ou=groups,{{ LDAP_BASE_DN }}
cn: admins cn: admins
member: cn=John Doe (external),ou=users,{{ LDAP_BASE_DN }} member: cn=John Doe (external),ou=users,{{ LDAP_BASE_DN }}
objectclass: groupOfNames objectClass: groupOfNames
objectclass: top objectClass: top
dn: cn=John Doe (external),ou=users,{{ LDAP_BASE_DN }} dn: cn=John Doe (external),ou=users,{{ LDAP_BASE_DN }}
cn: John Doe (external) cn: John Doe (external)
displayname: John Doe displayName: John Doe
givenName: John givenName: John
objectclass: inetOrgPerson objectClass: inetOrgPerson
objectclass: top objectClass: top
mail: john.doe@authelia.com mail: john.doe@authelia.com
sn: Doe sn: Doe
uid: john 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 }} dn: cn=Harry Potter,ou=users,{{ LDAP_BASE_DN }}
cn: Harry Potter cn: Harry Potter
displayname: Harry Potter displayName: Harry Potter
givenName: Harry givenName: Harry
objectclass: inetOrgPerson objectClass: inetOrgPerson
objectclass: top objectClass: top
mail: harry.potter@authelia.com mail: harry.potter@authelia.com
sn: Potter sn: Potter
uid: harry 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 }} dn: cn=Bob Dylan,ou=users,{{ LDAP_BASE_DN }}
cn: Bob Dylan cn: Bob Dylan
displayname: Bob Dylan displayName: Bob Dylan
givenName: Bob givenName: Bob
objectclass: inetOrgPerson objectClass: inetOrgPerson
objectclass: top objectClass: top
mail: bob.dylan@authelia.com mail: bob.dylan@authelia.com
sn: Dylan sn: Dylan
uid: bob 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 }} dn: cn=James Dean,ou=users,{{ LDAP_BASE_DN }}
cn: James Dean cn: James Dean
displayname: James Dean displayName: James Dean
givenName: James givenName: James
objectclass: inetOrgPerson objectClass: inetOrgPerson
objectclass: top objectClass: top
mail: james.dean@authelia.com mail: james.dean@authelia.com
sn: Dean sn: Dean
uid: james uid: james
userpassword: {CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/ userPassword: {CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/

View File

@ -24,7 +24,7 @@ authentication_backend:
additional_users_dn: ou=users additional_users_dn: ou=users
users_filter: (&({username_attribute}={input})(objectClass=person)) users_filter: (&({username_attribute}={input})(objectClass=person))
additional_groups_dn: ou=groups additional_groups_dn: ou=groups
groups_filter: (&(member={dn})(objectclass=groupOfNames)) groups_filter: (&(member={dn})(objectClass=groupOfNames))
group_name_attribute: cn group_name_attribute: cn
mail_attribute: mail mail_attribute: mail
display_name_attribute: displayName display_name_attribute: displayName

View File

@ -1,67 +1,67 @@
dn: ou=groups,dc=example,dc=com dn: ou=groups,dc=example,dc=com
objectclass: organizationalUnit objectClass: organizationalUnit
objectclass: top objectClass: top
ou: groups ou: groups
dn: ou=users,dc=example,dc=com dn: ou=users,dc=example,dc=com
objectclass: organizationalUnit objectClass: organizationalUnit
objectclass: top objectClass: top
ou: users ou: users
dn: cn=dev,ou=groups,dc=example,dc=com dn: cn=dev,ou=groups,dc=example,dc=com
cn: dev cn: dev
member: uid=john,ou=users,dc=example,dc=com member: uid=john,ou=users,dc=example,dc=com
member: uid=bob,ou=users,dc=example,dc=com member: uid=bob,ou=users,dc=example,dc=com
objectclass: groupOfNames objectClass: groupOfNames
objectclass: top objectClass: top
dn: cn=admins,ou=groups,dc=example,dc=com dn: cn=admins,ou=groups,dc=example,dc=com
cn: admins cn: admins
member: uid=john,ou=users,dc=example,dc=com member: uid=john,ou=users,dc=example,dc=com
objectclass: groupOfNames objectClass: groupOfNames
objectclass: top objectClass: top
dn: uid=john,ou=users,dc=example,dc=com dn: uid=john,ou=users,dc=example,dc=com
uid: john uid: john
cn: john cn: john
objectclass: inetOrgPerson objectClass: inetOrgPerson
objectclass: top objectClass: top
mail: john.doe@authelia.com mail: john.doe@authelia.com
sn: John Doe 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 dn: uid=harry,ou=users,dc=example,dc=com
uid: harry uid: harry
cn: harry cn: harry
objectclass: inetOrgPerson objectClass: inetOrgPerson
objectclass: top objectClass: top
mail: harry.potter@authelia.com mail: harry.potter@authelia.com
sn: Harry Potter 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 dn: uid=bob,ou=users,dc=example,dc=com
uid: bob uid: bob
cn: bob cn: bob
objectclass: inetOrgPerson objectClass: inetOrgPerson
objectclass: top objectClass: top
mail: bob.dylan@authelia.com mail: bob.dylan@authelia.com
sn: Bob Dylan 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 dn: uid=james,ou=users,dc=example,dc=com
uid: james uid: james
cn: james cn: james
objectclass: inetOrgPerson objectClass: inetOrgPerson
objectclass: top objectClass: top
mail: james.dean@authelia.com mail: james.dean@authelia.com
sn: James Dean 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 dn: uid=blackhat,ou=users,dc=example,dc=com
uid: blackhat uid: blackhat
cn: blackhat cn: blackhat
objectclass: inetOrgPerson objectClass: inetOrgPerson
objectclass: top objectClass: top
mail: billy.blackhat@authelia.com mail: billy.blackhat@authelia.com
sn: Billy BlackHat sn: Billy BlackHat
userpassword: {CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/ userPassword: {CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/

View File

@ -26,6 +26,12 @@ const (
// TLS10 is the textual representation of TLS 1.0. // TLS10 is the textual representation of TLS 1.0.
TLS10 = "1.0" TLS10 = "1.0"
clean = "clean"
tagged = "tagged"
unknown = "unknown"
)
const (
// Hour is an int based representation of the time unit. // Hour is an int based representation of the time unit.
Hour = time.Minute * 60 Hour = time.Minute * 60
@ -40,11 +46,9 @@ const (
// Month is an int based representation of the time unit. // Month is an int based representation of the time unit.
Month = Year / 12 Month = Year / 12
)
clean = "clean" const (
tagged = "tagged"
unknown = "unknown"
errFmtLinuxNotFound = "open %s: no such file or directory" errFmtLinuxNotFound = "open %s: no such file or directory"
) )