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
parent
c5c6bda8b0
commit
a3b14871ba
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
if p.usersFilterReplacementInput {
|
||||
// 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) {
|
||||
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
|
||||
|
||||
if p.groupsFilterReplacementInput {
|
||||
// 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))
|
||||
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
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -75,7 +75,7 @@ var DefaultLDAPAuthenticationBackendConfiguration = LDAPAuthenticationBackendCon
|
|||
Implementation: LDAPImplementationCustom,
|
||||
UsernameAttribute: "uid",
|
||||
MailAttribute: "mail",
|
||||
DisplayNameAttribute: "displayname",
|
||||
DisplayNameAttribute: "displayName",
|
||||
GroupNameAttribute: "cn",
|
||||
TLS: &TLSConfig{
|
||||
MinimumVersion: "TLS1.2",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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/
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
||||
|
|
Loading…
Reference in New Issue