mirror of
https://github.com/0rangebananaspy/authelia.git
synced 2024-09-14 22:47:21 +07:00
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.
This commit is contained in:
parent
c5c6bda8b0
commit
a3b14871ba
config.template.yml
docs/configuration/authentication
internal
authentication
configuration
suites
HighAvailability
LDAP
example
utils
|
@ -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
|
||||||
|
|
||||||
// The {input} placeholder is replaced by the users username input.
|
if p.usersFilterReplacementInput {
|
||||||
userFilter = strings.ReplaceAll(userFilter, "{input}", inputUsername)
|
// The {input} placeholder is replaced by the users username input.
|
||||||
|
filter = strings.ReplaceAll(filter, ldapPlaceholderInput, p.ldapEscape(inputUsername))
|
||||||
|
}
|
||||||
|
|
||||||
p.logger.Tracef("Computed user filter is %s", userFilter)
|
p.logger.Tracef("Computed user filter is %s", filter)
|
||||||
|
|
||||||
return userFilter
|
return filter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *LDAPUserProvider) getUserProfile(conn LDAPConnection, inputUsername string) (*ldapUserProfile, error) {
|
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
|
||||||
|
|
||||||
// The {input} placeholder is replaced by the users username input.
|
if p.groupsFilterReplacementInput {
|
||||||
groupFilter := strings.ReplaceAll(p.configuration.GroupsFilter, "{input}", inputUsername)
|
// The {input} placeholder is replaced by the users username input.
|
||||||
|
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
|
||||||
|
|
103
internal/authentication/ldap_user_provider_startup.go
Normal file
103
internal/authentication/ldap_user_provider_startup.go
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
package authentication
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-ldap/ldap/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *LDAPUserProvider) checkServer() (err error) {
|
||||||
|
conn, err := p.connect(p.configuration.User, p.configuration.Password)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
searchRequest := ldap.NewSearchRequest("", ldap.ScopeBaseObject, ldap.NeverDerefAliases,
|
||||||
|
1, 0, false, "(objectClass=*)", []string{ldapSupportedExtensionAttribute}, nil)
|
||||||
|
|
||||||
|
sr, err := conn.Search(searchRequest)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(sr.Entries) != 1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate the attribute values to see what the server supports.
|
||||||
|
for _, attr := range sr.Entries[0].Attributes {
|
||||||
|
if attr.Name == ldapSupportedExtensionAttribute {
|
||||||
|
p.logger.Tracef("LDAP Supported Extension OIDs: %s", strings.Join(attr.Values, ", "))
|
||||||
|
|
||||||
|
for _, oid := range attr.Values {
|
||||||
|
if oid == ldapOIDPasswdModifyExtension {
|
||||||
|
p.supportExtensionPasswdModify = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LDAPUserProvider) parseDynamicUsersConfiguration() {
|
||||||
|
p.configuration.UsersFilter = strings.ReplaceAll(p.configuration.UsersFilter, "{username_attribute}", p.configuration.UsernameAttribute)
|
||||||
|
p.configuration.UsersFilter = strings.ReplaceAll(p.configuration.UsersFilter, "{mail_attribute}", p.configuration.MailAttribute)
|
||||||
|
p.configuration.UsersFilter = strings.ReplaceAll(p.configuration.UsersFilter, "{display_name_attribute}", p.configuration.DisplayNameAttribute)
|
||||||
|
|
||||||
|
p.logger.Tracef("Dynamically generated users filter is %s", p.configuration.UsersFilter)
|
||||||
|
|
||||||
|
p.usersAttributes = []string{
|
||||||
|
p.configuration.DisplayNameAttribute,
|
||||||
|
p.configuration.MailAttribute,
|
||||||
|
p.configuration.UsernameAttribute,
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.configuration.AdditionalUsersDN != "" {
|
||||||
|
p.usersBaseDN = p.configuration.AdditionalUsersDN + "," + p.configuration.BaseDN
|
||||||
|
} else {
|
||||||
|
p.usersBaseDN = p.configuration.BaseDN
|
||||||
|
}
|
||||||
|
|
||||||
|
p.logger.Tracef("Dynamically generated users BaseDN is %s", p.usersBaseDN)
|
||||||
|
|
||||||
|
if strings.Contains(p.configuration.UsersFilter, ldapPlaceholderInput) {
|
||||||
|
p.usersFilterReplacementInput = true
|
||||||
|
}
|
||||||
|
|
||||||
|
p.logger.Tracef("Detected user filter replacements that need to be resolved per lookup are: %s=%v",
|
||||||
|
ldapPlaceholderInput, p.usersFilterReplacementInput)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LDAPUserProvider) parseDynamicGroupsConfiguration() {
|
||||||
|
p.groupsAttributes = []string{
|
||||||
|
p.configuration.GroupNameAttribute,
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.configuration.AdditionalGroupsDN != "" {
|
||||||
|
p.groupsBaseDN = ldap.EscapeFilter(p.configuration.AdditionalGroupsDN + "," + p.configuration.BaseDN)
|
||||||
|
} else {
|
||||||
|
p.groupsBaseDN = p.configuration.BaseDN
|
||||||
|
}
|
||||||
|
|
||||||
|
p.logger.Tracef("Dynamically generated groups BaseDN is %s", p.groupsBaseDN)
|
||||||
|
|
||||||
|
if strings.Contains(p.configuration.GroupsFilter, ldapPlaceholderInput) {
|
||||||
|
p.groupsFilterReplacementInput = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(p.configuration.GroupsFilter, ldapPlaceholderUsername) {
|
||||||
|
p.groupsFilterReplacementUsername = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(p.configuration.GroupsFilter, ldapPlaceholderDistinguishedName) {
|
||||||
|
p.groupsFilterReplacementDN = true
|
||||||
|
}
|
||||||
|
|
||||||
|
p.logger.Tracef("Detected group filter replacements that need to be resolved per lookup are: input=%v, username=%v, dn=%v", p.groupsFilterReplacementInput, p.groupsFilterReplacementUsername, p.groupsFilterReplacementDN)
|
||||||
|
}
|
|
@ -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
Block a user