2021-08-05 04:17:07 +00:00
package authentication
import (
2022-10-01 20:44:18 +00:00
"fmt"
2021-08-05 04:17:07 +00:00
"strings"
"github.com/go-ldap/ldap/v3"
2021-09-17 09:53:59 +00:00
"github.com/authelia/authelia/v4/internal/configuration/schema"
2022-05-15 06:37:23 +00:00
"github.com/authelia/authelia/v4/internal/utils"
2021-08-05 04:17:07 +00:00
)
2021-09-17 09:53:59 +00:00
// StartupCheck implements the startup check provider interface.
2021-11-23 09:45:38 +00:00
func ( p * LDAPUserProvider ) StartupCheck ( ) ( err error ) {
2022-05-10 04:38:36 +00:00
var client LDAPClient
2022-05-02 01:51:38 +00:00
2022-05-10 04:38:36 +00:00
if client , err = p . connect ( ) ; err != nil {
2021-08-05 04:17:07 +00:00
return err
}
2022-05-10 04:38:36 +00:00
defer client . Close ( )
2021-08-05 04:17:07 +00:00
2022-05-10 04:38:36 +00:00
if p . features , err = p . getServerSupportedFeatures ( client ) ; err != nil {
2021-08-05 04:17:07 +00:00
return err
}
2022-05-10 04:38:36 +00:00
if ! p . features . Extensions . PwdModifyExOp && ! p . disableResetPassword &&
p . config . Implementation != schema . LDAPImplementationActiveDirectory {
p . log . Warn ( "Your LDAP server implementation may not support a method for password hashing " +
"known to Authelia, it's strongly recommended you ensure your directory server hashes the password " +
"attribute when users reset their password via Authelia." )
}
2023-05-07 06:39:17 +00:00
if p . features . Extensions . TLS && ! p . config . StartTLS && ! p . config . Address . IsExplicitlySecure ( ) {
2022-06-21 00:56:20 +00:00
p . log . Error ( "Your LDAP Server supports TLS but you don't appear to be utilizing it. We strongly " +
"recommend using the scheme 'ldaps://' or enabling the StartTLS option to secure connections with your " +
2022-05-10 04:38:36 +00:00
"LDAP Server." )
}
return nil
}
func ( p * LDAPUserProvider ) getServerSupportedFeatures ( client LDAPClient ) ( features LDAPSupportedFeatures , err error ) {
var (
2022-10-17 10:51:59 +00:00
request * ldap . SearchRequest
result * ldap . SearchResult
2022-05-10 04:38:36 +00:00
)
2022-10-17 10:51:59 +00:00
request = ldap . NewSearchRequest ( "" , ldap . ScopeBaseObject , ldap . NeverDerefAliases ,
1 , 0 , false , ldapBaseObjectFilter , [ ] string { ldapSupportedExtensionAttribute , ldapSupportedControlAttribute } , nil )
2022-05-10 04:38:36 +00:00
2022-10-17 10:51:59 +00:00
if result , err = client . Search ( request ) ; err != nil {
2022-10-01 20:44:18 +00:00
if p . config . PermitFeatureDetectionFailure {
p . log . WithError ( err ) . Warnf ( "Error occurred during RootDSE search. This may result in reduced functionality." )
return features , nil
}
return features , fmt . Errorf ( "error occurred during RootDSE search: %w" , err )
2022-05-10 04:38:36 +00:00
}
2022-10-17 10:51:59 +00:00
if len ( result . Entries ) != 1 {
2022-05-10 04:38:36 +00:00
p . log . Errorf ( "The LDAP Server did not respond appropriately to a RootDSE search. This may result in reduced functionality." )
return features , nil
2021-08-05 04:17:07 +00:00
}
2022-05-10 04:38:36 +00:00
var controlTypeOIDs , extensionOIDs [ ] string
2022-10-17 10:51:59 +00:00
controlTypeOIDs , extensionOIDs , features = ldapGetFeatureSupportFromEntry ( result . Entries [ 0 ] )
2021-08-05 04:17:07 +00:00
2022-05-10 04:38:36 +00:00
controlTypes , extensions := none , none
if len ( controlTypeOIDs ) != 0 {
controlTypes = strings . Join ( controlTypeOIDs , ", " )
2021-08-05 04:17:07 +00:00
}
2022-05-10 04:38:36 +00:00
if len ( extensionOIDs ) != 0 {
extensions = strings . Join ( extensionOIDs , ", " )
2021-09-17 09:53:59 +00:00
}
2022-05-10 04:38:36 +00:00
p . log . Debugf ( "LDAP Supported OIDs. Control Types: %s. Extensions: %s" , controlTypes , extensions )
return features , nil
2021-08-05 04:17:07 +00:00
}
func ( p * LDAPUserProvider ) parseDynamicUsersConfiguration ( ) {
2023-06-18 04:40:38 +00:00
p . config . UsersFilter = strings . ReplaceAll ( p . config . UsersFilter , ldapPlaceholderDistinguishedNameAttribute , p . config . Attributes . DistinguishedName )
p . config . UsersFilter = strings . ReplaceAll ( p . config . UsersFilter , ldapPlaceholderUsernameAttribute , p . config . Attributes . Username )
p . config . UsersFilter = strings . ReplaceAll ( p . config . UsersFilter , ldapPlaceholderDisplayNameAttribute , p . config . Attributes . DisplayName )
p . config . UsersFilter = strings . ReplaceAll ( p . config . UsersFilter , ldapPlaceholderMailAttribute , p . config . Attributes . Mail )
p . config . UsersFilter = strings . ReplaceAll ( p . config . UsersFilter , ldapPlaceholderMemberOfAttribute , p . config . Attributes . MemberOf )
2021-08-05 04:17:07 +00:00
2022-05-02 01:51:38 +00:00
p . log . Tracef ( "Dynamically generated users filter is %s" , p . config . UsersFilter )
2021-08-05 04:17:07 +00:00
2023-06-18 04:40:38 +00:00
if len ( p . config . Attributes . Username ) != 0 && ! utils . IsStringInSlice ( p . config . Attributes . Username , p . usersAttributes ) {
p . usersAttributes = append ( p . usersAttributes , p . config . Attributes . Username )
2022-05-15 06:37:23 +00:00
}
2023-06-18 04:40:38 +00:00
if len ( p . config . Attributes . Mail ) != 0 && ! utils . IsStringInSlice ( p . config . Attributes . Mail , p . usersAttributes ) {
p . usersAttributes = append ( p . usersAttributes , p . config . Attributes . Mail )
2022-05-15 06:37:23 +00:00
}
2023-06-18 04:40:38 +00:00
if len ( p . config . Attributes . DisplayName ) != 0 && ! utils . IsStringInSlice ( p . config . Attributes . DisplayName , p . usersAttributes ) {
p . usersAttributes = append ( p . usersAttributes , p . config . Attributes . DisplayName )
2021-08-05 04:17:07 +00:00
}
2022-05-02 01:51:38 +00:00
if p . config . AdditionalUsersDN != "" {
p . usersBaseDN = p . config . AdditionalUsersDN + "," + p . config . BaseDN
2021-08-05 04:17:07 +00:00
} else {
2022-05-02 01:51:38 +00:00
p . usersBaseDN = p . config . BaseDN
2021-08-05 04:17:07 +00:00
}
2021-11-23 09:45:38 +00:00
p . log . Tracef ( "Dynamically generated users BaseDN is %s" , p . usersBaseDN )
2021-08-05 04:17:07 +00:00
2022-05-02 01:51:38 +00:00
if strings . Contains ( p . config . UsersFilter , ldapPlaceholderInput ) {
2021-08-05 04:17:07 +00:00
p . usersFilterReplacementInput = true
}
2022-12-21 10:31:21 +00:00
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
}
2021-11-23 09:45:38 +00:00
p . log . Tracef ( "Detected user filter replacements that need to be resolved per lookup are: %s=%v" ,
2021-08-05 04:17:07 +00:00
ldapPlaceholderInput , p . usersFilterReplacementInput )
}
func ( p * LDAPUserProvider ) parseDynamicGroupsConfiguration ( ) {
2023-06-18 04:40:38 +00:00
p . config . GroupsFilter = strings . ReplaceAll ( p . config . GroupsFilter , ldapPlaceholderDistinguishedNameAttribute , p . config . Attributes . DistinguishedName )
p . config . GroupsFilter = strings . ReplaceAll ( p . config . GroupsFilter , ldapPlaceholderUsernameAttribute , p . config . Attributes . Username )
p . config . GroupsFilter = strings . ReplaceAll ( p . config . GroupsFilter , ldapPlaceholderDisplayNameAttribute , p . config . Attributes . DisplayName )
p . config . GroupsFilter = strings . ReplaceAll ( p . config . GroupsFilter , ldapPlaceholderMailAttribute , p . config . Attributes . Mail )
p . config . GroupsFilter = strings . ReplaceAll ( p . config . GroupsFilter , ldapPlaceholderMemberOfAttribute , p . config . Attributes . MemberOf )
if len ( p . config . Attributes . GroupName ) != 0 && ! utils . IsStringInSlice ( p . config . Attributes . GroupName , p . groupsAttributes ) {
p . groupsAttributes = append ( p . groupsAttributes , p . config . Attributes . GroupName )
2021-08-05 04:17:07 +00:00
}
2022-05-02 01:51:38 +00:00
if p . config . AdditionalGroupsDN != "" {
2022-10-28 09:21:43 +00:00
p . groupsBaseDN = p . config . AdditionalGroupsDN + "," + p . config . BaseDN
2021-08-05 04:17:07 +00:00
} else {
2022-05-02 01:51:38 +00:00
p . groupsBaseDN = p . config . BaseDN
2021-08-05 04:17:07 +00:00
}
2021-11-23 09:45:38 +00:00
p . log . Tracef ( "Dynamically generated groups BaseDN is %s" , p . groupsBaseDN )
2021-08-05 04:17:07 +00:00
2022-05-02 01:51:38 +00:00
if strings . Contains ( p . config . GroupsFilter , ldapPlaceholderInput ) {
2021-08-05 04:17:07 +00:00
p . groupsFilterReplacementInput = true
}
2022-05-02 01:51:38 +00:00
if strings . Contains ( p . config . GroupsFilter , ldapPlaceholderUsername ) {
2021-08-05 04:17:07 +00:00
p . groupsFilterReplacementUsername = true
}
2022-05-02 01:51:38 +00:00
if strings . Contains ( p . config . GroupsFilter , ldapPlaceholderDistinguishedName ) {
2021-08-05 04:17:07 +00:00
p . groupsFilterReplacementDN = true
}
2023-06-18 04:40:38 +00:00
if strings . Contains ( p . config . GroupsFilter , ldapPlaceholderMemberOfDistinguishedName ) {
p . groupsFilterReplacementsMemberOfDN = true
}
if strings . Contains ( p . config . GroupsFilter , ldapPlaceholderMemberOfRelativeDistinguishedName ) {
p . groupsFilterReplacementsMemberOfRDN = true
}
2021-11-23 09:45:38 +00:00
p . log . Tracef ( "Detected group filter replacements that need to be resolved per lookup are: input=%v, username=%v, dn=%v" , p . groupsFilterReplacementInput , p . groupsFilterReplacementUsername , p . groupsFilterReplacementDN )
2021-08-05 04:17:07 +00:00
}
2023-06-18 04:40:38 +00:00
func ( p * LDAPUserProvider ) parseDynamicConfiguration ( ) {
if len ( p . config . Attributes . MemberOf ) != 0 {
if ! utils . IsStringInSlice ( p . config . Attributes . MemberOf , p . usersAttributes ) {
p . usersAttributes = append ( p . usersAttributes , p . config . Attributes . MemberOf )
}
if ! utils . IsStringInSlice ( p . config . Attributes . MemberOf , p . groupsAttributes ) {
p . groupsAttributes = append ( p . groupsAttributes , p . config . Attributes . MemberOf )
}
}
}