From d3d87ffe30408df11f6b7a464ff31a3e4fa6fcf4 Mon Sep 17 00:00:00 2001 From: James Elliott Date: Wed, 21 Dec 2022 22:14:22 +1100 Subject: [PATCH] feat(configuration): glauth ldap implementation (#4499) This adds a GLAuth LDAP implementation which purely adds sane defaults for GLAuth. There are no functional differences just when the implementation option is set to 'glauth' sane defaults which should be sufficient for most use cases are set. See the documentation at https://www.authelia.com/r/ldap#defaults for more details. --- docs/content/en/reference/guides/ldap.md | 31 +++-- .../configuration/schema/authentication.go | 14 +++ internal/configuration/schema/const.go | 4 + .../configuration/validator/authentication.go | 2 + .../validator/authentication_test.go | 114 +++++++++++++++++- 5 files changed, 150 insertions(+), 15 deletions(-) diff --git a/docs/content/en/reference/guides/ldap.md b/docs/content/en/reference/guides/ldap.md index d6ccec9b8..7c9d994db 100644 --- a/docs/content/en/reference/guides/ldap.md +++ b/docs/content/en/reference/guides/ldap.md @@ -62,10 +62,14 @@ The following implementations exist: - `lldap`: - Specific configuration defaults for [lldap] - No special implementation details +- `glauth`: + - Specific configuration defaults for [GLAuth] + - No special implementation details [Active Directory]: https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/active-directory-domain-services [FreeIPA]: https://www.freeipa.org/ [lldap]: https://github.com/nitnelave/lldap +[GLAuth]: https://glauth.github.io/ ### Filter replacements @@ -115,6 +119,7 @@ Username column. | activedirectory | sAMAccountName | displayName | mail | cn | | freeipa | uid | displayName | mail | cn | | lldap | uid | cn | mail | cn | +| glauth | cn | description | mail | cn | #### Filter defaults @@ -122,21 +127,23 @@ The filters are probably the most important part to get correct when setting up the following conditions: - 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. + - 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. + - The [GLAuth] implementation achieves this via the `(!(accountStatus=inactive))` 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. + - 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. + - 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)) | -| lldap | (&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=person)) | (&(member={dn})(objectClass=groupOfNames)) | +| 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)) | +| freeipa | (&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=person)(!(nsAccountLock=TRUE))(krbPasswordExpiration>={date-time:generalized})(|(!(krbPrincipalExpiration=*))(krbPrincipalExpiration>={date-time:generalized}))) | (&(member={dn})(objectClass=groupOfNames)) | +| lldap | (&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=person)) | (&(member={dn})(objectClass=groupOfNames)) | +| glauth | (&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=posixAccount)(!(accountStatus=inactive))) | (&(uniqueMember={dn})(objectClass=posixGroup)) | ##### Microsoft Active Directory sAMAccountType diff --git a/internal/configuration/schema/authentication.go b/internal/configuration/schema/authentication.go index 98e514f4a..b99e209ba 100644 --- a/internal/configuration/schema/authentication.go +++ b/internal/configuration/schema/authentication.go @@ -228,3 +228,17 @@ var DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP = LDAPAuthe MinimumVersion: TLSVersion{tls.VersionTLS12}, }, } + +// DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth represents the default LDAP config for the LDAPImplementationGLAuth Implementation. +var DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth = LDAPAuthenticationBackend{ + UsersFilter: "(&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=posixAccount)(!(accountStatus=inactive)))", + UsernameAttribute: ldapAttrCommonName, + MailAttribute: ldapAttrMail, + DisplayNameAttribute: ldapAttrDescription, + GroupsFilter: "(&(uniqueMember={dn})(objectClass=posixGroup))", + GroupNameAttribute: ldapAttrCommonName, + Timeout: time.Second * 5, + TLS: &TLSConfig{ + MinimumVersion: TLSVersion{tls.VersionTLS12}, + }, +} diff --git a/internal/configuration/schema/const.go b/internal/configuration/schema/const.go index 1578327cc..726f23e01 100644 --- a/internal/configuration/schema/const.go +++ b/internal/configuration/schema/const.go @@ -70,6 +70,9 @@ const ( // LDAPImplementationLLDAP is the string for the lldap LDAP implementation. LDAPImplementationLLDAP = "lldap" + + // LDAPImplementationGLAuth is the string for the GLAuth LDAP implementation. + LDAPImplementationGLAuth = "glauth" ) // TOTP Algorithm. @@ -110,5 +113,6 @@ const ( ldapAttrMail = "mail" ldapAttrUserID = "uid" ldapAttrDisplayName = "displayName" + ldapAttrDescription = "description" ldapAttrCommonName = "cn" ) diff --git a/internal/configuration/validator/authentication.go b/internal/configuration/validator/authentication.go index 0106c4e37..f05d264c2 100644 --- a/internal/configuration/validator/authentication.go +++ b/internal/configuration/validator/authentication.go @@ -332,6 +332,8 @@ func validateLDAPAuthenticationBackend(config *schema.AuthenticationBackend, val implementation = &schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA case schema.LDAPImplementationLLDAP: implementation = &schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP + case schema.LDAPImplementationGLAuth: + implementation = &schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth default: validator.Push(fmt.Errorf(errFmtLDAPAuthBackendImplementation, config.LDAP.Implementation, strings.Join(validLDAPImplementations, "', '"))) } diff --git a/internal/configuration/validator/authentication_test.go b/internal/configuration/validator/authentication_test.go index e2305c56a..4db6efba5 100644 --- a/internal/configuration/validator/authentication_test.go +++ b/internal/configuration/validator/authentication_test.go @@ -912,6 +912,12 @@ func (suite *ActiveDirectoryAuthenticationBackendSuite) TestShouldSetActiveDirec suite.Assert().Equal( schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.AdditionalGroupsDN, suite.config.LDAP.AdditionalGroupsDN) + suite.Assert().Equal( + schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.AdditionalUsersDN, + suite.config.LDAP.AdditionalUsersDN) + suite.Assert().Equal( + schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.AdditionalGroupsDN, + suite.config.LDAP.AdditionalGroupsDN) suite.Assert().Equal( schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.UsersFilter, suite.config.LDAP.UsersFilter) @@ -1153,9 +1159,9 @@ func (suite *LLDAPAuthenticationBackendSuite) TestShouldOnlySetDefaultsIfNotManu suite.config.LDAP.UsersFilter = "(&({username_attribute}={input})(objectClass=Person)(!(nsAccountLock=TRUE)))" suite.config.LDAP.UsernameAttribute = "username" suite.config.LDAP.MailAttribute = "m" - suite.config.LDAP.DisplayNameAttribute = "given" - suite.config.LDAP.GroupsFilter = "(&(member={dn})(objectClass=posixGroup))" - suite.config.LDAP.GroupNameAttribute = "grp" + suite.config.LDAP.DisplayNameAttribute = "fn" + suite.config.LDAP.GroupsFilter = "(&(member={dn})(!(objectClass=posixGroup)))" + suite.config.LDAP.GroupNameAttribute = "grpz" suite.config.LDAP.AdditionalUsersDN = "OU=no" suite.config.LDAP.AdditionalGroupsDN = "OU=yes" @@ -1196,3 +1202,105 @@ func (suite *LLDAPAuthenticationBackendSuite) TestShouldOnlySetDefaultsIfNotManu func TestLLDAPAuthenticationBackend(t *testing.T) { suite.Run(t, new(LLDAPAuthenticationBackendSuite)) } + +type GLAuthAuthenticationBackendSuite struct { + suite.Suite + config schema.AuthenticationBackend + validator *schema.StructValidator +} + +func (suite *GLAuthAuthenticationBackendSuite) SetupTest() { + suite.validator = schema.NewStructValidator() + suite.config = schema.AuthenticationBackend{} + suite.config.LDAP = &schema.LDAPAuthenticationBackend{} + suite.config.LDAP.Implementation = schema.LDAPImplementationGLAuth + suite.config.LDAP.URL = testLDAPURL + suite.config.LDAP.User = testLDAPUser + suite.config.LDAP.Password = testLDAPPassword + suite.config.LDAP.BaseDN = testLDAPBaseDN + suite.config.LDAP.TLS = schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.TLS +} + +func (suite *GLAuthAuthenticationBackendSuite) TestShouldSetDefaults() { + ValidateAuthenticationBackend(&suite.config, suite.validator) + + suite.Assert().Len(suite.validator.Warnings(), 0) + suite.Assert().Len(suite.validator.Errors(), 0) + + suite.Assert().Equal( + schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.Timeout, + suite.config.LDAP.Timeout) + suite.Assert().Equal( + schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.AdditionalUsersDN, + suite.config.LDAP.AdditionalUsersDN) + suite.Assert().Equal( + schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.AdditionalGroupsDN, + suite.config.LDAP.AdditionalGroupsDN) + suite.Assert().Equal( + schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.UsersFilter, + suite.config.LDAP.UsersFilter) + suite.Assert().Equal( + schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.UsernameAttribute, + suite.config.LDAP.UsernameAttribute) + suite.Assert().Equal( + schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.DisplayNameAttribute, + suite.config.LDAP.DisplayNameAttribute) + suite.Assert().Equal( + schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.MailAttribute, + suite.config.LDAP.MailAttribute) + suite.Assert().Equal( + schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.GroupsFilter, + suite.config.LDAP.GroupsFilter) + suite.Assert().Equal( + schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.GroupNameAttribute, + suite.config.LDAP.GroupNameAttribute) +} + +func (suite *GLAuthAuthenticationBackendSuite) TestShouldOnlySetDefaultsIfNotManuallyConfigured() { + suite.config.LDAP.Timeout = time.Second * 2 + suite.config.LDAP.UsersFilter = "(&({username_attribute}={input})(objectClass=Person)(!(accountStatus=inactive)))" + suite.config.LDAP.UsernameAttribute = "description" + suite.config.LDAP.MailAttribute = "sender" + suite.config.LDAP.DisplayNameAttribute = "given" + suite.config.LDAP.GroupsFilter = "(&(member={dn})(objectClass=posixGroup))" + suite.config.LDAP.GroupNameAttribute = "grp" + suite.config.LDAP.AdditionalUsersDN = "OU=users,OU=GlAuth" + suite.config.LDAP.AdditionalGroupsDN = "OU=groups,OU=GLAuth" + + ValidateAuthenticationBackend(&suite.config, suite.validator) + + suite.Assert().NotEqual( + schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.Timeout, + suite.config.LDAP.Timeout) + suite.Assert().NotEqual( + schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.AdditionalUsersDN, + suite.config.LDAP.AdditionalUsersDN) + suite.Assert().NotEqual( + schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.AdditionalGroupsDN, + suite.config.LDAP.AdditionalGroupsDN) + suite.Assert().NotEqual( + schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.Timeout, + suite.config.LDAP.Timeout) + suite.Assert().NotEqual( + schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.UsersFilter, + suite.config.LDAP.UsersFilter) + suite.Assert().NotEqual( + schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.UsernameAttribute, + suite.config.LDAP.UsernameAttribute) + suite.Assert().NotEqual( + schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.DisplayNameAttribute, + suite.config.LDAP.DisplayNameAttribute) + suite.Assert().NotEqual( + schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.MailAttribute, + suite.config.LDAP.MailAttribute) + suite.Assert().NotEqual( + schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.GroupsFilter, + suite.config.LDAP.GroupsFilter) + suite.Assert().NotEqual( + schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.GroupNameAttribute, + suite.config.LDAP.GroupNameAttribute) +} + +func TestGLAuthAuthenticationBackend(t *testing.T) { + suite.Run(t, new(GLAuthAuthenticationBackendSuite)) +}