Merge remote-tracking branch 'origin/master' into feat-oidc-auth-mode
Signed-off-by: James Elliott <james-d-elliott@users.noreply.github.com> # Conflicts: # internal/configuration/validator/const.gofeat-otp-verification
commit
928df8a698
|
@ -15,7 +15,7 @@ RUN yarn global add pnpm && \
|
|||
# =======================================
|
||||
# ===== Build image for the backend =====
|
||||
# =======================================
|
||||
FROM golang:1.20.2-alpine AS builder-backend
|
||||
FROM golang:1.20.3-alpine AS builder-backend
|
||||
|
||||
WORKDIR /go/src/app
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ RUN yarn install --frozen-lockfile && yarn build
|
|||
# =======================================
|
||||
# ===== Build image for the backend =====
|
||||
# =======================================
|
||||
FROM golang:1.20.2-alpine AS builder-backend
|
||||
FROM golang:1.20.3-alpine AS builder-backend
|
||||
|
||||
WORKDIR /go/src/app
|
||||
|
||||
|
|
283
api/openapi.yml
283
api/openapi.yml
|
@ -111,6 +111,8 @@ paths:
|
|||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/handlers.StateResponse'
|
||||
{{- $app := "" }}{{ if .Domain }}{{ $app = printf "https://%s/" .Domain }}{{ else if .BaseURL }}{{ $app = .BaseURL }}{{ else }}{{ $app = "https://app.example.com" }}{{ end }}
|
||||
{{- $redir := printf "%s?rd=%s&rm=GET" (.BaseURL | default "https://auth.example.com/") (urlquery $app) }}
|
||||
{{- range $name, $config := .EndpointsAuthz }}
|
||||
{{- $uri := printf "/api/authz/%s" $name }}
|
||||
{{- if (eq $name "legacy") }}{{ $uri = "/api/verify" }}{{ end }}
|
||||
|
@ -141,7 +143,7 @@ paths:
|
|||
required: false
|
||||
style: simple
|
||||
explode: true
|
||||
example: "https"
|
||||
example: 'https'
|
||||
schema:
|
||||
type: string
|
||||
- name: X-Forwarded-Host
|
||||
|
@ -150,7 +152,7 @@ paths:
|
|||
required: false
|
||||
style: simple
|
||||
explode: true
|
||||
example: "example.com"
|
||||
example: '{{ $.Domain | default "example.com" }}'
|
||||
schema:
|
||||
type: string
|
||||
- name: X-Forwarded-Uri
|
||||
|
@ -159,7 +161,7 @@ paths:
|
|||
required: false
|
||||
style: simple
|
||||
explode: true
|
||||
example: "/path/example"
|
||||
example: '/path/example'
|
||||
schema:
|
||||
type: string
|
||||
- $ref: '#/components/parameters/forwardedForParam'
|
||||
|
@ -188,8 +190,37 @@ paths:
|
|||
schema:
|
||||
type: string
|
||||
example: admin,devs
|
||||
set-cookie:
|
||||
description: Sets a new cookie value
|
||||
schema:
|
||||
type: string
|
||||
"302":
|
||||
description: Found
|
||||
headers:
|
||||
location:
|
||||
description: Redirect Location for user authorization
|
||||
example: '{{ $redir }}'
|
||||
set-cookie:
|
||||
description: Sets a new cookie value
|
||||
schema:
|
||||
type: string
|
||||
"303":
|
||||
description: See Other
|
||||
headers:
|
||||
location:
|
||||
description: Redirect Location for user authorization
|
||||
example: '{{ $redir }}'
|
||||
set-cookie:
|
||||
description: Sets a new cookie value
|
||||
schema:
|
||||
type: string
|
||||
"401":
|
||||
description: Unauthorized
|
||||
headers:
|
||||
set-cookie:
|
||||
description: Sets a new cookie value
|
||||
schema:
|
||||
type: string
|
||||
security:
|
||||
- authelia_auth: []
|
||||
{{- end }}
|
||||
|
@ -232,6 +263,32 @@ paths:
|
|||
schema:
|
||||
type: string
|
||||
example: admin,devs
|
||||
set-cookie:
|
||||
description: Sets a new cookie value
|
||||
schema:
|
||||
type: string
|
||||
"302":
|
||||
description: Found
|
||||
headers:
|
||||
location:
|
||||
description: Redirect Location for user authorization
|
||||
example: '{{ $redir }}'
|
||||
set-cookie:
|
||||
description: Sets a new cookie value
|
||||
schema:
|
||||
type: string
|
||||
"303":
|
||||
description: See Other
|
||||
headers:
|
||||
location:
|
||||
description: Redirect Location for user authorization
|
||||
example: '{{ $redir }}'
|
||||
set-cookie:
|
||||
description: Sets a new cookie value
|
||||
schema:
|
||||
type: string
|
||||
"400":
|
||||
description: Bad Request
|
||||
"401":
|
||||
description: Unauthorized
|
||||
security:
|
||||
|
@ -275,6 +332,32 @@ paths:
|
|||
schema:
|
||||
type: string
|
||||
example: admin,devs
|
||||
set-cookie:
|
||||
description: Sets a new cookie value
|
||||
schema:
|
||||
type: string
|
||||
"302":
|
||||
description: Found
|
||||
headers:
|
||||
location:
|
||||
description: Redirect Location for user authorization
|
||||
example: '{{ $redir }}'
|
||||
set-cookie:
|
||||
description: Sets a new cookie value
|
||||
schema:
|
||||
type: string
|
||||
"303":
|
||||
description: See Other
|
||||
headers:
|
||||
location:
|
||||
description: Redirect Location for user authorization
|
||||
example: '{{ $redir }}'
|
||||
set-cookie:
|
||||
description: Sets a new cookie value
|
||||
schema:
|
||||
type: string
|
||||
"400":
|
||||
description: Bad Request
|
||||
"401":
|
||||
description: Unauthorized
|
||||
security:
|
||||
|
@ -316,8 +399,22 @@ paths:
|
|||
schema:
|
||||
type: string
|
||||
example: admin,devs
|
||||
set-cookie:
|
||||
description: Sets a new cookie value
|
||||
schema:
|
||||
type: string
|
||||
"400":
|
||||
description: Bad Request
|
||||
"401":
|
||||
description: Unauthorized
|
||||
headers:
|
||||
location:
|
||||
description: Redirect Location for user authorization
|
||||
example: '{{ $redir }}'
|
||||
set-cookie:
|
||||
description: Sets a new cookie value
|
||||
schema:
|
||||
type: string
|
||||
security:
|
||||
- authelia_auth: []
|
||||
{{- end }}
|
||||
|
@ -906,14 +1003,14 @@ paths:
|
|||
type: string
|
||||
format: uuid
|
||||
pattern: '^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$'
|
||||
example: "713ef767-81bc-4a27-9b83-5fe2e101b2b4"
|
||||
example: '713ef767-81bc-4a27-9b83-5fe2e101b2b4'
|
||||
- in: query
|
||||
name: scope
|
||||
description: The requested scope.
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
example: "openid profile groups"
|
||||
example: 'openid profile groups'
|
||||
- in: query
|
||||
name: response_type
|
||||
description: The OAuth 2.0 response type.
|
||||
|
@ -926,7 +1023,7 @@ paths:
|
|||
required: true
|
||||
schema:
|
||||
type: string
|
||||
example: "app"
|
||||
example: 'app'
|
||||
- in: query
|
||||
name: redirect_uri
|
||||
description: >
|
||||
|
@ -940,7 +1037,7 @@ paths:
|
|||
required: true
|
||||
schema:
|
||||
type: string
|
||||
example: "https://app.example.com"
|
||||
example: 'https://app.{{ .Domain | default "example.com" }}'
|
||||
- in: query
|
||||
name: state
|
||||
description: >
|
||||
|
@ -950,7 +1047,7 @@ paths:
|
|||
required: false
|
||||
schema:
|
||||
type: string
|
||||
example: "oV84Vsy7wyCgRk2h4aZBmXZq4q3g2f"
|
||||
example: 'oV84Vsy7wyCgRk2h4aZBmXZq4q3g2f'
|
||||
- in: query
|
||||
name: response_mode
|
||||
description: >
|
||||
|
@ -970,7 +1067,7 @@ paths:
|
|||
required: false
|
||||
schema:
|
||||
type: string
|
||||
example: "TRMLqchoKGQNcooXvBvUy9PtmLdJGf"
|
||||
example: 'TRMLqchoKGQNcooXvBvUy9PtmLdJGf'
|
||||
- in: query
|
||||
name: display
|
||||
description: >
|
||||
|
@ -1010,7 +1107,7 @@ paths:
|
|||
required: false
|
||||
schema:
|
||||
type: string
|
||||
example: "en-US"
|
||||
example: 'en-US'
|
||||
- in: query
|
||||
name: claims_locales
|
||||
description: >
|
||||
|
@ -1020,7 +1117,7 @@ paths:
|
|||
required: false
|
||||
schema:
|
||||
type: string
|
||||
example: "en-US"
|
||||
example: 'en-US'
|
||||
- in: query
|
||||
name: id_token_hint
|
||||
required: false
|
||||
|
@ -1258,7 +1355,7 @@ paths:
|
|||
description: The OAuth 2.0 Access Token issued by this OpenID Connect 1.0 Provider.
|
||||
schema:
|
||||
type: string
|
||||
example: "authelia_at_cr4i4EtTn2F4k6mX4XzxbsBewkxCGn"
|
||||
example: 'authelia_at_cr4i4EtTn2F4k6mX4XzxbsBewkxCGn'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
|
@ -1287,7 +1384,7 @@ paths:
|
|||
description: The OAuth 2.0 Access Token issued by this OpenID Connect 1.0 Provider.
|
||||
schema:
|
||||
type: string
|
||||
example: "authelia_at_cr4i4EtTn2F4k6mX4XzxbsBewkxCGn"
|
||||
example: 'authelia_at_cr4i4EtTn2F4k6mX4XzxbsBewkxCGn'
|
||||
requestBody:
|
||||
content:
|
||||
application/x-www-form-urlencoded:
|
||||
|
@ -1297,7 +1394,7 @@ paths:
|
|||
access_token:
|
||||
description: The OAuth 2.0 Access Token issued by this OpenID Connect 1.0 Provider.
|
||||
type: string
|
||||
example: "authelia_at_cr4i4EtTn2F4k6mX4XzxbsBewkxCGn"
|
||||
example: 'authelia_at_cr4i4EtTn2F4k6mX4XzxbsBewkxCGn'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
|
@ -1429,7 +1526,7 @@ components:
|
|||
required: true
|
||||
style: simple
|
||||
explode: true
|
||||
example: "https"
|
||||
example: 'https'
|
||||
schema:
|
||||
type: string
|
||||
forwardedHostParam:
|
||||
|
@ -1439,7 +1536,7 @@ components:
|
|||
required: true
|
||||
style: simple
|
||||
explode: true
|
||||
example: "example.com"
|
||||
example: '{{ .Domain | default "example.com" }}'
|
||||
schema:
|
||||
type: string
|
||||
forwardedURIParam:
|
||||
|
@ -1449,7 +1546,7 @@ components:
|
|||
required: true
|
||||
style: simple
|
||||
explode: true
|
||||
example: "/path/example"
|
||||
example: '/path/example'
|
||||
schema:
|
||||
type: string
|
||||
forwardedForParam:
|
||||
|
@ -1459,7 +1556,7 @@ components:
|
|||
required: false
|
||||
style: simple
|
||||
explode: true
|
||||
example: "192.168.0.55,192.168.0.20"
|
||||
example: '192.168.0.55,192.168.0.20'
|
||||
schema:
|
||||
type: string
|
||||
autheliaURLParam:
|
||||
|
@ -1469,7 +1566,7 @@ components:
|
|||
required: false
|
||||
style: simple
|
||||
explode: true
|
||||
example: "https://auth.example.com"
|
||||
example: '{{ .BaseURL | default "https://auth.example.com" }}'
|
||||
schema:
|
||||
type: string
|
||||
authParam:
|
||||
|
@ -1493,7 +1590,7 @@ components:
|
|||
properties:
|
||||
uri:
|
||||
type: string
|
||||
example: https://secure.example.com
|
||||
example: 'https://secure.{{ .Domain | default "example.com" }}'
|
||||
handlers.checkURIWithinDomainResponseBody:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -1610,7 +1707,7 @@ components:
|
|||
example: password
|
||||
targetURL:
|
||||
type: string
|
||||
example: https://home.example.com
|
||||
example: 'https://home.{{ .Domain | default "example.com" }}'
|
||||
workflow:
|
||||
type: string
|
||||
example: openid_connect
|
||||
|
@ -1618,7 +1715,7 @@ components:
|
|||
type: string
|
||||
format: uuid
|
||||
pattern: '^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$'
|
||||
example: "3ebcfbc5-b0fd-4ee0-9d3c-080ae1e7298c"
|
||||
example: '3ebcfbc5-b0fd-4ee0-9d3c-080ae1e7298c'
|
||||
requestMethod:
|
||||
type: string
|
||||
example: GET
|
||||
|
@ -1630,7 +1727,7 @@ components:
|
|||
properties:
|
||||
targetURL:
|
||||
type: string
|
||||
example: https://redirect.example.com
|
||||
example: 'https://redirect.{{ .Domain | default "example.com" }}'
|
||||
handlers.logoutResponseBody:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -1654,7 +1751,7 @@ components:
|
|||
properties:
|
||||
redirect:
|
||||
type: string
|
||||
example: https://home.example.com
|
||||
example: 'https://home.{{ .Domain | default "example.com" }}'
|
||||
{{- if .PasswordReset }}
|
||||
handlers.PasswordResetStep1RequestBody:
|
||||
required:
|
||||
|
@ -1679,7 +1776,7 @@ components:
|
|||
properties:
|
||||
targetURL:
|
||||
type: string
|
||||
example: https://secure.example.com
|
||||
example: 'https://secure.{{ .Domain | default "example.com" }}'
|
||||
passcode:
|
||||
type: string
|
||||
workflow:
|
||||
|
@ -1689,7 +1786,7 @@ components:
|
|||
type: string
|
||||
format: uuid
|
||||
pattern: '^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$'
|
||||
example: "3ebcfbc5-b0fd-4ee0-9d3c-080ae1e7298c"
|
||||
example: '3ebcfbc5-b0fd-4ee0-9d3c-080ae1e7298c'
|
||||
{{- end }}
|
||||
handlers.StateResponse:
|
||||
type: object
|
||||
|
@ -1708,7 +1805,7 @@ components:
|
|||
example: 1
|
||||
default_redirection_url:
|
||||
type: string
|
||||
example: https://home.example.com
|
||||
example: 'https://home.{{ .Domain | default "example.com" }}'
|
||||
middlewares.ErrorResponse:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -1799,10 +1896,10 @@ components:
|
|||
properties:
|
||||
token:
|
||||
type: string
|
||||
example: "123456"
|
||||
example: '123456'
|
||||
targetURL:
|
||||
type: string
|
||||
example: https://secure.example.com
|
||||
example: 'https://secure.{{ .Domain | default "example.com" }}'
|
||||
workflow:
|
||||
type: string
|
||||
example: openid_connect
|
||||
|
@ -1810,7 +1907,7 @@ components:
|
|||
type: string
|
||||
format: uuid
|
||||
pattern: '^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$'
|
||||
example: "3ebcfbc5-b0fd-4ee0-9d3c-080ae1e7298c"
|
||||
example: '3ebcfbc5-b0fd-4ee0-9d3c-080ae1e7298c'
|
||||
handlers.TOTPKeyResponse:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -1825,7 +1922,7 @@ components:
|
|||
example: 5ZH7Y5CTFWOXN7EOLGBMMXADRNQFHVUDZSYKCN5HMFAIRSLAWY3Q
|
||||
otpauth_url:
|
||||
type: string
|
||||
example: otpauth://totp/auth.example.com:john?algorithm=SHA1&digits=6&issuer=auth.example.com&period=30&secret=5ZH7Y5CTFWOXN7EOLGBMMXADRNQFHVUDZSYKCN5HMFAIRSLAWY3Q
|
||||
example: 'otpauth://totp/{{ .Domain | default "example.com" }}:john?algorithm=SHA1&digits=6&issuer=auth.{{ .Domain | default "example.com" }}&period=30&secret=5ZH7Y5CTFWOXN7EOLGBMMXADRNQFHVUDZSYKCN5HMFAIRSLAWY3Q'
|
||||
{{- end }}
|
||||
{{- if .Webauthn }}
|
||||
webauthn.PublicKeyCredential:
|
||||
|
@ -1903,7 +2000,7 @@ components:
|
|||
type: string
|
||||
format: uuid
|
||||
pattern: '^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$'
|
||||
example: "3ebcfbc5-b0fd-4ee0-9d3c-080ae1e7298c"
|
||||
example: '3ebcfbc5-b0fd-4ee0-9d3c-080ae1e7298c'
|
||||
webauthn.DeviceUpdateRequest:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -1960,7 +2057,7 @@ components:
|
|||
properties:
|
||||
appidExclude:
|
||||
type: string
|
||||
example: https://auth.example.com
|
||||
example: '{{ .BaseURL }}'
|
||||
webauthn.PublicKeyCredentialRequestOptions:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -1984,7 +2081,7 @@ components:
|
|||
example: 60000
|
||||
rpId:
|
||||
type: string
|
||||
example: auth.example.com
|
||||
example: 'auth.{{ .Domain | default "example.com" }}'
|
||||
allowCredentials:
|
||||
type: array
|
||||
items:
|
||||
|
@ -1995,7 +2092,7 @@ components:
|
|||
properties:
|
||||
appid:
|
||||
type: string
|
||||
example: https://auth.example.com
|
||||
example: '{{ .BaseURL }}'
|
||||
webauthn.Transports:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -2150,11 +2247,11 @@ components:
|
|||
client_id:
|
||||
type: string
|
||||
description: The identifier of the client for the user to provide consent for.
|
||||
example: "app"
|
||||
example: 'app'
|
||||
client_description:
|
||||
description: The descriptive name of the client for the user to provide consent for.
|
||||
type: string
|
||||
example: "App Platform"
|
||||
example: 'App Platform'
|
||||
scopes:
|
||||
description: The list of the requested scopes for the user to provide consent for.
|
||||
type: array
|
||||
|
@ -2189,11 +2286,11 @@ components:
|
|||
type: string
|
||||
format: uuid
|
||||
pattern: '^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$'
|
||||
example: "713ef767-81bc-4a27-9b83-5fe2e101b2b4"
|
||||
example: '713ef767-81bc-4a27-9b83-5fe2e101b2b4'
|
||||
client_id:
|
||||
description: The identifier of the client for the user to provide consent for.
|
||||
type: string
|
||||
example: "app"
|
||||
example: 'app'
|
||||
consent:
|
||||
description: Indicates if the user consented to the consent request.
|
||||
type: boolean
|
||||
|
@ -2216,7 +2313,7 @@ components:
|
|||
URL of the OP''s OAuth 2.0 Authorization Endpoint [OpenID.Core].
|
||||
See Also: OpenID.Core: https://openid.net/specs/openid-connect-core-1_0.html
|
||||
type: string
|
||||
example: "{{ .BaseURL }}api/oidc/authorization"
|
||||
example: '{{ .BaseURL }}api/oidc/authorization'
|
||||
claims_supported:
|
||||
description: >
|
||||
JSON array containing a list of the Claim Names of the Claims that the OpenID Provider MAY be able to supply
|
||||
|
@ -2268,7 +2365,7 @@ components:
|
|||
URL of the authorization server''s OAuth 2.0 introspection endpoint [RFC7662]. See Also: OAuth 2.0 Token
|
||||
Introspection: https://datatracker.ietf.org/doc/html/rfc7662
|
||||
type: string
|
||||
example: "{{ .BaseURL }}api/oidc/introspection"
|
||||
example: '{{ .BaseURL }}api/oidc/introspection'
|
||||
introspection_endpoint_auth_methods_supported:
|
||||
description: >
|
||||
JSON array containing a list of client authentication methods supported by this introspection endpoint. The
|
||||
|
@ -2301,7 +2398,7 @@ components:
|
|||
If Issuer discovery is supported (see Section 2), this value MUST be identical to the issuer value returned
|
||||
by WebFinger. This also MUST be identical to the iss Claim value in ID Tokens issued from this Issuer.
|
||||
type: string
|
||||
example: "{{ .BaseURL }}"
|
||||
example: '{{ .BaseURL }}'
|
||||
jwks_uri:
|
||||
description: >
|
||||
URL of the OP's JSON Web Key Set [JWK] document. This contains the signing key(s) the RP uses to validate
|
||||
|
@ -2312,7 +2409,7 @@ components:
|
|||
RECOMMENDED, as it is less secure. The JWK x5c parameter MAY be used to provide X.509 representations of
|
||||
keys provided. When used, the bare key values MUST still be present and MUST match those in the certificate.
|
||||
type: string
|
||||
example: "{{ .BaseURL }}jwks.json"
|
||||
example: '{{ .BaseURL }}jwks.json'
|
||||
op_policy_uri:
|
||||
description:
|
||||
URL that the OpenID Provider provides to the person registering the Client to read about the OP's
|
||||
|
@ -2330,13 +2427,13 @@ components:
|
|||
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.
|
||||
type: string
|
||||
example: "{{ .BaseURL }}api/oidc/par"
|
||||
example: '{{ .BaseURL }}api/oidc/par'
|
||||
registration_endpoint:
|
||||
description: >
|
||||
URL of the authorization server''s OAuth 2.0 Dynamic Client Registration endpoint [RFC7591]. See Also:
|
||||
OAuth 2.0 Dynamic Client Registration Protocol: https://datatracker.ietf.org/doc/html/rfc7591
|
||||
type: string
|
||||
example: "{{ .BaseURL }}api/oidc/registration"
|
||||
example: '{{ .BaseURL }}api/oidc/registration'
|
||||
require_pushed_authorization_requests:
|
||||
description: >
|
||||
Boolean parameter indicating whether the authorization server accepts authorization request data only via
|
||||
|
@ -2365,7 +2462,7 @@ components:
|
|||
URL of the authorization server''s OAuth 2.0 revocation endpoint [RFC7009].
|
||||
See Also: OAuth 2.0 Token Revocation: https://datatracker.ietf.org/doc/html/rfc7009
|
||||
type: string
|
||||
example: "{{ .BaseURL }}api/oidc/revocation"
|
||||
example: '{{ .BaseURL }}api/oidc/revocation'
|
||||
revocation_endpoint_auth_methods_supported:
|
||||
description: >
|
||||
JSON array containing a list of client authentication methods supported by this revocation endpoint. The
|
||||
|
@ -2411,7 +2508,7 @@ components:
|
|||
the OpenID Provider. In particular, if the OpenID Provider does not support Dynamic Client Registration,
|
||||
then information on how to register Clients needs to be provided in this documentation.
|
||||
type: string
|
||||
example: "https://authelia.com"
|
||||
example: 'https://authelia.com'
|
||||
subject_types_supported:
|
||||
description: >
|
||||
JSON array containing a list of the Subject Identifier types that this OP supports.
|
||||
|
@ -2425,7 +2522,7 @@ components:
|
|||
URL of the OP''s OAuth 2.0 Token Endpoint [OpenID.Core]. This is REQUIRED unless only the Implicit Flow is
|
||||
used. See Also: OpenID.Core: https://openid.net/specs/openid-connect-core-1_0.html
|
||||
type: string
|
||||
example: "{{ .BaseURL }}api/oidc/token"
|
||||
example: '{{ .BaseURL }}api/oidc/token'
|
||||
token_endpoint_auth_methods_supported:
|
||||
description: >
|
||||
JSON array containing a list of Client Authentication methods supported by this Token Endpoint. The options
|
||||
|
@ -2483,7 +2580,7 @@ components:
|
|||
URL of the OP''s OAuth 2.0 Authorization Endpoint [OpenID.Core].
|
||||
See Also: OpenID.Core: https://openid.net/specs/openid-connect-core-1_0.html
|
||||
type: string
|
||||
example: "{{ .BaseURL }}api/oidc/authorization"
|
||||
example: '{{ .BaseURL }}api/oidc/authorization'
|
||||
backchannel_logout_session_supported:
|
||||
description: >
|
||||
Boolean value specifying whether the OP can pass a sid (session ID) Claim in the Logout Token to identify
|
||||
|
@ -2625,7 +2722,7 @@ components:
|
|||
URL of the authorization server''s OAuth 2.0 introspection endpoint [RFC7662]. See Also: OAuth 2.0
|
||||
Token Introspection: https://datatracker.ietf.org/doc/html/rfc7662'
|
||||
type: string
|
||||
example: "{{ .BaseURL }}api/oidc/introspection"
|
||||
example: '{{ .BaseURL }}api/oidc/introspection'
|
||||
introspection_endpoint_auth_methods_supported:
|
||||
description: >
|
||||
JSON array containing a list of client authentication methods supported by this introspection endpoint. The
|
||||
|
@ -2658,7 +2755,7 @@ components:
|
|||
If Issuer discovery is supported (see Section 2), this value MUST be identical to the issuer value returned
|
||||
by WebFinger. This also MUST be identical to the iss Claim value in ID Tokens issued from this Issuer.
|
||||
type: string
|
||||
example: "{{ .BaseURL }}"
|
||||
example: '{{ .BaseURL }}'
|
||||
jwks_uri:
|
||||
description: >
|
||||
URL of the OP's JSON Web Key Set [JWK] document. This contains the signing key(s) the RP uses to validate
|
||||
|
@ -2669,7 +2766,7 @@ components:
|
|||
RECOMMENDED, as it is less secure. The JWK x5c parameter MAY be used to provide X.509 representations of
|
||||
keys provided. When used, the bare key values MUST still be present and MUST match those in the certificate.
|
||||
type: string
|
||||
example: "{{ .BaseURL }}jwks.json"
|
||||
example: '{{ .BaseURL }}jwks.json'
|
||||
op_policy_uri:
|
||||
description: >
|
||||
URL that the OpenID Provider provides to the person registering the Client to read about the OP's
|
||||
|
@ -2687,13 +2784,13 @@ components:
|
|||
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.
|
||||
type: string
|
||||
example: "{{ .BaseURL }}api/oidc/par"
|
||||
example: '{{ .BaseURL }}api/oidc/par'
|
||||
registration_endpoint:
|
||||
description: >
|
||||
URL of the authorization server''s OAuth 2.0 Dynamic Client Registration endpoint [RFC7591]. See Also:
|
||||
OAuth 2.0 Dynamic Client Registration Protocol: https://datatracker.ietf.org/doc/html/rfc7591
|
||||
type: string
|
||||
example: "{{ .BaseURL }}api/oidc/registration"
|
||||
example: '{{ .BaseURL }}api/oidc/registration'
|
||||
request_object_encryption_alg_values_supported:
|
||||
description: >
|
||||
JSON array containing a list of the JWE encryption algorithms (alg values) supported by the OP for Request
|
||||
|
@ -2764,7 +2861,7 @@ components:
|
|||
URL of the authorization server''s OAuth 2.0 revocation endpoint [RFC7009]. See Also:
|
||||
OAuth 2.0 Token Revocation: https://datatracker.ietf.org/doc/html/rfc7009
|
||||
type: string
|
||||
example: "{{ .BaseURL }}api/oidc/revocation"
|
||||
example: '{{ .BaseURL }}api/oidc/revocation'
|
||||
revocation_endpoint_auth_methods_supported:
|
||||
description: >
|
||||
JSON array containing a list of client authentication methods supported by this revocation endpoint. The
|
||||
|
@ -2811,7 +2908,7 @@ components:
|
|||
the OpenID Provider. In particular, if the OpenID Provider does not support Dynamic Client Registration,
|
||||
then information on how to register Clients needs to be provided in this documentation.
|
||||
type: string
|
||||
example: "https://www.authelia.com"
|
||||
example: 'https://www.authelia.com'
|
||||
subject_types_supported:
|
||||
description: >
|
||||
JSON array containing a list of the Subject Identifier types that this OP supports. Valid types include
|
||||
|
@ -2825,7 +2922,7 @@ components:
|
|||
URL of the OP''s OAuth 2.0 Token Endpoint [OpenID.Core]. This is REQUIRED unless only the Implicit Flow is
|
||||
used. See Also: OpenID.Core: https://openid.net/specs/openid-connect-core-1_0.html
|
||||
type: string
|
||||
example: "{{ .BaseURL }}api/oidc/token"
|
||||
example: '{{ .BaseURL }}api/oidc/token'
|
||||
token_endpoint_auth_methods_supported:
|
||||
description: >
|
||||
JSON array containing a list of Client Authentication methods supported by this Token Endpoint. The options
|
||||
|
@ -2881,7 +2978,7 @@ components:
|
|||
path, and query parameter components.
|
||||
See Also: OpenID.Core: https://openid.net/specs/openid-connect-core-1_0.html
|
||||
type: string
|
||||
example: "{{ .BaseURL }}api/oidc/userinfo"
|
||||
example: '{{ .BaseURL }}api/oidc/userinfo'
|
||||
userinfo_signing_alg_values_supported:
|
||||
description: >
|
||||
JSON array containing a list of the JWS [JWS] signing algorithms (alg values) [JWA] supported by the
|
||||
|
@ -3008,7 +3105,7 @@ components:
|
|||
this is the "refresh_token" value returned from the token endpoint
|
||||
as defined in OAuth 2.0 [RFC6749], Section 5.1. Other token types
|
||||
are outside the scope of this specification.
|
||||
example: "authelia_at_cr4i4EtTn2F4k6mX4XzxbsBewkxCGn"
|
||||
example: 'authelia_at_cr4i4EtTn2F4k6mX4XzxbsBewkxCGn'
|
||||
type: string
|
||||
token_type_hint:
|
||||
description: >
|
||||
|
@ -3024,7 +3121,7 @@ components:
|
|||
enum:
|
||||
- "access_token"
|
||||
- "refresh_token"
|
||||
example: "access_token"
|
||||
example: 'access_token'
|
||||
type: string
|
||||
openid.spec.AccessRequest.ClientAuth:
|
||||
oneOf:
|
||||
|
@ -3040,7 +3137,7 @@ components:
|
|||
description: >
|
||||
REQUIRED if the client is not authenticating with the authorization server as described in
|
||||
Section 3.2.1. of [RFC6749]. The client identifier as described in Section 2.2 of [RFC6749].
|
||||
example: "my_client"
|
||||
example: 'my_client'
|
||||
type: string
|
||||
openid.spec.AccessRequest.ClientAuth.Secret:
|
||||
required:
|
||||
|
@ -3067,7 +3164,7 @@ components:
|
|||
"urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
|
||||
enum:
|
||||
- "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
|
||||
example: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
|
||||
example: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
|
||||
type: string
|
||||
client_assertion_type:
|
||||
description: >
|
||||
|
@ -3091,15 +3188,15 @@ components:
|
|||
type: string
|
||||
code:
|
||||
description: The Authorization Code.
|
||||
example: "authelia_ac_1j2kn3knj12n3kj12n"
|
||||
example: 'authelia_ac_1j2kn3knj12n3kj12n'
|
||||
type: string
|
||||
code_verifier:
|
||||
description: The Authorization Code Verifier (PKCE).
|
||||
example: "88a25754f7c0b3b3b88cf6cd4e29e8356b160524fdc1cb329a94471825628fd3"
|
||||
example: '88a25754f7c0b3b3b88cf6cd4e29e8356b160524fdc1cb329a94471825628fd3'
|
||||
type: string
|
||||
redirect_uri:
|
||||
description: The original Redirect URI used in the Authorization Request.
|
||||
example: "https://app.example.com/oidc/callback"
|
||||
example: 'https://app.{{ .Domain | default "example.com" }}/oidc/callback'
|
||||
type: string
|
||||
openid.spec.AccessRequest.DeviceCodeFlow:
|
||||
allOf:
|
||||
|
@ -3116,7 +3213,7 @@ components:
|
|||
type: string
|
||||
device_code:
|
||||
description: The Device Authorization Code.
|
||||
example: "authelia_dc_mn123kjn12kj3123njk"
|
||||
example: 'authelia_dc_mn123kjn12kj3123njk'
|
||||
type: string
|
||||
openid.spec.AccessRequest.RefreshTokenFlow:
|
||||
allOf:
|
||||
|
@ -3133,7 +3230,7 @@ components:
|
|||
type: string
|
||||
refresh_token:
|
||||
description: The Refresh Token.
|
||||
example: "authelia_rt_1n2j3kihn12kj3n12k"
|
||||
example: 'authelia_rt_1n2j3kihn12kj3n12k'
|
||||
type: string
|
||||
scope:
|
||||
description: >
|
||||
|
@ -3142,7 +3239,7 @@ components:
|
|||
not originally granted by the resource owner, and if omitted is
|
||||
treated as equal to the scope originally granted by the
|
||||
resource owner.
|
||||
example: "openid profile groups"
|
||||
example: 'openid profile groups'
|
||||
type: string
|
||||
openid.spec.AccessResponse:
|
||||
type: object
|
||||
|
@ -3153,17 +3250,17 @@ components:
|
|||
properties:
|
||||
access_token:
|
||||
description: The access token issued by the authorization server.
|
||||
example: "authelia_at_cr4i4EtTn2F4k6mX4XzxbsBewkxCGn"
|
||||
example: 'authelia_at_cr4i4EtTn2F4k6mX4XzxbsBewkxCGn'
|
||||
type: string
|
||||
id_token:
|
||||
description: The id token issued by the authorization server.
|
||||
example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
|
||||
example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'
|
||||
type: string
|
||||
refresh_token:
|
||||
description: >
|
||||
The refresh token, which can be used to obtain new access tokens using the
|
||||
same authorization grant as described in Section 6.
|
||||
example: "authelia_rt_kGBoSMbfVGP2RR6Kvujv3Xg7uXV2i"
|
||||
example: 'authelia_rt_kGBoSMbfVGP2RR6Kvujv3Xg7uXV2i'
|
||||
type: string
|
||||
token_type:
|
||||
description: >
|
||||
|
@ -3174,7 +3271,7 @@ components:
|
|||
type.
|
||||
enum:
|
||||
- "bearer"
|
||||
example: "bearer"
|
||||
example: 'bearer'
|
||||
type: string
|
||||
expires_in:
|
||||
description: >
|
||||
|
@ -3187,12 +3284,12 @@ components:
|
|||
type: integer
|
||||
state:
|
||||
description: Exactly the state value passed in the authorization request if present.
|
||||
example: "5dVZhNfri5XZS6wadskuzUk4MHYCvEcUgidjMeBjsktAhY7EKB"
|
||||
example: '5dVZhNfri5XZS6wadskuzUk4MHYCvEcUgidjMeBjsktAhY7EKB'
|
||||
type: string
|
||||
scope:
|
||||
description: >
|
||||
The scope of the access token as described by Section 3.3 if it differs from the requested scope.
|
||||
example: "openid profile groups"
|
||||
example: 'openid profile groups'
|
||||
type: string
|
||||
openid.spec.AuthorizeRequest:
|
||||
type: object
|
||||
|
@ -3204,13 +3301,13 @@ components:
|
|||
properties:
|
||||
scope:
|
||||
description: The requested scope.
|
||||
example: "openid profile groups"
|
||||
example: 'openid profile groups'
|
||||
type: string
|
||||
response_type:
|
||||
$ref: '#/components/schemas/openid.spec.ResponseType'
|
||||
client_id:
|
||||
description: The OAuth 2.0 client identifier.
|
||||
example: "app"
|
||||
example: 'app'
|
||||
type: string
|
||||
redirect_uri:
|
||||
description: >
|
||||
|
@ -3221,14 +3318,14 @@ components:
|
|||
that the Client Type is confidential, as defined in Section 2.1 of OAuth 2.0, and provided the OP
|
||||
allows the use of http Redirection URIs in this case. The Redirection URI MAY use an alternate
|
||||
scheme, such as one that is intended to identify a callback into a native application.
|
||||
example: "https://app.example.com"
|
||||
example: 'https://app.{{ .Domain | default "example.com" }}'
|
||||
type: string
|
||||
state:
|
||||
description: >
|
||||
Opaque value used to maintain state between the request and the callback. Typically, Cross-Site
|
||||
Request Forgery (CSRF, XSRF) mitigation is done by cryptographically binding the value of this
|
||||
parameter with a browser cookie.
|
||||
example: "oV84Vsy7wyCgRk2h4aZBmXZq4q3g2f"
|
||||
example: 'oV84Vsy7wyCgRk2h4aZBmXZq4q3g2f'
|
||||
type: string
|
||||
response_mode:
|
||||
$ref: '#/components/schemas/openid.spec.ResponseMode'
|
||||
|
@ -3238,7 +3335,7 @@ components:
|
|||
The value is passed through unmodified from the Authentication Request to the ID Token. Sufficient
|
||||
entropy MUST be present in the nonce values used to prevent attackers from guessing values. For
|
||||
implementation notes, see Section 15.5.2.
|
||||
example: "TRMLqchoKGQNcooXvBvUy9PtmLdJGf"
|
||||
example: 'TRMLqchoKGQNcooXvBvUy9PtmLdJGf'
|
||||
type: string
|
||||
display:
|
||||
$ref: '#/components/schemas/openid.spec.DisplayType'
|
||||
|
@ -3254,7 +3351,7 @@ components:
|
|||
- "login consent"
|
||||
- "login select_account"
|
||||
- "consent select_account"
|
||||
example: "consent"
|
||||
example: 'consent'
|
||||
type: string
|
||||
max_age:
|
||||
description: >
|
||||
|
@ -3354,7 +3451,7 @@ components:
|
|||
- "popup"
|
||||
- "touch"
|
||||
- "wap"
|
||||
example: "page"
|
||||
example: 'page'
|
||||
type: string
|
||||
openid.spec.ResponseType:
|
||||
description: The OAuth 2.0 / OpenID Connect 1.0 Response Type.
|
||||
|
@ -3367,7 +3464,7 @@ components:
|
|||
- "token id_token"
|
||||
- "code id_token token"
|
||||
- "none"
|
||||
example: "code"
|
||||
example: 'code'
|
||||
type: string
|
||||
openid.spec.ResponseMode:
|
||||
description: >
|
||||
|
@ -3378,7 +3475,7 @@ components:
|
|||
- "query"
|
||||
- "fragment"
|
||||
- "form_post"
|
||||
example: "query"
|
||||
example: 'query'
|
||||
type: string
|
||||
openid.spec.GrantType:
|
||||
description: The OAuth 2.0 / OpenID Connect 1.0 Grant Type.
|
||||
|
@ -3389,14 +3486,14 @@ components:
|
|||
- "password"
|
||||
- "client_credentials"
|
||||
- "urn:ietf:params:oauth:grant-type:device_code"
|
||||
example: "authorization_code"
|
||||
example: 'authorization_code'
|
||||
type: string
|
||||
openid.spec.CodeChallengeMethod:
|
||||
description: The RFC7636 Code Challenge Verifier Method.
|
||||
enum:
|
||||
- "plain"
|
||||
- "S256"
|
||||
example: "S256"
|
||||
example: 'S256'
|
||||
type: string
|
||||
openid.spec.ClaimType:
|
||||
description: The representation of claims.
|
||||
|
@ -3404,7 +3501,7 @@ components:
|
|||
- "normal"
|
||||
- "aggregated"
|
||||
- "distributed"
|
||||
example: "normal"
|
||||
example: 'normal'
|
||||
type: string
|
||||
jose.spec.None:
|
||||
description: The JSON Web Signature Algorithm
|
||||
|
@ -3477,7 +3574,7 @@ components:
|
|||
enum:
|
||||
- "sig"
|
||||
- "enc"
|
||||
example: "sig"
|
||||
example: 'sig'
|
||||
type: string
|
||||
key_ops:
|
||||
description: >
|
||||
|
@ -3579,13 +3676,13 @@ components:
|
|||
The "kty" (key type) parameter identifies the cryptographic algorithm
|
||||
family used with the key.
|
||||
type: string
|
||||
example: "RSA"
|
||||
example: 'RSA'
|
||||
enum:
|
||||
- "RSA"
|
||||
alg:
|
||||
description: The JSON Web Signature Algorithm
|
||||
type: string
|
||||
example: "RS256"
|
||||
example: 'RS256'
|
||||
enum:
|
||||
- "RS256"
|
||||
- "RS384"
|
||||
|
@ -3696,13 +3793,13 @@ components:
|
|||
The "kty" (key type) parameter identifies the cryptographic algorithm
|
||||
family used with the key.
|
||||
type: string
|
||||
example: "EC"
|
||||
example: 'EC'
|
||||
enum:
|
||||
- "EC"
|
||||
alg:
|
||||
description: The JSON Web Signature Algorithm
|
||||
type: string
|
||||
example: "ES256"
|
||||
example: 'ES256'
|
||||
enum:
|
||||
- "ES256"
|
||||
- "ES384"
|
||||
|
@ -3726,7 +3823,7 @@ components:
|
|||
The curve parameter identifies the cryptographic curve used with the key. Curve
|
||||
values from [DSS] used by this specification.
|
||||
type: string
|
||||
example: "P-521"
|
||||
example: 'P-521'
|
||||
enum:
|
||||
- "P-256"
|
||||
- "P-384"
|
||||
|
@ -3766,7 +3863,7 @@ components:
|
|||
The "kty" (key type) parameter identifies the cryptographic algorithm
|
||||
family used with the key.
|
||||
type: string
|
||||
example: "oct"
|
||||
example: 'oct'
|
||||
enum:
|
||||
- "oct"
|
||||
k:
|
||||
|
|
|
@ -17,6 +17,12 @@ obviously choose a different path if you are so inclined.
|
|||
|
||||
## Prerequisites
|
||||
|
||||
The most important prerequisite that users understand that there is no single way to deploy software similar to
|
||||
Authelia. We provide as much information as possible for users to configure the critical parts usually in the most
|
||||
common scenarios however those using more advanced architectures are likely going to have to adapt. We can generally
|
||||
help with answering less specific questions about this and it may be possible if provided adequate information more
|
||||
specific questions may be answered.
|
||||
|
||||
### Forwarded Authentication
|
||||
|
||||
Forwarded Authentication is a simple per-request authorization flow that checks the metadata of a request and a session
|
||||
|
|
|
@ -63,6 +63,47 @@ to the trusted proxy list in [Caddy]:
|
|||
* 192.168.0.0/16
|
||||
* fc00::/7
|
||||
|
||||
## Assumptions and Adaptation
|
||||
|
||||
This guide makes a few assumptions. These assumptions may require adaptation in more advanced and complex scenarios. We
|
||||
can not reasonably have examples for every advanced configuration option that exists. The
|
||||
following are the assumptions we make:
|
||||
|
||||
* Deployment Scenario:
|
||||
* Single Host
|
||||
* Authelia is deployed as a Container with the container name `authelia` on port `9091`
|
||||
* Proxy is deployed as a Container on a network shared with Authelia
|
||||
* The above assumption means that AUthelia should be accesible to the proxy on `http://authelia:9091` and as such:
|
||||
* You will have to adapt all instances of the above URL to be `https://` if Authelia configuration has a TLS key and
|
||||
certificate defined
|
||||
* You will have to adapt all instances of `authelia` in the URL if:
|
||||
* you're using a different container name
|
||||
* you deployed the proxy to a different location
|
||||
* You will have to adapt all instances of `9091` in the URL if:
|
||||
* you have adjusted the default port in the configuration
|
||||
* You will have to adapt the entire URL if:
|
||||
* Authelia is on a different host to the proxy
|
||||
* All services are part of the `example.com` domain:
|
||||
* This domain and the subdomains will have to be adapted in all examples to match your specific domains unless you're
|
||||
just testing or you want ot use that specific domain
|
||||
|
||||
## Implementation
|
||||
|
||||
[Caddy] utilizes the [ForwardAuth](../../reference/guides/proxy-authorization.md#forwardauth) Authz implementation. The
|
||||
associated [Metadata](../../reference/guides/proxy-authorization.md#forwardauth-metadata) should be considered required.
|
||||
|
||||
The examples below assume you are using the default
|
||||
[Authz Endpoints Configuration](../../configuration/miscellaneous/server-endpoints-authz.md) or one similar to the
|
||||
following minimal configuration:
|
||||
|
||||
```yaml
|
||||
server:
|
||||
endpoints:
|
||||
authz:
|
||||
forward-auth:
|
||||
implementation: ForwardAuth
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Below you will find commented examples of the following configuration:
|
||||
|
|
|
@ -37,6 +37,47 @@ how you can configure multiple IP ranges. You should customize this example to f
|
|||
You should only include the specific IP address ranges of the trusted proxies within your architecture and should not
|
||||
trust entire subnets unless that subnet only has trusted proxies and no other services.*
|
||||
|
||||
## Assumptions and Adaptation
|
||||
|
||||
This guide makes a few assumptions. These assumptions may require adaptation in more advanced and complex scenarios. We
|
||||
can not reasonably have examples for every advanced configuration option that exists. The
|
||||
following are the assumptions we make:
|
||||
|
||||
* Deployment Scenario:
|
||||
* Single Host
|
||||
* Authelia is deployed as a Container with the container name `authelia` on port `9091`
|
||||
* Proxy is deployed as a Container on a network shared with Authelia
|
||||
* The above assumption means that AUthelia should be accesible to the proxy on `http://authelia:9091` and as such:
|
||||
* You will have to adapt all instances of the above URL to be `https://` if Authelia configuration has a TLS key and
|
||||
certificate defined
|
||||
* You will have to adapt all instances of `authelia` in the URL if:
|
||||
* you're using a different container name
|
||||
* you deployed the proxy to a different location
|
||||
* You will have to adapt all instances of `9091` in the URL if:
|
||||
* you have adjusted the default port in the configuration
|
||||
* You will have to adapt the entire URL if:
|
||||
* Authelia is on a different host to the proxy
|
||||
* All services are part of the `example.com` domain:
|
||||
* This domain and the subdomains will have to be adapted in all examples to match your specific domains unless you're
|
||||
just testing or you want ot use that specific domain
|
||||
|
||||
## Implementation
|
||||
|
||||
[Envoy] utilizes the [ExtAuthz](../../reference/guides/proxy-authorization.md#extauthz) Authz implementation. The
|
||||
associated [Metadata](../../reference/guides/proxy-authorization.md#extauthz-metadata) should be considered required.
|
||||
|
||||
The examples below assume you are using the default
|
||||
[Authz Endpoints Configuration](../../configuration/miscellaneous/server-endpoints-authz.md) or one similar to the
|
||||
following minimal configuration:
|
||||
|
||||
```yaml
|
||||
server:
|
||||
endpoints:
|
||||
authz:
|
||||
ext-authz:
|
||||
implementation: ExtAuthz
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Below you will find commented examples of the following configuration:
|
||||
|
|
|
@ -66,22 +66,61 @@ the following networks to the trusted proxy list in [HAProxy]:
|
|||
* 192.168.0.0/16
|
||||
* fc00::/7
|
||||
|
||||
## Assumptions and Adaptation
|
||||
|
||||
This guide makes a few assumptions. These assumptions may require adaptation in more advanced and complex scenarios. We
|
||||
can not reasonably have examples for every advanced configuration option that exists. The
|
||||
following are the assumptions we make:
|
||||
|
||||
* Deployment Scenario:
|
||||
* Single Host
|
||||
* Authelia is deployed as a Container with the container name `authelia` on port `9091`
|
||||
* Proxy is deployed as a Container on a network shared with Authelia
|
||||
* The above assumption means that AUthelia should be accesible to the proxy on `http://authelia:9091` and as such:
|
||||
* You will have to adapt all instances of the above URL to be `https://` if Authelia configuration has a TLS key and
|
||||
certificate defined
|
||||
* You will have to adapt all instances of `authelia` in the URL if:
|
||||
* you're using a different container name
|
||||
* you deployed the proxy to a different location
|
||||
* You will have to adapt all instances of `9091` in the URL if:
|
||||
* you have adjusted the default port in the configuration
|
||||
* You will have to adapt the entire URL if:
|
||||
* Authelia is on a different host to the proxy
|
||||
* All services are part of the `example.com` domain:
|
||||
* This domain and the subdomains will have to be adapted in all examples to match your specific domains unless you're
|
||||
just testing or you want ot use that specific domain
|
||||
|
||||
## Implementation
|
||||
|
||||
[HAProxy] utilizes the [ForwardAuth](../../reference/guides/proxy-authorization.md#forwardauth) Authz implementation. The
|
||||
associated [Metadata](../../reference/guides/proxy-authorization.md#forwardauth-metadata) should be considered required.
|
||||
|
||||
The examples below assume you are using the default
|
||||
[Authz Endpoints Configuration](../../configuration/miscellaneous/server-endpoints-authz.md) or one similar to the
|
||||
following minimal configuration:
|
||||
|
||||
```yaml
|
||||
server:
|
||||
endpoints:
|
||||
authz:
|
||||
forward-auth:
|
||||
implementation: ForwardAuth
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Below you will find commented examples of the following configuration:
|
||||
|
||||
* Authelia Portal
|
||||
* Protected Endpoint (Nextcloud)
|
||||
* Protected Endpoint with `Authorization` header for basic authentication (Heimdall)
|
||||
* Protected Endpoints (Nextcloud)
|
||||
|
||||
With this configuration you can protect your virtual hosts with Authelia, by following the steps below:
|
||||
|
||||
1. Add host(s) to the `protected-frontends` or `protected-frontends-basic` ACLs to support protection with Authelia.
|
||||
You can separate each subdomain with a `|` in the regex, for example:
|
||||
1. Add host(s) to the `protected-frontends` ACLs to support protection with Authelia. You can separate each subdomain
|
||||
with a `|` in the regex, for example:
|
||||
|
||||
```text
|
||||
acl protected-frontends hdr(host) -m reg -i ^(?i)(jenkins|nextcloud|phpmyadmin)\.example\.com
|
||||
acl protected-frontends-basic hdr(host) -m reg -i ^(?i)(heimdall)\.example\.com
|
||||
```
|
||||
|
||||
2. Add host ACL(s) in the form of `host-service`, this will be utilised to route to the correct
|
||||
|
@ -166,46 +205,24 @@ frontend fe_http
|
|||
option forwardfor
|
||||
|
||||
# Host ACLs
|
||||
acl protected-frontends hdr(host) -m reg -i ^(?i)(nextcloud)\.example\.com
|
||||
acl protected-frontends-basic hdr(host) -m reg -i ^(?i)(heimdall)\.example\.com
|
||||
acl host-authelia hdr(host) -i auth.example.com
|
||||
acl host-nextcloud hdr(host) -i nextcloud.example.com
|
||||
acl host-heimdall hdr(host) -i heimdall.example.com
|
||||
|
||||
# This is required if utilising basic auth with /api/verify?auth=basic
|
||||
http-request set-var(txn.host) hdr(Host)
|
||||
acl protected-frontends hdr(Host) -m reg -i ^(?i)(nextcloud|heimdall)\.example\.com
|
||||
acl host-authelia hdr(Host) -i auth.example.com
|
||||
acl host-nextcloud hdr(Host) -i nextcloud.example.com
|
||||
acl host-heimdall hdr(Host) -i heimdall.example.com
|
||||
|
||||
http-request set-var(req.scheme) str(https) if { ssl_fc }
|
||||
http-request set-var(req.scheme) str(http) if !{ ssl_fc }
|
||||
http-request set-var(req.questionmark) str(?) if { query -m found }
|
||||
|
||||
# These are optional if you wish to use the Methods rule in the access_control section.
|
||||
#http-request set-var(req.method) str(CONNECT) if { method CONNECT }
|
||||
#http-request set-var(req.method) str(GET) if { method GET }
|
||||
#http-request set-var(req.method) str(HEAD) if { method HEAD }
|
||||
#http-request set-var(req.method) str(OPTIONS) if { method OPTIONS }
|
||||
#http-request set-var(req.method) str(POST) if { method POST }
|
||||
#http-request set-var(req.method) str(TRACE) if { method TRACE }
|
||||
#http-request set-var(req.method) str(PUT) if { method PUT }
|
||||
#http-request set-var(req.method) str(PATCH) if { method PATCH }
|
||||
#http-request set-var(req.method) str(DELETE) if { method DELETE }
|
||||
#http-request set-header X-Forwarded-Method %[var(req.method)]
|
||||
|
||||
# Required headers
|
||||
http-request set-header X-Real-IP %[src]
|
||||
http-request set-header X-Original-Method %[var(req.method)]
|
||||
http-request set-header X-Original-URL %[var(req.scheme)]://%[req.hdr(Host)]%[path]%[var(req.questionmark)]%[query]
|
||||
# Required Headers
|
||||
http-request set-header X-Forwarded-Method %[method]
|
||||
http-request set-header X-Forwarded-Proto %[var(req.scheme)]
|
||||
http-request set-header X-Forwarded-Host %[req.hdr(Host)]
|
||||
http-request set-header X-Forwarded-URI %[path]%[var(req.questionmark)]%[query]
|
||||
|
||||
# Protect endpoints with haproxy-auth-request and Authelia
|
||||
http-request lua.auth-request be_authelia /api/authz/auth-request if protected-frontends
|
||||
# Force `Authorization` header via query arg to /api/verify
|
||||
http-request lua.auth-request be_authelia /api/verify?auth=basic if protected-frontends-basic
|
||||
|
||||
# Redirect protected-frontends to Authelia if not authenticated
|
||||
http-request redirect location https://auth.example.com/?rd=%[var(req.scheme)]://%[base]%[var(req.questionmark)]%[query] if protected-frontends !{ var(txn.auth_response_successful) -m bool }
|
||||
# Send 401 and pass `WWW-Authenticate` header on protected-frontend-basic if not pre-authenticated
|
||||
http-request set-var(txn.auth) var(req.auth_response_header.www_authenticate) if protected-frontends-basic !{ var(txn.auth_response_successful) -m bool }
|
||||
http-response deny deny_status 401 hdr WWW-Authenticate %[var(txn.auth)] if { var(txn.host) -m reg -i ^(?i)(heimdall)\.example\.com } !{ var(txn.auth_response_successful) -m bool }
|
||||
http-request lua.auth-intercept be_authelia /api/authz/forward-auth HEAD * authorization,proxy-authorization,remote_user,remote-user,remote-groups,remote-name,remote-email - if protected-frontends
|
||||
http-request redirect location %[var(txn.auth_response_location)] if protected-frontends !{ var(txn.auth_response_successful) -m bool }
|
||||
|
||||
# Authelia backend route
|
||||
use_backend be_authelia if host-authelia
|
||||
|
@ -218,24 +235,6 @@ backend be_authelia
|
|||
server authelia authelia:9091
|
||||
|
||||
backend be_nextcloud
|
||||
## Pass the special authorization response headers to the protected application.
|
||||
acl authorization_exist var(req.auth_response_header.authorization) -m found
|
||||
acl proxy_authorization_exist var(req.auth_response_header.proxy_authorization) -m found
|
||||
|
||||
http-request set-header Authorization %[var(req.auth_response_header.authorization)] if authorization_exist
|
||||
http-request set-header Proxy-Authorization %[var(req.auth_response_header.proxy_authorization)] if proxy_authorization_exist
|
||||
|
||||
## Pass the special metadata response headers to the protected application.
|
||||
acl remote_user_exist var(req.auth_response_header.remote_user) -m found
|
||||
acl remote_groups_exist var(req.auth_response_header.remote_groups) -m found
|
||||
acl remote_name_exist var(req.auth_response_header.remote_name) -m found
|
||||
acl remote_email_exist var(req.auth_response_header.remote_email) -m found
|
||||
|
||||
http-request set-header Remote-User %[var(req.auth_response_header.remote_user)] if remote_user_exist
|
||||
http-request set-header Remote-Groups %[var(req.auth_response_header.remote_groups)] if remote_groups_exist
|
||||
http-request set-header Remote-Name %[var(req.auth_response_header.remote_name)] if remote_name_exist
|
||||
http-request set-header Remote-Email %[var(req.auth_response_header.remote_email)] if remote_email_exist
|
||||
|
||||
## Pass the Set-Cookie response headers to the user.
|
||||
acl set_cookie_exist var(req.auth_response_header.set_cookie) -m found
|
||||
http-response set-header Set-Cookie %[var(req.auth_response_header.set_cookie)] if set_cookie_exist
|
||||
|
@ -243,24 +242,6 @@ backend be_nextcloud
|
|||
server nextcloud nextcloud:443 ssl verify none
|
||||
|
||||
backend be_heimdall
|
||||
## Pass the special authorization response headers to the protected application.
|
||||
acl authorization_exist var(req.auth_response_header.authorization) -m found
|
||||
acl proxy_authorization_exist var(req.auth_response_header.proxy_authorization) -m found
|
||||
|
||||
http-request set-header Authorization %[var(req.auth_response_header.authorization)] if authorization_exist
|
||||
http-request set-header Proxy-Authorization %[var(req.auth_response_header.proxy_authorization)] if proxy_authorization_exist
|
||||
|
||||
## Pass the special metadata response headers to the protected application.
|
||||
acl remote_user_exist var(req.auth_response_header.remote_user) -m found
|
||||
acl remote_groups_exist var(req.auth_response_header.remote_groups) -m found
|
||||
acl remote_name_exist var(req.auth_response_header.remote_name) -m found
|
||||
acl remote_email_exist var(req.auth_response_header.remote_email) -m found
|
||||
|
||||
http-request set-header Remote-User %[var(req.auth_response_header.remote_user)] if remote_user_exist
|
||||
http-request set-header Remote-Groups %[var(req.auth_response_header.remote_groups)] if remote_groups_exist
|
||||
http-request set-header Remote-Name %[var(req.auth_response_header.remote_name)] if remote_name_exist
|
||||
http-request set-header Remote-Email %[var(req.auth_response_header.remote_email)] if remote_email_exist
|
||||
|
||||
## Pass the Set-Cookie response headers to the user.
|
||||
acl set_cookie_exist var(req.auth_response_header.set_cookie) -m found
|
||||
http-response set-header Set-Cookie %[var(req.auth_response_header.set_cookie)] if set_cookie_exist
|
||||
|
@ -287,47 +268,37 @@ defaults
|
|||
frontend fe_http
|
||||
bind *:443 ssl crt /usr/local/etc/haproxy/haproxy.pem
|
||||
|
||||
# Host ACLs
|
||||
acl protected-frontends hdr(host) -m reg -i ^(?i)(nextcloud)\.example\.com
|
||||
acl protected-frontends-basic hdr(host) -m reg -i ^(?i)(heimdall)\.example\.com
|
||||
acl host-authelia hdr(host) -i auth.example.com
|
||||
acl host-nextcloud hdr(host) -i nextcloud.example.com
|
||||
acl host-heimdall hdr(host) -i heimdall.example.com
|
||||
## Trusted Proxies.
|
||||
http-request del-header X-Forwarded-For
|
||||
|
||||
# This is required if utilising basic auth with /api/verify?auth=basic
|
||||
http-request set-var(txn.host) hdr(Host)
|
||||
## Comment the above directive and the two directives below to enable the trusted proxies ACL.
|
||||
# acl src-trusted_proxies src -f trusted_proxies.src.acl
|
||||
# http-request del-header X-Forwarded-For if !src-trusted_proxies
|
||||
|
||||
## Ensure X-Forwarded-For is set for the auth request.
|
||||
acl hdr-xff_exists req.hdr(X-Forwarded-For) -m found
|
||||
http-request set-header X-Forwarded-For %[src] if !hdr-xff_exists
|
||||
option forwardfor
|
||||
|
||||
# Host ACLs
|
||||
acl protected-frontends hdr(Host) -m reg -i ^(?i)(nextcloud|heimdall)\.example\.com
|
||||
acl host-authelia hdr(Host) -i auth.example.com
|
||||
acl host-nextcloud hdr(Host) -i nextcloud.example.com
|
||||
acl host-heimdall hdr(Host) -i heimdall.example.com
|
||||
|
||||
http-request set-var(req.scheme) str(https) if { ssl_fc }
|
||||
http-request set-var(req.scheme) str(http) if !{ ssl_fc }
|
||||
http-request set-var(req.questionmark) str(?) if { query -m found }
|
||||
|
||||
# These are optional if you wish to use the Methods rule in the access_control section.
|
||||
#http-request set-var(req.method) str(CONNECT) if { method CONNECT }
|
||||
#http-request set-var(req.method) str(GET) if { method GET }
|
||||
#http-request set-var(req.method) str(HEAD) if { method HEAD }
|
||||
#http-request set-var(req.method) str(OPTIONS) if { method OPTIONS }
|
||||
#http-request set-var(req.method) str(POST) if { method POST }
|
||||
#http-request set-var(req.method) str(TRACE) if { method TRACE }
|
||||
#http-request set-var(req.method) str(PUT) if { method PUT }
|
||||
#http-request set-var(req.method) str(PATCH) if { method PATCH }
|
||||
#http-request set-var(req.method) str(DELETE) if { method DELETE }
|
||||
#http-request set-header X-Forwarded-Method %[var(req.method)]
|
||||
|
||||
# Required headers
|
||||
http-request set-header X-Real-IP %[src]
|
||||
http-request set-header X-Original-Method %[var(req.method)]
|
||||
http-request set-header X-Original-URL %[var(req.scheme)]://%[req.hdr(Host)]%[path]%[var(req.questionmark)]%[query]
|
||||
# Required Headers
|
||||
http-request set-header X-Forwarded-Method %[method]
|
||||
http-request set-header X-Forwarded-Proto %[var(req.scheme)]
|
||||
http-request set-header X-Forwarded-Host %[req.hdr(Host)]
|
||||
http-request set-header X-Forwarded-URI %[path]%[var(req.questionmark)]%[query]
|
||||
|
||||
# Protect endpoints with haproxy-auth-request and Authelia
|
||||
http-request lua.auth-request be_authelia_proxy /api/authz/auth-request if protected-frontends
|
||||
# Force `Authorization` header via query arg to /api/verify
|
||||
http-request lua.auth-request be_authelia_proxy /api/verify?auth=basic if protected-frontends-basic
|
||||
|
||||
# Redirect protected-frontends to Authelia if not authenticated
|
||||
http-request redirect location https://auth.example.com/?rd=%[var(req.scheme)]://%[base]%[var(req.questionmark)]%[query] if protected-frontends !{ var(txn.auth_response_successful) -m bool }
|
||||
# Send 401 and pass `WWW-Authenticate` header on protected-frontend-basic if not pre-authenticated
|
||||
http-request set-var(txn.auth) var(req.auth_response_header.www_authenticate) if protected-frontends-basic !{ var(txn.auth_response_successful) -m bool }
|
||||
http-response deny deny_status 401 hdr WWW-Authenticate %[var(txn.auth)] if { var(txn.host) -m reg -i ^(?i)(heimdall)\.example\.com } !{ var(txn.auth_response_successful) -m bool }
|
||||
http-request lua.auth-intercept be_authelia_proxy /api/authz/forward-auth HEAD * authorization,proxy-authorization,remote_user,remote-user,remote-groups,remote-name,remote-email - if protected-frontends
|
||||
http-request redirect location %[var(txn.auth_response_location)] if protected-frontends !{ var(txn.auth_response_successful) -m bool }
|
||||
|
||||
# Authelia backend route
|
||||
use_backend be_authelia if host-authelia
|
||||
|
@ -349,24 +320,6 @@ listen authelia_proxy
|
|||
server authelia authelia:9091 ssl verify none
|
||||
|
||||
backend be_nextcloud
|
||||
## Pass the special authorization response headers to the protected application.
|
||||
acl authorization_exist var(req.auth_response_header.authorization) -m found
|
||||
acl proxy_authorization_exist var(req.auth_response_header.proxy_authorization) -m found
|
||||
|
||||
http-request set-header Authorization %[var(req.auth_response_header.authorization)] if authorization_exist
|
||||
http-request set-header Proxy-Authorization %[var(req.auth_response_header.proxy_authorization)] if proxy_authorization_exist
|
||||
|
||||
## Pass the special metadata response headers to the protected application.
|
||||
acl remote_user_exist var(req.auth_response_header.remote_user) -m found
|
||||
acl remote_groups_exist var(req.auth_response_header.remote_groups) -m found
|
||||
acl remote_name_exist var(req.auth_response_header.remote_name) -m found
|
||||
acl remote_email_exist var(req.auth_response_header.remote_email) -m found
|
||||
|
||||
http-request set-header Remote-User %[var(req.auth_response_header.remote_user)] if remote_user_exist
|
||||
http-request set-header Remote-Groups %[var(req.auth_response_header.remote_groups)] if remote_groups_exist
|
||||
http-request set-header Remote-Name %[var(req.auth_response_header.remote_name)] if remote_name_exist
|
||||
http-request set-header Remote-Email %[var(req.auth_response_header.remote_email)] if remote_email_exist
|
||||
|
||||
## Pass the Set-Cookie response headers to the user.
|
||||
acl set_cookie_exist var(req.auth_response_header.set_cookie) -m found
|
||||
http-response set-header Set-Cookie %[var(req.auth_response_header.set_cookie)] if set_cookie_exist
|
||||
|
@ -374,24 +327,6 @@ backend be_nextcloud
|
|||
server nextcloud nextcloud:443 ssl verify none
|
||||
|
||||
backend be_heimdall
|
||||
## Pass the special authorization response headers to the protected application.
|
||||
acl authorization_exist var(req.auth_response_header.authorization) -m found
|
||||
acl proxy_authorization_exist var(req.auth_response_header.proxy_authorization) -m found
|
||||
|
||||
http-request set-header Authorization %[var(req.auth_response_header.authorization)] if authorization_exist
|
||||
http-request set-header Proxy-Authorization %[var(req.auth_response_header.proxy_authorization)] if proxy_authorization_exist
|
||||
|
||||
## Pass the special metadata response headers to the protected application.
|
||||
acl remote_user_exist var(req.auth_response_header.remote_user) -m found
|
||||
acl remote_groups_exist var(req.auth_response_header.remote_groups) -m found
|
||||
acl remote_name_exist var(req.auth_response_header.remote_name) -m found
|
||||
acl remote_email_exist var(req.auth_response_header.remote_email) -m found
|
||||
|
||||
http-request set-header Remote-User %[var(req.auth_response_header.remote_user)] if remote_user_exist
|
||||
http-request set-header Remote-Groups %[var(req.auth_response_header.remote_groups)] if remote_groups_exist
|
||||
http-request set-header Remote-Name %[var(req.auth_response_header.remote_name)] if remote_name_exist
|
||||
http-request set-header Remote-Email %[var(req.auth_response_header.remote_email)] if remote_email_exist
|
||||
|
||||
## Pass the Set-Cookie response headers to the user.
|
||||
acl set_cookie_exist var(req.auth_response_header.set_cookie) -m found
|
||||
http-response set-header Set-Cookie %[var(req.auth_response_header.set_cookie)] if set_cookie_exist
|
||||
|
|
|
@ -40,6 +40,30 @@ To configure trusted proxies for [NGINX Proxy Manager] see the [NGINX] section o
|
|||
[Trusted Proxies](../nginx.md#trusted-proxies). Adapting this to [NGINX Proxy Manager] is beyond the scope of
|
||||
this documentation.
|
||||
|
||||
## Assumptions and Adaptation
|
||||
|
||||
This guide makes a few assumptions. These assumptions may require adaptation in more advanced and complex scenarios. We
|
||||
can not reasonably have examples for every advanced configuration option that exists. The
|
||||
following are the assumptions we make:
|
||||
|
||||
* Deployment Scenario:
|
||||
* Single Host
|
||||
* Authelia is deployed as a Container with the container name `authelia` on port `9091`
|
||||
* Proxy is deployed as a Container on a network shared with Authelia
|
||||
* The above assumption means that AUthelia should be accesible to the proxy on `http://authelia:9091` and as such:
|
||||
* You will have to adapt all instances of the above URL to be `https://` if Authelia configuration has a TLS key and
|
||||
certificate defined
|
||||
* You will have to adapt all instances of `authelia` in the URL if:
|
||||
* you're using a different container name
|
||||
* you deployed the proxy to a different location
|
||||
* You will have to adapt all instances of `9091` in the URL if:
|
||||
* you have adjusted the default port in the configuration
|
||||
* You will have to adapt the entire URL if:
|
||||
* Authelia is on a different host to the proxy
|
||||
* All services are part of the `example.com` domain:
|
||||
* This domain and the subdomains will have to be adapted in all examples to match your specific domains unless you're
|
||||
just testing or you want ot use that specific domain
|
||||
|
||||
## Docker Compose
|
||||
|
||||
The following docker compose example has various applications suitable for setting up an example environment.
|
||||
|
|
|
@ -34,8 +34,8 @@ You need the following to run __Authelia__ with [NGINX]:
|
|||
|
||||
* [NGINX] must be built with the `http_auth_request` module which is relatively common
|
||||
* [NGINX] must be built with the `http_realip` module which is relatively common
|
||||
* [NGINX] must be built with the `http_set_misc` module or the `nginx-mod-http-set-misc` package if you want to preserve
|
||||
more than one query parameter when redirected to the portal due to a limitation in [NGINX]
|
||||
* [NGINX] must be built with the `http_set_misc` module or the `nginx-mod-http-set-misc` package if you want to use the
|
||||
legacy method and preserve more than one query parameter when redirected to the portal due to a limitation in [NGINX]
|
||||
|
||||
## Trusted Proxies
|
||||
|
||||
|
@ -52,6 +52,47 @@ configured in the `proxy.conf` file. Each `set_realip_from` directive adds a tru
|
|||
proxies list. Any request that comes from a source IP not in one of the configured ranges results in the header being
|
||||
replaced with the source IP of the client.
|
||||
|
||||
## Assumptions and Adaptation
|
||||
|
||||
This guide makes a few assumptions. These assumptions may require adaptation in more advanced and complex scenarios. We
|
||||
can not reasonably have examples for every advanced configuration option that exists. The
|
||||
following are the assumptions we make:
|
||||
|
||||
* Deployment Scenario:
|
||||
* Single Host
|
||||
* Authelia is deployed as a Container with the container name `authelia` on port `9091`
|
||||
* Proxy is deployed as a Container on a network shared with Authelia
|
||||
* The above assumption means that AUthelia should be accesible to the proxy on `http://authelia:9091` and as such:
|
||||
* You will have to adapt all instances of the above URL to be `https://` if Authelia configuration has a TLS key and
|
||||
certificate defined
|
||||
* You will have to adapt all instances of `authelia` in the URL if:
|
||||
* you're using a different container name
|
||||
* you deployed the proxy to a different location
|
||||
* You will have to adapt all instances of `9091` in the URL if:
|
||||
* you have adjusted the default port in the configuration
|
||||
* You will have to adapt the entire URL if:
|
||||
* Authelia is on a different host to the proxy
|
||||
* All services are part of the `example.com` domain:
|
||||
* This domain and the subdomains will have to be adapted in all examples to match your specific domains unless you're
|
||||
just testing or you want ot use that specific domain
|
||||
|
||||
## Implementation
|
||||
|
||||
[NGINX] utilizes the [AuthRequest](../../reference/guides/proxy-authorization.md#authrequest) Authz implementation. The
|
||||
associated [Metadata](../../reference/guides/proxy-authorization.md#authrequest-metadata) should be considered required.
|
||||
|
||||
The examples below assume you are using the default
|
||||
[Authz Endpoints Configuration](../../configuration/miscellaneous/server-endpoints-authz.md) or one similar to the
|
||||
following minimal configuration:
|
||||
|
||||
```yaml
|
||||
server:
|
||||
endpoints:
|
||||
authz:
|
||||
auth-request:
|
||||
implementation: AuthRequest
|
||||
```
|
||||
|
||||
## Docker Compose
|
||||
|
||||
The following docker compose example has various applications suitable for setting up an example environment.
|
||||
|
@ -425,14 +466,6 @@ and is paired with [authelia-location.conf](#authelia-locationconf).*
|
|||
## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource.
|
||||
auth_request /internal/authelia/authz;
|
||||
|
||||
## Set the $target_url variable based on the original request.
|
||||
|
||||
## Comment this line if you're using nginx without the http_set_misc module.
|
||||
set_escape_uri $target_url $scheme://$http_host$request_uri;
|
||||
|
||||
## Uncomment this line if you're using NGINX without the http_set_misc module.
|
||||
# set $target_url $scheme://$http_host$request_uri;
|
||||
|
||||
## Save the upstream authorization response headers from Authelia to variables.
|
||||
auth_request_set $authorization $upstream_http_authorization;
|
||||
auth_request_set $proxy_authorization $upstream_http_proxy_authorization;
|
||||
|
@ -457,8 +490,23 @@ proxy_set_header Remote-Name $name;
|
|||
auth_request_set $cookie $upstream_http_set_cookie;
|
||||
add_header Set-Cookie $cookie;
|
||||
|
||||
## If the subreqest returns 200 pass to the backend, if the subrequest returns 401 redirect to the portal.
|
||||
error_page 401 =302 https://auth.example.com/?rd=$target_url;
|
||||
## Configure the redirection when the authz failure occurs. Lines starting with 'Modern Method' and 'Legacy Method'
|
||||
## should be commented / uncommented as pairs. The modern method uses the session cookies configuration's authelia_url
|
||||
## value to determine the redirection URL here. It's much simpler and compatible with the mutli-cookie domain easily.
|
||||
|
||||
## Modern Method: Set the $redirection_url to the Location header of the response to the Authz endpoint.
|
||||
auth_request_set $redirection_url $upstream_http_location;
|
||||
|
||||
## Modern Method: When there is a 401 response code from the authz endpoint redirect to the $redirection_url.
|
||||
error_page 401 =302 $redirection_url;
|
||||
|
||||
## Legacy Method: Set $target_url to the original requested URL.
|
||||
## This requires http_set_misc module, replace 'set_escape_uri' with 'set' if you don't have this module.
|
||||
# set_escape_uri $target_url $scheme://$http_host$request_uri;
|
||||
|
||||
## Legacy Method: When there is a 401 response code from the authz endpoint redirect to the portal with the 'rd'
|
||||
## URL parameter set to $target_url. This requires users update 'auth.example.com/' with their external authelia URL.
|
||||
# error_page 401 =302 https://auth.example.com/?rd=$target_url;
|
||||
```
|
||||
{{< /details >}}
|
||||
|
||||
|
@ -528,12 +576,6 @@ endpoint. It's recommended to use [authelia-authrequest.conf](#authelia-authrequ
|
|||
## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource.
|
||||
auth_request /internal/authelia/authz/basic;
|
||||
|
||||
## Comment this line if you're using nginx without the http_set_misc module.
|
||||
set_escape_uri $target_url $scheme://$http_host$request_uri;
|
||||
|
||||
## Uncomment this line if you're using NGINX without the http_set_misc module.
|
||||
# set $target_url $scheme://$http_host$request_uri;
|
||||
|
||||
## Save the upstream response headers from Authelia to variables.
|
||||
auth_request_set $user $upstream_http_remote_user;
|
||||
auth_request_set $groups $upstream_http_remote_groups;
|
||||
|
@ -580,6 +622,9 @@ location /internal/authelia/authz/detect {
|
|||
return 401;
|
||||
}
|
||||
|
||||
## IMPORTANT: The below URL `https://auth.example.com/` MUST be replaced with the externally accessible URL of the
|
||||
## Authelia Portal/Site.
|
||||
##
|
||||
## The original request didn't target /force-basic, redirect to the pretty login page
|
||||
## This is what `error_page 401 =302 https://auth.example.com/?rd=$target_url;` did.
|
||||
return 302 https://auth.example.com/$is_args$args;
|
||||
|
|
|
@ -44,6 +44,30 @@ how you can configure multiple IP ranges. You should customize this example to f
|
|||
You should only include the specific IP address ranges of the trusted proxies within your architecture and should not
|
||||
trust entire subnets unless that subnet only has trusted proxies and no other services.*
|
||||
|
||||
## Assumptions and Adaptation
|
||||
|
||||
This guide makes a few assumptions. These assumptions may require adaptation in more advanced and complex scenarios. We
|
||||
can not reasonably have examples for every advanced configuration option that exists. The
|
||||
following are the assumptions we make:
|
||||
|
||||
* Deployment Scenario:
|
||||
* Single Host
|
||||
* Authelia is deployed as a Container with the container name `authelia` on port `9091`
|
||||
* Proxy is deployed as a Container on a network shared with Authelia
|
||||
* The above assumption means that AUthelia should be accesible to the proxy on `http://authelia:9091` and as such:
|
||||
* You will have to adapt all instances of the above URL to be `https://` if Authelia configuration has a TLS key and
|
||||
certificate defined
|
||||
* You will have to adapt all instances of `authelia` in the URL if:
|
||||
* you're using a different container name
|
||||
* you deployed the proxy to a different location
|
||||
* You will have to adapt all instances of `9091` in the URL if:
|
||||
* you have adjusted the default port in the configuration
|
||||
* You will have to adapt the entire URL if:
|
||||
* Authelia is on a different host to the proxy
|
||||
* All services are part of the `example.com` domain:
|
||||
* This domain and the subdomains will have to be adapted in all examples to match your specific domains unless you're
|
||||
just testing or you want ot use that specific domain
|
||||
|
||||
## Potential
|
||||
|
||||
Support for [Skipper] should be possible via [Skipper]'s
|
||||
|
|
|
@ -55,13 +55,13 @@ possible that due to web standards this will never change.
|
|||
|
||||
In addition this represents a bad user experience in some instances such as:
|
||||
|
||||
- Users sometimes visit the `https://app.example.com/authelia` URL which doesn't automatically redirect the user to
|
||||
* Users sometimes visit the `https://app.example.com/authelia` URL which doesn't automatically redirect the user to
|
||||
`https://app.example.com` (if they visit `https://app.example.com` then they'll be redirected to authenticate then
|
||||
redirected back to their original URL)
|
||||
- Administrators may wish to setup [OpenID Connect 1.0](../../configuration/identity-providers/open-id-connect.md) in
|
||||
* Administrators may wish to setup [OpenID Connect 1.0](../../configuration/identity-providers/open-id-connect.md) in
|
||||
which case it also doesn't represent a good user experience as the `issuer` will be
|
||||
`https://app.example.com/authelia` for example
|
||||
- Using the [SWAG] default configurations are more difficult to support as our specific familiarity is with our own
|
||||
* Using the [SWAG] default configurations are more difficult to support as our specific familiarity is with our own
|
||||
example snippets
|
||||
|
||||
#### Option 1: Adjusting the Default Configuration
|
||||
|
@ -112,6 +112,30 @@ Especially if you have never read it before.*
|
|||
To configure trusted proxies for [SWAG] see the [NGINX] section on [Trusted Proxies](nginx.md#trusted-proxies).
|
||||
Adapting this to [SWAG] is beyond the scope of this documentation.
|
||||
|
||||
## Assumptions and Adaptation
|
||||
|
||||
This guide makes a few assumptions. These assumptions may require adaptation in more advanced and complex scenarios. We
|
||||
can not reasonably have examples for every advanced configuration option that exists. The
|
||||
following are the assumptions we make:
|
||||
|
||||
* Deployment Scenario:
|
||||
* Single Host
|
||||
* Authelia is deployed as a Container with the container name `authelia` on port `9091`
|
||||
* Proxy is deployed as a Container on a network shared with Authelia
|
||||
* The above assumption means that AUthelia should be accesible to the proxy on `http://authelia:9091` and as such:
|
||||
* You will have to adapt all instances of the above URL to be `https://` if Authelia configuration has a TLS key and
|
||||
certificate defined
|
||||
* You will have to adapt all instances of `authelia` in the URL if:
|
||||
* you're using a different container name
|
||||
* you deployed the proxy to a different location
|
||||
* You will have to adapt all instances of `9091` in the URL if:
|
||||
* you have adjusted the default port in the configuration
|
||||
* You will have to adapt the entire URL if:
|
||||
* Authelia is on a different host to the proxy
|
||||
* All services are part of the `example.com` domain:
|
||||
* This domain and the subdomains will have to be adapted in all examples to match your specific domains unless you're
|
||||
just testing or you want ot use that specific domain
|
||||
|
||||
## Docker Compose
|
||||
|
||||
The following docker compose example has various applications suitable for setting up an example environment.
|
||||
|
|
|
@ -61,6 +61,23 @@ networks to the trusted proxy list in [Traefik]:
|
|||
|
||||
See the [Entry Points](https://doc.traefik.io/traefik/routing/entrypoints) documentation for more information.
|
||||
|
||||
## Implementation
|
||||
|
||||
[Traefik] utilizes the [ForwardAuth](../../reference/guides/proxy-authorization.md#forwardauth) Authz implementation. The
|
||||
associated [Metadata](../../reference/guides/proxy-authorization.md#forwardauth-metadata) should be considered required.
|
||||
|
||||
The examples below assume you are using the default
|
||||
[Authz Endpoints Configuration](../../configuration/miscellaneous/server-endpoints-authz.md) or one similar to the
|
||||
following minimal configuration:
|
||||
|
||||
```yaml
|
||||
server:
|
||||
endpoints:
|
||||
authz:
|
||||
forward-auth:
|
||||
implementation: ForwardAuth
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Below you will find commented examples of the following docker deployment:
|
||||
|
@ -76,6 +93,30 @@ The below configuration looks to provide examples of running [Traefik] 2.x with
|
|||
Please ensure that you also setup the respective [ACME configuration](https://docs.traefik.io/https/acme/) for your
|
||||
[Traefik] setup as this is not covered in the example below.
|
||||
|
||||
## Assumptions and Adaptation
|
||||
|
||||
This guide makes a few assumptions. These assumptions may require adaptation in more advanced and complex scenarios. We
|
||||
can not reasonably have examples for every advanced configuration option that exists. The
|
||||
following are the assumptions we make:
|
||||
|
||||
* Deployment Scenario:
|
||||
* Single Host
|
||||
* Authelia is deployed as a Container with the container name `authelia` on port `9091`
|
||||
* Proxy is deployed as a Container on a network shared with Authelia
|
||||
* The above assumption means that AUthelia should be accesible to the proxy on `http://authelia:9091` and as such:
|
||||
* You will have to adapt all instances of the above URL to be `https://` if Authelia configuration has a TLS key and
|
||||
certificate defined
|
||||
* You will have to adapt all instances of `authelia` in the URL if:
|
||||
* you're using a different container name
|
||||
* you deployed the proxy to a different location
|
||||
* You will have to adapt all instances of `9091` in the URL if:
|
||||
* you have adjusted the default port in the configuration
|
||||
* You will have to adapt the entire URL if:
|
||||
* Authelia is on a different host to the proxy
|
||||
* All services are part of the `example.com` domain:
|
||||
* This domain and the subdomains will have to be adapted in all examples to match your specific domains unless you're
|
||||
just testing or you want ot use that specific domain
|
||||
|
||||
### Docker Compose
|
||||
|
||||
This is an example configuration using [docker compose] labels:
|
||||
|
|
|
@ -50,6 +50,47 @@ networks to the trusted proxy list in [Traefik]:
|
|||
* 192.168.0.0/16
|
||||
* fc00::/7
|
||||
|
||||
## Assumptions and Adaptation
|
||||
|
||||
This guide makes a few assumptions. These assumptions may require adaptation in more advanced and complex scenarios. We
|
||||
can not reasonably have examples for every advanced configuration option that exists. The
|
||||
following are the assumptions we make:
|
||||
|
||||
* Deployment Scenario:
|
||||
* Single Host
|
||||
* Authelia is deployed as a Container with the container name `authelia` on port `9091`
|
||||
* Proxy is deployed as a Container on a network shared with Authelia
|
||||
* The above assumption means that AUthelia should be accesible to the proxy on `http://authelia:9091` and as such:
|
||||
* You will have to adapt all instances of the above URL to be `https://` if Authelia configuration has a TLS key and
|
||||
certificate defined
|
||||
* You will have to adapt all instances of `authelia` in the URL if:
|
||||
* you're using a different container name
|
||||
* you deployed the proxy to a different location
|
||||
* You will have to adapt all instances of `9091` in the URL if:
|
||||
* you have adjusted the default port in the configuration
|
||||
* You will have to adapt the entire URL if:
|
||||
* Authelia is on a different host to the proxy
|
||||
* All services are part of the `example.com` domain:
|
||||
* This domain and the subdomains will have to be adapted in all examples to match your specific domains unless you're
|
||||
just testing or you want ot use that specific domain
|
||||
|
||||
## Implementation
|
||||
|
||||
[Traefik] utilizes the [ForwardAuth](../../reference/guides/proxy-authorization.md#forwardauth) Authz implementation. The
|
||||
associated [Metadata](../../reference/guides/proxy-authorization.md#forwardauth-metadata) should be considered required.
|
||||
|
||||
The examples below assume you are using the default
|
||||
[Authz Endpoints Configuration](../../configuration/miscellaneous/server-endpoints-authz.md) or one similar to the
|
||||
following minimal configuration:
|
||||
|
||||
```yaml
|
||||
server:
|
||||
endpoints:
|
||||
authz:
|
||||
forward-auth:
|
||||
implementation: ForwardAuth
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Below you will find commented examples of the following docker deployment:
|
||||
|
|
|
@ -64,7 +64,7 @@ completely unset.
|
|||
### ForwardAuth
|
||||
|
||||
This is the implementation which supports [Traefik] via the [ForwardAuth Middleware], [Caddy] via the
|
||||
[forward_auth directive], and [Skipper] via the [webhook auth filter].
|
||||
[forward_auth directive], [HAProxy] via the [auth-request lua plugin], and [Skipper] via the [webhook auth filter].
|
||||
|
||||
#### ForwardAuth Metadata
|
||||
|
||||
|
@ -87,7 +87,7 @@ This is the implementation which supports [Traefik] via the [ForwardAuth Middlew
|
|||
|
||||
### ExtAuthz
|
||||
|
||||
This is the implementation which supports [Envoy] via the [ExtAuthz Extension Filter].
|
||||
This is the implementation which supports [Envoy] via the [HTTP ExtAuthz Filter].
|
||||
|
||||
#### ExtAuthz Metadata
|
||||
|
||||
|
@ -110,26 +110,31 @@ This is the implementation which supports [Envoy] via the [ExtAuthz Extension Fi
|
|||
|
||||
### AuthRequest
|
||||
|
||||
This is the implementation which supports [NGINX] via the [auth_request HTTP module] and [HAProxy] via the
|
||||
[auth-request lua plugin].
|
||||
This is the implementation which supports [NGINX] via the [auth_request HTTP module], and can technically support
|
||||
[HAProxy] via the [auth-request lua plugin].
|
||||
|
||||
| Metadata | Source | Key |
|
||||
|:------------:|:--------:|:-------------------:|
|
||||
| Method | [Header] | `X-Original-Method` |
|
||||
| Scheme | [Header] | `X-Original-URL` |
|
||||
| Hostname | [Header] | `X-Original-URL` |
|
||||
| Path | [Header] | `X-Original-URL` |
|
||||
| IP | [Header] | [X-Forwarded-For] |
|
||||
| Authelia URL | _N/A_ | _N/A_ |
|
||||
#### AuthRequest Metadata
|
||||
|
||||
_**Note:** This endpoint does not support automatic redirection. This is because there is no support on NGINX's side to
|
||||
achieve this with `ngx_http_auth_request_module` and the redirection must be performed within the NGINX configuration._
|
||||
| Metadata | Source | Key |
|
||||
|:------------:|:----------------------------:|:-------------------:|
|
||||
| Method | [Header] | `X-Original-Method` |
|
||||
| Scheme | [Header] | `X-Original-URL` |
|
||||
| Hostname | [Header] | `X-Original-URL` |
|
||||
| Path | [Header] | `X-Original-URL` |
|
||||
| IP | [Header] | [X-Forwarded-For] |
|
||||
| Authelia URL | Session Cookie Configuration | `authelia_url` |
|
||||
|
||||
_**Note:** This endpoint does not support automatic redirection. This is because there is no support on [NGINX]'s side
|
||||
to achieve this with `ngx_http_auth_request_module` and the redirection must be performed within the [NGINX]
|
||||
configuration. However we return the appropriate URL to redirect users to with the `Location` header which
|
||||
simplifies this process especially for multi-cookie domain deployments._
|
||||
|
||||
#### AuthRequest Metadata Alternatives
|
||||
|
||||
| Metadata | Alternative Type | Source | Key |
|
||||
|:--------:|:----------------:|:----------:|:---------:|
|
||||
| IP | Fallback | TCP Packet | Source IP |
|
||||
| Metadata | Alternative Type | Source | Key |
|
||||
|:------------:|:----------------:|:--------------:|:--------------:|
|
||||
| IP | Fallback | TCP Packet | Source IP |
|
||||
| Authelia URL | Override | Query Argument | `authelia_url` |
|
||||
|
||||
### Legacy
|
||||
|
||||
|
@ -213,7 +218,7 @@ or the header is malformed it will respond with the [WWW-Authenticate] header.
|
|||
[Skipper]: https://opensource.zalando.com/skipper/
|
||||
[HAProxy]: http://www.haproxy.org/
|
||||
|
||||
[ExtAuthz Extension Filter]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ext_authz/v3/ext_authz.proto#envoy-v3-api-msg-extensions-filters-http-ext-authz-v3-extauthz
|
||||
[HTTP ExtAuthz Filter]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ext_authz/v3/ext_authz.proto#envoy-v3-api-msg-extensions-filters-http-ext-authz-v3-extauthz
|
||||
[auth_request HTTP module]: https://nginx.org/en/docs/http/ngx_http_auth_request_module.html
|
||||
[auth-request lua plugin]: https://github.com/TimWolla/haproxy-auth-request
|
||||
[ForwardAuth Middleware]: https://doc.traefik.io/traefik/middlewares/http/forwardauth/
|
||||
|
|
|
@ -81,6 +81,8 @@ The following functions which mimic the behaviour of helm exist in most templati
|
|||
- indent
|
||||
- nindent
|
||||
- uuidv4
|
||||
- urlquery
|
||||
- urlunquery (opposite of urlquery)
|
||||
|
||||
See the [Helm Documentation](https://helm.sh/docs/chart_template_guide/function_list/) for more information. Please
|
||||
note that only the functions listed above are supported and the functions don't necessarily behave exactly the same.
|
||||
|
|
|
@ -45,7 +45,7 @@ services:
|
|||
- TZ=Australia/Melbourne
|
||||
|
||||
traefik:
|
||||
image: traefik:v2.9.9
|
||||
image: traefik:v2.9.10
|
||||
container_name: traefik
|
||||
volumes:
|
||||
- ./traefik:/etc/traefik
|
||||
|
|
|
@ -32,7 +32,7 @@ services:
|
|||
- TZ=Australia/Melbourne
|
||||
|
||||
traefik:
|
||||
image: traefik:v2.9.9
|
||||
image: traefik:v2.9.10
|
||||
container_name: traefik
|
||||
volumes:
|
||||
- ./traefik:/etc/traefik
|
||||
|
|
34
go.mod
34
go.mod
|
@ -32,23 +32,23 @@ require (
|
|||
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.9.13
|
||||
github.com/ory/x v0.0.545
|
||||
github.com/otiai10/copy v1.9.0
|
||||
github.com/ory/herodot v0.10.0
|
||||
github.com/ory/x v0.0.549
|
||||
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/sirupsen/logrus v1.9.0
|
||||
github.com/spf13/cobra v1.6.1
|
||||
github.com/spf13/cobra v1.7.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.8.2
|
||||
github.com/trustelem/zxcvbn v1.0.1
|
||||
github.com/valyala/fasthttp v1.45.0
|
||||
github.com/wneessen/go-mail v0.3.9
|
||||
golang.org/x/net v0.8.0
|
||||
golang.org/x/net v0.9.0
|
||||
golang.org/x/sync v0.1.0
|
||||
golang.org/x/term v0.6.0
|
||||
golang.org/x/text v0.8.0
|
||||
golang.org/x/term v0.7.0
|
||||
golang.org/x/text v0.9.0
|
||||
gopkg.in/square/go-jose.v2 v2.6.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
@ -74,7 +74,7 @@ require (
|
|||
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
||||
github.com/go-webauthn/revoke v0.1.9 // indirect
|
||||
github.com/golang/glog v1.0.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/go-tpm v0.3.3 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
|
@ -102,11 +102,11 @@ require (
|
|||
github.com/prometheus/procfs v0.8.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.3 // indirect
|
||||
github.com/spf13/afero v1.9.5 // indirect
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/viper v1.14.0 // indirect
|
||||
github.com/subosito/gotenv v1.4.1 // indirect
|
||||
github.com/subosito/gotenv v1.4.2 // indirect
|
||||
github.com/test-go/testify v1.1.4 // indirect
|
||||
github.com/tinylib/msgp v1.1.8 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
|
@ -115,14 +115,14 @@ require (
|
|||
github.com/ysmood/gson v0.7.3 // indirect
|
||||
github.com/ysmood/leakless v0.8.0 // indirect
|
||||
golang.org/x/crypto v0.7.0 // indirect
|
||||
golang.org/x/mod v0.8.0 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect
|
||||
golang.org/x/sys v0.6.0 // indirect
|
||||
golang.org/x/tools v0.6.0 // indirect
|
||||
golang.org/x/mod v0.10.0 // indirect
|
||||
golang.org/x/oauth2 v0.4.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
|
||||
google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71 // indirect
|
||||
google.golang.org/grpc v1.50.1 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd // indirect
|
||||
google.golang.org/grpc v1.54.0 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
|
130
go.sum
130
go.sum
|
@ -50,7 +50,6 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF
|
|||
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/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ=
|
||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||
|
@ -73,11 +72,6 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
|
|||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
|
||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
|
@ -98,8 +92,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/deckarep/golang-set/v2 v2.3.0 h1:qs18EKUfHm2X9fA50Mr/M5hccg2tNnVqsiBImnyDs0g=
|
||||
github.com/deckarep/golang-set/v2 v2.3.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
|
||||
github.com/dgraph-io/ristretto v0.0.1/go.mod h1:T40EBc7CJke8TkpiYfGGKAeFjSaxuFXhuXRyumBd6RE=
|
||||
github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
||||
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
|
||||
github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
|
@ -121,8 +113,6 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
|
|||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A=
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
|
||||
|
@ -133,7 +123,6 @@ github.com/fasthttp/session/v2 v2.4.17/go.mod h1:+pr8HLEQp6h9X70KLBY/Y4NrdJR2ts7
|
|||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88=
|
||||
|
@ -205,8 +194,9 @@ 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 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
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=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
|
@ -219,7 +209,6 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-tpm v0.1.2-0.20190725015402-ae6dd98980d4/go.mod h1:H9HbmUG2YgV/PHITkO7p6wxEEj/v5nlsVWIwumwH2NI=
|
||||
github.com/google/go-tpm v0.3.0/go.mod h1:iVLWvrPp/bHeEkxTFi9WG6K9w0iy2yIszHwZGHPbzAw=
|
||||
|
@ -243,21 +232,18 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe
|
|||
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
|
@ -271,7 +257,6 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T
|
|||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
|
@ -292,7 +277,6 @@ github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/
|
|||
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/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
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=
|
||||
|
@ -318,7 +302,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv
|
|||
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.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
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=
|
||||
|
@ -326,7 +309,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
|||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
|
@ -343,7 +325,6 @@ github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa1
|
|||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
|
@ -364,34 +345,25 @@ github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
|||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||
github.com/ory/fosite v0.44.0 h1:Z3UjyO11/wlIoa3BotOqcTkfm7kUNA8F7dd8mOMfx0o=
|
||||
github.com/ory/fosite v0.44.0/go.mod h1:o/G4kAeNn65l6MCod2+KmFfU6JQBSojS7eXys6lKGzM=
|
||||
github.com/ory/go-acc v0.2.6/go.mod h1:4Kb/UnPcT8qRAk3IAxta+hvVapdxTLWtrr7bFLlEgpw=
|
||||
github.com/ory/go-acc v0.2.9-0.20230103102148-6b1c9a70dbbe h1:rvu4obdvqR0fkSIJ8IfgzKOWwZ5kOT2UNfLq81Qk7rc=
|
||||
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.9.13 h1:cN/Z4eOkErl/9W7hDIDLb79IO/bfsH+8yscBjRpB4IU=
|
||||
github.com/ory/herodot v0.9.13/go.mod h1:IWDs9kSvFQqw/cQ8zi5ksyYvITiUU4dI7glUrhZcJYo=
|
||||
github.com/ory/viper v1.7.5/go.mod h1:ypOuyJmEUb3oENywQZRgeAMwqgOyDqwboO1tj3DjTaM=
|
||||
github.com/ory/x v0.0.545 h1:B2zw7LrQwtdzbaRo0nz4EvDukH7A2UK+IdeYQF2iXBw=
|
||||
github.com/ory/x v0.0.545/go.mod h1:x0n1bElGPQeONaKO++izk4CIOhiDhan+i1MUygjrlfM=
|
||||
github.com/otiai10/copy v1.9.0 h1:7KFNiCgZ91Ru4qW4CWPf/7jqtxLagGRmIxWldPP9VY4=
|
||||
github.com/otiai10/copy v1.9.0/go.mod h1:hsfX19wcn0UWIHUQ3/4fHuehhk2UyArQ9dVFAn3FczI=
|
||||
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
|
||||
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
|
||||
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
|
||||
github.com/otiai10/mint v1.4.0 h1:umwcf7gbpEwf7WFzqmWwSv0CzbeMsae2u9ZvpP8j2q4=
|
||||
github.com/otiai10/mint v1.4.0/go.mod h1:gifjb2MYOoULtKLqUAEILUG/9KONW6f7YsJ6vQLTlFI=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/ory/herodot v0.10.0 h1:j4wDWezsHtZNTSWyXt0sVeQS3QUDCzpVWJMQx1A5Kmg=
|
||||
github.com/ory/herodot v0.10.0/go.mod h1:MMNmY6MG1uB6fnXYFaHoqdV23DTWctlPsmRCeq/2+wc=
|
||||
github.com/ory/x v0.0.549 h1:/ngQEYmHMEQAsYxK4uasAR9/WALxRLfHiDUPFQrD6/I=
|
||||
github.com/ory/x v0.0.549/go.mod h1:00UrEq/wEgXxpagcfjn5w2PsJPpfxAVnb94M+eg1bC0=
|
||||
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=
|
||||
github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
|
||||
github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs=
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
|
||||
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
|
||||
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc=
|
||||
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||
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=
|
||||
|
@ -435,7 +407,6 @@ github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5
|
|||
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
|
@ -450,22 +421,18 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
|
|||
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/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk=
|
||||
github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
|
||||
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
|
||||
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
|
||||
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
|
||||
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
|
||||
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
|
||||
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
|
@ -491,9 +458,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
|||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
|
||||
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
||||
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
|
||||
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
||||
github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE=
|
||||
github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU=
|
||||
github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
|
||||
|
@ -528,7 +494,6 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
|||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
|
@ -537,7 +502,6 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
|||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
|
@ -547,12 +511,11 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
|||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
|
@ -591,8 +554,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
|
||||
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -630,14 +593,13 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v
|
|||
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-20210805182204-aaa1db679c0d/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.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
|
@ -649,8 +611,8 @@ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ
|
|||
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.0.0-20221014153046-6fdb5e3db783 h1:nt+Q6cXKz4MosCSpnbMtqiQ8Oz0pxTef2B4Vca2lvfk=
|
||||
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
|
||||
golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M=
|
||||
golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -680,13 +642,11 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/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-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -697,7 +657,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-20200602225109-6fdc65e7d980/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=
|
||||
|
@ -716,7 +675,6 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
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-20210809222454-d867a43fc93e/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=
|
||||
|
@ -725,25 +683,24 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
|
||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
@ -754,7 +711,6 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3
|
|||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
|
@ -798,11 +754,10 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
|
|||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
|
||||
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
|
||||
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
@ -857,13 +812,11 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG
|
|||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
|
@ -872,9 +825,8 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D
|
|||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20211020151524-b7c3a969101a/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71 h1:GEgb2jF5zxsFJpJfg9RoDDWm7tiwc/DDSTE2BtLUkXU=
|
||||
google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s=
|
||||
google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd h1:sLpv7bNL1AsX3fdnWh9WVh7ejIzXdOc1RRHGeAmeStU=
|
||||
google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
|
@ -889,17 +841,12 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji
|
|||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
||||
google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY=
|
||||
google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
|
||||
google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag=
|
||||
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
|
||||
google.golang.org/grpc/examples v0.0.0-20210304020650-930c79186c99 h1:qA8rMbz1wQ4DOFfM2ouD29DG9aHWBm6ZOy9BGxiUMmY=
|
||||
google.golang.org/grpc/examples v0.0.0-20210304020650-930c79186c99/go.mod h1:Ly7ZA/ARzg8fnPU9TyZIxoz33sEUuWX7txiqs8lPTgE=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
|
@ -912,17 +859,14 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
|
|||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
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.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
|
@ -932,10 +876,8 @@ 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.3/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.2.8/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=
|
||||
|
|
|
@ -28,7 +28,9 @@ var (
|
|||
const (
|
||||
errFmtSecretAlreadyDefined = "secrets: error loading secret into key '%s': it's already defined in other " +
|
||||
"configuration sources"
|
||||
errFmtSecretIOIssue = "secrets: error loading secret path %s into key '%s': %v"
|
||||
errFmtSecretOSError = "secrets: error loading secret path %s into key '%s': %w"
|
||||
errFmtSecretOSPermission = "secrets: error loading secret path %s into key '%s': file permission error occurred: %w"
|
||||
errFmtSecretOSNotExist = "secrets: error loading secret path %s into key '%s': file does not exist error occurred: %w"
|
||||
errFmtGenerateConfiguration = "error occurred generating configuration: %+v"
|
||||
|
||||
errFmtDecodeHookCouldNotParse = "could not decode '%s' to a %s%s: %w"
|
||||
|
|
|
@ -59,7 +59,7 @@ var deprecations = map[string]Deprecation{
|
|||
},
|
||||
"host": {
|
||||
Version: model.SemanticVersion{Major: 4, Minor: 30},
|
||||
Key: "logs_file",
|
||||
Key: "host",
|
||||
NewKey: "server.host",
|
||||
AutoMap: true,
|
||||
MapFunc: nil,
|
||||
|
|
|
@ -2,6 +2,7 @@ package configuration
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
@ -40,13 +41,25 @@ func koanfEnvironmentSecretsCallback(keyMap map[string]string, validator *schema
|
|||
return "", nil
|
||||
}
|
||||
|
||||
v, err := loadSecret(value)
|
||||
if err != nil {
|
||||
validator.Push(fmt.Errorf(errFmtSecretIOIssue, value, k, err))
|
||||
return k, ""
|
||||
}
|
||||
switch v, err := loadSecret(value); err {
|
||||
case nil:
|
||||
return k, v
|
||||
default:
|
||||
switch {
|
||||
case os.IsNotExist(err):
|
||||
validator.Push(fmt.Errorf(errFmtSecretOSNotExist, value, k, err))
|
||||
|
||||
return k, v
|
||||
return "", nil
|
||||
case os.IsPermission(err):
|
||||
validator.Push(fmt.Errorf(errFmtSecretOSPermission, value, k, err))
|
||||
|
||||
return "", nil
|
||||
default:
|
||||
validator.Push(fmt.Errorf(errFmtSecretOSError, value, k, err))
|
||||
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -117,10 +117,10 @@ func TestKoanfSecretCallbackShouldErrorOnFSError(t *testing.T) {
|
|||
callback := koanfEnvironmentSecretsCallback(keyMap, val)
|
||||
|
||||
key, value := callback("AUTHELIA_THEME", secret)
|
||||
assert.Equal(t, "theme", key)
|
||||
assert.Equal(t, "", value)
|
||||
assert.Equal(t, "", key)
|
||||
assert.Equal(t, nil, value)
|
||||
|
||||
require.Len(t, val.Errors(), 1)
|
||||
assert.Len(t, val.Warnings(), 0)
|
||||
assert.EqualError(t, val.Errors()[0], fmt.Sprintf(errFmtSecretIOIssue, secret, "theme", fmt.Sprintf("open %s: permission denied", secret)))
|
||||
assert.EqualError(t, val.Errors()[0], fmt.Sprintf("secrets: error loading secret path %s into key 'theme': file permission error occurred: open %s: permission denied", secret, secret))
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ func TestShouldErrorSecretNotExist(t *testing.T) {
|
|||
testSetEnv(t, "JWT_SECRET_FILE", filepath.Join(dir, "jwt"))
|
||||
testSetEnv(t, "DUO_API_SECRET_KEY_FILE", filepath.Join(dir, "duo"))
|
||||
testSetEnv(t, "SESSION_SECRET_FILE", filepath.Join(dir, "session"))
|
||||
testSetEnv(t, "AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE", filepath.Join(dir, "authentication"))
|
||||
testSetEnv(t, "AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE", dir)
|
||||
testSetEnv(t, "NOTIFIER_SMTP_PASSWORD_FILE", filepath.Join(dir, "notifier"))
|
||||
testSetEnv(t, "SESSION_REDIS_PASSWORD_FILE", filepath.Join(dir, "redis"))
|
||||
testSetEnv(t, "SESSION_REDIS_HIGH_AVAILABILITY_SENTINEL_PASSWORD_FILE", filepath.Join(dir, "redis-sentinel"))
|
||||
|
@ -44,20 +44,21 @@ func TestShouldErrorSecretNotExist(t *testing.T) {
|
|||
sort.Sort(utils.ErrSliceSortAlphabetical(errs))
|
||||
|
||||
errFmt := utils.GetExpectedErrTxt("filenotfound")
|
||||
errFmtDir := utils.GetExpectedErrTxt("isdir")
|
||||
|
||||
// ignore the errors before this as they are checked by the validator.
|
||||
assert.EqualError(t, errs[0], fmt.Sprintf(errFmtSecretIOIssue, filepath.Join(dir, "authentication"), "authentication_backend.ldap.password", fmt.Sprintf(errFmt, filepath.Join(dir, "authentication"))))
|
||||
assert.EqualError(t, errs[1], fmt.Sprintf(errFmtSecretIOIssue, filepath.Join(dir, "duo"), "duo_api.secret_key", fmt.Sprintf(errFmt, filepath.Join(dir, "duo"))))
|
||||
assert.EqualError(t, errs[2], fmt.Sprintf(errFmtSecretIOIssue, filepath.Join(dir, "jwt"), "jwt_secret", fmt.Sprintf(errFmt, filepath.Join(dir, "jwt"))))
|
||||
assert.EqualError(t, errs[3], fmt.Sprintf(errFmtSecretIOIssue, filepath.Join(dir, "mysql"), "storage.mysql.password", fmt.Sprintf(errFmt, filepath.Join(dir, "mysql"))))
|
||||
assert.EqualError(t, errs[4], fmt.Sprintf(errFmtSecretIOIssue, filepath.Join(dir, "notifier"), "notifier.smtp.password", fmt.Sprintf(errFmt, filepath.Join(dir, "notifier"))))
|
||||
assert.EqualError(t, errs[5], fmt.Sprintf(errFmtSecretIOIssue, filepath.Join(dir, "oidc-hmac"), "identity_providers.oidc.hmac_secret", fmt.Sprintf(errFmt, filepath.Join(dir, "oidc-hmac"))))
|
||||
assert.EqualError(t, errs[6], fmt.Sprintf(errFmtSecretIOIssue, filepath.Join(dir, "oidc-key"), "identity_providers.oidc.issuer_private_key", fmt.Sprintf(errFmt, filepath.Join(dir, "oidc-key"))))
|
||||
assert.EqualError(t, errs[7], fmt.Sprintf(errFmtSecretIOIssue, filepath.Join(dir, "postgres"), "storage.postgres.password", fmt.Sprintf(errFmt, filepath.Join(dir, "postgres"))))
|
||||
assert.EqualError(t, errs[8], fmt.Sprintf(errFmtSecretIOIssue, filepath.Join(dir, "redis"), "session.redis.password", fmt.Sprintf(errFmt, filepath.Join(dir, "redis"))))
|
||||
assert.EqualError(t, errs[9], fmt.Sprintf(errFmtSecretIOIssue, filepath.Join(dir, "redis-sentinel"), "session.redis.high_availability.sentinel_password", fmt.Sprintf(errFmt, filepath.Join(dir, "redis-sentinel"))))
|
||||
assert.EqualError(t, errs[10], fmt.Sprintf(errFmtSecretIOIssue, filepath.Join(dir, "session"), "session.secret", fmt.Sprintf(errFmt, filepath.Join(dir, "session"))))
|
||||
assert.EqualError(t, errs[11], fmt.Sprintf(errFmtSecretIOIssue, filepath.Join(dir, "tls"), "server.tls.key", fmt.Sprintf(errFmt, filepath.Join(dir, "tls"))))
|
||||
assert.EqualError(t, errs[0], fmt.Sprintf("secrets: error loading secret path %s into key 'authentication_backend.ldap.password': %s", dir, fmt.Sprintf(errFmtDir, dir)))
|
||||
assert.EqualError(t, errs[1], fmt.Sprintf("secrets: error loading secret path %s into key 'duo_api.secret_key': file does not exist error occurred: %s", filepath.Join(dir, "duo"), fmt.Sprintf(errFmt, filepath.Join(dir, "duo"))))
|
||||
assert.EqualError(t, errs[2], fmt.Sprintf("secrets: error loading secret path %s into key 'jwt_secret': file does not exist error occurred: %s", filepath.Join(dir, "jwt"), fmt.Sprintf(errFmt, filepath.Join(dir, "jwt"))))
|
||||
assert.EqualError(t, errs[3], fmt.Sprintf("secrets: error loading secret path %s into key 'storage.mysql.password': file does not exist error occurred: %s", filepath.Join(dir, "mysql"), fmt.Sprintf(errFmt, filepath.Join(dir, "mysql"))))
|
||||
assert.EqualError(t, errs[4], fmt.Sprintf("secrets: error loading secret path %s into key 'notifier.smtp.password': file does not exist error occurred: %s", filepath.Join(dir, "notifier"), fmt.Sprintf(errFmt, filepath.Join(dir, "notifier"))))
|
||||
assert.EqualError(t, errs[5], fmt.Sprintf("secrets: error loading secret path %s into key 'identity_providers.oidc.hmac_secret': file does not exist error occurred: %s", filepath.Join(dir, "oidc-hmac"), fmt.Sprintf(errFmt, filepath.Join(dir, "oidc-hmac"))))
|
||||
assert.EqualError(t, errs[6], fmt.Sprintf("secrets: error loading secret path %s into key 'identity_providers.oidc.issuer_private_key': file does not exist error occurred: %s", filepath.Join(dir, "oidc-key"), fmt.Sprintf(errFmt, filepath.Join(dir, "oidc-key"))))
|
||||
assert.EqualError(t, errs[7], fmt.Sprintf("secrets: error loading secret path %s into key 'storage.postgres.password': file does not exist error occurred: %s", filepath.Join(dir, "postgres"), fmt.Sprintf(errFmt, filepath.Join(dir, "postgres"))))
|
||||
assert.EqualError(t, errs[8], fmt.Sprintf("secrets: error loading secret path %s into key 'session.redis.password': file does not exist error occurred: %s", filepath.Join(dir, "redis"), fmt.Sprintf(errFmt, filepath.Join(dir, "redis"))))
|
||||
assert.EqualError(t, errs[9], fmt.Sprintf("secrets: error loading secret path %s into key 'session.redis.high_availability.sentinel_password': file does not exist error occurred: %s", filepath.Join(dir, "redis-sentinel"), fmt.Sprintf(errFmt, filepath.Join(dir, "redis-sentinel"))))
|
||||
assert.EqualError(t, errs[10], fmt.Sprintf("secrets: error loading secret path %s into key 'session.secret': file does not exist error occurred: %s", filepath.Join(dir, "session"), fmt.Sprintf(errFmt, filepath.Join(dir, "session"))))
|
||||
assert.EqualError(t, errs[11], fmt.Sprintf("secrets: error loading secret path %s into key 'server.tls.key': file does not exist error occurred: %s", filepath.Join(dir, "tls"), fmt.Sprintf(errFmt, filepath.Join(dir, "tls"))))
|
||||
}
|
||||
|
||||
func TestLoadShouldReturnErrWithoutValidator(t *testing.T) {
|
||||
|
|
|
@ -142,11 +142,12 @@ const (
|
|||
const (
|
||||
errFmtOIDCNoClientsConfigured = "identity_providers: oidc: option 'clients' must have one or " +
|
||||
"more clients configured"
|
||||
errFmtOIDCNoPrivateKey = "identity_providers: oidc: option 'issuer_private_key' is required"
|
||||
errFmtOIDCInvalidPrivateKeyBitSize = "identity_providers: oidc: option 'issuer_private_key' must be an RSA private key with %d bits or more but it only has %d bits"
|
||||
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', " +
|
||||
errFmtOIDCNoPrivateKey = "identity_providers: oidc: option 'issuer_private_key' is required"
|
||||
errFmtOIDCInvalidPrivateKeyBitSize = "identity_providers: oidc: option 'issuer_private_key' must be an RSA private key with %d bits or more but it only has %d bits"
|
||||
errFmtOIDCInvalidPrivateKeyMalformedMissingPublicKey = "identity_providers: oidc: option 'issuer_private_key' must be a valid RSA private key but the provided data is missing the public key bits"
|
||||
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'"
|
||||
|
||||
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"
|
||||
|
|
|
@ -37,7 +37,9 @@ func validateOIDC(config *schema.OpenIDConnectConfiguration, val *schema.StructV
|
|||
}
|
||||
}
|
||||
|
||||
if config.IssuerPrivateKey.Size()*8 < 2048 {
|
||||
if config.IssuerPrivateKey.PublicKey.N == nil {
|
||||
val.Push(fmt.Errorf(errFmtOIDCInvalidPrivateKeyMalformedMissingPublicKey))
|
||||
} else if config.IssuerPrivateKey.Size()*8 < 2048 {
|
||||
val.Push(fmt.Errorf(errFmtOIDCInvalidPrivateKeyBitSize, 2048, config.IssuerPrivateKey.Size()*8))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -525,6 +525,35 @@ func TestShouldRaiseErrorOnKeySizeTooSmall(t *testing.T) {
|
|||
assert.EqualError(t, validator.Errors()[0], "identity_providers: oidc: option 'issuer_private_key' must be an RSA private key with 2048 bits or more but it only has 1024 bits")
|
||||
}
|
||||
|
||||
func TestShouldRaiseErrorOnKeyInvalidPublicKey(t *testing.T) {
|
||||
validator := schema.NewStructValidator()
|
||||
config := &schema.IdentityProvidersConfiguration{
|
||||
OIDC: &schema.OpenIDConnectConfiguration{
|
||||
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
||||
IssuerPrivateKey: MustParseRSAPrivateKey(testKey3),
|
||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||
{
|
||||
ID: "good_id",
|
||||
Secret: MustDecodeSecret(goodOpenIDConnectClientSecret),
|
||||
Policy: "two_factor",
|
||||
RedirectURIs: []string{
|
||||
"https://google.com/callback",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
config.OIDC.IssuerPrivateKey.PublicKey.N = nil
|
||||
|
||||
ValidateIdentityProviders(config, validator)
|
||||
|
||||
assert.Len(t, validator.Warnings(), 0)
|
||||
require.Len(t, validator.Errors(), 1)
|
||||
|
||||
assert.EqualError(t, validator.Errors()[0], "identity_providers: oidc: option 'issuer_private_key' must be a valid RSA private key but the provided data is missing the public key bits")
|
||||
}
|
||||
|
||||
func TestShouldRaiseErrorWhenOIDCClientConfiguredWithBadResponseModes(t *testing.T) {
|
||||
validator := schema.NewStructValidator()
|
||||
config := &schema.IdentityProvidersConfiguration{
|
||||
|
|
|
@ -15,12 +15,22 @@ const (
|
|||
ActionResetPassword = "ResetPassword"
|
||||
)
|
||||
|
||||
const (
|
||||
anonymous = "<anonymous>"
|
||||
)
|
||||
|
||||
var (
|
||||
headerAuthorization = []byte(fasthttp.HeaderAuthorization)
|
||||
headerWWWAuthenticate = []byte(fasthttp.HeaderWWWAuthenticate)
|
||||
|
||||
headerProxyAuthorization = []byte(fasthttp.HeaderProxyAuthorization)
|
||||
headerProxyAuthenticate = []byte(fasthttp.HeaderProxyAuthenticate)
|
||||
|
||||
headerSessionUsername = []byte("Session-Username")
|
||||
headerRemoteUser = []byte("Remote-User")
|
||||
headerRemoteGroups = []byte("Remote-Groups")
|
||||
headerRemoteName = []byte("Remote-Name")
|
||||
headerRemoteEmail = []byte("Remote-Email")
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -31,14 +41,6 @@ var (
|
|||
headerValueAuthenticateBasic = []byte(`Basic realm="Authorization Required"`)
|
||||
)
|
||||
|
||||
var (
|
||||
headerSessionUsername = []byte("Session-Username")
|
||||
headerRemoteUser = []byte("Remote-User")
|
||||
headerRemoteGroups = []byte("Remote-Groups")
|
||||
headerRemoteName = []byte("Remote-Name")
|
||||
headerRemoteEmail = []byte("Remote-Email")
|
||||
)
|
||||
|
||||
const (
|
||||
queryArgRD = "rd"
|
||||
queryArgRM = "rm"
|
||||
|
@ -88,6 +90,8 @@ const (
|
|||
)
|
||||
|
||||
const (
|
||||
logFmtAuthzRedirect = "Access to %s (method %s) is not authorized to user %s, responding with status code %d with location redirect to %s"
|
||||
|
||||
logFmtAuthorizationPrefix = "Authorization Request with id '%s' on client with id '%s' "
|
||||
|
||||
logFmtErrConsentCantDetermineConsentMode = logFmtAuthorizationPrefix + "could not be processed: error occurred generating consent: client consent mode could not be reliably determined"
|
||||
|
|
|
@ -21,9 +21,9 @@ func (authz *Authz) Handler(ctx *middlewares.AutheliaCtx) {
|
|||
)
|
||||
|
||||
if object, err = authz.handleGetObject(ctx); err != nil {
|
||||
ctx.Logger.Errorf("Error getting original request object: %v", err)
|
||||
ctx.Logger.WithError(err).Error("Error getting Target URL and Request Method")
|
||||
|
||||
ctx.ReplyUnauthorized()
|
||||
ctx.ReplyStatusCode(authz.config.StatusCodeBadRequest)
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -31,23 +31,23 @@ func (authz *Authz) Handler(ctx *middlewares.AutheliaCtx) {
|
|||
if !utils.IsURISecure(object.URL) {
|
||||
ctx.Logger.Errorf("Target URL '%s' has an insecure scheme '%s', only the 'https' and 'wss' schemes are supported so session cookies can be transmitted securely", object.URL.String(), object.URL.Scheme)
|
||||
|
||||
ctx.ReplyUnauthorized()
|
||||
ctx.ReplyStatusCode(authz.config.StatusCodeBadRequest)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if provider, err = ctx.GetSessionProviderByTargetURL(object.URL); err != nil {
|
||||
ctx.Logger.WithError(err).Errorf("Target URL '%s' does not appear to be configured as a session domain", object.URL.String())
|
||||
ctx.Logger.WithError(err).WithField("target_url", object.URL.String()).Error("Target URL does not appear to have a relevant session cookies configuration")
|
||||
|
||||
ctx.ReplyUnauthorized()
|
||||
ctx.ReplyStatusCode(authz.config.StatusCodeBadRequest)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if autheliaURL, err = authz.getAutheliaURL(ctx, provider); err != nil {
|
||||
ctx.Logger.WithError(err).Error("Error occurred trying to determine the URL of the portal")
|
||||
ctx.Logger.WithError(err).WithField("target_url", object.URL.String()).Error("Error occurred trying to determine the external Authelia URL for Target URL")
|
||||
|
||||
ctx.ReplyUnauthorized()
|
||||
ctx.ReplyStatusCode(authz.config.StatusCodeBadRequest)
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -104,23 +104,23 @@ func (authz *Authz) Handler(ctx *middlewares.AutheliaCtx) {
|
|||
}
|
||||
|
||||
func (authz *Authz) getAutheliaURL(ctx *middlewares.AutheliaCtx, provider *session.Session) (autheliaURL *url.URL, err error) {
|
||||
if authz.handleGetAutheliaURL == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if autheliaURL, err = authz.handleGetAutheliaURL(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if autheliaURL != nil || authz.legacy {
|
||||
switch {
|
||||
case authz.implementation == AuthzImplLegacy:
|
||||
return autheliaURL, nil
|
||||
case autheliaURL != nil:
|
||||
switch {
|
||||
case utils.HasURIDomainSuffix(autheliaURL, provider.Config.Domain):
|
||||
return autheliaURL, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("authelia url '%s' is not valid for detected domain '%s' as the url does not have the domain as a suffix", autheliaURL.String(), provider.Config.Domain)
|
||||
}
|
||||
}
|
||||
|
||||
if provider.Config.AutheliaURL != nil {
|
||||
if authz.legacy {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return provider.Config.AutheliaURL, nil
|
||||
}
|
||||
|
||||
|
@ -134,6 +134,10 @@ func (authz *Authz) getRedirectionURL(object *authorization.Object, autheliaURL
|
|||
|
||||
redirectionURL, _ = url.ParseRequestURI(autheliaURL.String())
|
||||
|
||||
if redirectionURL.Path == "" {
|
||||
redirectionURL.Path = "/"
|
||||
}
|
||||
|
||||
qry := redirectionURL.Query()
|
||||
|
||||
qry.Set(queryArgRD, object.URL.String())
|
||||
|
@ -151,10 +155,10 @@ func (authz *Authz) authn(ctx *middlewares.AutheliaCtx, provider *session.Sessio
|
|||
for _, strategy = range authz.strategies {
|
||||
if authn, err = strategy.Get(ctx, provider); err != nil {
|
||||
if strategy.CanHandleUnauthorized() {
|
||||
return Authn{Type: authn.Type, Level: authentication.NotAuthenticated}, strategy, err
|
||||
return Authn{Type: authn.Type, Level: authentication.NotAuthenticated, Username: anonymous}, strategy, err
|
||||
}
|
||||
|
||||
return Authn{Type: authn.Type, Level: authentication.NotAuthenticated}, nil, err
|
||||
return Authn{Type: authn.Type, Level: authentication.NotAuthenticated, Username: anonymous}, nil, err
|
||||
}
|
||||
|
||||
if authn.Level != authentication.NotAuthenticated {
|
||||
|
|
|
@ -81,13 +81,14 @@ type CookieSessionAuthnStrategy struct {
|
|||
|
||||
// Get returns the Authn information for this AuthnStrategy.
|
||||
func (s *CookieSessionAuthnStrategy) Get(ctx *middlewares.AutheliaCtx, provider *session.Session) (authn Authn, err error) {
|
||||
authn = Authn{
|
||||
Type: AuthnTypeCookie,
|
||||
Level: authentication.NotAuthenticated,
|
||||
}
|
||||
|
||||
var userSession session.UserSession
|
||||
|
||||
authn = Authn{
|
||||
Type: AuthnTypeCookie,
|
||||
Level: authentication.NotAuthenticated,
|
||||
Username: anonymous,
|
||||
}
|
||||
|
||||
if userSession, err = provider.GetSession(ctx.RequestCtx); err != nil {
|
||||
return authn, fmt.Errorf("failed to retrieve user session: %w", err)
|
||||
}
|
||||
|
@ -108,21 +109,21 @@ func (s *CookieSessionAuthnStrategy) Get(ctx *middlewares.AutheliaCtx, provider
|
|||
|
||||
if invalid := handleVerifyGETAuthnCookieValidate(ctx, provider, &userSession, s.refreshEnabled, s.refreshInterval); invalid {
|
||||
if err = ctx.DestroySession(); err != nil {
|
||||
ctx.Logger.Errorf("Unable to destroy user session: %+v", err)
|
||||
ctx.Logger.WithError(err).Errorf("Unable to destroy user session")
|
||||
}
|
||||
|
||||
userSession = provider.NewDefaultUserSession()
|
||||
userSession.LastActivity = ctx.Clock.Now().Unix()
|
||||
|
||||
if err = provider.SaveSession(ctx.RequestCtx, userSession); err != nil {
|
||||
ctx.Logger.Errorf("Unable to save updated user session: %+v", err)
|
||||
ctx.Logger.WithError(err).Error("Unable to save updated user session")
|
||||
}
|
||||
|
||||
return authn, nil
|
||||
}
|
||||
|
||||
if err = provider.SaveSession(ctx.RequestCtx, userSession); err != nil {
|
||||
ctx.Logger.Errorf("Unable to save updated user session: %+v", err)
|
||||
ctx.Logger.WithError(err).Error("Unable to save updated user session")
|
||||
}
|
||||
|
||||
return Authn{
|
||||
|
@ -164,8 +165,9 @@ func (s *HeaderAuthnStrategy) Get(ctx *middlewares.AutheliaCtx, _ *session.Sessi
|
|||
)
|
||||
|
||||
authn = Authn{
|
||||
Type: s.authn,
|
||||
Level: authentication.NotAuthenticated,
|
||||
Type: s.authn,
|
||||
Level: authentication.NotAuthenticated,
|
||||
Username: anonymous,
|
||||
}
|
||||
|
||||
if value = ctx.Request.Header.PeekBytes(s.headerAuthorize); value == nil {
|
||||
|
@ -195,7 +197,7 @@ func (s *HeaderAuthnStrategy) Get(ctx *middlewares.AutheliaCtx, _ *session.Sessi
|
|||
|
||||
if details, err = ctx.Providers.UserProvider.GetDetails(username); err != nil {
|
||||
if errors.Is(err, authentication.ErrUserNotFound) {
|
||||
ctx.Logger.Errorf("Error occurred while attempting to get user details for user '%s': the user was not found indicating they were deleted, disabled, or otherwise no longer authorized to login", username)
|
||||
ctx.Logger.WithField("username", username).Error("Error occurred while attempting to get user details for user: the user was not found indicating they were deleted, disabled, or otherwise no longer authorized to login")
|
||||
|
||||
return authn, err
|
||||
}
|
||||
|
@ -237,7 +239,8 @@ func (s *HeaderLegacyAuthnStrategy) Get(ctx *middlewares.AutheliaCtx, _ *session
|
|||
)
|
||||
|
||||
authn = Authn{
|
||||
Level: authentication.NotAuthenticated,
|
||||
Level: authentication.NotAuthenticated,
|
||||
Username: anonymous,
|
||||
}
|
||||
|
||||
if qryValueAuth := ctx.QueryArgs().PeekBytes(qryArgAuth); bytes.Equal(qryValueAuth, qryValueBasic) {
|
||||
|
@ -280,7 +283,7 @@ func (s *HeaderLegacyAuthnStrategy) Get(ctx *middlewares.AutheliaCtx, _ *session
|
|||
|
||||
if details, err = ctx.Providers.UserProvider.GetDetails(username); err != nil {
|
||||
if errors.Is(err, authentication.ErrUserNotFound) {
|
||||
ctx.Logger.Errorf("Error occurred while attempting to get user details for user '%s': the user was not found indicating they were deleted, disabled, or otherwise no longer authorized to login", username)
|
||||
ctx.Logger.WithField("username", username).Error("Error occurred while attempting to get user details for user: the user was not found indicating they were deleted, disabled, or otherwise no longer authorized to login")
|
||||
|
||||
return authn, err
|
||||
}
|
||||
|
@ -309,13 +312,13 @@ func handleVerifyGETAuthnCookieValidate(ctx *middlewares.AutheliaCtx, provider *
|
|||
isAnonymous := userSession.Username == ""
|
||||
|
||||
if isAnonymous && userSession.AuthenticationLevel != authentication.NotAuthenticated {
|
||||
ctx.Logger.Errorf("Session for anonymous user has an authentication level of '%s': this may be a sign of a compromise", userSession.AuthenticationLevel)
|
||||
ctx.Logger.WithFields(map[string]any{"username": anonymous, "level": userSession.AuthenticationLevel.String()}).Errorf("Session for user has an invalid authentication level: this may be a sign of a compromise")
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if invalid = handleVerifyGETAuthnCookieValidateInactivity(ctx, provider, userSession, isAnonymous); invalid {
|
||||
ctx.Logger.Infof("Session for user '%s' not marked as remembereded has exceeded configured session inactivity", userSession.Username)
|
||||
ctx.Logger.WithField("username", userSession.Username).Info("Session for user not marked as remembered has exceeded configured session inactivity")
|
||||
|
||||
return true
|
||||
}
|
||||
|
@ -325,7 +328,7 @@ func handleVerifyGETAuthnCookieValidate(ctx *middlewares.AutheliaCtx, provider *
|
|||
}
|
||||
|
||||
if username := ctx.Request.Header.PeekBytes(headerSessionUsername); username != nil && !strings.EqualFold(string(username), userSession.Username) {
|
||||
ctx.Logger.Warnf("Session for user '%s' does not match the Session-Username header with value '%s' which could be a sign of a cookie hijack", userSession.Username, username)
|
||||
ctx.Logger.WithField("username", userSession.Username).Warnf("Session for user does not match the Session-Username header with value '%s' which could be a sign of a cookie hijack", username)
|
||||
|
||||
return true
|
||||
}
|
||||
|
@ -342,7 +345,7 @@ func handleVerifyGETAuthnCookieValidateInactivity(ctx *middlewares.AutheliaCtx,
|
|||
return false
|
||||
}
|
||||
|
||||
ctx.Logger.Tracef("Inactivity report for user '%s'. Current Time: %d, Last Activity: %d, Maximum Inactivity: %d.", userSession.Username, ctx.Clock.Now().Unix(), userSession.LastActivity, int(provider.Config.Inactivity.Seconds()))
|
||||
ctx.Logger.WithField("username", userSession.Username).Tracef("Inactivity report for user. Current Time: %d, Last Activity: %d, Maximum Inactivity: %d.", ctx.Clock.Now().Unix(), userSession.LastActivity, int(provider.Config.Inactivity.Seconds()))
|
||||
|
||||
return time.Unix(userSession.LastActivity, 0).Add(provider.Config.Inactivity).Before(ctx.Clock.Now())
|
||||
}
|
||||
|
@ -352,13 +355,13 @@ func handleVerifyGETAuthnCookieValidateUpdate(ctx *middlewares.AutheliaCtx, user
|
|||
return false
|
||||
}
|
||||
|
||||
ctx.Logger.Tracef("Checking if we need check the authentication backend for an updated profile for user '%s'", userSession.Username)
|
||||
ctx.Logger.WithField("username", userSession.Username).Trace("Checking if we need check the authentication backend for an updated profile for user")
|
||||
|
||||
if interval != schema.RefreshIntervalAlways && userSession.RefreshTTL.After(ctx.Clock.Now()) {
|
||||
return false
|
||||
}
|
||||
|
||||
ctx.Logger.Debugf("Checking the authentication backend for an updated profile for user '%s'", userSession.Username)
|
||||
ctx.Logger.WithField("username", userSession.Username).Debug("Checking the authentication backend for an updated profile for user")
|
||||
|
||||
var (
|
||||
details *authentication.UserDetails
|
||||
|
@ -367,12 +370,12 @@ func handleVerifyGETAuthnCookieValidateUpdate(ctx *middlewares.AutheliaCtx, user
|
|||
|
||||
if details, err = ctx.Providers.UserProvider.GetDetails(userSession.Username); err != nil {
|
||||
if errors.Is(err, authentication.ErrUserNotFound) {
|
||||
ctx.Logger.Errorf("Error occurred while attempting to update user details for user '%s': the user was not found indicating they were deleted, disabled, or otherwise no longer authorized to login", userSession.Username)
|
||||
ctx.Logger.WithField("username", userSession.Username).Error("Error occurred while attempting to update user details for user: the user was not found indicating they were deleted, disabled, or otherwise no longer authorized to login")
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
ctx.Logger.Errorf("Error occurred while attempting to update user details for user '%s': %v", userSession.Username, err)
|
||||
ctx.Logger.WithError(err).WithField("username", userSession.Username).Error("Error occurred while attempting to update user details for user")
|
||||
|
||||
return false
|
||||
}
|
||||
|
@ -389,12 +392,12 @@ func handleVerifyGETAuthnCookieValidateUpdate(ctx *middlewares.AutheliaCtx, user
|
|||
}
|
||||
|
||||
if !diffEmails && !diffGroups && !diffDisplayName {
|
||||
ctx.Logger.Tracef("Updated profile not detected for user '%s'", userSession.Username)
|
||||
ctx.Logger.WithField("username", userSession.Username).Trace("Updated profile not detected for user")
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
ctx.Logger.Debugf("Updated profile detected for user '%s'", userSession.Username)
|
||||
ctx.Logger.WithField("username", userSession.Username).Debug("Updated profile detected for user")
|
||||
|
||||
if ctx.Logger.Level >= logrus.TraceLevel {
|
||||
generateVerifySessionHasUpToDateProfileTraceLogs(ctx, userSession, details)
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||
"github.com/authelia/authelia/v4/internal/utils"
|
||||
)
|
||||
|
@ -22,31 +23,10 @@ func (b *AuthzBuilder) WithStrategies(strategies ...AuthnStrategy) *AuthzBuilder
|
|||
return b
|
||||
}
|
||||
|
||||
// WithStrategyCookie adds the Cookie header strategy to the strategies in this builder.
|
||||
func (b *AuthzBuilder) WithStrategyCookie(refreshInterval time.Duration) *AuthzBuilder {
|
||||
b.strategies = append(b.strategies, NewCookieSessionAuthnStrategy(refreshInterval))
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// WithStrategyAuthorization adds the Authorization header strategy to the strategies in this builder.
|
||||
func (b *AuthzBuilder) WithStrategyAuthorization() *AuthzBuilder {
|
||||
b.strategies = append(b.strategies, NewHeaderAuthorizationAuthnStrategy())
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// WithStrategyProxyAuthorization adds the Proxy-Authorization header strategy to the strategies in this builder.
|
||||
func (b *AuthzBuilder) WithStrategyProxyAuthorization() *AuthzBuilder {
|
||||
b.strategies = append(b.strategies, NewHeaderProxyAuthorizationAuthnStrategy())
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// WithImplementationLegacy configures this builder to output an Authz which is used with the Legacy
|
||||
// implementation which is a mix of the other implementations and usually works with most proxies.
|
||||
func (b *AuthzBuilder) WithImplementationLegacy() *AuthzBuilder {
|
||||
b.impl = AuthzImplLegacy
|
||||
b.implementation = AuthzImplLegacy
|
||||
|
||||
return b
|
||||
}
|
||||
|
@ -54,7 +34,7 @@ func (b *AuthzBuilder) WithImplementationLegacy() *AuthzBuilder {
|
|||
// WithImplementationForwardAuth configures this builder to output an Authz which is used with the ForwardAuth
|
||||
// implementation traditionally used by Traefik, Caddy, and Skipper.
|
||||
func (b *AuthzBuilder) WithImplementationForwardAuth() *AuthzBuilder {
|
||||
b.impl = AuthzImplForwardAuth
|
||||
b.implementation = AuthzImplForwardAuth
|
||||
|
||||
return b
|
||||
}
|
||||
|
@ -62,7 +42,7 @@ func (b *AuthzBuilder) WithImplementationForwardAuth() *AuthzBuilder {
|
|||
// WithImplementationAuthRequest configures this builder to output an Authz which is used with the AuthRequest
|
||||
// implementation traditionally used by NGINX.
|
||||
func (b *AuthzBuilder) WithImplementationAuthRequest() *AuthzBuilder {
|
||||
b.impl = AuthzImplAuthRequest
|
||||
b.implementation = AuthzImplAuthRequest
|
||||
|
||||
return b
|
||||
}
|
||||
|
@ -70,7 +50,7 @@ func (b *AuthzBuilder) WithImplementationAuthRequest() *AuthzBuilder {
|
|||
// WithImplementationExtAuthz configures this builder to output an Authz which is used with the ExtAuthz
|
||||
// implementation traditionally used by Envoy.
|
||||
func (b *AuthzBuilder) WithImplementationExtAuthz() *AuthzBuilder {
|
||||
b.impl = AuthzImplExtAuthz
|
||||
b.implementation = AuthzImplExtAuthz
|
||||
|
||||
return b
|
||||
}
|
||||
|
@ -95,12 +75,6 @@ func (b *AuthzBuilder) WithConfig(config *schema.Configuration) *AuthzBuilder {
|
|||
|
||||
b.config = AuthzConfig{
|
||||
RefreshInterval: refreshInterval,
|
||||
Domains: []AuthzDomain{
|
||||
{
|
||||
Name: fmt.Sprintf(".%s", config.Session.Domain),
|
||||
PortalURL: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return b
|
||||
|
@ -140,24 +114,19 @@ func (b *AuthzBuilder) WithEndpointConfig(config schema.ServerAuthzEndpoint) *Au
|
|||
return b
|
||||
}
|
||||
|
||||
// WithAuthzConfig allows configuring the Authz config by providing a AuthzConfig directly. Recommended this is only
|
||||
// used in testing and WithConfig is used instead.
|
||||
func (b *AuthzBuilder) WithAuthzConfig(config AuthzConfig) *AuthzBuilder {
|
||||
b.config = config
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// Build returns a new Authz from the currently configured options in this builder.
|
||||
func (b *AuthzBuilder) Build() (authz *Authz) {
|
||||
authz = &Authz{
|
||||
config: b.config,
|
||||
strategies: b.strategies,
|
||||
handleAuthorized: handleAuthzAuthorizedStandard,
|
||||
implementation: b.implementation,
|
||||
}
|
||||
|
||||
authz.config.StatusCodeBadRequest = fasthttp.StatusBadRequest
|
||||
|
||||
if len(authz.strategies) == 0 {
|
||||
switch b.impl {
|
||||
switch b.implementation {
|
||||
case AuthzImplLegacy:
|
||||
authz.strategies = []AuthnStrategy{NewHeaderLegacyAuthnStrategy(), NewCookieSessionAuthnStrategy(b.config.RefreshInterval)}
|
||||
case AuthzImplAuthRequest:
|
||||
|
@ -167,9 +136,9 @@ func (b *AuthzBuilder) Build() (authz *Authz) {
|
|||
}
|
||||
}
|
||||
|
||||
switch b.impl {
|
||||
switch b.implementation {
|
||||
case AuthzImplLegacy:
|
||||
authz.legacy = true
|
||||
authz.config.StatusCodeBadRequest = fasthttp.StatusUnauthorized
|
||||
authz.handleGetObject = handleAuthzGetObjectLegacy
|
||||
authz.handleUnauthorized = handleAuthzUnauthorizedLegacy
|
||||
authz.handleGetAutheliaURL = handleAuthzPortalURLLegacy
|
||||
|
@ -180,6 +149,7 @@ func (b *AuthzBuilder) Build() (authz *Authz) {
|
|||
case AuthzImplAuthRequest:
|
||||
authz.handleGetObject = handleAuthzGetObjectAuthRequest
|
||||
authz.handleUnauthorized = handleAuthzUnauthorizedAuthRequest
|
||||
authz.handleGetAutheliaURL = handleAuthzPortalURLFromQuery
|
||||
case AuthzImplExtAuthz:
|
||||
authz.handleGetObject = handleAuthzGetObjectExtAuthz
|
||||
authz.handleUnauthorized = handleAuthzUnauthorizedExtAuthz
|
||||
|
|
|
@ -36,7 +36,13 @@ func handleAuthzGetObjectAuthRequest(ctx *middlewares.AutheliaCtx) (object autho
|
|||
return authorization.NewObjectRaw(targetURL, method), nil
|
||||
}
|
||||
|
||||
func handleAuthzUnauthorizedAuthRequest(ctx *middlewares.AutheliaCtx, authn *Authn, _ *url.URL) {
|
||||
ctx.Logger.Infof("Access to %s (method %s) is not authorized to user %s, responding with status code %d", authn.Object.URL.String(), authn.Method, authn.Username, fasthttp.StatusUnauthorized)
|
||||
ctx.ReplyUnauthorized()
|
||||
func handleAuthzUnauthorizedAuthRequest(ctx *middlewares.AutheliaCtx, authn *Authn, redirectionURL *url.URL) {
|
||||
ctx.Logger.Infof(logFmtAuthzRedirect, authn.Object.URL.String(), authn.Method, authn.Username, fasthttp.StatusUnauthorized, redirectionURL)
|
||||
|
||||
switch authn.Object.Method {
|
||||
case fasthttp.MethodHead:
|
||||
ctx.SpecialRedirectNoBody(redirectionURL.String(), fasthttp.StatusUnauthorized)
|
||||
default:
|
||||
ctx.SpecialRedirect(redirectionURL.String(), fasthttp.StatusUnauthorized)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
"github.com/authelia/authelia/v4/internal/authorization"
|
||||
"github.com/authelia/authelia/v4/internal/middlewares"
|
||||
"github.com/authelia/authelia/v4/internal/mocks"
|
||||
"github.com/authelia/authelia/v4/internal/session"
|
||||
"github.com/authelia/authelia/v4/internal/utils"
|
||||
)
|
||||
|
||||
func TestRunAuthRequestAuthzSuite(t *testing.T) {
|
||||
|
@ -35,26 +35,36 @@ type AuthRequestAuthzSuite struct {
|
|||
|
||||
func (s *AuthRequestAuthzSuite) TestShouldHandleAllMethodsDeny() {
|
||||
for _, method := range testRequestMethods {
|
||||
s.T().Run(fmt.Sprintf("OriginalMethod%s", method), func(t *testing.T) {
|
||||
for _, targetURI := range []*url.URL{
|
||||
s.RequireParseRequestURI("https://one-factor.example.com"),
|
||||
s.RequireParseRequestURI("https://one-factor.example.com/subpath"),
|
||||
s.RequireParseRequestURI("https://one-factor.example2.com"),
|
||||
s.RequireParseRequestURI("https://one-factor.example2.com/subpath"),
|
||||
s.T().Run(fmt.Sprintf("Method%s", method), func(t *testing.T) {
|
||||
for _, pairURI := range []urlpair{
|
||||
{s.RequireParseRequestURI("https://one-factor.example.com"), s.RequireParseRequestURI("https://auth.example.com/")},
|
||||
{s.RequireParseRequestURI("https://one-factor.example.com/subpath"), s.RequireParseRequestURI("https://auth.example.com/")},
|
||||
{s.RequireParseRequestURI("https://one-factor.example2.com"), s.RequireParseRequestURI("https://auth.example2.com/")},
|
||||
{s.RequireParseRequestURI("https://one-factor.example2.com/subpath"), s.RequireParseRequestURI("https://auth.example2.com/")},
|
||||
} {
|
||||
t.Run(targetURI.String(), func(t *testing.T) {
|
||||
t.Run(pairURI.TargetURI.String(), func(t *testing.T) {
|
||||
expected := s.RequireParseRequestURI(pairURI.AutheliaURI.String())
|
||||
|
||||
authz := s.Builder().Build()
|
||||
|
||||
mock := mocks.NewMockAutheliaCtx(t)
|
||||
|
||||
defer mock.Close()
|
||||
|
||||
s.setRequest(mock.Ctx, method, targetURI, true, false)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
s.setRequest(mock.Ctx, method, pairURI.TargetURI, true, false)
|
||||
|
||||
authz.Handler(mock.Ctx)
|
||||
|
||||
query := expected.Query()
|
||||
query.Set(queryArgRD, pairURI.TargetURI.String())
|
||||
query.Set(queryArgRM, method)
|
||||
expected.RawQuery = query.Encode()
|
||||
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))
|
||||
assert.Equal(t, expected.String(), string(mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation)))
|
||||
})
|
||||
}
|
||||
})
|
||||
|
@ -83,7 +93,7 @@ func (s *AuthRequestAuthzSuite) TestShouldHandleInvalidMethodCharsDeny() {
|
|||
|
||||
authz.Handler(mock.Ctx)
|
||||
|
||||
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fasthttp.StatusBadRequest, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))
|
||||
})
|
||||
}
|
||||
|
@ -109,7 +119,7 @@ func (s *AuthRequestAuthzSuite) TestShouldHandleMissingXOriginalMethodDeny() {
|
|||
|
||||
authz.Handler(mock.Ctx)
|
||||
|
||||
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fasthttp.StatusBadRequest, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))
|
||||
})
|
||||
}
|
||||
|
@ -128,7 +138,7 @@ func (s *AuthRequestAuthzSuite) TestShouldHandleMissingXOriginalURLDeny() {
|
|||
|
||||
authz.Handler(mock.Ctx)
|
||||
|
||||
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fasthttp.StatusBadRequest, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))
|
||||
})
|
||||
}
|
||||
|
@ -150,6 +160,8 @@ func (s *AuthRequestAuthzSuite) TestShouldHandleAllMethodsAllow() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
s.setRequest(mock.Ctx, method, targetURI, true, false)
|
||||
|
||||
authz.Handler(mock.Ctx)
|
||||
|
@ -174,11 +186,7 @@ func (s *AuthRequestAuthzSuite) TestShouldHandleAllMethodsWithMethodsACL() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
s.setRequest(mock.Ctx, method, targetURI, true, false)
|
||||
|
||||
|
@ -188,8 +196,21 @@ func (s *AuthRequestAuthzSuite) TestShouldHandleAllMethodsWithMethodsACL() {
|
|||
assert.Equal(t, fasthttp.StatusOK, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))
|
||||
} else {
|
||||
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))
|
||||
expected := s.RequireParseRequestURI("https://auth.example.com/")
|
||||
|
||||
query := expected.Query()
|
||||
query.Set(queryArgRD, targetURI.String())
|
||||
query.Set(queryArgRM, method)
|
||||
expected.RawQuery = query.Encode()
|
||||
|
||||
switch method {
|
||||
case fasthttp.MethodHead:
|
||||
assert.Nil(t, mock.Ctx.Response.Body())
|
||||
default:
|
||||
assert.Equal(t, fmt.Sprintf(`<a href="%s">%d %s</a>`, utils.StringHTMLEscape(expected.String()), fasthttp.StatusUnauthorized, fasthttp.StatusMessage(fasthttp.StatusUnauthorized)), string(mock.Ctx.Response.Body()))
|
||||
}
|
||||
|
||||
assert.Equal(t, expected.String(), string(mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation)))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -205,7 +226,7 @@ func (s *AuthRequestAuthzSuite) TestShouldHandleInvalidURLForCVE202132637() {
|
|||
}{
|
||||
{"Should401UnauthorizedWithNullByte",
|
||||
[]byte{104, 116, 116, 112, 115, 58, 47, 47, 0, 110, 111, 116, 45, 111, 110, 101, 45, 102, 97, 99, 116, 111, 114, 46, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109},
|
||||
fasthttp.StatusUnauthorized,
|
||||
fasthttp.StatusBadRequest,
|
||||
},
|
||||
{"Should200OkWithoutNullByte",
|
||||
[]byte{104, 116, 116, 112, 115, 58, 47, 47, 110, 111, 116, 45, 111, 110, 101, 45, 102, 97, 99, 116, 111, 114, 46, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109},
|
||||
|
@ -226,6 +247,8 @@ func (s *AuthRequestAuthzSuite) TestShouldHandleInvalidURLForCVE202132637() {
|
|||
mock.Ctx.Configuration.AccessControl.DefaultPolicy = testBypass
|
||||
mock.Ctx.Providers.Authorizer = authorization.NewAuthorizer(&mock.Ctx.Configuration)
|
||||
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
mock.Ctx.Request.Header.Set(testXOriginalMethod, method)
|
||||
mock.Ctx.Request.Header.SetBytesKV([]byte(testXOriginalUrl), tc.uri)
|
||||
|
||||
|
@ -255,17 +278,13 @@ func (s *AuthRequestAuthzSuite) TestShouldNotHandleExtAuthzAllMethodsAllow() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
setRequestExtAuthz(mock.Ctx, method, targetURI, true, false)
|
||||
|
||||
authz.Handler(mock.Ctx)
|
||||
|
||||
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fasthttp.StatusBadRequest, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))
|
||||
})
|
||||
}
|
||||
|
@ -291,17 +310,13 @@ func (s *AuthRequestAuthzSuite) TestShouldNotHandleExtAuthzAllMethodsAllowXHR()
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
setRequestExtAuthz(mock.Ctx, method, targetURI, x, x)
|
||||
|
||||
authz.Handler(mock.Ctx)
|
||||
|
||||
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fasthttp.StatusBadRequest, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))
|
||||
})
|
||||
}
|
||||
|
@ -323,17 +338,13 @@ func (s *AuthRequestAuthzSuite) TestShouldNotHandleExtAuthzAllMethodsWithMethods
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
setRequestExtAuthz(mock.Ctx, method, targetURI, true, false)
|
||||
|
||||
authz.Handler(mock.Ctx)
|
||||
|
||||
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fasthttp.StatusBadRequest, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))
|
||||
})
|
||||
}
|
||||
|
@ -357,17 +368,13 @@ func (s *AuthRequestAuthzSuite) TestShouldNotHandleForwardAuthAllMethodsAllow()
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
setRequestForwardAuth(mock.Ctx, method, targetURI, true, false)
|
||||
|
||||
authz.Handler(mock.Ctx)
|
||||
|
||||
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fasthttp.StatusBadRequest, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))
|
||||
})
|
||||
}
|
||||
|
@ -393,17 +400,13 @@ func (s *AuthRequestAuthzSuite) TestShouldNotHandleForwardAuthAllMethodsAllowXHR
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
setRequestForwardAuth(mock.Ctx, method, targetURI, x, x)
|
||||
|
||||
authz.Handler(mock.Ctx)
|
||||
|
||||
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fasthttp.StatusBadRequest, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))
|
||||
})
|
||||
}
|
||||
|
@ -425,17 +428,13 @@ func (s *AuthRequestAuthzSuite) TestShouldNotHandleForwardAuthAllMethodsWithMeth
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
setRequestForwardAuth(mock.Ctx, method, targetURI, true, false)
|
||||
|
||||
authz.Handler(mock.Ctx)
|
||||
|
||||
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fasthttp.StatusBadRequest, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))
|
||||
})
|
||||
}
|
||||
|
|
|
@ -50,6 +50,12 @@ func handleAuthzUnauthorizedExtAuthz(ctx *middlewares.AutheliaCtx, authn *Authn,
|
|||
}
|
||||
}
|
||||
|
||||
ctx.Logger.Infof("Access to %s (method %s) is not authorized to user %s, responding with status code %d with location redirect to %s", authn.Object.String(), authn.Method, authn.Username, statusCode, redirectionURL)
|
||||
ctx.SpecialRedirect(redirectionURL.String(), statusCode)
|
||||
ctx.Logger.Infof(logFmtAuthzRedirect, authn.Object.String(), authn.Method, authn.Username, statusCode, redirectionURL)
|
||||
|
||||
switch authn.Object.Method {
|
||||
case fasthttp.MethodHead:
|
||||
ctx.SpecialRedirectNoBody(redirectionURL.String(), statusCode)
|
||||
default:
|
||||
ctx.SpecialRedirect(redirectionURL.String(), statusCode)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
"github.com/authelia/authelia/v4/internal/authorization"
|
||||
"github.com/authelia/authelia/v4/internal/middlewares"
|
||||
"github.com/authelia/authelia/v4/internal/mocks"
|
||||
"github.com/authelia/authelia/v4/internal/session"
|
||||
"github.com/authelia/authelia/v4/internal/utils"
|
||||
)
|
||||
|
||||
func TestRunExtAuthzAuthzSuite(t *testing.T) {
|
||||
|
@ -51,11 +51,7 @@ func (s *ExtAuthzAuthzSuite) TestShouldHandleAllMethodsDeny() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
s.setRequest(mock.Ctx, method, pairURI.TargetURI, true, false)
|
||||
|
||||
|
@ -98,11 +94,7 @@ func (s *ExtAuthzAuthzSuite) TestShouldHandleAllMethodsOverrideAutheliaURLDeny()
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
mock.Ctx.Request.Header.Set("X-Authelia-Url", pairURI.AutheliaURI.String())
|
||||
s.setRequest(mock.Ctx, method, pairURI.TargetURI, true, false)
|
||||
|
@ -148,8 +140,10 @@ func (s *ExtAuthzAuthzSuite) TestShouldHandleAllMethodsMissingAutheliaURLDeny()
|
|||
|
||||
authz.Handler(mock.Ctx)
|
||||
|
||||
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fasthttp.StatusBadRequest, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fmt.Sprintf("%d %s", fasthttp.StatusBadRequest, fasthttp.StatusMessage(fasthttp.StatusBadRequest)), string(mock.Ctx.Response.Body()))
|
||||
assert.Equal(t, "", string(mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation)))
|
||||
assert.Equal(t, "text/plain; charset=utf-8", string(mock.Ctx.Response.Header.Peek(fasthttp.HeaderContentType)))
|
||||
})
|
||||
}
|
||||
})
|
||||
|
@ -176,11 +170,7 @@ func (s *ExtAuthzAuthzSuite) TestShouldHandleAllMethodsXHRDeny() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
s.setRequest(mock.Ctx, method, pairURI.TargetURI, x, x)
|
||||
|
||||
|
@ -222,17 +212,13 @@ func (s *ExtAuthzAuthzSuite) TestShouldHandleInvalidMethodCharsDeny() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
s.setRequest(mock.Ctx, method, targetURI, true, false)
|
||||
|
||||
authz.Handler(mock.Ctx)
|
||||
|
||||
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fasthttp.StatusBadRequest, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))
|
||||
})
|
||||
}
|
||||
|
@ -249,17 +235,13 @@ func (s *ExtAuthzAuthzSuite) TestShouldHandleMissingHostDeny() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
s.setRequest(mock.Ctx, method, nil, true, false)
|
||||
|
||||
authz.Handler(mock.Ctx)
|
||||
|
||||
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fasthttp.StatusBadRequest, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))
|
||||
})
|
||||
}
|
||||
|
@ -281,11 +263,7 @@ func (s *ExtAuthzAuthzSuite) TestShouldHandleAllMethodsAllow() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
s.setRequest(mock.Ctx, method, targetURI, true, false)
|
||||
|
||||
|
@ -317,11 +295,7 @@ func (s *ExtAuthzAuthzSuite) TestShouldHandleAllMethodsAllowXHR() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
s.setRequest(mock.Ctx, method, targetURI, x, x)
|
||||
|
||||
|
@ -349,11 +323,7 @@ func (s *ExtAuthzAuthzSuite) TestShouldHandleAllMethodsWithMethodsACL() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
s.setRequest(mock.Ctx, method, targetURI, true, false)
|
||||
|
||||
|
@ -365,18 +335,23 @@ func (s *ExtAuthzAuthzSuite) TestShouldHandleAllMethodsWithMethodsACL() {
|
|||
} else {
|
||||
expected := s.RequireParseRequestURI("https://auth.example.com/")
|
||||
|
||||
switch method {
|
||||
case fasthttp.MethodGet, fasthttp.MethodOptions, fasthttp.MethodHead:
|
||||
assert.Equal(t, fasthttp.StatusFound, mock.Ctx.Response.StatusCode())
|
||||
default:
|
||||
assert.Equal(t, fasthttp.StatusSeeOther, mock.Ctx.Response.StatusCode())
|
||||
}
|
||||
|
||||
query := expected.Query()
|
||||
query.Set(queryArgRD, targetURI.String())
|
||||
query.Set(queryArgRM, method)
|
||||
expected.RawQuery = query.Encode()
|
||||
|
||||
switch method {
|
||||
case fasthttp.MethodHead:
|
||||
assert.Equal(t, fasthttp.StatusFound, mock.Ctx.Response.StatusCode())
|
||||
assert.Nil(t, mock.Ctx.Response.Body())
|
||||
case fasthttp.MethodGet, fasthttp.MethodOptions:
|
||||
assert.Equal(t, fasthttp.StatusFound, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fmt.Sprintf(`<a href="%s">%d %s</a>`, utils.StringHTMLEscape(expected.String()), fasthttp.StatusFound, fasthttp.StatusMessage(fasthttp.StatusFound)), string(mock.Ctx.Response.Body()))
|
||||
default:
|
||||
assert.Equal(t, fasthttp.StatusSeeOther, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fmt.Sprintf(`<a href="%s">%d %s</a>`, utils.StringHTMLEscape(expected.String()), fasthttp.StatusSeeOther, fasthttp.StatusMessage(fasthttp.StatusSeeOther)), string(mock.Ctx.Response.Body()))
|
||||
}
|
||||
|
||||
assert.Equal(t, expected.String(), string(mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation)))
|
||||
}
|
||||
})
|
||||
|
@ -394,7 +369,7 @@ func (s *ExtAuthzAuthzSuite) TestShouldHandleInvalidURLForCVE202132637() {
|
|||
}{
|
||||
{"Should401UnauthorizedWithNullByte",
|
||||
[]byte("https"), []byte{0, 110, 111, 116, 45, 111, 110, 101, 45, 102, 97, 99, 116, 111, 114, 46, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109}, "/path-example",
|
||||
fasthttp.StatusUnauthorized,
|
||||
fasthttp.StatusBadRequest,
|
||||
},
|
||||
{"Should200OkWithoutNullByte",
|
||||
[]byte("https"), []byte{110, 111, 116, 45, 111, 110, 101, 45, 102, 97, 99, 116, 111, 114, 46, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109}, "/path-example",
|
||||
|
@ -415,11 +390,7 @@ func (s *ExtAuthzAuthzSuite) TestShouldHandleInvalidURLForCVE202132637() {
|
|||
mock.Ctx.Configuration.AccessControl.DefaultPolicy = testBypass
|
||||
mock.Ctx.Providers.Authorizer = authorization.NewAuthorizer(&mock.Ctx.Configuration)
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
mock.Ctx.Request.SetHostBytes(tc.host)
|
||||
mock.Ctx.Request.Header.SetMethodBytes([]byte(method))
|
||||
|
@ -458,7 +429,7 @@ func (s *ExtAuthzAuthzSuite) TestShouldNotHandleAuthRequestAllMethodsAllow() {
|
|||
|
||||
authz.Handler(mock.Ctx)
|
||||
|
||||
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fasthttp.StatusBadRequest, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))
|
||||
})
|
||||
}
|
||||
|
@ -478,17 +449,13 @@ func (s *ExtAuthzAuthzSuite) TestShouldNotHandleAuthRequestAllMethodsWithMethods
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
setRequestAuthRequest(mock.Ctx, method, targetURI, true, false)
|
||||
|
||||
authz.Handler(mock.Ctx)
|
||||
|
||||
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fasthttp.StatusBadRequest, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))
|
||||
})
|
||||
}
|
||||
|
@ -512,17 +479,13 @@ func (s *ExtAuthzAuthzSuite) TestShouldNotHandleForwardAuthAllMethodsAllow() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
setRequestForwardAuth(mock.Ctx, method, targetURI, true, false)
|
||||
|
||||
authz.Handler(mock.Ctx)
|
||||
|
||||
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fasthttp.StatusBadRequest, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))
|
||||
})
|
||||
}
|
||||
|
@ -548,17 +511,13 @@ func (s *ExtAuthzAuthzSuite) TestShouldNotHandleForwardAuthAllMethodsAllowXHR()
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
setRequestForwardAuth(mock.Ctx, method, targetURI, x, x)
|
||||
|
||||
authz.Handler(mock.Ctx)
|
||||
|
||||
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fasthttp.StatusBadRequest, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))
|
||||
})
|
||||
}
|
||||
|
@ -580,17 +539,13 @@ func (s *ExtAuthzAuthzSuite) TestShouldNotHandleForwardAuthAllMethodsWithMethods
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
setRequestForwardAuth(mock.Ctx, method, targetURI, true, false)
|
||||
|
||||
authz.Handler(mock.Ctx)
|
||||
|
||||
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fasthttp.StatusBadRequest, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))
|
||||
})
|
||||
}
|
||||
|
|
|
@ -50,6 +50,12 @@ func handleAuthzUnauthorizedForwardAuth(ctx *middlewares.AutheliaCtx, authn *Aut
|
|||
}
|
||||
}
|
||||
|
||||
ctx.Logger.Infof("Access to %s (method %s) is not authorized to user %s, responding with status code %d with location redirect to %s", authn.Object.String(), authn.Method, authn.Username, statusCode, redirectionURL)
|
||||
ctx.SpecialRedirect(redirectionURL.String(), statusCode)
|
||||
ctx.Logger.Infof(logFmtAuthzRedirect, authn.Object.String(), authn.Method, authn.Username, statusCode, redirectionURL)
|
||||
|
||||
switch authn.Object.Method {
|
||||
case fasthttp.MethodHead:
|
||||
ctx.SpecialRedirectNoBody(redirectionURL.String(), statusCode)
|
||||
default:
|
||||
ctx.SpecialRedirect(redirectionURL.String(), statusCode)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
"github.com/authelia/authelia/v4/internal/authorization"
|
||||
"github.com/authelia/authelia/v4/internal/middlewares"
|
||||
"github.com/authelia/authelia/v4/internal/mocks"
|
||||
"github.com/authelia/authelia/v4/internal/session"
|
||||
"github.com/authelia/authelia/v4/internal/utils"
|
||||
)
|
||||
|
||||
func TestRunForwardAuthAuthzSuite(t *testing.T) {
|
||||
|
@ -51,11 +51,9 @@ func (s *ForwardAuthAuthzSuite) TestShouldHandleAllMethodsDeny() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
s.setRequest(mock.Ctx, method, pairURI.TargetURI, true, false)
|
||||
|
||||
|
@ -98,11 +96,7 @@ func (s *ForwardAuthAuthzSuite) TestShouldHandleAllMethodsOverrideAutheliaURLDen
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
mock.Ctx.RequestCtx.QueryArgs().Set("authelia_url", pairURI.AutheliaURI.String())
|
||||
s.setRequest(mock.Ctx, method, pairURI.TargetURI, true, false)
|
||||
|
@ -148,8 +142,10 @@ func (s *ForwardAuthAuthzSuite) TestShouldHandleAllMethodsMissingAutheliaURLDeny
|
|||
|
||||
authz.Handler(mock.Ctx)
|
||||
|
||||
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fasthttp.StatusBadRequest, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fmt.Sprintf("%d %s", fasthttp.StatusBadRequest, fasthttp.StatusMessage(fasthttp.StatusBadRequest)), string(mock.Ctx.Response.Body()))
|
||||
assert.Equal(t, "", string(mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation)))
|
||||
assert.Equal(t, "text/plain; charset=utf-8", string(mock.Ctx.Response.Header.Peek(fasthttp.HeaderContentType)))
|
||||
})
|
||||
}
|
||||
})
|
||||
|
@ -176,11 +172,7 @@ func (s *ForwardAuthAuthzSuite) TestShouldHandleAllMethodsXHRDeny() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
s.setRequest(mock.Ctx, method, pairURI.TargetURI, x, x)
|
||||
|
||||
|
@ -220,17 +212,13 @@ func (s *ForwardAuthAuthzSuite) TestShouldHandleInvalidMethodCharsDeny() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
s.setRequest(mock.Ctx, method, targetURI, true, false)
|
||||
|
||||
authz.Handler(mock.Ctx)
|
||||
|
||||
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fasthttp.StatusBadRequest, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))
|
||||
})
|
||||
}
|
||||
|
@ -247,11 +235,7 @@ func (s *ForwardAuthAuthzSuite) TestShouldHandleMissingHostDeny() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
mock.Ctx.Request.Header.Set("X-Forwarded-Method", method)
|
||||
mock.Ctx.Request.Header.Set(fasthttp.HeaderXForwardedProto, "https")
|
||||
|
@ -261,7 +245,7 @@ func (s *ForwardAuthAuthzSuite) TestShouldHandleMissingHostDeny() {
|
|||
|
||||
authz.Handler(mock.Ctx)
|
||||
|
||||
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fasthttp.StatusBadRequest, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))
|
||||
})
|
||||
}
|
||||
|
@ -283,11 +267,7 @@ func (s *ForwardAuthAuthzSuite) TestShouldHandleAllMethodsAllow() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
s.setRequest(mock.Ctx, method, targetURI, true, false)
|
||||
|
||||
|
@ -313,11 +293,7 @@ func (s *ForwardAuthAuthzSuite) TestShouldHandleAllMethodsWithMethodsACL() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
s.setRequest(mock.Ctx, method, targetURI, true, false)
|
||||
|
||||
|
@ -329,18 +305,23 @@ func (s *ForwardAuthAuthzSuite) TestShouldHandleAllMethodsWithMethodsACL() {
|
|||
} else {
|
||||
expected := s.RequireParseRequestURI("https://auth.example.com/")
|
||||
|
||||
switch method {
|
||||
case fasthttp.MethodGet, fasthttp.MethodOptions, fasthttp.MethodHead:
|
||||
assert.Equal(t, fasthttp.StatusFound, mock.Ctx.Response.StatusCode())
|
||||
default:
|
||||
assert.Equal(t, fasthttp.StatusSeeOther, mock.Ctx.Response.StatusCode())
|
||||
}
|
||||
|
||||
query := expected.Query()
|
||||
query.Set(queryArgRD, targetURI.String())
|
||||
query.Set(queryArgRM, method)
|
||||
expected.RawQuery = query.Encode()
|
||||
|
||||
switch method {
|
||||
case fasthttp.MethodHead:
|
||||
assert.Equal(t, fasthttp.StatusFound, mock.Ctx.Response.StatusCode())
|
||||
assert.Nil(t, mock.Ctx.Response.Body())
|
||||
case fasthttp.MethodGet, fasthttp.MethodOptions:
|
||||
assert.Equal(t, fasthttp.StatusFound, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fmt.Sprintf(`<a href="%s">%d %s</a>`, utils.StringHTMLEscape(expected.String()), fasthttp.StatusFound, fasthttp.StatusMessage(fasthttp.StatusFound)), string(mock.Ctx.Response.Body()))
|
||||
default:
|
||||
assert.Equal(t, fasthttp.StatusSeeOther, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fmt.Sprintf(`<a href="%s">%d %s</a>`, utils.StringHTMLEscape(expected.String()), fasthttp.StatusSeeOther, fasthttp.StatusMessage(fasthttp.StatusSeeOther)), string(mock.Ctx.Response.Body()))
|
||||
}
|
||||
|
||||
assert.Equal(t, expected.String(), string(mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation)))
|
||||
}
|
||||
})
|
||||
|
@ -365,11 +346,7 @@ func (s *ForwardAuthAuthzSuite) TestShouldHandleAllMethodsAllowXHR() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
s.setRequest(mock.Ctx, method, targetURI, true, true)
|
||||
|
||||
|
@ -392,7 +369,7 @@ func (s *ForwardAuthAuthzSuite) TestShouldHandleInvalidURLForCVE202132637() {
|
|||
}{
|
||||
{"Should401UnauthorizedWithNullByte",
|
||||
[]byte("https"), []byte{0, 110, 111, 116, 45, 111, 110, 101, 45, 102, 97, 99, 116, 111, 114, 46, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109}, "/path-example",
|
||||
fasthttp.StatusUnauthorized,
|
||||
fasthttp.StatusBadRequest,
|
||||
},
|
||||
{"Should200OkWithoutNullByte",
|
||||
[]byte("https"), []byte{110, 111, 116, 45, 111, 110, 101, 45, 102, 97, 99, 116, 111, 114, 46, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109}, "/path-example",
|
||||
|
@ -413,11 +390,7 @@ func (s *ForwardAuthAuthzSuite) TestShouldHandleInvalidURLForCVE202132637() {
|
|||
mock.Ctx.Configuration.AccessControl.DefaultPolicy = testBypass
|
||||
mock.Ctx.Providers.Authorizer = authorization.NewAuthorizer(&mock.Ctx.Configuration)
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
mock.Ctx.Request.Header.Set("X-Forwarded-Method", method)
|
||||
mock.Ctx.Request.Header.SetBytesKV([]byte(fasthttp.HeaderXForwardedProto), tc.scheme)
|
||||
|
@ -455,7 +428,7 @@ func (s *ForwardAuthAuthzSuite) TestShouldNotHandleAuthRequestAllMethodsAllow()
|
|||
|
||||
authz.Handler(mock.Ctx)
|
||||
|
||||
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fasthttp.StatusBadRequest, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))
|
||||
})
|
||||
}
|
||||
|
@ -475,17 +448,13 @@ func (s *ForwardAuthAuthzSuite) TestShouldNotHandleAuthRequestAllMethodsWithMeth
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
setRequestAuthRequest(mock.Ctx, method, targetURI, true, false)
|
||||
|
||||
authz.Handler(mock.Ctx)
|
||||
|
||||
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fasthttp.StatusBadRequest, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))
|
||||
})
|
||||
}
|
||||
|
@ -509,17 +478,13 @@ func (s *ForwardAuthAuthzSuite) TestShouldNotHandleExtAuthzAllMethodsAllow() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
setRequestExtAuthz(mock.Ctx, method, targetURI, true, false)
|
||||
|
||||
authz.Handler(mock.Ctx)
|
||||
|
||||
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fasthttp.StatusBadRequest, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))
|
||||
})
|
||||
}
|
||||
|
@ -545,17 +510,13 @@ func (s *ForwardAuthAuthzSuite) TestShouldNotHandleExtAuthzAllMethodsAllowXHR()
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
setRequestExtAuthz(mock.Ctx, method, targetURI, x, x)
|
||||
|
||||
authz.Handler(mock.Ctx)
|
||||
|
||||
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fasthttp.StatusBadRequest, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))
|
||||
})
|
||||
}
|
||||
|
@ -577,17 +538,13 @@ func (s *ForwardAuthAuthzSuite) TestShouldNotHandleExtAuthzAllMethodsWithMethods
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
setRequestExtAuthz(mock.Ctx, method, targetURI, true, false)
|
||||
|
||||
authz.Handler(mock.Ctx)
|
||||
|
||||
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fasthttp.StatusBadRequest, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))
|
||||
})
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ func handleAuthzUnauthorizedLegacy(ctx *middlewares.AutheliaCtx, authn *Authn, r
|
|||
statusCode = fasthttp.StatusUnauthorized
|
||||
default:
|
||||
switch authn.Object.Method {
|
||||
case fasthttp.MethodGet, fasthttp.MethodOptions, "":
|
||||
case fasthttp.MethodGet, fasthttp.MethodOptions, fasthttp.MethodHead, "":
|
||||
statusCode = fasthttp.StatusFound
|
||||
default:
|
||||
statusCode = fasthttp.StatusSeeOther
|
||||
|
@ -55,8 +55,14 @@ func handleAuthzUnauthorizedLegacy(ctx *middlewares.AutheliaCtx, authn *Authn, r
|
|||
}
|
||||
|
||||
if redirectionURL != nil {
|
||||
ctx.Logger.Infof("Access to %s (method %s) is not authorized to user %s, responding with status code %d with location redirect to %s", authn.Object.URL.String(), authn.Method, authn.Username, statusCode, redirectionURL.String())
|
||||
ctx.SpecialRedirect(redirectionURL.String(), statusCode)
|
||||
ctx.Logger.Infof(logFmtAuthzRedirect, authn.Object.URL.String(), authn.Method, authn.Username, statusCode, redirectionURL)
|
||||
|
||||
switch authn.Object.Method {
|
||||
case fasthttp.MethodHead:
|
||||
ctx.SpecialRedirectNoBody(redirectionURL.String(), statusCode)
|
||||
default:
|
||||
ctx.SpecialRedirect(redirectionURL.String(), statusCode)
|
||||
}
|
||||
} else {
|
||||
ctx.Logger.Infof("Access to %s (method %s) is not authorized to user %s, responding with status code %d", authn.Object.URL.String(), authn.Method, authn.Username, statusCode)
|
||||
ctx.ReplyUnauthorized()
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
|
@ -15,7 +16,7 @@ import (
|
|||
"github.com/authelia/authelia/v4/internal/authorization"
|
||||
"github.com/authelia/authelia/v4/internal/middlewares"
|
||||
"github.com/authelia/authelia/v4/internal/mocks"
|
||||
"github.com/authelia/authelia/v4/internal/session"
|
||||
"github.com/authelia/authelia/v4/internal/utils"
|
||||
)
|
||||
|
||||
func TestRunLegacyAuthzSuite(t *testing.T) {
|
||||
|
@ -53,11 +54,7 @@ func (s *LegacyAuthzSuite) TestShouldHandleAllMethodsDeny() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
mock.Ctx.RequestCtx.QueryArgs().Set(queryArgRD, pairURI.AutheliaURI.String())
|
||||
mock.Ctx.Request.Header.Set("X-Forwarded-Method", method)
|
||||
|
@ -69,7 +66,7 @@ func (s *LegacyAuthzSuite) TestShouldHandleAllMethodsDeny() {
|
|||
authz.Handler(mock.Ctx)
|
||||
|
||||
switch method {
|
||||
case fasthttp.MethodGet, fasthttp.MethodOptions:
|
||||
case fasthttp.MethodGet, fasthttp.MethodOptions, fasthttp.MethodHead:
|
||||
assert.Equal(t, fasthttp.StatusFound, mock.Ctx.Response.StatusCode())
|
||||
default:
|
||||
assert.Equal(t, fasthttp.StatusSeeOther, mock.Ctx.Response.StatusCode())
|
||||
|
@ -105,11 +102,7 @@ func (s *LegacyAuthzSuite) TestShouldHandleAllMethodsOverrideAutheliaURLDeny() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
mock.Ctx.RequestCtx.QueryArgs().Set(queryArgRD, pairURI.AutheliaURI.String())
|
||||
mock.Ctx.Request.Header.Set("X-Forwarded-Method", method)
|
||||
|
@ -121,7 +114,7 @@ func (s *LegacyAuthzSuite) TestShouldHandleAllMethodsOverrideAutheliaURLDeny() {
|
|||
authz.Handler(mock.Ctx)
|
||||
|
||||
switch method {
|
||||
case fasthttp.MethodGet, fasthttp.MethodOptions:
|
||||
case fasthttp.MethodGet, fasthttp.MethodOptions, fasthttp.MethodHead:
|
||||
assert.Equal(t, fasthttp.StatusFound, mock.Ctx.Response.StatusCode())
|
||||
default:
|
||||
assert.Equal(t, fasthttp.StatusSeeOther, mock.Ctx.Response.StatusCode())
|
||||
|
@ -227,7 +220,7 @@ func (s *LegacyAuthzSuite) TestShouldHandleAllMethodsRDAutheliaURLOneFactorStatu
|
|||
authz.Handler(mock.Ctx)
|
||||
|
||||
switch method {
|
||||
case fasthttp.MethodGet, fasthttp.MethodOptions:
|
||||
case fasthttp.MethodGet, fasthttp.MethodOptions, fasthttp.MethodHead:
|
||||
assert.Equal(t, fasthttp.StatusFound, mock.Ctx.Response.StatusCode())
|
||||
default:
|
||||
assert.Equal(t, fasthttp.StatusSeeOther, mock.Ctx.Response.StatusCode())
|
||||
|
@ -264,11 +257,7 @@ func (s *LegacyAuthzSuite) TestShouldHandleAllMethodsXHRDeny() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
mock.Ctx.RequestCtx.QueryArgs().Set(queryArgRD, pairURI.AutheliaURI.String())
|
||||
mock.Ctx.Request.Header.Set("X-Forwarded-Method", method)
|
||||
|
@ -317,11 +306,7 @@ func (s *LegacyAuthzSuite) TestShouldHandleInvalidMethodCharsDeny() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
mock.Ctx.Request.Header.Set("X-Forwarded-Method", method)
|
||||
mock.Ctx.Request.Header.Set(fasthttp.HeaderXForwardedProto, targetURI.Scheme)
|
||||
|
@ -348,11 +333,7 @@ func (s *LegacyAuthzSuite) TestShouldHandleMissingHostDeny() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
mock.Ctx.Request.Header.Set("X-Forwarded-Method", method)
|
||||
mock.Ctx.Request.Header.Set(fasthttp.HeaderXForwardedProto, "https")
|
||||
|
@ -384,11 +365,7 @@ func (s *LegacyAuthzSuite) TestShouldHandleAllMethodsAllow() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
mock.Ctx.Request.Header.Set("X-Forwarded-Method", method)
|
||||
mock.Ctx.Request.Header.Set(fasthttp.HeaderXForwardedProto, targetURI.Scheme)
|
||||
|
@ -406,6 +383,56 @@ func (s *LegacyAuthzSuite) TestShouldHandleAllMethodsAllow() {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *LegacyAuthzSuite) TestShouldHandleAllMethodsWithMethodsACL() {
|
||||
for _, method := range testRequestMethods {
|
||||
s.T().Run(fmt.Sprintf("Method%s", method), func(t *testing.T) {
|
||||
for _, methodACL := range testRequestMethods {
|
||||
targetURI := s.RequireParseRequestURI(fmt.Sprintf("https://bypass-%s.example.com", strings.ToLower(methodACL)))
|
||||
t.Run(targetURI.String(), func(t *testing.T) {
|
||||
authz := s.Builder().Build()
|
||||
|
||||
mock := mocks.NewMockAutheliaCtx(t)
|
||||
|
||||
defer mock.Close()
|
||||
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
s.setRequest(mock.Ctx, method, targetURI, true, false)
|
||||
mock.Ctx.RequestCtx.QueryArgs().Set(queryArgRD, "https://auth.example.com")
|
||||
|
||||
authz.Handler(mock.Ctx)
|
||||
|
||||
if method == methodACL {
|
||||
assert.Equal(t, fasthttp.StatusOK, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))
|
||||
} else {
|
||||
expected := s.RequireParseRequestURI("https://auth.example.com/")
|
||||
|
||||
query := expected.Query()
|
||||
query.Set(queryArgRD, targetURI.String())
|
||||
query.Set(queryArgRM, method)
|
||||
expected.RawQuery = query.Encode()
|
||||
|
||||
switch method {
|
||||
case fasthttp.MethodHead:
|
||||
assert.Equal(t, fasthttp.StatusFound, mock.Ctx.Response.StatusCode())
|
||||
assert.Nil(t, mock.Ctx.Response.Body())
|
||||
case fasthttp.MethodGet, fasthttp.MethodOptions:
|
||||
assert.Equal(t, fasthttp.StatusFound, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fmt.Sprintf(`<a href="%s">%d %s</a>`, utils.StringHTMLEscape(expected.String()), fasthttp.StatusFound, fasthttp.StatusMessage(fasthttp.StatusFound)), string(mock.Ctx.Response.Body()))
|
||||
default:
|
||||
assert.Equal(t, fasthttp.StatusSeeOther, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, fmt.Sprintf(`<a href="%s">%d %s</a>`, utils.StringHTMLEscape(expected.String()), fasthttp.StatusSeeOther, fasthttp.StatusMessage(fasthttp.StatusSeeOther)), string(mock.Ctx.Response.Body()))
|
||||
}
|
||||
|
||||
assert.Equal(t, expected.String(), string(mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation)))
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *LegacyAuthzSuite) TestShouldHandleAllMethodsAllowXHR() {
|
||||
for _, method := range testRequestMethods {
|
||||
s.T().Run(fmt.Sprintf("Method%s", method), func(t *testing.T) {
|
||||
|
@ -422,11 +449,7 @@ func (s *LegacyAuthzSuite) TestShouldHandleAllMethodsAllowXHR() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
mock.Ctx.Request.Header.Set("X-Forwarded-Method", method)
|
||||
mock.Ctx.Request.Header.Set(fasthttp.HeaderXForwardedProto, targetURI.Scheme)
|
||||
|
@ -451,11 +474,7 @@ func (s *LegacyAuthzSuite) TestShouldHandleLegacyBasicAuth() { // TestShouldVeri
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
mock.Ctx.QueryArgs().Add("auth", "basic")
|
||||
mock.Ctx.Request.Header.Set("Authorization", "Basic am9objpwYXNzd29yZA==")
|
||||
|
@ -540,11 +559,7 @@ func (s *LegacyAuthzSuite) TestShouldHandleLegacyBasicAuthFailures() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
mock.Ctx.QueryArgs().Add("auth", "basic")
|
||||
mock.Ctx.Request.Header.Set("X-Original-URL", "https://one-factor.example.com")
|
||||
|
@ -593,11 +608,7 @@ func (s *LegacyAuthzSuite) TestShouldHandleInvalidURLForCVE202132637() {
|
|||
mock.Ctx.Configuration.AccessControl.DefaultPolicy = testBypass
|
||||
mock.Ctx.Providers.Authorizer = authorization.NewAuthorizer(&mock.Ctx.Configuration)
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
mock.Ctx.Request.Header.Set("X-Forwarded-Method", method)
|
||||
mock.Ctx.Request.Header.SetBytesKV([]byte(fasthttp.HeaderXForwardedProto), tc.scheme)
|
||||
|
|
|
@ -50,9 +50,12 @@ func (s *AuthzSuite) RequireParseRequestURI(rawURL string) *url.URL {
|
|||
return u
|
||||
}
|
||||
|
||||
type urlpair struct {
|
||||
TargetURI *url.URL
|
||||
AutheliaURI *url.URL
|
||||
func (s *AuthzSuite) ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock *mocks.MockAutheliaCtx) {
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
}
|
||||
|
||||
func (s *AuthzSuite) Builder() (builder *AuthzBuilder) {
|
||||
|
@ -87,11 +90,7 @@ func (s *AuthzSuite) TestShouldNotBeAbleToParseBasicAuth() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
targetURI := s.RequireParseRequestURI("https://test.example.com")
|
||||
|
||||
|
@ -124,11 +123,7 @@ func (s *AuthzSuite) TestShouldApplyDefaultPolicy() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
targetURI := s.RequireParseRequestURI("https://test.example.com")
|
||||
|
||||
|
@ -181,11 +176,7 @@ func (s *AuthzSuite) TestShouldDenyObject() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
targetURI := s.RequireParseRequestURI(tc.value)
|
||||
|
||||
|
@ -193,7 +184,12 @@ func (s *AuthzSuite) TestShouldDenyObject() {
|
|||
|
||||
authz.Handler(mock.Ctx)
|
||||
|
||||
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||
switch s.implementation {
|
||||
case AuthzImplLegacy:
|
||||
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||
default:
|
||||
assert.Equal(t, fasthttp.StatusBadRequest, mock.Ctx.Response.StatusCode())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -209,11 +205,7 @@ func (s *AuthzSuite) TestShouldApplyPolicyOfBypassDomain() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
targetURI := s.RequireParseRequestURI("https://bypass.example.com")
|
||||
|
||||
|
@ -250,11 +242,7 @@ func (s *AuthzSuite) TestShouldVerifyFailureToGetDetailsUsingBasicScheme() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
targetURI := s.RequireParseRequestURI("https://bypass.example.com")
|
||||
|
||||
|
@ -299,11 +287,7 @@ func (s *AuthzSuite) TestShouldNotFailOnMissingEmail() {
|
|||
|
||||
mock.Clock.Set(time.Now())
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
targetURI := s.RequireParseRequestURI("https://bypass.example.com")
|
||||
|
||||
|
@ -340,11 +324,7 @@ func (s *AuthzSuite) TestShouldApplyPolicyOfOneFactorDomain() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
targetURI := s.RequireParseRequestURI("https://one-factor.example.com")
|
||||
|
||||
|
@ -392,11 +372,7 @@ func (s *AuthzSuite) TestShouldHandleAnyCaseSchemeParameter() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
targetURI := s.RequireParseRequestURI("https://one-factor.example.com")
|
||||
|
||||
|
@ -435,11 +411,7 @@ func (s *AuthzSuite) TestShouldApplyPolicyOfTwoFactorDomain() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
targetURI := s.RequireParseRequestURI("https://two-factor.example.com")
|
||||
|
||||
|
@ -483,11 +455,7 @@ func (s *AuthzSuite) TestShouldApplyPolicyOfDenyDomain() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
targetURI := s.RequireParseRequestURI("https://deny.example.com")
|
||||
|
||||
|
@ -534,11 +502,7 @@ func (s *AuthzSuite) TestShouldApplyPolicyOfOneFactorDomainWithAuthorizationHead
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
targetURI := s.RequireParseRequestURI("https://one-factor.example.com")
|
||||
|
||||
|
@ -584,11 +548,7 @@ func (s *AuthzSuite) TestShouldHandleAuthzWithoutHeaderNoCookie() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
targetURI := s.RequireParseRequestURI("https://one-factor.example.com")
|
||||
|
||||
|
@ -621,11 +581,7 @@ func (s *AuthzSuite) TestShouldHandleAuthzWithEmptyAuthorizationHeader() {
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
targetURI := s.RequireParseRequestURI("https://one-factor.example.com")
|
||||
|
||||
|
@ -660,11 +616,7 @@ func (s *AuthzSuite) TestShouldHandleAuthzWithAuthorizationHeaderInvalidPassword
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
targetURI := s.RequireParseRequestURI("https://one-factor.example.com")
|
||||
|
||||
|
@ -700,11 +652,7 @@ func (s *AuthzSuite) TestShouldHandleAuthzWithIncorrectAuthHeader() { // TestSho
|
|||
|
||||
defer mock.Close()
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
targetURI := s.RequireParseRequestURI("https://one-factor.example.com")
|
||||
|
||||
|
@ -744,11 +692,7 @@ func (s *AuthzSuite) TestShouldDestroySessionWhenInactiveForTooLong() {
|
|||
|
||||
mock.Ctx.Configuration.Session.Cookies[0].Inactivity = testInactivity
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
targetURI := s.RequireParseRequestURI("https://two-factor.example.com")
|
||||
|
||||
|
@ -796,11 +740,7 @@ func (s *AuthzSuite) TestShouldNotDestroySessionWhenInactiveForTooLongRememberMe
|
|||
|
||||
mock.Ctx.Configuration.Session.Cookies[0].Inactivity = testInactivity
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
targetURI := s.RequireParseRequestURI("https://two-factor.example.com")
|
||||
|
||||
|
@ -850,11 +790,7 @@ func (s *AuthzSuite) TestShouldNotDestroySessionWhenNotInactiveForTooLong() {
|
|||
|
||||
mock.Ctx.Configuration.Session.Cookies[0].Inactivity = testInactivity
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
targetURI := s.RequireParseRequestURI("https://two-factor.example.com")
|
||||
|
||||
|
@ -905,11 +841,7 @@ func (s *AuthzSuite) TestShouldUpdateInactivityTimestampEvenWhenHittingForbidden
|
|||
|
||||
mock.Ctx.Configuration.Session.Cookies[0].Inactivity = testInactivity
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
targetURI := s.RequireParseRequestURI("https://deny.example.com")
|
||||
|
||||
|
@ -971,11 +903,7 @@ func (s *AuthzSuite) TestShouldNotRefreshUserDetailsFromBackendWhenRefreshDisabl
|
|||
mock.Ctx.Configuration.AuthenticationBackend.RefreshInterval = schema.ProfileRefreshDisabled
|
||||
mock.Ctx.Configuration.Session.Cookies[0].Inactivity = testInactivity
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
targetURI := s.RequireParseRequestURI("https://two-factor.example.com")
|
||||
|
||||
|
@ -1057,11 +985,7 @@ func (s *AuthzSuite) TestShouldDestroySessionWhenUserDoesNotExist() {
|
|||
|
||||
mock.Ctx.Configuration.Session.Cookies[0].Inactivity = testInactivity
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
targetURI := s.RequireParseRequestURI("https://two-factor.example.com")
|
||||
|
||||
|
@ -1149,11 +1073,7 @@ func (s *AuthzSuite) TestShouldUpdateRemovedUserGroupsFromBackendAndDeny() {
|
|||
|
||||
mock.Ctx.Configuration.Session.Cookies[0].Inactivity = testInactivity
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
targetURI := s.RequireParseRequestURI("https://admin.example.com")
|
||||
|
||||
|
@ -1239,11 +1159,7 @@ func (s *AuthzSuite) TestShouldUpdateAddedUserGroupsFromBackendAndDeny() {
|
|||
|
||||
mock.Ctx.Configuration.Session.Cookies[0].Inactivity = testInactivity
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
targetURI := s.RequireParseRequestURI("https://admin.example.com")
|
||||
|
||||
|
@ -1328,11 +1244,7 @@ func (s *AuthzSuite) TestShouldCheckValidSessionUsernameHeaderAndReturn200() {
|
|||
|
||||
mock.Ctx.Configuration.Session.Cookies[0].Inactivity = testInactivity
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
targetURI := s.RequireParseRequestURI("https://one-factor.example.com")
|
||||
|
||||
|
@ -1385,11 +1297,7 @@ func (s *AuthzSuite) TestShouldCheckInvalidSessionUsernameHeaderAndReturn401AndD
|
|||
|
||||
mock.Ctx.Configuration.Session.Cookies[0].Inactivity = testInactivity
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
targetURI := s.RequireParseRequestURI("https://one-factor.example.com")
|
||||
|
||||
|
@ -1462,11 +1370,7 @@ func (s *AuthzSuite) TestShouldNotRedirectRequestsForBypassACLWhenInactiveForToo
|
|||
|
||||
mock.Ctx.Configuration.Session.Cookies[0].Inactivity = testInactivity
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
targetURI := s.RequireParseRequestURI("https://bypass.example.com")
|
||||
|
||||
|
@ -1520,7 +1424,7 @@ func (s *AuthzSuite) TestShouldNotRedirectRequestsForBypassACLWhenInactiveForToo
|
|||
}
|
||||
|
||||
func (s *AuthzSuite) TestShouldFailToParsePortalURL() {
|
||||
if s.setRequest == nil || s.implementation == AuthzImplAuthRequest {
|
||||
if s.setRequest == nil {
|
||||
s.T().Skip()
|
||||
}
|
||||
|
||||
|
@ -1538,20 +1442,20 @@ func (s *AuthzSuite) TestShouldFailToParsePortalURL() {
|
|||
|
||||
mock.Ctx.Configuration.Session.Cookies[0].Inactivity = testInactivity
|
||||
|
||||
for i, cookie := range mock.Ctx.Configuration.Session.Cookies {
|
||||
mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain))
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
s.ConfigureMockSessionProviderWithAutomaticAutheliaURLs(mock)
|
||||
|
||||
targetURI := s.RequireParseRequestURI("https://bypass.example.com")
|
||||
|
||||
s.setRequest(mock.Ctx, fasthttp.MethodGet, targetURI, true, false)
|
||||
|
||||
expected := fasthttp.StatusBadRequest
|
||||
|
||||
switch s.implementation {
|
||||
case AuthzImplLegacy:
|
||||
expected = fasthttp.StatusUnauthorized
|
||||
|
||||
mock.Ctx.RequestCtx.QueryArgs().Set(queryArgRD, "JKL$#N%KJ#@$N")
|
||||
case AuthzImplForwardAuth:
|
||||
case AuthzImplForwardAuth, AuthzImplAuthRequest:
|
||||
mock.Ctx.RequestCtx.QueryArgs().Set("authelia_url", "JKL$#N%KJ#@$N")
|
||||
case AuthzImplExtAuthz:
|
||||
mock.Ctx.Request.Header.Set("X-Authelia-URL", "JKL$#N%KJ#@$N")
|
||||
|
@ -1559,7 +1463,10 @@ func (s *AuthzSuite) TestShouldFailToParsePortalURL() {
|
|||
|
||||
authz.Handler(mock.Ctx)
|
||||
|
||||
s.Equal(fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||
s.Equal(expected, mock.Ctx.Response.StatusCode())
|
||||
s.Equal(fmt.Sprintf("%d %s", expected, fasthttp.StatusMessage(expected)), string(mock.Ctx.Response.Body()))
|
||||
s.Equal("", string(mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation)))
|
||||
s.Equal("text/plain; charset=utf-8", string(mock.Ctx.Response.Header.Peek(fasthttp.HeaderContentType)))
|
||||
}
|
||||
|
||||
func setRequestXHRValues(ctx *middlewares.AutheliaCtx, accept, xhr bool) {
|
||||
|
@ -1571,3 +1478,8 @@ func setRequestXHRValues(ctx *middlewares.AutheliaCtx, accept, xhr bool) {
|
|||
ctx.Request.Header.Set(fasthttp.HeaderXRequestedWith, "XMLHttpRequest")
|
||||
}
|
||||
}
|
||||
|
||||
type urlpair struct {
|
||||
TargetURI *url.URL
|
||||
AutheliaURI *url.URL
|
||||
}
|
||||
|
|
|
@ -10,7 +10,8 @@ import (
|
|||
"github.com/authelia/authelia/v4/internal/session"
|
||||
)
|
||||
|
||||
// Authz is a type which is a effectively is a middlewares.RequestHandler for authorization requests.
|
||||
// Authz is a type which is a effectively is a middlewares.RequestHandler for authorization requests. This should NOT be
|
||||
// manually used and developers should instead use NewAuthzBuilder.
|
||||
type Authz struct {
|
||||
config AuthzConfig
|
||||
|
||||
|
@ -23,7 +24,7 @@ type Authz struct {
|
|||
handleAuthorized HandlerAuthzAuthorized
|
||||
handleUnauthorized HandlerAuthzUnauthorized
|
||||
|
||||
legacy bool
|
||||
implementation AuthzImplementation
|
||||
}
|
||||
|
||||
// HandlerAuthzUnauthorized is a Authz handler func that handles unauthorized responses.
|
||||
|
@ -75,20 +76,17 @@ type Authn struct {
|
|||
// AuthzConfig represents the configuration elements of the Authz type.
|
||||
type AuthzConfig struct {
|
||||
RefreshInterval time.Duration
|
||||
Domains []AuthzDomain
|
||||
}
|
||||
|
||||
// AuthzDomain represents a domain for the AuthzConfig.
|
||||
type AuthzDomain struct {
|
||||
Name string
|
||||
PortalURL *url.URL
|
||||
// StatusCodeBadRequest is sent for configuration issues prior to performing authorization checks. It's set by the
|
||||
// builder.
|
||||
StatusCodeBadRequest int
|
||||
}
|
||||
|
||||
// AuthzBuilder is a builder pattern for the Authz type.
|
||||
type AuthzBuilder struct {
|
||||
config AuthzConfig
|
||||
impl AuthzImplementation
|
||||
strategies []AuthnStrategy
|
||||
config AuthzConfig
|
||||
implementation AuthzImplementation
|
||||
strategies []AuthnStrategy
|
||||
}
|
||||
|
||||
// AuthnStrategy is a strategy used for Authz authentication.
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/authentication"
|
||||
"github.com/authelia/authelia/v4/internal/authorization"
|
||||
"github.com/authelia/authelia/v4/internal/middlewares"
|
||||
|
@ -23,7 +20,7 @@ func friendlyMethod(m string) (fm string) {
|
|||
func friendlyUsername(username string) (fusername string) {
|
||||
switch username {
|
||||
case "":
|
||||
return "<anonymous>"
|
||||
return anonymous
|
||||
default:
|
||||
return username
|
||||
}
|
||||
|
@ -54,39 +51,55 @@ func generateVerifySessionHasUpToDateProfileTraceLogs(ctx *middlewares.AutheliaC
|
|||
emailsAdded, emailsRemoved := utils.StringSlicesDelta(userSession.Emails, details.Emails)
|
||||
nameDelta := userSession.DisplayName != details.DisplayName
|
||||
|
||||
var groupsDelta []string
|
||||
if len(groupsAdded) != 0 {
|
||||
groupsDelta = append(groupsDelta, fmt.Sprintf("added: %s.", strings.Join(groupsAdded, ", ")))
|
||||
fields := map[string]any{"username": userSession.Username}
|
||||
msg := "User session groups are current"
|
||||
|
||||
if len(groupsAdded) != 0 || len(groupsRemoved) != 0 {
|
||||
if len(groupsAdded) != 0 {
|
||||
fields["added"] = groupsAdded
|
||||
}
|
||||
|
||||
if len(groupsRemoved) != 0 {
|
||||
fields["removed"] = groupsRemoved
|
||||
}
|
||||
|
||||
msg = "User session groups were updated"
|
||||
}
|
||||
|
||||
if len(groupsRemoved) != 0 {
|
||||
groupsDelta = append(groupsDelta, fmt.Sprintf("removed: %s.", strings.Join(groupsRemoved, ", ")))
|
||||
}
|
||||
ctx.Logger.WithFields(fields).Trace(msg)
|
||||
|
||||
if len(groupsDelta) != 0 {
|
||||
ctx.Logger.Tracef("Updated groups detected for %s. %s", userSession.Username, strings.Join(groupsDelta, " "))
|
||||
if len(emailsAdded) != 0 || len(emailsRemoved) != 0 {
|
||||
if len(emailsAdded) != 0 {
|
||||
fields["added"] = emailsAdded
|
||||
} else {
|
||||
delete(fields, "added")
|
||||
}
|
||||
|
||||
if len(emailsRemoved) != 0 {
|
||||
fields["removed"] = emailsRemoved
|
||||
} else {
|
||||
delete(fields, "removed")
|
||||
}
|
||||
|
||||
msg = "User session emails were updated"
|
||||
} else {
|
||||
ctx.Logger.Tracef("No updated groups detected for %s", userSession.Username)
|
||||
msg = "User session emails are current"
|
||||
|
||||
delete(fields, "added")
|
||||
delete(fields, "removed")
|
||||
}
|
||||
|
||||
var emailsDelta []string
|
||||
if len(emailsAdded) != 0 {
|
||||
emailsDelta = append(emailsDelta, fmt.Sprintf("added: %s.", strings.Join(emailsAdded, ", ")))
|
||||
}
|
||||
|
||||
if len(emailsRemoved) != 0 {
|
||||
emailsDelta = append(emailsDelta, fmt.Sprintf("removed: %s.", strings.Join(emailsRemoved, ", ")))
|
||||
}
|
||||
|
||||
if len(emailsDelta) != 0 {
|
||||
ctx.Logger.Tracef("Updated emails detected for %s. %s", userSession.Username, strings.Join(emailsDelta, " "))
|
||||
} else {
|
||||
ctx.Logger.Tracef("No updated emails detected for %s", userSession.Username)
|
||||
}
|
||||
ctx.Logger.WithFields(fields).Trace(msg)
|
||||
|
||||
if nameDelta {
|
||||
ctx.Logger.Tracef("Updated display name detected for %s. Added: %s. Removed: %s.", userSession.Username, details.DisplayName, userSession.DisplayName)
|
||||
ctx.Logger.
|
||||
WithFields(map[string]any{
|
||||
"username": userSession.Username,
|
||||
"before": userSession.DisplayName,
|
||||
"after": details.DisplayName,
|
||||
}).
|
||||
Trace("User session display name updated")
|
||||
} else {
|
||||
ctx.Logger.Tracef("No updated display name detected for %s", userSession.Username)
|
||||
ctx.Logger.Trace("User session display name is current")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ func OpenIDConnectUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter,
|
|||
req.Context(), fosite.AccessTokenFromRequest(req), fosite.AccessToken, oidcSession); err != nil {
|
||||
rfc := fosite.ErrorToRFC6749Error(err)
|
||||
|
||||
ctx.Logger.Errorf("UserInfo Request failed with error: %+v", rfc)
|
||||
ctx.Logger.Errorf("UserInfo Request failed with error: %s", rfc.WithExposeDebug(true).GetDescription())
|
||||
|
||||
if rfc.StatusCode() == http.StatusUnauthorized {
|
||||
rw.Header().Set(fasthttp.HeaderWWWAuthenticate, fmt.Sprintf(`Bearer error="%s",error_description="%s"`, rfc.ErrorField, rfc.GetDescription()))
|
||||
|
@ -47,7 +47,7 @@ func OpenIDConnectUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter,
|
|||
clientID := requester.GetClient().GetID()
|
||||
|
||||
if tokenType != fosite.AccessToken {
|
||||
ctx.Logger.Errorf("UserInfo Request with id '%s' on client with id '%s' failed with error: bearer authorization failed as the token is not an access_token", requester.GetID(), client.GetID())
|
||||
ctx.Logger.Errorf("UserInfo Request with id '%s' on client with id '%s' failed with error: bearer authorization failed as the token is not an access token", requester.GetID(), client.GetID())
|
||||
|
||||
errStr := "Only access tokens are allowed in the authorization header."
|
||||
rw.Header().Set(fasthttp.HeaderWWWAuthenticate, fmt.Sprintf(`Bearer error="invalid_token",error_description="%s"`, errStr))
|
||||
|
@ -57,7 +57,11 @@ func OpenIDConnectUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter,
|
|||
}
|
||||
|
||||
if client, err = ctx.Providers.OpenIDConnect.GetFullClient(clientID); err != nil {
|
||||
ctx.Providers.OpenIDConnect.WriteError(rw, req, errors.WithStack(fosite.ErrServerError.WithHint("Unable to assert type of client")))
|
||||
rfc := fosite.ErrorToRFC6749Error(err)
|
||||
|
||||
ctx.Logger.Errorf("UserInfo Request with id '%s' on client with id '%s' failed to retrieve client configuration with error: %s", requester.GetID(), client.GetID(), rfc.WithExposeDebug(true).GetDescription())
|
||||
|
||||
ctx.Providers.OpenIDConnect.WriteError(rw, req, errors.WithStack(rfc))
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -100,7 +104,7 @@ func OpenIDConnectUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter,
|
|||
var jti uuid.UUID
|
||||
|
||||
if jti, err = uuid.NewRandom(); err != nil {
|
||||
ctx.Providers.OpenIDConnect.WriteError(rw, req, fosite.ErrServerError.WithHintf("Could not generate JTI."))
|
||||
ctx.Providers.OpenIDConnect.WriteError(rw, req, fosite.ErrServerError.WithHint("Could not generate JTI."))
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -120,9 +124,9 @@ func OpenIDConnectUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter,
|
|||
return
|
||||
}
|
||||
|
||||
rw.Header().Set("Content-Type", "application/jwt")
|
||||
rw.Header().Set(fasthttp.HeaderContentType, "application/jwt")
|
||||
_, _ = rw.Write([]byte(token))
|
||||
case "none", "":
|
||||
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)))
|
||||
|
|
|
@ -586,13 +586,27 @@ func (ctx *AutheliaCtx) AcceptsMIME(mime string) (acceptsMime bool) {
|
|||
}
|
||||
|
||||
// SpecialRedirect performs a redirect similar to fasthttp.RequestCtx except it allows statusCode 401 and includes body
|
||||
// content in the form of a link to the location.
|
||||
// content in the form of a link to the location if the request method was not head.
|
||||
func (ctx *AutheliaCtx) SpecialRedirect(uri string, statusCode int) {
|
||||
var u []byte
|
||||
|
||||
u, statusCode = ctx.setSpecialRedirect(uri, statusCode)
|
||||
|
||||
ctx.SetContentTypeTextHTML()
|
||||
ctx.SetBodyString(fmt.Sprintf("<a href=\"%s\">%d %s</a>", utils.StringHTMLEscape(string(u)), statusCode, fasthttp.StatusMessage(statusCode)))
|
||||
}
|
||||
|
||||
// SpecialRedirectNoBody performs a redirect similar to fasthttp.RequestCtx except it allows statusCode 401 and includes
|
||||
// no body.
|
||||
func (ctx *AutheliaCtx) SpecialRedirectNoBody(uri string, statusCode int) {
|
||||
_, _ = ctx.setSpecialRedirect(uri, statusCode)
|
||||
}
|
||||
|
||||
func (ctx *AutheliaCtx) setSpecialRedirect(uri string, statusCode int) ([]byte, int) {
|
||||
if statusCode < fasthttp.StatusMovedPermanently || (statusCode > fasthttp.StatusSeeOther && statusCode != fasthttp.StatusTemporaryRedirect && statusCode != fasthttp.StatusPermanentRedirect && statusCode != fasthttp.StatusUnauthorized) {
|
||||
statusCode = fasthttp.StatusFound
|
||||
}
|
||||
|
||||
ctx.SetContentTypeTextHTML()
|
||||
ctx.SetStatusCode(statusCode)
|
||||
|
||||
u := fasthttp.AcquireURI()
|
||||
|
@ -600,11 +614,13 @@ func (ctx *AutheliaCtx) SpecialRedirect(uri string, statusCode int) {
|
|||
ctx.URI().CopyTo(u)
|
||||
u.Update(uri)
|
||||
|
||||
ctx.Response.Header.SetBytesKV(headerLocation, u.FullURI())
|
||||
raw := u.FullURI()
|
||||
|
||||
ctx.SetBodyString(fmt.Sprintf("<a href=\"%s\">%d %s</a>", utils.StringHTMLEscape(string(u.FullURI())), statusCode, fasthttp.StatusMessage(statusCode)))
|
||||
ctx.Response.Header.SetBytesKV(headerLocation, raw)
|
||||
|
||||
fasthttp.ReleaseURI(u)
|
||||
|
||||
return raw, statusCode
|
||||
}
|
||||
|
||||
// RecordAuthn records authentication metrics.
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SchemaMigration represents an intended migration.
|
||||
type SchemaMigration struct {
|
||||
Version int
|
||||
|
@ -9,6 +13,11 @@ type SchemaMigration struct {
|
|||
Query string
|
||||
}
|
||||
|
||||
// NotEmpty returns true if the SchemaMigration is not an empty string.
|
||||
func (m SchemaMigration) NotEmpty() bool {
|
||||
return len(strings.TrimSpace(m.Query)) != 0
|
||||
}
|
||||
|
||||
// Before returns the version the schema should be at Before the migration is applied.
|
||||
func (m SchemaMigration) Before() (before int) {
|
||||
if m.Up {
|
||||
|
|
|
@ -79,7 +79,7 @@ func (s *Store) GetClientPolicy(id string) (level authorization.Level) {
|
|||
func (s *Store) GetFullClient(id string) (client *Client, err error) {
|
||||
client, ok := s.clients[id]
|
||||
if !ok {
|
||||
return nil, fosite.ErrNotFound
|
||||
return nil, fosite.ErrInvalidClient
|
||||
}
|
||||
|
||||
return client, nil
|
||||
|
|
|
@ -59,7 +59,7 @@ func TestOpenIDConnectStore_GetInternalClient(t *testing.T) {
|
|||
}, nil)
|
||||
|
||||
client, err := s.GetClient(context.Background(), "myinvalidclient")
|
||||
assert.EqualError(t, err, "not_found")
|
||||
assert.EqualError(t, err, "invalid_client")
|
||||
assert.Nil(t, client)
|
||||
|
||||
client, err = s.GetClient(context.Background(), "myclient")
|
||||
|
@ -113,7 +113,7 @@ func TestOpenIDConnectStore_GetInternalClient_InvalidClient(t *testing.T) {
|
|||
|
||||
client, err := s.GetFullClient("another-client")
|
||||
assert.Nil(t, client)
|
||||
assert.EqualError(t, err, "not_found")
|
||||
assert.EqualError(t, err, "invalid_client")
|
||||
}
|
||||
|
||||
func TestOpenIDConnectStore_IsValidClientID(t *testing.T) {
|
||||
|
|
|
@ -201,8 +201,8 @@ func handleRouter(config *schema.Configuration, providers middlewares.Providers)
|
|||
case "legacy":
|
||||
log.
|
||||
WithField("path_prefix", pathAuthzLegacy).
|
||||
WithField("impl", endpoint.Implementation).
|
||||
WithField("methods", []string{"*"}).
|
||||
WithField("implementation", endpoint.Implementation).
|
||||
WithField("methods", "*").
|
||||
Trace("Registering Authz Endpoint")
|
||||
|
||||
r.ANY(pathAuthzLegacy, handler)
|
||||
|
@ -212,8 +212,8 @@ func handleRouter(config *schema.Configuration, providers middlewares.Providers)
|
|||
case handlers.AuthzImplLegacy.String(), handlers.AuthzImplExtAuthz.String():
|
||||
log.
|
||||
WithField("path_prefix", uri).
|
||||
WithField("impl", endpoint.Implementation).
|
||||
WithField("methods", []string{"*"}).
|
||||
WithField("implementation", endpoint.Implementation).
|
||||
WithField("methods", "*").
|
||||
Trace("Registering Authz Endpoint")
|
||||
|
||||
r.ANY(uri, handler)
|
||||
|
@ -221,7 +221,7 @@ func handleRouter(config *schema.Configuration, providers middlewares.Providers)
|
|||
default:
|
||||
log.
|
||||
WithField("path", uri).
|
||||
WithField("impl", endpoint.Implementation).
|
||||
WithField("implementation", endpoint.Implementation).
|
||||
WithField("methods", []string{fasthttp.MethodGet, fasthttp.MethodHead}).
|
||||
Trace("Registering Authz Endpoint")
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -26,12 +25,6 @@ import (
|
|||
"github.com/authelia/authelia/v4/internal/utils"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
fmt.Println(path.Join("/api/authz/", "abc"))
|
||||
fmt.Println(path.Join("/api/authz/", "abc/123/", "{path:*}"))
|
||||
fmt.Println(path.Join("/api/authz/", "abc/123/"))
|
||||
}
|
||||
|
||||
// TemporaryCertificate contains the FD of 2 temporary files containing the PEM format of the certificate and private key.
|
||||
type TemporaryCertificate struct {
|
||||
CertFile *os.File
|
||||
|
|
|
@ -61,16 +61,27 @@ func ServeTemplatedFile(t templates.Template, opts *TemplatedFileOptions) middle
|
|||
|
||||
var (
|
||||
rememberMe string
|
||||
baseURL string
|
||||
domain string
|
||||
provider *session.Session
|
||||
)
|
||||
|
||||
if provider, err = ctx.GetSessionProvider(); err == nil {
|
||||
if provider.Config.AutheliaURL != nil {
|
||||
baseURL = provider.Config.AutheliaURL.String()
|
||||
} else {
|
||||
baseURL = ctx.RootURLSlash().String()
|
||||
}
|
||||
|
||||
domain = provider.Config.Domain
|
||||
rememberMe = strconv.FormatBool(!provider.Config.DisableRememberMe)
|
||||
} else {
|
||||
baseURL = ctx.RootURLSlash().String()
|
||||
}
|
||||
|
||||
data := &bytes.Buffer{}
|
||||
|
||||
if err = t.Execute(data, opts.CommonData(ctx.BasePath(), ctx.RootURLSlash().String(), nonce, logoOverride, rememberMe)); err != nil {
|
||||
if err = t.Execute(data, opts.CommonData(ctx.BasePath(), baseURL, domain, nonce, logoOverride, rememberMe)); err != nil {
|
||||
ctx.RequestCtx.Error("an error occurred", fasthttp.StatusServiceUnavailable)
|
||||
ctx.Logger.WithError(err).Errorf("Error occcurred rendering template")
|
||||
|
||||
|
@ -118,11 +129,28 @@ func ServeTemplatedOpenAPI(t templates.Template, opts *TemplatedFileOptions) mid
|
|||
ctx.SetContentTypeTextPlain()
|
||||
}
|
||||
|
||||
var err error
|
||||
var (
|
||||
baseURL string
|
||||
domain string
|
||||
provider *session.Session
|
||||
err error
|
||||
)
|
||||
|
||||
if provider, err = ctx.GetSessionProvider(); err == nil {
|
||||
if provider.Config.AutheliaURL != nil {
|
||||
baseURL = provider.Config.AutheliaURL.String()
|
||||
} else {
|
||||
baseURL = ctx.RootURLSlash().String()
|
||||
}
|
||||
|
||||
domain = provider.Config.Domain
|
||||
} else {
|
||||
baseURL = ctx.RootURLSlash().String()
|
||||
}
|
||||
|
||||
data := &bytes.Buffer{}
|
||||
|
||||
if err = t.Execute(data, opts.OpenAPIData(ctx.BasePath(), ctx.RootURLSlash().String(), nonce)); err != nil {
|
||||
if err = t.Execute(data, opts.OpenAPIData(ctx.BasePath(), baseURL, domain, nonce)); err != nil {
|
||||
ctx.RequestCtx.Error("an error occurred", fasthttp.StatusServiceUnavailable)
|
||||
ctx.Logger.WithError(err).Errorf("Error occcurred rendering template")
|
||||
|
||||
|
@ -285,14 +313,15 @@ type TemplatedFileOptions struct {
|
|||
}
|
||||
|
||||
// CommonData returns a TemplatedFileCommonData with the dynamic options.
|
||||
func (options *TemplatedFileOptions) CommonData(base, baseURL, nonce, logoOverride, rememberMe string) TemplatedFileCommonData {
|
||||
func (options *TemplatedFileOptions) CommonData(base, baseURL, domain, nonce, logoOverride, rememberMe string) TemplatedFileCommonData {
|
||||
if rememberMe != "" {
|
||||
return options.commonDataWithRememberMe(base, baseURL, nonce, logoOverride, rememberMe)
|
||||
return options.commonDataWithRememberMe(base, baseURL, domain, nonce, logoOverride, rememberMe)
|
||||
}
|
||||
|
||||
return TemplatedFileCommonData{
|
||||
Base: base,
|
||||
BaseURL: baseURL,
|
||||
Domain: domain,
|
||||
CSPNonce: nonce,
|
||||
LogoOverride: logoOverride,
|
||||
DuoSelfEnrollment: options.DuoSelfEnrollment,
|
||||
|
@ -307,10 +336,11 @@ func (options *TemplatedFileOptions) CommonData(base, baseURL, nonce, logoOverri
|
|||
}
|
||||
|
||||
// CommonDataWithRememberMe returns a TemplatedFileCommonData with the dynamic options.
|
||||
func (options *TemplatedFileOptions) commonDataWithRememberMe(base, baseURL, nonce, logoOverride, rememberMe string) TemplatedFileCommonData {
|
||||
func (options *TemplatedFileOptions) commonDataWithRememberMe(base, baseURL, domain, nonce, logoOverride, rememberMe string) TemplatedFileCommonData {
|
||||
return TemplatedFileCommonData{
|
||||
Base: base,
|
||||
BaseURL: baseURL,
|
||||
Domain: domain,
|
||||
CSPNonce: nonce,
|
||||
LogoOverride: logoOverride,
|
||||
DuoSelfEnrollment: options.DuoSelfEnrollment,
|
||||
|
@ -323,10 +353,11 @@ func (options *TemplatedFileOptions) commonDataWithRememberMe(base, baseURL, non
|
|||
}
|
||||
|
||||
// OpenAPIData returns a TemplatedFileOpenAPIData with the dynamic options.
|
||||
func (options *TemplatedFileOptions) OpenAPIData(base, baseURL, nonce string) TemplatedFileOpenAPIData {
|
||||
func (options *TemplatedFileOptions) OpenAPIData(base, baseURL, domain, nonce string) TemplatedFileOpenAPIData {
|
||||
return TemplatedFileOpenAPIData{
|
||||
Base: base,
|
||||
BaseURL: baseURL,
|
||||
Domain: domain,
|
||||
CSPNonce: nonce,
|
||||
|
||||
Session: options.Session,
|
||||
|
@ -343,6 +374,7 @@ func (options *TemplatedFileOptions) OpenAPIData(base, baseURL, nonce string) Te
|
|||
type TemplatedFileCommonData struct {
|
||||
Base string
|
||||
BaseURL string
|
||||
Domain string
|
||||
CSPNonce string
|
||||
LogoOverride string
|
||||
DuoSelfEnrollment string
|
||||
|
@ -359,6 +391,7 @@ type TemplatedFileCommonData struct {
|
|||
type TemplatedFileOpenAPIData struct {
|
||||
Base string
|
||||
BaseURL string
|
||||
Domain string
|
||||
CSPNonce string
|
||||
Session string
|
||||
PasswordReset bool
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/valyala/fasthttp"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||
"github.com/authelia/authelia/v4/internal/mocks"
|
||||
"github.com/authelia/authelia/v4/internal/session"
|
||||
"github.com/authelia/authelia/v4/internal/templates"
|
||||
)
|
||||
|
||||
const (
|
||||
assetsOpenAPIPath = "public_html/api/openapi.yml"
|
||||
localOpenAPIPath = "../../api/openapi.yml"
|
||||
)
|
||||
|
||||
type ReadFileOpenAPI struct{}
|
||||
|
||||
func (lfs *ReadFileOpenAPI) Open(name string) (fs.File, error) {
|
||||
switch name {
|
||||
case assetsOpenAPIPath:
|
||||
return os.Open(localOpenAPIPath)
|
||||
default:
|
||||
return assets.Open(name)
|
||||
}
|
||||
}
|
||||
|
||||
func (lfs *ReadFileOpenAPI) ReadFile(name string) ([]byte, error) {
|
||||
switch name {
|
||||
case assetsOpenAPIPath:
|
||||
return os.ReadFile(localOpenAPIPath)
|
||||
default:
|
||||
return assets.ReadFile(name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShouldTemplateOpenAPI(t *testing.T) {
|
||||
provider, err := templates.New(templates.Config{})
|
||||
require.NoError(t, err)
|
||||
|
||||
fs := &ReadFileOpenAPI{}
|
||||
|
||||
require.NoError(t, provider.LoadTemplatedAssets(fs))
|
||||
|
||||
mock := mocks.NewMockAutheliaCtx(t)
|
||||
|
||||
mock.Ctx.Configuration.Server = schema.DefaultServerConfiguration
|
||||
mock.Ctx.Configuration.Session = schema.SessionConfiguration{
|
||||
Cookies: []schema.SessionCookieConfiguration{
|
||||
{
|
||||
SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
|
||||
Domain: "example.com",
|
||||
},
|
||||
AutheliaURL: &url.URL{Scheme: "https", Host: "auth.example.com", Path: "/"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
|
||||
|
||||
opts := NewTemplatedFileOptions(&mock.Ctx.Configuration)
|
||||
|
||||
handler := ServeTemplatedOpenAPI(provider.GetAssetOpenAPISpecTemplate(), opts)
|
||||
|
||||
mock.Ctx.Request.Header.Set(fasthttp.HeaderXForwardedProto, "https")
|
||||
mock.Ctx.Request.Header.Set(fasthttp.HeaderXForwardedHost, "example.com")
|
||||
mock.Ctx.Request.Header.Set("X-Forwarded-Uri", "/api/openapi.yml")
|
||||
|
||||
handler(mock.Ctx)
|
||||
|
||||
assert.Equal(t, fasthttp.StatusOK, mock.Ctx.Response.StatusCode())
|
||||
|
||||
body := string(mock.Ctx.Response.Body())
|
||||
|
||||
assert.NotEqual(t, "", body)
|
||||
assert.Contains(t, body, "example: 'https://auth.example.com/?rd=https%3A%2F%2Fexample.com%2F&rm=GET'")
|
||||
}
|
|
@ -4,6 +4,7 @@ import (
|
|||
"embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -15,8 +16,12 @@ import (
|
|||
var migrationsFS embed.FS
|
||||
|
||||
func latestMigrationVersion(providerName string) (version int, err error) {
|
||||
entries, err := migrationsFS.ReadDir("migrations")
|
||||
if err != nil {
|
||||
var (
|
||||
entries []fs.DirEntry
|
||||
migration model.SchemaMigration
|
||||
)
|
||||
|
||||
if entries, err = migrationsFS.ReadDir("migrations"); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
|
@ -25,21 +30,20 @@ func latestMigrationVersion(providerName string) (version int, err error) {
|
|||
continue
|
||||
}
|
||||
|
||||
m, err := scanMigration(entry.Name())
|
||||
if err != nil {
|
||||
if migration, err = scanMigration(entry.Name()); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
if m.Provider != providerName {
|
||||
if migration.Provider != providerName && migration.Provider != providerAll {
|
||||
continue
|
||||
}
|
||||
|
||||
if !m.Up {
|
||||
if !migration.Up {
|
||||
continue
|
||||
}
|
||||
|
||||
if m.Version > version {
|
||||
version = m.Version
|
||||
if migration.Version > version {
|
||||
version = migration.Version
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,12 +54,17 @@ func latestMigrationVersion(providerName string) (version int, err error) {
|
|||
// target versions. If the target version is -1 this indicates the latest version. If the target version is 0
|
||||
// this indicates the database zero state.
|
||||
func loadMigrations(providerName string, prior, target int) (migrations []model.SchemaMigration, err error) {
|
||||
if prior == target && (prior != -1 || target != -1) {
|
||||
if prior == target {
|
||||
return nil, ErrMigrateCurrentVersionSameAsTarget
|
||||
}
|
||||
|
||||
entries, err := migrationsFS.ReadDir("migrations")
|
||||
if err != nil {
|
||||
var (
|
||||
migrationsAll []model.SchemaMigration
|
||||
migration model.SchemaMigration
|
||||
entries []fs.DirEntry
|
||||
)
|
||||
|
||||
if entries, err = migrationsFS.ReadDir("migrations"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -66,8 +75,7 @@ func loadMigrations(providerName string, prior, target int) (migrations []model.
|
|||
continue
|
||||
}
|
||||
|
||||
migration, err := scanMigration(entry.Name())
|
||||
if err != nil {
|
||||
if migration, err = scanMigration(entry.Name()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -75,7 +83,28 @@ func loadMigrations(providerName string, prior, target int) (migrations []model.
|
|||
continue
|
||||
}
|
||||
|
||||
migrations = append(migrations, migration)
|
||||
if migration.Provider == providerAll {
|
||||
migrationsAll = append(migrationsAll, migration)
|
||||
} else {
|
||||
migrations = append(migrations, migration)
|
||||
}
|
||||
}
|
||||
|
||||
// Add "all" migrations for versions that don't exist.
|
||||
for _, am := range migrationsAll {
|
||||
found := false
|
||||
|
||||
for _, m := range migrations {
|
||||
if m.Version == am.Version {
|
||||
found = true
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
migrations = append(migrations, am)
|
||||
}
|
||||
}
|
||||
|
||||
if up {
|
||||
|
@ -103,7 +132,7 @@ func skipMigration(providerName string, up bool, target, prior int, migration *m
|
|||
return true
|
||||
}
|
||||
|
||||
if target != -1 && (migration.Version > target || migration.Version <= prior) {
|
||||
if migration.Version > target || migration.Version <= prior {
|
||||
// Skip if the migration version is greater than the target or less than or equal to the previous version.
|
||||
return true
|
||||
}
|
||||
|
@ -113,12 +142,6 @@ func skipMigration(providerName string, up bool, target, prior int, migration *m
|
|||
return true
|
||||
}
|
||||
|
||||
if migration.Version == 1 && target == -1 {
|
||||
// Skip if we're targeting pre1 and the migration version is 1 as this migration will destroy all data
|
||||
// preventing a successful migration.
|
||||
return true
|
||||
}
|
||||
|
||||
if migration.Version <= target || migration.Version > prior {
|
||||
// Skip the migration if we want to go down and the migration version is less than or equal to the target
|
||||
// or greater than the previous version.
|
||||
|
@ -141,8 +164,9 @@ func scanMigration(m string) (migration model.SchemaMigration, err error) {
|
|||
Provider: result[reMigration.SubexpIndex("Provider")],
|
||||
}
|
||||
|
||||
data, err := migrationsFS.ReadFile(fmt.Sprintf("migrations/%s", m))
|
||||
if err != nil {
|
||||
var data []byte
|
||||
|
||||
if data, err = migrationsFS.ReadFile(fmt.Sprintf("migrations/%s", m)); err != nil {
|
||||
return model.SchemaMigration{}, err
|
||||
}
|
||||
|
||||
|
|
|
@ -56,30 +56,8 @@ ALTER TABLE totp_configurations
|
|||
DROP INDEX IF EXISTS totp_configurations_username_key1;
|
||||
DROP INDEX IF EXISTS totp_configurations_username_key;
|
||||
|
||||
ALTER TABLE totp_configurations
|
||||
RENAME TO _bkp_UP_V0007_totp_configurations;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS totp_configurations (
|
||||
id SERIAL CONSTRAINT totp_configurations_pkey PRIMARY KEY,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
last_used_at TIMESTAMP WITH TIME ZONE NULL DEFAULT NULL,
|
||||
username VARCHAR(100) NOT NULL,
|
||||
issuer VARCHAR(100),
|
||||
algorithm VARCHAR(6) NOT NULL DEFAULT 'SHA1',
|
||||
digits INTEGER NOT NULL DEFAULT 6,
|
||||
period INTEGER NOT NULL DEFAULT 30,
|
||||
secret BYTEA NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX totp_configurations_username_key ON totp_configurations (username);
|
||||
|
||||
INSERT INTO totp_configurations (created_at, last_used_at, username, issuer, algorithm, digits, period, secret)
|
||||
SELECT created_at, last_used_at, username, issuer, algorithm, digits, period, secret
|
||||
FROM _bkp_UP_V0007_totp_configurations
|
||||
ORDER BY id;
|
||||
|
||||
DROP TABLE IF EXISTS _bkp_UP_V0007_totp_configurations;
|
||||
|
||||
ALTER TABLE webauthn_devices
|
||||
DROP CONSTRAINT IF EXISTS webauthn_devices_username_description_key1,
|
||||
DROP CONSTRAINT IF EXISTS webauthn_devices_kid_key1,
|
||||
|
@ -97,34 +75,9 @@ DROP INDEX IF EXISTS webauthn_devices_username_description_key;
|
|||
DROP INDEX IF EXISTS webauthn_devices_kid_key;
|
||||
DROP INDEX IF EXISTS webauthn_devices_lookup_key;
|
||||
|
||||
ALTER TABLE webauthn_devices
|
||||
RENAME TO _bkp_UP_V0007_webauthn_devices;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS webauthn_devices (
|
||||
id SERIAL CONSTRAINT webauthn_devices_pkey PRIMARY KEY,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
last_used_at TIMESTAMP WITH TIME ZONE NULL DEFAULT NULL,
|
||||
rpid TEXT,
|
||||
username VARCHAR(100) NOT NULL,
|
||||
description VARCHAR(30) NOT NULL DEFAULT 'Primary',
|
||||
kid VARCHAR(512) NOT NULL,
|
||||
public_key BYTEA NOT NULL,
|
||||
attestation_type VARCHAR(32),
|
||||
transport VARCHAR(20) DEFAULT '',
|
||||
aaguid CHAR(36) NOT NULL,
|
||||
sign_count INTEGER DEFAULT 0,
|
||||
clone_warning BOOLEAN NOT NULL DEFAULT FALSE
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX webauthn_devices_kid_key ON webauthn_devices (kid);
|
||||
CREATE UNIQUE INDEX webauthn_devices_lookup_key ON webauthn_devices (username, description);
|
||||
|
||||
INSERT INTO webauthn_devices (created_at, last_used_at, rpid, username, description, kid, public_key, attestation_type, transport, aaguid, sign_count, clone_warning)
|
||||
SELECT created_at, last_used_at, rpid, username, description, kid, public_key, attestation_type, transport, aaguid, sign_count, clone_warning
|
||||
FROM _bkp_UP_V0007_webauthn_devices;
|
||||
|
||||
DROP TABLE IF EXISTS _bkp_UP_V0007_webauthn_devices;
|
||||
|
||||
ALTER TABLE oauth2_consent_session
|
||||
DROP CONSTRAINT oauth2_consent_session_subject_fkey,
|
||||
DROP CONSTRAINT oauth2_consent_session_preconfiguration_fkey;
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
ALTER TABLE webauthn_devices
|
||||
ALTER COLUMN aaguid DROP NOT NULL;
|
||||
|
||||
UPDATE webauthn_devices
|
||||
SET aaguid = NULL
|
||||
WHERE aaguid = '' OR aaguid = '00000000-00000000-00000000-00000000';
|
|
@ -9,7 +9,7 @@ import (
|
|||
|
||||
const (
|
||||
// This is the latest schema version for the purpose of tests.
|
||||
LatestVersion = 8
|
||||
LatestVersion = 9
|
||||
)
|
||||
|
||||
func TestShouldObtainCorrectUpMigrations(t *testing.T) {
|
||||
|
@ -44,6 +44,47 @@ func TestShouldObtainCorrectDownMigrations(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestMigrationShouldGetSpecificMigrationIfAvaliable(t *testing.T) {
|
||||
upMigrationsPostgreSQL, err := loadMigrations(providerPostgres, 8, 9)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, upMigrationsPostgreSQL, 1)
|
||||
|
||||
assert.True(t, upMigrationsPostgreSQL[0].Up)
|
||||
assert.Equal(t, 9, upMigrationsPostgreSQL[0].Version)
|
||||
assert.Equal(t, providerPostgres, upMigrationsPostgreSQL[0].Provider)
|
||||
|
||||
upMigrationsSQLite, err := loadMigrations(providerSQLite, 8, 9)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, upMigrationsSQLite, 1)
|
||||
|
||||
assert.True(t, upMigrationsSQLite[0].Up)
|
||||
assert.Equal(t, 9, upMigrationsSQLite[0].Version)
|
||||
assert.Equal(t, providerAll, upMigrationsSQLite[0].Provider)
|
||||
|
||||
downMigrationsPostgreSQL, err := loadMigrations(providerPostgres, 9, 8)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, downMigrationsPostgreSQL, 1)
|
||||
|
||||
assert.False(t, downMigrationsPostgreSQL[0].Up)
|
||||
assert.Equal(t, 9, downMigrationsPostgreSQL[0].Version)
|
||||
assert.Equal(t, providerAll, downMigrationsPostgreSQL[0].Provider)
|
||||
|
||||
downMigrationsSQLite, err := loadMigrations(providerSQLite, 9, 8)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, downMigrationsSQLite, 1)
|
||||
|
||||
assert.False(t, downMigrationsSQLite[0].Up)
|
||||
assert.Equal(t, 9, downMigrationsSQLite[0].Version)
|
||||
assert.Equal(t, providerAll, downMigrationsSQLite[0].Provider)
|
||||
}
|
||||
|
||||
func TestMigrationShouldReturnErrorOnSame(t *testing.T) {
|
||||
migrations, err := loadMigrations(providerPostgres, 1, 1)
|
||||
|
||||
assert.EqualError(t, err, "current version is same as migration target, no action being taken")
|
||||
assert.Nil(t, migrations)
|
||||
}
|
||||
|
||||
func TestMigrationsShouldNotBeDuplicatedPostgres(t *testing.T) {
|
||||
migrations, err := loadMigrations(providerPostgres, 0, SchemaLatest)
|
||||
require.NoError(t, err)
|
||||
|
|
|
@ -244,7 +244,7 @@ func (p *SQLProvider) schemaMigrateLock(ctx context.Context, conn SQLXConnection
|
|||
}
|
||||
|
||||
func (p *SQLProvider) schemaMigrateApply(ctx context.Context, conn SQLXConnection, migration model.SchemaMigration) (err error) {
|
||||
if migration.Query != "" {
|
||||
if migration.NotEmpty() {
|
||||
if _, err = conn.ExecContext(ctx, migration.Query); err != nil {
|
||||
return fmt.Errorf(errFmtFailedMigration, migration.Version, migration.Name, err)
|
||||
}
|
||||
|
|
|
@ -31,10 +31,12 @@ authentication_backend:
|
|||
|
||||
session:
|
||||
secret: unsecure_session_secret
|
||||
domain: example.com
|
||||
expiration: 3600 # 1 hour
|
||||
inactivity: 300 # 5 minutes
|
||||
remember_me: 1y
|
||||
cookies:
|
||||
- domain: 'example.com'
|
||||
authelia_url: 'https://login.example.com:8080'
|
||||
|
||||
storage:
|
||||
encryption_key: a_not_so_secure_encryption_key
|
||||
|
|
|
@ -87,7 +87,9 @@ session:
|
|||
secret: unsecure_session_secret
|
||||
expiration: 3600 # 1 hour
|
||||
inactivity: 300 # 5 minutes
|
||||
domain: example.com
|
||||
cookies:
|
||||
- domain: 'example.com'
|
||||
authelia_url: 'https://login.example.com:8080'
|
||||
redis:
|
||||
username: authelia
|
||||
password: redis-user-password
|
||||
|
|
|
@ -32,13 +32,14 @@ session:
|
|||
cookies:
|
||||
- name: 'authelia_session'
|
||||
domain: 'example.com'
|
||||
authelia_url: 'https://login.example.com:8080'
|
||||
- name: 'example2_session'
|
||||
domain: 'example2.com'
|
||||
authelia_url: 'https://login.example2.com'
|
||||
authelia_url: 'https://login.example2.com:8080'
|
||||
remember_me: -1
|
||||
- name: 'authelia_session'
|
||||
domain: 'example3.com'
|
||||
authelia_url: 'https://login.example3.com'
|
||||
authelia_url: 'https://login.example3.com:8080'
|
||||
|
||||
storage:
|
||||
encryption_key: a_not_so_secure_encryption_key
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM golang:1.20.2-alpine
|
||||
FROM golang:1.20.3-alpine
|
||||
|
||||
ARG USER_ID
|
||||
ARG GROUP_ID
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
version: '3'
|
||||
services:
|
||||
envoy:
|
||||
image: envoyproxy/envoy:v1.25.3
|
||||
image: envoyproxy/envoy:v1.25.4
|
||||
volumes:
|
||||
- ./example/compose/envoy/envoy.yaml:/etc/envoy/envoy.yaml
|
||||
- ./common/pki:/pki
|
||||
|
|
|
@ -19,9 +19,42 @@
|
|||
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
-- SOFTWARE.
|
||||
--
|
||||
-- SPDX-License-Identifier: MIT
|
||||
|
||||
local http = require("haproxy-lua-http")
|
||||
|
||||
core.register_action("auth-request", { "http-req" }, function(txn, be, path)
|
||||
auth_request(txn, be, path, "HEAD", ".*", "-", "-")
|
||||
end, 2)
|
||||
|
||||
core.register_action("auth-intercept", { "http-req" }, function(txn, be, path, method, hdr_req, hdr_succeed, hdr_fail)
|
||||
hdr_req = globToLuaPattern(hdr_req)
|
||||
hdr_succeed = globToLuaPattern(hdr_succeed)
|
||||
hdr_fail = globToLuaPattern(hdr_fail)
|
||||
auth_request(txn, be, path, method, hdr_req, hdr_succeed, hdr_fail)
|
||||
end, 6)
|
||||
|
||||
function globToLuaPattern(glob)
|
||||
if glob == "-" then
|
||||
return "-"
|
||||
end
|
||||
-- magic chars: '^', '$', '(', ')', '%', '.', '[', ']', '*', '+', '-', '?'
|
||||
-- https://www.lua.org/manual/5.4/manual.html#6.4.1
|
||||
--
|
||||
-- this chain is:
|
||||
-- 1. escaping all the magic chars, adding a `%` in front of all of them,
|
||||
-- except the chars being processed later in the chain;
|
||||
-- 1.1. all the chars inside the [set] are magic chars and have special
|
||||
-- meaning inside a set, so we're also escaping all of them to avoid
|
||||
-- misbehavior;
|
||||
-- 2. converting "match all" `*` and "match one" `?` to their Lua pattern
|
||||
-- counterparts;
|
||||
-- 3. adding start and finish boundaries outside the whole string and,
|
||||
-- being a comma-separated list, between every single item as well.
|
||||
return "^" .. glob:gsub("[%^%$%(%)%%%.%[%]%+%-]", "%%%1"):gsub("*", ".*"):gsub("?", "."):gsub(",", "$,^") .. "$"
|
||||
end
|
||||
|
||||
function set_var_pre_2_2(txn, var, value)
|
||||
return txn:set_var(var, value)
|
||||
end
|
||||
|
@ -44,8 +77,49 @@ function sanitize_header_for_variable(header)
|
|||
return header:gsub("[^a-zA-Z0-9]", "_")
|
||||
end
|
||||
|
||||
-- header_match checks whether the provided header matches the pattern.
|
||||
-- pattern is a comma-separated list of Lua Patterns.
|
||||
function header_match(header, pattern)
|
||||
if header == "content-length" or header == "host" or pattern == "-" then
|
||||
return false
|
||||
end
|
||||
for p in pattern:gmatch("[^,]*") do
|
||||
if header:match(p) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
core.register_action("auth-request", { "http-req" }, function(txn, be, path)
|
||||
-- Terminates the transaction and sends the provided response to the client.
|
||||
-- hdr_fail filters header names that should be provided using Lua Patterns.
|
||||
function send_response(txn, response, hdr_fail)
|
||||
local reply = txn:reply()
|
||||
if response then
|
||||
reply:set_status(response.status_code)
|
||||
for header, value in response:get_headers(false) do
|
||||
if header_match(header, hdr_fail) then
|
||||
reply:add_header(header, value)
|
||||
end
|
||||
end
|
||||
if response.content then
|
||||
reply:set_body(response.content)
|
||||
end
|
||||
else
|
||||
reply:set_status(500)
|
||||
end
|
||||
txn:done(reply)
|
||||
end
|
||||
|
||||
-- auth_request makes the request to the external authentication service
|
||||
-- and waits for the response. hdr_* params receive a comma-separated
|
||||
-- list of Lua Patterns used to identify the headers that should be
|
||||
-- copied between the requests and responses. A dash `-` in these params
|
||||
-- mean that the headers shouldn't be copied at all.
|
||||
-- Special values and behavior:
|
||||
-- * method == "*": call the auth service using the same method used by the client.
|
||||
-- * hdr_fail == "-": make the Lua script to not terminate the request.
|
||||
function auth_request(txn, be, path, method, hdr_req, hdr_succeed, hdr_fail)
|
||||
set_var(txn, "txn.auth_response_successful", false)
|
||||
|
||||
-- Check whether the given backend exists.
|
||||
|
@ -75,7 +149,7 @@ core.register_action("auth-request", { "http-req" }, function(txn, be, path)
|
|||
-- socket.http's format.
|
||||
local headers = {}
|
||||
for header, values in pairs(txn.http:req_get_headers()) do
|
||||
if header ~= 'content-length' then
|
||||
if header_match(header, hdr_req) then
|
||||
for i, v in pairs(values) do
|
||||
if headers[header] == nil then
|
||||
headers[header] = v
|
||||
|
@ -87,28 +161,46 @@ core.register_action("auth-request", { "http-req" }, function(txn, be, path)
|
|||
end
|
||||
|
||||
-- Make request to backend.
|
||||
local response, err = http.head {
|
||||
if method == "*" then
|
||||
method = txn.sf:method()
|
||||
end
|
||||
local response, err = http.send(method:upper(), {
|
||||
url = "http://" .. addr .. path,
|
||||
headers = headers,
|
||||
}
|
||||
})
|
||||
|
||||
-- `terminate_on_failure == true` means that the Lua script should send the response
|
||||
-- and terminate the transaction in the case of a failure. This will happen when
|
||||
-- hdr_fail content isn't a dash `-`.
|
||||
local terminate_on_failure = hdr_fail ~= "-"
|
||||
|
||||
-- Check whether we received a valid HTTP response.
|
||||
if response == nil then
|
||||
txn:Warning("Failure in auth-request backend '" .. be .. "': " .. err)
|
||||
set_var(txn, "txn.auth_response_code", 500)
|
||||
if terminate_on_failure then
|
||||
send_response(txn)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
set_var(txn, "txn.auth_response_code", response.status_code)
|
||||
local response_ok = 200 <= response.status_code and response.status_code < 300
|
||||
|
||||
for header, value in response:get_headers(true) do
|
||||
set_var(txn, "req.auth_response_header." .. sanitize_header_for_variable(header), value)
|
||||
if response_ok and hdr_succeed ~= "-" and header_match(header, hdr_succeed) then
|
||||
txn.http:req_set_header(header, value)
|
||||
end
|
||||
end
|
||||
|
||||
-- 2xx: Allow request.
|
||||
if 200 <= response.status_code and response.status_code < 300 then
|
||||
-- response_ok means 2xx: allow request.
|
||||
if response_ok then
|
||||
set_var(txn, "txn.auth_response_successful", true)
|
||||
-- Don't allow other codes.
|
||||
-- Don't allow codes < 200 or >= 300.
|
||||
-- Forward the response to the client if required.
|
||||
elseif terminate_on_failure then
|
||||
send_response(txn, response, hdr_fail)
|
||||
-- Codes with Location: Passthrough location at redirect.
|
||||
elseif response.status_code == 301 or response.status_code == 302 or response.status_code == 303 or response.status_code == 307 or response.status_code == 308 then
|
||||
set_var(txn, "txn.auth_response_location", response:get_header("location", "last"))
|
||||
|
@ -116,4 +208,4 @@ core.register_action("auth-request", { "http-req" }, function(txn, be, path)
|
|||
elseif response.status_code ~= 401 and response.status_code ~= 403 then
|
||||
txn:Warning("Invalid status code in auth-request backend '" .. be .. "': " .. response.status_code)
|
||||
end
|
||||
end, 2)
|
||||
end
|
||||
|
|
|
@ -42,25 +42,17 @@ frontend fe_http
|
|||
http-request set-var(req.scheme) str(https) if { ssl_fc }
|
||||
http-request set-var(req.scheme) str(http) if !{ ssl_fc }
|
||||
http-request set-var(req.questionmark) str(?) if { query -m found }
|
||||
http-request set-var(req.method) str(CONNECT) if { method CONNECT }
|
||||
http-request set-var(req.method) str(GET) if { method GET }
|
||||
http-request set-var(req.method) str(HEAD) if { method HEAD }
|
||||
http-request set-var(req.method) str(OPTIONS) if { method OPTIONS }
|
||||
http-request set-var(req.method) str(POST) if { method POST }
|
||||
http-request set-var(req.method) str(TRACE) if { method TRACE }
|
||||
http-request set-var(req.method) str(PUT) if { method PUT }
|
||||
http-request set-var(req.method) str(PATCH) if { method PATCH }
|
||||
http-request set-var(req.method) str(DELETE) if { method DELETE }
|
||||
|
||||
http-request set-header X-Real-IP %[src]
|
||||
http-request set-header X-Original-Method %[var(req.method)]
|
||||
http-request set-header X-Original-URL %[var(req.scheme)]://%[req.hdr(Host)]%[path]%[var(req.questionmark)]%[query]
|
||||
http-request set-header X-Forwarded-Method %[method]
|
||||
http-request set-header X-Forwarded-Proto %[var(req.scheme)]
|
||||
http-request set-header X-Forwarded-Host %[req.hdr(Host)]
|
||||
http-request set-header X-Forwarded-URI %[path]%[var(req.questionmark)]%[query]
|
||||
|
||||
# be_auth_request is used to make HAProxy do the TLS termination since the Lua script
|
||||
# does not know how to handle it (see https://github.com/TimWolla/haproxy-auth-request/issues/12).
|
||||
http-request lua.auth-request be_auth_request /api/authz/auth-request if protected-frontends
|
||||
|
||||
http-request redirect location https://login.example.com:8080/?rd=%[var(req.scheme)]://%[base]%[var(req.questionmark)]%[query]&rm=%[var(req.method)] if protected-frontends !{ var(txn.auth_response_successful) -m bool }
|
||||
http-request lua.auth-intercept be_auth_request /api/authz/forward-auth HEAD * authorization,proxy-authorization,remote_user,remote-user,remote-groups,remote-name,remote-email - if protected-frontends
|
||||
http-request redirect location %[var(txn.auth_response_location)] if protected-frontends !{ var(txn.auth_response_successful) -m bool }
|
||||
|
||||
use_backend be_authelia if host-authelia-portal api-path || devworkflow-path || jwks-path || locales-path || wellknown-path
|
||||
use_backend fe_authelia if host-authelia-portal !api-path
|
||||
|
@ -86,24 +78,6 @@ backend fe_authelia
|
|||
server authelia-backend authelia-backend:9091 check backup resolvers docker ssl verify none
|
||||
|
||||
backend be_httpbin
|
||||
## Pass the special authorization response headers to the protected application.
|
||||
acl authorization_exist var(req.auth_response_header.authorization) -m found
|
||||
acl proxy_authorization_exist var(req.auth_response_header.proxy_authorization) -m found
|
||||
|
||||
http-request set-header Authorization %[var(req.auth_response_header.authorization)] if authorization_exist
|
||||
http-request set-header Proxy-Authorization %[var(req.auth_response_header.proxy_authorization)] if proxy_authorization_exist
|
||||
|
||||
## Pass the special metadata response headers to the protected application.
|
||||
acl remote_user_exist var(req.auth_response_header.remote_user) -m found
|
||||
acl remote_groups_exist var(req.auth_response_header.remote_groups) -m found
|
||||
acl remote_name_exist var(req.auth_response_header.remote_name) -m found
|
||||
acl remote_email_exist var(req.auth_response_header.remote_email) -m found
|
||||
|
||||
http-request set-header Remote-User %[var(req.auth_response_header.remote_user)] if remote_user_exist
|
||||
http-request set-header Remote-Groups %[var(req.auth_response_header.remote_groups)] if remote_groups_exist
|
||||
http-request set-header Remote-Name %[var(req.auth_response_header.remote_name)] if remote_name_exist
|
||||
http-request set-header Remote-Email %[var(req.auth_response_header.remote_email)] if remote_email_exist
|
||||
|
||||
## Pass the Set-Cookie response headers to the user.
|
||||
acl set_cookie_exist var(req.auth_response_header.set_cookie) -m found
|
||||
http-response set-header Set-Cookie %[var(req.auth_response_header.set_cookie)] if set_cookie_exist
|
||||
|
@ -114,4 +88,8 @@ backend be_mail
|
|||
server smtp-backend smtp:1080 resolvers docker
|
||||
|
||||
backend be_protected
|
||||
## Pass the Set-Cookie response headers to the user.
|
||||
acl set_cookie_exist var(req.auth_response_header.set_cookie) -m found
|
||||
http-response set-header Set-Cookie %[var(req.auth_response_header.set_cookie)] if set_cookie_exist
|
||||
|
||||
server nginx-backend nginx-backend:80 resolvers docker
|
||||
|
|
|
@ -166,9 +166,6 @@ http {
|
|||
## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource.
|
||||
auth_request /internal/authelia/authz;
|
||||
|
||||
## Set the $target_url variable based on the original request.
|
||||
set $target_url $scheme://$http_host$request_uri;
|
||||
|
||||
## Save the upstream authorization response headers from Authelia to variables.
|
||||
auth_request_set $authorization $upstream_http_authorization;
|
||||
auth_request_set $proxy_authorization $upstream_http_proxy_authorization;
|
||||
|
@ -193,8 +190,23 @@ http {
|
|||
auth_request_set $cookie $upstream_http_set_cookie;
|
||||
add_header Set-Cookie $cookie;
|
||||
|
||||
## If the subreqest returns 200 pass to the backend, if the subrequest returns 401 redirect to the portal.
|
||||
error_page 401 =302 https://login.$basedomain:8080/?rd=$target_url;
|
||||
## Configure the redirection when the Authz failure occurs. Lines starting with 'Modern Method' and 'Legacy Method'
|
||||
## should be commented / uncommented as pairs. The modern method uses the session cookies configuration's authelia_url
|
||||
## value to determine the redirection URL here. It's much simpler and compatible with the mutli-cookie domain easily.
|
||||
|
||||
## Modern Method: Set the $redirection_url to the Location header of the response to the Authz endpoint.
|
||||
auth_request_set $redirection_url $upstream_http_location;
|
||||
|
||||
## Modern Method: When there is a 401 response code from the Authz endpoint redirect to the $redirection_url.
|
||||
error_page 401 =302 $redirection_url;
|
||||
|
||||
## Legacy Method: Set $target_url to the original requested URL.
|
||||
## This requires http_set_misc module, replace 'set_escape_uri' with 'set' if you don't have this module.
|
||||
# set $target_url $scheme://$http_host$request_uri;
|
||||
|
||||
## Legacy Method: When there is a 401 response code from the Authz endpoint redirect to the portal with the 'rd'
|
||||
## URL parameter set to $target_url. This requires users update 'auth.example.com/' with their external authelia URL.
|
||||
# error_page 401 =302 https://login.$basedomain:8080/?rd=$target_url;
|
||||
|
||||
# Authelia relies on Proxy-Authorization header to authenticate in basic auth.
|
||||
# but for the sake of simplicity (because Authorization in supported in most
|
||||
|
@ -252,9 +264,6 @@ http {
|
|||
## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource.
|
||||
auth_request /internal/authelia/authz;
|
||||
|
||||
## Set the $target_url variable based on the original request.
|
||||
set $target_url $scheme://$http_host$request_uri;
|
||||
|
||||
## Save the upstream authorization response headers from Authelia to variables.
|
||||
auth_request_set $authorization $upstream_http_authorization;
|
||||
auth_request_set $proxy_authorization $upstream_http_proxy_authorization;
|
||||
|
@ -279,8 +288,23 @@ http {
|
|||
auth_request_set $cookie $upstream_http_set_cookie;
|
||||
add_header Set-Cookie $cookie;
|
||||
|
||||
## If the subreqest returns 200 pass to the backend, if the subrequest returns 401 redirect to the portal.
|
||||
error_page 401 =302 https://login.$basedomain:8080/?rd=$target_url;
|
||||
## Configure the redirection when the Authz failure occurs. Lines starting with 'Modern Method' and 'Legacy Method'
|
||||
## should be commented / uncommented as pairs. The modern method uses the session cookies configuration's authelia_url
|
||||
## value to determine the redirection URL here. It's much simpler and compatible with the mutli-cookie domain easily.
|
||||
|
||||
## Modern Method: Set the $redirection_url to the Location header of the response to the Authz endpoint.
|
||||
auth_request_set $redirection_url $upstream_http_location;
|
||||
|
||||
## Modern Method: When there is a 401 response code from the Authz endpoint redirect to the $redirection_url.
|
||||
error_page 401 =302 $redirection_url;
|
||||
|
||||
## Legacy Method: Set $target_url to the original requested URL.
|
||||
## This requires http_set_misc module, replace 'set_escape_uri' with 'set' if you don't have this module.
|
||||
# set $target_url $scheme://$http_host$request_uri;
|
||||
|
||||
## Legacy Method: When there is a 401 response code from the Authz endpoint redirect to the portal with the 'rd'
|
||||
## URL parameter set to $target_url. This requires users update 'auth.example.com/' with their external authelia URL.
|
||||
# error_page 401 =302 https://login.$basedomain:8080/?rd=$target_url;
|
||||
|
||||
proxy_pass $upstream_headers;
|
||||
}
|
||||
|
@ -309,9 +333,6 @@ http {
|
|||
## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource.
|
||||
auth_request /internal/authelia/authz;
|
||||
|
||||
## Set the $target_url variable based on the original request.
|
||||
set $target_url $scheme://$http_host$request_uri;
|
||||
|
||||
## Save the upstream authorization response headers from Authelia to variables.
|
||||
auth_request_set $authorization $upstream_http_authorization;
|
||||
auth_request_set $proxy_authorization $upstream_http_proxy_authorization;
|
||||
|
@ -336,8 +357,23 @@ http {
|
|||
auth_request_set $cookie $upstream_http_set_cookie;
|
||||
add_header Set-Cookie $cookie;
|
||||
|
||||
## If the subreqest returns 200 pass to the backend, if the subrequest returns 401 redirect to the portal.
|
||||
error_page 401 =302 https://login.$basedomain:8080/?rd=$target_url;
|
||||
## Configure the redirection when the Authz failure occurs. Lines starting with 'Modern Method' and 'Legacy Method'
|
||||
## should be commented / uncommented as pairs. The modern method uses the session cookies configuration's authelia_url
|
||||
## value to determine the redirection URL here. It's much simpler and compatible with the mutli-cookie domain easily.
|
||||
|
||||
## Modern Method: Set the $redirection_url to the Location header of the response to the Authz endpoint.
|
||||
auth_request_set $redirection_url $upstream_http_location;
|
||||
|
||||
## Modern Method: When there is a 401 response code from the Authz endpoint redirect to the $redirection_url.
|
||||
error_page 401 =302 $redirection_url;
|
||||
|
||||
## Legacy Method: Set $target_url to the original requested URL.
|
||||
## This requires http_set_misc module, replace 'set_escape_uri' with 'set' if you don't have this module.
|
||||
# set $target_url $scheme://$http_host$request_uri;
|
||||
|
||||
## Legacy Method: When there is a 401 response code from the Authz endpoint redirect to the portal with the 'rd'
|
||||
## URL parameter set to $target_url. This requires users update 'auth.example.com/' with their external authelia URL.
|
||||
# error_page 401 =302 https://login.$basedomain:8080/?rd=$target_url;
|
||||
|
||||
# Route the request to the correct virtual host in the backend.
|
||||
proxy_set_header Host $http_host;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
version: '3'
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v2.9.9
|
||||
image: traefik:v2.9.10
|
||||
volumes:
|
||||
- '/var/run/docker.sock:/var/run/docker.sock'
|
||||
labels:
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"encoding/hex"
|
||||
"fmt"
|
||||
"hash"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
@ -79,6 +80,8 @@ func FuncMap() map[string]any {
|
|||
"indent": FuncIndent,
|
||||
"nindent": FuncNewlineIndent,
|
||||
"uuidv4": FuncUUIDv4,
|
||||
"urlquery": url.QueryEscape,
|
||||
"urlunquery": url.QueryUnescape,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package templates
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
th "html/template"
|
||||
"io/fs"
|
||||
"path"
|
||||
tt "text/template"
|
||||
)
|
||||
|
@ -28,7 +28,7 @@ type Provider struct {
|
|||
}
|
||||
|
||||
// LoadTemplatedAssets takes an embed.FS and loads each templated asset document into a Template.
|
||||
func (p *Provider) LoadTemplatedAssets(fs embed.FS) (err error) {
|
||||
func (p *Provider) LoadTemplatedAssets(fs fs.ReadFileFS) (err error) {
|
||||
var (
|
||||
data []byte
|
||||
)
|
||||
|
|
|
@ -122,6 +122,15 @@ var htmlEscaper = strings.NewReplacer(
|
|||
var ErrTimeoutReached = errors.New("timeout reached")
|
||||
|
||||
const (
|
||||
windows = "windows"
|
||||
errFmtLinuxNotFound = "open %s: no such file or directory"
|
||||
windows = "windows"
|
||||
errFmtLinuxNotFound = "%s %%s: no such file or directory"
|
||||
errFmtWindowsNotFound = "%s %%s: The system cannot find the %s specified."
|
||||
|
||||
strStat = "stat"
|
||||
strOpen = "open"
|
||||
strFile = "file"
|
||||
strPath = "path"
|
||||
strIsDir = "isdir"
|
||||
strPathNotFound = "pathnotfound"
|
||||
strFileNotFound = "filenotfound"
|
||||
)
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package utils
|
||||
|
||||
import "runtime"
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// ErrSliceSortAlphabetical is a helper type that can be used with sort.Sort to sort a slice of errors in alphabetical
|
||||
// order. Usage is simple just do sort.Sort(ErrSliceSortAlphabetical([]error{})).
|
||||
|
@ -12,28 +15,29 @@ func (s ErrSliceSortAlphabetical) Less(i, j int) bool { return s[i].Error() < s[
|
|||
|
||||
func (s ErrSliceSortAlphabetical) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
|
||||
// GetExpectedErrTxt returns error text for expected errs.
|
||||
// GetExpectedErrTxt returns error text for expected errs. THIS IS A TEST UTILITY FUNCTION.
|
||||
func GetExpectedErrTxt(err string) string {
|
||||
switch err {
|
||||
case "pathnotfound":
|
||||
switch runtime.GOOS {
|
||||
case windows:
|
||||
return "open %s: The system cannot find the path specified."
|
||||
default:
|
||||
return errFmtLinuxNotFound
|
||||
}
|
||||
case "filenotfound":
|
||||
switch runtime.GOOS {
|
||||
case windows:
|
||||
return "open %s: The system cannot find the file specified."
|
||||
default:
|
||||
return errFmtLinuxNotFound
|
||||
}
|
||||
case "yamlisdir":
|
||||
switch runtime.GOOS {
|
||||
case windows:
|
||||
switch runtime.GOOS {
|
||||
case windows:
|
||||
switch err {
|
||||
case strPathNotFound:
|
||||
return fmt.Sprintf(errFmtWindowsNotFound, strOpen, strPath)
|
||||
case strStat + strPathNotFound:
|
||||
return fmt.Sprintf(errFmtWindowsNotFound, strStat, strPath)
|
||||
case strFileNotFound:
|
||||
return fmt.Sprintf(errFmtWindowsNotFound, strOpen, strFile)
|
||||
case strStat + strFileNotFound:
|
||||
return fmt.Sprintf(errFmtWindowsNotFound, strStat, strFile)
|
||||
case strIsDir:
|
||||
return "read %s: The handle is invalid."
|
||||
default:
|
||||
}
|
||||
default:
|
||||
switch err {
|
||||
case strPathNotFound, strFileNotFound:
|
||||
return fmt.Sprintf(errFmtLinuxNotFound, strOpen)
|
||||
case strStat + strPathNotFound, strStat + strFileNotFound:
|
||||
return fmt.Sprintf(errFmtLinuxNotFound, strStat)
|
||||
case strIsDir:
|
||||
return "read %s: is a directory"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,22 +22,22 @@
|
|||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/cache": "11.10.5",
|
||||
"@emotion/cache": "11.10.7",
|
||||
"@emotion/react": "11.10.6",
|
||||
"@emotion/styled": "11.10.6",
|
||||
"@fortawesome/fontawesome-svg-core": "6.4.0",
|
||||
"@fortawesome/free-regular-svg-icons": "6.4.0",
|
||||
"@fortawesome/free-solid-svg-icons": "6.4.0",
|
||||
"@fortawesome/react-fontawesome": "0.2.0",
|
||||
"@mui/icons-material": "5.11.11",
|
||||
"@mui/material": "5.11.15",
|
||||
"@mui/styles": "5.11.13",
|
||||
"@mui/icons-material": "5.11.16",
|
||||
"@mui/material": "5.11.16",
|
||||
"@mui/styles": "5.11.16",
|
||||
"@simplewebauthn/browser": "7.2.0",
|
||||
"@simplewebauthn/typescript-types": "7.0.0",
|
||||
"axios": "1.3.4",
|
||||
"axios": "1.3.5",
|
||||
"broadcast-channel": "5.0.3",
|
||||
"classnames": "2.3.2",
|
||||
"i18next": "22.4.13",
|
||||
"i18next": "22.4.14",
|
||||
"i18next-browser-languagedetector": "7.0.1",
|
||||
"i18next-http-backend": "2.2.0",
|
||||
"qrcode.react": "3.1.0",
|
||||
|
@ -155,19 +155,19 @@
|
|||
"@testing-library/react": "14.0.0",
|
||||
"@types/jest": "29.5.0",
|
||||
"@types/node": "18.15.11",
|
||||
"@types/react": "18.0.31",
|
||||
"@types/react": "18.0.33",
|
||||
"@types/react-dom": "18.0.11",
|
||||
"@types/zxcvbn": "4.4.1",
|
||||
"@typescript-eslint/eslint-plugin": "5.57.0",
|
||||
"@typescript-eslint/parser": "5.57.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.57.1",
|
||||
"@typescript-eslint/parser": "5.57.1",
|
||||
"@vitejs/plugin-react": "3.1.0",
|
||||
"esbuild": "0.17.15",
|
||||
"esbuild-jest": "0.5.0",
|
||||
"eslint": "8.37.0",
|
||||
"eslint": "8.38.0",
|
||||
"eslint-config-prettier": "8.8.0",
|
||||
"eslint-config-react-app": "7.0.1",
|
||||
"eslint-formatter-rdjson": "1.0.5",
|
||||
"eslint-import-resolver-typescript": "3.5.4",
|
||||
"eslint-import-resolver-typescript": "3.5.5",
|
||||
"eslint-plugin-import": "2.27.5",
|
||||
"eslint-plugin-jsx-a11y": "6.7.1",
|
||||
"eslint-plugin-prettier": "4.2.1",
|
||||
|
@ -180,11 +180,11 @@
|
|||
"jest-watch-typeahead": "2.2.2",
|
||||
"prettier": "2.8.7",
|
||||
"react-test-renderer": "18.2.0",
|
||||
"typescript": "5.0.3",
|
||||
"typescript": "5.0.4",
|
||||
"vite": "4.2.1",
|
||||
"vite-plugin-eslint": "1.8.1",
|
||||
"vite-plugin-istanbul": "4.0.1",
|
||||
"vite-plugin-svgr": "2.4.0",
|
||||
"vite-tsconfig-paths": "4.0.7"
|
||||
"vite-tsconfig-paths": "4.0.8"
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue