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
|
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
|
||||||
|
|
|
@ -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
|
|
@ -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}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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}))",
|
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",
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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/
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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/
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue