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
James Elliott 2021-08-05 14:30:00 +10:00 committed by GitHub
parent a3b14871ba
commit b2a49e1780
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 146 additions and 115 deletions

View File

@ -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

View File

@ -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.

View File

@ -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
}

View File

@ -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...)
}

View File

@ -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
}

View File

@ -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

View File

@ -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",
},

View File

@ -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
}

View File

@ -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) {