feat(authentication): ldap memberof
Signed-off-by: James Elliott <james-d-elliott@users.noreply.github.com>feat-ldap-reset-filter
parent
65ecfe4b9a
commit
92cf5a186d
|
@ -317,7 +317,7 @@ authentication_backend:
|
|||
## because it allows Authelia to offload the stateful operations
|
||||
## onto the LDAP service.
|
||||
# ldap:
|
||||
## The address of the LDAP server to connect to in the address common syntax.
|
||||
## The address of the directory server to connect to in the address common syntax.
|
||||
## Format: [<scheme>://]<hostname>[:<port>].
|
||||
## Square brackets indicate optional portions of the format. Scheme must be 'ldap', 'ldaps', or 'ldapi`.
|
||||
## The default scheme is 'ldapi' if the address is an absolute path otherwise it's 'ldaps'.
|
||||
|
@ -403,16 +403,6 @@ authentication_backend:
|
|||
## See also: additional_users_dn, additional_groups_dn.
|
||||
# base_dn: 'dc=example,dc=com'
|
||||
|
||||
## The attribute holding the username of the user. This attribute is used to populate the username in the session
|
||||
## information. For your information, Microsoft Active Directory usually uses 'sAMAccountName' and OpenLDAP usually
|
||||
## uses 'uid'. Beware that this attribute holds the unique identifiers for the users binding the user and the
|
||||
## configuration stored in database. Therefore only single value attributes are allowed and the value must never be
|
||||
## changed once attributed to a user otherwise it would break the configuration for that user. Technically,
|
||||
## non-unique attributes like 'mail' can also be used but we don't recommend using them, we instead advise to use
|
||||
## a filter to perform alternative lookups and the attributes mentioned above (sAMAccountName and uid) to
|
||||
## follow https://datatracker.ietf.org/doc/html/rfc2307.
|
||||
# username_attribute: 'uid'
|
||||
|
||||
## The additional_users_dn is prefixed to base_dn and delimited by a comma when searching for users.
|
||||
## i.e. with this set to OU=Users and base_dn set to DC=a,DC=com; OU=Users,DC=a,DC=com is searched for users.
|
||||
# additional_users_dn: 'ou=users'
|
||||
|
@ -443,15 +433,9 @@ authentication_backend:
|
|||
## (&(uniqueMember={dn})(objectClass=groupOfUniqueNames))
|
||||
# groups_filter: '(&(member={dn})(objectClass=groupOfNames))'
|
||||
|
||||
## The attribute holding the name of the group.
|
||||
# group_name_attribute: 'cn'
|
||||
|
||||
## The attribute holding the mail address of the user. If multiple email addresses are defined for a user, only the
|
||||
## first one returned by the LDAP server is used.
|
||||
# mail_attribute: 'mail'
|
||||
|
||||
## The attribute holding the display name of the user. This will be used to greet an authenticated user.
|
||||
# display_name_attribute: 'displayName'
|
||||
## The group search mode to use. Options are 'filter' or 'memberof'. It's essential to read the docs if you wish to
|
||||
## use 'memberof'. Also 'filter' is the best choice for most use cases.
|
||||
# group_search_mode: 'filter'
|
||||
|
||||
## Follow referrals returned by the server.
|
||||
## This is especially useful for environments where read-only servers exist. Only implemented for write operations.
|
||||
|
@ -462,6 +446,37 @@ authentication_backend:
|
|||
## Password can also be set using a secret: https://www.authelia.com/c/secrets
|
||||
# password: 'password'
|
||||
|
||||
## The attributes for users and objects from the directory server.
|
||||
# attributes:
|
||||
|
||||
## The distinguished name attribute if your directory server supports it. Users should read the docs before
|
||||
## configuring. Only used for the 'memberof' group search mode.
|
||||
# distinguished_name: ''
|
||||
|
||||
## The attribute holding the username of the user. This attribute is used to populate the username in the session
|
||||
## information. For your information, Microsoft Active Directory usually uses 'sAMAccountName' and OpenLDAP
|
||||
## usually uses 'uid'. Beware that this attribute holds the unique identifiers for the users binding the user and
|
||||
## the configuration stored in database; therefore only single value attributes are allowed and the value must
|
||||
## never be changed once attributed to a user otherwise it would break the configuration for that user.
|
||||
## Technically non-unique attributes like 'mail' can also be used but we don't recommend using them, we instead
|
||||
## advise to use a filter to perform alternative lookups and the attributes mentioned above
|
||||
## (sAMAccountName and uid) to follow https://datatracker.ietf.org/doc/html/rfc2307.
|
||||
# username: 'uid'
|
||||
|
||||
## The attribute holding the display name of the user. This will be used to greet an authenticated user.
|
||||
# display_name: 'displayName'
|
||||
|
||||
## The attribute holding the mail address of the user. If multiple email addresses are defined for a user, only
|
||||
## the first one returned by the directory server is used.
|
||||
# mail: 'mail'
|
||||
|
||||
## The attribute which provides distinguished names of groups an object is a member of.
|
||||
## Only used for the 'memberof' group search mode.
|
||||
# member_of: 'memberOf'
|
||||
|
||||
## The attribute holding the name of the group.
|
||||
# group_name: 'cn'
|
||||
|
||||
##
|
||||
## File (Authentication Provider)
|
||||
##
|
||||
|
|
|
@ -101,16 +101,20 @@ authentication_backend:
|
|||
base_dn: 'DC=example,DC=com'
|
||||
additional_users_dn: 'OU=users'
|
||||
users_filter: '(&({username_attribute}={input})(objectClass=person))'
|
||||
username_attribute: 'uid'
|
||||
mail_attribute: 'mail'
|
||||
display_name_attribute: 'displayName'
|
||||
additional_groups_dn: 'OU=groups'
|
||||
groups_filter: '(&(member={dn})(objectClass=groupOfNames))'
|
||||
group_name_attribute: 'cn'
|
||||
group_search_mode: 'filter'
|
||||
permit_referrals: false
|
||||
permit_unauthenticated_bind: false
|
||||
user: 'CN=admin,DC=example,DC=com'
|
||||
password: 'password'
|
||||
attributes:
|
||||
distinguished_name: 'distinguishedName'
|
||||
username: 'uid'
|
||||
display_name: 'displayName'
|
||||
mail: 'mail'
|
||||
member_of: 'memberOf'
|
||||
group_name: 'cn'
|
||||
```
|
||||
|
||||
## Options
|
||||
|
@ -209,66 +213,33 @@ The LDAP filter to narrow down which users are valid. This is important to set c
|
|||
The default value is dependent on the [implementation](#implementation), refer to the
|
||||
[attribute defaults](../../reference/guides/ldap.md#attribute-defaults) for more information.
|
||||
|
||||
### username_attribute
|
||||
|
||||
{{< confkey type="string" required="situational" >}}
|
||||
|
||||
*__Note:__ This option is technically required however the [implementation](#implementation) option can implicitly set a
|
||||
default negating this requirement. Refer to the [attribute defaults](../../reference/guides/ldap.md#attribute-defaults)
|
||||
for more information.*
|
||||
|
||||
The LDAP attribute that maps to the username in *Authelia*. This must contain the `{username_attribute}`
|
||||
[placeholder](../../reference/guides/ldap.md#users-filter-replacements).
|
||||
|
||||
### mail_attribute
|
||||
|
||||
{{< confkey type="string" required="situational" >}}
|
||||
|
||||
*__Note:__ This option is technically required however the [implementation](#implementation) option can implicitly set a
|
||||
default negating this requirement. Refer to the [attribute defaults](../../reference/guides/ldap.md#attribute-defaults)
|
||||
for more information.*
|
||||
|
||||
The attribute to retrieve which contains the users email addresses. This is important for the device registration and
|
||||
password reset processes. The user must have an email address in order for Authelia to perform identity verification
|
||||
when a user attempts to reset their password or register a second factor device.
|
||||
|
||||
### display_name_attribute
|
||||
|
||||
{{< confkey type="string" required="situational" >}}
|
||||
|
||||
*__Note:__ This option is technically required however the [implementation](#implementation) option can implicitly set a
|
||||
default negating this requirement. Refer to the [attribute defaults](#attribute-defaults) for more information.*
|
||||
|
||||
The attribute to retrieve which is shown on the Web UI to the user when they log in.
|
||||
|
||||
### additional_groups_dn
|
||||
|
||||
{{< confkey type="string" required="no" >}}
|
||||
|
||||
Similar to [additional_users_dn](#additional_users_dn) but it applies to group searches.
|
||||
Similar to [additional_users_dn](#additionalusersdn) but it applies to group searches.
|
||||
|
||||
### groups_filter
|
||||
|
||||
{{< confkey type="string" required="situational" >}}
|
||||
|
||||
*__Note:__ This option is technically required however the [implementation](#implementation) option can implicitly set a
|
||||
default negating this requirement. Refer to the [filter defaults](#filter-defaults) for more information.*
|
||||
default negating this requirement. Refer to the [filter defaults](../../reference/guides/ldap.md#filter-defaults) for
|
||||
more information.*
|
||||
|
||||
Similar to [users_filter](#users_filter) but it applies to group searches. In order to include groups the member is not
|
||||
Similar to [users_filter](#usersfilter) but it applies to group searches. In order to include groups the member is not
|
||||
a direct member of, but is a member of another group that is a member of those (i.e. recursive groups), you may try
|
||||
using the following filter which is currently only tested against Microsoft Active Directory:
|
||||
|
||||
`(&(member:1.2.840.113556.1.4.1941:={dn})(objectClass=group)(objectCategory=group))`
|
||||
|
||||
### group_name_attribute
|
||||
### group_search_mode
|
||||
|
||||
{{< confkey type="string" required="situational" >}}
|
||||
{{< confkey type="string" default="filter" required="no" >}}
|
||||
|
||||
*__Note:__ This option is technically required however the [implementation](#implementation) option can implicitly set a
|
||||
default negating this requirement. Refer to the [attribute defaults](#attribute-defaults) for more
|
||||
information.*
|
||||
|
||||
The LDAP attribute that is used by Authelia to determine the group name.
|
||||
The group search mode controls how user groups are discovered. The default of `filter` directly uses the filter to
|
||||
determine the result. The `memberof` experimental mode does another special filtered search. See the
|
||||
[Reference Documentation](../../reference/guides/ldap.md#group-search-modes) for more information.
|
||||
|
||||
### permit_referrals
|
||||
|
||||
|
@ -313,6 +284,71 @@ It's __strongly recommended__ this is a
|
|||
[Random Alphanumeric String](../../reference/guides/generating-secure-values.md#generating-a-random-alphanumeric-string) with 64 or more
|
||||
characters and the user password is changed to this value.
|
||||
|
||||
### attributes
|
||||
|
||||
The following options configure The directory server attribute mappings.
|
||||
|
||||
#### distinguished_name
|
||||
|
||||
{{< confkey type="string" required="situational" >}}
|
||||
|
||||
*__Note:__ This option is technically not required however it is required when using the group search mode
|
||||
`memberof` replacement `{memberof:dn}`.*
|
||||
|
||||
The directory server attribute which contains the distinguished name, primarily used to perform filtered searches. There
|
||||
is a clear distinction between the actual distinguished name and a distinguished name attribute, all directories have
|
||||
distinguished names for objects, but not all have an attribute representing this that can be searched on.
|
||||
|
||||
The only known support at this time is with Active Directory.
|
||||
|
||||
#### username
|
||||
|
||||
{{< confkey type="string" required="situational" >}}
|
||||
|
||||
*__Note:__ This option is technically required however the [implementation](#implementation) option can implicitly set a
|
||||
default negating this requirement. Refer to the [attribute defaults] for more information.*
|
||||
|
||||
The directory server attribute that maps to the username in *Authelia*. This must contain the `{username_attribute}` [placeholder].
|
||||
|
||||
#### display_name
|
||||
|
||||
{{< confkey type="string" required="situational" >}}
|
||||
|
||||
*__Note:__ This option is technically required however the [implementation](#implementation) option can implicitly set a
|
||||
default negating this requirement. Refer to the [attribute defaults] for more information.*
|
||||
|
||||
The directory server attribute to retrieve which is shown on the Web UI to the user when they log in.
|
||||
|
||||
#### mail
|
||||
|
||||
{{< confkey type="string" required="situational" >}}
|
||||
|
||||
*__Note:__ This option is technically required however the [implementation](#implementation) option can implicitly set a
|
||||
default negating this requirement. Refer to the [attribute defaults] for more information.*
|
||||
|
||||
The directory server attribute to retrieve which contains the users email addresses. This is important for the device
|
||||
registration and password reset processes. The user must have an email address in order for Authelia to perform
|
||||
identity verification when a user attempts to reset their password or register a second factor device.
|
||||
|
||||
#### member_of
|
||||
|
||||
{{< confkey type="string" required="situational" >}}
|
||||
|
||||
*__Note:__ This option is technically required however the [implementation](#implementation) option can implicitly set a
|
||||
default negating this requirement. Refer to the [attribute defaults] for more information.*
|
||||
|
||||
The directory server attribute which contains the groups a user is a member of. This is currently only used for the
|
||||
`memberof` group search mode.
|
||||
|
||||
#### group_name
|
||||
|
||||
{{< confkey type="string" required="situational" >}}
|
||||
|
||||
*__Note:__ This option is technically required however the [implementation](#implementation) option can implicitly set a
|
||||
default negating this requirement. Refer to the [attribute defaults] for more information.*
|
||||
|
||||
The directory server attribute that is used by Authelia to determine the group name.
|
||||
|
||||
## Refresh Interval
|
||||
|
||||
It's recommended you either use the default [refresh interval](introduction.md#refreshinterval) or configure this to
|
||||
|
@ -332,6 +368,8 @@ for your users.
|
|||
|
||||
- [LDAP Reference Guide](../../reference/guides/ldap.md)
|
||||
|
||||
[username attribute]: #usernameattribute
|
||||
[username attribute]: #username
|
||||
[TechNet wiki]: https://social.technet.microsoft.com/wiki/contents/articles/5392.active-directory-ldap-syntax-filters.aspx
|
||||
[RFC2307]: https://datatracker.ietf.org/doc/html/rfc2307
|
||||
[attribute defaults]: ../../reference/guides/ldap.md#attribute-defaults
|
||||
[placeholder]: ../../reference/guides/ldap.md#users-filter-replacements
|
||||
|
|
|
@ -24,16 +24,24 @@ server:
|
|||
authz:
|
||||
forward-auth:
|
||||
implementation: 'ForwardAuth'
|
||||
authn_strategies: []
|
||||
authn_strategies:
|
||||
- name: 'HeaderProxyAuthorization'
|
||||
- name: 'CookieSession'
|
||||
ext-authz:
|
||||
implementation: 'ExtAuthz'
|
||||
authn_strategies: []
|
||||
authn_strategies:
|
||||
- name: 'HeaderProxyAuthorization'
|
||||
- name: 'CookieSession'
|
||||
auth-request:
|
||||
implementation: 'AuthRequest'
|
||||
authn_strategies: []
|
||||
authn_strategies:
|
||||
- name: 'HeaderAuthRequestProxyAuthorization'
|
||||
- name: 'CookieSession'
|
||||
legacy:
|
||||
implementation: 'Legacy'
|
||||
authn_strategies: []
|
||||
authn_strategies:
|
||||
- name: 'HeaderLegacy'
|
||||
- name: 'CookieSession'
|
||||
```
|
||||
|
||||
## Name
|
||||
|
|
|
@ -92,35 +92,35 @@ Use this [Standalone Example](#standalone-example) if you want to use
|
|||
version: "3.8"
|
||||
secrets:
|
||||
JWT_SECRET:
|
||||
file: ${PWD}/data/authelia/secrets/JWT_SECRET
|
||||
file: '${PWD}/data/authelia/secrets/JWT_SECRET'
|
||||
SESSION_SECRET:
|
||||
file: ${PWD}/data/authelia/secrets/SESSION_SECRET
|
||||
file: '${PWD}/data/authelia/secrets/SESSION_SECRET'
|
||||
STORAGE_PASSWORD:
|
||||
file: ${PWD}/data/authelia/secrets/STORAGE_PASSWORD
|
||||
file: '${PWD}/data/authelia/secrets/STORAGE_PASSWORD'
|
||||
STORAGE_ENCRYPTION_KEY:
|
||||
file: ${PWD}/data/authelia/secrets/STORAGE_ENCRYPTION_KEY
|
||||
file: '${PWD}/data/authelia/secrets/STORAGE_ENCRYPTION_KEY'
|
||||
services:
|
||||
authelia:
|
||||
container_name: authelia
|
||||
image: docker.io/authelia/authelia:latest
|
||||
restart: unless-stopped
|
||||
container_name: 'authelia'
|
||||
image: 'docker.io/authelia/authelia:latest'
|
||||
restart: 'unless-stopped'
|
||||
networks:
|
||||
net:
|
||||
aliases: []
|
||||
expose:
|
||||
- 9091
|
||||
secrets: [JWT_SECRET, SESSION_SECRET, STORAGE_PASSWORD, STORAGE_ENCRYPTION_KEY]
|
||||
secrets: ['JWT_SECRET', 'SESSION_SECRET', 'STORAGE_PASSWORD', 'STORAGE_ENCRYPTION_KEY']
|
||||
environment:
|
||||
AUTHELIA_JWT_SECRET_FILE: /run/secrets/JWT_SECRET
|
||||
AUTHELIA_SESSION_SECRET_FILE: /run/secrets/SESSION_SECRET
|
||||
AUTHELIA_STORAGE_POSTGRES_PASSWORD_FILE: /run/secrets/STORAGE_PASSWORD
|
||||
AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE: /run/secrets/STORAGE_ENCRYPTION_KEY
|
||||
AUTHELIA_JWT_SECRET_FILE: '/run/secrets/JWT_SECRET'
|
||||
AUTHELIA_SESSION_SECRET_FILE: '/run/secrets/SESSION_SECRET'
|
||||
AUTHELIA_STORAGE_POSTGRES_PASSWORD_FILE: '/run/secrets/STORAGE_PASSWORD'
|
||||
AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE: '/run/secrets/STORAGE_ENCRYPTION_KEY'
|
||||
volumes:
|
||||
- ${PWD}/data/authelia/config:/config
|
||||
- '${PWD}/data/authelia/config:/config'
|
||||
networks:
|
||||
net:
|
||||
external: true
|
||||
name: net
|
||||
name: 'net'
|
||||
...
|
||||
```
|
||||
{{< /details >}}
|
||||
|
@ -136,26 +136,26 @@ Use this [Standalone Example](#standalone-example) if you want to use a standard
|
|||
version: "3.8"
|
||||
services:
|
||||
authelia:
|
||||
container_name: authelia
|
||||
image: docker.io/authelia/authelia:latest
|
||||
restart: unless-stopped
|
||||
container_name: 'authelia'
|
||||
image: 'docker.io/authelia/authelia:latest'
|
||||
restart: 'unless-stopped'
|
||||
networks:
|
||||
net:
|
||||
aliases: []
|
||||
expose:
|
||||
- 9091
|
||||
environment:
|
||||
AUTHELIA_JWT_SECRET_FILE: /secrets/JWT_SECRET
|
||||
AUTHELIA_SESSION_SECRET_FILE: /secrets/SESSION_SECRET
|
||||
AUTHELIA_STORAGE_POSTGRES_PASSWORD_FILE: /secrets/STORAGE_PASSWORD
|
||||
AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE: /secrets/STORAGE_ENCRYPTION_KEY
|
||||
AUTHELIA_JWT_SECRET_FILE: '/secrets/JWT_SECRET'
|
||||
AUTHELIA_SESSION_SECRET_FILE: '/secrets/SESSION_SECRET'
|
||||
AUTHELIA_STORAGE_POSTGRES_PASSWORD_FILE: '/secrets/STORAGE_PASSWORD'
|
||||
AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE: '/secrets/STORAGE_ENCRYPTION_KEY'
|
||||
volumes:
|
||||
- ${PWD}/data/authelia/config:/config
|
||||
- ${PWD}/data/authelia/secrets:/secrets
|
||||
- '${PWD}/data/authelia/config:/config'
|
||||
- '${PWD}/data/authelia/secrets:/secrets'
|
||||
networks:
|
||||
net:
|
||||
external: true
|
||||
name: net
|
||||
name: 'net'
|
||||
```
|
||||
...
|
||||
{{< /details >}}
|
||||
|
|
|
@ -55,14 +55,16 @@ In your Authelia configuration you will need to enter and update the following v
|
|||
base_dn: DC=example,DC=com
|
||||
additional_users_dn: OU=users
|
||||
users_filter: (&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=person))
|
||||
username_attribute: uid
|
||||
mail_attribute: mail
|
||||
display_name_attribute: displayName
|
||||
additional_groups_dn: OU=groups
|
||||
groups_filter: (&(member=UID={input},OU=users,DC=example,DC=com)(objectClass=groupOfNames))
|
||||
group_name_attribute: cn
|
||||
user: UID=authelia,OU=service accounts,DC=example,DC=com
|
||||
password: "SUPER_COMPLEX_PASSWORD"
|
||||
attributes:
|
||||
distinguished_name: 'distinguishedName'
|
||||
username: 'uid'
|
||||
mail: 'mail'
|
||||
member_of: 'memberOf'
|
||||
group_name: 'cn'
|
||||
```
|
||||
Following this, restart Authelia, and you should be able to begin using LDAP integration for your user logins, with
|
||||
Authelia taking the email attribute for users straight from the 'mail' attribute within the LDAP object.
|
||||
|
@ -100,16 +102,18 @@ In your Authelia configuration you will need to enter and update the following v
|
|||
skip_verify: true
|
||||
minimum_version: TLS1.2
|
||||
base_dn: dc=example,DC=com
|
||||
username_attribute: uid
|
||||
additional_users_dn: CN=users,CN=accounts
|
||||
users_filter: (&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=person))
|
||||
additional_groups_dn: OU=groups
|
||||
groups_filter: (&(member=UID={input},CN=users,CN=accounts,DC=example,DC=com)(objectClass=groupOfNames))
|
||||
group_name_attribute: cn
|
||||
mail_attribute: mail
|
||||
display_name_attribute: displayName
|
||||
user: UID=authelia,CN=users,CN=accounts,DC=example,DC=com
|
||||
password: "SUPER_COMPLEX_PASSWORD"
|
||||
attributes:
|
||||
distinguished_name: 'distinguishedName'
|
||||
username: 'uid'
|
||||
mail: 'mail'
|
||||
member_of: 'memberOf'
|
||||
group_name: 'cn'
|
||||
```
|
||||
Following this, restart Authelia, and you should be able to begin using LDAP integration for your user logins, with
|
||||
Authelia taking the email attribute for users straight from the 'mail' attribute within the LDAP object.
|
||||
|
@ -139,19 +143,21 @@ ldap:
|
|||
timeout: 5s
|
||||
start_tls: false
|
||||
base_dn: dc=example,DC=com
|
||||
username_attribute: uid
|
||||
additional_users_dn: OU=people
|
||||
# To allow sign in both with username and email, one can use a filter like
|
||||
# (&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=person))
|
||||
users_filter: (&({username_attribute}={input})(objectClass=person))
|
||||
additional_groups_dn: OU=groups
|
||||
groups_filter: (member={dn})
|
||||
group_name_attribute: cn
|
||||
mail_attribute: mail
|
||||
display_name_attribute: displayName
|
||||
# The username and password of the admin or service user.
|
||||
user: UID=authelia,OU=people,DC=example,DC=com
|
||||
password: "SUPER_COMPLEX_PASSWORD"
|
||||
attributes:
|
||||
distinguished_name: 'distinguishedName'
|
||||
username: 'uid'
|
||||
mail: 'mail'
|
||||
member_of: 'memberOf'
|
||||
group_name: 'cn'
|
||||
```
|
||||
Following this, restart Authelia, and you should be able to begin using lldap integration for your user logins, with
|
||||
Authelia taking the email attribute for users straight from the 'mail' attribute within the LDAP object.
|
||||
|
|
|
@ -75,6 +75,32 @@ The following implementations exist:
|
|||
[GLAuth]: https://glauth.github.io/
|
||||
[RFC2307bis]: https://datatracker.ietf.org/doc/html/draft-howard-rfc2307bis-02
|
||||
|
||||
### Group Search Modes
|
||||
|
||||
There are currently two group search modes that exist.
|
||||
|
||||
#### Search Mode: filter
|
||||
|
||||
The `filter` search mode is the default search mode. Generally this is recommended.
|
||||
|
||||
#### Search Mode: memberof
|
||||
|
||||
The `memberof` search mode is a special search mode. Generally this is discouraged and is currently experimental.
|
||||
|
||||
Some systems provide a `memberOf` attribute which may include additional groups that the user is a member of. This
|
||||
search mode allows using this attribute as a method to determine their groups. How it works is the search is performed
|
||||
against the base with the subtree scope and the groups filter must include one of the `{memberof:*}` replacements, and
|
||||
the distinguished names of the results from the search are compared (case-insensitive) against the users `memberOf`
|
||||
attribute to determine if they are members.
|
||||
|
||||
This means:
|
||||
|
||||
1. The groups still must be in the search base that you have configured.
|
||||
2. The `memberOf` attribute *__MUST__* include the distinguished name of the group.
|
||||
3. If the `{memberof:dn}` replacement is used:
|
||||
1. The distinguished name *__MUST__* be searchable by your directory server.
|
||||
3. The first relative distinguished name of the distinguished name *__MUST__* be search
|
||||
|
||||
### Filter replacements
|
||||
|
||||
Various replacements occur in the user and groups filter. The replacements either occur at startup or upon an LDAP
|
||||
|
@ -85,25 +111,65 @@ is ever established. In addition to this, during the startup phase we purposeful
|
|||
phase replacements exist so we only have to check if the replacement is necessary once, and we don't needlessly perform
|
||||
every possible replacement on every search regardless of if it's needed or not.
|
||||
|
||||
#### General filter replacements
|
||||
|
||||
| Placeholder | Phase | Replacement |
|
||||
|:------------------------------:|:-------:|:-------------------------------------------:|
|
||||
| {distinguished_name_attribute} | startup | The configured distinguished name attribute |
|
||||
| {username_attribute} | startup | The configured username attribute |
|
||||
| {mail_attribute} | startup | The configured mail attribute |
|
||||
| {display_name_attribute} | startup | The configured display name attribute |
|
||||
| {member_of_attribute} | startup | The configured member of attribute |
|
||||
| {input} | search | The input into the username field |
|
||||
|
||||
#### Users filter replacements
|
||||
|
||||
| Placeholder | Phase | Replacement |
|
||||
|:------------------------:|:-------:|:----------------------------------------------------------------------------------------------------------------:|
|
||||
| {username_attribute} | startup | The configured username attribute |
|
||||
| {mail_attribute} | startup | The configured mail attribute |
|
||||
| {display_name_attribute} | startup | The configured display name attribute |
|
||||
| {input} | search | The input into the username field |
|
||||
| {date-time:generalized} | search | The current UTC time formatted as a LDAP generalized time in the format of `20060102150405.0Z` |
|
||||
| {date-time:unix} | search | The current time formatted as a Unix epoch |
|
||||
| {date-time:microsoft-nt} | search | The current time formatted as a Microsoft NT epoch which is used by some Microsoft [Active Directory] attributes |
|
||||
| Placeholder | Phase | Replacement |
|
||||
|:------------------------------:|:-------:|:----------------------------------------------------------------------------------------------------------------:|
|
||||
| {date-time:generalized} | search | The current UTC time formatted as a LDAP generalized time in the format of `20060102150405.0Z` |
|
||||
| {date-time:unix} | search | The current time formatted as a Unix epoch |
|
||||
| {date-time:microsoft-nt} | search | The current time formatted as a Microsoft NT epoch which is used by some Microsoft [Active Directory] attributes |
|
||||
|
||||
#### 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 |
|
||||
| Placeholder | Phase | Replacement |
|
||||
|:--------------:|:------:|:----------------------------------------------------------------------------------------------------------------------------------------------------:|
|
||||
| {username} | search | The username from the profile lookup obtained from the username attribute |
|
||||
| {dn} | search | The distinguished name from the profile lookup |
|
||||
| {memberof:dn} | search | See the detailed section below |
|
||||
| {memberof:rdn} | search | Only allowed with the `memberof` search method and contains the first relative distinguished name of every `memberOf` entry a use has in parenthesis |
|
||||
|
||||
##### memberof:dn
|
||||
|
||||
Requirements:
|
||||
|
||||
1. Must be using the `memberof` search mode.
|
||||
2. Must have the distinguished name attribute configured in Authelia.
|
||||
3. Directory server must support searching by the distinguished name attribute (many directory services *__DO NOT__*
|
||||
have a distinguished name attribute).
|
||||
|
||||
##### memberof:rdn
|
||||
|
||||
Requirements:
|
||||
|
||||
1. Must be using the `memberof` search mode.
|
||||
2. Directory server must support searching by the first relative distinguished name as an attribute.
|
||||
|
||||
Splits every `memberOf` value to obtain th e first relative distinguished name and joins all of those after surrounding
|
||||
them in parenthesis. This makes the general suggested filter pattern for this particular replacement
|
||||
`(|{memberof:rdn})`. The format of this value is as follows:
|
||||
|
||||
```text
|
||||
(<RDN>)
|
||||
```
|
||||
|
||||
For example if the user has the following distinguished names in their object:
|
||||
|
||||
- CN=abc,OU=groups,DC=example,DC=com
|
||||
- CN=xyz,OU=groups,DC=example,DC=com
|
||||
|
||||
The value will be replaced with `(CN=abc)(CN=xyz)` which using the suggested pattern for the filter becomes
|
||||
`(|(CN=abc)(CN=xyz))` which will then return any user that as a `CN` of `abc` or `xyz`.
|
||||
|
||||
### Defaults
|
||||
|
||||
|
@ -122,14 +188,14 @@ The following set defaults for the `additional_users_dn` and `additional_groups_
|
|||
This table describes the attribute defaults for each implementation. i.e. the username_attribute is described by the
|
||||
Username column.
|
||||
|
||||
| Implementation | Username | Display Name | Mail | Group Name |
|
||||
|:---------------:|:--------------:|:------------:|:----:|:----------:|
|
||||
| custom | N/A | displayName | mail | cn |
|
||||
| activedirectory | sAMAccountName | displayName | mail | cn |
|
||||
| rfc2307bis | uid | displayName | mail | cn |
|
||||
| freeipa | uid | displayName | mail | cn |
|
||||
| lldap | uid | cn | mail | cn |
|
||||
| glauth | cn | description | mail | cn |
|
||||
| Implementation | Username | Display Name | Mail | Group Name | Distinguished Name | Member Of |
|
||||
|:---------------:|:--------------:|:------------:|:----:|:----------:|:------------------:|:---------:|
|
||||
| custom | N/A | displayName | mail | cn | N/A | N/A |
|
||||
| activedirectory | sAMAccountName | displayName | mail | cn | distinguishedName | memberOf |
|
||||
| rfc2307bis | uid | displayName | mail | cn | N/A | memberOf |
|
||||
| freeipa | uid | displayName | mail | cn | N/A | memberOf |
|
||||
| lldap | uid | cn | mail | cn | N/A | N/A |
|
||||
| glauth | cn | description | mail | cn | N/A | N/A |
|
||||
|
||||
#### Filter defaults
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -72,12 +72,19 @@ const (
|
|||
)
|
||||
|
||||
const (
|
||||
ldapPlaceholderInput = "{input}"
|
||||
ldapPlaceholderDistinguishedName = "{dn}"
|
||||
ldapPlaceholderUsername = "{username}"
|
||||
ldapPlaceholderDateTimeGeneralized = "{date-time:generalized}"
|
||||
ldapPlaceholderDateTimeMicrosoftNTTimeEpoch = "{date-time:microsoft-nt}"
|
||||
ldapPlaceholderDateTimeUnixEpoch = "{date-time:unix}"
|
||||
ldapPlaceholderInput = "{input}"
|
||||
ldapPlaceholderDistinguishedName = "{dn}"
|
||||
ldapPlaceholderMemberOfDistinguishedName = "{memberof:dn}"
|
||||
ldapPlaceholderMemberOfRelativeDistinguishedName = "{memberof:rdn}"
|
||||
ldapPlaceholderUsername = "{username}"
|
||||
ldapPlaceholderDateTimeGeneralized = "{date-time:generalized}"
|
||||
ldapPlaceholderDateTimeMicrosoftNTTimeEpoch = "{date-time:microsoft-nt}"
|
||||
ldapPlaceholderDateTimeUnixEpoch = "{date-time:unix}"
|
||||
ldapPlaceholderDistinguishedNameAttribute = "{distinguished_name_attribute}"
|
||||
ldapPlaceholderUsernameAttribute = "{username_attribute}"
|
||||
ldapPlaceholderDisplayNameAttribute = "{display_name_attribute}"
|
||||
ldapPlaceholderMailAttribute = "{mail_attribute}"
|
||||
ldapPlaceholderMemberOfAttribute = "{member_of_attribute}"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
ldap "github.com/go-ldap/ldap/v3"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||
|
@ -40,11 +40,13 @@ type LDAPUserProvider struct {
|
|||
usersFilterReplacementDateTimeMicrosoftNTTimeEpoch bool
|
||||
|
||||
// Dynamically generated groups values.
|
||||
groupsBaseDN string
|
||||
groupsAttributes []string
|
||||
groupsFilterReplacementInput bool
|
||||
groupsFilterReplacementUsername bool
|
||||
groupsFilterReplacementDN bool
|
||||
groupsBaseDN string
|
||||
groupsAttributes []string
|
||||
groupsFilterReplacementInput bool
|
||||
groupsFilterReplacementUsername bool
|
||||
groupsFilterReplacementDN bool
|
||||
groupsFilterReplacementsMemberOfDN bool
|
||||
groupsFilterReplacementsMemberOfRDN bool
|
||||
}
|
||||
|
||||
// NewLDAPUserProvider creates a new instance of LDAPUserProvider with the ProductionLDAPClientFactory.
|
||||
|
@ -86,6 +88,7 @@ func NewLDAPUserProviderWithFactory(config schema.LDAPAuthenticationBackend, dis
|
|||
|
||||
provider.parseDynamicUsersConfiguration()
|
||||
provider.parseDynamicGroupsConfiguration()
|
||||
provider.parseDynamicConfiguration()
|
||||
|
||||
return provider
|
||||
}
|
||||
|
@ -134,38 +137,11 @@ func (p *LDAPUserProvider) GetDetails(username string) (details *UserDetails, er
|
|||
}
|
||||
|
||||
var (
|
||||
request *ldap.SearchRequest
|
||||
result *ldap.SearchResult
|
||||
groups []string
|
||||
)
|
||||
|
||||
// Search for the users groups.
|
||||
request = ldap.NewSearchRequest(
|
||||
p.groupsBaseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases,
|
||||
0, 0, false, p.resolveGroupsFilter(username, profile), p.groupsAttributes, nil,
|
||||
)
|
||||
|
||||
p.log.
|
||||
WithField("base_dn", request.BaseDN).
|
||||
WithField("filter", request.Filter).
|
||||
WithField("attr", request.Attributes).
|
||||
WithField("scope", request.Scope).
|
||||
WithField("deref", request.DerefAliases).
|
||||
Trace("Performing group search")
|
||||
|
||||
if result, err = p.search(client, request); err != nil {
|
||||
return nil, fmt.Errorf("unable to retrieve groups of user '%s'. Cause: %w", username, err)
|
||||
}
|
||||
|
||||
groups := make([]string, 0)
|
||||
|
||||
for _, res := range result.Entries {
|
||||
if len(res.Attributes) == 0 {
|
||||
p.log.Warningf("No groups retrieved from LDAP for user %s", username)
|
||||
break
|
||||
}
|
||||
|
||||
// Append all values of the document. Normally there should be only one per document.
|
||||
groups = append(groups, res.Attributes[0].Values...)
|
||||
if groups, err = p.getUserGroups(client, username, profile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &UserDetails{
|
||||
|
@ -275,6 +251,8 @@ func (p *LDAPUserProvider) search(client LDAPClient, request *ldap.SearchRequest
|
|||
} else {
|
||||
result.Referrals = append(result.Referrals, referral)
|
||||
}
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -357,6 +335,11 @@ func (p *LDAPUserProvider) getUserProfile(client LDAPClient, username string) (p
|
|||
return nil, fmt.Errorf("there were %d users found when searching for '%s' but there should only be 1", len(result.Entries), username)
|
||||
}
|
||||
|
||||
return p.getUserProfileResultToProfile(username, result)
|
||||
}
|
||||
|
||||
//nolint:gocyclo // Not overly complex.
|
||||
func (p *LDAPUserProvider) getUserProfileResultToProfile(username string, result *ldap.SearchResult) (profile *ldapUserProfile, err error) {
|
||||
userProfile := ldapUserProfile{
|
||||
DN: result.Entries[0].DN,
|
||||
}
|
||||
|
@ -364,35 +347,50 @@ func (p *LDAPUserProvider) getUserProfile(client LDAPClient, username string) (p
|
|||
for _, attr := range result.Entries[0].Attributes {
|
||||
attrs := len(attr.Values)
|
||||
|
||||
if attr.Name == p.config.UsernameAttribute {
|
||||
switch attr.Name {
|
||||
case p.config.Attributes.Username:
|
||||
switch attrs {
|
||||
case 1:
|
||||
userProfile.Username = attr.Values[0]
|
||||
|
||||
if attr.Name == p.config.Attributes.DisplayName && userProfile.DisplayName == "" {
|
||||
userProfile.DisplayName = attr.Values[0]
|
||||
}
|
||||
|
||||
if attr.Name == p.config.Attributes.Mail && len(userProfile.Emails) == 0 {
|
||||
userProfile.Emails = []string{attr.Values[0]}
|
||||
}
|
||||
case 0:
|
||||
return nil, fmt.Errorf("user '%s' must have value for attribute '%s'",
|
||||
username, p.config.UsernameAttribute)
|
||||
username, p.config.Attributes.Username)
|
||||
default:
|
||||
return nil, fmt.Errorf("user '%s' has %d values for for attribute '%s' but the attribute must be a single value attribute",
|
||||
username, attrs, p.config.UsernameAttribute)
|
||||
username, attrs, p.config.Attributes.Username)
|
||||
}
|
||||
case p.config.Attributes.Mail:
|
||||
if attrs == 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if attrs == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if attr.Name == p.config.MailAttribute {
|
||||
userProfile.Emails = attr.Values
|
||||
}
|
||||
case p.config.Attributes.DisplayName:
|
||||
if attrs == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if attr.Name == p.config.DisplayNameAttribute {
|
||||
userProfile.DisplayName = attr.Values[0]
|
||||
case p.config.Attributes.MemberOf:
|
||||
if attrs == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
userProfile.MemberOf = attr.Values
|
||||
}
|
||||
}
|
||||
|
||||
if userProfile.Username == "" {
|
||||
return nil, fmt.Errorf("user '%s' must have value for attribute '%s'",
|
||||
username, p.config.UsernameAttribute)
|
||||
username, p.config.Attributes.Username)
|
||||
}
|
||||
|
||||
if userProfile.DN == "" {
|
||||
|
@ -402,6 +400,124 @@ func (p *LDAPUserProvider) getUserProfile(client LDAPClient, username string) (p
|
|||
return &userProfile, nil
|
||||
}
|
||||
|
||||
func (p *LDAPUserProvider) getUserGroups(client LDAPClient, username string, profile *ldapUserProfile) (groups []string, err error) {
|
||||
request := ldap.NewSearchRequest(
|
||||
p.groupsBaseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases,
|
||||
0, 0, false, p.resolveGroupsFilter(username, profile), p.groupsAttributes, nil,
|
||||
)
|
||||
|
||||
p.log.
|
||||
WithField("base_dn", request.BaseDN).
|
||||
WithField("filter", request.Filter).
|
||||
WithField("attributes", request.Attributes).
|
||||
WithField("scope", request.Scope).
|
||||
WithField("deref", request.DerefAliases).
|
||||
WithField("mode", p.config.GroupSearchMode).
|
||||
Trace("Performing group search")
|
||||
|
||||
switch p.config.GroupSearchMode {
|
||||
case "", "filter":
|
||||
return p.getUserGroupsRequestFilter(client, username, profile, request)
|
||||
case "memberof":
|
||||
return p.getUserGroupsRequestMemberOf(client, username, profile, request)
|
||||
default:
|
||||
return nil, fmt.Errorf("could not perform group search with mode '%s' as it's unknown", p.config.GroupSearchMode)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *LDAPUserProvider) getUserGroupsRequestFilter(client LDAPClient, username string, _ *ldapUserProfile, request *ldap.SearchRequest) (groups []string, err error) {
|
||||
var result *ldap.SearchResult
|
||||
|
||||
if result, err = p.search(client, request); err != nil {
|
||||
return nil, fmt.Errorf("unable to retrieve groups of user '%s'. Cause: %w", username, err)
|
||||
}
|
||||
|
||||
for _, entry := range result.Entries {
|
||||
attributes:
|
||||
for _, attr := range entry.Attributes {
|
||||
switch attr.Name {
|
||||
case p.config.Attributes.GroupName:
|
||||
switch n := len(attr.Values); n {
|
||||
case 0:
|
||||
continue
|
||||
case 1:
|
||||
groups = append(groups, attr.Values[0])
|
||||
default:
|
||||
fmt.Println(attr.Name, n, attr.Values)
|
||||
return nil, fmt.Errorf("unable to retrieve groups of user '%s': Cause: the group '%s' attribute '%s' (group name attribute) has more than one value", username, entry.DN, p.config.Attributes.GroupName)
|
||||
}
|
||||
|
||||
break attributes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return groups, nil
|
||||
}
|
||||
|
||||
func (p *LDAPUserProvider) getUserGroupsRequestMemberOf(client LDAPClient, username string, profile *ldapUserProfile, request *ldap.SearchRequest) (groups []string, err error) {
|
||||
var result *ldap.SearchResult
|
||||
|
||||
if result, err = p.search(client, request); err != nil {
|
||||
return nil, fmt.Errorf("unable to retrieve groups of user '%s'. Cause: %w", username, err)
|
||||
}
|
||||
|
||||
for _, entry := range result.Entries {
|
||||
if len(entry.Attributes) == 0 {
|
||||
p.log.
|
||||
WithField("dn", entry.DN).
|
||||
WithField("attributes", request.Attributes).
|
||||
WithField("mode", "memberof").
|
||||
Trace("Skipping Group as the server did not return any requested attributes")
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if !utils.IsStringInSliceFold(entry.DN, profile.MemberOf) {
|
||||
p.log.
|
||||
WithField("dn", entry.DN).
|
||||
WithField("mode", "memberof").
|
||||
Trace("Skipping Group as it doesn't match the users memberof entries")
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
attributes:
|
||||
for _, attr := range entry.Attributes {
|
||||
switch attr.Name {
|
||||
case p.config.Attributes.GroupName:
|
||||
switch len(attr.Values) {
|
||||
case 0:
|
||||
p.log.
|
||||
WithField("dn", entry.DN).
|
||||
WithField("attribute", attr.Name).
|
||||
Trace("Group skipped as the server returned a null attribute")
|
||||
case 1:
|
||||
switch len(attr.Values[0]) {
|
||||
case 0:
|
||||
p.log.
|
||||
WithField("dn", entry.DN).
|
||||
WithField("attribute", attr.Name).
|
||||
Trace("Skipping group as the configured group name attribute had no value")
|
||||
|
||||
default:
|
||||
groups = append(groups, attr.Values[0])
|
||||
}
|
||||
default:
|
||||
p.log.
|
||||
WithField("dn", entry.DN).
|
||||
WithField("attribute", attr.Name).
|
||||
Trace("Group skipped as the server returned a multi-valued attribute but it should be a single-valued attribute")
|
||||
}
|
||||
|
||||
break attributes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return groups, nil
|
||||
}
|
||||
|
||||
func (p *LDAPUserProvider) resolveUsersFilter(input string) (filter string) {
|
||||
filter = p.config.UsersFilter
|
||||
|
||||
|
@ -445,6 +561,27 @@ func (p *LDAPUserProvider) resolveGroupsFilter(input string, profile *ldapUserPr
|
|||
}
|
||||
}
|
||||
|
||||
if p.groupsFilterReplacementsMemberOfDN {
|
||||
sep := fmt.Sprintf(")(%s=", p.config.Attributes.DistinguishedName)
|
||||
values := make([]string, len(profile.MemberOf))
|
||||
|
||||
for i, memberof := range profile.MemberOf {
|
||||
values[i] = ldap.EscapeFilter(memberof)
|
||||
}
|
||||
|
||||
filter = strings.ReplaceAll(filter, ldapPlaceholderMemberOfDistinguishedName, fmt.Sprintf("(%s=%s)", p.config.Attributes.DistinguishedName, strings.Join(values, sep)))
|
||||
}
|
||||
|
||||
if p.groupsFilterReplacementsMemberOfRDN {
|
||||
values := make([]string, len(profile.MemberOf))
|
||||
|
||||
for i, memberof := range profile.MemberOf {
|
||||
values[i] = ldap.EscapeFilter(strings.SplitN(memberof, ",", 2)[0])
|
||||
}
|
||||
|
||||
filter = strings.ReplaceAll(filter, ldapPlaceholderMemberOfRelativeDistinguishedName, fmt.Sprintf("(%s)", strings.Join(values, ")(")))
|
||||
}
|
||||
|
||||
p.log.Tracef("Computed groups filter is %s", filter)
|
||||
|
||||
return filter
|
||||
|
|
|
@ -37,11 +37,6 @@ func (p *LDAPUserProvider) StartupCheck() (err error) {
|
|||
"LDAP Server.")
|
||||
}
|
||||
|
||||
if !p.features.Extensions.TLS && p.config.StartTLS {
|
||||
p.log.Info("Your LDAP Server does not appear to support TLS but you enabled StartTLS which may result " +
|
||||
"in an error.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -90,22 +85,24 @@ func (p *LDAPUserProvider) getServerSupportedFeatures(client LDAPClient) (featur
|
|||
}
|
||||
|
||||
func (p *LDAPUserProvider) parseDynamicUsersConfiguration() {
|
||||
p.config.UsersFilter = strings.ReplaceAll(p.config.UsersFilter, "{username_attribute}", p.config.UsernameAttribute)
|
||||
p.config.UsersFilter = strings.ReplaceAll(p.config.UsersFilter, "{mail_attribute}", p.config.MailAttribute)
|
||||
p.config.UsersFilter = strings.ReplaceAll(p.config.UsersFilter, "{display_name_attribute}", p.config.DisplayNameAttribute)
|
||||
p.config.UsersFilter = strings.ReplaceAll(p.config.UsersFilter, ldapPlaceholderDistinguishedNameAttribute, p.config.Attributes.DistinguishedName)
|
||||
p.config.UsersFilter = strings.ReplaceAll(p.config.UsersFilter, ldapPlaceholderUsernameAttribute, p.config.Attributes.Username)
|
||||
p.config.UsersFilter = strings.ReplaceAll(p.config.UsersFilter, ldapPlaceholderDisplayNameAttribute, p.config.Attributes.DisplayName)
|
||||
p.config.UsersFilter = strings.ReplaceAll(p.config.UsersFilter, ldapPlaceholderMailAttribute, p.config.Attributes.Mail)
|
||||
p.config.UsersFilter = strings.ReplaceAll(p.config.UsersFilter, ldapPlaceholderMemberOfAttribute, p.config.Attributes.MemberOf)
|
||||
|
||||
p.log.Tracef("Dynamically generated users filter is %s", p.config.UsersFilter)
|
||||
|
||||
if !utils.IsStringInSlice(p.config.UsernameAttribute, p.usersAttributes) {
|
||||
p.usersAttributes = append(p.usersAttributes, p.config.UsernameAttribute)
|
||||
if len(p.config.Attributes.Username) != 0 && !utils.IsStringInSlice(p.config.Attributes.Username, p.usersAttributes) {
|
||||
p.usersAttributes = append(p.usersAttributes, p.config.Attributes.Username)
|
||||
}
|
||||
|
||||
if !utils.IsStringInSlice(p.config.MailAttribute, p.usersAttributes) {
|
||||
p.usersAttributes = append(p.usersAttributes, p.config.MailAttribute)
|
||||
if len(p.config.Attributes.Mail) != 0 && !utils.IsStringInSlice(p.config.Attributes.Mail, p.usersAttributes) {
|
||||
p.usersAttributes = append(p.usersAttributes, p.config.Attributes.Mail)
|
||||
}
|
||||
|
||||
if !utils.IsStringInSlice(p.config.DisplayNameAttribute, p.usersAttributes) {
|
||||
p.usersAttributes = append(p.usersAttributes, p.config.DisplayNameAttribute)
|
||||
if len(p.config.Attributes.DisplayName) != 0 && !utils.IsStringInSlice(p.config.Attributes.DisplayName, p.usersAttributes) {
|
||||
p.usersAttributes = append(p.usersAttributes, p.config.Attributes.DisplayName)
|
||||
}
|
||||
|
||||
if p.config.AdditionalUsersDN != "" {
|
||||
|
@ -137,8 +134,14 @@ func (p *LDAPUserProvider) parseDynamicUsersConfiguration() {
|
|||
}
|
||||
|
||||
func (p *LDAPUserProvider) parseDynamicGroupsConfiguration() {
|
||||
p.groupsAttributes = []string{
|
||||
p.config.GroupNameAttribute,
|
||||
p.config.GroupsFilter = strings.ReplaceAll(p.config.GroupsFilter, ldapPlaceholderDistinguishedNameAttribute, p.config.Attributes.DistinguishedName)
|
||||
p.config.GroupsFilter = strings.ReplaceAll(p.config.GroupsFilter, ldapPlaceholderUsernameAttribute, p.config.Attributes.Username)
|
||||
p.config.GroupsFilter = strings.ReplaceAll(p.config.GroupsFilter, ldapPlaceholderDisplayNameAttribute, p.config.Attributes.DisplayName)
|
||||
p.config.GroupsFilter = strings.ReplaceAll(p.config.GroupsFilter, ldapPlaceholderMailAttribute, p.config.Attributes.Mail)
|
||||
p.config.GroupsFilter = strings.ReplaceAll(p.config.GroupsFilter, ldapPlaceholderMemberOfAttribute, p.config.Attributes.MemberOf)
|
||||
|
||||
if len(p.config.Attributes.GroupName) != 0 && !utils.IsStringInSlice(p.config.Attributes.GroupName, p.groupsAttributes) {
|
||||
p.groupsAttributes = append(p.groupsAttributes, p.config.Attributes.GroupName)
|
||||
}
|
||||
|
||||
if p.config.AdditionalGroupsDN != "" {
|
||||
|
@ -161,5 +164,25 @@ func (p *LDAPUserProvider) parseDynamicGroupsConfiguration() {
|
|||
p.groupsFilterReplacementDN = true
|
||||
}
|
||||
|
||||
if strings.Contains(p.config.GroupsFilter, ldapPlaceholderMemberOfDistinguishedName) {
|
||||
p.groupsFilterReplacementsMemberOfDN = true
|
||||
}
|
||||
|
||||
if strings.Contains(p.config.GroupsFilter, ldapPlaceholderMemberOfRelativeDistinguishedName) {
|
||||
p.groupsFilterReplacementsMemberOfRDN = true
|
||||
}
|
||||
|
||||
p.log.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)
|
||||
}
|
||||
|
||||
func (p *LDAPUserProvider) parseDynamicConfiguration() {
|
||||
if len(p.config.Attributes.MemberOf) != 0 {
|
||||
if !utils.IsStringInSlice(p.config.Attributes.MemberOf, p.usersAttributes) {
|
||||
p.usersAttributes = append(p.usersAttributes, p.config.Attributes.MemberOf)
|
||||
}
|
||||
|
||||
if !utils.IsStringInSlice(p.config.Attributes.MemberOf, p.groupsAttributes) {
|
||||
p.groupsAttributes = append(p.groupsAttributes, p.config.Attributes.MemberOf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -83,6 +83,7 @@ type ldapUserProfile struct {
|
|||
Emails []string
|
||||
DisplayName string
|
||||
Username string
|
||||
MemberOf []string
|
||||
}
|
||||
|
||||
// LDAPSupportedFeatures represents features which a server may support which are implemented in code.
|
||||
|
|
|
@ -317,7 +317,7 @@ authentication_backend:
|
|||
## because it allows Authelia to offload the stateful operations
|
||||
## onto the LDAP service.
|
||||
# ldap:
|
||||
## The address of the LDAP server to connect to in the address common syntax.
|
||||
## The address of the directory server to connect to in the address common syntax.
|
||||
## Format: [<scheme>://]<hostname>[:<port>].
|
||||
## Square brackets indicate optional portions of the format. Scheme must be 'ldap', 'ldaps', or 'ldapi`.
|
||||
## The default scheme is 'ldapi' if the address is an absolute path otherwise it's 'ldaps'.
|
||||
|
@ -403,16 +403,6 @@ authentication_backend:
|
|||
## See also: additional_users_dn, additional_groups_dn.
|
||||
# base_dn: 'dc=example,dc=com'
|
||||
|
||||
## The attribute holding the username of the user. This attribute is used to populate the username in the session
|
||||
## information. For your information, Microsoft Active Directory usually uses 'sAMAccountName' and OpenLDAP usually
|
||||
## uses 'uid'. Beware that this attribute holds the unique identifiers for the users binding the user and the
|
||||
## configuration stored in database. Therefore only single value attributes are allowed and the value must never be
|
||||
## changed once attributed to a user otherwise it would break the configuration for that user. Technically,
|
||||
## non-unique attributes like 'mail' can also be used but we don't recommend using them, we instead advise to use
|
||||
## a filter to perform alternative lookups and the attributes mentioned above (sAMAccountName and uid) to
|
||||
## follow https://datatracker.ietf.org/doc/html/rfc2307.
|
||||
# username_attribute: 'uid'
|
||||
|
||||
## The additional_users_dn is prefixed to base_dn and delimited by a comma when searching for users.
|
||||
## i.e. with this set to OU=Users and base_dn set to DC=a,DC=com; OU=Users,DC=a,DC=com is searched for users.
|
||||
# additional_users_dn: 'ou=users'
|
||||
|
@ -443,15 +433,9 @@ authentication_backend:
|
|||
## (&(uniqueMember={dn})(objectClass=groupOfUniqueNames))
|
||||
# groups_filter: '(&(member={dn})(objectClass=groupOfNames))'
|
||||
|
||||
## The attribute holding the name of the group.
|
||||
# group_name_attribute: 'cn'
|
||||
|
||||
## The attribute holding the mail address of the user. If multiple email addresses are defined for a user, only the
|
||||
## first one returned by the LDAP server is used.
|
||||
# mail_attribute: 'mail'
|
||||
|
||||
## The attribute holding the display name of the user. This will be used to greet an authenticated user.
|
||||
# display_name_attribute: 'displayName'
|
||||
## The group search mode to use. Options are 'filter' or 'memberof'. It's essential to read the docs if you wish to
|
||||
## use 'memberof'. Also 'filter' is the best choice for most use cases.
|
||||
# group_search_mode: 'filter'
|
||||
|
||||
## Follow referrals returned by the server.
|
||||
## This is especially useful for environments where read-only servers exist. Only implemented for write operations.
|
||||
|
@ -462,6 +446,37 @@ authentication_backend:
|
|||
## Password can also be set using a secret: https://www.authelia.com/c/secrets
|
||||
# password: 'password'
|
||||
|
||||
## The attributes for users and objects from the directory server.
|
||||
# attributes:
|
||||
|
||||
## The distinguished name attribute if your directory server supports it. Users should read the docs before
|
||||
## configuring. Only used for the 'memberof' group search mode.
|
||||
# distinguished_name: ''
|
||||
|
||||
## The attribute holding the username of the user. This attribute is used to populate the username in the session
|
||||
## information. For your information, Microsoft Active Directory usually uses 'sAMAccountName' and OpenLDAP
|
||||
## usually uses 'uid'. Beware that this attribute holds the unique identifiers for the users binding the user and
|
||||
## the configuration stored in database; therefore only single value attributes are allowed and the value must
|
||||
## never be changed once attributed to a user otherwise it would break the configuration for that user.
|
||||
## Technically non-unique attributes like 'mail' can also be used but we don't recommend using them, we instead
|
||||
## advise to use a filter to perform alternative lookups and the attributes mentioned above
|
||||
## (sAMAccountName and uid) to follow https://datatracker.ietf.org/doc/html/rfc2307.
|
||||
# username: 'uid'
|
||||
|
||||
## The attribute holding the display name of the user. This will be used to greet an authenticated user.
|
||||
# display_name: 'displayName'
|
||||
|
||||
## The attribute holding the mail address of the user. If multiple email addresses are defined for a user, only
|
||||
## the first one returned by the directory server is used.
|
||||
# mail: 'mail'
|
||||
|
||||
## The attribute which provides distinguished names of groups an object is a member of.
|
||||
## Only used for the 'memberof' group search mode.
|
||||
# member_of: 'memberOf'
|
||||
|
||||
## The attribute holding the name of the group.
|
||||
# group_name: 'cn'
|
||||
|
||||
##
|
||||
## File (Authentication Provider)
|
||||
##
|
||||
|
|
|
@ -261,4 +261,36 @@ var deprecations = map[string]Deprecation{
|
|||
MapFunc: nil,
|
||||
ErrFunc: nil,
|
||||
},
|
||||
"authentication_backend.ldap.username_attribute": {
|
||||
Version: model.SemanticVersion{Major: 4, Minor: 38},
|
||||
Key: "authentication_backend.ldap.username_attribute",
|
||||
NewKey: "authentication_backend.ldap.attributes.username",
|
||||
AutoMap: true,
|
||||
MapFunc: nil,
|
||||
ErrFunc: nil,
|
||||
},
|
||||
"authentication_backend.ldap.mail_attribute": {
|
||||
Version: model.SemanticVersion{Major: 4, Minor: 38},
|
||||
Key: "authentication_backend.ldap.mail_attribute",
|
||||
NewKey: "authentication_backend.ldap.attributes.mail",
|
||||
AutoMap: true,
|
||||
MapFunc: nil,
|
||||
ErrFunc: nil,
|
||||
},
|
||||
"authentication_backend.ldap.display_name_attribute": {
|
||||
Version: model.SemanticVersion{Major: 4, Minor: 38},
|
||||
Key: "authentication_backend.ldap.display_name_attribute",
|
||||
NewKey: "authentication_backend.ldap.attributes.display_name",
|
||||
AutoMap: true,
|
||||
MapFunc: nil,
|
||||
ErrFunc: nil,
|
||||
},
|
||||
"authentication_backend.ldap.group_name_attribute": {
|
||||
Version: model.SemanticVersion{Major: 4, Minor: 38},
|
||||
Key: "authentication_backend.ldap.group_name_attribute",
|
||||
NewKey: "authentication_backend.ldap.attributes.group_name",
|
||||
AutoMap: true,
|
||||
MapFunc: nil,
|
||||
ErrFunc: nil,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -108,11 +108,9 @@ type LDAPAuthenticationBackend struct {
|
|||
|
||||
AdditionalGroupsDN string `koanf:"additional_groups_dn"`
|
||||
GroupsFilter string `koanf:"groups_filter"`
|
||||
GroupSearchMode string `koanf:"group_search_mode"`
|
||||
|
||||
GroupNameAttribute string `koanf:"group_name_attribute"`
|
||||
UsernameAttribute string `koanf:"username_attribute"`
|
||||
MailAttribute string `koanf:"mail_attribute"`
|
||||
DisplayNameAttribute string `koanf:"display_name_attribute"`
|
||||
Attributes LDAPAuthenticationAttributes `koanf:"attributes"`
|
||||
|
||||
PermitReferrals bool `koanf:"permit_referrals"`
|
||||
PermitUnauthenticatedBind bool `koanf:"permit_unauthenticated_bind"`
|
||||
|
@ -122,6 +120,16 @@ type LDAPAuthenticationBackend struct {
|
|||
Password string `koanf:"password"`
|
||||
}
|
||||
|
||||
// LDAPAuthenticationAttributes represents the configuration related to LDAP server attributes.
|
||||
type LDAPAuthenticationAttributes struct {
|
||||
DistinguishedName string `koanf:"distinguished_name"`
|
||||
Username string `koanf:"username"`
|
||||
DisplayName string `koanf:"display_name"`
|
||||
Mail string `koanf:"mail"`
|
||||
MemberOf string `koanf:"member_of"`
|
||||
GroupName string `koanf:"group_name"`
|
||||
}
|
||||
|
||||
// DefaultPasswordConfig represents the default configuration related to Argon2id hashing.
|
||||
var DefaultPasswordConfig = Password{
|
||||
Algorithm: argon2,
|
||||
|
@ -175,11 +183,14 @@ var DefaultCIPasswordConfig = Password{
|
|||
|
||||
// DefaultLDAPAuthenticationBackendConfigurationImplementationCustom represents the default LDAP config.
|
||||
var DefaultLDAPAuthenticationBackendConfigurationImplementationCustom = LDAPAuthenticationBackend{
|
||||
UsernameAttribute: ldapAttrUserID,
|
||||
MailAttribute: ldapAttrMail,
|
||||
DisplayNameAttribute: ldapAttrDisplayName,
|
||||
GroupNameAttribute: ldapAttrCommonName,
|
||||
Timeout: time.Second * 5,
|
||||
GroupSearchMode: ldapGroupSearchModeFilter,
|
||||
Attributes: LDAPAuthenticationAttributes{
|
||||
Username: ldapAttrUserID,
|
||||
DisplayName: ldapAttrDisplayName,
|
||||
Mail: ldapAttrMail,
|
||||
GroupName: ldapAttrCommonName,
|
||||
},
|
||||
Timeout: time.Second * 5,
|
||||
TLS: &TLSConfig{
|
||||
MinimumVersion: TLSVersion{tls.VersionTLS12},
|
||||
},
|
||||
|
@ -187,13 +198,18 @@ var DefaultLDAPAuthenticationBackendConfigurationImplementationCustom = LDAPAuth
|
|||
|
||||
// DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory represents the default LDAP config for the LDAPImplementationActiveDirectory Implementation.
|
||||
var DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory = LDAPAuthenticationBackend{
|
||||
UsersFilter: "(&(|({username_attribute}={input})({mail_attribute}={input}))(sAMAccountType=805306368)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(!(pwdLastSet=0))(|(!(accountExpires=*))(accountExpires=0)(accountExpires>={date-time:microsoft-nt})))",
|
||||
UsernameAttribute: "sAMAccountName",
|
||||
MailAttribute: ldapAttrMail,
|
||||
DisplayNameAttribute: ldapAttrDisplayName,
|
||||
GroupsFilter: "(&(member={dn})(|(sAMAccountType=268435456)(sAMAccountType=536870912)))",
|
||||
GroupNameAttribute: ldapAttrCommonName,
|
||||
Timeout: time.Second * 5,
|
||||
UsersFilter: "(&(|({username_attribute}={input})({mail_attribute}={input}))(sAMAccountType=805306368)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(!(pwdLastSet=0))(|(!(accountExpires=*))(accountExpires=0)(accountExpires>={date-time:microsoft-nt})))",
|
||||
GroupsFilter: "(&(member={dn})(|(sAMAccountType=268435456)(sAMAccountType=536870912)))",
|
||||
GroupSearchMode: ldapGroupSearchModeFilter,
|
||||
Attributes: LDAPAuthenticationAttributes{
|
||||
DistinguishedName: ldapAttrDistinguishedName,
|
||||
Username: ldapAttrSAMAccountName,
|
||||
DisplayName: ldapAttrDisplayName,
|
||||
Mail: ldapAttrMail,
|
||||
MemberOf: ldapAttrMemberOf,
|
||||
GroupName: ldapAttrCommonName,
|
||||
},
|
||||
Timeout: time.Second * 5,
|
||||
TLS: &TLSConfig{
|
||||
MinimumVersion: TLSVersion{tls.VersionTLS12},
|
||||
},
|
||||
|
@ -201,13 +217,17 @@ var DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory =
|
|||
|
||||
// DefaultLDAPAuthenticationBackendConfigurationImplementationRFC2307bis represents the default LDAP config for the LDAPImplementationRFC2307bis Implementation.
|
||||
var DefaultLDAPAuthenticationBackendConfigurationImplementationRFC2307bis = LDAPAuthenticationBackend{
|
||||
UsersFilter: "(&(|({username_attribute}={input})({mail_attribute}={input}))(|(objectClass=inetOrgPerson)(objectClass=organizationalPerson)))",
|
||||
UsernameAttribute: ldapAttrUserID,
|
||||
MailAttribute: ldapAttrMail,
|
||||
DisplayNameAttribute: ldapAttrDisplayName,
|
||||
GroupsFilter: "(&(|(member={dn})(uniqueMember={dn}))(|(objectClass=groupOfNames)(objectClass=groupOfUniqueNames)(objectClass=groupOfMembers)))",
|
||||
GroupNameAttribute: ldapAttrCommonName,
|
||||
Timeout: time.Second * 5,
|
||||
UsersFilter: "(&(|({username_attribute}={input})({mail_attribute}={input}))(|(objectClass=inetOrgPerson)(objectClass=organizationalPerson)))",
|
||||
GroupsFilter: "(&(|(member={dn})(uniqueMember={dn}))(|(objectClass=groupOfNames)(objectClass=groupOfUniqueNames)(objectClass=groupOfMembers)))",
|
||||
GroupSearchMode: ldapGroupSearchModeFilter,
|
||||
Attributes: LDAPAuthenticationAttributes{
|
||||
Username: ldapAttrUserID,
|
||||
DisplayName: ldapAttrDisplayName,
|
||||
Mail: ldapAttrMail,
|
||||
MemberOf: ldapAttrMemberOf,
|
||||
GroupName: ldapAttrCommonName,
|
||||
},
|
||||
Timeout: time.Second * 5,
|
||||
TLS: &TLSConfig{
|
||||
MinimumVersion: TLSVersion{tls.VersionTLS12},
|
||||
},
|
||||
|
@ -215,13 +235,17 @@ var DefaultLDAPAuthenticationBackendConfigurationImplementationRFC2307bis = LDAP
|
|||
|
||||
// DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA represents the default LDAP config for the LDAPImplementationFreeIPA Implementation.
|
||||
var DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA = LDAPAuthenticationBackend{
|
||||
UsersFilter: "(&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=person)(!(nsAccountLock=TRUE))(krbPasswordExpiration>={date-time:generalized})(|(!(krbPrincipalExpiration=*))(krbPrincipalExpiration>={date-time:generalized})))",
|
||||
UsernameAttribute: ldapAttrUserID,
|
||||
MailAttribute: ldapAttrMail,
|
||||
DisplayNameAttribute: ldapAttrDisplayName,
|
||||
GroupsFilter: "(&(member={dn})(objectClass=groupOfNames))",
|
||||
GroupNameAttribute: ldapAttrCommonName,
|
||||
Timeout: time.Second * 5,
|
||||
UsersFilter: "(&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=person)(!(nsAccountLock=TRUE))(krbPasswordExpiration>={date-time:generalized})(|(!(krbPrincipalExpiration=*))(krbPrincipalExpiration>={date-time:generalized})))",
|
||||
GroupsFilter: "(&(member={dn})(objectClass=groupOfNames))",
|
||||
GroupSearchMode: ldapGroupSearchModeFilter,
|
||||
Attributes: LDAPAuthenticationAttributes{
|
||||
Username: ldapAttrUserID,
|
||||
DisplayName: ldapAttrDisplayName,
|
||||
Mail: ldapAttrMail,
|
||||
MemberOf: ldapAttrMemberOf,
|
||||
GroupName: ldapAttrCommonName,
|
||||
},
|
||||
Timeout: time.Second * 5,
|
||||
TLS: &TLSConfig{
|
||||
MinimumVersion: TLSVersion{tls.VersionTLS12},
|
||||
},
|
||||
|
@ -229,15 +253,18 @@ var DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA = LDAPAut
|
|||
|
||||
// DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP represents the default LDAP config for the LDAPImplementationLLDAP Implementation.
|
||||
var DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP = LDAPAuthenticationBackend{
|
||||
AdditionalUsersDN: "OU=people",
|
||||
AdditionalGroupsDN: "OU=groups",
|
||||
UsersFilter: "(&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=person))",
|
||||
UsernameAttribute: ldapAttrUserID,
|
||||
MailAttribute: ldapAttrMail,
|
||||
DisplayNameAttribute: ldapAttrCommonName,
|
||||
GroupsFilter: "(&(member={dn})(objectClass=groupOfUniqueNames))",
|
||||
GroupNameAttribute: ldapAttrCommonName,
|
||||
Timeout: time.Second * 5,
|
||||
AdditionalUsersDN: "OU=people",
|
||||
AdditionalGroupsDN: "OU=groups",
|
||||
UsersFilter: "(&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=person))",
|
||||
GroupsFilter: "(&(member={dn})(objectClass=groupOfUniqueNames))",
|
||||
GroupSearchMode: ldapGroupSearchModeFilter,
|
||||
Attributes: LDAPAuthenticationAttributes{
|
||||
Username: ldapAttrUserID,
|
||||
DisplayName: ldapAttrCommonName,
|
||||
Mail: ldapAttrMail,
|
||||
GroupName: ldapAttrCommonName,
|
||||
},
|
||||
Timeout: time.Second * 5,
|
||||
TLS: &TLSConfig{
|
||||
MinimumVersion: TLSVersion{tls.VersionTLS12},
|
||||
},
|
||||
|
@ -245,13 +272,16 @@ var DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP = LDAPAuthe
|
|||
|
||||
// DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth represents the default LDAP config for the LDAPImplementationGLAuth Implementation.
|
||||
var DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth = LDAPAuthenticationBackend{
|
||||
UsersFilter: "(&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=posixAccount)(!(accountStatus=inactive)))",
|
||||
UsernameAttribute: ldapAttrCommonName,
|
||||
MailAttribute: ldapAttrMail,
|
||||
DisplayNameAttribute: ldapAttrDescription,
|
||||
GroupsFilter: "(&(uniqueMember={dn})(objectClass=posixGroup))",
|
||||
GroupNameAttribute: ldapAttrCommonName,
|
||||
Timeout: time.Second * 5,
|
||||
UsersFilter: "(&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=posixAccount)(!(accountStatus=inactive)))",
|
||||
GroupsFilter: "(&(uniqueMember={dn})(objectClass=posixGroup))",
|
||||
GroupSearchMode: ldapGroupSearchModeFilter,
|
||||
Attributes: LDAPAuthenticationAttributes{
|
||||
Username: ldapAttrCommonName,
|
||||
DisplayName: ldapAttrDescription,
|
||||
Mail: ldapAttrMail,
|
||||
GroupName: ldapAttrCommonName,
|
||||
},
|
||||
Timeout: time.Second * 5,
|
||||
TLS: &TLSConfig{
|
||||
MinimumVersion: TLSVersion{tls.VersionTLS12},
|
||||
},
|
||||
|
|
|
@ -78,6 +78,14 @@ const (
|
|||
LDAPImplementationGLAuth = "glauth"
|
||||
)
|
||||
|
||||
const (
|
||||
// LDAPGroupSearchModeFilter is the string for the filter group search mode.
|
||||
LDAPGroupSearchModeFilter = "filter"
|
||||
|
||||
// LDAPGroupSearchModeMemberOf is the string for the memberOf group search mode.
|
||||
LDAPGroupSearchModeMemberOf = "memberof"
|
||||
)
|
||||
|
||||
// TOTP Algorithm.
|
||||
const (
|
||||
TOTPAlgorithmSHA1 = "SHA1"
|
||||
|
@ -121,11 +129,18 @@ const (
|
|||
)
|
||||
|
||||
const (
|
||||
ldapAttrMail = "mail"
|
||||
ldapAttrUserID = "uid"
|
||||
ldapAttrDisplayName = "displayName"
|
||||
ldapAttrDescription = "description"
|
||||
ldapAttrCommonName = "cn"
|
||||
ldapGroupSearchModeFilter = "filter"
|
||||
)
|
||||
|
||||
const (
|
||||
ldapAttrDistinguishedName = "distinguishedName"
|
||||
ldapAttrMail = "mail"
|
||||
ldapAttrUserID = "uid"
|
||||
ldapAttrSAMAccountName = "sAMAccountName"
|
||||
ldapAttrDisplayName = "displayName"
|
||||
ldapAttrDescription = "description"
|
||||
ldapAttrCommonName = "cn"
|
||||
ldapAttrMemberOf = "memberOf"
|
||||
)
|
||||
|
||||
// Address Schemes.
|
||||
|
|
|
@ -118,10 +118,13 @@ var Keys = []string{
|
|||
"authentication_backend.ldap.users_filter",
|
||||
"authentication_backend.ldap.additional_groups_dn",
|
||||
"authentication_backend.ldap.groups_filter",
|
||||
"authentication_backend.ldap.group_name_attribute",
|
||||
"authentication_backend.ldap.username_attribute",
|
||||
"authentication_backend.ldap.mail_attribute",
|
||||
"authentication_backend.ldap.display_name_attribute",
|
||||
"authentication_backend.ldap.group_search_mode",
|
||||
"authentication_backend.ldap.attributes.distinguished_name",
|
||||
"authentication_backend.ldap.attributes.username",
|
||||
"authentication_backend.ldap.attributes.display_name",
|
||||
"authentication_backend.ldap.attributes.mail",
|
||||
"authentication_backend.ldap.attributes.member_of",
|
||||
"authentication_backend.ldap.attributes.group_name",
|
||||
"authentication_backend.ldap.permit_referrals",
|
||||
"authentication_backend.ldap.permit_unauthenticated_bind",
|
||||
"authentication_backend.ldap.permit_feature_detection_failure",
|
||||
|
|
|
@ -66,14 +66,15 @@ authentication_backend:
|
|||
1YjCJ36UpTsLre2f8nOSLtNmRfDPtbOE2mkOoO9dD9UU0XZwnvn9xw==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
base_dn: dc=example,dc=com
|
||||
username_attribute: uid
|
||||
additional_users_dn: ou=users
|
||||
users_filter: (&({username_attribute}={input})(objectCategory=person)(objectClass=user))
|
||||
additional_groups_dn: ou=groups
|
||||
groups_filter: (&(member={dn})(objectClass=groupOfNames))
|
||||
group_name_attribute: cn
|
||||
mail_attribute: mail
|
||||
user: cn=admin,dc=example,dc=com
|
||||
attributes:
|
||||
username: 'uid'
|
||||
group_name: 'cn'
|
||||
mail: 'mail'
|
||||
|
||||
access_control:
|
||||
default_policy: deny
|
||||
|
|
|
@ -66,14 +66,15 @@ authentication_backend:
|
|||
1YjCJ36UpTsLre2f8nOSLtNmRfDPtbOE2mkOoO9dD9UU0XZwnvn9xw==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
base_dn: dc=example,dc=com
|
||||
username_attribute: uid
|
||||
additional_users_dn: ou=users
|
||||
users_filter: (&({username_attribute}={input})(objectCategory=person)(objectClass=user))
|
||||
additional_groups_dn: ou=groups
|
||||
groups_filter: (&(member={dn})(objectClass=groupOfNames))
|
||||
group_name_attribute: cn
|
||||
mail_attribute: mail
|
||||
user: cn=admin,dc=example,dc=com
|
||||
attributes:
|
||||
username: 'uid'
|
||||
group_name: 'cn'
|
||||
mail: 'mail'
|
||||
|
||||
access_control:
|
||||
default_policy: deny
|
||||
|
|
|
@ -66,14 +66,15 @@ authentication_backend:
|
|||
1YjCJ36UpTsLre2f8nOSLtNmRfDPtbOE2mkOoO9dD9UU0XZwnvn9xw==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
base_dn: 'dc=example,dc=com'
|
||||
username_attribute: 'uid'
|
||||
additional_users_dn: 'ou=users'
|
||||
users_filter: '(&({username_attribute}={input})(objectCategory=person)(objectClass=user))'
|
||||
additional_groups_dn: 'ou=groups'
|
||||
groups_filter: '(&(member={dn})(objectClass=groupOfNames))'
|
||||
group_name_attribute: 'cn'
|
||||
mail_attribute: 'mail'
|
||||
user: 'cn=admin,dc=example,dc=com'
|
||||
attributes:
|
||||
username: 'uid'
|
||||
group_name: 'cn'
|
||||
mail: 'mail'
|
||||
|
||||
access_control:
|
||||
default_policy: 'deny'
|
||||
|
|
|
@ -47,14 +47,15 @@ authentication_backend:
|
|||
1YjCJ36UpTsLre2f8nOSLtNmRfDPtbOE2mkOoO9dD9UU0XZwnvn9xw==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
base_dn: dc=example,dc=com
|
||||
username_attribute: uid
|
||||
additional_users_dn: ou=users
|
||||
users_filter: (&({username_attribute}={input})(objectCategory=person)(objectClass=user))
|
||||
additional_groups_dn: ou=groups
|
||||
groups_filter: (&(member={dn})(objectClass=groupOfNames))
|
||||
group_name_attribute: cn
|
||||
mail_attribute: mail
|
||||
user: cn=admin,dc=example,dc=com
|
||||
attributes:
|
||||
username: 'uid'
|
||||
group_name: 'cn'
|
||||
mail: 'mail'
|
||||
|
||||
access_control:
|
||||
default_policy: deny
|
||||
|
|
|
@ -66,14 +66,15 @@ authentication_backend:
|
|||
1YjCJ36UpTsLre2f8nOSLtNmRfDPtbOE2mkOoO9dD9UU0XZwnvn9xw==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
base_dn: 'dc=example,dc=com'
|
||||
username_attribute: 'uid'
|
||||
additional_users_dn: 'ou=users'
|
||||
users_filter: '(&({username_attribute}={input})(objectCategory=person)(objectClass=user))'
|
||||
additional_groups_dn: 'ou=groups'
|
||||
groups_filter: '(&(member={dn})(objectClass=groupOfNames))'
|
||||
group_name_attribute: 'cn'
|
||||
mail_attribute: 'mail'
|
||||
user: 'cn=admin,dc=example,dc=com'
|
||||
attributes:
|
||||
username: 'uid'
|
||||
group_name: 'cn'
|
||||
mail: 'mail'
|
||||
|
||||
access_control:
|
||||
default_policy: 'deny'
|
||||
|
|
|
@ -18,14 +18,16 @@ authentication_backend:
|
|||
ldap:
|
||||
address: 'ldap://127.0.0.1'
|
||||
base_dn: 'dc=example,dc=com'
|
||||
username_attribute: 'uid'
|
||||
additional_users_dn: 'ou=users'
|
||||
users_filter: '(&({username_attribute}={input})(objectCategory=person)(objectClass=user))'
|
||||
additional_groups_dn: 'ou=groups'
|
||||
groups_filter: '(&(member={dn})(objectClass=groupOfNames))'
|
||||
group_name_attribute: 'cn'
|
||||
mail_attribute: 'mail'
|
||||
|
||||
user: 'cn=admin,dc=example,dc=com'
|
||||
attributes:
|
||||
mail: 'mail'
|
||||
group_name: 'cn'
|
||||
username: 'uid'
|
||||
|
||||
access_control:
|
||||
default_policy: 'deny'
|
||||
|
|
|
@ -19,14 +19,15 @@ authentication_backend:
|
|||
ldap:
|
||||
address: 'ldap://127.0.0.1'
|
||||
base_dn: 'dc=example,dc=com'
|
||||
username_attribute: 'uid'
|
||||
additional_users_dn: 'ou=users'
|
||||
users_filter: '(&({username_attribute}={input})(objectCategory=person)(objectClass=user))'
|
||||
additional_groups_dn: 'ou=groups'
|
||||
groups_filter: '(&(member={dn})(objectClass=groupOfNames))'
|
||||
group_name_attribute: 'cn'
|
||||
mail_attribute: 'mail'
|
||||
user: 'cn=admin,dc=example,dc=com'
|
||||
attributes:
|
||||
username: 'uid'
|
||||
group_name: 'cn'
|
||||
mail: 'mail'
|
||||
|
||||
access_control:
|
||||
default_policy: 'deny'
|
||||
|
|
|
@ -18,14 +18,15 @@ authentication_backend:
|
|||
ldap:
|
||||
address: 'ldap://127.0.0.1'
|
||||
base_dn: 'dc=example,dc=com'
|
||||
username_attribute: 'uid'
|
||||
additional_users_dn: 'ou=users'
|
||||
users_filter: '(&({username_attribute}={input})(objectCategory=person)(objectClass=user))'
|
||||
additional_groups_dn: 'ou=groups'
|
||||
groups_filter: '(&(member={dn})(objectClass=groupOfNames))'
|
||||
group_name_attribute: 'cn'
|
||||
mail_attribute: 'mail'
|
||||
user: 'cn=admin,dc=example,dc=com'
|
||||
attributes:
|
||||
username: 'uid'
|
||||
group_name: 'cn'
|
||||
mail: 'mail'
|
||||
|
||||
access_control:
|
||||
default_policy: 'deny'
|
||||
|
|
|
@ -18,13 +18,15 @@ authentication_backend:
|
|||
ldap:
|
||||
address: 'ldap://127.0.0.1'
|
||||
base_dn: 'dc=example,dc=com'
|
||||
username_attribute: 'uid'
|
||||
additional_users_dn: 'ou=users'
|
||||
users_filter: '(&({username_attribute}={input})(objectCategory=person)(objectClass=user))'
|
||||
additional_groups_dn: 'ou=groups'
|
||||
groups_filter: '(&(member={dn})(objectClass=groupOfNames))'
|
||||
group_name_attribute: 'cn'
|
||||
mail_attribute: 'mail'
|
||||
attributes:
|
||||
group_name: 'cn'
|
||||
mail: 'mail'
|
||||
username: 'uid'
|
||||
|
||||
user: 'cn=admin,dc=example,dc=com'
|
||||
|
||||
access_control:
|
||||
|
|
|
@ -18,14 +18,15 @@ authentication_backend:
|
|||
ldap:
|
||||
address: 'ldap://127.0.0.1'
|
||||
base_dn: 'dc=example,dc=com'
|
||||
username_attribute: 'uid'
|
||||
additional_users_dn: 'ou=users'
|
||||
users_filter: '(&({username_attribute}={input})(objectCategory=person)(objectClass=user))'
|
||||
additional_groups_dn: 'ou=groups'
|
||||
groups_filter: '(&(member={dn})(objectClass=groupOfNames))'
|
||||
group_name_attribute: 'cn'
|
||||
mail_attribute: 'mail'
|
||||
user: 'cn=admin,dc=example,dc=com'
|
||||
attributes:
|
||||
username: 'uid'
|
||||
group_name: 'cn'
|
||||
mail: 'mail'
|
||||
|
||||
access_control:
|
||||
default_policy: 'deny'
|
||||
|
|
|
@ -18,14 +18,15 @@ authentication_backend:
|
|||
ldap:
|
||||
address: 'ldap://127.0.0.1'
|
||||
base_dn: 'dc=example,dc=com'
|
||||
username_attribute: 'uid'
|
||||
additional_users_dn: 'ou=users'
|
||||
users_filter: '(&({username_attribute}={input})(objectCategory=person)(objectClass=user))'
|
||||
additional_groups_dn: 'ou=groups'
|
||||
groups_filter: '(&(member={dn})(objectClass=groupOfNames))'
|
||||
group_name_attribute: 'cn'
|
||||
mail_attribute: 'mail'
|
||||
user: 'cn=admin,dc=example,dc=com'
|
||||
attributes:
|
||||
username: 'uid'
|
||||
group_name: 'cn'
|
||||
mail: 'mail'
|
||||
|
||||
access_control:
|
||||
default_policy: 'deny'
|
||||
|
|
|
@ -18,14 +18,15 @@ authentication_backend:
|
|||
ldap:
|
||||
address: 'ldap://127.0.0.1'
|
||||
base_dn: 'dc=example,dc=com'
|
||||
username_attribute: 'uid'
|
||||
additional_users_dn: 'ou=users'
|
||||
users_filter: '(&({username_attribute}={input})(objectCategory=person)(objectClass=user))'
|
||||
additional_groups_dn: 'ou=groups'
|
||||
groups_filter: '(&(member={dn})(objectClass=groupOfNames))'
|
||||
group_name_attribute: 'cn'
|
||||
mail_attribute: 'mail'
|
||||
user: 'cn=admin,dc=example,dc=com'
|
||||
attributes:
|
||||
username: 'uid'
|
||||
group_name: 'cn'
|
||||
mail: 'mail'
|
||||
|
||||
access_control:
|
||||
default_policy: 'deny'
|
||||
|
|
|
@ -20,14 +20,16 @@ authentication_backend:
|
|||
ldap:
|
||||
address: 'ldap://127.0.0.1'
|
||||
base_dn: 'dc=example,dc=com'
|
||||
username_attribute: 'uid'
|
||||
additional_users_dn: 'ou=users'
|
||||
users_filter: '(&({username_attribute}={input})(objectCategory=person)(objectClass=user))'
|
||||
additional_groups_dn: 'ou=groups'
|
||||
groups_filter: '(&(member={dn})(objectClass=groupOfNames))'
|
||||
group_name_attribute: 'cn'
|
||||
mail_attribute: 'mail'
|
||||
user: 'cn=admin,dc=example,dc=com'
|
||||
attributes:
|
||||
username: 'uid'
|
||||
group_name: 'cn'
|
||||
mail: 'mail'
|
||||
|
||||
|
||||
access_control:
|
||||
default_policy: 'deny'
|
||||
|
|
|
@ -364,7 +364,7 @@ func validateLDAPAuthenticationBackendImplementation(config *schema.Authenticati
|
|||
case schema.LDAPImplementationGLAuth:
|
||||
implementation = &schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth
|
||||
default:
|
||||
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendImplementation, strJoinOr(validLDAPImplementations), config.LDAP.Implementation))
|
||||
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendOptionMustBeOneOf, "implementation", strJoinOr(validLDAPImplementations), config.LDAP.Implementation))
|
||||
}
|
||||
|
||||
tlsconfig := &schema.TLSConfig{}
|
||||
|
@ -394,32 +394,44 @@ func setDefaultImplementationLDAPAuthenticationBackendProfileAttributes(config *
|
|||
config.AdditionalUsersDN = implementation.AdditionalUsersDN
|
||||
}
|
||||
|
||||
if ldapImplementationShouldSetStr(config.AdditionalGroupsDN, implementation.AdditionalGroupsDN) {
|
||||
config.AdditionalGroupsDN = implementation.AdditionalGroupsDN
|
||||
}
|
||||
|
||||
if ldapImplementationShouldSetStr(config.UsersFilter, implementation.UsersFilter) {
|
||||
config.UsersFilter = implementation.UsersFilter
|
||||
}
|
||||
|
||||
if ldapImplementationShouldSetStr(config.UsernameAttribute, implementation.UsernameAttribute) {
|
||||
config.UsernameAttribute = implementation.UsernameAttribute
|
||||
}
|
||||
|
||||
if ldapImplementationShouldSetStr(config.DisplayNameAttribute, implementation.DisplayNameAttribute) {
|
||||
config.DisplayNameAttribute = implementation.DisplayNameAttribute
|
||||
}
|
||||
|
||||
if ldapImplementationShouldSetStr(config.MailAttribute, implementation.MailAttribute) {
|
||||
config.MailAttribute = implementation.MailAttribute
|
||||
if ldapImplementationShouldSetStr(config.AdditionalGroupsDN, implementation.AdditionalGroupsDN) {
|
||||
config.AdditionalGroupsDN = implementation.AdditionalGroupsDN
|
||||
}
|
||||
|
||||
if ldapImplementationShouldSetStr(config.GroupsFilter, implementation.GroupsFilter) {
|
||||
config.GroupsFilter = implementation.GroupsFilter
|
||||
}
|
||||
|
||||
if ldapImplementationShouldSetStr(config.GroupNameAttribute, implementation.GroupNameAttribute) {
|
||||
config.GroupNameAttribute = implementation.GroupNameAttribute
|
||||
if ldapImplementationShouldSetStr(config.GroupSearchMode, implementation.GroupSearchMode) {
|
||||
config.GroupSearchMode = implementation.GroupSearchMode
|
||||
}
|
||||
|
||||
if ldapImplementationShouldSetStr(config.Attributes.DistinguishedName, implementation.Attributes.DistinguishedName) {
|
||||
config.Attributes.DistinguishedName = implementation.Attributes.DistinguishedName
|
||||
}
|
||||
|
||||
if ldapImplementationShouldSetStr(config.Attributes.Username, implementation.Attributes.Username) {
|
||||
config.Attributes.Username = implementation.Attributes.Username
|
||||
}
|
||||
|
||||
if ldapImplementationShouldSetStr(config.Attributes.DisplayName, implementation.Attributes.DisplayName) {
|
||||
config.Attributes.DisplayName = implementation.Attributes.DisplayName
|
||||
}
|
||||
|
||||
if ldapImplementationShouldSetStr(config.Attributes.Mail, implementation.Attributes.Mail) {
|
||||
config.Attributes.Mail = implementation.Attributes.Mail
|
||||
}
|
||||
|
||||
if ldapImplementationShouldSetStr(config.Attributes.MemberOf, implementation.Attributes.MemberOf) {
|
||||
config.Attributes.MemberOf = implementation.Attributes.MemberOf
|
||||
}
|
||||
|
||||
if ldapImplementationShouldSetStr(config.Attributes.GroupName, implementation.Attributes.GroupName) {
|
||||
config.Attributes.GroupName = implementation.Attributes.GroupName
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -486,4 +498,32 @@ func validateLDAPRequiredParameters(config *schema.AuthenticationBackend, valida
|
|||
} else if !strings.HasPrefix(config.LDAP.GroupsFilter, "(") || !strings.HasSuffix(config.LDAP.GroupsFilter, ")") {
|
||||
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendFilterEnclosingParenthesis, "groups_filter", config.LDAP.GroupsFilter, config.LDAP.GroupsFilter))
|
||||
}
|
||||
|
||||
validateLDAPGroupFilter(config, validator)
|
||||
}
|
||||
|
||||
func validateLDAPGroupFilter(config *schema.AuthenticationBackend, validator *schema.StructValidator) {
|
||||
if config.LDAP.GroupSearchMode == "" {
|
||||
config.LDAP.GroupSearchMode = schema.LDAPGroupSearchModeFilter
|
||||
}
|
||||
|
||||
if !utils.IsStringInSlice(config.LDAP.GroupSearchMode, validLDAPGroupSearchModes) {
|
||||
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendOptionMustBeOneOf, "group_search_mode", strJoinOr(validLDAPGroupSearchModes), config.LDAP.GroupSearchMode))
|
||||
}
|
||||
|
||||
pMemberOfDN, pMemberOfRDN := strings.Contains(config.LDAP.GroupsFilter, "{memberof:dn}"), strings.Contains(config.LDAP.GroupsFilter, "{memberof:rdn}")
|
||||
|
||||
if config.LDAP.GroupSearchMode == schema.LDAPGroupSearchModeMemberOf {
|
||||
if !pMemberOfDN && !pMemberOfRDN {
|
||||
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendFilterMissingPlaceholderGroupSearchMode, "groups_filter", strJoinOr([]string{"{memberof:rdn}", "{memberof:dn}"}), config.LDAP.GroupSearchMode))
|
||||
}
|
||||
}
|
||||
|
||||
if pMemberOfDN && config.LDAP.Attributes.DistinguishedName == "" {
|
||||
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendFilterMissingAttribute, "distinguished_name", strJoinOr([]string{"{memberof:dn}"})))
|
||||
}
|
||||
|
||||
if (pMemberOfDN || pMemberOfRDN) && config.LDAP.Attributes.MemberOf == "" {
|
||||
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendFilterMissingAttribute, "member_of", strJoinOr([]string{"{memberof:rdn}", "{memberof:dn}"})))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -577,7 +577,7 @@ func (suite *LDAPAuthenticationBackendSuite) SetupTest() {
|
|||
suite.config.LDAP.User = testLDAPUser
|
||||
suite.config.LDAP.Password = testLDAPPassword
|
||||
suite.config.LDAP.BaseDN = testLDAPBaseDN
|
||||
suite.config.LDAP.UsernameAttribute = "uid"
|
||||
suite.config.LDAP.Attributes.Username = "uid"
|
||||
suite.config.LDAP.UsersFilter = "({username_attribute}={input})"
|
||||
suite.config.LDAP.GroupsFilter = "(cn={input})"
|
||||
}
|
||||
|
@ -591,12 +591,12 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldValidateCompleteConfigura
|
|||
|
||||
func (suite *LDAPAuthenticationBackendSuite) TestShouldValidateDefaultImplementationAndUsernameAttribute() {
|
||||
suite.config.LDAP.Implementation = ""
|
||||
suite.config.LDAP.UsernameAttribute = ""
|
||||
suite.config.LDAP.Attributes.Username = ""
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Equal(schema.LDAPImplementationCustom, suite.config.LDAP.Implementation)
|
||||
|
||||
suite.Equal(suite.config.LDAP.UsernameAttribute, schema.DefaultLDAPAuthenticationBackendConfigurationImplementationCustom.UsernameAttribute)
|
||||
suite.Equal(suite.config.LDAP.Attributes.Username, schema.DefaultLDAPAuthenticationBackendConfigurationImplementationCustom.Attributes.Username)
|
||||
suite.Len(suite.validator.Warnings(), 0)
|
||||
suite.Len(suite.validator.Errors(), 0)
|
||||
}
|
||||
|
@ -743,7 +743,7 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseOnEmptyUsersFilter()
|
|||
}
|
||||
|
||||
func (suite *LDAPAuthenticationBackendSuite) TestShouldNotRaiseOnEmptyUsernameAttribute() {
|
||||
suite.config.LDAP.UsernameAttribute = ""
|
||||
suite.config.LDAP.Attributes.Username = ""
|
||||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
|
@ -793,7 +793,7 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultGroupNameAttrib
|
|||
suite.Len(suite.validator.Warnings(), 0)
|
||||
suite.Len(suite.validator.Errors(), 0)
|
||||
|
||||
suite.Equal("cn", suite.config.LDAP.GroupNameAttribute)
|
||||
suite.Equal("cn", suite.config.LDAP.Attributes.GroupName)
|
||||
}
|
||||
|
||||
func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultMailAttribute() {
|
||||
|
@ -802,7 +802,7 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultMailAttribute()
|
|||
suite.Len(suite.validator.Warnings(), 0)
|
||||
suite.Len(suite.validator.Errors(), 0)
|
||||
|
||||
suite.Equal("mail", suite.config.LDAP.MailAttribute)
|
||||
suite.Equal("mail", suite.config.LDAP.Attributes.Mail)
|
||||
}
|
||||
|
||||
func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultDisplayNameAttribute() {
|
||||
|
@ -811,7 +811,7 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultDisplayNameAttr
|
|||
suite.Len(suite.validator.Warnings(), 0)
|
||||
suite.Len(suite.validator.Errors(), 0)
|
||||
|
||||
suite.Equal("displayName", suite.config.LDAP.DisplayNameAttribute)
|
||||
suite.Equal("displayName", suite.config.LDAP.Attributes.DisplayName)
|
||||
}
|
||||
|
||||
func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultRefreshInterval() {
|
||||
|
@ -890,6 +890,64 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldNotAllowSSL30() {
|
|||
suite.EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: tls: option 'minimum_version' is invalid: minimum version is TLS1.0 but SSL3.0 was configured")
|
||||
}
|
||||
|
||||
func (suite *LDAPAuthenticationBackendSuite) TestShouldErrorOnBadSearchMode() {
|
||||
suite.config.LDAP.GroupSearchMode = "memberOF"
|
||||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: option 'group_search_mode' must be one of 'filter' or 'memberof' but it's configured as 'memberOF'")
|
||||
}
|
||||
|
||||
func (suite *LDAPAuthenticationBackendSuite) TestShouldNoErrorOnPlaceholderSearchMode() {
|
||||
suite.config.LDAP.GroupSearchMode = memberof
|
||||
suite.config.LDAP.GroupsFilter = filterMemberOfRDN
|
||||
suite.config.LDAP.Attributes.MemberOf = memberOf
|
||||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Len(suite.validator.Warnings(), 0)
|
||||
suite.Len(suite.validator.Errors(), 0)
|
||||
}
|
||||
|
||||
func (suite *LDAPAuthenticationBackendSuite) TestShouldErrorOnMissingPlaceholderSearchMode() {
|
||||
suite.config.LDAP.GroupSearchMode = memberof
|
||||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: option 'groups_filter' must contain one of the '{memberof:rdn}' or '{memberof:dn}' placeholders when using a group_search_mode of 'memberof' but they're absent")
|
||||
}
|
||||
|
||||
func (suite *LDAPAuthenticationBackendSuite) TestShouldErrorOnMissingDistinguishedNameDN() {
|
||||
suite.config.LDAP.Attributes.DistinguishedName = ""
|
||||
suite.config.LDAP.GroupsFilter = "(|({memberof:dn}))"
|
||||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 2)
|
||||
|
||||
suite.EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: attributes: option 'distinguished_name' must be provided when using the '{memberof:dn}' placeholder but it's absent")
|
||||
suite.EqualError(suite.validator.Errors()[1], "authentication_backend: ldap: attributes: option 'member_of' must be provided when using the '{memberof:rdn}' or '{memberof:dn}' placeholder but it's absent")
|
||||
}
|
||||
|
||||
func (suite *LDAPAuthenticationBackendSuite) TestShouldErrorOnMissingMemberOfRDN() {
|
||||
suite.config.LDAP.Attributes.DistinguishedName = ""
|
||||
suite.config.LDAP.GroupsFilter = filterMemberOfRDN
|
||||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: attributes: option 'member_of' must be provided when using the '{memberof:rdn}' or '{memberof:dn}' placeholder but it's absent")
|
||||
}
|
||||
|
||||
func (suite *LDAPAuthenticationBackendSuite) TestShouldNotAllowTLSVerMinGreaterThanVerMax() {
|
||||
suite.config.LDAP.TLS = &schema.TLSConfig{
|
||||
MinimumVersion: schema.TLSVersion{Value: tls.VersionTLS13},
|
||||
|
@ -951,32 +1009,39 @@ func (suite *ActiveDirectoryAuthenticationBackendSuite) TestShouldSetActiveDirec
|
|||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.UsersFilter,
|
||||
suite.config.LDAP.UsersFilter)
|
||||
suite.Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.UsernameAttribute,
|
||||
suite.config.LDAP.UsernameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.Attributes.Username,
|
||||
suite.config.LDAP.Attributes.Username)
|
||||
suite.Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.DisplayNameAttribute,
|
||||
suite.config.LDAP.DisplayNameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.Attributes.DisplayName,
|
||||
suite.config.LDAP.Attributes.DisplayName)
|
||||
suite.Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.MailAttribute,
|
||||
suite.config.LDAP.MailAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.Attributes.Mail,
|
||||
suite.config.LDAP.Attributes.Mail)
|
||||
suite.Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.GroupsFilter,
|
||||
suite.config.LDAP.GroupsFilter)
|
||||
suite.Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.GroupNameAttribute,
|
||||
suite.config.LDAP.GroupNameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.Attributes.GroupName,
|
||||
suite.config.LDAP.Attributes.GroupName)
|
||||
|
||||
suite.Equal(memberOf, suite.config.LDAP.Attributes.MemberOf)
|
||||
suite.Equal("distinguishedName", suite.config.LDAP.Attributes.DistinguishedName)
|
||||
suite.Equal(schema.LDAPGroupSearchModeFilter, suite.config.LDAP.GroupSearchMode)
|
||||
}
|
||||
|
||||
func (suite *ActiveDirectoryAuthenticationBackendSuite) TestShouldOnlySetDefaultsIfNotManuallyConfigured() {
|
||||
suite.config.LDAP.Timeout = time.Second * 2
|
||||
suite.config.LDAP.UsersFilter = "(&({username_attribute}={input})(objectCategory=person)(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=2))"
|
||||
suite.config.LDAP.UsernameAttribute = "cn"
|
||||
suite.config.LDAP.MailAttribute = "userPrincipalName"
|
||||
suite.config.LDAP.DisplayNameAttribute = "name"
|
||||
suite.config.LDAP.Attributes.Username = "cn"
|
||||
suite.config.LDAP.Attributes.Mail = "userPrincipalName"
|
||||
suite.config.LDAP.Attributes.DisplayName = "name"
|
||||
suite.config.LDAP.GroupsFilter = "(&(member={dn})(objectClass=group)(objectCategory=group))"
|
||||
suite.config.LDAP.GroupNameAttribute = "distinguishedName"
|
||||
suite.config.LDAP.Attributes.GroupName = "distinguishedName"
|
||||
suite.config.LDAP.AdditionalUsersDN = "OU=test"
|
||||
suite.config.LDAP.AdditionalGroupsDN = "OU=grps"
|
||||
suite.config.LDAP.Attributes.MemberOf = member
|
||||
suite.config.LDAP.GroupSearchMode = memberof
|
||||
suite.config.LDAP.Attributes.DistinguishedName = "objectGUID"
|
||||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
|
@ -993,20 +1058,24 @@ func (suite *ActiveDirectoryAuthenticationBackendSuite) TestShouldOnlySetDefault
|
|||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.UsersFilter,
|
||||
suite.config.LDAP.UsersFilter)
|
||||
suite.NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.UsernameAttribute,
|
||||
suite.config.LDAP.UsernameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.Attributes.Username,
|
||||
suite.config.LDAP.Attributes.Username)
|
||||
suite.NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.DisplayNameAttribute,
|
||||
suite.config.LDAP.DisplayNameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.Attributes.DisplayName,
|
||||
suite.config.LDAP.Attributes.DisplayName)
|
||||
suite.NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.MailAttribute,
|
||||
suite.config.LDAP.MailAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.Attributes.Mail,
|
||||
suite.config.LDAP.Attributes.Mail)
|
||||
suite.NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.GroupsFilter,
|
||||
suite.config.LDAP.GroupsFilter)
|
||||
suite.NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.GroupNameAttribute,
|
||||
suite.config.LDAP.GroupNameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.Attributes.GroupName,
|
||||
suite.config.LDAP.Attributes.GroupName)
|
||||
|
||||
suite.Equal(member, suite.config.LDAP.Attributes.MemberOf)
|
||||
suite.Equal("objectGUID", suite.config.LDAP.Attributes.DistinguishedName)
|
||||
suite.Equal(memberof, suite.config.LDAP.GroupSearchMode)
|
||||
}
|
||||
|
||||
func (suite *ActiveDirectoryAuthenticationBackendSuite) TestShouldRaiseErrorOnInvalidURLWithHTTP() {
|
||||
|
@ -1059,32 +1128,38 @@ func (suite *RFC2307bisAuthenticationBackendSuite) TestShouldSetDefaults() {
|
|||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationRFC2307bis.UsersFilter,
|
||||
suite.config.LDAP.UsersFilter)
|
||||
suite.Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationRFC2307bis.UsernameAttribute,
|
||||
suite.config.LDAP.UsernameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationRFC2307bis.Attributes.Username,
|
||||
suite.config.LDAP.Attributes.Username)
|
||||
suite.Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationRFC2307bis.DisplayNameAttribute,
|
||||
suite.config.LDAP.DisplayNameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationRFC2307bis.Attributes.DisplayName,
|
||||
suite.config.LDAP.Attributes.DisplayName)
|
||||
suite.Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationRFC2307bis.MailAttribute,
|
||||
suite.config.LDAP.MailAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationRFC2307bis.Attributes.Mail,
|
||||
suite.config.LDAP.Attributes.Mail)
|
||||
suite.Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationRFC2307bis.GroupsFilter,
|
||||
suite.config.LDAP.GroupsFilter)
|
||||
suite.Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationRFC2307bis.GroupNameAttribute,
|
||||
suite.config.LDAP.GroupNameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationRFC2307bis.Attributes.GroupName,
|
||||
suite.config.LDAP.Attributes.GroupName)
|
||||
|
||||
suite.Equal(memberOf, suite.config.LDAP.Attributes.MemberOf)
|
||||
suite.Equal("", suite.config.LDAP.Attributes.DistinguishedName)
|
||||
suite.Equal(schema.LDAPGroupSearchModeFilter, suite.config.LDAP.GroupSearchMode)
|
||||
}
|
||||
|
||||
func (suite *RFC2307bisAuthenticationBackendSuite) TestShouldOnlySetDefaultsIfNotManuallyConfigured() {
|
||||
suite.config.LDAP.Timeout = time.Second * 2
|
||||
suite.config.LDAP.UsersFilter = "(&({username_attribute}={input})(objectClass=Person))"
|
||||
suite.config.LDAP.UsernameAttribute = "o"
|
||||
suite.config.LDAP.MailAttribute = "Email"
|
||||
suite.config.LDAP.DisplayNameAttribute = "Given"
|
||||
suite.config.LDAP.Attributes.Username = "o"
|
||||
suite.config.LDAP.Attributes.Mail = "Email"
|
||||
suite.config.LDAP.Attributes.DisplayName = "Given"
|
||||
suite.config.LDAP.GroupsFilter = "(&(member={dn})(objectClass=posixGroup)(objectClass=top))"
|
||||
suite.config.LDAP.GroupNameAttribute = "gid"
|
||||
suite.config.LDAP.Attributes.GroupName = "gid"
|
||||
suite.config.LDAP.Attributes.MemberOf = member
|
||||
suite.config.LDAP.AdditionalUsersDN = "OU=users,OU=OpenLDAP"
|
||||
suite.config.LDAP.AdditionalGroupsDN = "OU=groups,OU=OpenLDAP"
|
||||
suite.config.LDAP.GroupSearchMode = memberof
|
||||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
|
@ -1104,20 +1179,24 @@ func (suite *RFC2307bisAuthenticationBackendSuite) TestShouldOnlySetDefaultsIfNo
|
|||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationRFC2307bis.UsersFilter,
|
||||
suite.config.LDAP.UsersFilter)
|
||||
suite.NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationRFC2307bis.UsernameAttribute,
|
||||
suite.config.LDAP.UsernameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationRFC2307bis.Attributes.Username,
|
||||
suite.config.LDAP.Attributes.Username)
|
||||
suite.NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationRFC2307bis.DisplayNameAttribute,
|
||||
suite.config.LDAP.DisplayNameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationRFC2307bis.Attributes.DisplayName,
|
||||
suite.config.LDAP.Attributes.DisplayName)
|
||||
suite.NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationRFC2307bis.MailAttribute,
|
||||
suite.config.LDAP.MailAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationRFC2307bis.Attributes.Mail,
|
||||
suite.config.LDAP.Attributes.Mail)
|
||||
suite.NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationRFC2307bis.GroupsFilter,
|
||||
suite.config.LDAP.GroupsFilter)
|
||||
suite.NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationRFC2307bis.GroupNameAttribute,
|
||||
suite.config.LDAP.GroupNameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationRFC2307bis.Attributes.GroupName,
|
||||
suite.config.LDAP.Attributes.GroupName)
|
||||
|
||||
suite.Equal(member, suite.config.LDAP.Attributes.MemberOf)
|
||||
suite.Equal("", suite.config.LDAP.Attributes.DistinguishedName)
|
||||
suite.Equal(memberof, suite.config.LDAP.GroupSearchMode)
|
||||
}
|
||||
|
||||
func TestRFC2307bisAuthenticationBackend(t *testing.T) {
|
||||
|
@ -1161,30 +1240,35 @@ func (suite *FreeIPAAuthenticationBackendSuite) TestShouldSetDefaults() {
|
|||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA.UsersFilter,
|
||||
suite.config.LDAP.UsersFilter)
|
||||
suite.Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA.UsernameAttribute,
|
||||
suite.config.LDAP.UsernameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA.Attributes.Username,
|
||||
suite.config.LDAP.Attributes.Username)
|
||||
suite.Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA.DisplayNameAttribute,
|
||||
suite.config.LDAP.DisplayNameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA.Attributes.DisplayName,
|
||||
suite.config.LDAP.Attributes.DisplayName)
|
||||
suite.Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA.MailAttribute,
|
||||
suite.config.LDAP.MailAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA.Attributes.Mail,
|
||||
suite.config.LDAP.Attributes.Mail)
|
||||
suite.Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA.GroupsFilter,
|
||||
suite.config.LDAP.GroupsFilter)
|
||||
suite.Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA.GroupNameAttribute,
|
||||
suite.config.LDAP.GroupNameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA.Attributes.GroupName,
|
||||
suite.config.LDAP.Attributes.GroupName)
|
||||
|
||||
suite.Equal(memberOf, suite.config.LDAP.Attributes.MemberOf)
|
||||
suite.Equal("", suite.config.LDAP.Attributes.DistinguishedName)
|
||||
suite.Equal(schema.LDAPGroupSearchModeFilter, suite.config.LDAP.GroupSearchMode)
|
||||
}
|
||||
|
||||
func (suite *FreeIPAAuthenticationBackendSuite) TestShouldOnlySetDefaultsIfNotManuallyConfigured() {
|
||||
suite.config.LDAP.Timeout = time.Second * 2
|
||||
suite.config.LDAP.UsersFilter = "(&({username_attribute}={input})(objectClass=person)(!(nsAccountLock=TRUE)))"
|
||||
suite.config.LDAP.UsernameAttribute = "dn"
|
||||
suite.config.LDAP.MailAttribute = "email"
|
||||
suite.config.LDAP.DisplayNameAttribute = "gecos"
|
||||
suite.config.LDAP.Attributes.Username = "dn"
|
||||
suite.config.LDAP.Attributes.Mail = "email"
|
||||
suite.config.LDAP.Attributes.DisplayName = "gecos"
|
||||
suite.config.LDAP.GroupsFilter = "(&(member={dn})(objectClass=posixgroup))"
|
||||
suite.config.LDAP.GroupNameAttribute = "groupName"
|
||||
suite.config.LDAP.Attributes.GroupName = "groupName"
|
||||
suite.config.LDAP.Attributes.MemberOf = member
|
||||
suite.config.LDAP.AdditionalUsersDN = "OU=people"
|
||||
suite.config.LDAP.AdditionalGroupsDN = "OU=grp"
|
||||
|
||||
|
@ -1203,20 +1287,24 @@ func (suite *FreeIPAAuthenticationBackendSuite) TestShouldOnlySetDefaultsIfNotMa
|
|||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA.UsersFilter,
|
||||
suite.config.LDAP.UsersFilter)
|
||||
suite.NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA.UsernameAttribute,
|
||||
suite.config.LDAP.UsernameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA.Attributes.Username,
|
||||
suite.config.LDAP.Attributes.Username)
|
||||
suite.NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA.DisplayNameAttribute,
|
||||
suite.config.LDAP.DisplayNameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA.Attributes.DisplayName,
|
||||
suite.config.LDAP.Attributes.DisplayName)
|
||||
suite.NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA.MailAttribute,
|
||||
suite.config.LDAP.MailAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA.Attributes.Mail,
|
||||
suite.config.LDAP.Attributes.Mail)
|
||||
suite.NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA.GroupsFilter,
|
||||
suite.config.LDAP.GroupsFilter)
|
||||
suite.NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA.GroupNameAttribute,
|
||||
suite.config.LDAP.GroupNameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA.Attributes.GroupName,
|
||||
suite.config.LDAP.Attributes.GroupName)
|
||||
|
||||
suite.Equal(member, suite.config.LDAP.Attributes.MemberOf)
|
||||
suite.Equal("", suite.config.LDAP.Attributes.DistinguishedName)
|
||||
suite.Equal(schema.LDAPGroupSearchModeFilter, suite.config.LDAP.GroupSearchMode)
|
||||
}
|
||||
|
||||
func TestFreeIPAAuthenticationBackend(t *testing.T) {
|
||||
|
@ -1260,30 +1348,34 @@ func (suite *LLDAPAuthenticationBackendSuite) TestShouldSetDefaults() {
|
|||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.UsersFilter,
|
||||
suite.config.LDAP.UsersFilter)
|
||||
suite.Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.UsernameAttribute,
|
||||
suite.config.LDAP.UsernameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.Attributes.Username,
|
||||
suite.config.LDAP.Attributes.Username)
|
||||
suite.Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.DisplayNameAttribute,
|
||||
suite.config.LDAP.DisplayNameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.Attributes.DisplayName,
|
||||
suite.config.LDAP.Attributes.DisplayName)
|
||||
suite.Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.MailAttribute,
|
||||
suite.config.LDAP.MailAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.Attributes.Mail,
|
||||
suite.config.LDAP.Attributes.Mail)
|
||||
suite.Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.GroupsFilter,
|
||||
suite.config.LDAP.GroupsFilter)
|
||||
suite.Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.GroupNameAttribute,
|
||||
suite.config.LDAP.GroupNameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.Attributes.GroupName,
|
||||
suite.config.LDAP.Attributes.GroupName)
|
||||
|
||||
suite.Equal("", suite.config.LDAP.Attributes.MemberOf)
|
||||
suite.Equal("", suite.config.LDAP.Attributes.DistinguishedName)
|
||||
suite.Equal(schema.LDAPGroupSearchModeFilter, suite.config.LDAP.GroupSearchMode)
|
||||
}
|
||||
|
||||
func (suite *LLDAPAuthenticationBackendSuite) TestShouldOnlySetDefaultsIfNotManuallyConfigured() {
|
||||
suite.config.LDAP.Timeout = time.Second * 2
|
||||
suite.config.LDAP.UsersFilter = "(&({username_attribute}={input})(objectClass=Person)(!(nsAccountLock=TRUE)))"
|
||||
suite.config.LDAP.UsernameAttribute = "username"
|
||||
suite.config.LDAP.MailAttribute = "m"
|
||||
suite.config.LDAP.DisplayNameAttribute = "fn"
|
||||
suite.config.LDAP.Attributes.Username = "username"
|
||||
suite.config.LDAP.Attributes.Mail = "m"
|
||||
suite.config.LDAP.Attributes.DisplayName = "fn"
|
||||
suite.config.LDAP.GroupsFilter = "(&(member={dn})(!(objectClass=posixGroup)))"
|
||||
suite.config.LDAP.GroupNameAttribute = "grpz"
|
||||
suite.config.LDAP.Attributes.GroupName = "grpz"
|
||||
suite.config.LDAP.AdditionalUsersDN = "OU=no"
|
||||
suite.config.LDAP.AdditionalGroupsDN = "OU=yes"
|
||||
|
||||
|
@ -1305,20 +1397,24 @@ func (suite *LLDAPAuthenticationBackendSuite) TestShouldOnlySetDefaultsIfNotManu
|
|||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.UsersFilter,
|
||||
suite.config.LDAP.UsersFilter)
|
||||
suite.NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.UsernameAttribute,
|
||||
suite.config.LDAP.UsernameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.Attributes.Username,
|
||||
suite.config.LDAP.Attributes.Username)
|
||||
suite.NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.DisplayNameAttribute,
|
||||
suite.config.LDAP.DisplayNameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.Attributes.DisplayName,
|
||||
suite.config.LDAP.Attributes.DisplayName)
|
||||
suite.NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.MailAttribute,
|
||||
suite.config.LDAP.MailAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.Attributes.Mail,
|
||||
suite.config.LDAP.Attributes.Mail)
|
||||
suite.NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.GroupsFilter,
|
||||
suite.config.LDAP.GroupsFilter)
|
||||
suite.NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.GroupNameAttribute,
|
||||
suite.config.LDAP.GroupNameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.Attributes.GroupName,
|
||||
suite.config.LDAP.Attributes.GroupName)
|
||||
|
||||
suite.Equal("", suite.config.LDAP.Attributes.MemberOf)
|
||||
suite.Equal("", suite.config.LDAP.Attributes.DistinguishedName)
|
||||
suite.Equal(schema.LDAPGroupSearchModeFilter, suite.config.LDAP.GroupSearchMode)
|
||||
}
|
||||
|
||||
func TestLLDAPAuthenticationBackend(t *testing.T) {
|
||||
|
@ -1362,30 +1458,34 @@ func (suite *GLAuthAuthenticationBackendSuite) TestShouldSetDefaults() {
|
|||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.UsersFilter,
|
||||
suite.config.LDAP.UsersFilter)
|
||||
suite.Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.UsernameAttribute,
|
||||
suite.config.LDAP.UsernameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.Attributes.Username,
|
||||
suite.config.LDAP.Attributes.Username)
|
||||
suite.Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.DisplayNameAttribute,
|
||||
suite.config.LDAP.DisplayNameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.Attributes.DisplayName,
|
||||
suite.config.LDAP.Attributes.DisplayName)
|
||||
suite.Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.MailAttribute,
|
||||
suite.config.LDAP.MailAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.Attributes.Mail,
|
||||
suite.config.LDAP.Attributes.Mail)
|
||||
suite.Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.GroupsFilter,
|
||||
suite.config.LDAP.GroupsFilter)
|
||||
suite.Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.GroupNameAttribute,
|
||||
suite.config.LDAP.GroupNameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.Attributes.GroupName,
|
||||
suite.config.LDAP.Attributes.GroupName)
|
||||
|
||||
suite.Equal("", suite.config.LDAP.Attributes.MemberOf)
|
||||
suite.Equal("", suite.config.LDAP.Attributes.DistinguishedName)
|
||||
suite.Equal(schema.LDAPGroupSearchModeFilter, suite.config.LDAP.GroupSearchMode)
|
||||
}
|
||||
|
||||
func (suite *GLAuthAuthenticationBackendSuite) TestShouldOnlySetDefaultsIfNotManuallyConfigured() {
|
||||
suite.config.LDAP.Timeout = time.Second * 2
|
||||
suite.config.LDAP.UsersFilter = "(&({username_attribute}={input})(objectClass=Person)(!(accountStatus=inactive)))"
|
||||
suite.config.LDAP.UsernameAttribute = "description"
|
||||
suite.config.LDAP.MailAttribute = "sender"
|
||||
suite.config.LDAP.DisplayNameAttribute = "given"
|
||||
suite.config.LDAP.Attributes.Username = "description"
|
||||
suite.config.LDAP.Attributes.Mail = "sender"
|
||||
suite.config.LDAP.Attributes.DisplayName = "given"
|
||||
suite.config.LDAP.GroupsFilter = "(&(member={dn})(objectClass=posixGroup))"
|
||||
suite.config.LDAP.GroupNameAttribute = "grp"
|
||||
suite.config.LDAP.Attributes.GroupName = "grp"
|
||||
suite.config.LDAP.AdditionalUsersDN = "OU=users,OU=GlAuth"
|
||||
suite.config.LDAP.AdditionalGroupsDN = "OU=groups,OU=GLAuth"
|
||||
|
||||
|
@ -1407,20 +1507,24 @@ func (suite *GLAuthAuthenticationBackendSuite) TestShouldOnlySetDefaultsIfNotMan
|
|||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.UsersFilter,
|
||||
suite.config.LDAP.UsersFilter)
|
||||
suite.NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.UsernameAttribute,
|
||||
suite.config.LDAP.UsernameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.Attributes.Username,
|
||||
suite.config.LDAP.Attributes.Username)
|
||||
suite.NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.DisplayNameAttribute,
|
||||
suite.config.LDAP.DisplayNameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.Attributes.DisplayName,
|
||||
suite.config.LDAP.Attributes.DisplayName)
|
||||
suite.NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.MailAttribute,
|
||||
suite.config.LDAP.MailAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.Attributes.Mail,
|
||||
suite.config.LDAP.Attributes.Mail)
|
||||
suite.NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.GroupsFilter,
|
||||
suite.config.LDAP.GroupsFilter)
|
||||
suite.NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.GroupNameAttribute,
|
||||
suite.config.LDAP.GroupNameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.Attributes.GroupName,
|
||||
suite.config.LDAP.Attributes.GroupName)
|
||||
|
||||
suite.Equal("", suite.config.LDAP.Attributes.MemberOf)
|
||||
suite.Equal("", suite.config.LDAP.Attributes.DistinguishedName)
|
||||
suite.Equal(schema.LDAPGroupSearchModeFilter, suite.config.LDAP.GroupSearchMode)
|
||||
}
|
||||
|
||||
func TestGLAuthAuthenticationBackend(t *testing.T) {
|
||||
|
|
|
@ -98,9 +98,9 @@ const (
|
|||
errFmtLDAPAuthBackendUnauthenticatedBindWithPassword = "authentication_backend: ldap: option 'permit_unauthenticated_bind' can't be enabled when a password is specified"
|
||||
errFmtLDAPAuthBackendUnauthenticatedBindWithResetEnabled = "authentication_backend: ldap: option 'permit_unauthenticated_bind' can't be enabled when password reset is enabled"
|
||||
|
||||
errFmtLDAPAuthBackendMissingOption = "authentication_backend: ldap: option '%s' is required"
|
||||
errFmtLDAPAuthBackendTLSConfigInvalid = "authentication_backend: ldap: tls: %w"
|
||||
errFmtLDAPAuthBackendImplementation = "authentication_backend: ldap: option 'implementation' " +
|
||||
errFmtLDAPAuthBackendMissingOption = "authentication_backend: ldap: option '%s' is required"
|
||||
errFmtLDAPAuthBackendTLSConfigInvalid = "authentication_backend: ldap: tls: %w"
|
||||
errFmtLDAPAuthBackendOptionMustBeOneOf = "authentication_backend: ldap: option '%s' " +
|
||||
errSuffixMustBeOneOf
|
||||
errFmtLDAPAuthBackendFilterReplacedPlaceholders = "authentication_backend: ldap: option " +
|
||||
"'%s' has an invalid placeholder: '%s' has been removed, please use '%s' instead"
|
||||
|
@ -109,6 +109,10 @@ const (
|
|||
"'%s' must contain enclosing parenthesis: '%s' should probably be '(%s)'"
|
||||
errFmtLDAPAuthBackendFilterMissingPlaceholder = "authentication_backend: ldap: option " +
|
||||
"'%s' must contain the placeholder '{%s}' but it's absent"
|
||||
errFmtLDAPAuthBackendFilterMissingPlaceholderGroupSearchMode = "authentication_backend: ldap: option " +
|
||||
"'%s' must contain one of the %s placeholders when using a group_search_mode of '%s' but they're absent"
|
||||
errFmtLDAPAuthBackendFilterMissingAttribute = "authentication_backend: ldap: attributes: option " +
|
||||
"'%s' must be provided when using the %s placeholder but it's absent"
|
||||
)
|
||||
|
||||
// TOTP Error constants.
|
||||
|
@ -371,17 +375,6 @@ const (
|
|||
operatorNotPattern = "not pattern"
|
||||
)
|
||||
|
||||
var (
|
||||
validLDAPImplementations = []string{
|
||||
schema.LDAPImplementationCustom,
|
||||
schema.LDAPImplementationActiveDirectory,
|
||||
schema.LDAPImplementationRFC2307bis,
|
||||
schema.LDAPImplementationFreeIPA,
|
||||
schema.LDAPImplementationLLDAP,
|
||||
schema.LDAPImplementationGLAuth,
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
legacy = "legacy"
|
||||
authzImplementationLegacy = "Legacy"
|
||||
|
@ -397,6 +390,22 @@ var (
|
|||
validAuthzAuthnStrategies = []string{"CookieSession", "HeaderAuthorization", "HeaderProxyAuthorization", "HeaderAuthRequestProxyAuthorization", "HeaderLegacy"}
|
||||
)
|
||||
|
||||
var (
|
||||
validLDAPImplementations = []string{
|
||||
schema.LDAPImplementationCustom,
|
||||
schema.LDAPImplementationActiveDirectory,
|
||||
schema.LDAPImplementationRFC2307bis,
|
||||
schema.LDAPImplementationFreeIPA,
|
||||
schema.LDAPImplementationLLDAP,
|
||||
schema.LDAPImplementationGLAuth,
|
||||
}
|
||||
|
||||
validLDAPGroupSearchModes = []string{
|
||||
schema.LDAPGroupSearchModeFilter,
|
||||
schema.LDAPGroupSearchModeMemberOf,
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
validArgon2Variants = []string{"argon2id", "id", "argon2i", "i", "argon2d", "d"}
|
||||
validSHA2CryptVariants = []string{digestSHA256, digestSHA512}
|
||||
|
|
|
@ -13,6 +13,11 @@ const (
|
|||
testLDAPURL = "ldap://ldap"
|
||||
testLDAPUser = "user"
|
||||
testEncryptionKey = "a_not_so_secure_encryption_key"
|
||||
|
||||
member = "member"
|
||||
memberof = "memberof"
|
||||
memberOf = "memberOf"
|
||||
filterMemberOfRDN = "(|({memberof:rdn}))"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -21,16 +21,19 @@ authentication_backend:
|
|||
ldap:
|
||||
address: 'ldap://openldap'
|
||||
base_dn: dc=example,dc=com
|
||||
username_attribute: uid
|
||||
additional_users_dn: ou=users
|
||||
users_filter: (&({username_attribute}={input})(objectClass=person))
|
||||
additional_groups_dn: ou=groups
|
||||
groups_filter: (&(member={dn})(objectClass=groupOfNames))
|
||||
group_name_attribute: cn
|
||||
mail_attribute: mail
|
||||
display_name_attribute: displayName
|
||||
user: cn=admin,dc=example,dc=com
|
||||
password: password
|
||||
attributes:
|
||||
distinguished_name: 'distinguishedName'
|
||||
username: 'uid'
|
||||
display_name: 'displayName'
|
||||
mail: 'mail'
|
||||
member_of: 'memberOf'
|
||||
group_name: 'cn'
|
||||
|
||||
access_control:
|
||||
default_policy: deny
|
||||
|
|
|
@ -22,16 +22,19 @@ authentication_backend:
|
|||
tls:
|
||||
skip_verify: true
|
||||
base_dn: dc=example,dc=com
|
||||
username_attribute: uid
|
||||
additional_users_dn: ou=users
|
||||
users_filter: (&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=person)(objectClass=inetOrgPerson)) # yamllint disable-line rule:line-length
|
||||
additional_groups_dn: ou=groups
|
||||
groups_filter: (&(member={dn})(objectClass=groupOfNames))
|
||||
group_name_attribute: cn
|
||||
mail_attribute: mail
|
||||
display_name_attribute: displayName
|
||||
user: cn=pwmanager,dc=example,dc=com
|
||||
password: password
|
||||
attributes:
|
||||
distinguished_name: ''
|
||||
username: 'uid'
|
||||
display_name: 'displayName'
|
||||
mail: 'mail'
|
||||
member_of: 'memberOf'
|
||||
group_name: 'cn'
|
||||
|
||||
session:
|
||||
secret: unsecure_session_secret
|
||||
|
|
|
@ -20,15 +20,18 @@ authentication_backend:
|
|||
tls:
|
||||
skip_verify: true
|
||||
base_dn: dc=example,dc=com
|
||||
username_attribute: uid
|
||||
additional_users_dn: ou=users
|
||||
users_filter: (&({username_attribute}={input})(objectClass=person))
|
||||
additional_groups_dn: ou=groups
|
||||
groups_filter: (&(member={dn})(objectClass=groupOfNames))
|
||||
group_name_attribute: cn
|
||||
mail_attribute: mail
|
||||
display_name_attribute: displayName
|
||||
user: cn=admin,dc=example,dc=com
|
||||
attributes:
|
||||
distinguished_name: ''
|
||||
username: 'uid'
|
||||
display_name: 'displayName'
|
||||
mail: 'mail'
|
||||
member_of: 'memberOf'
|
||||
group_name: 'cn'
|
||||
|
||||
access_control:
|
||||
default_policy: deny
|
||||
|
|
Loading…
Reference in New Issue