diff --git a/docs/content/en/reference/guides/ldap.md b/docs/content/en/reference/guides/ldap.md index 523cc258e..0d38b709d 100644 --- a/docs/content/en/reference/guides/ldap.md +++ b/docs/content/en/reference/guides/ldap.md @@ -60,12 +60,15 @@ search. #### Users filter replacements -| Placeholder | Phase | Replacement | -|:------------------------:|:-------:|:-------------------------------------:| -| {username_attribute} | startup | The configured username attribute | -| {mail_attribute} | startup | The configured mail attribute | -| {display_name_attribute} | startup | The configured display name attribute | -| {input} | search | The input into the username field | +| Placeholder | Phase | Replacement | +|:-------------------------:|:-------:|:--------------------------------------------------------------------------------------------------------------:| +| {username_attribute} | startup | The configured username attribute | +| {mail_attribute} | startup | The configured mail attribute | +| {display_name_attribute} | startup | The configured display name attribute | +| {input} | search | The input into the username field | +| {date-time:generalized} | search | The current UTC time formatted as a LDAP generalized time in the format of `20060102150405.0Z` | +| {date-time:unix-epoch} | search | The current time formatted as a Unix epoch | +| {date-time:msft-nt-epoch} | search | The current time formatted as a Microsoft NT epoch which is used by some Microsoft Active Directory attributes | #### Groups filter replacements @@ -92,16 +95,24 @@ Username column. #### Filter defaults -The filters are probably the most important part to get correct when setting up LDAP. You want to exclude disabled -accounts. The active directory example has two attribute filters that accomplish this as an example (more examples would -be appreciated). The userAccountControl filter checks that the account is not disabled and the pwdLastSet makes sure that -value is not 0 which means the password requires changing at the next login. +The filters are probably the most important part to get correct when setting up LDAP. You want to exclude accounts under +the following conditions: -| Implementation | Users Filter | Groups Filter | -|:---------------:|:---------------------------------------------------------------------------------------------------------------------------------------------------------------:|:----------------------------------------------------------------------------:| -| custom | N/A | N/A | -| activedirectory | (&(|({username_attribute}={input})({mail_attribute}={input}))(sAMAccountType=805306368)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(!(pwdLastSet=0))) | (&(member={dn})(|(sAMAccountType=268435456)(sAMAccountType=536870912))) | -| freeipa | (&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=person)(!(nsAccountLock=TRUE))) | (&(member={dn})(objectClass=groupOfNames)) | +- The account is disabled or locked: + - The Active Directory implementation achieves this via the `(!(userAccountControl:1.2.840.113556.1.4.803:=2))` filter. + - The FreeIPA implementation achieves this via the `(!(nsAccountLock=TRUE))` filter. +- Their password is expired: + - The Active Directory implementation achieves this via the `(!(pwdLastSet=0))` filter. + - The FreeIPA implementation achieves this via the `(krbPasswordExpiration>={date-time:generalized})` filter. +- Their account is expired: + - The Active Directory implementation achieves this via the `(|(!(accountExpires=*))(accountExpires=0)(accountExpires>={date-time:msft-nt-epoch}))` filter. + - The FreeIPA implementation achieves this via the `(|(!(krbPrincipalExpiration=*))(krbPrincipalExpiration>={date-time:generalized}))` filter. + +| Implementation | Users Filter | Groups Filter | +|:---------------:|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:----------------------------------------------------------------------------:| +| custom | N/A | N/A | +| activedirectory | (&(|({username_attribute}={input})({mail_attribute}={input}))(sAMAccountType=805306368)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(!(pwdLastSet=0))(|(!(accountExpires=*))(accountExpires=0)(accountExpires>={date-time:msft-nt-epoch}))) | (&(member={dn})(|(sAMAccountType=268435456)(sAMAccountType=536870912))) | +| freeipa | (&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=person)(!(nsAccountLock=TRUE))(krbPasswordExpiration>={date-time:generalized})(|(!(krbPrincipalExpiration=*))(krbPrincipalExpiration>={date-time:generalized}))) | (&(member={dn})(objectClass=groupOfNames)) | ##### Microsoft Active Directory sAMAccountType diff --git a/internal/authentication/const.go b/internal/authentication/const.go index 48d31c186..4bf9e3cdb 100644 --- a/internal/authentication/const.go +++ b/internal/authentication/const.go @@ -70,9 +70,16 @@ const ( ) const ( - ldapPlaceholderInput = "{input}" - ldapPlaceholderDistinguishedName = "{dn}" - ldapPlaceholderUsername = "{username}" + ldapPlaceholderInput = "{input}" + ldapPlaceholderDistinguishedName = "{dn}" + ldapPlaceholderUsername = "{username}" + ldapPlaceholderDateTimeGeneralized = "{date-time:generalized}" + ldapPlaceholderDateTimeMicrosoftNTTimeEpoch = "{date-time:msft-nt-epoch}" + ldapPlaceholderDateTimeUnixEpoch = "{date-time:unix-epoch}" +) + +const ( + ldapGeneralizedTimeDateTimeFormat = "20060102150405.0Z" ) const ( diff --git a/internal/authentication/ldap_user_provider.go b/internal/authentication/ldap_user_provider.go index 65f635bf7..56ad08c6f 100644 --- a/internal/authentication/ldap_user_provider.go +++ b/internal/authentication/ldap_user_provider.go @@ -5,6 +5,7 @@ import ( "crypto/x509" "fmt" "net" + "strconv" "strings" "github.com/go-ldap/ldap/v3" @@ -23,15 +24,20 @@ type LDAPUserProvider struct { log *logrus.Logger factory LDAPClientFactory + clock utils.Clock + disableResetPassword bool // Automatically detected LDAP features. features LDAPSupportedFeatures // Dynamically generated users values. - usersBaseDN string - usersAttributes []string - usersFilterReplacementInput bool + usersBaseDN string + usersAttributes []string + usersFilterReplacementInput bool + usersFilterReplacementDateTimeGeneralized bool + usersFilterReplacementDateTimeUnixEpoch bool + usersFilterReplacementDateTimeMicrosoftNTTimeEpoch bool // Dynamically generated groups values. groupsBaseDN string @@ -41,14 +47,15 @@ type LDAPUserProvider struct { groupsFilterReplacementDN bool } -// NewLDAPUserProvider creates a new instance of LDAPUserProvider. +// NewLDAPUserProvider creates a new instance of LDAPUserProvider with the ProductionLDAPClientFactory. func NewLDAPUserProvider(config schema.AuthenticationBackend, certPool *x509.CertPool) (provider *LDAPUserProvider) { - provider = newLDAPUserProvider(*config.LDAP, config.PasswordReset.Disable, certPool, nil) + provider = NewLDAPUserProviderWithFactory(*config.LDAP, config.PasswordReset.Disable, certPool, NewProductionLDAPClientFactory()) return provider } -func newLDAPUserProvider(config schema.LDAPAuthenticationBackend, disableResetPassword bool, certPool *x509.CertPool, factory LDAPClientFactory) (provider *LDAPUserProvider) { +// NewLDAPUserProviderWithFactory creates a new instance of LDAPUserProvider with the specified LDAPClientFactory. +func NewLDAPUserProviderWithFactory(config schema.LDAPAuthenticationBackend, disableResetPassword bool, certPool *x509.CertPool, factory LDAPClientFactory) (provider *LDAPUserProvider) { if config.TLS == nil { config.TLS = schema.DefaultLDAPAuthenticationBackendConfigurationImplementationCustom.TLS } @@ -74,6 +81,7 @@ func newLDAPUserProvider(config schema.LDAPAuthenticationBackend, disableResetPa log: logging.Logger(), factory: factory, disableResetPassword: disableResetPassword, + clock: &utils.RealClock{}, } provider.parseDynamicUsersConfiguration() @@ -394,12 +402,24 @@ func (p *LDAPUserProvider) getUserProfile(client LDAPClient, username string) (p return &userProfile, nil } -func (p *LDAPUserProvider) resolveUsersFilter(username string) (filter string) { +func (p *LDAPUserProvider) resolveUsersFilter(input string) (filter string) { filter = p.config.UsersFilter if p.usersFilterReplacementInput { // The {input} placeholder is replaced by the username input. - filter = strings.ReplaceAll(filter, ldapPlaceholderInput, ldapEscape(username)) + filter = strings.ReplaceAll(filter, ldapPlaceholderInput, ldapEscape(input)) + } + + if p.usersFilterReplacementDateTimeGeneralized { + filter = strings.ReplaceAll(filter, ldapPlaceholderDateTimeGeneralized, p.clock.Now().UTC().Format(ldapGeneralizedTimeDateTimeFormat)) + } + + if p.usersFilterReplacementDateTimeUnixEpoch { + filter = strings.ReplaceAll(filter, ldapPlaceholderDateTimeUnixEpoch, strconv.Itoa(int(p.clock.Now().Unix()))) + } + + if p.usersFilterReplacementDateTimeMicrosoftNTTimeEpoch { + filter = strings.ReplaceAll(filter, ldapPlaceholderDateTimeMicrosoftNTTimeEpoch, strconv.Itoa(int(utils.UnixNanoTimeToMicrosoftNTEpoch(p.clock.Now().UnixNano())))) } p.log.Tracef("Detected user filter is %s", filter) @@ -407,12 +427,12 @@ func (p *LDAPUserProvider) resolveUsersFilter(username string) (filter string) { return filter } -func (p *LDAPUserProvider) resolveGroupsFilter(username string, profile *ldapUserProfile) (filter string) { +func (p *LDAPUserProvider) resolveGroupsFilter(input string, profile *ldapUserProfile) (filter string) { filter = p.config.GroupsFilter if p.groupsFilterReplacementInput { // The {input} placeholder is replaced by the users username input. - filter = strings.ReplaceAll(p.config.GroupsFilter, ldapPlaceholderInput, ldapEscape(username)) + filter = strings.ReplaceAll(p.config.GroupsFilter, ldapPlaceholderInput, ldapEscape(input)) } if profile != nil { diff --git a/internal/authentication/ldap_user_provider_startup.go b/internal/authentication/ldap_user_provider_startup.go index a3a2e760a..edf668587 100644 --- a/internal/authentication/ldap_user_provider_startup.go +++ b/internal/authentication/ldap_user_provider_startup.go @@ -120,6 +120,18 @@ func (p *LDAPUserProvider) parseDynamicUsersConfiguration() { p.usersFilterReplacementInput = true } + if strings.Contains(p.config.UsersFilter, ldapPlaceholderDateTimeGeneralized) { + p.usersFilterReplacementDateTimeGeneralized = true + } + + if strings.Contains(p.config.UsersFilter, ldapPlaceholderDateTimeUnixEpoch) { + p.usersFilterReplacementDateTimeUnixEpoch = true + } + + if strings.Contains(p.config.UsersFilter, ldapPlaceholderDateTimeMicrosoftNTTimeEpoch) { + p.usersFilterReplacementDateTimeMicrosoftNTTimeEpoch = true + } + p.log.Tracef("Detected user filter replacements that need to be resolved per lookup are: %s=%v", ldapPlaceholderInput, p.usersFilterReplacementInput) } diff --git a/internal/authentication/ldap_user_provider_test.go b/internal/authentication/ldap_user_provider_test.go index d28d05e9c..2b929683e 100644 --- a/internal/authentication/ldap_user_provider_test.go +++ b/internal/authentication/ldap_user_provider_test.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "testing" + "time" "github.com/go-ldap/ldap/v3" "github.com/golang/mock/gomock" @@ -22,7 +23,7 @@ func TestShouldCreateRawConnectionWhenSchemeIsLDAP(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -42,7 +43,7 @@ func TestShouldCreateRawConnectionWhenSchemeIsLDAP(t *testing.T) { gomock.InOrder(dialURL, connBind) - _, err := ldapClient.connect() + _, err := provider.connect() require.NoError(t, err) } @@ -54,7 +55,7 @@ func TestShouldCreateTLSConnectionWhenSchemeIsLDAPS(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldaps://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -74,7 +75,7 @@ func TestShouldCreateTLSConnectionWhenSchemeIsLDAPS(t *testing.T) { gomock.InOrder(dialURL, connBind) - _, err := ldapClient.connect() + _, err := provider.connect() require.NoError(t, err) } @@ -104,7 +105,7 @@ func TestEscapeSpecialCharsInGroupsFilter(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldaps://127.0.0.1:389", GroupsFilter: "(|(member={dn})(uid={username})(uid={input}))", @@ -120,10 +121,10 @@ func TestEscapeSpecialCharsInGroupsFilter(t *testing.T) { Emails: []string{"john.doe@authelia.com"}, } - filter := ldapClient.resolveGroupsFilter("john", &profile) + filter := provider.resolveGroupsFilter("john", &profile) assert.Equal(t, "(|(member=cn=john \\28external\\29,dc=example,dc=com)(uid=john)(uid=john))", filter) - filter = ldapClient.resolveGroupsFilter("john#=(abc,def)", &profile) + filter = provider.resolveGroupsFilter("john#=(abc,def)", &profile) assert.Equal(t, "(|(member=cn=john \\28external\\29,dc=example,dc=com)(uid=john)(uid=john\\#\\=\\28abc\\,def\\29))", filter) } @@ -162,7 +163,7 @@ func TestShouldCheckLDAPServerExtensions(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -210,14 +211,14 @@ func TestShouldCheckLDAPServerExtensions(t *testing.T) { gomock.InOrder(dialURL, connBind, searchOIDs, connClose) - err := ldapClient.StartupCheck() + err := provider.StartupCheck() assert.NoError(t, err) - assert.True(t, ldapClient.features.Extensions.PwdModifyExOp) - assert.True(t, ldapClient.features.Extensions.TLS) + assert.True(t, provider.features.Extensions.PwdModifyExOp) + assert.True(t, provider.features.Extensions.TLS) - assert.False(t, ldapClient.features.ControlTypes.MsftPwdPolHints) - assert.False(t, ldapClient.features.ControlTypes.MsftPwdPolHintsDeprecated) + assert.False(t, provider.features.ControlTypes.MsftPwdPolHints) + assert.False(t, provider.features.ControlTypes.MsftPwdPolHintsDeprecated) } func TestShouldNotCheckLDAPServerExtensionsWhenRootDSEReturnsMoreThanOneEntry(t *testing.T) { @@ -227,7 +228,7 @@ func TestShouldNotCheckLDAPServerExtensionsWhenRootDSEReturnsMoreThanOneEntry(t mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -276,14 +277,14 @@ func TestShouldNotCheckLDAPServerExtensionsWhenRootDSEReturnsMoreThanOneEntry(t gomock.InOrder(dialURL, connBind, searchOIDs, connClose) - err := ldapClient.StartupCheck() + err := provider.StartupCheck() assert.NoError(t, err) - assert.False(t, ldapClient.features.Extensions.PwdModifyExOp) - assert.False(t, ldapClient.features.Extensions.TLS) + assert.False(t, provider.features.Extensions.PwdModifyExOp) + assert.False(t, provider.features.Extensions.TLS) - assert.False(t, ldapClient.features.ControlTypes.MsftPwdPolHints) - assert.False(t, ldapClient.features.ControlTypes.MsftPwdPolHintsDeprecated) + assert.False(t, provider.features.ControlTypes.MsftPwdPolHints) + assert.False(t, provider.features.ControlTypes.MsftPwdPolHintsDeprecated) } func TestShouldCheckLDAPServerControlTypes(t *testing.T) { @@ -293,7 +294,7 @@ func TestShouldCheckLDAPServerControlTypes(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -341,14 +342,14 @@ func TestShouldCheckLDAPServerControlTypes(t *testing.T) { gomock.InOrder(dialURL, connBind, searchOIDs, connClose) - err := ldapClient.StartupCheck() + err := provider.StartupCheck() assert.NoError(t, err) - assert.False(t, ldapClient.features.Extensions.PwdModifyExOp) - assert.False(t, ldapClient.features.Extensions.TLS) + assert.False(t, provider.features.Extensions.PwdModifyExOp) + assert.False(t, provider.features.Extensions.TLS) - assert.True(t, ldapClient.features.ControlTypes.MsftPwdPolHints) - assert.True(t, ldapClient.features.ControlTypes.MsftPwdPolHintsDeprecated) + assert.True(t, provider.features.ControlTypes.MsftPwdPolHints) + assert.True(t, provider.features.ControlTypes.MsftPwdPolHintsDeprecated) } func TestShouldNotEnablePasswdModifyExtensionOrControlTypes(t *testing.T) { @@ -358,7 +359,7 @@ func TestShouldNotEnablePasswdModifyExtensionOrControlTypes(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -406,14 +407,14 @@ func TestShouldNotEnablePasswdModifyExtensionOrControlTypes(t *testing.T) { gomock.InOrder(dialURL, connBind, searchOIDs, connClose) - err := ldapClient.StartupCheck() + err := provider.StartupCheck() assert.NoError(t, err) - assert.False(t, ldapClient.features.Extensions.PwdModifyExOp) - assert.False(t, ldapClient.features.Extensions.TLS) + assert.False(t, provider.features.Extensions.PwdModifyExOp) + assert.False(t, provider.features.Extensions.TLS) - assert.False(t, ldapClient.features.ControlTypes.MsftPwdPolHints) - assert.False(t, ldapClient.features.ControlTypes.MsftPwdPolHintsDeprecated) + assert.False(t, provider.features.ControlTypes.MsftPwdPolHints) + assert.False(t, provider.features.ControlTypes.MsftPwdPolHintsDeprecated) } func TestShouldReturnCheckServerConnectError(t *testing.T) { @@ -423,7 +424,7 @@ func TestShouldReturnCheckServerConnectError(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -443,10 +444,10 @@ func TestShouldReturnCheckServerConnectError(t *testing.T) { DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). Return(mockClient, errors.New("could not connect")) - err := ldapClient.StartupCheck() + err := provider.StartupCheck() assert.EqualError(t, err, "dial failed with error: could not connect") - assert.False(t, ldapClient.features.Extensions.PwdModifyExOp) + assert.False(t, provider.features.Extensions.PwdModifyExOp) } func TestShouldReturnCheckServerSearchError(t *testing.T) { @@ -456,7 +457,7 @@ func TestShouldReturnCheckServerSearchError(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -488,10 +489,10 @@ func TestShouldReturnCheckServerSearchError(t *testing.T) { gomock.InOrder(dialURL, connBind, searchOIDs, connClose) - err := ldapClient.StartupCheck() + err := provider.StartupCheck() assert.EqualError(t, err, "error occurred during RootDSE search: could not perform the search") - assert.False(t, ldapClient.features.Extensions.PwdModifyExOp) + assert.False(t, provider.features.Extensions.PwdModifyExOp) } type SearchRequestMatcher struct { @@ -518,7 +519,7 @@ func TestShouldEscapeUserInput(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -540,7 +541,7 @@ func TestShouldEscapeUserInput(t *testing.T) { Search(NewSearchRequestMatcher("(|(uid=john\\=abc)(mail=john\\=abc))")). Return(&ldap.SearchResult{}, nil) - _, err := ldapClient.getUserProfile(mockClient, "john=abc") + _, err := provider.getUserProfile(mockClient, "john=abc") require.Error(t, err) assert.EqualError(t, err, "user not found") } @@ -552,7 +553,7 @@ func TestShouldReturnEmailWhenAttributeSameAsUsername(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -568,7 +569,7 @@ func TestShouldReturnEmailWhenAttributeSameAsUsername(t *testing.T) { nil, mockFactory) - assert.Equal(t, []string{"mail", "displayName"}, ldapClient.usersAttributes) + assert.Equal(t, []string{"mail", "displayName"}, provider.usersAttributes) dialURL := mockFactory.EXPECT(). DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). @@ -600,10 +601,10 @@ func TestShouldReturnEmailWhenAttributeSameAsUsername(t *testing.T) { gomock.InOrder(dialURL, bind, search) - client, err := ldapClient.connect() + client, err := provider.connect() assert.NoError(t, err) - profile, err := ldapClient.getUserProfile(client, "john@example.com") + profile, err := provider.getUserProfile(client, "john@example.com") assert.NoError(t, err) require.NotNil(t, profile) @@ -623,7 +624,7 @@ func TestShouldReturnUsernameAndBlankDisplayNameWhenAttributesTheSame(t *testing mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -639,7 +640,7 @@ func TestShouldReturnUsernameAndBlankDisplayNameWhenAttributesTheSame(t *testing nil, mockFactory) - assert.Equal(t, []string{"uid", "mail"}, ldapClient.usersAttributes) + assert.Equal(t, []string{"uid", "mail"}, provider.usersAttributes) dialURL := mockFactory.EXPECT(). DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). @@ -671,10 +672,10 @@ func TestShouldReturnUsernameAndBlankDisplayNameWhenAttributesTheSame(t *testing gomock.InOrder(dialURL, bind, search) - client, err := ldapClient.connect() + client, err := provider.connect() assert.NoError(t, err) - profile, err := ldapClient.getUserProfile(client, "john@example.com") + profile, err := provider.getUserProfile(client, "john@example.com") assert.NoError(t, err) require.NotNil(t, profile) @@ -694,7 +695,7 @@ func TestShouldReturnBlankEmailAndDisplayNameWhenAttrsLenZero(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -710,7 +711,7 @@ func TestShouldReturnBlankEmailAndDisplayNameWhenAttrsLenZero(t *testing.T) { nil, mockFactory) - assert.Equal(t, []string{"uid", "mail", "displayName"}, ldapClient.usersAttributes) + assert.Equal(t, []string{"uid", "mail", "displayName"}, provider.usersAttributes) dialURL := mockFactory.EXPECT(). DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). @@ -746,10 +747,10 @@ func TestShouldReturnBlankEmailAndDisplayNameWhenAttrsLenZero(t *testing.T) { gomock.InOrder(dialURL, bind, search) - client, err := ldapClient.connect() + client, err := provider.connect() assert.NoError(t, err) - profile, err := ldapClient.getUserProfile(client, "john@example.com") + profile, err := provider.getUserProfile(client, "john@example.com") assert.NoError(t, err) require.NotNil(t, profile) @@ -768,7 +769,7 @@ func TestShouldCombineUsernameFilterAndUsersFilter(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -785,15 +786,15 @@ func TestShouldCombineUsernameFilterAndUsersFilter(t *testing.T) { nil, mockFactory) - assert.Equal(t, []string{"uid", "mail", "displayName"}, ldapClient.usersAttributes) + assert.Equal(t, []string{"uid", "mail", "displayName"}, provider.usersAttributes) - assert.True(t, ldapClient.usersFilterReplacementInput) + assert.True(t, provider.usersFilterReplacementInput) mockClient.EXPECT(). Search(NewSearchRequestMatcher("(&(uid=john)(&(objectCategory=person)(objectClass=user)))")). Return(&ldap.SearchResult{}, nil) - _, err := ldapClient.getUserProfile(mockClient, "john") + _, err := provider.getUserProfile(mockClient, "john") require.Error(t, err) assert.EqualError(t, err, "user not found") } @@ -819,7 +820,7 @@ func TestShouldNotCrashWhenGroupsAreNotRetrievedFromLDAP(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -876,7 +877,7 @@ func TestShouldNotCrashWhenGroupsAreNotRetrievedFromLDAP(t *testing.T) { gomock.InOrder(dialURL, connBind, searchProfile, searchGroups, connClose) - details, err := ldapClient.GetDetails("john") + details, err := provider.GetDetails("john") require.NoError(t, err) assert.ElementsMatch(t, details.Groups, []string{}) @@ -892,7 +893,7 @@ func TestShouldNotCrashWhenEmailsAreNotRetrievedFromLDAP(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -938,7 +939,7 @@ func TestShouldNotCrashWhenEmailsAreNotRetrievedFromLDAP(t *testing.T) { gomock.InOrder(dialURL, connBind, searchProfile, searchGroups, connClose) - details, err := ldapClient.GetDetails("john") + details, err := provider.GetDetails("john") require.NoError(t, err) assert.ElementsMatch(t, details.Groups, []string{"group1", "group2"}) @@ -953,7 +954,7 @@ func TestShouldReturnUsernameFromLDAP(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -1009,7 +1010,7 @@ func TestShouldReturnUsernameFromLDAP(t *testing.T) { gomock.InOrder(dialURL, connBind, searchProfile, searchGroups, connClose) - details, err := ldapClient.GetDetails("john") + details, err := provider.GetDetails("john") require.NoError(t, err) assert.ElementsMatch(t, details.Groups, []string{"group1", "group2"}) @@ -1026,7 +1027,7 @@ func TestShouldReturnUsernameFromLDAPWithReferrals(t *testing.T) { mockClient := NewMockLDAPClient(ctrl) mockClientReferral := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -1100,7 +1101,7 @@ func TestShouldReturnUsernameFromLDAPWithReferrals(t *testing.T) { gomock.InOrder(dialURL, connBind, searchProfile, dialURLReferral, connBindReferral, searchProfileReferral, connCloseReferral, searchGroups, connClose) - details, err := ldapClient.GetDetails("john") + details, err := provider.GetDetails("john") require.NoError(t, err) assert.ElementsMatch(t, details.Groups, []string{"group1", "group2"}) @@ -1118,7 +1119,7 @@ func TestShouldReturnUsernameFromLDAPWithReferralsInErrorAndResult(t *testing.T) mockClientReferral := NewMockLDAPClient(ctrl) mockClientReferralAlt := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -1226,7 +1227,7 @@ func TestShouldReturnUsernameFromLDAPWithReferralsInErrorAndResult(t *testing.T) gomock.InOrder(dialURL, connBind, searchProfile, dialURLReferral, connBindReferral, searchProfileReferral, connCloseReferral, dialURLReferralAlt, connBindReferralAlt, searchProfileReferralAlt, connCloseReferralAlt, searchGroups, connClose) - details, err := ldapClient.GetDetails("john") + details, err := provider.GetDetails("john") require.NoError(t, err) assert.ElementsMatch(t, details.Groups, []string{"group1", "group2"}) @@ -1243,7 +1244,7 @@ func TestShouldReturnUsernameFromLDAPWithReferralsErr(t *testing.T) { mockClient := NewMockLDAPClient(ctrl) mockClientReferral := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -1314,7 +1315,7 @@ func TestShouldReturnUsernameFromLDAPWithReferralsErr(t *testing.T) { gomock.InOrder(dialURL, connBind, searchProfile, dialURLReferral, connBindReferral, searchProfileReferral, connCloseReferral, searchGroups, connClose) - details, err := ldapClient.GetDetails("john") + details, err := provider.GetDetails("john") require.NoError(t, err) assert.ElementsMatch(t, details.Groups, []string{"group1", "group2"}) @@ -1330,7 +1331,7 @@ func TestShouldNotUpdateUserPasswordConnect(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -1383,10 +1384,10 @@ func TestShouldNotUpdateUserPasswordConnect(t *testing.T) { gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL) - err := ldapClient.StartupCheck() + err := provider.StartupCheck() require.NoError(t, err) - err = ldapClient.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "password") assert.EqualError(t, err, "unable to update password. Cause: dial failed with error: tcp timeout") } @@ -1397,7 +1398,7 @@ func TestShouldNotUpdateUserPasswordGetDetails(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -1460,10 +1461,10 @@ func TestShouldNotUpdateUserPasswordGetDetails(t *testing.T) { gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, connClose) - err := ldapClient.StartupCheck() + err := provider.StartupCheck() require.NoError(t, err) - err = ldapClient.UpdatePassword("john", "password") + 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") } @@ -1474,7 +1475,7 @@ func TestShouldUpdateUserPassword(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -1567,10 +1568,10 @@ func TestShouldUpdateUserPassword(t *testing.T) { gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, modify, connClose) - err := ldapClient.StartupCheck() + err := provider.StartupCheck() require.NoError(t, err) - err = ldapClient.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "password") require.NoError(t, err) } @@ -1581,7 +1582,7 @@ func TestShouldUpdateUserPasswordMSAD(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ Implementation: "activedirectory", URL: "ldap://127.0.0.1:389", @@ -1676,10 +1677,10 @@ func TestShouldUpdateUserPasswordMSAD(t *testing.T) { gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, modify, connClose) - err := ldapClient.StartupCheck() + err := provider.StartupCheck() require.NoError(t, err) - err = ldapClient.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "password") require.NoError(t, err) } @@ -1691,7 +1692,7 @@ func TestShouldUpdateUserPasswordMSADWithReferrals(t *testing.T) { mockClient := NewMockLDAPClient(ctrl) mockClientReferral := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ Implementation: "activedirectory", URL: "ldap://127.0.0.1:389", @@ -1805,10 +1806,10 @@ func TestShouldUpdateUserPasswordMSADWithReferrals(t *testing.T) { gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, modify, dialURLReferral, connBindReferral, modifyReferral, connCloseReferral, connClose) - err := ldapClient.StartupCheck() + err := provider.StartupCheck() require.NoError(t, err) - err = ldapClient.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "password") require.NoError(t, err) } @@ -1819,7 +1820,7 @@ func TestShouldUpdateUserPasswordMSADWithReferralsWithReferralConnectErr(t *test mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ Implementation: "activedirectory", URL: "ldap://127.0.0.1:389", @@ -1923,10 +1924,10 @@ func TestShouldUpdateUserPasswordMSADWithReferralsWithReferralConnectErr(t *test gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, modify, dialURLReferral, connClose) - err := ldapClient.StartupCheck() + err := provider.StartupCheck() require.NoError(t, err) - err = ldapClient.UpdatePassword("john", "password") + 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") } @@ -1938,7 +1939,7 @@ func TestShouldUpdateUserPasswordMSADWithReferralsWithReferralModifyErr(t *testi mockClient := NewMockLDAPClient(ctrl) mockClientReferral := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ Implementation: "activedirectory", URL: "ldap://127.0.0.1:389", @@ -2056,10 +2057,10 @@ func TestShouldUpdateUserPasswordMSADWithReferralsWithReferralModifyErr(t *testi gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, modify, dialURLReferral, connBindReferral, modifyReferral, connCloseReferral, connClose) - err := ldapClient.StartupCheck() + err := provider.StartupCheck() require.NoError(t, err) - err = ldapClient.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "password") assert.EqualError(t, err, "unable to update password. Cause: error occurred performing modify on referred LDAP server 'ldap://192.168.0.1': LDAP Result Code 51 \"Busy\": error occurred. Original Error: LDAP Result Code 10 \"Referral\": error occurred") } @@ -2070,7 +2071,7 @@ func TestShouldUpdateUserPasswordMSADWithoutReferrals(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ Implementation: "activedirectory", URL: "ldap://127.0.0.1:389", @@ -2170,10 +2171,10 @@ func TestShouldUpdateUserPasswordMSADWithoutReferrals(t *testing.T) { gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, modify, connClose) - err := ldapClient.StartupCheck() + err := provider.StartupCheck() require.NoError(t, err) - err = ldapClient.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "password") assert.EqualError(t, err, "unable to update password. Cause: LDAP Result Code 10 \"Referral\": error occurred") } @@ -2184,7 +2185,7 @@ func TestShouldUpdateUserPasswordPasswdModifyExtension(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -2276,10 +2277,10 @@ func TestShouldUpdateUserPasswordPasswdModifyExtension(t *testing.T) { gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, passwdModify, connClose) - err := ldapClient.StartupCheck() + err := provider.StartupCheck() require.NoError(t, err) - err = ldapClient.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "password") require.NoError(t, err) } @@ -2291,7 +2292,7 @@ func TestShouldUpdateUserPasswordPasswdModifyExtensionWithReferrals(t *testing.T mockClient := NewMockLDAPClient(ctrl) mockClientReferral := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -2404,10 +2405,10 @@ func TestShouldUpdateUserPasswordPasswdModifyExtensionWithReferrals(t *testing.T gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, passwdModify, dialURLReferral, connBindReferral, passwdModifyReferral, connCloseReferral, connClose) - err := ldapClient.StartupCheck() + err := provider.StartupCheck() require.NoError(t, err) - err = ldapClient.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "password") require.NoError(t, err) } @@ -2418,7 +2419,7 @@ func TestShouldUpdateUserPasswordPasswdModifyExtensionWithoutReferrals(t *testin mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -2517,10 +2518,10 @@ func TestShouldUpdateUserPasswordPasswdModifyExtensionWithoutReferrals(t *testin gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, passwdModify, connClose) - err := ldapClient.StartupCheck() + err := provider.StartupCheck() require.NoError(t, err) - err = ldapClient.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "password") assert.EqualError(t, err, "unable to update password. Cause: LDAP Result Code 10 \"Referral\": error occurred") } @@ -2531,7 +2532,7 @@ func TestShouldUpdateUserPasswordPasswdModifyExtensionWithReferralsReferralConne mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -2634,10 +2635,10 @@ func TestShouldUpdateUserPasswordPasswdModifyExtensionWithReferralsReferralConne gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, passwdModify, dialURLReferral, connClose) - err := ldapClient.StartupCheck() + err := provider.StartupCheck() require.NoError(t, err) - err = ldapClient.UpdatePassword("john", "password") + 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") } @@ -2649,7 +2650,7 @@ func TestShouldUpdateUserPasswordPasswdModifyExtensionWithReferralsReferralPassw mockClient := NewMockLDAPClient(ctrl) mockClientReferral := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -2766,10 +2767,10 @@ func TestShouldUpdateUserPasswordPasswdModifyExtensionWithReferralsReferralPassw gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, passwdModify, dialURLReferral, connBindReferral, passwdModifyReferral, connCloseReferral, connClose) - err := ldapClient.StartupCheck() + err := provider.StartupCheck() require.NoError(t, err) - err = ldapClient.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "password") assert.EqualError(t, err, "unable to update password. Cause: error occurred performing password modify on referred LDAP server 'ldap://192.168.0.1': LDAP Result Code 51 \"Busy\": too busy. Original Error: LDAP Result Code 10 \"Referral\": error occurred") } @@ -2780,7 +2781,7 @@ func TestShouldUpdateUserPasswordActiveDirectoryWithServerPolicyHints(t *testing mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ Implementation: "activedirectory", URL: "ldap://127.0.0.1:389", @@ -2877,10 +2878,10 @@ func TestShouldUpdateUserPasswordActiveDirectoryWithServerPolicyHints(t *testing gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, passwdModify, connClose) - err := ldapClient.StartupCheck() + err := provider.StartupCheck() require.NoError(t, err) - err = ldapClient.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "password") assert.NoError(t, err) } @@ -2891,7 +2892,7 @@ func TestShouldUpdateUserPasswordActiveDirectoryWithServerPolicyHintsDeprecated( mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ Implementation: "activedirectory", URL: "ldap://127.0.0.1:389", @@ -2988,10 +2989,10 @@ func TestShouldUpdateUserPasswordActiveDirectoryWithServerPolicyHintsDeprecated( gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, passwdModify, connClose) - err := ldapClient.StartupCheck() + err := provider.StartupCheck() require.NoError(t, err) - err = ldapClient.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "password") require.NoError(t, err) } @@ -3002,7 +3003,7 @@ func TestShouldUpdateUserPasswordActiveDirectory(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ Implementation: "activedirectory", URL: "ldap://127.0.0.1:389", @@ -3099,10 +3100,10 @@ func TestShouldUpdateUserPasswordActiveDirectory(t *testing.T) { gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, passwdModify, connClose) - err := ldapClient.StartupCheck() + err := provider.StartupCheck() require.NoError(t, err) - err = ldapClient.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "password") require.NoError(t, err) } @@ -3113,7 +3114,7 @@ func TestShouldUpdateUserPasswordBasic(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ Implementation: "custom", URL: "ldap://127.0.0.1:389", @@ -3207,10 +3208,10 @@ func TestShouldUpdateUserPasswordBasic(t *testing.T) { gomock.InOrder(dialURLOIDs, connBindOIDs, searchOIDs, connCloseOIDs, dialURL, connBind, searchProfile, passwdModify, connClose) - err := ldapClient.StartupCheck() + err := provider.StartupCheck() require.NoError(t, err) - err = ldapClient.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "password") require.NoError(t, err) } @@ -3221,7 +3222,7 @@ func TestShouldReturnErrorWhenMultipleUsernameAttributes(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -3271,10 +3272,10 @@ func TestShouldReturnErrorWhenMultipleUsernameAttributes(t *testing.T) { gomock.InOrder(dialURL, bind, search) - client, err := ldapClient.connect() + client, err := provider.connect() assert.NoError(t, err) - profile, err := ldapClient.getUserProfile(client, "john") + profile, err := provider.getUserProfile(client, "john") assert.Nil(t, profile) assert.EqualError(t, err, "user 'john' has 2 values for for attribute 'uid' but the attribute must be a single value attribute") @@ -3287,7 +3288,7 @@ func TestShouldReturnErrorWhenZeroUsernameAttributes(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -3337,10 +3338,10 @@ func TestShouldReturnErrorWhenZeroUsernameAttributes(t *testing.T) { gomock.InOrder(dialURL, bind, search) - client, err := ldapClient.connect() + client, err := provider.connect() assert.NoError(t, err) - profile, err := ldapClient.getUserProfile(client, "john") + profile, err := provider.getUserProfile(client, "john") assert.Nil(t, profile) assert.EqualError(t, err, "user 'john' must have value for attribute 'uid'") @@ -3353,7 +3354,7 @@ func TestShouldReturnErrorWhenUsernameAttributeNotReturned(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -3399,10 +3400,10 @@ func TestShouldReturnErrorWhenUsernameAttributeNotReturned(t *testing.T) { gomock.InOrder(dialURL, bind, search) - client, err := ldapClient.connect() + client, err := provider.connect() assert.NoError(t, err) - profile, err := ldapClient.getUserProfile(client, "john") + profile, err := provider.getUserProfile(client, "john") assert.Nil(t, profile) assert.EqualError(t, err, "user 'john' must have value for attribute 'uid'") @@ -3415,7 +3416,7 @@ func TestShouldReturnErrorWhenMultipleUsersFound(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -3482,10 +3483,10 @@ func TestShouldReturnErrorWhenMultipleUsersFound(t *testing.T) { gomock.InOrder(dialURL, bind, search) - client, err := ldapClient.connect() + client, err := provider.connect() assert.NoError(t, err) - profile, err := ldapClient.getUserProfile(client, "john") + profile, err := provider.getUserProfile(client, "john") assert.Nil(t, profile) assert.EqualError(t, err, "there were 2 users found when searching for 'john' but there should only be 1") @@ -3498,7 +3499,7 @@ func TestShouldReturnErrorWhenNoDN(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -3548,10 +3549,10 @@ func TestShouldReturnErrorWhenNoDN(t *testing.T) { gomock.InOrder(dialURL, bind, search) - client, err := ldapClient.connect() + client, err := provider.connect() assert.NoError(t, err) - profile, err := ldapClient.getUserProfile(client, "john") + profile, err := provider.getUserProfile(client, "john") assert.Nil(t, profile) assert.EqualError(t, err, "user 'john' must have a distinguished name but the result returned an empty distinguished name") @@ -3564,7 +3565,7 @@ func TestShouldCheckValidUserPassword(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -3619,7 +3620,7 @@ func TestShouldCheckValidUserPassword(t *testing.T) { mockClient.EXPECT().Close().Times(2), ) - valid, err := ldapClient.CheckUserPassword("john", "password") + valid, err := provider.CheckUserPassword("john", "password") assert.True(t, valid) require.NoError(t, err) @@ -3632,7 +3633,7 @@ func TestShouldNotCheckValidUserPasswordWithConnectError(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -3658,7 +3659,7 @@ func TestShouldNotCheckValidUserPasswordWithConnectError(t *testing.T) { gomock.InOrder(dialURL, bind, mockClient.EXPECT().Close()) - valid, err := ldapClient.CheckUserPassword("john", "password") + 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") @@ -3671,7 +3672,7 @@ func TestShouldCheckInvalidUserPassword(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -3726,7 +3727,7 @@ func TestShouldCheckInvalidUserPassword(t *testing.T) { mockClient.EXPECT().Close().Times(2), ) - valid, err := ldapClient.CheckUserPassword("john", "password") + 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") @@ -3739,7 +3740,7 @@ func TestShouldCallStartTLSWhenEnabled(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -3765,7 +3766,7 @@ func TestShouldCallStartTLSWhenEnabled(t *testing.T) { Return(nil) connStartTLS := mockClient.EXPECT(). - StartTLS(ldapClient.tlsConfig) + StartTLS(provider.tlsConfig) connClose := mockClient.EXPECT().Close() @@ -3799,7 +3800,7 @@ func TestShouldCallStartTLSWhenEnabled(t *testing.T) { gomock.InOrder(dialURL, connStartTLS, connBind, searchProfile, searchGroups, connClose) - details, err := ldapClient.GetDetails("john") + details, err := provider.GetDetails("john") require.NoError(t, err) assert.ElementsMatch(t, details.Groups, []string{}) @@ -3814,7 +3815,7 @@ func TestShouldParseDynamicConfiguration(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -3822,7 +3823,7 @@ func TestShouldParseDynamicConfiguration(t *testing.T) { UsernameAttribute: "uid", MailAttribute: "mail", DisplayNameAttribute: "displayName", - UsersFilter: "(&(|({username_attribute}={input})({mail_attribute}={input})({display_name_attribute}={input}))(objectCategory=person)(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=2)(!pwdLastSet=0))", + UsersFilter: "(&(|({username_attribute}={input})({mail_attribute}={input}))(sAMAccountType=805306368)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(!(pwdLastSet=0))(|(!(accountExpires=*))(accountExpires=0)(accountExpires>={date-time:msft-nt-epoch})(accountExpires>={date-time:generalized})))", GroupsFilter: "(&(|(member={dn})(member={input})(member={username}))(objectClass=group))", AdditionalUsersDN: "ou=users", AdditionalGroupsDN: "ou=groups", @@ -3833,16 +3834,27 @@ func TestShouldParseDynamicConfiguration(t *testing.T) { nil, mockFactory) - assert.True(t, ldapClient.groupsFilterReplacementInput) - assert.True(t, ldapClient.groupsFilterReplacementUsername) - assert.True(t, ldapClient.groupsFilterReplacementDN) + clock := &utils.TestingClock{} - assert.True(t, ldapClient.usersFilterReplacementInput) + provider.clock = clock - assert.Equal(t, "(&(|(uid={input})(mail={input})(displayName={input}))(objectCategory=person)(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=2)(!pwdLastSet=0))", ldapClient.config.UsersFilter) - assert.Equal(t, "(&(|(member={dn})(member={input})(member={username}))(objectClass=group))", ldapClient.config.GroupsFilter) - assert.Equal(t, "ou=users,dc=example,dc=com", ldapClient.usersBaseDN) - assert.Equal(t, "ou=groups,dc=example,dc=com", ldapClient.groupsBaseDN) + clock.Set(time.Unix(1670250519, 0)) + + assert.True(t, provider.groupsFilterReplacementInput) + assert.True(t, provider.groupsFilterReplacementUsername) + assert.True(t, provider.groupsFilterReplacementDN) + + assert.True(t, provider.usersFilterReplacementInput) + assert.True(t, provider.usersFilterReplacementDateTimeGeneralized) + assert.True(t, provider.usersFilterReplacementDateTimeMicrosoftNTTimeEpoch) + + assert.Equal(t, "(&(|(uid={input})(mail={input}))(sAMAccountType=805306368)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(!(pwdLastSet=0))(|(!(accountExpires=*))(accountExpires=0)(accountExpires>={date-time:msft-nt-epoch})(accountExpires>={date-time:generalized})))", provider.config.UsersFilter) + assert.Equal(t, "(&(|(member={dn})(member={input})(member={username}))(objectClass=group))", provider.config.GroupsFilter) + assert.Equal(t, "ou=users,dc=example,dc=com", provider.usersBaseDN) + assert.Equal(t, "ou=groups,dc=example,dc=com", provider.groupsBaseDN) + + assert.Equal(t, "(&(|(uid=test@example.com)(mail=test@example.com))(sAMAccountType=805306368)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(!(pwdLastSet=0))(|(!(accountExpires=*))(accountExpires=0)(accountExpires>=133147241190000000)(accountExpires>=20221205142839.0Z)))", provider.resolveUsersFilter("test@example.com")) + assert.Equal(t, "(&(|(member=cn=admin,dc=example,dc=com)(member=test@example.com)(member=test))(objectClass=group))", provider.resolveGroupsFilter("test@example.com", &ldapUserProfile{Username: "test", DN: "cn=admin,dc=example,dc=com"})) } func TestShouldCallStartTLSWithInsecureSkipVerifyWhenSkipVerifyTrue(t *testing.T) { @@ -3852,7 +3864,7 @@ func TestShouldCallStartTLSWithInsecureSkipVerifyWhenSkipVerifyTrue(t *testing.T mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldap://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -3872,9 +3884,9 @@ func TestShouldCallStartTLSWithInsecureSkipVerifyWhenSkipVerifyTrue(t *testing.T nil, mockFactory) - assert.False(t, ldapClient.groupsFilterReplacementInput) - assert.False(t, ldapClient.groupsFilterReplacementUsername) - assert.False(t, ldapClient.groupsFilterReplacementDN) + 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()). @@ -3885,7 +3897,7 @@ func TestShouldCallStartTLSWithInsecureSkipVerifyWhenSkipVerifyTrue(t *testing.T Return(nil) connStartTLS := mockClient.EXPECT(). - StartTLS(ldapClient.tlsConfig) + StartTLS(provider.tlsConfig) connClose := mockClient.EXPECT().Close() @@ -3919,7 +3931,7 @@ func TestShouldCallStartTLSWithInsecureSkipVerifyWhenSkipVerifyTrue(t *testing.T gomock.InOrder(dialURL, connStartTLS, connBind, searchProfile, searchGroups, connClose) - details, err := ldapClient.GetDetails("john") + details, err := provider.GetDetails("john") require.NoError(t, err) assert.ElementsMatch(t, details.Groups, []string{}) @@ -3935,7 +3947,7 @@ func TestShouldReturnLDAPSAlreadySecuredWhenStartTLSAttempted(t *testing.T) { mockFactory := NewMockLDAPClientFactory(ctrl) mockClient := NewMockLDAPClient(ctrl) - ldapClient := newLDAPUserProvider( + provider := NewLDAPUserProviderWithFactory( schema.LDAPAuthenticationBackend{ URL: "ldaps://127.0.0.1:389", User: "cn=admin,dc=example,dc=com", @@ -3960,11 +3972,11 @@ func TestShouldReturnLDAPSAlreadySecuredWhenStartTLSAttempted(t *testing.T) { Return(mockClient, nil) connStartTLS := mockClient.EXPECT(). - StartTLS(ldapClient.tlsConfig). + StartTLS(provider.tlsConfig). Return(errors.New("LDAP Result Code 200 \"Network Error\": ldap: already encrypted")) gomock.InOrder(dialURL, connStartTLS, mockClient.EXPECT().Close()) - _, err := ldapClient.GetDetails("john") + _, err := provider.GetDetails("john") assert.EqualError(t, err, "starttls failed with error: LDAP Result Code 200 \"Network Error\": ldap: already encrypted") } diff --git a/internal/configuration/schema/authentication.go b/internal/configuration/schema/authentication.go index 1d7bf181b..d830071ac 100644 --- a/internal/configuration/schema/authentication.go +++ b/internal/configuration/schema/authentication.go @@ -187,7 +187,7 @@ var DefaultLDAPAuthenticationBackendConfigurationImplementationCustom = LDAPAuth // DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory represents the default LDAP config for the LDAPImplementationActiveDirectory Implementation. var DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory = LDAPAuthenticationBackend{ - UsersFilter: "(&(|({username_attribute}={input})({mail_attribute}={input}))(sAMAccountType=805306368)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(!(pwdLastSet=0)))", + UsersFilter: "(&(|({username_attribute}={input})({mail_attribute}={input}))(sAMAccountType=805306368)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(!(pwdLastSet=0))(|(!(accountExpires=*))(accountExpires=0)(accountExpires>={date-time:msft-nt-epoch})))", UsernameAttribute: "sAMAccountName", MailAttribute: ldapAttrMail, DisplayNameAttribute: ldapAttrDisplayName, @@ -201,7 +201,7 @@ var DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory = // DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA represents the default LDAP config for the LDAPImplementationFreeIPA Implementation. var DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA = LDAPAuthenticationBackend{ - UsersFilter: "(&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=person)(!(nsAccountLock=TRUE)))", + UsersFilter: "(&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=person)(!(nsAccountLock=TRUE))(krbPasswordExpiration>={date-time:generalized})(|(!(krbPrincipalExpiration=*))(krbPrincipalExpiration>={date-time:generalized})))", UsernameAttribute: ldapAttrUserID, MailAttribute: ldapAttrMail, DisplayNameAttribute: ldapAttrDisplayName, diff --git a/internal/handlers/handler_verify_test.go b/internal/handlers/handler_verify_test.go index a0ef56858..518ac7ec0 100644 --- a/internal/handlers/handler_verify_test.go +++ b/internal/handlers/handler_verify_test.go @@ -702,7 +702,7 @@ func TestShouldDestroySessionWhenInactiveForTooLong(t *testing.T) { mock := mocks.NewMockAutheliaCtx(t) defer mock.Close() - clock := mocks.TestingClock{} + clock := utils.TestingClock{} clock.Set(time.Now()) past := clock.Now().Add(-1 * time.Hour) @@ -736,7 +736,7 @@ func TestShouldDestroySessionWhenInactiveForTooLongUsingDurationNotation(t *test mock := mocks.NewMockAutheliaCtx(t) defer mock.Close() - clock := mocks.TestingClock{} + clock := utils.TestingClock{} clock.Set(time.Now()) mock.Ctx.Configuration.Session.Inactivity = time.Second * 10 @@ -833,7 +833,7 @@ func TestShouldRedirectWhenSessionInactiveForTooLongAndRDParamProvided(t *testin mock := mocks.NewMockAutheliaCtx(t) defer mock.Close() - clock := mocks.TestingClock{} + clock := utils.TestingClock{} clock.Set(time.Now()) mock.Ctx.Configuration.Session.Inactivity = testInactivity @@ -1055,7 +1055,7 @@ func TestShouldNotRefreshUserGroupsFromBackend(t *testing.T) { mock.UserProviderMock.EXPECT().GetDetails("john").Times(0) - clock := mocks.TestingClock{} + clock := utils.TestingClock{} clock.Set(time.Now()) userSession := mock.Ctx.GetSession() @@ -1115,7 +1115,7 @@ func TestShouldNotRefreshUserGroupsFromBackendWhenDisabled(t *testing.T) { mock.UserProviderMock.EXPECT().GetDetails("john").Times(0) - clock := mocks.TestingClock{} + clock := utils.TestingClock{} clock.Set(time.Now()) userSession := mock.Ctx.GetSession() @@ -1161,7 +1161,7 @@ func TestShouldDestroySessionWhenUserNotExist(t *testing.T) { mock.UserProviderMock.EXPECT().GetDetails("john").Return(user, nil).Times(1) - clock := mocks.TestingClock{} + clock := utils.TestingClock{} clock.Set(time.Now()) userSession := mock.Ctx.GetSession() @@ -1222,7 +1222,7 @@ func TestShouldGetRemovedUserGroupsFromBackend(t *testing.T) { mock.UserProviderMock.EXPECT().GetDetails("john").Return(user, nil).Times(2) - clock := mocks.TestingClock{} + clock := utils.TestingClock{} clock.Set(time.Now()) userSession := mock.Ctx.GetSession() @@ -1431,7 +1431,7 @@ func TestShouldNotRedirectRequestsForBypassACLWhenInactiveForTooLong(t *testing. mock := mocks.NewMockAutheliaCtx(t) defer mock.Close() - clock := mocks.TestingClock{} + clock := utils.TestingClock{} clock.Set(time.Now()) past := clock.Now().Add(-1 * time.Hour) diff --git a/internal/mocks/authelia_ctx.go b/internal/mocks/authelia_ctx.go index 22c0f5003..26a4a876f 100644 --- a/internal/mocks/authelia_ctx.go +++ b/internal/mocks/authelia_ctx.go @@ -19,6 +19,7 @@ import ( "github.com/authelia/authelia/v4/internal/regulation" "github.com/authelia/authelia/v4/internal/session" "github.com/authelia/authelia/v4/internal/templates" + "github.com/authelia/authelia/v4/internal/utils" ) // MockAutheliaCtx a mock of AutheliaCtx. @@ -36,33 +37,13 @@ type MockAutheliaCtx struct { UserSession *session.UserSession - Clock TestingClock -} - -// TestingClock implementation of clock for tests. -type TestingClock struct { - now time.Time -} - -// Now return the stored clock. -func (dc *TestingClock) Now() time.Time { - return dc.now -} - -// After return a channel receiving the time after duration has elapsed. -func (dc *TestingClock) After(d time.Duration) <-chan time.Time { - return time.After(d) -} - -// Set set the time of the clock. -func (dc *TestingClock) Set(now time.Time) { - dc.now = now + Clock utils.TestingClock } // NewMockAutheliaCtx create an instance of AutheliaCtx mock. func NewMockAutheliaCtx(t *testing.T) *MockAutheliaCtx { mockAuthelia := new(MockAutheliaCtx) - mockAuthelia.Clock = TestingClock{} + mockAuthelia.Clock = utils.TestingClock{} datetime, _ := time.Parse("2006-Jan-02", "2013-Feb-03") mockAuthelia.Clock.Set(datetime) diff --git a/internal/regulation/regulator_test.go b/internal/regulation/regulator_test.go index 37f371169..6b61be4da 100644 --- a/internal/regulation/regulator_test.go +++ b/internal/regulation/regulator_test.go @@ -13,6 +13,7 @@ import ( "github.com/authelia/authelia/v4/internal/mocks" "github.com/authelia/authelia/v4/internal/model" "github.com/authelia/authelia/v4/internal/regulation" + "github.com/authelia/authelia/v4/internal/utils" ) type RegulatorSuite struct { @@ -22,7 +23,7 @@ type RegulatorSuite struct { ctrl *gomock.Controller storageMock *mocks.MockStorage config schema.RegulationConfiguration - clock mocks.TestingClock + clock utils.TestingClock } func (s *RegulatorSuite) SetupTest() { diff --git a/internal/utils/clock.go b/internal/utils/clock.go index 388275f3d..9b5cc423f 100644 --- a/internal/utils/clock.go +++ b/internal/utils/clock.go @@ -20,3 +20,23 @@ func (RealClock) Now() time.Time { func (RealClock) After(d time.Duration) <-chan time.Time { return time.After(d) } + +// TestingClock implementation of clock for tests. +type TestingClock struct { + now time.Time +} + +// Now return the stored clock. +func (dc *TestingClock) Now() time.Time { + return dc.now +} + +// After return a channel receiving the time after duration has elapsed. +func (dc *TestingClock) After(d time.Duration) <-chan time.Time { + return time.After(d) +} + +// Set set the time of the clock. +func (dc *TestingClock) Set(now time.Time) { + dc.now = now +} diff --git a/internal/utils/const.go b/internal/utils/const.go index 1188b5f9c..2e65da318 100644 --- a/internal/utils/const.go +++ b/internal/utils/const.go @@ -8,24 +8,9 @@ import ( ) const ( - windows = "windows" - testStringInput = "abcdefghijkl" - // RFC3339Zero is the default value for time.Time.Unix(). RFC3339Zero = int64(-62135596800) - // TLS13 is the textual representation of TLS 1.3. - TLS13 = "1.3" - - // TLS12 is the textual representation of TLS 1.2. - TLS12 = "1.2" - - // TLS11 is the textual representation of TLS 1.1. - TLS11 = "1.1" - - // TLS10 is the textual representation of TLS 1.0. - TLS10 = "1.0" - clean = "clean" tagged = "tagged" unknown = "unknown" @@ -84,10 +69,6 @@ const ( Month = Year / 12 ) -const ( - errFmtLinuxNotFound = "open %s: no such file or directory" -) - var ( standardDurationUnits = []string{"ns", "us", "µs", "μs", "ms", "s", "m", "h"} reDurationSeconds = regexp.MustCompile(`^\d+$`) @@ -110,6 +91,12 @@ const ( HoursInYear = HoursInDay * 365 ) +const ( + // timeUnixEpochAsMicrosoftNTEpoch represents the unix epoch as a Microsoft NT Epoch. + // The Microsoft NT Epoch is ticks since Jan 1, 1601 (1 tick is 100ns). + timeUnixEpochAsMicrosoftNTEpoch uint64 = 116444736000000000 +) + const ( // CharSetAlphabeticLower are literally just valid alphabetic lowercase printable ASCII chars. CharSetAlphabeticLower = "abcdefghijklmnopqrstuvwxyz" @@ -155,5 +142,7 @@ var htmlEscaper = strings.NewReplacer( // ErrTimeoutReached error thrown when a timeout is reached. var ErrTimeoutReached = errors.New("timeout reached") -// ErrTLSVersionNotSupported returned when an unknown TLS version supplied. -var ErrTLSVersionNotSupported = errors.New("supplied tls version isn't supported") +const ( + windows = "windows" + errFmtLinuxNotFound = "open %s: no such file or directory" +) diff --git a/internal/utils/const_test.go b/internal/utils/const_test.go new file mode 100644 index 000000000..879485536 --- /dev/null +++ b/internal/utils/const_test.go @@ -0,0 +1,5 @@ +package utils + +const ( + testStringInput = "abcdefghijkl" +) diff --git a/internal/utils/time.go b/internal/utils/time.go index 51952fcdc..4844e108e 100644 --- a/internal/utils/time.go +++ b/internal/utils/time.go @@ -67,3 +67,8 @@ func ParseDurationString(input string) (duration time.Duration, err error) { return time.ParseDuration(out) } + +// UnixNanoTimeToMicrosoftNTEpoch converts a unix timestamp in nanosecond format to win32 epoch format. +func UnixNanoTimeToMicrosoftNTEpoch(nano int64) (t uint64) { + return uint64(nano/100) + timeUnixEpochAsMicrosoftNTEpoch +} diff --git a/internal/utils/time_test.go b/internal/utils/time_test.go index f75871ec8..4cd651c9c 100644 --- a/internal/utils/time_test.go +++ b/internal/utils/time_test.go @@ -122,3 +122,11 @@ func TestShouldTimeIntervalsMakeSense(t *testing.T) { assert.Equal(t, Year, Day*365) assert.Equal(t, Month, Year/12) } + +func TestShouldConvertKnownUnixNanoTimeToKnownWin32Epoch(t *testing.T) { + exampleNanoTime := int64(1626234411 * 1000000000) + win32Epoch := uint64(132707080110000000) + + assert.Equal(t, win32Epoch, UnixNanoTimeToMicrosoftNTEpoch(exampleNanoTime)) + assert.Equal(t, timeUnixEpochAsMicrosoftNTEpoch, UnixNanoTimeToMicrosoftNTEpoch(0)) +}