diff options
34 files changed, 3276 insertions, 2313 deletions
diff --git a/config.template.yml b/config.template.yml index a3181081d..2e01b7a15 100644 --- a/config.template.yml +++ b/config.template.yml @@ -459,6 +459,7 @@ identity_validation: ## Use StartTLS with the LDAP connection. # start_tls: false + ## TLS configuration. # tls: ## The server subject name to check the servers certificate against during the validation process. ## This option is not required if the certificate has a SAN which matches the address options hostname. @@ -495,6 +496,20 @@ identity_validation: # ... # -----END RSA PRIVATE KEY----- + ## Connection Pooling configuration. + # pooling: + ## Enable Pooling. + # enable: false + + ## Pool count. + # count: 5 + + ## Retries to obtain a connection during the timeout. + # retries: 2 + + ## Timeout before the attempt to obtain a connection fails. + # timeout: '10 seconds' + ## The distinguished name of the container searched for objects in the directory information tree. ## See also: additional_users_dn, additional_groups_dn. # base_dn: 'dc=example,dc=com' diff --git a/docs/content/configuration/first-factor/ldap.md b/docs/content/configuration/first-factor/ldap.md index 3d2043f5f..6730e302c 100644 --- a/docs/content/configuration/first-factor/ldap.md +++ b/docs/content/configuration/first-factor/ldap.md @@ -50,6 +50,11 @@ authentication_backend: -----BEGIN RSA PRIVATE KEY----- ... -----END RSA PRIVATE KEY----- + pooling: + enable: false + count: 5 + retries: 2 + timeout: '10 seconds' base_dn: '{{< sitevar name="domain" format="dn" nojs="DC=example,DC=com" >}}' additional_users_dn: 'OU=users' users_filter: '(&({username_attribute}={input})(objectClass=person))' @@ -155,6 +160,35 @@ By default Authelia uses the system certificate trust for TLS certificate verifi this. +### pooling + +The connection pooling configuration. + +#### enable + +{{< confkey type="boolean" default="false" required="no" >}} + +Enables the connection pooling functionality. + +#### count + +{{< confkey type="integer" default="5" required="no" >}} + +The number of open connections to be available in the pool at any given time. + +#### retries + +{{< confkey type="integer" default="2" required="no" >}} + +The number of attempts to obtain a free connecting that are made within the timeout period. This effectively splits the +timeout into chunks. + +#### timeout + +{{< confkey type="string,integer" syntax="duration" default="5 seconds" required="no" >}} + +The amount of time that we wait for a connection to become free in the pool before giving up and failing with an error. + ### base_dn {{< confkey type="string" required="situational" >}} diff --git a/docs/content/integration/openid-connect/stirling-pdf/index.md b/docs/content/integration/openid-connect/stirling-pdf/index.md index 36c5ae29b..912c95391 100644 --- a/docs/content/integration/openid-connect/stirling-pdf/index.md +++ b/docs/content/integration/openid-connect/stirling-pdf/index.md @@ -2,7 +2,7 @@ title: "Stirling-PDF" description: "Integrating Stirling-PDF with the Authelia OpenID Connect 1.0 Provider." summary: "" -date: 2025-02-23T17:51:47+10:00 +date: 2025-02-23T04:38:52+00:00 draft: false images: [] weight: 620 diff --git a/docs/content/reference/guides/authentication-method-references.md b/docs/content/reference/guides/authentication-method-references.md index 6470e680e..c00d35fdc 100644 --- a/docs/content/reference/guides/authentication-method-references.md +++ b/docs/content/reference/guides/authentication-method-references.md @@ -2,7 +2,7 @@ title: "Authentication Method Reference Values" description: "This guide shows a list of Authentication Method Reference Values based on RFC8176 and how they are implemented within Authelia" summary: "This guide shows a list of other frequently asked question documents as well as some general ones." -date: 2025-01-27T09:23:06+11:00 +date: 2025-02-23T16:08:49+11:00 draft: false images: [] weight: 220 diff --git a/docs/content/reference/guides/webauthn.md b/docs/content/reference/guides/webauthn.md index 71e8d9e57..2ff01c566 100644 --- a/docs/content/reference/guides/webauthn.md +++ b/docs/content/reference/guides/webauthn.md @@ -2,7 +2,7 @@ title: "WebAuthn" description: "A reference guide on various WebAuthn features and topics" summary: "This section contains reference documentation for Authelia's WebAuthn implementation and capabilities." -date: 2025-01-27T09:23:06+11:00 +date: 2025-02-23T16:08:49+11:00 draft: false images: [] weight: 220 diff --git a/docs/data/configkeys.json b/docs/data/configkeys.json index 9f3f8de57..a93d83920 100644 --- a/docs/data/configkeys.json +++ b/docs/data/configkeys.json @@ -305,6 +305,26 @@ "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_CERTIFICATE_CHAIN_FILE" }, { + "path": "authentication_backend.ldap.pooling.enable", + "secret": false, + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_POOLING_ENABLE" + }, + { + "path": "authentication_backend.ldap.pooling.count", + "secret": false, + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_POOLING_COUNT" + }, + { + "path": "authentication_backend.ldap.pooling.retries", + "secret": false, + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_POOLING_RETRIES" + }, + { + "path": "authentication_backend.ldap.pooling.timeout", + "secret": false, + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_POOLING_TIMEOUT" + }, + { "path": "authentication_backend.ldap.base_dn", "secret": false, "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_BASE_DN" diff --git a/docs/static/schemas/latest/json-schema/configuration.json b/docs/static/schemas/latest/json-schema/configuration.json index 0bbeac431..a6d722ba3 100644 --- a/docs/static/schemas/latest/json-schema/configuration.json +++ b/docs/static/schemas/latest/json-schema/configuration.json @@ -713,6 +713,11 @@ "title": "TLS", "description": "The LDAP directory server TLS connection properties." }, + "pooling": { + "$ref": "#/$defs/AuthenticationBackendLDAPPooling", + "title": "Pooling", + "description": "The LDAP Connection Pooling properties." + }, "base_dn": { "type": "string", "title": "Base DN", @@ -821,6 +826,44 @@ "type": "object", "description": "AuthenticationBackendLDAPAttributes represents the configuration related to LDAP server attributes." }, + "AuthenticationBackendLDAPPooling": { + "properties": { + "enable": { + "type": "boolean", + "title": "Enable", + "description": "Enable LDAP connection pooling.", + "default": false + }, + "count": { + "type": "integer", + "title": "Count", + "description": "The number of connections to keep open for LDAP connection pooling.", + "default": 5 + }, + "retries": { + "type": "integer", + "title": "Retries", + "description": "The number of attempts to retrieve a connection from the pool during the timeout.", + "default": 2 + }, + "timeout": { + "oneOf": [ + { + "type": "string", + "pattern": "^\\d+\\s*(y|M|w|d|h|m|s|ms|((year|month|week|day|hour|minute|second|millisecond)s?))(\\s*\\d+\\s*(y|M|w|d|h|m|s|ms|((year|month|week|day|hour|minute|second|millisecond)s?)))*$" + }, + { + "type": "integer", + "description": "The duration in seconds" + } + ], + "title": "Timeout", + "description": "The duration of time to wait for a connection to become available in the connection pool." + } + }, + "additionalProperties": false, + "type": "object" + }, "AuthenticationBackendPasswordReset": { "properties": { "disable": { diff --git a/docs/static/schemas/v4.38/json-schema/configuration.json b/docs/static/schemas/v4.38/json-schema/configuration.json index 0bbeac431..a6d722ba3 100644 --- a/docs/static/schemas/v4.38/json-schema/configuration.json +++ b/docs/static/schemas/v4.38/json-schema/configuration.json @@ -713,6 +713,11 @@ "title": "TLS", "description": "The LDAP directory server TLS connection properties." }, + "pooling": { + "$ref": "#/$defs/AuthenticationBackendLDAPPooling", + "title": "Pooling", + "description": "The LDAP Connection Pooling properties." + }, "base_dn": { "type": "string", "title": "Base DN", @@ -821,6 +826,44 @@ "type": "object", "description": "AuthenticationBackendLDAPAttributes represents the configuration related to LDAP server attributes." }, + "AuthenticationBackendLDAPPooling": { + "properties": { + "enable": { + "type": "boolean", + "title": "Enable", + "description": "Enable LDAP connection pooling.", + "default": false + }, + "count": { + "type": "integer", + "title": "Count", + "description": "The number of connections to keep open for LDAP connection pooling.", + "default": 5 + }, + "retries": { + "type": "integer", + "title": "Retries", + "description": "The number of attempts to retrieve a connection from the pool during the timeout.", + "default": 2 + }, + "timeout": { + "oneOf": [ + { + "type": "string", + "pattern": "^\\d+\\s*(y|M|w|d|h|m|s|ms|((year|month|week|day|hour|minute|second|millisecond)s?))(\\s*\\d+\\s*(y|M|w|d|h|m|s|ms|((year|month|week|day|hour|minute|second|millisecond)s?)))*$" + }, + { + "type": "integer", + "description": "The duration in seconds" + } + ], + "title": "Timeout", + "description": "The duration of time to wait for a connection to become available in the connection pool." + } + }, + "additionalProperties": false, + "type": "object" + }, "AuthenticationBackendPasswordReset": { "properties": { "disable": { diff --git a/docs/static/schemas/v4.39/json-schema/configuration.json b/docs/static/schemas/v4.39/json-schema/configuration.json index 221ec38d6..39d9ad7d4 100644 --- a/docs/static/schemas/v4.39/json-schema/configuration.json +++ b/docs/static/schemas/v4.39/json-schema/configuration.json @@ -734,6 +734,11 @@ "title": "TLS", "description": "The LDAP directory server TLS connection properties." }, + "pooling": { + "$ref": "#/$defs/AuthenticationBackendLDAPPooling", + "title": "Pooling", + "description": "The LDAP Connection Pooling properties." + }, "base_dn": { "type": "string", "title": "Base DN", @@ -968,6 +973,44 @@ "additionalProperties": false, "type": "object" }, + "AuthenticationBackendLDAPPooling": { + "properties": { + "enable": { + "type": "boolean", + "title": "Enable", + "description": "Enable LDAP connection pooling.", + "default": false + }, + "count": { + "type": "integer", + "title": "Count", + "description": "The number of connections to keep open for LDAP connection pooling.", + "default": 5 + }, + "retries": { + "type": "integer", + "title": "Retries", + "description": "The number of attempts to retrieve a connection from the pool during the timeout.", + "default": 2 + }, + "timeout": { + "oneOf": [ + { + "type": "string", + "pattern": "^\\d+\\s*(y|M|w|d|h|m|s|ms|((year|month|week|day|hour|minute|second|millisecond)s?))(\\s*\\d+\\s*(y|M|w|d|h|m|s|ms|((year|month|week|day|hour|minute|second|millisecond)s?)))*$" + }, + { + "type": "integer", + "description": "The duration in seconds" + } + ], + "title": "Timeout", + "description": "The duration of time to wait for a connection to become available in the connection pool." + } + }, + "additionalProperties": false, + "type": "object" + }, "AuthenticationBackendPasswordReset": { "properties": { "disable": { diff --git a/internal/authentication/file_user_provider.go b/internal/authentication/file_user_provider.go index 02e0faa81..0dd84e085 100644 --- a/internal/authentication/file_user_provider.go +++ b/internal/authentication/file_user_provider.go @@ -25,7 +25,7 @@ type FileUserProvider struct { config *schema.AuthenticationBackendFile hash algorithm.Hash database FileUserProviderDatabase - mutex *sync.Mutex + mutex sync.Mutex timeoutReload time.Time } @@ -33,7 +33,6 @@ type FileUserProvider struct { func NewFileUserProvider(config *schema.AuthenticationBackendFile) (provider *FileUserProvider) { return &FileUserProvider{ config: config, - mutex: &sync.Mutex{}, timeoutReload: time.Now().Add(-1 * time.Second), database: NewFileUserDatabase(config.Path, config.Search.Email, config.Search.CaseInsensitive, getExtra(config)), } @@ -77,6 +76,10 @@ func (p *FileUserProvider) Reload() (reloaded bool, err error) { return true, nil } +func (p *FileUserProvider) Shutdown() (err error) { + return nil +} + // CheckUserPassword checks if provided password matches for the given user. func (p *FileUserProvider) CheckUserPassword(username string, password string) (match bool, err error) { var details FileUserDatabaseUserDetails diff --git a/internal/authentication/file_user_provider_test.go b/internal/authentication/file_user_provider_test.go index 1da33a389..0925d893f 100644 --- a/internal/authentication/file_user_provider_test.go +++ b/internal/authentication/file_user_provider_test.go @@ -86,7 +86,6 @@ func TestShouldNotPanicOnNilDB(t *testing.T) { provider := &FileUserProvider{ config: &schema.AuthenticationBackendFile{Path: f, Password: schema.DefaultPasswordConfig}, - mutex: &sync.Mutex{}, timeoutReload: time.Now().Add(-1 * time.Second), } @@ -102,7 +101,7 @@ func TestShouldHandleBadConfig(t *testing.T) { provider := &FileUserProvider{ config: &schema.AuthenticationBackendFile{Path: f, Password: schema.DefaultPasswordConfig, ExtraAttributes: map[string]schema.AuthenticationBackendExtraAttribute{"example": {ValueType: "integer"}}}, - mutex: &sync.Mutex{}, + mutex: sync.Mutex{}, timeoutReload: time.Now().Add(-1 * time.Second), } diff --git a/internal/authentication/gen.go b/internal/authentication/gen.go index 3e92fd178..c908f02d7 100644 --- a/internal/authentication/gen.go +++ b/internal/authentication/gen.go @@ -3,7 +3,8 @@ package authentication // This file is used to generate mocks. You can generate all mocks using the // command `go generate github.com/authelia/authelia/v4/internal/authentication`. -//go:generate mockgen -package authentication -destination ldap_client_mock_test.go -mock_names LDAPClient=MockLDAPClient github.com/authelia/authelia/v4/internal/authentication LDAPClient +//go:generate mockgen -package authentication -destination ldap_client_mock_test.go -mock_names Client=MockLDAPClient github.com/go-ldap/ldap/v3 Client +//go:generate mockgen -package authentication -destination ldap_client_dialer_test.go -mock_names LDAPClientDialer=MockLDAPClientDialer github.com/authelia/authelia/v4/internal/authentication LDAPClientDialer //go:generate mockgen -package authentication -destination ldap_client_factory_mock_test.go -mock_names LDAPClientFactory=MockLDAPClientFactory github.com/authelia/authelia/v4/internal/authentication LDAPClientFactory //go:generate mockgen -package authentication -destination file_user_provider_database_mock_test.go -mock_names FileUserProviderDatabase=MockFileUserDatabase github.com/authelia/authelia/v4/internal/authentication FileUserProviderDatabase //go:generate mockgen -package authentication -destination file_user_provider_hash_mock_test.go -mock_names Hash=MockHash github.com/go-crypt/crypt/algorithm Hash diff --git a/internal/authentication/ldap_client_dialer.go b/internal/authentication/ldap_client_dialer.go new file mode 100644 index 000000000..4577e2eae --- /dev/null +++ b/internal/authentication/ldap_client_dialer.go @@ -0,0 +1,31 @@ +package authentication + +import ( + "fmt" + + "github.com/go-ldap/ldap/v3" +) + +// LDAPClientDialer is an abstract type that dials a ldap.Client. +type LDAPClientDialer interface { + // DialURL takes a single address and dials it returning the ldap.Client. + DialURL(addr string, opts ...ldap.DialOpt) (client ldap.Client, err error) +} + +// NewLDAPClientDialerStandard returns a new *LDAPClientDialerStandard. +func NewLDAPClientDialerStandard() *LDAPClientDialerStandard { + return &LDAPClientDialerStandard{} +} + +// LDAPClientDialerStandard is a concrete type that dials a ldap.Client and returns it, implementing the +// LDAPClientDialer. +type LDAPClientDialerStandard struct{} + +// DialURL takes a single address and dials it returning the ldap.Client. +func (d *LDAPClientDialerStandard) DialURL(addr string, opts ...ldap.DialOpt) (client ldap.Client, err error) { + if client, err = ldap.DialURL(addr, opts...); err != nil { + return nil, fmt.Errorf("failed to dial LDAP server at %s: %w", addr, err) + } + + return client, nil +} diff --git a/internal/authentication/ldap_client_dialer_test.go b/internal/authentication/ldap_client_dialer_test.go new file mode 100644 index 000000000..40228a8ea --- /dev/null +++ b/internal/authentication/ldap_client_dialer_test.go @@ -0,0 +1,61 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/authelia/authelia/v4/internal/authentication (interfaces: LDAPClientDialer) +// +// Generated by this command: +// +// mockgen -package authentication -destination ldap_client_dialer_test.go -mock_names LDAPClientDialer=MockLDAPClientDialer github.com/authelia/authelia/v4/internal/authentication LDAPClientDialer +// + +// Package authentication is a generated GoMock package. +package authentication + +import ( + reflect "reflect" + + ldap "github.com/go-ldap/ldap/v3" + gomock "go.uber.org/mock/gomock" +) + +// MockLDAPClientDialer is a mock of LDAPClientDialer interface. +type MockLDAPClientDialer struct { + ctrl *gomock.Controller + recorder *MockLDAPClientDialerMockRecorder + isgomock struct{} +} + +// MockLDAPClientDialerMockRecorder is the mock recorder for MockLDAPClientDialer. +type MockLDAPClientDialerMockRecorder struct { + mock *MockLDAPClientDialer +} + +// NewMockLDAPClientDialer creates a new mock instance. +func NewMockLDAPClientDialer(ctrl *gomock.Controller) *MockLDAPClientDialer { + mock := &MockLDAPClientDialer{ctrl: ctrl} + mock.recorder = &MockLDAPClientDialerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockLDAPClientDialer) EXPECT() *MockLDAPClientDialerMockRecorder { + return m.recorder +} + +// DialURL mocks base method. +func (m *MockLDAPClientDialer) DialURL(addr string, opts ...ldap.DialOpt) (ldap.Client, error) { + m.ctrl.T.Helper() + varargs := []any{addr} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DialURL", varargs...) + ret0, _ := ret[0].(ldap.Client) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DialURL indicates an expected call of DialURL. +func (mr *MockLDAPClientDialerMockRecorder) DialURL(addr any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{addr}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DialURL", reflect.TypeOf((*MockLDAPClientDialer)(nil).DialURL), varargs...) +} diff --git a/internal/authentication/ldap_client_factory.go b/internal/authentication/ldap_client_factory.go index d360f05b3..9bb55502c 100644 --- a/internal/authentication/ldap_client_factory.go +++ b/internal/authentication/ldap_client_factory.go @@ -1,18 +1,314 @@ package authentication import ( + "context" + "crypto/tls" + "crypto/x509" + "errors" + "fmt" + "net" + "sync" + "time" + "github.com/go-ldap/ldap/v3" + + "github.com/authelia/authelia/v4/internal/configuration/schema" + "github.com/authelia/authelia/v4/internal/utils" ) -// ProductionLDAPClientFactory the production implementation of an ldap connection factory. -type ProductionLDAPClientFactory struct{} +// LDAPClientFactory an interface describing factories that produce LDAPConnection implementations. +type LDAPClientFactory interface { + Initialize() (err error) + GetClient(opts ...LDAPClientFactoryOption) (client ldap.Client, err error) + ReleaseClient(client ldap.Client) (err error) + Close() (err error) +} + +// NewStandardLDAPClientFactory create a concrete ldap connection factory. +func NewStandardLDAPClientFactory(config *schema.AuthenticationBackendLDAP, certs *x509.CertPool, dialer LDAPClientDialer) LDAPClientFactory { + if dialer == nil { + dialer = &LDAPClientDialerStandard{} + } + + tlsc := utils.NewTLSConfig(config.TLS, certs) + + opts := []ldap.DialOpt{ + ldap.DialWithDialer(&net.Dialer{Timeout: config.Timeout}), + ldap.DialWithTLSConfig(tlsc), + } + + return &StandardLDAPClientFactory{ + config: config, + tls: tlsc, + opts: opts, + dialer: dialer, + } +} + +// StandardLDAPClientFactory the production implementation of an ldap connection factory. +type StandardLDAPClientFactory struct { + config *schema.AuthenticationBackendLDAP + tls *tls.Config + opts []ldap.DialOpt + dialer LDAPClientDialer +} + +func (f *StandardLDAPClientFactory) Initialize() (err error) { + return nil +} + +func (f *StandardLDAPClientFactory) GetClient(opts ...LDAPClientFactoryOption) (client ldap.Client, err error) { + return getLDAPClient(f.config.Address.String(), f.config.User, f.config.Password, f.dialer, f.tls, f.config.StartTLS, f.opts, opts...) +} + +func (f *StandardLDAPClientFactory) ReleaseClient(client ldap.Client) (err error) { + if err = client.Close(); err != nil { + return fmt.Errorf("error occurred closing LDAP client: %w", err) + } + + return nil +} -// NewProductionLDAPClientFactory create a concrete ldap connection factory. -func NewProductionLDAPClientFactory() *ProductionLDAPClientFactory { - return &ProductionLDAPClientFactory{} +func (f *StandardLDAPClientFactory) Close() (err error) { + return nil } -// DialURL creates a client from an LDAP URL when successful. -func (f *ProductionLDAPClientFactory) DialURL(addr string, opts ...ldap.DialOpt) (client LDAPClient, err error) { - return ldap.DialURL(addr, opts...) +// NewPooledLDAPClientFactory is a decorator for a LDAPClientFactory that performs pooling. +func NewPooledLDAPClientFactory(config *schema.AuthenticationBackendLDAP, certs *x509.CertPool, dialer LDAPClientDialer) (factory LDAPClientFactory) { + if dialer == nil { + dialer = &LDAPClientDialerStandard{} + } + + tlsc := utils.NewTLSConfig(config.TLS, certs) + + opts := []ldap.DialOpt{ + ldap.DialWithDialer(&net.Dialer{Timeout: config.Timeout}), + ldap.DialWithTLSConfig(tlsc), + } + + if config.Pooling.Count <= 0 { + config.Pooling.Count = 3 + } + + if config.Pooling.Retries <= 0 { + config.Pooling.Retries = 3 + } + + if config.Pooling.Timeout <= 0 { + config.Pooling.Timeout = time.Second + } + + sleep := config.Pooling.Timeout / time.Duration(config.Pooling.Retries) + + return &PooledLDAPClientFactory{ + config: config, + tls: tlsc, + opts: opts, + dialer: dialer, + sleep: sleep, + } +} + +// PooledLDAPClientFactory is a LDAPClientFactory that takes another LDAPClientFactory and pools the +// factory generated connections using a channel for thread safety. +type PooledLDAPClientFactory struct { + config *schema.AuthenticationBackendLDAP + tls *tls.Config + opts []ldap.DialOpt + dialer LDAPClientDialer + + pool chan *LDAPClientPooled + mu sync.Mutex + + sleep time.Duration + + closing bool +} + +func (f *PooledLDAPClientFactory) Initialize() (err error) { + f.mu.Lock() + + defer f.mu.Unlock() + + if f.pool != nil { + return nil + } + + f.pool = make(chan *LDAPClientPooled, f.config.Pooling.Count) + + var ( + errs []error + client *LDAPClientPooled + ) + + for i := 0; i < f.config.Pooling.Count; i++ { + if client, err = f.new(); err != nil { + errs = append(errs, err) + + continue + } + + f.pool <- client + } + + if len(errs) == f.config.Pooling.Count { + return fmt.Errorf("errors occurred initializing the client pool: no connections could be established") + } + + return nil +} + +// GetClient opens new client using the pool. +func (f *PooledLDAPClientFactory) GetClient(opts ...LDAPClientFactoryOption) (conn ldap.Client, err error) { + if len(opts) != 0 { + return getLDAPClient(f.config.Address.String(), f.config.User, f.config.Password, f.dialer, f.tls, f.config.StartTLS, f.opts, opts...) + } + + return f.acquire(context.Background()) +} + +// The new function creates a pool based client. This function is not thread safe. +func (f *PooledLDAPClientFactory) new() (pooled *LDAPClientPooled, err error) { + var client ldap.Client + + if client, err = getLDAPClient(f.config.Address.String(), f.config.User, f.config.Password, f.dialer, f.tls, f.config.StartTLS, f.opts); err != nil { + return nil, fmt.Errorf("error occurred establishing new client for the pool: %w", err) + } + + return &LDAPClientPooled{Client: client}, nil +} + +// ReleaseClient returns a client using the pool or closes it. +func (f *PooledLDAPClientFactory) ReleaseClient(client ldap.Client) (err error) { + f.mu.Lock() + + defer f.mu.Unlock() + + if f.closing { + return client.Close() + } + + if pool, ok := client.(*LDAPClientPooled); !ok || cap(f.pool) == len(f.pool) { + // Prevent extra or non-pool connections from being returned into the pool. + return client.Close() + } else { + f.pool <- pool + } + + return nil +} + +func (f *PooledLDAPClientFactory) acquire(ctx context.Context) (client *LDAPClientPooled, err error) { + f.mu.Lock() + + defer f.mu.Unlock() + + if f.closing { + return nil, fmt.Errorf("error acquiring client: the pool is closed") + } + + if cap(f.pool) != f.config.Pooling.Count { + if err = f.Initialize(); err != nil { + return nil, err + } + } + + ctx, cancel := context.WithTimeout(ctx, f.config.Pooling.Timeout) + defer cancel() + + select { + case <-ctx.Done(): + return nil, ctx.Err() + case client = <-f.pool: + if client.IsClosing() || client.Client == nil { + for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + if client, err = f.new(); err != nil { + time.Sleep(f.sleep) + + continue + } + + return client, nil + } + } + } + + return client, nil + } +} + +func (f *PooledLDAPClientFactory) Close() (err error) { + f.mu.Lock() + + defer f.mu.Unlock() + + f.closing = true + + close(f.pool) + + var errs []error + + for client := range f.pool { + if client.IsClosing() { + continue + } + + if err = client.Close(); err != nil { + errs = append(errs, err) + } + } + + if len(errs) > 0 { + return fmt.Errorf("errors occurred closing the client pool: %w", errors.Join(errs...)) + } + + return nil +} + +// LDAPClientPooled is a decorator for the ldap.Client which handles the pooling functionality. i.e. prevents the client +// from being closed and instead relinquishes the connection back to the pool. +type LDAPClientPooled struct { + ldap.Client +} + +func getLDAPClient(address, username, password string, dialer LDAPClientDialer, tls *tls.Config, startTLS bool, dialerOpts []ldap.DialOpt, opts ...LDAPClientFactoryOption) (client ldap.Client, err error) { + config := &LDAPClientFactoryOptions{ + Address: address, + Username: username, + Password: password, + } + + for _, opt := range opts { + opt(config) + } + + if client, err = dialer.DialURL(config.Address, dialerOpts...); err != nil { + return nil, fmt.Errorf("error occurred dialing address: %w", err) + } + + if tls != nil && startTLS { + if err = client.StartTLS(tls); err != nil { + _ = client.Close() + + return nil, fmt.Errorf("error occurred performing starttls: %w", err) + } + } + + if config.Password == "" { + err = client.UnauthenticatedBind(config.Username) + } else { + err = client.Bind(config.Username, config.Password) + } + + if err != nil { + _ = client.Close() + + return nil, fmt.Errorf("error occurred performing bind: %w", err) + } + + return client, nil } diff --git a/internal/authentication/ldap_client_factory_config.go b/internal/authentication/ldap_client_factory_config.go new file mode 100644 index 000000000..c66222c79 --- /dev/null +++ b/internal/authentication/ldap_client_factory_config.go @@ -0,0 +1,27 @@ +package authentication + +type LDAPClientFactoryOptions struct { + Address string + Username string + Password string +} + +type LDAPClientFactoryOption func(*LDAPClientFactoryOptions) + +func WithAddress(address string) func(*LDAPClientFactoryOptions) { + return func(settings *LDAPClientFactoryOptions) { + settings.Address = address + } +} + +func WithUsername(username string) func(*LDAPClientFactoryOptions) { + return func(settings *LDAPClientFactoryOptions) { + settings.Username = username + } +} + +func WithPassword(password string) func(*LDAPClientFactoryOptions) { + return func(settings *LDAPClientFactoryOptions) { + settings.Password = password + } +} diff --git a/internal/authentication/ldap_client_factory_mock_test.go b/internal/authentication/ldap_client_factory_mock_test.go index f8ce5bb81..fe0a8c2e4 100644 --- a/internal/authentication/ldap_client_factory_mock_test.go +++ b/internal/authentication/ldap_client_factory_mock_test.go @@ -40,22 +40,63 @@ func (m *MockLDAPClientFactory) EXPECT() *MockLDAPClientFactoryMockRecorder { return m.recorder } -// DialURL mocks base method. -func (m *MockLDAPClientFactory) DialURL(addr string, opts ...ldap.DialOpt) (LDAPClient, error) { +// Close mocks base method. +func (m *MockLDAPClientFactory) Close() error { m.ctrl.T.Helper() - varargs := []any{addr} + ret := m.ctrl.Call(m, "Close") + ret0, _ := ret[0].(error) + return ret0 +} + +// Close indicates an expected call of Close. +func (mr *MockLDAPClientFactoryMockRecorder) Close() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockLDAPClientFactory)(nil).Close)) +} + +// GetClient mocks base method. +func (m *MockLDAPClientFactory) GetClient(opts ...LDAPClientFactoryOption) (ldap.Client, error) { + m.ctrl.T.Helper() + varargs := []any{} for _, a := range opts { varargs = append(varargs, a) } - ret := m.ctrl.Call(m, "DialURL", varargs...) - ret0, _ := ret[0].(LDAPClient) + ret := m.ctrl.Call(m, "GetClient", varargs...) + ret0, _ := ret[0].(ldap.Client) ret1, _ := ret[1].(error) return ret0, ret1 } -// DialURL indicates an expected call of DialURL. -func (mr *MockLDAPClientFactoryMockRecorder) DialURL(addr any, opts ...any) *gomock.Call { +// GetClient indicates an expected call of GetClient. +func (mr *MockLDAPClientFactoryMockRecorder) GetClient(opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClient", reflect.TypeOf((*MockLDAPClientFactory)(nil).GetClient), opts...) +} + +// Initialize mocks base method. +func (m *MockLDAPClientFactory) Initialize() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Initialize") + ret0, _ := ret[0].(error) + return ret0 +} + +// Initialize indicates an expected call of Initialize. +func (mr *MockLDAPClientFactoryMockRecorder) Initialize() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Initialize", reflect.TypeOf((*MockLDAPClientFactory)(nil).Initialize)) +} + +// ReleaseClient mocks base method. +func (m *MockLDAPClientFactory) ReleaseClient(client ldap.Client) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReleaseClient", client) + ret0, _ := ret[0].(error) + return ret0 +} + +// ReleaseClient indicates an expected call of ReleaseClient. +func (mr *MockLDAPClientFactoryMockRecorder) ReleaseClient(client any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{addr}, opts...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DialURL", reflect.TypeOf((*MockLDAPClientFactory)(nil).DialURL), varargs...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReleaseClient", reflect.TypeOf((*MockLDAPClientFactory)(nil).ReleaseClient), client) } diff --git a/internal/authentication/ldap_client_mock_test.go b/internal/authentication/ldap_client_mock_test.go index 3a492be2e..055beecbe 100644 --- a/internal/authentication/ldap_client_mock_test.go +++ b/internal/authentication/ldap_client_mock_test.go @@ -1,15 +1,16 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/authelia/authelia/v4/internal/authentication (interfaces: LDAPClient) +// Source: github.com/go-ldap/ldap/v3 (interfaces: Client) // // Generated by this command: // -// mockgen -package authentication -destination ldap_client_mock_test.go -mock_names LDAPClient=MockLDAPClient github.com/authelia/authelia/v4/internal/authentication LDAPClient +// mockgen -package authentication -destination ldap_client_mock_test.go -mock_names Client=MockLDAPClient github.com/go-ldap/ldap/v3 Client // // Package authentication is a generated GoMock package. package authentication import ( + context "context" tls "crypto/tls" reflect "reflect" time "time" @@ -18,7 +19,7 @@ import ( gomock "go.uber.org/mock/gomock" ) -// MockLDAPClient is a mock of LDAPClient interface. +// MockLDAPClient is a mock of Client interface. type MockLDAPClient struct { ctrl *gomock.Controller recorder *MockLDAPClientMockRecorder @@ -43,17 +44,17 @@ func (m *MockLDAPClient) EXPECT() *MockLDAPClientMockRecorder { } // Add mocks base method. -func (m *MockLDAPClient) Add(request *ldap.AddRequest) error { +func (m *MockLDAPClient) Add(arg0 *ldap.AddRequest) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Add", request) + ret := m.ctrl.Call(m, "Add", arg0) ret0, _ := ret[0].(error) return ret0 } // Add indicates an expected call of Add. -func (mr *MockLDAPClientMockRecorder) Add(request any) *gomock.Call { +func (mr *MockLDAPClientMockRecorder) Add(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Add", reflect.TypeOf((*MockLDAPClient)(nil).Add), request) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Add", reflect.TypeOf((*MockLDAPClient)(nil).Add), arg0) } // Bind mocks base method. @@ -100,160 +101,146 @@ func (mr *MockLDAPClientMockRecorder) Compare(dn, attribute, value any) *gomock. } // Del mocks base method. -func (m *MockLDAPClient) Del(request *ldap.DelRequest) error { +func (m *MockLDAPClient) Del(arg0 *ldap.DelRequest) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Del", request) + ret := m.ctrl.Call(m, "Del", arg0) ret0, _ := ret[0].(error) return ret0 } // Del indicates an expected call of Del. -func (mr *MockLDAPClientMockRecorder) Del(request any) *gomock.Call { +func (mr *MockLDAPClientMockRecorder) Del(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Del", reflect.TypeOf((*MockLDAPClient)(nil).Del), request) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Del", reflect.TypeOf((*MockLDAPClient)(nil).Del), arg0) } -// DigestMD5Bind mocks base method. -func (m *MockLDAPClient) DigestMD5Bind(request *ldap.DigestMD5BindRequest) (*ldap.DigestMD5BindResult, error) { +// DirSync mocks base method. +func (m *MockLDAPClient) DirSync(searchRequest *ldap.SearchRequest, flags, maxAttrCount int64, cookie []byte) (*ldap.SearchResult, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DigestMD5Bind", request) - ret0, _ := ret[0].(*ldap.DigestMD5BindResult) + ret := m.ctrl.Call(m, "DirSync", searchRequest, flags, maxAttrCount, cookie) + ret0, _ := ret[0].(*ldap.SearchResult) ret1, _ := ret[1].(error) return ret0, ret1 } -// DigestMD5Bind indicates an expected call of DigestMD5Bind. -func (mr *MockLDAPClientMockRecorder) DigestMD5Bind(request any) *gomock.Call { +// DirSync indicates an expected call of DirSync. +func (mr *MockLDAPClientMockRecorder) DirSync(searchRequest, flags, maxAttrCount, cookie any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DigestMD5Bind", reflect.TypeOf((*MockLDAPClient)(nil).DigestMD5Bind), request) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DirSync", reflect.TypeOf((*MockLDAPClient)(nil).DirSync), searchRequest, flags, maxAttrCount, cookie) } -// ExternalBind mocks base method. -func (m *MockLDAPClient) ExternalBind() error { +// DirSyncAsync mocks base method. +func (m *MockLDAPClient) DirSyncAsync(ctx context.Context, searchRequest *ldap.SearchRequest, bufferSize int, flags, maxAttrCount int64, cookie []byte) ldap.Response { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ExternalBind") - ret0, _ := ret[0].(error) + ret := m.ctrl.Call(m, "DirSyncAsync", ctx, searchRequest, bufferSize, flags, maxAttrCount, cookie) + ret0, _ := ret[0].(ldap.Response) return ret0 } -// ExternalBind indicates an expected call of ExternalBind. -func (mr *MockLDAPClientMockRecorder) ExternalBind() *gomock.Call { +// DirSyncAsync indicates an expected call of DirSyncAsync. +func (mr *MockLDAPClientMockRecorder) DirSyncAsync(ctx, searchRequest, bufferSize, flags, maxAttrCount, cookie any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExternalBind", reflect.TypeOf((*MockLDAPClient)(nil).ExternalBind)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DirSyncAsync", reflect.TypeOf((*MockLDAPClient)(nil).DirSyncAsync), ctx, searchRequest, bufferSize, flags, maxAttrCount, cookie) } -// IsClosing mocks base method. -func (m *MockLDAPClient) IsClosing() bool { +// Extended mocks base method. +func (m *MockLDAPClient) Extended(arg0 *ldap.ExtendedRequest) (*ldap.ExtendedResponse, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsClosing") - ret0, _ := ret[0].(bool) - return ret0 + ret := m.ctrl.Call(m, "Extended", arg0) + ret0, _ := ret[0].(*ldap.ExtendedResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 } -// IsClosing indicates an expected call of IsClosing. -func (mr *MockLDAPClientMockRecorder) IsClosing() *gomock.Call { +// Extended indicates an expected call of Extended. +func (mr *MockLDAPClientMockRecorder) Extended(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsClosing", reflect.TypeOf((*MockLDAPClient)(nil).IsClosing)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Extended", reflect.TypeOf((*MockLDAPClient)(nil).Extended), arg0) } -// MD5Bind mocks base method. -func (m *MockLDAPClient) MD5Bind(host, username, password string) error { +// ExternalBind mocks base method. +func (m *MockLDAPClient) ExternalBind() error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "MD5Bind", host, username, password) + ret := m.ctrl.Call(m, "ExternalBind") ret0, _ := ret[0].(error) return ret0 } -// MD5Bind indicates an expected call of MD5Bind. -func (mr *MockLDAPClientMockRecorder) MD5Bind(host, username, password any) *gomock.Call { +// ExternalBind indicates an expected call of ExternalBind. +func (mr *MockLDAPClientMockRecorder) ExternalBind() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MD5Bind", reflect.TypeOf((*MockLDAPClient)(nil).MD5Bind), host, username, password) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExternalBind", reflect.TypeOf((*MockLDAPClient)(nil).ExternalBind)) } -// Modify mocks base method. -func (m *MockLDAPClient) Modify(request *ldap.ModifyRequest) error { +// GetLastError mocks base method. +func (m *MockLDAPClient) GetLastError() error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Modify", request) + ret := m.ctrl.Call(m, "GetLastError") ret0, _ := ret[0].(error) return ret0 } -// Modify indicates an expected call of Modify. -func (mr *MockLDAPClientMockRecorder) Modify(request any) *gomock.Call { +// GetLastError indicates an expected call of GetLastError. +func (mr *MockLDAPClientMockRecorder) GetLastError() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Modify", reflect.TypeOf((*MockLDAPClient)(nil).Modify), request) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLastError", reflect.TypeOf((*MockLDAPClient)(nil).GetLastError)) } -// ModifyDN mocks base method. -func (m_2 *MockLDAPClient) ModifyDN(m *ldap.ModifyDNRequest) error { - m_2.ctrl.T.Helper() - ret := m_2.ctrl.Call(m_2, "ModifyDN", m) - ret0, _ := ret[0].(error) - return ret0 -} - -// ModifyDN indicates an expected call of ModifyDN. -func (mr *MockLDAPClientMockRecorder) ModifyDN(m any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyDN", reflect.TypeOf((*MockLDAPClient)(nil).ModifyDN), m) -} - -// ModifyWithResult mocks base method. -func (m *MockLDAPClient) ModifyWithResult(request *ldap.ModifyRequest) (*ldap.ModifyResult, error) { +// IsClosing mocks base method. +func (m *MockLDAPClient) IsClosing() bool { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ModifyWithResult", request) - ret0, _ := ret[0].(*ldap.ModifyResult) - ret1, _ := ret[1].(error) - return ret0, ret1 + ret := m.ctrl.Call(m, "IsClosing") + ret0, _ := ret[0].(bool) + return ret0 } -// ModifyWithResult indicates an expected call of ModifyWithResult. -func (mr *MockLDAPClientMockRecorder) ModifyWithResult(request any) *gomock.Call { +// IsClosing indicates an expected call of IsClosing. +func (mr *MockLDAPClientMockRecorder) IsClosing() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyWithResult", reflect.TypeOf((*MockLDAPClient)(nil).ModifyWithResult), request) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsClosing", reflect.TypeOf((*MockLDAPClient)(nil).IsClosing)) } -// NTLMBind mocks base method. -func (m *MockLDAPClient) NTLMBind(domain, username, password string) error { +// Modify mocks base method. +func (m *MockLDAPClient) Modify(arg0 *ldap.ModifyRequest) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NTLMBind", domain, username, password) + ret := m.ctrl.Call(m, "Modify", arg0) ret0, _ := ret[0].(error) return ret0 } -// NTLMBind indicates an expected call of NTLMBind. -func (mr *MockLDAPClientMockRecorder) NTLMBind(domain, username, password any) *gomock.Call { +// Modify indicates an expected call of Modify. +func (mr *MockLDAPClientMockRecorder) Modify(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NTLMBind", reflect.TypeOf((*MockLDAPClient)(nil).NTLMBind), domain, username, password) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Modify", reflect.TypeOf((*MockLDAPClient)(nil).Modify), arg0) } -// NTLMBindWithHash mocks base method. -func (m *MockLDAPClient) NTLMBindWithHash(domain, username, hash string) error { +// ModifyDN mocks base method. +func (m *MockLDAPClient) ModifyDN(arg0 *ldap.ModifyDNRequest) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NTLMBindWithHash", domain, username, hash) + ret := m.ctrl.Call(m, "ModifyDN", arg0) ret0, _ := ret[0].(error) return ret0 } -// NTLMBindWithHash indicates an expected call of NTLMBindWithHash. -func (mr *MockLDAPClientMockRecorder) NTLMBindWithHash(domain, username, hash any) *gomock.Call { +// ModifyDN indicates an expected call of ModifyDN. +func (mr *MockLDAPClientMockRecorder) ModifyDN(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NTLMBindWithHash", reflect.TypeOf((*MockLDAPClient)(nil).NTLMBindWithHash), domain, username, hash) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyDN", reflect.TypeOf((*MockLDAPClient)(nil).ModifyDN), arg0) } -// NTLMChallengeBind mocks base method. -func (m *MockLDAPClient) NTLMChallengeBind(request *ldap.NTLMBindRequest) (*ldap.NTLMBindResult, error) { +// ModifyWithResult mocks base method. +func (m *MockLDAPClient) ModifyWithResult(arg0 *ldap.ModifyRequest) (*ldap.ModifyResult, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NTLMChallengeBind", request) - ret0, _ := ret[0].(*ldap.NTLMBindResult) + ret := m.ctrl.Call(m, "ModifyWithResult", arg0) + ret0, _ := ret[0].(*ldap.ModifyResult) ret1, _ := ret[1].(error) return ret0, ret1 } -// NTLMChallengeBind indicates an expected call of NTLMChallengeBind. -func (mr *MockLDAPClientMockRecorder) NTLMChallengeBind(request any) *gomock.Call { +// ModifyWithResult indicates an expected call of ModifyWithResult. +func (mr *MockLDAPClientMockRecorder) ModifyWithResult(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NTLMChallengeBind", reflect.TypeOf((*MockLDAPClient)(nil).NTLMChallengeBind), request) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyWithResult", reflect.TypeOf((*MockLDAPClient)(nil).ModifyWithResult), arg0) } // NTLMUnauthenticatedBind mocks base method. @@ -271,89 +258,129 @@ func (mr *MockLDAPClientMockRecorder) NTLMUnauthenticatedBind(domain, username a } // PasswordModify mocks base method. -func (m *MockLDAPClient) PasswordModify(request *ldap.PasswordModifyRequest) (*ldap.PasswordModifyResult, error) { +func (m *MockLDAPClient) PasswordModify(arg0 *ldap.PasswordModifyRequest) (*ldap.PasswordModifyResult, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PasswordModify", request) + ret := m.ctrl.Call(m, "PasswordModify", arg0) ret0, _ := ret[0].(*ldap.PasswordModifyResult) ret1, _ := ret[1].(error) return ret0, ret1 } // PasswordModify indicates an expected call of PasswordModify. -func (mr *MockLDAPClientMockRecorder) PasswordModify(request any) *gomock.Call { +func (mr *MockLDAPClientMockRecorder) PasswordModify(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PasswordModify", reflect.TypeOf((*MockLDAPClient)(nil).PasswordModify), request) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PasswordModify", reflect.TypeOf((*MockLDAPClient)(nil).PasswordModify), arg0) } // Search mocks base method. -func (m *MockLDAPClient) Search(request *ldap.SearchRequest) (*ldap.SearchResult, error) { +func (m *MockLDAPClient) Search(arg0 *ldap.SearchRequest) (*ldap.SearchResult, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Search", request) + ret := m.ctrl.Call(m, "Search", arg0) ret0, _ := ret[0].(*ldap.SearchResult) ret1, _ := ret[1].(error) return ret0, ret1 } // Search indicates an expected call of Search. -func (mr *MockLDAPClientMockRecorder) Search(request any) *gomock.Call { +func (mr *MockLDAPClientMockRecorder) Search(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Search", reflect.TypeOf((*MockLDAPClient)(nil).Search), arg0) +} + +// SearchAsync mocks base method. +func (m *MockLDAPClient) SearchAsync(ctx context.Context, searchRequest *ldap.SearchRequest, bufferSize int) ldap.Response { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SearchAsync", ctx, searchRequest, bufferSize) + ret0, _ := ret[0].(ldap.Response) + return ret0 +} + +// SearchAsync indicates an expected call of SearchAsync. +func (mr *MockLDAPClientMockRecorder) SearchAsync(ctx, searchRequest, bufferSize any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Search", reflect.TypeOf((*MockLDAPClient)(nil).Search), request) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SearchAsync", reflect.TypeOf((*MockLDAPClient)(nil).SearchAsync), ctx, searchRequest, bufferSize) } // SearchWithPaging mocks base method. -func (m *MockLDAPClient) SearchWithPaging(request *ldap.SearchRequest, pagingSize uint32) (*ldap.SearchResult, error) { +func (m *MockLDAPClient) SearchWithPaging(searchRequest *ldap.SearchRequest, pagingSize uint32) (*ldap.SearchResult, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SearchWithPaging", request, pagingSize) + ret := m.ctrl.Call(m, "SearchWithPaging", searchRequest, pagingSize) ret0, _ := ret[0].(*ldap.SearchResult) ret1, _ := ret[1].(error) return ret0, ret1 } // SearchWithPaging indicates an expected call of SearchWithPaging. -func (mr *MockLDAPClientMockRecorder) SearchWithPaging(request, pagingSize any) *gomock.Call { +func (mr *MockLDAPClientMockRecorder) SearchWithPaging(searchRequest, pagingSize any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SearchWithPaging", reflect.TypeOf((*MockLDAPClient)(nil).SearchWithPaging), request, pagingSize) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SearchWithPaging", reflect.TypeOf((*MockLDAPClient)(nil).SearchWithPaging), searchRequest, pagingSize) } // SetTimeout mocks base method. -func (m *MockLDAPClient) SetTimeout(timeout time.Duration) { +func (m *MockLDAPClient) SetTimeout(arg0 time.Duration) { m.ctrl.T.Helper() - m.ctrl.Call(m, "SetTimeout", timeout) + m.ctrl.Call(m, "SetTimeout", arg0) } // SetTimeout indicates an expected call of SetTimeout. -func (mr *MockLDAPClientMockRecorder) SetTimeout(timeout any) *gomock.Call { +func (mr *MockLDAPClientMockRecorder) SetTimeout(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTimeout", reflect.TypeOf((*MockLDAPClient)(nil).SetTimeout), timeout) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTimeout", reflect.TypeOf((*MockLDAPClient)(nil).SetTimeout), arg0) } // SimpleBind mocks base method. -func (m *MockLDAPClient) SimpleBind(request *ldap.SimpleBindRequest) (*ldap.SimpleBindResult, error) { +func (m *MockLDAPClient) SimpleBind(arg0 *ldap.SimpleBindRequest) (*ldap.SimpleBindResult, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SimpleBind", request) + ret := m.ctrl.Call(m, "SimpleBind", arg0) ret0, _ := ret[0].(*ldap.SimpleBindResult) ret1, _ := ret[1].(error) return ret0, ret1 } // SimpleBind indicates an expected call of SimpleBind. -func (mr *MockLDAPClientMockRecorder) SimpleBind(request any) *gomock.Call { +func (mr *MockLDAPClientMockRecorder) SimpleBind(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SimpleBind", reflect.TypeOf((*MockLDAPClient)(nil).SimpleBind), request) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SimpleBind", reflect.TypeOf((*MockLDAPClient)(nil).SimpleBind), arg0) +} + +// Start mocks base method. +func (m *MockLDAPClient) Start() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Start") +} + +// Start indicates an expected call of Start. +func (mr *MockLDAPClientMockRecorder) Start() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockLDAPClient)(nil).Start)) } // StartTLS mocks base method. -func (m *MockLDAPClient) StartTLS(config *tls.Config) error { +func (m *MockLDAPClient) StartTLS(arg0 *tls.Config) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "StartTLS", config) + ret := m.ctrl.Call(m, "StartTLS", arg0) ret0, _ := ret[0].(error) return ret0 } // StartTLS indicates an expected call of StartTLS. -func (mr *MockLDAPClientMockRecorder) StartTLS(config any) *gomock.Call { +func (mr *MockLDAPClientMockRecorder) StartTLS(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartTLS", reflect.TypeOf((*MockLDAPClient)(nil).StartTLS), arg0) +} + +// Syncrepl mocks base method. +func (m *MockLDAPClient) Syncrepl(ctx context.Context, searchRequest *ldap.SearchRequest, bufferSize int, mode ldap.ControlSyncRequestMode, cookie []byte, reloadHint bool) ldap.Response { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Syncrepl", ctx, searchRequest, bufferSize, mode, cookie, reloadHint) + ret0, _ := ret[0].(ldap.Response) + return ret0 +} + +// Syncrepl indicates an expected call of Syncrepl. +func (mr *MockLDAPClientMockRecorder) Syncrepl(ctx, searchRequest, bufferSize, mode, cookie, reloadHint any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartTLS", reflect.TypeOf((*MockLDAPClient)(nil).StartTLS), config) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Syncrepl", reflect.TypeOf((*MockLDAPClient)(nil).Syncrepl), ctx, searchRequest, bufferSize, mode, cookie, reloadHint) } // TLSConnectionState mocks base method. @@ -398,18 +425,3 @@ func (mr *MockLDAPClientMockRecorder) Unbind() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Unbind", reflect.TypeOf((*MockLDAPClient)(nil).Unbind)) } - -// WhoAmI mocks base method. -func (m *MockLDAPClient) WhoAmI(controls []ldap.Control) (*ldap.WhoAmIResult, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "WhoAmI", controls) - ret0, _ := ret[0].(*ldap.WhoAmIResult) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// WhoAmI indicates an expected call of WhoAmI. -func (mr *MockLDAPClientMockRecorder) WhoAmI(controls any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WhoAmI", reflect.TypeOf((*MockLDAPClient)(nil).WhoAmI), controls) -} diff --git a/internal/authentication/ldap_user_provider.go b/internal/authentication/ldap_user_provider.go index 66687e1a1..905bf07e8 100644 --- a/internal/authentication/ldap_user_provider.go +++ b/internal/authentication/ldap_user_provider.go @@ -1,10 +1,8 @@ package authentication import ( - "crypto/tls" "crypto/x509" "fmt" - "net" "net/url" "strconv" "strings" @@ -21,11 +19,9 @@ import ( // LDAPUserProvider is a UserProvider that connects to LDAP servers like ActiveDirectory, OpenLDAP, OpenDJ, FreeIPA, etc. type LDAPUserProvider struct { - config schema.AuthenticationBackendLDAP - tlsConfig *tls.Config - dialOpts []ldap.DialOpt - log *logrus.Logger - factory LDAPClientFactory + config *schema.AuthenticationBackendLDAP + log *logrus.Logger + factory LDAPClientFactory clock clock.Provider @@ -53,37 +49,27 @@ type LDAPUserProvider struct { groupsFilterReplacementsMemberOfRDN bool } -// NewLDAPUserProvider creates a new instance of LDAPUserProvider with the ProductionLDAPClientFactory. -func NewLDAPUserProvider(config schema.AuthenticationBackend, certPool *x509.CertPool) (provider *LDAPUserProvider) { - provider = NewLDAPUserProviderWithFactory(*config.LDAP, config.PasswordReset.Disable, certPool, NewProductionLDAPClientFactory()) - - return provider -} - -// NewLDAPUserProviderWithFactory creates a new instance of LDAPUserProvider with the specified LDAPClientFactory. -func NewLDAPUserProviderWithFactory(config schema.AuthenticationBackendLDAP, disableResetPassword bool, certPool *x509.CertPool, factory LDAPClientFactory) (provider *LDAPUserProvider) { - if config.TLS == nil { - config.TLS = schema.DefaultLDAPAuthenticationBackendConfigurationImplementationCustom.TLS +// NewLDAPUserProvider creates a new instance of LDAPUserProvider with the StandardLDAPClientFactory. +func NewLDAPUserProvider(config schema.AuthenticationBackend, certs *x509.CertPool) (provider *LDAPUserProvider) { + if config.LDAP.TLS == nil { + config.LDAP.TLS = schema.DefaultLDAPAuthenticationBackendConfigurationImplementationCustom.TLS } - tlsConfig := utils.NewTLSConfig(config.TLS, certPool) + var factory LDAPClientFactory - var dialOpts = []ldap.DialOpt{ - ldap.DialWithDialer(&net.Dialer{Timeout: config.Timeout}), - } - - if tlsConfig != nil { - dialOpts = append(dialOpts, ldap.DialWithTLSConfig(tlsConfig)) + if config.LDAP.Pooling.Enable { + factory = NewPooledLDAPClientFactory(config.LDAP, certs, nil) + } else { + factory = NewStandardLDAPClientFactory(config.LDAP, certs, nil) } - if factory == nil { - factory = NewProductionLDAPClientFactory() - } + return NewLDAPUserProviderWithFactory(config.LDAP, config.PasswordReset.Disable, factory) +} +// NewLDAPUserProviderWithFactory creates a new instance of LDAPUserProvider with the specified LDAPClientFactory. +func NewLDAPUserProviderWithFactory(config *schema.AuthenticationBackendLDAP, disableResetPassword bool, factory LDAPClientFactory) (provider *LDAPUserProvider) { provider = &LDAPUserProvider{ config: config, - tlsConfig: tlsConfig, - dialOpts: dialOpts, log: logging.Logger(), factory: factory, disableResetPassword: disableResetPassword, @@ -99,25 +85,33 @@ func NewLDAPUserProviderWithFactory(config schema.AuthenticationBackendLDAP, dis // CheckUserPassword checks if provided password matches for the given user. func (p *LDAPUserProvider) CheckUserPassword(username string, password string) (valid bool, err error) { var ( - client, clientUser LDAPClient - profile *ldapUserProfile + client, uclient ldap.Client + profile *ldapUserProfile ) - if client, err = p.connect(); err != nil { + if client, err = p.factory.GetClient(); err != nil { return false, err } - defer client.Close() + defer func() { + if err := p.factory.ReleaseClient(client); err != nil { + p.log.WithError(err).Warn("Error occurred releasing the LDAP client") + } + }() if profile, err = p.getUserProfile(client, username); err != nil { return false, err } - if clientUser, err = p.connectCustom(p.config.Address.String(), profile.DN, password, p.config.StartTLS, p.dialOpts...); err != nil { + if uclient, err = p.factory.GetClient(WithUsername(profile.DN), WithPassword(password)); err != nil { return false, fmt.Errorf("authentication failed. Cause: %w", err) } - defer clientUser.Close() + defer func() { + if err := p.factory.ReleaseClient(uclient); err != nil { + p.log.WithError(err).Warn("Error occurred releasing the LDAP client") + } + }() return true, nil } @@ -125,15 +119,19 @@ func (p *LDAPUserProvider) CheckUserPassword(username string, password string) ( // GetDetails retrieve the groups a user belongs to. func (p *LDAPUserProvider) GetDetails(username string) (details *UserDetails, err error) { var ( - client LDAPClient + client ldap.Client profile *ldapUserProfile ) - if client, err = p.connect(); err != nil { + if client, err = p.factory.GetClient(); err != nil { return nil, err } - defer client.Close() + defer func() { + if err := p.factory.ReleaseClient(client); err != nil { + p.log.WithError(err).Warn("Error occurred releasing the LDAP client") + } + }() if profile, err = p.getUserProfile(client, username); err != nil { return nil, err @@ -158,11 +156,11 @@ func (p *LDAPUserProvider) GetDetails(username string) (details *UserDetails, er // GetDetailsExtended retrieves the UserDetailsExtended values. func (p *LDAPUserProvider) GetDetailsExtended(username string) (details *UserDetailsExtended, err error) { var ( - client LDAPClient + client ldap.Client profile *ldapUserProfileExtended ) - if client, err = p.connect(); err != nil { + if client, err = p.factory.GetClient(); err != nil { return nil, err } @@ -243,15 +241,19 @@ func (p *LDAPUserProvider) GetDetailsExtended(username string) (details *UserDet // UpdatePassword update the password of the given user. func (p *LDAPUserProvider) UpdatePassword(username, password string) (err error) { var ( - client LDAPClient + client ldap.Client profile *ldapUserProfile ) - if client, err = p.connect(); err != nil { + if client, err = p.factory.GetClient(); err != nil { return fmt.Errorf("unable to update password. Cause: %w", err) } - defer client.Close() + defer func() { + if err := p.factory.ReleaseClient(client); err != nil { + p.log.WithError(err).Warn("Error occurred releasing the LDAP client") + } + }() if profile, err = p.getUserProfile(client, username); err != nil { return fmt.Errorf("unable to update password. Cause: %w", err) @@ -297,39 +299,7 @@ func (p *LDAPUserProvider) UpdatePassword(username, password string) (err error) return nil } -func (p *LDAPUserProvider) connect() (client LDAPClient, err error) { - return p.connectCustom(p.config.Address.String(), p.config.User, p.config.Password, p.config.StartTLS, p.dialOpts...) -} - -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) - } - - if startTLS { - if err = client.StartTLS(p.tlsConfig); err != nil { - client.Close() - - return nil, fmt.Errorf("starttls failed with error: %w", err) - } - } - - 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) - } - - return client, nil -} - -func (p *LDAPUserProvider) search(client LDAPClient, request *ldap.SearchRequest) (result *ldap.SearchResult, err error) { +func (p *LDAPUserProvider) search(client ldap.Client, request *ldap.SearchRequest) (result *ldap.SearchResult, err error) { if result, err = client.Search(request); err != nil { if referral, ok := p.getReferral(err); ok { if result == nil { @@ -357,15 +327,19 @@ func (p *LDAPUserProvider) search(client LDAPClient, request *ldap.SearchRequest func (p *LDAPUserProvider) searchReferral(referral string, request *ldap.SearchRequest, searchResult *ldap.SearchResult) (err error) { var ( - client LDAPClient + client ldap.Client result *ldap.SearchResult ) - if client, err = p.connectCustom(referral, p.config.User, p.config.Password, p.config.StartTLS, p.dialOpts...); err != nil { + if client, err = p.factory.GetClient(WithAddress(referral)); err != nil { return fmt.Errorf("error occurred connecting to referred LDAP server '%s': %w", referral, err) } - defer client.Close() + defer func() { + if err := p.factory.ReleaseClient(client); err != nil { + p.log.WithError(err).Warn("Error occurred releasing the LDAP client") + } + }() if result, err = client.Search(request); err != nil { return fmt.Errorf("error occurred performing search on referred LDAP server '%s': %w", referral, err) @@ -390,7 +364,7 @@ func (p *LDAPUserProvider) searchReferrals(request *ldap.SearchRequest, result * return nil } -func (p *LDAPUserProvider) getUserProfile(client LDAPClient, username string) (profile *ldapUserProfile, err error) { +func (p *LDAPUserProvider) getUserProfile(client ldap.Client, username string) (profile *ldapUserProfile, err error) { // Search for the given username. request := ldap.NewSearchRequest( p.usersBaseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, @@ -463,7 +437,7 @@ func (p *LDAPUserProvider) getUserProfileResultToProfile(username string, entry return &userProfile, nil } -func (p *LDAPUserProvider) getUserProfileExtended(client LDAPClient, username string) (profile *ldapUserProfileExtended, err error) { +func (p *LDAPUserProvider) getUserProfileExtended(client ldap.Client, username string) (profile *ldapUserProfileExtended, err error) { // Search for the given username. request := ldap.NewSearchRequest( p.usersBaseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, @@ -552,7 +526,7 @@ func (p *LDAPUserProvider) getUserProfileResultToProfileExtended(username string return &userProfile, nil } -func (p *LDAPUserProvider) getUserGroups(client LDAPClient, username string, profile *ldapUserProfile) (groups []string, err error) { +func (p *LDAPUserProvider) getUserGroups(client ldap.Client, 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, @@ -577,7 +551,7 @@ func (p *LDAPUserProvider) getUserGroups(client LDAPClient, username string, pro } } -func (p *LDAPUserProvider) getUserGroupsRequestFilter(client LDAPClient, username string, _ *ldapUserProfile, request *ldap.SearchRequest) (groups []string, err error) { +func (p *LDAPUserProvider) getUserGroupsRequestFilter(client ldap.Client, username string, _ *ldapUserProfile, request *ldap.SearchRequest) (groups []string, err error) { var result *ldap.SearchResult if result, err = p.search(client, request); err != nil { @@ -593,7 +567,7 @@ func (p *LDAPUserProvider) getUserGroupsRequestFilter(client LDAPClient, usernam return groups, nil } -func (p *LDAPUserProvider) getUserGroupsRequestMemberOf(client LDAPClient, username string, profile *ldapUserProfile, request *ldap.SearchRequest) (groups []string, err error) { +func (p *LDAPUserProvider) getUserGroupsRequestMemberOf(client ldap.Client, username string, profile *ldapUserProfile, request *ldap.SearchRequest) (groups []string, err error) { var result *ldap.SearchResult if result, err = p.search(client, request); err != nil { @@ -733,7 +707,7 @@ func (p *LDAPUserProvider) resolveGroupsFilter(input string, profile *ldapUserPr return filter } -func (p *LDAPUserProvider) modify(client LDAPClient, modifyRequest *ldap.ModifyRequest) (err error) { +func (p *LDAPUserProvider) modify(client ldap.Client, modifyRequest *ldap.ModifyRequest) (err error) { if err = client.Modify(modifyRequest); err != nil { var ( referral string @@ -747,15 +721,19 @@ func (p *LDAPUserProvider) modify(client LDAPClient, modifyRequest *ldap.ModifyR p.log.Debugf("Attempting Modify on referred URL %s", referral) var ( - clientRef LDAPClient + clientRef ldap.Client errRef error ) - if clientRef, errRef = p.connectCustom(referral, p.config.User, p.config.Password, p.config.StartTLS, p.dialOpts...); errRef != nil { + if clientRef, errRef = p.factory.GetClient(WithAddress(referral)); errRef != nil { return fmt.Errorf("error occurred connecting to referred LDAP server '%s': %+v. Original Error: %w", referral, errRef, err) } - defer clientRef.Close() + defer func() { + if err := p.factory.ReleaseClient(clientRef); err != nil { + p.log.WithError(err).Warn("Error occurred releasing the LDAP client") + } + }() if errRef = clientRef.Modify(modifyRequest); errRef != nil { return fmt.Errorf("error occurred performing modify on referred LDAP server '%s': %+v. Original Error: %w", referral, errRef, err) @@ -767,7 +745,7 @@ func (p *LDAPUserProvider) modify(client LDAPClient, modifyRequest *ldap.ModifyR return nil } -func (p *LDAPUserProvider) pwdModify(client LDAPClient, pwdModifyRequest *ldap.PasswordModifyRequest) (err error) { +func (p *LDAPUserProvider) pwdModify(client ldap.Client, pwdModifyRequest *ldap.PasswordModifyRequest) (err error) { if _, err = client.PasswordModify(pwdModifyRequest); err != nil { var ( referral string @@ -781,15 +759,19 @@ func (p *LDAPUserProvider) pwdModify(client LDAPClient, pwdModifyRequest *ldap.P p.log.Debugf("Attempting PwdModify ExOp (1.3.6.1.4.1.4203.1.11.1) on referred URL %s", referral) var ( - clientRef LDAPClient + clientRef ldap.Client errRef error ) - if clientRef, errRef = p.connectCustom(referral, p.config.User, p.config.Password, p.config.StartTLS, p.dialOpts...); errRef != nil { + if clientRef, errRef = p.factory.GetClient(WithAddress(referral)); errRef != nil { return fmt.Errorf("error occurred connecting to referred LDAP server '%s': %+v. Original Error: %w", referral, errRef, err) } - defer clientRef.Close() + defer func() { + if err := p.factory.ReleaseClient(clientRef); err != nil { + p.log.WithError(err).Warn("Error occurred releasing the LDAP client") + } + }() if _, errRef = clientRef.PasswordModify(pwdModifyRequest); errRef != nil { return fmt.Errorf("error occurred performing password modify on referred LDAP server '%s': %+v. Original Error: %w", referral, errRef, err) diff --git a/internal/authentication/ldap_user_provider_startup.go b/internal/authentication/ldap_user_provider_lifecycle.go index 20d7e7653..73dceaa6a 100644 --- a/internal/authentication/ldap_user_provider_startup.go +++ b/internal/authentication/ldap_user_provider_lifecycle.go @@ -10,15 +10,27 @@ import ( "github.com/authelia/authelia/v4/internal/utils" ) +func (p *LDAPUserProvider) Shutdown() (err error) { + return p.factory.Close() +} + // StartupCheck implements the startup check provider interface. func (p *LDAPUserProvider) StartupCheck() (err error) { - var client LDAPClient + if err = p.factory.Initialize(); err != nil { + return err + } + + var client ldap.Client - if client, err = p.connect(); err != nil { + if client, err = p.factory.GetClient(); err != nil { return err } - defer client.Close() + defer func() { + if err := p.factory.ReleaseClient(client); err != nil { + p.log.WithError(err).Warn("Error occurred releasing the LDAP client") + } + }() if p.features, err = p.getServerSupportedFeatures(client); err != nil { return err @@ -40,7 +52,7 @@ func (p *LDAPUserProvider) StartupCheck() (err error) { return nil } -func (p *LDAPUserProvider) getServerSupportedFeatures(client LDAPClient) (features LDAPSupportedFeatures, err error) { +func (p *LDAPUserProvider) getServerSupportedFeatures(client ldap.Client) (features LDAPSupportedFeatures, err error) { var ( request *ldap.SearchRequest result *ldap.SearchResult diff --git a/internal/authentication/ldap_user_provider_test.go b/internal/authentication/ldap_user_provider_test.go index 04a37e5f2..3975faa39 100644 --- a/internal/authentication/ldap_user_provider_test.go +++ b/internal/authentication/ldap_user_provider_test.go @@ -25,42 +25,35 @@ func TestNewLDAPUserProvider(t *testing.T) { assert.NotNil(t, provider) } -func TestNewLDAPUserProviderWithFactoryWithoutFactory(t *testing.T) { - provider := NewLDAPUserProviderWithFactory(schema.AuthenticationBackendLDAP{}, false, nil, nil) - - assert.NotNil(t, provider) - - assert.IsType(t, &ProductionLDAPClientFactory{}, provider.factory) -} - func TestShouldCreateRawConnectionWhenSchemeIsLDAP(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + + factory := NewStandardLDAPClientFactory(config, nil, mockDialer) mockClient := NewMockLDAPClient(ctrl) provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - }, + config, false, - nil, - mockFactory) + factory) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + dialURL := mockDialer.EXPECT().DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()).Return(mockClient, nil) - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - gomock.InOrder(dialURL, connBind) + gomock.InOrder(dialURL, clientBind) - _, err := provider.connect() + _, err := provider.factory.GetClient() require.NoError(t, err) } @@ -69,30 +62,29 @@ func TestShouldCreateTLSConnectionWhenSchemeIsLDAPS(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPSAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + + factory := NewStandardLDAPClientFactory(config, nil, mockDialer) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPSAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - }, - false, - nil, - mockFactory) + provider := NewLDAPUserProviderWithFactory(config, false, factory) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldaps://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + dialURL := mockDialer.EXPECT().DialURL("ldaps://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - gomock.InOrder(dialURL, connBind) + gomock.InOrder(dialURL, clientBind) - _, err := provider.connect() + _, err := provider.factory.GetClient() require.NoError(t, err) } @@ -120,16 +112,16 @@ func TestEscapeSpecialCharsInGroupsFilter(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPSAddress, + GroupsFilter: "(|(member={dn})(uid={username})(uid={input}))", + } - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPSAddress, - GroupsFilter: "(|(member={dn})(uid={username})(uid={input}))", - }, - false, - nil, - mockFactory) + mockDialer := NewMockLDAPClientDialer(ctrl) + + factory := NewStandardLDAPClientFactory(config, nil, mockDialer) + + provider := NewLDAPUserProviderWithFactory(config, false, factory) profile := ldapUserProfile{ DN: "cn=john (external),dc=example,dc=com", @@ -149,7 +141,15 @@ func TestResolveGroupsFilter(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPSAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + + factory := NewStandardLDAPClientFactory(config, nil, mockDialer) testCases := []struct { name string @@ -207,11 +207,7 @@ func TestResolveGroupsFilter(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - provider := NewLDAPUserProviderWithFactory( - tc.have, - false, - nil, - mockFactory) + provider := NewLDAPUserProviderWithFactory(&tc.have, false, factory) assert.Equal(t, tc.expected, provider.resolveGroupsFilter("", tc.profile)) }) @@ -305,13 +301,12 @@ func TestShouldCheckLDAPEpochFilters(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ + &schema.AuthenticationBackendLDAP{ UsersFilter: tc.have.users, Attributes: tc.have.attr, BaseDN: "dc=example,dc=com", }, false, - nil, mockFactory) assert.Equal(t, tc.expected.dtgeneralized, provider.usersFilterReplacementDateTimeGeneralized) @@ -325,33 +320,102 @@ func TestShouldCheckLDAPServerExtensions(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + Password: "password", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + + factory := NewStandardLDAPClientFactory(config, nil, mockDialer) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", + provider := NewLDAPUserProviderWithFactory(config, false, factory) + + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) + + clientBind := mockClient.EXPECT(). + Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). + Return(nil) + + searchOIDs := mockClient.EXPECT(). + Search(NewExtendedSearchRequestMatcher("(objectClass=*)", "", ldap.ScopeBaseObject, ldap.NeverDerefAliases, false, []string{ldapSupportedExtensionAttribute, ldapSupportedControlAttribute})). + Return(&ldap.SearchResult{ + Entries: []*ldap.Entry{ + { + DN: "", + Attributes: []*ldap.EntryAttribute{ + { + Name: ldapSupportedExtensionAttribute, + Values: []string{ldapOIDExtensionPwdModifyExOp, ldapOIDExtensionTLS}, + }, + { + Name: ldapSupportedControlAttribute, + Values: []string{}, + }, + }, + }, }, - Password: "password", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", + }, nil) + + clientClose := mockClient.EXPECT().Close() + + gomock.InOrder(dialURL, clientBind, searchOIDs, clientClose) + + err := provider.StartupCheck() + assert.NoError(t, err) + + assert.True(t, provider.features.Extensions.PwdModifyExOp) + assert.True(t, provider.features.Extensions.TLS) + + assert.False(t, provider.features.ControlTypes.MsftPwdPolHints) + assert.False(t, provider.features.ControlTypes.MsftPwdPolHintsDeprecated) +} + +func TestShouldCheckLDAPServerExtensionsPooled(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", }, - false, - nil, - mockFactory) + Password: "password", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + Pooling: schema.AuthenticationBackendLDAPPooling{ + Count: 1, + }, + } - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + mockDialer := NewMockLDAPClientDialer(ctrl) - connBind := mockClient.EXPECT(). + factory := NewPooledLDAPClientFactory(config, nil, mockDialer) + + mockClient := NewMockLDAPClient(ctrl) + + provider := NewLDAPUserProviderWithFactory(config, false, factory) + + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) + + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -375,9 +439,14 @@ func TestShouldCheckLDAPServerExtensions(t *testing.T) { }, }, nil) - connClose := mockClient.EXPECT().Close() - - gomock.InOrder(dialURL, connBind, searchOIDs, connClose) + gomock.InOrder( + dialURL, + clientBind, + mockClient.EXPECT().IsClosing().Return(false), + searchOIDs, + mockClient.EXPECT().IsClosing().Return(false), + mockClient.EXPECT().Close().Return(fmt.Errorf("close error")), + ) err := provider.StartupCheck() assert.NoError(t, err) @@ -387,39 +456,105 @@ func TestShouldCheckLDAPServerExtensions(t *testing.T) { assert.False(t, provider.features.ControlTypes.MsftPwdPolHints) assert.False(t, provider.features.ControlTypes.MsftPwdPolHintsDeprecated) + + assert.EqualError(t, provider.Shutdown(), "errors occurred closing the client pool: close error") } func TestShouldNotCheckLDAPServerExtensionsWhenRootDSEReturnsMoreThanOneEntry(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + Password: "password", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) + + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) + + clientBind := mockClient.EXPECT(). + Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). + Return(nil) + + searchOIDs := mockClient.EXPECT(). + Search(NewExtendedSearchRequestMatcher("(objectClass=*)", "", ldap.ScopeBaseObject, ldap.NeverDerefAliases, false, []string{ldapSupportedExtensionAttribute, ldapSupportedControlAttribute})). + Return(&ldap.SearchResult{ + Entries: []*ldap.Entry{ + { + DN: "", + Attributes: []*ldap.EntryAttribute{ + { + Name: ldapSupportedExtensionAttribute, + Values: []string{ldapOIDExtensionPwdModifyExOp, ldapOIDExtensionTLS}, + }, + { + Name: ldapSupportedControlAttribute, + Values: []string{}, + }, + }, + }, + {}, }, - Password: "password", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", + }, nil) + + clientClose := mockClient.EXPECT().Close() + + gomock.InOrder(dialURL, clientBind, searchOIDs, clientClose) + + err := provider.StartupCheck() + assert.NoError(t, err) + + assert.False(t, provider.features.Extensions.PwdModifyExOp) + assert.False(t, provider.features.Extensions.TLS) + + assert.False(t, provider.features.ControlTypes.MsftPwdPolHints) + assert.False(t, provider.features.ControlTypes.MsftPwdPolHintsDeprecated) +} + +func TestShouldNotCheckLDAPServerExtensionsWhenRootDSEReturnsMoreThanOneEntryPooled(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", }, - false, - nil, - mockFactory) + Password: "password", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + Pooling: schema.AuthenticationBackendLDAPPooling{Count: 1}, + } - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + mockDialer := NewMockLDAPClientDialer(ctrl) - connBind := mockClient.EXPECT(). + mockClient := NewMockLDAPClient(ctrl) + + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) + + provider := NewLDAPUserProviderWithFactory(config, false, NewPooledLDAPClientFactory(config, nil, mockDialer)) + + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -444,9 +579,97 @@ func TestShouldNotCheckLDAPServerExtensionsWhenRootDSEReturnsMoreThanOneEntry(t }, }, nil) - connClose := mockClient.EXPECT().Close() + gomock.InOrder( + dialURL, + clientBind, + mockClient.EXPECT().IsClosing().Return(false), + searchOIDs, + mockClient.EXPECT().IsClosing().Return(false), + mockClient.EXPECT().Close().Return(nil), + ) + + err := provider.StartupCheck() + assert.NoError(t, err) + + assert.False(t, provider.features.Extensions.PwdModifyExOp) + assert.False(t, provider.features.Extensions.TLS) + + assert.False(t, provider.features.ControlTypes.MsftPwdPolHints) + assert.False(t, provider.features.ControlTypes.MsftPwdPolHintsDeprecated) + + assert.NoError(t, provider.Shutdown()) +} + +func TestShouldNotCheckLDAPServerExtensionsWhenRootDSEReturnsMoreThanOneEntryPooledClosing(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + Password: "password", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + Pooling: schema.AuthenticationBackendLDAPPooling{Count: 1}, + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + + mockClient := NewMockLDAPClient(ctrl) + mockClientSecond := NewMockLDAPClient(ctrl) + + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) + + dialURLSecond := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClientSecond, nil) + + provider := NewLDAPUserProviderWithFactory(config, false, NewPooledLDAPClientFactory(config, nil, mockDialer)) + + clientBind := mockClient.EXPECT(). + Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). + Return(nil) - gomock.InOrder(dialURL, connBind, searchOIDs, connClose) + clientBindSecond := mockClientSecond.EXPECT(). + Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). + Return(nil) + + searchOIDs := mockClientSecond.EXPECT(). + Search(NewExtendedSearchRequestMatcher("(objectClass=*)", "", ldap.ScopeBaseObject, ldap.NeverDerefAliases, false, []string{ldapSupportedExtensionAttribute, ldapSupportedControlAttribute})). + Return(&ldap.SearchResult{ + Entries: []*ldap.Entry{ + { + DN: "", + Attributes: []*ldap.EntryAttribute{ + { + Name: ldapSupportedExtensionAttribute, + Values: []string{ldapOIDExtensionPwdModifyExOp, ldapOIDExtensionTLS}, + }, + { + Name: ldapSupportedControlAttribute, + Values: []string{}, + }, + }, + }, + {}, + }, + }, nil) + + gomock.InOrder( + dialURL, + clientBind, + mockClient.EXPECT().IsClosing().Return(true), + dialURLSecond, + clientBindSecond, + searchOIDs, + mockClientSecond.EXPECT().IsClosing().Return(false), + mockClientSecond.EXPECT().Close().Return(nil), + ) err := provider.StartupCheck() assert.NoError(t, err) @@ -456,39 +679,104 @@ func TestShouldNotCheckLDAPServerExtensionsWhenRootDSEReturnsMoreThanOneEntry(t assert.False(t, provider.features.ControlTypes.MsftPwdPolHints) assert.False(t, provider.features.ControlTypes.MsftPwdPolHintsDeprecated) + + assert.NoError(t, provider.Shutdown()) } func TestShouldCheckLDAPServerControlTypes(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + Password: "password", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) + + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) + + clientBind := mockClient.EXPECT(). + Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). + Return(nil) + + searchOIDs := mockClient.EXPECT(). + Search(NewExtendedSearchRequestMatcher("(objectClass=*)", "", ldap.ScopeBaseObject, ldap.NeverDerefAliases, false, []string{ldapSupportedExtensionAttribute, ldapSupportedControlAttribute})). + Return(&ldap.SearchResult{ + Entries: []*ldap.Entry{ + { + DN: "", + Attributes: []*ldap.EntryAttribute{ + { + Name: ldapSupportedExtensionAttribute, + Values: []string{}, + }, + { + Name: ldapSupportedControlAttribute, + Values: []string{ldapOIDControlMsftServerPolicyHints, ldapOIDControlMsftServerPolicyHintsDeprecated}, + }, + }, + }, }, - Password: "password", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", + }, nil) + + clientClose := mockClient.EXPECT().Close() + + gomock.InOrder(dialURL, clientBind, searchOIDs, clientClose) + + err := provider.StartupCheck() + assert.NoError(t, err) + + assert.False(t, provider.features.Extensions.PwdModifyExOp) + assert.False(t, provider.features.Extensions.TLS) + + assert.True(t, provider.features.ControlTypes.MsftPwdPolHints) + assert.True(t, provider.features.ControlTypes.MsftPwdPolHintsDeprecated) +} + +func TestShouldCheckLDAPServerControlTypesPooled(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", }, - false, - nil, - mockFactory) + Password: "password", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + Pooling: schema.AuthenticationBackendLDAPPooling{Count: 1}, + } - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + mockDialer := NewMockLDAPClientDialer(ctrl) - connBind := mockClient.EXPECT(). + mockClient := NewMockLDAPClient(ctrl) + + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) + + provider := NewLDAPUserProviderWithFactory(config, false, NewPooledLDAPClientFactory(config, nil, mockDialer)) + + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -512,9 +800,16 @@ func TestShouldCheckLDAPServerControlTypes(t *testing.T) { }, }, nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() - gomock.InOrder(dialURL, connBind, searchOIDs, connClose) + gomock.InOrder( + dialURL, + clientBind, + mockClient.EXPECT().IsClosing().Return(false), + searchOIDs, + mockClient.EXPECT().IsClosing().Return(false), + clientClose, + ) err := provider.StartupCheck() assert.NoError(t, err) @@ -524,39 +819,38 @@ func TestShouldCheckLDAPServerControlTypes(t *testing.T) { assert.True(t, provider.features.ControlTypes.MsftPwdPolHints) assert.True(t, provider.features.ControlTypes.MsftPwdPolHintsDeprecated) + + assert.NoError(t, provider.Shutdown()) } func TestShouldNotEnablePasswdModifyExtensionOrControlTypes(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + Password: "password", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - Password: "password", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -580,52 +874,123 @@ func TestShouldNotEnablePasswdModifyExtensionOrControlTypes(t *testing.T) { }, }, nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() - gomock.InOrder(dialURL, connBind, searchOIDs, connClose) + gomock.InOrder(dialURL, clientBind, searchOIDs, clientClose) - err := provider.StartupCheck() - assert.NoError(t, err) + assert.NoError(t, provider.StartupCheck()) assert.False(t, provider.features.Extensions.PwdModifyExOp) assert.False(t, provider.features.Extensions.TLS) assert.False(t, provider.features.ControlTypes.MsftPwdPolHints) assert.False(t, provider.features.ControlTypes.MsftPwdPolHintsDeprecated) + + assert.NoError(t, provider.Shutdown()) } -func TestShouldReturnCheckServerConnectError(t *testing.T) { +func TestShouldNotEnablePasswdModifyExtensionOrControlTypesPooled(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + Password: "password", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + Pooling: schema.AuthenticationBackendLDAPPooling{ + Count: 1, + }, + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) + + provider := NewLDAPUserProviderWithFactory(config, false, NewPooledLDAPClientFactory(config, nil, mockDialer)) + + clientBind := mockClient.EXPECT(). + Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). + Return(nil) + + searchOIDs := mockClient.EXPECT(). + Search(NewExtendedSearchRequestMatcher("(objectClass=*)", "", ldap.ScopeBaseObject, ldap.NeverDerefAliases, false, []string{ldapSupportedExtensionAttribute, ldapSupportedControlAttribute})). + Return(&ldap.SearchResult{ + Entries: []*ldap.Entry{ + { + DN: "", + Attributes: []*ldap.EntryAttribute{ + { + Name: ldapSupportedExtensionAttribute, + Values: []string{}, + }, + { + Name: ldapSupportedControlAttribute, + Values: []string{}, + }, + }, + }, }, - Password: "password", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", + }, nil) + + clientClose := mockClient.EXPECT().Close() + + gomock.InOrder( + dialURL, + clientBind, + mockClient.EXPECT().IsClosing().Return(false), + searchOIDs, + mockClient.EXPECT().IsClosing().Return(false), + clientClose, + ) + + assert.NoError(t, provider.StartupCheck()) + + assert.False(t, provider.features.Extensions.PwdModifyExOp) + assert.False(t, provider.features.Extensions.TLS) + + assert.False(t, provider.features.ControlTypes.MsftPwdPolHints) + assert.False(t, provider.features.ControlTypes.MsftPwdPolHintsDeprecated) + + assert.NoError(t, provider.Shutdown()) +} + +func TestShouldReturnCheckServerConnectError(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", }, - false, - nil, - mockFactory) + Password: "password", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + } - mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, errors.New("could not connect")) + mockDialer := NewMockLDAPClientDialer(ctrl) - err := provider.StartupCheck() - assert.EqualError(t, err, "dial failed with error: could not connect") + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) + + mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(nil, errors.New("could not connect")) + + assert.EqualError(t, provider.StartupCheck(), "error occurred dialing address: could not connect") assert.False(t, provider.features.Extensions.PwdModifyExOp) } @@ -634,33 +999,76 @@ func TestShouldReturnCheckServerSearchError(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + Password: "password", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - Password: "password", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) + + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) + + clientBind := mockClient.EXPECT(). + Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). + Return(nil) + + searchOIDs := mockClient.EXPECT(). + Search(NewExtendedSearchRequestMatcher("(objectClass=*)", "", ldap.ScopeBaseObject, ldap.NeverDerefAliases, false, []string{ldapSupportedExtensionAttribute, ldapSupportedControlAttribute})). + Return(nil, errors.New("could not perform the search")) + + clientClose := mockClient.EXPECT().Close() + + gomock.InOrder(dialURL, clientBind, searchOIDs, clientClose) + + err := provider.StartupCheck() + assert.EqualError(t, err, "error occurred during RootDSE search: could not perform the search") + + assert.False(t, provider.features.Extensions.PwdModifyExOp) +} + +func TestShouldReturnCheckServerSearchErrorPooled(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", }, - false, - nil, - mockFactory) + Password: "password", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + Pooling: schema.AuthenticationBackendLDAPPooling{Count: 1}, + } - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + mockDialer := NewMockLDAPClientDialer(ctrl) - connBind := mockClient.EXPECT(). + mockClient := NewMockLDAPClient(ctrl) + + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) + + provider := NewLDAPUserProviderWithFactory(config, false, NewPooledLDAPClientFactory(config, nil, mockDialer)) + + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -668,48 +1076,98 @@ func TestShouldReturnCheckServerSearchError(t *testing.T) { Search(NewExtendedSearchRequestMatcher("(objectClass=*)", "", ldap.ScopeBaseObject, ldap.NeverDerefAliases, false, []string{ldapSupportedExtensionAttribute, ldapSupportedControlAttribute})). Return(nil, errors.New("could not perform the search")) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() - gomock.InOrder(dialURL, connBind, searchOIDs, connClose) + gomock.InOrder( + dialURL, + clientBind, + mockClient.EXPECT().IsClosing().Return(false), + searchOIDs, + mockClient.EXPECT().IsClosing().Return(false), + clientClose, + ) err := provider.StartupCheck() assert.EqualError(t, err, "error occurred during RootDSE search: could not perform the search") assert.False(t, provider.features.Extensions.PwdModifyExOp) + + assert.NoError(t, provider.Shutdown()) } func TestShouldPermitRootDSEFailure(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + PermitFeatureDetectionFailure: true, + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + Password: "password", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - PermitFeatureDetectionFailure: true, - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - Password: "password", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) + + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) + + clientBind := mockClient.EXPECT(). + Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). + Return(nil) + + searchOIDs := mockClient.EXPECT(). + Search(NewExtendedSearchRequestMatcher("(objectClass=*)", "", ldap.ScopeBaseObject, ldap.NeverDerefAliases, false, []string{ldapSupportedExtensionAttribute, ldapSupportedControlAttribute})). + Return(nil, errors.New("could not perform the search")) + + clientClose := mockClient.EXPECT().Close() + + gomock.InOrder(dialURL, clientBind, searchOIDs, clientClose) + + assert.NoError(t, provider.StartupCheck()) +} + +func TestShouldPermitRootDSEFailurePooled(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + config := &schema.AuthenticationBackendLDAP{ + PermitFeatureDetectionFailure: true, + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", }, - false, - nil, - mockFactory) + Password: "password", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + Pooling: schema.AuthenticationBackendLDAPPooling{Count: 1}, + } - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + mockDialer := NewMockLDAPClientDialer(ctrl) - connBind := mockClient.EXPECT(). + mockClient := NewMockLDAPClient(ctrl) + + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) + + provider := NewLDAPUserProviderWithFactory(config, false, NewPooledLDAPClientFactory(config, nil, mockDialer)) + + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -717,12 +1175,19 @@ func TestShouldPermitRootDSEFailure(t *testing.T) { Search(NewExtendedSearchRequestMatcher("(objectClass=*)", "", ldap.ScopeBaseObject, ldap.NeverDerefAliases, false, []string{ldapSupportedExtensionAttribute, ldapSupportedControlAttribute})). Return(nil, errors.New("could not perform the search")) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() - gomock.InOrder(dialURL, connBind, searchOIDs, connClose) + gomock.InOrder( + dialURL, + clientBind, + mockClient.EXPECT().IsClosing().Return(false), + searchOIDs, + mockClient.EXPECT().IsClosing().Return(false), + clientClose, + ) - err := provider.StartupCheck() - assert.NoError(t, err) + assert.NoError(t, provider.StartupCheck()) + assert.NoError(t, provider.Shutdown()) } type SearchRequestMatcher struct { @@ -750,7 +1215,7 @@ func TestShouldEscapeUserInput(t *testing.T) { mockClient := NewMockLDAPClient(ctrl) provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ + &schema.AuthenticationBackendLDAP{ Address: testLDAPAddress, User: "cn=admin,dc=example,dc=com", UsersFilter: "(|({username_attribute}={input})({mail_attribute}={input}))", @@ -766,7 +1231,6 @@ func TestShouldEscapeUserInput(t *testing.T) { PermitReferrals: true, }, false, - nil, mockFactory) mockClient.EXPECT(). @@ -783,35 +1247,32 @@ func TestShouldReturnEmailWhenAttributeSameAsUsername(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "mail", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "(&({username_attribute}={input})(objectClass=inetOrgPerson))", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "mail", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "(&({username_attribute}={input})(objectClass=inetOrgPerson))", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - assert.Equal(t, []string{"mail", "displayName", "memberOf"}, provider.usersAttributes) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + assert.Equal(t, []string{"mail", "displayName", "memberOf"}, provider.usersAttributes) - bind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -835,9 +1296,9 @@ func TestShouldReturnEmailWhenAttributeSameAsUsername(t *testing.T) { }, }, nil) - gomock.InOrder(dialURL, bind, search) + gomock.InOrder(dialURL, clientBind, search) - client, err := provider.connect() + client, err := provider.factory.GetClient() assert.NoError(t, err) profile, err := provider.getUserProfile(client, "john@example.com") @@ -857,35 +1318,32 @@ func TestShouldReturnUsernameAndBlankDisplayNameWhenAttributesTheSame(t *testing ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "uid", + MemberOf: "memberOf", + }, + UsersFilter: "(&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=inetOrgPerson))", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "uid", - MemberOf: "memberOf", - }, - UsersFilter: "(&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=inetOrgPerson))", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - assert.Equal(t, []string{"uid", "mail", "memberOf"}, provider.usersAttributes) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + assert.Equal(t, []string{"uid", "mail", "memberOf"}, provider.usersAttributes) - bind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -909,9 +1367,9 @@ func TestShouldReturnUsernameAndBlankDisplayNameWhenAttributesTheSame(t *testing }, }, nil) - gomock.InOrder(dialURL, bind, search) + gomock.InOrder(dialURL, clientBind, search) - client, err := provider.connect() + client, err := provider.factory.GetClient() assert.NoError(t, err) profile, err := provider.getUserProfile(client, "john@example.com") @@ -931,35 +1389,32 @@ func TestShouldReturnBlankEmailAndDisplayNameWhenAttrsLenZero(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "(&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=inetOrgPerson))", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "(&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=inetOrgPerson))", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - assert.Equal(t, []string{"uid", "mail", "displayName", "memberOf"}, provider.usersAttributes) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + assert.Equal(t, []string{"uid", "mail", "displayName", "memberOf"}, provider.usersAttributes) - bind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -991,9 +1446,9 @@ func TestShouldReturnBlankEmailAndDisplayNameWhenAttrsLenZero(t *testing.T) { }, }, nil) - gomock.InOrder(dialURL, bind, search) + gomock.InOrder(dialURL, clientBind, search) - client, err := provider.connect() + client, err := provider.factory.GetClient() assert.NoError(t, err) profile, err := provider.getUserProfile(client, "john@example.com") @@ -1016,7 +1471,7 @@ func TestShouldCombineUsernameFilterAndUsersFilter(t *testing.T) { mockClient := NewMockLDAPClient(ctrl) provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ + &schema.AuthenticationBackendLDAP{ Address: testLDAPAddress, User: "cn=admin,dc=example,dc=com", UsersFilter: "(&({username_attribute}={input})(&(objectCategory=person)(objectClass=user)))", @@ -1032,7 +1487,6 @@ func TestShouldCombineUsernameFilterAndUsersFilter(t *testing.T) { PermitReferrals: true, }, false, - nil, mockFactory) assert.Equal(t, []string{"uid", "mail", "displayName", "memberOf"}, provider.usersAttributes) @@ -1092,39 +1546,36 @@ func TestShouldNotCrashWhenGroupsAreNotRetrievedFromLDAP(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + GroupName: "cn", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + PermitReferrals: true, + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - GroupName: "cn", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - PermitReferrals: true, - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchGroups := mockClient.EXPECT(). Search(gomock.Any()). @@ -1154,7 +1605,7 @@ func TestShouldNotCrashWhenGroupsAreNotRetrievedFromLDAP(t *testing.T) { }, }, nil) - gomock.InOrder(dialURL, connBind, searchProfile, searchGroups, connClose) + gomock.InOrder(dialURL, clientBind, searchProfile, searchGroups, clientClose) details, err := provider.GetDetails("john") require.NoError(t, err) @@ -1169,99 +1620,100 @@ func TestLDAPUserProvider_GetDetailsExtended_ShouldPass(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) - mockClient := NewMockLDAPClient(ctrl) - - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - StreetAddress: "street", - FamilyName: "sn", - MiddleName: "middle", - GivenName: "givenName", - Nickname: "nickname", - Gender: "gender", - Birthdate: "birthDate", - Website: "website", - Profile: "profile", - Picture: "picture", - ZoneInfo: "zoneinfo", - Locale: "locale", - PhoneNumber: "phone", - PhoneExtension: "ext", - Locality: "locality", - Region: "region", - PostalCode: "postCode", - Country: "c", - Extra: map[string]schema.AuthenticationBackendLDAPAttributesAttribute{ - "exampleStr": { - AuthenticationBackendExtraAttribute: schema.AuthenticationBackendExtraAttribute{ - MultiValued: false, - ValueType: ValueTypeString, - }, + mockDialer := NewMockLDAPClientDialer(ctrl) + + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + StreetAddress: "street", + FamilyName: "sn", + MiddleName: "middle", + GivenName: "givenName", + Nickname: "nickname", + Gender: "gender", + Birthdate: "birthDate", + Website: "website", + Profile: "profile", + Picture: "picture", + ZoneInfo: "zoneinfo", + Locale: "locale", + PhoneNumber: "phone", + PhoneExtension: "ext", + Locality: "locality", + Region: "region", + PostalCode: "postCode", + Country: "c", + Extra: map[string]schema.AuthenticationBackendLDAPAttributesAttribute{ + "exampleStr": { + AuthenticationBackendExtraAttribute: schema.AuthenticationBackendExtraAttribute{ + MultiValued: false, + ValueType: ValueTypeString, }, - "exampleStrMV": { - AuthenticationBackendExtraAttribute: schema.AuthenticationBackendExtraAttribute{ - MultiValued: true, - ValueType: ValueTypeString, - }, + }, + "exampleStrMV": { + AuthenticationBackendExtraAttribute: schema.AuthenticationBackendExtraAttribute{ + MultiValued: true, + ValueType: ValueTypeString, }, - "exampleInt": { - Name: "exampleIntChangedAttributeName", - AuthenticationBackendExtraAttribute: schema.AuthenticationBackendExtraAttribute{ - MultiValued: false, - ValueType: ValueTypeInteger, - }, + }, + "exampleInt": { + Name: "exampleIntChangedAttributeName", + AuthenticationBackendExtraAttribute: schema.AuthenticationBackendExtraAttribute{ + MultiValued: false, + ValueType: ValueTypeInteger, }, - "exampleIntMV": { - AuthenticationBackendExtraAttribute: schema.AuthenticationBackendExtraAttribute{ - MultiValued: true, - ValueType: ValueTypeInteger, - }, + }, + "exampleIntMV": { + AuthenticationBackendExtraAttribute: schema.AuthenticationBackendExtraAttribute{ + MultiValued: true, + ValueType: ValueTypeInteger, }, - "exampleBool": { - AuthenticationBackendExtraAttribute: schema.AuthenticationBackendExtraAttribute{ - MultiValued: false, - ValueType: ValueTypeBoolean, - }, + }, + "exampleBool": { + AuthenticationBackendExtraAttribute: schema.AuthenticationBackendExtraAttribute{ + MultiValued: false, + ValueType: ValueTypeBoolean, }, - "exampleBoolMV": { - AuthenticationBackendExtraAttribute: schema.AuthenticationBackendExtraAttribute{ - MultiValued: true, - ValueType: ValueTypeBoolean, - }, + }, + "exampleBoolMV": { + AuthenticationBackendExtraAttribute: schema.AuthenticationBackendExtraAttribute{ + MultiValued: true, + ValueType: ValueTypeBoolean, }, - "exampleEmptyStringInt": { - AuthenticationBackendExtraAttribute: schema.AuthenticationBackendExtraAttribute{ - MultiValued: false, - ValueType: ValueTypeInteger, - }, + }, + "exampleEmptyStringInt": { + AuthenticationBackendExtraAttribute: schema.AuthenticationBackendExtraAttribute{ + MultiValued: false, + ValueType: ValueTypeInteger, }, - "exampleEmptyStringBoolean": { - AuthenticationBackendExtraAttribute: schema.AuthenticationBackendExtraAttribute{ - MultiValued: false, - ValueType: ValueTypeBoolean, - }, + }, + "exampleEmptyStringBoolean": { + AuthenticationBackendExtraAttribute: schema.AuthenticationBackendExtraAttribute{ + MultiValued: false, + ValueType: ValueTypeBoolean, }, }, }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - PermitReferrals: true, }, - false, - nil, - mockFactory) + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + PermitReferrals: true, + } + + factory := NewStandardLDAPClientFactory(config, nil, mockDialer) - dialURL := mockFactory.EXPECT(). + provider := NewLDAPUserProviderWithFactory(config, false, factory) + + mockClient := NewMockLDAPClient(ctrl) + + dialURL := mockDialer.EXPECT(). DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). Return(mockClient, nil) @@ -1487,39 +1939,40 @@ func TestLDAPUserProvider_GetDetailsExtended_ShouldParseError(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) - mockClient := NewMockLDAPClient(ctrl) + mockDialer := NewMockLDAPClientDialer(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - StreetAddress: "street", - Extra: map[string]schema.AuthenticationBackendLDAPAttributesAttribute{ - "example": { - AuthenticationBackendExtraAttribute: schema.AuthenticationBackendExtraAttribute{ - MultiValued: tc.multiValued, - ValueType: tc.valueType, - }, + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + StreetAddress: "street", + Extra: map[string]schema.AuthenticationBackendLDAPAttributesAttribute{ + "example": { + AuthenticationBackendExtraAttribute: schema.AuthenticationBackendExtraAttribute{ + MultiValued: tc.multiValued, + ValueType: tc.valueType, }, }, }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - PermitReferrals: true, }, - false, - nil, - mockFactory) + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + PermitReferrals: true, + } + + factory := NewStandardLDAPClientFactory(config, nil, mockDialer) + + provider := NewLDAPUserProviderWithFactory(config, false, factory) - dialURL := mockFactory.EXPECT(). + mockClient := NewMockLDAPClient(ctrl) + + dialURL := mockDialer.EXPECT(). DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). Return(mockClient, nil) @@ -1578,31 +2031,32 @@ func TestLDAPUserProvider_GetDetailsExtended_ShouldErrorBadPictureURL(t *testing ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) - mockClient := NewMockLDAPClient(ctrl) - - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - Picture: "photoURL", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - PermitReferrals: true, + mockDialer := NewMockLDAPClientDialer(ctrl) + + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + Picture: "photoURL", }, - false, - nil, - mockFactory) + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + PermitReferrals: true, + } + + factory := NewStandardLDAPClientFactory(config, nil, mockDialer) + + provider := NewLDAPUserProviderWithFactory(config, false, factory) - dialURL := mockFactory.EXPECT(). + mockClient := NewMockLDAPClient(ctrl) + + dialURL := mockDialer.EXPECT(). DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). Return(mockClient, nil) @@ -1657,31 +2111,32 @@ func TestLDAPUserProvider_GetDetailsExtended_ShouldErrorBadProfileURL(t *testing ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) - mockClient := NewMockLDAPClient(ctrl) - - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - Profile: "profile", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - PermitReferrals: true, + mockDialer := NewMockLDAPClientDialer(ctrl) + + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + Profile: "profile", }, - false, - nil, - mockFactory) + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + PermitReferrals: true, + } + + factory := NewStandardLDAPClientFactory(config, nil, mockDialer) - dialURL := mockFactory.EXPECT(). + provider := NewLDAPUserProviderWithFactory(config, false, factory) + + mockClient := NewMockLDAPClient(ctrl) + + dialURL := mockDialer.EXPECT(). DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). Return(mockClient, nil) @@ -1736,31 +2191,32 @@ func TestLDAPUserProvider_GetDetailsExtended_ShouldErrorBadWebsiteURL(t *testing ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) - mockClient := NewMockLDAPClient(ctrl) - - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - Website: "www", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - PermitReferrals: true, + mockDialer := NewMockLDAPClientDialer(ctrl) + + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + Website: "www", }, - false, - nil, - mockFactory) + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + PermitReferrals: true, + } + + factory := NewStandardLDAPClientFactory(config, nil, mockDialer) + + provider := NewLDAPUserProviderWithFactory(config, false, factory) + + mockClient := NewMockLDAPClient(ctrl) - dialURL := mockFactory.EXPECT(). + dialURL := mockDialer.EXPECT(). DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). Return(mockClient, nil) @@ -1815,31 +2271,32 @@ func TestLDAPUserProvider_GetDetailsExtended_ShouldErrorBadLocale(t *testing.T) ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) - mockClient := NewMockLDAPClient(ctrl) - - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - Locale: "locale", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - PermitReferrals: true, + mockDialer := NewMockLDAPClientDialer(ctrl) + + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + Locale: "locale", }, - false, - nil, - mockFactory) + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + PermitReferrals: true, + } + + factory := NewStandardLDAPClientFactory(config, nil, mockDialer) + + provider := NewLDAPUserProviderWithFactory(config, false, factory) + + mockClient := NewMockLDAPClient(ctrl) - dialURL := mockFactory.EXPECT(). + dialURL := mockDialer.EXPECT(). DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). Return(mockClient, nil) @@ -1894,44 +2351,41 @@ func TestLDAPUserProvider_GetDetails_ShouldReturnOnUserError(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + PermitReferrals: true, + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - PermitReferrals: true, - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchProfile := mockClient.EXPECT(). Search(gomock.Any()). Return(nil, fmt.Errorf("failed to search")) - gomock.InOrder(dialURL, connBind, searchProfile, connClose) + gomock.InOrder(dialURL, clientBind, searchProfile, clientClose) details, err := provider.GetDetails("john") assert.Nil(t, details) @@ -1942,30 +2396,31 @@ func TestLDAPUserProvider_GetDetailsExtendedShouldReturnOnBindError(t *testing.T ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) - mockClient := NewMockLDAPClient(ctrl) - - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - PermitReferrals: true, + mockDialer := NewMockLDAPClientDialer(ctrl) + + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", }, - false, - nil, - mockFactory) + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + PermitReferrals: true, + } + + factory := NewStandardLDAPClientFactory(config, nil, mockDialer) - dialURL := mockFactory.EXPECT(). + provider := NewLDAPUserProviderWithFactory(config, false, factory) + + mockClient := NewMockLDAPClient(ctrl) + + dialURL := mockDialer.EXPECT(). DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). Return(mockClient, nil) @@ -1979,36 +2434,36 @@ func TestLDAPUserProvider_GetDetailsExtendedShouldReturnOnBindError(t *testing.T details, err := provider.GetDetailsExtended("john") assert.Nil(t, details) - assert.EqualError(t, err, "bind failed with error: bad bind") + assert.EqualError(t, err, "error occurred performing bind: bad bind") } func TestLDAPUserProvider_GetDetailsExtendedShouldReturnOnDialError(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) - - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - PermitReferrals: true, + mockDialer := NewMockLDAPClientDialer(ctrl) + + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", }, - false, - nil, - mockFactory) + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + PermitReferrals: true, + } + + factory := NewStandardLDAPClientFactory(config, nil, mockDialer) - dialURL := mockFactory.EXPECT(). + provider := NewLDAPUserProviderWithFactory(config, false, factory) + + dialURL := mockDialer.EXPECT(). DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). Return(nil, fmt.Errorf("failed to dial")) @@ -2016,37 +2471,37 @@ func TestLDAPUserProvider_GetDetailsExtendedShouldReturnOnDialError(t *testing.T details, err := provider.GetDetailsExtended("john") assert.Nil(t, details) - assert.EqualError(t, err, "dial failed with error: failed to dial") + assert.EqualError(t, err, "error occurred dialing address: failed to dial") } func TestLDAPUserProvider_GetDetailsExtendedShouldReturnOnUserError(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + PermitReferrals: true, + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + + factory := NewStandardLDAPClientFactory(config, nil, mockDialer) mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - PermitReferrals: true, - }, - false, - nil, - mockFactory) + provider := NewLDAPUserProviderWithFactory(config, false, factory) - dialURL := mockFactory.EXPECT(). + dialURL := mockDialer.EXPECT(). DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). Return(mockClient, nil) @@ -2071,38 +2526,35 @@ func TestLDAPUserProvider_GetDetails_ShouldReturnOnGroupsError(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + PermitReferrals: true, + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - PermitReferrals: true, - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchGroups := mockClient.EXPECT(). Search(gomock.Any()). @@ -2132,7 +2584,7 @@ func TestLDAPUserProvider_GetDetails_ShouldReturnOnGroupsError(t *testing.T) { }, }, nil) - gomock.InOrder(dialURL, connBind, searchProfile, searchGroups, connClose) + gomock.InOrder(dialURL, clientBind, searchProfile, searchGroups, clientClose) details, err := provider.GetDetails("john") @@ -2144,37 +2596,34 @@ func TestShouldNotCrashWhenEmailsAreNotRetrievedFromLDAP(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + DisplayName: "displayName", + MemberOf: "memberOf", + GroupName: "displayName", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - DisplayName: "displayName", - MemberOf: "memberOf", - GroupName: "displayName", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchGroups := mockClient.EXPECT(). Search(gomock.Any()). @@ -2196,7 +2645,7 @@ func TestShouldNotCrashWhenEmailsAreNotRetrievedFromLDAP(t *testing.T) { }, }, nil) - gomock.InOrder(dialURL, connBind, searchProfile, searchGroups, connClose) + gomock.InOrder(dialURL, clientBind, searchProfile, searchGroups, clientClose) details, err := provider.GetDetails("john") require.NoError(t, err) @@ -2210,37 +2659,34 @@ func TestShouldUnauthenticatedBind(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + DisplayName: "displayName", + MemberOf: "memberOf", + GroupName: "displayName", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - DisplayName: "displayName", - MemberOf: "memberOf", - GroupName: "displayName", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). UnauthenticatedBind(gomock.Eq("cn=admin,dc=example,dc=com")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchGroups := mockClient.EXPECT(). Search(gomock.Any()). @@ -2262,7 +2708,7 @@ func TestShouldUnauthenticatedBind(t *testing.T) { }, }, nil) - gomock.InOrder(dialURL, connBind, searchProfile, searchGroups, connClose) + gomock.InOrder(dialURL, clientBind, searchProfile, searchGroups, clientClose) details, err := provider.GetDetails("john") require.NoError(t, err) @@ -2276,38 +2722,35 @@ func TestShouldReturnUsernameFromLDAP(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + GroupName: "cn", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - GroupName: "cn", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchGroups := mockClient.EXPECT(). Search(gomock.Any()). @@ -2337,7 +2780,7 @@ func TestShouldReturnUsernameFromLDAP(t *testing.T) { }, }, nil) - gomock.InOrder(dialURL, connBind, searchProfile, searchGroups, connClose) + gomock.InOrder(dialURL, clientBind, searchProfile, searchGroups, clientClose) details, err := provider.GetDetails("john") require.NoError(t, err) @@ -2352,40 +2795,37 @@ func TestShouldReturnUsernameFromLDAPSearchModeMemberOfRDN(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + GroupName: "cn", + }, + GroupSearchMode: "memberof", + UsersFilter: "uid={input}", + GroupsFilter: "(|{memberof:rdn})", + AdditionalUsersDN: "ou=users", + BaseDN: "DC=example,DC=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - GroupName: "cn", - }, - GroupSearchMode: "memberof", - UsersFilter: "uid={input}", - GroupsFilter: "(|{memberof:rdn})", - AdditionalUsersDN: "ou=users", - BaseDN: "DC=example,DC=com", - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() requestGroups := ldap.NewSearchRequest( provider.groupsBaseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, @@ -2427,7 +2867,7 @@ func TestShouldReturnUsernameFromLDAPSearchModeMemberOfRDN(t *testing.T) { }, }, nil) - gomock.InOrder(dialURL, connBind, searchProfile, searchGroups, connClose) + gomock.InOrder(dialURL, clientBind, searchProfile, searchGroups, clientClose) details, err := provider.GetDetails("john") require.NoError(t, err) @@ -2442,41 +2882,38 @@ func TestShouldReturnUsernameFromLDAPSearchModeMemberOfDN(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "CN=Administrator,CN=Users,DC=example,DC=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + DistinguishedName: "distinguishedName", + Username: "sAMAccountName", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + GroupName: "cn", + }, + GroupSearchMode: "memberof", + UsersFilter: "sAMAccountName={input}", + GroupsFilter: "(|{memberof:dn})", + AdditionalUsersDN: "CN=users", + BaseDN: "DC=example,DC=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "CN=Administrator,CN=Users,DC=example,DC=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - DistinguishedName: "distinguishedName", - Username: "sAMAccountName", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - GroupName: "cn", - }, - GroupSearchMode: "memberof", - UsersFilter: "sAMAccountName={input}", - GroupsFilter: "(|{memberof:dn})", - AdditionalUsersDN: "CN=users", - BaseDN: "DC=example,DC=com", - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("CN=Administrator,CN=Users,DC=example,DC=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() requestGroups := ldap.NewSearchRequest( provider.groupsBaseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, @@ -2515,7 +2952,7 @@ func TestShouldReturnUsernameFromLDAPSearchModeMemberOfDN(t *testing.T) { }, }, nil) - gomock.InOrder(dialURL, connBind, searchProfile, searchGroups, connClose) + gomock.InOrder(dialURL, clientBind, searchProfile, searchGroups, clientClose) details, err := provider.GetDetails("john") require.NoError(t, err) @@ -2530,41 +2967,38 @@ func TestShouldReturnErrSearchMemberOf(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "CN=Administrator,CN=Users,DC=example,DC=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + DistinguishedName: "distinguishedName", + Username: "sAMAccountName", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + GroupName: "cn", + }, + GroupSearchMode: "memberof", + UsersFilter: "sAMAccountName={input}", + GroupsFilter: "(|{memberof:dn})", + AdditionalUsersDN: "CN=users", + BaseDN: "DC=example,DC=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "CN=Administrator,CN=Users,DC=example,DC=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - DistinguishedName: "distinguishedName", - Username: "sAMAccountName", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - GroupName: "cn", - }, - GroupSearchMode: "memberof", - UsersFilter: "sAMAccountName={input}", - GroupsFilter: "(|{memberof:dn})", - AdditionalUsersDN: "CN=users", - BaseDN: "DC=example,DC=com", - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("CN=Administrator,CN=Users,DC=example,DC=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() requestGroups := ldap.NewSearchRequest( provider.groupsBaseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, @@ -2603,7 +3037,7 @@ func TestShouldReturnErrSearchMemberOf(t *testing.T) { }, }, nil) - gomock.InOrder(dialURL, connBind, searchProfile, searchGroups, connClose) + gomock.InOrder(dialURL, clientBind, searchProfile, searchGroups, clientClose) details, err := provider.GetDetails("john") assert.Nil(t, details) @@ -2614,41 +3048,38 @@ func TestShouldReturnErrUnknownSearchMode(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "CN=Administrator,CN=Users,DC=example,DC=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + DistinguishedName: "distinguishedName", + Username: "sAMAccountName", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + GroupName: "cn", + }, + GroupSearchMode: "bad", + UsersFilter: "sAMAccountName={input}", + GroupsFilter: "(|{memberof:dn})", + AdditionalUsersDN: "CN=users", + BaseDN: "DC=example,DC=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "CN=Administrator,CN=Users,DC=example,DC=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - DistinguishedName: "distinguishedName", - Username: "sAMAccountName", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - GroupName: "cn", - }, - GroupSearchMode: "bad", - UsersFilter: "sAMAccountName={input}", - GroupsFilter: "(|{memberof:dn})", - AdditionalUsersDN: "CN=users", - BaseDN: "DC=example,DC=com", - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("CN=Administrator,CN=Users,DC=example,DC=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchProfile := mockClient.EXPECT(). Search(gomock.Any()). @@ -2678,7 +3109,7 @@ func TestShouldReturnErrUnknownSearchMode(t *testing.T) { }, }, nil) - gomock.InOrder(dialURL, connBind, searchProfile, connClose) + gomock.InOrder(dialURL, clientBind, searchProfile, clientClose) details, err := provider.GetDetails("john") assert.Nil(t, details) @@ -2690,41 +3121,38 @@ func TestShouldSkipEmptyAttributesSearchModeMemberOf(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "CN=Administrator,CN=Users,DC=example,DC=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + DistinguishedName: "distinguishedName", + Username: "sAMAccountName", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + GroupName: "cn", + }, + GroupSearchMode: "memberof", + UsersFilter: "sAMAccountName={input}", + GroupsFilter: "(|{memberof:dn})", + AdditionalUsersDN: "CN=users", + BaseDN: "DC=example,DC=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "CN=Administrator,CN=Users,DC=example,DC=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - DistinguishedName: "distinguishedName", - Username: "sAMAccountName", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - GroupName: "cn", - }, - GroupSearchMode: "memberof", - UsersFilter: "sAMAccountName={input}", - GroupsFilter: "(|{memberof:dn})", - AdditionalUsersDN: "CN=users", - BaseDN: "DC=example,DC=com", - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("CN=Administrator,CN=Users,DC=example,DC=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchProfile := mockClient.EXPECT(). Search(gomock.Any()). @@ -2792,7 +3220,7 @@ func TestShouldSkipEmptyAttributesSearchModeMemberOf(t *testing.T) { }, }, nil) - gomock.InOrder(dialURL, connBind, searchProfile, searchGroups, connClose) + gomock.InOrder(dialURL, clientBind, searchProfile, searchGroups, clientClose) details, err := provider.GetDetails("john") @@ -2804,41 +3232,38 @@ func TestShouldSkipEmptyAttributesSearchModeFilter(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "CN=Administrator,CN=Users,DC=example,DC=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + DistinguishedName: "distinguishedName", + Username: "sAMAccountName", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + GroupName: "cn", + }, + GroupSearchMode: "filter", + UsersFilter: "sAMAccountName={input}", + GroupsFilter: "(|{memberof:dn})", + AdditionalUsersDN: "CN=users", + BaseDN: "DC=example,DC=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "CN=Administrator,CN=Users,DC=example,DC=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - DistinguishedName: "distinguishedName", - Username: "sAMAccountName", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - GroupName: "cn", - }, - GroupSearchMode: "filter", - UsersFilter: "sAMAccountName={input}", - GroupsFilter: "(|{memberof:dn})", - AdditionalUsersDN: "CN=users", - BaseDN: "DC=example,DC=com", - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("CN=Administrator,CN=Users,DC=example,DC=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchProfile := mockClient.EXPECT(). Search(gomock.Any()). @@ -2906,7 +3331,7 @@ func TestShouldSkipEmptyAttributesSearchModeFilter(t *testing.T) { }, }, nil) - gomock.InOrder(dialURL, connBind, searchProfile, searchGroups, connClose) + gomock.InOrder(dialURL, clientBind, searchProfile, searchGroups, clientClose) details, err := provider.GetDetails("john") @@ -2918,39 +3343,36 @@ func TestShouldSkipEmptyGroupsResultMemberOf(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + GroupName: "cn", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + PermitReferrals: true, + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - GroupName: "cn", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - PermitReferrals: true, - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchGroups := mockClient.EXPECT(). Search(gomock.Any()). @@ -2984,7 +3406,7 @@ func TestShouldSkipEmptyGroupsResultMemberOf(t *testing.T) { }, }, nil) - gomock.InOrder(dialURL, connBind, searchProfile, searchGroups, connClose) + gomock.InOrder(dialURL, clientBind, searchProfile, searchGroups, clientClose) details, err := provider.GetDetails("john") require.NoError(t, err) @@ -2998,41 +3420,38 @@ func TestShouldReturnUsernameFromLDAPWithReferralsInErrorAndResult(t *testing.T) ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + GroupName: "cn", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + PermitReferrals: true, + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) mockClientReferral := NewMockLDAPClient(ctrl) mockClientReferralAlt := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - GroupName: "cn", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - PermitReferrals: true, - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchGroups := mockClient.EXPECT(). Search(gomock.Any()). @@ -3045,15 +3464,13 @@ func TestShouldReturnUsernameFromLDAPWithReferralsInErrorAndResult(t *testing.T) Referrals: []string{"ldap://192.168.0.1"}, }, &ldap.Error{ResultCode: ldap.LDAPResultReferral, Err: errors.New("referral"), Packet: &testBERPacketReferral}) - dialURLReferral := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://192.168.0.1"), gomock.Any()). - Return(mockClientReferral, nil) + dialURLReferral := mockDialer.EXPECT().DialURL("ldap://192.168.0.1", gomock.Any()).Return(mockClientReferral, nil) - connBindReferral := mockClientReferral.EXPECT(). + clientBindReferral := mockClientReferral.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connCloseReferral := mockClientReferral.EXPECT().Close() + clientCloseReferral := mockClientReferral.EXPECT().Close() searchProfileReferral := mockClientReferral.EXPECT(). Search(gomock.Any()). @@ -3079,15 +3496,13 @@ func TestShouldReturnUsernameFromLDAPWithReferralsInErrorAndResult(t *testing.T) }, }, nil) - dialURLReferralAlt := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://192.168.0.1"), gomock.Any()). - Return(mockClientReferralAlt, nil) + dialURLReferralAlt := mockDialer.EXPECT().DialURL("ldap://192.168.0.1", gomock.Any()).Return(mockClientReferralAlt, nil) - connBindReferralAlt := mockClientReferralAlt.EXPECT(). + clientBindReferralAlt := mockClientReferralAlt.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connCloseReferralAlt := mockClientReferralAlt.EXPECT().Close() + clientCloseReferralAlt := mockClientReferralAlt.EXPECT().Close() searchProfileReferralAlt := mockClientReferralAlt.EXPECT(). Search(gomock.Any()). @@ -3113,7 +3528,7 @@ func TestShouldReturnUsernameFromLDAPWithReferralsInErrorAndResult(t *testing.T) }, }, nil) - gomock.InOrder(dialURL, connBind, searchProfile, dialURLReferral, connBindReferral, searchProfileReferral, connCloseReferral, dialURLReferralAlt, connBindReferralAlt, searchProfileReferralAlt, connCloseReferralAlt, searchGroups, connClose) + gomock.InOrder(dialURL, clientBind, searchProfile, dialURLReferral, clientBindReferral, searchProfileReferral, clientCloseReferral, dialURLReferralAlt, clientBindReferralAlt, searchProfileReferralAlt, clientCloseReferralAlt, searchGroups, clientClose) details, err := provider.GetDetails("john") require.NoError(t, err) @@ -3128,40 +3543,37 @@ func TestShouldReturnUsernameFromLDAPWithReferralsInErrorAndNoResult(t *testing. ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + GroupName: "cn", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + PermitReferrals: true, + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) mockClientReferral := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - GroupName: "cn", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - PermitReferrals: true, - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchGroups := mockClient.EXPECT(). Search(gomock.Any()). @@ -3171,15 +3583,13 @@ func TestShouldReturnUsernameFromLDAPWithReferralsInErrorAndNoResult(t *testing. Search(gomock.Any()). Return(nil, &ldap.Error{ResultCode: ldap.LDAPResultReferral, Err: errors.New("referral"), Packet: &testBERPacketReferral}) - dialURLReferral := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://192.168.0.1"), gomock.Any()). - Return(mockClientReferral, nil) + dialURLReferral := mockDialer.EXPECT().DialURL("ldap://192.168.0.1", gomock.Any()).Return(mockClientReferral, nil) - connBindReferral := mockClientReferral.EXPECT(). + clientBindReferral := mockClientReferral.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connCloseReferral := mockClientReferral.EXPECT().Close() + clientCloseReferral := mockClientReferral.EXPECT().Close() searchProfileReferral := mockClientReferral.EXPECT(). Search(gomock.Any()). @@ -3205,7 +3615,7 @@ func TestShouldReturnUsernameFromLDAPWithReferralsInErrorAndNoResult(t *testing. }, }, nil) - gomock.InOrder(dialURL, connBind, searchProfile, dialURLReferral, connBindReferral, searchProfileReferral, connCloseReferral, searchGroups, connClose) + gomock.InOrder(dialURL, clientBind, searchProfile, dialURLReferral, clientBindReferral, searchProfileReferral, clientCloseReferral, searchGroups, clientClose) details, err := provider.GetDetails("john") require.NoError(t, err) @@ -3220,114 +3630,104 @@ func TestShouldReturnDialErrDuringReferralSearchUsernameFromLDAPWithReferralsInE ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + GroupName: "cn", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + PermitReferrals: true, + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - GroupName: "cn", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - PermitReferrals: true, - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchProfile := mockClient.EXPECT(). Search(gomock.Any()). Return(nil, &ldap.Error{ResultCode: ldap.LDAPResultReferral, Err: errors.New("referral"), Packet: &testBERPacketReferral}) - dialURLReferral := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://192.168.0.1"), gomock.Any()). - Return(nil, fmt.Errorf("failed to connect")) + dialURLReferral := mockDialer.EXPECT().DialURL("ldap://192.168.0.1", gomock.Any()).Return(nil, fmt.Errorf("failed to connect")) - gomock.InOrder(dialURL, connBind, searchProfile, dialURLReferral, connClose) + gomock.InOrder(dialURL, clientBind, searchProfile, dialURLReferral, clientClose) details, err := provider.GetDetails("john") assert.Nil(t, details) - assert.EqualError(t, err, "cannot find user DN of user 'john'. Cause: error occurred connecting to referred LDAP server 'ldap://192.168.0.1': dial failed with error: failed to connect") + assert.EqualError(t, err, "cannot find user DN of user 'john'. Cause: error occurred connecting to referred LDAP server 'ldap://192.168.0.1': error occurred dialing address: failed to connect") } func TestShouldReturnSearchErrDuringReferralSearchUsernameFromLDAPWithReferralsInErrorAndNoResult(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + GroupName: "cn", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + PermitReferrals: true, + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) mockClientReferral := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - GroupName: "cn", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - PermitReferrals: true, - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchProfile := mockClient.EXPECT(). Search(gomock.Any()). Return(nil, &ldap.Error{ResultCode: ldap.LDAPResultReferral, Err: errors.New("referral"), Packet: &testBERPacketReferral}) - dialURLReferral := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://192.168.0.1"), gomock.Any()). - Return(mockClientReferral, nil) + dialURLReferral := mockDialer.EXPECT().DialURL("ldap://192.168.0.1", gomock.Any()).Return(mockClientReferral, nil) - connBindReferral := mockClientReferral.EXPECT(). + clientBindReferral := mockClientReferral.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connCloseReferral := mockClientReferral.EXPECT().Close() + clientCloseReferral := mockClientReferral.EXPECT().Close() searchProfileReferral := mockClientReferral.EXPECT(). Search(gomock.Any()). Return(nil, fmt.Errorf("not found")) - gomock.InOrder(dialURL, connBind, searchProfile, dialURLReferral, connBindReferral, searchProfileReferral, connCloseReferral, connClose) + gomock.InOrder(dialURL, clientBind, searchProfile, dialURLReferral, clientBindReferral, searchProfileReferral, clientCloseReferral, clientClose) details, err := provider.GetDetails("john") @@ -3339,45 +3739,42 @@ func TestShouldNotReturnUsernameFromLDAPWithReferralsInErrorAndReferralsNotPermi ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + GroupName: "cn", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + PermitReferrals: false, + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - GroupName: "cn", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - PermitReferrals: false, - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchProfile := mockClient.EXPECT(). Search(gomock.Any()). Return(nil, &ldap.Error{ResultCode: ldap.LDAPResultReferral, Err: errors.New("referral"), Packet: &testBERPacketReferral}) - gomock.InOrder(dialURL, connBind, searchProfile, connClose) + gomock.InOrder(dialURL, clientBind, searchProfile, clientClose) details, err := provider.GetDetails("john") assert.EqualError(t, err, "cannot find user DN of user 'john'. Cause: LDAP Result Code 10 \"Referral\": referral") @@ -3388,40 +3785,37 @@ func TestShouldReturnUsernameFromLDAPWithReferralsErr(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + GroupName: "cn", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + PermitReferrals: true, + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) mockClientReferral := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - GroupName: "cn", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - PermitReferrals: true, - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchGroups := mockClient.EXPECT(). Search(gomock.Any()). @@ -3431,15 +3825,13 @@ func TestShouldReturnUsernameFromLDAPWithReferralsErr(t *testing.T) { Search(gomock.Any()). Return(&ldap.SearchResult{}, &ldap.Error{ResultCode: ldap.LDAPResultReferral, Err: errors.New("referral"), Packet: &testBERPacketReferral}) - dialURLReferral := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://192.168.0.1"), gomock.Any()). - Return(mockClientReferral, nil) + dialURLReferral := mockDialer.EXPECT().DialURL("ldap://192.168.0.1", gomock.Any()).Return(mockClientReferral, nil) - connBindReferral := mockClientReferral.EXPECT(). + clientBindReferral := mockClientReferral.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connCloseReferral := mockClientReferral.EXPECT().Close() + clientCloseReferral := mockClientReferral.EXPECT().Close() searchProfileReferral := mockClientReferral.EXPECT(). Search(gomock.Any()). @@ -3465,7 +3857,7 @@ func TestShouldReturnUsernameFromLDAPWithReferralsErr(t *testing.T) { }, }, nil) - gomock.InOrder(dialURL, connBind, searchProfile, dialURLReferral, connBindReferral, searchProfileReferral, connCloseReferral, searchGroups, connClose) + gomock.InOrder(dialURL, clientBind, searchProfile, dialURLReferral, clientBindReferral, searchProfileReferral, clientCloseReferral, searchGroups, clientClose) details, err := provider.GetDetails("john") require.NoError(t, err) @@ -3480,34 +3872,33 @@ func TestShouldNotUpdateUserPasswordConnect(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + PermitReferrals: false, + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - PermitReferrals: false, - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(nil, errors.New("tcp timeout")) - dialURLOIDs := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - connBindOIDs := mockClient.EXPECT(). + dialURLOIDs := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) + + clientBindOIDs := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -3531,53 +3922,46 @@ func TestShouldNotUpdateUserPasswordConnect(t *testing.T) { }, }, nil) - connCloseOIDs := mockClient.EXPECT().Close() + clientCloseOIDs := mockClient.EXPECT().Close() - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(nil, errors.New("tcp timeout")) + gomock.InOrder(dialURLOIDs, clientBindOIDs, searchOIDs, clientCloseOIDs, dialURL) - gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL) + require.NoError(t, provider.StartupCheck()) - err := provider.StartupCheck() - require.NoError(t, err) - - err = provider.UpdatePassword("john", "password") - assert.EqualError(t, err, "unable to update password. Cause: dial failed with error: tcp timeout") + assert.EqualError(t, provider.UpdatePassword("john", "password"), "unable to update password. Cause: error occurred dialing address: tcp timeout") } func TestShouldNotUpdateUserPasswordGetDetails(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + PermitReferrals: false, + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - PermitReferrals: false, - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - dialURLOIDs := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - connBindOIDs := mockClient.EXPECT(). + dialURLOIDs := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) + + clientBindOIDs := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -3601,56 +3985,51 @@ func TestShouldNotUpdateUserPasswordGetDetails(t *testing.T) { }, }, nil) - connCloseOIDs := mockClient.EXPECT().Close() + clientCloseOIDs := mockClient.EXPECT().Close() - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) - - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchProfile := mockClient.EXPECT(). Search(gomock.Any()). Return(nil, &ldap.Error{ResultCode: ldap.LDAPResultProtocolError, Err: errors.New("permission error")}) - gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, connClose) + gomock.InOrder(dialURLOIDs, clientBindOIDs, searchOIDs, clientCloseOIDs, dialURL, clientBind, searchProfile, clientClose) - err := provider.StartupCheck() - require.NoError(t, err) + require.NoError(t, provider.StartupCheck()) - err = provider.UpdatePassword("john", "password") - assert.EqualError(t, err, "unable to update password. Cause: cannot find user DN of user 'john'. Cause: LDAP Result Code 2 \"Protocol Error\": permission error") + assert.EqualError(t, provider.UpdatePassword("john", "password"), "unable to update password. Cause: cannot find user DN of user 'john'. Cause: LDAP Result Code 2 \"Protocol Error\": permission error") } func TestShouldUpdateUserPassword(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) + + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) modifyRequest := ldap.NewModifyRequest( "uid=test,dc=example,dc=com", @@ -3659,11 +4038,9 @@ func TestShouldUpdateUserPassword(t *testing.T) { modifyRequest.Replace(ldapAttributeUserPassword, []string{"password"}) - dialURLOIDs := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + dialURLOIDs := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - connBindOIDs := mockClient.EXPECT(). + clientBindOIDs := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -3687,17 +4064,13 @@ func TestShouldUpdateUserPassword(t *testing.T) { }, }, nil) - connCloseOIDs := mockClient.EXPECT().Close() + clientCloseOIDs := mockClient.EXPECT().Close() - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) - - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchProfile := mockClient.EXPECT(). Search(gomock.Any()). @@ -3727,7 +4100,7 @@ func TestShouldUpdateUserPassword(t *testing.T) { Modify(modifyRequest). Return(nil) - gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, modify, connClose) + gomock.InOrder(dialURLOIDs, clientBindOIDs, searchOIDs, clientCloseOIDs, dialURL, clientBind, searchProfile, modify, clientClose) err := provider.StartupCheck() require.NoError(t, err) @@ -3740,28 +4113,29 @@ func TestShouldUpdateUserPasswordMSAD(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Implementation: "activedirectory", + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Implementation: "activedirectory", - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) + + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) modifyRequest := ldap.NewModifyRequest( "uid=test,dc=example,dc=com", @@ -3771,11 +4145,9 @@ func TestShouldUpdateUserPasswordMSAD(t *testing.T) { pwdEncoded, _ := encodingUTF16LittleEndian.NewEncoder().String(fmt.Sprintf("\"%s\"", "password")) modifyRequest.Replace(ldapAttributeUnicodePwd, []string{pwdEncoded}) - dialURLOIDs := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + dialURLOIDs := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - connBindOIDs := mockClient.EXPECT(). + clientBindOIDs := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -3799,17 +4171,13 @@ func TestShouldUpdateUserPasswordMSAD(t *testing.T) { }, }, nil) - connCloseOIDs := mockClient.EXPECT().Close() - - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + clientCloseOIDs := mockClient.EXPECT().Close() - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchProfile := mockClient.EXPECT(). Search(gomock.Any()). @@ -3839,7 +4207,7 @@ func TestShouldUpdateUserPasswordMSAD(t *testing.T) { Modify(modifyRequest). Return(nil) - gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, modify, connClose) + gomock.InOrder(dialURLOIDs, clientBindOIDs, searchOIDs, clientCloseOIDs, dialURL, clientBind, searchProfile, modify, clientClose) err := provider.StartupCheck() require.NoError(t, err) @@ -3852,30 +4220,31 @@ func TestShouldUpdateUserPasswordMSADWithReferrals(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Implementation: "activedirectory", + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + PermitReferrals: true, + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) mockClientReferral := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Implementation: "activedirectory", - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - PermitReferrals: true, - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) + + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) modifyRequest := ldap.NewModifyRequest( "uid=test,dc=example,dc=com", @@ -3885,11 +4254,9 @@ func TestShouldUpdateUserPasswordMSADWithReferrals(t *testing.T) { pwdEncoded, _ := encodingUTF16LittleEndian.NewEncoder().String(fmt.Sprintf("\"%s\"", "password")) modifyRequest.Replace(ldapAttributeUnicodePwd, []string{pwdEncoded}) - dialURLOIDs := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + dialURLOIDs := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - connBindOIDs := mockClient.EXPECT(). + clientBindOIDs := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -3913,17 +4280,13 @@ func TestShouldUpdateUserPasswordMSADWithReferrals(t *testing.T) { }, }, nil) - connCloseOIDs := mockClient.EXPECT().Close() + clientCloseOIDs := mockClient.EXPECT().Close() - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) - - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchProfile := mockClient.EXPECT(). Search(gomock.Any()). @@ -3957,21 +4320,19 @@ func TestShouldUpdateUserPasswordMSADWithReferrals(t *testing.T) { Packet: &testBERPacketReferral, }) - dialURLReferral := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://192.168.0.1"), gomock.Any()). - Return(mockClientReferral, nil) + dialURLReferral := mockDialer.EXPECT().DialURL("ldap://192.168.0.1", gomock.Any()).Return(mockClientReferral, nil) - connBindReferral := mockClientReferral.EXPECT(). + clientBindReferral := mockClientReferral.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connCloseReferral := mockClientReferral.EXPECT().Close() + clientCloseReferral := mockClientReferral.EXPECT().Close() modifyReferral := mockClientReferral.EXPECT(). Modify(modifyRequest). Return(nil) - gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, modify, dialURLReferral, connBindReferral, modifyReferral, connCloseReferral, connClose) + gomock.InOrder(dialURLOIDs, clientBindOIDs, searchOIDs, clientCloseOIDs, dialURL, clientBind, searchProfile, modify, dialURLReferral, clientBindReferral, modifyReferral, clientCloseReferral, clientClose) err := provider.StartupCheck() require.NoError(t, err) @@ -3984,29 +4345,30 @@ func TestShouldUpdateUserPasswordMSADWithReferralsWithReferralConnectErr(t *test ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Implementation: "activedirectory", + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + PermitReferrals: true, + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Implementation: "activedirectory", - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - PermitReferrals: true, - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) + + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) modifyRequest := ldap.NewModifyRequest( "uid=test,dc=example,dc=com", @@ -4016,11 +4378,9 @@ func TestShouldUpdateUserPasswordMSADWithReferralsWithReferralConnectErr(t *test pwdEncoded, _ := encodingUTF16LittleEndian.NewEncoder().String(fmt.Sprintf("\"%s\"", "password")) modifyRequest.Replace(ldapAttributeUnicodePwd, []string{pwdEncoded}) - dialURLOIDs := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + dialURLOIDs := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - connBindOIDs := mockClient.EXPECT(). + clientBindOIDs := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -4044,17 +4404,13 @@ func TestShouldUpdateUserPasswordMSADWithReferralsWithReferralConnectErr(t *test }, }, nil) - connCloseOIDs := mockClient.EXPECT().Close() - - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + clientCloseOIDs := mockClient.EXPECT().Close() - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchProfile := mockClient.EXPECT(). Search(gomock.Any()). @@ -4088,47 +4444,46 @@ func TestShouldUpdateUserPasswordMSADWithReferralsWithReferralConnectErr(t *test Packet: &testBERPacketReferral, }) - dialURLReferral := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://192.168.0.1"), gomock.Any()). - Return(nil, errors.New("tcp timeout")) + dialURLReferral := mockDialer.EXPECT().DialURL("ldap://192.168.0.1", gomock.Any()).Return(nil, errors.New("tcp timeout")) - gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, modify, dialURLReferral, connClose) + gomock.InOrder(dialURLOIDs, clientBindOIDs, searchOIDs, clientCloseOIDs, dialURL, clientBind, searchProfile, modify, dialURLReferral, clientClose) err := provider.StartupCheck() require.NoError(t, err) err = provider.UpdatePassword("john", "password") - assert.EqualError(t, err, "unable to update password. Cause: error occurred connecting to referred LDAP server 'ldap://192.168.0.1': dial failed with error: tcp timeout. Original Error: LDAP Result Code 10 \"Referral\": error occurred") + assert.EqualError(t, err, "unable to update password. Cause: error occurred connecting to referred LDAP server 'ldap://192.168.0.1': error occurred dialing address: tcp timeout. Original Error: LDAP Result Code 10 \"Referral\": error occurred") } func TestShouldUpdateUserPasswordMSADWithReferralsWithReferralModifyErr(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Implementation: "activedirectory", + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + PermitReferrals: true, + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) mockClientReferral := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Implementation: "activedirectory", - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - PermitReferrals: true, - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) + + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) modifyRequest := ldap.NewModifyRequest( "uid=test,dc=example,dc=com", @@ -4138,11 +4493,9 @@ func TestShouldUpdateUserPasswordMSADWithReferralsWithReferralModifyErr(t *testi pwdEncoded, _ := encodingUTF16LittleEndian.NewEncoder().String(fmt.Sprintf("\"%s\"", "password")) modifyRequest.Replace(ldapAttributeUnicodePwd, []string{pwdEncoded}) - dialURLOIDs := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + dialURLOIDs := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - connBindOIDs := mockClient.EXPECT(). + clientBindOIDs := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -4166,17 +4519,13 @@ func TestShouldUpdateUserPasswordMSADWithReferralsWithReferralModifyErr(t *testi }, }, nil) - connCloseOIDs := mockClient.EXPECT().Close() - - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + clientCloseOIDs := mockClient.EXPECT().Close() - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchProfile := mockClient.EXPECT(). Search(gomock.Any()). @@ -4210,15 +4559,13 @@ func TestShouldUpdateUserPasswordMSADWithReferralsWithReferralModifyErr(t *testi Packet: &testBERPacketReferral, }) - dialURLReferral := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://192.168.0.1"), gomock.Any()). - Return(mockClientReferral, nil) + dialURLReferral := mockDialer.EXPECT().DialURL("ldap://192.168.0.1", gomock.Any()).Return(mockClientReferral, nil) - connBindReferral := mockClientReferral.EXPECT(). + clientBindReferral := mockClientReferral.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connCloseReferral := mockClientReferral.EXPECT().Close() + clientCloseReferral := mockClientReferral.EXPECT().Close() modifyReferral := mockClientReferral.EXPECT(). Modify(modifyRequest). @@ -4228,7 +4575,7 @@ func TestShouldUpdateUserPasswordMSADWithReferralsWithReferralModifyErr(t *testi Packet: &testBERPacketReferral, }) - gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, modify, dialURLReferral, connBindReferral, modifyReferral, connCloseReferral, connClose) + gomock.InOrder(dialURLOIDs, clientBindOIDs, searchOIDs, clientCloseOIDs, dialURL, clientBind, searchProfile, modify, dialURLReferral, clientBindReferral, modifyReferral, clientCloseReferral, clientClose) err := provider.StartupCheck() require.NoError(t, err) @@ -4241,29 +4588,30 @@ func TestShouldUpdateUserPasswordMSADWithoutReferrals(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Implementation: "activedirectory", + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + PermitReferrals: false, + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Implementation: "activedirectory", - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - PermitReferrals: false, - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) + + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) modifyRequest := ldap.NewModifyRequest( "uid=test,dc=example,dc=com", @@ -4273,11 +4621,9 @@ func TestShouldUpdateUserPasswordMSADWithoutReferrals(t *testing.T) { pwdEncoded, _ := encodingUTF16LittleEndian.NewEncoder().String(fmt.Sprintf("\"%s\"", "password")) modifyRequest.Replace(ldapAttributeUnicodePwd, []string{pwdEncoded}) - dialURLOIDs := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + dialURLOIDs := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - connBindOIDs := mockClient.EXPECT(). + clientBindOIDs := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -4301,17 +4647,13 @@ func TestShouldUpdateUserPasswordMSADWithoutReferrals(t *testing.T) { }, }, nil) - connCloseOIDs := mockClient.EXPECT().Close() - - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + clientCloseOIDs := mockClient.EXPECT().Close() - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchProfile := mockClient.EXPECT(). Search(gomock.Any()). @@ -4345,7 +4687,7 @@ func TestShouldUpdateUserPasswordMSADWithoutReferrals(t *testing.T) { Packet: &testBERPacketReferral, }) - gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, modify, connClose) + gomock.InOrder(dialURLOIDs, clientBindOIDs, searchOIDs, clientCloseOIDs, dialURL, clientBind, searchProfile, modify, clientClose) err := provider.StartupCheck() require.NoError(t, err) @@ -4358,27 +4700,28 @@ func TestShouldUpdateUserPasswordPasswdModifyExtension(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) + + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) pwdModifyRequest := ldap.NewPasswordModifyRequest( "uid=test,dc=example,dc=com", @@ -4386,11 +4729,9 @@ func TestShouldUpdateUserPasswordPasswdModifyExtension(t *testing.T) { "password", ) - dialURLOIDs := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + dialURLOIDs := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - connBindOIDs := mockClient.EXPECT(). + clientBindOIDs := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -4414,17 +4755,13 @@ func TestShouldUpdateUserPasswordPasswdModifyExtension(t *testing.T) { }, }, nil) - connCloseOIDs := mockClient.EXPECT().Close() + clientCloseOIDs := mockClient.EXPECT().Close() - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) - - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchProfile := mockClient.EXPECT(). Search(gomock.Any()). @@ -4454,7 +4791,7 @@ func TestShouldUpdateUserPasswordPasswdModifyExtension(t *testing.T) { PasswordModify(pwdModifyRequest). Return(nil, nil) - gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, passwdModify, connClose) + gomock.InOrder(dialURLOIDs, clientBindOIDs, searchOIDs, clientCloseOIDs, dialURL, clientBind, searchProfile, passwdModify, clientClose) err := provider.StartupCheck() require.NoError(t, err) @@ -4467,29 +4804,30 @@ func TestShouldUpdateUserPasswordPasswdModifyExtensionWithReferrals(t *testing.T ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + PermitReferrals: true, + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) mockClientReferral := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - PermitReferrals: true, - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) + + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) pwdModifyRequest := ldap.NewPasswordModifyRequest( "uid=test,dc=example,dc=com", @@ -4497,11 +4835,9 @@ func TestShouldUpdateUserPasswordPasswdModifyExtensionWithReferrals(t *testing.T "password", ) - dialURLOIDs := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + dialURLOIDs := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - connBindOIDs := mockClient.EXPECT(). + clientBindOIDs := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -4525,17 +4861,13 @@ func TestShouldUpdateUserPasswordPasswdModifyExtensionWithReferrals(t *testing.T }, }, nil) - connCloseOIDs := mockClient.EXPECT().Close() + clientCloseOIDs := mockClient.EXPECT().Close() - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) - - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchProfile := mockClient.EXPECT(). Search(gomock.Any()). @@ -4571,21 +4903,19 @@ func TestShouldUpdateUserPasswordPasswdModifyExtensionWithReferrals(t *testing.T Packet: &testBERPacketReferral, }) - dialURLReferral := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://192.168.0.1"), gomock.Any()). - Return(mockClientReferral, nil) + dialURLReferral := mockDialer.EXPECT().DialURL("ldap://192.168.0.1", gomock.Any()).Return(mockClientReferral, nil) - connBindReferral := mockClientReferral.EXPECT(). + clientBindReferral := mockClientReferral.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connCloseReferral := mockClientReferral.EXPECT().Close() + clientCloseReferral := mockClientReferral.EXPECT().Close() passwdModifyReferral := mockClientReferral.EXPECT(). PasswordModify(pwdModifyRequest). Return(&ldap.PasswordModifyResult{}, nil) - gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, passwdModify, dialURLReferral, connBindReferral, passwdModifyReferral, connCloseReferral, connClose) + gomock.InOrder(dialURLOIDs, clientBindOIDs, searchOIDs, clientCloseOIDs, dialURL, clientBind, searchProfile, passwdModify, dialURLReferral, clientBindReferral, passwdModifyReferral, clientCloseReferral, clientClose) err := provider.StartupCheck() require.NoError(t, err) @@ -4598,28 +4928,29 @@ func TestShouldUpdateUserPasswordPasswdModifyExtensionWithoutReferrals(t *testin ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + PermitReferrals: false, + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - PermitReferrals: false, - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) + + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) pwdModifyRequest := ldap.NewPasswordModifyRequest( "uid=test,dc=example,dc=com", @@ -4627,11 +4958,9 @@ func TestShouldUpdateUserPasswordPasswdModifyExtensionWithoutReferrals(t *testin "password", ) - dialURLOIDs := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + dialURLOIDs := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - connBindOIDs := mockClient.EXPECT(). + clientBindOIDs := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -4655,17 +4984,13 @@ func TestShouldUpdateUserPasswordPasswdModifyExtensionWithoutReferrals(t *testin }, }, nil) - connCloseOIDs := mockClient.EXPECT().Close() + clientCloseOIDs := mockClient.EXPECT().Close() - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) - - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchProfile := mockClient.EXPECT(). Search(gomock.Any()). @@ -4701,7 +5026,7 @@ func TestShouldUpdateUserPasswordPasswdModifyExtensionWithoutReferrals(t *testin Packet: &testBERPacketReferral, }) - gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, passwdModify, connClose) + gomock.InOrder(dialURLOIDs, clientBindOIDs, searchOIDs, clientCloseOIDs, dialURL, clientBind, searchProfile, passwdModify, clientClose) err := provider.StartupCheck() require.NoError(t, err) @@ -4714,28 +5039,29 @@ func TestShouldUpdateUserPasswordPasswdModifyExtensionWithReferralsReferralConne ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + PermitReferrals: true, + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - PermitReferrals: true, - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) + + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) pwdModifyRequest := ldap.NewPasswordModifyRequest( "uid=test,dc=example,dc=com", @@ -4743,11 +5069,9 @@ func TestShouldUpdateUserPasswordPasswdModifyExtensionWithReferralsReferralConne "password", ) - dialURLOIDs := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + dialURLOIDs := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - connBindOIDs := mockClient.EXPECT(). + clientBindOIDs := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -4771,17 +5095,13 @@ func TestShouldUpdateUserPasswordPasswdModifyExtensionWithReferralsReferralConne }, }, nil) - connCloseOIDs := mockClient.EXPECT().Close() + clientCloseOIDs := mockClient.EXPECT().Close() - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) - - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchProfile := mockClient.EXPECT(). Search(gomock.Any()). @@ -4817,46 +5137,45 @@ func TestShouldUpdateUserPasswordPasswdModifyExtensionWithReferralsReferralConne Packet: &testBERPacketReferral, }) - dialURLReferral := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://192.168.0.1"), gomock.Any()). - Return(nil, errors.New("tcp timeout")) + dialURLReferral := mockDialer.EXPECT().DialURL("ldap://192.168.0.1", gomock.Any()).Return(nil, errors.New("tcp timeout")) - gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, passwdModify, dialURLReferral, connClose) + gomock.InOrder(dialURLOIDs, clientBindOIDs, searchOIDs, clientCloseOIDs, dialURL, clientBind, searchProfile, passwdModify, dialURLReferral, clientClose) err := provider.StartupCheck() require.NoError(t, err) err = provider.UpdatePassword("john", "password") - assert.EqualError(t, err, "unable to update password. Cause: error occurred connecting to referred LDAP server 'ldap://192.168.0.1': dial failed with error: tcp timeout. Original Error: LDAP Result Code 10 \"Referral\": error occurred") + assert.EqualError(t, err, "unable to update password. Cause: error occurred connecting to referred LDAP server 'ldap://192.168.0.1': error occurred dialing address: tcp timeout. Original Error: LDAP Result Code 10 \"Referral\": error occurred") } func TestShouldUpdateUserPasswordPasswdModifyExtensionWithReferralsReferralPasswordModifyErr(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + PermitReferrals: true, + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) mockClientReferral := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - PermitReferrals: true, - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) + + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) pwdModifyRequest := ldap.NewPasswordModifyRequest( "uid=test,dc=example,dc=com", @@ -4864,11 +5183,9 @@ func TestShouldUpdateUserPasswordPasswdModifyExtensionWithReferralsReferralPassw "password", ) - dialURLOIDs := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + dialURLOIDs := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - connBindOIDs := mockClient.EXPECT(). + clientBindOIDs := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -4892,17 +5209,13 @@ func TestShouldUpdateUserPasswordPasswdModifyExtensionWithReferralsReferralPassw }, }, nil) - connCloseOIDs := mockClient.EXPECT().Close() - - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + clientCloseOIDs := mockClient.EXPECT().Close() - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchProfile := mockClient.EXPECT(). Search(gomock.Any()). @@ -4938,15 +5251,13 @@ func TestShouldUpdateUserPasswordPasswdModifyExtensionWithReferralsReferralPassw Packet: &testBERPacketReferral, }) - dialURLReferral := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://192.168.0.1"), gomock.Any()). - Return(mockClientReferral, nil) + dialURLReferral := mockDialer.EXPECT().DialURL("ldap://192.168.0.1", gomock.Any()).Return(mockClientReferral, nil) - connBindReferral := mockClientReferral.EXPECT(). + clientBindReferral := mockClientReferral.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connCloseReferral := mockClientReferral.EXPECT().Close() + clientCloseReferral := mockClientReferral.EXPECT().Close() passwdModifyReferral := mockClientReferral.EXPECT(). PasswordModify(pwdModifyRequest). @@ -4956,7 +5267,7 @@ func TestShouldUpdateUserPasswordPasswdModifyExtensionWithReferralsReferralPassw Packet: &testBERPacketReferral, }) - gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, passwdModify, dialURLReferral, connBindReferral, passwdModifyReferral, connCloseReferral, connClose) + gomock.InOrder(dialURLOIDs, clientBindOIDs, searchOIDs, clientCloseOIDs, dialURL, clientBind, searchProfile, passwdModify, dialURLReferral, clientBindReferral, passwdModifyReferral, clientCloseReferral, clientClose) err := provider.StartupCheck() require.NoError(t, err) @@ -4969,29 +5280,30 @@ func TestShouldUpdateUserPasswordActiveDirectoryWithServerPolicyHints(t *testing ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Implementation: "activedirectory", + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + DistinguishedName: "distinguishedName", + Username: "sAMAccountName", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "cn={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Implementation: "activedirectory", - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - DistinguishedName: "distinguishedName", - Username: "sAMAccountName", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "cn={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) + + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) utf16 := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM) pwdEncoded, _ := utf16.NewEncoder().String("\"password\"") @@ -5003,11 +5315,9 @@ func TestShouldUpdateUserPasswordActiveDirectoryWithServerPolicyHints(t *testing modifyRequest.Replace("unicodePwd", []string{pwdEncoded}) - dialURLOIDs := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + dialURLOIDs := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - connBindOIDs := mockClient.EXPECT(). + clientBindOIDs := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -5031,17 +5341,13 @@ func TestShouldUpdateUserPasswordActiveDirectoryWithServerPolicyHints(t *testing }, }, nil) - connCloseOIDs := mockClient.EXPECT().Close() + clientCloseOIDs := mockClient.EXPECT().Close() - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) - - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchProfile := mockClient.EXPECT(). Search(gomock.Any()). @@ -5071,7 +5377,7 @@ func TestShouldUpdateUserPasswordActiveDirectoryWithServerPolicyHints(t *testing Modify(modifyRequest). Return(nil) - gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, passwdModify, connClose) + gomock.InOrder(dialURLOIDs, clientBindOIDs, searchOIDs, clientCloseOIDs, dialURL, clientBind, searchProfile, passwdModify, clientClose) err := provider.StartupCheck() require.NoError(t, err) @@ -5084,29 +5390,30 @@ func TestShouldUpdateUserPasswordActiveDirectoryWithServerPolicyHintsDeprecated( ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Implementation: "activedirectory", + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + DistinguishedName: "distinguishedName", + Username: "sAMAccountName", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "cn={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Implementation: "activedirectory", - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - DistinguishedName: "distinguishedName", - Username: "sAMAccountName", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "cn={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) + + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) utf16 := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM) pwdEncoded, _ := utf16.NewEncoder().String("\"password\"") @@ -5118,11 +5425,9 @@ func TestShouldUpdateUserPasswordActiveDirectoryWithServerPolicyHintsDeprecated( modifyRequest.Replace("unicodePwd", []string{pwdEncoded}) - dialURLOIDs := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + dialURLOIDs := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - connBindOIDs := mockClient.EXPECT(). + clientBindOIDs := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -5146,17 +5451,13 @@ func TestShouldUpdateUserPasswordActiveDirectoryWithServerPolicyHintsDeprecated( }, }, nil) - connCloseOIDs := mockClient.EXPECT().Close() - - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + clientCloseOIDs := mockClient.EXPECT().Close() - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchProfile := mockClient.EXPECT(). Search(gomock.Any()). @@ -5186,7 +5487,7 @@ func TestShouldUpdateUserPasswordActiveDirectoryWithServerPolicyHintsDeprecated( Modify(modifyRequest). Return(nil) - gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, passwdModify, connClose) + gomock.InOrder(dialURLOIDs, clientBindOIDs, searchOIDs, clientCloseOIDs, dialURL, clientBind, searchProfile, passwdModify, clientClose) err := provider.StartupCheck() require.NoError(t, err) @@ -5199,29 +5500,30 @@ func TestShouldUpdateUserPasswordActiveDirectory(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Implementation: "activedirectory", + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + DistinguishedName: "distinguishedName", + Username: "sAMAccountName", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "cn={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Implementation: "activedirectory", - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - DistinguishedName: "distinguishedName", - Username: "sAMAccountName", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "cn={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) + + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) utf16 := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM) pwdEncoded, _ := utf16.NewEncoder().String("\"password\"") @@ -5233,11 +5535,9 @@ func TestShouldUpdateUserPasswordActiveDirectory(t *testing.T) { modifyRequest.Replace("unicodePwd", []string{pwdEncoded}) - dialURLOIDs := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + dialURLOIDs := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - connBindOIDs := mockClient.EXPECT(). + clientBindOIDs := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -5261,17 +5561,13 @@ func TestShouldUpdateUserPasswordActiveDirectory(t *testing.T) { }, }, nil) - connCloseOIDs := mockClient.EXPECT().Close() - - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + clientCloseOIDs := mockClient.EXPECT().Close() - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchProfile := mockClient.EXPECT(). Search(gomock.Any()). @@ -5301,7 +5597,7 @@ func TestShouldUpdateUserPasswordActiveDirectory(t *testing.T) { Modify(modifyRequest). Return(nil) - gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, passwdModify, connClose) + gomock.InOrder(dialURLOIDs, clientBindOIDs, searchOIDs, clientCloseOIDs, dialURL, clientBind, searchProfile, passwdModify, clientClose) err := provider.StartupCheck() require.NoError(t, err) @@ -5314,28 +5610,29 @@ func TestShouldUpdateUserPasswordBasic(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Implementation: "custom", + Address: testLDAPAddress, + User: "uid=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Implementation: "custom", - Address: testLDAPAddress, - User: "uid=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) + + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) modifyRequest := ldap.NewModifyRequest( "uid=test,dc=example,dc=com", @@ -5344,11 +5641,9 @@ func TestShouldUpdateUserPasswordBasic(t *testing.T) { modifyRequest.Replace("userPassword", []string{"password"}) - dialURLOIDs := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + dialURLOIDs := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - connBindOIDs := mockClient.EXPECT(). + clientBindOIDs := mockClient.EXPECT(). Bind(gomock.Eq("uid=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -5372,17 +5667,13 @@ func TestShouldUpdateUserPasswordBasic(t *testing.T) { }, }, nil) - connCloseOIDs := mockClient.EXPECT().Close() + clientCloseOIDs := mockClient.EXPECT().Close() - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) - - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("uid=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchProfile := mockClient.EXPECT(). Search(gomock.Any()). @@ -5412,7 +5703,7 @@ func TestShouldUpdateUserPasswordBasic(t *testing.T) { Modify(modifyRequest). Return(nil) - gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, passwdModify, connClose) + gomock.InOrder(dialURLOIDs, clientBindOIDs, searchOIDs, clientCloseOIDs, dialURL, clientBind, searchProfile, passwdModify, clientClose) err := provider.StartupCheck() require.NoError(t, err) @@ -5425,33 +5716,30 @@ func TestShouldReturnErrorWhenMultipleUsernameAttributes(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - bind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -5479,9 +5767,9 @@ func TestShouldReturnErrorWhenMultipleUsernameAttributes(t *testing.T) { }, }, nil) - gomock.InOrder(dialURL, bind, search) + gomock.InOrder(dialURL, clientBind, search) - client, err := provider.connect() + client, err := provider.factory.GetClient() assert.NoError(t, err) profile, err := provider.getUserProfile(client, "john") @@ -5494,33 +5782,30 @@ func TestShouldReturnErrorWhenZeroUsernameAttributes(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - bind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -5548,9 +5833,9 @@ func TestShouldReturnErrorWhenZeroUsernameAttributes(t *testing.T) { }, }, nil) - gomock.InOrder(dialURL, bind, search) + gomock.InOrder(dialURL, clientBind, search) - client, err := provider.connect() + client, err := provider.factory.GetClient() assert.NoError(t, err) profile, err := provider.getUserProfile(client, "john") @@ -5563,33 +5848,30 @@ func TestShouldReturnErrorWhenUsernameAttributeNotReturned(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - bind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -5613,9 +5895,9 @@ func TestShouldReturnErrorWhenUsernameAttributeNotReturned(t *testing.T) { }, }, nil) - gomock.InOrder(dialURL, bind, search) + gomock.InOrder(dialURL, clientBind, search) - client, err := provider.connect() + client, err := provider.factory.GetClient() assert.NoError(t, err) profile, err := provider.getUserProfile(client, "john") @@ -5628,33 +5910,30 @@ func TestShouldReturnErrorWhenMultipleUsersFound(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "(|(uid={input})(uid=*))", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "(|(uid={input})(uid=*))", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - bind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -5699,9 +5978,9 @@ func TestShouldReturnErrorWhenMultipleUsersFound(t *testing.T) { }, }, nil) - gomock.InOrder(dialURL, bind, search) + gomock.InOrder(dialURL, clientBind, search) - client, err := provider.connect() + client, err := provider.factory.GetClient() assert.NoError(t, err) profile, err := provider.getUserProfile(client, "john") @@ -5714,33 +5993,30 @@ func TestShouldReturnErrorWhenNoDN(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "(|(uid={input})(uid=*))", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "(|(uid={input})(uid=*))", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - bind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) @@ -5768,9 +6044,9 @@ func TestShouldReturnErrorWhenNoDN(t *testing.T) { }, }, nil) - gomock.InOrder(dialURL, bind, search) + gomock.InOrder(dialURL, clientBind, search) - client, err := provider.connect() + client, err := provider.factory.GetClient() assert.NoError(t, err) profile, err := provider.getUserProfile(client, "john") @@ -5783,32 +6059,29 @@ func TestShouldCheckValidUserPassword(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - }, - false, - nil, - mockFactory) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) gomock.InOrder( - mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil), + mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil), mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil), @@ -5835,9 +6108,7 @@ func TestShouldCheckValidUserPassword(t *testing.T) { }, }, }, nil), - mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil), + mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil), mockClient.EXPECT(). Bind(gomock.Eq("uid=test,dc=example,dc=com"), gomock.Eq("password")). Return(nil), @@ -5854,74 +6125,68 @@ func TestShouldNotCheckValidUserPasswordWithConnectError(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - bind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(&ldap.Error{ResultCode: ldap.LDAPResultInvalidCredentials, Err: errors.New("invalid username or password")}) - gomock.InOrder(dialURL, bind, mockClient.EXPECT().Close()) + gomock.InOrder(dialURL, clientBind, mockClient.EXPECT().Close()) valid, err := provider.CheckUserPassword("john", "password") assert.False(t, valid) - assert.EqualError(t, err, "bind failed with error: LDAP Result Code 49 \"Invalid Credentials\": invalid username or password") + assert.EqualError(t, err, "error occurred performing bind: LDAP Result Code 49 \"Invalid Credentials\": invalid username or password") } func TestShouldNotCheckValidUserPasswordWithGetProfileError(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - }, - false, - nil, - mockFactory) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) gomock.InOrder( - mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil), + mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil), mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil), @@ -5941,32 +6206,29 @@ func TestShouldCheckInvalidUserPassword(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - }, - false, - nil, - mockFactory) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) gomock.InOrder( - mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil), + mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil), mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil), @@ -5993,9 +6255,7 @@ func TestShouldCheckInvalidUserPassword(t *testing.T) { }, }, }, nil), - mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil), + mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil), mockClient.EXPECT(). Bind(gomock.Eq("uid=test,dc=example,dc=com"), gomock.Eq("password")). Return(errors.New("invalid username or password")), @@ -6005,49 +6265,47 @@ func TestShouldCheckInvalidUserPassword(t *testing.T) { valid, err := provider.CheckUserPassword("john", "password") assert.False(t, valid) - require.EqualError(t, err, "authentication failed. Cause: bind failed with error: invalid username or password") + require.EqualError(t, err, "authentication failed. Cause: error occurred performing bind: invalid username or password") } func TestShouldCallStartTLSWhenEnabled(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + GroupName: "cn", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + TLS: schema.DefaultLDAPAuthenticationBackendConfigurationImplementationCustom.TLS, + StartTLS: true, + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - GroupName: "cn", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - StartTLS: true, - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) - connBind := mockClient.EXPECT(). + connStartTLS := mockClient.EXPECT(). + StartTLS(gomock.Any()) + + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) - connStartTLS := mockClient.EXPECT(). - StartTLS(provider.tlsConfig) - - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchGroups := mockClient.EXPECT(). Search(gomock.Any()). @@ -6077,7 +6335,7 @@ func TestShouldCallStartTLSWhenEnabled(t *testing.T) { }, }, nil) - gomock.InOrder(dialURL, connStartTLS, connBind, searchProfile, searchGroups, connClose) + gomock.InOrder(dialURL, connStartTLS, clientBind, searchProfile, searchGroups, clientClose) details, err := provider.GetDetails("john") require.NoError(t, err) @@ -6095,7 +6353,7 @@ func TestShouldParseDynamicConfiguration(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ + &schema.AuthenticationBackendLDAP{ Address: testLDAPAddress, User: "cn=admin,dc=example,dc=com", Password: "password", @@ -6113,7 +6371,6 @@ func TestShouldParseDynamicConfiguration(t *testing.T) { StartTLS: true, }, false, - nil, mockFactory) provider.clock = clock.NewFixed(time.Unix(1670250519, 0)) @@ -6139,49 +6396,46 @@ func TestShouldCallStartTLSWithInsecureSkipVerifyWhenSkipVerifyTrue(t *testing.T ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + GroupName: "cn", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + StartTLS: true, + TLS: &schema.TLS{ + SkipVerify: true, + }, + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - GroupName: "cn", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - StartTLS: true, - TLS: &schema.TLS{ - SkipVerify: true, - }, - }, - false, - nil, - mockFactory) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) assert.False(t, provider.groupsFilterReplacementInput) assert.False(t, provider.groupsFilterReplacementUsername) assert.False(t, provider.groupsFilterReplacementDN) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + dialURL := mockDialer.EXPECT().DialURL("ldap://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - connBind := mockClient.EXPECT(). + clientBind := mockClient.EXPECT(). Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")). Return(nil) connStartTLS := mockClient.EXPECT(). - StartTLS(provider.tlsConfig) + StartTLS(gomock.Not(gomock.Nil())) - connClose := mockClient.EXPECT().Close() + clientClose := mockClient.EXPECT().Close() searchGroups := mockClient.EXPECT(). Search(gomock.Any()). @@ -6215,7 +6469,7 @@ func TestShouldCallStartTLSWithInsecureSkipVerifyWhenSkipVerifyTrue(t *testing.T }, }, nil) - gomock.InOrder(dialURL, connStartTLS, connBind, searchProfile, searchGroups, connClose) + gomock.InOrder(dialURL, connStartTLS, clientBind, searchProfile, searchGroups, clientClose) details, err := provider.GetDetails("john") require.NoError(t, err) @@ -6230,42 +6484,39 @@ func TestShouldReturnLDAPSAlreadySecuredWhenStartTLSAttempted(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockFactory := NewMockLDAPClientFactory(ctrl) + config := &schema.AuthenticationBackendLDAP{ + Address: testLDAPSAddress, + User: "cn=admin,dc=example,dc=com", + Password: "password", + Attributes: schema.AuthenticationBackendLDAPAttributes{ + Username: "uid", + Mail: "mail", + DisplayName: "displayName", + MemberOf: "memberOf", + }, + UsersFilter: "uid={input}", + AdditionalUsersDN: "ou=users", + BaseDN: "dc=example,dc=com", + StartTLS: true, + TLS: &schema.TLS{ + SkipVerify: true, + }, + } + + mockDialer := NewMockLDAPClientDialer(ctrl) + mockClient := NewMockLDAPClient(ctrl) - provider := NewLDAPUserProviderWithFactory( - schema.AuthenticationBackendLDAP{ - Address: testLDAPSAddress, - User: "cn=admin,dc=example,dc=com", - Password: "password", - Attributes: schema.AuthenticationBackendLDAPAttributes{ - Username: "uid", - Mail: "mail", - DisplayName: "displayName", - MemberOf: "memberOf", - }, - UsersFilter: "uid={input}", - AdditionalUsersDN: "ou=users", - BaseDN: "dc=example,dc=com", - StartTLS: true, - TLS: &schema.TLS{ - SkipVerify: true, - }, - }, - false, - nil, - mockFactory) + dialURL := mockDialer.EXPECT().DialURL("ldaps://127.0.0.1:389", gomock.Any()).Return(mockClient, nil) - dialURL := mockFactory.EXPECT(). - DialURL(gomock.Eq("ldaps://127.0.0.1:389"), gomock.Any()). - Return(mockClient, nil) + provider := NewLDAPUserProviderWithFactory(config, false, NewStandardLDAPClientFactory(config, nil, mockDialer)) connStartTLS := mockClient.EXPECT(). - StartTLS(provider.tlsConfig). + StartTLS(gomock.Not(gomock.Nil())). Return(errors.New("LDAP Result Code 200 \"Network Error\": ldap: already encrypted")) gomock.InOrder(dialURL, connStartTLS, mockClient.EXPECT().Close()) _, err := provider.GetDetails("john") - assert.EqualError(t, err, "starttls failed with error: LDAP Result Code 200 \"Network Error\": ldap: already encrypted") + assert.EqualError(t, err, "error occurred performing starttls: LDAP Result Code 200 \"Network Error\": ldap: already encrypted") } diff --git a/internal/authentication/types.go b/internal/authentication/types.go index d1853d5f3..145a83818 100644 --- a/internal/authentication/types.go +++ b/internal/authentication/types.go @@ -1,59 +1,13 @@ package authentication import ( - "crypto/tls" "fmt" "net/mail" "net/url" - "time" - "github.com/go-ldap/ldap/v3" "golang.org/x/text/language" ) -// LDAPClientFactory an interface of factory of LDAP clients. -type LDAPClientFactory interface { - DialURL(addr string, opts ...ldap.DialOpt) (client LDAPClient, err error) -} - -// LDAPClient is a cut down version of the ldap.Client interface with just the methods we use. -// -// Methods added to this interface that have a direct correlation with one from ldap.Client should have the same signature. -type LDAPClient interface { - Close() (err error) - IsClosing() bool - SetTimeout(timeout time.Duration) - - TLSConnectionState() (state tls.ConnectionState, ok bool) - StartTLS(config *tls.Config) (err error) - - Unbind() (err error) - Bind(username, password string) (err error) - SimpleBind(request *ldap.SimpleBindRequest) (result *ldap.SimpleBindResult, err error) - MD5Bind(host string, username string, password string) (err error) - DigestMD5Bind(request *ldap.DigestMD5BindRequest) (result *ldap.DigestMD5BindResult, err error) - UnauthenticatedBind(username string) (err error) - ExternalBind() (err error) - NTLMBind(domain string, username string, password string) (err error) - NTLMUnauthenticatedBind(domain string, username string) (err error) - NTLMBindWithHash(domain string, username string, hash string) (err error) - NTLMChallengeBind(request *ldap.NTLMBindRequest) (result *ldap.NTLMBindResult, err error) - - Modify(request *ldap.ModifyRequest) (err error) - ModifyWithResult(request *ldap.ModifyRequest) (result *ldap.ModifyResult, err error) - ModifyDN(m *ldap.ModifyDNRequest) (err error) - PasswordModify(request *ldap.PasswordModifyRequest) (result *ldap.PasswordModifyResult, err error) - - Add(request *ldap.AddRequest) (err error) - Del(request *ldap.DelRequest) (err error) - - Search(request *ldap.SearchRequest) (result *ldap.SearchResult, err error) - SearchWithPaging(request *ldap.SearchRequest, pagingSize uint32) (result *ldap.SearchResult, err error) - Compare(dn string, attribute string, value string) (same bool, err error) - - WhoAmI(controls []ldap.Control) (result *ldap.WhoAmIResult, err error) -} - // UserDetails represent the details retrieved for a given user. type UserDetails struct { Username string diff --git a/internal/authentication/user_provider.go b/internal/authentication/user_provider.go index f6c63ca57..516345d15 100644 --- a/internal/authentication/user_provider.go +++ b/internal/authentication/user_provider.go @@ -13,4 +13,5 @@ type UserProvider interface { GetDetails(username string) (details *UserDetails, err error) GetDetailsExtended(username string) (details *UserDetailsExtended, err error) UpdatePassword(username, newPassword string) (err error) + Shutdown() (err error) } diff --git a/internal/commands/services.go b/internal/commands/services.go index 332a29357..18598b488 100644 --- a/internal/commands/services.go +++ b/internal/commands/services.go @@ -447,6 +447,10 @@ func servicesRun(ctx ServiceCtx) { var err error + if err = ctx.GetProviders().UserProvider.Shutdown(); err != nil { + ctx.GetLogger().WithError(err).Error("Error occurred closing authentication connections") + } + if err = ctx.GetProviders().StorageProvider.Close(); err != nil { ctx.GetLogger().WithError(err).Error("Error occurred closing database connections") } diff --git a/internal/configuration/config.template.yml b/internal/configuration/config.template.yml index a3181081d..2e01b7a15 100644 --- a/internal/configuration/config.template.yml +++ b/internal/configuration/config.template.yml @@ -459,6 +459,7 @@ identity_validation: ## Use StartTLS with the LDAP connection. # start_tls: false + ## TLS configuration. # tls: ## The server subject name to check the servers certificate against during the validation process. ## This option is not required if the certificate has a SAN which matches the address options hostname. @@ -495,6 +496,20 @@ identity_validation: # ... # -----END RSA PRIVATE KEY----- + ## Connection Pooling configuration. + # pooling: + ## Enable Pooling. + # enable: false + + ## Pool count. + # count: 5 + + ## Retries to obtain a connection during the timeout. + # retries: 2 + + ## Timeout before the attempt to obtain a connection fails. + # timeout: '10 seconds' + ## The distinguished name of the container searched for objects in the directory information tree. ## See also: additional_users_dn, additional_groups_dn. # base_dn: 'dc=example,dc=com' diff --git a/internal/configuration/schema/authentication.go b/internal/configuration/schema/authentication.go index 4ebfb680f..010262abf 100644 --- a/internal/configuration/schema/authentication.go +++ b/internal/configuration/schema/authentication.go @@ -127,6 +127,8 @@ type AuthenticationBackendLDAP struct { StartTLS bool `koanf:"start_tls" json:"start_tls" jsonschema:"default=false,title=StartTLS" jsonschema_description:"Enables the use of StartTLS."` TLS *TLS `koanf:"tls" json:"tls" jsonschema:"title=TLS" jsonschema_description:"The LDAP directory server TLS connection properties."` + Pooling AuthenticationBackendLDAPPooling `koanf:"pooling" json:"pooling" jsonschema:"title=Pooling" jsonschema_description:"The LDAP Connection Pooling properties."` + BaseDN string `koanf:"base_dn" json:"base_dn" jsonschema:"title=Base DN" jsonschema_description:"The base for all directory server operations."` AdditionalUsersDN string `koanf:"additional_users_dn" json:"additional_users_dn" jsonschema:"title=Additional User Base" jsonschema_description:"The base in addition to the Base DN for all directory server operations for users."` @@ -146,6 +148,13 @@ type AuthenticationBackendLDAP struct { Password string `koanf:"password" json:"password" jsonschema:"title=Password" jsonschema_description:"The password for LDAP authenticated binding."` } +type AuthenticationBackendLDAPPooling struct { + Enable bool `koanf:"enable" json:"enable" jsonschema:"title=Enable,default=false" jsonschema_description:"Enable LDAP connection pooling."` + Count int `koanf:"count" json:"count" jsonschema:"title=Count,default=5" jsonschema_description:"The number of connections to keep open for LDAP connection pooling."` + Retries int `koanf:"retries" json:"retries" jsonschema:"title=Retries,default=2" jsonschema_description:"The number of attempts to retrieve a connection from the pool during the timeout."` + Timeout time.Duration `koanf:"timeout" json:"timeout" jsonschema:"title=Timeout,default=10 seconds" jsonschema_description:"The duration of time to wait for a connection to become available in the connection pool."` +} + // AuthenticationBackendLDAPAttributes represents the configuration related to LDAP server attributes. type AuthenticationBackendLDAPAttributes struct { DistinguishedName string `koanf:"distinguished_name" json:"distinguished_name" jsonschema:"title=Attribute: Distinguished Name" jsonschema_description:"The directory server attribute which contains the distinguished name for all objects."` @@ -243,6 +252,11 @@ var DefaultLDAPAuthenticationBackendConfigurationImplementationCustom = Authenti GroupName: ldapAttrCommonName, }, Timeout: time.Second * 5, + Pooling: AuthenticationBackendLDAPPooling{ + Count: 5, + Retries: 2, + Timeout: time.Second * 10, + }, TLS: &TLS{ MinimumVersion: TLSVersion{tls.VersionTLS12}, }, diff --git a/internal/configuration/schema/keys.go b/internal/configuration/schema/keys.go index b065409ae..f2efaca93 100644 --- a/internal/configuration/schema/keys.go +++ b/internal/configuration/schema/keys.go @@ -197,6 +197,10 @@ var Keys = []string{ "authentication_backend.ldap.tls.server_name", "authentication_backend.ldap.tls.private_key", "authentication_backend.ldap.tls.certificate_chain", + "authentication_backend.ldap.pooling.enable", + "authentication_backend.ldap.pooling.count", + "authentication_backend.ldap.pooling.retries", + "authentication_backend.ldap.pooling.timeout", "authentication_backend.ldap.base_dn", "authentication_backend.ldap.additional_users_dn", "authentication_backend.ldap.users_filter", diff --git a/internal/configuration/validator/authentication.go b/internal/configuration/validator/authentication.go index e53c7695d..c0dde8a26 100644 --- a/internal/configuration/validator/authentication.go +++ b/internal/configuration/validator/authentication.go @@ -349,6 +349,20 @@ func validateLDAPAuthenticationBackend(config *schema.AuthenticationBackend, val validator.Push(fmt.Errorf(errFmtLDAPAuthBackendTLSConfigInvalid, err)) } + if config.LDAP.Pooling.Enable { + if config.LDAP.Pooling.Count < 1 { + config.LDAP.Pooling.Count = schema.DefaultLDAPAuthenticationBackendConfigurationImplementationCustom.Pooling.Count + } + + if config.LDAP.Pooling.Retries < 1 { + config.LDAP.Pooling.Retries = schema.DefaultLDAPAuthenticationBackendConfigurationImplementationCustom.Pooling.Retries + } + + if config.LDAP.Pooling.Timeout < 1 { + config.LDAP.Pooling.Timeout = schema.DefaultLDAPAuthenticationBackendConfigurationImplementationCustom.Pooling.Timeout + } + } + if strings.Contains(config.LDAP.UsersFilter, "{0}") { validator.Push(fmt.Errorf(errFmtLDAPAuthBackendFilterReplacedPlaceholders, "users_filter", "{0}", "{input}")) } diff --git a/internal/configuration/validator/authentication_test.go b/internal/configuration/validator/authentication_test.go index c1ee3fe14..1fe5e2937 100644 --- a/internal/configuration/validator/authentication_test.go +++ b/internal/configuration/validator/authentication_test.go @@ -608,9 +608,22 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldValidateDefaultImplementa suite.Equal(schema.LDAPImplementationCustom, suite.config.LDAP.Implementation) - suite.Equal(suite.config.LDAP.Attributes.Username, schema.DefaultLDAPAuthenticationBackendConfigurationImplementationCustom.Attributes.Username) + suite.Equal(schema.DefaultLDAPAuthenticationBackendConfigurationImplementationCustom.Attributes.Username, suite.config.LDAP.Attributes.Username) suite.Len(suite.validator.Warnings(), 0) suite.Len(suite.validator.Errors(), 0) + + suite.Equal(0, suite.config.LDAP.Pooling.Retries) + suite.Equal(0, suite.config.LDAP.Pooling.Count) + suite.Equal(time.Duration(0), suite.config.LDAP.Pooling.Timeout) +} + +func (suite *LDAPAuthenticationBackendSuite) TestShouldValidateDefaultPooling() { + suite.config.LDAP.Pooling.Enable = true + ValidateAuthenticationBackend(&suite.config, suite.validator) + + suite.Equal(schema.DefaultLDAPAuthenticationBackendConfigurationImplementationCustom.Pooling.Retries, suite.config.LDAP.Pooling.Retries) + suite.Equal(schema.DefaultLDAPAuthenticationBackendConfigurationImplementationCustom.Pooling.Count, suite.config.LDAP.Pooling.Count) + suite.Equal(schema.DefaultLDAPAuthenticationBackendConfigurationImplementationCustom.Pooling.Timeout, suite.config.LDAP.Pooling.Timeout) } func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorWhenImplementationIsInvalidMSAD() { diff --git a/internal/mocks/gen.go b/internal/mocks/gen.go index f7cab1b14..46c9446d2 100644 --- a/internal/mocks/gen.go +++ b/internal/mocks/gen.go @@ -10,7 +10,9 @@ package mocks //go:generate mockgen -package mocks -destination duo_api.go -mock_names API=MockAPI github.com/authelia/authelia/v4/internal/duo API //go:generate mockgen -package mocks -destination random.go -mock_names Provider=MockRandom github.com/authelia/authelia/v4/internal/random Provider -// Fosite Mocks. +// External Mocks. + +// Mocks for authelia.com/provider/oauth2. //go:generate mockgen -package mocks -destination oauth2_client_credentials_grant_storage.go -mock_names Provider=MockClientCredentialsGrantStorage authelia.com/provider/oauth2/handler/oauth2 ClientCredentialsGrantStorage //go:generate mockgen -package mocks -destination oauth2_token_revocation_storage.go -mock_names Provider=MockTokenRevocationStorage authelia.com/provider/oauth2/handler/oauth2 TokenRevocationStorage //go:generate mockgen -package mocks -destination oauth2_access_token_strategy.go -mock_names Provider=MockAccessTokenStrategy authelia.com/provider/oauth2/handler/oauth2 AccessTokenStrategy diff --git a/internal/mocks/user_provider.go b/internal/mocks/user_provider.go index 81fa53483..b235d80b7 100644 --- a/internal/mocks/user_provider.go +++ b/internal/mocks/user_provider.go @@ -85,6 +85,20 @@ func (mr *MockUserProviderMockRecorder) GetDetailsExtended(username any) *gomock return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDetailsExtended", reflect.TypeOf((*MockUserProvider)(nil).GetDetailsExtended), username) } +// Shutdown mocks base method. +func (m *MockUserProvider) Shutdown() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Shutdown") + ret0, _ := ret[0].(error) + return ret0 +} + +// Shutdown indicates an expected call of Shutdown. +func (mr *MockUserProviderMockRecorder) Shutdown() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Shutdown", reflect.TypeOf((*MockUserProvider)(nil).Shutdown)) +} + // StartupCheck mocks base method. func (m *MockUserProvider) StartupCheck() error { m.ctrl.T.Helper() diff --git a/internal/suites/ActiveDirectory/configuration.yml b/internal/suites/ActiveDirectory/configuration.yml index eb5ae0359..657bacc2c 100644 --- a/internal/suites/ActiveDirectory/configuration.yml +++ b/internal/suites/ActiveDirectory/configuration.yml @@ -43,6 +43,11 @@ session: authentication_backend: ldap: address: 'ldap://sambaldap' + pooling: + enable: true + count: 4 + retries: 3 + timeout: 10s implementation: 'activedirectory' tls: skip_verify: true diff --git a/internal/suites/LDAP/configuration.yml b/internal/suites/LDAP/configuration.yml index a96976996..72876c0b5 100644 --- a/internal/suites/LDAP/configuration.yml +++ b/internal/suites/LDAP/configuration.yml @@ -43,6 +43,11 @@ session: authentication_backend: ldap: address: 'ldaps://openldap' + pooling: + enable: true + count: 4 + retries: 3 + timeout: 10s tls: skip_verify: true base_dn: 'dc=example,dc=com' diff --git a/internal/utils/crypto.go b/internal/utils/crypto.go index 884aac5a2..9493ade5b 100644 --- a/internal/utils/crypto.go +++ b/internal/utils/crypto.go @@ -314,6 +314,10 @@ func IsX509PrivateKey(i any) bool { // NewTLSConfig generates a tls.Config from a schema.TLS and a x509.CertPool. func NewTLSConfig(config *schema.TLS, rootCAs *x509.CertPool) (tlsConfig *tls.Config) { + if config == nil { + return nil + } + var certificates []tls.Certificate if config.PrivateKey != nil && config.CertificateChain.HasCertificates() { |
