Merge remote tracking branch 'origin/master' into feat-settings-ui

Signed-off-by: James Elliott <james-d-elliott@users.noreply.github.com>
feat-otp-verification
James Elliott 2023-04-14 20:58:49 +10:00
commit 774f64a932
No known key found for this signature in database
GPG Key ID: 0F1C4A096E857E49
82 changed files with 7189 additions and 1151 deletions

View File

@ -1480,12 +1480,6 @@ notifier:
# - email # - email
# - profile # - profile
## Grant Types configures which grants this client can obtain.
## It's not recommended to define this unless you know what you're doing.
# grant_types:
# - refresh_token
# - authorization_code
## Response Types configures which responses this client can be sent. ## Response Types configures which responses this client can be sent.
## It's not recommended to define this unless you know what you're doing. ## It's not recommended to define this unless you know what you're doing.
# response_types: # response_types:
@ -1495,7 +1489,14 @@ notifier:
# response_modes: # response_modes:
# - form_post # - form_post
# - query # - query
# - fragment
## Grant Types configures which grants this client can obtain.
## It's not recommended to define this unless you know what you're doing.
# grant_types:
# - authorization_code
## The permitted client authentication method for the Token Endpoint for this client.
# token_endpoint_auth_method: client_secret_basic
## The policy to require for this client; one_factor or two_factor. ## The policy to require for this client; one_factor or two_factor.
# authorization_policy: two_factor # authorization_policy: two_factor

View File

@ -451,9 +451,40 @@ A list of scopes to allow this client to consume. See
documentation for the application you are trying to configure [OpenID Connect 1.0] for will likely have a list of scopes documentation for the application you are trying to configure [OpenID Connect 1.0] for will likely have a list of scopes
or claims required which can be matched with the above guide. or claims required which can be matched with the above guide.
#### response_types
{{< confkey type="list(string)" default="code" required="no" >}}
*__Security Note:__ It is recommended that only the `code` response type (i.e. the default) is used. The other response
types are not as secure as this response type.*
A list of response types this client supports. If a response type not in this list is requested by a client then an
error will be returned to the client. The response type indicates the types of values that are returned to the client.
See the [Response Types](../../integration/openid-connect/introduction.md#response-types) section of the
[OpenID Connect 1.0 Integration Guide](../../integration/openid-connect/introduction.md#response-types) for more information.
#### response_modes
{{< confkey type="list(string)" default="form_post, query" required="no" >}}
*__Important Note:__ It is recommended that this isn't configured at this time unless you know what you're doing.*
A list of response modes this client supports. If a response mode not in this list is requested by a client then an
error will be returned to the client. The response mode controls how the response type is returned to the client.
See the [Response Modes](../../integration/openid-connect/introduction.md#response-modes) section of the
[OpenID Connect 1.0 Integration Guide](../../integration/openid-connect/introduction.md#response-modes) for more
information.
The default values are based on the [response_types](#responsetypes) values. When the [response_types](#responsetypes)
values include the `code` type then the `query` response mode will be included. When any other type is included the
`fragment` response mode will be included. It's important to note at this time we do not support the `none` response
type, but when it is supported it will include the `query` response mode.
#### grant_types #### grant_types
{{< confkey type="list(string)" default="refresh_token, authorization_code" required="no" >}} {{< confkey type="list(string)" default="authorization_code" required="no" >}}
*__Important Note:__ It is recommended that this isn't configured at this time unless you know what you're doing.* *__Important Note:__ It is recommended that this isn't configured at this time unless you know what you're doing.*
@ -462,28 +493,6 @@ The list of grant types this client is permitted to use in order to obtain acces
See the [Grant Types](../../integration/openid-connect/introduction.md#grant-types) section of the See the [Grant Types](../../integration/openid-connect/introduction.md#grant-types) section of the
[OpenID Connect 1.0 Integration Guide](../../integration/openid-connect/introduction.md#grant-types) for more information. [OpenID Connect 1.0 Integration Guide](../../integration/openid-connect/introduction.md#grant-types) for more information.
#### response_types
{{< confkey type="list(string)" default="code" required="no" >}}
*__Important Note:__ It is recommended that this isn't configured at this time unless you know what you're doing.*
A list of response types this client supports.
See the [Response Types](../../integration/openid-connect/introduction.md#response-types) section of the
[OpenID Connect 1.0 Integration Guide](../../integration/openid-connect/introduction.md#response-types) for more information.
#### response_modes
{{< confkey type="list(string)" default="form_post, query, fragment" required="no" >}}
*__Important Note:__ It is recommended that this isn't configured at this time unless you know what you're doing.*
A list of response modes this client supports.
See the [Response Modes](../../integration/openid-connect/introduction.md#response-modes) section of the
[OpenID Connect 1.0 Integration Guide](../../integration/openid-connect/introduction.md#response-modes) for more information.
#### authorization_policy #### authorization_policy
{{< confkey type="string" default="two_factor" required="no" >}} {{< confkey type="string" default="two_factor" required="no" >}}
@ -522,6 +531,18 @@ The algorithm used to sign the userinfo endpoint responses. This can either be `
See the [integration guide](../../integration/openid-connect/introduction.md#user-information-signing-algorithm) for See the [integration guide](../../integration/openid-connect/introduction.md#user-information-signing-algorithm) for
more information. more information.
#### token_endpoint_auth_method
{{< confkey type="string" default="" required="no" >}}
The registered client authentication mechanism used by this client for the [Token Endpoint]. If no method is defined
the confidential client type will accept any supported method. The public client type defaults to `none` as this
is required by the specification. This may be required as a breaking change in future versions.
Supported values are `client_secret_basic`, `client_secret_post`, and `none`.
See the [integration guide](../../integration/openid-connect/introduction.md#client-authentication-method) for
more information.
#### consent_mode #### consent_mode
{{< confkey type="string" default="auto" required="no" >}} {{< confkey type="string" default="auto" required="no" >}}
@ -565,6 +586,7 @@ To integrate Authelia's [OpenID Connect 1.0] implementation with a relying party
[token lifespan]: https://docs.apigee.com/api-platform/antipatterns/oauth-long-expiration [token lifespan]: https://docs.apigee.com/api-platform/antipatterns/oauth-long-expiration
[OpenID Connect 1.0]: https://openid.net/connect/ [OpenID Connect 1.0]: https://openid.net/connect/
[Token Endpoint]: https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint
[JWT]: https://datatracker.ietf.org/doc/html/rfc7519 [JWT]: https://datatracker.ietf.org/doc/html/rfc7519
[RFC6234]: https://datatracker.ietf.org/doc/html/rfc6234 [RFC6234]: https://datatracker.ietf.org/doc/html/rfc6234
[RFC4648]: https://datatracker.ietf.org/doc/html/rfc4648 [RFC4648]: https://datatracker.ietf.org/doc/html/rfc4648

View File

@ -61,7 +61,7 @@ authelia --config configuration.yml,config-acl.yml,config-other.yml
``` ```
Authelia's configuration files use the YAML format. A template with all possible options can be found at the root of the Authelia's configuration files use the YAML format. A template with all possible options can be found at the root of the
repository [here](https://github.com/authelia/authelia/blob/master/config.template.yml). repository {{< github-link name="here" path="config.template.yml" >}}.
*__Important Note:__ You should not have configuration sections such as Access Control Rules or OpenID Connect clients *__Important Note:__ You should not have configuration sections such as Access Control Rules or OpenID Connect clients
configured in multiple files. If you wish to split these into their own files that is fine, but if you have two files that configured in multiple files. If you wish to split these into their own files that is fine, but if you have two files that

View File

@ -16,9 +16,8 @@ toc: true
We document the configuration in two ways: We document the configuration in two ways:
1. The [YAML] configuration template 1. The [YAML] configuration template {{< github-link path="config.template.yml" >}} has comments with very limited
[config.template.yml](https://github.com/authelia/authelia/blob/master/config.template.yml) has comments with very documentation on the effective use of a particular option. All documentation lines start with `##`. Lines
limited documentation on the effective use of a particular option. All documentation lines start with `##`. Lines
starting with a single `#` are [YAML] configuration options which are commented to disable them or as examples. starting with a single `#` are [YAML] configuration options which are commented to disable them or as examples.
2. This documentation site. Generally each section of the configuration is in its own section of the documentation 2. This documentation site. Generally each section of the configuration is in its own section of the documentation
site. Each configuration option is listed in its relevant section as a heading, under that heading generally are two site. Each configuration option is listed in its relevant section as a heading, under that heading generally are two

View File

@ -38,6 +38,23 @@ The additional tools are recommended:
* [yamllint] * [yamllint]
* [VSCodium] or [GoLand] * [VSCodium] or [GoLand]
## Certificate
Authelia utilizes a self-signed Root CA certificate for the development environment. This allows us to sign elements of
the CI process uniformly and only trust a single additional Root CA Certificate. The private key for this certificate is
maintained by the [Core Team] so if you need an additional certificate signed for this purpose please reach out to them.
While developing for Authelia you may also want to trust this Root CA. It is critical that you are aware of what this
means if you decide to do so.
1. It will allow us to generate trusted certificates for machines this is installed on.
2. If compromised there is no formal revocation process at this time as we are not a certified CA.
3. Trusting Root CA's is not necessary for the development process it only makes it smoother.
4. Trusting additional Root CA's for prolonged periods is not generally a good idea.
If you'd still like to trust the Root CA Certificate it's located (encoded as a PEM) in the main git repository at
[/internal/suites/common/pki/ca/ca.public.crt](https://github.com/authelia/authelia/blob/master/internal/suites/common/pki/ca/ca.public.crt).
## Scripts ## Scripts
There is a scripting context provided with __Authelia__ which can easily be configured. It allows running integration There is a scripting context provided with __Authelia__ which can easily be configured. It allows running integration

View File

@ -25,8 +25,8 @@ bootstrapping *Authelia*.
We publish two example [systemd] unit files: We publish two example [systemd] unit files:
* [authelia.service](https://github.com/authelia/authelia/blob/master/authelia.service) * {{< github-link path="authelia.service" >}}
* [authelia@.service](https://github.com/authelia/authelia/blob/master/authelia%40.service) * {{< github-link path="authelia@.service" >}}
## Arch Linux ## Arch Linux

View File

@ -13,7 +13,7 @@ toc: true
--- ---
Istio uses [Envoy](../proxies/envoy.md) as an Ingress. This means it has a relatively comprehensive integration option. Istio uses [Envoy](../proxies/envoy.md) as an Ingress. This means it has a relatively comprehensive integration option.
Istio is supported with Authelia v4.37.0 and higher via [Envoy]'s [external authorization] filter. Istio is supported with Authelia v4.37.0 and higher via the [Envoy] proxy [external authorization] filter.
[external authorization]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ext_authz/v3/ext_authz.proto.html#extensions-filters-http-ext-authz-v3-extauthz [external authorization]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ext_authz/v3/ext_authz.proto.html#extensions-filters-http-ext-authz-v3-extauthz

View File

@ -21,8 +21,15 @@ documentation for some [OpenID Connect 1.0] Relying Party implementations.
See the [configuration documentation](../../configuration/identity-providers/open-id-connect.md) for information on how See the [configuration documentation](../../configuration/identity-providers/open-id-connect.md) for information on how
to configure the Authelia [OpenID Connect 1.0] Provider. to configure the Authelia [OpenID Connect 1.0] Provider.
This page is intended as an integration reference point for any implementers who wish to integrate an
[OpenID Connect 1.0] Relying Party (client application) either as a developer or user of the third party Reyling Party.
## Scope Definitions ## Scope Definitions
The following scope definitions describe each scope supported and the associated effects including the individual claims
returned by granting this scope. By default we do not issue any claims which reveal the users identity which allows
administrators semi-granular control over which claims the client is entitled to.
### openid ### openid
This is the default scope for [OpenID Connect 1.0]. This field is forced on every client by the configuration validation This is the default scope for [OpenID Connect 1.0]. This field is forced on every client by the configuration validation
@ -54,9 +61,16 @@ This scope is a special scope designed to allow applications to obtain a [Refres
an application on behalf of a user. A [Refresh Token] is a special [Access Token] that allows refreshing previously an application on behalf of a user. A [Refresh Token] is a special [Access Token] that allows refreshing previously
issued token credentials, effectively it allows the relying party to obtain new tokens periodically. issued token credentials, effectively it allows the relying party to obtain new tokens periodically.
As per [OpenID Connect 1.0] Section 11 [Offline Access] can only be granted during the [Authorization Code Flow] or a
[Hybrid Flow]. The [Refresh Token] will only ever be returned at the [Token Endpoint] when the client is exchanging
their [OAuth 2.0 Authorization Code].
Generally unless an application supports this and actively requests this scope they should not be granted this scope via Generally unless an application supports this and actively requests this scope they should not be granted this scope via
the client configuration. the client configuration.
It is also important to note that we treat a [Refresh Token] as single use and reissue a new [Refresh Token] during the
refresh flow.
### groups ### groups
This scope includes the groups the authentication backend reports the user is a member of in the [Claims] of the This scope includes the groups the authentication backend reports the user is a member of in the [Claims] of the
@ -92,43 +106,21 @@ This scope includes the profile information the authentication backend reports a
The following section describes advanced parameters which can be used in various endpoints as well as their related The following section describes advanced parameters which can be used in various endpoints as well as their related
configuration options. configuration options.
### Grant Types
The following describes the various [OAuth 2.0] and [OpenID Connect 1.0] grant types and their support level. The value
field is both the required value for the `grant_type` parameter in the authorization request and the `grant_types`
configuration option.
| Grant Type | Supported | Value | Notes |
|:-----------------------------------------------:|:---------:|:----------------------------------------------:|:-------------------------------------------------------------------:|
| [OAuth 2.0 Authorization Code] | Yes | `authorization_code` | |
| [OAuth 2.0 Resource Owner Password Credentials] | No | `password` | This Grant Type has been deprecated and should not normally be used |
| [OAuth 2.0 Client Credentials] | Yes | `client_credentials` | |
| [OAuth 2.0 Implicit] | Yes | `implicit` | This Grant Type has been deprecated and should not normally be used |
| [OAuth 2.0 Refresh Token] | Yes | `refresh_token` | |
| [OAuth 2.0 Device Code] | No | `urn:ietf:params:oauth:grant-type:device_code` | |
|
[OAuth 2.0 Authorization Code]: https://datatracker.ietf.org/doc/html/rfc6749#section-1.3.1
[OAuth 2.0 Implicit]: https://datatracker.ietf.org/doc/html/rfc6749#section-1.3.2
[OAuth 2.0 Resource Owner Password Credentials]: https://datatracker.ietf.org/doc/html/rfc6749#section-1.3.3
[OAuth 2.0 Client Credentials]: https://datatracker.ietf.org/doc/html/rfc6749#section-1.3.4
[OAuth 2.0 Refresh Token]: https://datatracker.ietf.org/doc/html/rfc6749#section-1.5
[OAuth 2.0 Device Code]: https://datatracker.ietf.org/doc/html/rfc8628#section-3.4
### Response Types ### Response Types
The following describes the supported response types. See the [OAuth 2.0 Multiple Response Type Encoding Practices] for The following describes the supported response types. See the [OAuth 2.0 Multiple Response Type Encoding Practices] for
more technical information. more technical information. The default response modes column indicates which response modes are allowed by default on
clients configured with this flow type value. If more than a single response type is configured
| Flow Type | Values | | Flow Type | Value | Default [Response Modes](#response-modes) Values |
|:-------------------------:|:---------------------:| |:-------------------------:|:---------------------:|:------------------------------------------------:|
| [Authorization Code Flow] | `code` | | [Authorization Code Flow] | `code` | `form_post`, `query` |
| [Implicit Flow] | `token id_token` | | [Implicit Flow] | `id_token token` | `form_post`, `fragment` |
| [Implicit Flow] | `id_token` | | [Implicit Flow] | `id_token` | `form_post`, `fragment` |
| [Implicit Flow] | `token` | | [Implicit Flow] | `token` | `form_post`, `fragment` |
| [Hybrid Flow] | `code token` | | [Hybrid Flow] | `code token` | `form_post`, `fragment` |
| [Hybrid Flow] | `code id_token` | | [Hybrid Flow] | `code id_token` | `form_post`, `fragment` |
| [Hybrid Flow] | `code token id_token` | | [Hybrid Flow] | `code id_token token` | `form_post`, `fragment` |
[Authorization Code Flow]: https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth [Authorization Code Flow]: https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth
[Implicit Flow]: https://openid.net/specs/openid-connect-core-1_0.html#ImplicitFlowAuth [Implicit Flow]: https://openid.net/specs/openid-connect-core-1_0.html#ImplicitFlowAuth
@ -139,16 +131,60 @@ more technical information.
### Response Modes ### Response Modes
The following describes the supported response modes. See the [OAuth 2.0 Multiple Response Type Encoding Practices] for The following describes the supported response modes. See the [OAuth 2.0 Multiple Response Type Encoding Practices] for
more technical information. more technical information. The default response modes of a client is based on the [Response Types](#response-types)
configuration.
| Name | Value | | Name | Value |
|:---------------------:|:-----------:| |:---------------------:|:-----------:|
| [OAuth 2.0 Form Post] | `form_post` |
| Query String | `query` | | Query String | `query` |
| Fragment | `fragment` | | Fragment | `fragment` |
| [OAuth 2.0 Form Post] | `form_post` |
[OAuth 2.0 Form Post]: https://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html [OAuth 2.0 Form Post]: https://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html
### Grant Types
The following describes the various [OAuth 2.0] and [OpenID Connect 1.0] grant types and their support level. The value
field is both the required value for the `grant_type` parameter in the authorization request and the `grant_types`
configuration option.
| Grant Type | Supported | Value | Notes |
|:-----------------------------------------------:|:---------:|:----------------------------------------------:|:-----------------------------------------------------------------------------------------------:|
| [OAuth 2.0 Authorization Code] | Yes | `authorization_code` | |
| [OAuth 2.0 Resource Owner Password Credentials] | No | `password` | This Grant Type has been deprecated and should not normally be used |
| [OAuth 2.0 Client Credentials] | No | `client_credentials` | |
| [OAuth 2.0 Implicit] | Yes | `implicit` | This Grant Type has been deprecated and should not normally be used |
| [OAuth 2.0 Refresh Token] | Yes | `refresh_token` | This Grant Type should genreally only be used for clients which have the `offline_access` scope |
| [OAuth 2.0 Device Code] | No | `urn:ietf:params:oauth:grant-type:device_code` | |
|
[OAuth 2.0 Authorization Code]: https://datatracker.ietf.org/doc/html/rfc6749#section-1.3.1
[OAuth 2.0 Implicit]: https://datatracker.ietf.org/doc/html/rfc6749#section-1.3.2
[OAuth 2.0 Resource Owner Password Credentials]: https://datatracker.ietf.org/doc/html/rfc6749#section-1.3.3
[OAuth 2.0 Client Credentials]: https://datatracker.ietf.org/doc/html/rfc6749#section-1.3.4
[OAuth 2.0 Refresh Token]: https://datatracker.ietf.org/doc/html/rfc6749#section-1.5
[OAuth 2.0 Device Code]: https://datatracker.ietf.org/doc/html/rfc8628#section-3.4
### Client Authentication Method
The following describes the supported client authentication methods. See the [OpenID Connect 1.0 Client Authentication]
specification and the [OAuth 2.0 - Client Types] specification for more information.
| Description | Value / Name | Supported Client Types | Default for Client Type | Assertion Type |
|:------------------------------------:|:-----------------------------:|:----------------------:|:-----------------------:|:--------------------------------------------------------:|
| Secret via HTTP Basic Auth Scheme | `client_secret_basic` | `confidential` | N/A | N/A |
| Secret via HTTP POST Body | `client_secret_post` | `confidential` | N/A | N/A |
| JWT (signed by secret) | `client_secret_jwt` | Not Supported | N/A | `urn:ietf:params:oauth:client-assertion-type:jwt-bearer` |
| JWT (signed by private key) | `private_key_jwt` | Not Supported | N/A | `urn:ietf:params:oauth:client-assertion-type:jwt-bearer` |
| [OAuth 2.0 Mutual-TLS] | `tls_client_auth` | Not Supported | N/A | N/A |
| [OAuth 2.0 Mutual-TLS] (Self Signed) | `self_signed_tls_client_auth` | Not Supported | N/A | N/A |
| No Authentication | `none` | `public` | `public` | N/A |
[OpenID Connect 1.0 Client Authentication]: https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication
[OAuth 2.0 Mutual-TLS]: https://datatracker.ietf.org/doc/html/rfc8705
[OAuth 2.0 - Client Types]: https://datatracker.ietf.org/doc/html/rfc8705#section-2.1
## Authentication Method References ## Authentication Method References
Authelia currently supports adding the `amr` [Claim] to the [ID Token] utilizing the [RFC8176] Authentication Method Authelia currently supports adding the `amr` [Claim] to the [ID Token] utilizing the [RFC8176] Authentication Method
@ -289,10 +325,13 @@ The advantages of this approach are as follows:
[JSON Web Key Set]: https://datatracker.ietf.org/doc/html/rfc7517#section-5 [JSON Web Key Set]: https://datatracker.ietf.org/doc/html/rfc7517#section-5
[Offline Access]: https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess
[Authorization]: https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint [Authorization]: https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint
[Pushed Authorization Requests]: https://datatracker.ietf.org/doc/html/rfc9126
[Token]: https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint [Token]: https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint
[UserInfo]: https://openid.net/specs/openid-connect-core-1_0.html#UserInfo [UserInfo]: https://openid.net/specs/openid-connect-core-1_0.html#UserInfo
[Pushed Authorization Requests]: https://datatracker.ietf.org/doc/html/rfc9126
[Introspection]: https://datatracker.ietf.org/doc/html/rfc7662 [Introspection]: https://datatracker.ietf.org/doc/html/rfc7662
[Revocation]: https://datatracker.ietf.org/doc/html/rfc7009 [Revocation]: https://datatracker.ietf.org/doc/html/rfc7009
[Proof Key Code Exchange]: https://www.rfc-editor.org/rfc/rfc7636.html [Proof Key Code Exchange]: https://www.rfc-editor.org/rfc/rfc7636.html

View File

@ -23,24 +23,31 @@ common scenarios however those using more advanced architectures are likely goin
help with answering less specific questions about this and it may be possible if provided adequate information more help with answering less specific questions about this and it may be possible if provided adequate information more
specific questions may be answered. specific questions may be answered.
1. Authelia *__MUST__* be served via the `https` scheme. This is not optional even for testing. This is a deliberate
design decision to improve security directly (by using encrypted communication) and indirectly by reducing complexity.
### Forwarded Authentication ### Forwarded Authentication
Forwarded Authentication is a simple per-request authorization flow that checks the metadata of a request and a session Forwarded Authentication is a simple per-request authorization flow that checks the metadata of a request and a session
cookie to determine if a user must be forwarded to the authentication portal. cookie to determine if a user must be forwarded to the authentication portal.
Due to the fact a cookie is used, it's an intentional design decision that *__ALL__* applications/domains protected via In addition to the `https` scheme requirement for Authelia itself:
1. Due to the fact a cookie is used, it's an intentional design decision that *__ALL__* applications/domains protected via
this method *__MUST__* use secure schemes (`https` and `wss`) for all of their communication. this method *__MUST__* use secure schemes (`https` and `wss`) for all of their communication.
### OpenID Connect ### OpenID Connect
Only requires Authelia to be accessible via a secure scheme (`https`). No additional requirements other than the use of the `https` scheme for Authelia itself exist excluding those mandated
by the relevant specifications.
## Configuration ## Configuration
It's important to customize the configuration for *Authelia* in advance of deploying it. The configuration is static and It's important to customize the configuration for *Authelia* in advance of deploying it. The configuration is static and
not configured via web GUI. You can find a not configured via web GUI. You can find a configuration template named {{< github-link path="config.template.yml" >}}
[configuration template](https://github.com/authelia/authelia/blob/master/config.template.yml) on GitHub which can be on GitHub which can be used as a basis for configuration, alternatively *Authelia* will write this template relevant for
used as a basis for configuration. your version the first time it is started. Users should expect that they have to configure elements of this file as part
of initial setup.
The important sections to consider in initial configuration are as follows: The important sections to consider in initial configuration are as follows:

View File

@ -87,7 +87,7 @@ Below you will find commented examples of the following configuration:
### Example ### Example
Support for [Envoy] is possible with Authelia v4.37.0 and higher via [Envoy]'s [external authorization] filter. Support for [Envoy] is possible with Authelia v4.37.0 and higher via the [Envoy] proxy [external authorization] filter.
[external authorization]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ext_authz/v3/ext_authz.proto.html#extensions-filters-http-ext-authz-v3-extauthz [external authorization]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ext_authz/v3/ext_authz.proto.html#extensions-filters-http-ext-authz-v3-extauthz

View File

@ -92,7 +92,7 @@ available in [Kubernetes]. You would likely have to build your own [HAProxy] ima
### Envoy ### Envoy
[Envoy] is supported with Authelia v4.37.0 and higher via [Envoy]'s [external authorization] filter. [Envoy] is supported with Authelia v4.37.0 and higher via the [Envoy] proxy [external authorization] filter.
[external authorization]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ext_authz/v3/ext_authz.proto.html#extensions-filters-http-ext-authz-v3-extauthz [external authorization]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ext_authz/v3/ext_authz.proto.html#extensions-filters-http-ext-authz-v3-extauthz

View File

@ -44,7 +44,7 @@ case you have multiple devices available, you will be asked to select your prefe
### Why don't I have access to the *Push Notification* option? ### Why don't I have access to the *Push Notification* option?
It's likely that you have not configured __Authelia__ correctly. Please read this documentation again and be sure you It's likely that you have not configured __Authelia__ correctly. Please read this documentation again and be sure you
had a look at [config.template.yml](https://github.com/authelia/authelia/blob/master/config.template.yml) and had a look at {{< github-link path="config.template.yml" >}} and
[configuration documentation](../../../configuration/second-factor/duo.md). [configuration documentation](../../../configuration/second-factor/duo.md).
[Duo]: https://duo.com/ [Duo]: https://duo.com/

View File

@ -0,0 +1,17 @@
{{- $repo := "authelia/authelia" }}{{ with .Get "repo" }}{{ $repo = . }}{{ end }}
{{- $branch := printf "v%s" .Site.Data.misc.latest }}{{ with .Get "branch" }}{{ $branch = . }}{{ end }}
{{- $path := "" }}{{ with .Get "path" }}{{ $path = . }}{{ end }}
{{- $link := printf "https://github.com/%s/blob/%s/%s" $repo $branch (urlquery $path) }}
{{- $name := "" }}
{{- with .Get "name" }}
{{- $name = . }}
{{- else }}
{{- if (eq $repo "authelia/authelia") }}
{{- $name = $path }}
{{- else }}
{{- $name = printf "https://github.com/%s/blob/%s/%s" $repo $branch $path }}
{{- end }}
{{- end }}
{{- "" -}}
<a href="{{ $link }}" target="_blank">{{ $name }}</a>
{{- "" -}}

14
go.mod
View File

@ -27,17 +27,17 @@ require (
github.com/knadh/koanf/providers/env v0.1.0 github.com/knadh/koanf/providers/env v0.1.0
github.com/knadh/koanf/providers/posflag v0.1.0 github.com/knadh/koanf/providers/posflag v0.1.0
github.com/knadh/koanf/providers/rawbytes v0.1.0 github.com/knadh/koanf/providers/rawbytes v0.1.0
github.com/knadh/koanf/v2 v2.0.0 github.com/knadh/koanf/v2 v2.0.1
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.44.0 github.com/ory/fosite v0.44.0
github.com/ory/herodot v0.10.1 github.com/ory/herodot v0.10.2
github.com/ory/x v0.0.550 github.com/ory/x v0.0.552
github.com/otiai10/copy v1.10.0 github.com/otiai10/copy v1.10.0
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/pquerna/otp v1.4.0 github.com/pquerna/otp v1.4.0
github.com/prometheus/client_golang v1.14.0 github.com/prometheus/client_golang v1.15.0
github.com/sirupsen/logrus v1.9.0 github.com/sirupsen/logrus v1.9.0
github.com/spf13/cobra v1.7.0 github.com/spf13/cobra v1.7.0
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
@ -98,8 +98,8 @@ require (
github.com/philhofer/fwd v1.1.2 // indirect github.com/philhofer/fwd v1.1.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect github.com/prometheus/procfs v0.9.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
github.com/spf13/afero v1.9.5 // indirect github.com/spf13/afero v1.9.5 // indirect
@ -116,7 +116,7 @@ require (
github.com/ysmood/leakless v0.8.0 // indirect github.com/ysmood/leakless v0.8.0 // indirect
golang.org/x/crypto v0.7.0 // indirect golang.org/x/crypto v0.7.0 // indirect
golang.org/x/mod v0.10.0 // indirect golang.org/x/mod v0.10.0 // indirect
golang.org/x/oauth2 v0.4.0 // indirect golang.org/x/oauth2 v0.5.0 // indirect
golang.org/x/sys v0.7.0 // indirect golang.org/x/sys v0.7.0 // indirect
golang.org/x/tools v0.7.0 // indirect golang.org/x/tools v0.7.0 // indirect
google.golang.org/appengine v1.6.7 // indirect google.golang.org/appengine v1.6.7 // indirect

88
go.sum
View File

@ -44,10 +44,7 @@ github.com/Gurpartap/logrus-stack v0.0.0-20170710170904-89c00d8a28f4 h1:vdT7QwBh
github.com/Gurpartap/logrus-stack v0.0.0-20170710170904-89c00d8a28f4/go.mod h1:SvXOG8ElV28oAiG9zv91SDe5+9PfIr7PPccpr8YyXNs= github.com/Gurpartap/logrus-stack v0.0.0-20170710170904-89c00d8a28f4/go.mod h1:SvXOG8ElV28oAiG9zv91SDe5+9PfIr7PPccpr8YyXNs=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
@ -62,7 +59,6 @@ github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBW
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
@ -138,15 +134,10 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9
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=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
github.com/go-ldap/ldap/v3 v3.4.4 h1:qPjipEpt+qDa6SI/h1fzuGWoRUY+qqQ9sOZq67/PYUs= github.com/go-ldap/ldap/v3 v3.4.4 h1:qPjipEpt+qDa6SI/h1fzuGWoRUY+qqQ9sOZq67/PYUs=
github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXgXtJC+aI= github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXgXtJC+aI=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
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.8 h1:lYFnHv/lFyjW/Ye0IhyKLeHw/zfhHbSTqawoCi2z/nI= github.com/go-rod/rod v0.112.8 h1:lYFnHv/lFyjW/Ye0IhyKLeHw/zfhHbSTqawoCi2z/nI=
@ -194,7 +185,6 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@ -216,7 +206,6 @@ github.com/google/go-tpm v0.3.3 h1:P/ZFNBZYXRxc+z7i5uyd8VP7MaDteuLZInzrH2idRGo=
github.com/google/go-tpm v0.3.3/go.mod h1:9Hyn3rgnzWF9XBWVk6ml6A6hNkbWjNFlDQL51BeghL4= github.com/google/go-tpm v0.3.3/go.mod h1:9Hyn3rgnzWF9XBWVk6ml6A6hNkbWjNFlDQL51BeghL4=
github.com/google/go-tpm-tools v0.0.0-20190906225433-1614c142f845/go.mod h1:AVfHadzbdzHo54inR2x1v640jdi1YSi3NauM2DUsxk0= github.com/google/go-tpm-tools v0.0.0-20190906225433-1614c142f845/go.mod h1:AVfHadzbdzHo54inR2x1v640jdi1YSi3NauM2DUsxk0=
github.com/google/go-tpm-tools v0.2.0/go.mod h1:npUd03rQ60lxN7tzeBJreG38RvWwme2N1reF/eeiBk4= github.com/google/go-tpm-tools v0.2.0/go.mod h1:npUd03rQ60lxN7tzeBJreG38RvWwme2N1reF/eeiBk4=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@ -270,15 +259,9 @@ github.com/jandelgado/gcov2lcov v1.0.5/go.mod h1:NnSxK6TMlg1oGDBfGelGbjgorT5/L3c
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY= github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY=
@ -295,14 +278,13 @@ github.com/knadh/koanf/providers/posflag v0.1.0 h1:mKJlLrKPcAP7Ootf4pBZWJ6J+4wHY
github.com/knadh/koanf/providers/posflag v0.1.0/go.mod h1:SYg03v/t8ISBNrMBRMlojH8OsKowbkXV7giIbBVgbz0= github.com/knadh/koanf/providers/posflag v0.1.0/go.mod h1:SYg03v/t8ISBNrMBRMlojH8OsKowbkXV7giIbBVgbz0=
github.com/knadh/koanf/providers/rawbytes v0.1.0 h1:dpzgu2KO6uf6oCb4aP05KDmKmAmI51k5pe8RYKQ0qME= github.com/knadh/koanf/providers/rawbytes v0.1.0 h1:dpzgu2KO6uf6oCb4aP05KDmKmAmI51k5pe8RYKQ0qME=
github.com/knadh/koanf/providers/rawbytes v0.1.0/go.mod h1:mMTB1/IcJ/yE++A2iEZbY1MLygX7vttU+C+S/YmPu9c= github.com/knadh/koanf/providers/rawbytes v0.1.0/go.mod h1:mMTB1/IcJ/yE++A2iEZbY1MLygX7vttU+C+S/YmPu9c=
github.com/knadh/koanf/v2 v2.0.0 h1:XPQ5ilNnwnNaHrfQ1YpTVhUAjcGHnEKA+lRpipQv02Y= github.com/knadh/koanf/v2 v2.0.1 h1:1dYGITt1I23x8cfx8ZnldtezdyaZtfAuRtIFOiRzK7g=
github.com/knadh/koanf/v2 v2.0.0/go.mod h1:ZeiIlIDXTE7w1lMT6UVcNiRAS2/rCeLn/GdLNvY1Dus= github.com/knadh/koanf/v2 v2.0.1/go.mod h1:ZeiIlIDXTE7w1lMT6UVcNiRAS2/rCeLn/GdLNvY1Dus=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@ -329,15 +311,9 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/oleiade/reflections v1.0.1 h1:D1XO3LVEYroYskEsoSiGItp9RUxG6jWnCVvrqH0HHQM= github.com/oleiade/reflections v1.0.1 h1:D1XO3LVEYroYskEsoSiGItp9RUxG6jWnCVvrqH0HHQM=
@ -349,10 +325,10 @@ github.com/ory/go-acc v0.2.9-0.20230103102148-6b1c9a70dbbe h1:rvu4obdvqR0fkSIJ8I
github.com/ory/go-acc v0.2.9-0.20230103102148-6b1c9a70dbbe/go.mod h1:z4n3u6as84LbV4YmgjHhnwtccQqzf4cZlSk9f1FhygI= github.com/ory/go-acc v0.2.9-0.20230103102148-6b1c9a70dbbe/go.mod h1:z4n3u6as84LbV4YmgjHhnwtccQqzf4cZlSk9f1FhygI=
github.com/ory/go-convenience v0.1.0 h1:zouLKfF2GoSGnJwGq+PE/nJAE6dj2Zj5QlTgmMTsTS8= github.com/ory/go-convenience v0.1.0 h1:zouLKfF2GoSGnJwGq+PE/nJAE6dj2Zj5QlTgmMTsTS8=
github.com/ory/go-convenience v0.1.0/go.mod h1:uEY/a60PL5c12nYz4V5cHY03IBmwIAEm8TWB0yn9KNs= github.com/ory/go-convenience v0.1.0/go.mod h1:uEY/a60PL5c12nYz4V5cHY03IBmwIAEm8TWB0yn9KNs=
github.com/ory/herodot v0.10.1 h1:espcYXC2VJrgYXAhKCOX+3pvD9eD7cT2Ceqad9yWEXc= github.com/ory/herodot v0.10.2 h1:gGvNMHgAwWzdP/eo+roSiT5CGssygHSjDU7MSQNlJ4E=
github.com/ory/herodot v0.10.1/go.mod h1:MMNmY6MG1uB6fnXYFaHoqdV23DTWctlPsmRCeq/2+wc= github.com/ory/herodot v0.10.2/go.mod h1:MMNmY6MG1uB6fnXYFaHoqdV23DTWctlPsmRCeq/2+wc=
github.com/ory/x v0.0.550 h1:bcaOdUW/jHByoT8U6cfraBpRCtNtUB8lNBD97LTrB+Y= github.com/ory/x v0.0.552 h1:vgDw7FFQ7Ama3iyDLbjElY2Um1/ub82iIubK0pUj81M=
github.com/ory/x v0.0.550/go.mod h1:oRVemI3SQQOLvOCJWIRinHQKlgmay/NbwSyRUIsS/Yk= github.com/ory/x v0.0.552/go.mod h1:oRVemI3SQQOLvOCJWIRinHQKlgmay/NbwSyRUIsS/Yk=
github.com/otiai10/copy v1.10.0 h1:znyI7l134wNg/wDktoVQPxPkgvhDfGCYUasey+h0rDQ= github.com/otiai10/copy v1.10.0 h1:znyI7l134wNg/wDktoVQPxPkgvhDfGCYUasey+h0rDQ=
github.com/otiai10/copy v1.10.0/go.mod h1:rSaLseMUsZFFbsFGc7wCJnnkTAvdc5L6VWxPE4308Ww= github.com/otiai10/copy v1.10.0/go.mod h1:rSaLseMUsZFFbsFGc7wCJnnkTAvdc5L6VWxPE4308Ww=
github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks=
@ -367,7 +343,6 @@ github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoU
github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw= github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
@ -377,34 +352,21 @@ github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
github.com/pquerna/otp v1.4.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.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
@ -417,8 +379,6 @@ github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1Avp
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g= github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
@ -568,7 +528,6 @@ golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -592,10 +551,7 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
@ -609,10 +565,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.5.0 h1:HuArIo48skDwlrvM3sEdHXElYslAMsf3KwRkkW4MC4s=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M=
golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -635,7 +589,6 @@ golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -644,7 +597,6 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -657,8 +609,6 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -666,17 +616,13 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210629170331-7dc0b73dc9fb/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210629170331-7dc0b73dc9fb/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -864,7 +810,6 @@ google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
@ -876,9 +821,6 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -61,6 +61,7 @@ func newCryptoHashGenerateCmd(ctx *CmdCtx) (cmd *cobra.Command) {
Short: cmdAutheliaCryptoHashGenerateShort, Short: cmdAutheliaCryptoHashGenerateShort,
Long: cmdAutheliaCryptoHashGenerateLong, Long: cmdAutheliaCryptoHashGenerateLong,
Example: cmdAutheliaCryptoHashGenerateExample, Example: cmdAutheliaCryptoHashGenerateExample,
Args: cobra.NoArgs,
PreRunE: ctx.ChainRunE( PreRunE: ctx.ChainRunE(
ctx.ConfigSetDefaultsRunE(defaults), ctx.ConfigSetDefaultsRunE(defaults),
ctx.CryptoHashGenerateMapFlagsRunE, ctx.CryptoHashGenerateMapFlagsRunE,

View File

@ -1480,12 +1480,6 @@ notifier:
# - email # - email
# - profile # - profile
## Grant Types configures which grants this client can obtain.
## It's not recommended to define this unless you know what you're doing.
# grant_types:
# - refresh_token
# - authorization_code
## Response Types configures which responses this client can be sent. ## Response Types configures which responses this client can be sent.
## It's not recommended to define this unless you know what you're doing. ## It's not recommended to define this unless you know what you're doing.
# response_types: # response_types:
@ -1495,7 +1489,14 @@ notifier:
# response_modes: # response_modes:
# - form_post # - form_post
# - query # - query
# - fragment
## Grant Types configures which grants this client can obtain.
## It's not recommended to define this unless you know what you're doing.
# grant_types:
# - authorization_code
## The permitted client authentication method for the Token Endpoint for this client.
# token_endpoint_auth_method: client_secret_basic
## The policy to require for this client; one_factor or two_factor. ## The policy to require for this client; one_factor or two_factor.
# authorization_policy: two_factor # authorization_policy: two_factor

View File

@ -401,6 +401,9 @@ func TestShouldNotReadConfigurationOnFSAccessDenied(t *testing.T) {
func TestShouldLoadDirectoryConfiguration(t *testing.T) { func TestShouldLoadDirectoryConfiguration(t *testing.T) {
dir := t.TempDir() dir := t.TempDir()
cfg := filepath.Join(dir, "myconf.yml")
assert.NoError(t, testCreateFile(cfg, "server:\n port: 9091\n", 0700))
val := schema.NewStructValidator() val := schema.NewStructValidator()
_, _, err := Load(val, NewFileSource(dir)) _, _, err := Load(val, NewFileSource(dir))
@ -416,3 +419,70 @@ func testSetEnv(t *testing.T, key, value string) {
func testCreateFile(path, value string, perm os.FileMode) (err error) { func testCreateFile(path, value string, perm os.FileMode) (err error) {
return os.WriteFile(path, []byte(value), perm) return os.WriteFile(path, []byte(value), perm)
} }
func TestShouldErrorOnNoPath(t *testing.T) {
val := schema.NewStructValidator()
_, _, err := Load(val, NewFileSource(""))
assert.NoError(t, err)
assert.Len(t, val.Errors(), 1)
assert.ErrorContains(t, val.Errors()[0], "invalid file path source configuration")
}
func TestShouldErrorOnInvalidPath(t *testing.T) {
dir := t.TempDir()
cfg := filepath.Join(dir, "invalid-folder/config")
val := schema.NewStructValidator()
_, _, err := Load(val, NewFileSource(cfg))
assert.NoError(t, err)
assert.Len(t, val.Errors(), 1)
assert.ErrorContains(t, val.Errors()[0], fmt.Sprintf("stat %s: no such file or directory", cfg))
}
func TestShouldErrorOnDirFSPermissionDenied(t *testing.T) {
if runtime.GOOS == constWindows {
t.Skip("skipping test due to being on windows")
}
dir := t.TempDir()
err := os.Chmod(dir, 0200)
assert.NoError(t, err)
val := schema.NewStructValidator()
_, _, err = Load(val, NewFileSource(dir))
assert.NoError(t, err)
assert.Len(t, val.Errors(), 1)
assert.ErrorContains(t, val.Errors()[0], fmt.Sprintf("open %s: permission denied", dir))
}
func TestShouldSkipDirOnLoad(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "some-dir")
err := os.Mkdir(path, 0700)
assert.NoError(t, err)
val := schema.NewStructValidator()
_, _, err = Load(val, NewFileSource(dir))
assert.NoError(t, err)
assert.Len(t, val.Errors(), 0)
assert.Len(t, val.Warnings(), 0)
}
func TestShouldFailIfYmlIsInvalid(t *testing.T) {
dir := t.TempDir()
cfg := filepath.Join(dir, "myconf.yml")
assert.NoError(t, testCreateFile(cfg, "an invalid contend\n", 0700))
val := schema.NewStructValidator()
_, _, err := Load(val, NewFileSource(dir))
assert.NoError(t, err)
assert.Len(t, val.Errors(), 1)
assert.ErrorContains(t, val.Errors()[0], "unmarshal errors")
}

View File

@ -64,6 +64,8 @@ type OpenIDConnectClientConfiguration struct {
ResponseTypes []string `koanf:"response_types"` ResponseTypes []string `koanf:"response_types"`
ResponseModes []string `koanf:"response_modes"` ResponseModes []string `koanf:"response_modes"`
TokenEndpointAuthMethod string `koanf:"token_endpoint_auth_method"`
Policy string `koanf:"authorization_policy"` Policy string `koanf:"authorization_policy"`
EnforcePAR bool `koanf:"enforce_par"` EnforcePAR bool `koanf:"enforce_par"`
@ -91,9 +93,8 @@ var defaultOIDCClientConsentPreConfiguredDuration = time.Hour * 24 * 7
var DefaultOpenIDConnectClientConfiguration = OpenIDConnectClientConfiguration{ var DefaultOpenIDConnectClientConfiguration = OpenIDConnectClientConfiguration{
Policy: "two_factor", Policy: "two_factor",
Scopes: []string{"openid", "groups", "profile", "email"}, Scopes: []string{"openid", "groups", "profile", "email"},
GrantTypes: []string{"refresh_token", "authorization_code"},
ResponseTypes: []string{"code"}, ResponseTypes: []string{"code"},
ResponseModes: []string{"form_post", "query", "fragment"}, ResponseModes: []string{"form_post"},
UserinfoSigningAlgorithm: "none", UserinfoSigningAlgorithm: "none",
ConsentMode: "auto", ConsentMode: "auto",

View File

@ -45,6 +45,7 @@ var Keys = []string{
"identity_providers.oidc.clients[].grant_types", "identity_providers.oidc.clients[].grant_types",
"identity_providers.oidc.clients[].response_types", "identity_providers.oidc.clients[].response_types",
"identity_providers.oidc.clients[].response_modes", "identity_providers.oidc.clients[].response_modes",
"identity_providers.oidc.clients[].token_endpoint_auth_method",
"identity_providers.oidc.clients[].authorization_policy", "identity_providers.oidc.clients[].authorization_policy",
"identity_providers.oidc.clients[].enforce_par", "identity_providers.oidc.clients[].enforce_par",
"identity_providers.oidc.clients[].enforce_pkce", "identity_providers.oidc.clients[].enforce_pkce",

View File

@ -25,6 +25,23 @@ func TestShouldGenerateConfiguration(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
} }
func TestNotShouldGenerateConfigurationifExists(t *testing.T) {
dir := t.TempDir()
cfg := filepath.Join(dir, "config.yml")
created, err := EnsureConfigurationExists(cfg)
assert.NoError(t, err)
assert.True(t, created)
created, err = EnsureConfigurationExists(cfg)
assert.NoError(t, err)
assert.False(t, created)
_, err = os.Stat(cfg)
assert.NoError(t, err)
}
func TestShouldNotGenerateConfigurationOnFSAccessDenied(t *testing.T) { func TestShouldNotGenerateConfigurationOnFSAccessDenied(t *testing.T) {
if runtime.GOOS == constWindows { if runtime.GOOS == constWindows {
t.Skip("skipping test due to being on windows") t.Skip("skipping test due to being on windows")

View File

@ -59,7 +59,7 @@ func ValidateAccessControl(config *schema.Configuration, validator *schema.Struc
} }
if !IsPolicyValid(config.AccessControl.DefaultPolicy) { if !IsPolicyValid(config.AccessControl.DefaultPolicy) {
validator.Push(fmt.Errorf(errFmtAccessControlDefaultPolicyValue, strings.Join(validACLRulePolicies, "', '"), config.AccessControl.DefaultPolicy)) validator.Push(fmt.Errorf(errFmtAccessControlDefaultPolicyValue, strJoinOr(validACLRulePolicies), config.AccessControl.DefaultPolicy))
} }
if config.AccessControl.Networks != nil { if config.AccessControl.Networks != nil {
@ -92,8 +92,13 @@ func ValidateRules(config *schema.Configuration, validator *schema.StructValidat
validateDomains(rulePosition, rule, validator) validateDomains(rulePosition, rule, validator)
switch rule.Policy {
case "":
validator.Push(fmt.Errorf(errFmtAccessControlRuleNoPolicy, ruleDescriptor(rulePosition, rule)))
default:
if !IsPolicyValid(rule.Policy) { if !IsPolicyValid(rule.Policy) {
validator.Push(fmt.Errorf(errFmtAccessControlRuleInvalidPolicy, ruleDescriptor(rulePosition, rule), rule.Policy)) validator.Push(fmt.Errorf(errFmtAccessControlRuleInvalidPolicy, ruleDescriptor(rulePosition, rule), strJoinOr(validACLRulePolicies), rule.Policy))
}
} }
validateNetworks(rulePosition, rule, config.AccessControl, validator) validateNetworks(rulePosition, rule, config.AccessControl, validator)
@ -156,10 +161,14 @@ func validateSubjects(rulePosition int, rule schema.ACLRule, validator *schema.S
} }
func validateMethods(rulePosition int, rule schema.ACLRule, validator *schema.StructValidator) { func validateMethods(rulePosition int, rule schema.ACLRule, validator *schema.StructValidator) {
for _, method := range rule.Methods { invalid, duplicates := validateList(rule.Methods, validACLHTTPMethodVerbs, true)
if !utils.IsStringInSliceFold(method, validACLHTTPMethodVerbs) {
validator.Push(fmt.Errorf(errFmtAccessControlRuleMethodInvalid, ruleDescriptor(rulePosition, rule), method, strings.Join(validACLHTTPMethodVerbs, "', '"))) if len(invalid) != 0 {
validator.Push(fmt.Errorf(errFmtAccessControlRuleInvalidEntries, ruleDescriptor(rulePosition, rule), "methods", strJoinOr(validACLHTTPMethodVerbs), strJoinAnd(invalid)))
} }
if len(duplicates) != 0 {
validator.Push(fmt.Errorf(errFmtAccessControlRuleInvalidDuplicates, ruleDescriptor(rulePosition, rule), "methods", strJoinAnd(duplicates)))
} }
} }
@ -177,7 +186,7 @@ func validateQuery(i int, rule schema.ACLRule, config *schema.Configuration, val
} }
} }
} else if !utils.IsStringInSliceFold(config.AccessControl.Rules[i].Query[j][k].Operator, validACLRuleOperators) { } else if !utils.IsStringInSliceFold(config.AccessControl.Rules[i].Query[j][k].Operator, validACLRuleOperators) {
validator.Push(fmt.Errorf(errFmtAccessControlRuleQueryInvalid, ruleDescriptor(i+1, rule), config.AccessControl.Rules[i].Query[j][k].Operator, strings.Join(validACLRuleOperators, "', '"))) validator.Push(fmt.Errorf(errFmtAccessControlRuleQueryInvalid, ruleDescriptor(i+1, rule), strJoinOr(validACLRuleOperators), config.AccessControl.Rules[i].Query[j][k].Operator))
} }
if config.AccessControl.Rules[i].Query[j][k].Key == "" { if config.AccessControl.Rules[i].Query[j][k].Key == "" {

View File

@ -58,7 +58,7 @@ func (suite *AccessControl) TestShouldValidateEitherDomainsOrDomainsRegex() {
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)
assert.EqualError(suite.T(), suite.validator.Errors()[0], "access control: rule #3: rule is invalid: must have the option 'domain' or 'domain_regex' configured") assert.EqualError(suite.T(), suite.validator.Errors()[0], "access control: rule #3: option 'domain' or 'domain_regex' must be present but are both absent")
} }
func (suite *AccessControl) TestShouldRaiseErrorInvalidDefaultPolicy() { func (suite *AccessControl) TestShouldRaiseErrorInvalidDefaultPolicy() {
@ -69,7 +69,7 @@ func (suite *AccessControl) TestShouldRaiseErrorInvalidDefaultPolicy() {
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], "access control: option 'default_policy' must be one of 'bypass', 'one_factor', 'two_factor', 'deny' but it is configured as 'invalid'") suite.Assert().EqualError(suite.validator.Errors()[0], "access control: option 'default_policy' must be one of 'bypass', 'one_factor', 'two_factor', or 'deny' but it's configured as 'invalid'")
} }
func (suite *AccessControl) TestShouldRaiseErrorInvalidNetworkGroupNetwork() { func (suite *AccessControl) TestShouldRaiseErrorInvalidNetworkGroupNetwork() {
@ -141,10 +141,10 @@ func (suite *AccessControl) TestShouldRaiseErrorsWithEmptyRules() {
suite.Assert().Len(suite.validator.Warnings(), 0) suite.Assert().Len(suite.validator.Warnings(), 0)
suite.Require().Len(suite.validator.Errors(), 4) suite.Require().Len(suite.validator.Errors(), 4)
suite.Assert().EqualError(suite.validator.Errors()[0], "access control: rule #1: rule is invalid: must have the option 'domain' or 'domain_regex' configured") suite.Assert().EqualError(suite.validator.Errors()[0], "access control: rule #1: option 'domain' or 'domain_regex' must be present but are both absent")
suite.Assert().EqualError(suite.validator.Errors()[1], "access control: rule #1: rule 'policy' option '' is invalid: must be one of 'deny', 'two_factor', 'one_factor' or 'bypass'") suite.Assert().EqualError(suite.validator.Errors()[1], "access control: rule #1: option 'policy' must be present but it's absent")
suite.Assert().EqualError(suite.validator.Errors()[2], "access control: rule #2: rule is invalid: must have the option 'domain' or 'domain_regex' configured") suite.Assert().EqualError(suite.validator.Errors()[2], "access control: rule #2: option 'domain' or 'domain_regex' must be present but are both absent")
suite.Assert().EqualError(suite.validator.Errors()[3], "access control: rule #2: rule 'policy' option 'wrong' is invalid: must be one of 'deny', 'two_factor', 'one_factor' or 'bypass'") suite.Assert().EqualError(suite.validator.Errors()[3], "access control: rule #2: option 'policy' must be one of 'bypass', 'one_factor', 'two_factor', or 'deny' but it's configured as 'wrong'")
} }
func (suite *AccessControl) TestShouldRaiseErrorInvalidPolicy() { func (suite *AccessControl) TestShouldRaiseErrorInvalidPolicy() {
@ -160,7 +160,7 @@ func (suite *AccessControl) TestShouldRaiseErrorInvalidPolicy() {
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], "access control: rule #1 (domain 'public.example.com'): rule 'policy' option 'invalid' is invalid: must be one of 'deny', 'two_factor', 'one_factor' or 'bypass'") suite.Assert().EqualError(suite.validator.Errors()[0], "access control: rule #1 (domain 'public.example.com'): option 'policy' must be one of 'bypass', 'one_factor', 'two_factor', or 'deny' but it's configured as 'invalid'")
} }
func (suite *AccessControl) TestShouldRaiseErrorInvalidNetwork() { func (suite *AccessControl) TestShouldRaiseErrorInvalidNetwork() {
@ -194,7 +194,24 @@ func (suite *AccessControl) TestShouldRaiseErrorInvalidMethod() {
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], "access control: rule #1 (domain 'public.example.com'): 'methods' option 'HOP' is invalid: must be one of 'GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE', 'TRACE', 'CONNECT', 'OPTIONS', 'COPY', 'LOCK', 'MKCOL', 'MOVE', 'PROPFIND', 'PROPPATCH', 'UNLOCK'") suite.Assert().EqualError(suite.validator.Errors()[0], "access control: rule #1 (domain 'public.example.com'): option 'methods' must only have the values 'GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE', 'TRACE', 'CONNECT', 'OPTIONS', 'COPY', 'LOCK', 'MKCOL', 'MOVE', 'PROPFIND', 'PROPPATCH', or 'UNLOCK' but the values 'HOP' are present")
}
func (suite *AccessControl) TestShouldRaiseErrorDuplicateMethod() {
suite.config.AccessControl.Rules = []schema.ACLRule{
{
Domains: []string{"public.example.com"},
Policy: "bypass",
Methods: []string{"GET", "GET"},
},
}
ValidateRules(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], "access control: rule #1 (domain 'public.example.com'): option 'methods' must have unique values but the values 'GET' are duplicated")
} }
func (suite *AccessControl) TestShouldRaiseErrorInvalidSubject() { func (suite *AccessControl) TestShouldRaiseErrorInvalidSubject() {
@ -367,13 +384,13 @@ func (suite *AccessControl) TestShouldErrorOnInvalidRulesQuery() {
suite.Assert().Len(suite.validator.Warnings(), 0) suite.Assert().Len(suite.validator.Warnings(), 0)
suite.Require().Len(suite.validator.Errors(), 7) suite.Require().Len(suite.validator.Errors(), 7)
suite.Assert().EqualError(suite.validator.Errors()[0], "access control: rule #1 (domain 'public.example.com'): 'query' option 'value' is invalid: must have a value when the operator is 'equal'") suite.Assert().EqualError(suite.validator.Errors()[0], "access control: rule #1 (domain 'public.example.com'): query: option 'value' must be present when the option 'operator' is 'equal' but it's absent")
suite.Assert().EqualError(suite.validator.Errors()[1], "access control: rule #2 (domain 'public.example.com'): 'query' option 'key' is invalid: must have a value") suite.Assert().EqualError(suite.validator.Errors()[1], "access control: rule #2 (domain 'public.example.com'): query: option 'key' is required but it's absent")
suite.Assert().EqualError(suite.validator.Errors()[2], "access control: rule #5 (domain 'public.example.com'): 'query' option 'key' is invalid: must have a value") suite.Assert().EqualError(suite.validator.Errors()[2], "access control: rule #5 (domain 'public.example.com'): query: option 'key' is required but it's absent")
suite.Assert().EqualError(suite.validator.Errors()[3], "access control: rule #6 (domain 'public.example.com'): 'query' option 'operator' with value 'not' is invalid: must be one of 'present', 'absent', 'equal', 'not equal', 'pattern', 'not pattern'") suite.Assert().EqualError(suite.validator.Errors()[3], "access control: rule #6 (domain 'public.example.com'): query: option 'operator' must be one of 'present', 'absent', 'equal', 'not equal', 'pattern', or 'not pattern' but it's configured as 'not'")
suite.Assert().EqualError(suite.validator.Errors()[4], "access control: rule #7 (domain 'public.example.com'): 'query' option 'value' is invalid: error parsing regexp: missing closing ): `(bad pattern`") suite.Assert().EqualError(suite.validator.Errors()[4], "access control: rule #7 (domain 'public.example.com'): query: option 'value' is invalid: error parsing regexp: missing closing ): `(bad pattern`")
suite.Assert().EqualError(suite.validator.Errors()[5], "access control: rule #8 (domain 'public.example.com'): 'query' option 'value' is invalid: must not have a value when the operator is 'present'") suite.Assert().EqualError(suite.validator.Errors()[5], "access control: rule #8 (domain 'public.example.com'): query: option 'value' must not be present when the option 'operator' is 'present' but it's present")
suite.Assert().EqualError(suite.validator.Errors()[6], "access control: rule #9 (domain 'public.example.com'): 'query' option 'value' is invalid: expected type was string but got int") suite.Assert().EqualError(suite.validator.Errors()[6], "access control: rule #9 (domain 'public.example.com'): query: option 'value' is invalid: expected type was string but got int")
} }
func TestAccessControl(t *testing.T) { func TestAccessControl(t *testing.T) {

View File

@ -71,7 +71,7 @@ func ValidatePasswordConfiguration(config *schema.Password, validator *schema.St
case utils.IsStringInSlice(config.Algorithm, validHashAlgorithms): case utils.IsStringInSlice(config.Algorithm, validHashAlgorithms):
break break
default: default:
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordUnknownAlg, config.Algorithm, strings.Join(validHashAlgorithms, "', '"))) validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordUnknownAlg, strJoinOr(validHashAlgorithms), config.Algorithm))
} }
validateFileAuthenticationBackendPasswordConfigArgon2(config, validator) validateFileAuthenticationBackendPasswordConfigArgon2(config, validator)
@ -89,7 +89,7 @@ func validateFileAuthenticationBackendPasswordConfigArgon2(config *schema.Passwo
case utils.IsStringInSlice(config.Argon2.Variant, validArgon2Variants): case utils.IsStringInSlice(config.Argon2.Variant, validArgon2Variants):
break break
default: default:
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordInvalidVariant, hashArgon2, config.Argon2.Variant, strings.Join(validArgon2Variants, "', '"))) validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordInvalidVariant, hashArgon2, strJoinOr(validArgon2Variants), config.Argon2.Variant))
} }
switch { switch {
@ -147,7 +147,7 @@ func validateFileAuthenticationBackendPasswordConfigSHA2Crypt(config *schema.Pas
case utils.IsStringInSlice(config.SHA2Crypt.Variant, validSHA2CryptVariants): case utils.IsStringInSlice(config.SHA2Crypt.Variant, validSHA2CryptVariants):
break break
default: default:
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordInvalidVariant, hashSHA2Crypt, config.SHA2Crypt.Variant, strings.Join(validSHA2CryptVariants, "', '"))) validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordInvalidVariant, hashSHA2Crypt, strJoinOr(validSHA2CryptVariants), config.SHA2Crypt.Variant))
} }
switch { switch {
@ -176,7 +176,7 @@ func validateFileAuthenticationBackendPasswordConfigPBKDF2(config *schema.Passwo
case utils.IsStringInSlice(config.PBKDF2.Variant, validPBKDF2Variants): case utils.IsStringInSlice(config.PBKDF2.Variant, validPBKDF2Variants):
break break
default: default:
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordInvalidVariant, hashPBKDF2, config.PBKDF2.Variant, strings.Join(validPBKDF2Variants, "', '"))) validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordInvalidVariant, hashPBKDF2, strJoinOr(validPBKDF2Variants), config.PBKDF2.Variant))
} }
switch { switch {
@ -205,7 +205,7 @@ func validateFileAuthenticationBackendPasswordConfigBCrypt(config *schema.Passwo
case utils.IsStringInSlice(config.BCrypt.Variant, validBCryptVariants): case utils.IsStringInSlice(config.BCrypt.Variant, validBCryptVariants):
break break
default: default:
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordInvalidVariant, hashBCrypt, config.BCrypt.Variant, strings.Join(validBCryptVariants, "', '"))) validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordInvalidVariant, hashBCrypt, strJoinOr(validBCryptVariants), config.BCrypt.Variant))
} }
switch { switch {
@ -369,7 +369,7 @@ func validateLDAPAuthenticationBackendImplementation(config *schema.Authenticati
case schema.LDAPImplementationGLAuth: case schema.LDAPImplementationGLAuth:
implementation = &schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth implementation = &schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth
default: default:
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendImplementation, config.LDAP.Implementation, strings.Join(validLDAPImplementations, "', '"))) validator.Push(fmt.Errorf(errFmtLDAPAuthBackendImplementation, strJoinOr(validLDAPImplementations), config.LDAP.Implementation))
} }
tlsconfig := &schema.TLSConfig{} tlsconfig := &schema.TLSConfig{}

View File

@ -256,7 +256,7 @@ func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorOnInvalidArgon2
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 'variant' is configured as 'invalid' but must be one of the following values: 'argon2id', 'id', 'argon2i', 'i', 'argon2d', 'd'") suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: file: password: argon2: option 'variant' must be one of 'argon2id', 'id', 'argon2i', 'i', 'argon2d', or 'd' but it's configured as 'invalid'")
} }
func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorOnInvalidSHA2CryptVariant() { func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorOnInvalidSHA2CryptVariant() {
@ -270,7 +270,7 @@ func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorOnInvalidSHA2Cr
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: sha2crypt: option 'variant' is configured as 'invalid' but must be one of the following values: 'sha256', 'sha512'") suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: file: password: sha2crypt: option 'variant' must be one of 'sha256' or 'sha512' but it's configured as 'invalid'")
} }
func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorOnInvalidSHA2CryptSaltLength() { func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorOnInvalidSHA2CryptSaltLength() {
@ -298,7 +298,7 @@ func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorOnInvalidPBKDF2
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: pbkdf2: option 'variant' is configured as 'invalid' but must be one of the following values: 'sha1', 'sha224', 'sha256', 'sha384', 'sha512'") suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: file: password: pbkdf2: option 'variant' must be one of 'sha1', 'sha224', 'sha256', 'sha384', or 'sha512' but it's configured as 'invalid'")
} }
func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorOnInvalidBCryptVariant() { func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorOnInvalidBCryptVariant() {
@ -312,7 +312,7 @@ func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorOnInvalidBCrypt
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: bcrypt: option 'variant' is configured as 'invalid' but must be one of the following values: 'standard', 'sha256'") suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: file: password: bcrypt: option 'variant' must be one of 'standard' or 'sha256' but it's configured as 'invalid'")
} }
func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenSHA2CryptOptionsTooLow() { func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenSHA2CryptOptionsTooLow() {
@ -497,7 +497,7 @@ func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenBadAlgorith
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: option 'algorithm' is configured as 'bogus' but must be one of the following values: 'sha2crypt', 'pbkdf2', 'scrypt', 'bcrypt', 'argon2'") suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: file: password: option 'algorithm' must be one of 'sha2crypt', 'pbkdf2', 'scrypt', 'bcrypt', or 'argon2' but it's configured as 'bogus'")
} }
func (suite *FileBasedAuthenticationBackend) TestShouldSetDefaultValues() { func (suite *FileBasedAuthenticationBackend) TestShouldSetDefaultValues() {
@ -609,7 +609,7 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorWhenImplementat
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: ldap: option 'implementation' is configured as 'masd' but must be one of the following values: 'custom', 'activedirectory', 'rfc2307bis', 'freeipa', 'lldap', 'glauth'") suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: option 'implementation' must be one of 'custom', 'activedirectory', 'rfc2307bis', 'freeipa', 'lldap', or 'glauth' but it's configured as 'masd'")
} }
func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorWhenURLNotProvided() { func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorWhenURLNotProvided() {
@ -755,7 +755,7 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorOnBadFilterPlac
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: option 'users_filter' has an invalid placeholder: '{0}' has been removed, please use '{input}' instead") suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: option 'users_filter' has an invalid placeholder: '{0}' has been removed, please use '{input}' instead")
suite.Assert().EqualError(suite.validator.Errors()[1], "authentication_backend: ldap: option 'groups_filter' has an invalid placeholder: '{0}' has been removed, please use '{input}' instead") suite.Assert().EqualError(suite.validator.Errors()[1], "authentication_backend: ldap: option 'groups_filter' has an invalid placeholder: '{0}' has been removed, please use '{input}' instead")
suite.Assert().EqualError(suite.validator.Errors()[2], "authentication_backend: ldap: option 'groups_filter' has an invalid placeholder: '{1}' has been removed, please use '{username}' instead") suite.Assert().EqualError(suite.validator.Errors()[2], "authentication_backend: ldap: option 'groups_filter' has an invalid placeholder: '{1}' has been removed, please use '{username}' instead")
suite.Assert().EqualError(suite.validator.Errors()[3], "authentication_backend: ldap: option 'users_filter' must contain the placeholder '{input}' but it is required") suite.Assert().EqualError(suite.validator.Errors()[3], "authentication_backend: ldap: option 'users_filter' must contain the placeholder '{input}' but it's absent")
} }
func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultGroupNameAttribute() { func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultGroupNameAttribute() {
@ -823,7 +823,7 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseWhenUsersFilterDoesN
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: ldap: option 'users_filter' must contain the placeholder '{username_attribute}' but it is required") suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: option 'users_filter' must contain the placeholder '{username_attribute}' but it's absent")
} }
func (suite *LDAPAuthenticationBackendSuite) TestShouldHelpDetectNoInputPlaceholder() { func (suite *LDAPAuthenticationBackendSuite) TestShouldHelpDetectNoInputPlaceholder() {
@ -834,7 +834,7 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldHelpDetectNoInputPlacehol
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: ldap: option 'users_filter' must contain the placeholder '{input}' but it is required") suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: option 'users_filter' must contain the placeholder '{input}' but it's absent")
} }
func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultTLSMinimumVersion() { func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultTLSMinimumVersion() {
@ -986,7 +986,7 @@ func (suite *ActiveDirectoryAuthenticationBackendSuite) TestShouldRaiseErrorOnIn
validateLDAPAuthenticationBackendURL(suite.config.LDAP, suite.validator) validateLDAPAuthenticationBackendURL(suite.config.LDAP, suite.validator)
suite.Require().Len(suite.validator.Errors(), 1) suite.Require().Len(suite.validator.Errors(), 1)
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: option 'url' must have either the 'ldap' or 'ldaps' scheme but it is configured as 'http'") suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: option 'url' must have either the 'ldap' or 'ldaps' scheme but it's configured as 'http'")
} }
func (suite *ActiveDirectoryAuthenticationBackendSuite) TestShouldRaiseErrorOnInvalidURLWithBadCharacters() { func (suite *ActiveDirectoryAuthenticationBackendSuite) TestShouldRaiseErrorOnInvalidURLWithBadCharacters() {

View File

@ -78,7 +78,7 @@ func validateDefault2FAMethod(config *schema.Configuration, validator *schema.St
} }
if !utils.IsStringInSlice(config.Default2FAMethod, validDefault2FAMethods) { if !utils.IsStringInSlice(config.Default2FAMethod, validDefault2FAMethods) {
validator.Push(fmt.Errorf(errFmtInvalidDefault2FAMethod, config.Default2FAMethod, strings.Join(validDefault2FAMethods, "', '"))) validator.Push(fmt.Errorf(errFmtInvalidDefault2FAMethod, strJoinOr(validDefault2FAMethods), config.Default2FAMethod))
return return
} }
@ -98,6 +98,6 @@ func validateDefault2FAMethod(config *schema.Configuration, validator *schema.St
} }
if !utils.IsStringInSlice(config.Default2FAMethod, enabledMethods) { if !utils.IsStringInSlice(config.Default2FAMethod, enabledMethods) {
validator.Push(fmt.Errorf(errFmtInvalidDefault2FAMethodDisabled, config.Default2FAMethod, strings.Join(enabledMethods, "', '"))) validator.Push(fmt.Errorf(errFmtInvalidDefault2FAMethodDisabled, strJoinOr(enabledMethods), config.Default2FAMethod))
} }
} }

View File

@ -221,7 +221,7 @@ func TestValidateDefault2FAMethod(t *testing.T) {
TOTP: schema.TOTPConfiguration{Disable: true}, TOTP: schema.TOTPConfiguration{Disable: true},
}, },
expectedErrs: []string{ expectedErrs: []string{
"option 'default_2fa_method' is configured as 'totp' but must be one of the following enabled method values: 'webauthn', 'mobile_push'", "option 'default_2fa_method' must be one of the enabled options 'webauthn' or 'mobile_push' but it's configured as 'totp'",
}, },
}, },
{ {
@ -236,7 +236,7 @@ func TestValidateDefault2FAMethod(t *testing.T) {
WebAuthn: schema.WebAuthnConfiguration{Disable: true}, WebAuthn: schema.WebAuthnConfiguration{Disable: true},
}, },
expectedErrs: []string{ expectedErrs: []string{
"option 'default_2fa_method' is configured as 'webauthn' but must be one of the following enabled method values: 'totp', 'mobile_push'", "option 'default_2fa_method' must be one of the enabled options 'totp' or 'mobile_push' but it's configured as 'webauthn'",
}, },
}, },
{ {
@ -246,7 +246,7 @@ func TestValidateDefault2FAMethod(t *testing.T) {
DuoAPI: schema.DuoAPIConfiguration{Disable: true}, DuoAPI: schema.DuoAPIConfiguration{Disable: true},
}, },
expectedErrs: []string{ expectedErrs: []string{
"option 'default_2fa_method' is configured as 'mobile_push' but must be one of the following enabled method values: 'totp', 'webauthn'", "option 'default_2fa_method' must be one of the enabled options 'totp' or 'webauthn' but it's configured as 'mobile_push'",
}, },
}, },
{ {
@ -255,7 +255,7 @@ func TestValidateDefault2FAMethod(t *testing.T) {
Default2FAMethod: "duo", Default2FAMethod: "duo",
}, },
expectedErrs: []string{ expectedErrs: []string{
"option 'default_2fa_method' is configured as 'duo' but must be one of the following values: 'totp', 'webauthn', 'mobile_push'", "option 'default_2fa_method' must be one of 'totp', 'webauthn', or 'mobile_push' but it's configured as 'duo'",
}, },
}, },
} }

View File

@ -67,7 +67,7 @@ const (
) )
const ( const (
errSuffixMustBeOneOf = "is configured as '%s' but must be one of the following values: '%s'" errSuffixMustBeOneOf = "must be one of %s but it's configured as '%s'"
) )
// Authentication Backend Error constants. // Authentication Backend Error constants.
@ -105,19 +105,19 @@ const (
errFmtLDAPAuthBackendURLNotParsable = "authentication_backend: ldap: option " + errFmtLDAPAuthBackendURLNotParsable = "authentication_backend: ldap: option " +
"'url' could not be parsed: %w" "'url' could not be parsed: %w"
errFmtLDAPAuthBackendURLInvalidScheme = "authentication_backend: ldap: option " + errFmtLDAPAuthBackendURLInvalidScheme = "authentication_backend: ldap: option " +
"'url' must have either the 'ldap' or 'ldaps' scheme but it is configured as '%s'" "'url' must have either the 'ldap' or 'ldaps' scheme but it's configured as '%s'"
errFmtLDAPAuthBackendFilterEnclosingParenthesis = "authentication_backend: ldap: option " + errFmtLDAPAuthBackendFilterEnclosingParenthesis = "authentication_backend: ldap: option " +
"'%s' must contain enclosing parenthesis: '%s' should probably be '(%s)'" "'%s' must contain enclosing parenthesis: '%s' should probably be '(%s)'"
errFmtLDAPAuthBackendFilterMissingPlaceholder = "authentication_backend: ldap: option " + errFmtLDAPAuthBackendFilterMissingPlaceholder = "authentication_backend: ldap: option " +
"'%s' must contain the placeholder '{%s}' but it is required" "'%s' must contain the placeholder '{%s}' but it's absent"
) )
// TOTP Error constants. // TOTP Error constants.
const ( const (
errFmtTOTPInvalidAlgorithm = "totp: option 'algorithm' must be one of '%s' but it is configured as '%s'" errFmtTOTPInvalidAlgorithm = "totp: option 'algorithm' must be one of %s but it's configured as '%s'"
errFmtTOTPInvalidPeriod = "totp: option 'period' option must be 15 or more but it is configured as '%d'" errFmtTOTPInvalidPeriod = "totp: option 'period' option must be 15 or more but it's configured as '%d'"
errFmtTOTPInvalidDigits = "totp: option 'digits' must be 6 or 8 but it is configured as '%d'" errFmtTOTPInvalidDigits = "totp: option 'digits' must be 6 or 8 but it's configured as '%d'"
errFmtTOTPInvalidSecretSize = "totp: option 'secret_size' must be %d or higher but it is configured as '%d'" //nolint:gosec errFmtTOTPInvalidSecretSize = "totp: option 'secret_size' must be %d or higher but it's configured as '%d'" //nolint:gosec
) )
// Storage Error constants. // Storage Error constants.
@ -128,14 +128,14 @@ const (
errFmtStorageUserPassMustBeProvided = "storage: %s: option 'username' and 'password' are required" //nolint:gosec errFmtStorageUserPassMustBeProvided = "storage: %s: option 'username' and 'password' are required" //nolint:gosec
errFmtStorageOptionMustBeProvided = "storage: %s: option '%s' is required" errFmtStorageOptionMustBeProvided = "storage: %s: option '%s' is required"
errFmtStorageTLSConfigInvalid = "storage: %s: tls: %w" errFmtStorageTLSConfigInvalid = "storage: %s: tls: %w"
errFmtStoragePostgreSQLInvalidSSLMode = "storage: postgres: ssl: option 'mode' must be one of '%s' but it is configured as '%s'" errFmtStoragePostgreSQLInvalidSSLMode = "storage: postgres: ssl: option 'mode' must be one of %s but it's configured as '%s'"
errFmtStoragePostgreSQLInvalidSSLAndTLSConfig = "storage: postgres: can't define both 'tls' and 'ssl' configuration options" errFmtStoragePostgreSQLInvalidSSLAndTLSConfig = "storage: postgres: can't define both 'tls' and 'ssl' configuration options"
warnFmtStoragePostgreSQLInvalidSSLDeprecated = "storage: postgres: ssl: the ssl configuration options are deprecated and we recommend the tls options instead" warnFmtStoragePostgreSQLInvalidSSLDeprecated = "storage: postgres: ssl: the ssl configuration options are deprecated and we recommend the tls options instead"
) )
// Telemetry Error constants. // Telemetry Error constants.
const ( const (
errFmtTelemetryMetricsScheme = "telemetry: metrics: option 'address' must have a scheme 'tcp://' but it is configured as '%s'" errFmtTelemetryMetricsScheme = "telemetry: metrics: option 'address' must have a scheme 'tcp://' but it's configured as '%s'"
) )
// OpenID Error constants. // OpenID Error constants.
@ -148,17 +148,16 @@ const (
errFmtOIDCCertificateMismatch = "identity_providers: oidc: option 'issuer_private_key' does not appear to be the private key the certificate provided by option 'issuer_certificate_chain'" errFmtOIDCCertificateMismatch = "identity_providers: oidc: option 'issuer_private_key' does not appear to be the private key the certificate provided by option 'issuer_certificate_chain'"
errFmtOIDCCertificateChain = "identity_providers: oidc: option 'issuer_certificate_chain' produced an error during validation of the chain: %w" errFmtOIDCCertificateChain = "identity_providers: oidc: option 'issuer_certificate_chain' produced an error during validation of the chain: %w"
errFmtOIDCEnforcePKCEInvalidValue = "identity_providers: oidc: option 'enforce_pkce' must be 'never', " + errFmtOIDCEnforcePKCEInvalidValue = "identity_providers: oidc: option 'enforce_pkce' must be 'never', " +
"'public_clients_only' or 'always', but it is configured as '%s'" "'public_clients_only' or 'always', but it's configured as '%s'"
errFmtOIDCCORSInvalidOrigin = "identity_providers: oidc: cors: option 'allowed_origins' contains an invalid value '%s' as it has a %s: origins must only be scheme, hostname, and an optional port" errFmtOIDCCORSInvalidOrigin = "identity_providers: oidc: cors: option 'allowed_origins' contains an invalid value '%s' as it has a %s: origins must only be scheme, hostname, and an optional port"
errFmtOIDCCORSInvalidOriginWildcard = "identity_providers: oidc: cors: option 'allowed_origins' contains the wildcard origin '*' with more than one origin but the wildcard origin must be defined by itself" errFmtOIDCCORSInvalidOriginWildcard = "identity_providers: oidc: cors: option 'allowed_origins' contains the wildcard origin '*' with more than one origin but the wildcard origin must be defined by itself"
errFmtOIDCCORSInvalidOriginWildcardWithClients = "identity_providers: oidc: cors: option 'allowed_origins' contains the wildcard origin '*' cannot be specified with option 'allowed_origins_from_client_redirect_uris' enabled" errFmtOIDCCORSInvalidOriginWildcardWithClients = "identity_providers: oidc: cors: option 'allowed_origins' contains the wildcard origin '*' cannot be specified with option 'allowed_origins_from_client_redirect_uris' enabled"
errFmtOIDCCORSInvalidEndpoint = "identity_providers: oidc: cors: option 'endpoints' contains an invalid value '%s': must be one of '%s'" errFmtOIDCCORSInvalidEndpoint = "identity_providers: oidc: cors: option 'endpoints' contains an invalid value '%s': must be one of %s"
errFmtOIDCClientsDuplicateID = "identity_providers: oidc: one or more clients have the same id but all client" + errFmtOIDCClientsDuplicateID = "identity_providers: oidc: clients: option 'id' must be unique for every client but one or more clients share the following 'id' values %s"
"id's must be unique" errFmtOIDCClientsWithEmptyID = "identity_providers: oidc: clients: option 'id' is required but was absent on the clients in positions %s"
errFmtOIDCClientsWithEmptyID = "identity_providers: oidc: one or more clients have been configured with " + errFmtOIDCClientsDeprecated = "identity_providers: oidc: clients: warnings for clients above indicate deprecated functionality and it's strongly suggested these issues are checked and fixed if they're legitimate issues or reported if they are not as in a future version these warnings will become errors"
"an empty id"
errFmtOIDCClientInvalidSecret = "identity_providers: oidc: client '%s': option 'secret' is required" errFmtOIDCClientInvalidSecret = "identity_providers: oidc: client '%s': option 'secret' is required"
errFmtOIDCClientInvalidSecretPlainText = "identity_providers: oidc: client '%s': option 'secret' is plaintext but it should be a hashed value as plaintext values are deprecated and will be removed when oidc becomes stable" errFmtOIDCClientInvalidSecretPlainText = "identity_providers: oidc: client '%s': option 'secret' is plaintext but it should be a hashed value as plaintext values are deprecated and will be removed when oidc becomes stable"
@ -170,36 +169,43 @@ const (
"redirect uri '%s' when option 'public' is false but this is invalid as this uri is not valid " + "redirect uri '%s' when option 'public' is false but this is invalid as this uri is not valid " +
"for the openid connect confidential client type" "for the openid connect confidential client type"
errFmtOIDCClientRedirectURIAbsolute = "identity_providers: oidc: client '%s': option 'redirect_uris' has an " + errFmtOIDCClientRedirectURIAbsolute = "identity_providers: oidc: client '%s': option 'redirect_uris' has an " +
"invalid value: redirect uri '%s' must have the scheme but it is absent" "invalid value: redirect uri '%s' must have a scheme but it's absent"
errFmtOIDCClientInvalidPolicy = "identity_providers: oidc: client '%s': option 'policy' must be 'one_factor' " +
"or 'two_factor' but it is configured as '%s'"
errFmtOIDCClientInvalidPKCEChallengeMethod = "identity_providers: oidc: client '%s': option 'pkce_challenge_method' must be 'plain' " +
"or 'S256' but it is configured as '%s'"
errFmtOIDCClientInvalidConsentMode = "identity_providers: oidc: client '%s': consent: option 'mode' must be one of " + errFmtOIDCClientInvalidConsentMode = "identity_providers: oidc: client '%s': consent: option 'mode' must be one of " +
"'%s' but it is configured as '%s'" "%s but it's configured as '%s'"
errFmtOIDCClientInvalidEntry = "identity_providers: oidc: client '%s': option '%s' must only have the values " + errFmtOIDCClientInvalidEntries = "identity_providers: oidc: client '%s': option '%s' must only have the values " +
"'%s' but one option is configured as '%s'" "%s but the values %s are present"
errFmtOIDCClientInvalidUserinfoAlgorithm = "identity_providers: oidc: client '%s': option " + errFmtOIDCClientInvalidEntryDuplicates = "identity_providers: oidc: client '%s': option '%s' must have unique values but the values %s are duplicated"
"'userinfo_signing_algorithm' must be one of '%s' but it is configured as '%s'" errFmtOIDCClientInvalidValue = "identity_providers: oidc: client '%s': option " +
"'%s' must be one of %s but it's configured as '%s'"
errFmtOIDCClientInvalidTokenEndpointAuthMethod = "identity_providers: oidc: client '%s': option " +
"'token_endpoint_auth_method' must be one of %s when configured as the confidential client type unless it only includes implicit flow response types such as %s but it's configured as '%s'"
errFmtOIDCClientInvalidTokenEndpointAuthMethodPublic = "identity_providers: oidc: client '%s': option " +
"'token_endpoint_auth_method' must be 'none' when configured as the public client type but it's configured as '%s'"
errFmtOIDCClientInvalidSectorIdentifier = "identity_providers: oidc: client '%s': option " + errFmtOIDCClientInvalidSectorIdentifier = "identity_providers: oidc: client '%s': option " +
"'sector_identifier' with value '%s': must be a URL with only the host component for example '%s' but it has a %s with the value '%s'" "'sector_identifier' with value '%s': must be a URL with only the host component for example '%s' but it has a %s with the value '%s'"
errFmtOIDCClientInvalidSectorIdentifierWithoutValue = "identity_providers: oidc: client '%s': option " + errFmtOIDCClientInvalidSectorIdentifierWithoutValue = "identity_providers: oidc: client '%s': option " +
"'sector_identifier' with value '%s': must be a URL with only the host component for example '%s' but it has a %s" "'sector_identifier' with value '%s': must be a URL with only the host component for example '%s' but it has a %s"
errFmtOIDCClientInvalidSectorIdentifierHost = "identity_providers: oidc: client '%s': option " + errFmtOIDCClientInvalidSectorIdentifierHost = "identity_providers: oidc: client '%s': option " +
"'sector_identifier' with value '%s': must be a URL with only the host component but appears to be invalid" "'sector_identifier' with value '%s': must be a URL with only the host component but appears to be invalid"
errFmtOIDCClientInvalidGrantTypeMatch = "identity_providers: oidc: client '%s': option " +
"'grant_types' should only have grant type values which are valid with the configured 'response_types' for the client but '%s' expects a response type %s such as %s but the response types are %s"
errFmtOIDCClientInvalidGrantTypeRefresh = "identity_providers: oidc: client '%s': option " +
"'grant_types' should only have the 'refresh_token' value if the client is also configured with the 'offline_access' scope"
errFmtOIDCClientInvalidRefreshTokenOptionWithoutCodeResponseType = "identity_providers: oidc: client '%s': option " +
"'%s' should only have the values %s if the client is also configured with a 'response_type' such as %s which respond with authorization codes"
errFmtOIDCServerInsecureParameterEntropy = "openid connect provider: SECURITY ISSUE - minimum parameter entropy is " + errFmtOIDCServerInsecureParameterEntropy = "openid connect provider: SECURITY ISSUE - minimum parameter entropy is " +
"configured to an unsafe value, it should be above 8 but it's configured to %d" "configured to an unsafe value, it should be above 8 but it's configured to %d"
) )
// WebAuthn Error constants. // WebAuthn Error constants.
const ( const (
errFmtWebAuthnConveyancePreference = "webauthn: option 'attestation_conveyance_preference' must be one of '%s' but it is configured as '%s'" errFmtWebAuthnConveyancePreference = "webauthn: option 'attestation_conveyance_preference' must be one of %s but it's configured as '%s'"
errFmtWebAuthnUserVerification = "webauthn: option 'user_verification' must be one of 'discouraged', 'preferred', 'required' but it is configured as '%s'" errFmtWebAuthnUserVerification = "webauthn: option 'user_verification' must be one of %s but it's configured as '%s'"
) )
// Access Control error constants. // Access Control error constants.
const ( const (
errFmtAccessControlDefaultPolicyValue = "access control: option 'default_policy' must be one of '%s' but it is " + errFmtAccessControlDefaultPolicyValue = "access control: option 'default_policy' must be one of %s but it's " +
"configured as '%s'" "configured as '%s'"
errFmtAccessControlDefaultPolicyWithoutRules = "access control: 'default_policy' option '%s' is invalid: when " + errFmtAccessControlDefaultPolicyWithoutRules = "access control: 'default_policy' option '%s' is invalid: when " +
"no rules are specified it must be 'two_factor' or 'one_factor'" "no rules are specified it must be 'two_factor' or 'one_factor'"
@ -207,10 +213,9 @@ const (
"network '%s' is not a valid IP or CIDR notation" "network '%s' is not a valid IP or CIDR notation"
errFmtAccessControlWarnNoRulesDefaultPolicy = "access control: no rules have been specified so the " + errFmtAccessControlWarnNoRulesDefaultPolicy = "access control: no rules have been specified so the " +
"'default_policy' of '%s' is going to be applied to all requests" "'default_policy' of '%s' is going to be applied to all requests"
errFmtAccessControlRuleNoDomains = "access control: rule %s: rule is invalid: must have the option " + errFmtAccessControlRuleNoDomains = "access control: rule %s: option 'domain' or 'domain_regex' must be present but are both absent"
"'domain' or 'domain_regex' configured" errFmtAccessControlRuleNoPolicy = "access control: rule %s: option 'policy' must be present but it's absent"
errFmtAccessControlRuleInvalidPolicy = "access control: rule %s: rule 'policy' option '%s' " + errFmtAccessControlRuleInvalidPolicy = "access control: rule %s: option 'policy' must be one of %s but it's configured as '%s'"
"is invalid: must be one of 'deny', 'two_factor', 'one_factor' or 'bypass'"
errAccessControlRuleBypassPolicyInvalidWithSubjects = "access control: rule %s: 'policy' option 'bypass' is " + errAccessControlRuleBypassPolicyInvalidWithSubjects = "access control: rule %s: 'policy' option 'bypass' is " +
"not supported when 'subject' option is configured: see " + "not supported when 'subject' option is configured: see " +
"https://www.authelia.com/c/acl#bypass" "https://www.authelia.com/c/acl#bypass"
@ -221,39 +226,35 @@ const (
"valid Group Name, IP, or CIDR notation" "valid Group Name, IP, or CIDR notation"
errFmtAccessControlRuleSubjectInvalid = "access control: rule %s: 'subject' option '%s' is " + errFmtAccessControlRuleSubjectInvalid = "access control: rule %s: 'subject' option '%s' is " +
"invalid: must start with 'user:' or 'group:'" "invalid: must start with 'user:' or 'group:'"
errFmtAccessControlRuleMethodInvalid = "access control: rule %s: 'methods' option '%s' is " + errFmtAccessControlRuleInvalidEntries = "access control: rule %s: option '%s' must only have the values %s but the values %s are present"
"invalid: must be one of '%s'" errFmtAccessControlRuleInvalidDuplicates = "access control: rule %s: option '%s' must have unique values but the values %s are duplicated"
errFmtAccessControlRuleQueryInvalid = "access control: rule %s: 'query' option 'operator' with value '%s' is " + errFmtAccessControlRuleQueryInvalid = "access control: rule %s: query: option 'operator' must be one of %s but it's configured as '%s'"
"invalid: must be one of '%s'" errFmtAccessControlRuleQueryInvalidNoValue = "access control: rule %s: query: option '%s' is required but it's absent"
errFmtAccessControlRuleQueryInvalidNoValue = "access control: rule %s: 'query' option '%s' is " + errFmtAccessControlRuleQueryInvalidNoValueOperator = "access control: rule %s: query: option '%s' must be present when the option 'operator' is '%s' but it's absent"
"invalid: must have a value" errFmtAccessControlRuleQueryInvalidValue = "access control: rule %s: query: option '%s' must not be present when the option 'operator' is '%s' but it's present"
errFmtAccessControlRuleQueryInvalidNoValueOperator = "access control: rule %s: 'query' option '%s' is " + errFmtAccessControlRuleQueryInvalidValueParse = "access control: rule %s: query: option '%s' is " +
"invalid: must have a value when the operator is '%s'"
errFmtAccessControlRuleQueryInvalidValue = "access control: rule %s: 'query' option '%s' is " +
"invalid: must not have a value when the operator is '%s'"
errFmtAccessControlRuleQueryInvalidValueParse = "access control: rule %s: 'query' option '%s' is " +
"invalid: %w" "invalid: %w"
errFmtAccessControlRuleQueryInvalidValueType = "access control: rule %s: 'query' option 'value' is " + errFmtAccessControlRuleQueryInvalidValueType = "access control: rule %s: query: option 'value' is " +
"invalid: expected type was string but got %T" "invalid: expected type was string but got %T"
) )
// Theme Error constants. // Theme Error constants.
const ( const (
errFmtThemeName = "option 'theme' must be one of '%s' but it is configured as '%s'" errFmtThemeName = "option 'theme' must be one of %s but it's configured as '%s'"
) )
// NTP Error constants. // NTP Error constants.
const ( const (
errFmtNTPVersion = "ntp: option 'version' must be either 3 or 4 but it is configured as '%d'" errFmtNTPVersion = "ntp: option 'version' must be either 3 or 4 but it's configured as '%d'"
) )
// Session error constants. // Session error constants.
const ( const (
errFmtSessionOptionRequired = "session: option '%s' is required" errFmtSessionOptionRequired = "session: option '%s' is required"
errFmtSessionLegacyAndWarning = "session: option 'domain' and option 'cookies' can't be specified at the same time" errFmtSessionLegacyAndWarning = "session: option 'domain' and option 'cookies' can't be specified at the same time"
errFmtSessionSameSite = "session: option 'same_site' must be one of '%s' but is configured as '%s'" errFmtSessionSameSite = "session: option 'same_site' must be one of %s but it's configured as '%s'"
errFmtSessionSecretRequired = "session: option 'secret' is required when using the '%s' provider" errFmtSessionSecretRequired = "session: option 'secret' is required when using the '%s' provider"
errFmtSessionRedisPortRange = "session: redis: option 'port' must be between 1 and 65535 but is configured as '%d'" errFmtSessionRedisPortRange = "session: redis: option 'port' must be between 1 and 65535 but it's configured as '%d'"
errFmtSessionRedisHostRequired = "session: redis: option 'host' is required" errFmtSessionRedisHostRequired = "session: redis: option 'host' is required"
errFmtSessionRedisHostOrNodesRequired = "session: redis: option 'host' or the 'high_availability' option 'nodes' is required" errFmtSessionRedisHostOrNodesRequired = "session: redis: option 'host' or the 'high_availability' option 'nodes' is required"
errFmtSessionRedisTLSConfigInvalid = "session: redis: tls: %w" errFmtSessionRedisTLSConfigInvalid = "session: redis: tls: %w"
@ -261,8 +262,8 @@ const (
errFmtSessionRedisSentinelMissingName = "session: redis: high_availability: option 'sentinel_name' is required" errFmtSessionRedisSentinelMissingName = "session: redis: high_availability: option 'sentinel_name' is required"
errFmtSessionRedisSentinelNodeHostMissing = "session: redis: high_availability: option 'nodes': option 'host' is required for each node but one or more nodes are missing this" errFmtSessionRedisSentinelNodeHostMissing = "session: redis: high_availability: option 'nodes': option 'host' is required for each node but one or more nodes are missing this"
errFmtSessionDomainMustBeRoot = "session: domain config %s: option 'domain' must be the domain you wish to protect not a wildcard domain but it is configured as '%s'" errFmtSessionDomainMustBeRoot = "session: domain config %s: option 'domain' must be the domain you wish to protect not a wildcard domain but it's configured as '%s'"
errFmtSessionDomainSameSite = "session: domain config %s: option 'same_site' must be one of '%s' but is configured as '%s'" errFmtSessionDomainSameSite = "session: domain config %s: option 'same_site' must be one of %s but it's configured as '%s'"
errFmtSessionDomainRequired = "session: domain config %s: option 'domain' is required" errFmtSessionDomainRequired = "session: domain config %s: option 'domain' is required"
errFmtSessionDomainHasPeriodPrefix = "session: domain config %s: option 'domain' has a prefix of '.' which is not supported or intended behaviour: you can use this at your own risk but we recommend removing it" errFmtSessionDomainHasPeriodPrefix = "session: domain config %s: option 'domain' has a prefix of '.' which is not supported or intended behaviour: you can use this at your own risk but we recommend removing it"
errFmtSessionDomainDuplicate = "session: domain config %s: option 'domain' is a duplicate value for another configured session domain" errFmtSessionDomainDuplicate = "session: domain config %s: option 'domain' is a duplicate value for another configured session domain"
@ -291,8 +292,8 @@ const (
errFmtServerPathNoForwardSlashes = "server: option 'path' must not contain any forward slashes" errFmtServerPathNoForwardSlashes = "server: option 'path' must not contain any forward slashes"
errFmtServerPathAlphaNum = "server: option 'path' must only contain alpha numeric characters" errFmtServerPathAlphaNum = "server: option 'path' must only contain alpha numeric characters"
errFmtServerEndpointsAuthzImplementation = "server: endpoints: authz: %s: option 'implementation' must be one of '%s' but is configured as '%s'" errFmtServerEndpointsAuthzImplementation = "server: endpoints: authz: %s: option 'implementation' must be one of %s but it's configured as '%s'"
errFmtServerEndpointsAuthzStrategy = "server: endpoints: authz: %s: authn_strategies: option 'name' must be one of '%s' but is configured as '%s'" errFmtServerEndpointsAuthzStrategy = "server: endpoints: authz: %s: authn_strategies: option 'name' must be one of %s but it's configured as '%s'"
errFmtServerEndpointsAuthzStrategyDuplicate = "server: endpoints: authz: %s: authn_strategies: duplicate strategy name detected with name '%s'" errFmtServerEndpointsAuthzStrategyDuplicate = "server: endpoints: authz: %s: authn_strategies: duplicate strategy name detected with name '%s'"
errFmtServerEndpointsAuthzPrefixDuplicate = "server: endpoints: authz: %s: endpoint starts with the same prefix as the '%s' endpoint with the '%s' implementation which accepts prefixes as part of its implementation" errFmtServerEndpointsAuthzPrefixDuplicate = "server: endpoints: authz: %s: endpoint starts with the same prefix as the '%s' endpoint with the '%s' implementation which accepts prefixes as part of its implementation"
errFmtServerEndpointsAuthzInvalidName = "server: endpoints: authz: %s: contains invalid characters" errFmtServerEndpointsAuthzInvalidName = "server: endpoints: authz: %s: contains invalid characters"
@ -302,7 +303,7 @@ const (
const ( const (
errPasswordPolicyMultipleDefined = "password_policy: only a single password policy mechanism can be specified" errPasswordPolicyMultipleDefined = "password_policy: only a single password policy mechanism can be specified"
errFmtPasswordPolicyStandardMinLengthNotGreaterThanZero = "password_policy: standard: option 'min_length' must be greater than 0 but is configured as %d" errFmtPasswordPolicyStandardMinLengthNotGreaterThanZero = "password_policy: standard: option 'min_length' must be greater than 0 but it's configured as %d"
errFmtPasswordPolicyZXCVBNMinScoreInvalid = "password_policy: zxcvbn: option 'min_score' is invalid: must be between 1 and 4 but it's configured as %d" errFmtPasswordPolicyZXCVBNMinScoreInvalid = "password_policy: zxcvbn: option 'min_score' is invalid: must be between 1 and 4 but it's configured as %d"
) )
@ -312,19 +313,17 @@ const (
) )
const ( const (
errFmtDuoMissingOption = "duo_api: option '%s' is required when duo is enabled but it is missing" errFmtDuoMissingOption = "duo_api: option '%s' is required when duo is enabled but it's absent"
) )
// Error constants. // Error constants.
const ( const (
errFmtInvalidDefault2FAMethod = "option 'default_2fa_method' is configured as '%s' but must be one of " + errFmtInvalidDefault2FAMethod = "option 'default_2fa_method' must be one of %s but it's configured as '%s'"
"the following values: '%s'" errFmtInvalidDefault2FAMethodDisabled = "option 'default_2fa_method' must be one of the enabled options %s but it's configured as '%s'"
errFmtInvalidDefault2FAMethodDisabled = "option 'default_2fa_method' is configured as '%s' " +
"but must be one of the following enabled method values: '%s'"
errFmtReplacedConfigurationKey = "invalid configuration key '%s' was replaced by '%s'" errFmtReplacedConfigurationKey = "invalid configuration key '%s' was replaced by '%s'"
errFmtLoggingLevelInvalid = "log: option 'level' must be one of '%s' but it is configured as '%s'" errFmtLoggingLevelInvalid = "log: option 'level' must be one of %s but it's configured as '%s'"
errFileHashing = "config key incorrect: authentication_backend.file.hashing should be authentication_backend.file.password" errFileHashing = "config key incorrect: authentication_backend.file.hashing should be authentication_backend.file.password"
errFilePHashing = "config key incorrect: authentication_backend.file.password_hashing should be authentication_backend.file.password" errFilePHashing = "config key incorrect: authentication_backend.file.password_hashing should be authentication_backend.file.password"
@ -357,6 +356,10 @@ const (
authzImplementationExtAuthz = "ExtAuthz" authzImplementationExtAuthz = "ExtAuthz"
) )
const (
auto = "auto"
)
var ( var (
validAuthzImplementations = []string{"AuthRequest", "ForwardAuth", authzImplementationExtAuthz, authzImplementationLegacy} validAuthzImplementations = []string{"AuthRequest", "ForwardAuth", authzImplementationExtAuthz, authzImplementationLegacy}
validAuthzAuthnStrategies = []string{"CookieSession", "HeaderAuthorization", "HeaderProxyAuthorization", "HeaderAuthRequestProxyAuthorization", "HeaderLegacy"} validAuthzAuthnStrategies = []string{"CookieSession", "HeaderAuthorization", "HeaderProxyAuthorization", "HeaderAuthRequestProxyAuthorization", "HeaderLegacy"}
@ -372,7 +375,7 @@ var (
var ( var (
validStoragePostgreSQLSSLModes = []string{"disable", "require", "verify-ca", "verify-full"} validStoragePostgreSQLSSLModes = []string{"disable", "require", "verify-ca", "verify-full"}
validThemeNames = []string{"light", "dark", "grey", "auto"} validThemeNames = []string{"light", "dark", "grey", auto}
validSessionSameSiteValues = []string{"none", "lax", "strict"} validSessionSameSiteValues = []string{"none", "lax", "strict"}
validLogLevels = []string{"trace", "debug", "info", "warn", "error"} validLogLevels = []string{"trace", "debug", "info", "warn", "error"}
validWebAuthnConveyancePreferences = []string{string(protocol.PreferNoAttestation), string(protocol.PreferIndirectAttestation), string(protocol.PreferDirectAttestation)} validWebAuthnConveyancePreferences = []string{string(protocol.PreferNoAttestation), string(protocol.PreferIndirectAttestation), string(protocol.PreferDirectAttestation)}
@ -389,13 +392,32 @@ var (
var validDefault2FAMethods = []string{"totp", "webauthn", "mobile_push"} var validDefault2FAMethods = []string{"totp", "webauthn", "mobile_push"}
const (
attrOIDCScopes = "scopes"
attrOIDCResponseTypes = "response_types"
attrOIDCResponseModes = "response_modes"
attrOIDCGrantTypes = "grant_types"
attrOIDCRedirectURIs = "redirect_uris"
attrOIDCTokenAuthMethod = "token_endpoint_auth_method"
attrOIDCUsrSigAlg = "userinfo_signing_algorithm"
attrOIDCPKCEChallengeMethod = "pkce_challenge_method"
)
var ( var (
validOIDCScopes = []string{oidc.ScopeOpenID, oidc.ScopeEmail, oidc.ScopeProfile, oidc.ScopeGroups, oidc.ScopeOfflineAccess}
validOIDCGrantTypes = []string{oidc.GrantTypeImplicit, oidc.GrantTypeRefreshToken, oidc.GrantTypeAuthorizationCode, oidc.GrantTypePassword, oidc.GrantTypeClientCredentials}
validOIDCResponseModes = []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery, oidc.ResponseModeFragment}
validOIDCUserinfoAlgorithms = []string{oidc.SigningAlgorithmNone, oidc.SigningAlgorithmRSAWithSHA256}
validOIDCCORSEndpoints = []string{oidc.EndpointAuthorization, oidc.EndpointPushedAuthorizationRequest, oidc.EndpointToken, oidc.EndpointIntrospection, oidc.EndpointRevocation, oidc.EndpointUserinfo} validOIDCCORSEndpoints = []string{oidc.EndpointAuthorization, oidc.EndpointPushedAuthorizationRequest, oidc.EndpointToken, oidc.EndpointIntrospection, oidc.EndpointRevocation, oidc.EndpointUserinfo}
validOIDCClientConsentModes = []string{"auto", oidc.ClientConsentModeImplicit.String(), oidc.ClientConsentModeExplicit.String(), oidc.ClientConsentModePreConfigured.String()}
validOIDCClientScopes = []string{oidc.ScopeOpenID, oidc.ScopeEmail, oidc.ScopeProfile, oidc.ScopeGroups, oidc.ScopeOfflineAccess}
validOIDCClientUserinfoAlgorithms = []string{oidc.SigningAlgorithmNone, oidc.SigningAlgorithmRSAWithSHA256}
validOIDCClientConsentModes = []string{auto, oidc.ClientConsentModeImplicit.String(), oidc.ClientConsentModeExplicit.String(), oidc.ClientConsentModePreConfigured.String()}
validOIDCClientResponseModes = []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery, oidc.ResponseModeFragment}
validOIDCClientResponseTypes = []string{oidc.ResponseTypeAuthorizationCodeFlow, oidc.ResponseTypeImplicitFlowIDToken, oidc.ResponseTypeImplicitFlowToken, oidc.ResponseTypeImplicitFlowBoth, oidc.ResponseTypeHybridFlowIDToken, oidc.ResponseTypeHybridFlowToken, oidc.ResponseTypeHybridFlowBoth}
validOIDCClientResponseTypesImplicitFlow = []string{oidc.ResponseTypeImplicitFlowIDToken, oidc.ResponseTypeImplicitFlowToken, oidc.ResponseTypeImplicitFlowBoth}
validOIDCClientResponseTypesHybridFlow = []string{oidc.ResponseTypeHybridFlowIDToken, oidc.ResponseTypeHybridFlowToken, oidc.ResponseTypeHybridFlowBoth}
validOIDCClientResponseTypesRefreshToken = []string{oidc.ResponseTypeAuthorizationCodeFlow, oidc.ResponseTypeHybridFlowIDToken, oidc.ResponseTypeHybridFlowToken, oidc.ResponseTypeHybridFlowBoth}
validOIDCClientGrantTypes = []string{oidc.GrantTypeImplicit, oidc.GrantTypeRefreshToken, oidc.GrantTypeAuthorizationCode}
validOIDCClientTokenEndpointAuthMethods = []string{oidc.ClientAuthMethodNone, oidc.ClientAuthMethodClientSecretPost, oidc.ClientAuthMethodClientSecretBasic}
validOIDCClientTokenEndpointAuthMethodsConfidential = []string{oidc.ClientAuthMethodClientSecretPost, oidc.ClientAuthMethodClientSecretBasic}
) )
var ( var (

View File

@ -22,6 +22,11 @@ func TestValidateDuo(t *testing.T) {
have: &schema.Configuration{}, have: &schema.Configuration{},
expected: schema.DuoAPIConfiguration{Disable: true}, expected: schema.DuoAPIConfiguration{Disable: true},
}, },
{
desc: "ShouldDisableDuoConfigured",
have: &schema.Configuration{DuoAPI: schema.DuoAPIConfiguration{Disable: true, Hostname: "example.com"}},
expected: schema.DuoAPIConfiguration{Disable: true, Hostname: "example.com"},
},
{ {
desc: "ShouldNotDisableDuo", desc: "ShouldNotDisableDuo",
have: &schema.Configuration{DuoAPI: schema.DuoAPIConfiguration{ have: &schema.Configuration{DuoAPI: schema.DuoAPIConfiguration{
@ -46,7 +51,7 @@ func TestValidateDuo(t *testing.T) {
IntegrationKey: "test", IntegrationKey: "test",
}, },
errs: []string{ errs: []string{
"duo_api: option 'secret_key' is required when duo is enabled but it is missing", "duo_api: option 'secret_key' is required when duo is enabled but it's absent",
}, },
}, },
{ {
@ -60,7 +65,7 @@ func TestValidateDuo(t *testing.T) {
SecretKey: "test", SecretKey: "test",
}, },
errs: []string{ errs: []string{
"duo_api: option 'integration_key' is required when duo is enabled but it is missing", "duo_api: option 'integration_key' is required when duo is enabled but it's absent",
}, },
}, },
{ {
@ -74,7 +79,7 @@ func TestValidateDuo(t *testing.T) {
SecretKey: "test", SecretKey: "test",
}, },
errs: []string{ errs: []string{
"duo_api: option 'hostname' is required when duo is enabled but it is missing", "duo_api: option 'hostname' is required when duo is enabled but it's absent",
}, },
}, },
} }

View File

@ -3,6 +3,7 @@ package validator
import ( import (
"fmt" "fmt"
"net/url" "net/url"
"strconv"
"strings" "strings"
"time" "time"
@ -125,10 +126,10 @@ func validateOIDCOptionsCORSAllowedOriginsFromClientRedirectURIs(config *schema.
continue continue
} }
origin := utils.OriginFromURL(*uri) origin := utils.OriginFromURL(uri)
if !utils.IsURLInSlice(origin, config.CORS.AllowedOrigins) { if !utils.IsURLInSlice(*origin, config.CORS.AllowedOrigins) {
config.CORS.AllowedOrigins = append(config.CORS.AllowedOrigins, origin) config.CORS.AllowedOrigins = append(config.CORS.AllowedOrigins, *origin)
} }
} }
} }
@ -137,113 +138,135 @@ func validateOIDCOptionsCORSAllowedOriginsFromClientRedirectURIs(config *schema.
func validateOIDCOptionsCORSEndpoints(config *schema.OpenIDConnectConfiguration, val *schema.StructValidator) { func validateOIDCOptionsCORSEndpoints(config *schema.OpenIDConnectConfiguration, val *schema.StructValidator) {
for _, endpoint := range config.CORS.Endpoints { for _, endpoint := range config.CORS.Endpoints {
if !utils.IsStringInSlice(endpoint, validOIDCCORSEndpoints) { if !utils.IsStringInSlice(endpoint, validOIDCCORSEndpoints) {
val.Push(fmt.Errorf(errFmtOIDCCORSInvalidEndpoint, endpoint, strings.Join(validOIDCCORSEndpoints, "', '"))) val.Push(fmt.Errorf(errFmtOIDCCORSInvalidEndpoint, endpoint, strJoinOr(validOIDCCORSEndpoints)))
} }
} }
} }
func validateOIDCClients(config *schema.OpenIDConnectConfiguration, val *schema.StructValidator) { func validateOIDCClients(config *schema.OpenIDConnectConfiguration, val *schema.StructValidator) {
invalidID, duplicateIDs := false, false var (
errDeprecated bool
var ids []string clientIDs, duplicateClientIDs, blankClientIDs []string
)
errDeprecatedFunc := func() { errDeprecated = true }
for c, client := range config.Clients { for c, client := range config.Clients {
if client.ID == "" { if client.ID == "" {
invalidID = true blankClientIDs = append(blankClientIDs, "#"+strconv.Itoa(c+1))
} else { } else {
if client.Description == "" { if client.Description == "" {
config.Clients[c].Description = client.ID config.Clients[c].Description = client.ID
} }
if utils.IsStringInSliceFold(client.ID, ids) { if id := strings.ToLower(client.ID); utils.IsStringInSlice(id, clientIDs) {
duplicateIDs = true if !utils.IsStringInSlice(id, duplicateClientIDs) {
} duplicateClientIDs = append(duplicateClientIDs, id)
ids = append(ids, client.ID)
}
if client.Public {
if client.Secret != nil {
val.Push(fmt.Errorf(errFmtOIDCClientPublicInvalidSecret, client.ID))
} }
} else { } else {
if client.Secret == nil { clientIDs = append(clientIDs, id)
val.Push(fmt.Errorf(errFmtOIDCClientInvalidSecret, client.ID))
} else if client.Secret.IsPlainText() {
val.PushWarning(fmt.Errorf(errFmtOIDCClientInvalidSecretPlainText, client.ID))
} }
} }
if client.Policy == "" { validateOIDCClient(c, config, val, errDeprecatedFunc)
}
if errDeprecated {
val.PushWarning(fmt.Errorf(errFmtOIDCClientsDeprecated))
}
if len(blankClientIDs) != 0 {
val.Push(fmt.Errorf(errFmtOIDCClientsWithEmptyID, buildJoinedString(", ", "or", "", blankClientIDs)))
}
if len(duplicateClientIDs) != 0 {
val.Push(fmt.Errorf(errFmtOIDCClientsDuplicateID, strJoinOr(duplicateClientIDs)))
}
}
func validateOIDCClient(c int, config *schema.OpenIDConnectConfiguration, val *schema.StructValidator, errDeprecatedFunc func()) {
if config.Clients[c].Public {
if config.Clients[c].Secret != nil {
val.Push(fmt.Errorf(errFmtOIDCClientPublicInvalidSecret, config.Clients[c].ID))
}
} else {
if config.Clients[c].Secret == nil {
val.Push(fmt.Errorf(errFmtOIDCClientInvalidSecret, config.Clients[c].ID))
} else if config.Clients[c].Secret.IsPlainText() {
val.PushWarning(fmt.Errorf(errFmtOIDCClientInvalidSecretPlainText, config.Clients[c].ID))
}
}
switch config.Clients[c].Policy {
case "":
config.Clients[c].Policy = schema.DefaultOpenIDConnectClientConfiguration.Policy config.Clients[c].Policy = schema.DefaultOpenIDConnectClientConfiguration.Policy
} else if client.Policy != policyOneFactor && client.Policy != policyTwoFactor { case policyOneFactor, policyTwoFactor:
val.Push(fmt.Errorf(errFmtOIDCClientInvalidPolicy, client.ID, client.Policy))
}
switch client.PKCEChallengeMethod {
case "", "plain", "S256":
break break
default: default:
val.Push(fmt.Errorf(errFmtOIDCClientInvalidPKCEChallengeMethod, client.ID, client.PKCEChallengeMethod)) val.Push(fmt.Errorf(errFmtOIDCClientInvalidValue, config.Clients[c].ID, "policy", strJoinOr([]string{policyOneFactor, policyTwoFactor}), config.Clients[c].Policy))
}
switch config.Clients[c].PKCEChallengeMethod {
case "", oidc.PKCEChallengeMethodPlain, oidc.PKCEChallengeMethodSHA256:
break
default:
val.Push(fmt.Errorf(errFmtOIDCClientInvalidValue, config.Clients[c].ID, attrOIDCPKCEChallengeMethod, strJoinOr([]string{oidc.PKCEChallengeMethodPlain, oidc.PKCEChallengeMethodSHA256}), config.Clients[c].PKCEChallengeMethod))
} }
validateOIDCClientConsentMode(c, config, val) validateOIDCClientConsentMode(c, config, val)
validateOIDCClientSectorIdentifier(client, val)
validateOIDCClientScopes(c, config, val) validateOIDCClientScopes(c, config, val, errDeprecatedFunc)
validateOIDCClientGrantTypes(c, config, val) validateOIDCClientResponseTypes(c, config, val, errDeprecatedFunc)
validateOIDCClientResponseTypes(c, config, val) validateOIDCClientResponseModes(c, config, val, errDeprecatedFunc)
validateOIDCClientResponseModes(c, config, val) validateOIDCClientGrantTypes(c, config, val, errDeprecatedFunc)
validateOIDCClientRedirectURIs(c, config, val, errDeprecatedFunc)
validateOIDCClientTokenEndpointAuthMethod(c, config, val)
validateOIDDClientUserinfoAlgorithm(c, config, val) validateOIDDClientUserinfoAlgorithm(c, config, val)
validateOIDCClientRedirectURIs(client, val)
validateOIDCClientSectorIdentifier(c, config, val)
} }
if invalidID { func validateOIDCClientSectorIdentifier(c int, config *schema.OpenIDConnectConfiguration, val *schema.StructValidator) {
val.Push(fmt.Errorf(errFmtOIDCClientsWithEmptyID)) if config.Clients[c].SectorIdentifier.String() != "" {
} if utils.IsURLHostComponent(config.Clients[c].SectorIdentifier) || utils.IsURLHostComponentWithPort(config.Clients[c].SectorIdentifier) {
if duplicateIDs {
val.Push(fmt.Errorf(errFmtOIDCClientsDuplicateID))
}
}
func validateOIDCClientSectorIdentifier(client schema.OpenIDConnectClientConfiguration, val *schema.StructValidator) {
if client.SectorIdentifier.String() != "" {
if utils.IsURLHostComponent(client.SectorIdentifier) || utils.IsURLHostComponentWithPort(client.SectorIdentifier) {
return return
} }
if client.SectorIdentifier.Scheme != "" { if config.Clients[c].SectorIdentifier.Scheme != "" {
val.Push(fmt.Errorf(errFmtOIDCClientInvalidSectorIdentifier, client.ID, client.SectorIdentifier.String(), client.SectorIdentifier.Host, "scheme", client.SectorIdentifier.Scheme)) val.Push(fmt.Errorf(errFmtOIDCClientInvalidSectorIdentifier, config.Clients[c].ID, config.Clients[c].SectorIdentifier.String(), config.Clients[c].SectorIdentifier.Host, "scheme", config.Clients[c].SectorIdentifier.Scheme))
if client.SectorIdentifier.Path != "" { if config.Clients[c].SectorIdentifier.Path != "" {
val.Push(fmt.Errorf(errFmtOIDCClientInvalidSectorIdentifier, client.ID, client.SectorIdentifier.String(), client.SectorIdentifier.Host, "path", client.SectorIdentifier.Path)) val.Push(fmt.Errorf(errFmtOIDCClientInvalidSectorIdentifier, config.Clients[c].ID, config.Clients[c].SectorIdentifier.String(), config.Clients[c].SectorIdentifier.Host, "path", config.Clients[c].SectorIdentifier.Path))
} }
if client.SectorIdentifier.RawQuery != "" { if config.Clients[c].SectorIdentifier.RawQuery != "" {
val.Push(fmt.Errorf(errFmtOIDCClientInvalidSectorIdentifier, client.ID, client.SectorIdentifier.String(), client.SectorIdentifier.Host, "query", client.SectorIdentifier.RawQuery)) val.Push(fmt.Errorf(errFmtOIDCClientInvalidSectorIdentifier, config.Clients[c].ID, config.Clients[c].SectorIdentifier.String(), config.Clients[c].SectorIdentifier.Host, "query", config.Clients[c].SectorIdentifier.RawQuery))
} }
if client.SectorIdentifier.Fragment != "" { if config.Clients[c].SectorIdentifier.Fragment != "" {
val.Push(fmt.Errorf(errFmtOIDCClientInvalidSectorIdentifier, client.ID, client.SectorIdentifier.String(), client.SectorIdentifier.Host, "fragment", client.SectorIdentifier.Fragment)) val.Push(fmt.Errorf(errFmtOIDCClientInvalidSectorIdentifier, config.Clients[c].ID, config.Clients[c].SectorIdentifier.String(), config.Clients[c].SectorIdentifier.Host, "fragment", config.Clients[c].SectorIdentifier.Fragment))
} }
if client.SectorIdentifier.User != nil { if config.Clients[c].SectorIdentifier.User != nil {
if client.SectorIdentifier.User.Username() != "" { if config.Clients[c].SectorIdentifier.User.Username() != "" {
val.Push(fmt.Errorf(errFmtOIDCClientInvalidSectorIdentifier, client.ID, client.SectorIdentifier.String(), client.SectorIdentifier.Host, "username", client.SectorIdentifier.User.Username())) val.Push(fmt.Errorf(errFmtOIDCClientInvalidSectorIdentifier, config.Clients[c].ID, config.Clients[c].SectorIdentifier.String(), config.Clients[c].SectorIdentifier.Host, "username", config.Clients[c].SectorIdentifier.User.Username()))
} }
if _, set := client.SectorIdentifier.User.Password(); set { if _, set := config.Clients[c].SectorIdentifier.User.Password(); set {
val.Push(fmt.Errorf(errFmtOIDCClientInvalidSectorIdentifierWithoutValue, client.ID, client.SectorIdentifier.String(), client.SectorIdentifier.Host, "password")) val.Push(fmt.Errorf(errFmtOIDCClientInvalidSectorIdentifierWithoutValue, config.Clients[c].ID, config.Clients[c].SectorIdentifier.String(), config.Clients[c].SectorIdentifier.Host, "password"))
} }
} }
} else if client.SectorIdentifier.Host == "" { } else if config.Clients[c].SectorIdentifier.Host == "" {
val.Push(fmt.Errorf(errFmtOIDCClientInvalidSectorIdentifierHost, client.ID, client.SectorIdentifier.String())) val.Push(fmt.Errorf(errFmtOIDCClientInvalidSectorIdentifierHost, config.Clients[c].ID, config.Clients[c].SectorIdentifier.String()))
} }
} }
} }
func validateOIDCClientConsentMode(c int, config *schema.OpenIDConnectConfiguration, val *schema.StructValidator) { func validateOIDCClientConsentMode(c int, config *schema.OpenIDConnectConfiguration, val *schema.StructValidator) {
switch { switch {
case utils.IsStringInSlice(config.Clients[c].ConsentMode, []string{"", "auto"}): case utils.IsStringInSlice(config.Clients[c].ConsentMode, []string{"", auto}):
if config.Clients[c].ConsentPreConfiguredDuration != nil { if config.Clients[c].ConsentPreConfiguredDuration != nil {
config.Clients[c].ConsentMode = oidc.ClientConsentModePreConfigured.String() config.Clients[c].ConsentMode = oidc.ClientConsentModePreConfigured.String()
} else { } else {
@ -252,7 +275,7 @@ func validateOIDCClientConsentMode(c int, config *schema.OpenIDConnectConfigurat
case utils.IsStringInSlice(config.Clients[c].ConsentMode, validOIDCClientConsentModes): case utils.IsStringInSlice(config.Clients[c].ConsentMode, validOIDCClientConsentModes):
break break
default: default:
val.Push(fmt.Errorf(errFmtOIDCClientInvalidConsentMode, config.Clients[c].ID, strings.Join(append(validOIDCClientConsentModes, "auto"), "', '"), config.Clients[c].ConsentMode)) val.Push(fmt.Errorf(errFmtOIDCClientInvalidConsentMode, config.Clients[c].ID, strJoinOr(append(validOIDCClientConsentModes, auto)), config.Clients[c].ConsentMode))
} }
if config.Clients[c].ConsentMode == oidc.ClientConsentModePreConfigured.String() && config.Clients[c].ConsentPreConfiguredDuration == nil { if config.Clients[c].ConsentMode == oidc.ClientConsentModePreConfigured.String() && config.Clients[c].ConsentPreConfiguredDuration == nil {
@ -260,92 +283,233 @@ func validateOIDCClientConsentMode(c int, config *schema.OpenIDConnectConfigurat
} }
} }
func validateOIDCClientScopes(c int, config *schema.OpenIDConnectConfiguration, val *schema.StructValidator) { func validateOIDCClientScopes(c int, config *schema.OpenIDConnectConfiguration, val *schema.StructValidator, errDeprecatedFunc func()) {
if len(config.Clients[c].Scopes) == 0 { if len(config.Clients[c].Scopes) == 0 {
config.Clients[c].Scopes = schema.DefaultOpenIDConnectClientConfiguration.Scopes config.Clients[c].Scopes = schema.DefaultOpenIDConnectClientConfiguration.Scopes
return
} }
if !utils.IsStringInSlice(oidc.ScopeOpenID, config.Clients[c].Scopes) { if !utils.IsStringInSlice(oidc.ScopeOpenID, config.Clients[c].Scopes) {
config.Clients[c].Scopes = append(config.Clients[c].Scopes, oidc.ScopeOpenID) config.Clients[c].Scopes = append([]string{oidc.ScopeOpenID}, config.Clients[c].Scopes...)
} }
for _, scope := range config.Clients[c].Scopes { invalid, duplicates := validateList(config.Clients[c].Scopes, validOIDCClientScopes, true)
if !utils.IsStringInSlice(scope, validOIDCScopes) {
val.Push(fmt.Errorf( if len(invalid) != 0 {
errFmtOIDCClientInvalidEntry, val.Push(fmt.Errorf(errFmtOIDCClientInvalidEntries, config.Clients[c].ID, attrOIDCScopes, strJoinOr(validOIDCClientScopes), strJoinAnd(invalid)))
config.Clients[c].ID, "scopes", strings.Join(validOIDCScopes, "', '"), scope))
} }
if len(duplicates) != 0 {
errDeprecatedFunc()
val.PushWarning(fmt.Errorf(errFmtOIDCClientInvalidEntryDuplicates, config.Clients[c].ID, attrOIDCScopes, strJoinAnd(duplicates)))
}
if utils.IsStringSliceContainsAny([]string{oidc.ScopeOfflineAccess, oidc.ScopeOffline}, config.Clients[c].Scopes) &&
!utils.IsStringSliceContainsAny(validOIDCClientResponseTypesRefreshToken, config.Clients[c].ResponseTypes) {
errDeprecatedFunc()
val.PushWarning(fmt.Errorf(errFmtOIDCClientInvalidRefreshTokenOptionWithoutCodeResponseType,
config.Clients[c].ID, attrOIDCScopes,
strJoinOr([]string{oidc.ScopeOfflineAccess, oidc.ScopeOffline}),
strJoinOr(validOIDCClientResponseTypesRefreshToken)),
)
} }
} }
func validateOIDCClientGrantTypes(c int, config *schema.OpenIDConnectConfiguration, val *schema.StructValidator) { func validateOIDCClientResponseTypes(c int, config *schema.OpenIDConnectConfiguration, val *schema.StructValidator, errDeprecatedFunc func()) {
if len(config.Clients[c].GrantTypes) == 0 {
config.Clients[c].GrantTypes = schema.DefaultOpenIDConnectClientConfiguration.GrantTypes
return
}
for _, grantType := range config.Clients[c].GrantTypes {
if !utils.IsStringInSlice(grantType, validOIDCGrantTypes) {
val.Push(fmt.Errorf(
errFmtOIDCClientInvalidEntry,
config.Clients[c].ID, "grant_types", strings.Join(validOIDCGrantTypes, "', '"), grantType))
}
}
}
func validateOIDCClientResponseTypes(c int, config *schema.OpenIDConnectConfiguration, _ *schema.StructValidator) {
if len(config.Clients[c].ResponseTypes) == 0 { if len(config.Clients[c].ResponseTypes) == 0 {
config.Clients[c].ResponseTypes = schema.DefaultOpenIDConnectClientConfiguration.ResponseTypes config.Clients[c].ResponseTypes = schema.DefaultOpenIDConnectClientConfiguration.ResponseTypes
return }
invalid, duplicates := validateList(config.Clients[c].ResponseTypes, validOIDCClientResponseTypes, true)
if len(invalid) != 0 {
val.PushWarning(fmt.Errorf(errFmtOIDCClientInvalidEntries, config.Clients[c].ID, attrOIDCResponseTypes, strJoinOr(validOIDCClientResponseTypes), strJoinAnd(invalid)))
}
if len(duplicates) != 0 {
errDeprecatedFunc()
val.PushWarning(fmt.Errorf(errFmtOIDCClientInvalidEntryDuplicates, config.Clients[c].ID, attrOIDCResponseTypes, strJoinAnd(duplicates)))
} }
} }
func validateOIDCClientResponseModes(c int, config *schema.OpenIDConnectConfiguration, validator *schema.StructValidator) { func validateOIDCClientResponseModes(c int, config *schema.OpenIDConnectConfiguration, val *schema.StructValidator, errDeprecatedFunc func()) {
if len(config.Clients[c].ResponseModes) == 0 { if len(config.Clients[c].ResponseModes) == 0 {
config.Clients[c].ResponseModes = schema.DefaultOpenIDConnectClientConfiguration.ResponseModes config.Clients[c].ResponseModes = schema.DefaultOpenIDConnectClientConfiguration.ResponseModes
return
for _, responseType := range config.Clients[c].ResponseTypes {
switch responseType {
case oidc.ResponseTypeAuthorizationCodeFlow:
if !utils.IsStringInSlice(oidc.ResponseModeQuery, config.Clients[c].ResponseModes) {
config.Clients[c].ResponseModes = append(config.Clients[c].ResponseModes, oidc.ResponseModeQuery)
}
case oidc.ResponseTypeImplicitFlowIDToken, oidc.ResponseTypeImplicitFlowToken, oidc.ResponseTypeImplicitFlowBoth,
oidc.ResponseTypeHybridFlowIDToken, oidc.ResponseTypeHybridFlowToken, oidc.ResponseTypeHybridFlowBoth:
if !utils.IsStringInSlice(oidc.ResponseModeFragment, config.Clients[c].ResponseModes) {
config.Clients[c].ResponseModes = append(config.Clients[c].ResponseModes, oidc.ResponseModeFragment)
}
}
}
} }
for _, responseMode := range config.Clients[c].ResponseModes { invalid, duplicates := validateList(config.Clients[c].ResponseModes, validOIDCClientResponseModes, true)
if !utils.IsStringInSlice(responseMode, validOIDCResponseModes) {
validator.Push(fmt.Errorf( if len(invalid) != 0 {
errFmtOIDCClientInvalidEntry, val.Push(fmt.Errorf(errFmtOIDCClientInvalidEntries, config.Clients[c].ID, attrOIDCResponseModes, strJoinOr(validOIDCClientResponseModes), strJoinAnd(invalid)))
config.Clients[c].ID, "response_modes", strings.Join(validOIDCResponseModes, "', '"), responseMode))
} }
if len(duplicates) != 0 {
errDeprecatedFunc()
val.PushWarning(fmt.Errorf(errFmtOIDCClientInvalidEntryDuplicates, config.Clients[c].ID, attrOIDCResponseModes, strJoinAnd(duplicates)))
}
}
func validateOIDCClientGrantTypes(c int, config *schema.OpenIDConnectConfiguration, val *schema.StructValidator, errDeprecatedFunc func()) {
if len(config.Clients[c].GrantTypes) == 0 {
validateOIDCClientGrantTypesSetDefaults(c, config)
}
validateOIDCClientGrantTypesCheckRelated(c, config, val, errDeprecatedFunc)
invalid, duplicates := validateList(config.Clients[c].GrantTypes, validOIDCClientGrantTypes, true)
if len(invalid) != 0 {
val.Push(fmt.Errorf(errFmtOIDCClientInvalidEntries, config.Clients[c].ID, attrOIDCGrantTypes, strJoinOr(validOIDCClientGrantTypes), strJoinAnd(invalid)))
}
if len(duplicates) != 0 {
errDeprecatedFunc()
val.PushWarning(fmt.Errorf(errFmtOIDCClientInvalidEntryDuplicates, config.Clients[c].ID, attrOIDCGrantTypes, strJoinAnd(duplicates)))
}
}
func validateOIDCClientGrantTypesSetDefaults(c int, config *schema.OpenIDConnectConfiguration) {
for _, responseType := range config.Clients[c].ResponseTypes {
switch responseType {
case oidc.ResponseTypeAuthorizationCodeFlow:
if !utils.IsStringInSlice(oidc.GrantTypeAuthorizationCode, config.Clients[c].GrantTypes) {
config.Clients[c].GrantTypes = append(config.Clients[c].GrantTypes, oidc.GrantTypeAuthorizationCode)
}
case oidc.ResponseTypeImplicitFlowIDToken, oidc.ResponseTypeImplicitFlowToken, oidc.ResponseTypeImplicitFlowBoth:
if !utils.IsStringInSlice(oidc.GrantTypeImplicit, config.Clients[c].GrantTypes) {
config.Clients[c].GrantTypes = append(config.Clients[c].GrantTypes, oidc.GrantTypeImplicit)
}
case oidc.ResponseTypeHybridFlowIDToken, oidc.ResponseTypeHybridFlowToken, oidc.ResponseTypeHybridFlowBoth:
if !utils.IsStringInSlice(oidc.GrantTypeAuthorizationCode, config.Clients[c].GrantTypes) {
config.Clients[c].GrantTypes = append(config.Clients[c].GrantTypes, oidc.GrantTypeAuthorizationCode)
}
if !utils.IsStringInSlice(oidc.GrantTypeImplicit, config.Clients[c].GrantTypes) {
config.Clients[c].GrantTypes = append(config.Clients[c].GrantTypes, oidc.GrantTypeImplicit)
}
}
}
}
func validateOIDCClientGrantTypesCheckRelated(c int, config *schema.OpenIDConnectConfiguration, val *schema.StructValidator, errDeprecatedFunc func()) {
for _, grantType := range config.Clients[c].GrantTypes {
switch grantType {
case oidc.GrantTypeImplicit:
if !utils.IsStringSliceContainsAny(validOIDCClientResponseTypesImplicitFlow, config.Clients[c].ResponseTypes) && !utils.IsStringSliceContainsAny(validOIDCClientResponseTypesHybridFlow, config.Clients[c].ResponseTypes) {
errDeprecatedFunc()
val.PushWarning(fmt.Errorf(errFmtOIDCClientInvalidGrantTypeMatch, config.Clients[c].ID, grantType, "for either the implicit or hybrid flow", strJoinOr(append(append([]string{}, validOIDCClientResponseTypesImplicitFlow...), validOIDCClientResponseTypesHybridFlow...)), strJoinAnd(config.Clients[c].ResponseTypes)))
}
case oidc.GrantTypeAuthorizationCode:
if !utils.IsStringInSlice(oidc.ResponseTypeAuthorizationCodeFlow, config.Clients[c].ResponseTypes) && !utils.IsStringSliceContainsAny(validOIDCClientResponseTypesHybridFlow, config.Clients[c].ResponseTypes) {
errDeprecatedFunc()
val.PushWarning(fmt.Errorf(errFmtOIDCClientInvalidGrantTypeMatch, config.Clients[c].ID, grantType, "for either the authorization code or hybrid flow", strJoinOr(append([]string{oidc.ResponseTypeAuthorizationCodeFlow}, validOIDCClientResponseTypesHybridFlow...)), strJoinAnd(config.Clients[c].ResponseTypes)))
}
case oidc.GrantTypeRefreshToken:
if !utils.IsStringSliceContainsAny([]string{oidc.ScopeOfflineAccess, oidc.ScopeOffline}, config.Clients[c].Scopes) {
errDeprecatedFunc()
val.PushWarning(fmt.Errorf(errFmtOIDCClientInvalidGrantTypeRefresh, config.Clients[c].ID))
}
if !utils.IsStringSliceContainsAny(validOIDCClientResponseTypesRefreshToken, config.Clients[c].ResponseTypes) {
errDeprecatedFunc()
val.PushWarning(fmt.Errorf(errFmtOIDCClientInvalidRefreshTokenOptionWithoutCodeResponseType,
config.Clients[c].ID, attrOIDCGrantTypes,
strJoinOr([]string{oidc.GrantTypeRefreshToken}),
strJoinOr(validOIDCClientResponseTypesRefreshToken)),
)
}
}
}
}
func validateOIDCClientRedirectURIs(c int, config *schema.OpenIDConnectConfiguration, val *schema.StructValidator, errDeprecatedFunc func()) {
var (
parsedRedirectURI *url.URL
err error
)
for _, redirectURI := range config.Clients[c].RedirectURIs {
if redirectURI == oauth2InstalledApp {
if config.Clients[c].Public {
continue
}
val.Push(fmt.Errorf(errFmtOIDCClientRedirectURIPublic, config.Clients[c].ID, oauth2InstalledApp))
continue
}
if parsedRedirectURI, err = url.Parse(redirectURI); err != nil {
val.Push(fmt.Errorf(errFmtOIDCClientRedirectURICantBeParsed, config.Clients[c].ID, redirectURI, err))
continue
}
if !parsedRedirectURI.IsAbs() || (!config.Clients[c].Public && parsedRedirectURI.Scheme == "") {
val.Push(fmt.Errorf(errFmtOIDCClientRedirectURIAbsolute, config.Clients[c].ID, redirectURI))
return
}
}
_, duplicates := validateList(config.Clients[c].RedirectURIs, nil, true)
if len(duplicates) != 0 {
errDeprecatedFunc()
val.PushWarning(fmt.Errorf(errFmtOIDCClientInvalidEntryDuplicates, config.Clients[c].ID, attrOIDCRedirectURIs, strJoinAnd(duplicates)))
}
}
func validateOIDCClientTokenEndpointAuthMethod(c int, config *schema.OpenIDConnectConfiguration, val *schema.StructValidator) {
implcit := len(config.Clients[c].ResponseTypes) != 0 && utils.IsStringSliceContainsAll(config.Clients[c].ResponseTypes, validOIDCClientResponseTypesImplicitFlow)
if config.Clients[c].TokenEndpointAuthMethod == "" && (config.Clients[c].Public || implcit) {
config.Clients[c].TokenEndpointAuthMethod = oidc.ClientAuthMethodNone
}
switch {
case config.Clients[c].TokenEndpointAuthMethod == "":
break
case !utils.IsStringInSlice(config.Clients[c].TokenEndpointAuthMethod, validOIDCClientTokenEndpointAuthMethods):
val.Push(fmt.Errorf(errFmtOIDCClientInvalidValue,
config.Clients[c].ID, attrOIDCTokenAuthMethod, strJoinOr(validOIDCClientTokenEndpointAuthMethods), config.Clients[c].TokenEndpointAuthMethod))
case config.Clients[c].TokenEndpointAuthMethod == oidc.ClientAuthMethodNone && !config.Clients[c].Public && !implcit:
val.Push(fmt.Errorf(errFmtOIDCClientInvalidTokenEndpointAuthMethod,
config.Clients[c].ID, strJoinOr(validOIDCClientTokenEndpointAuthMethodsConfidential), strJoinAnd(validOIDCClientResponseTypesImplicitFlow), config.Clients[c].TokenEndpointAuthMethod))
case config.Clients[c].TokenEndpointAuthMethod != oidc.ClientAuthMethodNone && config.Clients[c].Public:
val.Push(fmt.Errorf(errFmtOIDCClientInvalidTokenEndpointAuthMethodPublic,
config.Clients[c].ID, config.Clients[c].TokenEndpointAuthMethod))
} }
} }
func validateOIDDClientUserinfoAlgorithm(c int, config *schema.OpenIDConnectConfiguration, val *schema.StructValidator) { func validateOIDDClientUserinfoAlgorithm(c int, config *schema.OpenIDConnectConfiguration, val *schema.StructValidator) {
if config.Clients[c].UserinfoSigningAlgorithm == "" { if config.Clients[c].UserinfoSigningAlgorithm == "" {
config.Clients[c].UserinfoSigningAlgorithm = schema.DefaultOpenIDConnectClientConfiguration.UserinfoSigningAlgorithm config.Clients[c].UserinfoSigningAlgorithm = schema.DefaultOpenIDConnectClientConfiguration.UserinfoSigningAlgorithm
} else if !utils.IsStringInSlice(config.Clients[c].UserinfoSigningAlgorithm, validOIDCUserinfoAlgorithms) {
val.Push(fmt.Errorf(errFmtOIDCClientInvalidUserinfoAlgorithm,
config.Clients[c].ID, strings.Join(validOIDCUserinfoAlgorithms, ", "), config.Clients[c].UserinfoSigningAlgorithm))
}
} }
func validateOIDCClientRedirectURIs(client schema.OpenIDConnectClientConfiguration, val *schema.StructValidator) { if !utils.IsStringInSlice(config.Clients[c].UserinfoSigningAlgorithm, validOIDCClientUserinfoAlgorithms) {
for _, redirectURI := range client.RedirectURIs { val.Push(fmt.Errorf(errFmtOIDCClientInvalidValue,
if redirectURI == oauth2InstalledApp { config.Clients[c].ID, attrOIDCUsrSigAlg, strJoinOr(validOIDCClientUserinfoAlgorithms), config.Clients[c].UserinfoSigningAlgorithm))
if client.Public {
continue
}
val.Push(fmt.Errorf(errFmtOIDCClientRedirectURIPublic, client.ID, oauth2InstalledApp))
continue
}
parsedURL, err := url.Parse(redirectURI)
if err != nil {
val.Push(fmt.Errorf(errFmtOIDCClientRedirectURICantBeParsed, client.ID, redirectURI, err))
continue
}
if !parsedURL.IsAbs() || (!client.Public && parsedURL.Scheme == "") {
val.Push(fmt.Errorf(errFmtOIDCClientRedirectURIAbsolute, client.ID, redirectURI))
return
}
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -100,7 +100,7 @@ func NewKeyMapPattern(key string) (pattern *regexp.Regexp, err error) {
} }
if i < n { if i < n {
buf.WriteString("\\.[a-z0-9]([a-z0-9-_]+)?[a-z0-9]") buf.WriteString("\\.[a-z0-9](([a-z0-9-_]+)?[a-z0-9])?")
} }
} }

View File

@ -101,6 +101,20 @@ func TestSpecificErrorKeys(t *testing.T) {
assert.EqualError(t, errs[4], specificErrorKeys["authentication_backend.file.hashing.algorithm"]) assert.EqualError(t, errs[4], specificErrorKeys["authentication_backend.file.hashing.algorithm"])
} }
func TestPatternKeys(t *testing.T) {
configKeys := []string{
"server.endpoints.authz.xx.implementation",
"server.endpoints.authz.x.implementation",
}
val := schema.NewStructValidator()
ValidateKeys(configKeys, "AUTHELIA_", val)
errs := val.Errors()
require.Len(t, errs, 0)
}
func TestReplacedErrors(t *testing.T) { func TestReplacedErrors(t *testing.T) {
configKeys := []string{ configKeys := []string{
"authentication_backend.ldap.skip_verify", "authentication_backend.ldap.skip_verify",

View File

@ -2,7 +2,6 @@ package validator
import ( import (
"fmt" "fmt"
"strings"
"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"
@ -19,6 +18,6 @@ func ValidateLog(config *schema.Configuration, validator *schema.StructValidator
} }
if !utils.IsStringInSlice(config.Log.Level, validLogLevels) { if !utils.IsStringInSlice(config.Log.Level, validLogLevels) {
validator.Push(fmt.Errorf(errFmtLoggingLevelInvalid, strings.Join(validLogLevels, "', '"), config.Log.Level)) validator.Push(fmt.Errorf(errFmtLoggingLevelInvalid, strJoinOr(validLogLevels), config.Log.Level))
} }
} }

View File

@ -40,5 +40,5 @@ func TestShouldRaiseErrorOnInvalidLoggingLevel(t *testing.T) {
assert.Len(t, validator.Warnings(), 0) assert.Len(t, validator.Warnings(), 0)
require.Len(t, validator.Errors(), 1) require.Len(t, validator.Errors(), 1)
assert.EqualError(t, validator.Errors()[0], "log: option 'level' must be one of 'trace', 'debug', 'info', 'warn', 'error' but it is configured as 'TRACE'") assert.EqualError(t, validator.Errors()[0], "log: option 'level' must be one of 'trace', 'debug', 'info', 'warn', or 'error' but it's configured as 'TRACE'")
} }

View File

@ -4,8 +4,10 @@ import (
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"net/mail" "net/mail"
"path/filepath"
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"github.com/authelia/authelia/v4/internal/configuration/schema" "github.com/authelia/authelia/v4/internal/configuration/schema"
@ -187,6 +189,32 @@ func (suite *NotifierSuite) TestSMTPShouldEnsureSenderIsProvided() {
suite.Assert().EqualError(suite.validator.Errors()[0], fmt.Sprintf(errFmtNotifierSMTPNotConfigured, "sender")) suite.Assert().EqualError(suite.validator.Errors()[0], fmt.Sprintf(errFmtNotifierSMTPNotConfigured, "sender"))
} }
func (suite *NotifierSuite) TestTemplatesEmptyDir() {
dir := suite.T().TempDir()
suite.config.TemplatePath = dir
ValidateNotifier(&suite.config, suite.validator)
suite.Assert().Len(suite.validator.Warnings(), 0)
suite.Assert().Len(suite.validator.Errors(), 0)
}
func (suite *NotifierSuite) TestTemplatesEmptyDirNoExist() {
dir := suite.T().TempDir()
p := filepath.Join(dir, "notexist")
suite.config.TemplatePath = p
ValidateNotifier(&suite.config, suite.validator)
suite.Assert().Len(suite.validator.Warnings(), 0)
suite.Assert().Len(suite.validator.Errors(), 1)
assert.EqualError(suite.T(), suite.validator.Errors()[0], fmt.Sprintf("notifier: option 'template_path' refers to location '%s' which does not exist", p))
}
/* /*
File Tests. File Tests.
*/ */

View File

@ -49,5 +49,5 @@ func TestShouldRaiseErrorOnInvalidNTPVersion(t *testing.T) {
require.Len(t, validator.Errors(), 1) require.Len(t, validator.Errors(), 1)
assert.EqualError(t, validator.Errors()[0], "ntp: option 'version' must be either 3 or 4 but it is configured as '1'") assert.EqualError(t, validator.Errors()[0], "ntp: option 'version' must be either 3 or 4 but it's configured as '1'")
} }

View File

@ -39,7 +39,7 @@ func TestValidatePasswordPolicy(t *testing.T) {
}, },
expectedErrs: []string{ expectedErrs: []string{
"password_policy: only a single password policy mechanism can be specified", "password_policy: only a single password policy mechanism can be specified",
"password_policy: standard: option 'min_length' must be greater than 0 but is configured as -1", "password_policy: standard: option 'min_length' must be greater than 0 but it's configured as -1",
}, },
}, },
{ {

View File

@ -155,13 +155,13 @@ func validateServerEndpointsAuthzEndpoint(config *schema.Configuration, name str
config.Server.Endpoints.Authz[name] = endpoint config.Server.Endpoints.Authz[name] = endpoint
default: default:
if !utils.IsStringInSlice(endpoint.Implementation, validAuthzImplementations) { if !utils.IsStringInSlice(endpoint.Implementation, validAuthzImplementations) {
validator.Push(fmt.Errorf(errFmtServerEndpointsAuthzImplementation, name, strings.Join(validAuthzImplementations, "', '"), endpoint.Implementation)) validator.Push(fmt.Errorf(errFmtServerEndpointsAuthzImplementation, name, strJoinOr(validAuthzImplementations), endpoint.Implementation))
} else { } else {
validator.Push(fmt.Errorf(errFmtServerEndpointsAuthzLegacyInvalidImplementation, name)) validator.Push(fmt.Errorf(errFmtServerEndpointsAuthzLegacyInvalidImplementation, name))
} }
} }
} else if !utils.IsStringInSlice(endpoint.Implementation, validAuthzImplementations) { } else if !utils.IsStringInSlice(endpoint.Implementation, validAuthzImplementations) {
validator.Push(fmt.Errorf(errFmtServerEndpointsAuthzImplementation, name, strings.Join(validAuthzImplementations, "', '"), endpoint.Implementation)) validator.Push(fmt.Errorf(errFmtServerEndpointsAuthzImplementation, name, strJoinOr(validAuthzImplementations), endpoint.Implementation))
} }
if !reAuthzEndpointName.MatchString(name) { if !reAuthzEndpointName.MatchString(name) {
@ -180,7 +180,7 @@ func validateServerEndpointsAuthzStrategies(name string, strategies []schema.Ser
names = append(names, strategy.Name) names = append(names, strategy.Name)
if !utils.IsStringInSlice(strategy.Name, validAuthzAuthnStrategies) { if !utils.IsStringInSlice(strategy.Name, validAuthzAuthnStrategies) {
validator.Push(fmt.Errorf(errFmtServerEndpointsAuthzStrategy, name, strings.Join(validAuthzAuthnStrategies, "', '"), strategy.Name)) validator.Push(fmt.Errorf(errFmtServerEndpointsAuthzStrategy, name, strJoinOr(validAuthzAuthnStrategies), strategy.Name))
} }
} }
} }

View File

@ -314,14 +314,18 @@ func TestServerAuthzEndpointErrors(t *testing.T) {
map[string]schema.ServerAuthzEndpoint{ map[string]schema.ServerAuthzEndpoint{
"example": {Implementation: "zero"}, "example": {Implementation: "zero"},
}, },
[]string{"server: endpoints: authz: example: option 'implementation' must be one of 'AuthRequest', 'ForwardAuth', 'ExtAuthz', 'Legacy' but is configured as 'zero'"}, []string{
"server: endpoints: authz: example: option 'implementation' must be one of 'AuthRequest', 'ForwardAuth', 'ExtAuthz', or 'Legacy' but it's configured as 'zero'",
},
}, },
{ {
"ShouldErrorOnInvalidEndpointImplementationLegacy", "ShouldErrorOnInvalidEndpointImplementationLegacy",
map[string]schema.ServerAuthzEndpoint{ map[string]schema.ServerAuthzEndpoint{
"legacy": {Implementation: "zero"}, "legacy": {Implementation: "zero"},
}, },
[]string{"server: endpoints: authz: legacy: option 'implementation' must be one of 'AuthRequest', 'ForwardAuth', 'ExtAuthz', 'Legacy' but is configured as 'zero'"}, []string{
"server: endpoints: authz: legacy: option 'implementation' must be one of 'AuthRequest', 'ForwardAuth', 'ExtAuthz', or 'Legacy' but it's configured as 'zero'",
},
}, },
{ {
"ShouldErrorOnInvalidEndpointLegacyImplementation", "ShouldErrorOnInvalidEndpointLegacyImplementation",
@ -335,7 +339,9 @@ func TestServerAuthzEndpointErrors(t *testing.T) {
map[string]schema.ServerAuthzEndpoint{ map[string]schema.ServerAuthzEndpoint{
"example": {Implementation: "ExtAuthz", AuthnStrategies: []schema.ServerAuthzEndpointAuthnStrategy{{Name: "bad-name"}}}, "example": {Implementation: "ExtAuthz", AuthnStrategies: []schema.ServerAuthzEndpointAuthnStrategy{{Name: "bad-name"}}},
}, },
[]string{"server: endpoints: authz: example: authn_strategies: option 'name' must be one of 'CookieSession', 'HeaderAuthorization', 'HeaderProxyAuthorization', 'HeaderAuthRequestProxyAuthorization', 'HeaderLegacy' but is configured as 'bad-name'"}, []string{
"server: endpoints: authz: example: authn_strategies: option 'name' must be one of 'CookieSession', 'HeaderAuthorization', 'HeaderProxyAuthorization', 'HeaderAuthRequestProxyAuthorization', or 'HeaderLegacy' but it's configured as 'bad-name'",
},
}, },
{ {
"ShouldErrorOnDuplicateName", "ShouldErrorOnDuplicateName",

View File

@ -45,7 +45,7 @@ func validateSession(config *schema.SessionConfiguration, validator *schema.Stru
if config.SameSite == "" { if config.SameSite == "" {
config.SameSite = schema.DefaultSessionConfiguration.SameSite config.SameSite = schema.DefaultSessionConfiguration.SameSite
} else if !utils.IsStringInSlice(config.SameSite, validSessionSameSiteValues) { } else if !utils.IsStringInSlice(config.SameSite, validSessionSameSiteValues) {
validator.Push(fmt.Errorf(errFmtSessionSameSite, strings.Join(validSessionSameSiteValues, "', '"), config.SameSite)) validator.Push(fmt.Errorf(errFmtSessionSameSite, strJoinOr(validSessionSameSiteValues), config.SameSite))
} }
cookies := len(config.Cookies) cookies := len(config.Cookies)
@ -73,7 +73,7 @@ func validateSession(config *schema.SessionConfiguration, validator *schema.Stru
func validateSessionCookieDomains(config *schema.SessionConfiguration, validator *schema.StructValidator) { func validateSessionCookieDomains(config *schema.SessionConfiguration, validator *schema.StructValidator) {
if len(config.Cookies) == 0 { if len(config.Cookies) == 0 {
validator.Push(fmt.Errorf(errFmtSessionOptionRequired, "domain")) validator.Push(fmt.Errorf(errFmtSessionOptionRequired, "cookies"))
} }
domains := make([]string, 0) domains := make([]string, 0)
@ -182,7 +182,7 @@ func validateSessionSameSite(i int, config *schema.SessionConfiguration, validat
config.Cookies[i].SameSite = schema.DefaultSessionConfiguration.SameSite config.Cookies[i].SameSite = schema.DefaultSessionConfiguration.SameSite
} }
} else if !utils.IsStringInSlice(config.Cookies[i].SameSite, validSessionSameSiteValues) { } else if !utils.IsStringInSlice(config.Cookies[i].SameSite, validSessionSameSiteValues) {
validator.Push(fmt.Errorf(errFmtSessionDomainSameSite, sessionDomainDescriptor(i, config.Cookies[i]), strings.Join(validSessionSameSiteValues, "', '"), config.Cookies[i].SameSite)) validator.Push(fmt.Errorf(errFmtSessionDomainSameSite, sessionDomainDescriptor(i, config.Cookies[i]), strJoinOr(validSessionSameSiteValues), config.Cookies[i].SameSite))
} }
} }

View File

@ -95,7 +95,7 @@ func TestShouldSetDefaultSessionDomainsValues(t *testing.T) {
}, },
}, },
[]string{ []string{
"session: option 'same_site' must be one of 'none', 'lax', 'strict' but is configured as 'BAD VALUE'", "session: option 'same_site' must be one of 'none', 'lax', or 'strict' but it's configured as 'BAD VALUE'",
}, },
}, },
{ {
@ -140,6 +140,24 @@ func TestShouldSetDefaultSessionDomainsValues(t *testing.T) {
}, },
nil, nil,
}, },
{
"ShouldErrorOnEmptyConfig",
schema.SessionConfiguration{
SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
Name: "", SameSite: "", Domain: "",
},
Cookies: []schema.SessionCookieConfiguration{},
},
schema.SessionConfiguration{
SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
Name: "authelia_session", SameSite: "lax", Expiration: time.Hour, Inactivity: time.Minute * 5, RememberMe: time.Hour * 24 * 30,
},
Cookies: []schema.SessionCookieConfiguration{},
},
[]string{
"session: option 'cookies' is required",
},
},
} }
validator := schema.NewStructValidator() validator := schema.NewStructValidator()
@ -302,7 +320,7 @@ func TestShouldRaiseErrorWhenRedisHasHostnameButNoPort(t *testing.T) {
assert.False(t, validator.HasWarnings()) assert.False(t, validator.HasWarnings())
assert.Len(t, validator.Errors(), 1) assert.Len(t, validator.Errors(), 1)
assert.EqualError(t, validator.Errors()[0], "session: redis: option 'port' must be between 1 and 65535 but is configured as '0'") assert.EqualError(t, validator.Errors()[0], "session: redis: option 'port' must be between 1 and 65535 but it's configured as '0'")
} }
func TestShouldRaiseOneErrorWhenRedisHighAvailabilityHasNodesWithNoHost(t *testing.T) { func TestShouldRaiseOneErrorWhenRedisHighAvailabilityHasNodesWithNoHost(t *testing.T) {
@ -646,7 +664,7 @@ func TestShouldRaiseErrorWhenDomainIsInvalid(t *testing.T) {
{"ShouldRaiseErrorOnPublicDomainDuckDNS", "duckdns.org", nil, []string{"session: domain config #1 (domain 'duckdns.org'): option 'domain' is not a valid cookie domain: the domain is part of the special public suffix list"}}, {"ShouldRaiseErrorOnPublicDomainDuckDNS", "duckdns.org", nil, []string{"session: domain config #1 (domain 'duckdns.org'): option 'domain' is not a valid cookie domain: the domain is part of the special public suffix list"}},
{"ShouldNotRaiseErrorOnSuffixOfPublicDomainDuckDNS", "example.duckdns.org", nil, nil}, {"ShouldNotRaiseErrorOnSuffixOfPublicDomainDuckDNS", "example.duckdns.org", nil, nil},
{"ShouldRaiseWarningOnDomainWithLeadingDot", ".example.com", []string{"session: domain config #1 (domain '.example.com'): option 'domain' has a prefix of '.' which is not supported or intended behaviour: you can use this at your own risk but we recommend removing it"}, nil}, {"ShouldRaiseWarningOnDomainWithLeadingDot", ".example.com", []string{"session: domain config #1 (domain '.example.com'): option 'domain' has a prefix of '.' which is not supported or intended behaviour: you can use this at your own risk but we recommend removing it"}, nil},
{"ShouldRaiseErrorOnDomainWithLeadingStarDot", "*.example.com", nil, []string{"session: domain config #1 (domain '*.example.com'): option 'domain' must be the domain you wish to protect not a wildcard domain but it is configured as '*.example.com'"}}, {"ShouldRaiseErrorOnDomainWithLeadingStarDot", "*.example.com", nil, []string{"session: domain config #1 (domain '*.example.com'): option 'domain' must be the domain you wish to protect not a wildcard domain but it's configured as '*.example.com'"}},
{"ShouldRaiseErrorOnDomainNotSet", "", nil, []string{"session: domain config #1 (domain ''): option 'domain' is required"}}, {"ShouldRaiseErrorOnDomainNotSet", "", nil, []string{"session: domain config #1 (domain ''): option 'domain' is required"}},
} }
@ -726,8 +744,8 @@ func TestShouldRaiseErrorWhenSameSiteSetIncorrectly(t *testing.T) {
assert.False(t, validator.HasWarnings()) assert.False(t, validator.HasWarnings())
require.Len(t, validator.Errors(), 2) require.Len(t, validator.Errors(), 2)
assert.EqualError(t, validator.Errors()[0], "session: option 'same_site' must be one of 'none', 'lax', 'strict' but is configured as 'NOne'") assert.EqualError(t, validator.Errors()[0], "session: option 'same_site' must be one of 'none', 'lax', or 'strict' but it's configured as 'NOne'")
assert.EqualError(t, validator.Errors()[1], "session: domain config #1 (domain 'example.com'): option 'same_site' must be one of 'none', 'lax', 'strict' but is configured as 'NOne'") assert.EqualError(t, validator.Errors()[1], "session: domain config #1 (domain 'example.com'): option 'same_site' must be one of 'none', 'lax', or 'strict' but it's configured as 'NOne'")
} }
func TestShouldNotRaiseErrorWhenSameSiteSetCorrectly(t *testing.T) { func TestShouldNotRaiseErrorWhenSameSiteSetCorrectly(t *testing.T) {

View File

@ -3,7 +3,6 @@ package validator
import ( import (
"errors" "errors"
"fmt" "fmt"
"strings"
"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"
@ -92,7 +91,7 @@ func validatePostgreSQLConfiguration(config *schema.PostgreSQLStorageConfigurati
case config.SSL.Mode == "": case config.SSL.Mode == "":
config.SSL.Mode = schema.DefaultPostgreSQLStorageConfiguration.SSL.Mode config.SSL.Mode = schema.DefaultPostgreSQLStorageConfiguration.SSL.Mode
case !utils.IsStringInSlice(config.SSL.Mode, validStoragePostgreSQLSSLModes): case !utils.IsStringInSlice(config.SSL.Mode, validStoragePostgreSQLSSLModes):
validator.Push(fmt.Errorf(errFmtStoragePostgreSQLInvalidSSLMode, strings.Join(validStoragePostgreSQLSSLModes, "', '"), config.SSL.Mode)) validator.Push(fmt.Errorf(errFmtStoragePostgreSQLInvalidSSLMode, strJoinOr(validStoragePostgreSQLSSLModes), config.SSL.Mode))
} }
} }
} }

View File

@ -360,7 +360,7 @@ func (suite *StorageSuite) TestShouldValidatePostgresSSLModeMustBeValid() {
suite.Assert().Len(suite.validator.Warnings(), 1) suite.Assert().Len(suite.validator.Warnings(), 1)
suite.Require().Len(suite.validator.Errors(), 1) suite.Require().Len(suite.validator.Errors(), 1)
suite.Assert().EqualError(suite.validator.Errors()[0], "storage: postgres: ssl: option 'mode' must be one of 'disable', 'require', 'verify-ca', 'verify-full' but it is configured as 'unknown'") suite.Assert().EqualError(suite.validator.Errors()[0], "storage: postgres: ssl: option 'mode' must be one of 'disable', 'require', 'verify-ca', or 'verify-full' but it's configured as 'unknown'")
} }
func (suite *StorageSuite) TestShouldRaiseErrorOnNoEncryptionKey() { func (suite *StorageSuite) TestShouldRaiseErrorOnNoEncryptionKey() {

View File

@ -58,7 +58,7 @@ func TestValidateTelemetry(t *testing.T) {
&schema.Configuration{Telemetry: schema.TelemetryConfig{Metrics: schema.TelemetryMetricsConfig{Address: mustParseAddress("udp://0.0.0.0")}}}, &schema.Configuration{Telemetry: schema.TelemetryConfig{Metrics: schema.TelemetryMetricsConfig{Address: mustParseAddress("udp://0.0.0.0")}}},
&schema.Configuration{Telemetry: schema.TelemetryConfig{Metrics: schema.TelemetryMetricsConfig{Address: mustParseAddress("udp://0.0.0.0:9959")}}}, &schema.Configuration{Telemetry: schema.TelemetryConfig{Metrics: schema.TelemetryMetricsConfig{Address: mustParseAddress("udp://0.0.0.0:9959")}}},
nil, nil,
[]string{"telemetry: metrics: option 'address' must have a scheme 'tcp://' but it is configured as 'udp'"}, []string{"telemetry: metrics: option 'address' must have a scheme 'tcp://' but it's configured as 'udp'"},
}, },
} }

View File

@ -2,7 +2,6 @@ package validator
import ( import (
"fmt" "fmt"
"strings"
"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"
@ -15,6 +14,6 @@ func ValidateTheme(config *schema.Configuration, validator *schema.StructValidat
} }
if !utils.IsStringInSlice(config.Theme, validThemeNames) { if !utils.IsStringInSlice(config.Theme, validThemeNames) {
validator.Push(fmt.Errorf(errFmtThemeName, strings.Join(validThemeNames, "', '"), config.Theme)) validator.Push(fmt.Errorf(errFmtThemeName, strJoinOr(validThemeNames), config.Theme))
} }
} }

View File

@ -36,7 +36,7 @@ func (suite *Theme) TestShouldRaiseErrorWhenInvalidThemeProvided() {
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], "option 'theme' must be one of 'light', 'dark', 'grey', 'auto' but it is configured as 'invalid'") suite.Assert().EqualError(suite.validator.Errors()[0], "option 'theme' must be one of 'light', 'dark', 'grey', or 'auto' but it's configured as 'invalid'")
} }
func TestThemes(t *testing.T) { func TestThemes(t *testing.T) {

View File

@ -24,7 +24,7 @@ func ValidateTOTP(config *schema.Configuration, validator *schema.StructValidato
config.TOTP.Algorithm = strings.ToUpper(config.TOTP.Algorithm) config.TOTP.Algorithm = strings.ToUpper(config.TOTP.Algorithm)
if !utils.IsStringInSlice(config.TOTP.Algorithm, schema.TOTPPossibleAlgorithms) { if !utils.IsStringInSlice(config.TOTP.Algorithm, schema.TOTPPossibleAlgorithms) {
validator.Push(fmt.Errorf(errFmtTOTPInvalidAlgorithm, strings.Join(schema.TOTPPossibleAlgorithms, "', '"), config.TOTP.Algorithm)) validator.Push(fmt.Errorf(errFmtTOTPInvalidAlgorithm, strJoinOr(schema.TOTPPossibleAlgorithms), config.TOTP.Algorithm))
} }
} }

View File

@ -56,7 +56,9 @@ func TestValidateTOTP(t *testing.T) {
Skew: schema.DefaultTOTPConfiguration.Skew, Skew: schema.DefaultTOTPConfiguration.Skew,
Issuer: "abc", Issuer: "abc",
}, },
errs: []string{"totp: option 'algorithm' must be one of 'SHA1', 'SHA256', 'SHA512' but it is configured as 'SHA3'"}, errs: []string{
"totp: option 'algorithm' must be one of 'SHA1', 'SHA256', or 'SHA512' but it's configured as 'SHA3'",
},
}, },
{ {
desc: "ShouldRaiseErrorWhenInvalidTOTPValue", desc: "ShouldRaiseErrorWhenInvalidTOTPValue",
@ -69,10 +71,10 @@ func TestValidateTOTP(t *testing.T) {
Issuer: "abc", Issuer: "abc",
}, },
errs: []string{ errs: []string{
"totp: option 'algorithm' must be one of 'SHA1', 'SHA256', 'SHA512' but it is configured as 'SHA3'", "totp: option 'algorithm' must be one of 'SHA1', 'SHA256', or 'SHA512' but it's configured as 'SHA3'",
"totp: option 'period' option must be 15 or more but it is configured as '5'", "totp: option 'period' option must be 15 or more but it's configured as '5'",
"totp: option 'digits' must be 6 or 8 but it is configured as '20'", "totp: option 'digits' must be 6 or 8 but it's configured as '20'",
"totp: option 'secret_size' must be 20 or higher but it is configured as '10'", "totp: option 'secret_size' must be 20 or higher but it's configured as '10'",
}, },
}, },
} }

View File

@ -4,6 +4,8 @@ import (
"strings" "strings"
"golang.org/x/net/publicsuffix" "golang.org/x/net/publicsuffix"
"github.com/authelia/authelia/v4/internal/utils"
) )
func isCookieDomainAPublicSuffix(domain string) (valid bool) { func isCookieDomainAPublicSuffix(domain string) (valid bool) {
@ -13,3 +15,95 @@ func isCookieDomainAPublicSuffix(domain string) (valid bool) {
return len(strings.TrimLeft(domain, ".")) == len(suffix) return len(strings.TrimLeft(domain, ".")) == len(suffix)
} }
func strJoinOr(items []string) string {
return strJoinComma("or", items)
}
func strJoinAnd(items []string) string {
return strJoinComma("and", items)
}
func strJoinComma(word string, items []string) string {
if word == "" {
return buildJoinedString(",", "", "'", items)
}
return buildJoinedString(",", word, "'", items)
}
func buildJoinedString(sep, sepFinal, quote string, items []string) string {
n := len(items)
if n == 0 {
return ""
}
b := &strings.Builder{}
for i := 0; i < n; i++ {
if quote != "" {
b.WriteString(quote)
}
b.WriteString(items[i])
if quote != "" {
b.WriteString(quote)
}
if i == (n - 1) {
continue
}
if sep != "" {
if sepFinal == "" || n != 2 {
b.WriteString(sep)
}
b.WriteString(" ")
}
if sepFinal != "" && i == (n-2) {
b.WriteString(strings.Trim(sepFinal, " "))
b.WriteString(" ")
}
}
return b.String()
}
func validateList(values, valid []string, chkDuplicate bool) (invalid, duplicates []string) { //nolint:unparam
chkValid := len(valid) != 0
for i, value := range values {
if chkValid {
if !utils.IsStringInSlice(value, valid) {
invalid = append(invalid, value)
// Skip checking duplicates for invalid values.
continue
}
}
if chkDuplicate {
for j, valueAlt := range values {
if i == j {
continue
}
if value != valueAlt {
continue
}
if utils.IsStringInSlice(value, duplicates) {
continue
}
duplicates = append(duplicates, value)
}
}
}
return
}

View File

@ -2,7 +2,6 @@ package validator
import ( import (
"fmt" "fmt"
"strings"
"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"
@ -22,13 +21,13 @@ func ValidateWebAuthn(config *schema.Configuration, validator *schema.StructVali
case config.WebAuthn.ConveyancePreference == "": case config.WebAuthn.ConveyancePreference == "":
config.WebAuthn.ConveyancePreference = schema.DefaultWebAuthnConfiguration.ConveyancePreference config.WebAuthn.ConveyancePreference = schema.DefaultWebAuthnConfiguration.ConveyancePreference
case !utils.IsStringInSlice(string(config.WebAuthn.ConveyancePreference), validWebAuthnConveyancePreferences): case !utils.IsStringInSlice(string(config.WebAuthn.ConveyancePreference), validWebAuthnConveyancePreferences):
validator.Push(fmt.Errorf(errFmtWebAuthnConveyancePreference, strings.Join(validWebAuthnConveyancePreferences, "', '"), config.WebAuthn.ConveyancePreference)) validator.Push(fmt.Errorf(errFmtWebAuthnConveyancePreference, strJoinOr(validWebAuthnConveyancePreferences), config.WebAuthn.ConveyancePreference))
} }
switch { switch {
case config.WebAuthn.UserVerification == "": case config.WebAuthn.UserVerification == "":
config.WebAuthn.UserVerification = schema.DefaultWebAuthnConfiguration.UserVerification config.WebAuthn.UserVerification = schema.DefaultWebAuthnConfiguration.UserVerification
case !utils.IsStringInSlice(string(config.WebAuthn.UserVerification), validWebAuthnUserVerificationRequirement): case !utils.IsStringInSlice(string(config.WebAuthn.UserVerification), validWebAuthnUserVerificationRequirement):
validator.Push(fmt.Errorf(errFmtWebAuthnUserVerification, config.WebAuthn.UserVerification)) validator.Push(fmt.Errorf(errFmtWebAuthnUserVerification, strJoinOr(validWebAuthnConveyancePreferences), config.WebAuthn.UserVerification))
} }
} }

View File

@ -93,6 +93,6 @@ func TestWebAuthnShouldRaiseErrorsOnInvalidOptions(t *testing.T) {
require.Len(t, validator.Errors(), 2) require.Len(t, validator.Errors(), 2)
assert.EqualError(t, validator.Errors()[0], "webauthn: option 'attestation_conveyance_preference' must be one of 'none', 'indirect', 'direct' but it is configured as 'no'") assert.EqualError(t, validator.Errors()[0], "webauthn: option 'attestation_conveyance_preference' must be one of 'none', 'indirect', or 'direct' but it's configured as 'no'")
assert.EqualError(t, validator.Errors()[1], "webauthn: option 'user_verification' must be one of 'discouraged', 'preferred', 'required' but it is configured as 'yes'") assert.EqualError(t, validator.Errors()[1], "webauthn: option 'user_verification' must be one of 'none', 'indirect', or 'direct' but it's configured as 'yes'")
} }

View File

@ -21,7 +21,7 @@ func OpenIDConnectAuthorization(ctx *middlewares.AutheliaCtx, rw http.ResponseWr
var ( var (
requester fosite.AuthorizeRequester requester fosite.AuthorizeRequester
responder fosite.AuthorizeResponder responder fosite.AuthorizeResponder
client *oidc.Client client oidc.Client
authTime time.Time authTime time.Time
issuer *url.URL issuer *url.URL
err error err error
@ -117,7 +117,7 @@ func OpenIDConnectAuthorization(ctx *middlewares.AutheliaCtx, rw http.ResponseWr
extraClaims := oidcGrantRequests(requester, consent, &userSession) extraClaims := oidcGrantRequests(requester, consent, &userSession)
if authTime, err = userSession.AuthenticatedTime(client.Policy); err != nil { if authTime, err = userSession.AuthenticatedTime(client.GetAuthorizationPolicy()); err != nil {
ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' could not be processed: error occurred checking authentication time: %+v", requester.GetID(), client.GetID(), err) ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' could not be processed: error occurred checking authentication time: %+v", requester.GetID(), client.GetID(), err)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, fosite.ErrServerError.WithHint("Could not obtain the authentication time.")) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, fosite.ErrServerError.WithHint("Could not obtain the authentication time."))
@ -178,7 +178,7 @@ func OpenIDConnectPushedAuthorizationRequest(ctx *middlewares.AutheliaCtx, rw ht
return return
} }
var client *oidc.Client var client oidc.Client
clientID := requester.GetClient().GetID() clientID := requester.GetClient().GetID()

View File

@ -18,7 +18,7 @@ import (
"github.com/authelia/authelia/v4/internal/utils" "github.com/authelia/authelia/v4/internal/utils"
) )
func handleOIDCAuthorizationConsent(ctx *middlewares.AutheliaCtx, issuer *url.URL, client *oidc.Client, func handleOIDCAuthorizationConsent(ctx *middlewares.AutheliaCtx, issuer *url.URL, client oidc.Client,
userSession session.UserSession, userSession session.UserSession,
rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) { rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {
var ( var (
@ -33,14 +33,14 @@ func handleOIDCAuthorizationConsent(ctx *middlewares.AutheliaCtx, issuer *url.UR
handler = handleOIDCAuthorizationConsentNotAuthenticated handler = handleOIDCAuthorizationConsentNotAuthenticated
case client.IsAuthenticationLevelSufficient(userSession.AuthenticationLevel): case client.IsAuthenticationLevelSufficient(userSession.AuthenticationLevel):
if subject, err = ctx.Providers.OpenIDConnect.GetSubject(ctx, client.GetSectorIdentifier(), userSession.Username); err != nil { if subject, err = ctx.Providers.OpenIDConnect.GetSubject(ctx, client.GetSectorIdentifier(), userSession.Username); err != nil {
ctx.Logger.Errorf(logFmtErrConsentCantGetSubject, requester.GetID(), client.GetID(), client.Consent, userSession.Username, client.GetSectorIdentifier(), err) ctx.Logger.Errorf(logFmtErrConsentCantGetSubject, requester.GetID(), client.GetID(), client.GetConsentPolicy(), userSession.Username, client.GetSectorIdentifier(), err)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrSubjectCouldNotLookup) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrSubjectCouldNotLookup)
return nil, true return nil, true
} }
switch client.Consent.Mode { switch client.GetConsentPolicy().Mode {
case oidc.ClientConsentModeExplicit: case oidc.ClientConsentModeExplicit:
handler = handleOIDCAuthorizationConsentModeExplicit handler = handleOIDCAuthorizationConsentModeExplicit
case oidc.ClientConsentModeImplicit: case oidc.ClientConsentModeImplicit:
@ -56,7 +56,7 @@ func handleOIDCAuthorizationConsent(ctx *middlewares.AutheliaCtx, issuer *url.UR
} }
default: default:
if subject, err = ctx.Providers.OpenIDConnect.GetSubject(ctx, client.GetSectorIdentifier(), userSession.Username); err != nil { if subject, err = ctx.Providers.OpenIDConnect.GetSubject(ctx, client.GetSectorIdentifier(), userSession.Username); err != nil {
ctx.Logger.Errorf(logFmtErrConsentCantGetSubject, requester.GetID(), client.GetID(), client.Consent, userSession.Username, client.GetSectorIdentifier(), err) ctx.Logger.Errorf(logFmtErrConsentCantGetSubject, requester.GetID(), client.GetID(), client.GetConsentPolicy(), userSession.Username, client.GetSectorIdentifier(), err)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrSubjectCouldNotLookup) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrSubjectCouldNotLookup)
@ -69,7 +69,7 @@ func handleOIDCAuthorizationConsent(ctx *middlewares.AutheliaCtx, issuer *url.UR
return handler(ctx, issuer, client, userSession, subject, rw, r, requester) return handler(ctx, issuer, client, userSession, subject, rw, r, requester)
} }
func handleOIDCAuthorizationConsentNotAuthenticated(_ *middlewares.AutheliaCtx, issuer *url.URL, _ *oidc.Client, func handleOIDCAuthorizationConsentNotAuthenticated(_ *middlewares.AutheliaCtx, issuer *url.URL, _ oidc.Client,
_ session.UserSession, _ uuid.UUID, _ session.UserSession, _ uuid.UUID,
rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) { rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {
redirectionURL := handleOIDCAuthorizationConsentGetRedirectionURL(issuer, nil, requester) redirectionURL := handleOIDCAuthorizationConsentGetRedirectionURL(issuer, nil, requester)
@ -79,17 +79,17 @@ func handleOIDCAuthorizationConsentNotAuthenticated(_ *middlewares.AutheliaCtx,
return nil, true return nil, true
} }
func handleOIDCAuthorizationConsentGenerate(ctx *middlewares.AutheliaCtx, issuer *url.URL, client *oidc.Client, func handleOIDCAuthorizationConsentGenerate(ctx *middlewares.AutheliaCtx, issuer *url.URL, client oidc.Client,
userSession session.UserSession, subject uuid.UUID, userSession session.UserSession, subject uuid.UUID,
rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) { rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {
var ( var (
err error err error
) )
ctx.Logger.Debugf(logFmtDbgConsentGenerate, requester.GetID(), client.GetID(), client.Consent) ctx.Logger.Debugf(logFmtDbgConsentGenerate, requester.GetID(), client.GetID(), client.GetConsentPolicy())
if len(ctx.QueryArgs().PeekBytes(qryArgConsentID)) != 0 { if len(ctx.QueryArgs().PeekBytes(qryArgConsentID)) != 0 {
ctx.Logger.Errorf(logFmtErrConsentGenerateError, requester.GetID(), client.GetID(), client.Consent, "generating", errors.New("consent id value was present when it should be absent")) ctx.Logger.Errorf(logFmtErrConsentGenerateError, requester.GetID(), client.GetID(), client.GetConsentPolicy(), "generating", errors.New("consent id value was present when it should be absent"))
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotGenerate) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotGenerate)
@ -97,7 +97,7 @@ func handleOIDCAuthorizationConsentGenerate(ctx *middlewares.AutheliaCtx, issuer
} }
if consent, err = model.NewOAuth2ConsentSession(subject, requester); err != nil { if consent, err = model.NewOAuth2ConsentSession(subject, requester); err != nil {
ctx.Logger.Errorf(logFmtErrConsentGenerateError, requester.GetID(), client.GetID(), client.Consent, "generating", err) ctx.Logger.Errorf(logFmtErrConsentGenerateError, requester.GetID(), client.GetID(), client.GetConsentPolicy(), "generating", err)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotGenerate) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotGenerate)
@ -105,7 +105,7 @@ func handleOIDCAuthorizationConsentGenerate(ctx *middlewares.AutheliaCtx, issuer
} }
if err = ctx.Providers.StorageProvider.SaveOAuth2ConsentSession(ctx, *consent); err != nil { if err = ctx.Providers.StorageProvider.SaveOAuth2ConsentSession(ctx, *consent); err != nil {
ctx.Logger.Errorf(logFmtErrConsentGenerateError, requester.GetID(), client.GetID(), client.Consent, "saving", err) ctx.Logger.Errorf(logFmtErrConsentGenerateError, requester.GetID(), client.GetID(), client.GetConsentPolicy(), "saving", err)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotSave) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotSave)
@ -117,7 +117,7 @@ func handleOIDCAuthorizationConsentGenerate(ctx *middlewares.AutheliaCtx, issuer
return consent, true return consent, true
} }
func handleOIDCAuthorizationConsentRedirect(ctx *middlewares.AutheliaCtx, issuer *url.URL, consent *model.OAuth2ConsentSession, client *oidc.Client, func handleOIDCAuthorizationConsentRedirect(ctx *middlewares.AutheliaCtx, issuer *url.URL, consent *model.OAuth2ConsentSession, client oidc.Client,
userSession session.UserSession, rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) { userSession session.UserSession, rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) {
var location *url.URL var location *url.URL
@ -130,14 +130,14 @@ func handleOIDCAuthorizationConsentRedirect(ctx *middlewares.AutheliaCtx, issuer
location.RawQuery = query.Encode() location.RawQuery = query.Encode()
ctx.Logger.Debugf(logFmtDbgConsentAuthenticationSufficiency, requester.GetID(), client.GetID(), client.Consent, userSession.AuthenticationLevel.String(), "sufficient", client.Policy) ctx.Logger.Debugf(logFmtDbgConsentAuthenticationSufficiency, requester.GetID(), client.GetID(), client.GetConsentPolicy(), userSession.AuthenticationLevel.String(), "sufficient", client.GetAuthorizationPolicy())
} else { } else {
location = handleOIDCAuthorizationConsentGetRedirectionURL(issuer, consent, requester) location = handleOIDCAuthorizationConsentGetRedirectionURL(issuer, consent, requester)
ctx.Logger.Debugf(logFmtDbgConsentAuthenticationSufficiency, requester.GetID(), client.GetID(), client.Consent, userSession.AuthenticationLevel.String(), "insufficient", client.Policy) ctx.Logger.Debugf(logFmtDbgConsentAuthenticationSufficiency, requester.GetID(), client.GetID(), client.GetConsentPolicy(), userSession.AuthenticationLevel.String(), "insufficient", client.GetAuthorizationPolicy())
} }
ctx.Logger.Debugf(logFmtDbgConsentRedirect, requester.GetID(), client.GetID(), client.Consent, location) ctx.Logger.Debugf(logFmtDbgConsentRedirect, requester.GetID(), client.GetID(), client.GetConsentPolicy(), location)
http.Redirect(rw, r, location.String(), http.StatusFound) http.Redirect(rw, r, location.String(), http.StatusFound)
} }
@ -170,7 +170,7 @@ func handleOIDCAuthorizationConsentGetRedirectionURL(issuer *url.URL, consent *m
return redirectURL return redirectURL
} }
func verifyOIDCUserAuthorizedForConsent(ctx *middlewares.AutheliaCtx, client *oidc.Client, userSession session.UserSession, consent *model.OAuth2ConsentSession, subject uuid.UUID) (err error) { func verifyOIDCUserAuthorizedForConsent(ctx *middlewares.AutheliaCtx, client oidc.Client, userSession session.UserSession, consent *model.OAuth2ConsentSession, subject uuid.UUID) (err error) {
var sid uint32 var sid uint32
if client == nil { if client == nil {

View File

@ -13,7 +13,7 @@ import (
"github.com/authelia/authelia/v4/internal/session" "github.com/authelia/authelia/v4/internal/session"
) )
func handleOIDCAuthorizationConsentModeExplicit(ctx *middlewares.AutheliaCtx, issuer *url.URL, client *oidc.Client, func handleOIDCAuthorizationConsentModeExplicit(ctx *middlewares.AutheliaCtx, issuer *url.URL, client oidc.Client,
userSession session.UserSession, subject uuid.UUID, userSession session.UserSession, subject uuid.UUID,
rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) { rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {
var ( var (
@ -28,7 +28,7 @@ func handleOIDCAuthorizationConsentModeExplicit(ctx *middlewares.AutheliaCtx, is
return handleOIDCAuthorizationConsentGenerate(ctx, issuer, client, userSession, subject, rw, r, requester) return handleOIDCAuthorizationConsentGenerate(ctx, issuer, client, userSession, subject, rw, r, requester)
default: default:
if consentID, err = uuid.ParseBytes(bytesConsentID); err != nil { if consentID, err = uuid.ParseBytes(bytesConsentID); err != nil {
ctx.Logger.Errorf(logFmtErrConsentParseChallengeID, requester.GetID(), client.GetID(), client.Consent, bytesConsentID, err) ctx.Logger.Errorf(logFmtErrConsentParseChallengeID, requester.GetID(), client.GetID(), client.GetConsentPolicy(), bytesConsentID, err)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentMalformedChallengeID) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentMalformedChallengeID)
@ -39,7 +39,7 @@ func handleOIDCAuthorizationConsentModeExplicit(ctx *middlewares.AutheliaCtx, is
} }
} }
func handleOIDCAuthorizationConsentModeExplicitWithID(ctx *middlewares.AutheliaCtx, issuer *url.URL, client *oidc.Client, func handleOIDCAuthorizationConsentModeExplicitWithID(ctx *middlewares.AutheliaCtx, issuer *url.URL, client oidc.Client,
userSession session.UserSession, subject uuid.UUID, consentID uuid.UUID, userSession session.UserSession, subject uuid.UUID, consentID uuid.UUID,
rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) { rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {
var ( var (
@ -47,7 +47,7 @@ func handleOIDCAuthorizationConsentModeExplicitWithID(ctx *middlewares.AutheliaC
) )
if consentID.ID() == 0 { if consentID.ID() == 0 {
ctx.Logger.Errorf(logFmtErrConsentZeroID, requester.GetID(), client.GetID(), client.Consent) ctx.Logger.Errorf(logFmtErrConsentZeroID, requester.GetID(), client.GetID(), client.GetConsentPolicy())
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotLookup) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotLookup)
@ -55,7 +55,7 @@ func handleOIDCAuthorizationConsentModeExplicitWithID(ctx *middlewares.AutheliaC
} }
if consent, err = ctx.Providers.StorageProvider.LoadOAuth2ConsentSessionByChallengeID(ctx, consentID); err != nil { if consent, err = ctx.Providers.StorageProvider.LoadOAuth2ConsentSessionByChallengeID(ctx, consentID); err != nil {
ctx.Logger.Errorf(logFmtErrConsentLookupLoadingSession, requester.GetID(), client.GetID(), client.Consent, consentID, err) ctx.Logger.Errorf(logFmtErrConsentLookupLoadingSession, requester.GetID(), client.GetID(), client.GetConsentPolicy(), consentID, err)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotLookup) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotLookup)
@ -63,7 +63,7 @@ func handleOIDCAuthorizationConsentModeExplicitWithID(ctx *middlewares.AutheliaC
} }
if subject.ID() != consent.Subject.UUID.ID() { if subject.ID() != consent.Subject.UUID.ID() {
ctx.Logger.Errorf(logFmtErrConsentSessionSubjectNotAuthorized, requester.GetID(), client.GetID(), client.Consent, consent.ChallengeID, userSession.Username, subject, consent.Subject.UUID) ctx.Logger.Errorf(logFmtErrConsentSessionSubjectNotAuthorized, requester.GetID(), client.GetID(), client.GetConsentPolicy(), consent.ChallengeID, userSession.Username, subject, consent.Subject.UUID)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotLookup) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotLookup)
@ -71,7 +71,7 @@ func handleOIDCAuthorizationConsentModeExplicitWithID(ctx *middlewares.AutheliaC
} }
if !consent.CanGrant() { if !consent.CanGrant() {
ctx.Logger.Errorf(logFmtErrConsentCantGrant, requester.GetID(), client.GetID(), client.Consent, consent.ChallengeID, "explicit") ctx.Logger.Errorf(logFmtErrConsentCantGrant, requester.GetID(), client.GetID(), client.GetConsentPolicy(), consent.ChallengeID, "explicit")
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotPerform) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotPerform)
@ -80,7 +80,7 @@ func handleOIDCAuthorizationConsentModeExplicitWithID(ctx *middlewares.AutheliaC
if !consent.IsAuthorized() { if !consent.IsAuthorized() {
if consent.Responded() { if consent.Responded() {
ctx.Logger.Errorf(logFmtErrConsentCantGrantRejected, requester.GetID(), client.GetID(), client.Consent, consent.ChallengeID) ctx.Logger.Errorf(logFmtErrConsentCantGrantRejected, requester.GetID(), client.GetID(), client.GetConsentPolicy(), consent.ChallengeID)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, fosite.ErrAccessDenied) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, fosite.ErrAccessDenied)

View File

@ -13,7 +13,7 @@ import (
"github.com/authelia/authelia/v4/internal/session" "github.com/authelia/authelia/v4/internal/session"
) )
func handleOIDCAuthorizationConsentModeImplicit(ctx *middlewares.AutheliaCtx, issuer *url.URL, client *oidc.Client, func handleOIDCAuthorizationConsentModeImplicit(ctx *middlewares.AutheliaCtx, issuer *url.URL, client oidc.Client,
userSession session.UserSession, subject uuid.UUID, userSession session.UserSession, subject uuid.UUID,
rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) { rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {
var ( var (
@ -26,7 +26,7 @@ func handleOIDCAuthorizationConsentModeImplicit(ctx *middlewares.AutheliaCtx, is
return handleOIDCAuthorizationConsentModeImplicitWithoutID(ctx, issuer, client, userSession, subject, rw, r, requester) return handleOIDCAuthorizationConsentModeImplicitWithoutID(ctx, issuer, client, userSession, subject, rw, r, requester)
default: default:
if consentID, err = uuid.ParseBytes(bytesConsentID); err != nil { if consentID, err = uuid.ParseBytes(bytesConsentID); err != nil {
ctx.Logger.Errorf(logFmtErrConsentParseChallengeID, requester.GetID(), client.GetID(), client.Consent, bytesConsentID, err) ctx.Logger.Errorf(logFmtErrConsentParseChallengeID, requester.GetID(), client.GetID(), client.GetConsentPolicy(), bytesConsentID, err)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentMalformedChallengeID) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentMalformedChallengeID)
@ -37,7 +37,7 @@ func handleOIDCAuthorizationConsentModeImplicit(ctx *middlewares.AutheliaCtx, is
} }
} }
func handleOIDCAuthorizationConsentModeImplicitWithID(ctx *middlewares.AutheliaCtx, _ *url.URL, client *oidc.Client, func handleOIDCAuthorizationConsentModeImplicitWithID(ctx *middlewares.AutheliaCtx, _ *url.URL, client oidc.Client,
userSession session.UserSession, subject uuid.UUID, consentID uuid.UUID, userSession session.UserSession, subject uuid.UUID, consentID uuid.UUID,
rw http.ResponseWriter, _ *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) { rw http.ResponseWriter, _ *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {
var ( var (
@ -45,7 +45,7 @@ func handleOIDCAuthorizationConsentModeImplicitWithID(ctx *middlewares.AutheliaC
) )
if consentID.ID() == 0 { if consentID.ID() == 0 {
ctx.Logger.Errorf(logFmtErrConsentZeroID, requester.GetID(), client.GetID(), client.Consent) ctx.Logger.Errorf(logFmtErrConsentZeroID, requester.GetID(), client.GetID(), client.GetConsentPolicy())
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotLookup) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotLookup)
@ -53,7 +53,7 @@ func handleOIDCAuthorizationConsentModeImplicitWithID(ctx *middlewares.AutheliaC
} }
if consent, err = ctx.Providers.StorageProvider.LoadOAuth2ConsentSessionByChallengeID(ctx, consentID); err != nil { if consent, err = ctx.Providers.StorageProvider.LoadOAuth2ConsentSessionByChallengeID(ctx, consentID); err != nil {
ctx.Logger.Errorf(logFmtErrConsentLookupLoadingSession, requester.GetID(), client.GetID(), client.Consent, consentID, err) ctx.Logger.Errorf(logFmtErrConsentLookupLoadingSession, requester.GetID(), client.GetID(), client.GetConsentPolicy(), consentID, err)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotLookup) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotLookup)
@ -61,7 +61,7 @@ func handleOIDCAuthorizationConsentModeImplicitWithID(ctx *middlewares.AutheliaC
} }
if subject.ID() != consent.Subject.UUID.ID() { if subject.ID() != consent.Subject.UUID.ID() {
ctx.Logger.Errorf(logFmtErrConsentSessionSubjectNotAuthorized, requester.GetID(), client.GetID(), client.Consent, consent.ChallengeID, userSession.Username, subject, consent.Subject.UUID) ctx.Logger.Errorf(logFmtErrConsentSessionSubjectNotAuthorized, requester.GetID(), client.GetID(), client.GetConsentPolicy(), consent.ChallengeID, userSession.Username, subject, consent.Subject.UUID)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotLookup) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotLookup)
@ -69,7 +69,7 @@ func handleOIDCAuthorizationConsentModeImplicitWithID(ctx *middlewares.AutheliaC
} }
if !consent.CanGrant() { if !consent.CanGrant() {
ctx.Logger.Errorf(logFmtErrConsentCantGrant, requester.GetID(), client.GetID(), client.Consent, consent.ChallengeID, "implicit") ctx.Logger.Errorf(logFmtErrConsentCantGrant, requester.GetID(), client.GetID(), client.GetConsentPolicy(), consent.ChallengeID, "implicit")
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotPerform) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotPerform)
@ -79,7 +79,7 @@ func handleOIDCAuthorizationConsentModeImplicitWithID(ctx *middlewares.AutheliaC
consent.Grant() consent.Grant()
if err = ctx.Providers.StorageProvider.SaveOAuth2ConsentSessionResponse(ctx, *consent, false); err != nil { if err = ctx.Providers.StorageProvider.SaveOAuth2ConsentSessionResponse(ctx, *consent, false); err != nil {
ctx.Logger.Errorf(logFmtErrConsentSaveSessionResponse, requester.GetID(), client.GetID(), client.Consent, consent.ChallengeID, err) ctx.Logger.Errorf(logFmtErrConsentSaveSessionResponse, requester.GetID(), client.GetID(), client.GetConsentPolicy(), consent.ChallengeID, err)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotSave) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotSave)
@ -89,7 +89,7 @@ func handleOIDCAuthorizationConsentModeImplicitWithID(ctx *middlewares.AutheliaC
return consent, false return consent, false
} }
func handleOIDCAuthorizationConsentModeImplicitWithoutID(ctx *middlewares.AutheliaCtx, _ *url.URL, client *oidc.Client, func handleOIDCAuthorizationConsentModeImplicitWithoutID(ctx *middlewares.AutheliaCtx, _ *url.URL, client oidc.Client,
_ session.UserSession, subject uuid.UUID, _ session.UserSession, subject uuid.UUID,
rw http.ResponseWriter, _ *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) { rw http.ResponseWriter, _ *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {
var ( var (
@ -97,7 +97,7 @@ func handleOIDCAuthorizationConsentModeImplicitWithoutID(ctx *middlewares.Authel
) )
if consent, err = model.NewOAuth2ConsentSession(subject, requester); err != nil { if consent, err = model.NewOAuth2ConsentSession(subject, requester); err != nil {
ctx.Logger.Errorf(logFmtErrConsentGenerate, requester.GetID(), client.GetID(), client.Consent, err) ctx.Logger.Errorf(logFmtErrConsentGenerate, requester.GetID(), client.GetID(), client.GetConsentPolicy(), err)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotGenerate) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotGenerate)
@ -105,7 +105,7 @@ func handleOIDCAuthorizationConsentModeImplicitWithoutID(ctx *middlewares.Authel
} }
if err = ctx.Providers.StorageProvider.SaveOAuth2ConsentSession(ctx, *consent); err != nil { if err = ctx.Providers.StorageProvider.SaveOAuth2ConsentSession(ctx, *consent); err != nil {
ctx.Logger.Errorf(logFmtErrConsentSaveSession, requester.GetID(), client.GetID(), client.Consent, consent.ChallengeID, err) ctx.Logger.Errorf(logFmtErrConsentSaveSession, requester.GetID(), client.GetID(), client.GetConsentPolicy(), consent.ChallengeID, err)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotSave) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotSave)
@ -113,7 +113,7 @@ func handleOIDCAuthorizationConsentModeImplicitWithoutID(ctx *middlewares.Authel
} }
if consent, err = ctx.Providers.StorageProvider.LoadOAuth2ConsentSessionByChallengeID(ctx, consent.ChallengeID); err != nil { if consent, err = ctx.Providers.StorageProvider.LoadOAuth2ConsentSessionByChallengeID(ctx, consent.ChallengeID); err != nil {
ctx.Logger.Errorf(logFmtErrConsentSaveSession, requester.GetID(), client.GetID(), client.Consent, consent.ChallengeID, err) ctx.Logger.Errorf(logFmtErrConsentSaveSession, requester.GetID(), client.GetID(), client.GetConsentPolicy(), consent.ChallengeID, err)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotSave) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotSave)
@ -123,7 +123,7 @@ func handleOIDCAuthorizationConsentModeImplicitWithoutID(ctx *middlewares.Authel
consent.Grant() consent.Grant()
if err = ctx.Providers.StorageProvider.SaveOAuth2ConsentSessionResponse(ctx, *consent, false); err != nil { if err = ctx.Providers.StorageProvider.SaveOAuth2ConsentSessionResponse(ctx, *consent, false); err != nil {
ctx.Logger.Errorf(logFmtErrConsentSaveSessionResponse, requester.GetID(), client.GetID(), client.Consent, consent.ChallengeID, err) ctx.Logger.Errorf(logFmtErrConsentSaveSessionResponse, requester.GetID(), client.GetID(), client.GetConsentPolicy(), consent.ChallengeID, err)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotSave) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotSave)

View File

@ -17,7 +17,7 @@ import (
"github.com/authelia/authelia/v4/internal/storage" "github.com/authelia/authelia/v4/internal/storage"
) )
func handleOIDCAuthorizationConsentModePreConfigured(ctx *middlewares.AutheliaCtx, issuer *url.URL, client *oidc.Client, func handleOIDCAuthorizationConsentModePreConfigured(ctx *middlewares.AutheliaCtx, issuer *url.URL, client oidc.Client,
userSession session.UserSession, subject uuid.UUID, userSession session.UserSession, subject uuid.UUID,
rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) { rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {
var ( var (
@ -32,7 +32,7 @@ func handleOIDCAuthorizationConsentModePreConfigured(ctx *middlewares.AutheliaCt
return handleOIDCAuthorizationConsentModePreConfiguredWithoutID(ctx, issuer, client, userSession, subject, rw, r, requester) return handleOIDCAuthorizationConsentModePreConfiguredWithoutID(ctx, issuer, client, userSession, subject, rw, r, requester)
default: default:
if consentID, err = uuid.ParseBytes(bytesConsentID); err != nil { if consentID, err = uuid.ParseBytes(bytesConsentID); err != nil {
ctx.Logger.Errorf(logFmtErrConsentParseChallengeID, requester.GetID(), client.GetID(), client.Consent, bytesConsentID, err) ctx.Logger.Errorf(logFmtErrConsentParseChallengeID, requester.GetID(), client.GetID(), client.GetConsentPolicy(), bytesConsentID, err)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentMalformedChallengeID) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentMalformedChallengeID)
@ -43,7 +43,7 @@ func handleOIDCAuthorizationConsentModePreConfigured(ctx *middlewares.AutheliaCt
} }
} }
func handleOIDCAuthorizationConsentModePreConfiguredWithID(ctx *middlewares.AutheliaCtx, issuer *url.URL, client *oidc.Client, func handleOIDCAuthorizationConsentModePreConfiguredWithID(ctx *middlewares.AutheliaCtx, issuer *url.URL, client oidc.Client,
userSession session.UserSession, subject uuid.UUID, consentID uuid.UUID, userSession session.UserSession, subject uuid.UUID, consentID uuid.UUID,
rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) { rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {
var ( var (
@ -52,7 +52,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithID(ctx *middlewares.Auth
) )
if consentID.ID() == 0 { if consentID.ID() == 0 {
ctx.Logger.Errorf(logFmtErrConsentZeroID, requester.GetID(), client.GetID(), client.Consent) ctx.Logger.Errorf(logFmtErrConsentZeroID, requester.GetID(), client.GetID(), client.GetConsentPolicy())
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotLookup) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotLookup)
@ -60,7 +60,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithID(ctx *middlewares.Auth
} }
if consent, err = ctx.Providers.StorageProvider.LoadOAuth2ConsentSessionByChallengeID(ctx, consentID); err != nil { if consent, err = ctx.Providers.StorageProvider.LoadOAuth2ConsentSessionByChallengeID(ctx, consentID); err != nil {
ctx.Logger.Errorf(logFmtErrConsentLookupLoadingSession, requester.GetID(), client.GetID(), client.Consent, consentID, err) ctx.Logger.Errorf(logFmtErrConsentLookupLoadingSession, requester.GetID(), client.GetID(), client.GetConsentPolicy(), consentID, err)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotLookup) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotLookup)
@ -68,7 +68,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithID(ctx *middlewares.Auth
} }
if subject.ID() != consent.Subject.UUID.ID() { if subject.ID() != consent.Subject.UUID.ID() {
ctx.Logger.Errorf(logFmtErrConsentSessionSubjectNotAuthorized, requester.GetID(), client.GetID(), client.Consent, consent.ChallengeID, userSession.Username, subject, consent.Subject.UUID) ctx.Logger.Errorf(logFmtErrConsentSessionSubjectNotAuthorized, requester.GetID(), client.GetID(), client.GetConsentPolicy(), consent.ChallengeID, userSession.Username, subject, consent.Subject.UUID)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotLookup) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotLookup)
@ -76,7 +76,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithID(ctx *middlewares.Auth
} }
if !consent.CanGrant() { if !consent.CanGrant() {
ctx.Logger.Errorf(logFmtErrConsentCantGrantPreConf, requester.GetID(), client.GetID(), client.Consent, consent.ChallengeID) ctx.Logger.Errorf(logFmtErrConsentCantGrantPreConf, requester.GetID(), client.GetID(), client.GetConsentPolicy(), consent.ChallengeID)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotPerform) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotPerform)
@ -84,7 +84,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithID(ctx *middlewares.Auth
} }
if config, err = handleOIDCAuthorizationConsentModePreConfiguredGetPreConfig(ctx, client, subject, requester); err != nil { if config, err = handleOIDCAuthorizationConsentModePreConfiguredGetPreConfig(ctx, client, subject, requester); err != nil {
ctx.Logger.Errorf(logFmtErrConsentPreConfLookup, requester.GetID(), client.GetID(), client.Consent, err) ctx.Logger.Errorf(logFmtErrConsentPreConfLookup, requester.GetID(), client.GetID(), client.GetConsentPolicy(), err)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotLookup) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotLookup)
@ -97,7 +97,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithID(ctx *middlewares.Auth
consent.PreConfiguration = sql.NullInt64{Int64: config.ID, Valid: true} consent.PreConfiguration = sql.NullInt64{Int64: config.ID, Valid: true}
if err = ctx.Providers.StorageProvider.SaveOAuth2ConsentSessionResponse(ctx, *consent, false); err != nil { if err = ctx.Providers.StorageProvider.SaveOAuth2ConsentSessionResponse(ctx, *consent, false); err != nil {
ctx.Logger.Errorf(logFmtErrConsentSaveSessionResponse, requester.GetID(), client.GetID(), client.Consent, consent.ChallengeID, err) ctx.Logger.Errorf(logFmtErrConsentSaveSessionResponse, requester.GetID(), client.GetID(), client.GetConsentPolicy(), consent.ChallengeID, err)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotSave) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotSave)
@ -109,7 +109,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithID(ctx *middlewares.Auth
if !consent.IsAuthorized() { if !consent.IsAuthorized() {
if consent.Responded() { if consent.Responded() {
ctx.Logger.Errorf(logFmtErrConsentCantGrantRejected, requester.GetID(), client.GetID(), client.Consent, consent.ChallengeID) ctx.Logger.Errorf(logFmtErrConsentCantGrantRejected, requester.GetID(), client.GetID(), client.GetConsentPolicy(), consent.ChallengeID)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, fosite.ErrAccessDenied) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, fosite.ErrAccessDenied)
@ -124,7 +124,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithID(ctx *middlewares.Auth
return consent, false return consent, false
} }
func handleOIDCAuthorizationConsentModePreConfiguredWithoutID(ctx *middlewares.AutheliaCtx, issuer *url.URL, client *oidc.Client, func handleOIDCAuthorizationConsentModePreConfiguredWithoutID(ctx *middlewares.AutheliaCtx, issuer *url.URL, client oidc.Client,
userSession session.UserSession, subject uuid.UUID, userSession session.UserSession, subject uuid.UUID,
rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) { rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {
var ( var (
@ -133,7 +133,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithoutID(ctx *middlewares.A
) )
if config, err = handleOIDCAuthorizationConsentModePreConfiguredGetPreConfig(ctx, client, subject, requester); err != nil { if config, err = handleOIDCAuthorizationConsentModePreConfiguredGetPreConfig(ctx, client, subject, requester); err != nil {
ctx.Logger.Errorf(logFmtErrConsentPreConfLookup, requester.GetID(), client.GetID(), client.Consent, err) ctx.Logger.Errorf(logFmtErrConsentPreConfLookup, requester.GetID(), client.GetID(), client.GetConsentPolicy(), err)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotLookup) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotLookup)
@ -145,7 +145,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithoutID(ctx *middlewares.A
} }
if consent, err = model.NewOAuth2ConsentSession(subject, requester); err != nil { if consent, err = model.NewOAuth2ConsentSession(subject, requester); err != nil {
ctx.Logger.Errorf(logFmtErrConsentGenerate, requester.GetID(), client.GetID(), client.Consent, err) ctx.Logger.Errorf(logFmtErrConsentGenerate, requester.GetID(), client.GetID(), client.GetConsentPolicy(), err)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotGenerate) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotGenerate)
@ -153,7 +153,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithoutID(ctx *middlewares.A
} }
if err = ctx.Providers.StorageProvider.SaveOAuth2ConsentSession(ctx, *consent); err != nil { if err = ctx.Providers.StorageProvider.SaveOAuth2ConsentSession(ctx, *consent); err != nil {
ctx.Logger.Errorf(logFmtErrConsentSaveSession, requester.GetID(), client.GetID(), client.Consent, consent.ChallengeID, err) ctx.Logger.Errorf(logFmtErrConsentSaveSession, requester.GetID(), client.GetID(), client.GetConsentPolicy(), consent.ChallengeID, err)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotSave) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotSave)
@ -161,7 +161,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithoutID(ctx *middlewares.A
} }
if consent, err = ctx.Providers.StorageProvider.LoadOAuth2ConsentSessionByChallengeID(ctx, consent.ChallengeID); err != nil { if consent, err = ctx.Providers.StorageProvider.LoadOAuth2ConsentSessionByChallengeID(ctx, consent.ChallengeID); err != nil {
ctx.Logger.Errorf(logFmtErrConsentSaveSession, requester.GetID(), client.GetID(), client.Consent, consent.ChallengeID, err) ctx.Logger.Errorf(logFmtErrConsentSaveSession, requester.GetID(), client.GetID(), client.GetConsentPolicy(), consent.ChallengeID, err)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotSave) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotSave)
@ -173,7 +173,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithoutID(ctx *middlewares.A
consent.PreConfiguration = sql.NullInt64{Int64: config.ID, Valid: true} consent.PreConfiguration = sql.NullInt64{Int64: config.ID, Valid: true}
if err = ctx.Providers.StorageProvider.SaveOAuth2ConsentSessionResponse(ctx, *consent, false); err != nil { if err = ctx.Providers.StorageProvider.SaveOAuth2ConsentSessionResponse(ctx, *consent, false); err != nil {
ctx.Logger.Errorf(logFmtErrConsentSaveSessionResponse, requester.GetID(), client.GetID(), client.Consent, consent.ChallengeID, err) ctx.Logger.Errorf(logFmtErrConsentSaveSessionResponse, requester.GetID(), client.GetID(), client.GetConsentPolicy(), consent.ChallengeID, err)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotSave) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrConsentCouldNotSave)
@ -183,12 +183,12 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithoutID(ctx *middlewares.A
return consent, false return consent, false
} }
func handleOIDCAuthorizationConsentModePreConfiguredGetPreConfig(ctx *middlewares.AutheliaCtx, client *oidc.Client, subject uuid.UUID, requester fosite.Requester) (config *model.OAuth2ConsentPreConfig, err error) { func handleOIDCAuthorizationConsentModePreConfiguredGetPreConfig(ctx *middlewares.AutheliaCtx, client oidc.Client, subject uuid.UUID, requester fosite.Requester) (config *model.OAuth2ConsentPreConfig, err error) {
var ( var (
rows *storage.ConsentPreConfigRows rows *storage.ConsentPreConfigRows
) )
ctx.Logger.Debugf(logFmtDbgConsentPreConfTryingLookup, requester.GetID(), client.GetID(), client.Consent, client.GetID(), subject, strings.Join(requester.GetRequestedScopes(), " ")) ctx.Logger.Debugf(logFmtDbgConsentPreConfTryingLookup, requester.GetID(), client.GetID(), client.GetConsentPolicy(), client.GetID(), subject, strings.Join(requester.GetRequestedScopes(), " "))
if rows, err = ctx.Providers.StorageProvider.LoadOAuth2ConsentPreConfigurations(ctx, client.GetID(), subject); err != nil { if rows, err = ctx.Providers.StorageProvider.LoadOAuth2ConsentPreConfigurations(ctx, client.GetID(), subject); err != nil {
return nil, fmt.Errorf("error loading rows: %w", err) return nil, fmt.Errorf("error loading rows: %w", err)
@ -196,7 +196,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredGetPreConfig(ctx *middleware
defer func() { defer func() {
if err := rows.Close(); err != nil { if err := rows.Close(); err != nil {
ctx.Logger.Errorf(logFmtErrConsentPreConfRowsClose, requester.GetID(), client.GetID(), client.Consent, err) ctx.Logger.Errorf(logFmtErrConsentPreConfRowsClose, requester.GetID(), client.GetID(), client.GetConsentPolicy(), err)
} }
}() }()
@ -208,13 +208,13 @@ func handleOIDCAuthorizationConsentModePreConfiguredGetPreConfig(ctx *middleware
} }
if config.HasExactGrants(scopes, audience) && config.CanConsent() { if config.HasExactGrants(scopes, audience) && config.CanConsent() {
ctx.Logger.Debugf(logFmtDbgConsentPreConfSuccessfulLookup, requester.GetID(), client.GetID(), client.Consent, client.GetID(), subject, strings.Join(requester.GetRequestedScopes(), " "), config.ID) ctx.Logger.Debugf(logFmtDbgConsentPreConfSuccessfulLookup, requester.GetID(), client.GetID(), client.GetConsentPolicy(), client.GetID(), subject, strings.Join(requester.GetRequestedScopes(), " "), config.ID)
return config, nil return config, nil
} }
} }
ctx.Logger.Debugf(logFmtDbgConsentPreConfUnsuccessfulLookup, requester.GetID(), client.GetID(), client.Consent, client.GetID(), subject, strings.Join(requester.GetRequestedScopes(), " ")) ctx.Logger.Debugf(logFmtDbgConsentPreConfUnsuccessfulLookup, requester.GetID(), client.GetID(), client.GetConsentPolicy(), client.GetID(), subject, strings.Join(requester.GetRequestedScopes(), " "))
return nil, nil return nil, nil
} }

View File

@ -32,7 +32,7 @@ func OpenIDConnectConsentGET(ctx *middlewares.AutheliaCtx) {
var ( var (
consent *model.OAuth2ConsentSession consent *model.OAuth2ConsentSession
client *oidc.Client client oidc.Client
handled bool handled bool
) )
@ -70,7 +70,7 @@ func OpenIDConnectConsentPOST(ctx *middlewares.AutheliaCtx) {
var ( var (
userSession session.UserSession userSession session.UserSession
consent *model.OAuth2ConsentSession consent *model.OAuth2ConsentSession
client *oidc.Client client oidc.Client
handled bool handled bool
) )
@ -90,12 +90,12 @@ func OpenIDConnectConsentPOST(ctx *middlewares.AutheliaCtx) {
consent.Grant() consent.Grant()
if bodyJSON.PreConfigure { if bodyJSON.PreConfigure {
if client.Consent.Mode == oidc.ClientConsentModePreConfigured { if client.GetConsentPolicy().Mode == oidc.ClientConsentModePreConfigured {
config := model.OAuth2ConsentPreConfig{ config := model.OAuth2ConsentPreConfig{
ClientID: consent.ClientID, ClientID: consent.ClientID,
Subject: consent.Subject.UUID, Subject: consent.Subject.UUID,
CreatedAt: time.Now(), CreatedAt: time.Now(),
ExpiresAt: sql.NullTime{Time: time.Now().Add(client.Consent.Duration), Valid: true}, ExpiresAt: sql.NullTime{Time: time.Now().Add(client.GetConsentPolicy().Duration), Valid: true},
Scopes: consent.GrantedScopes, Scopes: consent.GrantedScopes,
Audience: consent.GrantedAudience, Audience: consent.GrantedAudience,
} }
@ -151,7 +151,7 @@ func OpenIDConnectConsentPOST(ctx *middlewares.AutheliaCtx) {
} }
} }
func oidcConsentGetSessionsAndClient(ctx *middlewares.AutheliaCtx, consentID uuid.UUID) (userSession session.UserSession, consent *model.OAuth2ConsentSession, client *oidc.Client, handled bool) { func oidcConsentGetSessionsAndClient(ctx *middlewares.AutheliaCtx, consentID uuid.UUID) (userSession session.UserSession, consent *model.OAuth2ConsentSession, client oidc.Client, handled bool) {
var ( var (
err error err error
) )
@ -185,7 +185,7 @@ func oidcConsentGetSessionsAndClient(ctx *middlewares.AutheliaCtx, consentID uui
return userSession, nil, nil, true return userSession, nil, nil, true
} }
switch client.Consent.Mode { switch client.GetConsentPolicy().Mode {
case oidc.ClientConsentModeImplicit: case oidc.ClientConsentModeImplicit:
ctx.Logger.Errorf("Unable to perform OpenID Connect Consent for user '%s' and client id '%s': the client is using the implicit consent mode", userSession.Username, consent.ClientID) ctx.Logger.Errorf("Unable to perform OpenID Connect Consent for user '%s' and client id '%s': the client is using the implicit consent mode", userSession.Username, consent.ClientID)
ctx.ReplyForbidden() ctx.ReplyForbidden()

View File

@ -23,7 +23,7 @@ func OpenIDConnectUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter,
var ( var (
tokenType fosite.TokenType tokenType fosite.TokenType
requester fosite.AccessRequester requester fosite.AccessRequester
client *oidc.Client client oidc.Client
err error err error
) )
@ -99,7 +99,7 @@ func OpenIDConnectUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter,
ctx.Logger.Tracef("UserInfo Response with id '%s' on client with id '%s' is being sent with the following claims: %+v", requester.GetID(), clientID, claims) ctx.Logger.Tracef("UserInfo Response with id '%s' on client with id '%s' is being sent with the following claims: %+v", requester.GetID(), clientID, claims)
switch client.UserinfoSigningAlgorithm { switch client.GetUserinfoSigningAlgorithm() {
case oidc.SigningAlgorithmRSAWithSHA256: case oidc.SigningAlgorithmRSAWithSHA256:
var jti uuid.UUID var jti uuid.UUID
@ -129,6 +129,6 @@ func OpenIDConnectUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter,
case oidc.SigningAlgorithmNone, "": case oidc.SigningAlgorithmNone, "":
ctx.Providers.OpenIDConnect.Write(rw, req, claims) ctx.Providers.OpenIDConnect.Write(rw, req, claims)
default: default:
ctx.Providers.OpenIDConnect.WriteError(rw, req, errors.WithStack(fosite.ErrServerError.WithHintf("Unsupported UserInfo signing algorithm '%s'.", client.UserinfoSigningAlgorithm))) ctx.Providers.OpenIDConnect.WriteError(rw, req, errors.WithStack(fosite.ErrServerError.WithHintf("Unsupported UserInfo signing algorithm '%s'.", client.GetUserinfoSigningAlgorithm())))
} }
} }

View File

@ -178,7 +178,7 @@ func handleOIDCWorkflowResponseWithTargetURL(ctx *middlewares.AutheliaCtx, targe
func handleOIDCWorkflowResponseWithID(ctx *middlewares.AutheliaCtx, id string) { func handleOIDCWorkflowResponseWithID(ctx *middlewares.AutheliaCtx, id string) {
var ( var (
workflowID uuid.UUID workflowID uuid.UUID
client *oidc.Client client oidc.Client
consent *model.OAuth2ConsentSession consent *model.OAuth2ConsentSession
err error err error
) )
@ -210,19 +210,19 @@ func handleOIDCWorkflowResponseWithID(ctx *middlewares.AutheliaCtx, id string) {
var userSession session.UserSession var userSession session.UserSession
if userSession, err = ctx.GetSession(); err != nil { if userSession, err = ctx.GetSession(); err != nil {
ctx.Error(fmt.Errorf("unable to redirect for authorization/consent for client with id '%s' with consent challenge id '%s': failed to lookup session: %w", client.ID, consent.ChallengeID, err), messageAuthenticationFailed) ctx.Error(fmt.Errorf("unable to redirect for authorization/consent for client with id '%s' with consent challenge id '%s': failed to lookup session: %w", client.GetID(), consent.ChallengeID, err), messageAuthenticationFailed)
return return
} }
if userSession.IsAnonymous() { if userSession.IsAnonymous() {
ctx.Error(fmt.Errorf("unable to redirect for authorization/consent for client with id '%s' with consent challenge id '%s': user is anonymous", client.ID, consent.ChallengeID), messageAuthenticationFailed) ctx.Error(fmt.Errorf("unable to redirect for authorization/consent for client with id '%s' with consent challenge id '%s': user is anonymous", client.GetID(), consent.ChallengeID), messageAuthenticationFailed)
return return
} }
if !client.IsAuthenticationLevelSufficient(userSession.AuthenticationLevel) { if !client.IsAuthenticationLevelSufficient(userSession.AuthenticationLevel) {
ctx.Logger.Warnf("OpenID Connect client '%s' requires 2FA, cannot be redirected yet", client.ID) ctx.Logger.Warnf("OpenID Connect client '%s' requires 2FA, cannot be redirected yet", client.GetID())
ctx.ReplyOK() ctx.ReplyOK()
return return

View File

@ -154,7 +154,7 @@ type PasswordPolicyBody struct {
} }
type handlerAuthorizationConsent func( type handlerAuthorizationConsent func(
ctx *middlewares.AutheliaCtx, issuer *url.URL, client *oidc.Client, ctx *middlewares.AutheliaCtx, issuer *url.URL, client oidc.Client,
userSession session.UserSession, subject uuid.UUID, userSession session.UserSession, subject uuid.UUID,
rw http.ResponseWriter, r *http.Request, rw http.ResponseWriter, r *http.Request,
requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool)

View File

@ -1,8 +1,10 @@
package oidc package oidc
import ( import (
"github.com/go-crypt/crypt/algorithm"
"github.com/ory/fosite" "github.com/ory/fosite"
"github.com/ory/x/errorsx" "github.com/ory/x/errorsx"
"gopkg.in/square/go-jose.v2"
"github.com/authelia/authelia/v4/internal/authentication" "github.com/authelia/authelia/v4/internal/authentication"
"github.com/authelia/authelia/v4/internal/authorization" "github.com/authelia/authelia/v4/internal/authorization"
@ -11,8 +13,8 @@ import (
) )
// NewClient creates a new Client. // NewClient creates a new Client.
func NewClient(config schema.OpenIDConnectClientConfiguration) (client *Client) { func NewClient(config schema.OpenIDConnectClientConfiguration) (client Client) {
client = &Client{ base := &BaseClient{
ID: config.ID, ID: config.ID,
Description: config.Description, Description: config.Description,
Secret: config.Secret, Secret: config.Secret,
@ -40,14 +42,165 @@ func NewClient(config schema.OpenIDConnectClientConfiguration) (client *Client)
} }
for _, mode := range config.ResponseModes { for _, mode := range config.ResponseModes {
client.ResponseModes = append(client.ResponseModes, fosite.ResponseModeType(mode)) base.ResponseModes = append(base.ResponseModes, fosite.ResponseModeType(mode))
}
if config.TokenEndpointAuthMethod != "" && config.TokenEndpointAuthMethod != "auto" {
client = &FullClient{
BaseClient: base,
TokenEndpointAuthMethod: config.TokenEndpointAuthMethod,
}
} else {
client = base
} }
return client return client
} }
// GetID returns the ID.
func (c *BaseClient) GetID() string {
return c.ID
}
// GetDescription returns the Description.
func (c *BaseClient) GetDescription() string {
if c.Description == "" {
c.Description = c.GetID()
}
return c.Description
}
// GetSecret returns the Secret.
func (c *BaseClient) GetSecret() algorithm.Digest {
return c.Secret
}
// GetSectorIdentifier returns the SectorIdentifier for this client.
func (c *BaseClient) GetSectorIdentifier() string {
return c.SectorIdentifier
}
// GetHashedSecret returns the Secret.
func (c *BaseClient) GetHashedSecret() (secret []byte) {
if c.Secret == nil {
return []byte(nil)
}
return []byte(c.Secret.Encode())
}
// GetRedirectURIs returns the RedirectURIs.
func (c *BaseClient) GetRedirectURIs() (redirectURIs []string) {
return c.RedirectURIs
}
// GetGrantTypes returns the GrantTypes.
func (c *BaseClient) GetGrantTypes() fosite.Arguments {
if len(c.GrantTypes) == 0 {
return fosite.Arguments{"authorization_code"}
}
return c.GrantTypes
}
// GetResponseTypes returns the ResponseTypes.
func (c *BaseClient) GetResponseTypes() fosite.Arguments {
if len(c.ResponseTypes) == 0 {
return fosite.Arguments{"code"}
}
return c.ResponseTypes
}
// GetScopes returns the Scopes.
func (c *BaseClient) GetScopes() fosite.Arguments {
return c.Scopes
}
// GetAudience returns the Audience.
func (c *BaseClient) GetAudience() fosite.Arguments {
return c.Audience
}
// GetResponseModes returns the valid response modes for this client.
//
// Implements the fosite.ResponseModeClient.
func (c *BaseClient) GetResponseModes() []fosite.ResponseModeType {
return c.ResponseModes
}
// GetUserinfoSigningAlgorithm returns the UserinfoSigningAlgorithm.
func (c *BaseClient) GetUserinfoSigningAlgorithm() string {
if c.UserinfoSigningAlgorithm == "" {
c.UserinfoSigningAlgorithm = SigningAlgorithmNone
}
return c.UserinfoSigningAlgorithm
}
// GetPAREnforcement returns EnforcePAR.
func (c *BaseClient) GetPAREnforcement() bool {
return c.EnforcePAR
}
// GetPKCEEnforcement returns EnforcePKCE.
func (c *BaseClient) GetPKCEEnforcement() bool {
return c.EnforcePKCE
}
// GetPKCEChallengeMethodEnforcement returns EnforcePKCEChallengeMethod.
func (c *BaseClient) GetPKCEChallengeMethodEnforcement() bool {
return c.EnforcePKCEChallengeMethod
}
// GetPKCEChallengeMethod returns PKCEChallengeMethod.
func (c *BaseClient) GetPKCEChallengeMethod() string {
return c.PKCEChallengeMethod
}
// GetAuthorizationPolicy returns Policy.
func (c *BaseClient) GetAuthorizationPolicy() authorization.Level {
return c.Policy
}
// GetConsentPolicy returns Consent.
func (c *BaseClient) GetConsentPolicy() ClientConsent {
return c.Consent
}
// GetConsentResponseBody returns the proper consent response body for this session.OIDCWorkflowSession.
func (c *BaseClient) GetConsentResponseBody(consent *model.OAuth2ConsentSession) ConsentGetResponseBody {
body := ConsentGetResponseBody{
ClientID: c.ID,
ClientDescription: c.Description,
PreConfiguration: c.Consent.Mode == ClientConsentModePreConfigured,
}
if consent != nil {
body.Scopes = consent.RequestedScopes
body.Audience = consent.RequestedAudience
}
return body
}
// IsPublic returns the value of the Public property.
func (c *BaseClient) IsPublic() bool {
return c.Public
}
// IsAuthenticationLevelSufficient returns if the provided authentication.Level is sufficient for the client of the AutheliaClient.
func (c *BaseClient) IsAuthenticationLevelSufficient(level authentication.Level) bool {
if level == authentication.NotAuthenticated {
return false
}
return authorization.IsAuthLevelSufficient(level, c.Policy)
}
// ValidatePKCEPolicy is a helper function to validate PKCE policy constraints on a per-client basis. // ValidatePKCEPolicy is a helper function to validate PKCE policy constraints on a per-client basis.
func (c *Client) ValidatePKCEPolicy(r fosite.Requester) (err error) { func (c *BaseClient) ValidatePKCEPolicy(r fosite.Requester) (err error) {
form := r.GetRequestForm() form := r.GetRequestForm()
if c.EnforcePKCE { if c.EnforcePKCE {
@ -70,7 +223,7 @@ func (c *Client) ValidatePKCEPolicy(r fosite.Requester) (err error) {
} }
// ValidatePARPolicy is a helper function to validate additional policy constraints on a per-client basis. // ValidatePARPolicy is a helper function to validate additional policy constraints on a per-client basis.
func (c *Client) ValidatePARPolicy(r fosite.Requester, prefix string) (err error) { func (c *BaseClient) ValidatePARPolicy(r fosite.Requester, prefix string) (err error) {
if c.EnforcePAR { if c.EnforcePAR {
if !IsPushedAuthorizedRequest(r, prefix) { if !IsPushedAuthorizedRequest(r, prefix) {
switch requestURI := r.GetRequestForm().Get(FormParameterRequestURI); requestURI { switch requestURI := r.GetRequestForm().Get(FormParameterRequestURI); requestURI {
@ -87,7 +240,7 @@ func (c *Client) ValidatePARPolicy(r fosite.Requester, prefix string) (err error
// ValidateResponseModePolicy is an additional check to the response mode parameter to ensure if it's omitted that the // ValidateResponseModePolicy is an additional check to the response mode parameter to ensure if it's omitted that the
// default response mode for the fosite.AuthorizeRequester is permitted. // default response mode for the fosite.AuthorizeRequester is permitted.
func (c *Client) ValidateResponseModePolicy(r fosite.AuthorizeRequester) (err error) { func (c *BaseClient) ValidateResponseModePolicy(r fosite.AuthorizeRequester) (err error) {
if r.GetResponseMode() != fosite.ResponseModeDefault { if r.GetResponseMode() != fosite.ResponseModeDefault {
return nil return nil
} }
@ -109,91 +262,52 @@ func (c *Client) ValidateResponseModePolicy(r fosite.AuthorizeRequester) (err er
return errorsx.WithStack(fosite.ErrUnsupportedResponseMode.WithHintf(`The request omitted the response_mode making the default response_mode "%s" based on the other authorization request parameters but registered OAuth 2.0 client doesn't support this response_mode`, m)) return errorsx.WithStack(fosite.ErrUnsupportedResponseMode.WithHintf(`The request omitted the response_mode making the default response_mode "%s" based on the other authorization request parameters but registered OAuth 2.0 client doesn't support this response_mode`, m))
} }
// IsAuthenticationLevelSufficient returns if the provided authentication.Level is sufficient for the client of the AutheliaClient. // GetRequestURIs is an array of request_uri values that are pre-registered by the RP for use at the OP. Servers MAY
func (c *Client) IsAuthenticationLevelSufficient(level authentication.Level) bool { // cache the contents of the files referenced by these URIs and not retrieve them at the time they are used in a request.
if level == authentication.NotAuthenticated { // OPs can require that request_uri values used be pre-registered with the require_request_uri_registration
return false // discovery parameter.
func (c *FullClient) GetRequestURIs() []string {
return c.RequestURIs
} }
return authorization.IsAuthLevelSufficient(level, c.Policy) // GetJSONWebKeys returns the JSON Web Key Set containing the public key used by the client to authenticate.
func (c *FullClient) GetJSONWebKeys() *jose.JSONWebKeySet {
return c.JSONWebKeys
} }
// GetSectorIdentifier returns the SectorIdentifier for this client. // GetJSONWebKeysURI returns the URL for lookup of JSON Web Key Set containing the
func (c *Client) GetSectorIdentifier() string { // public key used by the client to authenticate.
return c.SectorIdentifier func (c *FullClient) GetJSONWebKeysURI() string {
return c.JSONWebKeysURI
} }
// GetConsentResponseBody returns the proper consent response body for this session.OIDCWorkflowSession. // GetRequestObjectSigningAlgorithm returns the JWS [JWS] alg algorithm [JWA] that MUST be used for signing Request
func (c *Client) GetConsentResponseBody(consent *model.OAuth2ConsentSession) ConsentGetResponseBody { // Objects sent to the OP. All Request Objects from this Client MUST be rejected, if not signed with this algorithm.
body := ConsentGetResponseBody{ func (c *FullClient) GetRequestObjectSigningAlgorithm() string {
ClientID: c.ID, return c.RequestObjectSigningAlgorithm
ClientDescription: c.Description,
PreConfiguration: c.Consent.Mode == ClientConsentModePreConfigured,
} }
if consent != nil { // GetTokenEndpointAuthMethod returns the requested Client Authentication Method for the Token Endpoint. The options are
body.Scopes = consent.RequestedScopes // client_secret_post, client_secret_basic, client_secret_jwt, private_key_jwt, and none.
body.Audience = consent.RequestedAudience func (c *FullClient) GetTokenEndpointAuthMethod() string {
if c.TokenEndpointAuthMethod == "" {
if c.Public {
c.TokenEndpointAuthMethod = ClientAuthMethodNone
} else {
c.TokenEndpointAuthMethod = ClientAuthMethodClientSecretPost
}
} }
return body return c.TokenEndpointAuthMethod
} }
// GetID returns the ID. // GetTokenEndpointAuthSigningAlgorithm returns the JWS [JWS] alg algorithm [JWA] that MUST be used for signing the JWT
func (c *Client) GetID() string { // [JWT] used to authenticate the Client at the Token Endpoint for the private_key_jwt and client_secret_jwt
return c.ID // authentication methods.
func (c *FullClient) GetTokenEndpointAuthSigningAlgorithm() string {
if c.TokenEndpointAuthSigningAlgorithm == "" {
c.TokenEndpointAuthSigningAlgorithm = SigningAlgorithmRSAWithSHA256
} }
// GetHashedSecret returns the Secret. return c.TokenEndpointAuthSigningAlgorithm
func (c *Client) GetHashedSecret() (secret []byte) {
if c.Secret == nil {
return []byte(nil)
}
return []byte(c.Secret.Encode())
}
// GetRedirectURIs returns the RedirectURIs.
func (c *Client) GetRedirectURIs() (redirectURIs []string) {
return c.RedirectURIs
}
// GetGrantTypes returns the GrantTypes.
func (c *Client) GetGrantTypes() fosite.Arguments {
if len(c.GrantTypes) == 0 {
return fosite.Arguments{"authorization_code"}
}
return c.GrantTypes
}
// GetResponseTypes returns the ResponseTypes.
func (c *Client) GetResponseTypes() fosite.Arguments {
if len(c.ResponseTypes) == 0 {
return fosite.Arguments{"code"}
}
return c.ResponseTypes
}
// GetScopes returns the Scopes.
func (c *Client) GetScopes() fosite.Arguments {
return c.Scopes
}
// IsPublic returns the value of the Public property.
func (c *Client) IsPublic() bool {
return c.Public
}
// GetAudience returns the Audience.
func (c *Client) GetAudience() fosite.Arguments {
return c.Audience
}
// GetResponseModes returns the valid response modes for this client.
//
// Implements the fosite.ResponseModeClient.
func (c *Client) GetResponseModes() []fosite.ResponseModeType {
return c.ResponseModes
} }

View File

@ -7,6 +7,7 @@ import (
"github.com/ory/fosite" "github.com/ory/fosite"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"gopkg.in/square/go-jose.v2"
"github.com/authelia/authelia/v4/internal/authentication" "github.com/authelia/authelia/v4/internal/authentication"
"github.com/authelia/authelia/v4/internal/authorization" "github.com/authelia/authelia/v4/internal/authorization"
@ -15,36 +16,136 @@ import (
) )
func TestNewClient(t *testing.T) { func TestNewClient(t *testing.T) {
blankConfig := schema.OpenIDConnectClientConfiguration{} config := schema.OpenIDConnectClientConfiguration{}
blankClient := NewClient(blankConfig) client := NewClient(config)
assert.Equal(t, "", blankClient.ID) assert.Equal(t, "", client.GetID())
assert.Equal(t, "", blankClient.Description) assert.Equal(t, "", client.GetDescription())
assert.Equal(t, "", blankClient.Description) assert.Len(t, client.GetResponseModes(), 0)
assert.Len(t, blankClient.ResponseModes, 0) assert.Len(t, client.GetResponseTypes(), 1)
assert.Equal(t, "", client.GetSectorIdentifier())
exampleConfig := schema.OpenIDConnectClientConfiguration{ bclient, ok := client.(*BaseClient)
ID: "myapp", require.True(t, ok)
Description: "My App", assert.Equal(t, "", bclient.UserinfoSigningAlgorithm)
Policy: "two_factor", assert.Equal(t, SigningAlgorithmNone, client.GetUserinfoSigningAlgorithm())
Secret: MustDecodeSecret("$plaintext$abcdef"),
RedirectURIs: []string{"https://google.com/callback"}, _, ok = client.(*FullClient)
assert.False(t, ok)
config = schema.OpenIDConnectClientConfiguration{
ID: myclient,
Description: myclientdesc,
Policy: twofactor,
Secret: MustDecodeSecret(badsecret),
RedirectURIs: []string{examplecom},
Scopes: schema.DefaultOpenIDConnectClientConfiguration.Scopes, Scopes: schema.DefaultOpenIDConnectClientConfiguration.Scopes,
ResponseTypes: schema.DefaultOpenIDConnectClientConfiguration.ResponseTypes, ResponseTypes: schema.DefaultOpenIDConnectClientConfiguration.ResponseTypes,
GrantTypes: schema.DefaultOpenIDConnectClientConfiguration.GrantTypes, GrantTypes: schema.DefaultOpenIDConnectClientConfiguration.GrantTypes,
ResponseModes: schema.DefaultOpenIDConnectClientConfiguration.ResponseModes, ResponseModes: schema.DefaultOpenIDConnectClientConfiguration.ResponseModes,
} }
exampleClient := NewClient(exampleConfig) client = NewClient(config)
assert.Equal(t, "myapp", exampleClient.ID) assert.Equal(t, myclient, client.GetID())
require.Len(t, exampleClient.ResponseModes, 3) require.Len(t, client.GetResponseModes(), 1)
assert.Equal(t, fosite.ResponseModeFormPost, exampleClient.ResponseModes[0]) assert.Equal(t, fosite.ResponseModeFormPost, client.GetResponseModes()[0])
assert.Equal(t, fosite.ResponseModeQuery, exampleClient.ResponseModes[1]) assert.Equal(t, authorization.TwoFactor, client.GetAuthorizationPolicy())
assert.Equal(t, fosite.ResponseModeFragment, exampleClient.ResponseModes[2])
assert.Equal(t, authorization.TwoFactor, exampleClient.Policy) config = schema.OpenIDConnectClientConfiguration{
TokenEndpointAuthMethod: ClientAuthMethodClientSecretBasic,
}
client = NewClient(config)
fclient, ok := client.(*FullClient)
var niljwks *jose.JSONWebKeySet
require.True(t, ok)
assert.Equal(t, "", fclient.UserinfoSigningAlgorithm)
assert.Equal(t, ClientAuthMethodClientSecretBasic, fclient.TokenEndpointAuthMethod)
assert.Equal(t, ClientAuthMethodClientSecretBasic, fclient.GetTokenEndpointAuthMethod())
assert.Equal(t, SigningAlgorithmNone, client.GetUserinfoSigningAlgorithm())
assert.Equal(t, "", fclient.TokenEndpointAuthSigningAlgorithm)
assert.Equal(t, SigningAlgorithmRSAWithSHA256, fclient.GetTokenEndpointAuthSigningAlgorithm())
assert.Equal(t, "", fclient.RequestObjectSigningAlgorithm)
assert.Equal(t, "", fclient.GetRequestObjectSigningAlgorithm())
assert.Equal(t, "", fclient.JSONWebKeysURI)
assert.Equal(t, "", fclient.GetJSONWebKeysURI())
assert.Equal(t, niljwks, fclient.JSONWebKeys)
assert.Equal(t, niljwks, fclient.GetJSONWebKeys())
assert.Equal(t, []string(nil), fclient.RequestURIs)
assert.Equal(t, []string(nil), fclient.GetRequestURIs())
}
func TestBaseClient_ValidatePARPolicy(t *testing.T) {
testCases := []struct {
name string
client *BaseClient
have *fosite.Request
expected string
}{
{
"ShouldNotEnforcePAR",
&BaseClient{
EnforcePAR: false,
},
&fosite.Request{},
"",
},
{
"ShouldEnforcePARAndErrorWithoutCorrectRequestURI",
&BaseClient{
EnforcePAR: true,
},
&fosite.Request{
Form: map[string][]string{
FormParameterRequestURI: {"https://google.com"},
},
},
"invalid_request",
},
{
"ShouldEnforcePARAndErrorWithEmptyRequestURI",
&BaseClient{
EnforcePAR: true,
},
&fosite.Request{
Form: map[string][]string{
FormParameterRequestURI: {""},
},
},
"invalid_request",
},
{
"ShouldEnforcePARAndNotErrorWithCorrectRequestURI",
&BaseClient{
EnforcePAR: true,
},
&fosite.Request{
Form: map[string][]string{
FormParameterRequestURI: {urnPARPrefix + "abc"},
},
},
"",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := tc.client.ValidatePARPolicy(tc.have, urnPARPrefix)
switch tc.expected {
case "":
assert.NoError(t, err)
default:
assert.EqualError(t, err, tc.expected)
}
})
}
} }
func TestIsAuthenticationLevelSufficient(t *testing.T) { func TestIsAuthenticationLevelSufficient(t *testing.T) {
c := Client{} c := &FullClient{BaseClient: &BaseClient{}}
c.Policy = authorization.Bypass c.Policy = authorization.Bypass
assert.False(t, c.IsAuthenticationLevelSufficient(authentication.NotAuthenticated)) assert.False(t, c.IsAuthenticationLevelSufficient(authentication.NotAuthenticated))
@ -68,7 +169,7 @@ func TestIsAuthenticationLevelSufficient(t *testing.T) {
} }
func TestClient_GetConsentResponseBody(t *testing.T) { func TestClient_GetConsentResponseBody(t *testing.T) {
c := Client{} c := &FullClient{BaseClient: &BaseClient{}}
consentRequestBody := c.GetConsentResponseBody(nil) consentRequestBody := c.GetConsentResponseBody(nil)
assert.Equal(t, "", consentRequestBody.ClientID) assert.Equal(t, "", consentRequestBody.ClientID)
@ -76,56 +177,56 @@ func TestClient_GetConsentResponseBody(t *testing.T) {
assert.Equal(t, []string(nil), consentRequestBody.Scopes) assert.Equal(t, []string(nil), consentRequestBody.Scopes)
assert.Equal(t, []string(nil), consentRequestBody.Audience) assert.Equal(t, []string(nil), consentRequestBody.Audience)
c.ID = "myclient" c.ID = myclient
c.Description = "My Client" c.Description = myclientdesc
consent := &model.OAuth2ConsentSession{ consent := &model.OAuth2ConsentSession{
RequestedAudience: []string{"https://example.com"}, RequestedAudience: []string{examplecom},
RequestedScopes: []string{"openid", "groups"}, RequestedScopes: []string{ScopeOpenID, ScopeGroups},
} }
expectedScopes := []string{"openid", "groups"} expectedScopes := []string{ScopeOpenID, ScopeGroups}
expectedAudiences := []string{"https://example.com"} expectedAudiences := []string{examplecom}
consentRequestBody = c.GetConsentResponseBody(consent) consentRequestBody = c.GetConsentResponseBody(consent)
assert.Equal(t, "myclient", consentRequestBody.ClientID) assert.Equal(t, myclient, consentRequestBody.ClientID)
assert.Equal(t, "My Client", consentRequestBody.ClientDescription) assert.Equal(t, myclientdesc, consentRequestBody.ClientDescription)
assert.Equal(t, expectedScopes, consentRequestBody.Scopes) assert.Equal(t, expectedScopes, consentRequestBody.Scopes)
assert.Equal(t, expectedAudiences, consentRequestBody.Audience) assert.Equal(t, expectedAudiences, consentRequestBody.Audience)
} }
func TestClient_GetAudience(t *testing.T) { func TestClient_GetAudience(t *testing.T) {
c := Client{} c := &FullClient{BaseClient: &BaseClient{}}
audience := c.GetAudience() audience := c.GetAudience()
assert.Len(t, audience, 0) assert.Len(t, audience, 0)
c.Audience = []string{"https://example.com"} c.Audience = []string{examplecom}
audience = c.GetAudience() audience = c.GetAudience()
require.Len(t, audience, 1) require.Len(t, audience, 1)
assert.Equal(t, "https://example.com", audience[0]) assert.Equal(t, examplecom, audience[0])
} }
func TestClient_GetScopes(t *testing.T) { func TestClient_GetScopes(t *testing.T) {
c := Client{} c := &FullClient{BaseClient: &BaseClient{}}
scopes := c.GetScopes() scopes := c.GetScopes()
assert.Len(t, scopes, 0) assert.Len(t, scopes, 0)
c.Scopes = []string{"openid"} c.Scopes = []string{ScopeOpenID}
scopes = c.GetScopes() scopes = c.GetScopes()
require.Len(t, scopes, 1) require.Len(t, scopes, 1)
assert.Equal(t, "openid", scopes[0]) assert.Equal(t, ScopeOpenID, scopes[0])
} }
func TestClient_GetGrantTypes(t *testing.T) { func TestClient_GetGrantTypes(t *testing.T) {
c := Client{} c := &FullClient{BaseClient: &BaseClient{}}
grantTypes := c.GetGrantTypes() grantTypes := c.GetGrantTypes()
require.Len(t, grantTypes, 1) require.Len(t, grantTypes, 1)
assert.Equal(t, "authorization_code", grantTypes[0]) assert.Equal(t, GrantTypeAuthorizationCode, grantTypes[0])
c.GrantTypes = []string{"device_code"} c.GrantTypes = []string{"device_code"}
@ -135,55 +236,55 @@ func TestClient_GetGrantTypes(t *testing.T) {
} }
func TestClient_Hashing(t *testing.T) { func TestClient_Hashing(t *testing.T) {
c := Client{} c := &FullClient{BaseClient: &BaseClient{}}
hashedSecret := c.GetHashedSecret() hashedSecret := c.GetHashedSecret()
assert.Equal(t, []byte(nil), hashedSecret) assert.Equal(t, []byte(nil), hashedSecret)
c.Secret = MustDecodeSecret("$plaintext$a_bad_secret") c.Secret = MustDecodeSecret(badsecret)
assert.True(t, c.Secret.MatchBytes([]byte("a_bad_secret"))) assert.True(t, c.Secret.MatchBytes([]byte("a_bad_secret")))
} }
func TestClient_GetHashedSecret(t *testing.T) { func TestClient_GetHashedSecret(t *testing.T) {
c := Client{} c := &FullClient{BaseClient: &BaseClient{}}
hashedSecret := c.GetHashedSecret() hashedSecret := c.GetHashedSecret()
assert.Equal(t, []byte(nil), hashedSecret) assert.Equal(t, []byte(nil), hashedSecret)
c.Secret = MustDecodeSecret("$plaintext$a_bad_secret") c.Secret = MustDecodeSecret(badsecret)
hashedSecret = c.GetHashedSecret() hashedSecret = c.GetHashedSecret()
assert.Equal(t, []byte("$plaintext$a_bad_secret"), hashedSecret) assert.Equal(t, []byte(badsecret), hashedSecret)
} }
func TestClient_GetID(t *testing.T) { func TestClient_GetID(t *testing.T) {
c := Client{} c := &FullClient{BaseClient: &BaseClient{}}
id := c.GetID() id := c.GetID()
assert.Equal(t, "", id) assert.Equal(t, "", id)
c.ID = "myid" c.ID = myclient
id = c.GetID() id = c.GetID()
assert.Equal(t, "myid", id) assert.Equal(t, myclient, id)
} }
func TestClient_GetRedirectURIs(t *testing.T) { func TestClient_GetRedirectURIs(t *testing.T) {
c := Client{} c := &FullClient{BaseClient: &BaseClient{}}
redirectURIs := c.GetRedirectURIs() redirectURIs := c.GetRedirectURIs()
require.Len(t, redirectURIs, 0) require.Len(t, redirectURIs, 0)
c.RedirectURIs = []string{"https://example.com/oauth2/callback"} c.RedirectURIs = []string{examplecom}
redirectURIs = c.GetRedirectURIs() redirectURIs = c.GetRedirectURIs()
require.Len(t, redirectURIs, 1) require.Len(t, redirectURIs, 1)
assert.Equal(t, "https://example.com/oauth2/callback", redirectURIs[0]) assert.Equal(t, examplecom, redirectURIs[0])
} }
func TestClient_GetResponseModes(t *testing.T) { func TestClient_GetResponseModes(t *testing.T) {
c := Client{} c := &FullClient{BaseClient: &BaseClient{}}
responseModes := c.GetResponseModes() responseModes := c.GetResponseModes()
require.Len(t, responseModes, 0) require.Len(t, responseModes, 0)
@ -202,18 +303,18 @@ func TestClient_GetResponseModes(t *testing.T) {
} }
func TestClient_GetResponseTypes(t *testing.T) { func TestClient_GetResponseTypes(t *testing.T) {
c := Client{} c := &FullClient{BaseClient: &BaseClient{}}
responseTypes := c.GetResponseTypes() responseTypes := c.GetResponseTypes()
require.Len(t, responseTypes, 1) require.Len(t, responseTypes, 1)
assert.Equal(t, "code", responseTypes[0]) assert.Equal(t, ResponseTypeAuthorizationCodeFlow, responseTypes[0])
c.ResponseTypes = []string{"code", "id_token"} c.ResponseTypes = []string{ResponseTypeAuthorizationCodeFlow, ResponseTypeImplicitFlowIDToken}
responseTypes = c.GetResponseTypes() responseTypes = c.GetResponseTypes()
require.Len(t, responseTypes, 2) require.Len(t, responseTypes, 2)
assert.Equal(t, "code", responseTypes[0]) assert.Equal(t, ResponseTypeAuthorizationCodeFlow, responseTypes[0])
assert.Equal(t, "id_token", responseTypes[1]) assert.Equal(t, ResponseTypeImplicitFlowIDToken, responseTypes[1])
} }
func TestNewClientPKCE(t *testing.T) { func TestNewClientPKCE(t *testing.T) {
@ -290,9 +391,9 @@ func TestNewClientPKCE(t *testing.T) {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
client := NewClient(tc.have) client := NewClient(tc.have)
assert.Equal(t, tc.expectedEnforcePKCE, client.EnforcePKCE) assert.Equal(t, tc.expectedEnforcePKCE, client.GetPKCEEnforcement())
assert.Equal(t, tc.expectedEnforcePKCEChallengeMethod, client.EnforcePKCEChallengeMethod) assert.Equal(t, tc.expectedEnforcePKCEChallengeMethod, client.GetPKCEChallengeMethodEnforcement())
assert.Equal(t, tc.expected, client.PKCEChallengeMethod) assert.Equal(t, tc.expected, client.GetPKCEChallengeMethod())
if tc.r != nil { if tc.r != nil {
err := client.ValidatePKCEPolicy(tc.r) err := client.ValidatePKCEPolicy(tc.r)
@ -355,7 +456,7 @@ func TestNewClientPAR(t *testing.T) {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
client := NewClient(tc.have) client := NewClient(tc.have)
assert.Equal(t, tc.expected, client.EnforcePAR) assert.Equal(t, tc.expected, client.GetPAREnforcement())
if tc.r != nil { if tc.r != nil {
err := client.ValidatePARPolicy(tc.r, urnPARPrefix) err := client.ValidatePARPolicy(tc.r, urnPARPrefix)
@ -437,7 +538,7 @@ func TestNewClientResponseModes(t *testing.T) {
} }
func TestClient_IsPublic(t *testing.T) { func TestClient_IsPublic(t *testing.T) {
c := Client{} c := &FullClient{BaseClient: &BaseClient{}}
assert.False(t, c.IsPublic()) assert.False(t, c.IsPublic())

View File

@ -169,12 +169,6 @@ type LifespanConfig struct {
RefreshToken time.Duration RefreshToken time.Duration
} }
const (
PromptNone = none
PromptLogin = "login"
PromptConsent = "consent"
)
// LoadHandlers reloads the handlers based on the current configuration. // LoadHandlers reloads the handlers based on the current configuration.
func (c *Config) LoadHandlers(store *Store, strategy jwt.Signer) { func (c *Config) LoadHandlers(store *Store, strategy jwt.Signer) {
validator := openid.NewOpenIDConnectRequestValidator(strategy, c) validator := openid.NewOpenIDConnectRequestValidator(strategy, c)

View File

@ -69,15 +69,12 @@ const (
GrantTypeImplicit = implicit GrantTypeImplicit = implicit
GrantTypeRefreshToken = "refresh_token" GrantTypeRefreshToken = "refresh_token"
GrantTypeAuthorizationCode = "authorization_code" GrantTypeAuthorizationCode = "authorization_code"
GrantTypePassword = "password"
GrantTypeClientCredentials = "client_credentials"
) )
// Client Auth Method strings. // Client Auth Method strings.
const ( const (
ClientAuthMethodClientSecretBasic = "client_secret_basic" ClientAuthMethodClientSecretBasic = "client_secret_basic"
ClientAuthMethodClientSecretPost = "client_secret_post" ClientAuthMethodClientSecretPost = "client_secret_post"
ClientAuthMethodClientSecretJWT = "client_secret_jwt"
ClientAuthMethodNone = "none" ClientAuthMethodNone = "none"
) )
@ -117,6 +114,13 @@ const (
FormParameterCodeChallengeMethod = "code_challenge_method" FormParameterCodeChallengeMethod = "code_challenge_method"
) )
const (
PromptNone = none
PromptLogin = "login"
PromptConsent = "consent"
// PromptCreate = "create" // This prompt value is currently unused.
)
// Endpoints. // Endpoints.
const ( const (
EndpointAuthorization = "authorization" EndpointAuthorization = "authorization"

View File

@ -0,0 +1,12 @@
package oidc
const (
myclient = "myclient"
myclientdesc = "My Client"
onefactor = "one_factor"
twofactor = "two_factor"
examplecom = "https://example.com"
examplecomsid = "example.com"
badsecret = "$plaintext$a_bad_secret"
badhmac = "asbdhaaskmdlkamdklasmdlkams"
)

View File

@ -5,11 +5,13 @@ import (
) )
// NewOpenIDConnectWellKnownConfiguration generates a new OpenIDConnectWellKnownConfiguration. // NewOpenIDConnectWellKnownConfiguration generates a new OpenIDConnectWellKnownConfiguration.
func NewOpenIDConnectWellKnownConfiguration(c *schema.OpenIDConnectConfiguration, clients map[string]*Client) (config OpenIDConnectWellKnownConfiguration) { func NewOpenIDConnectWellKnownConfiguration(c *schema.OpenIDConnectConfiguration) (config OpenIDConnectWellKnownConfiguration) {
config = OpenIDConnectWellKnownConfiguration{ config = OpenIDConnectWellKnownConfiguration{
OAuth2WellKnownConfiguration: OAuth2WellKnownConfiguration{
CommonDiscoveryOptions: CommonDiscoveryOptions{ CommonDiscoveryOptions: CommonDiscoveryOptions{
SubjectTypesSupported: []string{ SubjectTypesSupported: []string{
SubjectTypePublic, SubjectTypePublic,
SubjectTypePairwise,
}, },
ResponseTypesSupported: []string{ ResponseTypesSupported: []string{
ResponseTypeAuthorizationCodeFlow, ResponseTypeAuthorizationCodeFlow,
@ -60,7 +62,6 @@ func NewOpenIDConnectWellKnownConfiguration(c *schema.OpenIDConnectConfiguration
TokenEndpointAuthMethodsSupported: []string{ TokenEndpointAuthMethodsSupported: []string{
ClientAuthMethodClientSecretBasic, ClientAuthMethodClientSecretBasic,
ClientAuthMethodClientSecretPost, ClientAuthMethodClientSecretPost,
ClientAuthMethodClientSecretJWT,
ClientAuthMethodNone, ClientAuthMethodNone,
}, },
}, },
@ -69,6 +70,11 @@ func NewOpenIDConnectWellKnownConfiguration(c *schema.OpenIDConnectConfiguration
PKCEChallengeMethodSHA256, PKCEChallengeMethodSHA256,
}, },
}, },
OAuth2PushedAuthorizationDiscoveryOptions: &OAuth2PushedAuthorizationDiscoveryOptions{
RequirePushedAuthorizationRequests: c.PAR.Enforce,
},
},
OpenIDConnectDiscoveryOptions: OpenIDConnectDiscoveryOptions{ OpenIDConnectDiscoveryOptions: OpenIDConnectDiscoveryOptions{
IDTokenSigningAlgValuesSupported: []string{ IDTokenSigningAlgValuesSupported: []string{
SigningAlgorithmRSAWithSHA256, SigningAlgorithmRSAWithSHA256,
@ -77,30 +83,15 @@ func NewOpenIDConnectWellKnownConfiguration(c *schema.OpenIDConnectConfiguration
SigningAlgorithmNone, SigningAlgorithmNone,
SigningAlgorithmRSAWithSHA256, SigningAlgorithmRSAWithSHA256,
}, },
RequestObjectSigningAlgValuesSupported: []string{ },
SigningAlgorithmNone, OpenIDConnectFrontChannelLogoutDiscoveryOptions: &OpenIDConnectFrontChannelLogoutDiscoveryOptions{},
SigningAlgorithmRSAWithSHA256, OpenIDConnectBackChannelLogoutDiscoveryOptions: &OpenIDConnectBackChannelLogoutDiscoveryOptions{},
OpenIDConnectPromptCreateDiscoveryOptions: &OpenIDConnectPromptCreateDiscoveryOptions{
PromptValuesSupported: []string{
PromptNone,
PromptConsent,
}, },
}, },
PushedAuthorizationDiscoveryOptions: PushedAuthorizationDiscoveryOptions{
RequirePushedAuthorizationRequests: c.PAR.Enforce,
},
}
var pairwise, public bool
for _, client := range clients {
if pairwise && public {
break
}
if client.SectorIdentifier != "" {
pairwise = true
}
}
if pairwise {
config.SubjectTypesSupported = append(config.SubjectTypesSupported, SubjectTypePairwise)
} }
if c.EnablePKCEPlainChallenge { if c.EnablePKCEPlainChallenge {
@ -109,3 +100,93 @@ func NewOpenIDConnectWellKnownConfiguration(c *schema.OpenIDConnectConfiguration
return config return config
} }
// Copy the values of the OAuth2WellKnownConfiguration and return it as a new struct.
func (opts OAuth2WellKnownConfiguration) Copy() (optsCopy OAuth2WellKnownConfiguration) {
optsCopy = OAuth2WellKnownConfiguration{
CommonDiscoveryOptions: opts.CommonDiscoveryOptions,
OAuth2DiscoveryOptions: opts.OAuth2DiscoveryOptions,
}
if opts.OAuth2DeviceAuthorizationGrantDiscoveryOptions != nil {
optsCopy.OAuth2DeviceAuthorizationGrantDiscoveryOptions = &OAuth2DeviceAuthorizationGrantDiscoveryOptions{}
*optsCopy.OAuth2DeviceAuthorizationGrantDiscoveryOptions = *opts.OAuth2DeviceAuthorizationGrantDiscoveryOptions
}
if opts.OAuth2MutualTLSClientAuthenticationDiscoveryOptions != nil {
optsCopy.OAuth2MutualTLSClientAuthenticationDiscoveryOptions = &OAuth2MutualTLSClientAuthenticationDiscoveryOptions{}
*optsCopy.OAuth2MutualTLSClientAuthenticationDiscoveryOptions = *opts.OAuth2MutualTLSClientAuthenticationDiscoveryOptions
}
if opts.OAuth2IssuerIdentificationDiscoveryOptions != nil {
optsCopy.OAuth2IssuerIdentificationDiscoveryOptions = &OAuth2IssuerIdentificationDiscoveryOptions{}
*optsCopy.OAuth2IssuerIdentificationDiscoveryOptions = *opts.OAuth2IssuerIdentificationDiscoveryOptions
}
if opts.OAuth2JWTIntrospectionResponseDiscoveryOptions != nil {
optsCopy.OAuth2JWTIntrospectionResponseDiscoveryOptions = &OAuth2JWTIntrospectionResponseDiscoveryOptions{}
*optsCopy.OAuth2JWTIntrospectionResponseDiscoveryOptions = *opts.OAuth2JWTIntrospectionResponseDiscoveryOptions
}
if opts.OAuth2JWTSecuredAuthorizationRequestDiscoveryOptions != nil {
optsCopy.OAuth2JWTSecuredAuthorizationRequestDiscoveryOptions = &OAuth2JWTSecuredAuthorizationRequestDiscoveryOptions{}
*optsCopy.OAuth2JWTSecuredAuthorizationRequestDiscoveryOptions = *opts.OAuth2JWTSecuredAuthorizationRequestDiscoveryOptions
}
if opts.OAuth2PushedAuthorizationDiscoveryOptions != nil {
optsCopy.OAuth2PushedAuthorizationDiscoveryOptions = &OAuth2PushedAuthorizationDiscoveryOptions{}
*optsCopy.OAuth2PushedAuthorizationDiscoveryOptions = *opts.OAuth2PushedAuthorizationDiscoveryOptions
}
return optsCopy
}
// Copy the values of the OpenIDConnectWellKnownConfiguration and return it as a new struct.
func (opts OpenIDConnectWellKnownConfiguration) Copy() (optsCopy OpenIDConnectWellKnownConfiguration) {
optsCopy = OpenIDConnectWellKnownConfiguration{
OAuth2WellKnownConfiguration: opts.OAuth2WellKnownConfiguration.Copy(),
OpenIDConnectDiscoveryOptions: opts.OpenIDConnectDiscoveryOptions,
}
if opts.OpenIDConnectFrontChannelLogoutDiscoveryOptions != nil {
optsCopy.OpenIDConnectFrontChannelLogoutDiscoveryOptions = &OpenIDConnectFrontChannelLogoutDiscoveryOptions{}
*optsCopy.OpenIDConnectFrontChannelLogoutDiscoveryOptions = *opts.OpenIDConnectFrontChannelLogoutDiscoveryOptions
}
if opts.OpenIDConnectBackChannelLogoutDiscoveryOptions != nil {
optsCopy.OpenIDConnectBackChannelLogoutDiscoveryOptions = &OpenIDConnectBackChannelLogoutDiscoveryOptions{}
*optsCopy.OpenIDConnectBackChannelLogoutDiscoveryOptions = *opts.OpenIDConnectBackChannelLogoutDiscoveryOptions
}
if opts.OpenIDConnectSessionManagementDiscoveryOptions != nil {
optsCopy.OpenIDConnectSessionManagementDiscoveryOptions = &OpenIDConnectSessionManagementDiscoveryOptions{}
*optsCopy.OpenIDConnectSessionManagementDiscoveryOptions = *opts.OpenIDConnectSessionManagementDiscoveryOptions
}
if opts.OpenIDConnectRPInitiatedLogoutDiscoveryOptions != nil {
optsCopy.OpenIDConnectRPInitiatedLogoutDiscoveryOptions = &OpenIDConnectRPInitiatedLogoutDiscoveryOptions{}
*optsCopy.OpenIDConnectRPInitiatedLogoutDiscoveryOptions = *opts.OpenIDConnectRPInitiatedLogoutDiscoveryOptions
}
if opts.OpenIDConnectPromptCreateDiscoveryOptions != nil {
optsCopy.OpenIDConnectPromptCreateDiscoveryOptions = &OpenIDConnectPromptCreateDiscoveryOptions{}
*optsCopy.OpenIDConnectPromptCreateDiscoveryOptions = *opts.OpenIDConnectPromptCreateDiscoveryOptions
}
if opts.OpenIDConnectClientInitiatedBackChannelAuthFlowDiscoveryOptions != nil {
optsCopy.OpenIDConnectClientInitiatedBackChannelAuthFlowDiscoveryOptions = &OpenIDConnectClientInitiatedBackChannelAuthFlowDiscoveryOptions{}
*optsCopy.OpenIDConnectClientInitiatedBackChannelAuthFlowDiscoveryOptions = *opts.OpenIDConnectClientInitiatedBackChannelAuthFlowDiscoveryOptions
}
if opts.OpenIDConnectJWTSecuredAuthorizationResponseModeDiscoveryOptions != nil {
optsCopy.OpenIDConnectJWTSecuredAuthorizationResponseModeDiscoveryOptions = &OpenIDConnectJWTSecuredAuthorizationResponseModeDiscoveryOptions{}
*optsCopy.OpenIDConnectJWTSecuredAuthorizationResponseModeDiscoveryOptions = *opts.OpenIDConnectJWTSecuredAuthorizationResponseModeDiscoveryOptions
}
if opts.OpenIDFederationDiscoveryOptions != nil {
optsCopy.OpenIDFederationDiscoveryOptions = &OpenIDFederationDiscoveryOptions{}
*optsCopy.OpenIDFederationDiscoveryOptions = *opts.OpenIDFederationDiscoveryOptions
}
return optsCopy
}

View File

@ -13,51 +13,51 @@ func TestNewOpenIDConnectWellKnownConfiguration(t *testing.T) {
desc string desc string
pkcePlainChallenge bool pkcePlainChallenge bool
enforcePAR bool enforcePAR bool
clients map[string]*Client clients map[string]Client
expectCodeChallengeMethodsSupported, expectSubjectTypesSupported []string expectCodeChallengeMethodsSupported, expectSubjectTypesSupported []string
}{ }{
{ {
desc: "ShouldHaveChallengeMethodsS256ANDSubjectTypesSupportedPublic", desc: "ShouldHaveChallengeMethodsS256ANDSubjectTypesSupportedPublic",
pkcePlainChallenge: false, pkcePlainChallenge: false,
clients: map[string]*Client{"a": {}}, clients: map[string]Client{"a": &BaseClient{}},
expectCodeChallengeMethodsSupported: []string{PKCEChallengeMethodSHA256}, expectCodeChallengeMethodsSupported: []string{PKCEChallengeMethodSHA256},
expectSubjectTypesSupported: []string{SubjectTypePublic}, expectSubjectTypesSupported: []string{SubjectTypePublic, SubjectTypePairwise},
}, },
{ {
desc: "ShouldHaveChallengeMethodsS256PlainANDSubjectTypesSupportedPublic", desc: "ShouldHaveChallengeMethodsS256PlainANDSubjectTypesSupportedPublic",
pkcePlainChallenge: true, pkcePlainChallenge: true,
clients: map[string]*Client{"a": {}}, clients: map[string]Client{"a": &BaseClient{}},
expectCodeChallengeMethodsSupported: []string{PKCEChallengeMethodSHA256, PKCEChallengeMethodPlain}, expectCodeChallengeMethodsSupported: []string{PKCEChallengeMethodSHA256, PKCEChallengeMethodPlain},
expectSubjectTypesSupported: []string{SubjectTypePublic}, expectSubjectTypesSupported: []string{SubjectTypePublic, SubjectTypePairwise},
}, },
{ {
desc: "ShouldHaveChallengeMethodsS256ANDSubjectTypesSupportedPublicPairwise", desc: "ShouldHaveChallengeMethodsS256ANDSubjectTypesSupportedPublicPairwise",
pkcePlainChallenge: false, pkcePlainChallenge: false,
clients: map[string]*Client{"a": {SectorIdentifier: "yes"}}, clients: map[string]Client{"a": &BaseClient{SectorIdentifier: "yes"}},
expectCodeChallengeMethodsSupported: []string{PKCEChallengeMethodSHA256}, expectCodeChallengeMethodsSupported: []string{PKCEChallengeMethodSHA256},
expectSubjectTypesSupported: []string{SubjectTypePublic, SubjectTypePairwise}, expectSubjectTypesSupported: []string{SubjectTypePublic, SubjectTypePairwise},
}, },
{ {
desc: "ShouldHaveChallengeMethodsS256PlainANDSubjectTypesSupportedPublicPairwise", desc: "ShouldHaveChallengeMethodsS256PlainANDSubjectTypesSupportedPublicPairwise",
pkcePlainChallenge: true, pkcePlainChallenge: true,
clients: map[string]*Client{"a": {SectorIdentifier: "yes"}}, clients: map[string]Client{"a": &BaseClient{SectorIdentifier: "yes"}},
expectCodeChallengeMethodsSupported: []string{PKCEChallengeMethodSHA256, PKCEChallengeMethodPlain}, expectCodeChallengeMethodsSupported: []string{PKCEChallengeMethodSHA256, PKCEChallengeMethodPlain},
expectSubjectTypesSupported: []string{SubjectTypePublic, SubjectTypePairwise}, expectSubjectTypesSupported: []string{SubjectTypePublic, SubjectTypePairwise},
}, },
{ {
desc: "ShouldHaveTokenAuthMethodsNone", desc: "ShouldHaveTokenAuthMethodsNone",
pkcePlainChallenge: true, pkcePlainChallenge: true,
clients: map[string]*Client{"a": {SectorIdentifier: "yes"}}, clients: map[string]Client{"a": &BaseClient{SectorIdentifier: "yes"}},
expectCodeChallengeMethodsSupported: []string{PKCEChallengeMethodSHA256, PKCEChallengeMethodPlain}, expectCodeChallengeMethodsSupported: []string{PKCEChallengeMethodSHA256, PKCEChallengeMethodPlain},
expectSubjectTypesSupported: []string{SubjectTypePublic, SubjectTypePairwise}, expectSubjectTypesSupported: []string{SubjectTypePublic, SubjectTypePairwise},
}, },
{ {
desc: "ShouldHaveTokenAuthMethodsNone", desc: "ShouldHaveTokenAuthMethodsNone",
pkcePlainChallenge: true, pkcePlainChallenge: true,
clients: map[string]*Client{ clients: map[string]Client{
"a": {SectorIdentifier: "yes"}, "a": &BaseClient{SectorIdentifier: "yes"},
"b": {SectorIdentifier: "yes"}, "b": &BaseClient{SectorIdentifier: "yes"},
}, },
expectCodeChallengeMethodsSupported: []string{PKCEChallengeMethodSHA256, PKCEChallengeMethodPlain}, expectCodeChallengeMethodsSupported: []string{PKCEChallengeMethodSHA256, PKCEChallengeMethodPlain},
expectSubjectTypesSupported: []string{SubjectTypePublic, SubjectTypePairwise}, expectSubjectTypesSupported: []string{SubjectTypePublic, SubjectTypePairwise},
@ -73,7 +73,7 @@ func TestNewOpenIDConnectWellKnownConfiguration(t *testing.T) {
}, },
} }
actual := NewOpenIDConnectWellKnownConfiguration(&c, tc.clients) actual := NewOpenIDConnectWellKnownConfiguration(&c)
for _, codeChallengeMethod := range tc.expectCodeChallengeMethodsSupported { for _, codeChallengeMethod := range tc.expectCodeChallengeMethodsSupported {
assert.Contains(t, actual.CodeChallengeMethodsSupported, codeChallengeMethod) assert.Contains(t, actual.CodeChallengeMethodsSupported, codeChallengeMethod)
} }

View File

@ -37,17 +37,14 @@ func NewOpenIDConnectProvider(config *schema.OpenIDConnectConfiguration, store s
provider.Config.LoadHandlers(provider.Store, provider.KeyManager.Strategy()) provider.Config.LoadHandlers(provider.Store, provider.KeyManager.Strategy())
provider.discovery = NewOpenIDConnectWellKnownConfiguration(config, provider.Store.clients) provider.discovery = NewOpenIDConnectWellKnownConfiguration(config)
return provider, nil return provider, nil
} }
// GetOAuth2WellKnownConfiguration returns the discovery document for the OAuth Configuration. // GetOAuth2WellKnownConfiguration returns the discovery document for the OAuth Configuration.
func (p *OpenIDConnectProvider) GetOAuth2WellKnownConfiguration(issuer string) OAuth2WellKnownConfiguration { func (p *OpenIDConnectProvider) GetOAuth2WellKnownConfiguration(issuer string) OAuth2WellKnownConfiguration {
options := OAuth2WellKnownConfiguration{ options := p.discovery.OAuth2WellKnownConfiguration.Copy()
CommonDiscoveryOptions: p.discovery.CommonDiscoveryOptions,
OAuth2DiscoveryOptions: p.discovery.OAuth2DiscoveryOptions,
}
options.Issuer = issuer options.Issuer = issuer
@ -63,13 +60,7 @@ func (p *OpenIDConnectProvider) GetOAuth2WellKnownConfiguration(issuer string) O
// GetOpenIDConnectWellKnownConfiguration returns the discovery document for the OpenID Configuration. // GetOpenIDConnectWellKnownConfiguration returns the discovery document for the OpenID Configuration.
func (p *OpenIDConnectProvider) GetOpenIDConnectWellKnownConfiguration(issuer string) OpenIDConnectWellKnownConfiguration { func (p *OpenIDConnectProvider) GetOpenIDConnectWellKnownConfiguration(issuer string) OpenIDConnectWellKnownConfiguration {
options := OpenIDConnectWellKnownConfiguration{ options := p.discovery.Copy()
CommonDiscoveryOptions: p.discovery.CommonDiscoveryOptions,
OAuth2DiscoveryOptions: p.discovery.OAuth2DiscoveryOptions,
OpenIDConnectDiscoveryOptions: p.discovery.OpenIDConnectDiscoveryOptions,
OpenIDConnectFrontChannelLogoutDiscoveryOptions: p.discovery.OpenIDConnectFrontChannelLogoutDiscoveryOptions,
OpenIDConnectBackChannelLogoutDiscoveryOptions: p.discovery.OpenIDConnectBackChannelLogoutDiscoveryOptions,
}
options.Issuer = issuer options.Issuer = issuer

View File

@ -27,15 +27,15 @@ func TestNewOpenIDConnectProvider_ShouldEnableOptionalDiscoveryValues(t *testing
IssuerCertificateChain: schema.X509CertificateChain{}, IssuerCertificateChain: schema.X509CertificateChain{},
IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey), IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey),
EnablePKCEPlainChallenge: true, EnablePKCEPlainChallenge: true,
HMACSecret: "asbdhaaskmdlkamdklasmdlkams", HMACSecret: badhmac,
Clients: []schema.OpenIDConnectClientConfiguration{ Clients: []schema.OpenIDConnectClientConfiguration{
{ {
ID: "a-client", ID: myclient,
Secret: MustDecodeSecret("$plaintext$a-client-secret"), Secret: MustDecodeSecret(badsecret),
SectorIdentifier: url.URL{Host: "google.com"}, SectorIdentifier: url.URL{Host: examplecomsid},
Policy: "one_factor", Policy: onefactor,
RedirectURIs: []string{ RedirectURIs: []string{
"https://google.com", examplecom,
}, },
}, },
}, },
@ -43,7 +43,7 @@ func TestNewOpenIDConnectProvider_ShouldEnableOptionalDiscoveryValues(t *testing
assert.NoError(t, err) assert.NoError(t, err)
disco := provider.GetOpenIDConnectWellKnownConfiguration("https://example.com") disco := provider.GetOpenIDConnectWellKnownConfiguration(examplecom)
assert.Len(t, disco.SubjectTypesSupported, 2) assert.Len(t, disco.SubjectTypesSupported, 2)
assert.Contains(t, disco.SubjectTypesSupported, SubjectTypePublic) assert.Contains(t, disco.SubjectTypesSupported, SubjectTypePublic)
@ -58,12 +58,12 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GoodConfiguration(t *tes
provider, err := NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{ provider, err := NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
IssuerCertificateChain: schema.X509CertificateChain{}, IssuerCertificateChain: schema.X509CertificateChain{},
IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey), IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey),
HMACSecret: "asbdhaaskmdlkamdklasmdlkams", HMACSecret: badhmac,
Clients: []schema.OpenIDConnectClientConfiguration{ Clients: []schema.OpenIDConnectClientConfiguration{
{ {
ID: "a-client", ID: "a-client",
Secret: MustDecodeSecret("$plaintext$a-client-secret"), Secret: MustDecodeSecret("$plaintext$a-client-secret"),
Policy: "one_factor", Policy: onefactor,
RedirectURIs: []string{ RedirectURIs: []string{
"https://google.com", "https://google.com",
}, },
@ -72,7 +72,7 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GoodConfiguration(t *tes
ID: "b-client", ID: "b-client",
Description: "Normal DisplayName", Description: "Normal DisplayName",
Secret: MustDecodeSecret("$plaintext$b-client-secret"), Secret: MustDecodeSecret("$plaintext$b-client-secret"),
Policy: "two_factor", Policy: twofactor,
RedirectURIs: []string{ RedirectURIs: []string{
"https://google.com", "https://google.com",
}, },
@ -103,7 +103,7 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOpenIDConnectWellKnow
{ {
ID: "a-client", ID: "a-client",
Secret: MustDecodeSecret("$plaintext$a-client-secret"), Secret: MustDecodeSecret("$plaintext$a-client-secret"),
Policy: "one_factor", Policy: onefactor,
RedirectURIs: []string{ RedirectURIs: []string{
"https://google.com", "https://google.com",
}, },
@ -113,9 +113,9 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOpenIDConnectWellKnow
assert.NoError(t, err) assert.NoError(t, err)
disco := provider.GetOpenIDConnectWellKnownConfiguration("https://example.com") disco := provider.GetOpenIDConnectWellKnownConfiguration(examplecom)
assert.Equal(t, "https://example.com", disco.Issuer) assert.Equal(t, examplecom, disco.Issuer)
assert.Equal(t, "https://example.com/jwks.json", disco.JWKSURI) assert.Equal(t, "https://example.com/jwks.json", disco.JWKSURI)
assert.Equal(t, "https://example.com/api/oidc/authorization", disco.AuthorizationEndpoint) assert.Equal(t, "https://example.com/api/oidc/authorization", disco.AuthorizationEndpoint)
assert.Equal(t, "https://example.com/api/oidc/token", disco.TokenEndpoint) assert.Equal(t, "https://example.com/api/oidc/token", disco.TokenEndpoint)
@ -139,8 +139,9 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOpenIDConnectWellKnow
assert.Contains(t, disco.ResponseModesSupported, ResponseModeQuery) assert.Contains(t, disco.ResponseModesSupported, ResponseModeQuery)
assert.Contains(t, disco.ResponseModesSupported, ResponseModeFragment) assert.Contains(t, disco.ResponseModesSupported, ResponseModeFragment)
assert.Len(t, disco.SubjectTypesSupported, 1) assert.Len(t, disco.SubjectTypesSupported, 2)
assert.Contains(t, disco.SubjectTypesSupported, SubjectTypePublic) assert.Contains(t, disco.SubjectTypesSupported, SubjectTypePublic)
assert.Contains(t, disco.SubjectTypesSupported, SubjectTypePairwise)
assert.Len(t, disco.ResponseTypesSupported, 7) assert.Len(t, disco.ResponseTypesSupported, 7)
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeAuthorizationCodeFlow) assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeAuthorizationCodeFlow)
@ -151,10 +152,9 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOpenIDConnectWellKnow
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowToken) assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowToken)
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowBoth) assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowBoth)
assert.Len(t, disco.TokenEndpointAuthMethodsSupported, 4) assert.Len(t, disco.TokenEndpointAuthMethodsSupported, 3)
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretBasic) assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretBasic)
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretPost) assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretPost)
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretJWT)
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodNone) assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodNone)
assert.Len(t, disco.GrantTypesSupported, 3) assert.Len(t, disco.GrantTypesSupported, 3)
@ -169,9 +169,7 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOpenIDConnectWellKnow
assert.Contains(t, disco.UserinfoSigningAlgValuesSupported, SigningAlgorithmRSAWithSHA256) assert.Contains(t, disco.UserinfoSigningAlgValuesSupported, SigningAlgorithmRSAWithSHA256)
assert.Contains(t, disco.UserinfoSigningAlgValuesSupported, SigningAlgorithmNone) assert.Contains(t, disco.UserinfoSigningAlgValuesSupported, SigningAlgorithmNone)
assert.Len(t, disco.RequestObjectSigningAlgValuesSupported, 2) assert.Len(t, disco.RequestObjectSigningAlgValuesSupported, 0)
assert.Contains(t, disco.RequestObjectSigningAlgValuesSupported, SigningAlgorithmRSAWithSHA256)
assert.Contains(t, disco.RequestObjectSigningAlgValuesSupported, SigningAlgorithmNone)
assert.Len(t, disco.ClaimsSupported, 18) assert.Len(t, disco.ClaimsSupported, 18)
assert.Contains(t, disco.ClaimsSupported, ClaimAuthenticationMethodsReference) assert.Contains(t, disco.ClaimsSupported, ClaimAuthenticationMethodsReference)
@ -203,7 +201,7 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOAuth2WellKnownConfig
{ {
ID: "a-client", ID: "a-client",
Secret: MustDecodeSecret("$plaintext$a-client-secret"), Secret: MustDecodeSecret("$plaintext$a-client-secret"),
Policy: "one_factor", Policy: onefactor,
RedirectURIs: []string{ RedirectURIs: []string{
"https://google.com", "https://google.com",
}, },
@ -213,9 +211,9 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOAuth2WellKnownConfig
assert.NoError(t, err) assert.NoError(t, err)
disco := provider.GetOAuth2WellKnownConfiguration("https://example.com") disco := provider.GetOAuth2WellKnownConfiguration(examplecom)
assert.Equal(t, "https://example.com", disco.Issuer) assert.Equal(t, examplecom, disco.Issuer)
assert.Equal(t, "https://example.com/jwks.json", disco.JWKSURI) assert.Equal(t, "https://example.com/jwks.json", disco.JWKSURI)
assert.Equal(t, "https://example.com/api/oidc/authorization", disco.AuthorizationEndpoint) assert.Equal(t, "https://example.com/api/oidc/authorization", disco.AuthorizationEndpoint)
assert.Equal(t, "https://example.com/api/oidc/token", disco.TokenEndpoint) assert.Equal(t, "https://example.com/api/oidc/token", disco.TokenEndpoint)
@ -238,8 +236,9 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOAuth2WellKnownConfig
assert.Contains(t, disco.ResponseModesSupported, ResponseModeQuery) assert.Contains(t, disco.ResponseModesSupported, ResponseModeQuery)
assert.Contains(t, disco.ResponseModesSupported, ResponseModeFragment) assert.Contains(t, disco.ResponseModesSupported, ResponseModeFragment)
assert.Len(t, disco.SubjectTypesSupported, 1) assert.Len(t, disco.SubjectTypesSupported, 2)
assert.Contains(t, disco.SubjectTypesSupported, SubjectTypePublic) assert.Contains(t, disco.SubjectTypesSupported, SubjectTypePublic)
assert.Contains(t, disco.SubjectTypesSupported, SubjectTypePairwise)
assert.Len(t, disco.ResponseTypesSupported, 7) assert.Len(t, disco.ResponseTypesSupported, 7)
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeAuthorizationCodeFlow) assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeAuthorizationCodeFlow)
@ -250,10 +249,9 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOAuth2WellKnownConfig
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowToken) assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowToken)
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowBoth) assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowBoth)
assert.Len(t, disco.TokenEndpointAuthMethodsSupported, 4) assert.Len(t, disco.TokenEndpointAuthMethodsSupported, 3)
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretBasic) assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretBasic)
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretPost) assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretPost)
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretJWT)
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodNone) assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodNone)
assert.Len(t, disco.GrantTypesSupported, 3) assert.Len(t, disco.GrantTypesSupported, 3)
@ -292,7 +290,7 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOpenIDConnectWellKnow
{ {
ID: "a-client", ID: "a-client",
Secret: MustDecodeSecret("$plaintext$a-client-secret"), Secret: MustDecodeSecret("$plaintext$a-client-secret"),
Policy: "one_factor", Policy: onefactor,
RedirectURIs: []string{ RedirectURIs: []string{
"https://google.com", "https://google.com",
}, },
@ -302,7 +300,7 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOpenIDConnectWellKnow
assert.NoError(t, err) assert.NoError(t, err)
disco := provider.GetOpenIDConnectWellKnownConfiguration("https://example.com") disco := provider.GetOpenIDConnectWellKnownConfiguration(examplecom)
require.Len(t, disco.CodeChallengeMethodsSupported, 2) require.Len(t, disco.CodeChallengeMethodsSupported, 2)
assert.Equal(t, PKCEChallengeMethodSHA256, disco.CodeChallengeMethodsSupported[0]) assert.Equal(t, PKCEChallengeMethodSHA256, disco.CodeChallengeMethodsSupported[0])

View File

@ -24,7 +24,7 @@ func NewStore(config *schema.OpenIDConnectConfiguration, provider storage.Provid
store = &Store{ store = &Store{
provider: provider, provider: provider,
clients: map[string]*Client{}, clients: map[string]Client{},
} }
for _, client := range config.Clients { for _, client := range config.Clients {
@ -72,11 +72,11 @@ func (s *Store) GetClientPolicy(id string) (level authorization.Level) {
return authorization.TwoFactor return authorization.TwoFactor
} }
return client.Policy return client.GetAuthorizationPolicy()
} }
// GetFullClient returns a fosite.Client asserted as an Client matching the provided id. // GetFullClient returns a fosite.Client asserted as an Client matching the provided id.
func (s *Store) GetFullClient(id string) (client *Client, err error) { func (s *Store) GetFullClient(id string) (client Client, err error) {
client, ok := s.clients[id] client, ok := s.clients[id]
if !ok { if !ok {
return nil, fosite.ErrInvalidClient return nil, fosite.ErrInvalidClient

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"testing" "testing"
"github.com/ory/fosite"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -17,23 +18,23 @@ func TestOpenIDConnectStore_GetClientPolicy(t *testing.T) {
IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey), IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey),
Clients: []schema.OpenIDConnectClientConfiguration{ Clients: []schema.OpenIDConnectClientConfiguration{
{ {
ID: "myclient", ID: myclient,
Description: "myclient desc", Description: myclientdesc,
Policy: "one_factor", Policy: onefactor,
Scopes: []string{ScopeOpenID, ScopeProfile}, Scopes: []string{ScopeOpenID, ScopeProfile},
Secret: MustDecodeSecret("$plaintext$mysecret"), Secret: MustDecodeSecret("$plaintext$mysecret"),
}, },
{ {
ID: "myotherclient", ID: "myotherclient",
Description: "myclient desc", Description: myclientdesc,
Policy: "two_factor", Policy: twofactor,
Scopes: []string{ScopeOpenID, ScopeProfile}, Scopes: []string{ScopeOpenID, ScopeProfile},
Secret: MustDecodeSecret("$plaintext$mysecret"), Secret: MustDecodeSecret("$plaintext$mysecret"),
}, },
}, },
}, nil) }, nil)
policyOne := s.GetClientPolicy("myclient") policyOne := s.GetClientPolicy(myclient)
assert.Equal(t, authorization.OneFactor, policyOne) assert.Equal(t, authorization.OneFactor, policyOne)
policyTwo := s.GetClientPolicy("myotherclient") policyTwo := s.GetClientPolicy("myotherclient")
@ -49,9 +50,9 @@ func TestOpenIDConnectStore_GetInternalClient(t *testing.T) {
IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey), IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey),
Clients: []schema.OpenIDConnectClientConfiguration{ Clients: []schema.OpenIDConnectClientConfiguration{
{ {
ID: "myclient", ID: myclient,
Description: "myclient desc", Description: myclientdesc,
Policy: "one_factor", Policy: onefactor,
Scopes: []string{ScopeOpenID, ScopeProfile}, Scopes: []string{ScopeOpenID, ScopeProfile},
Secret: MustDecodeSecret("$plaintext$mysecret"), Secret: MustDecodeSecret("$plaintext$mysecret"),
}, },
@ -62,17 +63,19 @@ func TestOpenIDConnectStore_GetInternalClient(t *testing.T) {
assert.EqualError(t, err, "invalid_client") assert.EqualError(t, err, "invalid_client")
assert.Nil(t, client) assert.Nil(t, client)
client, err = s.GetClient(context.Background(), "myclient") client, err = s.GetClient(context.Background(), myclient)
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, client) require.NotNil(t, client)
assert.Equal(t, "myclient", client.GetID()) assert.Equal(t, myclient, client.GetID())
} }
func TestOpenIDConnectStore_GetInternalClient_ValidClient(t *testing.T) { func TestOpenIDConnectStore_GetInternalClient_ValidClient(t *testing.T) {
id := myclient
c1 := schema.OpenIDConnectClientConfiguration{ c1 := schema.OpenIDConnectClientConfiguration{
ID: "myclient", ID: id,
Description: "myclient desc", Description: myclientdesc,
Policy: "one_factor", Policy: onefactor,
Scopes: []string{ScopeOpenID, ScopeProfile}, Scopes: []string{ScopeOpenID, ScopeProfile},
Secret: MustDecodeSecret("$plaintext$mysecret"), Secret: MustDecodeSecret("$plaintext$mysecret"),
} }
@ -83,24 +86,24 @@ func TestOpenIDConnectStore_GetInternalClient_ValidClient(t *testing.T) {
Clients: []schema.OpenIDConnectClientConfiguration{c1}, Clients: []schema.OpenIDConnectClientConfiguration{c1},
}, nil) }, nil)
client, err := s.GetFullClient(c1.ID) client, err := s.GetFullClient(id)
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, client) require.NotNil(t, client)
assert.Equal(t, client.ID, c1.ID) assert.Equal(t, id, client.GetID())
assert.Equal(t, client.Description, c1.Description) assert.Equal(t, myclientdesc, client.GetDescription())
assert.Equal(t, client.Scopes, c1.Scopes) assert.Equal(t, fosite.Arguments(c1.Scopes), client.GetScopes())
assert.Equal(t, client.GrantTypes, c1.GrantTypes) assert.Equal(t, fosite.Arguments([]string{GrantTypeAuthorizationCode}), client.GetGrantTypes())
assert.Equal(t, client.ResponseTypes, c1.ResponseTypes) assert.Equal(t, fosite.Arguments([]string{ResponseTypeAuthorizationCodeFlow}), client.GetResponseTypes())
assert.Equal(t, client.RedirectURIs, c1.RedirectURIs) assert.Equal(t, []string(nil), client.GetRedirectURIs())
assert.Equal(t, client.Policy, authorization.OneFactor) assert.Equal(t, authorization.OneFactor, client.GetAuthorizationPolicy())
assert.Equal(t, client.Secret.Encode(), "$plaintext$mysecret") assert.Equal(t, "$plaintext$mysecret", client.GetSecret().Encode())
} }
func TestOpenIDConnectStore_GetInternalClient_InvalidClient(t *testing.T) { func TestOpenIDConnectStore_GetInternalClient_InvalidClient(t *testing.T) {
c1 := schema.OpenIDConnectClientConfiguration{ c1 := schema.OpenIDConnectClientConfiguration{
ID: "myclient", ID: myclient,
Description: "myclient desc", Description: myclientdesc,
Policy: "one_factor", Policy: onefactor,
Scopes: []string{ScopeOpenID, ScopeProfile}, Scopes: []string{ScopeOpenID, ScopeProfile},
Secret: MustDecodeSecret("$plaintext$mysecret"), Secret: MustDecodeSecret("$plaintext$mysecret"),
} }
@ -122,16 +125,16 @@ func TestOpenIDConnectStore_IsValidClientID(t *testing.T) {
IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey), IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey),
Clients: []schema.OpenIDConnectClientConfiguration{ Clients: []schema.OpenIDConnectClientConfiguration{
{ {
ID: "myclient", ID: myclient,
Description: "myclient desc", Description: myclientdesc,
Policy: "one_factor", Policy: onefactor,
Scopes: []string{ScopeOpenID, ScopeProfile}, Scopes: []string{ScopeOpenID, ScopeProfile},
Secret: MustDecodeSecret("$plaintext$mysecret"), Secret: MustDecodeSecret("$plaintext$mysecret"),
}, },
}, },
}, nil) }, nil)
validClient := s.IsValidClientID("myclient") validClient := s.IsValidClientID(myclient)
invalidClient := s.IsValidClientID("myinvalidclient") invalidClient := s.IsValidClientID("myinvalidclient")
assert.True(t, validClient) assert.True(t, validClient)

View File

@ -12,6 +12,7 @@ import (
"github.com/ory/herodot" "github.com/ory/herodot"
"gopkg.in/square/go-jose.v2" "gopkg.in/square/go-jose.v2"
"github.com/authelia/authelia/v4/internal/authentication"
"github.com/authelia/authelia/v4/internal/authorization" "github.com/authelia/authelia/v4/internal/authorization"
"github.com/authelia/authelia/v4/internal/model" "github.com/authelia/authelia/v4/internal/model"
"github.com/authelia/authelia/v4/internal/storage" "github.com/authelia/authelia/v4/internal/storage"
@ -97,17 +98,19 @@ type OpenIDConnectProvider struct {
// openid.OpenIDConnectRequestStorage, and partially implements rfc7523.RFC7523KeyStorage. // openid.OpenIDConnectRequestStorage, and partially implements rfc7523.RFC7523KeyStorage.
type Store struct { type Store struct {
provider storage.Provider provider storage.Provider
clients map[string]*Client clients map[string]Client
} }
// Client represents the client internally. // BaseClient is the base for all clients.
type Client struct { type BaseClient struct {
ID string ID string
Description string Description string
Secret algorithm.Digest Secret algorithm.Digest
SectorIdentifier string SectorIdentifier string
Public bool Public bool
EnforcePAR bool
EnforcePKCE bool EnforcePKCE bool
EnforcePKCEChallengeMethod bool EnforcePKCEChallengeMethod bool
PKCEChallengeMethod string PKCEChallengeMethod string
@ -119,8 +122,6 @@ type Client struct {
ResponseTypes []string ResponseTypes []string
ResponseModes []fosite.ResponseModeType ResponseModes []fosite.ResponseModeType
EnforcePAR bool
UserinfoSigningAlgorithm string UserinfoSigningAlgorithm string
Policy authorization.Level Policy authorization.Level
@ -128,6 +129,43 @@ type Client struct {
Consent ClientConsent Consent ClientConsent
} }
// FullClient is the client with comprehensive supported features.
type FullClient struct {
*BaseClient
RequestURIs []string
JSONWebKeys *jose.JSONWebKeySet
JSONWebKeysURI string
RequestObjectSigningAlgorithm string
TokenEndpointAuthMethod string
TokenEndpointAuthSigningAlgorithm string
}
// Client represents the internal client definitions.
type Client interface {
fosite.Client
fosite.ResponseModeClient
GetDescription() string
GetSecret() algorithm.Digest
GetSectorIdentifier() string
GetConsentResponseBody(consent *model.OAuth2ConsentSession) ConsentGetResponseBody
GetUserinfoSigningAlgorithm() string
GetPAREnforcement() bool
GetPKCEEnforcement() bool
GetPKCEChallengeMethodEnforcement() bool
GetPKCEChallengeMethod() string
GetAuthorizationPolicy() authorization.Level
GetConsentPolicy() ClientConsent
IsAuthenticationLevelSufficient(level authentication.Level) bool
ValidatePKCEPolicy(r fosite.Requester) (err error)
ValidatePARPolicy(r fosite.Requester, prefix string) (err error)
ValidateResponseModePolicy(r fosite.AuthorizeRequester) (err error)
}
// NewClientConsent converts the schema.OpenIDConnectClientConsentConfig into a oidc.ClientConsent. // NewClientConsent converts the schema.OpenIDConnectClientConsentConfig into a oidc.ClientConsent.
func NewClientConsent(mode string, duration *time.Duration) ClientConsent { func NewClientConsent(mode string, duration *time.Duration) ClientConsent {
switch mode { switch mode {
@ -344,6 +382,12 @@ type CommonDiscoveryOptions struct {
Client if it is given. Client if it is given.
*/ */
OPTOSURI string `json:"op_tos_uri,omitempty"` OPTOSURI string `json:"op_tos_uri,omitempty"`
/*
A JWT containing metadata values about the authorization server as claims. This is a string value consisting of
the entire signed JWT. A "signed_metadata" metadata value SHOULD NOT appear as a claim in the JWT.
*/
SignedMetadata string `json:"signed_metadata,omitempty"`
} }
// OAuth2DiscoveryOptions represents the discovery options specific to OAuth 2.0. // OAuth2DiscoveryOptions represents the discovery options specific to OAuth 2.0.
@ -427,6 +471,98 @@ type OAuth2DiscoveryOptions struct {
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported,omitempty"` CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported,omitempty"`
} }
type OAuth2JWTIntrospectionResponseDiscoveryOptions struct {
/*
OPTIONAL. JSON array containing a list of the JWS [RFC7515] signing algorithms ("alg" values) as defined in JWA
[RFC7518] supported by the introspection endpoint to sign the response.
*/
IntrospectionSigningAlgValuesSupported []string `json:"introspection_signing_alg_values_supported,omitempty"`
/*
OPTIONAL. JSON array containing a list of the JWE [RFC7516] encryption algorithms ("alg" values) as defined in
JWA [RFC7518] supported by the introspection endpoint to encrypt the content encryption key for introspection
responses (content key encryption).
*/
IntrospectionEncryptionAlgValuesSupported []string `json:"introspection_encryption_alg_values_supported"`
/*
OPTIONAL. JSON array containing a list of the JWE [RFC7516] encryption algorithms ("enc" values) as defined in
JWA [RFC7518] supported by the introspection endpoint to encrypt the response (content encryption).
*/
IntrospectionEncryptionEncValuesSupported []string `json:"introspection_encryption_enc_values_supported"`
}
type OAuth2DeviceAuthorizationGrantDiscoveryOptions struct {
/*
OPTIONAL. URL of the authorization server's device authorization endpoint, as defined in Section 3.1.
*/
DeviceAuthorizationEndpoint string `json:"device_authorization_endpoint"`
}
type OAuth2MutualTLSClientAuthenticationDiscoveryOptions struct {
/*
OPTIONAL. Boolean value indicating server support for mutual-TLS client certificate-bound access tokens. If
omitted, the default value is false.
*/
TLSClientCertificateBoundAccessTokens bool `json:"tls_client_certificate_bound_access_tokens"`
/*
OPTIONAL. A JSON object containing alternative authorization server endpoints that, when present, an OAuth
client intending to do mutual TLS uses in preference to the conventional endpoints. The parameter value itself
consists of one or more endpoint parameters, such as token_endpoint, revocation_endpoint,
introspection_endpoint, etc., conventionally defined for the top level of authorization server metadata. An
OAuth client intending to do mutual TLS (for OAuth client authentication and/or to acquire or use
certificate-bound tokens) when making a request directly to the authorization server MUST use the alias URL of
the endpoint within the mtls_endpoint_aliases, when present, in preference to the endpoint URL of the same name
at the top level of metadata. When an endpoint is not present in mtls_endpoint_aliases, then the client uses the
conventional endpoint URL defined at the top level of the authorization server metadata. Metadata parameters
within mtls_endpoint_aliases that do not define endpoints to which an OAuth client makes a direct request have
no meaning and SHOULD be ignored.
*/
MutualTLSEndpointAliases struct {
AuthorizationEndpoint string `json:"authorization_endpoint,omitempty"`
TokenEndpoint string `json:"token_endpoint,omitempty"`
IntrospectionEndpoint string `json:"introspection_endpoint,omitempty"`
RevocationEndpoint string `json:"revocation_endpoint,omitempty"`
EndSessionEndpoint string `json:"end_session_endpoint,omitempty"`
UserinfoEndpoint string `json:"userinfo_endpoint,omitempty"`
BackChannelAuthenticationEndpoint string `json:"backchannel_authentication_endpoint,omitempty"`
FederationRegistrationEndpoint string `json:"federation_registration_endpoint,omitempty"`
PushedAuthorizationRequestEndpoint string `json:"pushed_authorization_request_endpoint,omitempty"`
RegistrationEndpoint string `json:"registration_endpoint,omitempty"`
} `json:"mtls_endpoint_aliases"`
}
type OAuth2JWTSecuredAuthorizationRequestDiscoveryOptions struct {
/*
Indicates where authorization request needs to be protected as Request Object and provided through either
request or request_uri parameter.
*/
RequireSignedRequestObject bool `json:"require_signed_request_object"`
}
type OAuth2IssuerIdentificationDiscoveryOptions struct {
AuthorizationResponseIssuerParameterSupported bool `json:"authorization_response_iss_parameter_supported"`
}
// OAuth2PushedAuthorizationDiscoveryOptions represents the well known discovery document specific to the
// OAuth 2.0 Pushed Authorization Requests (RFC9126) implementation.
//
// OAuth 2.0 Pushed Authorization Requests: https://datatracker.ietf.org/doc/html/rfc9126#section-5
type OAuth2PushedAuthorizationDiscoveryOptions struct {
/*
The URL of the pushed authorization request endpoint at which a client can post an authorization request to
exchange for a "request_uri" value usable at the authorization server.
*/
PushedAuthorizationRequestEndpoint string `json:"pushed_authorization_request_endpoint"`
/*
Boolean parameter indicating whether the authorization server accepts authorization request data only via PAR.
If omitted, the default value is "false".
*/
RequirePushedAuthorizationRequests bool `json:"require_pushed_authorization_requests"`
}
// OpenIDConnectDiscoveryOptions represents the discovery options specific to OpenID Connect. // OpenIDConnectDiscoveryOptions represents the discovery options specific to OpenID Connect.
type OpenIDConnectDiscoveryOptions struct { type OpenIDConnectDiscoveryOptions struct {
/* /*
@ -552,6 +688,12 @@ type OpenIDConnectDiscoveryOptions struct {
*/ */
ClaimLocalesSupported []string `json:"claims_locales_supported,omitempty"` ClaimLocalesSupported []string `json:"claims_locales_supported,omitempty"`
/*
OPTIONAL. Boolean value specifying whether the OP supports use of the request parameter, with true indicating
support. If omitted, the default value is false.
*/
RequestParameterSupported bool `json:"request_parameter_supported"`
/* /*
OPTIONAL. Boolean value specifying whether the OP supports use of the request_uri parameter, with true indicating OPTIONAL. Boolean value specifying whether the OP supports use of the request_uri parameter, with true indicating
support. If omitted, the default value is true. support. If omitted, the default value is true.
@ -612,39 +754,202 @@ type OpenIDConnectBackChannelLogoutDiscoveryOptions struct {
BackChannelLogoutSessionSupported bool `json:"backchannel_logout_session_supported"` BackChannelLogoutSessionSupported bool `json:"backchannel_logout_session_supported"`
} }
// PushedAuthorizationDiscoveryOptions represents the well known discovery document specific to the // OpenIDConnectSessionManagementDiscoveryOptions represents the discovery options specific to OpenID Connect 1.0
// OAuth 2.0 Pushed Authorization Requests (RFC9126) implementation. // Session Management.
// //
// OAuth 2.0 Pushed Authorization Requests: https://datatracker.ietf.org/doc/html/rfc9126#section-5 // To support OpenID Connect Session Management, the RP needs to obtain the Session Management related OP metadata. This
type PushedAuthorizationDiscoveryOptions struct { // OP metadata is normally obtained via the OP's Discovery response, as described in OpenID Connect Discovery 1.0, or
// MAY be learned via other mechanisms. This OpenID Provider Metadata parameter MUST be included in the Server's
// discovery responses when Session Management and Discovery are supported.
//
// See Also:
//
// OpenID Connect 1.0 Session Management: https://openid.net/specs/openid-connect-session-1_0.html
type OpenIDConnectSessionManagementDiscoveryOptions struct {
/* /*
The URL of the pushed authorization request endpoint at which a client can post an authorization request to REQUIRED. URL of an OP iframe that supports cross-origin communications for session state information with the
exchange for a "request_uri" value usable at the authorization server. RP Client, using the HTML5 postMessage API. This URL MUST use the https scheme and MAY contain port, path, and
query parameter components. The page is loaded from an invisible iframe embedded in an RP page so that it can
run in the OP's security context. It accepts postMessage requests from the relevant RP iframe and uses
postMessage to post back the login status of the End-User at the OP.
*/ */
PushedAuthorizationRequestEndpoint string `json:"pushed_authorization_request_endpoint,omitempty"` CheckSessionIFrame string `json:"check_session_iframe"`
}
// OpenIDConnectRPInitiatedLogoutDiscoveryOptions represents the discovery options specific to
// OpenID Connect RP-Initiated Logout 1.0.
//
// To support OpenID Connect RP-Initiated Logout, the RP needs to obtain the RP-Initiated Logout related OP metadata.
// This OP metadata is normally obtained via the OP's Discovery response, as described in OpenID Connect Discovery 1.0,
// or MAY be learned via other mechanisms. This OpenID Provider Metadata parameter MUST be included in the Server's
// discovery responses when RP-Initiated Logout and Discovery are supported.
//
// See Also:
//
// OpenID Connect RP-Initiated Logout 1.0: https://openid.net/specs/openid-connect-rpinitiated-1_0.html
type OpenIDConnectRPInitiatedLogoutDiscoveryOptions struct {
/*
REQUIRED. URL at the OP to which an RP can perform a redirect to request that the End-User be logged out at the
OP. This URL MUST use the https scheme and MAY contain port, path, and query parameter components.
*/
EndSessionEndpoint string `json:"end_session_endpoint"`
}
// OpenIDConnectPromptCreateDiscoveryOptions represents the discovery options specific to Initiating User Registration
// via OpenID Connect 1.0 functionality.
//
// This specification extends the OpenID Connect Discovery Metadata Section 3.
//
// See Also:
//
// Initiating User Registration via OpenID Connect 1.0: https://openid.net/specs/openid-connect-prompt-create-1_0.html
type OpenIDConnectPromptCreateDiscoveryOptions struct {
/*
OPTIONAL. JSON array containing the list of prompt values that this OP supports.
This metadata element is OPTIONAL in the context of the OpenID Provider not supporting the create value. If
omitted, the Relying Party should assume that this specification is not supported. The OpenID Provider MAY
provide this metadata element even if it doesn't support the create value.
Specific to this specification, a value of create in the array indicates to the Relying party that this OpenID
Provider supports this specification. If an OpenID Provider supports this specification it MUST define this metadata
element in the openid-configuration file. Additionally, if this metadata element is defined by the OpenID
Provider, the OP must also specify all other prompt values which it supports.
See Also:
OpenID.PromptCreate: https://openid.net/specs/openid-connect-prompt-create-1_0.html
*/
PromptValuesSupported []string `json:"prompt_values_supported,omitempty"`
}
// OpenIDConnectClientInitiatedBackChannelAuthFlowDiscoveryOptions represents the discovery options specific to
// OpenID Connect Client-Initiated Backchannel Authentication Flow - Core 1.0
//
// The following authorization server metadata parameters are introduced by this specification for OPs publishing their
// support of the CIBA flow and details thereof.
//
// See Also:
//
// OpenID Connect Client-Initiated Backchannel Authentication Flow - Core 1.0:
// https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html#rfc.section.4
type OpenIDConnectClientInitiatedBackChannelAuthFlowDiscoveryOptions struct {
/*
REQUIRED. URL of the OP's Backchannel Authentication Endpoint as defined in Section 7.
*/
BackChannelAuthenticationEndpoint string `json:"backchannel_authentication_endpoint"`
/* /*
Boolean parameter indicating whether the authorization server accepts authorization request data only via PAR. REQUIRED. JSON array containing one or more of the following values: poll, ping, and push.
If omitted, the default value is "false".
*/ */
RequirePushedAuthorizationRequests bool `json:"require_pushed_authorization_requests"` BackChannelTokenDeliveryModesSupported []string `json:"backchannel_token_delivery_modes_supported"`
/*
OPTIONAL. JSON array containing a list of the JWS signing algorithms (alg values) supported by the OP for signed
authentication requests, which are described in Section 7.1.1. If omitted, signed authentication requests are
not supported by the OP.
*/
BackChannelAuthRequestSigningAlgValuesSupported []string `json:"backchannel_authentication_request_signing_alg_values_supported,omitempty"`
/*
OPTIONAL. Boolean value specifying whether the OP supports the use of the user_code parameter, with true
indicating support. If omitted, the default value is false.
*/
BackChannelUserCodeParameterSupported bool `json:"backchannel_user_code_parameter_supported"`
}
// OpenIDConnectJWTSecuredAuthorizationResponseModeDiscoveryOptions represents the discovery options specific to
// JWT Secured Authorization Response Mode for OAuth 2.0 (JARM).
//
// Authorization servers SHOULD publish the supported algorithms for signing and encrypting the JWT of an authorization
// response by utilizing OAuth 2.0 Authorization Server Metadata [RFC8414] parameters. The following parameters are
// introduced by this specification.
//
// See Also:
//
// JWT Secured Authorization Response Mode for OAuth 2.0 (JARM):
// https://openid.net/specs/oauth-v2-jarm.html#name-authorization-server-metada
type OpenIDConnectJWTSecuredAuthorizationResponseModeDiscoveryOptions struct {
/*
OPTIONAL. A JSON array containing a list of the JWS [RFC7515] signing algorithms (alg values) supported by the
authorization endpoint to sign the response.
*/
AuthorizationSigningAlgValuesSupported []string `json:"authorization_signing_alg_values_supported,omitempty"`
/*
OPTIONAL. A JSON array containing a list of the JWE [RFC7516] encryption algorithms (alg values) supported by
the authorization endpoint to encrypt the response.
*/
AuthorizationEncryptionAlgValuesSupported []string `json:"authorization_encryption_alg_values_supported,omitempty"`
/*
OPTIONAL. A JSON array containing a list of the JWE [RFC7516] encryption algorithms (enc values) supported by
the authorization endpoint to encrypt the response.
*/
AuthorizationEncryptionEncValuesSupported []string `json:"authorization_encryption_enc_values_supported,omitempty"`
}
type OpenIDFederationDiscoveryOptions struct {
/*
OPTIONAL. URL of the OP's federation-specific Dynamic Client Registration Endpoint. If the OP supports explicit
client registration as described in Section 10.2, then this claim is REQUIRED.
*/
FederationRegistrationEndpoint string `json:"federation_registration_endpoint,omitempty"`
/*
REQUIRED. Array specifying the federation types supported. Federation-type values defined by this specification
are automatic and explicit.
*/
ClientRegistrationTypesSupported []string `json:"client_registration_types_supported"`
/*
OPTIONAL. A JSON Object defining the client authentications supported for each endpoint. The endpoint names are
defined in the IANA "OAuth Authorization Server Metadata" registry [IANA.OAuth.Parameters]. Other endpoints and
authentication methods are possible if made recognizable according to established standards and not in conflict
with the operating principles of this specification. In OpenID Connect Core, no client authentication is
performed at the authentication endpoint. Instead, the request itself is authenticated. The OP maps information
in the request (like the redirect_uri) to information it has gained on the client through static or dynamic
registration. If the mapping is successful, the request can be processed. If the RP uses Automatic Registration,
as defined in Section 10.1, the OP has no prior knowledge of the RP. Therefore, the OP must start by gathering
information about the RP using the process outlined in Section 6. Once it has the RP's metadata, the OP can
verify the request in the same way as if it had known the RP's metadata beforehand. To make the request
verification more secure, we demand the use of a client authentication or verification method that proves that
the RP is in possession of a key that appears in the RP's metadata.
*/
RequestAuthenticationMethodsSupported []string `json:"request_authentication_methods_supported,omitempty"`
/*
OPTIONAL. JSON array containing a list of the JWS signing algorithms (alg values) supported for the signature on
the JWT [RFC7519] used in the request_object contained in the request parameter of an authorization request or
in the private_key_jwt of a pushed authorization request. This entry MUST be present if either of these
authentication methods are specified in the request_authentication_methods_supported entry. No default
algorithms are implied if this entry is omitted. Servers SHOULD support RS256. The value none MUST NOT be used.
*/
RequestAuthenticationSigningAlgValuesSupproted []string `json:"request_authentication_signing_alg_values_supported,omitempty"`
} }
// OAuth2WellKnownConfiguration represents the well known discovery document specific to OAuth 2.0. // OAuth2WellKnownConfiguration represents the well known discovery document specific to OAuth 2.0.
type OAuth2WellKnownConfiguration struct { type OAuth2WellKnownConfiguration struct {
CommonDiscoveryOptions CommonDiscoveryOptions
OAuth2DiscoveryOptions OAuth2DiscoveryOptions
PushedAuthorizationDiscoveryOptions *OAuth2DeviceAuthorizationGrantDiscoveryOptions
*OAuth2MutualTLSClientAuthenticationDiscoveryOptions
*OAuth2IssuerIdentificationDiscoveryOptions
*OAuth2JWTIntrospectionResponseDiscoveryOptions
*OAuth2JWTSecuredAuthorizationRequestDiscoveryOptions
*OAuth2PushedAuthorizationDiscoveryOptions
} }
// OpenIDConnectWellKnownConfiguration represents the well known discovery document specific to OpenID Connect. // OpenIDConnectWellKnownConfiguration represents the well known discovery document specific to OpenID Connect.
type OpenIDConnectWellKnownConfiguration struct { type OpenIDConnectWellKnownConfiguration struct {
CommonDiscoveryOptions OAuth2WellKnownConfiguration
OAuth2DiscoveryOptions
PushedAuthorizationDiscoveryOptions
OpenIDConnectDiscoveryOptions OpenIDConnectDiscoveryOptions
OpenIDConnectFrontChannelLogoutDiscoveryOptions *OpenIDConnectFrontChannelLogoutDiscoveryOptions
OpenIDConnectBackChannelLogoutDiscoveryOptions *OpenIDConnectBackChannelLogoutDiscoveryOptions
*OpenIDConnectSessionManagementDiscoveryOptions
*OpenIDConnectRPInitiatedLogoutDiscoveryOptions
*OpenIDConnectPromptCreateDiscoveryOptions
*OpenIDConnectClientInitiatedBackChannelAuthFlowDiscoveryOptions
*OpenIDConnectJWTSecuredAuthorizationResponseModeDiscoveryOptions
*OpenIDFederationDiscoveryOptions
} }
// OpenIDConnectContext represents the context implementation that is used by some OpenID Connect 1.0 implementations. // OpenIDConnectContext represents the context implementation that is used by some OpenID Connect 1.0 implementations.

View File

@ -40,7 +40,7 @@ func TestNewSessionWithAuthorizeRequest(t *testing.T) {
Request: fosite.Request{ Request: fosite.Request{
ID: requestID.String(), ID: requestID.String(),
Form: formValues, Form: formValues,
Client: &Client{ID: "example"}, Client: &BaseClient{ID: "example"},
}, },
} }
@ -50,7 +50,7 @@ func TestNewSessionWithAuthorizeRequest(t *testing.T) {
requested := time.Unix(1647332518, 0) requested := time.Unix(1647332518, 0)
authAt := time.Unix(1647332500, 0) authAt := time.Unix(1647332500, 0)
issuer := "https://example.com" issuer := examplecom
amr := []string{AMRPasswordBasedAuthentication} amr := []string{AMRPasswordBasedAuthentication}
consent := &model.OAuth2ConsentSession{ consent := &model.OAuth2ConsentSession{

View File

@ -0,0 +1,60 @@
<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Swagger UI</title>
<link rel="stylesheet" type="text/css" href="{{ .Base }}/api/swagger-ui.css" />
<link rel="icon" type="image/png" href="{{ .Base }}/api/favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="{{ .Base }}/api/favicon-16x16.png" sizes="16x16" />
<style nonce="{{ .CSPNonce }}">
html
{
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}
*,
*:before,
*:after
{
box-sizing: inherit;
}
body
{
margin:0;
background: #fafafa;
}
</style>
</head>
<body>
<div id="swagger-ui"></div>
<script src="{{ .Base }}/api/swagger-ui-bundle.js" charset="UTF-8"> </script>
<script src="{{ .Base }}/api/swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
<script nonce="{{ .CSPNonce }}">
window.onload = function() {
// Begin Swagger UI call region
const ui = SwaggerUIBundle({
url: "{{ .Base }}/api/openapi.yml",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
});
// End Swagger UI call region
window.ui = ui;
};
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,32 @@
{ <!DOCTYPE html>
"Base":"{{ .Base }}", <html lang="en">
"DuoSelfEnrollment":"{{ .DuoSelfEnrollment }}", <head>
"LogoOverride":"{{ .LogoOverride }}", <base href="{{ .BaseURL }}" />
"RememberMe":"{{ .RememberMe }}", <meta property="csp-nonce" content="{{ .CSPNonce }}" />
"ResetPassword":"{{ .ResetPassword }}", <meta charset="utf-8" />
"ResetPasswordCustomURL":"{{ .ResetPasswordCustomURL }}", <meta name="viewport" content="width=device-width, initial-scale=1" />
"PrivacyPolicyURL":"{{ .PrivacyPolicyURL }}", <meta name="theme-color" content="#000000" />
"PrivacyPolicyAccept":"{{ .PrivacyPolicyAccept }}", <meta name="description" content="Authelia login portal for your apps" />
"Theme":"{{ .Theme }}" <link rel="manifest" href="./manifest.json" />
} <link rel="icon" href="./favicon.ico" />
<title>Login - Authelia</title>
<script type="module" crossorigin src="./static/js/index.12a51e2c.js"></script>
<link rel="stylesheet" href="./static/css/index.9502ad8b.css">
</head>
<body
data-basepath="{{ .Base }}"
data-duoselfenrollment="{{ .DuoSelfEnrollment }}"
data-logooverride="{{ .LogoOverride }}"
data-privacypolicyaccept="{{ .PrivacyPolicyAccept }}"
data-privacypolicyurl="{{ .PrivacyPolicyURL }}"
data-rememberme="{{ .RememberMe }}"
data-resetpassword="{{ .ResetPassword }}"
data-resetpasswordcustomurl="{{ .ResetPasswordCustomURL }}"
data-theme="{{ .Theme }}"
>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>

View File

@ -2,7 +2,7 @@
version: '3' version: '3'
services: services:
envoy: envoy:
image: envoyproxy/envoy:v1.25.4 image: envoyproxy/envoy:v1.25.5
volumes: volumes:
- ./example/compose/envoy/envoy.yaml:/etc/envoy/envoy.yaml - ./example/compose/envoy/envoy.yaml:/etc/envoy/envoy.yaml
- ./common/pki:/pki - ./common/pki:/pki

View File

@ -200,8 +200,8 @@ func URLsFromStringSlice(urls []string) []url.URL {
} }
// OriginFromURL returns an origin url.URL given another url.URL. // OriginFromURL returns an origin url.URL given another url.URL.
func OriginFromURL(u url.URL) (origin url.URL) { func OriginFromURL(u *url.URL) (origin *url.URL) {
return url.URL{ return &url.URL{
Scheme: u.Scheme, Scheme: u.Scheme,
Host: u.Host, Host: u.Host,
} }

View File

@ -242,7 +242,7 @@ func TestOriginFromURL(t *testing.T) {
google, err := url.Parse("https://google.com/abc?a=123#five") google, err := url.Parse("https://google.com/abc?a=123#five")
assert.NoError(t, err) assert.NoError(t, err)
origin := OriginFromURL(*google) origin := OriginFromURL(google)
assert.Equal(t, "https://google.com", origin.String()) assert.Equal(t, "https://google.com", origin.String())
} }

View File

@ -72,13 +72,13 @@
] ]
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "17.5.1", "@commitlint/cli": "17.6.0",
"@commitlint/config-conventional": "17.4.4", "@commitlint/config-conventional": "17.6.0",
"@limegrass/eslint-plugin-import-alias": "1.0.6", "@limegrass/eslint-plugin-import-alias": "1.0.6",
"@testing-library/jest-dom": "5.16.5", "@testing-library/jest-dom": "5.16.5",
"@testing-library/react": "14.0.0", "@testing-library/react": "14.0.0",
"@types/node": "18.15.11", "@types/node": "18.15.11",
"@types/react": "18.0.34", "@types/react": "18.0.35",
"@types/react-dom": "18.0.11", "@types/react-dom": "18.0.11",
"@types/testing-library__jest-dom": "5.14.5", "@types/testing-library__jest-dom": "5.14.5",
"@types/zxcvbn": "4.4.1", "@types/zxcvbn": "4.4.1",
@ -97,7 +97,7 @@
"eslint-plugin-prettier": "4.2.1", "eslint-plugin-prettier": "4.2.1",
"eslint-plugin-react": "7.32.2", "eslint-plugin-react": "7.32.2",
"eslint-plugin-react-hooks": "4.6.0", "eslint-plugin-react-hooks": "4.6.0",
"happy-dom": "9.2.1", "happy-dom": "9.5.0",
"husky": "8.0.3", "husky": "8.0.3",
"prettier": "2.8.7", "prettier": "2.8.7",
"react-test-renderer": "18.2.0", "react-test-renderer": "18.2.0",
@ -106,7 +106,7 @@
"vite-plugin-eslint": "1.8.1", "vite-plugin-eslint": "1.8.1",
"vite-plugin-istanbul": "4.0.1", "vite-plugin-istanbul": "4.0.1",
"vite-plugin-svgr": "2.4.0", "vite-plugin-svgr": "2.4.0",
"vite-tsconfig-paths": "4.1.0", "vite-tsconfig-paths": "4.2.0",
"vitest": "0.30.1", "vitest": "0.30.1",
"vitest-preview": "0.0.1" "vitest-preview": "0.0.1"
} }

View File

@ -6,10 +6,10 @@ dependencies:
version: 11.10.7 version: 11.10.7
'@emotion/react': '@emotion/react':
specifier: 11.10.6 specifier: 11.10.6
version: 11.10.6(@types/react@18.0.34)(react@18.2.0) version: 11.10.6(@types/react@18.0.35)(react@18.2.0)
'@emotion/styled': '@emotion/styled':
specifier: 11.10.6 specifier: 11.10.6
version: 11.10.6(@emotion/react@11.10.6)(@types/react@18.0.34)(react@18.2.0) version: 11.10.6(@emotion/react@11.10.6)(@types/react@18.0.35)(react@18.2.0)
'@fortawesome/fontawesome-svg-core': '@fortawesome/fontawesome-svg-core':
specifier: 6.4.0 specifier: 6.4.0
version: 6.4.0 version: 6.4.0
@ -24,13 +24,16 @@ dependencies:
version: 0.2.0(@fortawesome/fontawesome-svg-core@6.4.0)(react@18.2.0) version: 0.2.0(@fortawesome/fontawesome-svg-core@6.4.0)(react@18.2.0)
'@mui/icons-material': '@mui/icons-material':
specifier: 5.11.16 specifier: 5.11.16
version: 5.11.16(@mui/material@5.12.0)(@types/react@18.0.34)(react@18.2.0) version: 5.11.16(@mui/material@5.12.0)(@types/react@18.0.35)(react@18.2.0)
'@mui/material': '@mui/material':
specifier: 5.12.0 specifier: 5.12.0
version: 5.12.0(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(@types/react@18.0.34)(react-dom@18.2.0)(react@18.2.0) version: 5.12.0(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(@types/react@18.0.35)(react-dom@18.2.0)(react@18.2.0)
'@mui/styles': '@mui/styles':
specifier: 5.12.0 specifier: 5.12.0
version: 5.12.0(@types/react@18.0.34)(react@18.2.0) version: 5.12.0(@types/react@18.0.35)(react@18.2.0)
'@simplewebauthn/browser':
specifier: 7.2.0
version: 7.2.0
'@simplewebauthn/typescript-types': '@simplewebauthn/typescript-types':
specifier: 7.0.0 specifier: 7.0.0
version: 7.0.0 version: 7.0.0
@ -79,11 +82,11 @@ dependencies:
devDependencies: devDependencies:
'@commitlint/cli': '@commitlint/cli':
specifier: 17.5.1 specifier: 17.6.0
version: 17.5.1 version: 17.6.0
'@commitlint/config-conventional': '@commitlint/config-conventional':
specifier: 17.4.4 specifier: 17.6.0
version: 17.4.4 version: 17.6.0
'@limegrass/eslint-plugin-import-alias': '@limegrass/eslint-plugin-import-alias':
specifier: 1.0.6 specifier: 1.0.6
version: 1.0.6(eslint@8.38.0) version: 1.0.6(eslint@8.38.0)
@ -97,8 +100,8 @@ devDependencies:
specifier: 18.15.11 specifier: 18.15.11
version: 18.15.11 version: 18.15.11
'@types/react': '@types/react':
specifier: 18.0.34 specifier: 18.0.35
version: 18.0.34 version: 18.0.35
'@types/react-dom': '@types/react-dom':
specifier: 18.0.11 specifier: 18.0.11
version: 18.0.11 version: 18.0.11
@ -154,8 +157,8 @@ devDependencies:
specifier: 4.6.0 specifier: 4.6.0
version: 4.6.0(eslint@8.38.0) version: 4.6.0(eslint@8.38.0)
happy-dom: happy-dom:
specifier: 9.2.1 specifier: 9.5.0
version: 9.2.1 version: 9.5.0
husky: husky:
specifier: 8.0.3 specifier: 8.0.3
version: 8.0.3 version: 8.0.3
@ -181,11 +184,11 @@ devDependencies:
specifier: 2.4.0 specifier: 2.4.0
version: 2.4.0(vite@4.2.1) version: 2.4.0(vite@4.2.1)
vite-tsconfig-paths: vite-tsconfig-paths:
specifier: 4.1.0 specifier: 4.2.0
version: 4.1.0(typescript@5.0.4)(vite@4.2.1) version: 4.2.0(typescript@5.0.4)(vite@4.2.1)
vitest: vitest:
specifier: 0.30.1 specifier: 0.30.1
version: 0.30.1(happy-dom@9.2.1) version: 0.30.1(happy-dom@9.5.0)
vitest-preview: vitest-preview:
specifier: 0.0.1 specifier: 0.0.1
version: 0.0.1 version: 0.0.1
@ -1551,12 +1554,12 @@ packages:
'@babel/helper-validator-identifier': 7.19.1 '@babel/helper-validator-identifier': 7.19.1
to-fast-properties: 2.0.0 to-fast-properties: 2.0.0
/@commitlint/cli@17.5.1: /@commitlint/cli@17.6.0:
resolution: {integrity: sha512-pRRgGSzdHQHehxZbGA3qF6wVPyl+EEQgTe/t321rtMLFbuJ7nRj2waS17s/v5oEbyZtiY5S8PGB6XtEIm0I+Sg==} resolution: {integrity: sha512-JaZeZ1p6kfkSiZlDoQjK09AuiI9zYQMiIUJzTOM8qNRHFOXOPmiTM56nI67yzeUSNTFu6M/DRqjmdjtA5q3hEg==}
engines: {node: '>=v14'} engines: {node: '>=v14'}
dependencies: dependencies:
'@commitlint/format': 17.4.4 '@commitlint/format': 17.4.4
'@commitlint/lint': 17.4.4 '@commitlint/lint': 17.6.0
'@commitlint/load': 17.5.0 '@commitlint/load': 17.5.0
'@commitlint/read': 17.5.1 '@commitlint/read': 17.5.1
'@commitlint/types': 17.4.4 '@commitlint/types': 17.4.4
@ -1570,8 +1573,8 @@ packages:
- '@swc/wasm' - '@swc/wasm'
dev: true dev: true
/@commitlint/config-conventional@17.4.4: /@commitlint/config-conventional@17.6.0:
resolution: {integrity: sha512-u6ztvxqzi6NuhrcEDR7a+z0yrh11elY66nRrQIpqsqW6sZmpxYkDLtpRH8jRML+mmxYQ8s4qqF06Q/IQx5aJeQ==} resolution: {integrity: sha512-2Y9M7MN942bTK5h70fJGknhXA02+OtWCkKeIzTSwsdwz1V7y6bxYv24x052E9XHKtZHJfvM3iLuTOsjRvLqWtA==}
engines: {node: '>=v14'} engines: {node: '>=v14'}
dependencies: dependencies:
conventional-changelog-conventionalcommits: 5.0.0 conventional-changelog-conventionalcommits: 5.0.0
@ -1618,13 +1621,13 @@ packages:
semver: 7.3.8 semver: 7.3.8
dev: true dev: true
/@commitlint/lint@17.4.4: /@commitlint/lint@17.6.0:
resolution: {integrity: sha512-qgkCRRFjyhbMDWsti/5jRYVJkgYZj4r+ZmweZObnbYqPUl5UKLWMf9a/ZZisOI4JfiPmRktYRZ2JmqlSvg+ccw==} resolution: {integrity: sha512-6cEXxpxZd7fbtYMxeosOum/Nnwu3VdSuZcrFSqP9lWNsrHRv4ijVsnLeomvo6WHPchGOeEWAazAI7Q6Ap22fJw==}
engines: {node: '>=v14'} engines: {node: '>=v14'}
dependencies: dependencies:
'@commitlint/is-ignored': 17.4.4 '@commitlint/is-ignored': 17.4.4
'@commitlint/parse': 17.4.4 '@commitlint/parse': 17.4.4
'@commitlint/rules': 17.4.4 '@commitlint/rules': 17.6.0
'@commitlint/types': 17.4.4 '@commitlint/types': 17.4.4
dev: true dev: true
@ -1688,8 +1691,8 @@ packages:
resolve-global: 1.0.0 resolve-global: 1.0.0
dev: true dev: true
/@commitlint/rules@17.4.4: /@commitlint/rules@17.6.0:
resolution: {integrity: sha512-0tgvXnHi/mVcyR8Y8mjTFZIa/FEQXA4uEutXS/imH2v1UNkYDSEMsK/68wiXRpfW1euSgEdwRkvE1z23+yhNrQ==} resolution: {integrity: sha512-Ka7AsRFvkKMYYE7itgo7hddRGCiV+0BgbTIAq4PWmnkHAECxYpdqMVzW5jaATmXZfwfRRTB57e7KZWj6EPmK1A==}
engines: {node: '>=v14'} engines: {node: '>=v14'}
dependencies: dependencies:
'@commitlint/ensure': 17.4.4 '@commitlint/ensure': 17.4.4
@ -1765,7 +1768,7 @@ packages:
resolution: {integrity: sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==} resolution: {integrity: sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==}
dev: false dev: false
/@emotion/react@11.10.6(@types/react@18.0.34)(react@18.2.0): /@emotion/react@11.10.6(@types/react@18.0.35)(react@18.2.0):
resolution: {integrity: sha512-6HT8jBmcSkfzO7mc+N1L9uwvOnlcGoix8Zn7srt+9ga0MjREo6lRpuVX0kzo6Jp6oTqDhREOFsygN6Ew4fEQbw==} resolution: {integrity: sha512-6HT8jBmcSkfzO7mc+N1L9uwvOnlcGoix8Zn7srt+9ga0MjREo6lRpuVX0kzo6Jp6oTqDhREOFsygN6Ew4fEQbw==}
peerDependencies: peerDependencies:
'@types/react': '*' '@types/react': '*'
@ -1781,7 +1784,7 @@ packages:
'@emotion/use-insertion-effect-with-fallbacks': 1.0.0(react@18.2.0) '@emotion/use-insertion-effect-with-fallbacks': 1.0.0(react@18.2.0)
'@emotion/utils': 1.2.0 '@emotion/utils': 1.2.0
'@emotion/weak-memoize': 0.3.0 '@emotion/weak-memoize': 0.3.0
'@types/react': 18.0.34 '@types/react': 18.0.35
hoist-non-react-statics: 3.3.2 hoist-non-react-statics: 3.3.2
react: 18.2.0 react: 18.2.0
dev: false dev: false
@ -1800,7 +1803,7 @@ packages:
resolution: {integrity: sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA==} resolution: {integrity: sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA==}
dev: false dev: false
/@emotion/styled@11.10.6(@emotion/react@11.10.6)(@types/react@18.0.34)(react@18.2.0): /@emotion/styled@11.10.6(@emotion/react@11.10.6)(@types/react@18.0.35)(react@18.2.0):
resolution: {integrity: sha512-OXtBzOmDSJo5Q0AFemHCfl+bUueT8BIcPSxu0EGTpGk6DmI5dnhSzQANm1e1ze0YZL7TDyAyy6s/b/zmGOS3Og==} resolution: {integrity: sha512-OXtBzOmDSJo5Q0AFemHCfl+bUueT8BIcPSxu0EGTpGk6DmI5dnhSzQANm1e1ze0YZL7TDyAyy6s/b/zmGOS3Og==}
peerDependencies: peerDependencies:
'@emotion/react': ^11.0.0-rc.0 '@emotion/react': ^11.0.0-rc.0
@ -1813,11 +1816,11 @@ packages:
'@babel/runtime': 7.21.0 '@babel/runtime': 7.21.0
'@emotion/babel-plugin': 11.10.6 '@emotion/babel-plugin': 11.10.6
'@emotion/is-prop-valid': 1.2.0 '@emotion/is-prop-valid': 1.2.0
'@emotion/react': 11.10.6(@types/react@18.0.34)(react@18.2.0) '@emotion/react': 11.10.6(@types/react@18.0.35)(react@18.2.0)
'@emotion/serialize': 1.1.1 '@emotion/serialize': 1.1.1
'@emotion/use-insertion-effect-with-fallbacks': 1.0.0(react@18.2.0) '@emotion/use-insertion-effect-with-fallbacks': 1.0.0(react@18.2.0)
'@emotion/utils': 1.2.0 '@emotion/utils': 1.2.0
'@types/react': 18.0.34 '@types/react': 18.0.35
react: 18.2.0 react: 18.2.0
dev: false dev: false
@ -2256,7 +2259,7 @@ packages:
tsconfig-paths: 3.14.2 tsconfig-paths: 3.14.2
dev: true dev: true
/@mui/base@5.0.0-alpha.125(@types/react@18.0.34)(react-dom@18.2.0)(react@18.2.0): /@mui/base@5.0.0-alpha.125(@types/react@18.0.35)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-hAHJJ97SATu6SrkLH/HsAayK1zMZt89lrWyKuAInBKVyn363H78d1MnwyZwre9vDK5MrPoDL/NnZxtAXhwTnBA==} resolution: {integrity: sha512-hAHJJ97SATu6SrkLH/HsAayK1zMZt89lrWyKuAInBKVyn363H78d1MnwyZwre9vDK5MrPoDL/NnZxtAXhwTnBA==}
engines: {node: '>=12.0.0'} engines: {node: '>=12.0.0'}
peerDependencies: peerDependencies:
@ -2269,10 +2272,10 @@ packages:
dependencies: dependencies:
'@babel/runtime': 7.21.0 '@babel/runtime': 7.21.0
'@emotion/is-prop-valid': 1.2.0 '@emotion/is-prop-valid': 1.2.0
'@mui/types': 7.2.4(@types/react@18.0.34) '@mui/types': 7.2.4(@types/react@18.0.35)
'@mui/utils': 5.12.0(react@18.2.0) '@mui/utils': 5.12.0(react@18.2.0)
'@popperjs/core': 2.11.7 '@popperjs/core': 2.11.7
'@types/react': 18.0.34 '@types/react': 18.0.35
clsx: 1.2.1 clsx: 1.2.1
prop-types: 15.8.1 prop-types: 15.8.1
react: 18.2.0 react: 18.2.0
@ -2284,7 +2287,7 @@ packages:
resolution: {integrity: sha512-1hoFIdlLI0sG+mkJgm70FjgIVpfLcE1vxPtNolg1tLFXrvbXGUYp9NHy3d6c41nDkg2OajuVS+Mn6A8UirFuMw==} resolution: {integrity: sha512-1hoFIdlLI0sG+mkJgm70FjgIVpfLcE1vxPtNolg1tLFXrvbXGUYp9NHy3d6c41nDkg2OajuVS+Mn6A8UirFuMw==}
dev: false dev: false
/@mui/icons-material@5.11.16(@mui/material@5.12.0)(@types/react@18.0.34)(react@18.2.0): /@mui/icons-material@5.11.16(@mui/material@5.12.0)(@types/react@18.0.35)(react@18.2.0):
resolution: {integrity: sha512-oKkx9z9Kwg40NtcIajF9uOXhxiyTZrrm9nmIJ4UjkU2IdHpd4QVLbCc/5hZN/y0C6qzi2Zlxyr9TGddQx2vx2A==} resolution: {integrity: sha512-oKkx9z9Kwg40NtcIajF9uOXhxiyTZrrm9nmIJ4UjkU2IdHpd4QVLbCc/5hZN/y0C6qzi2Zlxyr9TGddQx2vx2A==}
engines: {node: '>=12.0.0'} engines: {node: '>=12.0.0'}
peerDependencies: peerDependencies:
@ -2296,12 +2299,12 @@ packages:
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.21.0 '@babel/runtime': 7.21.0
'@mui/material': 5.12.0(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(@types/react@18.0.34)(react-dom@18.2.0)(react@18.2.0) '@mui/material': 5.12.0(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(@types/react@18.0.35)(react-dom@18.2.0)(react@18.2.0)
'@types/react': 18.0.34 '@types/react': 18.0.35
react: 18.2.0 react: 18.2.0
dev: false dev: false
/@mui/material@5.12.0(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(@types/react@18.0.34)(react-dom@18.2.0)(react@18.2.0): /@mui/material@5.12.0(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(@types/react@18.0.35)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-IMellv153zJ6+xfhLWgXpAm/9hsX8qE6gP66xWcW/Pf2B8ubyVhmkTXsp8pAJxk81D6p/EyYcnAjo5DiDVkj9g==} resolution: {integrity: sha512-IMellv153zJ6+xfhLWgXpAm/9hsX8qE6gP66xWcW/Pf2B8ubyVhmkTXsp8pAJxk81D6p/EyYcnAjo5DiDVkj9g==}
engines: {node: '>=12.0.0'} engines: {node: '>=12.0.0'}
peerDependencies: peerDependencies:
@ -2319,14 +2322,14 @@ packages:
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.21.0 '@babel/runtime': 7.21.0
'@emotion/react': 11.10.6(@types/react@18.0.34)(react@18.2.0) '@emotion/react': 11.10.6(@types/react@18.0.35)(react@18.2.0)
'@emotion/styled': 11.10.6(@emotion/react@11.10.6)(@types/react@18.0.34)(react@18.2.0) '@emotion/styled': 11.10.6(@emotion/react@11.10.6)(@types/react@18.0.35)(react@18.2.0)
'@mui/base': 5.0.0-alpha.125(@types/react@18.0.34)(react-dom@18.2.0)(react@18.2.0) '@mui/base': 5.0.0-alpha.125(@types/react@18.0.35)(react-dom@18.2.0)(react@18.2.0)
'@mui/core-downloads-tracker': 5.12.0 '@mui/core-downloads-tracker': 5.12.0
'@mui/system': 5.12.0(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(@types/react@18.0.34)(react@18.2.0) '@mui/system': 5.12.0(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(@types/react@18.0.35)(react@18.2.0)
'@mui/types': 7.2.4(@types/react@18.0.34) '@mui/types': 7.2.4(@types/react@18.0.35)
'@mui/utils': 5.12.0(react@18.2.0) '@mui/utils': 5.12.0(react@18.2.0)
'@types/react': 18.0.34 '@types/react': 18.0.35
'@types/react-transition-group': 4.4.5 '@types/react-transition-group': 4.4.5
clsx: 1.2.1 clsx: 1.2.1
csstype: 3.1.2 csstype: 3.1.2
@ -2337,7 +2340,7 @@ packages:
react-transition-group: 4.4.5(react-dom@18.2.0)(react@18.2.0) react-transition-group: 4.4.5(react-dom@18.2.0)(react@18.2.0)
dev: false dev: false
/@mui/private-theming@5.12.0(@types/react@18.0.34)(react@18.2.0): /@mui/private-theming@5.12.0(@types/react@18.0.35)(react@18.2.0):
resolution: {integrity: sha512-w5dwMen1CUm1puAtubqxY9BIzrBxbOThsg2iWMvRJmWyJAPdf3Z583fPXpqeA2lhTW79uH2jajk5Ka4FuGlTPg==} resolution: {integrity: sha512-w5dwMen1CUm1puAtubqxY9BIzrBxbOThsg2iWMvRJmWyJAPdf3Z583fPXpqeA2lhTW79uH2jajk5Ka4FuGlTPg==}
engines: {node: '>=12.0.0'} engines: {node: '>=12.0.0'}
peerDependencies: peerDependencies:
@ -2349,7 +2352,7 @@ packages:
dependencies: dependencies:
'@babel/runtime': 7.21.0 '@babel/runtime': 7.21.0
'@mui/utils': 5.12.0(react@18.2.0) '@mui/utils': 5.12.0(react@18.2.0)
'@types/react': 18.0.34 '@types/react': 18.0.35
prop-types: 15.8.1 prop-types: 15.8.1
react: 18.2.0 react: 18.2.0
dev: false dev: false
@ -2369,14 +2372,14 @@ packages:
dependencies: dependencies:
'@babel/runtime': 7.21.0 '@babel/runtime': 7.21.0
'@emotion/cache': 11.10.7 '@emotion/cache': 11.10.7
'@emotion/react': 11.10.6(@types/react@18.0.34)(react@18.2.0) '@emotion/react': 11.10.6(@types/react@18.0.35)(react@18.2.0)
'@emotion/styled': 11.10.6(@emotion/react@11.10.6)(@types/react@18.0.34)(react@18.2.0) '@emotion/styled': 11.10.6(@emotion/react@11.10.6)(@types/react@18.0.35)(react@18.2.0)
csstype: 3.1.2 csstype: 3.1.2
prop-types: 15.8.1 prop-types: 15.8.1
react: 18.2.0 react: 18.2.0
dev: false dev: false
/@mui/styles@5.12.0(@types/react@18.0.34)(react@18.2.0): /@mui/styles@5.12.0(@types/react@18.0.35)(react@18.2.0):
resolution: {integrity: sha512-X7obkgZTd9X+7igqwKKe8pEncyXYdUCNmyJfHruV9TSc6LThoI29OYs6hkN6n+7ueNli+YDKdZ+TCoC1GpJuOw==} resolution: {integrity: sha512-X7obkgZTd9X+7igqwKKe8pEncyXYdUCNmyJfHruV9TSc6LThoI29OYs6hkN6n+7ueNli+YDKdZ+TCoC1GpJuOw==}
engines: {node: '>=12.0.0'} engines: {node: '>=12.0.0'}
peerDependencies: peerDependencies:
@ -2388,10 +2391,10 @@ packages:
dependencies: dependencies:
'@babel/runtime': 7.21.0 '@babel/runtime': 7.21.0
'@emotion/hash': 0.9.0 '@emotion/hash': 0.9.0
'@mui/private-theming': 5.12.0(@types/react@18.0.34)(react@18.2.0) '@mui/private-theming': 5.12.0(@types/react@18.0.35)(react@18.2.0)
'@mui/types': 7.2.4(@types/react@18.0.34) '@mui/types': 7.2.4(@types/react@18.0.35)
'@mui/utils': 5.12.0(react@18.2.0) '@mui/utils': 5.12.0(react@18.2.0)
'@types/react': 18.0.34 '@types/react': 18.0.35
clsx: 1.2.1 clsx: 1.2.1
csstype: 3.1.2 csstype: 3.1.2
hoist-non-react-statics: 3.3.2 hoist-non-react-statics: 3.3.2
@ -2407,7 +2410,7 @@ packages:
react: 18.2.0 react: 18.2.0
dev: false dev: false
/@mui/system@5.12.0(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(@types/react@18.0.34)(react@18.2.0): /@mui/system@5.12.0(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(@types/react@18.0.35)(react@18.2.0):
resolution: {integrity: sha512-Zi+WHuiJfK1ya+9+oeJQ1rLIBdY8CGDYT5oVlQg/6kIuyiCaE6SnN9PVzxBxfY77wHuOPwz4kxcPe9srdZc12Q==} resolution: {integrity: sha512-Zi+WHuiJfK1ya+9+oeJQ1rLIBdY8CGDYT5oVlQg/6kIuyiCaE6SnN9PVzxBxfY77wHuOPwz4kxcPe9srdZc12Q==}
engines: {node: '>=12.0.0'} engines: {node: '>=12.0.0'}
peerDependencies: peerDependencies:
@ -2424,20 +2427,20 @@ packages:
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.21.0 '@babel/runtime': 7.21.0
'@emotion/react': 11.10.6(@types/react@18.0.34)(react@18.2.0) '@emotion/react': 11.10.6(@types/react@18.0.35)(react@18.2.0)
'@emotion/styled': 11.10.6(@emotion/react@11.10.6)(@types/react@18.0.34)(react@18.2.0) '@emotion/styled': 11.10.6(@emotion/react@11.10.6)(@types/react@18.0.35)(react@18.2.0)
'@mui/private-theming': 5.12.0(@types/react@18.0.34)(react@18.2.0) '@mui/private-theming': 5.12.0(@types/react@18.0.35)(react@18.2.0)
'@mui/styled-engine': 5.12.0(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(react@18.2.0) '@mui/styled-engine': 5.12.0(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(react@18.2.0)
'@mui/types': 7.2.4(@types/react@18.0.34) '@mui/types': 7.2.4(@types/react@18.0.35)
'@mui/utils': 5.12.0(react@18.2.0) '@mui/utils': 5.12.0(react@18.2.0)
'@types/react': 18.0.34 '@types/react': 18.0.35
clsx: 1.2.1 clsx: 1.2.1
csstype: 3.1.2 csstype: 3.1.2
prop-types: 15.8.1 prop-types: 15.8.1
react: 18.2.0 react: 18.2.0
dev: false dev: false
/@mui/types@7.2.4(@types/react@18.0.34): /@mui/types@7.2.4(@types/react@18.0.35):
resolution: {integrity: sha512-LBcwa8rN84bKF+f5sDyku42w1NTxaPgPyYKODsh01U1fVstTClbUoSA96oyRBnSNyEiAVjKm6Gwx9vjR+xyqHA==} resolution: {integrity: sha512-LBcwa8rN84bKF+f5sDyku42w1NTxaPgPyYKODsh01U1fVstTClbUoSA96oyRBnSNyEiAVjKm6Gwx9vjR+xyqHA==}
peerDependencies: peerDependencies:
'@types/react': '*' '@types/react': '*'
@ -2445,7 +2448,7 @@ packages:
'@types/react': '@types/react':
optional: true optional: true
dependencies: dependencies:
'@types/react': 18.0.34 '@types/react': 18.0.35
dev: false dev: false
/@mui/utils@5.12.0(react@18.2.0): /@mui/utils@5.12.0(react@18.2.0):
@ -2853,23 +2856,23 @@ packages:
/@types/react-dom@18.0.11: /@types/react-dom@18.0.11:
resolution: {integrity: sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw==} resolution: {integrity: sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw==}
dependencies: dependencies:
'@types/react': 18.0.34 '@types/react': 18.0.35
dev: true dev: true
/@types/react-is@17.0.3: /@types/react-is@17.0.3:
resolution: {integrity: sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==} resolution: {integrity: sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==}
dependencies: dependencies:
'@types/react': 18.0.34 '@types/react': 18.0.35
dev: false dev: false
/@types/react-transition-group@4.4.5: /@types/react-transition-group@4.4.5:
resolution: {integrity: sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==} resolution: {integrity: sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==}
dependencies: dependencies:
'@types/react': 18.0.34 '@types/react': 18.0.35
dev: false dev: false
/@types/react@18.0.34: /@types/react@18.0.35:
resolution: {integrity: sha512-NO1UO8941541CJl1BeOXi8a9dNKFK09Gnru5ZJqkm4Q3/WoQJtHvmwt0VX0SB9YCEwe7TfSSxDuaNmx6H2BAIQ==} resolution: {integrity: sha512-6Laome31HpetaIUGFWl1VQ3mdSImwxtFZ39rh059a1MNnKGqBpC88J6NJ8n/Is3Qx7CefDGLgf/KhN/sYCf7ag==}
dependencies: dependencies:
'@types/prop-types': 15.7.5 '@types/prop-types': 15.7.5
'@types/scheduler': 0.16.3 '@types/scheduler': 0.16.3
@ -3089,7 +3092,7 @@ packages:
istanbul-lib-source-maps: 4.0.1 istanbul-lib-source-maps: 4.0.1
istanbul-reports: 3.1.5 istanbul-reports: 3.1.5
test-exclude: 6.0.0 test-exclude: 6.0.0
vitest: 0.30.1(happy-dom@9.2.1) vitest: 0.30.1(happy-dom@9.5.0)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
dev: true dev: true
@ -5092,8 +5095,8 @@ packages:
resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==}
dev: true dev: true
/happy-dom@9.2.1: /happy-dom@9.5.0:
resolution: {integrity: sha512-6L1p1XalEVw/yzb+HVpCpfR0R3HDTAbTaTIDquJCzFPVVUC7q8+jHENH1W+4P2HsI9klCsPyeKm2/Ttfz/B5ag==} resolution: {integrity: sha512-pNdHSZRWIckzg8aDQRbBgaivr2Ef+uSTpCCRGnxIETyewHA6841T8EPE+cmfhPjGi5jQN6c+oloXGGYB5SrpcA==}
dependencies: dependencies:
css.escape: 1.5.1 css.escape: 1.5.1
he: 1.2.0 he: 1.2.0
@ -7379,8 +7382,8 @@ packages:
- supports-color - supports-color
dev: true dev: true
/vite-tsconfig-paths@4.1.0(typescript@5.0.4)(vite@4.2.1): /vite-tsconfig-paths@4.2.0(typescript@5.0.4)(vite@4.2.1):
resolution: {integrity: sha512-Ps275He1fF6Wpm/3tvyokIfXd3lcmk4KsdCG4yduSTRVt+htaxIoEor88M1h3soOeXg5fn77ZiupVRwGeB/XNQ==} resolution: {integrity: sha512-jGpus0eUy5qbbMVGiTxCL1iB9ZGN6Bd37VGLJU39kTDD6ZfULTTb1bcc5IeTWqWJKiWV5YihCaibeASPiGi8kw==}
peerDependencies: peerDependencies:
vite: '*' vite: '*'
peerDependenciesMeta: peerDependenciesMeta:
@ -7479,7 +7482,7 @@ packages:
- terser - terser
dev: true dev: true
/vitest@0.30.1(happy-dom@9.2.1): /vitest@0.30.1(happy-dom@9.5.0):
resolution: {integrity: sha512-y35WTrSTlTxfMLttgQk4rHcaDkbHQwDP++SNwPb+7H8yb13Q3cu2EixrtHzF27iZ8v0XCciSsLg00RkPAzB/aA==} resolution: {integrity: sha512-y35WTrSTlTxfMLttgQk4rHcaDkbHQwDP++SNwPb+7H8yb13Q3cu2EixrtHzF27iZ8v0XCciSsLg00RkPAzB/aA==}
engines: {node: '>=v14.18.0'} engines: {node: '>=v14.18.0'}
peerDependencies: peerDependencies:
@ -7523,7 +7526,7 @@ packages:
chai: 4.3.7 chai: 4.3.7
concordance: 5.0.4 concordance: 5.0.4
debug: 4.3.4 debug: 4.3.4
happy-dom: 9.2.1 happy-dom: 9.5.0
local-pkg: 0.4.3 local-pkg: 0.4.3
magic-string: 0.30.0 magic-string: 0.30.0
pathe: 1.1.0 pathe: 1.1.0