diff options
| author | James Elliott <james-d-elliott@users.noreply.github.com> | 2025-02-23 19:22:26 +1100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-02-23 08:22:26 +0000 |
| commit | 2934c16dd0895789a75313ad661920c42e1b5169 (patch) | |
| tree | da1ac150acc1f3eb91edcf9282b899e687158fbe | |
| parent | 0af038e0ced689db90da480876a0bb26d78c6fb9 (diff) | |
feat(storage): allow postgres failover (#7775)
This allows configuring failover PostgreSQL servers.
Signed-off-by: James Elliott <james-d-elliott@users.noreply.github.com>
| -rw-r--r-- | cmd/authelia-gen/cmd_code.go | 2 | ||||
| -rw-r--r-- | cmd/authelia-gen/cmd_docs_data.go | 2 | ||||
| -rw-r--r-- | cmd/authelia-gen/helpers.go | 51 | ||||
| -rw-r--r-- | cmd/authelia-gen/helpers_test.go | 4 | ||||
| -rw-r--r-- | config.template.yml | 24 | ||||
| -rw-r--r-- | docs/content/configuration/storage/postgres.md | 30 | ||||
| -rw-r--r-- | docs/data/configkeys.json | 1076 | ||||
| -rw-r--r-- | docs/static/schemas/v4.39/json-schema/configuration.json | 30 | ||||
| -rw-r--r-- | internal/configuration/config.template.yml | 24 | ||||
| -rw-r--r-- | internal/configuration/schema/keys.go | 731 | ||||
| -rw-r--r-- | internal/configuration/schema/storage.go | 35 | ||||
| -rw-r--r-- | internal/configuration/validator/const.go | 1 | ||||
| -rw-r--r-- | internal/configuration/validator/storage.go | 42 | ||||
| -rw-r--r-- | internal/configuration/validator/storage_test.go | 280 | ||||
| -rw-r--r-- | internal/storage/sql_provider_backend_mysql_test.go | 60 | ||||
| -rw-r--r-- | internal/storage/sql_provider_backend_postgres.go | 42 | ||||
| -rw-r--r-- | internal/storage/sql_provider_backend_postgres_test.go | 8 | ||||
| -rw-r--r-- | internal/storage/sql_provider_backend_sqlite_test.go | 44 |
18 files changed, 1475 insertions, 1011 deletions
diff --git a/cmd/authelia-gen/cmd_code.go b/cmd/authelia-gen/cmd_code.go index a704c2949..0d4624237 100644 --- a/cmd/authelia-gen/cmd_code.go +++ b/cmd/authelia-gen/cmd_code.go @@ -173,7 +173,7 @@ func codeKeysRunE(cmd *cobra.Command, args []string) (err error) { data := tmplConfigurationKeysData{ Timestamp: time.Now(), - Keys: readTags("", reflect.TypeOf(schema.Configuration{}), false, false), + Keys: readTags("", reflect.TypeOf(schema.Configuration{}), false, false, true), } if root, err = cmd.Flags().GetString(cmdFlagRoot); err != nil { diff --git a/cmd/authelia-gen/cmd_docs_data.go b/cmd/authelia-gen/cmd_docs_data.go index 36dd4423b..91f157a54 100644 --- a/cmd/authelia-gen/cmd_docs_data.go +++ b/cmd/authelia-gen/cmd_docs_data.go @@ -123,7 +123,7 @@ func docsKeysRunE(cmd *cobra.Command, args []string) (err error) { data []ConfigurationKey ) - keys := readTags("", reflect.TypeOf(schema.Configuration{}), true, true) + keys := readTags("", reflect.TypeOf(schema.Configuration{}), true, true, true) for _, key := range keys { if strings.HasSuffix(key, ".*") { diff --git a/cmd/authelia-gen/helpers.go b/cmd/authelia-gen/helpers.go index 0a9894da4..814a35c7e 100644 --- a/cmd/authelia-gen/helpers.go +++ b/cmd/authelia-gen/helpers.go @@ -12,6 +12,7 @@ import ( "path/filepath" "reflect" "regexp" + "sort" "strings" "time" @@ -116,8 +117,20 @@ func readVersion(cmd *cobra.Command) (version *model.SemanticVersion, err error) return model.NewSemanticVersion(packageJSON.Version) } +func readTags(prefix string, t reflect.Type, envSkip, deprecatedSkip, doSort bool) (tags []string) { + tags = iReadTags(prefix, t, envSkip, deprecatedSkip, false) + + tags = removeDuplicate(tags) + + if doSort { + sort.Strings(tags) + } + + return tags +} + //nolint:gocyclo -func readTags(prefix string, t reflect.Type, envSkip, deprecatedSkip bool) (tags []string) { +func iReadTags(prefix string, t reflect.Type, envSkip, deprecatedSkip, parentSlice bool) (tags []string) { tags = make([]string, 0) if envSkip && (t.Kind() == reflect.Slice || t.Kind() == reflect.Map) { @@ -126,7 +139,7 @@ func readTags(prefix string, t reflect.Type, envSkip, deprecatedSkip bool) (tags if t.Kind() != reflect.Struct { if t.Kind() == reflect.Slice { - tags = append(tags, readTags(getKeyNameFromTagAndPrefix(prefix, "", true, false), t.Elem(), envSkip, deprecatedSkip)...) + tags = append(tags, iReadTags(getKeyNameFromTagAndPrefix(prefix, "", true, false), t.Elem(), envSkip, deprecatedSkip, true)...) } return @@ -150,7 +163,11 @@ func readTags(prefix string, t reflect.Type, envSkip, deprecatedSkip bool) (tags switch kind := field.Type.Kind(); kind { case reflect.Struct: if !containsType(field.Type, decodedTypes) { - tags = append(tags, readTags(getKeyNameFromTagAndPrefix(prefix, tag, false, false), field.Type, envSkip, deprecatedSkip)...) + if parentSlice { + tags = append(tags, getKeyNameFromTagAndPrefix(prefix, tag, false, false)) + } + + tags = append(tags, iReadTags(getKeyNameFromTagAndPrefix(prefix, tag, false, false), field.Type, envSkip, deprecatedSkip, false)...) continue } @@ -169,7 +186,7 @@ func readTags(prefix string, t reflect.Type, envSkip, deprecatedSkip bool) (tags case reflect.Struct: if !containsType(field.Type.Elem(), decodedTypes) { tags = append(tags, getKeyNameFromTagAndPrefix(prefix, tag, false, false)) - tags = append(tags, readTags(getKeyNameFromTagAndPrefix(prefix, tag, kind == reflect.Slice, kind == reflect.Map), field.Type.Elem(), envSkip, deprecatedSkip)...) + tags = append(tags, iReadTags(getKeyNameFromTagAndPrefix(prefix, tag, kind == reflect.Slice, kind == reflect.Map), field.Type.Elem(), envSkip, deprecatedSkip, kind == reflect.Slice)...) continue } @@ -178,13 +195,17 @@ func readTags(prefix string, t reflect.Type, envSkip, deprecatedSkip bool) (tags tags = append(tags, getKeyNameFromTagAndPrefix(prefix, tag, true, true)) } - tags = append(tags, readTags(getKeyNameFromTagAndPrefix(prefix, tag, kind == reflect.Slice, kind == reflect.Map), field.Type.Elem(), envSkip, deprecatedSkip)...) + tags = append(tags, iReadTags(getKeyNameFromTagAndPrefix(prefix, tag, kind == reflect.Slice, kind == reflect.Map), field.Type.Elem(), envSkip, deprecatedSkip, true)...) } 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, false), field.Type.Elem(), envSkip, deprecatedSkip)...) + if parentSlice { + tags = append(tags, getKeyNameFromTagAndPrefix(prefix, tag, false, false)) + } + + tags = append(tags, iReadTags(getKeyNameFromTagAndPrefix(prefix, tag, false, false), field.Type.Elem(), envSkip, deprecatedSkip, false)...) continue } @@ -197,7 +218,7 @@ func readTags(prefix string, t reflect.Type, envSkip, deprecatedSkip bool) (tags if k == reflect.Struct { if !containsType(field.Type.Elem(), decodedTypes) { - tags = append(tags, readTags(getKeyNameFromTagAndPrefix(prefix, tag, true, false), field.Type.Elem(), envSkip, deprecatedSkip)...) + tags = append(tags, iReadTags(getKeyNameFromTagAndPrefix(prefix, tag, true, false), field.Type.Elem(), envSkip, deprecatedSkip, field.Type.Elem().Kind() == reflect.Slice)...) continue } @@ -309,3 +330,19 @@ func readCompose(path string) (compose *Compose, err error) { return compose, nil } + +func removeDuplicate[T comparable](sliceList []T) []T { + var list []T + + allKeys := make(map[T]bool) + + for _, item := range sliceList { + if _, value := allKeys[item]; !value { + allKeys[item] = true + + list = append(list, item) + } + } + + return list +} diff --git a/cmd/authelia-gen/helpers_test.go b/cmd/authelia-gen/helpers_test.go index a161e5d8e..a6aac0ccc 100644 --- a/cmd/authelia-gen/helpers_test.go +++ b/cmd/authelia-gen/helpers_test.go @@ -136,10 +136,10 @@ func TestContainsType(t *testing.T) { func TestReadTags(t *testing.T) { assert.NotPanics(t, func() { - readTags("", reflect.TypeOf(schema.Configuration{}), false, false) + iReadTags("", reflect.TypeOf(schema.Configuration{}), false, false, false) }) assert.NotPanics(t, func() { - readTags("", reflect.TypeOf(schema.Configuration{}), true, true) + iReadTags("", reflect.TypeOf(schema.Configuration{}), true, true, false) }) } diff --git a/config.template.yml b/config.template.yml index 2e01b7a15..0fd1756e9 100644 --- a/config.template.yml +++ b/config.template.yml @@ -1076,6 +1076,30 @@ session: ## The default scheme is 'unix' if the address is an absolute path otherwise it's 'tcp'. The default port is '5432'. # address: 'tcp://127.0.0.1:5432' + ## List of additional server instance configurations to fallback to when the primary instance is not available. + # servers: + # - + ## The Address of this individual instance. + # address: 'tcp://127.0.0.1:5432' + + ## The TLS configuration for this individual instance. + # tls: + # server_name: 'postgres.example.com' + # skip_verify: false + # minimum_version: 'TLS1.2' + # maximum_version: 'TLS1.3' + # certificate_chain: | + # -----BEGIN CERTIFICATE----- + # ... + # -----END CERTIFICATE----- + # -----BEGIN CERTIFICATE----- + # ... + # -----END CERTIFICATE----- + # private_key: | + # -----BEGIN RSA PRIVATE KEY----- + # ... + # -----END RSA PRIVATE KEY----- + ## The database name to use. # database: 'authelia' diff --git a/docs/content/configuration/storage/postgres.md b/docs/content/configuration/storage/postgres.md index 2e04f3aeb..78bdeda34 100644 --- a/docs/content/configuration/storage/postgres.md +++ b/docs/content/configuration/storage/postgres.md @@ -36,6 +36,7 @@ storage: encryption_key: 'a_very_important_secret' postgres: address: 'tcp://127.0.0.1:5432' + servers: [] database: 'authelia' schema: 'public' username: 'authelia' @@ -94,6 +95,35 @@ storage: address: 'unix:///var/run/postgres.sock' ``` +### servers + +{{< confkey type="list(object)" required="no" >}} + +This specifies a list of additional fallback [PostgreSQL] instances to use should issues occur with the primary instance +which is configured with the [address](#address) and [tls](#tls) options. + +Each server instance has the [address](#address) and [tls](#tls) option which both have the same requirements and +effect, and have the same configuration syntax. This means all other settings including but not limited to +[database](#database), [schema](#schema), [username](#username), and [password](#password); must be the same as the +primary instance, and they must be fully replicated. + +Example configuration: + +```yaml +storage: + postgres: + address: 'tcp://postgres1:5432' + tls: + server_name: 'postgres1.local' + servers: + - address: 'tcp://postgres2:5432' + tls: + server_name: 'postgres2.local' + - address: 'tcp://postgres3:5432' + tls: + server_name: 'postgres3.local' +``` + ### database {{< confkey type="string" required="yes" >}} diff --git a/docs/data/configkeys.json b/docs/data/configkeys.json index a93d83920..19ff5d878 100644 --- a/docs/data/configkeys.json +++ b/docs/data/configkeys.json @@ -1,308 +1,308 @@ [ { - "path": "theme", + "path": "access_control.default_policy", "secret": false, - "env": "AUTHELIA_THEME" + "env": "AUTHELIA_ACCESS_CONTROL_DEFAULT_POLICY" }, { - "path": "certificates_directory", + "path": "authentication_backend.file.password.algorithm", "secret": false, - "env": "AUTHELIA_CERTIFICATES_DIRECTORY" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ALGORITHM" }, { - "path": "default_2fa_method", + "path": "authentication_backend.file.password.argon2.iterations", "secret": false, - "env": "AUTHELIA_DEFAULT_2FA_METHOD" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_ITERATIONS" }, { - "path": "log.level", + "path": "authentication_backend.file.password.argon2.key_length", "secret": false, - "env": "AUTHELIA_LOG_LEVEL" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_KEY_LENGTH" }, { - "path": "log.format", + "path": "authentication_backend.file.password.argon2.memory", "secret": false, - "env": "AUTHELIA_LOG_FORMAT" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_MEMORY" }, { - "path": "log.file_path", + "path": "authentication_backend.file.password.argon2.parallelism", "secret": false, - "env": "AUTHELIA_LOG_FILE_PATH" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_PARALLELISM" }, { - "path": "log.keep_stdout", + "path": "authentication_backend.file.password.argon2.salt_length", "secret": false, - "env": "AUTHELIA_LOG_KEEP_STDOUT" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_SALT_LENGTH" }, { - "path": "identity_providers.oidc.hmac_secret", - "secret": true, - "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_HMAC_SECRET_FILE" + "path": "authentication_backend.file.password.argon2.variant", + "secret": false, + "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_VARIANT" }, { - "path": "identity_providers.oidc.enable_client_debug_messages", + "path": "authentication_backend.file.password.bcrypt.cost", "secret": false, - "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_ENABLE_CLIENT_DEBUG_MESSAGES" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_BCRYPT_COST" }, { - "path": "identity_providers.oidc.minimum_parameter_entropy", + "path": "authentication_backend.file.password.bcrypt.variant", "secret": false, - "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_MINIMUM_PARAMETER_ENTROPY" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_BCRYPT_VARIANT" }, { - "path": "identity_providers.oidc.enforce_pkce", + "path": "authentication_backend.file.password.pbkdf2.iterations", "secret": false, - "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_ENFORCE_PKCE" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_PBKDF2_ITERATIONS" }, { - "path": "identity_providers.oidc.enable_pkce_plain_challenge", + "path": "authentication_backend.file.password.pbkdf2.salt_length", "secret": false, - "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_ENABLE_PKCE_PLAIN_CHALLENGE" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_PBKDF2_SALT_LENGTH" }, { - "path": "identity_providers.oidc.enable_jwt_access_token_stateless_introspection", + "path": "authentication_backend.file.password.pbkdf2.variant", "secret": false, - "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_ENABLE_JWT_ACCESS_TOKEN_STATELESS_INTROSPECTION" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_PBKDF2_VARIANT" }, { - "path": "identity_providers.oidc.discovery_signed_response_alg", + "path": "authentication_backend.file.password.scrypt.block_size", "secret": false, - "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_DISCOVERY_SIGNED_RESPONSE_ALG" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SCRYPT_BLOCK_SIZE" }, { - "path": "identity_providers.oidc.discovery_signed_response_key_id", + "path": "authentication_backend.file.password.scrypt.iterations", "secret": false, - "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_DISCOVERY_SIGNED_RESPONSE_KEY_ID" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SCRYPT_ITERATIONS" }, { - "path": "identity_providers.oidc.require_pushed_authorization_requests", + "path": "authentication_backend.file.password.scrypt.key_length", "secret": false, - "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_REQUIRE_PUSHED_AUTHORIZATION_REQUESTS" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SCRYPT_KEY_LENGTH" }, { - "path": "identity_providers.oidc.cors.endpoints", + "path": "authentication_backend.file.password.scrypt.parallelism", "secret": false, - "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_CORS_ENDPOINTS" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SCRYPT_PARALLELISM" }, { - "path": "identity_providers.oidc.cors.allowed_origins_from_client_redirect_uris", + "path": "authentication_backend.file.password.scrypt.salt_length", "secret": false, - "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_CORS_ALLOWED_ORIGINS_FROM_CLIENT_REDIRECT_URIS" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SCRYPT_SALT_LENGTH" }, { - "path": "identity_providers.oidc.lifespans.access_token", + "path": "authentication_backend.file.password.sha2crypt.iterations", "secret": false, - "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_LIFESPANS_ACCESS_TOKEN" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SHA2CRYPT_ITERATIONS" }, { - "path": "identity_providers.oidc.lifespans.authorize_code", + "path": "authentication_backend.file.password.sha2crypt.salt_length", "secret": false, - "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_LIFESPANS_AUTHORIZE_CODE" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SHA2CRYPT_SALT_LENGTH" }, { - "path": "identity_providers.oidc.lifespans.id_token", + "path": "authentication_backend.file.password.sha2crypt.variant", "secret": false, - "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_LIFESPANS_ID_TOKEN" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SHA2CRYPT_VARIANT" }, { - "path": "identity_providers.oidc.lifespans.refresh_token", + "path": "authentication_backend.file.path", "secret": false, - "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_LIFESPANS_REFRESH_TOKEN" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PATH" }, { - "path": "identity_providers.oidc.lifespans.jwt_secured_authorization", + "path": "authentication_backend.file.search.case_insensitive", "secret": false, - "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_LIFESPANS_JWT_SECURED_AUTHORIZATION" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_SEARCH_CASE_INSENSITIVE" }, { - "path": "identity_providers.oidc", + "path": "authentication_backend.file.search.email", "secret": false, - "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_SEARCH_EMAIL" }, { - "path": "authentication_backend.password_reset.disable", + "path": "authentication_backend.file.watch", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_PASSWORD_RESET_DISABLE" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_WATCH" }, { - "path": "authentication_backend.password_reset.custom_url", + "path": "authentication_backend.ldap.additional_groups_dn", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_PASSWORD_RESET_CUSTOM_URL" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ADDITIONAL_GROUPS_DN" }, { - "path": "authentication_backend.refresh_interval", + "path": "authentication_backend.ldap.additional_users_dn", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_REFRESH_INTERVAL" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ADDITIONAL_USERS_DN" }, { - "path": "authentication_backend.file.path", + "path": "authentication_backend.ldap.address", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PATH" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ADDRESS" }, { - "path": "authentication_backend.file.watch", + "path": "authentication_backend.ldap.attributes.birthdate", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_WATCH" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_BIRTHDATE" }, { - "path": "authentication_backend.file.password.algorithm", + "path": "authentication_backend.ldap.attributes.country", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ALGORITHM" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_COUNTRY" }, { - "path": "authentication_backend.file.password.argon2.variant", + "path": "authentication_backend.ldap.attributes.display_name", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_VARIANT" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_DISPLAY_NAME" }, { - "path": "authentication_backend.file.password.argon2.iterations", + "path": "authentication_backend.ldap.attributes.distinguished_name", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_ITERATIONS" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_DISTINGUISHED_NAME" }, { - "path": "authentication_backend.file.password.argon2.memory", + "path": "authentication_backend.ldap.attributes.family_name", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_MEMORY" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_FAMILY_NAME" }, { - "path": "authentication_backend.file.password.argon2.parallelism", + "path": "authentication_backend.ldap.attributes.gender", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_PARALLELISM" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_GENDER" }, { - "path": "authentication_backend.file.password.argon2.key_length", + "path": "authentication_backend.ldap.attributes.given_name", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_KEY_LENGTH" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_GIVEN_NAME" }, { - "path": "authentication_backend.file.password.argon2.salt_length", + "path": "authentication_backend.ldap.attributes.group_name", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_SALT_LENGTH" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_GROUP_NAME" }, { - "path": "authentication_backend.file.password.sha2crypt.variant", + "path": "authentication_backend.ldap.attributes.locale", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SHA2CRYPT_VARIANT" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_LOCALE" }, { - "path": "authentication_backend.file.password.sha2crypt.iterations", + "path": "authentication_backend.ldap.attributes.locality", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SHA2CRYPT_ITERATIONS" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_LOCALITY" }, { - "path": "authentication_backend.file.password.sha2crypt.salt_length", + "path": "authentication_backend.ldap.attributes.mail", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SHA2CRYPT_SALT_LENGTH" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_MAIL" }, { - "path": "authentication_backend.file.password.pbkdf2.variant", + "path": "authentication_backend.ldap.attributes.member_of", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_PBKDF2_VARIANT" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_MEMBER_OF" }, { - "path": "authentication_backend.file.password.pbkdf2.iterations", + "path": "authentication_backend.ldap.attributes.middle_name", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_PBKDF2_ITERATIONS" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_MIDDLE_NAME" }, { - "path": "authentication_backend.file.password.pbkdf2.salt_length", + "path": "authentication_backend.ldap.attributes.nickname", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_PBKDF2_SALT_LENGTH" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_NICKNAME" }, { - "path": "authentication_backend.file.password.bcrypt.variant", + "path": "authentication_backend.ldap.attributes.phone_extension", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_BCRYPT_VARIANT" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_PHONE_EXTENSION" }, { - "path": "authentication_backend.file.password.bcrypt.cost", + "path": "authentication_backend.ldap.attributes.phone_number", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_BCRYPT_COST" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_PHONE_NUMBER" }, { - "path": "authentication_backend.file.password.scrypt.iterations", + "path": "authentication_backend.ldap.attributes.picture", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SCRYPT_ITERATIONS" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_PICTURE" }, { - "path": "authentication_backend.file.password.scrypt.block_size", + "path": "authentication_backend.ldap.attributes.postal_code", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SCRYPT_BLOCK_SIZE" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_POSTAL_CODE" }, { - "path": "authentication_backend.file.password.scrypt.parallelism", + "path": "authentication_backend.ldap.attributes.profile", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SCRYPT_PARALLELISM" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_PROFILE" }, { - "path": "authentication_backend.file.password.scrypt.key_length", + "path": "authentication_backend.ldap.attributes.region", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SCRYPT_KEY_LENGTH" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_REGION" }, { - "path": "authentication_backend.file.password.scrypt.salt_length", + "path": "authentication_backend.ldap.attributes.street_address", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SCRYPT_SALT_LENGTH" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_STREET_ADDRESS" }, { - "path": "authentication_backend.file.search.email", + "path": "authentication_backend.ldap.attributes.username", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_SEARCH_EMAIL" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_USERNAME" }, { - "path": "authentication_backend.file.search.case_insensitive", + "path": "authentication_backend.ldap.attributes.website", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_FILE_SEARCH_CASE_INSENSITIVE" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_WEBSITE" }, { - "path": "authentication_backend.ldap.address", + "path": "authentication_backend.ldap.attributes.zoneinfo", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ADDRESS" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_ZONEINFO" }, { - "path": "authentication_backend.ldap.implementation", + "path": "authentication_backend.ldap.base_dn", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_IMPLEMENTATION" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_BASE_DN" }, { - "path": "authentication_backend.ldap.timeout", + "path": "authentication_backend.ldap.group_search_mode", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TIMEOUT" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_GROUP_SEARCH_MODE" }, { - "path": "authentication_backend.ldap.start_tls", + "path": "authentication_backend.ldap.groups_filter", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_START_TLS" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_GROUPS_FILTER" }, { - "path": "authentication_backend.ldap.tls.minimum_version", + "path": "authentication_backend.ldap.implementation", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_MINIMUM_VERSION" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_IMPLEMENTATION" }, { - "path": "authentication_backend.ldap.tls.maximum_version", - "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_MAXIMUM_VERSION" + "path": "authentication_backend.ldap.password", + "secret": true, + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE" }, { - "path": "authentication_backend.ldap.tls.skip_verify", + "path": "authentication_backend.ldap.permit_feature_detection_failure", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_SKIP_VERIFY" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PERMIT_FEATURE_DETECTION_FAILURE" }, { - "path": "authentication_backend.ldap.tls.server_name", + "path": "authentication_backend.ldap.permit_referrals", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_SERVER_NAME" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PERMIT_REFERRALS" }, { - "path": "authentication_backend.ldap.tls.private_key", - "secret": true, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_PRIVATE_KEY_FILE" + "path": "authentication_backend.ldap.permit_unauthenticated_bind", + "secret": false, + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PERMIT_UNAUTHENTICATED_BIND" }, { - "path": "authentication_backend.ldap.tls.certificate_chain", - "secret": true, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_CERTIFICATE_CHAIN_FILE" + "path": "authentication_backend.ldap.pooling.count", + "secret": false, + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_POOLING_COUNT" }, { "path": "authentication_backend.ldap.pooling.enable", @@ -310,11 +310,6 @@ "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_POOLING_ENABLE" }, { - "path": "authentication_backend.ldap.pooling.count", - "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_POOLING_COUNT" - }, - { "path": "authentication_backend.ldap.pooling.retries", "secret": false, "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_POOLING_RETRIES" @@ -325,434 +320,434 @@ "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_POOLING_TIMEOUT" }, { - "path": "authentication_backend.ldap.base_dn", + "path": "authentication_backend.ldap.start_tls", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_BASE_DN" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_START_TLS" }, { - "path": "authentication_backend.ldap.additional_users_dn", + "path": "authentication_backend.ldap.timeout", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ADDITIONAL_USERS_DN" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TIMEOUT" }, { - "path": "authentication_backend.ldap.users_filter", - "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_USERS_FILTER" + "path": "authentication_backend.ldap.tls.certificate_chain", + "secret": true, + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_CERTIFICATE_CHAIN_FILE" }, { - "path": "authentication_backend.ldap.additional_groups_dn", + "path": "authentication_backend.ldap.tls.maximum_version", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ADDITIONAL_GROUPS_DN" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_MAXIMUM_VERSION" }, { - "path": "authentication_backend.ldap.groups_filter", + "path": "authentication_backend.ldap.tls.minimum_version", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_GROUPS_FILTER" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_MINIMUM_VERSION" }, { - "path": "authentication_backend.ldap.group_search_mode", - "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_GROUP_SEARCH_MODE" + "path": "authentication_backend.ldap.tls.private_key", + "secret": true, + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_PRIVATE_KEY_FILE" }, { - "path": "authentication_backend.ldap.attributes.distinguished_name", + "path": "authentication_backend.ldap.tls.server_name", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_DISTINGUISHED_NAME" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_SERVER_NAME" }, { - "path": "authentication_backend.ldap.attributes.username", + "path": "authentication_backend.ldap.tls.skip_verify", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_USERNAME" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_SKIP_VERIFY" }, { - "path": "authentication_backend.ldap.attributes.display_name", + "path": "authentication_backend.ldap.user", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_DISPLAY_NAME" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_USER" }, { - "path": "authentication_backend.ldap.attributes.family_name", + "path": "authentication_backend.ldap.users_filter", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_FAMILY_NAME" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_USERS_FILTER" }, { - "path": "authentication_backend.ldap.attributes.given_name", + "path": "authentication_backend.password_reset.custom_url", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_GIVEN_NAME" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_PASSWORD_RESET_CUSTOM_URL" }, { - "path": "authentication_backend.ldap.attributes.middle_name", + "path": "authentication_backend.password_reset.disable", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_MIDDLE_NAME" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_PASSWORD_RESET_DISABLE" }, { - "path": "authentication_backend.ldap.attributes.nickname", + "path": "authentication_backend.refresh_interval", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_NICKNAME" + "env": "AUTHELIA_AUTHENTICATION_BACKEND_REFRESH_INTERVAL" }, { - "path": "authentication_backend.ldap.attributes.gender", + "path": "certificates_directory", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_GENDER" + "env": "AUTHELIA_CERTIFICATES_DIRECTORY" }, { - "path": "authentication_backend.ldap.attributes.birthdate", + "path": "default_2fa_method", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_BIRTHDATE" + "env": "AUTHELIA_DEFAULT_2FA_METHOD" }, { - "path": "authentication_backend.ldap.attributes.website", + "path": "duo_api.disable", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_WEBSITE" + "env": "AUTHELIA_DUO_API_DISABLE" }, { - "path": "authentication_backend.ldap.attributes.profile", + "path": "duo_api.enable_self_enrollment", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_PROFILE" + "env": "AUTHELIA_DUO_API_ENABLE_SELF_ENROLLMENT" }, { - "path": "authentication_backend.ldap.attributes.picture", + "path": "duo_api.hostname", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_PICTURE" + "env": "AUTHELIA_DUO_API_HOSTNAME" }, { - "path": "authentication_backend.ldap.attributes.zoneinfo", - "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_ZONEINFO" + "path": "duo_api.integration_key", + "secret": true, + "env": "AUTHELIA_DUO_API_INTEGRATION_KEY_FILE" }, { - "path": "authentication_backend.ldap.attributes.locale", - "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_LOCALE" + "path": "duo_api.secret_key", + "secret": true, + "env": "AUTHELIA_DUO_API_SECRET_KEY_FILE" }, { - "path": "authentication_backend.ldap.attributes.phone_number", + "path": "identity_providers.oidc", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_PHONE_NUMBER" + "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC" }, { - "path": "authentication_backend.ldap.attributes.phone_extension", + "path": "identity_providers.oidc.cors.allowed_origins_from_client_redirect_uris", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_PHONE_EXTENSION" + "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_CORS_ALLOWED_ORIGINS_FROM_CLIENT_REDIRECT_URIS" }, { - "path": "authentication_backend.ldap.attributes.street_address", + "path": "identity_providers.oidc.cors.endpoints", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_STREET_ADDRESS" + "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_CORS_ENDPOINTS" }, { - "path": "authentication_backend.ldap.attributes.locality", + "path": "identity_providers.oidc.discovery_signed_response_alg", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_LOCALITY" + "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_DISCOVERY_SIGNED_RESPONSE_ALG" }, { - "path": "authentication_backend.ldap.attributes.region", + "path": "identity_providers.oidc.discovery_signed_response_key_id", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_REGION" + "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_DISCOVERY_SIGNED_RESPONSE_KEY_ID" }, { - "path": "authentication_backend.ldap.attributes.postal_code", + "path": "identity_providers.oidc.enable_client_debug_messages", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_POSTAL_CODE" + "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_ENABLE_CLIENT_DEBUG_MESSAGES" }, { - "path": "authentication_backend.ldap.attributes.country", + "path": "identity_providers.oidc.enable_jwt_access_token_stateless_introspection", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_COUNTRY" + "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_ENABLE_JWT_ACCESS_TOKEN_STATELESS_INTROSPECTION" }, { - "path": "authentication_backend.ldap.attributes.mail", + "path": "identity_providers.oidc.enable_pkce_plain_challenge", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_MAIL" + "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_ENABLE_PKCE_PLAIN_CHALLENGE" }, { - "path": "authentication_backend.ldap.attributes.member_of", + "path": "identity_providers.oidc.enforce_pkce", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_MEMBER_OF" + "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_ENFORCE_PKCE" }, { - "path": "authentication_backend.ldap.attributes.group_name", - "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ATTRIBUTES_GROUP_NAME" + "path": "identity_providers.oidc.hmac_secret", + "secret": true, + "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_HMAC_SECRET_FILE" }, { - "path": "authentication_backend.ldap.permit_referrals", + "path": "identity_providers.oidc.lifespans.access_token", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PERMIT_REFERRALS" + "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_LIFESPANS_ACCESS_TOKEN" }, { - "path": "authentication_backend.ldap.permit_unauthenticated_bind", + "path": "identity_providers.oidc.lifespans.authorize_code", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PERMIT_UNAUTHENTICATED_BIND" + "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_LIFESPANS_AUTHORIZE_CODE" }, { - "path": "authentication_backend.ldap.permit_feature_detection_failure", + "path": "identity_providers.oidc.lifespans.id_token", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PERMIT_FEATURE_DETECTION_FAILURE" + "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_LIFESPANS_ID_TOKEN" }, { - "path": "authentication_backend.ldap.user", + "path": "identity_providers.oidc.lifespans.jwt_secured_authorization", "secret": false, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_USER" + "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_LIFESPANS_JWT_SECURED_AUTHORIZATION" }, { - "path": "authentication_backend.ldap.password", - "secret": true, - "env": "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE" + "path": "identity_providers.oidc.lifespans.refresh_token", + "secret": false, + "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_LIFESPANS_REFRESH_TOKEN" }, { - "path": "session.name", + "path": "identity_providers.oidc.minimum_parameter_entropy", "secret": false, - "env": "AUTHELIA_SESSION_NAME" + "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_MINIMUM_PARAMETER_ENTROPY" }, { - "path": "session.same_site", + "path": "identity_providers.oidc.require_pushed_authorization_requests", "secret": false, - "env": "AUTHELIA_SESSION_SAME_SITE" + "env": "AUTHELIA_IDENTITY_PROVIDERS_OIDC_REQUIRE_PUSHED_AUTHORIZATION_REQUESTS" }, { - "path": "session.expiration", + "path": "identity_validation.elevated_session.characters", "secret": false, - "env": "AUTHELIA_SESSION_EXPIRATION" + "env": "AUTHELIA_IDENTITY_VALIDATION_ELEVATED_SESSION_CHARACTERS" }, { - "path": "session.inactivity", + "path": "identity_validation.elevated_session.code_lifespan", "secret": false, - "env": "AUTHELIA_SESSION_INACTIVITY" + "env": "AUTHELIA_IDENTITY_VALIDATION_ELEVATED_SESSION_CODE_LIFESPAN" }, { - "path": "session.remember_me", + "path": "identity_validation.elevated_session.elevation_lifespan", "secret": false, - "env": "AUTHELIA_SESSION_REMEMBER_ME" + "env": "AUTHELIA_IDENTITY_VALIDATION_ELEVATED_SESSION_ELEVATION_LIFESPAN" }, { - "path": "session", + "path": "identity_validation.elevated_session.require_second_factor", "secret": false, - "env": "AUTHELIA_SESSION" + "env": "AUTHELIA_IDENTITY_VALIDATION_ELEVATED_SESSION_REQUIRE_SECOND_FACTOR" }, { - "path": "session.secret", - "secret": true, - "env": "AUTHELIA_SESSION_SECRET_FILE" + "path": "identity_validation.elevated_session.skip_second_factor", + "secret": false, + "env": "AUTHELIA_IDENTITY_VALIDATION_ELEVATED_SESSION_SKIP_SECOND_FACTOR" }, { - "path": "session.redis.host", + "path": "identity_validation.reset_password.jwt_algorithm", "secret": false, - "env": "AUTHELIA_SESSION_REDIS_HOST" + "env": "AUTHELIA_IDENTITY_VALIDATION_RESET_PASSWORD_JWT_ALGORITHM" }, { - "path": "session.redis.port", + "path": "identity_validation.reset_password.jwt_lifespan", "secret": false, - "env": "AUTHELIA_SESSION_REDIS_PORT" + "env": "AUTHELIA_IDENTITY_VALIDATION_RESET_PASSWORD_JWT_LIFESPAN" }, { - "path": "session.redis.timeout", - "secret": false, - "env": "AUTHELIA_SESSION_REDIS_TIMEOUT" + "path": "identity_validation.reset_password.jwt_secret", + "secret": true, + "env": "AUTHELIA_IDENTITY_VALIDATION_RESET_PASSWORD_JWT_SECRET_FILE" }, { - "path": "session.redis.max_retries", + "path": "log.file_path", "secret": false, - "env": "AUTHELIA_SESSION_REDIS_MAX_RETRIES" + "env": "AUTHELIA_LOG_FILE_PATH" }, { - "path": "session.redis.username", + "path": "log.format", "secret": false, - "env": "AUTHELIA_SESSION_REDIS_USERNAME" + "env": "AUTHELIA_LOG_FORMAT" }, { - "path": "session.redis.password", - "secret": true, - "env": "AUTHELIA_SESSION_REDIS_PASSWORD_FILE" + "path": "log.keep_stdout", + "secret": false, + "env": "AUTHELIA_LOG_KEEP_STDOUT" }, { - "path": "session.redis.database_index", + "path": "log.level", "secret": false, - "env": "AUTHELIA_SESSION_REDIS_DATABASE_INDEX" + "env": "AUTHELIA_LOG_LEVEL" }, { - "path": "session.redis.maximum_active_connections", + "path": "notifier.disable_startup_check", "secret": false, - "env": "AUTHELIA_SESSION_REDIS_MAXIMUM_ACTIVE_CONNECTIONS" + "env": "AUTHELIA_NOTIFIER_DISABLE_STARTUP_CHECK" }, { - "path": "session.redis.minimum_idle_connections", + "path": "notifier.filesystem.filename", "secret": false, - "env": "AUTHELIA_SESSION_REDIS_MINIMUM_IDLE_CONNECTIONS" + "env": "AUTHELIA_NOTIFIER_FILESYSTEM_FILENAME" }, { - "path": "session.redis.tls.minimum_version", + "path": "notifier.smtp.address", "secret": false, - "env": "AUTHELIA_SESSION_REDIS_TLS_MINIMUM_VERSION" + "env": "AUTHELIA_NOTIFIER_SMTP_ADDRESS" }, { - "path": "session.redis.tls.maximum_version", + "path": "notifier.smtp.disable_html_emails", "secret": false, - "env": "AUTHELIA_SESSION_REDIS_TLS_MAXIMUM_VERSION" + "env": "AUTHELIA_NOTIFIER_SMTP_DISABLE_HTML_EMAILS" }, { - "path": "session.redis.tls.skip_verify", + "path": "notifier.smtp.disable_require_tls", "secret": false, - "env": "AUTHELIA_SESSION_REDIS_TLS_SKIP_VERIFY" + "env": "AUTHELIA_NOTIFIER_SMTP_DISABLE_REQUIRE_TLS" }, { - "path": "session.redis.tls.server_name", + "path": "notifier.smtp.disable_starttls", "secret": false, - "env": "AUTHELIA_SESSION_REDIS_TLS_SERVER_NAME" + "env": "AUTHELIA_NOTIFIER_SMTP_DISABLE_STARTTLS" }, { - "path": "session.redis.tls.private_key", - "secret": true, - "env": "AUTHELIA_SESSION_REDIS_TLS_PRIVATE_KEY_FILE" + "path": "notifier.smtp.identifier", + "secret": false, + "env": "AUTHELIA_NOTIFIER_SMTP_IDENTIFIER" }, { - "path": "session.redis.tls.certificate_chain", + "path": "notifier.smtp.password", "secret": true, - "env": "AUTHELIA_SESSION_REDIS_TLS_CERTIFICATE_CHAIN_FILE" + "env": "AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE" }, { - "path": "session.redis.high_availability.sentinel_name", + "path": "notifier.smtp.sender", "secret": false, - "env": "AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_SENTINEL_NAME" + "env": "AUTHELIA_NOTIFIER_SMTP_SENDER" }, { - "path": "session.redis.high_availability.sentinel_username", + "path": "notifier.smtp.startup_check_address", "secret": false, - "env": "AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_SENTINEL_USERNAME" + "env": "AUTHELIA_NOTIFIER_SMTP_STARTUP_CHECK_ADDRESS" }, { - "path": "session.redis.high_availability.sentinel_password", - "secret": true, - "env": "AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_SENTINEL_PASSWORD_FILE" + "path": "notifier.smtp.subject", + "secret": false, + "env": "AUTHELIA_NOTIFIER_SMTP_SUBJECT" }, { - "path": "session.redis.high_availability.route_by_latency", + "path": "notifier.smtp.timeout", "secret": false, - "env": "AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_ROUTE_BY_LATENCY" + "env": "AUTHELIA_NOTIFIER_SMTP_TIMEOUT" }, { - "path": "session.redis.high_availability.route_randomly", - "secret": false, - "env": "AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_ROUTE_RANDOMLY" + "path": "notifier.smtp.tls.certificate_chain", + "secret": true, + "env": "AUTHELIA_NOTIFIER_SMTP_TLS_CERTIFICATE_CHAIN_FILE" }, { - "path": "totp.disable", + "path": "notifier.smtp.tls.maximum_version", "secret": false, - "env": "AUTHELIA_TOTP_DISABLE" + "env": "AUTHELIA_NOTIFIER_SMTP_TLS_MAXIMUM_VERSION" }, { - "path": "totp.issuer", + "path": "notifier.smtp.tls.minimum_version", "secret": false, - "env": "AUTHELIA_TOTP_ISSUER" + "env": "AUTHELIA_NOTIFIER_SMTP_TLS_MINIMUM_VERSION" }, { - "path": "totp.algorithm", - "secret": false, - "env": "AUTHELIA_TOTP_ALGORITHM" + "path": "notifier.smtp.tls.private_key", + "secret": true, + "env": "AUTHELIA_NOTIFIER_SMTP_TLS_PRIVATE_KEY_FILE" }, { - "path": "totp.digits", + "path": "notifier.smtp.tls.server_name", "secret": false, - "env": "AUTHELIA_TOTP_DIGITS" + "env": "AUTHELIA_NOTIFIER_SMTP_TLS_SERVER_NAME" }, { - "path": "totp.period", + "path": "notifier.smtp.tls.skip_verify", "secret": false, - "env": "AUTHELIA_TOTP_PERIOD" + "env": "AUTHELIA_NOTIFIER_SMTP_TLS_SKIP_VERIFY" }, { - "path": "totp.skew", + "path": "notifier.smtp.username", "secret": false, - "env": "AUTHELIA_TOTP_SKEW" + "env": "AUTHELIA_NOTIFIER_SMTP_USERNAME" }, { - "path": "totp.secret_size", + "path": "notifier.template_path", "secret": false, - "env": "AUTHELIA_TOTP_SECRET_SIZE" + "env": "AUTHELIA_NOTIFIER_TEMPLATE_PATH" }, { - "path": "totp.allowed_algorithms", + "path": "ntp.address", "secret": false, - "env": "AUTHELIA_TOTP_ALLOWED_ALGORITHMS" + "env": "AUTHELIA_NTP_ADDRESS" }, { - "path": "totp.allowed_digits", + "path": "ntp.disable_failure", "secret": false, - "env": "AUTHELIA_TOTP_ALLOWED_DIGITS" + "env": "AUTHELIA_NTP_DISABLE_FAILURE" }, { - "path": "totp.allowed_periods", + "path": "ntp.disable_startup_check", "secret": false, - "env": "AUTHELIA_TOTP_ALLOWED_PERIODS" + "env": "AUTHELIA_NTP_DISABLE_STARTUP_CHECK" }, { - "path": "totp.disable_reuse_security_policy", + "path": "ntp.max_desync", "secret": false, - "env": "AUTHELIA_TOTP_DISABLE_REUSE_SECURITY_POLICY" + "env": "AUTHELIA_NTP_MAX_DESYNC" }, { - "path": "duo_api.disable", + "path": "ntp.version", "secret": false, - "env": "AUTHELIA_DUO_API_DISABLE" + "env": "AUTHELIA_NTP_VERSION" }, { - "path": "duo_api.hostname", + "path": "password_policy.standard.enabled", "secret": false, - "env": "AUTHELIA_DUO_API_HOSTNAME" + "env": "AUTHELIA_PASSWORD_POLICY_STANDARD_ENABLED" }, { - "path": "duo_api.integration_key", - "secret": true, - "env": "AUTHELIA_DUO_API_INTEGRATION_KEY_FILE" + "path": "password_policy.standard.max_length", + "secret": false, + "env": "AUTHELIA_PASSWORD_POLICY_STANDARD_MAX_LENGTH" }, { - "path": "duo_api.secret_key", - "secret": true, - "env": "AUTHELIA_DUO_API_SECRET_KEY_FILE" + "path": "password_policy.standard.min_length", + "secret": false, + "env": "AUTHELIA_PASSWORD_POLICY_STANDARD_MIN_LENGTH" }, { - "path": "duo_api.enable_self_enrollment", + "path": "password_policy.standard.require_lowercase", "secret": false, - "env": "AUTHELIA_DUO_API_ENABLE_SELF_ENROLLMENT" + "env": "AUTHELIA_PASSWORD_POLICY_STANDARD_REQUIRE_LOWERCASE" }, { - "path": "access_control.default_policy", + "path": "password_policy.standard.require_number", "secret": false, - "env": "AUTHELIA_ACCESS_CONTROL_DEFAULT_POLICY" + "env": "AUTHELIA_PASSWORD_POLICY_STANDARD_REQUIRE_NUMBER" }, { - "path": "ntp.address", + "path": "password_policy.standard.require_special", "secret": false, - "env": "AUTHELIA_NTP_ADDRESS" + "env": "AUTHELIA_PASSWORD_POLICY_STANDARD_REQUIRE_SPECIAL" }, { - "path": "ntp.version", + "path": "password_policy.standard.require_uppercase", "secret": false, - "env": "AUTHELIA_NTP_VERSION" + "env": "AUTHELIA_PASSWORD_POLICY_STANDARD_REQUIRE_UPPERCASE" }, { - "path": "ntp.max_desync", + "path": "password_policy.zxcvbn.enabled", "secret": false, - "env": "AUTHELIA_NTP_MAX_DESYNC" + "env": "AUTHELIA_PASSWORD_POLICY_ZXCVBN_ENABLED" }, { - "path": "ntp.disable_startup_check", + "path": "password_policy.zxcvbn.min_score", "secret": false, - "env": "AUTHELIA_NTP_DISABLE_STARTUP_CHECK" + "env": "AUTHELIA_PASSWORD_POLICY_ZXCVBN_MIN_SCORE" }, { - "path": "ntp.disable_failure", + "path": "privacy_policy.enabled", "secret": false, - "env": "AUTHELIA_NTP_DISABLE_FAILURE" + "env": "AUTHELIA_PRIVACY_POLICY_ENABLED" }, { - "path": "regulation.max_retries", + "path": "privacy_policy.policy_url", "secret": false, - "env": "AUTHELIA_REGULATION_MAX_RETRIES" + "env": "AUTHELIA_PRIVACY_POLICY_POLICY_URL" }, { - "path": "regulation.find_time", + "path": "privacy_policy.require_user_acceptance", "secret": false, - "env": "AUTHELIA_REGULATION_FIND_TIME" + "env": "AUTHELIA_PRIVACY_POLICY_REQUIRE_USER_ACCEPTANCE" }, { "path": "regulation.ban_time", @@ -760,518 +755,523 @@ "env": "AUTHELIA_REGULATION_BAN_TIME" }, { - "path": "storage.local.path", + "path": "regulation.find_time", "secret": false, - "env": "AUTHELIA_STORAGE_LOCAL_PATH" + "env": "AUTHELIA_REGULATION_FIND_TIME" }, { - "path": "storage.mysql.address", + "path": "regulation.max_retries", "secret": false, - "env": "AUTHELIA_STORAGE_MYSQL_ADDRESS" + "env": "AUTHELIA_REGULATION_MAX_RETRIES" }, { - "path": "storage.mysql.database", + "path": "server.address", "secret": false, - "env": "AUTHELIA_STORAGE_MYSQL_DATABASE" + "env": "AUTHELIA_SERVER_ADDRESS" }, { - "path": "storage.mysql.username", + "path": "server.asset_path", "secret": false, - "env": "AUTHELIA_STORAGE_MYSQL_USERNAME" + "env": "AUTHELIA_SERVER_ASSET_PATH" }, { - "path": "storage.mysql.password", - "secret": true, - "env": "AUTHELIA_STORAGE_MYSQL_PASSWORD_FILE" + "path": "server.buffers.read", + "secret": false, + "env": "AUTHELIA_SERVER_BUFFERS_READ" }, { - "path": "storage.mysql.timeout", + "path": "server.buffers.write", "secret": false, - "env": "AUTHELIA_STORAGE_MYSQL_TIMEOUT" + "env": "AUTHELIA_SERVER_BUFFERS_WRITE" }, { - "path": "storage.mysql.tls.minimum_version", + "path": "server.disable_healthcheck", "secret": false, - "env": "AUTHELIA_STORAGE_MYSQL_TLS_MINIMUM_VERSION" + "env": "AUTHELIA_SERVER_DISABLE_HEALTHCHECK" }, { - "path": "storage.mysql.tls.maximum_version", + "path": "server.endpoints.enable_expvars", "secret": false, - "env": "AUTHELIA_STORAGE_MYSQL_TLS_MAXIMUM_VERSION" + "env": "AUTHELIA_SERVER_ENDPOINTS_ENABLE_EXPVARS" }, { - "path": "storage.mysql.tls.skip_verify", + "path": "server.endpoints.enable_pprof", "secret": false, - "env": "AUTHELIA_STORAGE_MYSQL_TLS_SKIP_VERIFY" + "env": "AUTHELIA_SERVER_ENDPOINTS_ENABLE_PPROF" }, { - "path": "storage.mysql.tls.server_name", + "path": "server.headers.csp_template", "secret": false, - "env": "AUTHELIA_STORAGE_MYSQL_TLS_SERVER_NAME" + "env": "AUTHELIA_SERVER_HEADERS_CSP_TEMPLATE" }, { - "path": "storage.mysql.tls.private_key", - "secret": true, - "env": "AUTHELIA_STORAGE_MYSQL_TLS_PRIVATE_KEY_FILE" + "path": "server.timeouts.idle", + "secret": false, + "env": "AUTHELIA_SERVER_TIMEOUTS_IDLE" }, { - "path": "storage.mysql.tls.certificate_chain", - "secret": true, - "env": "AUTHELIA_STORAGE_MYSQL_TLS_CERTIFICATE_CHAIN_FILE" + "path": "server.timeouts.read", + "secret": false, + "env": "AUTHELIA_SERVER_TIMEOUTS_READ" }, { - "path": "storage.postgres.address", + "path": "server.timeouts.write", "secret": false, - "env": "AUTHELIA_STORAGE_POSTGRES_ADDRESS" + "env": "AUTHELIA_SERVER_TIMEOUTS_WRITE" }, { - "path": "storage.postgres.database", + "path": "server.tls.certificate", "secret": false, - "env": "AUTHELIA_STORAGE_POSTGRES_DATABASE" + "env": "AUTHELIA_SERVER_TLS_CERTIFICATE" }, { - "path": "storage.postgres.username", + "path": "server.tls.client_certificates", "secret": false, - "env": "AUTHELIA_STORAGE_POSTGRES_USERNAME" + "env": "AUTHELIA_SERVER_TLS_CLIENT_CERTIFICATES" }, { - "path": "storage.postgres.password", - "secret": true, - "env": "AUTHELIA_STORAGE_POSTGRES_PASSWORD_FILE" + "path": "server.tls.key", + "secret": false, + "env": "AUTHELIA_SERVER_TLS_KEY" }, { - "path": "storage.postgres.timeout", + "path": "session", "secret": false, - "env": "AUTHELIA_STORAGE_POSTGRES_TIMEOUT" + "env": "AUTHELIA_SESSION" }, { - "path": "storage.postgres.schema", + "path": "session.expiration", "secret": false, - "env": "AUTHELIA_STORAGE_POSTGRES_SCHEMA" + "env": "AUTHELIA_SESSION_EXPIRATION" }, { - "path": "storage.postgres.tls.minimum_version", + "path": "session.inactivity", "secret": false, - "env": "AUTHELIA_STORAGE_POSTGRES_TLS_MINIMUM_VERSION" + "env": "AUTHELIA_SESSION_INACTIVITY" }, { - "path": "storage.postgres.tls.maximum_version", + "path": "session.name", "secret": false, - "env": "AUTHELIA_STORAGE_POSTGRES_TLS_MAXIMUM_VERSION" + "env": "AUTHELIA_SESSION_NAME" }, { - "path": "storage.postgres.tls.skip_verify", + "path": "session.redis.database_index", "secret": false, - "env": "AUTHELIA_STORAGE_POSTGRES_TLS_SKIP_VERIFY" + "env": "AUTHELIA_SESSION_REDIS_DATABASE_INDEX" }, { - "path": "storage.postgres.tls.server_name", + "path": "session.redis.high_availability.route_by_latency", "secret": false, - "env": "AUTHELIA_STORAGE_POSTGRES_TLS_SERVER_NAME" + "env": "AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_ROUTE_BY_LATENCY" }, { - "path": "storage.postgres.tls.private_key", - "secret": true, - "env": "AUTHELIA_STORAGE_POSTGRES_TLS_PRIVATE_KEY_FILE" + "path": "session.redis.high_availability.route_randomly", + "secret": false, + "env": "AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_ROUTE_RANDOMLY" }, { - "path": "storage.postgres.tls.certificate_chain", - "secret": true, - "env": "AUTHELIA_STORAGE_POSTGRES_TLS_CERTIFICATE_CHAIN_FILE" + "path": "session.redis.high_availability.sentinel_name", + "secret": false, + "env": "AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_SENTINEL_NAME" }, { - "path": "storage.encryption_key", + "path": "session.redis.high_availability.sentinel_password", "secret": true, - "env": "AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE" + "env": "AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_SENTINEL_PASSWORD_FILE" }, { - "path": "notifier.disable_startup_check", + "path": "session.redis.high_availability.sentinel_username", "secret": false, - "env": "AUTHELIA_NOTIFIER_DISABLE_STARTUP_CHECK" + "env": "AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_SENTINEL_USERNAME" }, { - "path": "notifier.filesystem.filename", + "path": "session.redis.host", "secret": false, - "env": "AUTHELIA_NOTIFIER_FILESYSTEM_FILENAME" + "env": "AUTHELIA_SESSION_REDIS_HOST" }, { - "path": "notifier.smtp.address", + "path": "session.redis.max_retries", "secret": false, - "env": "AUTHELIA_NOTIFIER_SMTP_ADDRESS" + "env": "AUTHELIA_SESSION_REDIS_MAX_RETRIES" }, { - "path": "notifier.smtp.timeout", + "path": "session.redis.maximum_active_connections", "secret": false, - "env": "AUTHELIA_NOTIFIER_SMTP_TIMEOUT" + "env": "AUTHELIA_SESSION_REDIS_MAXIMUM_ACTIVE_CONNECTIONS" }, { - "path": "notifier.smtp.username", + "path": "session.redis.minimum_idle_connections", "secret": false, - "env": "AUTHELIA_NOTIFIER_SMTP_USERNAME" + "env": "AUTHELIA_SESSION_REDIS_MINIMUM_IDLE_CONNECTIONS" }, { - "path": "notifier.smtp.password", + "path": "session.redis.password", "secret": true, - "env": "AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE" + "env": "AUTHELIA_SESSION_REDIS_PASSWORD_FILE" }, { - "path": "notifier.smtp.identifier", + "path": "session.redis.port", "secret": false, - "env": "AUTHELIA_NOTIFIER_SMTP_IDENTIFIER" + "env": "AUTHELIA_SESSION_REDIS_PORT" }, { - "path": "notifier.smtp.sender", + "path": "session.redis.timeout", "secret": false, - "env": "AUTHELIA_NOTIFIER_SMTP_SENDER" + "env": "AUTHELIA_SESSION_REDIS_TIMEOUT" }, { - "path": "notifier.smtp.subject", - "secret": false, - "env": "AUTHELIA_NOTIFIER_SMTP_SUBJECT" + "path": "session.redis.tls.certificate_chain", + "secret": true, + "env": "AUTHELIA_SESSION_REDIS_TLS_CERTIFICATE_CHAIN_FILE" }, { - "path": "notifier.smtp.startup_check_address", + "path": "session.redis.tls.maximum_version", "secret": false, - "env": "AUTHELIA_NOTIFIER_SMTP_STARTUP_CHECK_ADDRESS" + "env": "AUTHELIA_SESSION_REDIS_TLS_MAXIMUM_VERSION" }, { - "path": "notifier.smtp.disable_require_tls", + "path": "session.redis.tls.minimum_version", "secret": false, - "env": "AUTHELIA_NOTIFIER_SMTP_DISABLE_REQUIRE_TLS" + "env": "AUTHELIA_SESSION_REDIS_TLS_MINIMUM_VERSION" }, { - "path": "notifier.smtp.disable_html_emails", - "secret": false, - "env": "AUTHELIA_NOTIFIER_SMTP_DISABLE_HTML_EMAILS" + "path": "session.redis.tls.private_key", + "secret": true, + "env": "AUTHELIA_SESSION_REDIS_TLS_PRIVATE_KEY_FILE" }, { - "path": "notifier.smtp.disable_starttls", + "path": "session.redis.tls.server_name", "secret": false, - "env": "AUTHELIA_NOTIFIER_SMTP_DISABLE_STARTTLS" + "env": "AUTHELIA_SESSION_REDIS_TLS_SERVER_NAME" }, { - "path": "notifier.smtp.tls.minimum_version", + "path": "session.redis.tls.skip_verify", "secret": false, - "env": "AUTHELIA_NOTIFIER_SMTP_TLS_MINIMUM_VERSION" + "env": "AUTHELIA_SESSION_REDIS_TLS_SKIP_VERIFY" }, { - "path": "notifier.smtp.tls.maximum_version", + "path": "session.redis.username", "secret": false, - "env": "AUTHELIA_NOTIFIER_SMTP_TLS_MAXIMUM_VERSION" + "env": "AUTHELIA_SESSION_REDIS_USERNAME" }, { - "path": "notifier.smtp.tls.skip_verify", + "path": "session.remember_me", "secret": false, - "env": "AUTHELIA_NOTIFIER_SMTP_TLS_SKIP_VERIFY" + "env": "AUTHELIA_SESSION_REMEMBER_ME" }, { - "path": "notifier.smtp.tls.server_name", + "path": "session.same_site", "secret": false, - "env": "AUTHELIA_NOTIFIER_SMTP_TLS_SERVER_NAME" + "env": "AUTHELIA_SESSION_SAME_SITE" }, { - "path": "notifier.smtp.tls.private_key", + "path": "session.secret", "secret": true, - "env": "AUTHELIA_NOTIFIER_SMTP_TLS_PRIVATE_KEY_FILE" + "env": "AUTHELIA_SESSION_SECRET_FILE" }, { - "path": "notifier.smtp.tls.certificate_chain", + "path": "storage.encryption_key", "secret": true, - "env": "AUTHELIA_NOTIFIER_SMTP_TLS_CERTIFICATE_CHAIN_FILE" + "env": "AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE" }, { - "path": "notifier.template_path", + "path": "storage.local.path", "secret": false, - "env": "AUTHELIA_NOTIFIER_TEMPLATE_PATH" + "env": "AUTHELIA_STORAGE_LOCAL_PATH" }, { - "path": "server.address", + "path": "storage.mysql.address", "secret": false, - "env": "AUTHELIA_SERVER_ADDRESS" + "env": "AUTHELIA_STORAGE_MYSQL_ADDRESS" }, { - "path": "server.asset_path", + "path": "storage.mysql.database", "secret": false, - "env": "AUTHELIA_SERVER_ASSET_PATH" + "env": "AUTHELIA_STORAGE_MYSQL_DATABASE" }, { - "path": "server.disable_healthcheck", - "secret": false, - "env": "AUTHELIA_SERVER_DISABLE_HEALTHCHECK" + "path": "storage.mysql.password", + "secret": true, + "env": "AUTHELIA_STORAGE_MYSQL_PASSWORD_FILE" }, { - "path": "server.tls.certificate", + "path": "storage.mysql.timeout", "secret": false, - "env": "AUTHELIA_SERVER_TLS_CERTIFICATE" + "env": "AUTHELIA_STORAGE_MYSQL_TIMEOUT" }, { - "path": "server.tls.key", - "secret": false, - "env": "AUTHELIA_SERVER_TLS_KEY" + "path": "storage.mysql.tls.certificate_chain", + "secret": true, + "env": "AUTHELIA_STORAGE_MYSQL_TLS_CERTIFICATE_CHAIN_FILE" }, { - "path": "server.tls.client_certificates", + "path": "storage.mysql.tls.maximum_version", "secret": false, - "env": "AUTHELIA_SERVER_TLS_CLIENT_CERTIFICATES" + "env": "AUTHELIA_STORAGE_MYSQL_TLS_MAXIMUM_VERSION" }, { - "path": "server.headers.csp_template", + "path": "storage.mysql.tls.minimum_version", "secret": false, - "env": "AUTHELIA_SERVER_HEADERS_CSP_TEMPLATE" + "env": "AUTHELIA_STORAGE_MYSQL_TLS_MINIMUM_VERSION" }, { - "path": "server.endpoints.enable_pprof", - "secret": false, - "env": "AUTHELIA_SERVER_ENDPOINTS_ENABLE_PPROF" + "path": "storage.mysql.tls.private_key", + "secret": true, + "env": "AUTHELIA_STORAGE_MYSQL_TLS_PRIVATE_KEY_FILE" }, { - "path": "server.endpoints.enable_expvars", + "path": "storage.mysql.tls.server_name", "secret": false, - "env": "AUTHELIA_SERVER_ENDPOINTS_ENABLE_EXPVARS" + "env": "AUTHELIA_STORAGE_MYSQL_TLS_SERVER_NAME" }, { - "path": "server.buffers.read", + "path": "storage.mysql.tls.skip_verify", "secret": false, - "env": "AUTHELIA_SERVER_BUFFERS_READ" + "env": "AUTHELIA_STORAGE_MYSQL_TLS_SKIP_VERIFY" }, { - "path": "server.buffers.write", + "path": "storage.mysql.username", "secret": false, - "env": "AUTHELIA_SERVER_BUFFERS_WRITE" + "env": "AUTHELIA_STORAGE_MYSQL_USERNAME" }, { - "path": "server.timeouts.read", + "path": "storage.postgres.address", "secret": false, - "env": "AUTHELIA_SERVER_TIMEOUTS_READ" + "env": "AUTHELIA_STORAGE_POSTGRES_ADDRESS" }, { - "path": "server.timeouts.write", + "path": "storage.postgres.database", "secret": false, - "env": "AUTHELIA_SERVER_TIMEOUTS_WRITE" + "env": "AUTHELIA_STORAGE_POSTGRES_DATABASE" }, { - "path": "server.timeouts.idle", - "secret": false, - "env": "AUTHELIA_SERVER_TIMEOUTS_IDLE" + "path": "storage.postgres.password", + "secret": true, + "env": "AUTHELIA_STORAGE_POSTGRES_PASSWORD_FILE" }, { - "path": "telemetry.metrics.enabled", + "path": "storage.postgres.schema", "secret": false, - "env": "AUTHELIA_TELEMETRY_METRICS_ENABLED" + "env": "AUTHELIA_STORAGE_POSTGRES_SCHEMA" }, { - "path": "telemetry.metrics.address", + "path": "storage.postgres.timeout", "secret": false, - "env": "AUTHELIA_TELEMETRY_METRICS_ADDRESS" + "env": "AUTHELIA_STORAGE_POSTGRES_TIMEOUT" }, { - "path": "telemetry.metrics.buffers.read", - "secret": false, - "env": "AUTHELIA_TELEMETRY_METRICS_BUFFERS_READ" + "path": "storage.postgres.tls.certificate_chain", + "secret": true, + "env": "AUTHELIA_STORAGE_POSTGRES_TLS_CERTIFICATE_CHAIN_FILE" }, { - "path": "telemetry.metrics.buffers.write", + "path": "storage.postgres.tls.maximum_version", "secret": false, - "env": "AUTHELIA_TELEMETRY_METRICS_BUFFERS_WRITE" + "env": "AUTHELIA_STORAGE_POSTGRES_TLS_MAXIMUM_VERSION" }, { - "path": "telemetry.metrics.timeouts.read", + "path": "storage.postgres.tls.minimum_version", "secret": false, - "env": "AUTHELIA_TELEMETRY_METRICS_TIMEOUTS_READ" + "env": "AUTHELIA_STORAGE_POSTGRES_TLS_MINIMUM_VERSION" }, { - "path": "telemetry.metrics.timeouts.write", + "path": "storage.postgres.tls.private_key", + "secret": true, + "env": "AUTHELIA_STORAGE_POSTGRES_TLS_PRIVATE_KEY_FILE" + }, + { + "path": "storage.postgres.tls.server_name", "secret": false, - "env": "AUTHELIA_TELEMETRY_METRICS_TIMEOUTS_WRITE" + "env": "AUTHELIA_STORAGE_POSTGRES_TLS_SERVER_NAME" }, { - "path": "telemetry.metrics.timeouts.idle", + "path": "storage.postgres.tls.skip_verify", "secret": false, - "env": "AUTHELIA_TELEMETRY_METRICS_TIMEOUTS_IDLE" + "env": "AUTHELIA_STORAGE_POSTGRES_TLS_SKIP_VERIFY" }, { - "path": "webauthn.disable", + "path": "storage.postgres.username", "secret": false, - "env": "AUTHELIA_WEBAUTHN_DISABLE" + "env": "AUTHELIA_STORAGE_POSTGRES_USERNAME" }, { - "path": "webauthn.enable_passkey_login", + "path": "telemetry.metrics.address", "secret": false, - "env": "AUTHELIA_WEBAUTHN_ENABLE_PASSKEY_LOGIN" + "env": "AUTHELIA_TELEMETRY_METRICS_ADDRESS" }, { - "path": "webauthn.experimental_enable_passkey_uv_two_factors", + "path": "telemetry.metrics.buffers.read", "secret": false, - "env": "AUTHELIA_WEBAUTHN_EXPERIMENTAL_ENABLE_PASSKEY_UV_TWO_FACTORS" + "env": "AUTHELIA_TELEMETRY_METRICS_BUFFERS_READ" }, { - "path": "webauthn.display_name", + "path": "telemetry.metrics.buffers.write", "secret": false, - "env": "AUTHELIA_WEBAUTHN_DISPLAY_NAME" + "env": "AUTHELIA_TELEMETRY_METRICS_BUFFERS_WRITE" }, { - "path": "webauthn.attestation_conveyance_preference", + "path": "telemetry.metrics.enabled", "secret": false, - "env": "AUTHELIA_WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE" + "env": "AUTHELIA_TELEMETRY_METRICS_ENABLED" }, { - "path": "webauthn.timeout", + "path": "telemetry.metrics.timeouts.idle", "secret": false, - "env": "AUTHELIA_WEBAUTHN_TIMEOUT" + "env": "AUTHELIA_TELEMETRY_METRICS_TIMEOUTS_IDLE" }, { - "path": "webauthn.filtering.prohibit_backup_eligibility", + "path": "telemetry.metrics.timeouts.read", "secret": false, - "env": "AUTHELIA_WEBAUTHN_FILTERING_PROHIBIT_BACKUP_ELIGIBILITY" + "env": "AUTHELIA_TELEMETRY_METRICS_TIMEOUTS_READ" }, { - "path": "webauthn.selection_criteria.attachment", + "path": "telemetry.metrics.timeouts.write", "secret": false, - "env": "AUTHELIA_WEBAUTHN_SELECTION_CRITERIA_ATTACHMENT" + "env": "AUTHELIA_TELEMETRY_METRICS_TIMEOUTS_WRITE" }, { - "path": "webauthn.selection_criteria.discoverability", + "path": "theme", "secret": false, - "env": "AUTHELIA_WEBAUTHN_SELECTION_CRITERIA_DISCOVERABILITY" + "env": "AUTHELIA_THEME" }, { - "path": "webauthn.selection_criteria.user_verification", + "path": "totp.algorithm", "secret": false, - "env": "AUTHELIA_WEBAUTHN_SELECTION_CRITERIA_USER_VERIFICATION" + "env": "AUTHELIA_TOTP_ALGORITHM" }, { - "path": "webauthn.metadata.enabled", + "path": "totp.allowed_algorithms", "secret": false, - "env": "AUTHELIA_WEBAUTHN_METADATA_ENABLED" + "env": "AUTHELIA_TOTP_ALLOWED_ALGORITHMS" }, { - "path": "webauthn.metadata.validate_trust_anchor", + "path": "totp.allowed_digits", "secret": false, - "env": "AUTHELIA_WEBAUTHN_METADATA_VALIDATE_TRUST_ANCHOR" + "env": "AUTHELIA_TOTP_ALLOWED_DIGITS" }, { - "path": "webauthn.metadata.validate_entry", + "path": "totp.allowed_periods", "secret": false, - "env": "AUTHELIA_WEBAUTHN_METADATA_VALIDATE_ENTRY" + "env": "AUTHELIA_TOTP_ALLOWED_PERIODS" }, { - "path": "webauthn.metadata.validate_entry_permit_zero_aaguid", + "path": "totp.digits", "secret": false, - "env": "AUTHELIA_WEBAUTHN_METADATA_VALIDATE_ENTRY_PERMIT_ZERO_AAGUID" + "env": "AUTHELIA_TOTP_DIGITS" }, { - "path": "webauthn.metadata.validate_status", + "path": "totp.disable", "secret": false, - "env": "AUTHELIA_WEBAUTHN_METADATA_VALIDATE_STATUS" + "env": "AUTHELIA_TOTP_DISABLE" }, { - "path": "webauthn.metadata.validate_status_permitted", + "path": "totp.disable_reuse_security_policy", "secret": false, - "env": "AUTHELIA_WEBAUTHN_METADATA_VALIDATE_STATUS_PERMITTED" + "env": "AUTHELIA_TOTP_DISABLE_REUSE_SECURITY_POLICY" }, { - "path": "webauthn.metadata.validate_status_prohibited", + "path": "totp.issuer", "secret": false, - "env": "AUTHELIA_WEBAUTHN_METADATA_VALIDATE_STATUS_PROHIBITED" + "env": "AUTHELIA_TOTP_ISSUER" }, { - "path": "password_policy.standard.enabled", + "path": "totp.period", "secret": false, - "env": "AUTHELIA_PASSWORD_POLICY_STANDARD_ENABLED" + "env": "AUTHELIA_TOTP_PERIOD" }, { - "path": "password_policy.standard.min_length", + "path": "totp.secret_size", "secret": false, - "env": "AUTHELIA_PASSWORD_POLICY_STANDARD_MIN_LENGTH" + "env": "AUTHELIA_TOTP_SECRET_SIZE" }, { - "path": "password_policy.standard.max_length", + "path": "totp.skew", "secret": false, - "env": "AUTHELIA_PASSWORD_POLICY_STANDARD_MAX_LENGTH" + "env": "AUTHELIA_TOTP_SKEW" }, { - "path": "password_policy.standard.require_uppercase", + "path": "webauthn.attestation_conveyance_preference", "secret": false, - "env": "AUTHELIA_PASSWORD_POLICY_STANDARD_REQUIRE_UPPERCASE" + "env": "AUTHELIA_WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE" }, { - "path": "password_policy.standard.require_lowercase", + "path": "webauthn.disable", "secret": false, - "env": "AUTHELIA_PASSWORD_POLICY_STANDARD_REQUIRE_LOWERCASE" + "env": "AUTHELIA_WEBAUTHN_DISABLE" }, { - "path": "password_policy.standard.require_number", + "path": "webauthn.display_name", "secret": false, - "env": "AUTHELIA_PASSWORD_POLICY_STANDARD_REQUIRE_NUMBER" + "env": "AUTHELIA_WEBAUTHN_DISPLAY_NAME" }, { - "path": "password_policy.standard.require_special", + "path": "webauthn.enable_passkey_login", "secret": false, - "env": "AUTHELIA_PASSWORD_POLICY_STANDARD_REQUIRE_SPECIAL" + "env": "AUTHELIA_WEBAUTHN_ENABLE_PASSKEY_LOGIN" }, { - "path": "password_policy.zxcvbn.enabled", + "path": "webauthn.experimental_enable_passkey_uv_two_factors", "secret": false, - "env": "AUTHELIA_PASSWORD_POLICY_ZXCVBN_ENABLED" + "env": "AUTHELIA_WEBAUTHN_EXPERIMENTAL_ENABLE_PASSKEY_UV_TWO_FACTORS" }, { - "path": "password_policy.zxcvbn.min_score", + "path": "webauthn.filtering.prohibit_backup_eligibility", "secret": false, - "env": "AUTHELIA_PASSWORD_POLICY_ZXCVBN_MIN_SCORE" + "env": "AUTHELIA_WEBAUTHN_FILTERING_PROHIBIT_BACKUP_ELIGIBILITY" }, { - "path": "privacy_policy.enabled", + "path": "webauthn.metadata.enabled", "secret": false, - "env": "AUTHELIA_PRIVACY_POLICY_ENABLED" + "env": "AUTHELIA_WEBAUTHN_METADATA_ENABLED" }, { - "path": "privacy_policy.require_user_acceptance", + "path": "webauthn.metadata.validate_entry", "secret": false, - "env": "AUTHELIA_PRIVACY_POLICY_REQUIRE_USER_ACCEPTANCE" + "env": "AUTHELIA_WEBAUTHN_METADATA_VALIDATE_ENTRY" }, { - "path": "privacy_policy.policy_url", + "path": "webauthn.metadata.validate_entry_permit_zero_aaguid", "secret": false, - "env": "AUTHELIA_PRIVACY_POLICY_POLICY_URL" + "env": "AUTHELIA_WEBAUTHN_METADATA_VALIDATE_ENTRY_PERMIT_ZERO_AAGUID" }, { - "path": "identity_validation.reset_password.jwt_lifespan", + "path": "webauthn.metadata.validate_status", "secret": false, - "env": "AUTHELIA_IDENTITY_VALIDATION_RESET_PASSWORD_JWT_LIFESPAN" + "env": "AUTHELIA_WEBAUTHN_METADATA_VALIDATE_STATUS" }, { - "path": "identity_validation.reset_password.jwt_algorithm", + "path": "webauthn.metadata.validate_status_permitted", "secret": false, - "env": "AUTHELIA_IDENTITY_VALIDATION_RESET_PASSWORD_JWT_ALGORITHM" + "env": "AUTHELIA_WEBAUTHN_METADATA_VALIDATE_STATUS_PERMITTED" }, { - "path": "identity_validation.reset_password.jwt_secret", - "secret": true, - "env": "AUTHELIA_IDENTITY_VALIDATION_RESET_PASSWORD_JWT_SECRET_FILE" + "path": "webauthn.metadata.validate_status_prohibited", + "secret": false, + "env": "AUTHELIA_WEBAUTHN_METADATA_VALIDATE_STATUS_PROHIBITED" }, { - "path": "identity_validation.elevated_session.code_lifespan", + "path": "webauthn.metadata.validate_trust_anchor", "secret": false, - "env": "AUTHELIA_IDENTITY_VALIDATION_ELEVATED_SESSION_CODE_LIFESPAN" + "env": "AUTHELIA_WEBAUTHN_METADATA_VALIDATE_TRUST_ANCHOR" }, { - "path": "identity_validation.elevated_session.elevation_lifespan", + "path": "webauthn.selection_criteria.attachment", "secret": false, - "env": "AUTHELIA_IDENTITY_VALIDATION_ELEVATED_SESSION_ELEVATION_LIFESPAN" + "env": "AUTHELIA_WEBAUTHN_SELECTION_CRITERIA_ATTACHMENT" }, { - "path": "identity_validation.elevated_session.characters", + "path": "webauthn.selection_criteria.discoverability", "secret": false, - "env": "AUTHELIA_IDENTITY_VALIDATION_ELEVATED_SESSION_CHARACTERS" + "env": "AUTHELIA_WEBAUTHN_SELECTION_CRITERIA_DISCOVERABILITY" }, { - "path": "identity_validation.elevated_session.require_second_factor", + "path": "webauthn.selection_criteria.user_verification", "secret": false, - "env": "AUTHELIA_IDENTITY_VALIDATION_ELEVATED_SESSION_REQUIRE_SECOND_FACTOR" + "env": "AUTHELIA_WEBAUTHN_SELECTION_CRITERIA_USER_VERIFICATION" }, { - "path": "identity_validation.elevated_session.skip_second_factor", + "path": "webauthn.timeout", "secret": false, - "env": "AUTHELIA_IDENTITY_VALIDATION_ELEVATED_SESSION_SKIP_SECOND_FACTOR" + "env": "AUTHELIA_WEBAUTHN_TIMEOUT" } ] diff --git a/docs/static/schemas/v4.39/json-schema/configuration.json b/docs/static/schemas/v4.39/json-schema/configuration.json index 39d9ad7d4..2e72ef96c 100644 --- a/docs/static/schemas/v4.39/json-schema/configuration.json +++ b/docs/static/schemas/v4.39/json-schema/configuration.json @@ -3679,7 +3679,7 @@ "address": { "$ref": "#/$defs/AddressTCP", "title": "Address", - "description": "The address of the database." + "description": "The address of the SQL Server." }, "database": { "type": "string", @@ -3723,7 +3723,7 @@ "address": { "$ref": "#/$defs/AddressTCP", "title": "Address", - "description": "The address of the database." + "description": "The address of the SQL Server." }, "database": { "type": "string", @@ -3754,14 +3754,22 @@ "title": "Timeout", "description": "The timeout for the database connection." }, + "tls": { + "$ref": "#/$defs/TLS" + }, "schema": { "type": "string", "title": "Schema", "description": "The default schema name to use.", "default": "public" }, - "tls": { - "$ref": "#/$defs/TLS" + "servers": { + "items": { + "$ref": "#/$defs/StoragePostgreSQLServer" + }, + "type": "array", + "title": "Servers", + "description": "The fallback PostgreSQL severs to connect to in addition to the one defined by the address." }, "ssl": { "$ref": "#/$defs/StoragePostgreSQLSSL", @@ -3811,6 +3819,20 @@ "type": "object", "description": "StoragePostgreSQLSSL represents the SSL configuration of a PostgreSQL database." }, + "StoragePostgreSQLServer": { + "properties": { + "address": { + "$ref": "#/$defs/AddressTCP", + "title": "Address", + "description": "The address of the PostgreSQL Server." + }, + "tls": { + "$ref": "#/$defs/TLS" + } + }, + "additionalProperties": false, + "type": "object" + }, "TLS": { "properties": { "minimum_version": { diff --git a/internal/configuration/config.template.yml b/internal/configuration/config.template.yml index 2e01b7a15..0fd1756e9 100644 --- a/internal/configuration/config.template.yml +++ b/internal/configuration/config.template.yml @@ -1076,6 +1076,30 @@ session: ## The default scheme is 'unix' if the address is an absolute path otherwise it's 'tcp'. The default port is '5432'. # address: 'tcp://127.0.0.1:5432' + ## List of additional server instance configurations to fallback to when the primary instance is not available. + # servers: + # - + ## The Address of this individual instance. + # address: 'tcp://127.0.0.1:5432' + + ## The TLS configuration for this individual instance. + # tls: + # server_name: 'postgres.example.com' + # skip_verify: false + # minimum_version: 'TLS1.2' + # maximum_version: 'TLS1.3' + # certificate_chain: | + # -----BEGIN CERTIFICATE----- + # ... + # -----END CERTIFICATE----- + # -----BEGIN CERTIFICATE----- + # ... + # -----END CERTIFICATE----- + # private_key: | + # -----BEGIN RSA PRIVATE KEY----- + # ... + # -----END RSA PRIVATE KEY----- + ## The database name to use. # database: 'authelia' diff --git a/internal/configuration/schema/keys.go b/internal/configuration/schema/keys.go index f2efaca93..fbab487ef 100644 --- a/internal/configuration/schema/keys.go +++ b/internal/configuration/schema/keys.go @@ -8,443 +8,450 @@ package schema // Keys is a list of valid schema keys detected by reflecting over a schema.Configuration struct. var Keys = []string{ - "theme", + "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[].policy", + "access_control.rules[].query", + "access_control.rules[].query[][].key", + "access_control.rules[].query[][].operator", + "access_control.rules[].query[][].value", + "access_control.rules[].resources", + "access_control.rules[].subject", + "authentication_backend.file.extra_attributes", + "authentication_backend.file.extra_attributes.*", + "authentication_backend.file.extra_attributes.*.multi_valued", + "authentication_backend.file.extra_attributes.*.value_type", + "authentication_backend.file.password.algorithm", + "authentication_backend.file.password.argon2.iterations", + "authentication_backend.file.password.argon2.key_length", + "authentication_backend.file.password.argon2.memory", + "authentication_backend.file.password.argon2.parallelism", + "authentication_backend.file.password.argon2.salt_length", + "authentication_backend.file.password.argon2.variant", + "authentication_backend.file.password.bcrypt.cost", + "authentication_backend.file.password.bcrypt.variant", + "authentication_backend.file.password.iterations", + "authentication_backend.file.password.key_length", + "authentication_backend.file.password.memory", + "authentication_backend.file.password.parallelism", + "authentication_backend.file.password.pbkdf2.iterations", + "authentication_backend.file.password.pbkdf2.salt_length", + "authentication_backend.file.password.pbkdf2.variant", + "authentication_backend.file.password.salt_length", + "authentication_backend.file.password.scrypt.block_size", + "authentication_backend.file.password.scrypt.iterations", + "authentication_backend.file.password.scrypt.key_length", + "authentication_backend.file.password.scrypt.parallelism", + "authentication_backend.file.password.scrypt.salt_length", + "authentication_backend.file.password.sha2crypt.iterations", + "authentication_backend.file.password.sha2crypt.salt_length", + "authentication_backend.file.password.sha2crypt.variant", + "authentication_backend.file.path", + "authentication_backend.file.search.case_insensitive", + "authentication_backend.file.search.email", + "authentication_backend.file.watch", + "authentication_backend.ldap.additional_groups_dn", + "authentication_backend.ldap.additional_users_dn", + "authentication_backend.ldap.address", + "authentication_backend.ldap.attributes.birthdate", + "authentication_backend.ldap.attributes.country", + "authentication_backend.ldap.attributes.display_name", + "authentication_backend.ldap.attributes.distinguished_name", + "authentication_backend.ldap.attributes.extra", + "authentication_backend.ldap.attributes.extra.*", + "authentication_backend.ldap.attributes.extra.*.multi_valued", + "authentication_backend.ldap.attributes.extra.*.name", + "authentication_backend.ldap.attributes.extra.*.value_type", + "authentication_backend.ldap.attributes.family_name", + "authentication_backend.ldap.attributes.gender", + "authentication_backend.ldap.attributes.given_name", + "authentication_backend.ldap.attributes.group_name", + "authentication_backend.ldap.attributes.locale", + "authentication_backend.ldap.attributes.locality", + "authentication_backend.ldap.attributes.mail", + "authentication_backend.ldap.attributes.member_of", + "authentication_backend.ldap.attributes.middle_name", + "authentication_backend.ldap.attributes.nickname", + "authentication_backend.ldap.attributes.phone_extension", + "authentication_backend.ldap.attributes.phone_number", + "authentication_backend.ldap.attributes.picture", + "authentication_backend.ldap.attributes.postal_code", + "authentication_backend.ldap.attributes.profile", + "authentication_backend.ldap.attributes.region", + "authentication_backend.ldap.attributes.street_address", + "authentication_backend.ldap.attributes.username", + "authentication_backend.ldap.attributes.website", + "authentication_backend.ldap.attributes.zoneinfo", + "authentication_backend.ldap.base_dn", + "authentication_backend.ldap.group_search_mode", + "authentication_backend.ldap.groups_filter", + "authentication_backend.ldap.implementation", + "authentication_backend.ldap.password", + "authentication_backend.ldap.permit_feature_detection_failure", + "authentication_backend.ldap.permit_referrals", + "authentication_backend.ldap.permit_unauthenticated_bind", + "authentication_backend.ldap.pooling.count", + "authentication_backend.ldap.pooling.enable", + "authentication_backend.ldap.pooling.retries", + "authentication_backend.ldap.pooling.timeout", + "authentication_backend.ldap.start_tls", + "authentication_backend.ldap.timeout", + "authentication_backend.ldap.tls.certificate_chain", + "authentication_backend.ldap.tls.maximum_version", + "authentication_backend.ldap.tls.minimum_version", + "authentication_backend.ldap.tls.private_key", + "authentication_backend.ldap.tls.server_name", + "authentication_backend.ldap.tls.skip_verify", + "authentication_backend.ldap.user", + "authentication_backend.ldap.users_filter", + "authentication_backend.password_reset.custom_url", + "authentication_backend.password_reset.disable", + "authentication_backend.refresh_interval", "certificates_directory", "default_2fa_method", - "log.level", - "log.format", - "log.file_path", - "log.keep_stdout", - "identity_providers.oidc.hmac_secret", - "identity_providers.oidc.jwks", - "identity_providers.oidc.jwks[].key_id", - "identity_providers.oidc.jwks[].use", - "identity_providers.oidc.jwks[].algorithm", - "identity_providers.oidc.jwks[].key", - "identity_providers.oidc.jwks[].certificate_chain", - "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.enable_jwt_access_token_stateless_introspection", - "identity_providers.oidc.discovery_signed_response_alg", - "identity_providers.oidc.discovery_signed_response_key_id", - "identity_providers.oidc.require_pushed_authorization_requests", - "identity_providers.oidc.cors.endpoints", - "identity_providers.oidc.cors.allowed_origins", - "identity_providers.oidc.cors.allowed_origins_from_client_redirect_uris", + "default_redirection_url", + "definitions.network", + "definitions.network.*", + "definitions.user_attributes", + "definitions.user_attributes.*", + "definitions.user_attributes.*.expression", + "duo_api.disable", + "duo_api.enable_self_enrollment", + "duo_api.hostname", + "duo_api.integration_key", + "duo_api.secret_key", + "identity_providers.oidc", + "identity_providers.oidc.authorization_policies", + "identity_providers.oidc.authorization_policies.*", + "identity_providers.oidc.authorization_policies.*.default_policy", + "identity_providers.oidc.authorization_policies.*.rules", + "identity_providers.oidc.authorization_policies.*.rules[].networks", + "identity_providers.oidc.authorization_policies.*.rules[].policy", + "identity_providers.oidc.authorization_policies.*.rules[].subject", + "identity_providers.oidc.claims_policies", + "identity_providers.oidc.claims_policies.*", + "identity_providers.oidc.claims_policies.*.access_token", + "identity_providers.oidc.claims_policies.*.custom_claims", + "identity_providers.oidc.claims_policies.*.custom_claims.*", + "identity_providers.oidc.claims_policies.*.custom_claims.*.attribute", + "identity_providers.oidc.claims_policies.*.id_token", "identity_providers.oidc.clients", - "identity_providers.oidc.clients[].client_id", - "identity_providers.oidc.clients[].client_name", - "identity_providers.oidc.clients[].client_secret", - "identity_providers.oidc.clients[].sector_identifier_uri", - "identity_providers.oidc.clients[].public", - "identity_providers.oidc.clients[].redirect_uris", - "identity_providers.oidc.clients[].request_uris", + "identity_providers.oidc.clients[]", + "identity_providers.oidc.clients[].access_token_encrypted_response_alg", + "identity_providers.oidc.clients[].access_token_encrypted_response_enc", + "identity_providers.oidc.clients[].access_token_encrypted_response_key_id", + "identity_providers.oidc.clients[].access_token_signed_response_alg", + "identity_providers.oidc.clients[].access_token_signed_response_key_id", + "identity_providers.oidc.clients[].allow_multiple_auth_methods", "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[].authorization_policy", - "identity_providers.oidc.clients[].lifespan", - "identity_providers.oidc.clients[].claims_policy", - "identity_providers.oidc.clients[].requested_audience_mode", - "identity_providers.oidc.clients[].consent_mode", - "identity_providers.oidc.clients[].pre_configured_consent_duration", - "identity_providers.oidc.clients[].require_pushed_authorization_requests", - "identity_providers.oidc.clients[].require_pkce", - "identity_providers.oidc.clients[].pkce_challenge_method", - "identity_providers.oidc.clients[].authorization_signed_response_alg", - "identity_providers.oidc.clients[].authorization_signed_response_key_id", "identity_providers.oidc.clients[].authorization_encrypted_response_alg", "identity_providers.oidc.clients[].authorization_encrypted_response_enc", "identity_providers.oidc.clients[].authorization_encrypted_response_key_id", - "identity_providers.oidc.clients[].id_token_signed_response_alg", - "identity_providers.oidc.clients[].id_token_signed_response_key_id", + "identity_providers.oidc.clients[].authorization_policy", + "identity_providers.oidc.clients[].authorization_signed_response_alg", + "identity_providers.oidc.clients[].authorization_signed_response_key_id", + "identity_providers.oidc.clients[].claims_policy", + "identity_providers.oidc.clients[].client_id", + "identity_providers.oidc.clients[].client_name", + "identity_providers.oidc.clients[].client_secret", + "identity_providers.oidc.clients[].consent_mode", + "identity_providers.oidc.clients[].grant_types", "identity_providers.oidc.clients[].id_token_encrypted_response_alg", "identity_providers.oidc.clients[].id_token_encrypted_response_enc", "identity_providers.oidc.clients[].id_token_encrypted_response_key_id", - "identity_providers.oidc.clients[].access_token_signed_response_alg", - "identity_providers.oidc.clients[].access_token_signed_response_key_id", - "identity_providers.oidc.clients[].access_token_encrypted_response_alg", - "identity_providers.oidc.clients[].access_token_encrypted_response_enc", - "identity_providers.oidc.clients[].access_token_encrypted_response_key_id", - "identity_providers.oidc.clients[].userinfo_signed_response_alg", - "identity_providers.oidc.clients[].userinfo_signed_response_key_id", - "identity_providers.oidc.clients[].userinfo_encrypted_response_alg", - "identity_providers.oidc.clients[].userinfo_encrypted_response_enc", - "identity_providers.oidc.clients[].userinfo_encrypted_response_key_id", - "identity_providers.oidc.clients[].introspection_signed_response_alg", - "identity_providers.oidc.clients[].introspection_signed_response_key_id", + "identity_providers.oidc.clients[].id_token_signed_response_alg", + "identity_providers.oidc.clients[].id_token_signed_response_key_id", "identity_providers.oidc.clients[].introspection_encrypted_response_alg", "identity_providers.oidc.clients[].introspection_encrypted_response_enc", "identity_providers.oidc.clients[].introspection_encrypted_response_key_id", - "identity_providers.oidc.clients[].request_object_signing_alg", - "identity_providers.oidc.clients[].request_object_encryption_alg", - "identity_providers.oidc.clients[].request_object_encryption_enc", - "identity_providers.oidc.clients[].token_endpoint_auth_method", - "identity_providers.oidc.clients[].token_endpoint_auth_signing_alg", - "identity_providers.oidc.clients[].revocation_endpoint_auth_method", - "identity_providers.oidc.clients[].revocation_endpoint_auth_signing_alg", "identity_providers.oidc.clients[].introspection_endpoint_auth_method", "identity_providers.oidc.clients[].introspection_endpoint_auth_signing_alg", - "identity_providers.oidc.clients[].pushed_authorization_request_endpoint_auth_method", - "identity_providers.oidc.clients[].pushed_authorization_request_endpoint_auth_signing_alg", - "identity_providers.oidc.clients[].allow_multiple_auth_methods", - "identity_providers.oidc.clients[].jwks_uri", + "identity_providers.oidc.clients[].introspection_signed_response_alg", + "identity_providers.oidc.clients[].introspection_signed_response_key_id", "identity_providers.oidc.clients[].jwks", - "identity_providers.oidc.clients[].jwks[].key_id", - "identity_providers.oidc.clients[].jwks[].use", "identity_providers.oidc.clients[].jwks[].algorithm", - "identity_providers.oidc.clients[].jwks[].key", "identity_providers.oidc.clients[].jwks[].certificate_chain", - "identity_providers.oidc.clients[]", - "identity_providers.oidc.authorization_policies.*", - "identity_providers.oidc.authorization_policies", - "identity_providers.oidc.authorization_policies.*.default_policy", - "identity_providers.oidc.authorization_policies.*.rules", - "identity_providers.oidc.authorization_policies.*.rules[].policy", - "identity_providers.oidc.authorization_policies.*.rules[].subject", - "identity_providers.oidc.authorization_policies.*.rules[].networks", + "identity_providers.oidc.clients[].jwks[].key", + "identity_providers.oidc.clients[].jwks[].key_id", + "identity_providers.oidc.clients[].jwks[].use", + "identity_providers.oidc.clients[].jwks_uri", + "identity_providers.oidc.clients[].lifespan", + "identity_providers.oidc.clients[].pkce_challenge_method", + "identity_providers.oidc.clients[].pre_configured_consent_duration", + "identity_providers.oidc.clients[].public", + "identity_providers.oidc.clients[].pushed_authorization_request_endpoint_auth_method", + "identity_providers.oidc.clients[].pushed_authorization_request_endpoint_auth_signing_alg", + "identity_providers.oidc.clients[].redirect_uris", + "identity_providers.oidc.clients[].request_object_encryption_alg", + "identity_providers.oidc.clients[].request_object_encryption_enc", + "identity_providers.oidc.clients[].request_object_signing_alg", + "identity_providers.oidc.clients[].request_uris", + "identity_providers.oidc.clients[].requested_audience_mode", + "identity_providers.oidc.clients[].require_pkce", + "identity_providers.oidc.clients[].require_pushed_authorization_requests", + "identity_providers.oidc.clients[].response_modes", + "identity_providers.oidc.clients[].response_types", + "identity_providers.oidc.clients[].revocation_endpoint_auth_method", + "identity_providers.oidc.clients[].revocation_endpoint_auth_signing_alg", + "identity_providers.oidc.clients[].scopes", + "identity_providers.oidc.clients[].sector_identifier_uri", + "identity_providers.oidc.clients[].token_endpoint_auth_method", + "identity_providers.oidc.clients[].token_endpoint_auth_signing_alg", + "identity_providers.oidc.clients[].userinfo_encrypted_response_alg", + "identity_providers.oidc.clients[].userinfo_encrypted_response_enc", + "identity_providers.oidc.clients[].userinfo_encrypted_response_key_id", + "identity_providers.oidc.clients[].userinfo_signed_response_alg", + "identity_providers.oidc.clients[].userinfo_signed_response_key_id", + "identity_providers.oidc.cors.allowed_origins", + "identity_providers.oidc.cors.allowed_origins_from_client_redirect_uris", + "identity_providers.oidc.cors.endpoints", + "identity_providers.oidc.discovery_signed_response_alg", + "identity_providers.oidc.discovery_signed_response_key_id", + "identity_providers.oidc.enable_client_debug_messages", + "identity_providers.oidc.enable_jwt_access_token_stateless_introspection", + "identity_providers.oidc.enable_pkce_plain_challenge", + "identity_providers.oidc.enforce_pkce", + "identity_providers.oidc.hmac_secret", + "identity_providers.oidc.issuer_certificate_chain", + "identity_providers.oidc.issuer_private_key", + "identity_providers.oidc.jwks", + "identity_providers.oidc.jwks[].algorithm", + "identity_providers.oidc.jwks[].certificate_chain", + "identity_providers.oidc.jwks[].key", + "identity_providers.oidc.jwks[].key_id", + "identity_providers.oidc.jwks[].use", "identity_providers.oidc.lifespans.access_token", "identity_providers.oidc.lifespans.authorize_code", - "identity_providers.oidc.lifespans.id_token", - "identity_providers.oidc.lifespans.refresh_token", - "identity_providers.oidc.lifespans.jwt_secured_authorization", - "identity_providers.oidc.lifespans.custom.*", "identity_providers.oidc.lifespans.custom", + "identity_providers.oidc.lifespans.custom.*", "identity_providers.oidc.lifespans.custom.*.access_token", "identity_providers.oidc.lifespans.custom.*.authorize_code", - "identity_providers.oidc.lifespans.custom.*.id_token", - "identity_providers.oidc.lifespans.custom.*.refresh_token", "identity_providers.oidc.lifespans.custom.*.grants.authorize_code.access_token", "identity_providers.oidc.lifespans.custom.*.grants.authorize_code.authorize_code", "identity_providers.oidc.lifespans.custom.*.grants.authorize_code.id_token", "identity_providers.oidc.lifespans.custom.*.grants.authorize_code.refresh_token", - "identity_providers.oidc.lifespans.custom.*.grants.implicit.access_token", - "identity_providers.oidc.lifespans.custom.*.grants.implicit.authorize_code", - "identity_providers.oidc.lifespans.custom.*.grants.implicit.id_token", - "identity_providers.oidc.lifespans.custom.*.grants.implicit.refresh_token", "identity_providers.oidc.lifespans.custom.*.grants.client_credentials.access_token", "identity_providers.oidc.lifespans.custom.*.grants.client_credentials.authorize_code", "identity_providers.oidc.lifespans.custom.*.grants.client_credentials.id_token", "identity_providers.oidc.lifespans.custom.*.grants.client_credentials.refresh_token", - "identity_providers.oidc.lifespans.custom.*.grants.refresh_token.access_token", - "identity_providers.oidc.lifespans.custom.*.grants.refresh_token.authorize_code", - "identity_providers.oidc.lifespans.custom.*.grants.refresh_token.id_token", - "identity_providers.oidc.lifespans.custom.*.grants.refresh_token.refresh_token", + "identity_providers.oidc.lifespans.custom.*.grants.implicit.access_token", + "identity_providers.oidc.lifespans.custom.*.grants.implicit.authorize_code", + "identity_providers.oidc.lifespans.custom.*.grants.implicit.id_token", + "identity_providers.oidc.lifespans.custom.*.grants.implicit.refresh_token", "identity_providers.oidc.lifespans.custom.*.grants.jwt_bearer.access_token", "identity_providers.oidc.lifespans.custom.*.grants.jwt_bearer.authorize_code", "identity_providers.oidc.lifespans.custom.*.grants.jwt_bearer.id_token", "identity_providers.oidc.lifespans.custom.*.grants.jwt_bearer.refresh_token", - "identity_providers.oidc.claims_policies.*", - "identity_providers.oidc.claims_policies", - "identity_providers.oidc.claims_policies.*.id_token", - "identity_providers.oidc.claims_policies.*.access_token", - "identity_providers.oidc.claims_policies.*.custom_claims.*", - "identity_providers.oidc.claims_policies.*.custom_claims", - "identity_providers.oidc.claims_policies.*.custom_claims.*.attribute", - "identity_providers.oidc.scopes.*", + "identity_providers.oidc.lifespans.custom.*.grants.refresh_token.access_token", + "identity_providers.oidc.lifespans.custom.*.grants.refresh_token.authorize_code", + "identity_providers.oidc.lifespans.custom.*.grants.refresh_token.id_token", + "identity_providers.oidc.lifespans.custom.*.grants.refresh_token.refresh_token", + "identity_providers.oidc.lifespans.custom.*.id_token", + "identity_providers.oidc.lifespans.custom.*.refresh_token", + "identity_providers.oidc.lifespans.id_token", + "identity_providers.oidc.lifespans.jwt_secured_authorization", + "identity_providers.oidc.lifespans.refresh_token", + "identity_providers.oidc.minimum_parameter_entropy", + "identity_providers.oidc.require_pushed_authorization_requests", "identity_providers.oidc.scopes", + "identity_providers.oidc.scopes.*", "identity_providers.oidc.scopes.*.claims", - "identity_providers.oidc", - "identity_providers.oidc.issuer_certificate_chain", - "identity_providers.oidc.issuer_private_key", - "authentication_backend.password_reset.disable", - "authentication_backend.password_reset.custom_url", - "authentication_backend.refresh_interval", - "authentication_backend.file.path", - "authentication_backend.file.watch", - "authentication_backend.file.password.algorithm", - "authentication_backend.file.password.argon2.variant", - "authentication_backend.file.password.argon2.iterations", - "authentication_backend.file.password.argon2.memory", - "authentication_backend.file.password.argon2.parallelism", - "authentication_backend.file.password.argon2.key_length", - "authentication_backend.file.password.argon2.salt_length", - "authentication_backend.file.password.sha2crypt.variant", - "authentication_backend.file.password.sha2crypt.iterations", - "authentication_backend.file.password.sha2crypt.salt_length", - "authentication_backend.file.password.pbkdf2.variant", - "authentication_backend.file.password.pbkdf2.iterations", - "authentication_backend.file.password.pbkdf2.salt_length", - "authentication_backend.file.password.bcrypt.variant", - "authentication_backend.file.password.bcrypt.cost", - "authentication_backend.file.password.scrypt.iterations", - "authentication_backend.file.password.scrypt.block_size", - "authentication_backend.file.password.scrypt.parallelism", - "authentication_backend.file.password.scrypt.key_length", - "authentication_backend.file.password.scrypt.salt_length", - "authentication_backend.file.password.iterations", - "authentication_backend.file.password.memory", - "authentication_backend.file.password.parallelism", - "authentication_backend.file.password.key_length", - "authentication_backend.file.password.salt_length", - "authentication_backend.file.search.email", - "authentication_backend.file.search.case_insensitive", - "authentication_backend.file.extra_attributes.*", - "authentication_backend.file.extra_attributes", - "authentication_backend.file.extra_attributes.*.multi_valued", - "authentication_backend.file.extra_attributes.*.value_type", - "authentication_backend.ldap.address", - "authentication_backend.ldap.implementation", - "authentication_backend.ldap.timeout", - "authentication_backend.ldap.start_tls", - "authentication_backend.ldap.tls.minimum_version", - "authentication_backend.ldap.tls.maximum_version", - "authentication_backend.ldap.tls.skip_verify", - "authentication_backend.ldap.tls.server_name", - "authentication_backend.ldap.tls.private_key", - "authentication_backend.ldap.tls.certificate_chain", - "authentication_backend.ldap.pooling.enable", - "authentication_backend.ldap.pooling.count", - "authentication_backend.ldap.pooling.retries", - "authentication_backend.ldap.pooling.timeout", - "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_search_mode", - "authentication_backend.ldap.attributes.distinguished_name", - "authentication_backend.ldap.attributes.username", - "authentication_backend.ldap.attributes.display_name", - "authentication_backend.ldap.attributes.family_name", - "authentication_backend.ldap.attributes.given_name", - "authentication_backend.ldap.attributes.middle_name", - "authentication_backend.ldap.attributes.nickname", - "authentication_backend.ldap.attributes.gender", - "authentication_backend.ldap.attributes.birthdate", - "authentication_backend.ldap.attributes.website", - "authentication_backend.ldap.attributes.profile", - "authentication_backend.ldap.attributes.picture", - "authentication_backend.ldap.attributes.zoneinfo", - "authentication_backend.ldap.attributes.locale", - "authentication_backend.ldap.attributes.phone_number", - "authentication_backend.ldap.attributes.phone_extension", - "authentication_backend.ldap.attributes.street_address", - "authentication_backend.ldap.attributes.locality", - "authentication_backend.ldap.attributes.region", - "authentication_backend.ldap.attributes.postal_code", - "authentication_backend.ldap.attributes.country", - "authentication_backend.ldap.attributes.mail", - "authentication_backend.ldap.attributes.member_of", - "authentication_backend.ldap.attributes.group_name", - "authentication_backend.ldap.attributes.extra.*", - "authentication_backend.ldap.attributes.extra", - "authentication_backend.ldap.attributes.extra.*.name", - "authentication_backend.ldap.attributes.extra.*.multi_valued", - "authentication_backend.ldap.attributes.extra.*.value_type", - "authentication_backend.ldap.permit_referrals", - "authentication_backend.ldap.permit_unauthenticated_bind", - "authentication_backend.ldap.permit_feature_detection_failure", - "authentication_backend.ldap.user", - "authentication_backend.ldap.password", - "session.name", - "session.same_site", - "session.expiration", - "session.inactivity", - "session.remember_me", + "identity_validation.elevated_session.characters", + "identity_validation.elevated_session.code_lifespan", + "identity_validation.elevated_session.elevation_lifespan", + "identity_validation.elevated_session.require_second_factor", + "identity_validation.elevated_session.skip_second_factor", + "identity_validation.reset_password.jwt_algorithm", + "identity_validation.reset_password.jwt_lifespan", + "identity_validation.reset_password.jwt_secret", + "log.file_path", + "log.format", + "log.keep_stdout", + "log.level", + "notifier.disable_startup_check", + "notifier.filesystem.filename", + "notifier.smtp.address", + "notifier.smtp.disable_html_emails", + "notifier.smtp.disable_require_tls", + "notifier.smtp.disable_starttls", + "notifier.smtp.host", + "notifier.smtp.identifier", + "notifier.smtp.password", + "notifier.smtp.port", + "notifier.smtp.sender", + "notifier.smtp.startup_check_address", + "notifier.smtp.subject", + "notifier.smtp.timeout", + "notifier.smtp.tls.certificate_chain", + "notifier.smtp.tls.maximum_version", + "notifier.smtp.tls.minimum_version", + "notifier.smtp.tls.private_key", + "notifier.smtp.tls.server_name", + "notifier.smtp.tls.skip_verify", + "notifier.smtp.username", + "notifier.template_path", + "ntp.address", + "ntp.disable_failure", + "ntp.disable_startup_check", + "ntp.max_desync", + "ntp.version", + "password_policy.standard.enabled", + "password_policy.standard.max_length", + "password_policy.standard.min_length", + "password_policy.standard.require_lowercase", + "password_policy.standard.require_number", + "password_policy.standard.require_special", + "password_policy.standard.require_uppercase", + "password_policy.zxcvbn.enabled", + "password_policy.zxcvbn.min_score", + "privacy_policy.enabled", + "privacy_policy.policy_url", + "privacy_policy.require_user_acceptance", + "regulation.ban_time", + "regulation.find_time", + "regulation.max_retries", + "server.address", + "server.asset_path", + "server.buffers.read", + "server.buffers.write", + "server.disable_healthcheck", + "server.endpoints.authz", + "server.endpoints.authz.*", + "server.endpoints.authz.*.authn_strategies", + "server.endpoints.authz.*.authn_strategies[].name", + "server.endpoints.authz.*.authn_strategies[].schemes", + "server.endpoints.authz.*.implementation", + "server.endpoints.enable_expvars", + "server.endpoints.enable_pprof", + "server.headers.csp_template", + "server.timeouts.idle", + "server.timeouts.read", + "server.timeouts.write", + "server.tls.certificate", + "server.tls.client_certificates", + "server.tls.key", "session", - "session.secret", "session.cookies", - "session.cookies[].name", - "session.cookies[].same_site", - "session.cookies[].expiration", - "session.cookies[].inactivity", - "session.cookies[].remember_me", "session.cookies[]", - "session.cookies[].domain", "session.cookies[].authelia_url", "session.cookies[].default_redirection_url", - "session.cookies[]", + "session.cookies[].domain", + "session.cookies[].expiration", + "session.cookies[].inactivity", + "session.cookies[].name", + "session.cookies[].remember_me", + "session.cookies[].same_site", + "session.domain", + "session.expiration", + "session.inactivity", + "session.name", + "session.redis.database_index", + "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", + "session.redis.high_availability.sentinel_name", + "session.redis.high_availability.sentinel_password", + "session.redis.high_availability.sentinel_username", "session.redis.host", - "session.redis.port", - "session.redis.timeout", "session.redis.max_retries", - "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.password", + "session.redis.port", + "session.redis.timeout", + "session.redis.tls.certificate_chain", "session.redis.tls.maximum_version", - "session.redis.tls.skip_verify", - "session.redis.tls.server_name", + "session.redis.tls.minimum_version", "session.redis.tls.private_key", - "session.redis.tls.certificate_chain", - "session.redis.high_availability.sentinel_name", - "session.redis.high_availability.sentinel_username", - "session.redis.high_availability.sentinel_password", - "session.redis.high_availability.route_by_latency", - "session.redis.high_availability.route_randomly", - "session.redis.high_availability.nodes", - "session.redis.high_availability.nodes[].host", - "session.redis.high_availability.nodes[].port", - "session.domain", - "totp.disable", - "totp.issuer", - "totp.algorithm", - "totp.digits", - "totp.period", - "totp.skew", - "totp.secret_size", - "totp.allowed_algorithms", - "totp.allowed_digits", - "totp.allowed_periods", - "totp.disable_reuse_security_policy", - "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", - "access_control.rules[].query[][].operator", - "access_control.rules[].query[][].key", - "access_control.rules[].query[][].value", - "access_control.rules[].query", - "ntp.address", - "ntp.version", - "ntp.max_desync", - "ntp.disable_startup_check", - "ntp.disable_failure", - "regulation.max_retries", - "regulation.find_time", - "regulation.ban_time", + "session.redis.tls.server_name", + "session.redis.tls.skip_verify", + "session.redis.username", + "session.remember_me", + "session.same_site", + "session.secret", + "storage.encryption_key", "storage.local.path", "storage.mysql.address", "storage.mysql.database", - "storage.mysql.username", "storage.mysql.password", "storage.mysql.timeout", - "storage.mysql.tls.minimum_version", + "storage.mysql.tls.certificate_chain", "storage.mysql.tls.maximum_version", - "storage.mysql.tls.skip_verify", - "storage.mysql.tls.server_name", + "storage.mysql.tls.minimum_version", "storage.mysql.tls.private_key", - "storage.mysql.tls.certificate_chain", + "storage.mysql.tls.server_name", + "storage.mysql.tls.skip_verify", + "storage.mysql.username", "storage.postgres.address", "storage.postgres.database", - "storage.postgres.username", "storage.postgres.password", - "storage.postgres.timeout", "storage.postgres.schema", - "storage.postgres.tls.minimum_version", - "storage.postgres.tls.maximum_version", - "storage.postgres.tls.skip_verify", - "storage.postgres.tls.server_name", - "storage.postgres.tls.private_key", - "storage.postgres.tls.certificate_chain", - "storage.postgres.ssl.mode", - "storage.postgres.ssl.root_certificate", + "storage.postgres.servers", + "storage.postgres.servers[].address", + "storage.postgres.servers[].tls", + "storage.postgres.servers[].tls.certificate_chain", + "storage.postgres.servers[].tls.maximum_version", + "storage.postgres.servers[].tls.minimum_version", + "storage.postgres.servers[].tls.private_key", + "storage.postgres.servers[].tls.server_name", + "storage.postgres.servers[].tls.skip_verify", "storage.postgres.ssl.certificate", "storage.postgres.ssl.key", - "storage.encryption_key", - "notifier.disable_startup_check", - "notifier.filesystem.filename", - "notifier.smtp.address", - "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.disable_starttls", - "notifier.smtp.tls.minimum_version", - "notifier.smtp.tls.maximum_version", - "notifier.smtp.tls.skip_verify", - "notifier.smtp.tls.server_name", - "notifier.smtp.tls.private_key", - "notifier.smtp.tls.certificate_chain", - "notifier.smtp.host", - "notifier.smtp.port", - "notifier.template_path", - "server.address", - "server.asset_path", - "server.disable_healthcheck", - "server.tls.certificate", - "server.tls.key", - "server.tls.client_certificates", - "server.headers.csp_template", - "server.endpoints.enable_pprof", - "server.endpoints.enable_expvars", - "server.endpoints.authz.*", - "server.endpoints.authz", - "server.endpoints.authz.*.implementation", - "server.endpoints.authz.*.authn_strategies", - "server.endpoints.authz.*.authn_strategies[].name", - "server.endpoints.authz.*.authn_strategies[].schemes", - "server.buffers.read", - "server.buffers.write", - "server.timeouts.read", - "server.timeouts.write", - "server.timeouts.idle", - "telemetry.metrics.enabled", + "storage.postgres.ssl.mode", + "storage.postgres.ssl.root_certificate", + "storage.postgres.timeout", + "storage.postgres.tls.certificate_chain", + "storage.postgres.tls.maximum_version", + "storage.postgres.tls.minimum_version", + "storage.postgres.tls.private_key", + "storage.postgres.tls.server_name", + "storage.postgres.tls.skip_verify", + "storage.postgres.username", "telemetry.metrics.address", "telemetry.metrics.buffers.read", "telemetry.metrics.buffers.write", + "telemetry.metrics.enabled", + "telemetry.metrics.timeouts.idle", "telemetry.metrics.timeouts.read", "telemetry.metrics.timeouts.write", - "telemetry.metrics.timeouts.idle", + "theme", + "totp.algorithm", + "totp.allowed_algorithms", + "totp.allowed_digits", + "totp.allowed_periods", + "totp.digits", + "totp.disable", + "totp.disable_reuse_security_policy", + "totp.issuer", + "totp.period", + "totp.secret_size", + "totp.skew", + "webauthn.attestation_conveyance_preference", "webauthn.disable", + "webauthn.display_name", "webauthn.enable_passkey_login", "webauthn.experimental_enable_passkey_uv_two_factors", - "webauthn.display_name", - "webauthn.attestation_conveyance_preference", - "webauthn.timeout", - "webauthn.filtering.prohibit_backup_eligibility", "webauthn.filtering.permitted_aaguids", + "webauthn.filtering.prohibit_backup_eligibility", "webauthn.filtering.prohibited_aaguids", - "webauthn.selection_criteria.attachment", - "webauthn.selection_criteria.discoverability", - "webauthn.selection_criteria.user_verification", "webauthn.metadata.enabled", - "webauthn.metadata.validate_trust_anchor", "webauthn.metadata.validate_entry", "webauthn.metadata.validate_entry_permit_zero_aaguid", "webauthn.metadata.validate_status", "webauthn.metadata.validate_status_permitted", "webauthn.metadata.validate_status_prohibited", - "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", - "privacy_policy.enabled", - "privacy_policy.require_user_acceptance", - "privacy_policy.policy_url", - "identity_validation.reset_password.jwt_lifespan", - "identity_validation.reset_password.jwt_algorithm", - "identity_validation.reset_password.jwt_secret", - "identity_validation.elevated_session.code_lifespan", - "identity_validation.elevated_session.elevation_lifespan", - "identity_validation.elevated_session.characters", - "identity_validation.elevated_session.require_second_factor", - "identity_validation.elevated_session.skip_second_factor", - "definitions.network.*", - "definitions.network.*", - "definitions.network", - "definitions.user_attributes.*", - "definitions.user_attributes", - "definitions.user_attributes.*.expression", - "default_redirection_url", + "webauthn.metadata.validate_trust_anchor", + "webauthn.selection_criteria.attachment", + "webauthn.selection_criteria.discoverability", + "webauthn.selection_criteria.user_verification", + "webauthn.timeout", } diff --git a/internal/configuration/schema/storage.go b/internal/configuration/schema/storage.go index b51982a59..4bdf397f5 100644 --- a/internal/configuration/schema/storage.go +++ b/internal/configuration/schema/storage.go @@ -22,31 +22,36 @@ type StorageLocal struct { // StorageSQL represents the configuration of the SQL database. type StorageSQL struct { - Address *AddressTCP `koanf:"address" json:"address" jsonschema:"title=Address" jsonschema_description:"The address of the database."` + Address *AddressTCP `koanf:"address" json:"address" jsonschema:"title=Address" jsonschema_description:"The address of the SQL Server."` Database string `koanf:"database" json:"database" jsonschema:"title=Database" jsonschema_description:"The database name to use upon a successful connection."` Username string `koanf:"username" json:"username" jsonschema:"title=Username" jsonschema_description:"The username to use to authenticate."` Password string `koanf:"password" json:"password" jsonschema:"title=Password" jsonschema_description:"The password to use to authenticate."` Timeout time.Duration `koanf:"timeout" json:"timeout" jsonschema:"default=5 seconds,title=Timeout" jsonschema_description:"The timeout for the database connection."` + TLS *TLS `koanf:"tls" json:"tls"` } // StorageMySQL represents the configuration of a MySQL database. type StorageMySQL struct { StorageSQL `koanf:",squash"` - - TLS *TLS `koanf:"tls" json:"tls"` } // StoragePostgreSQL represents the configuration of a PostgreSQL database. type StoragePostgreSQL struct { StorageSQL `koanf:",squash"` - Schema string `koanf:"schema" json:"schema" jsonschema:"default=public,title=Schema" jsonschema_description:"The default schema name to use."` - TLS *TLS `koanf:"tls" json:"tls"` + Schema string `koanf:"schema" json:"schema" jsonschema:"default=public,title=Schema" jsonschema_description:"The default schema name to use."` + + Servers []StoragePostgreSQLServer `koanf:"servers" json:"servers" jsonschema:"title=Servers" jsonschema_description:"The fallback PostgreSQL severs to connect to in addition to the one defined by the address."` // Deprecated: Use the TLS configuration instead. SSL *StoragePostgreSQLSSL `koanf:"ssl" json:"ssl" jsonschema:"deprecated,title=SSL"` } +type StoragePostgreSQLServer struct { + Address *AddressTCP `koanf:"address" json:"address" jsonschema:"title=Address" jsonschema_description:"The address of the PostgreSQL Server."` + TLS *TLS `koanf:"tls" json:"tls"` +} + // StoragePostgreSQLSSL represents the SSL configuration of a PostgreSQL database. type StoragePostgreSQLSSL struct { Mode string `koanf:"mode" json:"mode" jsonschema:"deprecated,enum=disable,enum=verify-ca,enum=require,enum=verify-full,title=Mode" jsonschema_description:"The SSL mode to use, deprecated and replaced with the TLS options."` @@ -64,9 +69,9 @@ var DefaultSQLStorageConfiguration = StorageSQL{ var DefaultMySQLStorageConfiguration = StorageMySQL{ StorageSQL: StorageSQL{ Address: &AddressTCP{Address{true, false, -1, 3306, &url.URL{Scheme: AddressSchemeTCP, Host: "localhost:3306"}}}, - }, - TLS: &TLS{ - MinimumVersion: TLSVersion{tls.VersionTLS12}, + TLS: &TLS{ + MinimumVersion: TLSVersion{tls.VersionTLS12}, + }, }, } @@ -74,11 +79,19 @@ var DefaultMySQLStorageConfiguration = StorageMySQL{ var DefaultPostgreSQLStorageConfiguration = StoragePostgreSQL{ StorageSQL: StorageSQL{ Address: &AddressTCP{Address{true, false, -1, 5432, &url.URL{Scheme: AddressSchemeTCP, Host: "localhost:5432"}}}, + TLS: &TLS{ + MinimumVersion: TLSVersion{tls.VersionTLS12}, + }, }, - Schema: "public", - TLS: &TLS{ - MinimumVersion: TLSVersion{tls.VersionTLS12}, + Servers: []StoragePostgreSQLServer{ + { + Address: &AddressTCP{Address{true, false, -1, 5432, &url.URL{Scheme: AddressSchemeTCP, Host: "localhost:5432"}}}, + TLS: &TLS{ + MinimumVersion: TLSVersion{tls.VersionTLS12}, + }, + }, }, + Schema: "public", SSL: &StoragePostgreSQLSSL{ Mode: "disable", }, diff --git a/internal/configuration/validator/const.go b/internal/configuration/validator/const.go index 7298f293d..396423982 100644 --- a/internal/configuration/validator/const.go +++ b/internal/configuration/validator/const.go @@ -155,6 +155,7 @@ const ( errStrStorageMultiple = "storage: option 'local', 'mysql' and 'postgres' are mutually exclusive but %s have been configured" errStrStorageEncryptionKeyMustBeProvided = "storage: option 'encryption_key' is required" errStrStorageEncryptionKeyTooShort = "storage: option 'encryption_key' must be 20 characters or longer" + errFmtStorageAddressValidate = "storage: %s: option 'address' with value '%s' is invalid: %w" errFmtStorageUserPassMustBeProvided = "storage: %s: option 'username' and 'password' are required" //nolint:gosec errFmtStorageOptionMustBeProvided = "storage: %s: option '%s' is required" errFmtStorageOptionAddressConflictWithHostPort = "storage: %s: option 'host' and 'port' can't be configured at the same time as 'address'" diff --git a/internal/configuration/validator/storage.go b/internal/configuration/validator/storage.go index d0ad1bb7d..647847dc9 100644 --- a/internal/configuration/validator/storage.go +++ b/internal/configuration/validator/storage.go @@ -62,7 +62,7 @@ func validateSQLConfiguration(config, defaults *schema.StorageSQL, validator *sc var err error if err = config.Address.ValidateSQL(); err != nil { - validator.Push(fmt.Errorf(errFmtServerAddress, config.Address.String(), err)) + validator.Push(fmt.Errorf(errFmtStorageAddressValidate, provider, config.Address.String(), err)) } } @@ -132,6 +132,46 @@ func validatePostgreSQLConfiguration(config *schema.StoragePostgreSQL, validator validator.Push(fmt.Errorf(errFmtStoragePostgreSQLInvalidSSLMode, utils.StringJoinOr(validStoragePostgreSQLSSLModes), config.SSL.Mode)) //nolint:staticcheck } } + + validatePostgreSQLConfigurationServers(config, validator) +} + +func validatePostgreSQLConfigurationServers(config *schema.StoragePostgreSQL, validator *schema.StructValidator) { + for i, server := range config.Servers { + description := fmt.Sprintf("postgres: servers: #%d", i+1) + + if server.Address == nil { + validator.Push(fmt.Errorf(errFmtStorageOptionMustBeProvided, description, "address")) + } else { + var err error + + if err = server.Address.ValidateSQL(); err != nil { + validator.Push(fmt.Errorf(errFmtStorageAddressValidate, description, server.Address.String(), err)) + } + } + + if server.Address != nil && server.Address.IsTCP() && server.Address.Port() == 0 { + server.Address.SetPort(schema.DefaultPostgreSQLStorageConfiguration.Address.Port()) + } + + if server.TLS != nil { + configDefaultTLS := &schema.TLS{ + ServerName: server.Address.Hostname(), + } + + if config.TLS != nil { + configDefaultTLS.MinimumVersion = config.TLS.MinimumVersion + configDefaultTLS.MaximumVersion = config.TLS.MaximumVersion + } else { + configDefaultTLS.MinimumVersion = schema.DefaultPostgreSQLStorageConfiguration.TLS.MinimumVersion + configDefaultTLS.MaximumVersion = schema.DefaultPostgreSQLStorageConfiguration.TLS.MaximumVersion + } + + if err := ValidateTLSConfig(server.TLS, configDefaultTLS); err != nil { + validator.Push(fmt.Errorf(errFmtStorageTLSConfigInvalid, description, err)) + } + } + } } func validateLocalStorageConfiguration(config *schema.StorageLocal, validator *schema.StructValidator) { diff --git a/internal/configuration/validator/storage_test.go b/internal/configuration/validator/storage_test.go index fae3ab681..8cf5c83f8 100644 --- a/internal/configuration/validator/storage_test.go +++ b/internal/configuration/validator/storage_test.go @@ -32,7 +32,7 @@ func (suite *StorageSuite) TestShouldValidateOneStorageIsConfigured() { suite.Require().Len(suite.val.Warnings(), 0) suite.Require().Len(suite.val.Errors(), 1) - suite.Assert().EqualError(suite.val.Errors()[0], "storage: configuration for a 'local', 'mysql' or 'postgres' database must be provided") + suite.EqualError(suite.val.Errors()[0], "storage: configuration for a 'local', 'mysql' or 'postgres' database must be provided") } func (suite *StorageSuite) TestShouldValidateMultipleStorageIsConfigured() { @@ -44,7 +44,7 @@ func (suite *StorageSuite) TestShouldValidateMultipleStorageIsConfigured() { suite.Require().Len(suite.val.Warnings(), 0) suite.Require().Len(suite.val.Errors(), 1) - suite.Assert().EqualError(suite.val.Errors()[0], "storage: option 'local', 'mysql' and 'postgres' are mutually exclusive but 'local', 'mysql', and 'postgres' have been configured") + suite.EqualError(suite.val.Errors()[0], "storage: option 'local', 'mysql' and 'postgres' are mutually exclusive but 'local', 'mysql', and 'postgres' have been configured") } func (suite *StorageSuite) TestShouldValidateLocalPathIsProvided() { @@ -57,7 +57,7 @@ func (suite *StorageSuite) TestShouldValidateLocalPathIsProvided() { suite.Require().Len(suite.val.Warnings(), 0) suite.Require().Len(suite.val.Errors(), 1) - suite.Assert().EqualError(suite.val.Errors()[0], "storage: local: option 'path' is required") + suite.EqualError(suite.val.Errors()[0], "storage: local: option 'path' is required") suite.val.Clear() suite.config.Local.Path = "/myapth" @@ -73,9 +73,9 @@ func (suite *StorageSuite) TestShouldValidateMySQLHostUsernamePasswordAndDatabas ValidateStorage(suite.config, suite.val) suite.Require().Len(suite.val.Errors(), 3) - suite.Assert().EqualError(suite.val.Errors()[0], "storage: mysql: option 'address' is required") - suite.Assert().EqualError(suite.val.Errors()[1], "storage: mysql: option 'username' and 'password' are required") - suite.Assert().EqualError(suite.val.Errors()[2], "storage: mysql: option 'database' is required") + suite.EqualError(suite.val.Errors()[0], "storage: mysql: option 'address' is required") + suite.EqualError(suite.val.Errors()[1], "storage: mysql: option 'username' and 'password' are required") + suite.EqualError(suite.val.Errors()[2], "storage: mysql: option 'database' is required") suite.val.Clear() suite.config.MySQL = &schema.StorageMySQL{ @@ -101,19 +101,19 @@ func (suite *StorageSuite) TestShouldSetDefaultMySQLTLSServerName() { Username: "myuser", Password: "pass", Database: "database", - }, - TLS: &schema.TLS{ - MinimumVersion: schema.TLSVersion{Value: tls.VersionTLS12}, + TLS: &schema.TLS{ + MinimumVersion: schema.TLSVersion{Value: tls.VersionTLS12}, + }, }, } ValidateStorage(suite.config, suite.val) - suite.Assert().Len(suite.val.Warnings(), 0) - suite.Assert().Len(suite.val.Errors(), 0) + suite.Len(suite.val.Warnings(), 0) + suite.Len(suite.val.Errors(), 0) - suite.Assert().Equal(suite.config.MySQL.Address.Hostname(), suite.config.MySQL.TLS.ServerName) - suite.Assert().Equal("mysql", suite.config.MySQL.TLS.ServerName) + suite.Equal(suite.config.MySQL.Address.Hostname(), suite.config.MySQL.TLS.ServerName) + suite.Equal("mysql", suite.config.MySQL.TLS.ServerName) } func (suite *StorageSuite) TestShouldRaiseErrorOnInvalidMySQLAddressScheme() { @@ -131,7 +131,7 @@ func (suite *StorageSuite) TestShouldRaiseErrorOnInvalidMySQLAddressScheme() { suite.Len(suite.val.Warnings(), 0) suite.Require().Len(suite.val.Errors(), 1) - suite.EqualError(suite.val.Errors()[0], "server: option 'address' with value 'udp://mysql:1234' is invalid: scheme must be one of 'tcp', 'tcp4', 'tcp6', or 'unix' but is configured as 'udp'") + suite.EqualError(suite.val.Errors()[0], "storage: mysql: option 'address' with value 'udp://mysql:1234' is invalid: scheme must be one of 'tcp', 'tcp4', 'tcp6', or 'unix' but is configured as 'udp'") } func (suite *StorageSuite) TestShouldRaiseErrorOnInvalidMySQLTLSVersion() { @@ -143,18 +143,18 @@ func (suite *StorageSuite) TestShouldRaiseErrorOnInvalidMySQLTLSVersion() { Username: "myuser", Password: "pass", Database: "database", - }, - TLS: &schema.TLS{ - MinimumVersion: schema.TLSVersion{Value: tls.VersionSSL30}, //nolint:staticcheck + TLS: &schema.TLS{ + MinimumVersion: schema.TLSVersion{Value: tls.VersionSSL30}, //nolint:staticcheck + }, }, } ValidateStorage(suite.config, suite.val) - suite.Assert().Len(suite.val.Warnings(), 0) + suite.Len(suite.val.Warnings(), 0) suite.Require().Len(suite.val.Errors(), 1) - suite.Assert().EqualError(suite.val.Errors()[0], "storage: mysql: tls: option 'minimum_version' is invalid: minimum version is TLS1.0 but SSL3.0 was configured") + suite.EqualError(suite.val.Errors()[0], "storage: mysql: tls: option 'minimum_version' is invalid: minimum version is TLS1.0 but SSL3.0 was configured") } func (suite *StorageSuite) TestShouldRaiseErrorOnInvalidMySQLTLSMinVersionGreaterThanMaximum() { @@ -166,19 +166,19 @@ func (suite *StorageSuite) TestShouldRaiseErrorOnInvalidMySQLTLSMinVersionGreate Username: "myuser", Password: "pass", Database: "database", - }, - TLS: &schema.TLS{ - MinimumVersion: schema.TLSVersion{Value: tls.VersionTLS13}, - MaximumVersion: schema.TLSVersion{Value: tls.VersionTLS11}, + TLS: &schema.TLS{ + MinimumVersion: schema.TLSVersion{Value: tls.VersionTLS13}, + MaximumVersion: schema.TLSVersion{Value: tls.VersionTLS11}, + }, }, } ValidateStorage(suite.config, suite.val) - suite.Assert().Len(suite.val.Warnings(), 0) + suite.Len(suite.val.Warnings(), 0) suite.Require().Len(suite.val.Errors(), 1) - suite.Assert().EqualError(suite.val.Errors()[0], "storage: mysql: tls: option combination of 'minimum_version' and 'maximum_version' is invalid: minimum version TLS1.3 is greater than the maximum version TLS1.1") + suite.EqualError(suite.val.Errors()[0], "storage: mysql: tls: option combination of 'minimum_version' and 'maximum_version' is invalid: minimum version TLS1.3 is greater than the maximum version TLS1.1") } func (suite *StorageSuite) TestShouldValidatePostgreSQLHostUsernamePasswordAndDatabaseAreProvided() { @@ -187,9 +187,9 @@ func (suite *StorageSuite) TestShouldValidatePostgreSQLHostUsernamePasswordAndDa ValidateStorage(suite.config, suite.val) suite.Require().Len(suite.val.Errors(), 3) - suite.Assert().EqualError(suite.val.Errors()[0], "storage: postgres: option 'address' is required") - suite.Assert().EqualError(suite.val.Errors()[1], "storage: postgres: option 'username' and 'password' are required") - suite.Assert().EqualError(suite.val.Errors()[2], "storage: postgres: option 'database' is required") + suite.EqualError(suite.val.Errors()[0], "storage: postgres: option 'address' is required") + suite.EqualError(suite.val.Errors()[1], "storage: postgres: option 'username' and 'password' are required") + suite.EqualError(suite.val.Errors()[2], "storage: postgres: option 'database' is required") suite.val.Clear() suite.config.PostgreSQL = &schema.StoragePostgreSQL{ @@ -204,8 +204,8 @@ func (suite *StorageSuite) TestShouldValidatePostgreSQLHostUsernamePasswordAndDa } ValidateStorage(suite.config, suite.val) - suite.Assert().Len(suite.val.Warnings(), 0) - suite.Assert().Len(suite.val.Errors(), 0) + suite.Len(suite.val.Warnings(), 0) + suite.Len(suite.val.Errors(), 0) } func (suite *StorageSuite) TestShouldValidatePostgresSchemaDefault() { @@ -222,13 +222,13 @@ func (suite *StorageSuite) TestShouldValidatePostgresSchemaDefault() { ValidateStorage(suite.config, suite.val) - suite.Assert().Len(suite.val.Warnings(), 0) - suite.Assert().Len(suite.val.Errors(), 0) + suite.Len(suite.val.Warnings(), 0) + suite.Len(suite.val.Errors(), 0) - suite.Assert().Nil(suite.config.PostgreSQL.SSL) //nolint:staticcheck - suite.Assert().Nil(suite.config.PostgreSQL.TLS) + suite.Nil(suite.config.PostgreSQL.SSL) //nolint:staticcheck + suite.Nil(suite.config.PostgreSQL.TLS) - suite.Assert().Equal("public", suite.config.PostgreSQL.Schema) + suite.Equal("public", suite.config.PostgreSQL.Schema) } func (suite *StorageSuite) TestShouldValidatePostgresPortDefault() { @@ -261,22 +261,22 @@ func (suite *StorageSuite) TestShouldValidatePostgresTLSDefaults() { Username: "myuser", Password: "pass", Database: "database", + TLS: &schema.TLS{}, }, - TLS: &schema.TLS{}, } ValidateStorage(suite.config, suite.val) - suite.Assert().Len(suite.val.Warnings(), 0) - suite.Assert().Len(suite.val.Errors(), 0) + suite.Len(suite.val.Warnings(), 0) + suite.Len(suite.val.Errors(), 0) - suite.Assert().Nil(suite.config.PostgreSQL.SSL) //nolint:staticcheck + suite.Nil(suite.config.PostgreSQL.SSL) //nolint:staticcheck suite.Require().NotNil(suite.config.PostgreSQL.TLS) - suite.Assert().Equal(uint16(tls.VersionTLS12), suite.config.PostgreSQL.TLS.MinimumVersion.Value) + suite.Equal(uint16(tls.VersionTLS12), suite.config.PostgreSQL.TLS.MinimumVersion.Value) } -func (suite *StorageSuite) TestShouldSetDefaultPostgreSQLTLSServerName() { +func (suite *StorageSuite) TestShouldValidatePostgresServers() { suite.config.PostgreSQL = &schema.StoragePostgreSQL{ StorageSQL: schema.StorageSQL{ Address: &schema.AddressTCP{ @@ -286,20 +286,54 @@ func (suite *StorageSuite) TestShouldSetDefaultPostgreSQLTLSServerName() { Password: "pass", Database: "database", }, - TLS: &schema.TLS{ - MinimumVersion: schema.TLSVersion{Value: tls.VersionTLS12}, + Servers: []schema.StoragePostgreSQLServer{ + { + Address: &schema.AddressTCP{ + Address: MustParseAddress("udp://server1:4321"), + }, + }, + {}, + { + Address: &schema.AddressTCP{ + Address: MustParseAddress("tcp://server1"), + }, + }, + { + Address: &schema.AddressTCP{ + Address: MustParseAddress("tcp://server1:5432"), + }, + TLS: &schema.TLS{}, + }, + { + Address: &schema.AddressTCP{ + Address: MustParseAddress("tcp://server1:5432"), + }, + TLS: &schema.TLS{ + MinimumVersion: schema.TLSVersion{Value: tls.VersionTLS13}, + MaximumVersion: schema.TLSVersion{Value: tls.VersionTLS10}, + }, + }, }, } ValidateStorage(suite.config, suite.val) - suite.Assert().Len(suite.val.Warnings(), 0) - suite.Assert().Len(suite.val.Errors(), 0) + suite.Len(suite.val.Warnings(), 0) + suite.Require().Len(suite.config.PostgreSQL.Servers, 5) - suite.Assert().Equal(suite.config.PostgreSQL.Address.Hostname(), suite.config.PostgreSQL.TLS.ServerName) + suite.Equal(uint16(5432), suite.config.PostgreSQL.Servers[2].Address.Port()) + suite.Equal("server1", suite.config.PostgreSQL.Servers[3].TLS.ServerName) + suite.Equal(schema.TLSVersion{Value: tls.VersionTLS12}, suite.config.PostgreSQL.Servers[3].TLS.MinimumVersion) + + errors := suite.val.Errors() + + suite.Require().Len(errors, 3) + suite.EqualError(errors[0], "storage: postgres: servers: #1: option 'address' with value 'udp://server1:4321' is invalid: scheme must be one of 'tcp', 'tcp4', 'tcp6', or 'unix' but is configured as 'udp'") + suite.EqualError(errors[1], "storage: postgres: servers: #2: option 'address' is required") + suite.EqualError(errors[2], "storage: postgres: servers: #5: tls: option combination of 'minimum_version' and 'maximum_version' is invalid: minimum version TLS1.3 is greater than the maximum version TLS1.0") } -func (suite *StorageSuite) TestShouldRaiseErrorOnInvalidPostgreSQLTLSVersion() { +func (suite *StorageSuite) TestShouldValidatePostgresServersTLSMinMaxFromPrimary() { suite.config.PostgreSQL = &schema.StoragePostgreSQL{ StorageSQL: schema.StorageSQL{ Address: &schema.AddressTCP{ @@ -308,21 +342,77 @@ func (suite *StorageSuite) TestShouldRaiseErrorOnInvalidPostgreSQLTLSVersion() { Username: "myuser", Password: "pass", Database: "database", + TLS: &schema.TLS{ + MinimumVersion: schema.TLSVersion{Value: tls.VersionTLS10}, + MaximumVersion: schema.TLSVersion{Value: tls.VersionTLS13}, + }, }, - TLS: &schema.TLS{ - MinimumVersion: schema.TLSVersion{Value: tls.VersionSSL30}, //nolint:staticcheck + Servers: []schema.StoragePostgreSQLServer{ + { + Address: &schema.AddressTCP{ + Address: MustParseAddress("tcp://server1:5432"), + }, + TLS: &schema.TLS{}, + }, }, } ValidateStorage(suite.config, suite.val) - suite.Assert().Len(suite.val.Warnings(), 0) + suite.Len(suite.val.Warnings(), 0) + suite.Len(suite.val.Errors(), 0) + suite.Require().Len(suite.config.PostgreSQL.Servers, 1) + + suite.Equal("server1", suite.config.PostgreSQL.Servers[0].TLS.ServerName) + suite.Equal(schema.TLSVersion{Value: tls.VersionTLS10}, suite.config.PostgreSQL.Servers[0].TLS.MinimumVersion) + suite.Equal(schema.TLSVersion{Value: tls.VersionTLS13}, suite.config.PostgreSQL.Servers[0].TLS.MaximumVersion) +} + +func (suite *StorageSuite) TestShouldValidatePostgresUDP() { + suite.config.PostgreSQL = &schema.StoragePostgreSQL{ + StorageSQL: schema.StorageSQL{ + Address: &schema.AddressTCP{ + Address: MustParseAddress("udp://postgre:4321"), + }, + Username: "myuser", + Password: "pass", + Database: "database", + TLS: &schema.TLS{ + MinimumVersion: schema.TLSVersion{Value: tls.VersionTLS10}, + MaximumVersion: schema.TLSVersion{Value: tls.VersionTLS13}, + }, + }, + } + + ValidateStorage(suite.config, suite.val) + + suite.Len(suite.val.Warnings(), 0) suite.Require().Len(suite.val.Errors(), 1) - suite.Assert().EqualError(suite.val.Errors()[0], "storage: postgres: tls: option 'minimum_version' is invalid: minimum version is TLS1.0 but SSL3.0 was configured") + suite.EqualError(suite.val.Errors()[0], "storage: postgres: option 'address' with value 'udp://postgre:4321' is invalid: scheme must be one of 'tcp', 'tcp4', 'tcp6', or 'unix' but is configured as 'udp'") } -func (suite *StorageSuite) TestShouldRaiseErrorOnInvalidPostgreSQLMinVersionGreaterThanMaximum() { +func (suite *StorageSuite) TestShouldValidatePostgresSetPort() { + suite.config.PostgreSQL = &schema.StoragePostgreSQL{ + StorageSQL: schema.StorageSQL{ + Address: &schema.AddressTCP{ + Address: MustParseAddress("tcp://postgre"), + }, + Username: "myuser", + Password: "pass", + Database: "database", + }, + } + + ValidateStorage(suite.config, suite.val) + + suite.Len(suite.val.Warnings(), 0) + suite.Len(suite.val.Errors(), 0) + + suite.Equal(uint16(5432), suite.config.PostgreSQL.Address.Port()) +} + +func (suite *StorageSuite) TestShouldSetDefaultPostgreSQLTLSServerName() { suite.config.PostgreSQL = &schema.StoragePostgreSQL{ StorageSQL: schema.StorageSQL{ Address: &schema.AddressTCP{ @@ -331,19 +421,65 @@ func (suite *StorageSuite) TestShouldRaiseErrorOnInvalidPostgreSQLMinVersionGrea Username: "myuser", Password: "pass", Database: "database", + TLS: &schema.TLS{ + MinimumVersion: schema.TLSVersion{Value: tls.VersionTLS12}, + }, }, - TLS: &schema.TLS{ - MinimumVersion: schema.TLSVersion{Value: tls.VersionTLS13}, - MaximumVersion: schema.TLSVersion{Value: tls.VersionTLS11}, + } + + ValidateStorage(suite.config, suite.val) + + suite.Len(suite.val.Warnings(), 0) + suite.Len(suite.val.Errors(), 0) + + suite.Equal(suite.config.PostgreSQL.Address.Hostname(), suite.config.PostgreSQL.TLS.ServerName) +} + +func (suite *StorageSuite) TestShouldRaiseErrorOnInvalidPostgreSQLTLSVersion() { + suite.config.PostgreSQL = &schema.StoragePostgreSQL{ + StorageSQL: schema.StorageSQL{ + Address: &schema.AddressTCP{ + Address: MustParseAddress("tcp://postgre:4321"), + }, + Username: "myuser", + Password: "pass", + Database: "database", + TLS: &schema.TLS{ + MinimumVersion: schema.TLSVersion{Value: tls.VersionSSL30}, //nolint:staticcheck + }, }, } ValidateStorage(suite.config, suite.val) - suite.Assert().Len(suite.val.Warnings(), 0) + suite.Len(suite.val.Warnings(), 0) suite.Require().Len(suite.val.Errors(), 1) - suite.Assert().EqualError(suite.val.Errors()[0], "storage: postgres: tls: option combination of 'minimum_version' and 'maximum_version' is invalid: minimum version TLS1.3 is greater than the maximum version TLS1.1") + suite.EqualError(suite.val.Errors()[0], "storage: postgres: tls: option 'minimum_version' is invalid: minimum version is TLS1.0 but SSL3.0 was configured") +} + +func (suite *StorageSuite) TestShouldRaiseErrorOnInvalidPostgreSQLMinVersionGreaterThanMaximum() { + suite.config.PostgreSQL = &schema.StoragePostgreSQL{ + StorageSQL: schema.StorageSQL{ + Address: &schema.AddressTCP{ + Address: MustParseAddress("tcp://postgre:4321"), + }, + Username: "myuser", + Password: "pass", + Database: "database", + TLS: &schema.TLS{ + MinimumVersion: schema.TLSVersion{Value: tls.VersionTLS13}, + MaximumVersion: schema.TLSVersion{Value: tls.VersionTLS11}, + }, + }, + } + + ValidateStorage(suite.config, suite.val) + + suite.Len(suite.val.Warnings(), 0) + suite.Require().Len(suite.val.Errors(), 1) + + suite.EqualError(suite.val.Errors()[0], "storage: postgres: tls: option combination of 'minimum_version' and 'maximum_version' is invalid: minimum version TLS1.3 is greater than the maximum version TLS1.1") } func (suite *StorageSuite) TestShouldValidatePostgresSSLDefaults() { @@ -361,13 +497,13 @@ func (suite *StorageSuite) TestShouldValidatePostgresSSLDefaults() { ValidateStorage(suite.config, suite.val) - suite.Assert().Len(suite.val.Warnings(), 1) - suite.Assert().Len(suite.val.Errors(), 0) + suite.Len(suite.val.Warnings(), 1) + suite.Len(suite.val.Errors(), 0) - suite.Assert().NotNil(suite.config.PostgreSQL.SSL) //nolint:staticcheck + suite.NotNil(suite.config.PostgreSQL.SSL) //nolint:staticcheck suite.Require().Nil(suite.config.PostgreSQL.TLS) - suite.Assert().Equal(schema.DefaultPostgreSQLStorageConfiguration.SSL.Mode, suite.config.PostgreSQL.SSL.Mode) //nolint:staticcheck + suite.Equal(schema.DefaultPostgreSQLStorageConfiguration.SSL.Mode, suite.config.PostgreSQL.SSL.Mode) //nolint:staticcheck } func (suite *StorageSuite) TestShouldRaiseErrorOnTLSAndLegacySSL() { @@ -379,17 +515,17 @@ func (suite *StorageSuite) TestShouldRaiseErrorOnTLSAndLegacySSL() { Username: "myuser", Password: "pass", Database: "database", + TLS: &schema.TLS{}, }, SSL: &schema.StoragePostgreSQLSSL{}, - TLS: &schema.TLS{}, } ValidateStorage(suite.config, suite.val) - suite.Assert().Len(suite.val.Warnings(), 0) + suite.Len(suite.val.Warnings(), 0) suite.Require().Len(suite.val.Errors(), 1) - suite.Assert().EqualError(suite.val.Errors()[0], "storage: postgres: can't define both 'tls' and 'ssl' configuration options") + suite.EqualError(suite.val.Errors()[0], "storage: postgres: can't define both 'tls' and 'ssl' configuration options") } func (suite *StorageSuite) TestShouldValidatePostgresDefaultsDontOverrideConfiguration() { @@ -411,12 +547,12 @@ func (suite *StorageSuite) TestShouldValidatePostgresDefaultsDontOverrideConfigu ValidateStorage(suite.config, suite.val) suite.Require().Len(suite.val.Warnings(), 1) - suite.Assert().Len(suite.val.Errors(), 0) + suite.Len(suite.val.Errors(), 0) - suite.Assert().Equal("require", suite.config.PostgreSQL.SSL.Mode) //nolint:staticcheck - suite.Assert().Equal("authelia", suite.config.PostgreSQL.Schema) + suite.Equal("require", suite.config.PostgreSQL.SSL.Mode) //nolint:staticcheck + suite.Equal("authelia", suite.config.PostgreSQL.Schema) - suite.Assert().EqualError(suite.val.Warnings()[0], "storage: postgres: ssl: the ssl configuration options are deprecated and we recommend the tls options instead") + suite.EqualError(suite.val.Warnings()[0], "storage: postgres: ssl: the ssl configuration options are deprecated and we recommend the tls options instead") } func (suite *StorageSuite) TestShouldValidatePostgresSSLModeMustBeValid() { @@ -436,9 +572,9 @@ func (suite *StorageSuite) TestShouldValidatePostgresSSLModeMustBeValid() { ValidateStorage(suite.config, suite.val) - suite.Assert().Len(suite.val.Warnings(), 1) + suite.Len(suite.val.Warnings(), 1) suite.Require().Len(suite.val.Errors(), 1) - suite.Assert().EqualError(suite.val.Errors()[0], "storage: postgres: ssl: option 'mode' must be one of 'disable', 'require', 'verify-ca', or 'verify-full' but it's configured as 'unknown'") + suite.EqualError(suite.val.Errors()[0], "storage: postgres: ssl: option 'mode' must be one of 'disable', 'require', 'verify-ca', or 'verify-full' but it's configured as 'unknown'") } func (suite *StorageSuite) TestShouldRaiseErrorOnNoEncryptionKey() { @@ -451,7 +587,7 @@ func (suite *StorageSuite) TestShouldRaiseErrorOnNoEncryptionKey() { suite.Require().Len(suite.val.Warnings(), 0) suite.Require().Len(suite.val.Errors(), 1) - suite.Assert().EqualError(suite.val.Errors()[0], "storage: option 'encryption_key' is required") + suite.EqualError(suite.val.Errors()[0], "storage: option 'encryption_key' is required") } func (suite *StorageSuite) TestShouldRaiseErrorOnShortEncryptionKey() { @@ -464,7 +600,7 @@ func (suite *StorageSuite) TestShouldRaiseErrorOnShortEncryptionKey() { suite.Require().Len(suite.val.Warnings(), 0) suite.Require().Len(suite.val.Errors(), 1) - suite.Assert().EqualError(suite.val.Errors()[0], "storage: option 'encryption_key' must be 20 characters or longer") + suite.EqualError(suite.val.Errors()[0], "storage: option 'encryption_key' must be 20 characters or longer") } func TestShouldRunStorageSuite(t *testing.T) { diff --git a/internal/storage/sql_provider_backend_mysql_test.go b/internal/storage/sql_provider_backend_mysql_test.go new file mode 100644 index 000000000..f5bd2bee7 --- /dev/null +++ b/internal/storage/sql_provider_backend_mysql_test.go @@ -0,0 +1,60 @@ +package storage + +import ( + "crypto/tls" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/authelia/authelia/v4/internal/configuration/schema" +) + +func TestNewMySQLProvider(t *testing.T) { + standardAddress, err := schema.NewAddress("tcp://mysql") + require.NoError(t, err) + + testCases := []struct { + name string + have *schema.Configuration + }{ + { + "ShouldHandleBasic", + &schema.Configuration{ + Storage: schema.Storage{ + MySQL: &schema.StorageMySQL{ + StorageSQL: schema.StorageSQL{ + Address: &schema.AddressTCP{Address: *standardAddress}, + Database: "authelia", + }, + }, + }, + }, + }, + { + "ShouldHandleTLS", + &schema.Configuration{ + Storage: schema.Storage{ + MySQL: &schema.StorageMySQL{ + StorageSQL: schema.StorageSQL{ + Address: &schema.AddressTCP{Address: *standardAddress}, + Database: "authelia", + TLS: &schema.TLS{ + MinimumVersion: schema.TLSVersion{Value: tls.VersionTLS12}, + MaximumVersion: schema.TLSVersion{Value: tls.VersionTLS13}, + SkipVerify: false, + ServerName: "mysql", + }, + }, + }, + }, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + assert.NotNil(t, NewMySQLProvider(tc.have, nil)) + }) + } +} diff --git a/internal/storage/sql_provider_backend_postgres.go b/internal/storage/sql_provider_backend_postgres.go index a410e01fd..daa7eef02 100644 --- a/internal/storage/sql_provider_backend_postgres.go +++ b/internal/storage/sql_provider_backend_postgres.go @@ -11,6 +11,7 @@ import ( "strconv" "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" "github.com/jackc/pgx/v5/stdlib" "github.com/authelia/authelia/v4/internal/configuration/schema" @@ -174,7 +175,12 @@ func dsnPostgreSQL(config *schema.StoragePostgreSQL, globalCACertPool *x509.Cert dsnConfig.TLSConfig = loadPostgreSQLTLSConfig(config, globalCACertPool) dsnConfig.ConnectTimeout = config.Timeout dsnConfig.RuntimeParams = map[string]string{ - "search_path": config.Schema, + "application_name": fmt.Sprintf("Authelia %s", utils.Version()), + "search_path": config.Schema, + } + + if len(config.Servers) != 0 { + dsnPostgreSQLFallbacks(config, globalCACertPool, dsnConfig) } return stdlib.RegisterConnConfig(dsnConfig) @@ -207,20 +213,40 @@ func dsnPostgreSQLHostPort(address *schema.AddressTCP) (host string, port uint16 return host, port } +func dsnPostgreSQLFallbacks(config *schema.StoragePostgreSQL, globalCACertPool *x509.CertPool, dsnConfig *pgx.ConnConfig) { + dsnConfig.Fallbacks = make([]*pgconn.FallbackConfig, len(config.Servers)) + + for i, server := range config.Servers { + fallback := &pgconn.FallbackConfig{ + TLSConfig: loadPostgreSQLModernTLSConfig(server.TLS, globalCACertPool), + } + + fallback.Host, fallback.Port = dsnPostgreSQLHostPort(server.Address) + + if fallback.Port == 0 && !server.Address.IsUnixDomainSocket() { + fallback.Port = 5432 + } + + dsnConfig.Fallbacks[i] = fallback + } +} + func loadPostgreSQLTLSConfig(config *schema.StoragePostgreSQL, globalCACertPool *x509.CertPool) (tlsConfig *tls.Config) { if config.TLS != nil { - return utils.NewTLSConfig(config.TLS, globalCACertPool) + return loadPostgreSQLModernTLSConfig(config.TLS, globalCACertPool) + } else if config.SSL != nil { //nolint:staticcheck + return loadPostgreSQLLegacyTLSConfig(config, globalCACertPool) } - return loadPostgreSQLLegacyTLSConfig(config, globalCACertPool) + return nil +} + +func loadPostgreSQLModernTLSConfig(config *schema.TLS, globalCACertPool *x509.CertPool) (tlsConfig *tls.Config) { + return utils.NewTLSConfig(config, globalCACertPool) } //nolint:staticcheck // Used for legacy purposes. func loadPostgreSQLLegacyTLSConfig(config *schema.StoragePostgreSQL, globalCACertPool *x509.CertPool) (tlsConfig *tls.Config) { - if config.SSL == nil { - return nil - } - var ( ca *x509.Certificate certs []tls.Certificate @@ -238,7 +264,7 @@ func loadPostgreSQLLegacyTLSConfig(config *schema.StoragePostgreSQL, globalCACer case nil: caCertPool = globalCACertPool default: - caCertPool = globalCACertPool.Clone() + caCertPool = globalCACertPool caCertPool.AddCert(ca) } diff --git a/internal/storage/sql_provider_backend_postgres_test.go b/internal/storage/sql_provider_backend_postgres_test.go index 4a56c25a1..a8ed6d584 100644 --- a/internal/storage/sql_provider_backend_postgres_test.go +++ b/internal/storage/sql_provider_backend_postgres_test.go @@ -38,10 +38,10 @@ func TestNewPostgreSQLProvider(t *testing.T) { PostgreSQL: &schema.StoragePostgreSQL{ StorageSQL: schema.StorageSQL{ Address: &schema.AddressTCP{Address: *address}, - }, - TLS: &schema.TLS{ - MinimumVersion: schema.TLSVersion{Value: tls.VersionTLS12}, - MaximumVersion: schema.TLSVersion{Value: tls.VersionTLS13}, + TLS: &schema.TLS{ + MinimumVersion: schema.TLSVersion{Value: tls.VersionTLS12}, + MaximumVersion: schema.TLSVersion{Value: tls.VersionTLS13}, + }, }, }, }, diff --git a/internal/storage/sql_provider_backend_sqlite_test.go b/internal/storage/sql_provider_backend_sqlite_test.go new file mode 100644 index 000000000..a591d0bed --- /dev/null +++ b/internal/storage/sql_provider_backend_sqlite_test.go @@ -0,0 +1,44 @@ +package storage + +import ( + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/authelia/authelia/v4/internal/configuration/schema" +) + +func TestNewSQLiteProvider(t *testing.T) { + dir := t.TempDir() + testCases := []struct { + name string + have *schema.Configuration + }{ + { + "ShouldHandleBasic", + &schema.Configuration{ + Storage: schema.Storage{ + Local: &schema.StorageLocal{ + Path: filepath.Join(dir, "sqlite1.db"), + }, + }, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + assert.NotNil(t, NewSQLiteProvider(tc.have)) + }) + } +} + +func TestSQLiteRegisteredFuncs(t *testing.T) { + output := sqlite3BLOBToTEXTBase64([]byte("example")) + assert.Equal(t, "ZXhhbXBsZQ==", output) + + decoded, err := sqlite3TEXTBase64ToBLOB("ZXhhbXBsZQ==") + assert.NoError(t, err) + assert.Equal(t, []byte("example"), decoded) +} |
