From dc7ca6f03c97f22057b7a02cd0e71bb79926b4b9 Mon Sep 17 00:00:00 2001 From: James Elliott Date: Sat, 16 Apr 2022 19:00:39 +1000 Subject: [PATCH] refactor: introduce config key gen (#3206) This adjusts the validated keys to utilize a generated code section. --- cmd/authelia-scripts/cmd_gen.go | 199 ++++++++++++++ cmd/authelia-scripts/main.go | 2 +- internal/configuration/koanf_callbacks.go | 5 +- internal/configuration/schema/keys.go | 196 ++++++++++++++ internal/configuration/schema/keys_test.go | 253 ++++++++++++++++++ internal/configuration/sources.go | 5 +- internal/configuration/validator/const.go | 234 ---------------- internal/configuration/validator/keys.go | 2 +- internal/configuration/validator/keys_test.go | 6 +- 9 files changed, 657 insertions(+), 245 deletions(-) create mode 100644 cmd/authelia-scripts/cmd_gen.go create mode 100644 internal/configuration/schema/keys.go create mode 100644 internal/configuration/schema/keys_test.go diff --git a/cmd/authelia-scripts/cmd_gen.go b/cmd/authelia-scripts/cmd_gen.go new file mode 100644 index 000000000..cc8bfa943 --- /dev/null +++ b/cmd/authelia-scripts/cmd_gen.go @@ -0,0 +1,199 @@ +package main + +import ( + "fmt" + "net/mail" + "net/url" + "os" + "reflect" + "regexp" + "strings" + "text/template" + "time" + + "github.com/spf13/cobra" + + "github.com/authelia/authelia/v4/internal/configuration/schema" +) + +// NewRunGenCmd implements the code generation cobra command. +func NewRunGenCmd() (cmd *cobra.Command) { + cmd = &cobra.Command{ + Use: "gen", + RunE: runGenE, + } + + return cmd +} + +func runGenE(cmd *cobra.Command, args []string) (err error) { + if err = genConfigurationKeys(); err != nil { + return err + } + + return nil +} + +func genConfigurationKeys() (err error) { + data := loadKeysTemplate() + + f, err := os.Create("./internal/configuration/schema/keys.go") + if err != nil { + return err + } + + return keysTemplate.Execute(f, data) +} + +var keysTemplate = template.Must(template.New("keys").Parse(`// Code generated by go generate. DO NOT EDIT. +// +// Run the following command to generate this file: +// go run ./cmd/authelia-scripts gen +// + +package schema + +// Keys represents the detected schema keys. +var Keys = []string{ +{{- range .Keys }} + {{ printf "%q" . }}, +{{- end }} +} +`)) + +type keysTemplateStruct struct { + Timestamp time.Time + Keys []string +} + +func loadKeysTemplate() keysTemplateStruct { + config := schema.Configuration{ + Storage: schema.StorageConfiguration{ + Local: &schema.LocalStorageConfiguration{}, + MySQL: &schema.MySQLStorageConfiguration{}, + PostgreSQL: &schema.PostgreSQLStorageConfiguration{}, + }, + Notifier: schema.NotifierConfiguration{ + FileSystem: &schema.FileSystemNotifierConfiguration{}, + SMTP: &schema.SMTPNotifierConfiguration{ + TLS: &schema.TLSConfig{}, + }, + }, + AuthenticationBackend: schema.AuthenticationBackendConfiguration{ + File: &schema.FileAuthenticationBackendConfiguration{ + Password: &schema.PasswordConfiguration{}, + }, + LDAP: &schema.LDAPAuthenticationBackendConfiguration{ + TLS: &schema.TLSConfig{}, + }, + }, + Session: schema.SessionConfiguration{ + Redis: &schema.RedisSessionConfiguration{ + TLS: &schema.TLSConfig{}, + HighAvailability: &schema.RedisHighAvailabilityConfiguration{}, + }, + }, + IdentityProviders: schema.IdentityProvidersConfiguration{ + OIDC: &schema.OpenIDConnectConfiguration{}, + }, + } + + return keysTemplateStruct{ + Timestamp: time.Now(), + Keys: readTags("", reflect.TypeOf(config)), + } +} + +var decodedTypes = []reflect.Type{ + reflect.TypeOf(mail.Address{}), + reflect.TypeOf(regexp.Regexp{}), + reflect.TypeOf(url.URL{}), + reflect.TypeOf(time.Duration(0)), +} + +func containsType(needle reflect.Type, haystack []reflect.Type) (contains bool) { + for _, t := range haystack { + if needle.Kind() == reflect.Ptr { + if needle.Elem() == t { + return true + } + } else if needle == t { + return true + } + } + + return false +} + +func readTags(prefix string, t reflect.Type) (tags []string) { + tags = make([]string, 0) + + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + + tag := field.Tag.Get("koanf") + + if tag == "" { + tags = append(tags, prefix) + + continue + } + + switch field.Type.Kind() { + case reflect.Struct: + if !containsType(field.Type, decodedTypes) { + tags = append(tags, readTags(getKeyNameFromTagAndPrefix(prefix, tag, false), field.Type)...) + + continue + } + case reflect.Slice: + if field.Type.Elem().Kind() == reflect.Struct { + if !containsType(field.Type.Elem(), decodedTypes) { + tags = append(tags, getKeyNameFromTagAndPrefix(prefix, tag, false)) + tags = append(tags, readTags(getKeyNameFromTagAndPrefix(prefix, tag, true), field.Type.Elem())...) + + continue + } + } + case reflect.Ptr: + switch field.Type.Elem().Kind() { + case reflect.Struct: + if !containsType(field.Type.Elem(), decodedTypes) { + tags = append(tags, readTags(getKeyNameFromTagAndPrefix(prefix, tag, false), field.Type.Elem())...) + + continue + } + case reflect.Slice: + if field.Type.Elem().Elem().Kind() == reflect.Struct { + if !containsType(field.Type.Elem(), decodedTypes) { + tags = append(tags, readTags(getKeyNameFromTagAndPrefix(prefix, tag, true), field.Type.Elem())...) + + continue + } + } + } + } + + tags = append(tags, getKeyNameFromTagAndPrefix(prefix, tag, false)) + } + + return tags +} + +func getKeyNameFromTagAndPrefix(prefix, name string, slice bool) string { + nameParts := strings.SplitN(name, ",", 2) + + if prefix == "" { + return nameParts[0] + } + + if len(nameParts) == 2 && nameParts[1] == "squash" { + return prefix + } + + if slice { + return fmt.Sprintf("%s.%s[]", prefix, nameParts[0]) + } + + return fmt.Sprintf("%s.%s", prefix, nameParts[0]) +} diff --git a/cmd/authelia-scripts/main.go b/cmd/authelia-scripts/main.go index cd812d29f..011cb09b2 100755 --- a/cmd/authelia-scripts/main.go +++ b/cmd/authelia-scripts/main.go @@ -136,7 +136,7 @@ func main() { cobraCommands = append(cobraCommands, command) } - cobraCommands = append(cobraCommands, commands.NewHashPasswordCmd(), commands.NewCertificatesCmd(), commands.NewRSACmd(), xflagsCmd) + cobraCommands = append(cobraCommands, commands.NewHashPasswordCmd(), commands.NewCertificatesCmd(), commands.NewRSACmd(), NewRunGenCmd(), xflagsCmd) rootCmd.PersistentFlags().BoolVar(&buildkite, "buildkite", false, "Set CI flag for Buildkite") rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "info", "Set the log level for the command") diff --git a/internal/configuration/koanf_callbacks.go b/internal/configuration/koanf_callbacks.go index 384b87c88..26d11ebcc 100644 --- a/internal/configuration/koanf_callbacks.go +++ b/internal/configuration/koanf_callbacks.go @@ -7,7 +7,6 @@ import ( "github.com/spf13/pflag" "github.com/authelia/authelia/v4/internal/configuration/schema" - "github.com/authelia/authelia/v4/internal/configuration/validator" "github.com/authelia/authelia/v4/internal/utils" ) @@ -25,7 +24,7 @@ func koanfEnvironmentCallback(keyMap map[string]string, ignoredKeys []string, pr formattedKey := strings.TrimPrefix(key, prefix) formattedKey = strings.ReplaceAll(strings.ToLower(formattedKey), delimiter, constDelimiter) - if utils.IsStringInSlice(formattedKey, validator.ValidKeys) { + if utils.IsStringInSlice(formattedKey, schema.Keys) { return formattedKey, value } @@ -64,7 +63,7 @@ func koanfCommandLineWithMappingCallback(mapping map[string]string, includeValid if includeValidKeys { formattedKey := strings.ReplaceAll(flag.Name, "-", "_") - if utils.IsStringInSlice(formattedKey, validator.ValidKeys) { + if utils.IsStringInSlice(formattedKey, schema.Keys) { return formattedKey, flag.Value.String() } } diff --git a/internal/configuration/schema/keys.go b/internal/configuration/schema/keys.go new file mode 100644 index 000000000..6c3124e7d --- /dev/null +++ b/internal/configuration/schema/keys.go @@ -0,0 +1,196 @@ +// Code generated by go generate. DO NOT EDIT. +// +// Run the following command to generate this file: +// go run ./cmd/authelia-scripts gen +// + +package schema + +// Keys represents the detected schema keys. +var Keys = []string{ + "theme", + "certificates_directory", + "jwt_secret", + "default_redirection_url", + "log.level", + "log.format", + "log.file_path", + "log.keep_stdout", + "identity_providers.oidc.hmac_secret", + "identity_providers.oidc.issuer_private_key", + "identity_providers.oidc.access_token_lifespan", + "identity_providers.oidc.authorize_code_lifespan", + "identity_providers.oidc.id_token_lifespan", + "identity_providers.oidc.refresh_token_lifespan", + "identity_providers.oidc.enable_client_debug_messages", + "identity_providers.oidc.minimum_parameter_entropy", + "identity_providers.oidc.enforce_pkce", + "identity_providers.oidc.enable_pkce_plain_challenge", + "identity_providers.oidc.cors.endpoints", + "identity_providers.oidc.cors.allowed_origins", + "identity_providers.oidc.cors.allowed_origins_from_client_redirect_uris", + "identity_providers.oidc.clients", + "identity_providers.oidc.clients[].id", + "identity_providers.oidc.clients[].description", + "identity_providers.oidc.clients[].secret", + "identity_providers.oidc.clients[].sector_identifier", + "identity_providers.oidc.clients[].public", + "identity_providers.oidc.clients[].redirect_uris", + "identity_providers.oidc.clients[].audience", + "identity_providers.oidc.clients[].scopes", + "identity_providers.oidc.clients[].grant_types", + "identity_providers.oidc.clients[].response_types", + "identity_providers.oidc.clients[].response_modes", + "identity_providers.oidc.clients[].userinfo_signing_algorithm", + "identity_providers.oidc.clients[].authorization_policy", + "identity_providers.oidc.clients[].pre_configured_consent_duration", + "authentication_backend.ldap.implementation", + "authentication_backend.ldap.url", + "authentication_backend.ldap.timeout", + "authentication_backend.ldap.start_tls", + "authentication_backend.ldap.tls.minimum_version", + "authentication_backend.ldap.tls.skip_verify", + "authentication_backend.ldap.tls.server_name", + "authentication_backend.ldap.base_dn", + "authentication_backend.ldap.additional_users_dn", + "authentication_backend.ldap.users_filter", + "authentication_backend.ldap.additional_groups_dn", + "authentication_backend.ldap.groups_filter", + "authentication_backend.ldap.group_name_attribute", + "authentication_backend.ldap.username_attribute", + "authentication_backend.ldap.mail_attribute", + "authentication_backend.ldap.display_name_attribute", + "authentication_backend.ldap.user", + "authentication_backend.ldap.password", + "authentication_backend.file.path", + "authentication_backend.file.password.iterations", + "authentication_backend.file.password.key_length", + "authentication_backend.file.password.salt_length", + "authentication_backend.file.password.algorithm", + "authentication_backend.file.password.memory", + "authentication_backend.file.password.parallelism", + "authentication_backend.password_reset.custom_url", + "authentication_backend.disable_reset_password", + "authentication_backend.refresh_interval", + "session.name", + "session.domain", + "session.same_site", + "session.secret", + "session.expiration", + "session.inactivity", + "session.remember_me_duration", + "session.redis.host", + "session.redis.port", + "session.redis.username", + "session.redis.password", + "session.redis.database_index", + "session.redis.maximum_active_connections", + "session.redis.minimum_idle_connections", + "session.redis.tls.minimum_version", + "session.redis.tls.skip_verify", + "session.redis.tls.server_name", + "session.redis.high_availability.sentinel_name", + "session.redis.high_availability.sentinel_username", + "session.redis.high_availability.sentinel_password", + "session.redis.high_availability.nodes", + "session.redis.high_availability.nodes[].host", + "session.redis.high_availability.nodes[].port", + "session.redis.high_availability.route_by_latency", + "session.redis.high_availability.route_randomly", + "totp.disable", + "totp.issuer", + "totp.algorithm", + "totp.digits", + "totp.period", + "totp.skew", + "totp.secret_size", + "duo_api.disable", + "duo_api.hostname", + "duo_api.integration_key", + "duo_api.secret_key", + "duo_api.enable_self_enrollment", + "access_control.default_policy", + "access_control.networks", + "access_control.networks[].name", + "access_control.networks[].networks", + "access_control.rules", + "access_control.rules[].domain", + "access_control.rules[].domain_regex", + "access_control.rules[].policy", + "access_control.rules[].subject", + "access_control.rules[].networks", + "access_control.rules[].resources", + "access_control.rules[].methods", + "ntp.address", + "ntp.version", + "ntp.max_desync", + "ntp.disable_startup_check", + "ntp.disable_failure", + "regulation.max_retries", + "regulation.find_time", + "regulation.ban_time", + "storage.local.path", + "storage.mysql.host", + "storage.mysql.port", + "storage.mysql.database", + "storage.mysql.username", + "storage.mysql.password", + "storage.mysql.timeout", + "storage.postgres.host", + "storage.postgres.port", + "storage.postgres.database", + "storage.postgres.username", + "storage.postgres.password", + "storage.postgres.timeout", + "storage.postgres.schema", + "storage.postgres.ssl.mode", + "storage.postgres.ssl.root_certificate", + "storage.postgres.ssl.certificate", + "storage.postgres.ssl.key", + "storage.postgres.sslmode", + "storage.encryption_key", + "notifier.disable_startup_check", + "notifier.filesystem.filename", + "notifier.smtp.host", + "notifier.smtp.port", + "notifier.smtp.timeout", + "notifier.smtp.username", + "notifier.smtp.password", + "notifier.smtp.identifier", + "notifier.smtp.sender", + "notifier.smtp.subject", + "notifier.smtp.startup_check_address", + "notifier.smtp.disable_require_tls", + "notifier.smtp.disable_html_emails", + "notifier.smtp.tls.minimum_version", + "notifier.smtp.tls.skip_verify", + "notifier.smtp.tls.server_name", + "notifier.template_path", + "server.host", + "server.port", + "server.path", + "server.asset_path", + "server.read_buffer_size", + "server.write_buffer_size", + "server.enable_pprof", + "server.enable_expvars", + "server.disable_healthcheck", + "server.tls.certificate", + "server.tls.key", + "server.tls.client_certificates", + "server.headers.csp_template", + "webauthn.disable", + "webauthn.display_name", + "webauthn.attestation_conveyance_preference", + "webauthn.user_verification", + "webauthn.timeout", + "password_policy.standard.enabled", + "password_policy.standard.min_length", + "password_policy.standard.max_length", + "password_policy.standard.require_uppercase", + "password_policy.standard.require_lowercase", + "password_policy.standard.require_number", + "password_policy.standard.require_special", + "password_policy.zxcvbn.enabled", + "password_policy.zxcvbn.min_score", +} diff --git a/internal/configuration/schema/keys_test.go b/internal/configuration/schema/keys_test.go new file mode 100644 index 000000000..13990c80e --- /dev/null +++ b/internal/configuration/schema/keys_test.go @@ -0,0 +1,253 @@ +package schema + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +var ValidKeys = []string{ + // Root Keys. + "certificates_directory", + "theme", + "default_redirection_url", + "jwt_secret", + + // Log keys. + "log.level", + "log.format", + "log.file_path", + "log.keep_stdout", + + // Server Keys. + "server.host", + "server.port", + "server.read_buffer_size", + "server.write_buffer_size", + "server.path", + "server.asset_path", + "server.enable_pprof", + "server.enable_expvars", + "server.disable_healthcheck", + "server.tls.key", + "server.tls.certificate", + "server.tls.client_certificates", + "server.headers.csp_template", + + // TOTP Keys. + "totp.disable", + "totp.issuer", + "totp.algorithm", + "totp.digits", + "totp.period", + "totp.skew", + "totp.secret_size", + + // Webauthn Keys. + "webauthn.disable", + "webauthn.display_name", + "webauthn.attestation_conveyance_preference", + "webauthn.user_verification", + "webauthn.timeout", + + // DUO API Keys. + "duo_api.disable", + "duo_api.hostname", + "duo_api.enable_self_enrollment", + "duo_api.secret_key", + "duo_api.integration_key", + + // Access Control Keys. + "access_control.default_policy", + "access_control.networks", + "access_control.networks[].name", + "access_control.networks[].networks", + "access_control.rules", + "access_control.rules[].domain", + "access_control.rules[].domain_regex", + "access_control.rules[].methods", + "access_control.rules[].networks", + "access_control.rules[].subject", + "access_control.rules[].policy", + "access_control.rules[].resources", + + // Session Keys. + "session.name", + "session.domain", + "session.secret", + "session.same_site", + "session.expiration", + "session.inactivity", + "session.remember_me_duration", + + // Redis Session Keys. + "session.redis.host", + "session.redis.port", + "session.redis.username", + "session.redis.password", + "session.redis.database_index", + "session.redis.maximum_active_connections", + "session.redis.minimum_idle_connections", + "session.redis.tls.minimum_version", + "session.redis.tls.skip_verify", + "session.redis.tls.server_name", + "session.redis.high_availability.sentinel_name", + "session.redis.high_availability.sentinel_username", + "session.redis.high_availability.sentinel_password", + "session.redis.high_availability.nodes", + "session.redis.high_availability.nodes[].host", + "session.redis.high_availability.nodes[].port", + "session.redis.high_availability.route_by_latency", + "session.redis.high_availability.route_randomly", + + // Storage Keys. + "storage.encryption_key", + + // Local Storage Keys. + "storage.local.path", + + // MySQL Storage Keys. + "storage.mysql.host", + "storage.mysql.port", + "storage.mysql.database", + "storage.mysql.username", + "storage.mysql.password", + "storage.mysql.timeout", + + // PostgreSQL Storage Keys. + "storage.postgres.host", + "storage.postgres.port", + "storage.postgres.database", + "storage.postgres.username", + "storage.postgres.password", + "storage.postgres.timeout", + "storage.postgres.schema", + "storage.postgres.ssl.mode", + "storage.postgres.ssl.root_certificate", + "storage.postgres.ssl.certificate", + "storage.postgres.ssl.key", + + "storage.postgres.sslmode", // Deprecated. TODO: Remove in v4.36.0. + + // FileSystem Notifier Keys. + "notifier.filesystem.filename", + "notifier.disable_startup_check", + + // SMTP Notifier Keys. + "notifier.smtp.host", + "notifier.smtp.port", + "notifier.smtp.timeout", + "notifier.smtp.username", + "notifier.smtp.password", + "notifier.smtp.identifier", + "notifier.smtp.sender", + "notifier.smtp.subject", + "notifier.smtp.startup_check_address", + "notifier.smtp.disable_require_tls", + "notifier.smtp.disable_html_emails", + "notifier.smtp.tls.minimum_version", + "notifier.smtp.tls.skip_verify", + "notifier.smtp.tls.server_name", + "notifier.template_path", + + // Regulation Keys. + "regulation.max_retries", + "regulation.find_time", + "regulation.ban_time", + + // Authentication Backend Keys. + "authentication_backend.disable_reset_password", + "authentication_backend.password_reset.custom_url", + "authentication_backend.refresh_interval", + + // LDAP Authentication Backend Keys. + "authentication_backend.ldap.implementation", + "authentication_backend.ldap.url", + "authentication_backend.ldap.timeout", + "authentication_backend.ldap.base_dn", + "authentication_backend.ldap.username_attribute", + "authentication_backend.ldap.additional_users_dn", + "authentication_backend.ldap.users_filter", + "authentication_backend.ldap.additional_groups_dn", + "authentication_backend.ldap.groups_filter", + "authentication_backend.ldap.group_name_attribute", + "authentication_backend.ldap.mail_attribute", + "authentication_backend.ldap.display_name_attribute", + "authentication_backend.ldap.user", + "authentication_backend.ldap.password", + "authentication_backend.ldap.start_tls", + "authentication_backend.ldap.tls.minimum_version", + "authentication_backend.ldap.tls.skip_verify", + "authentication_backend.ldap.tls.server_name", + + // File Authentication Backend Keys. + "authentication_backend.file.path", + "authentication_backend.file.password.algorithm", + "authentication_backend.file.password.iterations", + "authentication_backend.file.password.key_length", + "authentication_backend.file.password.salt_length", + "authentication_backend.file.password.memory", + "authentication_backend.file.password.parallelism", + + // Identity Provider Keys. + "identity_providers.oidc.hmac_secret", + "identity_providers.oidc.issuer_private_key", + "identity_providers.oidc.id_token_lifespan", + "identity_providers.oidc.access_token_lifespan", + "identity_providers.oidc.refresh_token_lifespan", + "identity_providers.oidc.authorize_code_lifespan", + "identity_providers.oidc.enforce_pkce", + "identity_providers.oidc.enable_pkce_plain_challenge", + "identity_providers.oidc.enable_client_debug_messages", + "identity_providers.oidc.minimum_parameter_entropy", + "identity_providers.oidc.cors.endpoints", + "identity_providers.oidc.cors.allowed_origins", + "identity_providers.oidc.cors.allowed_origins_from_client_redirect_uris", + "identity_providers.oidc.clients", + "identity_providers.oidc.clients[].id", + "identity_providers.oidc.clients[].description", + "identity_providers.oidc.clients[].secret", + "identity_providers.oidc.clients[].sector_identifier", + "identity_providers.oidc.clients[].public", + "identity_providers.oidc.clients[].redirect_uris", + "identity_providers.oidc.clients[].authorization_policy", + "identity_providers.oidc.clients[].pre_configured_consent_duration", + "identity_providers.oidc.clients[].scopes", + "identity_providers.oidc.clients[].audience", + "identity_providers.oidc.clients[].grant_types", + "identity_providers.oidc.clients[].response_types", + "identity_providers.oidc.clients[].response_modes", + "identity_providers.oidc.clients[].userinfo_signing_algorithm", + + // NTP keys. + "ntp.address", + "ntp.version", + "ntp.max_desync", + "ntp.disable_startup_check", + "ntp.disable_failure", + + // Password Policy keys. + "password_policy.standard.enabled", + "password_policy.standard.min_length", + "password_policy.standard.max_length", + "password_policy.standard.require_uppercase", + "password_policy.standard.require_lowercase", + "password_policy.standard.require_number", + "password_policy.standard.require_special", + "password_policy.zxcvbn.enabled", + "password_policy.zxcvbn.min_score", +} + +func TestOldKeys(t *testing.T) { + for _, key := range ValidKeys { + assert.Contains(t, Keys, key) + } + + for _, key := range Keys { + assert.Contains(t, ValidKeys, key) + } +} + +func TestDuplicates(t *testing.T) { + assert.Equal(t, len(Keys), len(ValidKeys)) +} diff --git a/internal/configuration/sources.go b/internal/configuration/sources.go index 7506b2f03..22876e5b8 100644 --- a/internal/configuration/sources.go +++ b/internal/configuration/sources.go @@ -12,7 +12,6 @@ import ( "github.com/spf13/pflag" "github.com/authelia/authelia/v4/internal/configuration/schema" - "github.com/authelia/authelia/v4/internal/configuration/validator" ) // NewYAMLFileSource returns a Source configured to load from a specified YAML path. If there is an issue accessing this @@ -75,7 +74,7 @@ func (s *EnvironmentSource) Merge(ko *koanf.Koanf, _ *schema.StructValidator) (e // Load the Source into the EnvironmentSource koanf.Koanf. func (s *EnvironmentSource) Load(_ *schema.StructValidator) (err error) { - keyMap, ignoredKeys := getEnvConfigMap(validator.ValidKeys, s.prefix, s.delimiter) + keyMap, ignoredKeys := getEnvConfigMap(schema.Keys, s.prefix, s.delimiter) return s.koanf.Load(env.ProviderWithValue(s.prefix, constDelimiter, koanfEnvironmentCallback(keyMap, ignoredKeys, s.prefix, s.delimiter)), nil) } @@ -109,7 +108,7 @@ func (s *SecretsSource) Merge(ko *koanf.Koanf, val *schema.StructValidator) (err // Load the Source into the SecretsSource koanf.Koanf. func (s *SecretsSource) Load(val *schema.StructValidator) (err error) { - keyMap := getSecretConfigMap(validator.ValidKeys, s.prefix, s.delimiter) + keyMap := getSecretConfigMap(schema.Keys, s.prefix, s.delimiter) return s.koanf.Load(env.ProviderWithValue(s.prefix, constDelimiter, koanfEnvironmentSecretsCallback(keyMap, val)), nil) } diff --git a/internal/configuration/validator/const.go b/internal/configuration/validator/const.go index b84652229..47484abb5 100644 --- a/internal/configuration/validator/const.go +++ b/internal/configuration/validator/const.go @@ -300,240 +300,6 @@ var validOIDCCORSEndpoints = []string{oidc.AuthorizationEndpoint, oidc.TokenEndp var reKeyReplacer = regexp.MustCompile(`\[\d+]`) -// ValidKeys is a list of valid keys that are not secret names. For the sake of consistency please place any secret in -// the secret names map and reuse it in relevant sections. -var ValidKeys = []string{ - // Root Keys. - "certificates_directory", - "theme", - "default_redirection_url", - "jwt_secret", - - // Log keys. - "log.level", - "log.format", - "log.file_path", - "log.keep_stdout", - - // Server Keys. - "server.host", - "server.port", - "server.read_buffer_size", - "server.write_buffer_size", - "server.path", - "server.asset_path", - "server.enable_pprof", - "server.enable_expvars", - "server.disable_healthcheck", - "server.tls.key", - "server.tls.certificate", - "server.tls.client_certificates", - "server.headers.csp_template", - - // TOTP Keys. - "totp.disable", - "totp.issuer", - "totp.algorithm", - "totp.digits", - "totp.period", - "totp.skew", - "totp.secret_size", - - // Webauthn Keys. - "webauthn.disable", - "webauthn.display_name", - "webauthn.attestation_conveyance_preference", - "webauthn.user_verification", - "webauthn.timeout", - - // DUO API Keys. - "duo_api.disable", - "duo_api.hostname", - "duo_api.enable_self_enrollment", - "duo_api.secret_key", - "duo_api.integration_key", - - // Access Control Keys. - "access_control.default_policy", - "access_control.networks", - "access_control.networks[].name", - "access_control.networks[].networks", - "access_control.rules", - "access_control.rules[].domain", - "access_control.rules[].domain_regex", - "access_control.rules[].methods", - "access_control.rules[].networks", - "access_control.rules[].subject", - "access_control.rules[].policy", - "access_control.rules[].resources", - - // Session Keys. - "session.name", - "session.domain", - "session.secret", - "session.same_site", - "session.expiration", - "session.inactivity", - "session.remember_me_duration", - - // Redis Session Keys. - "session.redis.host", - "session.redis.port", - "session.redis.username", - "session.redis.password", - "session.redis.database_index", - "session.redis.maximum_active_connections", - "session.redis.minimum_idle_connections", - "session.redis.tls.minimum_version", - "session.redis.tls.skip_verify", - "session.redis.tls.server_name", - "session.redis.high_availability.sentinel_name", - "session.redis.high_availability.sentinel_username", - "session.redis.high_availability.sentinel_password", - "session.redis.high_availability.nodes", - "session.redis.high_availability.nodes[].host", - "session.redis.high_availability.nodes[].port", - "session.redis.high_availability.route_by_latency", - "session.redis.high_availability.route_randomly", - - // Storage Keys. - "storage.encryption_key", - - // Local Storage Keys. - "storage.local.path", - - // MySQL Storage Keys. - "storage.mysql.host", - "storage.mysql.port", - "storage.mysql.database", - "storage.mysql.username", - "storage.mysql.password", - "storage.mysql.timeout", - - // PostgreSQL Storage Keys. - "storage.postgres.host", - "storage.postgres.port", - "storage.postgres.database", - "storage.postgres.username", - "storage.postgres.password", - "storage.postgres.timeout", - "storage.postgres.schema", - "storage.postgres.ssl.mode", - "storage.postgres.ssl.root_certificate", - "storage.postgres.ssl.certificate", - "storage.postgres.ssl.key", - - "storage.postgres.sslmode", // Deprecated. TODO: Remove in v4.36.0. - - // FileSystem Notifier Keys. - "notifier.filesystem.filename", - "notifier.disable_startup_check", - - // SMTP Notifier Keys. - "notifier.smtp.host", - "notifier.smtp.port", - "notifier.smtp.timeout", - "notifier.smtp.username", - "notifier.smtp.password", - "notifier.smtp.identifier", - "notifier.smtp.sender", - "notifier.smtp.subject", - "notifier.smtp.startup_check_address", - "notifier.smtp.disable_require_tls", - "notifier.smtp.disable_html_emails", - "notifier.smtp.tls.minimum_version", - "notifier.smtp.tls.skip_verify", - "notifier.smtp.tls.server_name", - "notifier.template_path", - - // Regulation Keys. - "regulation.max_retries", - "regulation.find_time", - "regulation.ban_time", - - // Authentication Backend Keys. - "authentication_backend.disable_reset_password", - "authentication_backend.password_reset.custom_url", - "authentication_backend.refresh_interval", - - // LDAP Authentication Backend Keys. - "authentication_backend.ldap.implementation", - "authentication_backend.ldap.url", - "authentication_backend.ldap.timeout", - "authentication_backend.ldap.base_dn", - "authentication_backend.ldap.username_attribute", - "authentication_backend.ldap.additional_users_dn", - "authentication_backend.ldap.users_filter", - "authentication_backend.ldap.additional_groups_dn", - "authentication_backend.ldap.groups_filter", - "authentication_backend.ldap.group_name_attribute", - "authentication_backend.ldap.mail_attribute", - "authentication_backend.ldap.display_name_attribute", - "authentication_backend.ldap.user", - "authentication_backend.ldap.password", - "authentication_backend.ldap.start_tls", - "authentication_backend.ldap.tls.minimum_version", - "authentication_backend.ldap.tls.skip_verify", - "authentication_backend.ldap.tls.server_name", - - // File Authentication Backend Keys. - "authentication_backend.file.path", - "authentication_backend.file.password.algorithm", - "authentication_backend.file.password.iterations", - "authentication_backend.file.password.key_length", - "authentication_backend.file.password.salt_length", - "authentication_backend.file.password.memory", - "authentication_backend.file.password.parallelism", - - // Identity Provider Keys. - "identity_providers.oidc.hmac_secret", - "identity_providers.oidc.issuer_private_key", - "identity_providers.oidc.id_token_lifespan", - "identity_providers.oidc.access_token_lifespan", - "identity_providers.oidc.refresh_token_lifespan", - "identity_providers.oidc.authorize_code_lifespan", - "identity_providers.oidc.enforce_pkce", - "identity_providers.oidc.enable_pkce_plain_challenge", - "identity_providers.oidc.enable_client_debug_messages", - "identity_providers.oidc.minimum_parameter_entropy", - "identity_providers.oidc.cors.endpoints", - "identity_providers.oidc.cors.allowed_origins", - "identity_providers.oidc.cors.allowed_origins_from_client_redirect_uris", - "identity_providers.oidc.clients", - "identity_providers.oidc.clients[].id", - "identity_providers.oidc.clients[].description", - "identity_providers.oidc.clients[].secret", - "identity_providers.oidc.clients[].sector_identifier", - "identity_providers.oidc.clients[].public", - "identity_providers.oidc.clients[].redirect_uris", - "identity_providers.oidc.clients[].authorization_policy", - "identity_providers.oidc.clients[].pre_configured_consent_duration", - "identity_providers.oidc.clients[].scopes", - "identity_providers.oidc.clients[].audience", - "identity_providers.oidc.clients[].grant_types", - "identity_providers.oidc.clients[].response_types", - "identity_providers.oidc.clients[].response_modes", - "identity_providers.oidc.clients[].userinfo_signing_algorithm", - - // NTP keys. - "ntp.address", - "ntp.version", - "ntp.max_desync", - "ntp.disable_startup_check", - "ntp.disable_failure", - - // Password Policy keys. - "password_policy.standard.enabled", - "password_policy.standard.min_length", - "password_policy.standard.max_length", - "password_policy.standard.require_uppercase", - "password_policy.standard.require_lowercase", - "password_policy.standard.require_number", - "password_policy.standard.require_special", - "password_policy.zxcvbn.enabled", - "password_policy.zxcvbn.min_score", -} - var replacedKeys = map[string]string{ "authentication_backend.ldap.skip_verify": "authentication_backend.ldap.tls.skip_verify", "authentication_backend.ldap.minimum_tls_version": "authentication_backend.ldap.tls.minimum_version", diff --git a/internal/configuration/validator/keys.go b/internal/configuration/validator/keys.go index 4291447f8..e3e5fdcf7 100644 --- a/internal/configuration/validator/keys.go +++ b/internal/configuration/validator/keys.go @@ -16,7 +16,7 @@ func ValidateKeys(keys []string, prefix string, validator *schema.StructValidato for _, key := range keys { expectedKey := reKeyReplacer.ReplaceAllString(key, "[]") - if utils.IsStringInSlice(expectedKey, ValidKeys) { + if utils.IsStringInSlice(expectedKey, schema.Keys) { continue } diff --git a/internal/configuration/validator/keys_test.go b/internal/configuration/validator/keys_test.go index 23153666b..989b4b815 100644 --- a/internal/configuration/validator/keys_test.go +++ b/internal/configuration/validator/keys_test.go @@ -12,7 +12,7 @@ import ( ) func TestShouldValidateGoodKeys(t *testing.T) { - configKeys := ValidKeys + configKeys := schema.Keys val := schema.NewStructValidator() ValidateKeys(configKeys, "AUTHELIA_", val) @@ -20,7 +20,7 @@ func TestShouldValidateGoodKeys(t *testing.T) { } func TestShouldNotValidateBadKeys(t *testing.T) { - configKeys := ValidKeys + configKeys := schema.Keys configKeys = append(configKeys, "bad_key") configKeys = append(configKeys, "totp.skewy") val := schema.NewStructValidator() @@ -34,7 +34,7 @@ func TestShouldNotValidateBadKeys(t *testing.T) { } func TestShouldNotValidateBadEnvKeys(t *testing.T) { - configKeys := ValidKeys + configKeys := schema.Keys configKeys = append(configKeys, "AUTHELIA__BAD_ENV_KEY") configKeys = append(configKeys, "AUTHELIA_BAD_ENV_KEY")