authelia/internal/configuration/validator/authentication.go

264 lines
10 KiB
Go
Raw Normal View History

package validator
import (
2019-12-06 08:15:54 +00:00
"fmt"
"net/url"
"strings"
"github.com/authelia/authelia/v4/internal/configuration/schema"
"github.com/authelia/authelia/v4/internal/utils"
)
// ValidateAuthenticationBackend validates and updates the authentication backend configuration.
func ValidateAuthenticationBackend(config *schema.AuthenticationBackendConfiguration, validator *schema.StructValidator) {
if config.LDAP == nil && config.File == nil {
validator.Push(fmt.Errorf(errFmtAuthBackendNotConfigured))
}
if config.LDAP != nil && config.File != nil {
validator.Push(fmt.Errorf(errFmtAuthBackendMultipleConfigured))
}
if config.File != nil {
validateFileAuthenticationBackend(config.File, validator)
} else if config.LDAP != nil {
validateLDAPAuthenticationBackend(config.LDAP, validator)
}
if config.RefreshInterval == "" {
config.RefreshInterval = schema.RefreshIntervalDefault
} else {
_, err := utils.ParseDurationString(config.RefreshInterval)
if err != nil && config.RefreshInterval != schema.ProfileRefreshDisabled && config.RefreshInterval != schema.ProfileRefreshAlways {
validator.Push(fmt.Errorf(errFmtAuthBackendRefreshInterval, config.RefreshInterval, err))
}
}
}
// validateFileAuthenticationBackend validates and updates the file authentication backend configuration.
func validateFileAuthenticationBackend(config *schema.FileAuthenticationBackendConfiguration, validator *schema.StructValidator) {
if config.Path == "" {
validator.Push(fmt.Errorf(errFmtFileAuthBackendPathNotConfigured))
}
[FEATURE] Support Argon2id password hasing and improved entropy (#679) * [FEATURE] Support Argon2id Passwords - Updated go module github.com/simia-tech/crypt - Added Argon2id support for file based authentication backend - Made it the default method - Made it so backwards compatibility with SHA512 exists - Force seeding of the random string generator used for salts to ensure they are all different - Added command params to the authelia hash-password command - Automatically remove {CRYPT} from hashes as they are updated - Automatically change hashes when they are updated to the configured algorithm - Made the hashing algorithm parameters completely configurable - Added reasonably comprehensive test suites - Updated docs - Updated config template * Adjust error output * Fix unit test * Add unit tests and argon2 version check * Fix new unit tests * Update docs, added tests * Implement configurable values and more comprehensive testing * Added cmd params to hash_password, updated docs, misc fixes * More detailed error for cmd, fixed a typo * Fixed cmd flag error, minor refactoring * Requested Changes and Minor refactoring * Increase entropy * Update docs for entropy changes * Refactor to reduce nesting and easier code maintenance * Cleanup Errors (uniformity for the function call) * Check salt length, fix docs * Add Base64 string validation for argon2id * Cleanup and Finalization - Moved RandomString function from ./internal/authentication/password_hash.go to ./internal/utils/strings.go - Added SplitStringToArrayOfStrings func that splits strings into an array with a fixed max string len - Fixed an error in validator that would allow a zero salt length - Added a test to verify the upstream crypt module supports our defined random salt chars - Updated docs - Removed unused "HashingAlgorithm" string type * Update crypt go mod, support argon2id key length and major refactor * Config Template Update, Final Tests * Use schema defaults for hash-password cmd * Iterations check * Docs requested changes * Test Coverage, suggested edits * Wording edit * Doc changes * Default sanity changes * Default sanity changes - docs * CI Sanity changes * Memory in MB
2020-03-06 01:38:02 +00:00
if config.Password == nil {
config.Password = &schema.DefaultPasswordConfiguration
[FEATURE] Support Argon2id password hasing and improved entropy (#679) * [FEATURE] Support Argon2id Passwords - Updated go module github.com/simia-tech/crypt - Added Argon2id support for file based authentication backend - Made it the default method - Made it so backwards compatibility with SHA512 exists - Force seeding of the random string generator used for salts to ensure they are all different - Added command params to the authelia hash-password command - Automatically remove {CRYPT} from hashes as they are updated - Automatically change hashes when they are updated to the configured algorithm - Made the hashing algorithm parameters completely configurable - Added reasonably comprehensive test suites - Updated docs - Updated config template * Adjust error output * Fix unit test * Add unit tests and argon2 version check * Fix new unit tests * Update docs, added tests * Implement configurable values and more comprehensive testing * Added cmd params to hash_password, updated docs, misc fixes * More detailed error for cmd, fixed a typo * Fixed cmd flag error, minor refactoring * Requested Changes and Minor refactoring * Increase entropy * Update docs for entropy changes * Refactor to reduce nesting and easier code maintenance * Cleanup Errors (uniformity for the function call) * Check salt length, fix docs * Add Base64 string validation for argon2id * Cleanup and Finalization - Moved RandomString function from ./internal/authentication/password_hash.go to ./internal/utils/strings.go - Added SplitStringToArrayOfStrings func that splits strings into an array with a fixed max string len - Fixed an error in validator that would allow a zero salt length - Added a test to verify the upstream crypt module supports our defined random salt chars - Updated docs - Removed unused "HashingAlgorithm" string type * Update crypt go mod, support argon2id key length and major refactor * Config Template Update, Final Tests * Use schema defaults for hash-password cmd * Iterations check * Docs requested changes * Test Coverage, suggested edits * Wording edit * Doc changes * Default sanity changes * Default sanity changes - docs * CI Sanity changes * Memory in MB
2020-03-06 01:38:02 +00:00
} else {
// Salt Length.
switch {
case config.Password.SaltLength == 0:
config.Password.SaltLength = schema.DefaultPasswordConfiguration.SaltLength
case config.Password.SaltLength < 8:
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordSaltLength, config.Password.SaltLength))
[FEATURE] Support Argon2id password hasing and improved entropy (#679) * [FEATURE] Support Argon2id Passwords - Updated go module github.com/simia-tech/crypt - Added Argon2id support for file based authentication backend - Made it the default method - Made it so backwards compatibility with SHA512 exists - Force seeding of the random string generator used for salts to ensure they are all different - Added command params to the authelia hash-password command - Automatically remove {CRYPT} from hashes as they are updated - Automatically change hashes when they are updated to the configured algorithm - Made the hashing algorithm parameters completely configurable - Added reasonably comprehensive test suites - Updated docs - Updated config template * Adjust error output * Fix unit test * Add unit tests and argon2 version check * Fix new unit tests * Update docs, added tests * Implement configurable values and more comprehensive testing * Added cmd params to hash_password, updated docs, misc fixes * More detailed error for cmd, fixed a typo * Fixed cmd flag error, minor refactoring * Requested Changes and Minor refactoring * Increase entropy * Update docs for entropy changes * Refactor to reduce nesting and easier code maintenance * Cleanup Errors (uniformity for the function call) * Check salt length, fix docs * Add Base64 string validation for argon2id * Cleanup and Finalization - Moved RandomString function from ./internal/authentication/password_hash.go to ./internal/utils/strings.go - Added SplitStringToArrayOfStrings func that splits strings into an array with a fixed max string len - Fixed an error in validator that would allow a zero salt length - Added a test to verify the upstream crypt module supports our defined random salt chars - Updated docs - Removed unused "HashingAlgorithm" string type * Update crypt go mod, support argon2id key length and major refactor * Config Template Update, Final Tests * Use schema defaults for hash-password cmd * Iterations check * Docs requested changes * Test Coverage, suggested edits * Wording edit * Doc changes * Default sanity changes * Default sanity changes - docs * CI Sanity changes * Memory in MB
2020-03-06 01:38:02 +00:00
}
switch config.Password.Algorithm {
case "":
config.Password.Algorithm = schema.DefaultPasswordConfiguration.Algorithm
fallthrough
case hashArgon2id:
validateFileAuthenticationBackendArgon2id(config, validator)
case hashSHA512:
validateFileAuthenticationBackendSHA512(config)
default:
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordUnknownAlg, config.Password.Algorithm))
}
[FEATURE] Support Argon2id password hasing and improved entropy (#679) * [FEATURE] Support Argon2id Passwords - Updated go module github.com/simia-tech/crypt - Added Argon2id support for file based authentication backend - Made it the default method - Made it so backwards compatibility with SHA512 exists - Force seeding of the random string generator used for salts to ensure they are all different - Added command params to the authelia hash-password command - Automatically remove {CRYPT} from hashes as they are updated - Automatically change hashes when they are updated to the configured algorithm - Made the hashing algorithm parameters completely configurable - Added reasonably comprehensive test suites - Updated docs - Updated config template * Adjust error output * Fix unit test * Add unit tests and argon2 version check * Fix new unit tests * Update docs, added tests * Implement configurable values and more comprehensive testing * Added cmd params to hash_password, updated docs, misc fixes * More detailed error for cmd, fixed a typo * Fixed cmd flag error, minor refactoring * Requested Changes and Minor refactoring * Increase entropy * Update docs for entropy changes * Refactor to reduce nesting and easier code maintenance * Cleanup Errors (uniformity for the function call) * Check salt length, fix docs * Add Base64 string validation for argon2id * Cleanup and Finalization - Moved RandomString function from ./internal/authentication/password_hash.go to ./internal/utils/strings.go - Added SplitStringToArrayOfStrings func that splits strings into an array with a fixed max string len - Fixed an error in validator that would allow a zero salt length - Added a test to verify the upstream crypt module supports our defined random salt chars - Updated docs - Removed unused "HashingAlgorithm" string type * Update crypt go mod, support argon2id key length and major refactor * Config Template Update, Final Tests * Use schema defaults for hash-password cmd * Iterations check * Docs requested changes * Test Coverage, suggested edits * Wording edit * Doc changes * Default sanity changes * Default sanity changes - docs * CI Sanity changes * Memory in MB
2020-03-06 01:38:02 +00:00
if config.Password.Iterations < 1 {
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordInvalidIterations, config.Password.Iterations))
[FEATURE] Support Argon2id password hasing and improved entropy (#679) * [FEATURE] Support Argon2id Passwords - Updated go module github.com/simia-tech/crypt - Added Argon2id support for file based authentication backend - Made it the default method - Made it so backwards compatibility with SHA512 exists - Force seeding of the random string generator used for salts to ensure they are all different - Added command params to the authelia hash-password command - Automatically remove {CRYPT} from hashes as they are updated - Automatically change hashes when they are updated to the configured algorithm - Made the hashing algorithm parameters completely configurable - Added reasonably comprehensive test suites - Updated docs - Updated config template * Adjust error output * Fix unit test * Add unit tests and argon2 version check * Fix new unit tests * Update docs, added tests * Implement configurable values and more comprehensive testing * Added cmd params to hash_password, updated docs, misc fixes * More detailed error for cmd, fixed a typo * Fixed cmd flag error, minor refactoring * Requested Changes and Minor refactoring * Increase entropy * Update docs for entropy changes * Refactor to reduce nesting and easier code maintenance * Cleanup Errors (uniformity for the function call) * Check salt length, fix docs * Add Base64 string validation for argon2id * Cleanup and Finalization - Moved RandomString function from ./internal/authentication/password_hash.go to ./internal/utils/strings.go - Added SplitStringToArrayOfStrings func that splits strings into an array with a fixed max string len - Fixed an error in validator that would allow a zero salt length - Added a test to verify the upstream crypt module supports our defined random salt chars - Updated docs - Removed unused "HashingAlgorithm" string type * Update crypt go mod, support argon2id key length and major refactor * Config Template Update, Final Tests * Use schema defaults for hash-password cmd * Iterations check * Docs requested changes * Test Coverage, suggested edits * Wording edit * Doc changes * Default sanity changes * Default sanity changes - docs * CI Sanity changes * Memory in MB
2020-03-06 01:38:02 +00:00
}
}
}
func validateFileAuthenticationBackendSHA512(config *schema.FileAuthenticationBackendConfiguration) {
// Iterations (time).
if config.Password.Iterations == 0 {
config.Password.Iterations = schema.DefaultPasswordSHA512Configuration.Iterations
}
}
func validateFileAuthenticationBackendArgon2id(config *schema.FileAuthenticationBackendConfiguration, validator *schema.StructValidator) {
// Iterations (time).
if config.Password.Iterations == 0 {
config.Password.Iterations = schema.DefaultPasswordConfiguration.Iterations
}
// Parallelism.
if config.Password.Parallelism == 0 {
config.Password.Parallelism = schema.DefaultPasswordConfiguration.Parallelism
} else if config.Password.Parallelism < 1 {
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordArgon2idInvalidParallelism, config.Password.Parallelism))
}
// Memory.
if config.Password.Memory == 0 {
config.Password.Memory = schema.DefaultPasswordConfiguration.Memory
} else if config.Password.Memory < config.Password.Parallelism*8 {
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordArgon2idInvalidMemory, config.Password.Parallelism, config.Password.Parallelism*8, config.Password.Memory))
}
// Key Length.
if config.Password.KeyLength == 0 {
config.Password.KeyLength = schema.DefaultPasswordConfiguration.KeyLength
} else if config.Password.KeyLength < 16 {
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordArgon2idInvalidKeyLength, config.Password.KeyLength))
}
}
func validateLDAPAuthenticationBackend(config *schema.LDAPAuthenticationBackendConfiguration, validator *schema.StructValidator) {
if config.Timeout == 0 {
config.Timeout = schema.DefaultLDAPAuthenticationBackendConfiguration.Timeout
}
if config.Implementation == "" {
config.Implementation = schema.DefaultLDAPAuthenticationBackendConfiguration.Implementation
}
if config.TLS == nil {
config.TLS = schema.DefaultLDAPAuthenticationBackendConfiguration.TLS
[FEATURE] Enhance LDAP/SMTP TLS Configuration and Unify Them (#1557) * add new directive in the global scope `certificates_directory` which is used to bulk load certs and trust them in Authelia * this is in ADDITION to system certs and are trusted by both LDAP and SMTP * added a shared TLSConfig struct to be used by both SMTP and LDAP, and anything else in the future that requires tuning the TLS * remove usage of deprecated LDAP funcs Dial and DialTLS in favor of DialURL which is also easier to use * use the server name from LDAP URL or SMTP host when validating the certificate unless otherwise defined in the TLS section * added temporary translations from the old names to the new ones for all deprecated options * added docs * updated example configuration * final deprecations to be done in 4.28.0 * doc updates * fix misc linting issues * uniform deprecation notices for ease of final removal * added additional tests covering previously uncovered areas and the new configuration options * add non-fatal to certificate loading when system certs could not be loaded * adjust timeout of Suite ShortTimeouts * add warnings pusher for the StructValidator * make the schema suites uninform * utilize the warnings in the StructValidator * fix test suite usage for skip_verify * extract LDAP filter parsing into it's own function to make it possible to test * test LDAP filter parsing * update ErrorContainer interface * add tests to the StructValidator * add NewTLSConfig test * move baseDN for users/groups into parsed values * add tests to cover many of the outstanding areas in LDAP * add explicit deferred LDAP conn close to UpdatePassword * add some basic testing to SMTP notifier * suggestions from code review
2021-01-04 10:28:55 +00:00
}
if config.TLS.MinimumVersion == "" {
config.TLS.MinimumVersion = schema.DefaultLDAPAuthenticationBackendConfiguration.TLS.MinimumVersion
[FEATURE] Enhance LDAP/SMTP TLS Configuration and Unify Them (#1557) * add new directive in the global scope `certificates_directory` which is used to bulk load certs and trust them in Authelia * this is in ADDITION to system certs and are trusted by both LDAP and SMTP * added a shared TLSConfig struct to be used by both SMTP and LDAP, and anything else in the future that requires tuning the TLS * remove usage of deprecated LDAP funcs Dial and DialTLS in favor of DialURL which is also easier to use * use the server name from LDAP URL or SMTP host when validating the certificate unless otherwise defined in the TLS section * added temporary translations from the old names to the new ones for all deprecated options * added docs * updated example configuration * final deprecations to be done in 4.28.0 * doc updates * fix misc linting issues * uniform deprecation notices for ease of final removal * added additional tests covering previously uncovered areas and the new configuration options * add non-fatal to certificate loading when system certs could not be loaded * adjust timeout of Suite ShortTimeouts * add warnings pusher for the StructValidator * make the schema suites uninform * utilize the warnings in the StructValidator * fix test suite usage for skip_verify * extract LDAP filter parsing into it's own function to make it possible to test * test LDAP filter parsing * update ErrorContainer interface * add tests to the StructValidator * add NewTLSConfig test * move baseDN for users/groups into parsed values * add tests to cover many of the outstanding areas in LDAP * add explicit deferred LDAP conn close to UpdatePassword * add some basic testing to SMTP notifier * suggestions from code review
2021-01-04 10:28:55 +00:00
}
if _, err := utils.TLSStringToTLSConfigVersion(config.TLS.MinimumVersion); err != nil {
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendTLSMinVersion, config.TLS.MinimumVersion, err))
}
switch config.Implementation {
case schema.LDAPImplementationCustom:
setDefaultImplementationCustomLDAPAuthenticationBackend(config)
case schema.LDAPImplementationActiveDirectory:
setDefaultImplementationActiveDirectoryLDAPAuthenticationBackend(config)
default:
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendImplementation, config.Implementation, strings.Join([]string{schema.LDAPImplementationCustom, schema.LDAPImplementationActiveDirectory}, "', '")))
}
if strings.Contains(config.UsersFilter, "{0}") {
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendFilterReplacedPlaceholders, "users_filter", "{0}", "{input}"))
}
if strings.Contains(config.GroupsFilter, "{0}") {
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendFilterReplacedPlaceholders, "groups_filter", "{0}", "{input}"))
}
if strings.Contains(config.GroupsFilter, "{1}") {
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendFilterReplacedPlaceholders, "groups_filter", "{1}", "{username}"))
}
[FEATURE] Enhance LDAP/SMTP TLS Configuration and Unify Them (#1557) * add new directive in the global scope `certificates_directory` which is used to bulk load certs and trust them in Authelia * this is in ADDITION to system certs and are trusted by both LDAP and SMTP * added a shared TLSConfig struct to be used by both SMTP and LDAP, and anything else in the future that requires tuning the TLS * remove usage of deprecated LDAP funcs Dial and DialTLS in favor of DialURL which is also easier to use * use the server name from LDAP URL or SMTP host when validating the certificate unless otherwise defined in the TLS section * added temporary translations from the old names to the new ones for all deprecated options * added docs * updated example configuration * final deprecations to be done in 4.28.0 * doc updates * fix misc linting issues * uniform deprecation notices for ease of final removal * added additional tests covering previously uncovered areas and the new configuration options * add non-fatal to certificate loading when system certs could not be loaded * adjust timeout of Suite ShortTimeouts * add warnings pusher for the StructValidator * make the schema suites uninform * utilize the warnings in the StructValidator * fix test suite usage for skip_verify * extract LDAP filter parsing into it's own function to make it possible to test * test LDAP filter parsing * update ErrorContainer interface * add tests to the StructValidator * add NewTLSConfig test * move baseDN for users/groups into parsed values * add tests to cover many of the outstanding areas in LDAP * add explicit deferred LDAP conn close to UpdatePassword * add some basic testing to SMTP notifier * suggestions from code review
2021-01-04 10:28:55 +00:00
if config.URL == "" {
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendMissingOption, "url"))
} else {
validateLDAPAuthenticationBackendURL(config, validator)
}
validateLDAPRequiredParameters(config, validator)
}
func validateLDAPAuthenticationBackendURL(config *schema.LDAPAuthenticationBackendConfiguration, validator *schema.StructValidator) {
var (
parsedURL *url.URL
err error
)
if parsedURL, err = url.Parse(config.URL); err != nil {
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendURLNotParsable, err))
return
}
if parsedURL.Scheme != schemeLDAP && parsedURL.Scheme != schemeLDAPS {
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendURLInvalidScheme, parsedURL.Scheme))
return
}
config.URL = parsedURL.String()
if config.TLS.ServerName == "" {
config.TLS.ServerName = parsedURL.Hostname()
}
}
func validateLDAPRequiredParameters(config *schema.LDAPAuthenticationBackendConfiguration, validator *schema.StructValidator) {
// TODO: see if it's possible to disable this check if disable_reset_password is set and when anonymous/user binding is supported (#101 and #387).
if config.User == "" {
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendMissingOption, "user"))
}
// TODO: see if it's possible to disable this check if disable_reset_password is set and when anonymous/user binding is supported (#101 and #387).
if config.Password == "" {
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendMissingOption, "password"))
}
if config.BaseDN == "" {
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendMissingOption, "base_dn"))
}
if config.UsersFilter == "" {
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendMissingOption, "users_filter"))
} else {
if !strings.HasPrefix(config.UsersFilter, "(") || !strings.HasSuffix(config.UsersFilter, ")") {
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendFilterEnclosingParenthesis, "users_filter", config.UsersFilter, config.UsersFilter))
}
if !strings.Contains(config.UsersFilter, "{username_attribute}") {
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendFilterMissingPlaceholder, "users_filter", "username_attribute"))
}
// This test helps the user know that users_filter is broken after the breaking change induced by this commit.
if !strings.Contains(config.UsersFilter, "{input}") {
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendFilterMissingPlaceholder, "users_filter", "input"))
}
}
if config.GroupsFilter == "" {
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendMissingOption, "groups_filter"))
} else if !strings.HasPrefix(config.GroupsFilter, "(") || !strings.HasSuffix(config.GroupsFilter, ")") {
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendFilterEnclosingParenthesis, "groups_filter", config.GroupsFilter, config.GroupsFilter))
}
}
func setDefaultImplementationActiveDirectoryLDAPAuthenticationBackend(config *schema.LDAPAuthenticationBackendConfiguration) {
if config.UsersFilter == "" {
config.UsersFilter = schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.UsersFilter
}
if config.UsernameAttribute == "" {
config.UsernameAttribute = schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.UsernameAttribute
}
if config.DisplayNameAttribute == "" {
config.DisplayNameAttribute = schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.DisplayNameAttribute
}
if config.MailAttribute == "" {
config.MailAttribute = schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.MailAttribute
}
if config.GroupsFilter == "" {
config.GroupsFilter = schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.GroupsFilter
}
if config.GroupNameAttribute == "" {
config.GroupNameAttribute = schema.DefaultLDAPAuthenticationBackendImplementationActiveDirectoryConfiguration.GroupNameAttribute
}
}
func setDefaultImplementationCustomLDAPAuthenticationBackend(config *schema.LDAPAuthenticationBackendConfiguration) {
if config.UsernameAttribute == "" {
config.UsernameAttribute = schema.DefaultLDAPAuthenticationBackendConfiguration.UsernameAttribute
}
if config.GroupNameAttribute == "" {
config.GroupNameAttribute = schema.DefaultLDAPAuthenticationBackendConfiguration.GroupNameAttribute
}
if config.MailAttribute == "" {
config.MailAttribute = schema.DefaultLDAPAuthenticationBackendConfiguration.MailAttribute
}
if config.DisplayNameAttribute == "" {
config.DisplayNameAttribute = schema.DefaultLDAPAuthenticationBackendConfiguration.DisplayNameAttribute
}
}