Merge remote-tracking branch 'origin/master' into feat-settings-ui
# Conflicts: # api/openapi.ymlpull/4806/head
|
@ -3,7 +3,7 @@
|
||||||
set +u
|
set +u
|
||||||
|
|
||||||
declare -A BUILDS=(["linux"]="amd64 arm arm64 amd64-musl arm-musl arm64-musl" ["freebsd"]="amd64")
|
declare -A BUILDS=(["linux"]="amd64 arm arm64 amd64-musl arm-musl arm64-musl" ["freebsd"]="amd64")
|
||||||
DOCKER_IMAGE=authelia/authelia
|
DOCKER_IMAGE=authelia:dist
|
||||||
|
|
||||||
if [[ "${BUILDKITE_LABEL}" == ":hammer_and_wrench: Unit Test" ]]; then
|
if [[ "${BUILDKITE_LABEL}" == ":hammer_and_wrench: Unit Test" ]]; then
|
||||||
if [[ ! "${BUILDKITE_BRANCH}" =~ ^renovate/ ]]; then
|
if [[ ! "${BUILDKITE_BRANCH}" =~ ^renovate/ ]]; then
|
||||||
|
@ -35,4 +35,4 @@ if [[ "${BUILDKITE_LABEL}" =~ ":debian: Build Package" ]]; then
|
||||||
for f in *.deb; do mv "$f" "$(echo "$f" | sed s/${VERSION}-1_//)"; done
|
for f in *.deb; do mv "$f" "$(echo "$f" | sed s/${VERSION}-1_//)"; done
|
||||||
fi
|
fi
|
||||||
sha256sum "authelia_${PACKAGE}.deb" > "authelia_${PACKAGE}.deb.sha256"
|
sha256sum "authelia_${PACKAGE}.deb" > "authelia_${PACKAGE}.deb.sha256"
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -17,7 +17,7 @@ if [[ "${BUILDKITE_LABEL}" == ":service_dog: Linting" ]]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "${BUILDKITE_LABEL}" == ":docker: Build Image [coverage]" ]]; then
|
if [[ "${BUILDKITE_LABEL}" == ":docker: Build Image [coverage]" ]]; then
|
||||||
cp -R /buildkite/.pnpm-store .
|
cp -R /buildkite/.local .
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "${BUILDKITE_STEP_KEY}" =~ build-deb-package-(arm64|armhf) && "${BUILDKITE_AGENT_NAME}" =~ sauron* ]]; then
|
if [[ "${BUILDKITE_STEP_KEY}" =~ build-deb-package-(arm64|armhf) && "${BUILDKITE_AGENT_NAME}" =~ sauron* ]]; then
|
||||||
|
@ -30,10 +30,13 @@ fi
|
||||||
|
|
||||||
if [[ "${BUILDKITE_LABEL}" =~ ":selenium:" ]]; then
|
if [[ "${BUILDKITE_LABEL}" =~ ":selenium:" ]]; then
|
||||||
DEFAULT_ARCH=coverage
|
DEFAULT_ARCH=coverage
|
||||||
echo "--- :docker: Extract, load and tag build container"
|
echo "--- :docker: Extract and load build container"
|
||||||
buildkite-agent artifact download "authelia-image-${DEFAULT_ARCH}*" .
|
buildkite-agent artifact download "authelia-image-${DEFAULT_ARCH}*" .
|
||||||
zstdcat "authelia-image-${DEFAULT_ARCH}.tar.zst" | docker load
|
if [[ "${SUITE}" == "Kubernetes" ]]; then
|
||||||
docker tag authelia/authelia authelia:dist
|
zstd -d authelia-image-coverage.tar.zst --stdout > ./internal/suites/example/kube/authelia-image-${DEFAULT_ARCH}.tar
|
||||||
|
else
|
||||||
|
zstdcat "authelia-image-${DEFAULT_ARCH}.tar.zst" | docker load
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ "${BUILD_DUO}" == "true" ]] && [[ "${SUITE}" == "DuoPush" ]]; then
|
if [[ "${BUILD_DUO}" == "true" ]] && [[ "${SUITE}" == "DuoPush" ]]; then
|
||||||
CONTAINER="integration-duo"
|
CONTAINER="integration-duo"
|
||||||
|
|
|
@ -7,7 +7,11 @@ trim_trailing_whitespace = true
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
|
||||||
[{.buildkite/hooks/**,*.sh,*.yml,*.yaml}]
|
[*.{sh,yml,yaml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[.buildkite/hooks/**]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ body:
|
||||||
description: What version(s) of Authelia can you reproduce this bug on?
|
description: What version(s) of Authelia can you reproduce this bug on?
|
||||||
multiple: true
|
multiple: true
|
||||||
options:
|
options:
|
||||||
|
- v4.37.3
|
||||||
- v4.37.2
|
- v4.37.2
|
||||||
- v4.37.1
|
- v4.37.1
|
||||||
- v4.37.0
|
- v4.37.0
|
||||||
|
|
|
@ -1,50 +1,27 @@
|
||||||
|
# IDE user configuration
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
|
||||||
# NodeJs modules
|
# Nodejs modules
|
||||||
|
.pnpm-store/
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|
||||||
# npm debug logs
|
|
||||||
npm-debug.log*
|
|
||||||
|
|
||||||
# Coverage reports
|
# Coverage reports
|
||||||
coverage/
|
coverage/
|
||||||
|
.nyc_output/
|
||||||
coverage.txt
|
coverage.txt
|
||||||
|
|
||||||
.vscode/
|
|
||||||
|
|
||||||
*.swp
|
|
||||||
*~
|
|
||||||
|
|
||||||
# Directory used by example
|
|
||||||
/notifications/
|
|
||||||
|
|
||||||
# VSCode user configuration
|
|
||||||
.vscode/
|
|
||||||
|
|
||||||
# Generated by TypeScript compiler
|
# Generated by TypeScript compiler
|
||||||
dist/
|
dist/
|
||||||
|
|
||||||
.nyc_output/
|
|
||||||
|
|
||||||
*.tgz
|
|
||||||
|
|
||||||
# Specific files
|
# Specific files
|
||||||
/configuration.yml
|
/configuration.yml
|
||||||
/config.yml
|
/config.yml
|
||||||
/config.test.yml
|
/config.test.yml
|
||||||
|
|
||||||
internal/suites/example/ldap/private.ldif
|
|
||||||
|
|
||||||
Configuration.schema.json
|
|
||||||
|
|
||||||
.suite
|
.suite
|
||||||
.kube
|
.kube
|
||||||
.idea
|
authelia-image-dev.tar
|
||||||
|
|
||||||
.authelia-interrupt
|
/authelia
|
||||||
|
|
||||||
qemu-*-static
|
|
||||||
|
|
||||||
public_html.gen.go
|
|
||||||
|
|
||||||
authelia
|
|
||||||
__debug_bin
|
__debug_bin
|
||||||
|
|
14
.renovaterc
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
"extends": [
|
"extends": [
|
||||||
"config:base",
|
"config:base",
|
||||||
":semanticCommitTypeAll(build)",
|
":semanticCommitTypeAll(build)",
|
||||||
|
@ -14,12 +15,15 @@
|
||||||
"workarounds:all"
|
"workarounds:all"
|
||||||
],
|
],
|
||||||
"enabledManagers": [
|
"enabledManagers": [
|
||||||
"bundler",
|
|
||||||
"docker-compose",
|
"docker-compose",
|
||||||
"dockerfile",
|
"dockerfile",
|
||||||
"gomod",
|
"gomod",
|
||||||
|
"kubernetes",
|
||||||
"npm"
|
"npm"
|
||||||
],
|
],
|
||||||
|
"kubernetes": {
|
||||||
|
"fileMatch": ["kube/.+\\.yml$"],
|
||||||
|
},
|
||||||
"labels": [
|
"labels": [
|
||||||
"dependencies"
|
"dependencies"
|
||||||
],
|
],
|
||||||
|
@ -47,6 +51,14 @@
|
||||||
"go"
|
"go"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"datasources": [
|
||||||
|
"kubernetes"
|
||||||
|
],
|
||||||
|
"addLabels": [
|
||||||
|
"kubernetes"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"datasources": [
|
"datasources": [
|
||||||
"npm"
|
"npm"
|
||||||
|
|
|
@ -1,6 +1,13 @@
|
||||||
---
|
---
|
||||||
extends: default
|
extends: default
|
||||||
|
|
||||||
|
locale: en_US.UTF-8
|
||||||
|
|
||||||
|
yaml-files:
|
||||||
|
- '*.yaml'
|
||||||
|
- '*.yml'
|
||||||
|
- '.yamllint'
|
||||||
|
|
||||||
ignore: |
|
ignore: |
|
||||||
docs/pnpm-lock.yaml
|
docs/pnpm-lock.yaml
|
||||||
internal/configuration/test_resources/config_bad_quoting.yml
|
internal/configuration/test_resources/config_bad_quoting.yml
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
Anybody willing to contribute to the project either with code, documentation, security reviews or whatever, are very
|
Anybody willing to contribute to the project either with code, documentation, security reviews or whatever, are very
|
||||||
welcome to create or review pull requests and take part in discussions in any of our public
|
welcome to create or review pull requests and take part in discussions in any of our public
|
||||||
[chat rooms](./README.md#contact-options).
|
[chat rooms](README.md#contact-options).
|
||||||
|
|
||||||
It's also possible to contribute financially in order to support the community.
|
It's also possible to contribute financially in order to support the community.
|
||||||
|
|
||||||
|
@ -42,4 +42,4 @@ Read more about this in the [GitHub docs, Re-requesting a review](https://docs.g
|
||||||
Sometimes the codebase can be a challenge to navigate, especially for a first-time contributor. We don't want you
|
Sometimes the codebase can be a challenge to navigate, especially for a first-time contributor. We don't want you
|
||||||
spending an hour trying to work out something that would take us only a minute to explain.
|
spending an hour trying to work out something that would take us only a minute to explain.
|
||||||
|
|
||||||
If you'd like some help getting started we have several [contact options](./README.md#contact-options) available.
|
If you'd like some help getting started we have several [contact options](README.md#contact-options) available.
|
||||||
|
|
|
@ -5,7 +5,7 @@ FROM node:19-alpine AS builder-frontend
|
||||||
|
|
||||||
WORKDIR /node/src/app
|
WORKDIR /node/src/app
|
||||||
|
|
||||||
COPY .pnpm-store /root/.pnpm-store
|
COPY .local /root/.local
|
||||||
COPY web ./
|
COPY web ./
|
||||||
|
|
||||||
# Install the dependencies and build
|
# Install the dependencies and build
|
||||||
|
@ -15,7 +15,7 @@ RUN yarn global add pnpm && \
|
||||||
# =======================================
|
# =======================================
|
||||||
# ===== Build image for the backend =====
|
# ===== Build image for the backend =====
|
||||||
# =======================================
|
# =======================================
|
||||||
FROM golang:1.19.3-alpine AS builder-backend
|
FROM golang:1.19.4-alpine AS builder-backend
|
||||||
|
|
||||||
WORKDIR /go/src/app
|
WORKDIR /go/src/app
|
||||||
|
|
||||||
|
|
|
@ -20,4 +20,4 @@ bootstrap.sh
|
||||||
|
|
||||||
# Overrides
|
# Overrides
|
||||||
!.healthcheck.env
|
!.healthcheck.env
|
||||||
!.pnpm-store
|
!.local
|
||||||
|
|
|
@ -13,7 +13,7 @@ RUN yarn install --frozen-lockfile && yarn build
|
||||||
# =======================================
|
# =======================================
|
||||||
# ===== Build image for the backend =====
|
# ===== Build image for the backend =====
|
||||||
# =======================================
|
# =======================================
|
||||||
FROM golang:1.19.3-alpine AS builder-backend
|
FROM golang:1.19.4-alpine AS builder-backend
|
||||||
|
|
||||||
WORKDIR /go/src/app
|
WORKDIR /go/src/app
|
||||||
|
|
||||||
|
|
|
@ -184,7 +184,7 @@ Internet (your reverse proxies are) however, it's still the control plane for yo
|
||||||
|
|
||||||
## Contribute
|
## Contribute
|
||||||
|
|
||||||
If you want to contribute to Authelia, please read our [contribution guidelines](./CONTRIBUTING.md).
|
If you want to contribute to Authelia, please read our [contribution guidelines](CONTRIBUTING.md).
|
||||||
|
|
||||||
Authelia exists thanks to all the people who contribute so don't be shy, come chat with us on either [Matrix](#matrix)
|
Authelia exists thanks to all the people who contribute so don't be shy, come chat with us on either [Matrix](#matrix)
|
||||||
or [Discord](#discord) and start contributing too.
|
or [Discord](#discord) and start contributing too.
|
||||||
|
@ -379,7 +379,7 @@ Companies contributing to Authelia via Open Collective will have a special menti
|
||||||
## License
|
## License
|
||||||
|
|
||||||
**Authelia** is **licensed** under the **[Apache 2.0]** license. The terms of the license are detailed in
|
**Authelia** is **licensed** under the **[Apache 2.0]** license. The terms of the license are detailed in
|
||||||
[LICENSE](./LICENSE).
|
[LICENSE](LICENSE).
|
||||||
|
|
||||||
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fauthelia%2Fauthelia.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fauthelia%2Fauthelia?ref=badge_large)
|
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fauthelia%2Fauthelia.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fauthelia%2Fauthelia?ref=badge_large)
|
||||||
|
|
||||||
|
|
|
@ -19,14 +19,14 @@ For more information about [security](https://www.authelia.com/information/secur
|
||||||
|
|
||||||
## Contact Options
|
## Contact Options
|
||||||
|
|
||||||
Several [contact options](./README.md#contact-options) exist, it's important to make sure you contact the maintainers
|
Several [contact options](README.md#contact-options) exist, it's important to make sure you contact the maintainers
|
||||||
privately which is described in each available contact method. The methods include our [security email](./README.md#security),
|
privately which is described in each available contact method. The methods include our [security email](README.md#security),
|
||||||
[Matrix](./README.md#matrix), and [Discord](./README.md#discord).
|
[Matrix](README.md#matrix), and [Discord](README.md#discord).
|
||||||
|
|
||||||
## Credit
|
## Credit
|
||||||
|
|
||||||
Users who report bugs will optionally be credited for the discovery. Both in the [security advisory] and in our
|
Users who report bugs will optionally be credited for the discovery. Both in the [security advisory] and in our
|
||||||
[all contributors](./README.md#contribute) configuration/documentation.
|
[all contributors](README.md#contribute) configuration/documentation.
|
||||||
|
|
||||||
## Process
|
## Process
|
||||||
|
|
||||||
|
|
2158
api/openapi.yml
|
@ -87,12 +87,19 @@ var hostEntries = []HostEntry{
|
||||||
{Domain: "mail.example.com", IP: "192.168.240.100"},
|
{Domain: "mail.example.com", IP: "192.168.240.100"},
|
||||||
{Domain: "duo.example.com", IP: "192.168.240.100"},
|
{Domain: "duo.example.com", IP: "192.168.240.100"},
|
||||||
|
|
||||||
// For Traefik suite.
|
|
||||||
{Domain: "traefik.example.com", IP: "192.168.240.100"},
|
|
||||||
|
|
||||||
// For HAProxy suite.
|
// For HAProxy suite.
|
||||||
{Domain: "haproxy.example.com", IP: "192.168.240.100"},
|
{Domain: "haproxy.example.com", IP: "192.168.240.100"},
|
||||||
|
|
||||||
|
// Kubernetes dashboard.
|
||||||
|
{Domain: "kubernetes.example.com", IP: "192.168.240.100"},
|
||||||
|
|
||||||
|
// OIDC tester app.
|
||||||
|
{Domain: "oidc.example.com", IP: "192.168.240.100"},
|
||||||
|
{Domain: "oidc-public.example.com", IP: "192.168.240.100"},
|
||||||
|
|
||||||
|
// For Traefik suite.
|
||||||
|
{Domain: "traefik.example.com", IP: "192.168.240.100"},
|
||||||
|
|
||||||
// For testing network ACLs.
|
// For testing network ACLs.
|
||||||
{Domain: "proxy-client1.example.com", IP: "192.168.240.201"},
|
{Domain: "proxy-client1.example.com", IP: "192.168.240.201"},
|
||||||
{Domain: "proxy-client2.example.com", IP: "192.168.240.202"},
|
{Domain: "proxy-client2.example.com", IP: "192.168.240.202"},
|
||||||
|
@ -107,12 +114,6 @@ var hostEntries = []HostEntry{
|
||||||
{Domain: "redis-sentinel-0.example.com", IP: "192.168.240.120"},
|
{Domain: "redis-sentinel-0.example.com", IP: "192.168.240.120"},
|
||||||
{Domain: "redis-sentinel-1.example.com", IP: "192.168.240.121"},
|
{Domain: "redis-sentinel-1.example.com", IP: "192.168.240.121"},
|
||||||
{Domain: "redis-sentinel-2.example.com", IP: "192.168.240.122"},
|
{Domain: "redis-sentinel-2.example.com", IP: "192.168.240.122"},
|
||||||
|
|
||||||
// Kubernetes dashboard.
|
|
||||||
{Domain: "kubernetes.example.com", IP: "192.168.240.110"},
|
|
||||||
// OIDC tester app.
|
|
||||||
{Domain: "oidc.example.com", IP: "192.168.240.100"},
|
|
||||||
{Domain: "oidc-public.example.com", IP: "192.168.240.100"},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runCommand(cmd string, args ...string) {
|
func runCommand(cmd string, args ...string) {
|
||||||
|
@ -151,8 +152,8 @@ func createTemporaryDirectory() {
|
||||||
func createPNPMDirectory() {
|
func createPNPMDirectory() {
|
||||||
home := os.Getenv("HOME")
|
home := os.Getenv("HOME")
|
||||||
if home != "" {
|
if home != "" {
|
||||||
bootstrapPrintln("Creating ", home+"/.pnpm-store")
|
bootstrapPrintln("Creating ", home+"/.local/share/pnpm/store")
|
||||||
err := os.MkdirAll(home+"/.pnpm-store", 0755)
|
err := os.MkdirAll(home+"/.local/share/pnpm/store", 0755)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -161,7 +162,7 @@ func createPNPMDirectory() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func pnpmInstall() {
|
func pnpmInstall() {
|
||||||
bootstrapPrintln("Installing web dependences ")
|
bootstrapPrintln("Installing web dependencies ")
|
||||||
|
|
||||||
cwd, err := os.Getwd()
|
cwd, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -167,14 +167,14 @@ section [here](../prologue/common.md#tls-configuration).
|
||||||
|
|
||||||
Sets the base distinguished name container for all LDAP queries. If your LDAP domain is example.com this is usually
|
Sets the base distinguished name container for all LDAP queries. If your LDAP domain is example.com this is usually
|
||||||
`DC=example,DC=com`, however you can fine tune this to be more specific for example to only include objects inside the
|
`DC=example,DC=com`, however you can fine tune this to be more specific for example to only include objects inside the
|
||||||
authelia OU: `OU=authelia,DC=example,DC=com`. This is prefixed with the [additional_users_dn](#additional_users_dn) for
|
authelia OU: `OU=authelia,DC=example,DC=com`. This is prefixed with the [additional_users_dn](#additionalusersdn) for
|
||||||
user searches and [additional_groups_dn](#additional_groups_dn) for groups searches.
|
user searches and [additional_groups_dn](#additionalgroupsdn) for groups searches.
|
||||||
|
|
||||||
### additional_users_dn
|
### additional_users_dn
|
||||||
|
|
||||||
{{< confkey type="string" required="no" >}}
|
{{< confkey type="string" required="no" >}}
|
||||||
|
|
||||||
Additional LDAP path to append to the [base_dn](#base_dn) when searching for users. Useful if you want to restrict
|
Additional LDAP path to append to the [base_dn](#basedn) when searching for users. Useful if you want to restrict
|
||||||
exactly which OU to get users from for either security or performance reasons. For example setting it to
|
exactly which OU to get users from for either security or performance reasons. For example setting it to
|
||||||
`OU=users,OU=people` with a base_dn set to `DC=example,DC=com` will mean user searches will occur in
|
`OU=users,OU=people` with a base_dn set to `DC=example,DC=com` will mean user searches will occur in
|
||||||
`OU=users,OU=people,DC=example,DC=com`.
|
`OU=users,OU=people,DC=example,DC=com`.
|
||||||
|
@ -184,28 +184,31 @@ exactly which OU to get users from for either security or performance reasons. F
|
||||||
{{< confkey type="string" required="situational" >}}
|
{{< confkey type="string" required="situational" >}}
|
||||||
|
|
||||||
*__Note:__ This option is technically required however the [implementation](#implementation) option can implicitly set a
|
*__Note:__ This option is technically required however the [implementation](#implementation) option can implicitly set a
|
||||||
default negating this requirement. Refer to the [filter defaults](#filter-defaults) for more information.*
|
default negating this requirement. Refer to the [filter defaults](../../reference/guides/ldap.md#filter-defaults) for
|
||||||
|
more information.*
|
||||||
|
|
||||||
The LDAP filter to narrow down which users are valid. This is important to set correctly as to exclude disabled users.
|
The LDAP filter to narrow down which users are valid. This is important to set correctly as to exclude disabled users.
|
||||||
The default value is dependent on the [implementation](#implementation), refer to the
|
The default value is dependent on the [implementation](#implementation), refer to the
|
||||||
[attribute defaults](#attribute-defaults) for more information.
|
[attribute defaults](../../reference/guides/ldap.md#attribute-defaults) for more information.
|
||||||
|
|
||||||
### username_attribute
|
### username_attribute
|
||||||
|
|
||||||
{{< confkey type="string" required="situational" >}}
|
{{< confkey type="string" required="situational" >}}
|
||||||
|
|
||||||
*__Note:__ This option is technically required however the [implementation](#implementation) option can implicitly set a
|
*__Note:__ This option is technically required however the [implementation](#implementation) option can implicitly set a
|
||||||
default negating this requirement. Refer to the [attribute defaults](#attribute-defaults) for more information.*
|
default negating this requirement. Refer to the [attribute defaults](../../reference/guides/ldap.md#attribute-defaults)
|
||||||
|
for more information.*
|
||||||
|
|
||||||
The LDAP attribute that maps to the username in *Authelia*. This must contain the `{username_attribute}`
|
The LDAP attribute that maps to the username in *Authelia*. This must contain the `{username_attribute}`
|
||||||
[placeholder](#users-filter-replacements).
|
[placeholder](../../reference/guides/ldap.md#users-filter-replacements).
|
||||||
|
|
||||||
### mail_attribute
|
### mail_attribute
|
||||||
|
|
||||||
{{< confkey type="string" required="situational" >}}
|
{{< confkey type="string" required="situational" >}}
|
||||||
|
|
||||||
*__Note:__ This option is technically required however the [implementation](#implementation) option can implicitly set a
|
*__Note:__ This option is technically required however the [implementation](#implementation) option can implicitly set a
|
||||||
default negating this requirement. Refer to the [attribute defaults](#attribute-defaults) for more information.*
|
default negating this requirement. Refer to the [attribute defaults](../../reference/guides/ldap.md#attribute-defaults)
|
||||||
|
for more information.*
|
||||||
|
|
||||||
The attribute to retrieve which contains the users email addresses. This is important for the device registration and
|
The attribute to retrieve which contains the users email addresses. This is important for the device registration and
|
||||||
password reset processes. The user must have an email address in order for Authelia to perform identity verification
|
password reset processes. The user must have an email address in order for Authelia to perform identity verification
|
||||||
|
@ -294,7 +297,7 @@ characters and the user password is changed to this value.
|
||||||
|
|
||||||
## Refresh Interval
|
## Refresh Interval
|
||||||
|
|
||||||
It's recommended you either use the default [refresh interval](./introduction.md#refresh_interval) or configure this to
|
It's recommended you either use the default [refresh interval](introduction.md#refreshinterval) or configure this to
|
||||||
a value low enough to refresh the user groups and status (deleted, disabled, etc) to adequately secure your environment.
|
a value low enough to refresh the user groups and status (deleted, disabled, etc) to adequately secure your environment.
|
||||||
|
|
||||||
## Important notes
|
## Important notes
|
||||||
|
@ -311,6 +314,6 @@ for your users.
|
||||||
|
|
||||||
- [LDAP Reference Guide](../../reference/guides/ldap.md)
|
- [LDAP Reference Guide](../../reference/guides/ldap.md)
|
||||||
|
|
||||||
[username attribute]: #username_attribute
|
[username attribute]: #usernameattribute
|
||||||
[TechNet wiki]: https://social.technet.microsoft.com/wiki/contents/articles/5392.active-directory-ldap-syntax-filters.aspx
|
[TechNet wiki]: https://social.technet.microsoft.com/wiki/contents/articles/5392.active-directory-ldap-syntax-filters.aspx
|
||||||
[RFC2307]: https://www.rfc-editor.org/rfc/rfc2307.html
|
[RFC2307]: https://www.rfc-editor.org/rfc/rfc2307.html
|
||||||
|
|
|
@ -157,8 +157,8 @@ The HMAC secret used to sign the [JWT]'s. The provided string is hashed to a SHA
|
||||||
purpose of meeting the required format.
|
purpose of meeting the required format.
|
||||||
|
|
||||||
It's __strongly recommended__ this is a
|
It's __strongly recommended__ this is a
|
||||||
[Random Alphanumeric String](../../reference/guides/generating-secure-values.md#generating-a-random-alphanumeric-string) with 64 or more
|
[Random Alphanumeric String](../../reference/guides/generating-secure-values.md#generating-a-random-alphanumeric-string)
|
||||||
characters.
|
with 64 or more characters.
|
||||||
|
|
||||||
### issuer_certificate_chain
|
### issuer_certificate_chain
|
||||||
|
|
||||||
|
@ -173,7 +173,7 @@ as per [RFC7517].
|
||||||
[x5c]: https://www.rfc-editor.org/rfc/rfc7517#section-4.7
|
[x5c]: https://www.rfc-editor.org/rfc/rfc7517#section-4.7
|
||||||
[x5t]: https://www.rfc-editor.org/rfc/rfc7517#section-4.8
|
[x5t]: https://www.rfc-editor.org/rfc/rfc7517#section-4.8
|
||||||
|
|
||||||
The first certificate in the chain must have the public key for the [issuer_private_key](#issuer_private_key), each
|
The first certificate in the chain must have the public key for the [issuer_private_key](#issuerprivatekey), each
|
||||||
certificate in the chain must be valid for the current date, and each certificate in the chain should be signed by the
|
certificate in the chain must be valid for the current date, and each certificate in the chain should be signed by the
|
||||||
certificate immediately following it if present.
|
certificate immediately following it if present.
|
||||||
|
|
||||||
|
@ -185,14 +185,15 @@ certificate immediately following it if present.
|
||||||
especially for containerized deployments.*
|
especially for containerized deployments.*
|
||||||
|
|
||||||
The private key used to sign/encrypt the [OpenID Connect] issued [JWT]'s. The key must be generated by the administrator
|
The private key used to sign/encrypt the [OpenID Connect] issued [JWT]'s. The key must be generated by the administrator
|
||||||
and can be done by following the [Generating an RSA Keypair](../../reference/guides/generating-secure-values.md#generating-an-rsa-keypair) guide.
|
and can be done by following the
|
||||||
|
[Generating an RSA Keypair](../../reference/guides/generating-secure-values.md#generating-an-rsa-keypair) guide.
|
||||||
|
|
||||||
The private key *__MUST__*:
|
The private key *__MUST__*:
|
||||||
* Be a PEM block encoded in the DER base64 format ([RFC4648]).
|
* Be a PEM block encoded in the DER base64 format ([RFC4648]).
|
||||||
* Be an RSA Key.
|
* Be an RSA Key.
|
||||||
* Have a key size of at least 2048 bits.
|
* Have a key size of at least 2048 bits.
|
||||||
|
|
||||||
If the [issuer_certificate_chain](#issuer_certificate_chain) is provided the private key must include matching public
|
If the [issuer_certificate_chain](#issuercertificatechain) is provided the private key must include matching public
|
||||||
key data for the first certificate in the chain.
|
key data for the first certificate in the chain.
|
||||||
|
|
||||||
### access_token_lifespan
|
### access_token_lifespan
|
||||||
|
@ -302,7 +303,7 @@ you must configure this option manually if you want http endpoints to be permitt
|
||||||
Origins must only have the scheme, hostname and port, they may not have a trailing slash or path.
|
Origins must only have the scheme, hostname and port, they may not have a trailing slash or path.
|
||||||
|
|
||||||
In addition to an Origin URI, you may specify the wildcard origin in the allowed_origins. It MUST be specified by itself
|
In addition to an Origin URI, you may specify the wildcard origin in the allowed_origins. It MUST be specified by itself
|
||||||
and the [allowed_origins_from_client_redirect_uris](#allowed_origins_from_client_redirect_uris) MUST NOT be enabled. The
|
and the [allowed_origins_from_client_redirect_uris](#allowedoriginsfromclientredirecturis) MUST NOT be enabled. The
|
||||||
wildcard origin is denoted as `*`. Examples:
|
wildcard origin is denoted as `*`. Examples:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -422,7 +423,7 @@ Configures the consent mode. The following table describes the different modes:
|
||||||
| implicit | Automatically assumes consent for every authorization, never asking the user if they wish to give consent. *__Note:__* this option is not technically part of the specification. |
|
| implicit | Automatically assumes consent for every authorization, never asking the user if they wish to give consent. *__Note:__* this option is not technically part of the specification. |
|
||||||
| pre-configured | Allows the end-user to remember their consent for the [pre_configured_consent_duration]. |
|
| pre-configured | Allows the end-user to remember their consent for the [pre_configured_consent_duration]. |
|
||||||
|
|
||||||
[pre_configured_consent_duration]: #pre_configured_consent_duration
|
[pre_configured_consent_duration]: #preconfiguredconsentduration
|
||||||
|
|
||||||
#### pre_configured_consent_duration
|
#### pre_configured_consent_duration
|
||||||
|
|
||||||
|
@ -439,7 +440,7 @@ The period of time dictates how long a users choice to remember the pre-configur
|
||||||
Pre-configured consents are only valid if the subject, client id are exactly the same and the requested scopes/audience
|
Pre-configured consents are only valid if the subject, client id are exactly the same and the requested scopes/audience
|
||||||
match exactly with the granted scopes/audience.
|
match exactly with the granted scopes/audience.
|
||||||
|
|
||||||
[consent_mode]: #consent_mode
|
[consent_mode]: #consentmode
|
||||||
|
|
||||||
#### audience
|
#### audience
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ likely result in an error or even worse misconfiguration.
|
||||||
### Kubernetes
|
### Kubernetes
|
||||||
|
|
||||||
Please see the
|
Please see the
|
||||||
[Kubernetes Integration: Enable Service Links](../../integration/kubernetes/introduction/index.md#enable-service-links)
|
[Kubernetes Integration: Enable Service Links](../../integration/kubernetes/introduction.md#enable-service-links)
|
||||||
documentation for specific requirements for using *Authelia* with Kubernetes.
|
documentation for specific requirements for using *Authelia* with Kubernetes.
|
||||||
|
|
||||||
## Mapping
|
## Mapping
|
||||||
|
|
|
@ -55,15 +55,15 @@ other configuration using the environment but instead of loading a file the valu
|
||||||
{{% table-config-keys secrets="true" %}}
|
{{% table-config-keys secrets="true" %}}
|
||||||
|
|
||||||
[server.tls.key]: ../miscellaneous/server.md#key
|
[server.tls.key]: ../miscellaneous/server.md#key
|
||||||
[jwt_secret]: ../miscellaneous/introduction.md#jwt_secret
|
[jwt_secret]: ../miscellaneous/introduction.md#jwtsecret
|
||||||
[duo_api.integration_key]: ../second-factor/duo.md#integration_key
|
[duo_api.integration_key]: ../second-factor/duo.md#integrationkey
|
||||||
[duo_api.secret_key]: ../second-factor/duo.md#secret_key
|
[duo_api.secret_key]: ../second-factor/duo.md#secretkey
|
||||||
[session.secret]: ../session/introduction.md#secret
|
[session.secret]: ../session/introduction.md#secret
|
||||||
[session.redis.password]: ../session/redis.md#password
|
[session.redis.password]: ../session/redis.md#password
|
||||||
[session.redis.tls.certificate_chain]: ../session/redis.md#tls
|
[session.redis.tls.certificate_chain]: ../session/redis.md#tls
|
||||||
[session.redis.tls.private_key]: ../session/redis.md#tls
|
[session.redis.tls.private_key]: ../session/redis.md#tls
|
||||||
[session.redis.high_availability.sentinel_password]: ../session/redis.md#sentinel_password
|
[session.redis.high_availability.sentinel_password]: ../session/redis.md#sentinelpassword
|
||||||
[storage.encryption_key]: ../storage/introduction.md#encryption_key
|
[storage.encryption_key]: ../storage/introduction.md#encryptionkey
|
||||||
[storage.mysql.password]: ../storage/mysql.md#password
|
[storage.mysql.password]: ../storage/mysql.md#password
|
||||||
[storage.mysql.tls.certificate_chain]: ../storage/mysql.md#tls
|
[storage.mysql.tls.certificate_chain]: ../storage/mysql.md#tls
|
||||||
[storage.mysql.tls.private_key]: ../storage/mysql.md#tls
|
[storage.mysql.tls.private_key]: ../storage/mysql.md#tls
|
||||||
|
@ -77,9 +77,9 @@ other configuration using the environment but instead of loading a file the valu
|
||||||
[authentication_backend.ldap.password]: ../first-factor/ldap.md#password
|
[authentication_backend.ldap.password]: ../first-factor/ldap.md#password
|
||||||
[authentication_backend.ldap.tls.certificate_chain]: ../first-factor/ldap.md#tls
|
[authentication_backend.ldap.tls.certificate_chain]: ../first-factor/ldap.md#tls
|
||||||
[authentication_backend.ldap.tls.private_key]: ../first-factor/ldap.md#tls
|
[authentication_backend.ldap.tls.private_key]: ../first-factor/ldap.md#tls
|
||||||
[identity_providers.oidc.issuer_certificate_chain]: ../identity-providers/open-id-connect.md#issuer_certificate_chain
|
[identity_providers.oidc.issuer_certificate_chain]: ../identity-providers/open-id-connect.md#issuercertificatechain
|
||||||
[identity_providers.oidc.issuer_private_key]: ../identity-providers/open-id-connect.md#issuer_private_key
|
[identity_providers.oidc.issuer_private_key]: ../identity-providers/open-id-connect.md#issuerprivatekey
|
||||||
[identity_providers.oidc.hmac_secret]: ../identity-providers/open-id-connect.md#hmac_secret
|
[identity_providers.oidc.hmac_secret]: ../identity-providers/open-id-connect.md#hmacsecret
|
||||||
|
|
||||||
|
|
||||||
## Secrets in configuration file
|
## Secrets in configuration file
|
||||||
|
|
|
@ -73,7 +73,7 @@ default_2fa_method: totp
|
||||||
especially for containerized deployments.*
|
especially for containerized deployments.*
|
||||||
|
|
||||||
Defines the secret used to craft JWT tokens leveraged by the identity verification process. This can a random string.
|
Defines the secret used to craft JWT tokens leveraged by the identity verification process. This can a random string.
|
||||||
It's strongly recommended this is a [Random Alphanumeric String](../../reference/guides/generating-secure-values.md/#generating-a-random-alphanumeric-string) with
|
It's strongly recommended this is a [Random Alphanumeric String](../../reference/guides/generating-secure-values.md#generating-a-random-alphanumeric-string) with
|
||||||
64 or more characters.
|
64 or more characters.
|
||||||
|
|
||||||
### theme
|
### theme
|
||||||
|
|
|
@ -68,4 +68,4 @@ Setting this to true will disable the startup check entirely.
|
||||||
|
|
||||||
Setting this to true will allow Authelia to start and just log an error instead of exiting. The default is that if
|
Setting this to true will allow Authelia to start and just log an error instead of exiting. The default is that if
|
||||||
Authelia can contact the NTP server successfully, and the time reported by the server is greater than what is configured
|
Authelia can contact the NTP server successfully, and the time reported by the server is greater than what is configured
|
||||||
in [max_desync](#max_desync) that Authelia fails to start and logs a fatal error.
|
in [max_desync](#maxdesync) that Authelia fails to start and logs a fatal error.
|
||||||
|
|
|
@ -123,7 +123,7 @@ require an IP address for the host of the backend service but want to verify a s
|
||||||
|
|
||||||
The key `skip_verify` completely negates validating the certificate of the backend service. This is not recommended,
|
The key `skip_verify` completely negates validating the certificate of the backend service. This is not recommended,
|
||||||
instead you should tweak the `server_name` option, and the global option
|
instead you should tweak the `server_name` option, and the global option
|
||||||
[certificates directory](../miscellaneous/introduction.md#certificates_directory).
|
[certificates directory](../miscellaneous/introduction.md#certificatesdirectory).
|
||||||
|
|
||||||
### minimum_version
|
### minimum_version
|
||||||
|
|
||||||
|
@ -147,7 +147,7 @@ this value. At the time of this writing `SSL3.0` will always produce errors.
|
||||||
|
|
||||||
{{< confkey type="string" required="no" >}}
|
{{< confkey type="string" required="no" >}}
|
||||||
|
|
||||||
The certificate chain/bundle to be used with the [private_key](#private_key) to perform mutual TLS authentication with
|
The certificate chain/bundle to be used with the [private_key](#privatekey) to perform mutual TLS authentication with
|
||||||
the server.
|
the server.
|
||||||
|
|
||||||
The value must be one or more certificates encoded in the DER base64 ([RFC4648]) encoded PEM format.
|
The value must be one or more certificates encoded in the DER base64 ([RFC4648]) encoded PEM format.
|
||||||
|
@ -159,7 +159,7 @@ The value must be one or more certificates encoded in the DER base64 ([RFC4648])
|
||||||
*__Important Note:__ This can also be defined using a [secret](../methods/secrets.md) which is __strongly recommended__
|
*__Important Note:__ This can also be defined using a [secret](../methods/secrets.md) which is __strongly recommended__
|
||||||
especially for containerized deployments.*
|
especially for containerized deployments.*
|
||||||
|
|
||||||
The private key to be used with the [certificate_chain](#certificate_chain) for mutual TLS authentication.
|
The private key to be used with the [certificate_chain](#certificatechain) for mutual TLS authentication.
|
||||||
|
|
||||||
The value must be one private key encoded in the DER base64 ([RFC4648]) encoded PEM format.
|
The value must be one private key encoded in the DER base64 ([RFC4648]) encoded PEM format.
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,7 @@ environment variable or other environment variables set. This also applies to ot
|
||||||
|
|
||||||
*__Please Note:__ if you're using Authelia with Kubernetes and are not using the provided
|
*__Please Note:__ if you're using Authelia with Kubernetes and are not using the provided
|
||||||
[helm chart](https://charts.authelia.com) you will be required to
|
[helm chart](https://charts.authelia.com) you will be required to
|
||||||
[configure the enableServiceLinks](../../integration/kubernetes/introduction/index.md#enable-service-links) option.*
|
[configure the enableServiceLinks](../../integration/kubernetes/introduction.md#enable-service-links) option.*
|
||||||
|
|
||||||
### 4.25.0
|
### 4.25.0
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ The following changes occurred in 4.7.0:
|
||||||
| logs_level | log_level |
|
| logs_level | log_level |
|
||||||
| logs_file | log_file |
|
| logs_file | log_file |
|
||||||
|
|
||||||
*__Please Note:__ The new keys also changed in [4.30.0](#4.30.0) so you will need to update them to the new values if you
|
*__Please Note:__ The new keys also changed in [4.30.0](#4300) so you will need to update them to the new values if you
|
||||||
are using [4.30.0](#4.30.0) or newer instead of the new keys listed here.*
|
are using [4.30.0](#4300) or newer instead of the new keys listed here.*
|
||||||
|
|
||||||
[YAML]: https://yaml.org/
|
[YAML]: https://yaml.org/
|
||||||
|
|
|
@ -61,10 +61,12 @@ by Authelia from others.
|
||||||
|
|
||||||
*__Important Note:__ Many TOTP applications do not support this option. It is strongly advised you find out which
|
*__Important Note:__ Many TOTP applications do not support this option. It is strongly advised you find out which
|
||||||
applications your users use and test them before changing this option. It is insufficient to test that the application
|
applications your users use and test them before changing this option. It is insufficient to test that the application
|
||||||
can add the key, it must also authenticate with Authelia as some applications silently ignore these options. Bitwarden
|
can add the key, it must also authenticate with Authelia as some applications silently ignore these options. [Bitwarden]
|
||||||
is the only one that has been tested at this time. If you'd like to contribute to documenting support for this option
|
is the only one that has been tested at this time. If you'd like to contribute to documenting support for this option
|
||||||
please see [Issue 2650](https://github.com/authelia/authelia/issues/2650).*
|
please see [Issue 2650](https://github.com/authelia/authelia/issues/2650).*
|
||||||
|
|
||||||
|
[Bitwarden]: https://bitwarden.com/
|
||||||
|
|
||||||
The algorithm used for the TOTP key.
|
The algorithm used for the TOTP key.
|
||||||
|
|
||||||
Possible Values (case-insensitive):
|
Possible Values (case-insensitive):
|
||||||
|
@ -82,7 +84,7 @@ information.
|
||||||
|
|
||||||
*__Important Note:__ Some TOTP applications do not support this option. It is strongly advised you find out which
|
*__Important Note:__ Some TOTP applications do not support this option. It is strongly advised you find out which
|
||||||
applications your users use and test them before changing this option. It is insufficient to test that the application
|
applications your users use and test them before changing this option. It is insufficient to test that the application
|
||||||
can add the key, it must also authenticate with Authelia as some applications silently ignore these options. Bitwarden
|
can add the key, it must also authenticate with Authelia as some applications silently ignore these options. [Bitwarden]
|
||||||
is the only one that has been tested at this time. If you'd like to contribute to documenting support for this option
|
is the only one that has been tested at this time. If you'd like to contribute to documenting support for this option
|
||||||
please see [Issue 2650](https://github.com/authelia/authelia/issues/2650).*
|
please see [Issue 2650](https://github.com/authelia/authelia/issues/2650).*
|
||||||
|
|
||||||
|
@ -137,7 +139,7 @@ validations.
|
||||||
This means if the configuration options are changed, users will not need to regenerate their keys. This functionality
|
This means if the configuration options are changed, users will not need to regenerate their keys. This functionality
|
||||||
takes effect from 4.33.0 onwards, previously the effect was the keys would just fail to validate. If you'd like to force
|
takes effect from 4.33.0 onwards, previously the effect was the keys would just fail to validate. If you'd like to force
|
||||||
users to register a new device, you can delete the old device for a particular user by using the
|
users to register a new device, you can delete the old device for a particular user by using the
|
||||||
`authelia storage totp delete <username>` command regardless of if you change the settings or not.
|
`authelia storage user totp delete <username>` command regardless of if you change the settings or not.
|
||||||
|
|
||||||
## Input Validation
|
## Input Validation
|
||||||
|
|
||||||
|
@ -160,7 +162,7 @@ check the clients.
|
||||||
|
|
||||||
## Encryption
|
## Encryption
|
||||||
|
|
||||||
The TOTP secret is [encrypted](../storage/introduction.md#encryption_key) in the database in version 4.33.0 and above.
|
The TOTP secret is [encrypted](../storage/introduction.md#encryptionkey) in the database in version 4.33.0 and above.
|
||||||
This is so a user having access to only the database cannot easily compromise your two-factor authentication method.
|
This is so a user having access to only the database cannot easily compromise your two-factor authentication method.
|
||||||
|
|
||||||
This may be inconvenient for some users who wish to export TOTP keys from Authelia to other services. As such there is
|
This may be inconvenient for some users who wish to export TOTP keys from Authelia to other services. As such there is
|
||||||
|
@ -170,19 +172,19 @@ at least a minimal configuration that has the storage backend connection details
|
||||||
Export in [Key URI Format](https://github.com/google/google-authenticator/wiki/Key-Uri-Format):
|
Export in [Key URI Format](https://github.com/google/google-authenticator/wiki/Key-Uri-Format):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
authelia storage totp export --format uri
|
authelia storage user totp export --format uri
|
||||||
```
|
```
|
||||||
|
|
||||||
Export as CSV:
|
Export as CSV:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
authelia storage totp export --format csv
|
authelia storage user totp export --format csv
|
||||||
```
|
```
|
||||||
|
|
||||||
Help:
|
Help:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
authelia storage totp export --help
|
authelia storage user totp export --help
|
||||||
```
|
```
|
||||||
|
|
||||||
[RFC4226]: https://www.rfc-editor.org/rfc/rfc4226.html
|
[RFC4226]: https://www.rfc-editor.org/rfc/rfc4226.html
|
||||||
|
|
|
@ -198,7 +198,7 @@ When used in conjunction with [domain] the rule will match when either the [doma
|
||||||
|
|
||||||
In addition to standard regex patterns this criteria can match some [Named Regex Groups].
|
In addition to standard regex patterns this criteria can match some [Named Regex Groups].
|
||||||
|
|
||||||
[domain_regex]: #domain_regex
|
[domain_regex]: #domainregex
|
||||||
|
|
||||||
##### Examples
|
##### Examples
|
||||||
|
|
||||||
|
@ -339,7 +339,7 @@ access_control:
|
||||||
{{< confkey type="list(string)" required="no" >}}
|
{{< confkey type="list(string)" required="no" >}}
|
||||||
|
|
||||||
This criteria is a list of values which can be an IP Address, network address range in CIDR notation, or an alias from
|
This criteria is a list of values which can be an IP Address, network address range in CIDR notation, or an alias from
|
||||||
the [global](#networks-global) section. It matches against the first address in the `X-Forwarded-For` header, or if there
|
the [global](#networks--global-) section. It matches against the first address in the `X-Forwarded-For` header, or if there
|
||||||
are none it will fall back to the IP address of the packet TCP source IP address. For this reason it's important for you
|
are none it will fall back to the IP address of the packet TCP source IP address. For this reason it's important for you
|
||||||
to configure the proxy server correctly in order to accurately match requests with this criteria. *__Note:__ you may
|
to configure the proxy server correctly in order to accurately match requests with this criteria. *__Note:__ you may
|
||||||
combine CIDR networks with the alias rules as you please.*
|
combine CIDR networks with the alias rules as you please.*
|
||||||
|
@ -360,7 +360,7 @@ for administrators to tune the security to their specific needs if desired.
|
||||||
|
|
||||||
##### Examples
|
##### Examples
|
||||||
|
|
||||||
*Require [two_factor](#two_factor) for all clients other than internal clients and `112.134.145.167`. The first two
|
*Require [two_factor](#twofactor) for all clients other than internal clients and `112.134.145.167`. The first two
|
||||||
rules in this list are effectively the same rule just expressed in different ways.*
|
rules in this list are effectively the same rule just expressed in different ways.*
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -485,7 +485,7 @@ access_control:
|
||||||
## Policies
|
## Policies
|
||||||
|
|
||||||
The policy of the first matching rule in the configured list decides the policy applied to the request, if no rule
|
The policy of the first matching rule in the configured list decides the policy applied to the request, if no rule
|
||||||
matches the request the [default_policy](#default_policy) is applied.
|
matches the request the [default_policy](#defaultpolicy) is applied.
|
||||||
|
|
||||||
[policies]: #policies
|
[policies]: #policies
|
||||||
|
|
||||||
|
@ -510,14 +510,14 @@ about the subject is [one_factor]. See [Rule Matching Concept 2] for more inform
|
||||||
This policy requires the user at minimum complete 1FA successfully (username and password). This means if they have
|
This policy requires the user at minimum complete 1FA successfully (username and password). This means if they have
|
||||||
performed 2FA then they will be allowed to access the resource.
|
performed 2FA then they will be allowed to access the resource.
|
||||||
|
|
||||||
[one_factor]: #one_factor
|
[one_factor]: #onefactor
|
||||||
|
|
||||||
### two_factor
|
### two_factor
|
||||||
|
|
||||||
This policy requires the user to complete 2FA successfully. This is currently the highest level of authentication
|
This policy requires the user to complete 2FA successfully. This is currently the highest level of authentication
|
||||||
policy available.
|
policy available.
|
||||||
|
|
||||||
[two_factor]: #two_factor
|
[two_factor]: #twofactor
|
||||||
|
|
||||||
## Rule Matching
|
## Rule Matching
|
||||||
|
|
||||||
|
@ -554,7 +554,7 @@ a match for that request.
|
||||||
policy: two_factor
|
policy: two_factor
|
||||||
```
|
```
|
||||||
|
|
||||||
[Rule Matching Concept 1]: #rule-matching-concept-1-sequential-order
|
[Rule Matching Concept 1]: #rule-matching-concept-1--sequential-order
|
||||||
|
|
||||||
### Rule Matching Concept 2: Subject Criteria Requires Authentication
|
### Rule Matching Concept 2: Subject Criteria Requires Authentication
|
||||||
|
|
||||||
|
@ -569,7 +569,7 @@ for authentication if no prior rules match the request per [Rule Matching Concep
|
||||||
identical rules, and one of them has a subject based reliant criteria, and the other one is a [bypass] rule then the
|
identical rules, and one of them has a subject based reliant criteria, and the other one is a [bypass] rule then the
|
||||||
[bypass] rule should generally come first.
|
[bypass] rule should generally come first.
|
||||||
|
|
||||||
[Rule Matching Concept 2]: #rule-matching-concept-2-subject-criteria-requires-authentication
|
[Rule Matching Concept 2]: #rule-matching-concept-2--subject-criteria-requires-authentication
|
||||||
|
|
||||||
## Named Regex Groups
|
## Named Regex Groups
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ There are currently two providers for session storage (three if you count Redis
|
||||||
|
|
||||||
* Memory (default, stateful, no additional configuration)
|
* Memory (default, stateful, no additional configuration)
|
||||||
* [Redis](redis.md) (stateless).
|
* [Redis](redis.md) (stateless).
|
||||||
* [Redis Sentinel](redis.md#high_availability) (stateless, highly available).
|
* [Redis Sentinel](redis.md#highavailability) (stateless, highly available).
|
||||||
|
|
||||||
### Kubernetes or High Availability
|
### Kubernetes or High Availability
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ characters.
|
||||||
the [common options](../prologue/common.md#duration-notation-format) documentation for information on this format.*
|
the [common options](../prologue/common.md#duration-notation-format) documentation for information on this format.*
|
||||||
|
|
||||||
The period of time before the cookie expires and the session is destroyed. This is overriden by
|
The period of time before the cookie expires and the session is destroyed. This is overriden by
|
||||||
[remember_me_duration](#remember_me_duration) when the remember me box is checked.
|
[remember_me_duration](#remembermeduration) when the remember me box is checked.
|
||||||
|
|
||||||
### inactivity
|
### inactivity
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ storage:
|
||||||
username: authelia
|
username: authelia
|
||||||
password: mypassword
|
password: mypassword
|
||||||
tls:
|
tls:
|
||||||
server_name: psotgres.example.com
|
server_name: postgres.example.com
|
||||||
skip_verify: false
|
skip_verify: false
|
||||||
minimum_version: TLS1.2
|
minimum_version: TLS1.2
|
||||||
maximum_version: TLS1.3
|
maximum_version: TLS1.3
|
||||||
|
|
|
@ -35,7 +35,7 @@ storage:
|
||||||
|
|
||||||
### encryption_key
|
### encryption_key
|
||||||
|
|
||||||
See the [encryption_key docs](introduction.md#encryption_key).
|
See the [encryption_key docs](introduction.md#encryptionkey).
|
||||||
|
|
||||||
### path
|
### path
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ toc: true
|
||||||
---
|
---
|
||||||
|
|
||||||
*Authelia* allows collecting telemetry for the purpose of monitoring it. At the present time we only allow collecting
|
*Authelia* allows collecting telemetry for the purpose of monitoring it. At the present time we only allow collecting
|
||||||
[metrics](./metrics.md). These [metrics](./metrics.md) are stored in memory and must be scraped manually by the
|
[metrics](metrics.md). These [metrics](metrics.md) are stored in memory and must be scraped manually by the
|
||||||
administrator.
|
administrator.
|
||||||
|
|
||||||
No metrics or telemetry are reported from an *Authelia* binary to any location the administrator doesn't explicitly
|
No metrics or telemetry are reported from an *Authelia* binary to any location the administrator doesn't explicitly
|
||||||
|
|
|
@ -24,7 +24,7 @@ was not prompted by any bug bounty program as we do not have one, but we hope to
|
||||||
|
|
||||||
Potential usage for the money, ranked in order of priority:
|
Potential usage for the money, ranked in order of priority:
|
||||||
|
|
||||||
1. Put Authelia through a comprehensive [Security Audit](../../../information/security.md#help-wanted).
|
1. Put Authelia through a comprehensive [Security Audit](../../policies/security.md#help-wanted).
|
||||||
1. Audit of Code Security via Analysis.
|
1. Audit of Code Security via Analysis.
|
||||||
2. Audit via Penetration Testing.
|
2. Audit via Penetration Testing.
|
||||||
2. Bug Bounty Program.
|
2. Bug Bounty Program.
|
||||||
|
@ -38,11 +38,11 @@ Please visit [Open Collective] in order to financially contribute to Authelia.
|
||||||
Authelia is sponsored by several companies via indirect means. These companies deserve a special mention since their
|
Authelia is sponsored by several companies via indirect means. These companies deserve a special mention since their
|
||||||
contributions are very important to us but not easily visible.
|
contributions are very important to us but not easily visible.
|
||||||
|
|
||||||
If you feel you have a product or service that Authelia could benefit from please feel free to [contact](../../../information/contact.md) us.
|
If you feel you have a product or service that Authelia could benefit from please feel free to [contact](../../information/contact.md) us.
|
||||||
|
|
||||||
We are currently directly looking for someone to sponsor:
|
We are currently directly looking for someone to sponsor:
|
||||||
|
|
||||||
* [Security Audit](../../../information/security.md#help-wanted)
|
* [Security Audit](../../policies/security.md#help-wanted)
|
||||||
|
|
||||||
### Balto
|
### Balto
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ If the language you wish to translate is not on [Crowdin] then you have a few op
|
||||||
## Overrides
|
## Overrides
|
||||||
|
|
||||||
Users can override translations easily locally using the
|
Users can override translations easily locally using the
|
||||||
[assets](../../configuration/miscellaneous/server.md#asset_path) directory. This is useful if you wish to perform a
|
[assets](../../configuration/miscellaneous/server.md#assetpath) directory. This is useful if you wish to perform a
|
||||||
translation and see if it looks correct in the browser.
|
translation and see if it looks correct in the browser.
|
||||||
|
|
||||||
[Crowdin]: https://translate.authelia.com
|
[Crowdin]: https://translate.authelia.com
|
||||||
|
|
|
@ -11,8 +11,8 @@ aliases:
|
||||||
|
|
||||||
## Security
|
## Security
|
||||||
|
|
||||||
If you believe you have identified a security related bug with Authelia please visit the [security policy](security.md)
|
If you believe you have identified a security related bug with Authelia please visit the
|
||||||
documentation.
|
[security policy](../policies/security.md) documentation.
|
||||||
|
|
||||||
## GitHub
|
## GitHub
|
||||||
|
|
||||||
|
|
|
@ -42,10 +42,10 @@ It expects the following:
|
||||||
|
|
||||||
* The file `data/authelia/config/configuration.yml` is present and the configuration file.
|
* The file `data/authelia/config/configuration.yml` is present and the configuration file.
|
||||||
* The directory `data/authelia/secrets/` exists and contain the relevant [secret](../../configuration/methods/secrets.md) files:
|
* The directory `data/authelia/secrets/` exists and contain the relevant [secret](../../configuration/methods/secrets.md) files:
|
||||||
* A file named `JWT_SECRET` for the [jwt_secret](../../configuration/miscellaneous/introduction.md#jwt_secret)
|
* A file named `JWT_SECRET` for the [jwt_secret](../../configuration/miscellaneous/introduction.md#jwtsecret)
|
||||||
* A file named `SESSION_SECRET` for the [session secret](../../configuration/session/introduction.md#secret)
|
* A file named `SESSION_SECRET` for the [session secret](../../configuration/session/introduction.md#secret)
|
||||||
* A file named `STORAGE_PASSWORD` for the [PostgreSQL password secret](../../configuration/storage/postgres.md#password)
|
* A file named `STORAGE_PASSWORD` for the [PostgreSQL password secret](../../configuration/storage/postgres.md#password)
|
||||||
* A file named `STORAGE_ENCRYPTION_KEY` for the [storage encryption_key secret](../../configuration/storage/introduction.md#encryption_key)
|
* A file named `STORAGE_ENCRYPTION_KEY` for the [storage encryption_key secret](../../configuration/storage/introduction.md#encryptionkey)
|
||||||
* You're using PostgreSQL.
|
* You're using PostgreSQL.
|
||||||
* You have an external network named `net` which is in bridge mode.
|
* You have an external network named `net` which is in bridge mode.
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ toc: true
|
||||||
There are three main methods to deploy *Authelia*.
|
There are three main methods to deploy *Authelia*.
|
||||||
|
|
||||||
1. [Docker](docker.md)
|
1. [Docker](docker.md)
|
||||||
2. [Kubernetes](../kubernetes/introduction/index.md)
|
2. [Kubernetes](../kubernetes/introduction.md)
|
||||||
3. [Bare-Metal](bare-metal.md)
|
3. [Bare-Metal](bare-metal.md)
|
||||||
|
|
||||||
## Get Started
|
## Get Started
|
||||||
|
|
|
@ -15,4 +15,4 @@ search:
|
||||||
index: false
|
index: false
|
||||||
---
|
---
|
||||||
|
|
||||||
Please see the dedicated [Kubernetes Documentation](../kubernetes/introduction/index.md).
|
Please see the dedicated [Kubernetes Documentation](../kubernetes/introduction.md).
|
||||||
|
|
|
@ -30,7 +30,7 @@ Users are welcome to reach out directly by using any of our various [contact opt
|
||||||
## Get Started
|
## Get Started
|
||||||
|
|
||||||
It's __*strongly recommended*__ that users setting up *Authelia* for the first time take a look at our
|
It's __*strongly recommended*__ that users setting up *Authelia* for the first time take a look at our
|
||||||
[Get Started](../../prologue/get-started) guide. This takes you through various steps which are essential to
|
[Get Started](../prologue/get-started.md) guide. This takes you through various steps which are essential to
|
||||||
bootstrapping *Authelia*.
|
bootstrapping *Authelia*.
|
||||||
|
|
||||||
## Important Notes
|
## Important Notes
|
||||||
|
@ -70,7 +70,7 @@ spec:
|
||||||
If using file-based authentication, the argon2id provider will by default use 1GB of RAM for password generation. This
|
If using file-based authentication, the argon2id provider will by default use 1GB of RAM for password generation. This
|
||||||
means you should allow for at least this amount in your deployment/daemonset spec and have this much available on your
|
means you should allow for at least this amount in your deployment/daemonset spec and have this much available on your
|
||||||
node, alternatively you can
|
node, alternatively you can
|
||||||
[tweak the providers settings](../../../configuration/first-factor/file.md#memory). Otherwise,
|
[tweak the providers settings](../../configuration/first-factor/file.md#memory). Otherwise,
|
||||||
your Authelia may OOM during login. See [here](https://github.com/authelia/authelia/issues/1234#issuecomment-663910799)
|
your Authelia may OOM during login. See [here](https://github.com/authelia/authelia/issues/1234#issuecomment-663910799)
|
||||||
for more info.
|
for more info.
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,7 @@ metadata:
|
||||||
name: app
|
name: app
|
||||||
namespace: default
|
namespace: default
|
||||||
annotations:
|
annotations:
|
||||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
traefik.ingress.kubernetes.io/router.entryPoints: websecure
|
||||||
traefik.ingress.kubernetes.io/router.middlewares: default-forwardauth-authelia@kubernetescrd
|
traefik.ingress.kubernetes.io/router.middlewares: default-forwardauth-authelia@kubernetescrd
|
||||||
traefik.ingress.kubernetes.io/router.tls: "true"
|
traefik.ingress.kubernetes.io/router.tls: "true"
|
||||||
spec:
|
spec:
|
||||||
|
|
|
@ -111,7 +111,7 @@ Below is a list of the potential values we place in the [Claim] and their meanin
|
||||||
## User Information Signing Algorithm
|
## User Information Signing Algorithm
|
||||||
|
|
||||||
The following table describes the response from the [UserInfo] endpoint depending on the
|
The following table describes the response from the [UserInfo] endpoint depending on the
|
||||||
[userinfo_signing_algorithm](../../configuration/identity-providers/open-id-connect.md#userinfo_signing_algorithm).
|
[userinfo_signing_algorithm](../../configuration/identity-providers/open-id-connect.md#userinfosigningalgorithm).
|
||||||
|
|
||||||
| Signing Algorithm | Encoding | Content Type |
|
| Signing Algorithm | Encoding | Content Type |
|
||||||
|:-----------------:|:------------:|:-----------------------------------:|
|
|:-----------------:|:------------:|:-----------------------------------:|
|
||||||
|
|
|
@ -38,9 +38,9 @@ used as a basis for configuration.
|
||||||
|
|
||||||
The important sections to consider in initial configuration are as follows:
|
The important sections to consider in initial configuration are as follows:
|
||||||
|
|
||||||
1. [jwt_secret](../../configuration/miscellaneous/introduction.md#jwt_secret) which is used to sign identity
|
1. [jwt_secret](../../configuration/miscellaneous/introduction.md#jwtsecret) which is used to sign identity
|
||||||
verification emails
|
verification emails
|
||||||
2. [default_redirection_url](../../configuration/miscellaneous/introduction.md#default_redirection_url) which is the
|
2. [default_redirection_url](../../configuration/miscellaneous/introduction.md#defaultredirectionurl) which is the
|
||||||
default URL users will be redirected to when visiting *Authelia* directly
|
default URL users will be redirected to when visiting *Authelia* directly
|
||||||
3. [authentication_backend](../../configuration/first-factor/introduction.md) which you must pick between
|
3. [authentication_backend](../../configuration/first-factor/introduction.md) which you must pick between
|
||||||
[LDAP](../../configuration/first-factor/ldap.md) and a [YAML File](../../configuration/first-factor/file.md) and is
|
[LDAP](../../configuration/first-factor/ldap.md) and a [YAML File](../../configuration/first-factor/file.md) and is
|
||||||
|
@ -76,8 +76,8 @@ There are several methods of deploying *Authelia* and we recommend reading the
|
||||||
The default method of utilizing *Authelia* is via the [Proxy Integrations](../proxies/introduction.md). It's
|
The default method of utilizing *Authelia* is via the [Proxy Integrations](../proxies/introduction.md). It's
|
||||||
recommended that you read the relevant [Proxy Integration Documentation](../proxies/introduction.md).
|
recommended that you read the relevant [Proxy Integration Documentation](../proxies/introduction.md).
|
||||||
|
|
||||||
*__Important Note:__ When your [Deployment](#deployment) is on [Kubernetes](../kubernetes/introduction/index.md) we
|
*__Important Note:__ When your [Deployment](#deployment) is on [Kubernetes](../kubernetes/introduction.md) we
|
||||||
recommend viewing the dedicated [Kubernetes Documentation](../kubernetes/introduction/index.md) prior to viewing the
|
recommend viewing the dedicated [Kubernetes Documentation](../kubernetes/introduction.md) prior to viewing the
|
||||||
[Proxy Integration Documentation](../proxies/introduction.md).*
|
[Proxy Integration Documentation](../proxies/introduction.md).*
|
||||||
|
|
||||||
## Moving to Production
|
## Moving to Production
|
||||||
|
|
|
@ -24,12 +24,12 @@ throughout this documentation and in the [See Also](#see-also) section.*
|
||||||
## Get Started
|
## Get Started
|
||||||
|
|
||||||
It's __*strongly recommended*__ that users setting up *Authelia* for the first time take a look at our
|
It's __*strongly recommended*__ that users setting up *Authelia* for the first time take a look at our
|
||||||
[Get Started](../prologue/get-started.md) guide. This takes you through various steps which are essential to
|
[Get Started](../../prologue/get-started.md) guide. This takes you through various steps which are essential to
|
||||||
bootstrapping *Authelia*.
|
bootstrapping *Authelia*.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
[NGINX Proxy Manager] supports the required [NGINX](nginx.md#requirements) requirements for __Authelia__ out-of-the-box.
|
[NGINX Proxy Manager] supports the required [NGINX](../nginx.md#requirements) requirements for __Authelia__ out-of-the-box.
|
||||||
|
|
||||||
## Trusted Proxies
|
## Trusted Proxies
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ bootstrapping *Authelia*.
|
||||||
Especially if you have never read it before.*
|
Especially if you have never read it before.*
|
||||||
|
|
||||||
To configure trusted proxies for [NGINX Proxy Manager] see the [NGINX] section on
|
To configure trusted proxies for [NGINX Proxy Manager] see the [NGINX] section on
|
||||||
[Trusted Proxies](nginx.md#trusted-proxies). Adapting this to [NGINX Proxy Manager] is beyond the scope of
|
[Trusted Proxies](../nginx.md#trusted-proxies). Adapting this to [NGINX Proxy Manager] is beyond the scope of
|
||||||
this documentation.
|
this documentation.
|
||||||
|
|
||||||
## Docker Compose
|
## Docker Compose
|
||||||
|
@ -137,9 +137,9 @@ either most likely require an adjustment, or may require an adjustment if you're
|
||||||
### Snippets
|
### Snippets
|
||||||
|
|
||||||
The examples assume you've mounted a volume containing the relevant
|
The examples assume you've mounted a volume containing the relevant
|
||||||
[NGINX Snippets](nginx.md#supporting-configuration-snippets) from the [NGINX Integration Guide](nginx.md). The suggested
|
[NGINX Snippets](../nginx.md#supporting-configuration-snippets) from the [NGINX Integration Guide](../nginx.md). The
|
||||||
snippets are the `proxy.conf`, `authelia-location.conf`, and `authelia-authrequest.conf`. It may be fine to substitute
|
suggested snippets are the `proxy.conf`, `authelia-location.conf`, and `authelia-authrequest.conf`. It may be fine to
|
||||||
the standard variant of the `proxy.conf` for the headers only variant but this is untested.
|
substitute the standard variant of the `proxy.conf` for the headers only variant but this is untested.
|
||||||
|
|
||||||
These snippets make the addition of a protected proxy host substantially easier.
|
These snippets make the addition of a protected proxy host substantially easier.
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ required modules including the `http_set_misc` module.
|
||||||
It also includes the [nginx-proxy-confs](https://github.com/linuxserver/docker-mods/tree/nginx-proxy-confs) mod where
|
It also includes the [nginx-proxy-confs](https://github.com/linuxserver/docker-mods/tree/nginx-proxy-confs) mod where
|
||||||
they have several configuration examples in the `/config/nginx/proxy-confs` directory. This can be omitted if desired.
|
they have several configuration examples in the `/config/nginx/proxy-confs` directory. This can be omitted if desired.
|
||||||
|
|
||||||
If you're looking for a more complete solution [linuxserver.io] also have an nginx container called [SWAG](./swag.md)
|
If you're looking for a more complete solution [linuxserver.io] also have an nginx container called [SWAG](swag.md)
|
||||||
which includes ACME and various other useful utilities.
|
which includes ACME and various other useful utilities.
|
||||||
|
|
||||||
{{< details "docker-compose.yaml" >}}
|
{{< details "docker-compose.yaml" >}}
|
||||||
|
|
|
@ -76,7 +76,7 @@ For example the nginx ngx_http_auth_request_module does not seem to support this
|
||||||
|
|
||||||
Authelia detects the upstream request method using the X-Forwarded-Method header. Some proxies set this out of the box,
|
Authelia detects the upstream request method using the X-Forwarded-Method header. Some proxies set this out of the box,
|
||||||
some require you to configure this manually. At the present time all proxies that have
|
some require you to configure this manually. At the present time all proxies that have
|
||||||
[Standard Support](#standard-support) do support this.
|
[Standard Support](#standard) do support this.
|
||||||
|
|
||||||
## Specific proxy notes
|
## Specific proxy notes
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ required modules including the `http_set_misc` module.
|
||||||
It also includes the [nginx-proxy-confs](https://github.com/linuxserver/docker-mods/tree/nginx-proxy-confs) mod where
|
It also includes the [nginx-proxy-confs](https://github.com/linuxserver/docker-mods/tree/nginx-proxy-confs) mod where
|
||||||
they have several configuration examples in the `/config/nginx/proxy-confs` directory. This can be omitted if desired.
|
they have several configuration examples in the `/config/nginx/proxy-confs` directory. This can be omitted if desired.
|
||||||
|
|
||||||
If you're looking for a more complete solution [linuxserver.io] also have an nginx container called [SWAG](./swag.md)
|
If you're looking for a more complete solution [linuxserver.io] also have an nginx container called [SWAG](swag.md)
|
||||||
which includes ACME and various other useful utilities.
|
which includes ACME and various other useful utilities.
|
||||||
|
|
||||||
{{< details "docker-compose.yaml" >}}
|
{{< details "docker-compose.yaml" >}}
|
||||||
|
|
|
@ -25,8 +25,8 @@ unreliable and simple usernames and passwords are not sufficient for security.
|
||||||
|
|
||||||
__Authelia__ enables primarily two-factor authentication. These methods offered come in two forms:
|
__Authelia__ enables primarily two-factor authentication. These methods offered come in two forms:
|
||||||
|
|
||||||
* 1FA or first-factor authentication which is handled by a username and password. This falls into the *something you know*
|
* 1FA or first-factor authentication which is handled by a username and password. This falls into the
|
||||||
categorization.
|
*something you know* categorization.
|
||||||
* 2FA or second-factor authentication which is handled by several methods including one-time passwords, authentication
|
* 2FA or second-factor authentication which is handled by several methods including one-time passwords, authentication
|
||||||
keys, etc. This falls into the *something you have* categorization.
|
keys, etc. This falls into the *something you have* categorization.
|
||||||
|
|
||||||
|
|
|
@ -28,14 +28,7 @@ the user must match the name of the user in Authelia, or must have an alias that
|
||||||
|
|
||||||
Then, in Duo interface, click on *Applications* and *Protect an Application*. Select the option *Partner Auth API*. This
|
Then, in Duo interface, click on *Applications* and *Protect an Application*. Select the option *Partner Auth API*. This
|
||||||
will generate an integration key, a secret key and a hostname. You can set the name of the application to __Authelia__
|
will generate an integration key, a secret key and a hostname. You can set the name of the application to __Authelia__
|
||||||
and then you must add the generated information to Authelia [configuration](../../deployment/index.md) as shown below:
|
and then you must add the generated information to Authelia [configuration](../../../configuration/second-factor/duo.md).
|
||||||
|
|
||||||
```yaml
|
|
||||||
duo_api:
|
|
||||||
hostname: api-123456789.example.com
|
|
||||||
integration_key: ABCDEF
|
|
||||||
secret_key: 1234567890abcdefghifjkl
|
|
||||||
```
|
|
||||||
|
|
||||||
See the [configuration documentation](../../../configuration/second-factor/duo.md) for more details.
|
See the [configuration documentation](../../../configuration/second-factor/duo.md) for more details.
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
title: "OpenID Connect 1.0"
|
title: "OpenID Connect 1.0"
|
||||||
description: "OpenID Connect 1.0 is a authorization identity framework supported by Authelia."
|
description: "OpenID Connect 1.0 is a authorization identity framework supported by Authelia."
|
||||||
date: 2022-06-15T17:51:47+10:00
|
date: 2022-11-27T16:07:08+11:00
|
||||||
draft: false
|
draft: false
|
||||||
images: []
|
images: []
|
||||||
menu:
|
menu:
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
title: "Trusted Headers SSO"
|
title: "Trusted Headers SSO"
|
||||||
description: "Trusted Headers SSO is a simple header authorization framework supported by Authelia."
|
description: "Trusted Headers SSO is a simple header authorization framework supported by Authelia."
|
||||||
lead: "Trusted Headers is a simple header authorization framework supported by Authelia."
|
lead: "Trusted Headers is a simple header authorization framework supported by Authelia."
|
||||||
date: 2022-06-15T17:51:47+10:00
|
date: 2022-11-27T16:07:08+11:00
|
||||||
draft: false
|
draft: false
|
||||||
images: []
|
images: []
|
||||||
menu:
|
menu:
|
||||||
|
|
|
@ -53,6 +53,6 @@ Authelia only works for websites served over HTTPS because the session cookie ca
|
||||||
connections. Please note that it has been decided that we won't support websites served over HTTP in order to avoid any
|
connections. Please note that it has been decided that we won't support websites served over HTTP in order to avoid any
|
||||||
risk due to misconfiguration (see [#590](https://github.com/authelia/authelia/issues/590)).
|
risk due to misconfiguration (see [#590](https://github.com/authelia/authelia/issues/590)).
|
||||||
|
|
||||||
If a self-signed certificate is required, the
|
If a self-signed certificate is required, the [Generating an RSA Self-Signed Certificate] guide should be followed.
|
||||||
[Generating an RSA Self-Signed Certificate](../../../reference/guides/generating-secure-values.md#generating-an-rsa-self-signed-certificate)
|
|
||||||
guide should be followed.
|
[Generating an RSA Self-Signed Certificate]: ../../../reference/guides/generating-secure-values.md#generating-an-rsa-self-signed-certificate
|
||||||
|
|
|
@ -73,7 +73,7 @@ attacker obtains the file, each password has to be brute forced individually.
|
||||||
|
|
||||||
Lastly Authelia's implementation of Argon2id is highly tunable. You can tune the key length, salt used, iterations
|
Lastly Authelia's implementation of Argon2id is highly tunable. You can tune the key length, salt used, iterations
|
||||||
(time), parallelism, and memory usage. To read more about this please read how to
|
(time), parallelism, and memory usage. To read more about this please read how to
|
||||||
[configure](../configuration/authentication/file.md) file authentication.
|
[configure](../../configuration/first-factor/file.md) file authentication.
|
||||||
|
|
||||||
## User profile and group membership always kept up-to-date (LDAP authentication provider)
|
## User profile and group membership always kept up-to-date (LDAP authentication provider)
|
||||||
|
|
||||||
|
@ -147,7 +147,7 @@ If you wish to change your encryption key for any reason you can do so using the
|
||||||
|
|
||||||
## Notifier security measures (SMTP)
|
## Notifier security measures (SMTP)
|
||||||
|
|
||||||
The SMTP Notifier implementation does not allow connections that are not secure without changing default configuration
|
The SMTP Notifier implementation does not allow connections that are not secure without changing default configuration
|
||||||
values.
|
values.
|
||||||
|
|
||||||
As such all SMTP connections require the following:
|
As such all SMTP connections require the following:
|
||||||
|
@ -158,59 +158,60 @@ As such all SMTP connections require the following:
|
||||||
|
|
||||||
There is an option to disable both of these security measures however they are __not recommended__.
|
There is an option to disable both of these security measures however they are __not recommended__.
|
||||||
|
|
||||||
The following configuration options exist to configure the security level in order of most preferable to least
|
The following configuration options exist to configure the security level in order of most preferable to least
|
||||||
preferable:
|
preferable:
|
||||||
|
|
||||||
### Configuration Option: certificates_directory
|
### Configuration Option: certificates_directory
|
||||||
|
|
||||||
You can [configure a directory](../../configuration/miscellaneous/introduction.md#certificates_directory) of
|
You can configure a [certificates_directory] option which contains certificates for Authelia to trust. These certificates
|
||||||
certificates for Authelia
|
can either be CA's or individual public certificates that should be trusted. These are added in addition to the
|
||||||
to trust. These certificates can either be CA's or individual public certificates that should be trusted. These
|
environments PKI trusted certificates if available. This is useful for trusting a certificate that is self-signed without
|
||||||
are added in addition to the environments PKI trusted certificates if available. This is useful for trusting a
|
drastically reducing security. This is the most recommended workaround to not having a valid PKI trusted certificate as
|
||||||
certificate that is self-signed without drastically reducing security. This is the most recommended workaround to not
|
it gives you complete control over which ones are trusted without disabling critically needed validation of the identity
|
||||||
having a valid PKI trusted certificate as it gives you complete control over which ones are trusted without disabling
|
of the target service.
|
||||||
critically needed validation of the identity of the target service.
|
|
||||||
|
|
||||||
Read more in the [documentation](../../configuration/miscellaneous/introduction.md#certificates_directory) for this
|
Read more in the [certificates_directory] documentation for this option.
|
||||||
option.
|
|
||||||
|
[certificates_directory]: ../../configuration/miscellaneous/introduction.md#certificatesdirectory
|
||||||
|
[certificates directory]: #configuration-option--certificatesdirectory
|
||||||
|
|
||||||
### Configuration Option: tls.skip_verify
|
### Configuration Option: tls.skip_verify
|
||||||
|
|
||||||
The [tls.skip_verify](../../configuration/notifications/smtp.md#tls) option allows you to skip verifying the certificate
|
The [tls.skip_verify](../../configuration/notifications/smtp.md#tls) option allows you to skip verifying the certificate
|
||||||
entirely which is why [certificates_directory](#configuration-option-certificates_directory) is preferred over this.
|
entirely which is why [certificates directory] is preferred over this. This will effectively mean you cannot be sure the
|
||||||
This will effectively mean you cannot be sure the certificate is valid which means an attacker via DNS poisoning or MITM
|
certificate is valid which means an attacker via DNS poisoning or MITM attacks could intercept emails from Authelia
|
||||||
attacks could intercept emails from Authelia compromising a user's security without their knowledge.
|
compromising a user's security without their knowledge.
|
||||||
|
|
||||||
### Configuration Option: disable_require_tls
|
### Configuration Option: disable_require_tls
|
||||||
|
|
||||||
Authelia by default ensures that the SMTP server connection is secured via TLS prior to sending sensitive information.
|
Authelia by default ensures that the SMTP server connection is secured via TLS prior to sending sensitive information.
|
||||||
|
|
||||||
The [disable_require_tls](../../configuration/notifications/smtp.md#disable_require_tls) option disables this
|
The [disable_require_tls](../../configuration/notifications/smtp.md#disablerequiretls) option disables this
|
||||||
requirement which means the emails may be sent in cleartext. This is the least secure option as it effectively removes
|
requirement which means the emails may be sent in cleartext. This is the least secure option as it effectively removes
|
||||||
the validation of SMTP certificates and makes using an encrypted connection with TLS optional.
|
the validation of SMTP certificates and makes using an encrypted connection with TLS optional.
|
||||||
|
|
||||||
This means not only can the vulnerabilities of the [skip_verify](#configuration-option-tlsskip_verify) option be
|
This means not only can the vulnerabilities of the [skip_verify](#configuration-option--tlsskipverify) option be
|
||||||
exploited, but any router or switch along the route of the email which receives the packets could be used to silently
|
exploited, but any router or switch along the route of the email which receives the packets could be used to silently
|
||||||
exploit the cleartext nature of the connection to manipulate the email in transit.
|
exploit the cleartext nature of the connection to manipulate the email in transit.
|
||||||
|
|
||||||
This is only usable currently with authentication disabled (_comment out the password_), and as such is only an option
|
This is only usable currently with authentication disabled (_comment out the password_), and as such is only an option
|
||||||
for SMTP servers that allow unauthenticated relaying (bad practice).
|
for SMTP servers that allow unauthenticated relaying (bad practice).
|
||||||
|
|
||||||
### SMTP Ports
|
### SMTP Ports
|
||||||
|
|
||||||
All SMTP connections begin as [cleartext], and then negotiate to upgrade to a secure TLS connection via STARTTLS.
|
All SMTP connections begin as [cleartext], and then negotiate to upgrade to a secure TLS connection via STARTTLS.
|
||||||
|
|
||||||
The [`submissions` service][service-submissions] (_typically port 465_) is an exception to this rule, where the
|
The [`submissions` service][service-submissions] (_typically port 465_) is an exception to this rule, where the
|
||||||
connection begins immediately secured with TLS (_similar to HTTPS_). When the configured [port for
|
connection begins immediately secured with TLS (_similar to HTTPS_). When the configured [port for
|
||||||
SMTP][docs-config-smtp-port] is set to `465`, Authelia will initiate TLS connections without requiring STARTTLS
|
SMTP][docs-config-smtp-port] is set to `465`, Authelia will initiate TLS connections without requiring STARTTLS
|
||||||
negotiation.
|
negotiation.
|
||||||
|
|
||||||
When the `submissions` service port is available, it [should be preferred][port-465] over any STARTTLS port for
|
When the `submissions` service port is available, it [should be preferred][port-465] over any STARTTLS port for
|
||||||
submitting mail.
|
submitting mail.
|
||||||
|
|
||||||
**NOTE:** Prior to 2018, port 465 was previously assigned for a similar purpose known as [`smtps`][port-465] (_A TLS
|
**NOTE:** Prior to 2018, port 465 was previously assigned for a similar purpose known as [`smtps`][port-465] (_A TLS
|
||||||
only equivalent of the `smtp` port 25_), which it had been deprecated for. Port 465 has since been re-assigned for only
|
only equivalent of the `smtp` port 25_), which it had been deprecated for. Port 465 has since been re-assigned for only
|
||||||
supporting mail submission (_which unlike SMTP transfers via port 25, [requires authentication][smtp-auth]_), similar
|
supporting mail submission (_which unlike SMTP transfers via port 25, [requires authentication][smtp-auth]_), similar
|
||||||
to port 587 (_the `submission` port, a common alternative that uses STARTTLS instead_).
|
to port 587 (_the `submission` port, a common alternative that uses STARTTLS instead_).
|
||||||
|
|
||||||
[docs-config-smtp-port]: ../../configuration/notifications/smtp.md#port
|
[docs-config-smtp-port]: ../../configuration/notifications/smtp.md#port
|
||||||
|
@ -237,7 +238,7 @@ would not even be able to create a TCP connection. This measure is recommended i
|
||||||
configured some kind of ACLs specifically allowing the communication between proxies and Authelia instances like in a
|
configured some kind of ACLs specifically allowing the communication between proxies and Authelia instances like in a
|
||||||
service mesh or some kind of network overlay.
|
service mesh or some kind of network overlay.
|
||||||
|
|
||||||
To configure mutual TLS, please refer to [this document](../../configuration/miscellaneous/server.md#client_certificates)
|
To configure mutual TLS, please refer to [this document](../../configuration/miscellaneous/server.md#clientcertificates)
|
||||||
|
|
||||||
## Additional security
|
## Additional security
|
||||||
|
|
||||||
|
@ -255,7 +256,7 @@ database. The value of this option should be long and as random as possible. See
|
||||||
[documentation](../../configuration/session/introduction.md#secret) for this option.
|
[documentation](../../configuration/session/introduction.md#secret) for this option.
|
||||||
|
|
||||||
The validity period of session is highly configurable. For example in a highly security conscious domain you could
|
The validity period of session is highly configurable. For example in a highly security conscious domain you could
|
||||||
set the session [remember_me_duration](../../configuration/session/introduction.md#remember_me_duration) to 0 to disable this
|
set the session [remember_me_duration](../../configuration/session/introduction.md#remembermeduration) to 0 to disable this
|
||||||
feature, and set the [expiration](../../configuration/session/introduction.md#expiration) to 2 hours and the
|
feature, and set the [expiration](../../configuration/session/introduction.md#expiration) to 2 hours and the
|
||||||
[inactivity](../../configuration/session/introduction.md#inactivity) of 10 minutes. Configuring the session security in this
|
[inactivity](../../configuration/session/introduction.md#inactivity) of 10 minutes. Configuring the session security in this
|
||||||
manner would mean if the cookie age was more than 2 hours or if the user was inactive for more than 10 minutes the
|
manner would mean if the cookie age was more than 2 hours or if the user was inactive for more than 10 minutes the
|
||||||
|
|
|
@ -37,11 +37,11 @@ This is the preferred method of reporting.
|
||||||
|
|
||||||
### Chat
|
### Chat
|
||||||
|
|
||||||
If you wish to chat directly instead of sending an email please use one of the [chat options](../information/contact.md#chat) but it
|
If you wish to chat directly instead of sending an email please use one of the
|
||||||
is vital that when you do that you only do so privately with one of the maintainers. In order to start a private
|
[chat options](../information/contact.md#chat) but it is vital that when you do that you only do so privately with one
|
||||||
discussion you should ask to have a private discussion with a team member without mentioning the reason why you wish to
|
of the maintainers. In order to start a private discussion you should ask to have a private discussion with a team
|
||||||
have a private discussion so that provided the bug is confirmed we can coordinate the release of fixes and information
|
member without mentioning the reason why you wish to have a private discussion so that provided the bug is confirmed we
|
||||||
responsibly.
|
can coordinate the release of fixes and information responsibly.
|
||||||
|
|
||||||
## Credit
|
## Credit
|
||||||
|
|
||||||
|
|
|
@ -20,12 +20,12 @@ modifications that are in harmony with the following rules which are not intende
|
||||||
only intended to preserve the Authelia branding identity:
|
only intended to preserve the Authelia branding identity:
|
||||||
|
|
||||||
1. They do not unreasonably alter the quality of the branding:
|
1. They do not unreasonably alter the quality of the branding:
|
||||||
- Image size changes should be done only when the size is appropriate for the intended display scenario.
|
- Image size changes should be done only when the size is appropriate for the intended display scenario.
|
||||||
- Compression should not be applied overly aggressively for the intended display scenario.
|
- Compression should not be applied overly aggressively for the intended display scenario.
|
||||||
2. The changes do not unreasonably alter the design of the branding and should fit one or more of the following
|
2. The changes do not unreasonably alter the design of the branding and should fit one or more of the following
|
||||||
categories:
|
categories:
|
||||||
- Layout
|
- Layout
|
||||||
- Format
|
- Format
|
||||||
|
|
||||||
Examples of changes which fit these categories include:
|
Examples of changes which fit these categories include:
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ The logo is just the Authelia circle logo without any text.
|
||||||
|
|
||||||
#### Logo Files
|
#### Logo Files
|
||||||
|
|
||||||
[PSD](https://www.authelia.com/images/branding/logo.psd) | [SVG](https://www.authelia.com/images/branding/logo.svg) | [PNG](https://www.authelia.com/images/branding/logo.png)
|
[PSD](https://www.authelia.com/images/branding/logo.psd) | [AI](https://www.authelia.com/images/branding/logo.ai) | [SVG](https://www.authelia.com/images/branding/logo.svg) | [PNG](https://www.authelia.com/images/branding/logo.png)
|
||||||
|
|
||||||
#### Logo Example
|
#### Logo Example
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ The logo is the same as the standard logo without padding.
|
||||||
|
|
||||||
#### Logo (Cropped) Files
|
#### Logo (Cropped) Files
|
||||||
|
|
||||||
[PSD](https://www.authelia.com/images/branding/logo-cropped.psd) | [SVG](https://www.authelia.com/images/branding/logo-cropped.svg) | [PNG](https://www.authelia.com/images/branding/logo-cropped.png)
|
[PSD](https://www.authelia.com/images/branding/logo-cropped.psd) | [AI](https://www.authelia.com/images/branding/logo-cropped.ai) | [SVG](https://www.authelia.com/images/branding/logo-cropped.svg) | [PNG](https://www.authelia.com/images/branding/logo-cropped.png)
|
||||||
|
|
||||||
#### Logo (Cropped) Example
|
#### Logo (Cropped) Example
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ The title is the Authelia circle logo with the `authelia` branded text.
|
||||||
|
|
||||||
#### Title Files
|
#### Title Files
|
||||||
|
|
||||||
[PSD](https://www.authelia.com/images/branding/title.psd) | [SVG](https://www.authelia.com/images/branding/title.svg) | [PNG](https://www.authelia.com/images/branding/title.png)
|
[PSD](https://www.authelia.com/images/branding/title.psd) | [AI](https://www.authelia.com/images/branding/title.ai) | [SVG](https://www.authelia.com/images/branding/title.svg) | [PNG](https://www.authelia.com/images/branding/title.png)
|
||||||
|
|
||||||
#### Title Example
|
#### Title Example
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ The most insecure method is unauthenticated binds. They are generally considered
|
||||||
at all ensures anyone with any level of network access can easily obtain objects and their attributes.
|
at all ensures anyone with any level of network access can easily obtain objects and their attributes.
|
||||||
|
|
||||||
Authelia does support unauthenticated binds but it is not by default, you must configure the
|
Authelia does support unauthenticated binds but it is not by default, you must configure the
|
||||||
[permit_unauthenticated_bind](../../configuration/first-factor/ldap.md#permit_unauthenticated_bind) configuration
|
[permit_unauthenticated_bind](../../configuration/first-factor/ldap.md#permitunauthenticatedbind) configuration
|
||||||
option.
|
option.
|
||||||
|
|
||||||
### End-User Binding
|
### End-User Binding
|
||||||
|
@ -94,17 +94,18 @@ accounts. The active directory example has two attribute filters that accomplish
|
||||||
be appreciated). The userAccountControl filter checks that the account is not disabled and the pwdLastSet makes sure that
|
be appreciated). The userAccountControl filter checks that the account is not disabled and the pwdLastSet makes sure that
|
||||||
value is not 0 which means the password requires changing at the next login.
|
value is not 0 which means the password requires changing at the next login.
|
||||||
|
|
||||||
| Implementation | Users Filter | Groups Filter |
|
| Implementation | Users Filter | Groups Filter |
|
||||||
|:---------------:|:---------------------------------------------------------------------------------------------------------------------------------------------------------------:|:------------------------------------------:|
|
|:---------------:|:---------------------------------------------------------------------------------------------------------------------------------------------------------------:|:----------------------------------------------------------------------------:|
|
||||||
| custom | N/A | N/A |
|
| custom | N/A | N/A |
|
||||||
| activedirectory | (&(|({username_attribute}={input})({mail_attribute}={input}))(sAMAccountType=805306368)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(!(pwdLastSet=0))) | (&(member={dn})(sAMAccountType=268435456)) |
|
| activedirectory | (&(|({username_attribute}={input})({mail_attribute}={input}))(sAMAccountType=805306368)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(!(pwdLastSet=0))) | (&(member={dn})(|(sAMAccountType=268435456)(sAMAccountType=536870912))) |
|
||||||
|
|
||||||
##### Microsoft Active Directory sAMAccountType
|
##### Microsoft Active Directory sAMAccountType
|
||||||
|
|
||||||
| Account Type Value | Description | Equivalent Filter |
|
| Account Type Value | Description | Equivalent Filter |
|
||||||
|:------------------:|:--------------------------:|:----------------------------------------------:|
|
|:------------------:|:---------------------------------------:|:----------------------------------------------:|
|
||||||
| 268435456 | Normal Group Objects | N/A |
|
| 268435456 | Global/Universal Security Group Objects | N/A |
|
||||||
| 805306368 | Normal User Accounts | `(&(objectCategory=person)(objectClass=user))` |
|
| 536870912 | Domain Local Security Group Objects | N/A |
|
||||||
|
| 805306368 | Normal User Accounts | `(&(objectCategory=person)(objectClass=user))` |
|
||||||
|
|
||||||
*__References:__*
|
*__References:__*
|
||||||
- Account Type Values: [Microsoft Learn](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/e742be45-665d-4576-b872-0bc99d1e1fbe).
|
- Account Type Values: [Microsoft Learn](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/e742be45-665d-4576-b872-0bc99d1e1fbe).
|
||||||
|
|
|
@ -16,7 +16,7 @@ Authelia uses templates to generate the HTML and plaintext emails sent via the n
|
||||||
two extensions; `.html` for HTML templates, and `.txt` for plaintext templates.
|
two extensions; `.html` for HTML templates, and `.txt` for plaintext templates.
|
||||||
|
|
||||||
This guide effectively documents the usage of the
|
This guide effectively documents the usage of the
|
||||||
[template_path](../../configuration/notifications/introduction.md#template_path) notification configuration option.
|
[template_path](../../configuration/notifications/introduction.md#templatepath) notification configuration option.
|
||||||
|
|
||||||
## Important Notes
|
## Important Notes
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ This guide effectively documents the usage of the
|
||||||
| PasswordReset | Used to render notifications sent when password has successfully been reset |
|
| PasswordReset | Used to render notifications sent when password has successfully been reset |
|
||||||
|
|
||||||
For example, to modify the `IdentityVerification` HTML template, if your
|
For example, to modify the `IdentityVerification` HTML template, if your
|
||||||
[template_path](../../configuration/notifications/introduction.md#template_path) was configured as
|
[template_path](../../configuration/notifications/introduction.md#templatepath) was configured as
|
||||||
`/config/email_templates`, you would create the `/config/email_templates/IdentityVerification.html` file to override the
|
`/config/email_templates`, you would create the `/config/email_templates/IdentityVerification.html` file to override the
|
||||||
HTML `IdentityVerification` template.
|
HTML `IdentityVerification` template.
|
||||||
|
|
||||||
|
|
|
@ -156,7 +156,7 @@ See the [Crypt (C) Wiki page](https://en.wikipedia.org/wiki/Crypt_(C)) for more
|
||||||
#### Tuning
|
#### Tuning
|
||||||
|
|
||||||
The configuration variables are unique to the file authentication provider, thus they all exist in a key under the file
|
The configuration variables are unique to the file authentication provider, thus they all exist in a key under the file
|
||||||
authentication configuration key called [password](../../configuration/first-factor/file.md#password). The defaults are
|
authentication configuration key called [password](../../configuration/first-factor/file.md#password-options). The defaults are
|
||||||
considered as sane for a reasonable system however we still recommend taking time to figure out the best values to
|
considered as sane for a reasonable system however we still recommend taking time to figure out the best values to
|
||||||
adequately determine the [cost](#cost).
|
adequately determine the [cost](#cost).
|
||||||
|
|
||||||
|
|
|
@ -26,10 +26,10 @@ This guide effectively documents the usage of the
|
||||||
|
|
||||||
## Assets
|
## Assets
|
||||||
|
|
||||||
| Asset | File Name | Directory | Notes |
|
| Asset | File Name | Directory | Notes |
|
||||||
|:-------------------:|:-----------:|:---------:|:-------------:|
|
|:-------------------:|:-----------:|:---------:|:-----------------------:|
|
||||||
| Favicon | favicon.ico | No | N/A |
|
| Favicon | favicon.ico | No | N/A |
|
||||||
| Logo | logo.png | No | N/A |
|
| Logo | logo.png | No | N/A |
|
||||||
| Translation Locales | locales | Yes | see [locales](#locales) |
|
| Translation Locales | locales | Yes | see [locales](#locales) |
|
||||||
|
|
||||||
## locales
|
## locales
|
||||||
|
|
|
@ -12,7 +12,6 @@ weight: 220
|
||||||
toc: true
|
toc: true
|
||||||
aliases:
|
aliases:
|
||||||
- /r/sanitize
|
- /r/sanitize
|
||||||
- /reference/guides/domain-sanitizaiton
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Some users may wish to hide their domain in files provided during troubleshooting. While this is discouraged, if a user
|
Some users may wish to hide their domain in files provided during troubleshooting. While this is discouraged, if a user
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
---
|
||||||
|
title: "Cache Integrations"
|
||||||
|
description: "A cache integration reference guide"
|
||||||
|
lead: "This section contains a cache integration reference guide for Authelia."
|
||||||
|
date: 2022-11-19T16:47:09+11:00
|
||||||
|
draft: false
|
||||||
|
images: []
|
||||||
|
menu:
|
||||||
|
reference:
|
||||||
|
parent: "integrations"
|
||||||
|
weight: 320
|
||||||
|
toc: true
|
||||||
|
---
|
||||||
|
|
||||||
|
We currently only support [Redis Standalone] and [Redis Sentinel] for cached information like sessions
|
||||||
|
(other than in-memory).
|
||||||
|
|
||||||
|
## Redis
|
||||||
|
|
||||||
|
The following is guidance on versions of [Redis] supported.
|
||||||
|
|
||||||
|
### Standalone
|
||||||
|
|
||||||
|
When it comes to [Redis Standalone] we support the versions supported by [Redis] themselves which can be found in the
|
||||||
|
[Redis release cycle](https://redis.io/docs/about/releases/) documentation. This is typically the latest available
|
||||||
|
version.
|
||||||
|
|
||||||
|
|
||||||
|
### Sentinel
|
||||||
|
|
||||||
|
When it comes to [Redis Sentinel] we support the versions supported by [Redis] themselves which can be found in the
|
||||||
|
[Redis release cycle](https://redis.io/docs/about/releases/) documentation. This is typically the latest available
|
||||||
|
version.
|
||||||
|
|
||||||
|
_**Note:** Currently we only support [Redis Sentinel] version 6.x due to a breaking change to [Redis Sentinel] in
|
||||||
|
version 7.x. This will be resolved in the near future._
|
||||||
|
|
||||||
|
[Redis]: https://redis.io/
|
||||||
|
[Redis Standalone]: https://redis.io/docs/getting-started/
|
||||||
|
[Redis Sentinel]: https://redis.io/docs/management/sentinel/
|
|
@ -15,16 +15,29 @@ toc: true
|
||||||
We generally recommend using [PostgreSQL] for a database. If high availability is not a consideration we also support
|
We generally recommend using [PostgreSQL] for a database. If high availability is not a consideration we also support
|
||||||
[SQLite3].
|
[SQLite3].
|
||||||
|
|
||||||
|
It is also a general recommendation that if you're using [PostgreSQL], [MySQL], or [MariaDB]; that you do not
|
||||||
|
automatically upgrade the major/minor version of these databases, and pin the image tag so at most the patch version
|
||||||
|
is updated. For example for database version `x.y.z` only the `z` should change, `x` and `y` should remain the same.
|
||||||
|
|
||||||
|
It is also generally recommended that you do not rely on automatic update tools to perform this action
|
||||||
|
unless you are sure they shut down the container properly (i.e. with a graceful stop).
|
||||||
|
|
||||||
|
While this guide exists and it contains some guidance on managing the database being used, it is by no means exhaustive
|
||||||
|
or intended as such and users should refer to the database vendors documentation.
|
||||||
|
|
||||||
## PostgreSQL
|
## PostgreSQL
|
||||||
|
|
||||||
The only current support criteria for [PostgreSQL] at present is that the version you're using is supported by the
|
The only current support criteria for [PostgreSQL] at present is that the version you're using is supported by the
|
||||||
[PostgreSQL] developers. See their [Versioning Policy](https://www.postgresql.org/support/versioning/) for more
|
[PostgreSQL] developers. See [Vendor Supported Versions](#vendor-supported-versions) more information.
|
||||||
information.
|
|
||||||
|
|
||||||
We generally perform integration testing against the latest supported version of [PostgreSQL] and that is generally the
|
We generally perform integration testing against the latest supported version of [PostgreSQL] and that is generally the
|
||||||
recommended version for new installations.
|
recommended version for new installations.
|
||||||
|
|
||||||
|
### Vendor Supported Versions
|
||||||
|
|
||||||
|
See the [PostgreSQL Versioning Policy](https://www.postgresql.org/support/versioning/) for information on the versions
|
||||||
|
and platforms that are currently supported by this vendor.
|
||||||
|
|
||||||
## MySQL
|
## MySQL
|
||||||
|
|
||||||
[MySQL] and [MariaDB] are both supported as part of the [MySQL] implementation. This is generally discouraged as
|
[MySQL] and [MariaDB] are both supported as part of the [MySQL] implementation. This is generally discouraged as
|
||||||
|
@ -40,8 +53,8 @@ party.
|
||||||
4. Must support maximum index size of no less than 2048 bytes. The default maximum index size for the InnoDB engine is
|
4. Must support maximum index size of no less than 2048 bytes. The default maximum index size for the InnoDB engine is
|
||||||
3072 bytes on:
|
3072 bytes on:
|
||||||
1. [MySQL] [8.0](https://dev.mysql.com/doc/refman/8.0/en/innodb-limits.html) or later.
|
1. [MySQL] [8.0](https://dev.mysql.com/doc/refman/8.0/en/innodb-limits.html) or later.
|
||||||
2. [MySQL] [5.7](https://dev.mysql.com/doc/refman/5.7/en/innodb-limits.html) provided
|
2. [MySQL] [5.7](https://dev.mysql.com/doc/refman/5.7/en/innodb-limits.html) or later provided:
|
||||||
[innodb_large_prefix](#innodb-large-prefixes) or later.
|
1. The [innodb_large_prefix](#innodb-large-prefixes) option is **_ON_**.
|
||||||
3. [MariaDB] [10.3](https://mariadb.com/kb/en/innodb-system-variables/#innodb_large_prefix) or later.
|
3. [MariaDB] [10.3](https://mariadb.com/kb/en/innodb-system-variables/#innodb_large_prefix) or later.
|
||||||
5. Must support ANSI standard time behaviours. See [ANSI standard time behaviours](#ansi-standard-time-behaviours).
|
5. Must support ANSI standard time behaviours. See [ANSI standard time behaviours](#ansi-standard-time-behaviours).
|
||||||
|
|
||||||
|
@ -52,8 +65,8 @@ supported version of [MariaDB] is generally the recommended version for new inst
|
||||||
|
|
||||||
#### InnoDB Large Prefixes
|
#### InnoDB Large Prefixes
|
||||||
|
|
||||||
This can be configured in the [MySQL] configuration file by setting the `innodb_large_prefix` value to on.
|
This can be configured in the [MySQL] configuration file by setting the `innodb_large_prefix` option to on.
|
||||||
According to the Oracle documentation this is the default behaviour in
|
According to the [Oracle] documentation this is the default behaviour in
|
||||||
[MySQL] [5.7](https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_large_prefix) and it can't be
|
[MySQL] [5.7](https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_large_prefix) and it can't be
|
||||||
turned off in [MySQL] [8.0](https://dev.mysql.com/doc/refman/8.0/en/innodb-limits.html) or in [MariaDB] 10.3 and later.
|
turned off in [MySQL] [8.0](https://dev.mysql.com/doc/refman/8.0/en/innodb-limits.html) or in [MariaDB] 10.3 and later.
|
||||||
|
|
||||||
|
@ -65,7 +78,7 @@ innodb_large_prefix = ON
|
||||||
#### ANSI standard time behaviours
|
#### ANSI standard time behaviours
|
||||||
|
|
||||||
This can be configured in the [MySQL] configuration file by setting the `explicit_defaults_for_timestamp` value to on.
|
This can be configured in the [MySQL] configuration file by setting the `explicit_defaults_for_timestamp` value to on.
|
||||||
According to the Oracle documentation this is the default behaviour in
|
According to the [Oracle] documentation this is the default behaviour in
|
||||||
[MySQL] [5.7](https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_explicit_defaults_for_timestamp)
|
[MySQL] [5.7](https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_explicit_defaults_for_timestamp)
|
||||||
and [MySQL] [8.0](https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_explicit_defaults_for_timestamp).
|
and [MySQL] [8.0](https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_explicit_defaults_for_timestamp).
|
||||||
This is however not the default behaviour in
|
This is however not the default behaviour in
|
||||||
|
@ -76,19 +89,43 @@ This is however not the default behaviour in
|
||||||
explicit_defaults_for_timestamp = ON
|
explicit_defaults_for_timestamp = ON
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Upgrades
|
||||||
|
|
||||||
|
[MySQL] and [MariaDB] have several standard but important system databases named `mysql`, `sys`, and
|
||||||
|
`performance_schema`. These databases are outside the scope and not intended for individual applications to manage as
|
||||||
|
they are system databases used by [MySQL] and [MariaDB] internally.
|
||||||
|
|
||||||
|
These servers/engines may successfully start when these databases are incompatible with your particular [MySQL] or
|
||||||
|
[MariaDB] version, but may raise errors when you attempt to use particular features of the database. This may lead a
|
||||||
|
user to believe the server/engine is functioning correctly when it is in fact running with a potentially badly corrupted
|
||||||
|
schema.
|
||||||
|
|
||||||
|
The risk here is that the database may run for an extended period of time unnoticed and may be getting more and more
|
||||||
|
corrupt with no visible signs until it's no longer recoverable. This makes it critically important users do not neglect
|
||||||
|
this operation or ensure it's happening.
|
||||||
|
|
||||||
|
While some [MySQL] or [MariaDB] containers will do this automatically or give users an option to perform this
|
||||||
|
automatically, it is strongly recommended that this process is manually done and only done **_after_** doing a backup of
|
||||||
|
all databases on the server as is the recommendation from both [MySQL] and [MariaDB].
|
||||||
|
|
||||||
|
It is your responsibility to ensure these tables are upgraded as per the
|
||||||
|
[mysql_upgrade](https://dev.mysql.com/doc/refman/8.0/en/mysql-upgrade.html) and
|
||||||
|
[mariadb_upgrade](https://mariadb.com/kb/en/mysql_upgrade/) documentation.
|
||||||
|
|
||||||
### Vendor Supported Versions
|
### Vendor Supported Versions
|
||||||
|
|
||||||
#### MariaDB Vendor Supported Versions
|
#### MariaDB Vendor Supported Versions
|
||||||
|
|
||||||
See the [MariaDB Server Releases](https://mariadb.com/kb/en/mariadb-server-release-dates/) for more information.
|
See the [MariaDB Server Releases](https://mariadb.com/kb/en/mariadb-server-release-dates/) for information on the
|
||||||
|
versions and platforms that are currently supported by this vendor.
|
||||||
|
|
||||||
#### MySQL Vendor Supported Versions
|
#### MySQL Vendor Supported Versions
|
||||||
|
|
||||||
See the [MySQL Supported Platforms](https://www.mysql.com/support/supportedplatforms/database.html) for information on
|
See the [MySQL Supported Platforms](https://www.mysql.com/support/supportedplatforms/database.html) for information on
|
||||||
which versions and platforms they support.
|
the versions and platforms that are currently supported by this vendor.
|
||||||
|
|
||||||
[PostgreSQL]: https://www.postgresql.org/
|
[PostgreSQL]: https://www.postgresql.org/
|
||||||
[MySQL]: https://www.mysql.com/
|
[MySQL]: https://www.mysql.com/
|
||||||
[MariaDB]: https://mariadb.org/
|
[MariaDB]: https://mariadb.org/
|
||||||
[SQLite3]: https://www.sqlite.org/index.html
|
[SQLite3]: https://www.sqlite.org/index.html
|
||||||
|
[Oracle]: https://www.oracle.com/
|
||||||
|
|
|
@ -3,20 +3,21 @@
|
||||||
functions = "functions"
|
functions = "functions"
|
||||||
|
|
||||||
[build.environment]
|
[build.environment]
|
||||||
NODE_VERSION = "16.16.0"
|
NODE_VERSION = "16.18.1"
|
||||||
NPM_VERSION = "8.11.0"
|
NPM_VERSION = "8.19.2"
|
||||||
|
GO_VERSION = "1.19.4"
|
||||||
|
|
||||||
[context.production]
|
[context.production]
|
||||||
command = "npm run build"
|
command = "pnpm run build"
|
||||||
|
|
||||||
[context.deploy-preview]
|
[context.deploy-preview]
|
||||||
command = "npm run build -- -b $DEPLOY_PRIME_URL"
|
command = "pnpm run build --baseURL $DEPLOY_URL"
|
||||||
|
|
||||||
[context.branch-deploy]
|
[context.branch-deploy]
|
||||||
command = "npm run build -- -b $DEPLOY_PRIME_URL"
|
command = "pnpm run build --baseURL $DEPLOY_URL"
|
||||||
|
|
||||||
[context.next]
|
[context.next]
|
||||||
command = "npm run build"
|
command = "pnpm run build"
|
||||||
|
|
||||||
[context.next.environment]
|
[context.next.environment]
|
||||||
HUGO_ENV = "next"
|
HUGO_ENV = "next"
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"description": "Doks theme",
|
"description": "Doks theme",
|
||||||
"version": "0.5.0",
|
"version": "0.5.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16.16.0"
|
"node": ">=16.18.1"
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
"defaults"
|
"defaults"
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "7.19.3",
|
"@babel/cli": "7.19.3",
|
||||||
"@babel/core": "7.20.2",
|
"@babel/core": "7.20.5",
|
||||||
"@babel/preset-env": "7.20.2",
|
"@babel/preset-env": "7.20.2",
|
||||||
"@fullhuman/postcss-purgecss": "5.0.0",
|
"@fullhuman/postcss-purgecss": "5.0.0",
|
||||||
"@hyas/images": "0.3.2",
|
"@hyas/images": "0.3.2",
|
||||||
|
@ -49,25 +49,25 @@
|
||||||
"bootstrap": "5.2.3",
|
"bootstrap": "5.2.3",
|
||||||
"bootstrap-icons": "1.10.2",
|
"bootstrap-icons": "1.10.2",
|
||||||
"clipboard": "2.0.11",
|
"clipboard": "2.0.11",
|
||||||
"eslint": "8.28.0",
|
"eslint": "8.29.0",
|
||||||
"exec-bin": "1.0.0",
|
"exec-bin": "1.0.0",
|
||||||
"flexsearch": "0.7.31",
|
"flexsearch": "0.7.31",
|
||||||
"highlight.js": "11.7.0",
|
"highlight.js": "11.7.0",
|
||||||
"hugo-installer": "4.0.1",
|
"hugo-installer": "4.0.1",
|
||||||
"instant.page": "5.1.1",
|
"instant.page": "5.1.1",
|
||||||
"katex": "0.16.3",
|
"katex": "0.16.4",
|
||||||
"lazysizes": "5.3.2",
|
"lazysizes": "5.3.2",
|
||||||
"markdownlint-cli2": "0.5.1",
|
"markdownlint-cli2": "0.5.1",
|
||||||
"netlify-plugin-submit-sitemap": "0.4.0",
|
"netlify-plugin-submit-sitemap": "0.4.0",
|
||||||
"node-fetch": "3.3.0",
|
"node-fetch": "3.3.0",
|
||||||
"postcss": "8.4.19",
|
"postcss": "8.4.19",
|
||||||
"postcss-cli": "10.0.0",
|
"postcss-cli": "10.1.0",
|
||||||
"purgecss-whitelister": "2.4.0",
|
"purgecss-whitelister": "2.4.0",
|
||||||
"shx": "0.3.4",
|
"shx": "0.3.4",
|
||||||
"stylelint": "14.15.0",
|
"stylelint": "14.16.0",
|
||||||
"stylelint-config-standard-scss": "6.1.0"
|
"stylelint-config-standard-scss": "6.1.0"
|
||||||
},
|
},
|
||||||
"otherDependencies": {
|
"otherDependencies": {
|
||||||
"hugo": "0.107.0"
|
"hugo": "0.108.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 13 KiB |
|
@ -45,7 +45,7 @@ services:
|
||||||
- TZ=Australia/Melbourne
|
- TZ=Australia/Melbourne
|
||||||
|
|
||||||
traefik:
|
traefik:
|
||||||
image: traefik:v2.9.5
|
image: traefik:v2.9.6
|
||||||
container_name: traefik
|
container_name: traefik
|
||||||
volumes:
|
volumes:
|
||||||
- ./traefik:/etc/traefik
|
- ./traefik:/etc/traefik
|
||||||
|
|
|
@ -32,7 +32,7 @@ services:
|
||||||
- TZ=Australia/Melbourne
|
- TZ=Australia/Melbourne
|
||||||
|
|
||||||
traefik:
|
traefik:
|
||||||
image: traefik:v2.9.5
|
image: traefik:v2.9.6
|
||||||
container_name: traefik
|
container_name: traefik
|
||||||
volumes:
|
volumes:
|
||||||
- ./traefik:/etc/traefik
|
- ./traefik:/etc/traefik
|
||||||
|
|
20
go.mod
|
@ -11,27 +11,27 @@ require (
|
||||||
github.com/fasthttp/session/v2 v2.4.13
|
github.com/fasthttp/session/v2 v2.4.13
|
||||||
github.com/fsnotify/fsnotify v1.6.0
|
github.com/fsnotify/fsnotify v1.6.0
|
||||||
github.com/go-asn1-ber/asn1-ber v1.5.4
|
github.com/go-asn1-ber/asn1-ber v1.5.4
|
||||||
github.com/go-crypt/crypt v0.1.14
|
github.com/go-crypt/crypt v0.2.3
|
||||||
github.com/go-ldap/ldap/v3 v3.4.4
|
github.com/go-ldap/ldap/v3 v3.4.4
|
||||||
github.com/go-rod/rod v0.112.2
|
github.com/go-rod/rod v0.112.2
|
||||||
github.com/go-sql-driver/mysql v1.6.0
|
github.com/go-sql-driver/mysql v1.7.0
|
||||||
github.com/go-webauthn/webauthn v0.5.0
|
github.com/go-webauthn/webauthn v0.5.0
|
||||||
github.com/golang-jwt/jwt/v4 v4.4.3
|
github.com/golang-jwt/jwt/v4 v4.4.3
|
||||||
github.com/golang/mock v1.6.0
|
github.com/golang/mock v1.6.0
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.1
|
github.com/hashicorp/go-retryablehttp v0.7.1
|
||||||
github.com/jackc/pgx/v5 v5.1.1
|
github.com/jackc/pgx/v5 v5.2.0
|
||||||
github.com/jmoiron/sqlx v1.3.5
|
github.com/jmoiron/sqlx v1.3.5
|
||||||
github.com/knadh/koanf v1.4.4
|
github.com/knadh/koanf v1.4.4
|
||||||
github.com/mattn/go-sqlite3 v1.14.16
|
github.com/mattn/go-sqlite3 v1.14.16
|
||||||
github.com/mitchellh/mapstructure v1.5.0
|
github.com/mitchellh/mapstructure v1.5.0
|
||||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
|
||||||
github.com/ory/fosite v0.43.0
|
github.com/ory/fosite v0.44.0
|
||||||
github.com/ory/herodot v0.9.13
|
github.com/ory/herodot v0.9.13
|
||||||
github.com/ory/x v0.0.517
|
github.com/ory/x v0.0.520
|
||||||
github.com/otiai10/copy v1.9.0
|
github.com/otiai10/copy v1.9.0
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/pquerna/otp v1.3.0
|
github.com/pquerna/otp v1.4.0
|
||||||
github.com/prometheus/client_golang v1.14.0
|
github.com/prometheus/client_golang v1.14.0
|
||||||
github.com/sirupsen/logrus v1.9.0
|
github.com/sirupsen/logrus v1.9.0
|
||||||
github.com/spf13/cobra v1.6.1
|
github.com/spf13/cobra v1.6.1
|
||||||
|
@ -40,8 +40,8 @@ require (
|
||||||
github.com/trustelem/zxcvbn v1.0.1
|
github.com/trustelem/zxcvbn v1.0.1
|
||||||
github.com/valyala/fasthttp v1.43.0
|
github.com/valyala/fasthttp v1.43.0
|
||||||
golang.org/x/sync v0.1.0
|
golang.org/x/sync v0.1.0
|
||||||
golang.org/x/term v0.2.0
|
golang.org/x/term v0.3.0
|
||||||
golang.org/x/text v0.4.0
|
golang.org/x/text v0.5.0
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0
|
gopkg.in/square/go-jose.v2 v2.6.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
@ -63,7 +63,7 @@ require (
|
||||||
github.com/ecordell/optgen v0.0.6 // indirect
|
github.com/ecordell/optgen v0.0.6 // indirect
|
||||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect
|
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect
|
||||||
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
|
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
|
||||||
github.com/go-crypt/x v0.1.3 // indirect
|
github.com/go-crypt/x v0.1.10 // indirect
|
||||||
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
||||||
github.com/go-webauthn/revoke v0.1.6 // indirect
|
github.com/go-webauthn/revoke v0.1.6 // indirect
|
||||||
github.com/golang/glog v1.0.0 // indirect
|
github.com/golang/glog v1.0.0 // indirect
|
||||||
|
@ -110,7 +110,7 @@ require (
|
||||||
golang.org/x/mod v0.6.0 // indirect
|
golang.org/x/mod v0.6.0 // indirect
|
||||||
golang.org/x/net v0.1.0 // indirect
|
golang.org/x/net v0.1.0 // indirect
|
||||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect
|
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect
|
||||||
golang.org/x/sys v0.2.0 // indirect
|
golang.org/x/sys v0.3.0 // indirect
|
||||||
golang.org/x/tools v0.2.0 // indirect
|
golang.org/x/tools v0.2.0 // indirect
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71 // indirect
|
google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71 // indirect
|
||||||
|
|
39
go.sum
|
@ -162,10 +162,10 @@ github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrt
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A=
|
github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A=
|
||||||
github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||||
github.com/go-crypt/crypt v0.1.14 h1:Pd8iBYlbwDXJNi0lz8CS/qYvpvxCfP0XO/f5PYvVQ4o=
|
github.com/go-crypt/crypt v0.2.3 h1:g9OPe39VmqonsTXP/zo4byEoxrxAAUG+DzBvjzGWvuw=
|
||||||
github.com/go-crypt/crypt v0.1.14/go.mod h1:VNLdWMD0go46arq5WVZB2MV/9Vw02FOWhKDORXl7K2c=
|
github.com/go-crypt/crypt v0.2.3/go.mod h1:mbhOIjybuVuh0Vxveb//7UbGV8OCugJC7UPzqs1awYw=
|
||||||
github.com/go-crypt/x v0.1.3 h1:3YSlHqOZsw4gcPzfqrcc5kg4GIhTKmkjl/ZVqJ3CbbU=
|
github.com/go-crypt/x v0.1.10 h1:HN8oQGrWcg5xPtIIGwPDWs9MvdYEUJrP7JeNkC46dvM=
|
||||||
github.com/go-crypt/x v0.1.3/go.mod h1:/6X1DjQki055ajXV/7pCHZM0OmMR1+csiXFkxK73Kc8=
|
github.com/go-crypt/x v0.1.10/go.mod h1:OI04rm/Ojti3mrUFZAJnx66nFbnZ0CVPF7qG49mBZgI=
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
|
@ -184,8 +184,9 @@ github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC
|
||||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||||
github.com/go-rod/rod v0.112.2 h1:dwauKYC/H2em8/BcGk3gC0LTzZHf5MIDKf2DVM4z9gU=
|
github.com/go-rod/rod v0.112.2 h1:dwauKYC/H2em8/BcGk3gC0LTzZHf5MIDKf2DVM4z9gU=
|
||||||
github.com/go-rod/rod v0.112.2/go.mod h1:ElViL9ABbcshNQw93+11FrYRH92RRhMKleuILo6+5V0=
|
github.com/go-rod/rod v0.112.2/go.mod h1:ElViL9ABbcshNQw93+11FrYRH92RRhMKleuILo6+5V0=
|
||||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
|
||||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
|
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
||||||
|
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||||
|
@ -338,8 +339,8 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI
|
||||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
|
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||||
github.com/jackc/pgx/v5 v5.1.1 h1:pZD79K1SYv8wc2HmCQA6VdmRQi7/OtCfv9bM3WAXUYA=
|
github.com/jackc/pgx/v5 v5.2.0 h1:NdPpngX0Y6z6XDFKqmFQaE+bCtkqzvQIOt1wvBlAqs8=
|
||||||
github.com/jackc/pgx/v5 v5.1.1/go.mod h1:Ptn7zmohNsWEsdxRawMzk3gaKma2obW+NWTnKa0S4nk=
|
github.com/jackc/pgx/v5 v5.2.0/go.mod h1:Ptn7zmohNsWEsdxRawMzk3gaKma2obW+NWTnKa0S4nk=
|
||||||
github.com/jandelgado/gcov2lcov v1.0.5 h1:rkBt40h0CVK4oCb8Dps950gvfd1rYvQ8+cWa346lVU0=
|
github.com/jandelgado/gcov2lcov v1.0.5 h1:rkBt40h0CVK4oCb8Dps950gvfd1rYvQ8+cWa346lVU0=
|
||||||
github.com/jandelgado/gcov2lcov v1.0.5/go.mod h1:NnSxK6TMlg1oGDBfGelGbjgorT5/L3cchlbtgFYZSss=
|
github.com/jandelgado/gcov2lcov v1.0.5/go.mod h1:NnSxK6TMlg1oGDBfGelGbjgorT5/L3cchlbtgFYZSss=
|
||||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||||
|
@ -449,8 +450,8 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
|
||||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||||
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
||||||
github.com/ory/fosite v0.43.0 h1:9H1O3I7CFxS2Y6j9FDAx2W3I5uAyEubc9hECS0UTOgI=
|
github.com/ory/fosite v0.44.0 h1:Z3UjyO11/wlIoa3BotOqcTkfm7kUNA8F7dd8mOMfx0o=
|
||||||
github.com/ory/fosite v0.43.0/go.mod h1:BTd8+oG1mRtezZbQq0S4D2HBc815bedZHjjs2KRs39Y=
|
github.com/ory/fosite v0.44.0/go.mod h1:o/G4kAeNn65l6MCod2+KmFfU6JQBSojS7eXys6lKGzM=
|
||||||
github.com/ory/go-acc v0.2.6/go.mod h1:4Kb/UnPcT8qRAk3IAxta+hvVapdxTLWtrr7bFLlEgpw=
|
github.com/ory/go-acc v0.2.6/go.mod h1:4Kb/UnPcT8qRAk3IAxta+hvVapdxTLWtrr7bFLlEgpw=
|
||||||
github.com/ory/go-acc v0.2.8 h1:rOHHAPQjf0u7eHFGWpiXK+gIu/e0GRSJNr9pDukdNC4=
|
github.com/ory/go-acc v0.2.8 h1:rOHHAPQjf0u7eHFGWpiXK+gIu/e0GRSJNr9pDukdNC4=
|
||||||
github.com/ory/go-acc v0.2.8/go.mod h1:iCRZUdGb/7nqvSn8xWZkhfVrtXRZ9Wru2E5rabCjFPI=
|
github.com/ory/go-acc v0.2.8/go.mod h1:iCRZUdGb/7nqvSn8xWZkhfVrtXRZ9Wru2E5rabCjFPI=
|
||||||
|
@ -460,8 +461,8 @@ github.com/ory/herodot v0.9.13 h1:cN/Z4eOkErl/9W7hDIDLb79IO/bfsH+8yscBjRpB4IU=
|
||||||
github.com/ory/herodot v0.9.13/go.mod h1:IWDs9kSvFQqw/cQ8zi5ksyYvITiUU4dI7glUrhZcJYo=
|
github.com/ory/herodot v0.9.13/go.mod h1:IWDs9kSvFQqw/cQ8zi5ksyYvITiUU4dI7glUrhZcJYo=
|
||||||
github.com/ory/viper v1.7.5 h1:+xVdq7SU3e1vNaCsk/ixsfxE4zylk1TJUiJrY647jUE=
|
github.com/ory/viper v1.7.5 h1:+xVdq7SU3e1vNaCsk/ixsfxE4zylk1TJUiJrY647jUE=
|
||||||
github.com/ory/viper v1.7.5/go.mod h1:ypOuyJmEUb3oENywQZRgeAMwqgOyDqwboO1tj3DjTaM=
|
github.com/ory/viper v1.7.5/go.mod h1:ypOuyJmEUb3oENywQZRgeAMwqgOyDqwboO1tj3DjTaM=
|
||||||
github.com/ory/x v0.0.517 h1:20FrwHie18q78WGaHcaH0+XoPNdE88zqSXCQNPNlYUs=
|
github.com/ory/x v0.0.520 h1:ryfKHQEViUBv/UdlZhePOG52RpPAooZtK/I7+x58lBI=
|
||||||
github.com/ory/x v0.0.517/go.mod h1:xUtRpoiRARyJNPVk/fcCNKzyp25Foxt9GPlj8pd7egY=
|
github.com/ory/x v0.0.520/go.mod h1:7f32P5XMBLCy6aVT+fUYq3WPcMVpzsjC0C+FovlMNqY=
|
||||||
github.com/otiai10/copy v1.9.0 h1:7KFNiCgZ91Ru4qW4CWPf/7jqtxLagGRmIxWldPP9VY4=
|
github.com/otiai10/copy v1.9.0 h1:7KFNiCgZ91Ru4qW4CWPf/7jqtxLagGRmIxWldPP9VY4=
|
||||||
github.com/otiai10/copy v1.9.0/go.mod h1:hsfX19wcn0UWIHUQ3/4fHuehhk2UyArQ9dVFAn3FczI=
|
github.com/otiai10/copy v1.9.0/go.mod h1:hsfX19wcn0UWIHUQ3/4fHuehhk2UyArQ9dVFAn3FczI=
|
||||||
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
|
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
|
||||||
|
@ -493,8 +494,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||||
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
||||||
github.com/pquerna/otp v1.3.0 h1:oJV/SkzR33anKXwQU3Of42rL4wbrffP4uvUf1SvS5Xs=
|
github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
|
||||||
github.com/pquerna/otp v1.3.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||||
|
@ -846,12 +847,12 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
|
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM=
|
golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI=
|
||||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
@ -862,8 +863,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
||||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
|
|
@ -8,7 +8,12 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-crypt/crypt"
|
"github.com/go-crypt/crypt/algorithm"
|
||||||
|
"github.com/go-crypt/crypt/algorithm/argon2"
|
||||||
|
"github.com/go-crypt/crypt/algorithm/bcrypt"
|
||||||
|
"github.com/go-crypt/crypt/algorithm/pbkdf2"
|
||||||
|
"github.com/go-crypt/crypt/algorithm/scrypt"
|
||||||
|
"github.com/go-crypt/crypt/algorithm/shacrypt"
|
||||||
|
|
||||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||||
"github.com/authelia/authelia/v4/internal/logging"
|
"github.com/authelia/authelia/v4/internal/logging"
|
||||||
|
@ -17,7 +22,7 @@ import (
|
||||||
// FileUserProvider is a provider reading details from a file.
|
// FileUserProvider is a provider reading details from a file.
|
||||||
type FileUserProvider struct {
|
type FileUserProvider struct {
|
||||||
config *schema.FileAuthenticationBackend
|
config *schema.FileAuthenticationBackend
|
||||||
hash crypt.Hash
|
hash algorithm.Hash
|
||||||
database *FileUserDatabase
|
database *FileUserDatabase
|
||||||
mutex *sync.Mutex
|
mutex *sync.Mutex
|
||||||
timeoutReload time.Time
|
timeoutReload time.Time
|
||||||
|
@ -145,39 +150,50 @@ func (p *FileUserProvider) setTimeoutReload(now time.Time) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFileCryptoHashFromConfig returns a crypt.Hash given a valid configuration.
|
// NewFileCryptoHashFromConfig returns a crypt.Hash given a valid configuration.
|
||||||
func NewFileCryptoHashFromConfig(config schema.Password) (hash crypt.Hash, err error) {
|
func NewFileCryptoHashFromConfig(config schema.Password) (hash algorithm.Hash, err error) {
|
||||||
switch config.Algorithm {
|
switch config.Algorithm {
|
||||||
case hashArgon2, "":
|
case hashArgon2, "":
|
||||||
hash = crypt.NewArgon2Hash().
|
hash, err = argon2.New(
|
||||||
WithVariant(crypt.NewArgon2Variant(config.Argon2.Variant)).
|
argon2.WithVariantName(config.Argon2.Variant),
|
||||||
WithT(config.Argon2.Iterations).
|
argon2.WithT(config.Argon2.Iterations),
|
||||||
WithM(config.Argon2.Memory).
|
argon2.WithM(uint32(config.Argon2.Memory)),
|
||||||
WithP(config.Argon2.Parallelism).
|
argon2.WithP(config.Argon2.Parallelism),
|
||||||
WithK(config.Argon2.KeyLength).
|
argon2.WithK(config.Argon2.KeyLength),
|
||||||
WithS(config.Argon2.SaltLength)
|
argon2.WithS(config.Argon2.SaltLength),
|
||||||
|
)
|
||||||
case hashSHA2Crypt:
|
case hashSHA2Crypt:
|
||||||
hash = crypt.NewSHA2CryptHash().
|
hash, err = shacrypt.New(
|
||||||
WithVariant(crypt.NewSHA2CryptVariant(config.SHA2Crypt.Variant)).
|
shacrypt.WithVariantName(config.SHA2Crypt.Variant),
|
||||||
WithRounds(config.SHA2Crypt.Iterations).
|
shacrypt.WithIterations(config.SHA2Crypt.Iterations),
|
||||||
WithSaltLength(config.SHA2Crypt.SaltLength)
|
shacrypt.WithSaltLength(config.SHA2Crypt.SaltLength),
|
||||||
|
)
|
||||||
case hashPBKDF2:
|
case hashPBKDF2:
|
||||||
hash = crypt.NewPBKDF2Hash().
|
hash, err = pbkdf2.New(
|
||||||
WithVariant(crypt.NewPBKDF2Variant(config.PBKDF2.Variant)).
|
pbkdf2.WithVariantName(config.PBKDF2.Variant),
|
||||||
WithIterations(config.PBKDF2.Iterations).
|
pbkdf2.WithIterations(config.PBKDF2.Iterations),
|
||||||
WithSaltLength(config.PBKDF2.SaltLength)
|
pbkdf2.WithSaltLength(config.PBKDF2.SaltLength),
|
||||||
|
)
|
||||||
case hashSCrypt:
|
case hashSCrypt:
|
||||||
hash = crypt.NewScryptHash().
|
hash, err = scrypt.New(
|
||||||
WithLN(config.SCrypt.Iterations).
|
scrypt.WithLN(config.SCrypt.Iterations),
|
||||||
WithP(config.SCrypt.Parallelism).
|
scrypt.WithP(config.SCrypt.Parallelism),
|
||||||
WithR(config.SCrypt.BlockSize)
|
scrypt.WithR(config.SCrypt.BlockSize),
|
||||||
|
scrypt.WithKeyLength(config.SCrypt.KeyLength),
|
||||||
|
scrypt.WithSaltLength(config.SCrypt.SaltLength),
|
||||||
|
)
|
||||||
case hashBCrypt:
|
case hashBCrypt:
|
||||||
hash = crypt.NewBcryptHash().
|
hash, err = bcrypt.New(
|
||||||
WithVariant(crypt.NewBcryptVariant(config.BCrypt.Variant)).
|
bcrypt.WithVariantName(config.BCrypt.Variant),
|
||||||
WithCost(config.BCrypt.Cost)
|
bcrypt.WithIterations(config.BCrypt.Cost),
|
||||||
|
)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("algorithm '%s' is unknown", config.Algorithm)
|
return nil, fmt.Errorf("algorithm '%s' is unknown", config.Algorithm)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to initialize hash settings: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
if err = hash.Validate(); err != nil {
|
if err = hash.Validate(); err != nil {
|
||||||
return nil, fmt.Errorf("failed to validate hash settings: %w", err)
|
return nil, fmt.Errorf("failed to validate hash settings: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
"github.com/asaskevich/govalidator"
|
"github.com/asaskevich/govalidator"
|
||||||
"github.com/go-crypt/crypt"
|
"github.com/go-crypt/crypt"
|
||||||
|
"github.com/go-crypt/crypt/algorithm"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -203,7 +204,7 @@ func (m *FileUserDatabase) ToDatabaseModel() (model *DatabaseModel) {
|
||||||
// DatabaseUserDetails is the model of user details in the file database.
|
// DatabaseUserDetails is the model of user details in the file database.
|
||||||
type DatabaseUserDetails struct {
|
type DatabaseUserDetails struct {
|
||||||
Username string
|
Username string
|
||||||
Digest crypt.Digest
|
Digest algorithm.Digest
|
||||||
Disabled bool
|
Disabled bool
|
||||||
DisplayName string
|
DisplayName string
|
||||||
Email string
|
Email string
|
||||||
|
@ -308,7 +309,7 @@ type UserDetailsModel struct {
|
||||||
|
|
||||||
// ToDatabaseUserDetailsModel converts a UserDetailsModel into a *DatabaseUserDetails.
|
// ToDatabaseUserDetailsModel converts a UserDetailsModel into a *DatabaseUserDetails.
|
||||||
func (m UserDetailsModel) ToDatabaseUserDetailsModel(username string) (model *DatabaseUserDetails, err error) {
|
func (m UserDetailsModel) ToDatabaseUserDetailsModel(username string) (model *DatabaseUserDetails, err error) {
|
||||||
var d crypt.Digest
|
var d algorithm.Digest
|
||||||
|
|
||||||
if d, err = crypt.Decode(m.HashedPassword); err != nil {
|
if d, err = crypt.Decode(m.HashedPassword); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -252,7 +252,7 @@ func TestShouldRaiseWhenLoadingDatabaseWithBadSHA512HashesForTheFirstTime(t *tes
|
||||||
|
|
||||||
provider := NewFileUserProvider(&config)
|
provider := NewFileUserProvider(&config)
|
||||||
|
|
||||||
assert.EqualError(t, provider.StartupCheck(), "error decoding the authentication database: failed to parse hash for user 'john': sha2crypt decode error: provided encoded hash has an invalid option: option 'rounds00000' is invalid")
|
assert.EqualError(t, provider.StartupCheck(), "error decoding the authentication database: failed to parse hash for user 'john': shacrypt decode error: parameter pair 'rounds00000' is not properly encoded: does not contain kv separator '='")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,7 +263,7 @@ func TestShouldRaiseWhenLoadingDatabaseWithBadArgon2idHashSettingsForTheFirstTim
|
||||||
|
|
||||||
provider := NewFileUserProvider(&config)
|
provider := NewFileUserProvider(&config)
|
||||||
|
|
||||||
assert.EqualError(t, provider.StartupCheck(), "error decoding the authentication database: failed to parse hash for user 'john': argon2 decode error: provided encoded hash has an invalid option: option 'm65536' is invalid")
|
assert.EqualError(t, provider.StartupCheck(), "error decoding the authentication database: failed to parse hash for user 'john': argon2 decode error: parameter pair 'm65536' is not properly encoded: does not contain kv separator '='")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-crypt/crypt"
|
"github.com/go-crypt/crypt"
|
||||||
|
"github.com/go-crypt/crypt/algorithm"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
|
@ -272,8 +273,8 @@ func newCryptoHashValidateCmd() (cmd *cobra.Command) {
|
||||||
|
|
||||||
func cmdCryptoHashGenerateFinish(cmd *cobra.Command, args []string, flagsMap map[string]string) (err error) {
|
func cmdCryptoHashGenerateFinish(cmd *cobra.Command, args []string, flagsMap map[string]string) (err error) {
|
||||||
var (
|
var (
|
||||||
algorithm string
|
algName string
|
||||||
configs []string
|
configs []string
|
||||||
|
|
||||||
c schema.Password
|
c schema.Password
|
||||||
)
|
)
|
||||||
|
@ -294,25 +295,25 @@ func cmdCryptoHashGenerateFinish(cmd *cobra.Command, args []string, flagsMap map
|
||||||
break
|
break
|
||||||
case legacy:
|
case legacy:
|
||||||
if sha512, _ := cmd.Flags().GetBool(cmdFlagNameSHA512); sha512 {
|
if sha512, _ := cmd.Flags().GetBool(cmdFlagNameSHA512); sha512 {
|
||||||
algorithm = cmdUseHashSHA2Crypt
|
algName = cmdUseHashSHA2Crypt
|
||||||
} else {
|
} else {
|
||||||
algorithm = cmdUseHashArgon2
|
algName = cmdUseHashArgon2
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
algorithm = cmd.Use
|
algName = cmd.Use
|
||||||
}
|
}
|
||||||
|
|
||||||
if c, err = cmdCryptoHashGetConfig(algorithm, configs, cmd.Flags(), flagsMap); err != nil {
|
if c, err = cmdCryptoHashGetConfig(algName, configs, cmd.Flags(), flagsMap); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if legacy && algorithm == cmdUseHashArgon2 && cmd.Flags().Changed(cmdFlagNameMemory) {
|
if legacy && algName == cmdUseHashArgon2 && cmd.Flags().Changed(cmdFlagNameMemory) {
|
||||||
c.Argon2.Memory *= 1024
|
c.Argon2.Memory *= 1024
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
hash crypt.Hash
|
hash algorithm.Hash
|
||||||
digest crypt.Digest
|
digest algorithm.Digest
|
||||||
password string
|
password string
|
||||||
random bool
|
random bool
|
||||||
)
|
)
|
||||||
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-crypt/crypt"
|
"github.com/go-crypt/crypt/algorithm/plaintext"
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
|
|
||||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||||
|
@ -486,7 +486,7 @@ func StringToPrivateKeyHookFunc() mapstructure.DecodeHookFuncType {
|
||||||
}
|
}
|
||||||
|
|
||||||
// StringToPasswordDigestHookFunc decodes a string into a crypt.Digest.
|
// StringToPasswordDigestHookFunc decodes a string into a crypt.Digest.
|
||||||
func StringToPasswordDigestHookFunc(plaintext bool) mapstructure.DecodeHookFuncType {
|
func StringToPasswordDigestHookFunc() mapstructure.DecodeHookFuncType {
|
||||||
return func(f reflect.Type, t reflect.Type, data interface{}) (value interface{}, err error) {
|
return func(f reflect.Type, t reflect.Type, data interface{}) (value interface{}, err error) {
|
||||||
var ptr bool
|
var ptr bool
|
||||||
|
|
||||||
|
@ -514,11 +514,11 @@ func StringToPasswordDigestHookFunc(plaintext bool) mapstructure.DecodeHookFuncT
|
||||||
var result *schema.PasswordDigest
|
var result *schema.PasswordDigest
|
||||||
|
|
||||||
if !strings.HasPrefix(dataStr, "$") {
|
if !strings.HasPrefix(dataStr, "$") {
|
||||||
dataStr = fmt.Sprintf(crypt.StorageFormatSimple, crypt.AlgorithmPrefixPlainText, dataStr)
|
dataStr = fmt.Sprintf(plaintext.EncodingFmt, plaintext.AlgIdentifierPlainText, dataStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if dataStr != "" {
|
if dataStr != "" {
|
||||||
if result, err = schema.NewPasswordDigest(dataStr, plaintext); err != nil {
|
if result, err = schema.DecodePasswordDigest(dataStr); err != nil {
|
||||||
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParse, dataStr, prefixType, expectedType.String(), err)
|
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParse, dataStr, prefixType, expectedType.String(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -58,8 +57,7 @@ func TestKoanfSecretCallbackWithValidSecrets(t *testing.T) {
|
||||||
"AUTHELIA__STORAGE_MYSQL_FAKE_PASSWORD": "storage.mysql.fake_password",
|
"AUTHELIA__STORAGE_MYSQL_FAKE_PASSWORD": "storage.mysql.fake_password",
|
||||||
}
|
}
|
||||||
|
|
||||||
dir, err := os.MkdirTemp("", "authelia-test-callbacks")
|
dir := t.TempDir()
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
secretOne := filepath.Join(dir, "secert_one")
|
secretOne := filepath.Join(dir, "secert_one")
|
||||||
secretTwo := filepath.Join(dir, "secret_two")
|
secretTwo := filepath.Join(dir, "secret_two")
|
||||||
|
@ -108,8 +106,7 @@ func TestKoanfSecretCallbackShouldErrorOnFSError(t *testing.T) {
|
||||||
"AUTHELIA_THEME": "theme",
|
"AUTHELIA_THEME": "theme",
|
||||||
}
|
}
|
||||||
|
|
||||||
dir, err := os.MkdirTemp("", "authelia-test-callbacks")
|
dir := t.TempDir()
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
secret := filepath.Join(dir, "inaccessible")
|
secret := filepath.Join(dir, "inaccessible")
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,7 @@ func unmarshal(ko *koanf.Koanf, val *schema.StructValidator, path string, o any)
|
||||||
StringToPrivateKeyHookFunc(),
|
StringToPrivateKeyHookFunc(),
|
||||||
StringToCryptoPrivateKeyHookFunc(),
|
StringToCryptoPrivateKeyHookFunc(),
|
||||||
StringToTLSVersionHookFunc(),
|
StringToTLSVersionHookFunc(),
|
||||||
StringToPasswordDigestHookFunc(true),
|
StringToPasswordDigestHookFunc(),
|
||||||
ToTimeDurationHookFunc(),
|
ToTimeDurationHookFunc(),
|
||||||
),
|
),
|
||||||
Metadata: nil,
|
Metadata: nil,
|
||||||
|
|
|
@ -19,8 +19,7 @@ import (
|
||||||
func TestShouldErrorSecretNotExist(t *testing.T) {
|
func TestShouldErrorSecretNotExist(t *testing.T) {
|
||||||
testReset()
|
testReset()
|
||||||
|
|
||||||
dir, err := os.MkdirTemp("", "authelia-test-secret-not-exist")
|
dir := t.TempDir()
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
testSetEnv(t, "JWT_SECRET_FILE", filepath.Join(dir, "jwt"))
|
testSetEnv(t, "JWT_SECRET_FILE", filepath.Join(dir, "jwt"))
|
||||||
testSetEnv(t, "DUO_API_SECRET_KEY_FILE", filepath.Join(dir, "duo"))
|
testSetEnv(t, "DUO_API_SECRET_KEY_FILE", filepath.Join(dir, "duo"))
|
||||||
|
@ -36,7 +35,7 @@ func TestShouldErrorSecretNotExist(t *testing.T) {
|
||||||
testSetEnv(t, "IDENTITY_PROVIDERS_OIDC_HMAC_SECRET_FILE", filepath.Join(dir, "oidc-hmac"))
|
testSetEnv(t, "IDENTITY_PROVIDERS_OIDC_HMAC_SECRET_FILE", filepath.Join(dir, "oidc-hmac"))
|
||||||
|
|
||||||
val := schema.NewStructValidator()
|
val := schema.NewStructValidator()
|
||||||
_, _, err = Load(val, NewEnvironmentSource(DefaultEnvPrefix, DefaultEnvDelimiter), NewSecretsSource(DefaultEnvPrefix, DefaultEnvDelimiter))
|
_, _, err := Load(val, NewEnvironmentSource(DefaultEnvPrefix, DefaultEnvDelimiter), NewSecretsSource(DefaultEnvPrefix, DefaultEnvDelimiter))
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, val.Warnings(), 0)
|
assert.Len(t, val.Warnings(), 0)
|
||||||
|
@ -162,15 +161,14 @@ func TestShouldRaiseIOErrOnUnreadableFile(t *testing.T) {
|
||||||
|
|
||||||
testReset()
|
testReset()
|
||||||
|
|
||||||
dir, err := os.MkdirTemp("", "authelia-conf")
|
dir := t.TempDir()
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
assert.NoError(t, os.WriteFile(filepath.Join(dir, "myconf.yml"), []byte("server:\n port: 9091\n"), 0000))
|
assert.NoError(t, os.WriteFile(filepath.Join(dir, "myconf.yml"), []byte("server:\n port: 9091\n"), 0000))
|
||||||
|
|
||||||
cfg := filepath.Join(dir, "myconf.yml")
|
cfg := filepath.Join(dir, "myconf.yml")
|
||||||
|
|
||||||
val := schema.NewStructValidator()
|
val := schema.NewStructValidator()
|
||||||
_, _, err = Load(val, NewYAMLFileSource(cfg))
|
_, _, err := Load(val, NewYAMLFileSource(cfg))
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
require.Len(t, val.Errors(), 1)
|
require.Len(t, val.Errors(), 1)
|
||||||
|
@ -390,14 +388,13 @@ func TestShouldNotReadConfigurationOnFSAccessDenied(t *testing.T) {
|
||||||
|
|
||||||
testReset()
|
testReset()
|
||||||
|
|
||||||
dir, err := os.MkdirTemp("", "authelia-config")
|
dir := t.TempDir()
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
cfg := filepath.Join(dir, "config.yml")
|
cfg := filepath.Join(dir, "config.yml")
|
||||||
assert.NoError(t, testCreateFile(filepath.Join(dir, "config.yml"), "port: 9091\n", 0000))
|
assert.NoError(t, testCreateFile(filepath.Join(dir, "config.yml"), "port: 9091\n", 0000))
|
||||||
|
|
||||||
val := schema.NewStructValidator()
|
val := schema.NewStructValidator()
|
||||||
_, _, err = Load(val, NewYAMLFileSource(cfg))
|
_, _, err := Load(val, NewYAMLFileSource(cfg))
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
require.Len(t, val.Errors(), 1)
|
require.Len(t, val.Errors(), 1)
|
||||||
|
@ -408,11 +405,10 @@ func TestShouldNotReadConfigurationOnFSAccessDenied(t *testing.T) {
|
||||||
func TestShouldNotLoadDirectoryConfiguration(t *testing.T) {
|
func TestShouldNotLoadDirectoryConfiguration(t *testing.T) {
|
||||||
testReset()
|
testReset()
|
||||||
|
|
||||||
dir, err := os.MkdirTemp("", "authelia-config")
|
dir := t.TempDir()
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
val := schema.NewStructValidator()
|
val := schema.NewStructValidator()
|
||||||
_, _, err = Load(val, NewYAMLFileSource(dir))
|
_, _, err := Load(val, NewYAMLFileSource(dir))
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
require.Len(t, val.Errors(), 1)
|
require.Len(t, val.Errors(), 1)
|
||||||
|
|
|
@ -191,7 +191,7 @@ var DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory =
|
||||||
UsernameAttribute: "sAMAccountName",
|
UsernameAttribute: "sAMAccountName",
|
||||||
MailAttribute: "mail",
|
MailAttribute: "mail",
|
||||||
DisplayNameAttribute: "displayName",
|
DisplayNameAttribute: "displayName",
|
||||||
GroupsFilter: "(&(member={dn})(sAMAccountType=268435456))",
|
GroupsFilter: "(&(member={dn})(|(sAMAccountType=268435456)(sAMAccountType=536870912)))",
|
||||||
GroupNameAttribute: "cn",
|
GroupNameAttribute: "cn",
|
||||||
Timeout: time.Second * 5,
|
Timeout: time.Second * 5,
|
||||||
TLS: &TLSConfig{
|
TLS: &TLSConfig{
|
||||||
|
|
|
@ -16,6 +16,8 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-crypt/crypt"
|
"github.com/go-crypt/crypt"
|
||||||
|
"github.com/go-crypt/crypt/algorithm"
|
||||||
|
"github.com/go-crypt/crypt/algorithm/plaintext"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewAddressFromString returns an *Address and error depending on the ability to parse the string as an Address.
|
// NewAddressFromString returns an *Address and error depending on the ability to parse the string as an Address.
|
||||||
|
@ -110,27 +112,32 @@ func (a Address) Listener() (net.Listener, error) {
|
||||||
return net.Listen(a.Scheme, a.HostPort())
|
return net.Listen(a.Scheme, a.HostPort())
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPasswordDigest returns a new PasswordDigest.
|
var cdecoder algorithm.DecoderRegister
|
||||||
func NewPasswordDigest(value string, plaintext bool) (digest *PasswordDigest, err error) {
|
|
||||||
var d crypt.Digest
|
|
||||||
|
|
||||||
switch {
|
// DecodePasswordDigest returns a new PasswordDigest if it can be decoded.
|
||||||
case plaintext:
|
func DecodePasswordDigest(encodedDigest string) (digest *PasswordDigest, err error) {
|
||||||
d, err = crypt.DecodeWithPlainText(value)
|
if cdecoder == nil {
|
||||||
default:
|
if cdecoder, err = crypt.NewDefaultDecoder(); err != nil {
|
||||||
d, err = crypt.Decode(value)
|
return nil, fmt.Errorf("failed to initialize decoder: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = plaintext.RegisterDecoderPlainText(cdecoder); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to initialize decoder: could not register the plaintext decoder: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
var d algorithm.Digest
|
||||||
|
|
||||||
|
if d, err = cdecoder.Decode(encodedDigest); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &PasswordDigest{d}, err
|
return &PasswordDigest{Digest: d}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PasswordDigest is a configuration type for the crypt.Digest.
|
// PasswordDigest is a configuration type for the crypt.Digest.
|
||||||
type PasswordDigest struct {
|
type PasswordDigest struct {
|
||||||
crypt.Digest
|
algorithm.Digest
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewX509CertificateChain creates a new *X509CertificateChain from a given string, parsing each PEM block one by one.
|
// NewX509CertificateChain creates a new *X509CertificateChain from a given string, parsing each PEM block one by one.
|
||||||
|
|
|
@ -13,8 +13,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestShouldGenerateConfiguration(t *testing.T) {
|
func TestShouldGenerateConfiguration(t *testing.T) {
|
||||||
dir, err := os.MkdirTemp("", "authelia-config")
|
dir := t.TempDir()
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
cfg := filepath.Join(dir, "config.yml")
|
cfg := filepath.Join(dir, "config.yml")
|
||||||
|
|
||||||
|
@ -31,8 +30,7 @@ func TestShouldNotGenerateConfigurationOnFSAccessDenied(t *testing.T) {
|
||||||
t.Skip("skipping test due to being on windows")
|
t.Skip("skipping test due to being on windows")
|
||||||
}
|
}
|
||||||
|
|
||||||
dir, err := os.MkdirTemp("", "authelia-config")
|
dir := t.TempDir()
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
assert.NoError(t, os.Mkdir(filepath.Join(dir, "zero"), 0000))
|
assert.NoError(t, os.Mkdir(filepath.Join(dir, "zero"), 0000))
|
||||||
|
|
||||||
|
@ -44,8 +42,7 @@ func TestShouldNotGenerateConfigurationOnFSAccessDenied(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShouldNotGenerateConfiguration(t *testing.T) {
|
func TestShouldNotGenerateConfiguration(t *testing.T) {
|
||||||
dir, err := os.MkdirTemp("", "authelia-config")
|
dir := t.TempDir()
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
cfg := filepath.Join(dir, "..", "not-a-dir", "config.yml")
|
cfg := filepath.Join(dir, "..", "not-a-dir", "config.yml")
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,11 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-crypt/crypt"
|
"github.com/go-crypt/crypt/algorithm/argon2"
|
||||||
|
"github.com/go-crypt/crypt/algorithm/bcrypt"
|
||||||
|
"github.com/go-crypt/crypt/algorithm/pbkdf2"
|
||||||
|
"github.com/go-crypt/crypt/algorithm/scrypt"
|
||||||
|
"github.com/go-crypt/crypt/algorithm/shacrypt"
|
||||||
|
|
||||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||||
"github.com/authelia/authelia/v4/internal/utils"
|
"github.com/authelia/authelia/v4/internal/utils"
|
||||||
|
@ -91,46 +95,48 @@ func validateFileAuthenticationBackendPasswordConfigArgon2(config *schema.Passwo
|
||||||
switch {
|
switch {
|
||||||
case config.Argon2.Iterations == 0:
|
case config.Argon2.Iterations == 0:
|
||||||
config.Argon2.Iterations = schema.DefaultPasswordConfig.Argon2.Iterations
|
config.Argon2.Iterations = schema.DefaultPasswordConfig.Argon2.Iterations
|
||||||
case config.Argon2.Iterations < crypt.Argon2IterationsMin:
|
case config.Argon2.Iterations < argon2.IterationsMin:
|
||||||
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooSmall, hashArgon2, "iterations", config.Argon2.Iterations, crypt.Argon2IterationsMin))
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooSmall, hashArgon2, "iterations", config.Argon2.Iterations, argon2.IterationsMin))
|
||||||
case config.Argon2.Iterations > crypt.Argon2IterationsMax:
|
case config.Argon2.Iterations > argon2.IterationsMax:
|
||||||
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooLarge, hashArgon2, "iterations", config.Argon2.Iterations, crypt.Argon2IterationsMax))
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooLarge, hashArgon2, "iterations", config.Argon2.Iterations, argon2.IterationsMax))
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case config.Argon2.Parallelism == 0:
|
case config.Argon2.Parallelism == 0:
|
||||||
config.Argon2.Parallelism = schema.DefaultPasswordConfig.Argon2.Parallelism
|
config.Argon2.Parallelism = schema.DefaultPasswordConfig.Argon2.Parallelism
|
||||||
case config.Argon2.Parallelism < crypt.Argon2ParallelismMin:
|
case config.Argon2.Parallelism < argon2.ParallelismMin:
|
||||||
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooSmall, hashArgon2, "parallelism", config.Argon2.Parallelism, crypt.Argon2ParallelismMin))
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooSmall, hashArgon2, "parallelism", config.Argon2.Parallelism, argon2.ParallelismMin))
|
||||||
case config.Argon2.Parallelism > crypt.Argon2ParallelismMax:
|
case config.Argon2.Parallelism > argon2.ParallelismMax:
|
||||||
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooLarge, hashArgon2, "parallelism", config.Argon2.Parallelism, crypt.Argon2ParallelismMax))
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooLarge, hashArgon2, "parallelism", config.Argon2.Parallelism, argon2.ParallelismMax))
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case config.Argon2.Memory == 0:
|
case config.Argon2.Memory == 0:
|
||||||
config.Argon2.Memory = schema.DefaultPasswordConfig.Argon2.Memory
|
config.Argon2.Memory = schema.DefaultPasswordConfig.Argon2.Memory
|
||||||
case config.Argon2.Memory < 0:
|
case config.Argon2.Memory < argon2.MemoryMin:
|
||||||
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooSmall, hashArgon2, "memory", config.Argon2.Parallelism, 1))
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooSmall, hashArgon2, "memory", config.Argon2.Memory, argon2.MemoryMin))
|
||||||
case config.Argon2.Memory < (crypt.Argon2MemoryMinParallelismMultiplier * config.Argon2.Parallelism):
|
case uint64(config.Argon2.Memory) > uint64(argon2.MemoryMax):
|
||||||
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordArgon2MemoryTooLow, config.Argon2.Memory, config.Argon2.Parallelism*crypt.Argon2MemoryMinParallelismMultiplier, config.Argon2.Parallelism, crypt.Argon2MemoryMinParallelismMultiplier))
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooLarge, hashArgon2, "memory", config.Argon2.Memory, argon2.MemoryMax))
|
||||||
|
case config.Argon2.Memory < (config.Argon2.Parallelism * argon2.MemoryMinParallelismMultiplier):
|
||||||
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordArgon2MemoryTooLow, config.Argon2.Memory, config.Argon2.Parallelism*argon2.MemoryMinParallelismMultiplier, config.Argon2.Parallelism, argon2.MemoryMinParallelismMultiplier))
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case config.Argon2.KeyLength == 0:
|
case config.Argon2.KeyLength == 0:
|
||||||
config.Argon2.KeyLength = schema.DefaultPasswordConfig.Argon2.KeyLength
|
config.Argon2.KeyLength = schema.DefaultPasswordConfig.Argon2.KeyLength
|
||||||
case config.Argon2.KeyLength < crypt.Argon2KeySizeMin:
|
case config.Argon2.KeyLength < argon2.KeyLengthMin:
|
||||||
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooSmall, hashArgon2, "key_length", config.Argon2.KeyLength, crypt.Argon2KeySizeMin))
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooSmall, hashArgon2, "key_length", config.Argon2.KeyLength, argon2.KeyLengthMin))
|
||||||
case config.Argon2.KeyLength > crypt.Argon2KeySizeMax:
|
case config.Argon2.KeyLength > argon2.KeyLengthMax:
|
||||||
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooLarge, hashArgon2, "key_length", config.Argon2.KeyLength, crypt.Argon2KeySizeMax))
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooLarge, hashArgon2, "key_length", config.Argon2.KeyLength, argon2.KeyLengthMax))
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case config.Argon2.SaltLength == 0:
|
case config.Argon2.SaltLength == 0:
|
||||||
config.Argon2.SaltLength = schema.DefaultPasswordConfig.Argon2.SaltLength
|
config.Argon2.SaltLength = schema.DefaultPasswordConfig.Argon2.SaltLength
|
||||||
case config.Argon2.SaltLength < crypt.Argon2SaltSizeMin:
|
case config.Argon2.SaltLength < argon2.SaltLengthMin:
|
||||||
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooSmall, hashArgon2, "salt_length", config.Argon2.SaltLength, crypt.Argon2SaltSizeMin))
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooSmall, hashArgon2, "salt_length", config.Argon2.SaltLength, argon2.SaltLengthMin))
|
||||||
case config.Argon2.SaltLength > crypt.Argon2SaltSizeMax:
|
case config.Argon2.SaltLength > argon2.SaltLengthMax:
|
||||||
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooLarge, hashArgon2, "salt_length", config.Argon2.SaltLength, crypt.Argon2SaltSizeMax))
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooLarge, hashArgon2, "salt_length", config.Argon2.SaltLength, argon2.SaltLengthMax))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,19 +153,19 @@ func validateFileAuthenticationBackendPasswordConfigSHA2Crypt(config *schema.Pas
|
||||||
switch {
|
switch {
|
||||||
case config.SHA2Crypt.Iterations == 0:
|
case config.SHA2Crypt.Iterations == 0:
|
||||||
config.SHA2Crypt.Iterations = schema.DefaultPasswordConfig.SHA2Crypt.Iterations
|
config.SHA2Crypt.Iterations = schema.DefaultPasswordConfig.SHA2Crypt.Iterations
|
||||||
case config.SHA2Crypt.Iterations < crypt.SHA2CryptIterationsMin:
|
case config.SHA2Crypt.Iterations < shacrypt.IterationsMin:
|
||||||
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooSmall, hashSHA2Crypt, "iterations", config.SHA2Crypt.Iterations, crypt.SHA2CryptIterationsMin))
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooSmall, hashSHA2Crypt, "iterations", config.SHA2Crypt.Iterations, shacrypt.IterationsMin))
|
||||||
case config.SHA2Crypt.Iterations > crypt.SHA2CryptIterationsMax:
|
case config.SHA2Crypt.Iterations > shacrypt.IterationsMax:
|
||||||
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooLarge, hashSHA2Crypt, "iterations", config.SHA2Crypt.Iterations, crypt.SHA2CryptIterationsMax))
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooLarge, hashSHA2Crypt, "iterations", config.SHA2Crypt.Iterations, shacrypt.IterationsMax))
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case config.SHA2Crypt.SaltLength == 0:
|
case config.SHA2Crypt.SaltLength == 0:
|
||||||
config.SHA2Crypt.SaltLength = schema.DefaultPasswordConfig.SHA2Crypt.SaltLength
|
config.SHA2Crypt.SaltLength = schema.DefaultPasswordConfig.SHA2Crypt.SaltLength
|
||||||
case config.SHA2Crypt.SaltLength < crypt.SHA2CryptSaltSizeMin:
|
case config.SHA2Crypt.SaltLength < shacrypt.SaltLengthMin:
|
||||||
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooSmall, hashSHA2Crypt, "salt_length", config.SHA2Crypt.SaltLength, crypt.SHA2CryptSaltSizeMin))
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooSmall, hashSHA2Crypt, "salt_length", config.SHA2Crypt.SaltLength, shacrypt.SaltLengthMin))
|
||||||
case config.SHA2Crypt.SaltLength > crypt.SHA2CryptSaltSizeMax:
|
case config.SHA2Crypt.SaltLength > shacrypt.SaltLengthMax:
|
||||||
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooLarge, hashSHA2Crypt, "salt_length", config.SHA2Crypt.SaltLength, crypt.SHA2CryptSaltSizeMax))
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooLarge, hashSHA2Crypt, "salt_length", config.SHA2Crypt.SaltLength, shacrypt.SaltLengthMax))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,19 +182,19 @@ func validateFileAuthenticationBackendPasswordConfigPBKDF2(config *schema.Passwo
|
||||||
switch {
|
switch {
|
||||||
case config.PBKDF2.Iterations == 0:
|
case config.PBKDF2.Iterations == 0:
|
||||||
config.PBKDF2.Iterations = schema.DefaultPasswordConfig.PBKDF2.Iterations
|
config.PBKDF2.Iterations = schema.DefaultPasswordConfig.PBKDF2.Iterations
|
||||||
case config.PBKDF2.Iterations < crypt.PBKDF2IterationsMin:
|
case config.PBKDF2.Iterations < pbkdf2.IterationsMin:
|
||||||
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooSmall, hashPBKDF2, "iterations", config.PBKDF2.Iterations, crypt.PBKDF2IterationsMin))
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooSmall, hashPBKDF2, "iterations", config.PBKDF2.Iterations, pbkdf2.IterationsMin))
|
||||||
case config.PBKDF2.Iterations > crypt.PBKDF2IterationsMax:
|
case config.PBKDF2.Iterations > pbkdf2.IterationsMax:
|
||||||
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooLarge, hashPBKDF2, "iterations", config.PBKDF2.Iterations, crypt.PBKDF2IterationsMax))
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooLarge, hashPBKDF2, "iterations", config.PBKDF2.Iterations, pbkdf2.IterationsMax))
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case config.PBKDF2.SaltLength == 0:
|
case config.PBKDF2.SaltLength == 0:
|
||||||
config.PBKDF2.SaltLength = schema.DefaultPasswordConfig.PBKDF2.SaltLength
|
config.PBKDF2.SaltLength = schema.DefaultPasswordConfig.PBKDF2.SaltLength
|
||||||
case config.PBKDF2.SaltLength < crypt.PBKDF2SaltSizeMin:
|
case config.PBKDF2.SaltLength < pbkdf2.SaltLengthMin:
|
||||||
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooSmall, hashPBKDF2, "salt_length", config.PBKDF2.SaltLength, crypt.PBKDF2SaltSizeMin))
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooSmall, hashPBKDF2, "salt_length", config.PBKDF2.SaltLength, pbkdf2.SaltLengthMin))
|
||||||
case config.PBKDF2.SaltLength > crypt.PBKDF2SaltSizeMax:
|
case config.PBKDF2.SaltLength > pbkdf2.SaltLengthMax:
|
||||||
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooLarge, hashPBKDF2, "salt_length", config.PBKDF2.SaltLength, crypt.PBKDF2SaltSizeMax))
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooLarge, hashPBKDF2, "salt_length", config.PBKDF2.SaltLength, pbkdf2.SaltLengthMax))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,53 +211,58 @@ func validateFileAuthenticationBackendPasswordConfigBCrypt(config *schema.Passwo
|
||||||
switch {
|
switch {
|
||||||
case config.BCrypt.Cost == 0:
|
case config.BCrypt.Cost == 0:
|
||||||
config.BCrypt.Cost = schema.DefaultPasswordConfig.BCrypt.Cost
|
config.BCrypt.Cost = schema.DefaultPasswordConfig.BCrypt.Cost
|
||||||
case config.BCrypt.Cost < crypt.BcryptCostMin:
|
case config.BCrypt.Cost < bcrypt.IterationsMin:
|
||||||
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooSmall, hashBCrypt, "cost", config.BCrypt.Cost, crypt.BcryptCostMin))
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooSmall, hashBCrypt, "cost", config.BCrypt.Cost, bcrypt.IterationsMin))
|
||||||
case config.BCrypt.Cost > crypt.BcryptCostMax:
|
case config.BCrypt.Cost > bcrypt.IterationsMax:
|
||||||
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooLarge, hashBCrypt, "cost", config.BCrypt.Cost, crypt.BcryptCostMax))
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooLarge, hashBCrypt, "cost", config.BCrypt.Cost, bcrypt.IterationsMax))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:gocyclo
|
||||||
func validateFileAuthenticationBackendPasswordConfigSCrypt(config *schema.Password, validator *schema.StructValidator) {
|
func validateFileAuthenticationBackendPasswordConfigSCrypt(config *schema.Password, validator *schema.StructValidator) {
|
||||||
switch {
|
switch {
|
||||||
case config.SCrypt.Iterations == 0:
|
case config.SCrypt.Iterations == 0:
|
||||||
config.SCrypt.Iterations = schema.DefaultPasswordConfig.SCrypt.Iterations
|
config.SCrypt.Iterations = schema.DefaultPasswordConfig.SCrypt.Iterations
|
||||||
case config.SCrypt.Iterations < crypt.ScryptIterationsMin:
|
case config.SCrypt.Iterations < scrypt.IterationsMin:
|
||||||
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooSmall, hashSCrypt, "iterations", config.SCrypt.Iterations, crypt.ScryptIterationsMin))
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooSmall, hashSCrypt, "iterations", config.SCrypt.Iterations, scrypt.IterationsMin))
|
||||||
|
case config.SCrypt.Iterations > scrypt.IterationsMax:
|
||||||
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooLarge, hashSCrypt, "iterations", config.SCrypt.Iterations, scrypt.IterationsMax))
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case config.SCrypt.BlockSize == 0:
|
case config.SCrypt.BlockSize == 0:
|
||||||
config.SCrypt.BlockSize = schema.DefaultPasswordConfig.SCrypt.BlockSize
|
config.SCrypt.BlockSize = schema.DefaultPasswordConfig.SCrypt.BlockSize
|
||||||
case config.SCrypt.BlockSize < crypt.ScryptBlockSizeMin:
|
case config.SCrypt.BlockSize < scrypt.BlockSizeMin:
|
||||||
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooSmall, hashSCrypt, "block_size", config.SCrypt.BlockSize, crypt.ScryptBlockSizeMin))
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooSmall, hashSCrypt, "block_size", config.SCrypt.BlockSize, scrypt.BlockSizeMin))
|
||||||
case config.SCrypt.BlockSize > crypt.ScryptBlockSizeMax:
|
case config.SCrypt.BlockSize > scrypt.BlockSizeMax:
|
||||||
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooLarge, hashSCrypt, "block_size", config.SCrypt.BlockSize, crypt.ScryptBlockSizeMax))
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooLarge, hashSCrypt, "block_size", config.SCrypt.BlockSize, scrypt.BlockSizeMax))
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case config.SCrypt.Parallelism == 0:
|
case config.SCrypt.Parallelism == 0:
|
||||||
config.SCrypt.Parallelism = schema.DefaultPasswordConfig.SCrypt.Parallelism
|
config.SCrypt.Parallelism = schema.DefaultPasswordConfig.SCrypt.Parallelism
|
||||||
case config.SCrypt.Parallelism < crypt.ScryptParallelismMin:
|
case config.SCrypt.Parallelism < scrypt.ParallelismMin:
|
||||||
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooSmall, hashSCrypt, "parallelism", config.SCrypt.Parallelism, crypt.ScryptParallelismMin))
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooSmall, hashSCrypt, "parallelism", config.SCrypt.Parallelism, scrypt.ParallelismMin))
|
||||||
|
case config.SCrypt.Parallelism > scrypt.ParallelismMax:
|
||||||
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooLarge, hashSCrypt, "parallelism", config.SCrypt.Parallelism, scrypt.ParallelismMax))
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case config.SCrypt.KeyLength == 0:
|
case config.SCrypt.KeyLength == 0:
|
||||||
config.SCrypt.KeyLength = schema.DefaultPasswordConfig.SCrypt.KeyLength
|
config.SCrypt.KeyLength = schema.DefaultPasswordConfig.SCrypt.KeyLength
|
||||||
case config.SCrypt.KeyLength < crypt.ScryptKeySizeMin:
|
case config.SCrypt.KeyLength < scrypt.KeyLengthMin:
|
||||||
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooSmall, hashSCrypt, "key_length", config.SCrypt.KeyLength, crypt.ScryptKeySizeMin))
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooSmall, hashSCrypt, "key_length", config.SCrypt.KeyLength, scrypt.KeyLengthMin))
|
||||||
case config.SCrypt.KeyLength > crypt.ScryptKeySizeMax:
|
case config.SCrypt.KeyLength > scrypt.KeyLengthMax:
|
||||||
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooLarge, hashSCrypt, "key_length", config.SCrypt.KeyLength, crypt.ScryptKeySizeMax))
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooLarge, hashSCrypt, "key_length", config.SCrypt.KeyLength, scrypt.KeyLengthMax))
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case config.SCrypt.SaltLength == 0:
|
case config.SCrypt.SaltLength == 0:
|
||||||
config.SCrypt.SaltLength = schema.DefaultPasswordConfig.SCrypt.SaltLength
|
config.SCrypt.SaltLength = schema.DefaultPasswordConfig.SCrypt.SaltLength
|
||||||
case config.SCrypt.SaltLength < crypt.ScryptSaltSizeMin:
|
case config.SCrypt.SaltLength < scrypt.SaltLengthMin:
|
||||||
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooSmall, hashSCrypt, "salt_length", config.SCrypt.SaltLength, crypt.ScryptSaltSizeMin))
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooSmall, hashSCrypt, "salt_length", config.SCrypt.SaltLength, scrypt.SaltLengthMin))
|
||||||
case config.SCrypt.SaltLength > crypt.ScryptSaltSizeMax:
|
case config.SCrypt.SaltLength > scrypt.SaltLengthMax:
|
||||||
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooLarge, hashSCrypt, "salt_length", config.SCrypt.SaltLength, crypt.ScryptSaltSizeMax))
|
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordOptionTooLarge, hashSCrypt, "salt_length", config.SCrypt.SaltLength, scrypt.SaltLengthMax))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -409,18 +409,22 @@ func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenSCryptOptio
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenSCryptOptionsTooHigh() {
|
func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenSCryptOptionsTooHigh() {
|
||||||
|
suite.config.File.Password.SCrypt.Iterations = 59
|
||||||
suite.config.File.Password.SCrypt.BlockSize = 360287970189639672
|
suite.config.File.Password.SCrypt.BlockSize = 360287970189639672
|
||||||
|
suite.config.File.Password.SCrypt.Parallelism = 1073741825
|
||||||
suite.config.File.Password.SCrypt.KeyLength = 1374389534409
|
suite.config.File.Password.SCrypt.KeyLength = 1374389534409
|
||||||
suite.config.File.Password.SCrypt.SaltLength = 2147483647
|
suite.config.File.Password.SCrypt.SaltLength = 2147483647
|
||||||
|
|
||||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||||
|
|
||||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||||
suite.Require().Len(suite.validator.Errors(), 3)
|
suite.Require().Len(suite.validator.Errors(), 5)
|
||||||
|
|
||||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: file: password: scrypt: option 'block_size' is configured as '360287970189639672' but must be less than or equal to '36028797018963967'")
|
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: file: password: scrypt: option 'iterations' is configured as '59' but must be less than or equal to '58'")
|
||||||
suite.Assert().EqualError(suite.validator.Errors()[1], "authentication_backend: file: password: scrypt: option 'key_length' is configured as '1374389534409' but must be less than or equal to '137438953440'")
|
suite.Assert().EqualError(suite.validator.Errors()[1], "authentication_backend: file: password: scrypt: option 'block_size' is configured as '360287970189639672' but must be less than or equal to '36028797018963967'")
|
||||||
suite.Assert().EqualError(suite.validator.Errors()[2], "authentication_backend: file: password: scrypt: option 'salt_length' is configured as '2147483647' but must be less than or equal to '1024'")
|
suite.Assert().EqualError(suite.validator.Errors()[2], "authentication_backend: file: password: scrypt: option 'parallelism' is configured as '1073741825' but must be less than or equal to '1073741823'")
|
||||||
|
suite.Assert().EqualError(suite.validator.Errors()[3], "authentication_backend: file: password: scrypt: option 'key_length' is configured as '1374389534409' but must be less than or equal to '137438953440'")
|
||||||
|
suite.Assert().EqualError(suite.validator.Errors()[4], "authentication_backend: file: password: scrypt: option 'salt_length' is configured as '2147483647' but must be less than or equal to '1024'")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenArgon2OptionsTooLow() {
|
func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenArgon2OptionsTooLow() {
|
||||||
|
@ -437,13 +441,14 @@ func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenArgon2Optio
|
||||||
|
|
||||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: file: password: argon2: option 'iterations' is configured as '-1' but must be greater than or equal to '1'")
|
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: file: password: argon2: option 'iterations' is configured as '-1' but must be greater than or equal to '1'")
|
||||||
suite.Assert().EqualError(suite.validator.Errors()[1], "authentication_backend: file: password: argon2: option 'parallelism' is configured as '-1' but must be greater than or equal to '1'")
|
suite.Assert().EqualError(suite.validator.Errors()[1], "authentication_backend: file: password: argon2: option 'parallelism' is configured as '-1' but must be greater than or equal to '1'")
|
||||||
suite.Assert().EqualError(suite.validator.Errors()[2], "authentication_backend: file: password: argon2: option 'memory' is configured as '-1' but must be greater than or equal to '1'")
|
suite.Assert().EqualError(suite.validator.Errors()[2], "authentication_backend: file: password: argon2: option 'memory' is configured as '-1' but must be greater than or equal to '8'")
|
||||||
suite.Assert().EqualError(suite.validator.Errors()[3], "authentication_backend: file: password: argon2: option 'key_length' is configured as '1' but must be greater than or equal to '4'")
|
suite.Assert().EqualError(suite.validator.Errors()[3], "authentication_backend: file: password: argon2: option 'key_length' is configured as '1' but must be greater than or equal to '4'")
|
||||||
suite.Assert().EqualError(suite.validator.Errors()[4], "authentication_backend: file: password: argon2: option 'salt_length' is configured as '-1' but must be greater than or equal to '1'")
|
suite.Assert().EqualError(suite.validator.Errors()[4], "authentication_backend: file: password: argon2: option 'salt_length' is configured as '-1' but must be greater than or equal to '1'")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenArgon2OptionsTooHigh() {
|
func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenArgon2OptionsTooHigh() {
|
||||||
suite.config.File.Password.Argon2.Iterations = 9999999999
|
suite.config.File.Password.Argon2.Iterations = 9999999999
|
||||||
|
suite.config.File.Password.Argon2.Memory = 4294967296
|
||||||
suite.config.File.Password.Argon2.Parallelism = 16777216
|
suite.config.File.Password.Argon2.Parallelism = 16777216
|
||||||
suite.config.File.Password.Argon2.KeyLength = 9999999998
|
suite.config.File.Password.Argon2.KeyLength = 9999999998
|
||||||
suite.config.File.Password.Argon2.SaltLength = 9999999997
|
suite.config.File.Password.Argon2.SaltLength = 9999999997
|
||||||
|
@ -455,6 +460,7 @@ func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenArgon2Optio
|
||||||
|
|
||||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: file: password: argon2: option 'iterations' is configured as '9999999999' but must be less than or equal to '2147483647'")
|
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: file: password: argon2: option 'iterations' is configured as '9999999999' but must be less than or equal to '2147483647'")
|
||||||
suite.Assert().EqualError(suite.validator.Errors()[1], "authentication_backend: file: password: argon2: option 'parallelism' is configured as '16777216' but must be less than or equal to '16777215'")
|
suite.Assert().EqualError(suite.validator.Errors()[1], "authentication_backend: file: password: argon2: option 'parallelism' is configured as '16777216' but must be less than or equal to '16777215'")
|
||||||
|
suite.Assert().EqualError(suite.validator.Errors()[2], "authentication_backend: file: password: argon2: option 'memory' is configured as '4294967296' but must be less than or equal to '4294967295'")
|
||||||
suite.Assert().EqualError(suite.validator.Errors()[3], "authentication_backend: file: password: argon2: option 'key_length' is configured as '9999999998' but must be less than or equal to '2147483647'")
|
suite.Assert().EqualError(suite.validator.Errors()[3], "authentication_backend: file: password: argon2: option 'key_length' is configured as '9999999998' but must be less than or equal to '2147483647'")
|
||||||
suite.Assert().EqualError(suite.validator.Errors()[4], "authentication_backend: file: password: argon2: option 'salt_length' is configured as '9999999997' but must be less than or equal to '2147483647'")
|
suite.Assert().EqualError(suite.validator.Errors()[4], "authentication_backend: file: password: argon2: option 'salt_length' is configured as '9999999997' but must be less than or equal to '2147483647'")
|
||||||
}
|
}
|
||||||
|
@ -468,7 +474,19 @@ func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenArgon2Memor
|
||||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||||
suite.Require().Len(suite.validator.Errors(), 1)
|
suite.Require().Len(suite.validator.Errors(), 1)
|
||||||
|
|
||||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: file: password: argon2: option 'memory' is configured as '4' but must be greater than or equal to '32' or '4' (the value of 'parallelism) multiplied by '8'")
|
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: file: password: argon2: option 'memory' is configured as '4' but must be greater than or equal to '8'")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenArgon2MemoryTooLowMultiplier() {
|
||||||
|
suite.config.File.Password.Argon2.Memory = 8
|
||||||
|
suite.config.File.Password.Argon2.Parallelism = 4
|
||||||
|
|
||||||
|
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||||
|
|
||||||
|
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||||
|
suite.Require().Len(suite.validator.Errors(), 1)
|
||||||
|
|
||||||
|
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: file: password: argon2: option 'memory' is configured as '8' but must be greater than or equal to '32' or '4' (the value of 'parallelism) multiplied by '8'")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenBadAlgorithmDefined() {
|
func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenBadAlgorithmDefined() {
|
||||||
|
|
|
@ -838,7 +838,7 @@ func TestValidateOIDCClientRedirectURIsSupportingPrivateUseURISchemes(t *testing
|
||||||
}
|
}
|
||||||
|
|
||||||
func MustDecodeSecret(value string) *schema.PasswordDigest {
|
func MustDecodeSecret(value string) *schema.PasswordDigest {
|
||||||
if secret, err := schema.NewPasswordDigest(value, true); err != nil {
|
if secret, err := schema.DecodePasswordDigest(value); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
} else {
|
} else {
|
||||||
return secret
|
return secret
|
||||||
|
|
|
@ -52,13 +52,7 @@ func OpenIDConnectAuthorization(ctx *middlewares.AutheliaCtx, rw http.ResponseWr
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if issuer, err = ctx.IssuerURL(); err != nil {
|
issuer = ctx.RootURL()
|
||||||
ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' could not be processed: error occurred determining issuer: %+v", requester.GetID(), clientID, err)
|
|
||||||
|
|
||||||
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrIssuerCouldNotDerive)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
userSession := ctx.GetSession()
|
userSession := ctx.GetSession()
|
||||||
|
|
||||||
|
|
|
@ -130,12 +130,7 @@ func OpenIDConnectConsentPOST(ctx *middlewares.AutheliaCtx) {
|
||||||
query url.Values
|
query url.Values
|
||||||
)
|
)
|
||||||
|
|
||||||
if redirectURI, err = ctx.IssuerURL(); err != nil {
|
redirectURI = ctx.RootURL()
|
||||||
ctx.Logger.Errorf("Failed to parse the consent redirect URL: %+v", err)
|
|
||||||
ctx.SetJSONError(messageOperationFailed)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if query, err = url.ParseQuery(consent.Form); err != nil {
|
if query, err = url.ParseQuery(consent.Form); err != nil {
|
||||||
ctx.Logger.Errorf("Failed to parse the consent form values: %+v", err)
|
ctx.Logger.Errorf("Failed to parse the consent form values: %+v", err)
|
||||||
|
|
|
@ -20,13 +20,7 @@ func OpenIDConnectConfigurationWellKnownGET(ctx *middlewares.AutheliaCtx) {
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
if issuer, err = ctx.IssuerURL(); err != nil {
|
issuer = ctx.RootURL()
|
||||||
ctx.Logger.Errorf("Error occurred determining OpenID Connect issuer details: %+v", err)
|
|
||||||
|
|
||||||
ctx.ReplyStatusCode(fasthttp.StatusBadRequest)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
wellKnown := ctx.Providers.OpenIDConnect.GetOpenIDConnectWellKnownConfiguration(issuer.String())
|
wellKnown := ctx.Providers.OpenIDConnect.GetOpenIDConnectWellKnownConfiguration(issuer.String())
|
||||||
|
|
||||||
|
@ -52,13 +46,7 @@ func OAuthAuthorizationServerWellKnownGET(ctx *middlewares.AutheliaCtx) {
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
if issuer, err = ctx.IssuerURL(); err != nil {
|
issuer = ctx.RootURL()
|
||||||
ctx.Logger.Errorf("Error occurred determining OpenID Connect issuer details: %+v", err)
|
|
||||||
|
|
||||||
ctx.ReplyStatusCode(fasthttp.StatusBadRequest)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
wellKnown := ctx.Providers.OpenIDConnect.GetOAuth2WellKnownConfiguration(issuer.String())
|
wellKnown := ctx.Providers.OpenIDConnect.GetOAuth2WellKnownConfiguration(issuer.String())
|
||||||
|
|
||||||
|
|
|
@ -144,11 +144,7 @@ func handleOIDCWorkflowResponseWithTargetURL(ctx *middlewares.AutheliaCtx, targe
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if issuerURL, err = ctx.IssuerURL(); err != nil {
|
issuerURL = ctx.RootURL()
|
||||||
ctx.Error(fmt.Errorf("unable to get issuer for redirection: %w", err), messageAuthenticationFailed)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if targetURL.Host != issuerURL.Host {
|
if targetURL.Host != issuerURL.Host {
|
||||||
ctx.Error(fmt.Errorf("unable to redirect to '%s': target host '%s' does not match expected issuer host '%s'", targetURL, targetURL.Host, issuerURL.Host), messageAuthenticationFailed)
|
ctx.Error(fmt.Errorf("unable to redirect to '%s': target host '%s' does not match expected issuer host '%s'", targetURL, targetURL.Host, issuerURL.Host), messageAuthenticationFailed)
|
||||||
|
@ -221,11 +217,7 @@ func handleOIDCWorkflowResponseWithID(ctx *middlewares.AutheliaCtx, id string) {
|
||||||
form url.Values
|
form url.Values
|
||||||
)
|
)
|
||||||
|
|
||||||
if targetURL, err = ctx.IssuerURL(); err != nil {
|
targetURL = ctx.RootURL()
|
||||||
ctx.Error(fmt.Errorf("unable to get issuer for redirection: %w", err), messageAuthenticationFailed)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if form, err = consent.GetForm(); err != nil {
|
if form, err = consent.GetForm(); err != nil {
|
||||||
ctx.Error(fmt.Errorf("unable to get authorization form values from consent session with challenge id '%s': %w", consent.ChallengeID, err), messageAuthenticationFailed)
|
ctx.Error(fmt.Errorf("unable to get authorization form values from consent session with challenge id '%s': %w", consent.ChallengeID, err), messageAuthenticationFailed)
|
||||||
|
|
|
@ -3,7 +3,6 @@ package logging
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -16,15 +15,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestShouldWriteLogsToFile(t *testing.T) {
|
func TestShouldWriteLogsToFile(t *testing.T) {
|
||||||
dir, err := os.MkdirTemp("/tmp", "logs-dir")
|
dir := t.TempDir()
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
|
|
||||||
path := fmt.Sprintf("%s/authelia.log", dir)
|
path := fmt.Sprintf("%s/authelia.log", dir)
|
||||||
err = InitializeLogger(schema.LogConfiguration{Format: "text", FilePath: path, KeepStdout: false}, false)
|
err := InitializeLogger(schema.LogConfiguration{Format: "text", FilePath: path, KeepStdout: false}, false)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
Logger().Info("This is a test")
|
Logger().Info("This is a test")
|
||||||
|
@ -39,15 +33,10 @@ func TestShouldWriteLogsToFile(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShouldWriteLogsToFileAndStdout(t *testing.T) {
|
func TestShouldWriteLogsToFileAndStdout(t *testing.T) {
|
||||||
dir, err := os.MkdirTemp("/tmp", "logs-dir")
|
dir := t.TempDir()
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
|
|
||||||
path := fmt.Sprintf("%s/authelia.log", dir)
|
path := fmt.Sprintf("%s/authelia.log", dir)
|
||||||
err = InitializeLogger(schema.LogConfiguration{Format: "text", FilePath: path, KeepStdout: true}, false)
|
err := InitializeLogger(schema.LogConfiguration{Format: "text", FilePath: path, KeepStdout: true}, false)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
Logger().Info("This is a test")
|
Logger().Info("This is a test")
|
||||||
|
@ -62,15 +51,10 @@ func TestShouldWriteLogsToFileAndStdout(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShouldFormatLogsAsJSON(t *testing.T) {
|
func TestShouldFormatLogsAsJSON(t *testing.T) {
|
||||||
dir, err := os.MkdirTemp("/tmp", "logs-dir")
|
dir := t.TempDir()
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
|
|
||||||
path := fmt.Sprintf("%s/authelia.log", dir)
|
path := fmt.Sprintf("%s/authelia.log", dir)
|
||||||
err = InitializeLogger(schema.LogConfiguration{Format: "json", FilePath: path, KeepStdout: false}, false)
|
err := InitializeLogger(schema.LogConfiguration{Format: "json", FilePath: path, KeepStdout: false}, false)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
Logger().Info("This is a test")
|
Logger().Info("This is a test")
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/asaskevich/govalidator"
|
"github.com/asaskevich/govalidator"
|
||||||
|
@ -81,7 +80,7 @@ func (ctx *AutheliaCtx) ReplyError(err error, message string) {
|
||||||
ctx.Logger.Error(marshalErr)
|
ctx.Logger.Error(marshalErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.SetContentTypeBytes(contentTypeApplicationJSON)
|
ctx.SetContentTypeApplicationJSON()
|
||||||
ctx.SetBody(b)
|
ctx.SetBody(b)
|
||||||
ctx.Logger.Debug(err)
|
ctx.Logger.Debug(err)
|
||||||
}
|
}
|
||||||
|
@ -90,7 +89,7 @@ func (ctx *AutheliaCtx) ReplyError(err error, message string) {
|
||||||
func (ctx *AutheliaCtx) ReplyStatusCode(statusCode int) {
|
func (ctx *AutheliaCtx) ReplyStatusCode(statusCode int) {
|
||||||
ctx.Response.Reset()
|
ctx.Response.Reset()
|
||||||
ctx.SetStatusCode(statusCode)
|
ctx.SetStatusCode(statusCode)
|
||||||
ctx.SetContentTypeBytes(contentTypeTextPlain)
|
ctx.SetContentTypeTextPlain()
|
||||||
ctx.SetBodyString(fmt.Sprintf("%d %s", statusCode, fasthttp.StatusMessage(statusCode)))
|
ctx.SetBodyString(fmt.Sprintf("%d %s", statusCode, fasthttp.StatusMessage(statusCode)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +107,7 @@ func (ctx *AutheliaCtx) ReplyJSON(data any, statusCode int) (err error) {
|
||||||
ctx.SetStatusCode(statusCode)
|
ctx.SetStatusCode(statusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.SetContentTypeBytes(contentTypeApplicationJSON)
|
ctx.SetContentTypeApplicationJSON()
|
||||||
ctx.SetBody(body)
|
ctx.SetBody(body)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -145,7 +144,7 @@ func (ctx *AutheliaCtx) XForwardedProto() (proto []byte) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// XForwardedMethod return the content of the X-Forwarded-Method header.
|
// XForwardedMethod return the content of the X-Forwarded-Method header.
|
||||||
func (ctx *AutheliaCtx) XForwardedMethod() (method []byte) {
|
func (ctx *AutheliaCtx) XForwardedMethod() []byte {
|
||||||
return ctx.RequestCtx.Request.Header.PeekBytes(headerXForwardedMethod)
|
return ctx.RequestCtx.Request.Header.PeekBytes(headerXForwardedMethod)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,79 +170,61 @@ func (ctx *AutheliaCtx) XForwardedURI() (uri []byte) {
|
||||||
return uri
|
return uri
|
||||||
}
|
}
|
||||||
|
|
||||||
// XAutheliaURL return the content of the X-Authelia-URL header.
|
// XOriginalURL returns the content of the X-Original-URL header.
|
||||||
func (ctx *AutheliaCtx) XAutheliaURL() (autheliaURL []byte) {
|
func (ctx *AutheliaCtx) XOriginalURL() []byte {
|
||||||
|
return ctx.RequestCtx.Request.Header.PeekBytes(headerXOriginalURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// XOriginalMethod return the content of the X-Original-Method header.
|
||||||
|
func (ctx *AutheliaCtx) XOriginalMethod() []byte {
|
||||||
|
return ctx.RequestCtx.Request.Header.PeekBytes(headerXOriginalMethod)
|
||||||
|
}
|
||||||
|
|
||||||
|
// XAutheliaURL return the content of the X-Authelia-URL header which is used to communicate the location of the
|
||||||
|
// portal when using proxies like Envoy.
|
||||||
|
func (ctx *AutheliaCtx) XAutheliaURL() []byte {
|
||||||
return ctx.RequestCtx.Request.Header.PeekBytes(headerXAutheliaURL)
|
return ctx.RequestCtx.Request.Header.PeekBytes(headerXAutheliaURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryArgRedirect return the content of the rd query argument.
|
// QueryArgRedirect return the content of the rd query argument.
|
||||||
func (ctx *AutheliaCtx) QueryArgRedirect() (val []byte) {
|
func (ctx *AutheliaCtx) QueryArgRedirect() []byte {
|
||||||
return ctx.RequestCtx.QueryArgs().PeekBytes(queryArgRedirect)
|
return ctx.RequestCtx.QueryArgs().PeekBytes(qryArgRedirect)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BasePath returns the base_url as per the path visited by the client.
|
// BasePath returns the base_url as per the path visited by the client.
|
||||||
func (ctx *AutheliaCtx) BasePath() (base string) {
|
func (ctx *AutheliaCtx) BasePath() string {
|
||||||
if baseURL := ctx.UserValueBytes(UserValueKeyBaseURL); baseURL != nil {
|
if baseURL := ctx.UserValueBytes(UserValueKeyBaseURL); baseURL != nil {
|
||||||
return baseURL.(string)
|
return baseURL.(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
return base
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExternalRootURL gets the X-Forwarded-Proto, X-Forwarded-Host headers and the BasePath and forms them into a URL.
|
// BasePathSlash is the same as BasePath but returns a final slash as well.
|
||||||
func (ctx *AutheliaCtx) ExternalRootURL() (string, error) {
|
func (ctx *AutheliaCtx) BasePathSlash() string {
|
||||||
protocol := ctx.XForwardedProto()
|
if baseURL := ctx.UserValueBytes(UserValueKeyBaseURL); baseURL != nil {
|
||||||
if protocol == nil {
|
return baseURL.(string) + strSlash
|
||||||
return "", errMissingXForwardedProto
|
|
||||||
}
|
}
|
||||||
|
|
||||||
host := ctx.XForwardedHost()
|
return strSlash
|
||||||
if host == nil {
|
|
||||||
return "", errMissingXForwardedHost
|
|
||||||
}
|
|
||||||
|
|
||||||
externalRootURL := fmt.Sprintf("%s://%s", protocol, host)
|
|
||||||
|
|
||||||
if base := ctx.BasePath(); base != "" {
|
|
||||||
externalBaseURL, err := url.ParseRequestURI(externalRootURL)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
externalBaseURL.Path = path.Join(externalBaseURL.Path, base)
|
|
||||||
|
|
||||||
return externalBaseURL.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return externalRootURL, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IssuerURL returns the expected Issuer.
|
// RootURL returns the Root URL.
|
||||||
func (ctx *AutheliaCtx) IssuerURL() (issuerURL *url.URL, err error) {
|
func (ctx *AutheliaCtx) RootURL() (issuerURL *url.URL) {
|
||||||
issuerURL = &url.URL{
|
return &url.URL{
|
||||||
Scheme: "https",
|
Scheme: string(ctx.XForwardedProto()),
|
||||||
|
Host: string(ctx.XForwardedHost()),
|
||||||
|
Path: ctx.BasePath(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if scheme := ctx.XForwardedProto(); scheme != nil {
|
|
||||||
issuerURL.Scheme = string(scheme)
|
|
||||||
}
|
|
||||||
|
|
||||||
if host := ctx.XForwardedHost(); len(host) != 0 {
|
|
||||||
issuerURL.Host = string(host)
|
|
||||||
} else {
|
|
||||||
return nil, errMissingXForwardedHost
|
|
||||||
}
|
|
||||||
|
|
||||||
if base := ctx.BasePath(); base != "" {
|
|
||||||
issuerURL.Path = path.Join(issuerURL.Path, base)
|
|
||||||
}
|
|
||||||
|
|
||||||
return issuerURL, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// XOriginalURL return the content of the X-Original-URL header.
|
// RootURLSlash is the same as RootURL but includes a final slash as well.
|
||||||
func (ctx *AutheliaCtx) XOriginalURL() []byte {
|
func (ctx *AutheliaCtx) RootURLSlash() (issuerURL *url.URL) {
|
||||||
return ctx.RequestCtx.Request.Header.PeekBytes(headerXOriginalURL)
|
return &url.URL{
|
||||||
|
Scheme: string(ctx.XForwardedProto()),
|
||||||
|
Host: string(ctx.XForwardedHost()),
|
||||||
|
Path: ctx.BasePathSlash(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSession return the user session. Any update will be saved in cache.
|
// GetSession return the user session. Any update will be saved in cache.
|
||||||
|
@ -264,7 +245,7 @@ func (ctx *AutheliaCtx) SaveSession(userSession session.UserSession) error {
|
||||||
|
|
||||||
// ReplyOK is a helper method to reply ok.
|
// ReplyOK is a helper method to reply ok.
|
||||||
func (ctx *AutheliaCtx) ReplyOK() {
|
func (ctx *AutheliaCtx) ReplyOK() {
|
||||||
ctx.SetContentTypeBytes(contentTypeApplicationJSON)
|
ctx.SetContentTypeApplicationJSON()
|
||||||
ctx.SetBody(okMessageBytes)
|
ctx.SetBody(okMessageBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,7 +358,7 @@ func (ctx *AutheliaCtx) SpecialRedirect(uri string, statusCode int) {
|
||||||
statusCode = fasthttp.StatusFound
|
statusCode = fasthttp.StatusFound
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.SetContentTypeBytes(contentTypeTextHTML)
|
ctx.SetContentTypeTextHTML()
|
||||||
ctx.SetStatusCode(statusCode)
|
ctx.SetStatusCode(statusCode)
|
||||||
|
|
||||||
u := fasthttp.AcquireURI()
|
u := fasthttp.AcquireURI()
|
||||||
|
@ -400,3 +381,18 @@ func (ctx *AutheliaCtx) RecordAuthentication(success, regulated bool, method str
|
||||||
|
|
||||||
ctx.Providers.Metrics.RecordAuthentication(success, regulated, method)
|
ctx.Providers.Metrics.RecordAuthentication(success, regulated, method)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetContentTypeTextPlain efficiently sets the Content-Type header to 'text/plain; charset=utf-8'.
|
||||||
|
func (ctx *AutheliaCtx) SetContentTypeTextPlain() {
|
||||||
|
ctx.SetContentTypeBytes(contentTypeTextPlain)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetContentTypeTextHTML efficiently sets the Content-Type header to 'text/html; charset=utf-8'.
|
||||||
|
func (ctx *AutheliaCtx) SetContentTypeTextHTML() {
|
||||||
|
ctx.SetContentTypeBytes(contentTypeTextHTML)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetContentTypeApplicationJSON efficiently sets the Content-Type header to 'application/json; charset=utf-8'.
|
||||||
|
func (ctx *AutheliaCtx) SetContentTypeApplicationJSON() {
|
||||||
|
ctx.SetContentTypeBytes(contentTypeApplicationJSON)
|
||||||
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@ func TestIssuerURL(t *testing.T) {
|
||||||
name string
|
name string
|
||||||
proto, host, base string
|
proto, host, base string
|
||||||
expected string
|
expected string
|
||||||
err string
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Standard",
|
name: "Standard",
|
||||||
|
@ -36,7 +35,7 @@ func TestIssuerURL(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "NoHost",
|
name: "NoHost",
|
||||||
proto: "https", host: "", base: "",
|
proto: "https", host: "", base: "",
|
||||||
err: "Missing header X-Forwarded-Host",
|
expected: "https:",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,21 +51,14 @@ func TestIssuerURL(t *testing.T) {
|
||||||
mock.Ctx.SetUserValue("base_url", tc.base)
|
mock.Ctx.SetUserValue("base_url", tc.base)
|
||||||
}
|
}
|
||||||
|
|
||||||
actual, err := mock.Ctx.IssuerURL()
|
actual := mock.Ctx.RootURL()
|
||||||
|
|
||||||
switch tc.err {
|
require.NotNil(t, actual)
|
||||||
case "":
|
|
||||||
assert.NoError(t, err)
|
|
||||||
require.NotNil(t, actual)
|
|
||||||
|
|
||||||
assert.Equal(t, tc.expected, actual.String())
|
assert.Equal(t, tc.expected, actual.String())
|
||||||
assert.Equal(t, tc.proto, actual.Scheme)
|
assert.Equal(t, tc.proto, actual.Scheme)
|
||||||
assert.Equal(t, tc.host, actual.Host)
|
assert.Equal(t, tc.host, actual.Host)
|
||||||
assert.Equal(t, tc.base, actual.Path)
|
assert.Equal(t, tc.base, actual.Path)
|
||||||
default:
|
|
||||||
assert.EqualError(t, err, tc.err)
|
|
||||||
assert.Nil(t, actual)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ var (
|
||||||
|
|
||||||
headerXForwardedURI = []byte("X-Forwarded-URI")
|
headerXForwardedURI = []byte("X-Forwarded-URI")
|
||||||
headerXOriginalURL = []byte("X-Original-URL")
|
headerXOriginalURL = []byte("X-Original-URL")
|
||||||
|
headerXOriginalMethod = []byte("X-Original-Method")
|
||||||
headerXForwardedMethod = []byte("X-Forwarded-Method")
|
headerXForwardedMethod = []byte("X-Forwarded-Method")
|
||||||
|
|
||||||
headerVary = []byte(fasthttp.HeaderVary)
|
headerVary = []byte(fasthttp.HeaderVary)
|
||||||
|
@ -67,13 +68,17 @@ var (
|
||||||
const (
|
const (
|
||||||
strProtoHTTPS = "https"
|
strProtoHTTPS = "https"
|
||||||
strProtoHTTP = "http"
|
strProtoHTTP = "http"
|
||||||
|
strSlash = "/"
|
||||||
|
|
||||||
|
queryArgRedirect = "rd"
|
||||||
|
queryArgToken = "token"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
protoHTTPS = []byte(strProtoHTTPS)
|
protoHTTPS = []byte(strProtoHTTPS)
|
||||||
protoHTTP = []byte(strProtoHTTP)
|
protoHTTP = []byte(strProtoHTTP)
|
||||||
|
|
||||||
queryArgRedirect = []byte("rd")
|
qryArgRedirect = []byte(queryArgRedirect)
|
||||||
|
|
||||||
// UserValueKeyBaseURL is the User Value key where we store the Base URL.
|
// UserValueKeyBaseURL is the User Value key where we store the Base URL.
|
||||||
UserValueKeyBaseURL = []byte("base_url")
|
UserValueKeyBaseURL = []byte("base_url")
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
|
"path"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt/v4"
|
"github.com/golang-jwt/jwt/v4"
|
||||||
|
@ -62,7 +63,7 @@ func IdentityVerificationStart(args IdentityVerificationStartArgs, delayFunc Tim
|
||||||
|
|
||||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||||
|
|
||||||
ss, err := token.SignedString([]byte(ctx.Configuration.JWTSecret))
|
signedToken, err := token.SignedString([]byte(ctx.Configuration.JWTSecret))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(err, messageOperationFailed)
|
ctx.Error(err, messageOperationFailed)
|
||||||
return
|
return
|
||||||
|
@ -73,23 +74,23 @@ func IdentityVerificationStart(args IdentityVerificationStartArgs, delayFunc Tim
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
uri string
|
|
||||||
)
|
|
||||||
|
|
||||||
if uri, err = ctx.ExternalRootURL(); err != nil {
|
|
||||||
ctx.Error(err, messageOperationFailed)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
disableHTML := false
|
disableHTML := false
|
||||||
if ctx.Configuration.Notifier.SMTP != nil {
|
if ctx.Configuration.Notifier.SMTP != nil {
|
||||||
disableHTML = ctx.Configuration.Notifier.SMTP.DisableHTMLEmails
|
disableHTML = ctx.Configuration.Notifier.SMTP.DisableHTMLEmails
|
||||||
}
|
}
|
||||||
|
|
||||||
|
linkURL := ctx.RootURL()
|
||||||
|
|
||||||
|
query := linkURL.Query()
|
||||||
|
|
||||||
|
query.Set(queryArgToken, signedToken)
|
||||||
|
|
||||||
|
linkURL.Path = path.Join(linkURL.Path, args.TargetEndpoint)
|
||||||
|
linkURL.RawQuery = query.Encode()
|
||||||
|
|
||||||
values := templates.EmailIdentityVerificationValues{
|
values := templates.EmailIdentityVerificationValues{
|
||||||
Title: args.MailTitle,
|
Title: args.MailTitle,
|
||||||
LinkURL: fmt.Sprintf("%s%s?token=%s", uri, args.TargetEndpoint, ss),
|
LinkURL: linkURL.String(),
|
||||||
LinkText: args.MailButtonContent,
|
LinkText: args.MailButtonContent,
|
||||||
DisplayName: identity.DisplayName,
|
DisplayName: identity.DisplayName,
|
||||||
RemoteIP: ctx.RemoteIP().String(),
|
RemoteIP: ctx.RemoteIP().String(),
|
||||||
|
|