feat(authentication): unauthenticated ldap bind (#3291)
This allows configuring unauthenticated LDAP binding.pull/3538/head
parent
ee6ef51620
commit
25b5c1ee2e
|
@ -790,8 +790,7 @@ notifier:
|
|||
## Enables additional debug messages.
|
||||
# enable_client_debug_messages: false
|
||||
|
||||
## SECURITY NOTICE: It's not recommended changing this option, and highly discouraged to have it below 8 for
|
||||
## security reasons.
|
||||
## SECURITY NOTICE: It's not recommended changing this option and values below 8 are strongly discouraged.
|
||||
# minimum_parameter_entropy: 8
|
||||
|
||||
## SECURITY NOTICE: It's not recommended changing this option, and highly discouraged to have it set to 'never'
|
||||
|
|
|
@ -38,6 +38,7 @@ authentication_backend:
|
|||
groups_filter: (&(member={dn})(objectClass=groupOfNames))
|
||||
group_name_attribute: cn
|
||||
permit_referrals: false
|
||||
permit_unauthenticated_bind: false
|
||||
user: CN=admin,DC=example,DC=com
|
||||
password: password
|
||||
```
|
||||
|
@ -50,7 +51,7 @@ authentication_backend:
|
|||
|
||||
Configures the LDAP implementation used by Authelia.
|
||||
|
||||
See the [Implementation Guide](#implementation-guide) for information.
|
||||
See the [Implementation Guide](../../reference/guides/ldap.md#implementation-guide) for information.
|
||||
|
||||
### url
|
||||
|
||||
|
@ -170,7 +171,7 @@ using the following filter which is currently only tested against Microsoft Acti
|
|||
|
||||
`(&(member:1.2.840.113556.1.4.1941:={dn})(objectClass=group)(objectCategory=group))`
|
||||
|
||||
## group_name_attribute
|
||||
### group_name_attribute
|
||||
|
||||
{{< confkey type="string" required="situational" >}}
|
||||
|
||||
|
@ -180,13 +181,24 @@ information.*
|
|||
|
||||
The LDAP attribute that is used by Authelia to determine the group name.
|
||||
|
||||
## permit_referrals
|
||||
### permit_referrals
|
||||
|
||||
{{< confkey type="boolean" default="false" required="no" >}}
|
||||
|
||||
Permits following referrals. This is useful if you have read-only servers in your architecture and thus require
|
||||
referrals to be followed when performing write operations.
|
||||
|
||||
### permit_unauthenticated_bind
|
||||
|
||||
{{< confkey type="boolean" default="false" required="no" >}}
|
||||
|
||||
*__WARNING:__ This option is strongly discouraged. Please consider disabling unauthenticated binding to your LDAP
|
||||
server and utilizing a service account.*
|
||||
|
||||
Permits binding to the server without a password. For this option to be enabled both the [password](#password)
|
||||
configuration option must be blank and [disable_reset_password](introduction.md#disable_reset_password) must be
|
||||
disabled.
|
||||
|
||||
### user
|
||||
|
||||
{{< confkey type="string" required="yes" >}}
|
||||
|
@ -200,65 +212,6 @@ The distinguished name of the user paired with the password to bind with for loo
|
|||
The password of the user paired with the user to bind with for lookup and password change operations.
|
||||
Can also be defined using a [secret](../methods/secrets.md) which is the recommended for containerized deployments.
|
||||
|
||||
## Implementation Guide
|
||||
|
||||
There are currently two implementations, `custom` and `activedirectory`. The `activedirectory` implementation
|
||||
must be used if you wish to allow users to change or reset their password as Active Directory
|
||||
uses a custom attribute for this, and an input format other implementations do not use. The long term
|
||||
intention of this is to have logical defaults for various RFC implementations of LDAP.
|
||||
|
||||
### Filter replacements
|
||||
|
||||
Various replacements occur in the user and groups filter. The replacements either occur at startup or upon an LDAP
|
||||
search.
|
||||
|
||||
#### Users filter replacements
|
||||
|
||||
| Placeholder | Phase | Replacement |
|
||||
|:------------------------:|:-------:|:-------------------------------------:|
|
||||
| {username_attribute} | startup | The 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 |
|
||||
|
||||
#### Groups filter replacements
|
||||
|
||||
| Placeholder | Phase | Replacement |
|
||||
|:-----------:|:------:|:-------------------------------------------------------------------------:|
|
||||
| {input} | search | The input into the username field |
|
||||
| {username} | search | The username from the profile lookup obtained from the username attribute |
|
||||
| {dn} | search | The distinguished name from the profile lookup |
|
||||
|
||||
### Defaults
|
||||
|
||||
The below tables describes the current attribute defaults for each implementation.
|
||||
|
||||
#### Attribute defaults
|
||||
|
||||
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 |
|
||||
|
||||
#### Filter defaults
|
||||
|
||||
The filters are probably the most important part to get correct when setting up LDAP. You want to exclude disabled
|
||||
accounts. The active directory example has two attribute filters that accomplish this as an example (more examples would
|
||||
be appreciated). The userAccountControl filter checks that the account is not disabled and the pwdLastSet makes sure that
|
||||
value is not 0 which means the password requires changing at the next login.
|
||||
|
||||
| Implementation | Users Filter | Groups Filter |
|
||||
|:---------------:|:---------------------------------------------------------------------------------------------------------------------------------------------------------------:|:---------------------------------------------------------:|
|
||||
| custom | N/A | N/A |
|
||||
| activedirectory | (&(|({username_attribute}={input})({mail_attribute}={input}))(sAMAccountType=805306368)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(!(pwdLastSet=0))) | (&(member={dn})(objectClass=group)(objectCategory=group)) |
|
||||
|
||||
*__Note:__* The Active Directory filter `(sAMAccountType=805306368)` is exactly the same as
|
||||
`(&(objectCategory=person)(objectClass=user))` except that the former is more performant, you can read more about this
|
||||
and other Active Directory filters on the [TechNet wiki](https://social.technet.microsoft.com/wiki/contents/articles/5392.active-directory-ldap-syntax-filters.aspx).
|
||||
|
||||
## Refresh Interval
|
||||
|
||||
It's recommended you either use the default [refresh interval](./introduction.md#refresh_interval) or configure this to
|
||||
|
@ -274,6 +227,10 @@ In order to avoid such problems, we highly recommended you follow [RFC2307] by u
|
|||
Directory and `uid` for other implementations as the attribute holding the unique identifier
|
||||
for your users.
|
||||
|
||||
## See Also
|
||||
|
||||
- [LDAP Reference Guide](../../reference/guides/ldap.md)
|
||||
|
||||
[username attribute]: #username_attribute
|
||||
[TechNet wiki]: https://social.technet.microsoft.com/wiki/contents/articles/5392.active-directory-ldap-syntax-filters.aspx
|
||||
[RFC2307]: https://www.rfc-editor.org/rfc/rfc2307.html
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
---
|
||||
title: "LDAP"
|
||||
description: "A reference guide on the LDAP implementation specifics"
|
||||
lead: "This section contains reference documentation for Authelia's LDAP implementation specifics."
|
||||
date: 2022-03-20T12:52:27+11:00
|
||||
draft: false
|
||||
images: []
|
||||
menu:
|
||||
reference:
|
||||
parent: "guides"
|
||||
weight: 220
|
||||
toc: true
|
||||
---
|
||||
|
||||
## Binding
|
||||
|
||||
When it comes to LDAP there are several considerations for deciding how to bind to the LDAP server.
|
||||
|
||||
### Unauthenticated Binding
|
||||
|
||||
The most insecure method is unauthenticated binds. They are generally considered insecure due to the fact allowing them
|
||||
at all ensures anyone with any level of network access can easily obtain objects and their attributes.
|
||||
|
||||
Authelia does support unauthenticated binds but it is not by default, you must configure the
|
||||
[permit_unauthenticated_bind](../../configuration/first-factor/ldap.md#permit_unauthenticated_bind) configuration
|
||||
option.
|
||||
|
||||
### End-User Binding
|
||||
|
||||
One method to bind to the server that is favored by a lot of people is binding to the LDAP server as the end user. While
|
||||
this is more secure than methods such as [Unauthenticated Binding](#unauthenticated-binding) the drawback is that it can
|
||||
only be used securely at the time the user enters their credentials. Storing a password in memory in general is not very
|
||||
secure and prone to breakage due to outside influences (i.e. the user changes their password).
|
||||
|
||||
In addition, this method is not compatible with the password reset / forgot password flow at all (not to be confused
|
||||
with a change password flow).
|
||||
|
||||
Authelia doesn't currently support such a binding method excluding for checking user passwords.
|
||||
|
||||
### Service-User Binding
|
||||
|
||||
This is the most common method of binding to LDAP. This involves setting up a special service user with a complex
|
||||
password which has the minimum permissions required to do the tasks required.
|
||||
|
||||
Authelia primarily supports this method.
|
||||
|
||||
## Implementation Guide
|
||||
|
||||
There are currently two implementations, `custom` and `activedirectory`. The `activedirectory` implementation
|
||||
must be used if you wish to allow users to change or reset their password as Active Directory
|
||||
uses a custom attribute for this, and an input format other implementations do not use. The long term
|
||||
intention of this is to have logical defaults for various RFC implementations of LDAP.
|
||||
|
||||
### Filter replacements
|
||||
|
||||
Various replacements occur in the user and groups filter. The replacements either occur at startup or upon an LDAP
|
||||
search.
|
||||
|
||||
#### Users filter replacements
|
||||
|
||||
| Placeholder | Phase | Replacement |
|
||||
|:------------------------:|:-------:|:-------------------------------------:|
|
||||
| {username_attribute} | startup | The 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 |
|
||||
|
||||
#### Groups filter replacements
|
||||
|
||||
| Placeholder | Phase | Replacement |
|
||||
|:-----------:|:------:|:-------------------------------------------------------------------------:|
|
||||
| {input} | search | The input into the username field |
|
||||
| {username} | search | The username from the profile lookup obtained from the username attribute |
|
||||
| {dn} | search | The distinguished name from the profile lookup |
|
||||
|
||||
### Defaults
|
||||
|
||||
The below tables describes the current attribute defaults for each implementation.
|
||||
|
||||
#### Attribute defaults
|
||||
|
||||
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 |
|
||||
|
||||
#### Filter defaults
|
||||
|
||||
The filters are probably the most important part to get correct when setting up LDAP. You want to exclude disabled
|
||||
accounts. The active directory example has two attribute filters that accomplish this as an example (more examples would
|
||||
be appreciated). The userAccountControl filter checks that the account is not disabled and the pwdLastSet makes sure that
|
||||
value is not 0 which means the password requires changing at the next login.
|
||||
|
||||
| Implementation | Users Filter | Groups Filter |
|
||||
|:---------------:|:---------------------------------------------------------------------------------------------------------------------------------------------------------------:|:---------------------------------------------------------:|
|
||||
| custom | N/A | N/A |
|
||||
| activedirectory | (&(|({username_attribute}={input})({mail_attribute}={input}))(sAMAccountType=805306368)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(!(pwdLastSet=0))) | (&(member={dn})(objectClass=group)(objectCategory=group)) |
|
||||
|
||||
*__Note:__* The Active Directory filter `(sAMAccountType=805306368)` is exactly the same as
|
||||
`(&(objectCategory=person)(objectClass=user))` except that the former is more performant, you can read more about this
|
||||
and other Active Directory filters on the [TechNet wiki](https://social.technet.microsoft.com/wiki/contents/articles/5392.active-directory-ldap-syntax-filters.aspx).
|
|
@ -118,3 +118,17 @@ func (mr *MockLDAPClientMockRecorder) StartTLS(arg0 interface{}) *gomock.Call {
|
|||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartTLS", reflect.TypeOf((*MockLDAPClient)(nil).StartTLS), arg0)
|
||||
}
|
||||
|
||||
// UnauthenticatedBind mocks base method.
|
||||
func (m *MockLDAPClient) UnauthenticatedBind(arg0 string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "UnauthenticatedBind", arg0)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// UnauthenticatedBind indicates an expected call of UnauthenticatedBind.
|
||||
func (mr *MockLDAPClientMockRecorder) UnauthenticatedBind(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnauthenticatedBind", reflect.TypeOf((*MockLDAPClient)(nil).UnauthenticatedBind), arg0)
|
||||
}
|
||||
|
|
|
@ -226,7 +226,7 @@ func (p *LDAPUserProvider) connect() (client LDAPClient, err error) {
|
|||
return p.connectCustom(p.config.URL, p.config.User, p.config.Password, p.config.StartTLS, p.dialOpts...)
|
||||
}
|
||||
|
||||
func (p *LDAPUserProvider) connectCustom(url, userDN, password string, startTLS bool, opts ...ldap.DialOpt) (client LDAPClient, err error) {
|
||||
func (p *LDAPUserProvider) connectCustom(url, username, password string, startTLS bool, opts ...ldap.DialOpt) (client LDAPClient, err error) {
|
||||
if client, err = p.factory.DialURL(url, opts...); err != nil {
|
||||
return nil, fmt.Errorf("dial failed with error: %w", err)
|
||||
}
|
||||
|
@ -239,7 +239,13 @@ func (p *LDAPUserProvider) connectCustom(url, userDN, password string, startTLS
|
|||
}
|
||||
}
|
||||
|
||||
if err = client.Bind(userDN, password); err != nil {
|
||||
if password == "" {
|
||||
err = client.UnauthenticatedBind(username)
|
||||
} else {
|
||||
err = client.Bind(username, password)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
client.Close()
|
||||
|
||||
return nil, fmt.Errorf("bind failed with error: %w", err)
|
||||
|
|
|
@ -20,6 +20,7 @@ type LDAPClient interface {
|
|||
StartTLS(config *tls.Config) (err error)
|
||||
|
||||
Bind(username, password string) (err error)
|
||||
UnauthenticatedBind(username string) (err error)
|
||||
|
||||
Modify(modifyRequest *ldap.ModifyRequest) (err error)
|
||||
PasswordModify(pwdModifyRequest *ldap.PasswordModifyRequest) (pwdModifyResult *ldap.PasswordModifyResult, err error)
|
||||
|
|
|
@ -790,8 +790,7 @@ notifier:
|
|||
## Enables additional debug messages.
|
||||
# enable_client_debug_messages: false
|
||||
|
||||
## SECURITY NOTICE: It's not recommended changing this option, and highly discouraged to have it below 8 for
|
||||
## security reasons.
|
||||
## SECURITY NOTICE: It's not recommended changing this option and values below 8 are strongly discouraged.
|
||||
# minimum_parameter_entropy: 8
|
||||
|
||||
## SECURITY NOTICE: It's not recommended changing this option, and highly discouraged to have it set to 'never'
|
||||
|
|
|
@ -26,7 +26,8 @@ type LDAPAuthenticationBackendConfiguration struct {
|
|||
MailAttribute string `koanf:"mail_attribute"`
|
||||
DisplayNameAttribute string `koanf:"display_name_attribute"`
|
||||
|
||||
PermitReferrals bool `koanf:"permit_referrals"`
|
||||
PermitReferrals bool `koanf:"permit_referrals"`
|
||||
PermitUnauthenticatedBind bool `koanf:"permit_unauthenticated_bind"`
|
||||
|
||||
User string `koanf:"user"`
|
||||
Password string `koanf:"password"`
|
||||
|
|
|
@ -62,6 +62,7 @@ var Keys = []string{
|
|||
"authentication_backend.ldap.mail_attribute",
|
||||
"authentication_backend.ldap.display_name_attribute",
|
||||
"authentication_backend.ldap.permit_referrals",
|
||||
"authentication_backend.ldap.permit_unauthenticated_bind",
|
||||
"authentication_backend.ldap.user",
|
||||
"authentication_backend.ldap.password",
|
||||
"authentication_backend.file.path",
|
||||
|
|
|
@ -22,7 +22,7 @@ func ValidateAuthenticationBackend(config *schema.AuthenticationBackendConfigura
|
|||
if config.File != nil {
|
||||
validateFileAuthenticationBackend(config.File, validator)
|
||||
} else if config.LDAP != nil {
|
||||
validateLDAPAuthenticationBackend(config.LDAP, validator)
|
||||
validateLDAPAuthenticationBackend(config, validator)
|
||||
}
|
||||
|
||||
if config.RefreshInterval == "" {
|
||||
|
@ -118,50 +118,50 @@ func validateFileAuthenticationBackendArgon2id(config *schema.PasswordConfigurat
|
|||
}
|
||||
}
|
||||
|
||||
func validateLDAPAuthenticationBackend(config *schema.LDAPAuthenticationBackendConfiguration, validator *schema.StructValidator) {
|
||||
if config.Timeout == 0 {
|
||||
config.Timeout = schema.DefaultLDAPAuthenticationBackendConfiguration.Timeout
|
||||
func validateLDAPAuthenticationBackend(config *schema.AuthenticationBackendConfiguration, validator *schema.StructValidator) {
|
||||
if config.LDAP.Timeout == 0 {
|
||||
config.LDAP.Timeout = schema.DefaultLDAPAuthenticationBackendConfiguration.Timeout
|
||||
}
|
||||
|
||||
if config.Implementation == "" {
|
||||
config.Implementation = schema.DefaultLDAPAuthenticationBackendConfiguration.Implementation
|
||||
if config.LDAP.Implementation == "" {
|
||||
config.LDAP.Implementation = schema.DefaultLDAPAuthenticationBackendConfiguration.Implementation
|
||||
}
|
||||
|
||||
if config.TLS == nil {
|
||||
config.TLS = schema.DefaultLDAPAuthenticationBackendConfiguration.TLS
|
||||
} else if config.TLS.MinimumVersion == "" {
|
||||
config.TLS.MinimumVersion = schema.DefaultLDAPAuthenticationBackendConfiguration.TLS.MinimumVersion
|
||||
if config.LDAP.TLS == nil {
|
||||
config.LDAP.TLS = schema.DefaultLDAPAuthenticationBackendConfiguration.TLS
|
||||
} else if config.LDAP.TLS.MinimumVersion == "" {
|
||||
config.LDAP.TLS.MinimumVersion = schema.DefaultLDAPAuthenticationBackendConfiguration.TLS.MinimumVersion
|
||||
}
|
||||
|
||||
if _, err := utils.TLSStringToTLSConfigVersion(config.TLS.MinimumVersion); err != nil {
|
||||
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendTLSMinVersion, config.TLS.MinimumVersion, err))
|
||||
if _, err := utils.TLSStringToTLSConfigVersion(config.LDAP.TLS.MinimumVersion); err != nil {
|
||||
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendTLSMinVersion, config.LDAP.TLS.MinimumVersion, err))
|
||||
}
|
||||
|
||||
switch config.Implementation {
|
||||
switch config.LDAP.Implementation {
|
||||
case schema.LDAPImplementationCustom:
|
||||
setDefaultImplementationCustomLDAPAuthenticationBackend(config)
|
||||
setDefaultImplementationCustomLDAPAuthenticationBackend(config.LDAP)
|
||||
case schema.LDAPImplementationActiveDirectory:
|
||||
setDefaultImplementationActiveDirectoryLDAPAuthenticationBackend(config)
|
||||
setDefaultImplementationActiveDirectoryLDAPAuthenticationBackend(config.LDAP)
|
||||
default:
|
||||
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendImplementation, config.Implementation, strings.Join([]string{schema.LDAPImplementationCustom, schema.LDAPImplementationActiveDirectory}, "', '")))
|
||||
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendImplementation, config.LDAP.Implementation, strings.Join([]string{schema.LDAPImplementationCustom, schema.LDAPImplementationActiveDirectory}, "', '")))
|
||||
}
|
||||
|
||||
if strings.Contains(config.UsersFilter, "{0}") {
|
||||
if strings.Contains(config.LDAP.UsersFilter, "{0}") {
|
||||
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendFilterReplacedPlaceholders, "users_filter", "{0}", "{input}"))
|
||||
}
|
||||
|
||||
if strings.Contains(config.GroupsFilter, "{0}") {
|
||||
if strings.Contains(config.LDAP.GroupsFilter, "{0}") {
|
||||
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendFilterReplacedPlaceholders, "groups_filter", "{0}", "{input}"))
|
||||
}
|
||||
|
||||
if strings.Contains(config.GroupsFilter, "{1}") {
|
||||
if strings.Contains(config.LDAP.GroupsFilter, "{1}") {
|
||||
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendFilterReplacedPlaceholders, "groups_filter", "{1}", "{username}"))
|
||||
}
|
||||
|
||||
if config.URL == "" {
|
||||
if config.LDAP.URL == "" {
|
||||
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendMissingOption, "url"))
|
||||
} else {
|
||||
validateLDAPAuthenticationBackendURL(config, validator)
|
||||
validateLDAPAuthenticationBackendURL(config.LDAP, validator)
|
||||
}
|
||||
|
||||
validateLDAPRequiredParameters(config, validator)
|
||||
|
@ -191,42 +191,50 @@ func validateLDAPAuthenticationBackendURL(config *schema.LDAPAuthenticationBacke
|
|||
}
|
||||
}
|
||||
|
||||
func validateLDAPRequiredParameters(config *schema.LDAPAuthenticationBackendConfiguration, validator *schema.StructValidator) {
|
||||
// TODO: see if it's possible to disable this check if disable_reset_password is set and when anonymous/user binding is supported (#101 and #387).
|
||||
if config.User == "" {
|
||||
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendMissingOption, "user"))
|
||||
func validateLDAPRequiredParameters(config *schema.AuthenticationBackendConfiguration, validator *schema.StructValidator) {
|
||||
if config.LDAP.PermitUnauthenticatedBind {
|
||||
if config.LDAP.Password != "" {
|
||||
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendUnauthenticatedBindWithPassword))
|
||||
}
|
||||
|
||||
if !config.DisableResetPassword {
|
||||
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendUnauthenticatedBindWithResetEnabled))
|
||||
}
|
||||
} else {
|
||||
if config.LDAP.User == "" {
|
||||
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendMissingOption, "user"))
|
||||
}
|
||||
|
||||
if config.LDAP.Password == "" {
|
||||
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendMissingOption, "password"))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: see if it's possible to disable this check if disable_reset_password is set and when anonymous/user binding is supported (#101 and #387).
|
||||
if config.Password == "" {
|
||||
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendMissingOption, "password"))
|
||||
}
|
||||
|
||||
if config.BaseDN == "" {
|
||||
if config.LDAP.BaseDN == "" {
|
||||
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendMissingOption, "base_dn"))
|
||||
}
|
||||
|
||||
if config.UsersFilter == "" {
|
||||
if config.LDAP.UsersFilter == "" {
|
||||
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendMissingOption, "users_filter"))
|
||||
} else {
|
||||
if !strings.HasPrefix(config.UsersFilter, "(") || !strings.HasSuffix(config.UsersFilter, ")") {
|
||||
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendFilterEnclosingParenthesis, "users_filter", config.UsersFilter, config.UsersFilter))
|
||||
if !strings.HasPrefix(config.LDAP.UsersFilter, "(") || !strings.HasSuffix(config.LDAP.UsersFilter, ")") {
|
||||
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendFilterEnclosingParenthesis, "users_filter", config.LDAP.UsersFilter, config.LDAP.UsersFilter))
|
||||
}
|
||||
|
||||
if !strings.Contains(config.UsersFilter, "{username_attribute}") {
|
||||
if !strings.Contains(config.LDAP.UsersFilter, "{username_attribute}") {
|
||||
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendFilterMissingPlaceholder, "users_filter", "username_attribute"))
|
||||
}
|
||||
|
||||
// This test helps the user know that users_filter is broken after the breaking change induced by this commit.
|
||||
if !strings.Contains(config.UsersFilter, "{input}") {
|
||||
if !strings.Contains(config.LDAP.UsersFilter, "{input}") {
|
||||
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendFilterMissingPlaceholder, "users_filter", "input"))
|
||||
}
|
||||
}
|
||||
|
||||
if config.GroupsFilter == "" {
|
||||
if config.LDAP.GroupsFilter == "" {
|
||||
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendMissingOption, "groups_filter"))
|
||||
} else if !strings.HasPrefix(config.GroupsFilter, "(") || !strings.HasSuffix(config.GroupsFilter, ")") {
|
||||
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendFilterEnclosingParenthesis, "groups_filter", config.GroupsFilter, config.GroupsFilter))
|
||||
} 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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -85,6 +85,9 @@ const (
|
|||
"must at least be parallelism multiplied by 8 when using algorithm 'argon2id' " +
|
||||
"with parallelism %d it should be at least %d but it is configured as '%d'"
|
||||
|
||||
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"
|
||||
errFmtLDAPAuthBackendTLSMinVersion = "authentication_backend: ldap: tls: option " +
|
||||
"'minimum_tls_version' is invalid: %s: %w"
|
||||
|
|
Loading…
Reference in New Issue