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
commit
774f64a932
|
@ -1480,12 +1480,6 @@ notifier:
|
|||
# - email
|
||||
# - 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.
|
||||
## It's not recommended to define this unless you know what you're doing.
|
||||
# response_types:
|
||||
|
@ -1495,7 +1489,14 @@ notifier:
|
|||
# response_modes:
|
||||
# - form_post
|
||||
# - 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.
|
||||
# authorization_policy: two_factor
|
||||
|
|
|
@ -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
|
||||
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
|
||||
|
||||
{{< 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.*
|
||||
|
||||
|
@ -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
|
||||
[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
|
||||
|
||||
{{< 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
|
||||
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
|
||||
|
||||
{{< 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
|
||||
[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
|
||||
[RFC6234]: https://datatracker.ietf.org/doc/html/rfc6234
|
||||
[RFC4648]: https://datatracker.ietf.org/doc/html/rfc4648
|
||||
|
|
|
@ -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
|
||||
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
|
||||
configured in multiple files. If you wish to split these into their own files that is fine, but if you have two files that
|
||||
|
|
|
@ -16,9 +16,8 @@ toc: true
|
|||
|
||||
We document the configuration in two ways:
|
||||
|
||||
1. The [YAML] configuration template
|
||||
[config.template.yml](https://github.com/authelia/authelia/blob/master/config.template.yml) has comments with very
|
||||
limited documentation on the effective use of a particular option. All documentation lines start with `##`. Lines
|
||||
1. The [YAML] configuration template {{< github-link path="config.template.yml" >}} has comments with very 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.
|
||||
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
|
||||
|
|
|
@ -38,6 +38,23 @@ The additional tools are recommended:
|
|||
* [yamllint]
|
||||
* [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
|
||||
|
||||
There is a scripting context provided with __Authelia__ which can easily be configured. It allows running integration
|
||||
|
|
|
@ -25,8 +25,8 @@ bootstrapping *Authelia*.
|
|||
|
||||
We publish two example [systemd] unit files:
|
||||
|
||||
* [authelia.service](https://github.com/authelia/authelia/blob/master/authelia.service)
|
||||
* [authelia@.service](https://github.com/authelia/authelia/blob/master/authelia%40.service)
|
||||
* {{< github-link path="authelia.service" >}}
|
||||
* {{< github-link path="authelia@.service" >}}
|
||||
|
||||
## Arch Linux
|
||||
|
||||
|
|
|
@ -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 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
|
||||
|
||||
|
|
|
@ -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
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
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 |
|
||||
|:-------------------------:|:---------------------:|
|
||||
| [Authorization Code Flow] | `code` |
|
||||
| [Implicit Flow] | `token id_token` |
|
||||
| [Implicit Flow] | `id_token` |
|
||||
| [Implicit Flow] | `token` |
|
||||
| [Hybrid Flow] | `code token` |
|
||||
| [Hybrid Flow] | `code id_token` |
|
||||
| [Hybrid Flow] | `code token id_token` |
|
||||
| Flow Type | Value | Default [Response Modes](#response-modes) Values |
|
||||
|:-------------------------:|:---------------------:|:------------------------------------------------:|
|
||||
| [Authorization Code Flow] | `code` | `form_post`, `query` |
|
||||
| [Implicit Flow] | `id_token token` | `form_post`, `fragment` |
|
||||
| [Implicit Flow] | `id_token` | `form_post`, `fragment` |
|
||||
| [Implicit Flow] | `token` | `form_post`, `fragment` |
|
||||
| [Hybrid Flow] | `code token` | `form_post`, `fragment` |
|
||||
| [Hybrid Flow] | `code id_token` | `form_post`, `fragment` |
|
||||
| [Hybrid Flow] | `code id_token token` | `form_post`, `fragment` |
|
||||
|
||||
[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
|
||||
|
@ -139,16 +131,60 @@ more technical information.
|
|||
### Response Modes
|
||||
|
||||
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 |
|
||||
|:---------------------:|:-----------:|
|
||||
| [OAuth 2.0 Form Post] | `form_post` |
|
||||
| Query String | `query` |
|
||||
| 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
|
||||
|
||||
### 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
|
||||
|
||||
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
|
||||
|
||||
[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
|
||||
[Pushed Authorization Requests]: https://datatracker.ietf.org/doc/html/rfc9126
|
||||
[Token]: https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint
|
||||
[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
|
||||
[Revocation]: https://datatracker.ietf.org/doc/html/rfc7009
|
||||
[Proof Key Code Exchange]: https://www.rfc-editor.org/rfc/rfc7636.html
|
||||
|
|
|
@ -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
|
||||
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 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.
|
||||
|
||||
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.
|
||||
|
||||
### 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
|
||||
|
||||
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
|
||||
[configuration template](https://github.com/authelia/authelia/blob/master/config.template.yml) on GitHub which can be
|
||||
used as a basis for configuration.
|
||||
not configured via web GUI. You can find a configuration template named {{< github-link path="config.template.yml" >}}
|
||||
on GitHub which can be used as a basis for configuration, alternatively *Authelia* will write this template relevant for
|
||||
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:
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ Below you will find commented examples of the following configuration:
|
|||
|
||||
### 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
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ available in [Kubernetes]. You would likely have to build your own [HAProxy] ima
|
|||
|
||||
### 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
|
||||
|
||||
|
|
|
@ -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?
|
||||
|
||||
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).
|
||||
|
||||
[Duo]: https://duo.com/
|
||||
|
|
|
@ -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
14
go.mod
|
@ -27,17 +27,17 @@ require (
|
|||
github.com/knadh/koanf/providers/env 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/v2 v2.0.0
|
||||
github.com/knadh/koanf/v2 v2.0.1
|
||||
github.com/mattn/go-sqlite3 v1.14.16
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
|
||||
github.com/ory/fosite v0.44.0
|
||||
github.com/ory/herodot v0.10.1
|
||||
github.com/ory/x v0.0.550
|
||||
github.com/ory/herodot v0.10.2
|
||||
github.com/ory/x v0.0.552
|
||||
github.com/otiai10/copy v1.10.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
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/spf13/cobra v1.7.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
|
@ -98,8 +98,8 @@ require (
|
|||
github.com/philhofer/fwd v1.1.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.3.0 // indirect
|
||||
github.com/prometheus/common v0.37.0 // indirect
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
github.com/prometheus/common v0.42.0 // indirect
|
||||
github.com/prometheus/procfs v0.9.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
|
||||
github.com/spf13/afero v1.9.5 // indirect
|
||||
|
@ -116,7 +116,7 @@ require (
|
|||
github.com/ysmood/leakless v0.8.0 // indirect
|
||||
golang.org/x/crypto v0.7.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/tools v0.7.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
|
|
88
go.sum
88
go.sum
|
@ -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/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-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-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/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
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/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.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/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
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-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.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/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.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/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||
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.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
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/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
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-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/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/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=
|
||||
|
@ -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/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
|
||||
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.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
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/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
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/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/v2 v2.0.0 h1:XPQ5ilNnwnNaHrfQ1YpTVhUAjcGHnEKA+lRpipQv02Y=
|
||||
github.com/knadh/koanf/v2 v2.0.0/go.mod h1:ZeiIlIDXTE7w1lMT6UVcNiRAS2/rCeLn/GdLNvY1Dus=
|
||||
github.com/knadh/koanf/v2 v2.0.1 h1:1dYGITt1I23x8cfx8ZnldtezdyaZtfAuRtIFOiRzK7g=
|
||||
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.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
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/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/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
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/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
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/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-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
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-convenience v0.1.0 h1:zouLKfF2GoSGnJwGq+PE/nJAE6dj2Zj5QlTgmMTsTS8=
|
||||
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.1/go.mod h1:MMNmY6MG1uB6fnXYFaHoqdV23DTWctlPsmRCeq/2+wc=
|
||||
github.com/ory/x v0.0.550 h1:bcaOdUW/jHByoT8U6cfraBpRCtNtUB8lNBD97LTrB+Y=
|
||||
github.com/ory/x v0.0.550/go.mod h1:oRVemI3SQQOLvOCJWIRinHQKlgmay/NbwSyRUIsS/Yk=
|
||||
github.com/ory/herodot v0.10.2 h1:gGvNMHgAwWzdP/eo+roSiT5CGssygHSjDU7MSQNlJ4E=
|
||||
github.com/ory/herodot v0.10.2/go.mod h1:MMNmY6MG1uB6fnXYFaHoqdV23DTWctlPsmRCeq/2+wc=
|
||||
github.com/ory/x v0.0.552 h1:vgDw7FFQ7Ama3iyDLbjElY2Um1/ub82iIubK0pUj81M=
|
||||
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/go.mod h1:rSaLseMUsZFFbsFGc7wCJnnkTAvdc5L6VWxPE4308Ww=
|
||||
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/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
|
||||
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/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
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/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 v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
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_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM=
|
||||
github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
|
||||
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-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/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.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
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/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
|
||||
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
|
||||
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.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
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/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
|
||||
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
|
||||
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/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/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.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/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
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-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-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-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=
|
||||
|
@ -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-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-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-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.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||
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-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-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
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/oauth2 v0.5.0 h1:HuArIo48skDwlrvM3sEdHXElYslAMsf3KwRkkW4MC4s=
|
||||
golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
|
||||
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-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-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-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-20190507160741-ecd444e8653b/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-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-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-20200122134326-e047566fdf82/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-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-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-20200905004654-be1d3432aa8f/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-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-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-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-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-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-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-20220715151400-c0bba94af5f8/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/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-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
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/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.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.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/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
@ -61,6 +61,7 @@ func newCryptoHashGenerateCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
|||
Short: cmdAutheliaCryptoHashGenerateShort,
|
||||
Long: cmdAutheliaCryptoHashGenerateLong,
|
||||
Example: cmdAutheliaCryptoHashGenerateExample,
|
||||
Args: cobra.NoArgs,
|
||||
PreRunE: ctx.ChainRunE(
|
||||
ctx.ConfigSetDefaultsRunE(defaults),
|
||||
ctx.CryptoHashGenerateMapFlagsRunE,
|
||||
|
|
|
@ -1480,12 +1480,6 @@ notifier:
|
|||
# - email
|
||||
# - 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.
|
||||
## It's not recommended to define this unless you know what you're doing.
|
||||
# response_types:
|
||||
|
@ -1495,7 +1489,14 @@ notifier:
|
|||
# response_modes:
|
||||
# - form_post
|
||||
# - 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.
|
||||
# authorization_policy: two_factor
|
||||
|
|
|
@ -401,6 +401,9 @@ func TestShouldNotReadConfigurationOnFSAccessDenied(t *testing.T) {
|
|||
func TestShouldLoadDirectoryConfiguration(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
cfg := filepath.Join(dir, "myconf.yml")
|
||||
assert.NoError(t, testCreateFile(cfg, "server:\n port: 9091\n", 0700))
|
||||
|
||||
val := schema.NewStructValidator()
|
||||
_, _, 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) {
|
||||
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")
|
||||
}
|
||||
|
|
|
@ -64,6 +64,8 @@ type OpenIDConnectClientConfiguration struct {
|
|||
ResponseTypes []string `koanf:"response_types"`
|
||||
ResponseModes []string `koanf:"response_modes"`
|
||||
|
||||
TokenEndpointAuthMethod string `koanf:"token_endpoint_auth_method"`
|
||||
|
||||
Policy string `koanf:"authorization_policy"`
|
||||
|
||||
EnforcePAR bool `koanf:"enforce_par"`
|
||||
|
@ -91,9 +93,8 @@ var defaultOIDCClientConsentPreConfiguredDuration = time.Hour * 24 * 7
|
|||
var DefaultOpenIDConnectClientConfiguration = OpenIDConnectClientConfiguration{
|
||||
Policy: "two_factor",
|
||||
Scopes: []string{"openid", "groups", "profile", "email"},
|
||||
GrantTypes: []string{"refresh_token", "authorization_code"},
|
||||
ResponseTypes: []string{"code"},
|
||||
ResponseModes: []string{"form_post", "query", "fragment"},
|
||||
ResponseModes: []string{"form_post"},
|
||||
|
||||
UserinfoSigningAlgorithm: "none",
|
||||
ConsentMode: "auto",
|
||||
|
|
|
@ -45,6 +45,7 @@ var Keys = []string{
|
|||
"identity_providers.oidc.clients[].grant_types",
|
||||
"identity_providers.oidc.clients[].response_types",
|
||||
"identity_providers.oidc.clients[].response_modes",
|
||||
"identity_providers.oidc.clients[].token_endpoint_auth_method",
|
||||
"identity_providers.oidc.clients[].authorization_policy",
|
||||
"identity_providers.oidc.clients[].enforce_par",
|
||||
"identity_providers.oidc.clients[].enforce_pkce",
|
||||
|
|
|
@ -25,6 +25,23 @@ func TestShouldGenerateConfiguration(t *testing.T) {
|
|||
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) {
|
||||
if runtime.GOOS == constWindows {
|
||||
t.Skip("skipping test due to being on windows")
|
||||
|
|
|
@ -59,7 +59,7 @@ func ValidateAccessControl(config *schema.Configuration, validator *schema.Struc
|
|||
}
|
||||
|
||||
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 {
|
||||
|
@ -92,8 +92,13 @@ func ValidateRules(config *schema.Configuration, validator *schema.StructValidat
|
|||
|
||||
validateDomains(rulePosition, rule, validator)
|
||||
|
||||
if !IsPolicyValid(rule.Policy) {
|
||||
validator.Push(fmt.Errorf(errFmtAccessControlRuleInvalidPolicy, ruleDescriptor(rulePosition, rule), rule.Policy))
|
||||
switch rule.Policy {
|
||||
case "":
|
||||
validator.Push(fmt.Errorf(errFmtAccessControlRuleNoPolicy, ruleDescriptor(rulePosition, rule)))
|
||||
default:
|
||||
if !IsPolicyValid(rule.Policy) {
|
||||
validator.Push(fmt.Errorf(errFmtAccessControlRuleInvalidPolicy, ruleDescriptor(rulePosition, rule), strJoinOr(validACLRulePolicies), rule.Policy))
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
for _, method := range rule.Methods {
|
||||
if !utils.IsStringInSliceFold(method, validACLHTTPMethodVerbs) {
|
||||
validator.Push(fmt.Errorf(errFmtAccessControlRuleMethodInvalid, ruleDescriptor(rulePosition, rule), method, strings.Join(validACLHTTPMethodVerbs, "', '")))
|
||||
}
|
||||
invalid, duplicates := validateList(rule.Methods, validACLHTTPMethodVerbs, true)
|
||||
|
||||
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) {
|
||||
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 == "" {
|
||||
|
|
|
@ -58,7 +58,7 @@ func (suite *AccessControl) TestShouldValidateEitherDomainsOrDomainsRegex() {
|
|||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
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() {
|
||||
|
@ -69,7 +69,7 @@ func (suite *AccessControl) TestShouldRaiseErrorInvalidDefaultPolicy() {
|
|||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
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() {
|
||||
|
@ -141,10 +141,10 @@ func (suite *AccessControl) TestShouldRaiseErrorsWithEmptyRules() {
|
|||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
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()[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()[2], "access control: rule #2: rule is invalid: must have the option 'domain' or 'domain_regex' configured")
|
||||
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()[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: option 'policy' must be present but it's absent")
|
||||
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: option 'policy' must be one of 'bypass', 'one_factor', 'two_factor', or 'deny' but it's configured as 'wrong'")
|
||||
}
|
||||
|
||||
func (suite *AccessControl) TestShouldRaiseErrorInvalidPolicy() {
|
||||
|
@ -160,7 +160,7 @@ func (suite *AccessControl) TestShouldRaiseErrorInvalidPolicy() {
|
|||
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'): 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() {
|
||||
|
@ -194,7 +194,24 @@ func (suite *AccessControl) TestShouldRaiseErrorInvalidMethod() {
|
|||
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'): '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() {
|
||||
|
@ -367,13 +384,13 @@ func (suite *AccessControl) TestShouldErrorOnInvalidRulesQuery() {
|
|||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
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()[1], "access control: rule #2 (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 invalid: must have a value")
|
||||
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()[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()[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()[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 required but it's absent")
|
||||
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' 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()[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")
|
||||
}
|
||||
|
||||
func TestAccessControl(t *testing.T) {
|
||||
|
|
|
@ -71,7 +71,7 @@ func ValidatePasswordConfiguration(config *schema.Password, validator *schema.St
|
|||
case utils.IsStringInSlice(config.Algorithm, validHashAlgorithms):
|
||||
break
|
||||
default:
|
||||
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordUnknownAlg, config.Algorithm, strings.Join(validHashAlgorithms, "', '")))
|
||||
validator.Push(fmt.Errorf(errFmtFileAuthBackendPasswordUnknownAlg, strJoinOr(validHashAlgorithms), config.Algorithm))
|
||||
}
|
||||
|
||||
validateFileAuthenticationBackendPasswordConfigArgon2(config, validator)
|
||||
|
@ -89,7 +89,7 @@ func validateFileAuthenticationBackendPasswordConfigArgon2(config *schema.Passwo
|
|||
case utils.IsStringInSlice(config.Argon2.Variant, validArgon2Variants):
|
||||
break
|
||||
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 {
|
||||
|
@ -147,7 +147,7 @@ func validateFileAuthenticationBackendPasswordConfigSHA2Crypt(config *schema.Pas
|
|||
case utils.IsStringInSlice(config.SHA2Crypt.Variant, validSHA2CryptVariants):
|
||||
break
|
||||
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 {
|
||||
|
@ -176,7 +176,7 @@ func validateFileAuthenticationBackendPasswordConfigPBKDF2(config *schema.Passwo
|
|||
case utils.IsStringInSlice(config.PBKDF2.Variant, validPBKDF2Variants):
|
||||
break
|
||||
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 {
|
||||
|
@ -205,7 +205,7 @@ func validateFileAuthenticationBackendPasswordConfigBCrypt(config *schema.Passwo
|
|||
case utils.IsStringInSlice(config.BCrypt.Variant, validBCryptVariants):
|
||||
break
|
||||
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 {
|
||||
|
@ -369,7 +369,7 @@ func validateLDAPAuthenticationBackendImplementation(config *schema.Authenticati
|
|||
case schema.LDAPImplementationGLAuth:
|
||||
implementation = &schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth
|
||||
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{}
|
||||
|
|
|
@ -256,7 +256,7 @@ func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorOnInvalidArgon2
|
|||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: file: password: argon2: option '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() {
|
||||
|
@ -270,7 +270,7 @@ func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorOnInvalidSHA2Cr
|
|||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: file: password: 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() {
|
||||
|
@ -298,7 +298,7 @@ func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorOnInvalidPBKDF2
|
|||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: file: password: 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() {
|
||||
|
@ -312,7 +312,7 @@ func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorOnInvalidBCrypt
|
|||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: file: password: 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() {
|
||||
|
@ -497,7 +497,7 @@ func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenBadAlgorith
|
|||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: file: password: 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() {
|
||||
|
@ -609,7 +609,7 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorWhenImplementat
|
|||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
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() {
|
||||
|
@ -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()[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()[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() {
|
||||
|
@ -823,7 +823,7 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseWhenUsersFilterDoesN
|
|||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
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() {
|
||||
|
@ -834,7 +834,7 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldHelpDetectNoInputPlacehol
|
|||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
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() {
|
||||
|
@ -986,7 +986,7 @@ func (suite *ActiveDirectoryAuthenticationBackendSuite) TestShouldRaiseErrorOnIn
|
|||
validateLDAPAuthenticationBackendURL(suite.config.LDAP, suite.validator)
|
||||
|
||||
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() {
|
||||
|
|
|
@ -78,7 +78,7 @@ func validateDefault2FAMethod(config *schema.Configuration, validator *schema.St
|
|||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -98,6 +98,6 @@ func validateDefault2FAMethod(config *schema.Configuration, validator *schema.St
|
|||
}
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -221,7 +221,7 @@ func TestValidateDefault2FAMethod(t *testing.T) {
|
|||
TOTP: schema.TOTPConfiguration{Disable: true},
|
||||
},
|
||||
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},
|
||||
},
|
||||
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},
|
||||
},
|
||||
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",
|
||||
},
|
||||
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'",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ 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.
|
||||
|
@ -105,19 +105,19 @@ const (
|
|||
errFmtLDAPAuthBackendURLNotParsable = "authentication_backend: ldap: option " +
|
||||
"'url' could not be parsed: %w"
|
||||
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 " +
|
||||
"'%s' must contain enclosing parenthesis: '%s' should probably be '(%s)'"
|
||||
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.
|
||||
const (
|
||||
errFmtTOTPInvalidAlgorithm = "totp: option 'algorithm' must be one of '%s' but it is configured as '%s'"
|
||||
errFmtTOTPInvalidPeriod = "totp: option 'period' option must be 15 or more but it is configured as '%d'"
|
||||
errFmtTOTPInvalidDigits = "totp: option 'digits' must be 6 or 8 but it is configured as '%d'"
|
||||
errFmtTOTPInvalidSecretSize = "totp: option 'secret_size' must be %d or higher but it is configured as '%d'" //nolint:gosec
|
||||
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's 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's configured as '%d'" //nolint:gosec
|
||||
)
|
||||
|
||||
// Storage Error constants.
|
||||
|
@ -128,14 +128,14 @@ const (
|
|||
errFmtStorageUserPassMustBeProvided = "storage: %s: option 'username' and 'password' are required" //nolint:gosec
|
||||
errFmtStorageOptionMustBeProvided = "storage: %s: option '%s' is required"
|
||||
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"
|
||||
warnFmtStoragePostgreSQLInvalidSSLDeprecated = "storage: postgres: ssl: the ssl configuration options are deprecated and we recommend the tls options instead"
|
||||
)
|
||||
|
||||
// Telemetry Error constants.
|
||||
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.
|
||||
|
@ -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'"
|
||||
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', " +
|
||||
"'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"
|
||||
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"
|
||||
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" +
|
||||
"id's must be unique"
|
||||
errFmtOIDCClientsWithEmptyID = "identity_providers: oidc: one or more clients have been configured with " +
|
||||
"an empty id"
|
||||
errFmtOIDCClientsDuplicateID = "identity_providers: oidc: clients: option 'id' must be unique for every client but one or more clients share the following 'id' values %s"
|
||||
errFmtOIDCClientsWithEmptyID = "identity_providers: oidc: clients: option 'id' is required but was absent on the clients in positions %s"
|
||||
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"
|
||||
|
||||
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"
|
||||
|
@ -170,36 +169,43 @@ const (
|
|||
"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"
|
||||
errFmtOIDCClientRedirectURIAbsolute = "identity_providers: oidc: client '%s': option 'redirect_uris' has an " +
|
||||
"invalid value: redirect uri '%s' must have the scheme but it is 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'"
|
||||
"invalid value: redirect uri '%s' must have a scheme but it's absent"
|
||||
errFmtOIDCClientInvalidConsentMode = "identity_providers: oidc: client '%s': consent: option 'mode' must be one of " +
|
||||
"'%s' but it is configured as '%s'"
|
||||
errFmtOIDCClientInvalidEntry = "identity_providers: oidc: client '%s': option '%s' must only have the values " +
|
||||
"'%s' but one option is configured as '%s'"
|
||||
errFmtOIDCClientInvalidUserinfoAlgorithm = "identity_providers: oidc: client '%s': option " +
|
||||
"'userinfo_signing_algorithm' must be one of '%s' but it is configured as '%s'"
|
||||
"%s but it's configured as '%s'"
|
||||
errFmtOIDCClientInvalidEntries = "identity_providers: oidc: client '%s': option '%s' must only have the values " +
|
||||
"%s but the values %s are present"
|
||||
errFmtOIDCClientInvalidEntryDuplicates = "identity_providers: oidc: client '%s': option '%s' must have unique values but the values %s are duplicated"
|
||||
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 " +
|
||||
"'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 " +
|
||||
"'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 " +
|
||||
"'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 " +
|
||||
"configured to an unsafe value, it should be above 8 but it's configured to %d"
|
||||
)
|
||||
|
||||
// WebAuthn Error constants.
|
||||
const (
|
||||
errFmtWebAuthnConveyancePreference = "webauthn: option 'attestation_conveyance_preference' must be one of '%s' but it is configured as '%s'"
|
||||
errFmtWebAuthnUserVerification = "webauthn: option 'user_verification' must be one of 'discouraged', 'preferred', 'required' 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 %s but it's configured as '%s'"
|
||||
)
|
||||
|
||||
// Access Control error constants.
|
||||
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'"
|
||||
errFmtAccessControlDefaultPolicyWithoutRules = "access control: 'default_policy' option '%s' is invalid: when " +
|
||||
"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"
|
||||
errFmtAccessControlWarnNoRulesDefaultPolicy = "access control: no rules have been specified so the " +
|
||||
"'default_policy' of '%s' is going to be applied to all requests"
|
||||
errFmtAccessControlRuleNoDomains = "access control: rule %s: rule is invalid: must have the option " +
|
||||
"'domain' or 'domain_regex' configured"
|
||||
errFmtAccessControlRuleInvalidPolicy = "access control: rule %s: rule 'policy' option '%s' " +
|
||||
"is invalid: must be one of 'deny', 'two_factor', 'one_factor' or 'bypass'"
|
||||
errFmtAccessControlRuleNoDomains = "access control: rule %s: option 'domain' or 'domain_regex' must be present but are both absent"
|
||||
errFmtAccessControlRuleNoPolicy = "access control: rule %s: option 'policy' must be present but it's absent"
|
||||
errFmtAccessControlRuleInvalidPolicy = "access control: rule %s: option 'policy' must be one of %s but it's configured as '%s'"
|
||||
errAccessControlRuleBypassPolicyInvalidWithSubjects = "access control: rule %s: 'policy' option 'bypass' is " +
|
||||
"not supported when 'subject' option is configured: see " +
|
||||
"https://www.authelia.com/c/acl#bypass"
|
||||
|
@ -221,39 +226,35 @@ const (
|
|||
"valid Group Name, IP, or CIDR notation"
|
||||
errFmtAccessControlRuleSubjectInvalid = "access control: rule %s: 'subject' option '%s' is " +
|
||||
"invalid: must start with 'user:' or 'group:'"
|
||||
errFmtAccessControlRuleMethodInvalid = "access control: rule %s: 'methods' option '%s' is " +
|
||||
"invalid: must be one of '%s'"
|
||||
errFmtAccessControlRuleQueryInvalid = "access control: rule %s: 'query' option 'operator' with value '%s' is " +
|
||||
"invalid: must be one of '%s'"
|
||||
errFmtAccessControlRuleQueryInvalidNoValue = "access control: rule %s: 'query' option '%s' is " +
|
||||
"invalid: must have a value"
|
||||
errFmtAccessControlRuleQueryInvalidNoValueOperator = "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 " +
|
||||
errFmtAccessControlRuleInvalidEntries = "access control: rule %s: option '%s' must only have the values %s but the values %s are present"
|
||||
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' must be one of %s but it's configured as '%s'"
|
||||
errFmtAccessControlRuleQueryInvalidNoValue = "access control: rule %s: query: option '%s' is required but it's absent"
|
||||
errFmtAccessControlRuleQueryInvalidNoValueOperator = "access control: rule %s: query: option '%s' must be present when the option 'operator' is '%s' but it's absent"
|
||||
errFmtAccessControlRuleQueryInvalidValue = "access control: rule %s: query: option '%s' must not be present when the option 'operator' is '%s' but it's present"
|
||||
errFmtAccessControlRuleQueryInvalidValueParse = "access control: rule %s: query: option '%s' is " +
|
||||
"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"
|
||||
)
|
||||
|
||||
// Theme Error constants.
|
||||
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.
|
||||
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.
|
||||
const (
|
||||
errFmtSessionOptionRequired = "session: option '%s' is required"
|
||||
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"
|
||||
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"
|
||||
errFmtSessionRedisHostOrNodesRequired = "session: redis: option 'host' or the 'high_availability' option 'nodes' is required"
|
||||
errFmtSessionRedisTLSConfigInvalid = "session: redis: tls: %w"
|
||||
|
@ -261,8 +262,8 @@ const (
|
|||
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"
|
||||
|
||||
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'"
|
||||
errFmtSessionDomainSameSite = "session: domain config %s: option 'same_site' must be one of '%s' but 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 it's configured as '%s'"
|
||||
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"
|
||||
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"
|
||||
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'"
|
||||
errFmtServerEndpointsAuthzStrategy = "server: endpoints: authz: %s: authn_strategies: option 'name' 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 it's configured as '%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"
|
||||
errFmtServerEndpointsAuthzInvalidName = "server: endpoints: authz: %s: contains invalid characters"
|
||||
|
@ -302,7 +303,7 @@ const (
|
|||
|
||||
const (
|
||||
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"
|
||||
)
|
||||
|
||||
|
@ -312,19 +313,17 @@ 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.
|
||||
const (
|
||||
errFmtInvalidDefault2FAMethod = "option 'default_2fa_method' is configured as '%s' but must be one of " +
|
||||
"the following values: '%s'"
|
||||
errFmtInvalidDefault2FAMethodDisabled = "option 'default_2fa_method' is configured as '%s' " +
|
||||
"but must be one of the following enabled method values: '%s'"
|
||||
errFmtInvalidDefault2FAMethod = "option 'default_2fa_method' must be one of %s but it's configured as '%s'"
|
||||
errFmtInvalidDefault2FAMethodDisabled = "option 'default_2fa_method' must be one of the enabled options %s but it's configured as '%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"
|
||||
errFilePHashing = "config key incorrect: authentication_backend.file.password_hashing should be authentication_backend.file.password"
|
||||
|
@ -357,6 +356,10 @@ const (
|
|||
authzImplementationExtAuthz = "ExtAuthz"
|
||||
)
|
||||
|
||||
const (
|
||||
auto = "auto"
|
||||
)
|
||||
|
||||
var (
|
||||
validAuthzImplementations = []string{"AuthRequest", "ForwardAuth", authzImplementationExtAuthz, authzImplementationLegacy}
|
||||
validAuthzAuthnStrategies = []string{"CookieSession", "HeaderAuthorization", "HeaderProxyAuthorization", "HeaderAuthRequestProxyAuthorization", "HeaderLegacy"}
|
||||
|
@ -372,7 +375,7 @@ var (
|
|||
|
||||
var (
|
||||
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"}
|
||||
validLogLevels = []string{"trace", "debug", "info", "warn", "error"}
|
||||
validWebAuthnConveyancePreferences = []string{string(protocol.PreferNoAttestation), string(protocol.PreferIndirectAttestation), string(protocol.PreferDirectAttestation)}
|
||||
|
@ -389,13 +392,32 @@ var (
|
|||
|
||||
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 (
|
||||
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}
|
||||
validOIDCClientConsentModes = []string{"auto", oidc.ClientConsentModeImplicit.String(), oidc.ClientConsentModeExplicit.String(), oidc.ClientConsentModePreConfigured.String()}
|
||||
validOIDCCORSEndpoints = []string{oidc.EndpointAuthorization, oidc.EndpointPushedAuthorizationRequest, oidc.EndpointToken, oidc.EndpointIntrospection, oidc.EndpointRevocation, oidc.EndpointUserinfo}
|
||||
|
||||
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 (
|
||||
|
|
|
@ -22,6 +22,11 @@ func TestValidateDuo(t *testing.T) {
|
|||
have: &schema.Configuration{},
|
||||
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",
|
||||
have: &schema.Configuration{DuoAPI: schema.DuoAPIConfiguration{
|
||||
|
@ -46,7 +51,7 @@ func TestValidateDuo(t *testing.T) {
|
|||
IntegrationKey: "test",
|
||||
},
|
||||
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",
|
||||
},
|
||||
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",
|
||||
},
|
||||
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",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package validator
|
|||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -125,10 +126,10 @@ func validateOIDCOptionsCORSAllowedOriginsFromClientRedirectURIs(config *schema.
|
|||
continue
|
||||
}
|
||||
|
||||
origin := utils.OriginFromURL(*uri)
|
||||
origin := utils.OriginFromURL(uri)
|
||||
|
||||
if !utils.IsURLInSlice(origin, config.CORS.AllowedOrigins) {
|
||||
config.CORS.AllowedOrigins = append(config.CORS.AllowedOrigins, origin)
|
||||
if !utils.IsURLInSlice(*origin, config.CORS.AllowedOrigins) {
|
||||
config.CORS.AllowedOrigins = append(config.CORS.AllowedOrigins, *origin)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -137,113 +138,135 @@ func validateOIDCOptionsCORSAllowedOriginsFromClientRedirectURIs(config *schema.
|
|||
func validateOIDCOptionsCORSEndpoints(config *schema.OpenIDConnectConfiguration, val *schema.StructValidator) {
|
||||
for _, endpoint := range config.CORS.Endpoints {
|
||||
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) {
|
||||
invalidID, duplicateIDs := false, false
|
||||
var (
|
||||
errDeprecated bool
|
||||
|
||||
var ids []string
|
||||
clientIDs, duplicateClientIDs, blankClientIDs []string
|
||||
)
|
||||
|
||||
errDeprecatedFunc := func() { errDeprecated = true }
|
||||
|
||||
for c, client := range config.Clients {
|
||||
if client.ID == "" {
|
||||
invalidID = true
|
||||
blankClientIDs = append(blankClientIDs, "#"+strconv.Itoa(c+1))
|
||||
} else {
|
||||
if client.Description == "" {
|
||||
config.Clients[c].Description = client.ID
|
||||
}
|
||||
|
||||
if utils.IsStringInSliceFold(client.ID, ids) {
|
||||
duplicateIDs = true
|
||||
}
|
||||
ids = append(ids, client.ID)
|
||||
}
|
||||
|
||||
if client.Public {
|
||||
if client.Secret != nil {
|
||||
val.Push(fmt.Errorf(errFmtOIDCClientPublicInvalidSecret, client.ID))
|
||||
}
|
||||
} else {
|
||||
if client.Secret == nil {
|
||||
val.Push(fmt.Errorf(errFmtOIDCClientInvalidSecret, client.ID))
|
||||
} else if client.Secret.IsPlainText() {
|
||||
val.PushWarning(fmt.Errorf(errFmtOIDCClientInvalidSecretPlainText, client.ID))
|
||||
if id := strings.ToLower(client.ID); utils.IsStringInSlice(id, clientIDs) {
|
||||
if !utils.IsStringInSlice(id, duplicateClientIDs) {
|
||||
duplicateClientIDs = append(duplicateClientIDs, id)
|
||||
}
|
||||
} else {
|
||||
clientIDs = append(clientIDs, id)
|
||||
}
|
||||
}
|
||||
|
||||
if client.Policy == "" {
|
||||
config.Clients[c].Policy = schema.DefaultOpenIDConnectClientConfiguration.Policy
|
||||
} else if client.Policy != policyOneFactor && client.Policy != policyTwoFactor {
|
||||
val.Push(fmt.Errorf(errFmtOIDCClientInvalidPolicy, client.ID, client.Policy))
|
||||
}
|
||||
|
||||
switch client.PKCEChallengeMethod {
|
||||
case "", "plain", "S256":
|
||||
break
|
||||
default:
|
||||
val.Push(fmt.Errorf(errFmtOIDCClientInvalidPKCEChallengeMethod, client.ID, client.PKCEChallengeMethod))
|
||||
}
|
||||
|
||||
validateOIDCClientConsentMode(c, config, val)
|
||||
validateOIDCClientSectorIdentifier(client, val)
|
||||
validateOIDCClientScopes(c, config, val)
|
||||
validateOIDCClientGrantTypes(c, config, val)
|
||||
validateOIDCClientResponseTypes(c, config, val)
|
||||
validateOIDCClientResponseModes(c, config, val)
|
||||
validateOIDDClientUserinfoAlgorithm(c, config, val)
|
||||
validateOIDCClientRedirectURIs(client, val)
|
||||
validateOIDCClient(c, config, val, errDeprecatedFunc)
|
||||
}
|
||||
|
||||
if invalidID {
|
||||
val.Push(fmt.Errorf(errFmtOIDCClientsWithEmptyID))
|
||||
if errDeprecated {
|
||||
val.PushWarning(fmt.Errorf(errFmtOIDCClientsDeprecated))
|
||||
}
|
||||
|
||||
if duplicateIDs {
|
||||
val.Push(fmt.Errorf(errFmtOIDCClientsDuplicateID))
|
||||
if len(blankClientIDs) != 0 {
|
||||
val.Push(fmt.Errorf(errFmtOIDCClientsWithEmptyID, buildJoinedString(", ", "or", "", blankClientIDs)))
|
||||
}
|
||||
|
||||
if len(duplicateClientIDs) != 0 {
|
||||
val.Push(fmt.Errorf(errFmtOIDCClientsDuplicateID, strJoinOr(duplicateClientIDs)))
|
||||
}
|
||||
}
|
||||
|
||||
func validateOIDCClientSectorIdentifier(client schema.OpenIDConnectClientConfiguration, val *schema.StructValidator) {
|
||||
if client.SectorIdentifier.String() != "" {
|
||||
if utils.IsURLHostComponent(client.SectorIdentifier) || utils.IsURLHostComponentWithPort(client.SectorIdentifier) {
|
||||
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
|
||||
case policyOneFactor, policyTwoFactor:
|
||||
break
|
||||
default:
|
||||
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)
|
||||
|
||||
validateOIDCClientScopes(c, config, val, errDeprecatedFunc)
|
||||
validateOIDCClientResponseTypes(c, config, val, errDeprecatedFunc)
|
||||
validateOIDCClientResponseModes(c, config, val, errDeprecatedFunc)
|
||||
validateOIDCClientGrantTypes(c, config, val, errDeprecatedFunc)
|
||||
validateOIDCClientRedirectURIs(c, config, val, errDeprecatedFunc)
|
||||
|
||||
validateOIDCClientTokenEndpointAuthMethod(c, config, val)
|
||||
validateOIDDClientUserinfoAlgorithm(c, config, val)
|
||||
|
||||
validateOIDCClientSectorIdentifier(c, config, val)
|
||||
}
|
||||
|
||||
func validateOIDCClientSectorIdentifier(c int, config *schema.OpenIDConnectConfiguration, val *schema.StructValidator) {
|
||||
if config.Clients[c].SectorIdentifier.String() != "" {
|
||||
if utils.IsURLHostComponent(config.Clients[c].SectorIdentifier) || utils.IsURLHostComponentWithPort(config.Clients[c].SectorIdentifier) {
|
||||
return
|
||||
}
|
||||
|
||||
if client.SectorIdentifier.Scheme != "" {
|
||||
val.Push(fmt.Errorf(errFmtOIDCClientInvalidSectorIdentifier, client.ID, client.SectorIdentifier.String(), client.SectorIdentifier.Host, "scheme", client.SectorIdentifier.Scheme))
|
||||
if config.Clients[c].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 != "" {
|
||||
val.Push(fmt.Errorf(errFmtOIDCClientInvalidSectorIdentifier, client.ID, client.SectorIdentifier.String(), client.SectorIdentifier.Host, "path", client.SectorIdentifier.Path))
|
||||
if config.Clients[c].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 != "" {
|
||||
val.Push(fmt.Errorf(errFmtOIDCClientInvalidSectorIdentifier, client.ID, client.SectorIdentifier.String(), client.SectorIdentifier.Host, "query", client.SectorIdentifier.RawQuery))
|
||||
if config.Clients[c].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 != "" {
|
||||
val.Push(fmt.Errorf(errFmtOIDCClientInvalidSectorIdentifier, client.ID, client.SectorIdentifier.String(), client.SectorIdentifier.Host, "fragment", client.SectorIdentifier.Fragment))
|
||||
if config.Clients[c].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 client.SectorIdentifier.User.Username() != "" {
|
||||
val.Push(fmt.Errorf(errFmtOIDCClientInvalidSectorIdentifier, client.ID, client.SectorIdentifier.String(), client.SectorIdentifier.Host, "username", client.SectorIdentifier.User.Username()))
|
||||
if config.Clients[c].SectorIdentifier.User != nil {
|
||||
if config.Clients[c].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 {
|
||||
val.Push(fmt.Errorf(errFmtOIDCClientInvalidSectorIdentifierWithoutValue, client.ID, client.SectorIdentifier.String(), client.SectorIdentifier.Host, "password"))
|
||||
if _, set := config.Clients[c].SectorIdentifier.User.Password(); set {
|
||||
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 == "" {
|
||||
val.Push(fmt.Errorf(errFmtOIDCClientInvalidSectorIdentifierHost, client.ID, client.SectorIdentifier.String()))
|
||||
} else if config.Clients[c].SectorIdentifier.Host == "" {
|
||||
val.Push(fmt.Errorf(errFmtOIDCClientInvalidSectorIdentifierHost, config.Clients[c].ID, config.Clients[c].SectorIdentifier.String()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func validateOIDCClientConsentMode(c int, config *schema.OpenIDConnectConfiguration, val *schema.StructValidator) {
|
||||
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 {
|
||||
config.Clients[c].ConsentMode = oidc.ClientConsentModePreConfigured.String()
|
||||
} else {
|
||||
|
@ -252,7 +275,7 @@ func validateOIDCClientConsentMode(c int, config *schema.OpenIDConnectConfigurat
|
|||
case utils.IsStringInSlice(config.Clients[c].ConsentMode, validOIDCClientConsentModes):
|
||||
break
|
||||
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 {
|
||||
|
@ -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 {
|
||||
config.Clients[c].Scopes = schema.DefaultOpenIDConnectClientConfiguration.Scopes
|
||||
return
|
||||
}
|
||||
|
||||
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 {
|
||||
if !utils.IsStringInSlice(scope, validOIDCScopes) {
|
||||
val.Push(fmt.Errorf(
|
||||
errFmtOIDCClientInvalidEntry,
|
||||
config.Clients[c].ID, "scopes", strings.Join(validOIDCScopes, "', '"), scope))
|
||||
}
|
||||
invalid, duplicates := validateList(config.Clients[c].Scopes, validOIDCClientScopes, true)
|
||||
|
||||
if len(invalid) != 0 {
|
||||
val.Push(fmt.Errorf(errFmtOIDCClientInvalidEntries, config.Clients[c].ID, attrOIDCScopes, strJoinOr(validOIDCClientScopes), strJoinAnd(invalid)))
|
||||
}
|
||||
|
||||
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) {
|
||||
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) {
|
||||
func validateOIDCClientResponseTypes(c int, config *schema.OpenIDConnectConfiguration, val *schema.StructValidator, errDeprecatedFunc func()) {
|
||||
if len(config.Clients[c].ResponseTypes) == 0 {
|
||||
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 {
|
||||
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 {
|
||||
if !utils.IsStringInSlice(responseMode, validOIDCResponseModes) {
|
||||
validator.Push(fmt.Errorf(
|
||||
errFmtOIDCClientInvalidEntry,
|
||||
config.Clients[c].ID, "response_modes", strings.Join(validOIDCResponseModes, "', '"), responseMode))
|
||||
invalid, duplicates := validateList(config.Clients[c].ResponseModes, validOIDCClientResponseModes, true)
|
||||
|
||||
if len(invalid) != 0 {
|
||||
val.Push(fmt.Errorf(errFmtOIDCClientInvalidEntries, config.Clients[c].ID, attrOIDCResponseModes, strJoinOr(validOIDCClientResponseModes), strJoinAnd(invalid)))
|
||||
}
|
||||
|
||||
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) {
|
||||
if config.Clients[c].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) {
|
||||
for _, redirectURI := range client.RedirectURIs {
|
||||
if redirectURI == oauth2InstalledApp {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
if !utils.IsStringInSlice(config.Clients[c].UserinfoSigningAlgorithm, validOIDCClientUserinfoAlgorithms) {
|
||||
val.Push(fmt.Errorf(errFmtOIDCClientInvalidValue,
|
||||
config.Clients[c].ID, attrOIDCUsrSigAlg, strJoinOr(validOIDCClientUserinfoAlgorithms), config.Clients[c].UserinfoSigningAlgorithm))
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -100,7 +100,7 @@ func NewKeyMapPattern(key string) (pattern *regexp.Regexp, err error) {
|
|||
}
|
||||
|
||||
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])?")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -101,6 +101,20 @@ func TestSpecificErrorKeys(t *testing.T) {
|
|||
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) {
|
||||
configKeys := []string{
|
||||
"authentication_backend.ldap.skip_verify",
|
||||
|
|
|
@ -2,7 +2,6 @@ package validator
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||
"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) {
|
||||
validator.Push(fmt.Errorf(errFmtLoggingLevelInvalid, strings.Join(validLogLevels, "', '"), config.Log.Level))
|
||||
validator.Push(fmt.Errorf(errFmtLoggingLevelInvalid, strJoinOr(validLogLevels), config.Log.Level))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,5 +40,5 @@ func TestShouldRaiseErrorOnInvalidLoggingLevel(t *testing.T) {
|
|||
assert.Len(t, validator.Warnings(), 0)
|
||||
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'")
|
||||
}
|
||||
|
|
|
@ -4,8 +4,10 @@ import (
|
|||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/mail"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"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"))
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
|
|
|
@ -49,5 +49,5 @@ func TestShouldRaiseErrorOnInvalidNTPVersion(t *testing.T) {
|
|||
|
||||
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'")
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ func TestValidatePasswordPolicy(t *testing.T) {
|
|||
},
|
||||
expectedErrs: []string{
|
||||
"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",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -155,13 +155,13 @@ func validateServerEndpointsAuthzEndpoint(config *schema.Configuration, name str
|
|||
config.Server.Endpoints.Authz[name] = endpoint
|
||||
default:
|
||||
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 {
|
||||
validator.Push(fmt.Errorf(errFmtServerEndpointsAuthzLegacyInvalidImplementation, name))
|
||||
}
|
||||
}
|
||||
} 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) {
|
||||
|
@ -180,7 +180,7 @@ func validateServerEndpointsAuthzStrategies(name string, strategies []schema.Ser
|
|||
names = append(names, strategy.Name)
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -314,14 +314,18 @@ func TestServerAuthzEndpointErrors(t *testing.T) {
|
|||
map[string]schema.ServerAuthzEndpoint{
|
||||
"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",
|
||||
map[string]schema.ServerAuthzEndpoint{
|
||||
"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",
|
||||
|
@ -335,7 +339,9 @@ func TestServerAuthzEndpointErrors(t *testing.T) {
|
|||
map[string]schema.ServerAuthzEndpoint{
|
||||
"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",
|
||||
|
|
|
@ -45,7 +45,7 @@ func validateSession(config *schema.SessionConfiguration, validator *schema.Stru
|
|||
if config.SameSite == "" {
|
||||
config.SameSite = schema.DefaultSessionConfiguration.SameSite
|
||||
} 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)
|
||||
|
@ -73,7 +73,7 @@ func validateSession(config *schema.SessionConfiguration, validator *schema.Stru
|
|||
|
||||
func validateSessionCookieDomains(config *schema.SessionConfiguration, validator *schema.StructValidator) {
|
||||
if len(config.Cookies) == 0 {
|
||||
validator.Push(fmt.Errorf(errFmtSessionOptionRequired, "domain"))
|
||||
validator.Push(fmt.Errorf(errFmtSessionOptionRequired, "cookies"))
|
||||
}
|
||||
|
||||
domains := make([]string, 0)
|
||||
|
@ -182,7 +182,7 @@ func validateSessionSameSite(i int, config *schema.SessionConfiguration, validat
|
|||
config.Cookies[i].SameSite = schema.DefaultSessionConfiguration.SameSite
|
||||
}
|
||||
} 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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ func TestShouldSetDefaultSessionDomainsValues(t *testing.T) {
|
|||
},
|
||||
},
|
||||
[]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,
|
||||
},
|
||||
{
|
||||
"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()
|
||||
|
@ -302,7 +320,7 @@ func TestShouldRaiseErrorWhenRedisHasHostnameButNoPort(t *testing.T) {
|
|||
|
||||
assert.False(t, validator.HasWarnings())
|
||||
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) {
|
||||
|
@ -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"}},
|
||||
{"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},
|
||||
{"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"}},
|
||||
}
|
||||
|
||||
|
@ -726,8 +744,8 @@ func TestShouldRaiseErrorWhenSameSiteSetIncorrectly(t *testing.T) {
|
|||
assert.False(t, validator.HasWarnings())
|
||||
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()[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()[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', or 'strict' but it's configured as 'NOne'")
|
||||
}
|
||||
|
||||
func TestShouldNotRaiseErrorWhenSameSiteSetCorrectly(t *testing.T) {
|
||||
|
|
|
@ -3,7 +3,6 @@ package validator
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||
"github.com/authelia/authelia/v4/internal/utils"
|
||||
|
@ -92,7 +91,7 @@ func validatePostgreSQLConfiguration(config *schema.PostgreSQLStorageConfigurati
|
|||
case config.SSL.Mode == "":
|
||||
config.SSL.Mode = schema.DefaultPostgreSQLStorageConfiguration.SSL.Mode
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -360,7 +360,7 @@ func (suite *StorageSuite) TestShouldValidatePostgresSSLModeMustBeValid() {
|
|||
|
||||
suite.Assert().Len(suite.validator.Warnings(), 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() {
|
||||
|
|
|
@ -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:9959")}}},
|
||||
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'"},
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ package validator
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||
"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) {
|
||||
validator.Push(fmt.Errorf(errFmtThemeName, strings.Join(validThemeNames, "', '"), config.Theme))
|
||||
validator.Push(fmt.Errorf(errFmtThemeName, strJoinOr(validThemeNames), config.Theme))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ func (suite *Theme) TestShouldRaiseErrorWhenInvalidThemeProvided() {
|
|||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
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) {
|
||||
|
|
|
@ -24,7 +24,7 @@ func ValidateTOTP(config *schema.Configuration, validator *schema.StructValidato
|
|||
config.TOTP.Algorithm = strings.ToUpper(config.TOTP.Algorithm)
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -56,7 +56,9 @@ func TestValidateTOTP(t *testing.T) {
|
|||
Skew: schema.DefaultTOTPConfiguration.Skew,
|
||||
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",
|
||||
|
@ -69,10 +71,10 @@ func TestValidateTOTP(t *testing.T) {
|
|||
Issuer: "abc",
|
||||
},
|
||||
errs: []string{
|
||||
"totp: option 'algorithm' must be one of 'SHA1', 'SHA256', 'SHA512' but it is configured as 'SHA3'",
|
||||
"totp: option 'period' option must be 15 or more but it is configured as '5'",
|
||||
"totp: option 'digits' must be 6 or 8 but it is configured as '20'",
|
||||
"totp: option 'secret_size' must be 20 or higher but it is configured as '10'",
|
||||
"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's configured as '5'",
|
||||
"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's configured as '10'",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ import (
|
|||
"strings"
|
||||
|
||||
"golang.org/x/net/publicsuffix"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/utils"
|
||||
)
|
||||
|
||||
func isCookieDomainAPublicSuffix(domain string) (valid bool) {
|
||||
|
@ -13,3 +15,95 @@ func isCookieDomainAPublicSuffix(domain string) (valid bool) {
|
|||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package validator
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||
"github.com/authelia/authelia/v4/internal/utils"
|
||||
|
@ -22,13 +21,13 @@ func ValidateWebAuthn(config *schema.Configuration, validator *schema.StructVali
|
|||
case config.WebAuthn.ConveyancePreference == "":
|
||||
config.WebAuthn.ConveyancePreference = schema.DefaultWebAuthnConfiguration.ConveyancePreference
|
||||
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 {
|
||||
case config.WebAuthn.UserVerification == "":
|
||||
config.WebAuthn.UserVerification = schema.DefaultWebAuthnConfiguration.UserVerification
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,6 +93,6 @@ func TestWebAuthnShouldRaiseErrorsOnInvalidOptions(t *testing.T) {
|
|||
|
||||
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()[1], "webauthn: option 'user_verification' must be one of 'discouraged', 'preferred', 'required' but it is configured as 'yes'")
|
||||
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 'none', 'indirect', or 'direct' but it's configured as 'yes'")
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ func OpenIDConnectAuthorization(ctx *middlewares.AutheliaCtx, rw http.ResponseWr
|
|||
var (
|
||||
requester fosite.AuthorizeRequester
|
||||
responder fosite.AuthorizeResponder
|
||||
client *oidc.Client
|
||||
client oidc.Client
|
||||
authTime time.Time
|
||||
issuer *url.URL
|
||||
err error
|
||||
|
@ -117,7 +117,7 @@ func OpenIDConnectAuthorization(ctx *middlewares.AutheliaCtx, rw http.ResponseWr
|
|||
|
||||
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.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
|
||||
}
|
||||
|
||||
var client *oidc.Client
|
||||
var client oidc.Client
|
||||
|
||||
clientID := requester.GetClient().GetID()
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ import (
|
|||
"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,
|
||||
rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {
|
||||
var (
|
||||
|
@ -33,14 +33,14 @@ func handleOIDCAuthorizationConsent(ctx *middlewares.AutheliaCtx, issuer *url.UR
|
|||
handler = handleOIDCAuthorizationConsentNotAuthenticated
|
||||
case client.IsAuthenticationLevelSufficient(userSession.AuthenticationLevel):
|
||||
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)
|
||||
|
||||
return nil, true
|
||||
}
|
||||
|
||||
switch client.Consent.Mode {
|
||||
switch client.GetConsentPolicy().Mode {
|
||||
case oidc.ClientConsentModeExplicit:
|
||||
handler = handleOIDCAuthorizationConsentModeExplicit
|
||||
case oidc.ClientConsentModeImplicit:
|
||||
|
@ -56,7 +56,7 @@ func handleOIDCAuthorizationConsent(ctx *middlewares.AutheliaCtx, issuer *url.UR
|
|||
}
|
||||
default:
|
||||
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)
|
||||
|
||||
|
@ -69,7 +69,7 @@ func handleOIDCAuthorizationConsent(ctx *middlewares.AutheliaCtx, issuer *url.UR
|
|||
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,
|
||||
rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {
|
||||
redirectionURL := handleOIDCAuthorizationConsentGetRedirectionURL(issuer, nil, requester)
|
||||
|
@ -79,17 +79,17 @@ func handleOIDCAuthorizationConsentNotAuthenticated(_ *middlewares.AutheliaCtx,
|
|||
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,
|
||||
rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {
|
||||
var (
|
||||
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 {
|
||||
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)
|
||||
|
||||
|
@ -97,7 +97,7 @@ func handleOIDCAuthorizationConsentGenerate(ctx *middlewares.AutheliaCtx, issuer
|
|||
}
|
||||
|
||||
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)
|
||||
|
||||
|
@ -105,7 +105,7 @@ func handleOIDCAuthorizationConsentGenerate(ctx *middlewares.AutheliaCtx, issuer
|
|||
}
|
||||
|
||||
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)
|
||||
|
||||
|
@ -117,7 +117,7 @@ func handleOIDCAuthorizationConsentGenerate(ctx *middlewares.AutheliaCtx, issuer
|
|||
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) {
|
||||
var location *url.URL
|
||||
|
||||
|
@ -130,14 +130,14 @@ func handleOIDCAuthorizationConsentRedirect(ctx *middlewares.AutheliaCtx, issuer
|
|||
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
|
@ -170,7 +170,7 @@ func handleOIDCAuthorizationConsentGetRedirectionURL(issuer *url.URL, consent *m
|
|||
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
|
||||
|
||||
if client == nil {
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
"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,
|
||||
rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {
|
||||
var (
|
||||
|
@ -28,7 +28,7 @@ func handleOIDCAuthorizationConsentModeExplicit(ctx *middlewares.AutheliaCtx, is
|
|||
return handleOIDCAuthorizationConsentGenerate(ctx, issuer, client, userSession, subject, rw, r, requester)
|
||||
default:
|
||||
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)
|
||||
|
||||
|
@ -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,
|
||||
rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {
|
||||
var (
|
||||
|
@ -47,7 +47,7 @@ func handleOIDCAuthorizationConsentModeExplicitWithID(ctx *middlewares.AutheliaC
|
|||
)
|
||||
|
||||
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)
|
||||
|
||||
|
@ -55,7 +55,7 @@ func handleOIDCAuthorizationConsentModeExplicitWithID(ctx *middlewares.AutheliaC
|
|||
}
|
||||
|
||||
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)
|
||||
|
||||
|
@ -63,7 +63,7 @@ func handleOIDCAuthorizationConsentModeExplicitWithID(ctx *middlewares.AutheliaC
|
|||
}
|
||||
|
||||
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)
|
||||
|
||||
|
@ -71,7 +71,7 @@ func handleOIDCAuthorizationConsentModeExplicitWithID(ctx *middlewares.AutheliaC
|
|||
}
|
||||
|
||||
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)
|
||||
|
||||
|
@ -80,7 +80,7 @@ func handleOIDCAuthorizationConsentModeExplicitWithID(ctx *middlewares.AutheliaC
|
|||
|
||||
if !consent.IsAuthorized() {
|
||||
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)
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
"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,
|
||||
rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {
|
||||
var (
|
||||
|
@ -26,7 +26,7 @@ func handleOIDCAuthorizationConsentModeImplicit(ctx *middlewares.AutheliaCtx, is
|
|||
return handleOIDCAuthorizationConsentModeImplicitWithoutID(ctx, issuer, client, userSession, subject, rw, r, requester)
|
||||
default:
|
||||
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)
|
||||
|
||||
|
@ -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,
|
||||
rw http.ResponseWriter, _ *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {
|
||||
var (
|
||||
|
@ -45,7 +45,7 @@ func handleOIDCAuthorizationConsentModeImplicitWithID(ctx *middlewares.AutheliaC
|
|||
)
|
||||
|
||||
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)
|
||||
|
||||
|
@ -53,7 +53,7 @@ func handleOIDCAuthorizationConsentModeImplicitWithID(ctx *middlewares.AutheliaC
|
|||
}
|
||||
|
||||
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)
|
||||
|
||||
|
@ -61,7 +61,7 @@ func handleOIDCAuthorizationConsentModeImplicitWithID(ctx *middlewares.AutheliaC
|
|||
}
|
||||
|
||||
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)
|
||||
|
||||
|
@ -69,7 +69,7 @@ func handleOIDCAuthorizationConsentModeImplicitWithID(ctx *middlewares.AutheliaC
|
|||
}
|
||||
|
||||
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)
|
||||
|
||||
|
@ -79,7 +79,7 @@ func handleOIDCAuthorizationConsentModeImplicitWithID(ctx *middlewares.AutheliaC
|
|||
consent.Grant()
|
||||
|
||||
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)
|
||||
|
||||
|
@ -89,7 +89,7 @@ func handleOIDCAuthorizationConsentModeImplicitWithID(ctx *middlewares.AutheliaC
|
|||
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,
|
||||
rw http.ResponseWriter, _ *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {
|
||||
var (
|
||||
|
@ -97,7 +97,7 @@ func handleOIDCAuthorizationConsentModeImplicitWithoutID(ctx *middlewares.Authel
|
|||
)
|
||||
|
||||
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)
|
||||
|
||||
|
@ -105,7 +105,7 @@ func handleOIDCAuthorizationConsentModeImplicitWithoutID(ctx *middlewares.Authel
|
|||
}
|
||||
|
||||
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)
|
||||
|
||||
|
@ -113,7 +113,7 @@ func handleOIDCAuthorizationConsentModeImplicitWithoutID(ctx *middlewares.Authel
|
|||
}
|
||||
|
||||
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)
|
||||
|
||||
|
@ -123,7 +123,7 @@ func handleOIDCAuthorizationConsentModeImplicitWithoutID(ctx *middlewares.Authel
|
|||
consent.Grant()
|
||||
|
||||
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)
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ import (
|
|||
"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,
|
||||
rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {
|
||||
var (
|
||||
|
@ -32,7 +32,7 @@ func handleOIDCAuthorizationConsentModePreConfigured(ctx *middlewares.AutheliaCt
|
|||
return handleOIDCAuthorizationConsentModePreConfiguredWithoutID(ctx, issuer, client, userSession, subject, rw, r, requester)
|
||||
default:
|
||||
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)
|
||||
|
||||
|
@ -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,
|
||||
rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {
|
||||
var (
|
||||
|
@ -52,7 +52,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithID(ctx *middlewares.Auth
|
|||
)
|
||||
|
||||
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)
|
||||
|
||||
|
@ -60,7 +60,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithID(ctx *middlewares.Auth
|
|||
}
|
||||
|
||||
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)
|
||||
|
||||
|
@ -68,7 +68,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithID(ctx *middlewares.Auth
|
|||
}
|
||||
|
||||
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)
|
||||
|
||||
|
@ -76,7 +76,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithID(ctx *middlewares.Auth
|
|||
}
|
||||
|
||||
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)
|
||||
|
||||
|
@ -84,7 +84,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithID(ctx *middlewares.Auth
|
|||
}
|
||||
|
||||
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)
|
||||
|
||||
|
@ -97,7 +97,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithID(ctx *middlewares.Auth
|
|||
consent.PreConfiguration = sql.NullInt64{Int64: config.ID, Valid: true}
|
||||
|
||||
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)
|
||||
|
||||
|
@ -109,7 +109,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithID(ctx *middlewares.Auth
|
|||
|
||||
if !consent.IsAuthorized() {
|
||||
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)
|
||||
|
||||
|
@ -124,7 +124,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithID(ctx *middlewares.Auth
|
|||
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,
|
||||
rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {
|
||||
var (
|
||||
|
@ -133,7 +133,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithoutID(ctx *middlewares.A
|
|||
)
|
||||
|
||||
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)
|
||||
|
||||
|
@ -145,7 +145,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithoutID(ctx *middlewares.A
|
|||
}
|
||||
|
||||
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)
|
||||
|
||||
|
@ -153,7 +153,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithoutID(ctx *middlewares.A
|
|||
}
|
||||
|
||||
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)
|
||||
|
||||
|
@ -161,7 +161,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithoutID(ctx *middlewares.A
|
|||
}
|
||||
|
||||
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)
|
||||
|
||||
|
@ -173,7 +173,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithoutID(ctx *middlewares.A
|
|||
consent.PreConfiguration = sql.NullInt64{Int64: config.ID, Valid: true}
|
||||
|
||||
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)
|
||||
|
||||
|
@ -183,12 +183,12 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithoutID(ctx *middlewares.A
|
|||
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 (
|
||||
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 {
|
||||
return nil, fmt.Errorf("error loading rows: %w", err)
|
||||
|
@ -196,7 +196,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredGetPreConfig(ctx *middleware
|
|||
|
||||
defer func() {
|
||||
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() {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ func OpenIDConnectConsentGET(ctx *middlewares.AutheliaCtx) {
|
|||
|
||||
var (
|
||||
consent *model.OAuth2ConsentSession
|
||||
client *oidc.Client
|
||||
client oidc.Client
|
||||
handled bool
|
||||
)
|
||||
|
||||
|
@ -70,7 +70,7 @@ func OpenIDConnectConsentPOST(ctx *middlewares.AutheliaCtx) {
|
|||
var (
|
||||
userSession session.UserSession
|
||||
consent *model.OAuth2ConsentSession
|
||||
client *oidc.Client
|
||||
client oidc.Client
|
||||
handled bool
|
||||
)
|
||||
|
||||
|
@ -90,12 +90,12 @@ func OpenIDConnectConsentPOST(ctx *middlewares.AutheliaCtx) {
|
|||
consent.Grant()
|
||||
|
||||
if bodyJSON.PreConfigure {
|
||||
if client.Consent.Mode == oidc.ClientConsentModePreConfigured {
|
||||
if client.GetConsentPolicy().Mode == oidc.ClientConsentModePreConfigured {
|
||||
config := model.OAuth2ConsentPreConfig{
|
||||
ClientID: consent.ClientID,
|
||||
Subject: consent.Subject.UUID,
|
||||
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,
|
||||
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 (
|
||||
err error
|
||||
)
|
||||
|
@ -185,7 +185,7 @@ func oidcConsentGetSessionsAndClient(ctx *middlewares.AutheliaCtx, consentID uui
|
|||
return userSession, nil, nil, true
|
||||
}
|
||||
|
||||
switch client.Consent.Mode {
|
||||
switch client.GetConsentPolicy().Mode {
|
||||
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.ReplyForbidden()
|
||||
|
|
|
@ -23,7 +23,7 @@ func OpenIDConnectUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter,
|
|||
var (
|
||||
tokenType fosite.TokenType
|
||||
requester fosite.AccessRequester
|
||||
client *oidc.Client
|
||||
client oidc.Client
|
||||
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)
|
||||
|
||||
switch client.UserinfoSigningAlgorithm {
|
||||
switch client.GetUserinfoSigningAlgorithm() {
|
||||
case oidc.SigningAlgorithmRSAWithSHA256:
|
||||
var jti uuid.UUID
|
||||
|
||||
|
@ -129,6 +129,6 @@ func OpenIDConnectUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter,
|
|||
case oidc.SigningAlgorithmNone, "":
|
||||
ctx.Providers.OpenIDConnect.Write(rw, req, claims)
|
||||
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())))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -178,7 +178,7 @@ func handleOIDCWorkflowResponseWithTargetURL(ctx *middlewares.AutheliaCtx, targe
|
|||
func handleOIDCWorkflowResponseWithID(ctx *middlewares.AutheliaCtx, id string) {
|
||||
var (
|
||||
workflowID uuid.UUID
|
||||
client *oidc.Client
|
||||
client oidc.Client
|
||||
consent *model.OAuth2ConsentSession
|
||||
err error
|
||||
)
|
||||
|
@ -210,19 +210,19 @@ func handleOIDCWorkflowResponseWithID(ctx *middlewares.AutheliaCtx, id string) {
|
|||
var userSession session.UserSession
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
return
|
||||
|
|
|
@ -154,7 +154,7 @@ type PasswordPolicyBody struct {
|
|||
}
|
||||
|
||||
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,
|
||||
rw http.ResponseWriter, r *http.Request,
|
||||
requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool)
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package oidc
|
||||
|
||||
import (
|
||||
"github.com/go-crypt/crypt/algorithm"
|
||||
"github.com/ory/fosite"
|
||||
"github.com/ory/x/errorsx"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/authentication"
|
||||
"github.com/authelia/authelia/v4/internal/authorization"
|
||||
|
@ -11,8 +13,8 @@ import (
|
|||
)
|
||||
|
||||
// NewClient creates a new Client.
|
||||
func NewClient(config schema.OpenIDConnectClientConfiguration) (client *Client) {
|
||||
client = &Client{
|
||||
func NewClient(config schema.OpenIDConnectClientConfiguration) (client Client) {
|
||||
base := &BaseClient{
|
||||
ID: config.ID,
|
||||
Description: config.Description,
|
||||
Secret: config.Secret,
|
||||
|
@ -40,14 +42,165 @@ func NewClient(config schema.OpenIDConnectClientConfiguration) (client *Client)
|
|||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (c *Client) ValidatePKCEPolicy(r fosite.Requester) (err error) {
|
||||
func (c *BaseClient) ValidatePKCEPolicy(r fosite.Requester) (err error) {
|
||||
form := r.GetRequestForm()
|
||||
|
||||
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.
|
||||
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 !IsPushedAuthorizedRequest(r, prefix) {
|
||||
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
|
||||
// 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 {
|
||||
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))
|
||||
}
|
||||
|
||||
// IsAuthenticationLevelSufficient returns if the provided authentication.Level is sufficient for the client of the AutheliaClient.
|
||||
func (c *Client) IsAuthenticationLevelSufficient(level authentication.Level) bool {
|
||||
if level == authentication.NotAuthenticated {
|
||||
return false
|
||||
// GetRequestURIs is an array of request_uri values that are pre-registered by the RP for use at the OP. Servers MAY
|
||||
// cache the contents of the files referenced by these URIs and not retrieve them at the time they are used in a request.
|
||||
// OPs can require that request_uri values used be pre-registered with the require_request_uri_registration
|
||||
// discovery parameter.
|
||||
func (c *FullClient) GetRequestURIs() []string {
|
||||
return c.RequestURIs
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// GetJSONWebKeysURI returns the URL for lookup of JSON Web Key Set containing the
|
||||
// public key used by the client to authenticate.
|
||||
func (c *FullClient) GetJSONWebKeysURI() string {
|
||||
return c.JSONWebKeysURI
|
||||
}
|
||||
|
||||
// GetRequestObjectSigningAlgorithm returns the JWS [JWS] alg algorithm [JWA] that MUST be used for signing Request
|
||||
// Objects sent to the OP. All Request Objects from this Client MUST be rejected, if not signed with this algorithm.
|
||||
func (c *FullClient) GetRequestObjectSigningAlgorithm() string {
|
||||
return c.RequestObjectSigningAlgorithm
|
||||
}
|
||||
|
||||
// GetTokenEndpointAuthMethod returns the requested Client Authentication Method for the Token Endpoint. The options are
|
||||
// client_secret_post, client_secret_basic, client_secret_jwt, private_key_jwt, and none.
|
||||
func (c *FullClient) GetTokenEndpointAuthMethod() string {
|
||||
if c.TokenEndpointAuthMethod == "" {
|
||||
if c.Public {
|
||||
c.TokenEndpointAuthMethod = ClientAuthMethodNone
|
||||
} else {
|
||||
c.TokenEndpointAuthMethod = ClientAuthMethodClientSecretPost
|
||||
}
|
||||
}
|
||||
|
||||
return authorization.IsAuthLevelSufficient(level, c.Policy)
|
||||
return c.TokenEndpointAuthMethod
|
||||
}
|
||||
|
||||
// GetSectorIdentifier returns the SectorIdentifier for this client.
|
||||
func (c *Client) GetSectorIdentifier() string {
|
||||
return c.SectorIdentifier
|
||||
}
|
||||
|
||||
// GetConsentResponseBody returns the proper consent response body for this session.OIDCWorkflowSession.
|
||||
func (c *Client) GetConsentResponseBody(consent *model.OAuth2ConsentSession) ConsentGetResponseBody {
|
||||
body := ConsentGetResponseBody{
|
||||
ClientID: c.ID,
|
||||
ClientDescription: c.Description,
|
||||
PreConfiguration: c.Consent.Mode == ClientConsentModePreConfigured,
|
||||
// GetTokenEndpointAuthSigningAlgorithm returns the JWS [JWS] alg algorithm [JWA] that MUST be used for signing the JWT
|
||||
// [JWT] used to authenticate the Client at the Token Endpoint for the private_key_jwt and client_secret_jwt
|
||||
// authentication methods.
|
||||
func (c *FullClient) GetTokenEndpointAuthSigningAlgorithm() string {
|
||||
if c.TokenEndpointAuthSigningAlgorithm == "" {
|
||||
c.TokenEndpointAuthSigningAlgorithm = SigningAlgorithmRSAWithSHA256
|
||||
}
|
||||
|
||||
if consent != nil {
|
||||
body.Scopes = consent.RequestedScopes
|
||||
body.Audience = consent.RequestedAudience
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
// GetID returns the ID.
|
||||
func (c *Client) GetID() string {
|
||||
return c.ID
|
||||
}
|
||||
|
||||
// GetHashedSecret returns the Secret.
|
||||
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
|
||||
return c.TokenEndpointAuthSigningAlgorithm
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/ory/fosite"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/authentication"
|
||||
"github.com/authelia/authelia/v4/internal/authorization"
|
||||
|
@ -15,36 +16,136 @@ import (
|
|||
)
|
||||
|
||||
func TestNewClient(t *testing.T) {
|
||||
blankConfig := schema.OpenIDConnectClientConfiguration{}
|
||||
blankClient := NewClient(blankConfig)
|
||||
assert.Equal(t, "", blankClient.ID)
|
||||
assert.Equal(t, "", blankClient.Description)
|
||||
assert.Equal(t, "", blankClient.Description)
|
||||
assert.Len(t, blankClient.ResponseModes, 0)
|
||||
config := schema.OpenIDConnectClientConfiguration{}
|
||||
client := NewClient(config)
|
||||
assert.Equal(t, "", client.GetID())
|
||||
assert.Equal(t, "", client.GetDescription())
|
||||
assert.Len(t, client.GetResponseModes(), 0)
|
||||
assert.Len(t, client.GetResponseTypes(), 1)
|
||||
assert.Equal(t, "", client.GetSectorIdentifier())
|
||||
|
||||
exampleConfig := schema.OpenIDConnectClientConfiguration{
|
||||
ID: "myapp",
|
||||
Description: "My App",
|
||||
Policy: "two_factor",
|
||||
Secret: MustDecodeSecret("$plaintext$abcdef"),
|
||||
RedirectURIs: []string{"https://google.com/callback"},
|
||||
bclient, ok := client.(*BaseClient)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "", bclient.UserinfoSigningAlgorithm)
|
||||
assert.Equal(t, SigningAlgorithmNone, client.GetUserinfoSigningAlgorithm())
|
||||
|
||||
_, 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,
|
||||
ResponseTypes: schema.DefaultOpenIDConnectClientConfiguration.ResponseTypes,
|
||||
GrantTypes: schema.DefaultOpenIDConnectClientConfiguration.GrantTypes,
|
||||
ResponseModes: schema.DefaultOpenIDConnectClientConfiguration.ResponseModes,
|
||||
}
|
||||
|
||||
exampleClient := NewClient(exampleConfig)
|
||||
assert.Equal(t, "myapp", exampleClient.ID)
|
||||
require.Len(t, exampleClient.ResponseModes, 3)
|
||||
assert.Equal(t, fosite.ResponseModeFormPost, exampleClient.ResponseModes[0])
|
||||
assert.Equal(t, fosite.ResponseModeQuery, exampleClient.ResponseModes[1])
|
||||
assert.Equal(t, fosite.ResponseModeFragment, exampleClient.ResponseModes[2])
|
||||
assert.Equal(t, authorization.TwoFactor, exampleClient.Policy)
|
||||
client = NewClient(config)
|
||||
assert.Equal(t, myclient, client.GetID())
|
||||
require.Len(t, client.GetResponseModes(), 1)
|
||||
assert.Equal(t, fosite.ResponseModeFormPost, client.GetResponseModes()[0])
|
||||
assert.Equal(t, authorization.TwoFactor, client.GetAuthorizationPolicy())
|
||||
|
||||
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) {
|
||||
c := Client{}
|
||||
c := &FullClient{BaseClient: &BaseClient{}}
|
||||
|
||||
c.Policy = authorization.Bypass
|
||||
assert.False(t, c.IsAuthenticationLevelSufficient(authentication.NotAuthenticated))
|
||||
|
@ -68,7 +169,7 @@ func TestIsAuthenticationLevelSufficient(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_GetConsentResponseBody(t *testing.T) {
|
||||
c := Client{}
|
||||
c := &FullClient{BaseClient: &BaseClient{}}
|
||||
|
||||
consentRequestBody := c.GetConsentResponseBody(nil)
|
||||
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.Audience)
|
||||
|
||||
c.ID = "myclient"
|
||||
c.Description = "My Client"
|
||||
c.ID = myclient
|
||||
c.Description = myclientdesc
|
||||
|
||||
consent := &model.OAuth2ConsentSession{
|
||||
RequestedAudience: []string{"https://example.com"},
|
||||
RequestedScopes: []string{"openid", "groups"},
|
||||
RequestedAudience: []string{examplecom},
|
||||
RequestedScopes: []string{ScopeOpenID, ScopeGroups},
|
||||
}
|
||||
|
||||
expectedScopes := []string{"openid", "groups"}
|
||||
expectedAudiences := []string{"https://example.com"}
|
||||
expectedScopes := []string{ScopeOpenID, ScopeGroups}
|
||||
expectedAudiences := []string{examplecom}
|
||||
|
||||
consentRequestBody = c.GetConsentResponseBody(consent)
|
||||
assert.Equal(t, "myclient", consentRequestBody.ClientID)
|
||||
assert.Equal(t, "My Client", consentRequestBody.ClientDescription)
|
||||
assert.Equal(t, myclient, consentRequestBody.ClientID)
|
||||
assert.Equal(t, myclientdesc, consentRequestBody.ClientDescription)
|
||||
assert.Equal(t, expectedScopes, consentRequestBody.Scopes)
|
||||
assert.Equal(t, expectedAudiences, consentRequestBody.Audience)
|
||||
}
|
||||
|
||||
func TestClient_GetAudience(t *testing.T) {
|
||||
c := Client{}
|
||||
c := &FullClient{BaseClient: &BaseClient{}}
|
||||
|
||||
audience := c.GetAudience()
|
||||
assert.Len(t, audience, 0)
|
||||
|
||||
c.Audience = []string{"https://example.com"}
|
||||
c.Audience = []string{examplecom}
|
||||
|
||||
audience = c.GetAudience()
|
||||
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) {
|
||||
c := Client{}
|
||||
c := &FullClient{BaseClient: &BaseClient{}}
|
||||
|
||||
scopes := c.GetScopes()
|
||||
assert.Len(t, scopes, 0)
|
||||
|
||||
c.Scopes = []string{"openid"}
|
||||
c.Scopes = []string{ScopeOpenID}
|
||||
|
||||
scopes = c.GetScopes()
|
||||
require.Len(t, scopes, 1)
|
||||
assert.Equal(t, "openid", scopes[0])
|
||||
assert.Equal(t, ScopeOpenID, scopes[0])
|
||||
}
|
||||
|
||||
func TestClient_GetGrantTypes(t *testing.T) {
|
||||
c := Client{}
|
||||
c := &FullClient{BaseClient: &BaseClient{}}
|
||||
|
||||
grantTypes := c.GetGrantTypes()
|
||||
require.Len(t, grantTypes, 1)
|
||||
assert.Equal(t, "authorization_code", grantTypes[0])
|
||||
assert.Equal(t, GrantTypeAuthorizationCode, grantTypes[0])
|
||||
|
||||
c.GrantTypes = []string{"device_code"}
|
||||
|
||||
|
@ -135,55 +236,55 @@ func TestClient_GetGrantTypes(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_Hashing(t *testing.T) {
|
||||
c := Client{}
|
||||
c := &FullClient{BaseClient: &BaseClient{}}
|
||||
|
||||
hashedSecret := c.GetHashedSecret()
|
||||
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")))
|
||||
}
|
||||
|
||||
func TestClient_GetHashedSecret(t *testing.T) {
|
||||
c := Client{}
|
||||
c := &FullClient{BaseClient: &BaseClient{}}
|
||||
|
||||
hashedSecret := c.GetHashedSecret()
|
||||
assert.Equal(t, []byte(nil), hashedSecret)
|
||||
|
||||
c.Secret = MustDecodeSecret("$plaintext$a_bad_secret")
|
||||
c.Secret = MustDecodeSecret(badsecret)
|
||||
|
||||
hashedSecret = c.GetHashedSecret()
|
||||
assert.Equal(t, []byte("$plaintext$a_bad_secret"), hashedSecret)
|
||||
assert.Equal(t, []byte(badsecret), hashedSecret)
|
||||
}
|
||||
|
||||
func TestClient_GetID(t *testing.T) {
|
||||
c := Client{}
|
||||
c := &FullClient{BaseClient: &BaseClient{}}
|
||||
|
||||
id := c.GetID()
|
||||
assert.Equal(t, "", id)
|
||||
|
||||
c.ID = "myid"
|
||||
c.ID = myclient
|
||||
|
||||
id = c.GetID()
|
||||
assert.Equal(t, "myid", id)
|
||||
assert.Equal(t, myclient, id)
|
||||
}
|
||||
|
||||
func TestClient_GetRedirectURIs(t *testing.T) {
|
||||
c := Client{}
|
||||
c := &FullClient{BaseClient: &BaseClient{}}
|
||||
|
||||
redirectURIs := c.GetRedirectURIs()
|
||||
require.Len(t, redirectURIs, 0)
|
||||
|
||||
c.RedirectURIs = []string{"https://example.com/oauth2/callback"}
|
||||
c.RedirectURIs = []string{examplecom}
|
||||
|
||||
redirectURIs = c.GetRedirectURIs()
|
||||
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) {
|
||||
c := Client{}
|
||||
c := &FullClient{BaseClient: &BaseClient{}}
|
||||
|
||||
responseModes := c.GetResponseModes()
|
||||
require.Len(t, responseModes, 0)
|
||||
|
@ -202,18 +303,18 @@ func TestClient_GetResponseModes(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_GetResponseTypes(t *testing.T) {
|
||||
c := Client{}
|
||||
c := &FullClient{BaseClient: &BaseClient{}}
|
||||
|
||||
responseTypes := c.GetResponseTypes()
|
||||
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()
|
||||
require.Len(t, responseTypes, 2)
|
||||
assert.Equal(t, "code", responseTypes[0])
|
||||
assert.Equal(t, "id_token", responseTypes[1])
|
||||
assert.Equal(t, ResponseTypeAuthorizationCodeFlow, responseTypes[0])
|
||||
assert.Equal(t, ResponseTypeImplicitFlowIDToken, responseTypes[1])
|
||||
}
|
||||
|
||||
func TestNewClientPKCE(t *testing.T) {
|
||||
|
@ -290,9 +391,9 @@ func TestNewClientPKCE(t *testing.T) {
|
|||
t.Run(tc.name, func(t *testing.T) {
|
||||
client := NewClient(tc.have)
|
||||
|
||||
assert.Equal(t, tc.expectedEnforcePKCE, client.EnforcePKCE)
|
||||
assert.Equal(t, tc.expectedEnforcePKCEChallengeMethod, client.EnforcePKCEChallengeMethod)
|
||||
assert.Equal(t, tc.expected, client.PKCEChallengeMethod)
|
||||
assert.Equal(t, tc.expectedEnforcePKCE, client.GetPKCEEnforcement())
|
||||
assert.Equal(t, tc.expectedEnforcePKCEChallengeMethod, client.GetPKCEChallengeMethodEnforcement())
|
||||
assert.Equal(t, tc.expected, client.GetPKCEChallengeMethod())
|
||||
|
||||
if tc.r != nil {
|
||||
err := client.ValidatePKCEPolicy(tc.r)
|
||||
|
@ -355,7 +456,7 @@ func TestNewClientPAR(t *testing.T) {
|
|||
t.Run(tc.name, func(t *testing.T) {
|
||||
client := NewClient(tc.have)
|
||||
|
||||
assert.Equal(t, tc.expected, client.EnforcePAR)
|
||||
assert.Equal(t, tc.expected, client.GetPAREnforcement())
|
||||
|
||||
if tc.r != nil {
|
||||
err := client.ValidatePARPolicy(tc.r, urnPARPrefix)
|
||||
|
@ -437,7 +538,7 @@ func TestNewClientResponseModes(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_IsPublic(t *testing.T) {
|
||||
c := Client{}
|
||||
c := &FullClient{BaseClient: &BaseClient{}}
|
||||
|
||||
assert.False(t, c.IsPublic())
|
||||
|
||||
|
|
|
@ -169,12 +169,6 @@ type LifespanConfig struct {
|
|||
RefreshToken time.Duration
|
||||
}
|
||||
|
||||
const (
|
||||
PromptNone = none
|
||||
PromptLogin = "login"
|
||||
PromptConsent = "consent"
|
||||
)
|
||||
|
||||
// LoadHandlers reloads the handlers based on the current configuration.
|
||||
func (c *Config) LoadHandlers(store *Store, strategy jwt.Signer) {
|
||||
validator := openid.NewOpenIDConnectRequestValidator(strategy, c)
|
||||
|
|
|
@ -69,15 +69,12 @@ const (
|
|||
GrantTypeImplicit = implicit
|
||||
GrantTypeRefreshToken = "refresh_token"
|
||||
GrantTypeAuthorizationCode = "authorization_code"
|
||||
GrantTypePassword = "password"
|
||||
GrantTypeClientCredentials = "client_credentials"
|
||||
)
|
||||
|
||||
// Client Auth Method strings.
|
||||
const (
|
||||
ClientAuthMethodClientSecretBasic = "client_secret_basic"
|
||||
ClientAuthMethodClientSecretPost = "client_secret_post"
|
||||
ClientAuthMethodClientSecretJWT = "client_secret_jwt"
|
||||
ClientAuthMethodNone = "none"
|
||||
)
|
||||
|
||||
|
@ -117,6 +114,13 @@ const (
|
|||
FormParameterCodeChallengeMethod = "code_challenge_method"
|
||||
)
|
||||
|
||||
const (
|
||||
PromptNone = none
|
||||
PromptLogin = "login"
|
||||
PromptConsent = "consent"
|
||||
// PromptCreate = "create" // This prompt value is currently unused.
|
||||
)
|
||||
|
||||
// Endpoints.
|
||||
const (
|
||||
EndpointAuthorization = "authorization"
|
||||
|
|
|
@ -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"
|
||||
)
|
|
@ -5,70 +5,76 @@ import (
|
|||
)
|
||||
|
||||
// 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{
|
||||
CommonDiscoveryOptions: CommonDiscoveryOptions{
|
||||
SubjectTypesSupported: []string{
|
||||
SubjectTypePublic,
|
||||
OAuth2WellKnownConfiguration: OAuth2WellKnownConfiguration{
|
||||
CommonDiscoveryOptions: CommonDiscoveryOptions{
|
||||
SubjectTypesSupported: []string{
|
||||
SubjectTypePublic,
|
||||
SubjectTypePairwise,
|
||||
},
|
||||
ResponseTypesSupported: []string{
|
||||
ResponseTypeAuthorizationCodeFlow,
|
||||
ResponseTypeImplicitFlowIDToken,
|
||||
ResponseTypeImplicitFlowToken,
|
||||
ResponseTypeImplicitFlowBoth,
|
||||
ResponseTypeHybridFlowIDToken,
|
||||
ResponseTypeHybridFlowToken,
|
||||
ResponseTypeHybridFlowBoth,
|
||||
},
|
||||
GrantTypesSupported: []string{
|
||||
GrantTypeAuthorizationCode,
|
||||
GrantTypeImplicit,
|
||||
GrantTypeRefreshToken,
|
||||
},
|
||||
ResponseModesSupported: []string{
|
||||
ResponseModeFormPost,
|
||||
ResponseModeQuery,
|
||||
ResponseModeFragment,
|
||||
},
|
||||
ScopesSupported: []string{
|
||||
ScopeOfflineAccess,
|
||||
ScopeOpenID,
|
||||
ScopeProfile,
|
||||
ScopeGroups,
|
||||
ScopeEmail,
|
||||
},
|
||||
ClaimsSupported: []string{
|
||||
ClaimAuthenticationMethodsReference,
|
||||
ClaimAudience,
|
||||
ClaimAuthorizedParty,
|
||||
ClaimClientIdentifier,
|
||||
ClaimExpirationTime,
|
||||
ClaimIssuedAt,
|
||||
ClaimIssuer,
|
||||
ClaimJWTID,
|
||||
ClaimRequestedAt,
|
||||
ClaimSubject,
|
||||
ClaimAuthenticationTime,
|
||||
ClaimNonce,
|
||||
ClaimPreferredEmail,
|
||||
ClaimEmailVerified,
|
||||
ClaimEmailAlts,
|
||||
ClaimGroups,
|
||||
ClaimPreferredUsername,
|
||||
ClaimFullName,
|
||||
},
|
||||
TokenEndpointAuthMethodsSupported: []string{
|
||||
ClientAuthMethodClientSecretBasic,
|
||||
ClientAuthMethodClientSecretPost,
|
||||
ClientAuthMethodNone,
|
||||
},
|
||||
},
|
||||
ResponseTypesSupported: []string{
|
||||
ResponseTypeAuthorizationCodeFlow,
|
||||
ResponseTypeImplicitFlowIDToken,
|
||||
ResponseTypeImplicitFlowToken,
|
||||
ResponseTypeImplicitFlowBoth,
|
||||
ResponseTypeHybridFlowIDToken,
|
||||
ResponseTypeHybridFlowToken,
|
||||
ResponseTypeHybridFlowBoth,
|
||||
OAuth2DiscoveryOptions: OAuth2DiscoveryOptions{
|
||||
CodeChallengeMethodsSupported: []string{
|
||||
PKCEChallengeMethodSHA256,
|
||||
},
|
||||
},
|
||||
GrantTypesSupported: []string{
|
||||
GrantTypeAuthorizationCode,
|
||||
GrantTypeImplicit,
|
||||
GrantTypeRefreshToken,
|
||||
},
|
||||
ResponseModesSupported: []string{
|
||||
ResponseModeFormPost,
|
||||
ResponseModeQuery,
|
||||
ResponseModeFragment,
|
||||
},
|
||||
ScopesSupported: []string{
|
||||
ScopeOfflineAccess,
|
||||
ScopeOpenID,
|
||||
ScopeProfile,
|
||||
ScopeGroups,
|
||||
ScopeEmail,
|
||||
},
|
||||
ClaimsSupported: []string{
|
||||
ClaimAuthenticationMethodsReference,
|
||||
ClaimAudience,
|
||||
ClaimAuthorizedParty,
|
||||
ClaimClientIdentifier,
|
||||
ClaimExpirationTime,
|
||||
ClaimIssuedAt,
|
||||
ClaimIssuer,
|
||||
ClaimJWTID,
|
||||
ClaimRequestedAt,
|
||||
ClaimSubject,
|
||||
ClaimAuthenticationTime,
|
||||
ClaimNonce,
|
||||
ClaimPreferredEmail,
|
||||
ClaimEmailVerified,
|
||||
ClaimEmailAlts,
|
||||
ClaimGroups,
|
||||
ClaimPreferredUsername,
|
||||
ClaimFullName,
|
||||
},
|
||||
TokenEndpointAuthMethodsSupported: []string{
|
||||
ClientAuthMethodClientSecretBasic,
|
||||
ClientAuthMethodClientSecretPost,
|
||||
ClientAuthMethodClientSecretJWT,
|
||||
ClientAuthMethodNone,
|
||||
},
|
||||
},
|
||||
OAuth2DiscoveryOptions: OAuth2DiscoveryOptions{
|
||||
CodeChallengeMethodsSupported: []string{
|
||||
PKCEChallengeMethodSHA256,
|
||||
OAuth2PushedAuthorizationDiscoveryOptions: &OAuth2PushedAuthorizationDiscoveryOptions{
|
||||
RequirePushedAuthorizationRequests: c.PAR.Enforce,
|
||||
},
|
||||
},
|
||||
|
||||
OpenIDConnectDiscoveryOptions: OpenIDConnectDiscoveryOptions{
|
||||
IDTokenSigningAlgValuesSupported: []string{
|
||||
SigningAlgorithmRSAWithSHA256,
|
||||
|
@ -77,30 +83,15 @@ func NewOpenIDConnectWellKnownConfiguration(c *schema.OpenIDConnectConfiguration
|
|||
SigningAlgorithmNone,
|
||||
SigningAlgorithmRSAWithSHA256,
|
||||
},
|
||||
RequestObjectSigningAlgValuesSupported: []string{
|
||||
SigningAlgorithmNone,
|
||||
SigningAlgorithmRSAWithSHA256,
|
||||
},
|
||||
OpenIDConnectFrontChannelLogoutDiscoveryOptions: &OpenIDConnectFrontChannelLogoutDiscoveryOptions{},
|
||||
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 {
|
||||
|
@ -109,3 +100,93 @@ func NewOpenIDConnectWellKnownConfiguration(c *schema.OpenIDConnectConfiguration
|
|||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -13,51 +13,51 @@ func TestNewOpenIDConnectWellKnownConfiguration(t *testing.T) {
|
|||
desc string
|
||||
pkcePlainChallenge bool
|
||||
enforcePAR bool
|
||||
clients map[string]*Client
|
||||
clients map[string]Client
|
||||
|
||||
expectCodeChallengeMethodsSupported, expectSubjectTypesSupported []string
|
||||
}{
|
||||
{
|
||||
desc: "ShouldHaveChallengeMethodsS256ANDSubjectTypesSupportedPublic",
|
||||
pkcePlainChallenge: false,
|
||||
clients: map[string]*Client{"a": {}},
|
||||
clients: map[string]Client{"a": &BaseClient{}},
|
||||
expectCodeChallengeMethodsSupported: []string{PKCEChallengeMethodSHA256},
|
||||
expectSubjectTypesSupported: []string{SubjectTypePublic},
|
||||
expectSubjectTypesSupported: []string{SubjectTypePublic, SubjectTypePairwise},
|
||||
},
|
||||
{
|
||||
desc: "ShouldHaveChallengeMethodsS256PlainANDSubjectTypesSupportedPublic",
|
||||
pkcePlainChallenge: true,
|
||||
clients: map[string]*Client{"a": {}},
|
||||
clients: map[string]Client{"a": &BaseClient{}},
|
||||
expectCodeChallengeMethodsSupported: []string{PKCEChallengeMethodSHA256, PKCEChallengeMethodPlain},
|
||||
expectSubjectTypesSupported: []string{SubjectTypePublic},
|
||||
expectSubjectTypesSupported: []string{SubjectTypePublic, SubjectTypePairwise},
|
||||
},
|
||||
{
|
||||
desc: "ShouldHaveChallengeMethodsS256ANDSubjectTypesSupportedPublicPairwise",
|
||||
pkcePlainChallenge: false,
|
||||
clients: map[string]*Client{"a": {SectorIdentifier: "yes"}},
|
||||
clients: map[string]Client{"a": &BaseClient{SectorIdentifier: "yes"}},
|
||||
expectCodeChallengeMethodsSupported: []string{PKCEChallengeMethodSHA256},
|
||||
expectSubjectTypesSupported: []string{SubjectTypePublic, SubjectTypePairwise},
|
||||
},
|
||||
{
|
||||
desc: "ShouldHaveChallengeMethodsS256PlainANDSubjectTypesSupportedPublicPairwise",
|
||||
pkcePlainChallenge: true,
|
||||
clients: map[string]*Client{"a": {SectorIdentifier: "yes"}},
|
||||
clients: map[string]Client{"a": &BaseClient{SectorIdentifier: "yes"}},
|
||||
expectCodeChallengeMethodsSupported: []string{PKCEChallengeMethodSHA256, PKCEChallengeMethodPlain},
|
||||
expectSubjectTypesSupported: []string{SubjectTypePublic, SubjectTypePairwise},
|
||||
},
|
||||
{
|
||||
desc: "ShouldHaveTokenAuthMethodsNone",
|
||||
pkcePlainChallenge: true,
|
||||
clients: map[string]*Client{"a": {SectorIdentifier: "yes"}},
|
||||
clients: map[string]Client{"a": &BaseClient{SectorIdentifier: "yes"}},
|
||||
expectCodeChallengeMethodsSupported: []string{PKCEChallengeMethodSHA256, PKCEChallengeMethodPlain},
|
||||
expectSubjectTypesSupported: []string{SubjectTypePublic, SubjectTypePairwise},
|
||||
},
|
||||
{
|
||||
desc: "ShouldHaveTokenAuthMethodsNone",
|
||||
pkcePlainChallenge: true,
|
||||
clients: map[string]*Client{
|
||||
"a": {SectorIdentifier: "yes"},
|
||||
"b": {SectorIdentifier: "yes"},
|
||||
clients: map[string]Client{
|
||||
"a": &BaseClient{SectorIdentifier: "yes"},
|
||||
"b": &BaseClient{SectorIdentifier: "yes"},
|
||||
},
|
||||
expectCodeChallengeMethodsSupported: []string{PKCEChallengeMethodSHA256, PKCEChallengeMethodPlain},
|
||||
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 {
|
||||
assert.Contains(t, actual.CodeChallengeMethodsSupported, codeChallengeMethod)
|
||||
}
|
||||
|
|
|
@ -37,17 +37,14 @@ func NewOpenIDConnectProvider(config *schema.OpenIDConnectConfiguration, store s
|
|||
|
||||
provider.Config.LoadHandlers(provider.Store, provider.KeyManager.Strategy())
|
||||
|
||||
provider.discovery = NewOpenIDConnectWellKnownConfiguration(config, provider.Store.clients)
|
||||
provider.discovery = NewOpenIDConnectWellKnownConfiguration(config)
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
|
||||
// GetOAuth2WellKnownConfiguration returns the discovery document for the OAuth Configuration.
|
||||
func (p *OpenIDConnectProvider) GetOAuth2WellKnownConfiguration(issuer string) OAuth2WellKnownConfiguration {
|
||||
options := OAuth2WellKnownConfiguration{
|
||||
CommonDiscoveryOptions: p.discovery.CommonDiscoveryOptions,
|
||||
OAuth2DiscoveryOptions: p.discovery.OAuth2DiscoveryOptions,
|
||||
}
|
||||
options := p.discovery.OAuth2WellKnownConfiguration.Copy()
|
||||
|
||||
options.Issuer = issuer
|
||||
|
||||
|
@ -63,13 +60,7 @@ func (p *OpenIDConnectProvider) GetOAuth2WellKnownConfiguration(issuer string) O
|
|||
|
||||
// GetOpenIDConnectWellKnownConfiguration returns the discovery document for the OpenID Configuration.
|
||||
func (p *OpenIDConnectProvider) GetOpenIDConnectWellKnownConfiguration(issuer string) OpenIDConnectWellKnownConfiguration {
|
||||
options := OpenIDConnectWellKnownConfiguration{
|
||||
CommonDiscoveryOptions: p.discovery.CommonDiscoveryOptions,
|
||||
OAuth2DiscoveryOptions: p.discovery.OAuth2DiscoveryOptions,
|
||||
OpenIDConnectDiscoveryOptions: p.discovery.OpenIDConnectDiscoveryOptions,
|
||||
OpenIDConnectFrontChannelLogoutDiscoveryOptions: p.discovery.OpenIDConnectFrontChannelLogoutDiscoveryOptions,
|
||||
OpenIDConnectBackChannelLogoutDiscoveryOptions: p.discovery.OpenIDConnectBackChannelLogoutDiscoveryOptions,
|
||||
}
|
||||
options := p.discovery.Copy()
|
||||
|
||||
options.Issuer = issuer
|
||||
|
||||
|
|
|
@ -27,15 +27,15 @@ func TestNewOpenIDConnectProvider_ShouldEnableOptionalDiscoveryValues(t *testing
|
|||
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||
IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey),
|
||||
EnablePKCEPlainChallenge: true,
|
||||
HMACSecret: "asbdhaaskmdlkamdklasmdlkams",
|
||||
HMACSecret: badhmac,
|
||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||
{
|
||||
ID: "a-client",
|
||||
Secret: MustDecodeSecret("$plaintext$a-client-secret"),
|
||||
SectorIdentifier: url.URL{Host: "google.com"},
|
||||
Policy: "one_factor",
|
||||
ID: myclient,
|
||||
Secret: MustDecodeSecret(badsecret),
|
||||
SectorIdentifier: url.URL{Host: examplecomsid},
|
||||
Policy: onefactor,
|
||||
RedirectURIs: []string{
|
||||
"https://google.com",
|
||||
examplecom,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -43,7 +43,7 @@ func TestNewOpenIDConnectProvider_ShouldEnableOptionalDiscoveryValues(t *testing
|
|||
|
||||
assert.NoError(t, err)
|
||||
|
||||
disco := provider.GetOpenIDConnectWellKnownConfiguration("https://example.com")
|
||||
disco := provider.GetOpenIDConnectWellKnownConfiguration(examplecom)
|
||||
|
||||
assert.Len(t, disco.SubjectTypesSupported, 2)
|
||||
assert.Contains(t, disco.SubjectTypesSupported, SubjectTypePublic)
|
||||
|
@ -58,12 +58,12 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GoodConfiguration(t *tes
|
|||
provider, err := NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
||||
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||
IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey),
|
||||
HMACSecret: "asbdhaaskmdlkamdklasmdlkams",
|
||||
HMACSecret: badhmac,
|
||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||
{
|
||||
ID: "a-client",
|
||||
Secret: MustDecodeSecret("$plaintext$a-client-secret"),
|
||||
Policy: "one_factor",
|
||||
Policy: onefactor,
|
||||
RedirectURIs: []string{
|
||||
"https://google.com",
|
||||
},
|
||||
|
@ -72,7 +72,7 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GoodConfiguration(t *tes
|
|||
ID: "b-client",
|
||||
Description: "Normal DisplayName",
|
||||
Secret: MustDecodeSecret("$plaintext$b-client-secret"),
|
||||
Policy: "two_factor",
|
||||
Policy: twofactor,
|
||||
RedirectURIs: []string{
|
||||
"https://google.com",
|
||||
},
|
||||
|
@ -103,7 +103,7 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOpenIDConnectWellKnow
|
|||
{
|
||||
ID: "a-client",
|
||||
Secret: MustDecodeSecret("$plaintext$a-client-secret"),
|
||||
Policy: "one_factor",
|
||||
Policy: onefactor,
|
||||
RedirectURIs: []string{
|
||||
"https://google.com",
|
||||
},
|
||||
|
@ -113,9 +113,9 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOpenIDConnectWellKnow
|
|||
|
||||
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/api/oidc/authorization", disco.AuthorizationEndpoint)
|
||||
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, ResponseModeFragment)
|
||||
|
||||
assert.Len(t, disco.SubjectTypesSupported, 1)
|
||||
assert.Len(t, disco.SubjectTypesSupported, 2)
|
||||
assert.Contains(t, disco.SubjectTypesSupported, SubjectTypePublic)
|
||||
assert.Contains(t, disco.SubjectTypesSupported, SubjectTypePairwise)
|
||||
|
||||
assert.Len(t, disco.ResponseTypesSupported, 7)
|
||||
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, ResponseTypeHybridFlowBoth)
|
||||
|
||||
assert.Len(t, disco.TokenEndpointAuthMethodsSupported, 4)
|
||||
assert.Len(t, disco.TokenEndpointAuthMethodsSupported, 3)
|
||||
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretBasic)
|
||||
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretPost)
|
||||
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretJWT)
|
||||
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodNone)
|
||||
|
||||
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, SigningAlgorithmNone)
|
||||
|
||||
assert.Len(t, disco.RequestObjectSigningAlgValuesSupported, 2)
|
||||
assert.Contains(t, disco.RequestObjectSigningAlgValuesSupported, SigningAlgorithmRSAWithSHA256)
|
||||
assert.Contains(t, disco.RequestObjectSigningAlgValuesSupported, SigningAlgorithmNone)
|
||||
assert.Len(t, disco.RequestObjectSigningAlgValuesSupported, 0)
|
||||
|
||||
assert.Len(t, disco.ClaimsSupported, 18)
|
||||
assert.Contains(t, disco.ClaimsSupported, ClaimAuthenticationMethodsReference)
|
||||
|
@ -203,7 +201,7 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOAuth2WellKnownConfig
|
|||
{
|
||||
ID: "a-client",
|
||||
Secret: MustDecodeSecret("$plaintext$a-client-secret"),
|
||||
Policy: "one_factor",
|
||||
Policy: onefactor,
|
||||
RedirectURIs: []string{
|
||||
"https://google.com",
|
||||
},
|
||||
|
@ -213,9 +211,9 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOAuth2WellKnownConfig
|
|||
|
||||
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/api/oidc/authorization", disco.AuthorizationEndpoint)
|
||||
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, ResponseModeFragment)
|
||||
|
||||
assert.Len(t, disco.SubjectTypesSupported, 1)
|
||||
assert.Len(t, disco.SubjectTypesSupported, 2)
|
||||
assert.Contains(t, disco.SubjectTypesSupported, SubjectTypePublic)
|
||||
assert.Contains(t, disco.SubjectTypesSupported, SubjectTypePairwise)
|
||||
|
||||
assert.Len(t, disco.ResponseTypesSupported, 7)
|
||||
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, ResponseTypeHybridFlowBoth)
|
||||
|
||||
assert.Len(t, disco.TokenEndpointAuthMethodsSupported, 4)
|
||||
assert.Len(t, disco.TokenEndpointAuthMethodsSupported, 3)
|
||||
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretBasic)
|
||||
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretPost)
|
||||
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretJWT)
|
||||
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodNone)
|
||||
|
||||
assert.Len(t, disco.GrantTypesSupported, 3)
|
||||
|
@ -292,7 +290,7 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOpenIDConnectWellKnow
|
|||
{
|
||||
ID: "a-client",
|
||||
Secret: MustDecodeSecret("$plaintext$a-client-secret"),
|
||||
Policy: "one_factor",
|
||||
Policy: onefactor,
|
||||
RedirectURIs: []string{
|
||||
"https://google.com",
|
||||
},
|
||||
|
@ -302,7 +300,7 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOpenIDConnectWellKnow
|
|||
|
||||
assert.NoError(t, err)
|
||||
|
||||
disco := provider.GetOpenIDConnectWellKnownConfiguration("https://example.com")
|
||||
disco := provider.GetOpenIDConnectWellKnownConfiguration(examplecom)
|
||||
|
||||
require.Len(t, disco.CodeChallengeMethodsSupported, 2)
|
||||
assert.Equal(t, PKCEChallengeMethodSHA256, disco.CodeChallengeMethodsSupported[0])
|
||||
|
|
|
@ -24,7 +24,7 @@ func NewStore(config *schema.OpenIDConnectConfiguration, provider storage.Provid
|
|||
|
||||
store = &Store{
|
||||
provider: provider,
|
||||
clients: map[string]*Client{},
|
||||
clients: map[string]Client{},
|
||||
}
|
||||
|
||||
for _, client := range config.Clients {
|
||||
|
@ -72,11 +72,11 @@ func (s *Store) GetClientPolicy(id string) (level authorization.Level) {
|
|||
return authorization.TwoFactor
|
||||
}
|
||||
|
||||
return client.Policy
|
||||
return client.GetAuthorizationPolicy()
|
||||
}
|
||||
|
||||
// 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]
|
||||
if !ok {
|
||||
return nil, fosite.ErrInvalidClient
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/ory/fosite"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
|
@ -17,23 +18,23 @@ func TestOpenIDConnectStore_GetClientPolicy(t *testing.T) {
|
|||
IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey),
|
||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||
{
|
||||
ID: "myclient",
|
||||
Description: "myclient desc",
|
||||
Policy: "one_factor",
|
||||
ID: myclient,
|
||||
Description: myclientdesc,
|
||||
Policy: onefactor,
|
||||
Scopes: []string{ScopeOpenID, ScopeProfile},
|
||||
Secret: MustDecodeSecret("$plaintext$mysecret"),
|
||||
},
|
||||
{
|
||||
ID: "myotherclient",
|
||||
Description: "myclient desc",
|
||||
Policy: "two_factor",
|
||||
Description: myclientdesc,
|
||||
Policy: twofactor,
|
||||
Scopes: []string{ScopeOpenID, ScopeProfile},
|
||||
Secret: MustDecodeSecret("$plaintext$mysecret"),
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
|
||||
policyOne := s.GetClientPolicy("myclient")
|
||||
policyOne := s.GetClientPolicy(myclient)
|
||||
assert.Equal(t, authorization.OneFactor, policyOne)
|
||||
|
||||
policyTwo := s.GetClientPolicy("myotherclient")
|
||||
|
@ -49,9 +50,9 @@ func TestOpenIDConnectStore_GetInternalClient(t *testing.T) {
|
|||
IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey),
|
||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||
{
|
||||
ID: "myclient",
|
||||
Description: "myclient desc",
|
||||
Policy: "one_factor",
|
||||
ID: myclient,
|
||||
Description: myclientdesc,
|
||||
Policy: onefactor,
|
||||
Scopes: []string{ScopeOpenID, ScopeProfile},
|
||||
Secret: MustDecodeSecret("$plaintext$mysecret"),
|
||||
},
|
||||
|
@ -62,17 +63,19 @@ func TestOpenIDConnectStore_GetInternalClient(t *testing.T) {
|
|||
assert.EqualError(t, err, "invalid_client")
|
||||
assert.Nil(t, client)
|
||||
|
||||
client, err = s.GetClient(context.Background(), "myclient")
|
||||
client, err = s.GetClient(context.Background(), myclient)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, client)
|
||||
assert.Equal(t, "myclient", client.GetID())
|
||||
assert.Equal(t, myclient, client.GetID())
|
||||
}
|
||||
|
||||
func TestOpenIDConnectStore_GetInternalClient_ValidClient(t *testing.T) {
|
||||
id := myclient
|
||||
|
||||
c1 := schema.OpenIDConnectClientConfiguration{
|
||||
ID: "myclient",
|
||||
Description: "myclient desc",
|
||||
Policy: "one_factor",
|
||||
ID: id,
|
||||
Description: myclientdesc,
|
||||
Policy: onefactor,
|
||||
Scopes: []string{ScopeOpenID, ScopeProfile},
|
||||
Secret: MustDecodeSecret("$plaintext$mysecret"),
|
||||
}
|
||||
|
@ -83,24 +86,24 @@ func TestOpenIDConnectStore_GetInternalClient_ValidClient(t *testing.T) {
|
|||
Clients: []schema.OpenIDConnectClientConfiguration{c1},
|
||||
}, nil)
|
||||
|
||||
client, err := s.GetFullClient(c1.ID)
|
||||
client, err := s.GetFullClient(id)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, client)
|
||||
assert.Equal(t, client.ID, c1.ID)
|
||||
assert.Equal(t, client.Description, c1.Description)
|
||||
assert.Equal(t, client.Scopes, c1.Scopes)
|
||||
assert.Equal(t, client.GrantTypes, c1.GrantTypes)
|
||||
assert.Equal(t, client.ResponseTypes, c1.ResponseTypes)
|
||||
assert.Equal(t, client.RedirectURIs, c1.RedirectURIs)
|
||||
assert.Equal(t, client.Policy, authorization.OneFactor)
|
||||
assert.Equal(t, client.Secret.Encode(), "$plaintext$mysecret")
|
||||
assert.Equal(t, id, client.GetID())
|
||||
assert.Equal(t, myclientdesc, client.GetDescription())
|
||||
assert.Equal(t, fosite.Arguments(c1.Scopes), client.GetScopes())
|
||||
assert.Equal(t, fosite.Arguments([]string{GrantTypeAuthorizationCode}), client.GetGrantTypes())
|
||||
assert.Equal(t, fosite.Arguments([]string{ResponseTypeAuthorizationCodeFlow}), client.GetResponseTypes())
|
||||
assert.Equal(t, []string(nil), client.GetRedirectURIs())
|
||||
assert.Equal(t, authorization.OneFactor, client.GetAuthorizationPolicy())
|
||||
assert.Equal(t, "$plaintext$mysecret", client.GetSecret().Encode())
|
||||
}
|
||||
|
||||
func TestOpenIDConnectStore_GetInternalClient_InvalidClient(t *testing.T) {
|
||||
c1 := schema.OpenIDConnectClientConfiguration{
|
||||
ID: "myclient",
|
||||
Description: "myclient desc",
|
||||
Policy: "one_factor",
|
||||
ID: myclient,
|
||||
Description: myclientdesc,
|
||||
Policy: onefactor,
|
||||
Scopes: []string{ScopeOpenID, ScopeProfile},
|
||||
Secret: MustDecodeSecret("$plaintext$mysecret"),
|
||||
}
|
||||
|
@ -122,16 +125,16 @@ func TestOpenIDConnectStore_IsValidClientID(t *testing.T) {
|
|||
IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey),
|
||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||
{
|
||||
ID: "myclient",
|
||||
Description: "myclient desc",
|
||||
Policy: "one_factor",
|
||||
ID: myclient,
|
||||
Description: myclientdesc,
|
||||
Policy: onefactor,
|
||||
Scopes: []string{ScopeOpenID, ScopeProfile},
|
||||
Secret: MustDecodeSecret("$plaintext$mysecret"),
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
|
||||
validClient := s.IsValidClientID("myclient")
|
||||
validClient := s.IsValidClientID(myclient)
|
||||
invalidClient := s.IsValidClientID("myinvalidclient")
|
||||
|
||||
assert.True(t, validClient)
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/ory/herodot"
|
||||
"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/model"
|
||||
"github.com/authelia/authelia/v4/internal/storage"
|
||||
|
@ -97,17 +98,19 @@ type OpenIDConnectProvider struct {
|
|||
// openid.OpenIDConnectRequestStorage, and partially implements rfc7523.RFC7523KeyStorage.
|
||||
type Store struct {
|
||||
provider storage.Provider
|
||||
clients map[string]*Client
|
||||
clients map[string]Client
|
||||
}
|
||||
|
||||
// Client represents the client internally.
|
||||
type Client struct {
|
||||
// BaseClient is the base for all clients.
|
||||
type BaseClient struct {
|
||||
ID string
|
||||
Description string
|
||||
Secret algorithm.Digest
|
||||
SectorIdentifier string
|
||||
Public bool
|
||||
|
||||
EnforcePAR bool
|
||||
|
||||
EnforcePKCE bool
|
||||
EnforcePKCEChallengeMethod bool
|
||||
PKCEChallengeMethod string
|
||||
|
@ -119,8 +122,6 @@ type Client struct {
|
|||
ResponseTypes []string
|
||||
ResponseModes []fosite.ResponseModeType
|
||||
|
||||
EnforcePAR bool
|
||||
|
||||
UserinfoSigningAlgorithm string
|
||||
|
||||
Policy authorization.Level
|
||||
|
@ -128,6 +129,43 @@ type Client struct {
|
|||
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.
|
||||
func NewClientConsent(mode string, duration *time.Duration) ClientConsent {
|
||||
switch mode {
|
||||
|
@ -344,6 +382,12 @@ type CommonDiscoveryOptions struct {
|
|||
Client if it is given.
|
||||
*/
|
||||
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.
|
||||
|
@ -427,6 +471,98 @@ type OAuth2DiscoveryOptions struct {
|
|||
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.
|
||||
type OpenIDConnectDiscoveryOptions struct {
|
||||
/*
|
||||
|
@ -552,6 +688,12 @@ type OpenIDConnectDiscoveryOptions struct {
|
|||
*/
|
||||
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
|
||||
support. If omitted, the default value is true.
|
||||
|
@ -612,39 +754,202 @@ type OpenIDConnectBackChannelLogoutDiscoveryOptions struct {
|
|||
BackChannelLogoutSessionSupported bool `json:"backchannel_logout_session_supported"`
|
||||
}
|
||||
|
||||
// PushedAuthorizationDiscoveryOptions represents the well known discovery document specific to the
|
||||
// OAuth 2.0 Pushed Authorization Requests (RFC9126) implementation.
|
||||
// OpenIDConnectSessionManagementDiscoveryOptions represents the discovery options specific to OpenID Connect 1.0
|
||||
// Session Management.
|
||||
//
|
||||
// OAuth 2.0 Pushed Authorization Requests: https://datatracker.ietf.org/doc/html/rfc9126#section-5
|
||||
type PushedAuthorizationDiscoveryOptions struct {
|
||||
// To support OpenID Connect Session Management, the RP needs to obtain the Session Management 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 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
|
||||
exchange for a "request_uri" value usable at the authorization server.
|
||||
REQUIRED. URL of an OP iframe that supports cross-origin communications for session state information with the
|
||||
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.
|
||||
If omitted, the default value is "false".
|
||||
REQUIRED. JSON array containing one or more of the following values: poll, ping, and push.
|
||||
*/
|
||||
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.
|
||||
type OAuth2WellKnownConfiguration struct {
|
||||
CommonDiscoveryOptions
|
||||
OAuth2DiscoveryOptions
|
||||
PushedAuthorizationDiscoveryOptions
|
||||
*OAuth2DeviceAuthorizationGrantDiscoveryOptions
|
||||
*OAuth2MutualTLSClientAuthenticationDiscoveryOptions
|
||||
*OAuth2IssuerIdentificationDiscoveryOptions
|
||||
*OAuth2JWTIntrospectionResponseDiscoveryOptions
|
||||
*OAuth2JWTSecuredAuthorizationRequestDiscoveryOptions
|
||||
*OAuth2PushedAuthorizationDiscoveryOptions
|
||||
}
|
||||
|
||||
// OpenIDConnectWellKnownConfiguration represents the well known discovery document specific to OpenID Connect.
|
||||
type OpenIDConnectWellKnownConfiguration struct {
|
||||
CommonDiscoveryOptions
|
||||
OAuth2DiscoveryOptions
|
||||
PushedAuthorizationDiscoveryOptions
|
||||
OAuth2WellKnownConfiguration
|
||||
|
||||
OpenIDConnectDiscoveryOptions
|
||||
OpenIDConnectFrontChannelLogoutDiscoveryOptions
|
||||
OpenIDConnectBackChannelLogoutDiscoveryOptions
|
||||
*OpenIDConnectFrontChannelLogoutDiscoveryOptions
|
||||
*OpenIDConnectBackChannelLogoutDiscoveryOptions
|
||||
*OpenIDConnectSessionManagementDiscoveryOptions
|
||||
*OpenIDConnectRPInitiatedLogoutDiscoveryOptions
|
||||
*OpenIDConnectPromptCreateDiscoveryOptions
|
||||
*OpenIDConnectClientInitiatedBackChannelAuthFlowDiscoveryOptions
|
||||
*OpenIDConnectJWTSecuredAuthorizationResponseModeDiscoveryOptions
|
||||
*OpenIDFederationDiscoveryOptions
|
||||
}
|
||||
|
||||
// OpenIDConnectContext represents the context implementation that is used by some OpenID Connect 1.0 implementations.
|
||||
|
|
|
@ -40,7 +40,7 @@ func TestNewSessionWithAuthorizeRequest(t *testing.T) {
|
|||
Request: fosite.Request{
|
||||
ID: requestID.String(),
|
||||
Form: formValues,
|
||||
Client: &Client{ID: "example"},
|
||||
Client: &BaseClient{ID: "example"},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ func TestNewSessionWithAuthorizeRequest(t *testing.T) {
|
|||
|
||||
requested := time.Unix(1647332518, 0)
|
||||
authAt := time.Unix(1647332500, 0)
|
||||
issuer := "https://example.com"
|
||||
issuer := examplecom
|
||||
amr := []string{AMRPasswordBasedAuthentication}
|
||||
|
||||
consent := &model.OAuth2ConsentSession{
|
||||
|
|
|
@ -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
|
@ -1,11 +1,32 @@
|
|||
{
|
||||
"Base":"{{ .Base }}",
|
||||
"DuoSelfEnrollment":"{{ .DuoSelfEnrollment }}",
|
||||
"LogoOverride":"{{ .LogoOverride }}",
|
||||
"RememberMe":"{{ .RememberMe }}",
|
||||
"ResetPassword":"{{ .ResetPassword }}",
|
||||
"ResetPasswordCustomURL":"{{ .ResetPasswordCustomURL }}",
|
||||
"PrivacyPolicyURL":"{{ .PrivacyPolicyURL }}",
|
||||
"PrivacyPolicyAccept":"{{ .PrivacyPolicyAccept }}",
|
||||
"Theme":"{{ .Theme }}"
|
||||
}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<base href="{{ .BaseURL }}" />
|
||||
<meta property="csp-nonce" content="{{ .CSPNonce }}" />
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="description" content="Authelia login portal for your apps" />
|
||||
<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>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
version: '3'
|
||||
services:
|
||||
envoy:
|
||||
image: envoyproxy/envoy:v1.25.4
|
||||
image: envoyproxy/envoy:v1.25.5
|
||||
volumes:
|
||||
- ./example/compose/envoy/envoy.yaml:/etc/envoy/envoy.yaml
|
||||
- ./common/pki:/pki
|
||||
|
|
|
@ -200,8 +200,8 @@ func URLsFromStringSlice(urls []string) []url.URL {
|
|||
}
|
||||
|
||||
// OriginFromURL returns an origin url.URL given another url.URL.
|
||||
func OriginFromURL(u url.URL) (origin url.URL) {
|
||||
return url.URL{
|
||||
func OriginFromURL(u *url.URL) (origin *url.URL) {
|
||||
return &url.URL{
|
||||
Scheme: u.Scheme,
|
||||
Host: u.Host,
|
||||
}
|
||||
|
|
|
@ -242,7 +242,7 @@ func TestOriginFromURL(t *testing.T) {
|
|||
google, err := url.Parse("https://google.com/abc?a=123#five")
|
||||
assert.NoError(t, err)
|
||||
|
||||
origin := OriginFromURL(*google)
|
||||
origin := OriginFromURL(google)
|
||||
assert.Equal(t, "https://google.com", origin.String())
|
||||
}
|
||||
|
||||
|
|
|
@ -72,13 +72,13 @@
|
|||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "17.5.1",
|
||||
"@commitlint/config-conventional": "17.4.4",
|
||||
"@commitlint/cli": "17.6.0",
|
||||
"@commitlint/config-conventional": "17.6.0",
|
||||
"@limegrass/eslint-plugin-import-alias": "1.0.6",
|
||||
"@testing-library/jest-dom": "5.16.5",
|
||||
"@testing-library/react": "14.0.0",
|
||||
"@types/node": "18.15.11",
|
||||
"@types/react": "18.0.34",
|
||||
"@types/react": "18.0.35",
|
||||
"@types/react-dom": "18.0.11",
|
||||
"@types/testing-library__jest-dom": "5.14.5",
|
||||
"@types/zxcvbn": "4.4.1",
|
||||
|
@ -97,7 +97,7 @@
|
|||
"eslint-plugin-prettier": "4.2.1",
|
||||
"eslint-plugin-react": "7.32.2",
|
||||
"eslint-plugin-react-hooks": "4.6.0",
|
||||
"happy-dom": "9.2.1",
|
||||
"happy-dom": "9.5.0",
|
||||
"husky": "8.0.3",
|
||||
"prettier": "2.8.7",
|
||||
"react-test-renderer": "18.2.0",
|
||||
|
@ -106,7 +106,7 @@
|
|||
"vite-plugin-eslint": "1.8.1",
|
||||
"vite-plugin-istanbul": "4.0.1",
|
||||
"vite-plugin-svgr": "2.4.0",
|
||||
"vite-tsconfig-paths": "4.1.0",
|
||||
"vite-tsconfig-paths": "4.2.0",
|
||||
"vitest": "0.30.1",
|
||||
"vitest-preview": "0.0.1"
|
||||
}
|
||||
|
|
|
@ -6,10 +6,10 @@ dependencies:
|
|||
version: 11.10.7
|
||||
'@emotion/react':
|
||||
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':
|
||||
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':
|
||||
specifier: 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)
|
||||
'@mui/icons-material':
|
||||
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':
|
||||
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':
|
||||
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':
|
||||
specifier: 7.0.0
|
||||
version: 7.0.0
|
||||
|
@ -79,11 +82,11 @@ dependencies:
|
|||
|
||||
devDependencies:
|
||||
'@commitlint/cli':
|
||||
specifier: 17.5.1
|
||||
version: 17.5.1
|
||||
specifier: 17.6.0
|
||||
version: 17.6.0
|
||||
'@commitlint/config-conventional':
|
||||
specifier: 17.4.4
|
||||
version: 17.4.4
|
||||
specifier: 17.6.0
|
||||
version: 17.6.0
|
||||
'@limegrass/eslint-plugin-import-alias':
|
||||
specifier: 1.0.6
|
||||
version: 1.0.6(eslint@8.38.0)
|
||||
|
@ -97,8 +100,8 @@ devDependencies:
|
|||
specifier: 18.15.11
|
||||
version: 18.15.11
|
||||
'@types/react':
|
||||
specifier: 18.0.34
|
||||
version: 18.0.34
|
||||
specifier: 18.0.35
|
||||
version: 18.0.35
|
||||
'@types/react-dom':
|
||||
specifier: 18.0.11
|
||||
version: 18.0.11
|
||||
|
@ -154,8 +157,8 @@ devDependencies:
|
|||
specifier: 4.6.0
|
||||
version: 4.6.0(eslint@8.38.0)
|
||||
happy-dom:
|
||||
specifier: 9.2.1
|
||||
version: 9.2.1
|
||||
specifier: 9.5.0
|
||||
version: 9.5.0
|
||||
husky:
|
||||
specifier: 8.0.3
|
||||
version: 8.0.3
|
||||
|
@ -181,11 +184,11 @@ devDependencies:
|
|||
specifier: 2.4.0
|
||||
version: 2.4.0(vite@4.2.1)
|
||||
vite-tsconfig-paths:
|
||||
specifier: 4.1.0
|
||||
version: 4.1.0(typescript@5.0.4)(vite@4.2.1)
|
||||
specifier: 4.2.0
|
||||
version: 4.2.0(typescript@5.0.4)(vite@4.2.1)
|
||||
vitest:
|
||||
specifier: 0.30.1
|
||||
version: 0.30.1(happy-dom@9.2.1)
|
||||
version: 0.30.1(happy-dom@9.5.0)
|
||||
vitest-preview:
|
||||
specifier: 0.0.1
|
||||
version: 0.0.1
|
||||
|
@ -1551,12 +1554,12 @@ packages:
|
|||
'@babel/helper-validator-identifier': 7.19.1
|
||||
to-fast-properties: 2.0.0
|
||||
|
||||
/@commitlint/cli@17.5.1:
|
||||
resolution: {integrity: sha512-pRRgGSzdHQHehxZbGA3qF6wVPyl+EEQgTe/t321rtMLFbuJ7nRj2waS17s/v5oEbyZtiY5S8PGB6XtEIm0I+Sg==}
|
||||
/@commitlint/cli@17.6.0:
|
||||
resolution: {integrity: sha512-JaZeZ1p6kfkSiZlDoQjK09AuiI9zYQMiIUJzTOM8qNRHFOXOPmiTM56nI67yzeUSNTFu6M/DRqjmdjtA5q3hEg==}
|
||||
engines: {node: '>=v14'}
|
||||
dependencies:
|
||||
'@commitlint/format': 17.4.4
|
||||
'@commitlint/lint': 17.4.4
|
||||
'@commitlint/lint': 17.6.0
|
||||
'@commitlint/load': 17.5.0
|
||||
'@commitlint/read': 17.5.1
|
||||
'@commitlint/types': 17.4.4
|
||||
|
@ -1570,8 +1573,8 @@ packages:
|
|||
- '@swc/wasm'
|
||||
dev: true
|
||||
|
||||
/@commitlint/config-conventional@17.4.4:
|
||||
resolution: {integrity: sha512-u6ztvxqzi6NuhrcEDR7a+z0yrh11elY66nRrQIpqsqW6sZmpxYkDLtpRH8jRML+mmxYQ8s4qqF06Q/IQx5aJeQ==}
|
||||
/@commitlint/config-conventional@17.6.0:
|
||||
resolution: {integrity: sha512-2Y9M7MN942bTK5h70fJGknhXA02+OtWCkKeIzTSwsdwz1V7y6bxYv24x052E9XHKtZHJfvM3iLuTOsjRvLqWtA==}
|
||||
engines: {node: '>=v14'}
|
||||
dependencies:
|
||||
conventional-changelog-conventionalcommits: 5.0.0
|
||||
|
@ -1618,13 +1621,13 @@ packages:
|
|||
semver: 7.3.8
|
||||
dev: true
|
||||
|
||||
/@commitlint/lint@17.4.4:
|
||||
resolution: {integrity: sha512-qgkCRRFjyhbMDWsti/5jRYVJkgYZj4r+ZmweZObnbYqPUl5UKLWMf9a/ZZisOI4JfiPmRktYRZ2JmqlSvg+ccw==}
|
||||
/@commitlint/lint@17.6.0:
|
||||
resolution: {integrity: sha512-6cEXxpxZd7fbtYMxeosOum/Nnwu3VdSuZcrFSqP9lWNsrHRv4ijVsnLeomvo6WHPchGOeEWAazAI7Q6Ap22fJw==}
|
||||
engines: {node: '>=v14'}
|
||||
dependencies:
|
||||
'@commitlint/is-ignored': 17.4.4
|
||||
'@commitlint/parse': 17.4.4
|
||||
'@commitlint/rules': 17.4.4
|
||||
'@commitlint/rules': 17.6.0
|
||||
'@commitlint/types': 17.4.4
|
||||
dev: true
|
||||
|
||||
|
@ -1688,8 +1691,8 @@ packages:
|
|||
resolve-global: 1.0.0
|
||||
dev: true
|
||||
|
||||
/@commitlint/rules@17.4.4:
|
||||
resolution: {integrity: sha512-0tgvXnHi/mVcyR8Y8mjTFZIa/FEQXA4uEutXS/imH2v1UNkYDSEMsK/68wiXRpfW1euSgEdwRkvE1z23+yhNrQ==}
|
||||
/@commitlint/rules@17.6.0:
|
||||
resolution: {integrity: sha512-Ka7AsRFvkKMYYE7itgo7hddRGCiV+0BgbTIAq4PWmnkHAECxYpdqMVzW5jaATmXZfwfRRTB57e7KZWj6EPmK1A==}
|
||||
engines: {node: '>=v14'}
|
||||
dependencies:
|
||||
'@commitlint/ensure': 17.4.4
|
||||
|
@ -1765,7 +1768,7 @@ packages:
|
|||
resolution: {integrity: sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==}
|
||||
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==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
|
@ -1781,7 +1784,7 @@ packages:
|
|||
'@emotion/use-insertion-effect-with-fallbacks': 1.0.0(react@18.2.0)
|
||||
'@emotion/utils': 1.2.0
|
||||
'@emotion/weak-memoize': 0.3.0
|
||||
'@types/react': 18.0.34
|
||||
'@types/react': 18.0.35
|
||||
hoist-non-react-statics: 3.3.2
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
@ -1800,7 +1803,7 @@ packages:
|
|||
resolution: {integrity: sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA==}
|
||||
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==}
|
||||
peerDependencies:
|
||||
'@emotion/react': ^11.0.0-rc.0
|
||||
|
@ -1813,11 +1816,11 @@ packages:
|
|||
'@babel/runtime': 7.21.0
|
||||
'@emotion/babel-plugin': 11.10.6
|
||||
'@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/use-insertion-effect-with-fallbacks': 1.0.0(react@18.2.0)
|
||||
'@emotion/utils': 1.2.0
|
||||
'@types/react': 18.0.34
|
||||
'@types/react': 18.0.35
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
|
@ -2256,7 +2259,7 @@ packages:
|
|||
tsconfig-paths: 3.14.2
|
||||
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==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
|
@ -2269,10 +2272,10 @@ packages:
|
|||
dependencies:
|
||||
'@babel/runtime': 7.21.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)
|
||||
'@popperjs/core': 2.11.7
|
||||
'@types/react': 18.0.34
|
||||
'@types/react': 18.0.35
|
||||
clsx: 1.2.1
|
||||
prop-types: 15.8.1
|
||||
react: 18.2.0
|
||||
|
@ -2284,7 +2287,7 @@ packages:
|
|||
resolution: {integrity: sha512-1hoFIdlLI0sG+mkJgm70FjgIVpfLcE1vxPtNolg1tLFXrvbXGUYp9NHy3d6c41nDkg2OajuVS+Mn6A8UirFuMw==}
|
||||
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==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
|
@ -2296,12 +2299,12 @@ packages:
|
|||
optional: true
|
||||
dependencies:
|
||||
'@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)
|
||||
'@types/react': 18.0.34
|
||||
'@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.35
|
||||
react: 18.2.0
|
||||
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==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
|
@ -2319,14 +2322,14 @@ packages:
|
|||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.21.0
|
||||
'@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.34)(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)
|
||||
'@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.35)(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/system': 5.12.0(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(@types/react@18.0.34)(react@18.2.0)
|
||||
'@mui/types': 7.2.4(@types/react@18.0.34)
|
||||
'@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.35)
|
||||
'@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
|
||||
clsx: 1.2.1
|
||||
csstype: 3.1.2
|
||||
|
@ -2337,7 +2340,7 @@ packages:
|
|||
react-transition-group: 4.4.5(react-dom@18.2.0)(react@18.2.0)
|
||||
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==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
|
@ -2349,7 +2352,7 @@ packages:
|
|||
dependencies:
|
||||
'@babel/runtime': 7.21.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
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
@ -2369,14 +2372,14 @@ packages:
|
|||
dependencies:
|
||||
'@babel/runtime': 7.21.0
|
||||
'@emotion/cache': 11.10.7
|
||||
'@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.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.35)(react@18.2.0)
|
||||
csstype: 3.1.2
|
||||
prop-types: 15.8.1
|
||||
react: 18.2.0
|
||||
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==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
|
@ -2388,10 +2391,10 @@ packages:
|
|||
dependencies:
|
||||
'@babel/runtime': 7.21.0
|
||||
'@emotion/hash': 0.9.0
|
||||
'@mui/private-theming': 5.12.0(@types/react@18.0.34)(react@18.2.0)
|
||||
'@mui/types': 7.2.4(@types/react@18.0.34)
|
||||
'@mui/private-theming': 5.12.0(@types/react@18.0.35)(react@18.2.0)
|
||||
'@mui/types': 7.2.4(@types/react@18.0.35)
|
||||
'@mui/utils': 5.12.0(react@18.2.0)
|
||||
'@types/react': 18.0.34
|
||||
'@types/react': 18.0.35
|
||||
clsx: 1.2.1
|
||||
csstype: 3.1.2
|
||||
hoist-non-react-statics: 3.3.2
|
||||
|
@ -2407,7 +2410,7 @@ packages:
|
|||
react: 18.2.0
|
||||
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==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
|
@ -2424,20 +2427,20 @@ packages:
|
|||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.21.0
|
||||
'@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.34)(react@18.2.0)
|
||||
'@mui/private-theming': 5.12.0(@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.35)(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/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)
|
||||
'@types/react': 18.0.34
|
||||
'@types/react': 18.0.35
|
||||
clsx: 1.2.1
|
||||
csstype: 3.1.2
|
||||
prop-types: 15.8.1
|
||||
react: 18.2.0
|
||||
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==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
|
@ -2445,7 +2448,7 @@ packages:
|
|||
'@types/react':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@types/react': 18.0.34
|
||||
'@types/react': 18.0.35
|
||||
dev: false
|
||||
|
||||
/@mui/utils@5.12.0(react@18.2.0):
|
||||
|
@ -2853,23 +2856,23 @@ packages:
|
|||
/@types/react-dom@18.0.11:
|
||||
resolution: {integrity: sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw==}
|
||||
dependencies:
|
||||
'@types/react': 18.0.34
|
||||
'@types/react': 18.0.35
|
||||
dev: true
|
||||
|
||||
/@types/react-is@17.0.3:
|
||||
resolution: {integrity: sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==}
|
||||
dependencies:
|
||||
'@types/react': 18.0.34
|
||||
'@types/react': 18.0.35
|
||||
dev: false
|
||||
|
||||
/@types/react-transition-group@4.4.5:
|
||||
resolution: {integrity: sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==}
|
||||
dependencies:
|
||||
'@types/react': 18.0.34
|
||||
'@types/react': 18.0.35
|
||||
dev: false
|
||||
|
||||
/@types/react@18.0.34:
|
||||
resolution: {integrity: sha512-NO1UO8941541CJl1BeOXi8a9dNKFK09Gnru5ZJqkm4Q3/WoQJtHvmwt0VX0SB9YCEwe7TfSSxDuaNmx6H2BAIQ==}
|
||||
/@types/react@18.0.35:
|
||||
resolution: {integrity: sha512-6Laome31HpetaIUGFWl1VQ3mdSImwxtFZ39rh059a1MNnKGqBpC88J6NJ8n/Is3Qx7CefDGLgf/KhN/sYCf7ag==}
|
||||
dependencies:
|
||||
'@types/prop-types': 15.7.5
|
||||
'@types/scheduler': 0.16.3
|
||||
|
@ -3089,7 +3092,7 @@ packages:
|
|||
istanbul-lib-source-maps: 4.0.1
|
||||
istanbul-reports: 3.1.5
|
||||
test-exclude: 6.0.0
|
||||
vitest: 0.30.1(happy-dom@9.2.1)
|
||||
vitest: 0.30.1(happy-dom@9.5.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
@ -5092,8 +5095,8 @@ packages:
|
|||
resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==}
|
||||
dev: true
|
||||
|
||||
/happy-dom@9.2.1:
|
||||
resolution: {integrity: sha512-6L1p1XalEVw/yzb+HVpCpfR0R3HDTAbTaTIDquJCzFPVVUC7q8+jHENH1W+4P2HsI9klCsPyeKm2/Ttfz/B5ag==}
|
||||
/happy-dom@9.5.0:
|
||||
resolution: {integrity: sha512-pNdHSZRWIckzg8aDQRbBgaivr2Ef+uSTpCCRGnxIETyewHA6841T8EPE+cmfhPjGi5jQN6c+oloXGGYB5SrpcA==}
|
||||
dependencies:
|
||||
css.escape: 1.5.1
|
||||
he: 1.2.0
|
||||
|
@ -7379,8 +7382,8 @@ packages:
|
|||
- supports-color
|
||||
dev: true
|
||||
|
||||
/vite-tsconfig-paths@4.1.0(typescript@5.0.4)(vite@4.2.1):
|
||||
resolution: {integrity: sha512-Ps275He1fF6Wpm/3tvyokIfXd3lcmk4KsdCG4yduSTRVt+htaxIoEor88M1h3soOeXg5fn77ZiupVRwGeB/XNQ==}
|
||||
/vite-tsconfig-paths@4.2.0(typescript@5.0.4)(vite@4.2.1):
|
||||
resolution: {integrity: sha512-jGpus0eUy5qbbMVGiTxCL1iB9ZGN6Bd37VGLJU39kTDD6ZfULTTb1bcc5IeTWqWJKiWV5YihCaibeASPiGi8kw==}
|
||||
peerDependencies:
|
||||
vite: '*'
|
||||
peerDependenciesMeta:
|
||||
|
@ -7479,7 +7482,7 @@ packages:
|
|||
- terser
|
||||
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==}
|
||||
engines: {node: '>=v14.18.0'}
|
||||
peerDependencies:
|
||||
|
@ -7523,7 +7526,7 @@ packages:
|
|||
chai: 4.3.7
|
||||
concordance: 5.0.4
|
||||
debug: 4.3.4
|
||||
happy-dom: 9.2.1
|
||||
happy-dom: 9.5.0
|
||||
local-pkg: 0.4.3
|
||||
magic-string: 0.30.0
|
||||
pathe: 1.1.0
|
||||
|
|
Loading…
Reference in New Issue