feat(authentication): allow customizable ldap connection timeout (#2240)
This implements both a connection timeout for LDAP connections, and makes it configurable by administrators. The default is 5s. The reason for this commit is currently if a connection to an LDAP server cannot be established it does not timeout in a reasonable period.pull/2241/head
parent
a3b14871ba
commit
b2a49e1780
|
@ -149,6 +149,9 @@ authentication_backend:
|
|||
## Scheme can be ldap or ldaps in the format (port optional).
|
||||
url: ldap://127.0.0.1
|
||||
|
||||
## The dial timeout for LDAP.
|
||||
timeout: 5s
|
||||
|
||||
## Use StartTLS with the LDAP connection.
|
||||
start_tls: false
|
||||
|
||||
|
|
|
@ -7,11 +7,9 @@ nav_order: 2
|
|||
---
|
||||
|
||||
# LDAP
|
||||
|
||||
**Authelia** supports using a LDAP server as the users database.
|
||||
|
||||
## Configuration
|
||||
|
||||
```yaml
|
||||
authentication_backend:
|
||||
disable_reset_password: false
|
||||
|
@ -19,12 +17,13 @@ authentication_backend:
|
|||
ldap:
|
||||
implementation: custom
|
||||
url: ldap://127.0.0.1
|
||||
timeout: 5s
|
||||
start_tls: false
|
||||
tls:
|
||||
server_name: ldap.example.com
|
||||
skip_verify: false
|
||||
minimum_version: TLS1.2
|
||||
base_dn: dc=example,dc=com
|
||||
base_dn: DC=example,DC=com
|
||||
username_attribute: uid
|
||||
additional_users_dn: ou=users
|
||||
users_filter: (&({username_attribute}={input})(objectClass=person))
|
||||
|
@ -33,7 +32,7 @@ authentication_backend:
|
|||
group_name_attribute: cn
|
||||
mail_attribute: mail
|
||||
display_name_attribute: displayName
|
||||
user: cn=admin,dc=example,dc=com
|
||||
user: CN=admin,DC=example,DC=com
|
||||
password: password
|
||||
```
|
||||
|
||||
|
@ -70,6 +69,18 @@ If utilising an IPv6 literal address it must be enclosed by square brackets:
|
|||
url: ldap://[fd00:1111:2222:3333::1]
|
||||
```
|
||||
|
||||
### timeout
|
||||
<div markdown="1">
|
||||
type: duration
|
||||
{: .label .label-config .label-purple }
|
||||
default: 5s
|
||||
{: .label .label-config .label-blue }
|
||||
required: no
|
||||
{: .label .label-config .label-green }
|
||||
</div>
|
||||
|
||||
The timeout for dialing an LDAP connection.
|
||||
|
||||
### start_tls
|
||||
<div markdown="1">
|
||||
type: boolean
|
||||
|
@ -86,7 +97,6 @@ URL's are slightly more secure.
|
|||
|
||||
|
||||
### tls
|
||||
|
||||
Controls the TLS connection validation process. You can see how to configure the tls
|
||||
section [here](../index.md#tls-configuration).
|
||||
|
||||
|
@ -143,11 +153,9 @@ The default value is dependent on the [implementation](#implementation), refer t
|
|||
[attribute defaults](#attribute-defaults) for more information.
|
||||
|
||||
### additional_groups_dn
|
||||
|
||||
Similar to [additional_users_dn](#additional_users_dn) but it applies to group searches.
|
||||
|
||||
### groups_filter
|
||||
|
||||
Similar to [users_filter](#users_filter) but it applies to group searches. In order to include groups the memeber 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:
|
||||
|
@ -155,7 +163,6 @@ 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))`
|
||||
|
||||
### mail_attribute
|
||||
|
||||
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
|
||||
|
@ -163,32 +170,26 @@ identity verification when a user attempts to reset their password or
|
|||
register a second factor device.
|
||||
|
||||
### display_name_attribute
|
||||
|
||||
The attribute to retrieve which is shown on the Web UI to the user when they log in.
|
||||
|
||||
### user
|
||||
|
||||
The distinguished name of the user paired with the password to bind with for lookup and password change operations.
|
||||
|
||||
### password
|
||||
|
||||
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](../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 [username attribute](#username_attribute) configured |
|
||||
|
@ -197,7 +198,6 @@ search.
|
|||
|{input} |search |The input into the username field |
|
||||
|
||||
#### Groups filter replacements
|
||||
|
||||
|Placeholder |Phase |Replacement |
|
||||
|:----------------------:|:-----:|:-------------------------------------------------------------------------:|
|
||||
|{input} |search |The input into the username field |
|
||||
|
@ -205,7 +205,6 @@ search.
|
|||
|{dn} |search |The distinguished name from the profile lookup |
|
||||
|
||||
### Defaults
|
||||
|
||||
The below tables describes the current attribute defaults for each implementation.
|
||||
|
||||
#### Attribute defaults
|
||||
|
@ -218,7 +217,6 @@ described by the Username column.
|
|||
|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
|
||||
|
@ -236,7 +234,6 @@ _**Note:**_ The Active Directory filter `(sAMAccountType=805306368)` is exactly
|
|||
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
|
||||
|
||||
This setting takes a [duration notation](../index.md#duration-notation-format) that sets the max frequency
|
||||
for how often Authelia contacts the backend to verify the user still exists and that the groups stored
|
||||
in the session are up to date. This allows us to destroy sessions when the user no longer matches the
|
||||
|
@ -254,7 +251,6 @@ on a page loads which could be substantially costly. It's a trade-off between lo
|
|||
you should adapt according to your own security policy.
|
||||
|
||||
## Important notes
|
||||
|
||||
Users must be uniquely identified by an attribute, this attribute must obviously contain a single value and
|
||||
be guaranteed by the administrator to be unique. If multiple users have the same value, Authelia will simply
|
||||
fail authenticating the user and display an error message in the logs.
|
||||
|
|
|
@ -64,7 +64,7 @@ func (lc *LDAPConnectionImpl) StartTLS(config *tls.Config) error {
|
|||
|
||||
// LDAPConnectionFactory an interface of factory of ldap connections.
|
||||
type LDAPConnectionFactory interface {
|
||||
DialURL(addr string, opts ldap.DialOpt) (LDAPConnection, error)
|
||||
DialURL(addr string, opts ...ldap.DialOpt) (LDAPConnection, error)
|
||||
}
|
||||
|
||||
// LDAPConnectionFactoryImpl the production implementation of an ldap connection factory.
|
||||
|
@ -76,8 +76,8 @@ func NewLDAPConnectionFactoryImpl() *LDAPConnectionFactoryImpl {
|
|||
}
|
||||
|
||||
// DialURL creates a connection from an LDAP URL when successful.
|
||||
func (lcf *LDAPConnectionFactoryImpl) DialURL(addr string, opts ldap.DialOpt) (LDAPConnection, error) {
|
||||
conn, err := ldap.DialURL(addr, opts)
|
||||
func (lcf *LDAPConnectionFactoryImpl) DialURL(addr string, opts ...ldap.DialOpt) (LDAPConnection, error) {
|
||||
conn, err := ldap.DialURL(addr, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -10,30 +10,30 @@ import (
|
|||
gomock "github.com/golang/mock/gomock"
|
||||
)
|
||||
|
||||
// MockLDAPConnection is a mock of LDAPConnection interface
|
||||
// MockLDAPConnection is a mock of LDAPConnection interface.
|
||||
type MockLDAPConnection struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockLDAPConnectionMockRecorder
|
||||
}
|
||||
|
||||
// MockLDAPConnectionMockRecorder is the mock recorder for MockLDAPConnection
|
||||
// MockLDAPConnectionMockRecorder is the mock recorder for MockLDAPConnection.
|
||||
type MockLDAPConnectionMockRecorder struct {
|
||||
mock *MockLDAPConnection
|
||||
}
|
||||
|
||||
// NewMockLDAPConnection creates a new mock instance
|
||||
// NewMockLDAPConnection creates a new mock instance.
|
||||
func NewMockLDAPConnection(ctrl *gomock.Controller) *MockLDAPConnection {
|
||||
mock := &MockLDAPConnection{ctrl: ctrl}
|
||||
mock.recorder = &MockLDAPConnectionMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockLDAPConnection) EXPECT() *MockLDAPConnectionMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Bind mocks base method
|
||||
// Bind mocks base method.
|
||||
func (m *MockLDAPConnection) Bind(username, password string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Bind", username, password)
|
||||
|
@ -41,25 +41,53 @@ func (m *MockLDAPConnection) Bind(username, password string) error {
|
|||
return ret0
|
||||
}
|
||||
|
||||
// Bind indicates an expected call of Bind
|
||||
// Bind indicates an expected call of Bind.
|
||||
func (mr *MockLDAPConnectionMockRecorder) Bind(username, password interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Bind", reflect.TypeOf((*MockLDAPConnection)(nil).Bind), username, password)
|
||||
}
|
||||
|
||||
// Close mocks base method
|
||||
// Close mocks base method.
|
||||
func (m *MockLDAPConnection) Close() {
|
||||
m.ctrl.T.Helper()
|
||||
m.ctrl.Call(m, "Close")
|
||||
}
|
||||
|
||||
// Close indicates an expected call of Close
|
||||
// Close indicates an expected call of Close.
|
||||
func (mr *MockLDAPConnectionMockRecorder) Close() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockLDAPConnection)(nil).Close))
|
||||
}
|
||||
|
||||
// Search mocks base method
|
||||
// Modify mocks base method.
|
||||
func (m *MockLDAPConnection) Modify(modifyRequest *ldap.ModifyRequest) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Modify", modifyRequest)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Modify indicates an expected call of Modify.
|
||||
func (mr *MockLDAPConnectionMockRecorder) Modify(modifyRequest interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Modify", reflect.TypeOf((*MockLDAPConnection)(nil).Modify), modifyRequest)
|
||||
}
|
||||
|
||||
// PasswordModify mocks base method.
|
||||
func (m *MockLDAPConnection) PasswordModify(pwdModifyRequest *ldap.PasswordModifyRequest) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "PasswordModify", pwdModifyRequest)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// PasswordModify indicates an expected call of PasswordModify.
|
||||
func (mr *MockLDAPConnectionMockRecorder) PasswordModify(pwdModifyRequest interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PasswordModify", reflect.TypeOf((*MockLDAPConnection)(nil).PasswordModify), pwdModifyRequest)
|
||||
}
|
||||
|
||||
// Search mocks base method.
|
||||
func (m *MockLDAPConnection) Search(searchRequest *ldap.SearchRequest) (*ldap.SearchResult, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Search", searchRequest)
|
||||
|
@ -68,41 +96,13 @@ func (m *MockLDAPConnection) Search(searchRequest *ldap.SearchRequest) (*ldap.Se
|
|||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Search indicates an expected call of Search
|
||||
// Search indicates an expected call of Search.
|
||||
func (mr *MockLDAPConnectionMockRecorder) Search(searchRequest interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Search", reflect.TypeOf((*MockLDAPConnection)(nil).Search), searchRequest)
|
||||
}
|
||||
|
||||
// Modify mocks base method
|
||||
func (m *MockLDAPConnection) Modify(modifyRequest *ldap.ModifyRequest) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Modify", modifyRequest)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Modify indicates an expected call of Modify
|
||||
func (mr *MockLDAPConnectionMockRecorder) Modify(modifyRequest interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Modify", reflect.TypeOf((*MockLDAPConnection)(nil).Modify), modifyRequest)
|
||||
}
|
||||
|
||||
// PasswordModify mocks base method
|
||||
func (m *MockLDAPConnection) PasswordModify(pwdModifyRequest *ldap.PasswordModifyRequest) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "PasswordModify", pwdModifyRequest)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// PasswordModify indicates an expected call of PasswordModify
|
||||
func (mr *MockLDAPConnectionMockRecorder) PasswordModify(pwdModifyRequest interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PasswordModify", reflect.TypeOf((*MockLDAPConnection)(nil).Modify), pwdModifyRequest)
|
||||
}
|
||||
|
||||
// StartTLS mocks base method
|
||||
// StartTLS mocks base method.
|
||||
func (m *MockLDAPConnection) StartTLS(config *tls.Config) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "StartTLS", config)
|
||||
|
@ -110,46 +110,51 @@ func (m *MockLDAPConnection) StartTLS(config *tls.Config) error {
|
|||
return ret0
|
||||
}
|
||||
|
||||
// StartTLS indicates an expected call of StartTLS
|
||||
// StartTLS indicates an expected call of StartTLS.
|
||||
func (mr *MockLDAPConnectionMockRecorder) StartTLS(config interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartTLS", reflect.TypeOf((*MockLDAPConnection)(nil).StartTLS), config)
|
||||
}
|
||||
|
||||
// MockLDAPConnectionFactory is a mock of LDAPConnectionFactory interface
|
||||
// MockLDAPConnectionFactory is a mock of LDAPConnectionFactory interface.
|
||||
type MockLDAPConnectionFactory struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockLDAPConnectionFactoryMockRecorder
|
||||
}
|
||||
|
||||
// MockLDAPConnectionFactoryMockRecorder is the mock recorder for MockLDAPConnectionFactory
|
||||
// MockLDAPConnectionFactoryMockRecorder is the mock recorder for MockLDAPConnectionFactory.
|
||||
type MockLDAPConnectionFactoryMockRecorder struct {
|
||||
mock *MockLDAPConnectionFactory
|
||||
}
|
||||
|
||||
// NewMockLDAPConnectionFactory creates a new mock instance
|
||||
// NewMockLDAPConnectionFactory creates a new mock instance.
|
||||
func NewMockLDAPConnectionFactory(ctrl *gomock.Controller) *MockLDAPConnectionFactory {
|
||||
mock := &MockLDAPConnectionFactory{ctrl: ctrl}
|
||||
mock.recorder = &MockLDAPConnectionFactoryMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockLDAPConnectionFactory) EXPECT() *MockLDAPConnectionFactoryMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// DialURL mocks base method
|
||||
func (m *MockLDAPConnectionFactory) DialURL(addr string, opts ldap.DialOpt) (LDAPConnection, error) {
|
||||
// DialURL mocks base method.
|
||||
func (m *MockLDAPConnectionFactory) DialURL(addr string, opts ...ldap.DialOpt) (LDAPConnection, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "DialURL", addr, opts)
|
||||
varargs := []interface{}{addr}
|
||||
for _, a := range opts {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
ret := m.ctrl.Call(m, "DialURL", varargs...)
|
||||
ret0, _ := ret[0].(LDAPConnection)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// DialURL indicates an expected call of DialURL
|
||||
func (mr *MockLDAPConnectionFactoryMockRecorder) DialURL(addr, opts interface{}) *gomock.Call {
|
||||
// DialURL indicates an expected call of DialURL.
|
||||
func (mr *MockLDAPConnectionFactoryMockRecorder) DialURL(addr interface{}, opts ...interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DialURL", reflect.TypeOf((*MockLDAPConnectionFactory)(nil).DialURL), addr, opts)
|
||||
varargs := append([]interface{}{addr}, opts...)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DialURL", reflect.TypeOf((*MockLDAPConnectionFactory)(nil).DialURL), varargs...)
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
|
@ -19,7 +20,7 @@ import (
|
|||
type LDAPUserProvider struct {
|
||||
configuration schema.LDAPAuthenticationBackendConfiguration
|
||||
tlsConfig *tls.Config
|
||||
dialOpts ldap.DialOpt
|
||||
dialOpts []ldap.DialOpt
|
||||
logger *logrus.Logger
|
||||
connectionFactory LDAPConnectionFactory
|
||||
|
||||
|
@ -65,10 +66,12 @@ func newLDAPUserProvider(configuration schema.LDAPAuthenticationBackendConfigura
|
|||
|
||||
tlsConfig := utils.NewTLSConfig(configuration.TLS, tls.VersionTLS12, certPool)
|
||||
|
||||
var dialOpts ldap.DialOpt
|
||||
var dialOpts = []ldap.DialOpt{
|
||||
ldap.DialWithDialer(&net.Dialer{Timeout: configuration.Timeout}),
|
||||
}
|
||||
|
||||
if tlsConfig != nil {
|
||||
dialOpts = ldap.DialWithTLSConfig(tlsConfig)
|
||||
dialOpts = append(dialOpts, ldap.DialWithTLSConfig(tlsConfig))
|
||||
}
|
||||
|
||||
if factory == nil {
|
||||
|
@ -90,7 +93,7 @@ func newLDAPUserProvider(configuration schema.LDAPAuthenticationBackendConfigura
|
|||
}
|
||||
|
||||
func (p *LDAPUserProvider) connect(userDN string, password string) (LDAPConnection, error) {
|
||||
conn, err := p.connectionFactory.DialURL(p.configuration.URL, p.dialOpts)
|
||||
conn, err := p.connectionFactory.DialURL(p.configuration.URL, p.dialOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -149,6 +149,9 @@ authentication_backend:
|
|||
## Scheme can be ldap or ldaps in the format (port optional).
|
||||
url: ldap://127.0.0.1
|
||||
|
||||
## The dial timeout for LDAP.
|
||||
timeout: 5s
|
||||
|
||||
## Use StartTLS with the LDAP connection.
|
||||
start_tls: false
|
||||
|
||||
|
|
|
@ -1,22 +1,30 @@
|
|||
package schema
|
||||
|
||||
import "time"
|
||||
|
||||
// LDAPAuthenticationBackendConfiguration represents the configuration related to LDAP server.
|
||||
type LDAPAuthenticationBackendConfiguration struct {
|
||||
Implementation string `koanf:"implementation"`
|
||||
URL string `koanf:"url"`
|
||||
BaseDN string `koanf:"base_dn"`
|
||||
AdditionalUsersDN string `koanf:"additional_users_dn"`
|
||||
UsersFilter string `koanf:"users_filter"`
|
||||
AdditionalGroupsDN string `koanf:"additional_groups_dn"`
|
||||
GroupsFilter string `koanf:"groups_filter"`
|
||||
GroupNameAttribute string `koanf:"group_name_attribute"`
|
||||
UsernameAttribute string `koanf:"username_attribute"`
|
||||
MailAttribute string `koanf:"mail_attribute"`
|
||||
DisplayNameAttribute string `koanf:"display_name_attribute"`
|
||||
User string `koanf:"user"`
|
||||
Password string `koanf:"password"`
|
||||
StartTLS bool `koanf:"start_tls"`
|
||||
TLS *TLSConfig `koanf:"tls"`
|
||||
Implementation string `koanf:"implementation"`
|
||||
URL string `koanf:"url"`
|
||||
Timeout time.Duration `koanf:"timeout"`
|
||||
StartTLS bool `koanf:"start_tls"`
|
||||
TLS *TLSConfig `koanf:"tls"`
|
||||
|
||||
BaseDN string `koanf:"base_dn"`
|
||||
|
||||
AdditionalUsersDN string `koanf:"additional_users_dn"`
|
||||
UsersFilter string `koanf:"users_filter"`
|
||||
|
||||
AdditionalGroupsDN string `koanf:"additional_groups_dn"`
|
||||
GroupsFilter string `koanf:"groups_filter"`
|
||||
|
||||
GroupNameAttribute string `koanf:"group_name_attribute"`
|
||||
UsernameAttribute string `koanf:"username_attribute"`
|
||||
MailAttribute string `koanf:"mail_attribute"`
|
||||
DisplayNameAttribute string `koanf:"display_name_attribute"`
|
||||
|
||||
User string `koanf:"user"`
|
||||
Password string `koanf:"password"`
|
||||
}
|
||||
|
||||
// FileAuthenticationBackendConfiguration represents the configuration related to file-based backend.
|
||||
|
@ -77,6 +85,7 @@ var DefaultLDAPAuthenticationBackendConfiguration = LDAPAuthenticationBackendCon
|
|||
MailAttribute: "mail",
|
||||
DisplayNameAttribute: "displayName",
|
||||
GroupNameAttribute: "cn",
|
||||
Timeout: time.Second * 5,
|
||||
TLS: &TLSConfig{
|
||||
MinimumVersion: "TLS1.2",
|
||||
},
|
||||
|
|
|
@ -106,6 +106,10 @@ func validateFileAuthenticationBackendArgon2id(configuration *schema.FileAuthent
|
|||
}
|
||||
|
||||
func validateLDAPAuthenticationBackend(configuration *schema.LDAPAuthenticationBackendConfiguration, validator *schema.StructValidator) {
|
||||
if configuration.Timeout == 0 {
|
||||
configuration.Timeout = schema.DefaultLDAPAuthenticationBackendConfiguration.Timeout
|
||||
}
|
||||
|
||||
if configuration.Implementation == "" {
|
||||
configuration.Implementation = schema.DefaultLDAPAuthenticationBackendConfiguration.Implementation
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package validator
|
|||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -518,26 +519,30 @@ func (suite *ActiveDirectoryAuthenticationBackendSuite) TestShouldSetActiveDirec
|
|||
suite.Assert().False(suite.validator.HasErrors())
|
||||
|
||||
suite.Assert().Equal(
|
||||
suite.configuration.LDAP.UsersFilter,
|
||||
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.UsersFilter)
|
||||
schema.DefaultLDAPAuthenticationBackendConfiguration.Timeout,
|
||||
suite.configuration.LDAP.Timeout)
|
||||
suite.Assert().Equal(
|
||||
suite.configuration.LDAP.UsernameAttribute,
|
||||
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.UsernameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.UsersFilter,
|
||||
suite.configuration.LDAP.UsersFilter)
|
||||
suite.Assert().Equal(
|
||||
suite.configuration.LDAP.DisplayNameAttribute,
|
||||
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.DisplayNameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.UsernameAttribute,
|
||||
suite.configuration.LDAP.UsernameAttribute)
|
||||
suite.Assert().Equal(
|
||||
suite.configuration.LDAP.MailAttribute,
|
||||
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.MailAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.DisplayNameAttribute,
|
||||
suite.configuration.LDAP.DisplayNameAttribute)
|
||||
suite.Assert().Equal(
|
||||
suite.configuration.LDAP.GroupsFilter,
|
||||
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.GroupsFilter)
|
||||
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.MailAttribute,
|
||||
suite.configuration.LDAP.MailAttribute)
|
||||
suite.Assert().Equal(
|
||||
suite.configuration.LDAP.GroupNameAttribute,
|
||||
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.GroupNameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.GroupsFilter,
|
||||
suite.configuration.LDAP.GroupsFilter)
|
||||
suite.Assert().Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.GroupNameAttribute,
|
||||
suite.configuration.LDAP.GroupNameAttribute)
|
||||
}
|
||||
|
||||
func (suite *ActiveDirectoryAuthenticationBackendSuite) TestShouldOnlySetDefaultsIfNotManuallyConfigured() {
|
||||
suite.configuration.LDAP.Timeout = time.Second * 2
|
||||
suite.configuration.LDAP.UsersFilter = "(&({username_attribute}={input})(objectCategory=person)(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=2))"
|
||||
suite.configuration.LDAP.UsernameAttribute = "cn"
|
||||
suite.configuration.LDAP.MailAttribute = "userPrincipalName"
|
||||
|
@ -548,23 +553,26 @@ func (suite *ActiveDirectoryAuthenticationBackendSuite) TestShouldOnlySetDefault
|
|||
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
|
||||
|
||||
suite.Assert().NotEqual(
|
||||
suite.configuration.LDAP.UsersFilter,
|
||||
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.UsersFilter)
|
||||
schema.DefaultLDAPAuthenticationBackendConfiguration.Timeout,
|
||||
suite.configuration.LDAP.Timeout)
|
||||
suite.Assert().NotEqual(
|
||||
suite.configuration.LDAP.UsernameAttribute,
|
||||
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.UsernameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.UsersFilter,
|
||||
suite.configuration.LDAP.UsersFilter)
|
||||
suite.Assert().NotEqual(
|
||||
suite.configuration.LDAP.DisplayNameAttribute,
|
||||
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.DisplayNameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.UsernameAttribute,
|
||||
suite.configuration.LDAP.UsernameAttribute)
|
||||
suite.Assert().NotEqual(
|
||||
suite.configuration.LDAP.MailAttribute,
|
||||
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.MailAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.DisplayNameAttribute,
|
||||
suite.configuration.LDAP.DisplayNameAttribute)
|
||||
suite.Assert().NotEqual(
|
||||
suite.configuration.LDAP.GroupsFilter,
|
||||
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.GroupsFilter)
|
||||
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.MailAttribute,
|
||||
suite.configuration.LDAP.MailAttribute)
|
||||
suite.Assert().NotEqual(
|
||||
suite.configuration.LDAP.GroupNameAttribute,
|
||||
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.GroupNameAttribute)
|
||||
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.GroupsFilter,
|
||||
suite.configuration.LDAP.GroupsFilter)
|
||||
suite.Assert().NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.GroupNameAttribute,
|
||||
suite.configuration.LDAP.GroupNameAttribute)
|
||||
}
|
||||
|
||||
func TestActiveDirectoryAuthenticationBackend(t *testing.T) {
|
||||
|
|
Loading…
Reference in New Issue