Compare commits
82 Commits
esbuild-ts
...
channels-p
Author | SHA1 | Date | |
---|---|---|---|
44f4ba3385 | |||
bfbe8b8038 | |||
36ba8bc4e7 | |||
dd5edf7fd9 | |||
da1b252f3b | |||
a8e543972a | |||
6e03045d1f | |||
f4b39e7465 | |||
e7cd5880b5 | |||
d8c6a2417d | |||
a1fe471a59 | |||
054dfda73f | |||
2e5e8f5c58 | |||
c28b65a3f2 | |||
afc9847e36 | |||
620c95dfa1 | |||
15c7a0a9be | |||
5fed8ca575 | |||
f471ddfb29 | |||
1b1f06c9f7 | |||
67c31a8ac3 | |||
638180d246 | |||
a3be1bbb57 | |||
fbd0ba2865 | |||
182ad912cb | |||
e850b2ba1a | |||
b4ce7f9ab0 | |||
5f0bd6f5ea | |||
f5944ccb95 | |||
9bd3bad605 | |||
dff60ee9fb | |||
4e932e47c9 | |||
e57a98aeb5 | |||
807ea2a52a | |||
0775bc0f1e | |||
35a4d9cc71 | |||
ed9008a7d4 | |||
a377ce6b45 | |||
dac24ba62d | |||
826acbde2a | |||
b7d97da2bc | |||
cc6fcd831d | |||
e5e3a5df80 | |||
74268500b0 | |||
614740a4ff | |||
f48496b2cf | |||
35da3d65d2 | |||
fb53fe2b3e | |||
dda2338258 | |||
f582e66c67 | |||
f595375f2d | |||
fd8317de7f | |||
2f1eab5aed | |||
70460bfb30 | |||
0be9c60a71 | |||
abaf8d9544 | |||
73a3f29001 | |||
159bf4012e | |||
9b3c1b5cff | |||
19aa268e4e | |||
1c5e906a3e | |||
c133ba9bd3 | |||
65517f3b7f | |||
b361dd3b59 | |||
40f598f3f1 | |||
b72d0e84c9 | |||
d97297e0ce | |||
1a80353bc0 | |||
beece507fd | |||
e2bec88403 | |||
26b6c2e130 | |||
1a38679ecf | |||
b2334c3680 | |||
13251bb8c4 | |||
9fe6bac99d | |||
7c9fe53b47 | |||
b20c4eab29 | |||
8ca09a9ece | |||
856598fc54 | |||
fdb7b29d9a | |||
3748781368 | |||
9cf2f89bf6 |
14
.github/dependabot.yml
vendored
14
.github/dependabot.yml
vendored
@ -23,7 +23,13 @@ updates:
|
||||
- package-ecosystem: npm
|
||||
directories:
|
||||
- "/web"
|
||||
- "/web/sfe"
|
||||
- "/web/packages/sfe"
|
||||
- "/web/packages/core"
|
||||
- "/web/packages/esbuild-plugin-live-reload"
|
||||
- "/packages/prettier-config"
|
||||
- "/packages/tsconfig"
|
||||
- "/packages/docusaurus-config"
|
||||
- "/packages/eslint-config"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "04:00"
|
||||
@ -68,6 +74,9 @@ updates:
|
||||
wdio:
|
||||
patterns:
|
||||
- "@wdio/*"
|
||||
goauthentik:
|
||||
patterns:
|
||||
- "@goauthentik/*"
|
||||
- package-ecosystem: npm
|
||||
directory: "/website"
|
||||
schedule:
|
||||
@ -88,6 +97,9 @@ updates:
|
||||
- "swc-*"
|
||||
- "lightningcss*"
|
||||
- "@rspack/binding*"
|
||||
goauthentik:
|
||||
patterns:
|
||||
- "@goauthentik/*"
|
||||
- package-ecosystem: npm
|
||||
directory: "/lifecycle/aws"
|
||||
schedule:
|
||||
|
1
.github/workflows/api-ts-publish.yml
vendored
1
.github/workflows/api-ts-publish.yml
vendored
@ -53,6 +53,7 @@ jobs:
|
||||
signoff: true
|
||||
# ID from https://api.github.com/users/authentik-automation[bot]
|
||||
author: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
|
||||
labels: dependencies
|
||||
- uses: peter-evans/enable-pull-request-automerge@v3
|
||||
with:
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
|
@ -37,6 +37,7 @@ jobs:
|
||||
signoff: true
|
||||
# ID from https://api.github.com/users/authentik-automation[bot]
|
||||
author: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
|
||||
labels: dependencies
|
||||
- uses: peter-evans/enable-pull-request-automerge@v3
|
||||
with:
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
|
1
.github/workflows/image-compress.yml
vendored
1
.github/workflows/image-compress.yml
vendored
@ -53,6 +53,7 @@ jobs:
|
||||
body: ${{ steps.compress.outputs.markdown }}
|
||||
delete-branch: true
|
||||
signoff: true
|
||||
labels: dependencies
|
||||
- uses: peter-evans/enable-pull-request-automerge@v3
|
||||
if: "${{ github.event_name != 'pull_request' && steps.compress.outputs.markdown != '' }}"
|
||||
with:
|
||||
|
16
.github/workflows/packages-npm-publish.yml
vendored
16
.github/workflows/packages-npm-publish.yml
vendored
@ -7,6 +7,7 @@ on:
|
||||
- packages/eslint-config/**
|
||||
- packages/prettier-config/**
|
||||
- packages/tsconfig/**
|
||||
- packages/web/esbuild-plugin-live-reload/**
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
publish:
|
||||
@ -16,27 +17,28 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
package:
|
||||
- docusaurus-config
|
||||
- eslint-config
|
||||
- prettier-config
|
||||
- tsconfig
|
||||
- packages/docusaurus-config
|
||||
- packages/eslint-config
|
||||
- packages/prettier-config
|
||||
- packages/tsconfig
|
||||
- packages/web/esbuild-plugin-live-reload
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 2
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: packages/${{ matrix.package }}/package.json
|
||||
node-version-file: ${{ matrix.package }}/package.json
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
- name: Get changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@ed68ef82c095e0d48ec87eccea555d944a631a4c
|
||||
with:
|
||||
files: |
|
||||
packages/${{ matrix.package }}/package.json
|
||||
${{ matrix.package }}/package.json
|
||||
- name: Publish package
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
working-directory: packages/${{ matrix.package}}
|
||||
working-directory: ${{ matrix.package }}
|
||||
run: |
|
||||
npm ci
|
||||
npm run build
|
||||
|
@ -52,3 +52,6 @@ jobs:
|
||||
body: "core, web: update translations"
|
||||
delete-branch: true
|
||||
signoff: true
|
||||
labels: dependencies
|
||||
# ID from https://api.github.com/users/authentik-automation[bot]
|
||||
author: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
|
||||
|
15
.github/workflows/translation-rename.yml
vendored
15
.github/workflows/translation-rename.yml
vendored
@ -15,6 +15,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.pull_request.user.login == 'transifex-integration[bot]'}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- id: generate_token
|
||||
uses: tibdex/github-app-token@v2
|
||||
with:
|
||||
@ -25,23 +26,13 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
|
||||
run: |
|
||||
title=$(curl -q -L \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer ${GH_TOKEN}" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
https://api.github.com/repos/${GITHUB_REPOSITORY}/pulls/${{ github.event.pull_request.number }} | jq -r .title)
|
||||
title=$(gh pr view ${{ github.event.pull_request.number }} --json "title" -q ".title")
|
||||
echo "title=${title}" >> "$GITHUB_OUTPUT"
|
||||
- name: Rename
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
|
||||
run: |
|
||||
curl -L \
|
||||
-X PATCH \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer ${GH_TOKEN}" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
https://api.github.com/repos/${GITHUB_REPOSITORY}/pulls/${{ github.event.pull_request.number }} \
|
||||
-d "{\"title\":\"translate: ${{ steps.title.outputs.title }}\"}"
|
||||
gh pr edit ${{ github.event.pull_request.number }} -t "translate: ${{ steps.title.outputs.title }}" --add-label dependencies
|
||||
- uses: peter-evans/enable-pull-request-automerge@v3
|
||||
with:
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
|
@ -1,7 +1,7 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# Stage 1: Build website
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/node:22 AS website-builder
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/node:24 AS website-builder
|
||||
|
||||
ENV NODE_ENV=production
|
||||
|
||||
@ -20,7 +20,7 @@ COPY ./SECURITY.md /work/
|
||||
RUN npm run build-bundled
|
||||
|
||||
# Stage 2: Build webui
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/node:22 AS web-builder
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/node:24 AS web-builder
|
||||
|
||||
ARG GIT_BUILD_HASH
|
||||
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
|
||||
@ -94,7 +94,7 @@ RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
|
||||
/bin/sh -c "GEOIPUPDATE_LICENSE_KEY_FILE=/run/secrets/GEOIPUPDATE_LICENSE_KEY /usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0"
|
||||
|
||||
# Stage 5: Download uv
|
||||
FROM ghcr.io/astral-sh/uv:0.7.4 AS uv
|
||||
FROM ghcr.io/astral-sh/uv:0.7.8 AS uv
|
||||
# Stage 6: Base python image
|
||||
FROM ghcr.io/goauthentik/fips-python:3.13.3-slim-bookworm-fips AS python-base
|
||||
|
||||
|
51
Makefile
51
Makefile
@ -1,6 +1,7 @@
|
||||
.PHONY: gen dev-reset all clean test web website
|
||||
|
||||
.SHELLFLAGS += ${SHELLFLAGS} -e
|
||||
SHELL := /usr/bin/env bash
|
||||
.SHELLFLAGS += ${SHELLFLAGS} -e -o pipefail
|
||||
PWD = $(shell pwd)
|
||||
UID = $(shell id -u)
|
||||
GID = $(shell id -g)
|
||||
@ -8,9 +9,9 @@ NPM_VERSION = $(shell python -m scripts.generate_semver)
|
||||
PY_SOURCES = authentik tests scripts lifecycle .github
|
||||
DOCKER_IMAGE ?= "authentik:test"
|
||||
|
||||
GEN_API_TS = "gen-ts-api"
|
||||
GEN_API_PY = "gen-py-api"
|
||||
GEN_API_GO = "gen-go-api"
|
||||
GEN_API_TS = gen-ts-api
|
||||
GEN_API_PY = gen-py-api
|
||||
GEN_API_GO = gen-go-api
|
||||
|
||||
pg_user := $(shell uv run python -m authentik.lib.config postgresql.user 2>/dev/null)
|
||||
pg_host := $(shell uv run python -m authentik.lib.config postgresql.host 2>/dev/null)
|
||||
@ -117,14 +118,19 @@ gen-diff: ## (Release) generate the changelog diff between the current schema a
|
||||
npx prettier --write diff.md
|
||||
|
||||
gen-clean-ts: ## Remove generated API client for Typescript
|
||||
rm -rf ./${GEN_API_TS}/
|
||||
rm -rf ./web/node_modules/@goauthentik/api/
|
||||
rm -rf ${PWD}/${GEN_API_TS}/
|
||||
rm -rf ${PWD}/web/node_modules/@goauthentik/api/
|
||||
|
||||
gen-clean-go: ## Remove generated API client for Go
|
||||
rm -rf ./${GEN_API_GO}/
|
||||
mkdir -p ${PWD}/${GEN_API_GO}
|
||||
ifneq ($(wildcard ${PWD}/${GEN_API_GO}/.*),)
|
||||
make -C ${PWD}/${GEN_API_GO} clean
|
||||
else
|
||||
rm -rf ${PWD}/${GEN_API_GO}
|
||||
endif
|
||||
|
||||
gen-clean-py: ## Remove generated API client for Python
|
||||
rm -rf ./${GEN_API_PY}/
|
||||
rm -rf ${PWD}/${GEN_API_PY}/
|
||||
|
||||
gen-clean: gen-clean-ts gen-clean-go gen-clean-py ## Remove generated API clients
|
||||
|
||||
@ -141,8 +147,8 @@ gen-client-ts: gen-clean-ts ## Build and install the authentik API for Typescri
|
||||
--git-repo-id authentik \
|
||||
--git-user-id goauthentik
|
||||
mkdir -p web/node_modules/@goauthentik/api
|
||||
cd ./${GEN_API_TS} && npm i
|
||||
\cp -rf ./${GEN_API_TS}/* web/node_modules/@goauthentik/api
|
||||
cd ${PWD}/${GEN_API_TS} && npm i
|
||||
\cp -rf ${PWD}/${GEN_API_TS}/* web/node_modules/@goauthentik/api
|
||||
|
||||
gen-client-py: gen-clean-py ## Build and install the authentik API for Python
|
||||
docker run \
|
||||
@ -156,24 +162,17 @@ gen-client-py: gen-clean-py ## Build and install the authentik API for Python
|
||||
--additional-properties=packageVersion=${NPM_VERSION} \
|
||||
--git-repo-id authentik \
|
||||
--git-user-id goauthentik
|
||||
pip install ./${GEN_API_PY}
|
||||
|
||||
gen-client-go: gen-clean-go ## Build and install the authentik API for Golang
|
||||
mkdir -p ./${GEN_API_GO} ./${GEN_API_GO}/templates
|
||||
wget https://raw.githubusercontent.com/goauthentik/client-go/main/config.yaml -O ./${GEN_API_GO}/config.yaml
|
||||
wget https://raw.githubusercontent.com/goauthentik/client-go/main/templates/README.mustache -O ./${GEN_API_GO}/templates/README.mustache
|
||||
wget https://raw.githubusercontent.com/goauthentik/client-go/main/templates/go.mod.mustache -O ./${GEN_API_GO}/templates/go.mod.mustache
|
||||
cp schema.yml ./${GEN_API_GO}/
|
||||
docker run \
|
||||
--rm -v ${PWD}/${GEN_API_GO}:/local \
|
||||
--user ${UID}:${GID} \
|
||||
docker.io/openapitools/openapi-generator-cli:v6.5.0 generate \
|
||||
-i /local/schema.yml \
|
||||
-g go \
|
||||
-o /local/ \
|
||||
-c /local/config.yaml
|
||||
mkdir -p ${PWD}/${GEN_API_GO}
|
||||
ifeq ($(wildcard ${PWD}/${GEN_API_GO}/.*),)
|
||||
git clone --depth 1 https://github.com/goauthentik/client-go.git ${PWD}/${GEN_API_GO}
|
||||
else
|
||||
cd ${PWD}/${GEN_API_GO} && git pull
|
||||
endif
|
||||
cp ${PWD}/schema.yml ${PWD}/${GEN_API_GO}
|
||||
make -C ${PWD}/${GEN_API_GO} build
|
||||
go mod edit -replace goauthentik.io/api/v3=./${GEN_API_GO}
|
||||
rm -rf ./${GEN_API_GO}/config.yaml ./${GEN_API_GO}/templates/
|
||||
|
||||
gen-dev-config: ## Generate a local development config file
|
||||
uv run scripts/generate_config.py
|
||||
@ -244,7 +243,7 @@ docker: ## Build a docker image of the current source tree
|
||||
DOCKER_BUILDKIT=1 docker build . --progress plain --tag ${DOCKER_IMAGE}
|
||||
|
||||
test-docker:
|
||||
BUILD=true ./scripts/test_docker.sh
|
||||
BUILD=true ${PWD}/scripts/test_docker.sh
|
||||
|
||||
#########################
|
||||
## CI
|
||||
|
@ -1,9 +1,12 @@
|
||||
"""API Authentication"""
|
||||
|
||||
from hmac import compare_digest
|
||||
from pathlib import Path
|
||||
from tempfile import gettempdir
|
||||
from typing import Any
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from drf_spectacular.extensions import OpenApiAuthenticationExtension
|
||||
from rest_framework.authentication import BaseAuthentication, get_authorization_header
|
||||
from rest_framework.exceptions import AuthenticationFailed
|
||||
@ -11,11 +14,17 @@ from rest_framework.request import Request
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.core.middleware import CTX_AUTH_VIA
|
||||
from authentik.core.models import Token, TokenIntents, User
|
||||
from authentik.core.models import Token, TokenIntents, User, UserTypes
|
||||
from authentik.outposts.models import Outpost
|
||||
from authentik.providers.oauth2.constants import SCOPE_AUTHENTIK_API
|
||||
|
||||
LOGGER = get_logger()
|
||||
_tmp = Path(gettempdir())
|
||||
try:
|
||||
with open(_tmp / "authentik-core-ipc.key") as _f:
|
||||
ipc_key = _f.read()
|
||||
except OSError:
|
||||
ipc_key = None
|
||||
|
||||
|
||||
def validate_auth(header: bytes) -> str | None:
|
||||
@ -73,6 +82,11 @@ def auth_user_lookup(raw_header: bytes) -> User | None:
|
||||
if user:
|
||||
CTX_AUTH_VIA.set("secret_key")
|
||||
return user
|
||||
# then try to auth via secret key (for embedded outpost/etc)
|
||||
user = token_ipc(auth_credentials)
|
||||
if user:
|
||||
CTX_AUTH_VIA.set("ipc")
|
||||
return user
|
||||
raise AuthenticationFailed("Token invalid/expired")
|
||||
|
||||
|
||||
@ -90,6 +104,43 @@ def token_secret_key(value: str) -> User | None:
|
||||
return outpost.user
|
||||
|
||||
|
||||
class IPCUser(AnonymousUser):
|
||||
"""'Virtual' user for IPC communication between authentik core and the authentik router"""
|
||||
|
||||
username = "authentik:system"
|
||||
is_active = True
|
||||
is_superuser = True
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
return UserTypes.INTERNAL_SERVICE_ACCOUNT
|
||||
|
||||
def has_perm(self, perm, obj=None):
|
||||
return True
|
||||
|
||||
def has_perms(self, perm_list, obj=None):
|
||||
return True
|
||||
|
||||
def has_module_perms(self, module):
|
||||
return True
|
||||
|
||||
@property
|
||||
def is_anonymous(self):
|
||||
return False
|
||||
|
||||
@property
|
||||
def is_authenticated(self):
|
||||
return True
|
||||
|
||||
|
||||
def token_ipc(value: str) -> User | None:
|
||||
"""Check if the token is the secret key
|
||||
and return the service account for the managed outpost"""
|
||||
if not ipc_key or not compare_digest(value, ipc_key):
|
||||
return None
|
||||
return IPCUser()
|
||||
|
||||
|
||||
class TokenAuthentication(BaseAuthentication):
|
||||
"""Token-based authentication using HTTP Bearer authentication"""
|
||||
|
||||
|
@ -59,6 +59,7 @@ class BrandSerializer(ModelSerializer):
|
||||
"flow_device_code",
|
||||
"default_application",
|
||||
"web_certificate",
|
||||
"client_certificates",
|
||||
"attributes",
|
||||
]
|
||||
extra_kwargs = {
|
||||
@ -120,6 +121,7 @@ class BrandViewSet(UsedByMixin, ModelViewSet):
|
||||
"domain",
|
||||
"branding_title",
|
||||
"web_certificate__name",
|
||||
"client_certificates__name",
|
||||
]
|
||||
filterset_fields = [
|
||||
"brand_uuid",
|
||||
@ -136,6 +138,7 @@ class BrandViewSet(UsedByMixin, ModelViewSet):
|
||||
"flow_user_settings",
|
||||
"flow_device_code",
|
||||
"web_certificate",
|
||||
"client_certificates",
|
||||
]
|
||||
ordering = ["domain"]
|
||||
|
||||
|
@ -0,0 +1,37 @@
|
||||
# Generated by Django 5.1.9 on 2025-05-19 15:09
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_brands", "0009_brand_branding_default_flow_background"),
|
||||
("authentik_crypto", "0004_alter_certificatekeypair_name"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="brand",
|
||||
name="client_certificates",
|
||||
field=models.ManyToManyField(
|
||||
blank=True,
|
||||
default=None,
|
||||
help_text="Certificates used for client authentication.",
|
||||
to="authentik_crypto.certificatekeypair",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="brand",
|
||||
name="web_certificate",
|
||||
field=models.ForeignKey(
|
||||
default=None,
|
||||
help_text="Web Certificate used by the authentik Core webserver.",
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_DEFAULT,
|
||||
related_name="+",
|
||||
to="authentik_crypto.certificatekeypair",
|
||||
),
|
||||
),
|
||||
]
|
@ -73,6 +73,13 @@ class Brand(SerializerModel):
|
||||
default=None,
|
||||
on_delete=models.SET_DEFAULT,
|
||||
help_text=_("Web Certificate used by the authentik Core webserver."),
|
||||
related_name="+",
|
||||
)
|
||||
client_certificates = models.ManyToManyField(
|
||||
CertificateKeyPair,
|
||||
default=None,
|
||||
blank=True,
|
||||
help_text=_("Certificates used for client authentication."),
|
||||
)
|
||||
attributes = models.JSONField(default=dict, blank=True)
|
||||
|
||||
|
@ -30,6 +30,7 @@ from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.core.api.utils import ModelSerializer, PassiveSerializer
|
||||
from authentik.core.models import UserTypes
|
||||
from authentik.crypto.apps import MANAGED_KEY
|
||||
from authentik.crypto.builder import CertificateBuilder, PrivateKeyAlg
|
||||
from authentik.crypto.models import CertificateKeyPair
|
||||
@ -272,11 +273,12 @@ class CertificateKeyPairViewSet(UsedByMixin, ModelViewSet):
|
||||
def view_certificate(self, request: Request, pk: str) -> Response:
|
||||
"""Return certificate-key pairs certificate and log access"""
|
||||
certificate: CertificateKeyPair = self.get_object()
|
||||
Event.new( # noqa # nosec
|
||||
EventAction.SECRET_VIEW,
|
||||
secret=certificate,
|
||||
type="certificate",
|
||||
).from_http(request)
|
||||
if request.user.type != UserTypes.INTERNAL_SERVICE_ACCOUNT:
|
||||
Event.new( # noqa # nosec
|
||||
EventAction.SECRET_VIEW,
|
||||
secret=certificate,
|
||||
type="certificate",
|
||||
).from_http(request)
|
||||
if "download" in request.query_params:
|
||||
# Mime type from https://pki-tutorial.readthedocs.io/en/latest/mime.html
|
||||
response = HttpResponse(
|
||||
@ -302,11 +304,12 @@ class CertificateKeyPairViewSet(UsedByMixin, ModelViewSet):
|
||||
def view_private_key(self, request: Request, pk: str) -> Response:
|
||||
"""Return certificate-key pairs private key and log access"""
|
||||
certificate: CertificateKeyPair = self.get_object()
|
||||
Event.new( # noqa # nosec
|
||||
EventAction.SECRET_VIEW,
|
||||
secret=certificate,
|
||||
type="private_key",
|
||||
).from_http(request)
|
||||
if request.user.type != UserTypes.INTERNAL_SERVICE_ACCOUNT:
|
||||
Event.new( # noqa # nosec
|
||||
EventAction.SECRET_VIEW,
|
||||
secret=certificate,
|
||||
type="private_key",
|
||||
).from_http(request)
|
||||
if "download" in request.query_params:
|
||||
# Mime type from https://pki-tutorial.readthedocs.io/en/latest/mime.html
|
||||
response = HttpResponse(certificate.key_data, content_type="application/x-pem-file")
|
||||
|
@ -25,7 +25,7 @@ class GoogleWorkspaceGroupClient(
|
||||
"""Google client for groups"""
|
||||
|
||||
connection_type = GoogleWorkspaceProviderGroup
|
||||
connection_type_query = "group"
|
||||
connection_attr = "googleworkspaceprovidergroup_set"
|
||||
can_discover = True
|
||||
|
||||
def __init__(self, provider: GoogleWorkspaceProvider) -> None:
|
||||
|
@ -20,7 +20,7 @@ class GoogleWorkspaceUserClient(GoogleWorkspaceSyncClient[User, GoogleWorkspaceP
|
||||
"""Sync authentik users into google workspace"""
|
||||
|
||||
connection_type = GoogleWorkspaceProviderUser
|
||||
connection_type_query = "user"
|
||||
connection_attr = "googleworkspaceprovideruser_set"
|
||||
can_discover = True
|
||||
|
||||
def __init__(self, provider: GoogleWorkspaceProvider) -> None:
|
||||
|
@ -132,7 +132,11 @@ class GoogleWorkspaceProvider(OutgoingSyncProvider, BackchannelProvider):
|
||||
if type == User:
|
||||
# Get queryset of all users with consistent ordering
|
||||
# according to the provider's settings
|
||||
base = User.objects.all().exclude_anonymous()
|
||||
base = (
|
||||
User.objects.prefetch_related("googleworkspaceprovideruser_set")
|
||||
.all()
|
||||
.exclude_anonymous()
|
||||
)
|
||||
if self.exclude_users_service_account:
|
||||
base = base.exclude(type=UserTypes.SERVICE_ACCOUNT).exclude(
|
||||
type=UserTypes.INTERNAL_SERVICE_ACCOUNT
|
||||
@ -142,7 +146,11 @@ class GoogleWorkspaceProvider(OutgoingSyncProvider, BackchannelProvider):
|
||||
return base.order_by("pk")
|
||||
if type == Group:
|
||||
# Get queryset of all groups with consistent ordering
|
||||
return Group.objects.all().order_by("pk")
|
||||
return (
|
||||
Group.objects.prefetch_related("googleworkspaceprovidergroup_set")
|
||||
.all()
|
||||
.order_by("pk")
|
||||
)
|
||||
raise ValueError(f"Invalid type {type}")
|
||||
|
||||
def google_credentials(self):
|
||||
|
@ -29,7 +29,7 @@ class MicrosoftEntraGroupClient(
|
||||
"""Microsoft client for groups"""
|
||||
|
||||
connection_type = MicrosoftEntraProviderGroup
|
||||
connection_type_query = "group"
|
||||
connection_attr = "microsoftentraprovidergroup_set"
|
||||
can_discover = True
|
||||
|
||||
def __init__(self, provider: MicrosoftEntraProvider) -> None:
|
||||
|
@ -24,7 +24,7 @@ class MicrosoftEntraUserClient(MicrosoftEntraSyncClient[User, MicrosoftEntraProv
|
||||
"""Sync authentik users into microsoft entra"""
|
||||
|
||||
connection_type = MicrosoftEntraProviderUser
|
||||
connection_type_query = "user"
|
||||
connection_attr = "microsoftentraprovideruser_set"
|
||||
can_discover = True
|
||||
|
||||
def __init__(self, provider: MicrosoftEntraProvider) -> None:
|
||||
|
@ -121,7 +121,11 @@ class MicrosoftEntraProvider(OutgoingSyncProvider, BackchannelProvider):
|
||||
if type == User:
|
||||
# Get queryset of all users with consistent ordering
|
||||
# according to the provider's settings
|
||||
base = User.objects.all().exclude_anonymous()
|
||||
base = (
|
||||
User.objects.prefetch_related("microsoftentraprovideruser_set")
|
||||
.all()
|
||||
.exclude_anonymous()
|
||||
)
|
||||
if self.exclude_users_service_account:
|
||||
base = base.exclude(type=UserTypes.SERVICE_ACCOUNT).exclude(
|
||||
type=UserTypes.INTERNAL_SERVICE_ACCOUNT
|
||||
@ -131,7 +135,11 @@ class MicrosoftEntraProvider(OutgoingSyncProvider, BackchannelProvider):
|
||||
return base.order_by("pk")
|
||||
if type == Group:
|
||||
# Get queryset of all groups with consistent ordering
|
||||
return Group.objects.all().order_by("pk")
|
||||
return (
|
||||
Group.objects.prefetch_related("microsoftentraprovidergroup_set")
|
||||
.all()
|
||||
.order_by("pk")
|
||||
)
|
||||
raise ValueError(f"Invalid type {type}")
|
||||
|
||||
def microsoft_credentials(self):
|
||||
|
@ -19,6 +19,7 @@ TENANT_APPS = [
|
||||
"authentik.enterprise.providers.microsoft_entra",
|
||||
"authentik.enterprise.providers.ssf",
|
||||
"authentik.enterprise.stages.authenticator_endpoint_gdtc",
|
||||
"authentik.enterprise.stages.mtls",
|
||||
"authentik.enterprise.stages.source",
|
||||
]
|
||||
|
||||
|
0
authentik/enterprise/stages/mtls/__init__.py
Normal file
0
authentik/enterprise/stages/mtls/__init__.py
Normal file
31
authentik/enterprise/stages/mtls/api.py
Normal file
31
authentik/enterprise/stages/mtls/api.py
Normal file
@ -0,0 +1,31 @@
|
||||
"""Mutual TLS Stage API Views"""
|
||||
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.enterprise.api import EnterpriseRequiredMixin
|
||||
from authentik.enterprise.stages.mtls.models import MutualTLSStage
|
||||
from authentik.flows.api.stages import StageSerializer
|
||||
|
||||
|
||||
class MutualTLSStageSerializer(EnterpriseRequiredMixin, StageSerializer):
|
||||
"""MutualTLSStage Serializer"""
|
||||
|
||||
class Meta:
|
||||
model = MutualTLSStage
|
||||
fields = StageSerializer.Meta.fields + [
|
||||
"mode",
|
||||
"certificate_authorities",
|
||||
"cert_attribute",
|
||||
"user_attribute",
|
||||
]
|
||||
|
||||
|
||||
class MutualTLSStageViewSet(UsedByMixin, ModelViewSet):
|
||||
"""MutualTLSStage Viewset"""
|
||||
|
||||
queryset = MutualTLSStage.objects.all()
|
||||
serializer_class = MutualTLSStageSerializer
|
||||
filterset_fields = "__all__"
|
||||
ordering = ["name"]
|
||||
search_fields = ["name"]
|
12
authentik/enterprise/stages/mtls/apps.py
Normal file
12
authentik/enterprise/stages/mtls/apps.py
Normal file
@ -0,0 +1,12 @@
|
||||
"""authentik stage app config"""
|
||||
|
||||
from authentik.enterprise.apps import EnterpriseConfig
|
||||
|
||||
|
||||
class AuthentikEnterpriseStageMTLSConfig(EnterpriseConfig):
|
||||
"""authentik MTLS stage config"""
|
||||
|
||||
name = "authentik.enterprise.stages.mtls"
|
||||
label = "authentik_stages_mtls"
|
||||
verbose_name = "authentik Enterprise.Stages.MTLS"
|
||||
default = True
|
68
authentik/enterprise/stages/mtls/migrations/0001_initial.py
Normal file
68
authentik/enterprise/stages/mtls/migrations/0001_initial.py
Normal file
@ -0,0 +1,68 @@
|
||||
# Generated by Django 5.1.9 on 2025-05-19 18:29
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("authentik_crypto", "0004_alter_certificatekeypair_name"),
|
||||
("authentik_flows", "0027_auto_20231028_1424"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="MutualTLSStage",
|
||||
fields=[
|
||||
(
|
||||
"stage_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="authentik_flows.stage",
|
||||
),
|
||||
),
|
||||
(
|
||||
"mode",
|
||||
models.TextField(choices=[("optional", "Optional"), ("required", "Required")]),
|
||||
),
|
||||
(
|
||||
"cert_attribute",
|
||||
models.TextField(
|
||||
choices=[
|
||||
("subject", "Subject"),
|
||||
("common_name", "Common Name"),
|
||||
("email", "Email"),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"user_attribute",
|
||||
models.TextField(choices=[("username", "Username"), ("email", "Email")]),
|
||||
),
|
||||
(
|
||||
"certificate_authorities",
|
||||
models.ManyToManyField(
|
||||
blank=True,
|
||||
default=None,
|
||||
help_text="Configure certificate authorities to validate the certificate against. This option has a higher priority than the `client_certificate` option on `Brand`.",
|
||||
to="authentik_crypto.certificatekeypair",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Mutual TLS Stage",
|
||||
"verbose_name_plural": "Mutual TLS Stages",
|
||||
"permissions": [
|
||||
("pass_outpost_certificate", "Permissions to pass Certificates for outposts.")
|
||||
],
|
||||
},
|
||||
bases=("authentik_flows.stage",),
|
||||
),
|
||||
]
|
71
authentik/enterprise/stages/mtls/models.py
Normal file
71
authentik/enterprise/stages/mtls/models.py
Normal file
@ -0,0 +1,71 @@
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from rest_framework.serializers import Serializer
|
||||
|
||||
from authentik.crypto.models import CertificateKeyPair
|
||||
from authentik.flows.models import Stage
|
||||
from authentik.flows.stage import StageView
|
||||
|
||||
|
||||
class TLSMode(models.TextChoices):
|
||||
"""Modes the TLS Stage can operate in"""
|
||||
|
||||
OPTIONAL = "optional"
|
||||
REQUIRED = "required"
|
||||
|
||||
|
||||
class CertAttributes(models.TextChoices):
|
||||
"""Certificate attribute used for user matching"""
|
||||
|
||||
SUBJECT = "subject"
|
||||
COMMON_NAME = "common_name"
|
||||
EMAIL = "email"
|
||||
|
||||
|
||||
class UserAttributes(models.TextChoices):
|
||||
"""User attribute for user matching"""
|
||||
|
||||
USERNAME = "username"
|
||||
EMAIL = "email"
|
||||
|
||||
|
||||
class MutualTLSStage(Stage):
|
||||
"""Authenticate/enroll users using a client-certificate."""
|
||||
|
||||
mode = models.TextField(choices=TLSMode.choices)
|
||||
|
||||
certificate_authorities = models.ManyToManyField(
|
||||
CertificateKeyPair,
|
||||
default=None,
|
||||
blank=True,
|
||||
help_text=_(
|
||||
"Configure certificate authorities to validate the certificate against. "
|
||||
"This option has a higher priority than the `client_certificate` option on `Brand`."
|
||||
),
|
||||
)
|
||||
|
||||
cert_attribute = models.TextField(choices=CertAttributes.choices)
|
||||
user_attribute = models.TextField(choices=UserAttributes.choices)
|
||||
|
||||
@property
|
||||
def view(self) -> type[StageView]:
|
||||
from authentik.enterprise.stages.mtls.stage import MTLSStageView
|
||||
|
||||
return MTLSStageView
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[Serializer]:
|
||||
from authentik.enterprise.stages.mtls.api import MutualTLSStageSerializer
|
||||
|
||||
return MutualTLSStageSerializer
|
||||
|
||||
@property
|
||||
def component(self) -> str:
|
||||
return "ak-stage-mtls-form"
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Mutual TLS Stage")
|
||||
verbose_name_plural = _("Mutual TLS Stages")
|
||||
permissions = [
|
||||
("pass_outpost_certificate", _("Permissions to pass Certificates for outposts.")),
|
||||
]
|
230
authentik/enterprise/stages/mtls/stage.py
Normal file
230
authentik/enterprise/stages/mtls/stage.py
Normal file
@ -0,0 +1,230 @@
|
||||
from binascii import hexlify
|
||||
from urllib.parse import unquote_plus
|
||||
|
||||
from cryptography.exceptions import InvalidSignature
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.x509 import (
|
||||
Certificate,
|
||||
NameOID,
|
||||
ObjectIdentifier,
|
||||
UnsupportedGeneralNameType,
|
||||
load_pem_x509_certificate,
|
||||
)
|
||||
from cryptography.x509.verification import PolicyBuilder, Store, VerificationError
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from authentik.brands.models import Brand
|
||||
from authentik.core.models import User
|
||||
from authentik.crypto.models import CertificateKeyPair
|
||||
from authentik.enterprise.stages.mtls.models import (
|
||||
CertAttributes,
|
||||
MutualTLSStage,
|
||||
TLSMode,
|
||||
UserAttributes,
|
||||
)
|
||||
from authentik.flows.challenge import AccessDeniedChallenge
|
||||
from authentik.flows.models import FlowDesignation
|
||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
|
||||
from authentik.flows.stage import ChallengeStageView
|
||||
from authentik.root.middleware import ClientIPMiddleware
|
||||
from authentik.stages.password.stage import PLAN_CONTEXT_METHOD, PLAN_CONTEXT_METHOD_ARGS
|
||||
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
|
||||
|
||||
# All of these headers must only be accepted from "trusted" reverse proxies
|
||||
# See internal/web/proxy.go:39
|
||||
HEADER_PROXY_FORWARDED = "X-Forwarded-Client-Cert"
|
||||
HEADER_NGINX_FORWARDED = "SSL-Client-Cert"
|
||||
HEADER_TRAEFIK_FORWARDED = "X-Forwarded-TLS-Client-Cert"
|
||||
HEADER_OUTPOST_FORWARDED = "X-Authentik-Outpost-Certificate"
|
||||
|
||||
|
||||
PLAN_CONTEXT_CERTIFICATE = "certificate"
|
||||
|
||||
|
||||
class MTLSStageView(ChallengeStageView):
|
||||
|
||||
def __parse_single_cert(self, raw: str | None) -> list[Certificate]:
|
||||
"""Helper to parse a single certificate"""
|
||||
if not raw:
|
||||
return []
|
||||
try:
|
||||
cert = load_pem_x509_certificate(unquote_plus(raw).encode())
|
||||
return [cert]
|
||||
except ValueError as exc:
|
||||
self.logger.info("Failed to parse certificate", exc=exc)
|
||||
return []
|
||||
|
||||
def _parse_cert_xfcc(self) -> list[Certificate]:
|
||||
"""Parse certificates in the format given to us in
|
||||
the format of the authentik router/envoy"""
|
||||
xfcc_raw = self.request.headers.get(HEADER_PROXY_FORWARDED)
|
||||
if not xfcc_raw:
|
||||
return []
|
||||
certs = []
|
||||
for r_cert in xfcc_raw.split(","):
|
||||
el = r_cert.split(";")
|
||||
raw_cert = {k.split("=")[0]: k.split("=")[1] for k in el}
|
||||
if "Cert" not in raw_cert:
|
||||
continue
|
||||
certs.extend(self.__parse_single_cert(raw_cert["Cert"]))
|
||||
return certs
|
||||
|
||||
def _parse_cert_nginx(self) -> list[Certificate]:
|
||||
"""Parse certificates in the format nginx-ingress gives to us"""
|
||||
sslcc_raw = self.request.headers.get(HEADER_NGINX_FORWARDED)
|
||||
return self.__parse_single_cert(sslcc_raw)
|
||||
|
||||
def _parse_cert_traefik(self) -> list[Certificate]:
|
||||
"""Parse certificates in the format traefik gives to us"""
|
||||
ftcc_raw = self.request.headers.get(HEADER_TRAEFIK_FORWARDED)
|
||||
return self.__parse_single_cert(ftcc_raw)
|
||||
|
||||
def _parse_cert_outpost(self) -> list[Certificate]:
|
||||
"""Parse certificates in the format outposts give to us. Also authenticates
|
||||
the outpost to ensure it has the permission to do so"""
|
||||
user = ClientIPMiddleware.get_outpost_user(self.request)
|
||||
if not user:
|
||||
return []
|
||||
if not user.has_perm(
|
||||
"pass_outpost_certificate", self.executor.current_stage
|
||||
) and not user.has_perm("authentik_stages_mtls.pass_outpost_certificate"):
|
||||
return []
|
||||
outpost_raw = self.request.headers.get(HEADER_OUTPOST_FORWARDED)
|
||||
return self.__parse_single_cert(outpost_raw)
|
||||
|
||||
def get_authorities(self) -> list[CertificateKeyPair] | None:
|
||||
# We can't access `certificate_authorities` on `self.executor.current_stage`, as that would
|
||||
# load the certificate into the directly referenced foreign key, which we have to pickle
|
||||
# as part of the flow plan, and cryptography certs can't be pickled
|
||||
stage: MutualTLSStage = (
|
||||
MutualTLSStage.objects.filter(pk=self.executor.current_stage.pk)
|
||||
.prefetch_related("certificate_authorities")
|
||||
.first()
|
||||
)
|
||||
if stage.certificate_authorities.exists():
|
||||
return stage.certificate_authorities.order_by("name")
|
||||
brand: Brand = self.request.brand
|
||||
if brand.client_certificates.exists():
|
||||
return brand.client_certificates.order_by("name")
|
||||
return None
|
||||
|
||||
def validate_cert(self, authorities: list[CertificateKeyPair], certs: list[Certificate]):
|
||||
authorities_cert = [x.certificate for x in authorities]
|
||||
for _cert in certs:
|
||||
try:
|
||||
PolicyBuilder().store(Store(authorities_cert)).build_client_verifier().verify(
|
||||
_cert, []
|
||||
)
|
||||
return _cert
|
||||
except (
|
||||
InvalidSignature,
|
||||
TypeError,
|
||||
ValueError,
|
||||
VerificationError,
|
||||
UnsupportedGeneralNameType,
|
||||
) as exc:
|
||||
self.logger.warning("Discarding invalid certificate", cert=_cert, exc=exc)
|
||||
continue
|
||||
return None
|
||||
|
||||
def check_if_user(self, cert: Certificate):
|
||||
stage: MutualTLSStage = self.executor.current_stage
|
||||
cert_attr = None
|
||||
user_attr = None
|
||||
match stage.cert_attribute:
|
||||
case CertAttributes.SUBJECT:
|
||||
cert_attr = cert.subject.rfc4514_string()
|
||||
case CertAttributes.COMMON_NAME:
|
||||
cert_attr = self.get_cert_attribute(cert, NameOID.COMMON_NAME)
|
||||
case CertAttributes.EMAIL:
|
||||
cert_attr = self.get_cert_attribute(cert, NameOID.EMAIL_ADDRESS)
|
||||
match stage.user_attribute:
|
||||
case UserAttributes.USERNAME:
|
||||
user_attr = "username"
|
||||
case UserAttributes.EMAIL:
|
||||
user_attr = "email"
|
||||
if not user_attr or not cert_attr:
|
||||
return None
|
||||
return User.objects.filter(**{user_attr: cert_attr}).first()
|
||||
|
||||
def _cert_to_dict(self, cert: Certificate) -> dict:
|
||||
"""Represent a certificate in a dictionary, as certificate objects cannot be pickled"""
|
||||
return {
|
||||
"serial_number": str(cert.serial_number),
|
||||
"subject": cert.subject.rfc4514_string(),
|
||||
"issuer": cert.issuer.rfc4514_string(),
|
||||
"fingerprint_sha256": hexlify(cert.fingerprint(hashes.SHA256()), ":").decode("utf-8"),
|
||||
"fingerprint_sha1": hexlify(cert.fingerprint(hashes.SHA1()), ":").decode( # nosec
|
||||
"utf-8"
|
||||
),
|
||||
}
|
||||
|
||||
def auth_user(self, user: User, cert: Certificate):
|
||||
self.executor.plan.context[PLAN_CONTEXT_PENDING_USER] = user
|
||||
self.executor.plan.context.setdefault(PLAN_CONTEXT_METHOD, "mtls")
|
||||
self.executor.plan.context.setdefault(PLAN_CONTEXT_METHOD_ARGS, {})
|
||||
self.executor.plan.context[PLAN_CONTEXT_METHOD_ARGS].update(
|
||||
{"certificate": self._cert_to_dict(cert)}
|
||||
)
|
||||
|
||||
def enroll_prepare_user(self, cert: Certificate):
|
||||
self.executor.plan.context.setdefault(PLAN_CONTEXT_PROMPT, {})
|
||||
self.executor.plan.context[PLAN_CONTEXT_PROMPT].update(
|
||||
{
|
||||
"email": self.get_cert_attribute(cert, NameOID.EMAIL_ADDRESS),
|
||||
"name": self.get_cert_attribute(cert, NameOID.COMMON_NAME),
|
||||
}
|
||||
)
|
||||
self.executor.plan.context[PLAN_CONTEXT_CERTIFICATE] = self._cert_to_dict(cert)
|
||||
|
||||
def get_cert_attribute(self, cert: Certificate, oid: ObjectIdentifier) -> str | None:
|
||||
attr = cert.subject.get_attributes_for_oid(oid)
|
||||
if len(attr) < 1:
|
||||
return None
|
||||
return str(attr[0].value)
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
stage: MutualTLSStage = self.executor.current_stage
|
||||
certs = [
|
||||
*self._parse_cert_xfcc(),
|
||||
*self._parse_cert_nginx(),
|
||||
*self._parse_cert_traefik(),
|
||||
*self._parse_cert_outpost(),
|
||||
]
|
||||
authorities = self.get_authorities()
|
||||
if not authorities:
|
||||
self.logger.warning("No Certificate authority found")
|
||||
if stage.mode == TLSMode.OPTIONAL:
|
||||
return self.executor.stage_ok()
|
||||
if stage.mode == TLSMode.REQUIRED:
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
cert = self.validate_cert(authorities, certs)
|
||||
if not cert and stage.mode == TLSMode.REQUIRED:
|
||||
self.logger.warning("Client certificate required but no certificates given")
|
||||
return super().dispatch(
|
||||
request,
|
||||
*args,
|
||||
error_message=_("Certificate required but no certificate was given."),
|
||||
**kwargs,
|
||||
)
|
||||
if not cert and stage.mode == TLSMode.OPTIONAL:
|
||||
self.logger.info("No certificate given, continuing")
|
||||
return self.executor.stage_ok()
|
||||
existing_user = self.check_if_user(cert)
|
||||
if self.executor.flow.designation == FlowDesignation.ENROLLMENT:
|
||||
self.enroll_prepare_user(cert)
|
||||
elif existing_user:
|
||||
self.auth_user(existing_user, cert)
|
||||
else:
|
||||
return super().dispatch(
|
||||
request, *args, error_message=_("No user found for certificate."), **kwargs
|
||||
)
|
||||
return self.executor.stage_ok()
|
||||
|
||||
def get_challenge(self, *args, error_message: str | None = None, **kwargs):
|
||||
return AccessDeniedChallenge(
|
||||
data={
|
||||
"component": "ak-stage-access-denied",
|
||||
"error_message": str(error_message or "Unknown error"),
|
||||
}
|
||||
)
|
0
authentik/enterprise/stages/mtls/tests/__init__.py
Normal file
0
authentik/enterprise/stages/mtls/tests/__init__.py
Normal file
31
authentik/enterprise/stages/mtls/tests/fixtures/ca.pem
vendored
Normal file
31
authentik/enterprise/stages/mtls/tests/fixtures/ca.pem
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFXDCCA0SgAwIBAgIUBmV7zREyC1SPr72/75/L9zpwV18wDQYJKoZIhvcNAQEL
|
||||
BQAwRjEaMBgGA1UEAwwRYXV0aGVudGlrIFRlc3QgQ0ExEjAQBgNVBAoMCWF1dGhl
|
||||
bnRpazEUMBIGA1UECwwLU2VsZi1zaWduZWQwHhcNMjUwNDI3MTgzMDUwWhcNMzUw
|
||||
MzA3MTgzMDUwWjBGMRowGAYDVQQDDBFhdXRoZW50aWsgVGVzdCBDQTESMBAGA1UE
|
||||
CgwJYXV0aGVudGlrMRQwEgYDVQQLDAtTZWxmLXNpZ25lZDCCAiIwDQYJKoZIhvcN
|
||||
AQEBBQADggIPADCCAgoCggIBAMc0NxZj7j1mPu0aRToo8oMPdC3T99xgxnqdr18x
|
||||
LV4pWyi/YLghgZHqNQY2xNP6JIlSeUZD6KFUYT2sPL4Av/zSg5zO8bl+/lf7ckje
|
||||
O1/Bt5A8xtL0CpmpMDGiI6ibdDElaywM6AohisbxrV29pygSKGq2wugF/urqGtE+
|
||||
5z4y5Kt6qMdKkd0iXT+WagbQTIUlykFKgB0+qqTLzDl01lVDa/DoLl8Hqp45mVx2
|
||||
pqrGsSa3TCErLIv9hUlZklF7A8UV4ZB4JL20UKcP8dKzQClviNie17tpsUpOuy3A
|
||||
SQ6+guWTHTLJNCSdLn1xIqc5q+f5wd2dIDf8zXCTHj+Xp0bJE3Vgaq5R31K9+b+1
|
||||
2dDWz1KcNJaLEnw2+b0O8M64wTMLxhqOv7QfLUr6Pmg1ZymghjLcZ6bnU9e31Vza
|
||||
hlPKhxjqYQUC4Kq+oaYF6qdUeJy+dsYf0iDv5tTC+eReZDWIjxTPrNpwA773ZwT7
|
||||
WVmL7ULGpuP2g9rNvFBcZiN+i6d7CUoN+jd/iRdo79lrI0dfXiyy4bYgW/2HeZfF
|
||||
HaOsc1xsoqnJdWbWkX/ooyaCjAfm07kS3HiOzz4q3QW4wgGrwV8lEraLPxYYeOQu
|
||||
YcGMOM8NfnVkjc8gmyXUxedCje5Vz/Tu5fKrQEInnCmXxVsWbwr/LzEjMKAM/ivY
|
||||
0TXxAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0G
|
||||
A1UdDgQWBBTa+Ns6QzqlNvnTGszkouQQtZnVJDANBgkqhkiG9w0BAQsFAAOCAgEA
|
||||
NpJEDMXjuEIzSzafkxSshvjnt5sMYmzmvjNoRlkxgN2YcWvPoxbalGAYzcpyggT2
|
||||
6xZY8R4tvB1oNTCArqwf860kkofUoJCr88D/pU3Cv4JhjCWs4pmXTsvSqlBSlJbo
|
||||
+jPBZwbn6it/6jcit6Be3rW2PtHe8tASd9Lf8/2r1ZvupXwPzcR84R4Z10ve2lqV
|
||||
xxcWlMmBh51CaYI0b1/WTe9Ua+wgkCVkxbf9zNcDQXjxw2ICWK+nR/4ld4nmqVm2
|
||||
C7nhvXwU8FAHl7ZgR2Z3PLrwPuhd+kd6NXQqNkS9A+n+1vSRLbRjmV8pwIPpdPEq
|
||||
nslUAGJJBHDUBArxC3gOJSB+WtmaCfzDu2gepMf9Ng1H2ZhwSF/FH3v3fsJqZkzz
|
||||
NBstT9KuNGQRYiCmAPJaoVAc9BoLa+BFML1govtWtpdmbFk8PZEcuUsP7iAZqFF1
|
||||
uuldPyZ8huGpQSR6Oq2bILRHowfGY0npTZAyxg0Vs8UMy1HTwNOp9OuRtArMZmsJ
|
||||
jFIx1QzRf9S1i6bYpOzOudoXj4ARkS1KmVExGjJFcIT0xlFSSERie2fEKSeEYOyG
|
||||
G+PA2qRt/F51FGOMm1ZscjPXqk2kt3C4BFbz6Vvxsq7D3lmhvFLn4jVA8+OidsM0
|
||||
YUrVMtWET/RkjEIbADbgRXxNUNo+jtQZDU9C1IiAdfk=
|
||||
-----END CERTIFICATE-----
|
31
authentik/enterprise/stages/mtls/tests/fixtures/cert_client.pem
vendored
Normal file
31
authentik/enterprise/stages/mtls/tests/fixtures/cert_client.pem
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFWTCCA0GgAwIBAgIUDEnKCSmIXG/akySGes7bhOGrN/8wDQYJKoZIhvcNAQEL
|
||||
BQAwRjEaMBgGA1UEAwwRYXV0aGVudGlrIFRlc3QgQ0ExEjAQBgNVBAoMCWF1dGhl
|
||||
bnRpazEUMBIGA1UECwwLU2VsZi1zaWduZWQwHhcNMjUwNTE5MTIzODQ2WhcNMjYw
|
||||
NTE1MTIzODQ2WjARMQ8wDQYDVQQDDAZjbGllbnQwggIiMA0GCSqGSIb3DQEBAQUA
|
||||
A4ICDwAwggIKAoICAQCkPkS1V6l0gj0ulxMznkxkgrw4p9Tjd8teSsGZt02A2Eo6
|
||||
7D8FbJ7pp3d5fYW/TWuEKVBLWTID6rijW5EGcdgTM5Jxf/QR+aZTEK6umQxUd4yO
|
||||
mOtp+xVS3KlcsSej2dFpeE5h5VkZizHpvh5xkoAP8W5VtQLOVF0hIeumHnJmaeLj
|
||||
+mhK9PBFpO7k9SFrYYhd/uLrYbIdANihbIO2Q74rNEJHewhFNM7oNSjjEWzRd/7S
|
||||
qNdQij9JGrVG7u8YJJscEQHqyHMYFVCEMjxmsge5BO6Vx5OWmUE3wXPzb5TbyTS4
|
||||
+yg88g9rYTUXrzz+poCyKpaur45qBsdw35lJ8nq69VJj2xJLGQDwoTgGSXRuPciC
|
||||
3OilQI+Ma+j8qQGJxJ8WJxISlf1cuhp+V4ZUd1lawlM5hAXyXmHRlH4pun4y+g7O
|
||||
O34+fE3pK25JjVCicMT/rC2A/sb95j/fHTzzJpbB70U0I50maTcIsOkyw6aiF//E
|
||||
0ShTDz14x22SCMolUc6hxTDZvBB6yrcJHd7d9CCnFH2Sgo13QrtNJ/atXgm13HGh
|
||||
wBzRwK38XUGl/J4pJaxAupTVCPriStUM3m0EYHNelRRUE91pbyeGT0rvOuv00uLw
|
||||
Rj7K7hJZR8avTKWmKrVBVpq+gSojGW1DwBS0NiDNkZs0d/IjB1wkzczEgdZjXwID
|
||||
AQABo3QwcjAfBgNVHSMEGDAWgBTa+Ns6QzqlNvnTGszkouQQtZnVJDAdBgNVHSUE
|
||||
FjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwEQYDVR0RBAowCIIGY2xpZW50MB0GA1Ud
|
||||
DgQWBBT1xg5sXkypRBwvCxBuyfoanaiZ5jANBgkqhkiG9w0BAQsFAAOCAgEAvUAz
|
||||
YwIjxY/0KHZDU8owdILVqKChzfLcy9OHNPyEI3TSOI8X6gNtBO+HE6r8aWGcC9vw
|
||||
zzeIsNQ3UEjvRWi2r+vUVbiPTbFdZboNDSZv6ZmGHxwd85VsjXRGoXV6koCT/9zi
|
||||
9/lCM1DwqwYSwBphMJdRVFRUMluSYk1oHflGeA18xgGuts4eFivJwhabGm1AdVVQ
|
||||
/CYvqCuTxd/DCzWZBdyxYpDru64i/kyeJCt1pThKEFDWmpumFdBI4CxJ0OhxVSGp
|
||||
dOXzK+Y6ULepxCvi6/OpSog52jQ6PnNd1ghiYtq7yO1T4GQz65M1vtHHVvQ3gfBE
|
||||
AuKYQp6io7ypitRx+LpjsBQenyP4FFGfrq7pm90nLluOBOArfSdF0N+CP2wo/YFV
|
||||
9BGf89OtvRi3BXCm2NXkE/Sc4We26tY8x7xNLOmNs8YOT0O3r/EQ690W9GIwRMx0
|
||||
m0r/RXWn5V3o4Jib9r8eH9NzaDstD8g9dECcGfM4fHoM/DAGFaRrNcjMsS1APP3L
|
||||
jp7+BfBSXtrz9V6rVJ3CBLXlLK0AuSm7bqd1MJsGA9uMLpsVZIUA+KawcmPGdPU+
|
||||
NxdpBCtzyurQSUyaTLtVqSeP35gMAwaNzUDph8Uh+vHz+kRwgXS19OQvTaud5LJu
|
||||
nQe4JNS+u5e2VDEBWUxt8NTpu6eShDN0iIEHtxA=
|
||||
-----END CERTIFICATE-----
|
228
authentik/enterprise/stages/mtls/tests/test_stage.py
Normal file
228
authentik/enterprise/stages/mtls/tests/test_stage.py
Normal file
@ -0,0 +1,228 @@
|
||||
from unittest.mock import MagicMock, patch
|
||||
from urllib.parse import quote_plus
|
||||
|
||||
from django.urls import reverse
|
||||
from guardian.shortcuts import assign_perm
|
||||
|
||||
from authentik.core.models import User
|
||||
from authentik.core.tests.utils import (
|
||||
create_test_brand,
|
||||
create_test_cert,
|
||||
create_test_flow,
|
||||
create_test_user,
|
||||
)
|
||||
from authentik.crypto.models import CertificateKeyPair
|
||||
from authentik.enterprise.stages.mtls.models import (
|
||||
CertAttributes,
|
||||
MutualTLSStage,
|
||||
TLSMode,
|
||||
UserAttributes,
|
||||
)
|
||||
from authentik.enterprise.stages.mtls.stage import PLAN_CONTEXT_CERTIFICATE
|
||||
from authentik.flows.models import FlowDesignation, FlowStageBinding
|
||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
|
||||
from authentik.flows.tests import FlowTestCase
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.lib.tests.utils import load_fixture
|
||||
from authentik.outposts.models import Outpost, OutpostType
|
||||
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
|
||||
|
||||
|
||||
class MTLSStageTests(FlowTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.flow = create_test_flow(FlowDesignation.AUTHENTICATION)
|
||||
self.ca = CertificateKeyPair.objects.create(
|
||||
name=generate_id(),
|
||||
certificate_data=load_fixture("fixtures/ca.pem"),
|
||||
)
|
||||
self.stage = MutualTLSStage.objects.create(
|
||||
name=generate_id(),
|
||||
mode=TLSMode.REQUIRED,
|
||||
cert_attribute=CertAttributes.COMMON_NAME,
|
||||
user_attribute=UserAttributes.USERNAME,
|
||||
)
|
||||
|
||||
self.stage.certificate_authorities.add(self.ca)
|
||||
self.binding = FlowStageBinding.objects.create(target=self.flow, stage=self.stage, order=0)
|
||||
self.client_cert = load_fixture("fixtures/cert_client.pem")
|
||||
# User matching the certificate
|
||||
User.objects.filter(username="client").delete()
|
||||
self.cert_user = create_test_user(username="client")
|
||||
|
||||
def test_parse_xfcc(self):
|
||||
"""Test authentik Proxy/Envoy's XFCC format"""
|
||||
with self.assertFlowFinishes() as plan:
|
||||
res = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||
headers={"X-Forwarded-Client-Cert": f"Cert={quote_plus(self.client_cert)}"},
|
||||
)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertStageRedirects(res, reverse("authentik_core:root-redirect"))
|
||||
self.assertEqual(plan().context[PLAN_CONTEXT_PENDING_USER], self.cert_user)
|
||||
|
||||
def test_parse_nginx(self):
|
||||
"""Test nginx's format"""
|
||||
with self.assertFlowFinishes() as plan:
|
||||
res = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||
headers={"SSL-Client-Cert": quote_plus(self.client_cert)},
|
||||
)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertStageRedirects(res, reverse("authentik_core:root-redirect"))
|
||||
self.assertEqual(plan().context[PLAN_CONTEXT_PENDING_USER], self.cert_user)
|
||||
|
||||
def test_parse_traefik(self):
|
||||
"""Test traefik's format"""
|
||||
with self.assertFlowFinishes() as plan:
|
||||
res = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||
headers={"X-Forwarded-TLS-Client-Cert": quote_plus(self.client_cert)},
|
||||
)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertStageRedirects(res, reverse("authentik_core:root-redirect"))
|
||||
self.assertEqual(plan().context[PLAN_CONTEXT_PENDING_USER], self.cert_user)
|
||||
|
||||
def test_parse_outpost_object(self):
|
||||
"""Test outposts's format"""
|
||||
outpost = Outpost.objects.create(name=generate_id(), type=OutpostType.PROXY)
|
||||
assign_perm("pass_outpost_certificate", outpost.user, self.stage)
|
||||
with patch(
|
||||
"authentik.root.middleware.ClientIPMiddleware.get_outpost_user",
|
||||
MagicMock(return_value=outpost.user),
|
||||
):
|
||||
with self.assertFlowFinishes() as plan:
|
||||
res = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||
headers={"X-Authentik-Outpost-Certificate": quote_plus(self.client_cert)},
|
||||
)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertStageRedirects(res, reverse("authentik_core:root-redirect"))
|
||||
self.assertEqual(plan().context[PLAN_CONTEXT_PENDING_USER], self.cert_user)
|
||||
|
||||
def test_parse_outpost_global(self):
|
||||
"""Test outposts's format"""
|
||||
outpost = Outpost.objects.create(name=generate_id(), type=OutpostType.PROXY)
|
||||
assign_perm("authentik_stages_mtls.pass_outpost_certificate", outpost.user)
|
||||
with patch(
|
||||
"authentik.root.middleware.ClientIPMiddleware.get_outpost_user",
|
||||
MagicMock(return_value=outpost.user),
|
||||
):
|
||||
with self.assertFlowFinishes() as plan:
|
||||
res = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||
headers={"X-Authentik-Outpost-Certificate": quote_plus(self.client_cert)},
|
||||
)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertStageRedirects(res, reverse("authentik_core:root-redirect"))
|
||||
self.assertEqual(plan().context[PLAN_CONTEXT_PENDING_USER], self.cert_user)
|
||||
|
||||
def test_parse_outpost_no_perm(self):
|
||||
"""Test outposts's format"""
|
||||
outpost = Outpost.objects.create(name=generate_id(), type=OutpostType.PROXY)
|
||||
with patch(
|
||||
"authentik.root.middleware.ClientIPMiddleware.get_outpost_user",
|
||||
MagicMock(return_value=outpost.user),
|
||||
):
|
||||
res = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||
headers={"X-Authentik-Outpost-Certificate": quote_plus(self.client_cert)},
|
||||
)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertStageResponse(res, self.flow, component="ak-stage-access-denied")
|
||||
|
||||
def test_invalid_cert(self):
|
||||
"""Test invalid certificate"""
|
||||
cert = create_test_cert()
|
||||
with self.assertFlowFinishes() as plan:
|
||||
res = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||
headers={"X-Forwarded-TLS-Client-Cert": quote_plus(cert.certificate_data)},
|
||||
)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertStageResponse(res, self.flow, component="ak-stage-access-denied")
|
||||
self.assertNotIn(PLAN_CONTEXT_PENDING_USER, plan().context)
|
||||
|
||||
def test_auth_no_user(self):
|
||||
"""Test auth with no user"""
|
||||
User.objects.filter(username="client").delete()
|
||||
res = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||
headers={"X-Forwarded-TLS-Client-Cert": quote_plus(self.client_cert)},
|
||||
)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertStageResponse(res, self.flow, component="ak-stage-access-denied")
|
||||
|
||||
def test_brand_ca(self):
|
||||
"""Test using a CA from the brand"""
|
||||
self.stage.certificate_authorities.clear()
|
||||
|
||||
brand = create_test_brand()
|
||||
brand.client_certificates.add(self.ca)
|
||||
with self.assertFlowFinishes() as plan:
|
||||
res = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||
headers={"X-Forwarded-TLS-Client-Cert": quote_plus(self.client_cert)},
|
||||
)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertStageRedirects(res, reverse("authentik_core:root-redirect"))
|
||||
self.assertEqual(plan().context[PLAN_CONTEXT_PENDING_USER], self.cert_user)
|
||||
|
||||
def test_no_ca_optional(self):
|
||||
"""Test using no CA Set"""
|
||||
self.stage.mode = TLSMode.OPTIONAL
|
||||
self.stage.certificate_authorities.clear()
|
||||
self.stage.save()
|
||||
res = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||
headers={"X-Forwarded-TLS-Client-Cert": quote_plus(self.client_cert)},
|
||||
)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertStageRedirects(res, reverse("authentik_core:root-redirect"))
|
||||
|
||||
def test_no_ca_required(self):
|
||||
"""Test using no CA Set"""
|
||||
self.stage.certificate_authorities.clear()
|
||||
self.stage.save()
|
||||
res = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||
headers={"X-Forwarded-TLS-Client-Cert": quote_plus(self.client_cert)},
|
||||
)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertStageResponse(res, self.flow, component="ak-stage-access-denied")
|
||||
|
||||
def test_no_cert_optional(self):
|
||||
"""Test using no cert Set"""
|
||||
self.stage.mode = TLSMode.OPTIONAL
|
||||
self.stage.save()
|
||||
res = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||
)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertStageRedirects(res, reverse("authentik_core:root-redirect"))
|
||||
|
||||
def test_enroll(self):
|
||||
"""Test Enrollment flow"""
|
||||
self.flow.designation = FlowDesignation.ENROLLMENT
|
||||
self.flow.save()
|
||||
with self.assertFlowFinishes() as plan:
|
||||
res = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||
headers={"X-Forwarded-TLS-Client-Cert": quote_plus(self.client_cert)},
|
||||
)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertStageRedirects(res, reverse("authentik_core:root-redirect"))
|
||||
self.assertEqual(plan().context[PLAN_CONTEXT_PROMPT], {"email": None, "name": "client"})
|
||||
self.assertEqual(
|
||||
plan().context[PLAN_CONTEXT_CERTIFICATE],
|
||||
{
|
||||
"fingerprint_sha1": "52:39:ca:1e:3a:1f:78:3a:9f:26:3b:c2:84:99:48:68:99:99:81:8a",
|
||||
"fingerprint_sha256": (
|
||||
"c1:07:8b:7c:e9:02:57:87:1e:92:e5:81:83:21:bc:92:c7:47:65:e3:97:fb:05:97:6f:36:9e:b5:31:77:98:b7"
|
||||
),
|
||||
"issuer": "OU=Self-signed,O=authentik,CN=authentik Test CA",
|
||||
"serial_number": "70153443448884702681996102271549704759327537151",
|
||||
"subject": "CN=client",
|
||||
},
|
||||
)
|
5
authentik/enterprise/stages/mtls/urls.py
Normal file
5
authentik/enterprise/stages/mtls/urls.py
Normal file
@ -0,0 +1,5 @@
|
||||
"""API URLs"""
|
||||
|
||||
from authentik.enterprise.stages.mtls.api import MutualTLSStageViewSet
|
||||
|
||||
api_urlpatterns = [("stages/mtls", MutualTLSStageViewSet)]
|
@ -1,7 +1,10 @@
|
||||
"""Test helpers"""
|
||||
|
||||
from collections.abc import Callable, Generator
|
||||
from contextlib import contextmanager
|
||||
from json import loads
|
||||
from typing import Any
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from django.http.response import HttpResponse
|
||||
from django.urls.base import reverse
|
||||
@ -9,6 +12,8 @@ from rest_framework.test import APITestCase
|
||||
|
||||
from authentik.core.models import User
|
||||
from authentik.flows.models import Flow
|
||||
from authentik.flows.planner import FlowPlan
|
||||
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
||||
|
||||
|
||||
class FlowTestCase(APITestCase):
|
||||
@ -44,3 +49,12 @@ class FlowTestCase(APITestCase):
|
||||
def assertStageRedirects(self, response: HttpResponse, to: str) -> dict[str, Any]:
|
||||
"""Wrapper around assertStageResponse that checks for a redirect"""
|
||||
return self.assertStageResponse(response, component="xak-flow-redirect", to=to)
|
||||
|
||||
@contextmanager
|
||||
def assertFlowFinishes(self) -> Generator[Callable[[], FlowPlan]]:
|
||||
"""Capture the flow plan before the flow finishes and return it"""
|
||||
try:
|
||||
with patch("authentik.flows.views.executor.FlowExecutorView.cancel", MagicMock()):
|
||||
yield lambda: self.client.session.get(SESSION_KEY_PLAN)
|
||||
finally:
|
||||
pass
|
||||
|
@ -5,7 +5,6 @@ from typing import Any
|
||||
|
||||
from billiard.exceptions import SoftTimeLimitExceeded, WorkerLostError
|
||||
from celery.exceptions import CeleryError
|
||||
from channels_redis.core import ChannelFull
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation, ValidationError
|
||||
from django.db import DatabaseError, InternalError, OperationalError, ProgrammingError
|
||||
@ -127,7 +126,6 @@ def before_send(event: dict, hint: dict) -> dict | None:
|
||||
RedisError,
|
||||
ResponseError,
|
||||
# websocket errors
|
||||
ChannelFull,
|
||||
WebSocketException,
|
||||
LocalProtocolError,
|
||||
# rest_framework error
|
||||
|
@ -23,7 +23,6 @@ if TYPE_CHECKING:
|
||||
|
||||
|
||||
class Direction(StrEnum):
|
||||
|
||||
add = "add"
|
||||
remove = "remove"
|
||||
|
||||
@ -37,13 +36,16 @@ SAFE_METHODS = [
|
||||
|
||||
|
||||
class BaseOutgoingSyncClient[
|
||||
TModel: "Model", TConnection: "Model", TSchema: dict, TProvider: "OutgoingSyncProvider"
|
||||
TModel: "Model",
|
||||
TConnection: "Model",
|
||||
TSchema: dict,
|
||||
TProvider: "OutgoingSyncProvider",
|
||||
]:
|
||||
"""Basic Outgoing sync client Client"""
|
||||
|
||||
provider: TProvider
|
||||
connection_type: type[TConnection]
|
||||
connection_type_query: str
|
||||
connection_attr: str
|
||||
mapper: PropertyMappingManager
|
||||
|
||||
can_discover = False
|
||||
@ -63,9 +65,7 @@ class BaseOutgoingSyncClient[
|
||||
def write(self, obj: TModel) -> tuple[TConnection, bool]:
|
||||
"""Write object to destination. Uses self.create and self.update, but
|
||||
can be overwritten for further logic"""
|
||||
connection = self.connection_type.objects.filter(
|
||||
provider=self.provider, **{self.connection_type_query: obj}
|
||||
).first()
|
||||
connection = getattr(obj, self.connection_attr).filter(provider=self.provider).first()
|
||||
try:
|
||||
if not connection:
|
||||
connection = self.create(obj)
|
||||
|
@ -1,9 +1,11 @@
|
||||
"""Websocket tests"""
|
||||
|
||||
from dataclasses import asdict
|
||||
from unittest.mock import patch
|
||||
|
||||
from channels.routing import URLRouter
|
||||
from channels.testing import WebsocketCommunicator
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import TransactionTestCase
|
||||
|
||||
from authentik import __version__
|
||||
@ -14,6 +16,12 @@ from authentik.providers.proxy.models import ProxyProvider
|
||||
from authentik.root import websocket
|
||||
|
||||
|
||||
def patched__get_ct_cached(app_label, codename):
|
||||
"""Caches `ContentType` instances like its `QuerySet` does."""
|
||||
return ContentType.objects.get(app_label=app_label, permission__codename=codename)
|
||||
|
||||
|
||||
@patch("guardian.shortcuts._get_ct_cached", patched__get_ct_cached)
|
||||
class TestOutpostWS(TransactionTestCase):
|
||||
"""Websocket tests"""
|
||||
|
||||
@ -38,6 +46,7 @@ class TestOutpostWS(TransactionTestCase):
|
||||
)
|
||||
connected, _ = await communicator.connect()
|
||||
self.assertFalse(connected)
|
||||
await communicator.disconnect()
|
||||
|
||||
async def test_auth_valid(self):
|
||||
"""Test auth with token"""
|
||||
@ -48,6 +57,7 @@ class TestOutpostWS(TransactionTestCase):
|
||||
)
|
||||
connected, _ = await communicator.connect()
|
||||
self.assertTrue(connected)
|
||||
await communicator.disconnect()
|
||||
|
||||
async def test_send(self):
|
||||
"""Test sending of Hello"""
|
||||
|
@ -7,10 +7,8 @@ from django.db import migrations
|
||||
|
||||
|
||||
def migrate_search_group(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
|
||||
from authentik.core.models import User
|
||||
from django.apps import apps as real_apps
|
||||
from django.contrib.auth.management import create_permissions
|
||||
from guardian.shortcuts import UserObjectPermission
|
||||
|
||||
db_alias = schema_editor.connection.alias
|
||||
|
||||
|
@ -50,3 +50,4 @@ AMR_PASSWORD = "pwd" # nosec
|
||||
AMR_MFA = "mfa"
|
||||
AMR_OTP = "otp"
|
||||
AMR_WEBAUTHN = "user"
|
||||
AMR_SMART_CARD = "sc"
|
||||
|
@ -16,6 +16,7 @@ from authentik.providers.oauth2.constants import (
|
||||
ACR_AUTHENTIK_DEFAULT,
|
||||
AMR_MFA,
|
||||
AMR_PASSWORD,
|
||||
AMR_SMART_CARD,
|
||||
AMR_WEBAUTHN,
|
||||
)
|
||||
from authentik.stages.password.stage import PLAN_CONTEXT_METHOD, PLAN_CONTEXT_METHOD_ARGS
|
||||
@ -139,9 +140,10 @@ class IDToken:
|
||||
amr.append(AMR_PASSWORD)
|
||||
if method == "auth_webauthn_pwl":
|
||||
amr.append(AMR_WEBAUTHN)
|
||||
if "certificate" in method_args:
|
||||
amr.append(AMR_SMART_CARD)
|
||||
if "mfa_devices" in method_args:
|
||||
if len(amr) > 0:
|
||||
amr.append(AMR_MFA)
|
||||
amr.append(AMR_MFA)
|
||||
if amr:
|
||||
id_token.amr = amr
|
||||
|
||||
|
@ -47,6 +47,8 @@ class IngressReconciler(KubernetesObjectReconciler[V1Ingress]):
|
||||
def reconcile(self, current: V1Ingress, reference: V1Ingress):
|
||||
super().reconcile(current, reference)
|
||||
self._check_annotations(current, reference)
|
||||
if current.spec.ingress_class_name != reference.spec.ingress_class_name:
|
||||
raise NeedsUpdate()
|
||||
# Create a list of all expected host and tls hosts
|
||||
expected_hosts = []
|
||||
expected_hosts_tls = []
|
||||
|
@ -34,7 +34,7 @@ class SCIMGroupClient(SCIMClient[Group, SCIMProviderGroup, SCIMGroupSchema]):
|
||||
"""SCIM client for groups"""
|
||||
|
||||
connection_type = SCIMProviderGroup
|
||||
connection_type_query = "group"
|
||||
connection_attr = "scimprovidergroup_set"
|
||||
mapper: PropertyMappingManager
|
||||
|
||||
def __init__(self, provider: SCIMProvider):
|
||||
|
@ -18,7 +18,7 @@ class SCIMUserClient(SCIMClient[User, SCIMProviderUser, SCIMUserSchema]):
|
||||
"""SCIM client for users"""
|
||||
|
||||
connection_type = SCIMProviderUser
|
||||
connection_type_query = "user"
|
||||
connection_attr = "scimprovideruser_set"
|
||||
mapper: PropertyMappingManager
|
||||
|
||||
def __init__(self, provider: SCIMProvider):
|
||||
|
@ -116,7 +116,7 @@ class SCIMProvider(OutgoingSyncProvider, BackchannelProvider):
|
||||
if type == User:
|
||||
# Get queryset of all users with consistent ordering
|
||||
# according to the provider's settings
|
||||
base = User.objects.all().exclude_anonymous()
|
||||
base = User.objects.prefetch_related("scimprovideruser_set").all().exclude_anonymous()
|
||||
if self.exclude_users_service_account:
|
||||
base = base.exclude(type=UserTypes.SERVICE_ACCOUNT).exclude(
|
||||
type=UserTypes.INTERNAL_SERVICE_ACCOUNT
|
||||
@ -126,7 +126,7 @@ class SCIMProvider(OutgoingSyncProvider, BackchannelProvider):
|
||||
return base.order_by("pk")
|
||||
if type == Group:
|
||||
# Get queryset of all groups with consistent ordering
|
||||
return Group.objects.all().order_by("pk")
|
||||
return Group.objects.prefetch_related("scimprovidergroup_set").all().order_by("pk")
|
||||
raise ValueError(f"Invalid type {type}")
|
||||
|
||||
@property
|
||||
|
@ -65,6 +65,7 @@ SHARED_APPS = [
|
||||
"pgactivity",
|
||||
"pglock",
|
||||
"channels",
|
||||
"channels_postgres",
|
||||
]
|
||||
TENANT_APPS = [
|
||||
"django.contrib.auth",
|
||||
@ -132,7 +133,7 @@ TENANT_CREATION_FAKES_MIGRATIONS = True
|
||||
TENANT_BASE_SCHEMA = "template"
|
||||
PUBLIC_SCHEMA_NAME = CONFIG.get("postgresql.default_schema")
|
||||
|
||||
GUARDIAN_MONKEY_PATCH = False
|
||||
GUARDIAN_MONKEY_PATCH_USER = False
|
||||
|
||||
SPECTACULAR_SETTINGS = {
|
||||
"TITLE": "authentik",
|
||||
@ -278,16 +279,6 @@ TEMPLATES = [
|
||||
|
||||
ASGI_APPLICATION = "authentik.root.asgi.application"
|
||||
|
||||
CHANNEL_LAYERS = {
|
||||
"default": {
|
||||
"BACKEND": "channels_redis.pubsub.RedisPubSubChannelLayer",
|
||||
"CONFIG": {
|
||||
"hosts": [CONFIG.get("channel.url") or redis_url(CONFIG.get("redis.db"))],
|
||||
"prefix": "authentik_channels_",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases
|
||||
@ -300,6 +291,16 @@ DATABASE_ROUTERS = (
|
||||
"django_tenants.routers.TenantSyncRouter",
|
||||
)
|
||||
|
||||
CHANNEL_LAYERS = {
|
||||
"default": {
|
||||
"BACKEND": "channels_postgres.core.PostgresChannelLayer",
|
||||
"CONFIG": {
|
||||
**DATABASES["default"],
|
||||
"TIME_ZONE": None,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
# Email
|
||||
# These values should never actually be used, emails are only sent from email stages, which
|
||||
# loads the config directly from CONFIG
|
||||
|
@ -317,7 +317,7 @@ class KerberosSource(Source):
|
||||
usage="accept", name=name, store=self.get_gssapi_store()
|
||||
)
|
||||
except gssapi.exceptions.GSSError as exc:
|
||||
LOGGER.warn("GSSAPI credentials failure", exc=exc)
|
||||
LOGGER.warning("GSSAPI credentials failure", exc=exc)
|
||||
return None
|
||||
|
||||
|
||||
|
@ -97,7 +97,8 @@ class GroupsView(SCIMObjectView):
|
||||
self.logger.warning("Invalid group member", exc=exc)
|
||||
continue
|
||||
query |= Q(uuid=member.value)
|
||||
group.users.set(User.objects.filter(query))
|
||||
if query:
|
||||
group.users.set(User.objects.filter(query))
|
||||
if not connection:
|
||||
connection, _ = SCIMSourceGroup.objects.get_or_create(
|
||||
source=self.source,
|
||||
|
@ -3921,6 +3921,46 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"model",
|
||||
"identifiers"
|
||||
],
|
||||
"properties": {
|
||||
"model": {
|
||||
"const": "authentik_stages_mtls.mutualtlsstage"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"state": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"absent",
|
||||
"present",
|
||||
"created",
|
||||
"must_created"
|
||||
],
|
||||
"default": "present"
|
||||
},
|
||||
"conditions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"permissions": {
|
||||
"$ref": "#/$defs/model_authentik_stages_mtls.mutualtlsstage_permissions"
|
||||
},
|
||||
"attrs": {
|
||||
"$ref": "#/$defs/model_authentik_stages_mtls.mutualtlsstage"
|
||||
},
|
||||
"identifiers": {
|
||||
"$ref": "#/$defs/model_authentik_stages_mtls.mutualtlsstage"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@ -4867,6 +4907,7 @@
|
||||
"authentik.enterprise.providers.microsoft_entra",
|
||||
"authentik.enterprise.providers.ssf",
|
||||
"authentik.enterprise.stages.authenticator_endpoint_gdtc",
|
||||
"authentik.enterprise.stages.mtls",
|
||||
"authentik.enterprise.stages.source",
|
||||
"authentik.events"
|
||||
],
|
||||
@ -4977,6 +5018,7 @@
|
||||
"authentik_providers_microsoft_entra.microsoftentraprovidermapping",
|
||||
"authentik_providers_ssf.ssfprovider",
|
||||
"authentik_stages_authenticator_endpoint_gdtc.authenticatorendpointgdtcstage",
|
||||
"authentik_stages_mtls.mutualtlsstage",
|
||||
"authentik_stages_source.sourcestage",
|
||||
"authentik_events.event",
|
||||
"authentik_events.notificationtransport",
|
||||
@ -7477,6 +7519,11 @@
|
||||
"authentik_stages_invitation.delete_invitationstage",
|
||||
"authentik_stages_invitation.view_invitation",
|
||||
"authentik_stages_invitation.view_invitationstage",
|
||||
"authentik_stages_mtls.add_mutualtlsstage",
|
||||
"authentik_stages_mtls.change_mutualtlsstage",
|
||||
"authentik_stages_mtls.delete_mutualtlsstage",
|
||||
"authentik_stages_mtls.pass_outpost_certificate",
|
||||
"authentik_stages_mtls.view_mutualtlsstage",
|
||||
"authentik_stages_password.add_passwordstage",
|
||||
"authentik_stages_password.change_passwordstage",
|
||||
"authentik_stages_password.delete_passwordstage",
|
||||
@ -13422,6 +13469,16 @@
|
||||
"title": "Web certificate",
|
||||
"description": "Web Certificate used by the authentik Core webserver."
|
||||
},
|
||||
"client_certificates": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Certificates used for client authentication."
|
||||
},
|
||||
"title": "Client certificates",
|
||||
"description": "Certificates used for client authentication."
|
||||
},
|
||||
"attributes": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
@ -14185,6 +14242,11 @@
|
||||
"authentik_stages_invitation.delete_invitationstage",
|
||||
"authentik_stages_invitation.view_invitation",
|
||||
"authentik_stages_invitation.view_invitationstage",
|
||||
"authentik_stages_mtls.add_mutualtlsstage",
|
||||
"authentik_stages_mtls.change_mutualtlsstage",
|
||||
"authentik_stages_mtls.delete_mutualtlsstage",
|
||||
"authentik_stages_mtls.pass_outpost_certificate",
|
||||
"authentik_stages_mtls.view_mutualtlsstage",
|
||||
"authentik_stages_password.add_passwordstage",
|
||||
"authentik_stages_password.change_passwordstage",
|
||||
"authentik_stages_password.delete_passwordstage",
|
||||
@ -15088,6 +15150,161 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"model_authentik_stages_mtls.mutualtlsstage": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"title": "Name"
|
||||
},
|
||||
"flow_set": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"title": "Name"
|
||||
},
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"maxLength": 50,
|
||||
"minLength": 1,
|
||||
"pattern": "^[-a-zA-Z0-9_]+$",
|
||||
"title": "Slug",
|
||||
"description": "Visible in the URL."
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"title": "Title",
|
||||
"description": "Shown as the Title in Flow pages."
|
||||
},
|
||||
"designation": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"authentication",
|
||||
"authorization",
|
||||
"invalidation",
|
||||
"enrollment",
|
||||
"unenrollment",
|
||||
"recovery",
|
||||
"stage_configuration"
|
||||
],
|
||||
"title": "Designation",
|
||||
"description": "Decides what this Flow is used for. For example, the Authentication flow is redirect to when an un-authenticated user visits authentik."
|
||||
},
|
||||
"policy_engine_mode": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"all",
|
||||
"any"
|
||||
],
|
||||
"title": "Policy engine mode"
|
||||
},
|
||||
"compatibility_mode": {
|
||||
"type": "boolean",
|
||||
"title": "Compatibility mode",
|
||||
"description": "Enable compatibility mode, increases compatibility with password managers on mobile devices."
|
||||
},
|
||||
"layout": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"stacked",
|
||||
"content_left",
|
||||
"content_right",
|
||||
"sidebar_left",
|
||||
"sidebar_right"
|
||||
],
|
||||
"title": "Layout"
|
||||
},
|
||||
"denied_action": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"message_continue",
|
||||
"message",
|
||||
"continue"
|
||||
],
|
||||
"title": "Denied action",
|
||||
"description": "Configure what should happen when a flow denies access to a user."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"slug",
|
||||
"title",
|
||||
"designation"
|
||||
]
|
||||
},
|
||||
"title": "Flow set"
|
||||
},
|
||||
"mode": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"optional",
|
||||
"required"
|
||||
],
|
||||
"title": "Mode"
|
||||
},
|
||||
"certificate_authorities": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Configure certificate authorities to validate the certificate against. This option has a higher priority than the `client_certificate` option on `Brand`."
|
||||
},
|
||||
"title": "Certificate authorities",
|
||||
"description": "Configure certificate authorities to validate the certificate against. This option has a higher priority than the `client_certificate` option on `Brand`."
|
||||
},
|
||||
"cert_attribute": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"subject",
|
||||
"common_name",
|
||||
"email"
|
||||
],
|
||||
"title": "Cert attribute"
|
||||
},
|
||||
"user_attribute": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"username",
|
||||
"email"
|
||||
],
|
||||
"title": "User attribute"
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
},
|
||||
"model_authentik_stages_mtls.mutualtlsstage_permissions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"permission"
|
||||
],
|
||||
"properties": {
|
||||
"permission": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"pass_outpost_certificate",
|
||||
"add_mutualtlsstage",
|
||||
"change_mutualtlsstage",
|
||||
"delete_mutualtlsstage",
|
||||
"view_mutualtlsstage"
|
||||
]
|
||||
},
|
||||
"user": {
|
||||
"type": "integer"
|
||||
},
|
||||
"role": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"model_authentik_stages_source.sourcestage": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -19,7 +19,6 @@ import (
|
||||
sentryutils "goauthentik.io/internal/utils/sentry"
|
||||
webutils "goauthentik.io/internal/utils/web"
|
||||
"goauthentik.io/internal/web"
|
||||
"goauthentik.io/internal/web/brand_tls"
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
@ -67,12 +66,12 @@ var rootCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
ws := web.NewWebServer()
|
||||
ws.Core().HealthyCallback = func() {
|
||||
ws.Core().AddHealthyCallback(func() {
|
||||
if config.Get().Outposts.DisableEmbeddedOutpost {
|
||||
return
|
||||
}
|
||||
go attemptProxyStart(ws, u)
|
||||
}
|
||||
})
|
||||
ws.Start()
|
||||
<-ex
|
||||
l.Info("shutting down webserver")
|
||||
@ -95,13 +94,8 @@ func attemptProxyStart(ws *web.WebServer, u *url.URL) {
|
||||
}
|
||||
continue
|
||||
}
|
||||
// Init brand_tls here too since it requires an API Client,
|
||||
// so we just reuse the same one as the outpost uses
|
||||
tw := brand_tls.NewWatcher(ac.Client)
|
||||
go tw.Start()
|
||||
ws.BrandTLS = tw
|
||||
ac.AddRefreshHandler(func() {
|
||||
tw.Check()
|
||||
ws.BrandTLS.Check()
|
||||
})
|
||||
|
||||
srv := proxyv2.NewProxyServer(ac)
|
||||
|
2
go.mod
2
go.mod
@ -27,7 +27,7 @@ require (
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/wwt/guac v1.3.2
|
||||
goauthentik.io/api/v3 v3.2025041.1
|
||||
goauthentik.io/api/v3 v3.2025041.2
|
||||
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
|
||||
golang.org/x/oauth2 v0.30.0
|
||||
golang.org/x/sync v0.14.0
|
||||
|
4
go.sum
4
go.sum
@ -290,8 +290,8 @@ go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y
|
||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
goauthentik.io/api/v3 v3.2025041.1 h1:GAN6AoTmfnCGgx1SyM07jP4/LR/T3rkTEyShSBd3Co8=
|
||||
goauthentik.io/api/v3 v3.2025041.1/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
||||
goauthentik.io/api/v3 v3.2025041.2 h1:vFYYnhcDcxL95RczZwhzt3i4LptFXMvIRN+vgf8sQYg=
|
||||
goauthentik.io/api/v3 v3.2025041.2/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
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=
|
||||
|
@ -21,10 +21,14 @@ func FullVersion() string {
|
||||
return ver
|
||||
}
|
||||
|
||||
func OutpostUserAgent() string {
|
||||
func UserAgentOutpost() string {
|
||||
return fmt.Sprintf("goauthentik.io/outpost/%s", FullVersion())
|
||||
}
|
||||
|
||||
func UserAgentIPC() string {
|
||||
return fmt.Sprintf("goauthentik.io/ipc/%s", FullVersion())
|
||||
}
|
||||
|
||||
func UserAgent() string {
|
||||
return fmt.Sprintf("authentik@%s", FullVersion())
|
||||
}
|
||||
|
@ -18,8 +18,8 @@ import (
|
||||
)
|
||||
|
||||
type GoUnicorn struct {
|
||||
Healthcheck func() bool
|
||||
HealthyCallback func()
|
||||
Healthcheck func() bool
|
||||
healthyCallbacks []func()
|
||||
|
||||
log *log.Entry
|
||||
p *exec.Cmd
|
||||
@ -32,12 +32,12 @@ type GoUnicorn struct {
|
||||
func New(healthcheck func() bool) *GoUnicorn {
|
||||
logger := log.WithField("logger", "authentik.router.unicorn")
|
||||
g := &GoUnicorn{
|
||||
Healthcheck: healthcheck,
|
||||
log: logger,
|
||||
started: false,
|
||||
killed: false,
|
||||
alive: false,
|
||||
HealthyCallback: func() {},
|
||||
Healthcheck: healthcheck,
|
||||
log: logger,
|
||||
started: false,
|
||||
killed: false,
|
||||
alive: false,
|
||||
healthyCallbacks: []func(){},
|
||||
}
|
||||
g.initCmd()
|
||||
c := make(chan os.Signal, 1)
|
||||
@ -79,6 +79,10 @@ func (g *GoUnicorn) initCmd() {
|
||||
g.p.Stderr = os.Stderr
|
||||
}
|
||||
|
||||
func (g *GoUnicorn) AddHealthyCallback(cb func()) {
|
||||
g.healthyCallbacks = append(g.healthyCallbacks, cb)
|
||||
}
|
||||
|
||||
func (g *GoUnicorn) IsRunning() bool {
|
||||
return g.alive
|
||||
}
|
||||
@ -101,7 +105,9 @@ func (g *GoUnicorn) healthcheck() {
|
||||
if g.Healthcheck() {
|
||||
g.alive = true
|
||||
g.log.Debug("backend is alive, backing off with healthchecks")
|
||||
g.HealthyCallback()
|
||||
for _, cb := range g.healthyCallbacks {
|
||||
cb()
|
||||
}
|
||||
break
|
||||
}
|
||||
g.log.Debug("backend not alive yet")
|
||||
|
@ -62,7 +62,7 @@ func NewAPIController(akURL url.URL, token string) *APIController {
|
||||
apiConfig.Scheme = akURL.Scheme
|
||||
apiConfig.HTTPClient = &http.Client{
|
||||
Transport: web.NewUserAgentTransport(
|
||||
constants.OutpostUserAgent(),
|
||||
constants.UserAgentOutpost(),
|
||||
web.NewTracingTransport(
|
||||
rsp.Context(),
|
||||
GetTLSTransport(),
|
||||
|
@ -38,7 +38,7 @@ func (ac *APIController) initWS(akURL url.URL, outpostUUID string) error {
|
||||
|
||||
header := http.Header{
|
||||
"Authorization": []string{authHeader},
|
||||
"User-Agent": []string{constants.OutpostUserAgent()},
|
||||
"User-Agent": []string{constants.UserAgentOutpost()},
|
||||
}
|
||||
|
||||
dialer := websocket.Dialer{
|
||||
|
@ -3,6 +3,8 @@ package ak
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"goauthentik.io/api/v3"
|
||||
@ -67,16 +69,34 @@ func (cs *CryptoStore) Fetch(uuid string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
x509cert, err := tls.X509KeyPair([]byte(cert.Data), []byte(key.Data))
|
||||
if err != nil {
|
||||
return err
|
||||
var tcert tls.Certificate
|
||||
if key.Data != "" {
|
||||
x509cert, err := tls.X509KeyPair([]byte(cert.Data), []byte(key.Data))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tcert = x509cert
|
||||
} else {
|
||||
p, _ := pem.Decode([]byte(cert.Data))
|
||||
x509cert, err := x509.ParseCertificate(p.Bytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tcert = tls.Certificate{
|
||||
Certificate: [][]byte{x509cert.Raw},
|
||||
Leaf: x509cert,
|
||||
}
|
||||
}
|
||||
cs.certificates[uuid] = &x509cert
|
||||
cs.certificates[uuid] = &tcert
|
||||
cs.fingerprints[uuid] = cfp
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cs *CryptoStore) Get(uuid string) *tls.Certificate {
|
||||
c, ok := cs.certificates[uuid]
|
||||
if ok {
|
||||
return c
|
||||
}
|
||||
err := cs.Fetch(uuid)
|
||||
if err != nil {
|
||||
cs.log.WithError(err).Warning("failed to fetch certificate")
|
||||
|
@ -55,7 +55,7 @@ func doGlobalSetup(outpost api.Outpost, globalConfig *api.Config) {
|
||||
EnableTracing: true,
|
||||
TracesSampler: sentryutils.SamplerFunc(float64(globalConfig.ErrorReporting.TracesSampleRate)),
|
||||
Release: fmt.Sprintf("authentik@%s", constants.VERSION),
|
||||
HTTPTransport: webutils.NewUserAgentTransport(constants.OutpostUserAgent(), http.DefaultTransport),
|
||||
HTTPTransport: webutils.NewUserAgentTransport(constants.UserAgentOutpost(), http.DefaultTransport),
|
||||
IgnoreErrors: []string{
|
||||
http.ErrAbortHandler.Error(),
|
||||
},
|
||||
|
@ -61,7 +61,7 @@ func NewFlowExecutor(ctx context.Context, flowSlug string, refConfig *api.Config
|
||||
l.WithError(err).Warning("Failed to create cookiejar")
|
||||
panic(err)
|
||||
}
|
||||
transport := web.NewUserAgentTransport(constants.OutpostUserAgent(), web.NewTracingTransport(rsp.Context(), ak.GetTLSTransport()))
|
||||
transport := web.NewUserAgentTransport(constants.UserAgentOutpost(), web.NewTracingTransport(rsp.Context(), ak.GetTLSTransport()))
|
||||
fe := &FlowExecutor{
|
||||
Params: url.Values{},
|
||||
Answers: make(map[StageComponent]string),
|
||||
|
@ -52,7 +52,7 @@ func (a *Application) addHeaders(headers http.Header, c *Claims) {
|
||||
headers.Set("X-authentik-meta-outpost", a.outpostName)
|
||||
headers.Set("X-authentik-meta-provider", a.proxyConfig.Name)
|
||||
headers.Set("X-authentik-meta-app", a.proxyConfig.AssignedApplicationSlug)
|
||||
headers.Set("X-authentik-meta-version", constants.OutpostUserAgent())
|
||||
headers.Set("X-authentik-meta-version", constants.UserAgentOutpost())
|
||||
|
||||
if c.Proxy == nil {
|
||||
return
|
||||
|
@ -31,7 +31,7 @@ func (ps *ProxyServer) Refresh() error {
|
||||
ua := fmt.Sprintf(" (provider=%s)", provider.Name)
|
||||
hc := &http.Client{
|
||||
Transport: web.NewUserAgentTransport(
|
||||
constants.OutpostUserAgent()+ua,
|
||||
constants.UserAgentOutpost()+ua,
|
||||
web.NewTracingTransport(
|
||||
rsp.Context(),
|
||||
ak.GetTLSTransport(),
|
||||
|
@ -61,7 +61,7 @@ func (c *Connection) initSocket(forChannel string) error {
|
||||
|
||||
header := http.Header{
|
||||
"Authorization": []string{authHeader},
|
||||
"User-Agent": []string{constants.OutpostUserAgent()},
|
||||
"User-Agent": []string{constants.UserAgentOutpost()},
|
||||
}
|
||||
|
||||
dialer := websocket.Dialer{
|
||||
|
@ -1,6 +1,7 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
@ -9,6 +10,14 @@ import (
|
||||
"goauthentik.io/internal/config"
|
||||
)
|
||||
|
||||
type allowedProxyRequestContext string
|
||||
|
||||
const allowedProxyRequest allowedProxyRequestContext = ""
|
||||
|
||||
func IsRequestFromTrustedProxy(r *http.Request) bool {
|
||||
return r.Context().Value(allowedProxyRequest) != nil
|
||||
}
|
||||
|
||||
// ProxyHeaders Set proxy headers like X-Forwarded-For and such, but only if the direct connection
|
||||
// comes from a client that's in a list of trusted CIDRs
|
||||
func ProxyHeaders() func(http.Handler) http.Handler {
|
||||
@ -20,7 +29,6 @@ func ProxyHeaders() func(http.Handler) http.Handler {
|
||||
}
|
||||
nets = append(nets, cidr)
|
||||
}
|
||||
ph := handlers.ProxyHeaders
|
||||
return func(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
host, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
@ -30,7 +38,8 @@ func ProxyHeaders() func(http.Handler) http.Handler {
|
||||
for _, allowedCidr := range nets {
|
||||
if remoteAddr != nil && allowedCidr.Contains(remoteAddr) {
|
||||
log.WithField("remoteAddr", remoteAddr).WithField("cidr", allowedCidr.String()).Trace("Setting proxy headers")
|
||||
ph(h).ServeHTTP(w, r)
|
||||
rr := r.WithContext(context.WithValue(r.Context(), allowedProxyRequest, true))
|
||||
handlers.ProxyHeaders(h).ServeHTTP(w, rr)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package brand_tls
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -56,22 +57,37 @@ func (w *Watcher) Check() {
|
||||
return
|
||||
}
|
||||
for _, b := range brands {
|
||||
kp := b.WebCertificate.Get()
|
||||
if kp == nil {
|
||||
continue
|
||||
kp := b.GetWebCertificate()
|
||||
if kp != "" {
|
||||
err := w.cs.AddKeypair(kp)
|
||||
if err != nil {
|
||||
w.log.WithError(err).WithField("kp", kp).Warning("failed to add web certificate")
|
||||
}
|
||||
}
|
||||
err := w.cs.AddKeypair(*kp)
|
||||
if err != nil {
|
||||
w.log.WithError(err).Warning("failed to add certificate")
|
||||
for _, crt := range b.GetClientCertificates() {
|
||||
if crt != "" {
|
||||
err := w.cs.AddKeypair(crt)
|
||||
if err != nil {
|
||||
w.log.WithError(err).WithField("kp", kp).Warning("failed to add client certificate")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
w.brands = brands
|
||||
}
|
||||
|
||||
func (w *Watcher) GetCertificate(ch *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
type CertificateConfig struct {
|
||||
Web *tls.Certificate
|
||||
Client *x509.CertPool
|
||||
}
|
||||
|
||||
func (w *Watcher) GetCertificate(ch *tls.ClientHelloInfo) *CertificateConfig {
|
||||
var bestSelection *api.Brand
|
||||
config := CertificateConfig{
|
||||
Web: w.fallback,
|
||||
}
|
||||
for _, t := range w.brands {
|
||||
if t.WebCertificate.Get() == nil {
|
||||
if !t.WebCertificate.IsSet() && len(t.GetClientCertificates()) < 1 {
|
||||
continue
|
||||
}
|
||||
if *t.Default {
|
||||
@ -82,11 +98,20 @@ func (w *Watcher) GetCertificate(ch *tls.ClientHelloInfo) (*tls.Certificate, err
|
||||
}
|
||||
}
|
||||
if bestSelection == nil {
|
||||
return w.fallback, nil
|
||||
return &config
|
||||
}
|
||||
cert := w.cs.Get(bestSelection.GetWebCertificate())
|
||||
if cert == nil {
|
||||
return w.fallback, nil
|
||||
if bestSelection.GetWebCertificate() != "" {
|
||||
if cert := w.cs.Get(bestSelection.GetWebCertificate()); cert != nil {
|
||||
config.Web = cert
|
||||
}
|
||||
}
|
||||
return cert, nil
|
||||
if len(bestSelection.GetClientCertificates()) > 0 {
|
||||
config.Client = x509.NewCertPool()
|
||||
for _, kp := range bestSelection.GetClientCertificates() {
|
||||
if cert := w.cs.Get(kp); cert != nil {
|
||||
config.Client.AddCert(cert.Leaf)
|
||||
}
|
||||
}
|
||||
}
|
||||
return &config
|
||||
}
|
||||
|
@ -1,15 +1,11 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gorilla/securecookie"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
@ -18,8 +14,6 @@ import (
|
||||
"goauthentik.io/internal/utils/sentry"
|
||||
)
|
||||
|
||||
const MetricsKeyFile = "authentik-core-metrics.key"
|
||||
|
||||
var Requests = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Name: "authentik_main_request_duration_seconds",
|
||||
Help: "API request latencies in seconds",
|
||||
@ -27,14 +21,6 @@ var Requests = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
|
||||
func (ws *WebServer) runMetricsServer() {
|
||||
l := log.WithField("logger", "authentik.router.metrics")
|
||||
tmp := os.TempDir()
|
||||
key := base64.StdEncoding.EncodeToString(securecookie.GenerateRandomKey(64))
|
||||
keyPath := path.Join(tmp, MetricsKeyFile)
|
||||
err := os.WriteFile(keyPath, []byte(key), 0o600)
|
||||
if err != nil {
|
||||
l.WithError(err).Warning("failed to save metrics key")
|
||||
return
|
||||
}
|
||||
|
||||
m := mux.NewRouter()
|
||||
m.Use(sentry.SentryNoSampleMiddleware)
|
||||
@ -51,7 +37,7 @@ func (ws *WebServer) runMetricsServer() {
|
||||
l.WithError(err).Warning("failed to get upstream metrics")
|
||||
return
|
||||
}
|
||||
re.Header.Set("Authorization", fmt.Sprintf("Bearer %s", key))
|
||||
re.Header.Set("Authorization", fmt.Sprintf("Bearer %s", ws.metricsKey))
|
||||
res, err := ws.upstreamHttpClient().Do(re)
|
||||
if err != nil {
|
||||
l.WithError(err).Warning("failed to get upstream metrics")
|
||||
@ -64,13 +50,9 @@ func (ws *WebServer) runMetricsServer() {
|
||||
}
|
||||
})
|
||||
l.WithField("listen", config.Get().Listen.Metrics).Info("Starting Metrics server")
|
||||
err = http.ListenAndServe(config.Get().Listen.Metrics, m)
|
||||
err := http.ListenAndServe(config.Get().Listen.Metrics, m)
|
||||
if err != nil {
|
||||
l.WithError(err).Warning("Failed to start metrics server")
|
||||
}
|
||||
l.WithField("listen", config.Get().Listen.Metrics).Info("Stopping Metrics server")
|
||||
err = os.Remove(keyPath)
|
||||
if err != nil {
|
||||
l.WithError(err).Warning("failed to remove metrics key file")
|
||||
}
|
||||
}
|
||||
|
@ -2,21 +2,29 @@ package web
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"goauthentik.io/internal/config"
|
||||
"goauthentik.io/internal/utils/sentry"
|
||||
"goauthentik.io/internal/utils/web"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrAuthentikStarting = errors.New("authentik starting")
|
||||
)
|
||||
|
||||
const (
|
||||
maxBodyBytes = 32 * 1024 * 1024
|
||||
)
|
||||
|
||||
func (ws *WebServer) configureProxy() {
|
||||
// Reverse proxy to the application server
|
||||
director := func(req *http.Request) {
|
||||
@ -26,8 +34,25 @@ func (ws *WebServer) configureProxy() {
|
||||
// explicitly disable User-Agent so it's not set to default value
|
||||
req.Header.Set("User-Agent", "")
|
||||
}
|
||||
if !web.IsRequestFromTrustedProxy(req) {
|
||||
// If the request isn't coming from a trusted proxy, delete MTLS headers
|
||||
req.Header.Del("SSL-Client-Cert") // nginx-ingress
|
||||
req.Header.Del("X-Forwarded-TLS-Client-Cert") // traefik
|
||||
req.Header.Del("X-Forwarded-Client-Cert") // envoy
|
||||
}
|
||||
if req.TLS != nil {
|
||||
req.Header.Set("X-Forwarded-Proto", "https")
|
||||
if len(req.TLS.PeerCertificates) > 0 {
|
||||
pems := make([]string, len(req.TLS.PeerCertificates))
|
||||
for i, crt := range req.TLS.PeerCertificates {
|
||||
pem := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: crt.Raw,
|
||||
})
|
||||
pems[i] = "Cert=" + url.QueryEscape(string(pem))
|
||||
}
|
||||
req.Header.Set("X-Forwarded-Client-Cert", strings.Join(pems, ","))
|
||||
}
|
||||
}
|
||||
ws.log.WithField("url", req.URL.String()).WithField("headers", req.Header).Trace("tracing request to backend")
|
||||
}
|
||||
@ -57,7 +82,7 @@ func (ws *WebServer) configureProxy() {
|
||||
Requests.With(prometheus.Labels{
|
||||
"dest": "core",
|
||||
}).Observe(float64(elapsed) / float64(time.Second))
|
||||
r.Body = http.MaxBytesReader(rw, r.Body, 32*1024*1024)
|
||||
r.Body = http.MaxBytesReader(rw, r.Body, maxBodyBytes)
|
||||
rp.ServeHTTP(rw, r)
|
||||
}))
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package web
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
@ -13,17 +14,27 @@ import (
|
||||
|
||||
"github.com/gorilla/handlers"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gorilla/securecookie"
|
||||
"github.com/pires/go-proxyproto"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"goauthentik.io/api/v3"
|
||||
"goauthentik.io/internal/config"
|
||||
"goauthentik.io/internal/constants"
|
||||
"goauthentik.io/internal/gounicorn"
|
||||
"goauthentik.io/internal/outpost/ak"
|
||||
"goauthentik.io/internal/outpost/proxyv2"
|
||||
"goauthentik.io/internal/utils"
|
||||
"goauthentik.io/internal/utils/web"
|
||||
"goauthentik.io/internal/web/brand_tls"
|
||||
)
|
||||
|
||||
const (
|
||||
IPCKeyFile = "authentik-core-ipc.key"
|
||||
MetricsKeyFile = "authentik-core-metrics.key"
|
||||
UnixSocketName = "authentik-core.sock"
|
||||
)
|
||||
|
||||
type WebServer struct {
|
||||
Bind string
|
||||
BindTLS bool
|
||||
@ -40,9 +51,10 @@ type WebServer struct {
|
||||
log *log.Entry
|
||||
upstreamClient *http.Client
|
||||
upstreamURL *url.URL
|
||||
}
|
||||
|
||||
const UnixSocketName = "authentik-core.sock"
|
||||
metricsKey string
|
||||
ipcKey string
|
||||
}
|
||||
|
||||
func NewWebServer() *WebServer {
|
||||
l := log.WithField("logger", "authentik.router")
|
||||
@ -76,7 +88,7 @@ func NewWebServer() *WebServer {
|
||||
mainRouter: mainHandler,
|
||||
loggingRouter: loggingHandler,
|
||||
log: l,
|
||||
gunicornReady: true,
|
||||
gunicornReady: false,
|
||||
upstreamClient: upstreamClient,
|
||||
upstreamURL: u,
|
||||
}
|
||||
@ -103,7 +115,59 @@ func NewWebServer() *WebServer {
|
||||
return ws
|
||||
}
|
||||
|
||||
func (ws *WebServer) prepareKeys() {
|
||||
tmp := os.TempDir()
|
||||
key := base64.StdEncoding.EncodeToString(securecookie.GenerateRandomKey(64))
|
||||
err := os.WriteFile(path.Join(tmp, MetricsKeyFile), []byte(key), 0o600)
|
||||
if err != nil {
|
||||
ws.log.WithError(err).Warning("failed to save metrics key")
|
||||
return
|
||||
}
|
||||
ws.metricsKey = key
|
||||
|
||||
key = base64.StdEncoding.EncodeToString(securecookie.GenerateRandomKey(64))
|
||||
err = os.WriteFile(path.Join(tmp, IPCKeyFile), []byte(key), 0o600)
|
||||
if err != nil {
|
||||
ws.log.WithError(err).Warning("failed to save ipc key")
|
||||
return
|
||||
}
|
||||
ws.ipcKey = key
|
||||
}
|
||||
|
||||
func (ws *WebServer) Start() {
|
||||
ws.prepareKeys()
|
||||
|
||||
u, err := url.Parse(fmt.Sprintf("http://%s%s", config.Get().Listen.HTTP, config.Get().Web.Path))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
apiConfig := api.NewConfiguration()
|
||||
apiConfig.Host = u.Host
|
||||
apiConfig.Scheme = u.Scheme
|
||||
apiConfig.HTTPClient = &http.Client{
|
||||
Transport: web.NewUserAgentTransport(
|
||||
constants.UserAgentIPC(),
|
||||
ak.GetTLSTransport(),
|
||||
),
|
||||
}
|
||||
apiConfig.Servers = api.ServerConfigurations{
|
||||
{
|
||||
URL: fmt.Sprintf("%sapi/v3", u.Path),
|
||||
},
|
||||
}
|
||||
apiConfig.AddDefaultHeader("Authorization", fmt.Sprintf("Bearer %s", ws.ipcKey))
|
||||
|
||||
// create the API client, with the transport
|
||||
apiClient := api.NewAPIClient(apiConfig)
|
||||
|
||||
// Init brand_tls here too since it requires an API Client,
|
||||
// so we just reuse the same one as the outpost uses
|
||||
tw := brand_tls.NewWatcher(apiClient)
|
||||
ws.BrandTLS = tw
|
||||
ws.g.AddHealthyCallback(func() {
|
||||
go tw.Start()
|
||||
})
|
||||
|
||||
go ws.runMetricsServer()
|
||||
go ws.attemptStartBackend()
|
||||
go ws.listenPlain()
|
||||
@ -112,23 +176,23 @@ func (ws *WebServer) Start() {
|
||||
|
||||
func (ws *WebServer) attemptStartBackend() {
|
||||
for {
|
||||
if !ws.gunicornReady {
|
||||
if ws.gunicornReady {
|
||||
return
|
||||
}
|
||||
err := ws.g.Start()
|
||||
log.WithField("logger", "authentik.router").WithError(err).Warning("gunicorn process died, restarting")
|
||||
ws.log.WithError(err).Warning("gunicorn process died, restarting")
|
||||
if err != nil {
|
||||
log.WithField("logger", "authentik.router").WithError(err).Error("gunicorn failed to start, restarting")
|
||||
ws.log.WithError(err).Error("gunicorn failed to start, restarting")
|
||||
continue
|
||||
}
|
||||
failedChecks := 0
|
||||
for range time.NewTicker(30 * time.Second).C {
|
||||
if !ws.g.IsRunning() {
|
||||
log.WithField("logger", "authentik.router").Warningf("gunicorn process failed healthcheck %d times", failedChecks)
|
||||
ws.log.Warningf("gunicorn process failed healthcheck %d times", failedChecks)
|
||||
failedChecks += 1
|
||||
}
|
||||
if failedChecks >= 3 {
|
||||
log.WithField("logger", "authentik.router").WithError(err).Error("gunicorn process failed healthcheck three times, restarting")
|
||||
ws.log.WithError(err).Error("gunicorn process failed healthcheck three times, restarting")
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -146,6 +210,15 @@ func (ws *WebServer) upstreamHttpClient() *http.Client {
|
||||
func (ws *WebServer) Shutdown() {
|
||||
ws.log.Info("shutting down gunicorn")
|
||||
ws.g.Kill()
|
||||
tmp := os.TempDir()
|
||||
err := os.Remove(path.Join(tmp, MetricsKeyFile))
|
||||
if err != nil {
|
||||
ws.log.WithError(err).Warning("failed to remove metrics key file")
|
||||
}
|
||||
err = os.Remove(path.Join(tmp, IPCKeyFile))
|
||||
if err != nil {
|
||||
ws.log.WithError(err).Warning("failed to remove ipc key file")
|
||||
}
|
||||
ws.stop <- struct{}{}
|
||||
}
|
||||
|
||||
|
@ -12,40 +12,57 @@ import (
|
||||
"goauthentik.io/internal/utils/web"
|
||||
)
|
||||
|
||||
func (ws *WebServer) GetCertificate() func(ch *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
cert, err := crypto.GenerateSelfSignedCert()
|
||||
func (ws *WebServer) GetCertificate() func(ch *tls.ClientHelloInfo) (*tls.Config, error) {
|
||||
fallback, err := crypto.GenerateSelfSignedCert()
|
||||
if err != nil {
|
||||
ws.log.WithError(err).Error("failed to generate default cert")
|
||||
}
|
||||
return func(ch *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
return func(ch *tls.ClientHelloInfo) (*tls.Config, error) {
|
||||
cfg := utils.GetTLSConfig()
|
||||
if ch.ServerName == "" {
|
||||
return &cert, nil
|
||||
cfg.Certificates = []tls.Certificate{fallback}
|
||||
return cfg, nil
|
||||
}
|
||||
if ws.ProxyServer != nil {
|
||||
appCert := ws.ProxyServer.GetCertificate(ch.ServerName)
|
||||
if appCert != nil {
|
||||
return appCert, nil
|
||||
cfg.Certificates = []tls.Certificate{*appCert}
|
||||
return cfg, nil
|
||||
}
|
||||
}
|
||||
if ws.BrandTLS != nil {
|
||||
return ws.BrandTLS.GetCertificate(ch)
|
||||
bcert := ws.BrandTLS.GetCertificate(ch)
|
||||
cfg.Certificates = []tls.Certificate{*bcert.Web}
|
||||
ws.log.Trace("using brand web Certificate")
|
||||
if bcert.Client != nil {
|
||||
cfg.ClientCAs = bcert.Client
|
||||
cfg.ClientAuth = tls.RequestClientCert
|
||||
ws.log.Trace("using brand client Certificate")
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
ws.log.Trace("using default, self-signed certificate")
|
||||
return &cert, nil
|
||||
cfg.Certificates = []tls.Certificate{fallback}
|
||||
return cfg, nil
|
||||
}
|
||||
}
|
||||
|
||||
// ServeHTTPS constructs a net.Listener and starts handling HTTPS requests
|
||||
func (ws *WebServer) listenTLS() {
|
||||
tlsConfig := utils.GetTLSConfig()
|
||||
tlsConfig.GetCertificate = ws.GetCertificate()
|
||||
tlsConfig.GetConfigForClient = ws.GetCertificate()
|
||||
|
||||
ln, err := net.Listen("tcp", config.Get().Listen.HTTPS)
|
||||
if err != nil {
|
||||
ws.log.WithError(err).Warning("failed to listen (TLS)")
|
||||
return
|
||||
}
|
||||
proxyListener := &proxyproto.Listener{Listener: web.TCPKeepAliveListener{TCPListener: ln.(*net.TCPListener)}, ConnPolicy: utils.GetProxyConnectionPolicy()}
|
||||
proxyListener := &proxyproto.Listener{
|
||||
Listener: web.TCPKeepAliveListener{
|
||||
TCPListener: ln.(*net.TCPListener),
|
||||
},
|
||||
ConnPolicy: utils.GetProxyConnectionPolicy(),
|
||||
}
|
||||
defer func() {
|
||||
err := proxyListener.Close()
|
||||
if err != nil {
|
||||
|
@ -83,7 +83,8 @@ if [[ "$1" == "server" ]]; then
|
||||
run_authentik
|
||||
elif [[ "$1" == "worker" ]]; then
|
||||
set_mode "worker"
|
||||
check_if_root "python -m manage worker"
|
||||
shift
|
||||
check_if_root "python -m manage worker $@"
|
||||
elif [[ "$1" == "worker-status" ]]; then
|
||||
wait_for_db
|
||||
celery -A authentik.root.celery flower \
|
||||
|
8
lifecycle/aws/package-lock.json
generated
8
lifecycle/aws/package-lock.json
generated
@ -9,7 +9,7 @@
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"aws-cdk": "^2.1015.0",
|
||||
"aws-cdk": "^2.1016.1",
|
||||
"cross-env": "^7.0.3"
|
||||
},
|
||||
"engines": {
|
||||
@ -17,9 +17,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk": {
|
||||
"version": "2.1015.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1015.0.tgz",
|
||||
"integrity": "sha512-txd+yMVVybtLfiwT409+fahbP0SkiwhmQvQf6PVVYnWzDPSknxYlUNJHisHV4tJEcbHWn1QPsLmqqMT0bw8hBg==",
|
||||
"version": "2.1016.1",
|
||||
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1016.1.tgz",
|
||||
"integrity": "sha512-248TBiluT8jHUjkpzvWJOHv2fS+An9fiII3eji8H7jwfTu5yMBk7on4B/AVNr9A1GXJk9I32qf9Q0A3rLWRYPQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
|
@ -10,7 +10,7 @@
|
||||
"node": ">=20"
|
||||
},
|
||||
"devDependencies": {
|
||||
"aws-cdk": "^2.1015.0",
|
||||
"aws-cdk": "^2.1016.1",
|
||||
"cross-env": "^7.0.3"
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
|
||||
"POT-Creation-Date: 2025-05-20 00:10+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -93,6 +93,10 @@ msgstr ""
|
||||
msgid "Web Certificate used by the authentik Core webserver."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Certificates used for client authentication."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Brand"
|
||||
msgstr ""
|
||||
@ -616,6 +620,32 @@ msgstr ""
|
||||
msgid "Verifying your browser..."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid ""
|
||||
"Configure certificate authorities to validate the certificate against. This "
|
||||
"option has a higher priority than the `client_certificate` option on `Brand`."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Mutual TLS Stage"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Mutual TLS Stages"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Permissions to pass Certificates for outposts."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/stage.py
|
||||
msgid "Certificate required but no certificate was given."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/stage.py
|
||||
msgid "No user found for certificate."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/source/models.py
|
||||
msgid ""
|
||||
"Amount of time a user can take to return from the source to continue the "
|
||||
|
@ -19,7 +19,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
|
||||
"POT-Creation-Date: 2025-05-20 00:10+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: Marc Schmitt, 2025\n"
|
||||
"Language-Team: French (https://app.transifex.com/authentik/teams/119923/fr/)\n"
|
||||
@ -113,6 +113,10 @@ msgstr ""
|
||||
msgid "Web Certificate used by the authentik Core webserver."
|
||||
msgstr "Certificate Web utilisé par le serveur web d'authentik core."
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Certificates used for client authentication."
|
||||
msgstr "Certificats utilisés pour l'authentification client."
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Brand"
|
||||
msgstr "Marque"
|
||||
@ -675,6 +679,36 @@ msgstr "Appareils point de terminaison"
|
||||
msgid "Verifying your browser..."
|
||||
msgstr "Vérification de votre navigateur..."
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid ""
|
||||
"Configure certificate authorities to validate the certificate against. This "
|
||||
"option has a higher priority than the `client_certificate` option on "
|
||||
"`Brand`."
|
||||
msgstr ""
|
||||
"Configurez les autorités de certification pour valider le certificat. Cette "
|
||||
"option a une priorité plus élevée que l'option `client_certificate` sur "
|
||||
"`Marques`."
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Mutual TLS Stage"
|
||||
msgstr "Étape TLS mutuel"
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Mutual TLS Stages"
|
||||
msgstr "Étapes TLS mutuel"
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Permissions to pass Certificates for outposts."
|
||||
msgstr "Autorisations de délivrer des certificats pour les avant-postes."
|
||||
|
||||
#: authentik/enterprise/stages/mtls/stage.py
|
||||
msgid "Certificate required but no certificate was given."
|
||||
msgstr "Certificat requis mais aucun certificat n'a été fourni."
|
||||
|
||||
#: authentik/enterprise/stages/mtls/stage.py
|
||||
msgid "No user found for certificate."
|
||||
msgstr "Aucun utilisateur trouvé pour le certificat."
|
||||
|
||||
#: authentik/enterprise/stages/source/models.py
|
||||
msgid ""
|
||||
"Amount of time a user can take to return from the source to continue the "
|
||||
|
Binary file not shown.
@ -15,7 +15,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
|
||||
"POT-Creation-Date: 2025-05-20 00:10+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: deluxghost, 2025\n"
|
||||
"Language-Team: Chinese Simplified (https://app.transifex.com/authentik/teams/119923/zh-Hans/)\n"
|
||||
@ -102,6 +102,10 @@ msgstr "设置时,外部用户在验证身份后会被重定向到此应用程
|
||||
msgid "Web Certificate used by the authentik Core webserver."
|
||||
msgstr "authentik 核心 Web 服务器使用的 Web 证书。"
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Certificates used for client authentication."
|
||||
msgstr "用于客户端身份验证的证书"
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Brand"
|
||||
msgstr "品牌"
|
||||
@ -626,6 +630,33 @@ msgstr "端点设备"
|
||||
msgid "Verifying your browser..."
|
||||
msgstr "正在验证您的浏览器…"
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid ""
|
||||
"Configure certificate authorities to validate the certificate against. This "
|
||||
"option has a higher priority than the `client_certificate` option on "
|
||||
"`Brand`."
|
||||
msgstr "配置用于验证证书的证书机构。此选项的优先级比“品牌”中的“客户端证书”更高。"
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Mutual TLS Stage"
|
||||
msgstr "双向 TLS 阶段"
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Mutual TLS Stages"
|
||||
msgstr "双向 TLS 阶段"
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Permissions to pass Certificates for outposts."
|
||||
msgstr "为前哨传递证书的权限。"
|
||||
|
||||
#: authentik/enterprise/stages/mtls/stage.py
|
||||
msgid "Certificate required but no certificate was given."
|
||||
msgstr "需要证书但未提供。"
|
||||
|
||||
#: authentik/enterprise/stages/mtls/stage.py
|
||||
msgid "No user found for certificate."
|
||||
msgstr "未找到证书的用户。"
|
||||
|
||||
#: authentik/enterprise/stages/source/models.py
|
||||
msgid ""
|
||||
"Amount of time a user can take to return from the source to continue the "
|
||||
|
Binary file not shown.
@ -14,7 +14,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
|
||||
"POT-Creation-Date: 2025-05-20 00:10+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: deluxghost, 2025\n"
|
||||
"Language-Team: Chinese (China) (https://app.transifex.com/authentik/teams/119923/zh_CN/)\n"
|
||||
@ -101,6 +101,10 @@ msgstr "设置时,外部用户在验证身份后会被重定向到此应用程
|
||||
msgid "Web Certificate used by the authentik Core webserver."
|
||||
msgstr "authentik 核心 Web 服务器使用的 Web 证书。"
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Certificates used for client authentication."
|
||||
msgstr "用于客户端身份验证的证书"
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Brand"
|
||||
msgstr "品牌"
|
||||
@ -625,6 +629,33 @@ msgstr "端点设备"
|
||||
msgid "Verifying your browser..."
|
||||
msgstr "正在验证您的浏览器…"
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid ""
|
||||
"Configure certificate authorities to validate the certificate against. This "
|
||||
"option has a higher priority than the `client_certificate` option on "
|
||||
"`Brand`."
|
||||
msgstr "配置用于验证证书的证书机构。此选项的优先级比“品牌”中的“客户端证书”更高。"
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Mutual TLS Stage"
|
||||
msgstr "双向 TLS 阶段"
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Mutual TLS Stages"
|
||||
msgstr "双向 TLS 阶段"
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Permissions to pass Certificates for outposts."
|
||||
msgstr "为前哨传递证书的权限。"
|
||||
|
||||
#: authentik/enterprise/stages/mtls/stage.py
|
||||
msgid "Certificate required but no certificate was given."
|
||||
msgstr "需要证书但未提供。"
|
||||
|
||||
#: authentik/enterprise/stages/mtls/stage.py
|
||||
msgid "No user found for certificate."
|
||||
msgstr "未找到证书的用户。"
|
||||
|
||||
#: authentik/enterprise/stages/source/models.py
|
||||
msgid ""
|
||||
"Amount of time a user can take to return from the source to continue the "
|
||||
|
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@goauthentik/authentik",
|
||||
"version": "2025.4.0",
|
||||
"version": "2025.4.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@goauthentik/authentik",
|
||||
"version": "2025.4.0",
|
||||
"version": "2025.4.1",
|
||||
"devDependencies": {
|
||||
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
||||
"prettier": "^3.3.3",
|
||||
|
4134
packages/docusaurus-config/package-lock.json
generated
4134
packages/docusaurus-config/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@goauthentik/docusaurus-config",
|
||||
"version": "1.0.6",
|
||||
"version": "1.1.0",
|
||||
"description": "authentik's Docusaurus config",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
@ -20,23 +20,24 @@
|
||||
"prism-react-renderer": "^2.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/core": "^3.7.0",
|
||||
"@docusaurus/preset-classic": "^3.7.0",
|
||||
"@docusaurus/theme-common": "^3.7.0",
|
||||
"@docusaurus/theme-mermaid": "^3.7.0",
|
||||
"@docusaurus/theme-search-algolia": "^3.7.0",
|
||||
"@docusaurus/types": "^3.7.0",
|
||||
"@goauthentik/prettier-config": "^1.0.1",
|
||||
"@goauthentik/tsconfig": "^1.0.1",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"typescript": "^5.8.2"
|
||||
"@types/react": "^19.1.5",
|
||||
"@types/react-dom": "^19.1.5",
|
||||
"prettier": "^3.5.3",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"typescript": "^5.8.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@docusaurus/core": "^3.7.0",
|
||||
"@docusaurus/preset-classic": "^3.7.0",
|
||||
"@docusaurus/theme-common": "^3.7.0",
|
||||
"@docusaurus/theme-mermaid": "^3.7.0",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
"@docusaurus/theme-search-algolia": "^3.7.0",
|
||||
"@docusaurus/types": "^3.7.0",
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.11"
|
||||
@ -49,6 +50,11 @@
|
||||
"out/**/*"
|
||||
],
|
||||
"prettier": "@goauthentik/prettier-config",
|
||||
"peerDependenciesMeta": {
|
||||
"@docusaurus/theme-search-algolia": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
|
384
packages/eslint-config/package-lock.json
generated
384
packages/eslint-config/package-lock.json
generated
@ -1,37 +1,44 @@
|
||||
{
|
||||
"name": "@goauthentik/eslint-config",
|
||||
"version": "1.0.1",
|
||||
"version": "1.0.5",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@goauthentik/eslint-config",
|
||||
"version": "1.0.1",
|
||||
"version": "1.0.5",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"eslint": "^9.23.0",
|
||||
"eslint": "^9.27.0",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint-plugin-lit": "^1.15.0",
|
||||
"eslint-plugin-react": "^7.37.4",
|
||||
"eslint-plugin-lit": "^2.1.1",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-wc": "^2.1.1"
|
||||
"eslint-plugin-wc": "^3.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@goauthentik/prettier-config": "^1.0.1",
|
||||
"@goauthentik/tsconfig": "^1.0.1",
|
||||
"@types/eslint": "^9.6.1",
|
||||
"typescript": "^5.8.2",
|
||||
"typescript-eslint": "^8.29.0"
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.32.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.11"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"react": "^18.3.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5.8.2",
|
||||
"typescript-eslint": "^8.29.0"
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.32.1"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
@ -170,9 +177,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint-community/eslint-utils": {
|
||||
"version": "4.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz",
|
||||
"integrity": "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==",
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz",
|
||||
"integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"eslint-visitor-keys": "^3.4.3"
|
||||
@ -232,9 +239,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/core": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz",
|
||||
"integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==",
|
||||
"version": "0.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz",
|
||||
"integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.15"
|
||||
@ -267,12 +274,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "9.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.24.0.tgz",
|
||||
"integrity": "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==",
|
||||
"version": "9.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.27.0.tgz",
|
||||
"integrity": "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://eslint.org/donate"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/object-schema": {
|
||||
@ -285,34 +295,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/plugin-kit": {
|
||||
"version": "0.2.8",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz",
|
||||
"integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==",
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz",
|
||||
"integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@eslint/core": "^0.13.0",
|
||||
"@eslint/core": "^0.14.0",
|
||||
"levn": "^0.4.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/plugin-kit/node_modules/@eslint/core": {
|
||||
"version": "0.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz",
|
||||
"integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.15"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@goauthentik/prettier-config": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/prettier-config/-/prettier-config-1.0.1.tgz",
|
||||
"integrity": "sha512-6N0cCG3Uw3Nt+gTxRJ/FYFi/NfuL849CrQkrx307PvEBaG66OjxFFee4bhS/si4XvLdxFdog7oQsPwYmqZeZ+w==",
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/prettier-config/-/prettier-config-1.0.5.tgz",
|
||||
"integrity": "sha512-3W1uJvhzBPerDao53hSXhNzB7Ev8DbGYh+gVkuku1FaUZGBpiwD/6U3ah4sny8NoRiObGQ1geF4dhNLtlRbC/Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -322,13 +320,13 @@
|
||||
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-plugin-organize-imports": "^4.1.0",
|
||||
"prettier-plugin-packagejson": "^2.5.10"
|
||||
"prettier-plugin-packagejson": "^2.5.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@goauthentik/tsconfig": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/tsconfig/-/tsconfig-1.0.1.tgz",
|
||||
"integrity": "sha512-kxMDkgUHhAmQ2iIhUZJjrx/CgDb1AwvRoPtU4vrjAZu7x66+qczCjRTK+GzIGCeqB97GEpvCCjU8CThmozVFqA==",
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/tsconfig/-/tsconfig-1.0.4.tgz",
|
||||
"integrity": "sha512-BTGVpGh8SbCRHTULBf+2WTcw6OHJ8Ws9VtVfAMUUgcq8whbH/A7Q/n8WbkDaEeihzHUFkLk3JBenHKzEKAZWlw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -493,9 +491,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@pkgr/core": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.2.tgz",
|
||||
"integrity": "sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==",
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.4.tgz",
|
||||
"integrity": "sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
@ -503,7 +501,7 @@
|
||||
"node": "^12.20.0 || ^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/unts"
|
||||
"url": "https://opencollective.com/pkgr"
|
||||
}
|
||||
},
|
||||
"node_modules/@rtsao/scc": {
|
||||
@ -578,21 +576,21 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.29.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.29.1.tgz",
|
||||
"integrity": "sha512-ba0rr4Wfvg23vERs3eB+P3lfj2E+2g3lhWcCVukUuhtcdUx5lSIFZlGFEBHKr+3zizDa/TvZTptdNHVZWAkSBg==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz",
|
||||
"integrity": "sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.10.0",
|
||||
"@typescript-eslint/scope-manager": "8.29.1",
|
||||
"@typescript-eslint/type-utils": "8.29.1",
|
||||
"@typescript-eslint/utils": "8.29.1",
|
||||
"@typescript-eslint/visitor-keys": "8.29.1",
|
||||
"@typescript-eslint/scope-manager": "8.32.1",
|
||||
"@typescript-eslint/type-utils": "8.32.1",
|
||||
"@typescript-eslint/utils": "8.32.1",
|
||||
"@typescript-eslint/visitor-keys": "8.32.1",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^5.3.1",
|
||||
"ignore": "^7.0.0",
|
||||
"natural-compare": "^1.4.0",
|
||||
"ts-api-utils": "^2.0.1"
|
||||
"ts-api-utils": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -607,17 +605,27 @@
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz",
|
||||
"integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.29.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.29.1.tgz",
|
||||
"integrity": "sha512-zczrHVEqEaTwh12gWBIJWj8nx+ayDcCJs06yoNMY0kwjMWDM6+kppljY+BxWI06d2Ja+h4+WdufDcwMnnMEWmg==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.1.tgz",
|
||||
"integrity": "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.29.1",
|
||||
"@typescript-eslint/types": "8.29.1",
|
||||
"@typescript-eslint/typescript-estree": "8.29.1",
|
||||
"@typescript-eslint/visitor-keys": "8.29.1",
|
||||
"@typescript-eslint/scope-manager": "8.32.1",
|
||||
"@typescript-eslint/types": "8.32.1",
|
||||
"@typescript-eslint/typescript-estree": "8.32.1",
|
||||
"@typescript-eslint/visitor-keys": "8.32.1",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@ -633,14 +641,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.29.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.29.1.tgz",
|
||||
"integrity": "sha512-2nggXGX5F3YrsGN08pw4XpMLO1Rgtnn4AzTegC2MDesv6q3QaTU5yU7IbS1tf1IwCR0Hv/1EFygLn9ms6LIpDA==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.32.1.tgz",
|
||||
"integrity": "sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.29.1",
|
||||
"@typescript-eslint/visitor-keys": "8.29.1"
|
||||
"@typescript-eslint/types": "8.32.1",
|
||||
"@typescript-eslint/visitor-keys": "8.32.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -651,16 +659,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.29.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.29.1.tgz",
|
||||
"integrity": "sha512-DkDUSDwZVCYN71xA4wzySqqcZsHKic53A4BLqmrWFFpOpNSoxX233lwGu/2135ymTCR04PoKiEEEvN1gFYg4Tw==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.32.1.tgz",
|
||||
"integrity": "sha512-mv9YpQGA8iIsl5KyUPi+FGLm7+bA4fgXaeRcFKRDRwDMu4iwrSHeDPipwueNXhdIIZltwCJv+NkxftECbIZWfA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "8.29.1",
|
||||
"@typescript-eslint/utils": "8.29.1",
|
||||
"@typescript-eslint/typescript-estree": "8.32.1",
|
||||
"@typescript-eslint/utils": "8.32.1",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^2.0.1"
|
||||
"ts-api-utils": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -675,9 +683,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.29.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.29.1.tgz",
|
||||
"integrity": "sha512-VT7T1PuJF1hpYC3AGm2rCgJBjHL3nc+A/bhOp9sGMKfi5v0WufsX/sHCFBfNTx2F+zA6qBc/PD0/kLRLjdt8mQ==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.32.1.tgz",
|
||||
"integrity": "sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -689,20 +697,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.29.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.29.1.tgz",
|
||||
"integrity": "sha512-l1enRoSaUkQxOQnbi0KPUtqeZkSiFlqrx9/3ns2rEDhGKfTa+88RmXqedC1zmVTOWrLc2e6DEJrTA51C9iLH5g==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.1.tgz",
|
||||
"integrity": "sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.29.1",
|
||||
"@typescript-eslint/visitor-keys": "8.29.1",
|
||||
"@typescript-eslint/types": "8.32.1",
|
||||
"@typescript-eslint/visitor-keys": "8.32.1",
|
||||
"debug": "^4.3.4",
|
||||
"fast-glob": "^3.3.2",
|
||||
"is-glob": "^4.0.3",
|
||||
"minimatch": "^9.0.4",
|
||||
"semver": "^7.6.0",
|
||||
"ts-api-utils": "^2.0.1"
|
||||
"ts-api-utils": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -742,9 +750,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
|
||||
"version": "7.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
|
||||
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
@ -755,16 +763,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.29.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.29.1.tgz",
|
||||
"integrity": "sha512-QAkFEbytSaB8wnmB+DflhUPz6CLbFWE2SnSCrRMEa+KnXIzDYbpsn++1HGvnfAsUY44doDXmvRkO5shlM/3UfA==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.1.tgz",
|
||||
"integrity": "sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"@typescript-eslint/scope-manager": "8.29.1",
|
||||
"@typescript-eslint/types": "8.29.1",
|
||||
"@typescript-eslint/typescript-estree": "8.29.1"
|
||||
"@eslint-community/eslint-utils": "^4.7.0",
|
||||
"@typescript-eslint/scope-manager": "8.32.1",
|
||||
"@typescript-eslint/types": "8.32.1",
|
||||
"@typescript-eslint/typescript-estree": "8.32.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -779,13 +787,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.29.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.29.1.tgz",
|
||||
"integrity": "sha512-RGLh5CRaUEf02viP5c1Vh1cMGffQscyHe7HPAzGpfmfflFg1wUz2rYxd+OZqwpeypYvZ8UxSxuIpF++fmOzEcg==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.1.tgz",
|
||||
"integrity": "sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.29.1",
|
||||
"@typescript-eslint/types": "8.32.1",
|
||||
"eslint-visitor-keys": "^4.2.0"
|
||||
},
|
||||
"engines": {
|
||||
@ -1505,19 +1513,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "9.24.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.24.0.tgz",
|
||||
"integrity": "sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==",
|
||||
"version": "9.27.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz",
|
||||
"integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
"@eslint/config-array": "^0.20.0",
|
||||
"@eslint/config-helpers": "^0.2.0",
|
||||
"@eslint/core": "^0.12.0",
|
||||
"@eslint/config-helpers": "^0.2.1",
|
||||
"@eslint/core": "^0.14.0",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "9.24.0",
|
||||
"@eslint/plugin-kit": "^0.2.7",
|
||||
"@eslint/js": "9.27.0",
|
||||
"@eslint/plugin-kit": "^0.3.1",
|
||||
"@humanfs/node": "^0.16.6",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
"@humanwhocodes/retry": "^0.4.2",
|
||||
@ -1653,20 +1661,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-lit": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-lit/-/eslint-plugin-lit-1.15.0.tgz",
|
||||
"integrity": "sha512-Yhr2MYNz6Ln8megKcX503aVZQln8wsywCG49g0heiJ/Qr5UjkE4pGr4Usez2anNcc7NvlvHbQWMYwWcgH3XRKA==",
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-lit/-/eslint-plugin-lit-2.1.1.tgz",
|
||||
"integrity": "sha512-qmyAOnnTCdS+vDnNxtCoF0icSKIio4GUv6ZLnaCtTX6G/YezRa6Ag6tOQ+MfV5Elvtw9CIXeliRX4mIBSwrPIA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"parse5": "^6.0.1",
|
||||
"parse5-htmlparser2-tree-adapter": "^6.0.1",
|
||||
"requireindex": "^1.2.0"
|
||||
"parse5-htmlparser2-tree-adapter": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
"node": ">= 18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": ">= 5"
|
||||
"eslint": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-react": {
|
||||
@ -1731,13 +1738,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-wc": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-wc/-/eslint-plugin-wc-2.2.1.tgz",
|
||||
"integrity": "sha512-KstLqGmyQz088DvFlDYHg0sHih+w2QeulreCi1D1ftr357klO2zqHdG/bbnNMmuQdVFDuNkopNIyNhmG0XCT/g==",
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-wc/-/eslint-plugin-wc-3.0.1.tgz",
|
||||
"integrity": "sha512-0p1wkSlA2Ue3FA4qW+5LZ+15sy0p1nUyVl1eyBMLq4rtN1LtE9IdI49BXNWMz8N8bM/y7Ulx8SWGAni5f8XO5g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-valid-element-name": "^1.0.0",
|
||||
"js-levenshtein-esm": "^1.2.0"
|
||||
"js-levenshtein-esm": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": ">=8.40.0"
|
||||
@ -2038,20 +2045,6 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/get-stdin": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz",
|
||||
"integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/get-symbol-description": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz",
|
||||
@ -2070,9 +2063,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/git-hooks-list": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-3.2.0.tgz",
|
||||
"integrity": "sha512-ZHG9a1gEhUMX1TvGrLdyWb9kDopCBbTnI8z4JgRMYxsijWipgjSEYoPWqBuIB0DnRnvqlQSEeVmzpeuPm7NdFQ==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-4.1.1.tgz",
|
||||
"integrity": "sha512-cmP497iLq54AZnv4YRAEMnEyQ1eIn4tGKbmswqwmFV4GBnAqE8NLtWxxdXa++AalfgL5EBH4IxTPyquEuGY/jA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
@ -2696,9 +2689,9 @@
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/js-levenshtein-esm": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/js-levenshtein-esm/-/js-levenshtein-esm-1.2.0.tgz",
|
||||
"integrity": "sha512-fzreKVq1eD7eGcQr7MtRpQH94f8gIfhdrc7yeih38xh684TNMK9v5aAu2wxfIRMk/GpAJRrzcirMAPIaSDaByQ==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-levenshtein-esm/-/js-levenshtein-esm-2.0.0.tgz",
|
||||
"integrity": "sha512-1n4LEPOL4wRXY8rOQcuA7Iuaphe5xCMayvufCzlLAi+hRsnBRDbSS6XPuV58CBVJxj5D9ApFLyjQ7KzFToyHBw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
@ -3212,15 +3205,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier-plugin-packagejson": {
|
||||
"version": "2.5.10",
|
||||
"resolved": "https://registry.npmjs.org/prettier-plugin-packagejson/-/prettier-plugin-packagejson-2.5.10.tgz",
|
||||
"integrity": "sha512-LUxATI5YsImIVSaaLJlJ3aE6wTD+nvots18U3GuQMJpUyClChaZlQrqx3dBnbhF20OnKWZyx8EgyZypQtBDtgQ==",
|
||||
"version": "2.5.14",
|
||||
"resolved": "https://registry.npmjs.org/prettier-plugin-packagejson/-/prettier-plugin-packagejson-2.5.14.tgz",
|
||||
"integrity": "sha512-h+3tSpr2nVpp+YOK1MDIYtYhHVXr8/0V59UUbJpIJFaqi3w4fvUokJo6eV8W+vELrUXIZzJ+DKm5G7lYzrMcKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"sort-package-json": "2.15.1",
|
||||
"synckit": "0.9.2"
|
||||
"sort-package-json": "3.2.1",
|
||||
"synckit": "0.11.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prettier": ">= 1.16.0"
|
||||
@ -3273,18 +3266,30 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
||||
"version": "19.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
|
||||
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
},
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "19.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
|
||||
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.26.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
@ -3333,15 +3338,6 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/requireindex": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz",
|
||||
"integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.5"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.10",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
|
||||
@ -3458,6 +3454,14 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/scheduler": {
|
||||
"version": "0.26.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
|
||||
"integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
@ -3615,30 +3619,29 @@
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/sort-package-json": {
|
||||
"version": "2.15.1",
|
||||
"resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-2.15.1.tgz",
|
||||
"integrity": "sha512-9x9+o8krTT2saA9liI4BljNjwAbvUnWf11Wq+i/iZt8nl2UGYnf3TH5uBydE7VALmP7AGwlfszuEeL8BDyb0YA==",
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-3.2.1.tgz",
|
||||
"integrity": "sha512-rTfRdb20vuoAn7LDlEtCqOkYfl2X+Qze6cLbNOzcDpbmKEhJI30tTN44d5shbKJnXsvz24QQhlCm81Bag7EOKg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"detect-indent": "^7.0.1",
|
||||
"detect-newline": "^4.0.0",
|
||||
"get-stdin": "^9.0.0",
|
||||
"git-hooks-list": "^3.0.0",
|
||||
"detect-newline": "^4.0.1",
|
||||
"git-hooks-list": "^4.0.0",
|
||||
"is-plain-obj": "^4.1.0",
|
||||
"semver": "^7.6.0",
|
||||
"semver": "^7.7.1",
|
||||
"sort-object-keys": "^1.1.3",
|
||||
"tinyglobby": "^0.2.9"
|
||||
"tinyglobby": "^0.2.12"
|
||||
},
|
||||
"bin": {
|
||||
"sort-package-json": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/sort-package-json/node_modules/semver": {
|
||||
"version": "7.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
|
||||
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
@ -3788,32 +3791,31 @@
|
||||
}
|
||||
},
|
||||
"node_modules/synckit": {
|
||||
"version": "0.9.2",
|
||||
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz",
|
||||
"integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==",
|
||||
"version": "0.11.6",
|
||||
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.6.tgz",
|
||||
"integrity": "sha512-2pR2ubZSV64f/vqm9eLPz/KOvR9Dm+Co/5ChLgeHl0yEDRc6h5hXHoxEQH8Y5Ljycozd3p1k5TTSVdzYGkPvLw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@pkgr/core": "^0.1.0",
|
||||
"tslib": "^2.6.2"
|
||||
"@pkgr/core": "^0.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/unts"
|
||||
"url": "https://opencollective.com/synckit"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby": {
|
||||
"version": "0.2.12",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz",
|
||||
"integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==",
|
||||
"version": "0.2.13",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",
|
||||
"integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fdir": "^6.4.3",
|
||||
"fdir": "^6.4.4",
|
||||
"picomatch": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -3824,9 +3826,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby/node_modules/fdir": {
|
||||
"version": "6.4.3",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz",
|
||||
"integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==",
|
||||
"version": "6.4.4",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
|
||||
"integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
@ -3891,14 +3893,6 @@
|
||||
"strip-bom": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"dev": true,
|
||||
"license": "0BSD",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/type-check": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||
@ -4000,15 +3994,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-eslint": {
|
||||
"version": "8.29.1",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.29.1.tgz",
|
||||
"integrity": "sha512-f8cDkvndhbQMPcysk6CUSGBWV+g1utqdn71P5YKwMumVMOG/5k7cHq0KyG4O52nB0oKS4aN2Tp5+wB4APJGC+w==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.32.1.tgz",
|
||||
"integrity": "sha512-D7el+eaDHAmXvrZBy1zpzSNIRqnCOrkwTgZxTu3MUqRWk8k0q9m9Ho4+vPf7iHtgUfrK/o8IZaEApsxPlHTFCg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "8.29.1",
|
||||
"@typescript-eslint/parser": "8.29.1",
|
||||
"@typescript-eslint/utils": "8.29.1"
|
||||
"@typescript-eslint/eslint-plugin": "8.32.1",
|
||||
"@typescript-eslint/parser": "8.32.1",
|
||||
"@typescript-eslint/utils": "8.32.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@goauthentik/eslint-config",
|
||||
"version": "1.0.4",
|
||||
"version": "1.0.5",
|
||||
"description": "authentik's ESLint config",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
@ -27,26 +27,25 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"eslint": "^9.23.0",
|
||||
"eslint": "^9.27.0",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint-plugin-lit": "^1.15.0",
|
||||
"eslint-plugin-react": "^7.37.4",
|
||||
"eslint-plugin-lit": "^2.1.1",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-wc": "^2.1.1"
|
||||
"eslint-plugin-wc": "^3.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@goauthentik/prettier-config": "^1.0.1",
|
||||
"@goauthentik/tsconfig": "^1.0.1",
|
||||
"@types/eslint": "^9.6.1",
|
||||
"typescript": "^5.8.2",
|
||||
"typescript-eslint": "^8.29.0"
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.32.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5.8.2",
|
||||
"typescript-eslint": "^8.29.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"react": "^18.3.1"
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.32.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.11"
|
||||
@ -58,6 +57,14 @@
|
||||
"out/**/*"
|
||||
],
|
||||
"prettier": "@goauthentik/prettier-config",
|
||||
"peerDependenciesMeta": {
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
|
@ -31,8 +31,33 @@ export const AuthentikPrettierConfig = {
|
||||
trailingComma: "all",
|
||||
useTabs: false,
|
||||
vueIndentScriptAndStyle: false,
|
||||
plugins: ["prettier-plugin-packagejson", "@trivago/prettier-plugin-sort-imports"],
|
||||
importOrder: ["^(@?)lit(.*)$", "\\.css$", "^@goauthentik/api$", "^[./]"],
|
||||
plugins: [
|
||||
// ---
|
||||
"prettier-plugin-packagejson",
|
||||
"@trivago/prettier-plugin-sort-imports",
|
||||
],
|
||||
importOrder: [
|
||||
// ---
|
||||
|
||||
"^(@goauthentik/|#)common.+",
|
||||
"^(@goauthentik/|#)elements.+",
|
||||
"^(@goauthentik/|#)components.+",
|
||||
"^(@goauthentik/|#)user.+",
|
||||
"^(@goauthentik/|#)admin.+",
|
||||
"^(@goauthentik/|#)flow.+",
|
||||
"^(@goauthentik/|#)flow.+",
|
||||
|
||||
"^#.+",
|
||||
"^@goauthentik.+",
|
||||
|
||||
"<THIRD_PARTY_MODULES>",
|
||||
|
||||
"^(@?)lit(.*)$",
|
||||
"\\.css$",
|
||||
"^@goauthentik/api$",
|
||||
"^[./]",
|
||||
],
|
||||
importOrderSideEffects: false,
|
||||
importOrderSeparation: true,
|
||||
importOrderSortSpecifiers: true,
|
||||
importOrderParserPlugins: ["typescript", "jsx", "classProperties", "decorators-legacy"],
|
||||
|
104
packages/prettier-config/package-lock.json
generated
104
packages/prettier-config/package-lock.json
generated
@ -1,19 +1,19 @@
|
||||
{
|
||||
"name": "@goauthentik/prettier-config",
|
||||
"version": "1.0.1",
|
||||
"version": "2.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@goauthentik/prettier-config",
|
||||
"version": "1.0.1",
|
||||
"version": "2.0.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@goauthentik/tsconfig": "^1.0.1",
|
||||
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-plugin-organize-imports": "^4.1.0",
|
||||
"prettier-plugin-packagejson": "^2.5.10",
|
||||
"prettier-plugin-packagejson": "^2.5.14",
|
||||
"typescript": "^5.8.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -23,7 +23,7 @@
|
||||
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-plugin-organize-imports": "^4.1.0",
|
||||
"prettier-plugin-packagejson": "^2.5.10"
|
||||
"prettier-plugin-packagejson": "^2.5.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
@ -143,9 +143,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@goauthentik/tsconfig": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/tsconfig/-/tsconfig-1.0.1.tgz",
|
||||
"integrity": "sha512-kxMDkgUHhAmQ2iIhUZJjrx/CgDb1AwvRoPtU4vrjAZu7x66+qczCjRTK+GzIGCeqB97GEpvCCjU8CThmozVFqA==",
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/tsconfig/-/tsconfig-1.0.4.tgz",
|
||||
"integrity": "sha512-BTGVpGh8SbCRHTULBf+2WTcw6OHJ8Ws9VtVfAMUUgcq8whbH/A7Q/n8WbkDaEeihzHUFkLk3JBenHKzEKAZWlw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -206,16 +206,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@pkgr/core": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.2.tgz",
|
||||
"integrity": "sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==",
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.4.tgz",
|
||||
"integrity": "sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/unts"
|
||||
"url": "https://opencollective.com/pkgr"
|
||||
}
|
||||
},
|
||||
"node_modules/@trivago/prettier-plugin-sort-imports": {
|
||||
@ -295,9 +295,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fdir": {
|
||||
"version": "6.4.3",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz",
|
||||
"integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==",
|
||||
"version": "6.4.4",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
|
||||
"integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
@ -309,23 +309,10 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/get-stdin": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz",
|
||||
"integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/git-hooks-list": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-3.2.0.tgz",
|
||||
"integrity": "sha512-ZHG9a1gEhUMX1TvGrLdyWb9kDopCBbTnI8z4JgRMYxsijWipgjSEYoPWqBuIB0DnRnvqlQSEeVmzpeuPm7NdFQ==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-4.1.1.tgz",
|
||||
"integrity": "sha512-cmP497iLq54AZnv4YRAEMnEyQ1eIn4tGKbmswqwmFV4GBnAqE8NLtWxxdXa++AalfgL5EBH4IxTPyquEuGY/jA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
@ -450,14 +437,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier-plugin-packagejson": {
|
||||
"version": "2.5.10",
|
||||
"resolved": "https://registry.npmjs.org/prettier-plugin-packagejson/-/prettier-plugin-packagejson-2.5.10.tgz",
|
||||
"integrity": "sha512-LUxATI5YsImIVSaaLJlJ3aE6wTD+nvots18U3GuQMJpUyClChaZlQrqx3dBnbhF20OnKWZyx8EgyZypQtBDtgQ==",
|
||||
"version": "2.5.14",
|
||||
"resolved": "https://registry.npmjs.org/prettier-plugin-packagejson/-/prettier-plugin-packagejson-2.5.14.tgz",
|
||||
"integrity": "sha512-h+3tSpr2nVpp+YOK1MDIYtYhHVXr8/0V59UUbJpIJFaqi3w4fvUokJo6eV8W+vELrUXIZzJ+DKm5G7lYzrMcKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"sort-package-json": "2.15.1",
|
||||
"synckit": "0.9.2"
|
||||
"sort-package-json": "3.2.1",
|
||||
"synckit": "0.11.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prettier": ">= 1.16.0"
|
||||
@ -469,9 +456,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
|
||||
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
@ -489,50 +476,48 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/sort-package-json": {
|
||||
"version": "2.15.1",
|
||||
"resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-2.15.1.tgz",
|
||||
"integrity": "sha512-9x9+o8krTT2saA9liI4BljNjwAbvUnWf11Wq+i/iZt8nl2UGYnf3TH5uBydE7VALmP7AGwlfszuEeL8BDyb0YA==",
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-3.2.1.tgz",
|
||||
"integrity": "sha512-rTfRdb20vuoAn7LDlEtCqOkYfl2X+Qze6cLbNOzcDpbmKEhJI30tTN44d5shbKJnXsvz24QQhlCm81Bag7EOKg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"detect-indent": "^7.0.1",
|
||||
"detect-newline": "^4.0.0",
|
||||
"get-stdin": "^9.0.0",
|
||||
"git-hooks-list": "^3.0.0",
|
||||
"detect-newline": "^4.0.1",
|
||||
"git-hooks-list": "^4.0.0",
|
||||
"is-plain-obj": "^4.1.0",
|
||||
"semver": "^7.6.0",
|
||||
"semver": "^7.7.1",
|
||||
"sort-object-keys": "^1.1.3",
|
||||
"tinyglobby": "^0.2.9"
|
||||
"tinyglobby": "^0.2.12"
|
||||
},
|
||||
"bin": {
|
||||
"sort-package-json": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/synckit": {
|
||||
"version": "0.9.2",
|
||||
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz",
|
||||
"integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==",
|
||||
"version": "0.11.6",
|
||||
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.6.tgz",
|
||||
"integrity": "sha512-2pR2ubZSV64f/vqm9eLPz/KOvR9Dm+Co/5ChLgeHl0yEDRc6h5hXHoxEQH8Y5Ljycozd3p1k5TTSVdzYGkPvLw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@pkgr/core": "^0.1.0",
|
||||
"tslib": "^2.6.2"
|
||||
"@pkgr/core": "^0.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/unts"
|
||||
"url": "https://opencollective.com/synckit"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby": {
|
||||
"version": "0.2.12",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz",
|
||||
"integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==",
|
||||
"version": "0.2.13",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",
|
||||
"integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fdir": "^6.4.3",
|
||||
"fdir": "^6.4.4",
|
||||
"picomatch": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -542,13 +527,6 @@
|
||||
"url": "https://github.com/sponsors/SuperchupuDev"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"dev": true,
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
||||
|
@ -1,32 +1,26 @@
|
||||
{
|
||||
"name": "@goauthentik/prettier-config",
|
||||
"version": "1.0.4",
|
||||
"version": "2.0.0",
|
||||
"description": "authentik's Prettier config",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "tsc -p ."
|
||||
},
|
||||
"type": "module",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"import": "./index.js",
|
||||
"types": "./out/index.d.ts"
|
||||
}
|
||||
},
|
||||
"exports": "./index.js",
|
||||
"devDependencies": {
|
||||
"@goauthentik/tsconfig": "^1.0.1",
|
||||
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-plugin-organize-imports": "^4.1.0",
|
||||
"prettier-plugin-packagejson": "^2.5.10",
|
||||
"prettier-plugin-packagejson": "^2.5.14",
|
||||
"typescript": "^5.8.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-plugin-organize-imports": "^4.1.0",
|
||||
"prettier-plugin-packagejson": "^2.5.10"
|
||||
"prettier-plugin-packagejson": "^2.5.14"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.11"
|
||||
|
@ -1,7 +1,7 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# Stage 1: Build web
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/node:22 AS web-builder
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/node:24 AS web-builder
|
||||
|
||||
ENV NODE_ENV=production
|
||||
WORKDIR /static
|
||||
|
@ -8,8 +8,8 @@ dependencies = [
|
||||
"argon2-cffi==23.1.0",
|
||||
"celery==5.5.2",
|
||||
"channels==4.2.2",
|
||||
"channels-redis==4.2.1",
|
||||
"cryptography==44.0.3",
|
||||
"channels-postgres==1.1.2",
|
||||
"cryptography==45.0.3",
|
||||
"dacite==1.9.2",
|
||||
"deepmerge==2.0",
|
||||
"defusedxml==0.7.1",
|
||||
@ -17,13 +17,13 @@ dependencies = [
|
||||
"django-countries==7.6.1",
|
||||
"django-cte==1.3.3",
|
||||
"django-filter==25.1",
|
||||
"django-guardian<3.0.0",
|
||||
"django-guardian==3.0.0",
|
||||
"django-model-utils==5.0.0",
|
||||
"django-pglock==1.7.2",
|
||||
"django-prometheus==2.3.1",
|
||||
"django-redis==5.4.0",
|
||||
"django-storages[s3]==1.14.6",
|
||||
"django-tenants==3.7.0",
|
||||
"django-tenants==3.8.0",
|
||||
"djangorestframework==3.16.0",
|
||||
"djangorestframework-guardian==0.3.0",
|
||||
"docker==7.1.0",
|
||||
@ -31,11 +31,11 @@ dependencies = [
|
||||
"drf-spectacular==0.28.0",
|
||||
"dumb-init==1.2.5.post1",
|
||||
"duo-client==5.5.0",
|
||||
"fido2==1.2.0",
|
||||
"fido2==2.0.0",
|
||||
"flower==2.0.1",
|
||||
"geoip2==5.1.0",
|
||||
"geopy==2.4.1",
|
||||
"google-api-python-client==2.169.0",
|
||||
"google-api-python-client==2.170.0",
|
||||
"gssapi==1.9.0",
|
||||
"gunicorn==23.0.0",
|
||||
"jsonpatch==1.33",
|
||||
@ -48,7 +48,7 @@ dependencies = [
|
||||
"packaging==25.0",
|
||||
"paramiko==3.5.1",
|
||||
"psycopg[c,pool]==3.2.9",
|
||||
"pydantic==2.11.4",
|
||||
"pydantic==2.11.5",
|
||||
"pydantic-scim==0.0.8",
|
||||
"pyjwt==2.10.1",
|
||||
"pyrad==2.4",
|
||||
@ -56,7 +56,7 @@ dependencies = [
|
||||
"pyyaml==6.0.2",
|
||||
"requests-oauthlib==2.0.0",
|
||||
"scim2-filter-parser==0.7.0",
|
||||
"sentry-sdk==2.28.0",
|
||||
"sentry-sdk==2.29.1",
|
||||
"service-identity==24.2.0",
|
||||
"setproctitle==1.3.6",
|
||||
"structlog==25.3.0",
|
||||
@ -114,9 +114,8 @@ no-binary-package = [
|
||||
]
|
||||
|
||||
[tool.uv.sources]
|
||||
django-tenants = { git = "https://github.com/rissson/django-tenants.git", branch = "authentik-fixes" }
|
||||
opencontainers = { git = "https://github.com/BeryJu/oci-python", rev = "c791b19056769cd67957322806809ab70f5bead8" }
|
||||
djangorestframework = { git = "https://github.com/authentik-community/django-rest-framework", rev = "896722bab969fabc74a08b827da59409cf9f1a4e" }
|
||||
opencontainers = { git = "https://github.com/vsoch/oci-python", rev = "ceb4fcc090851717a3069d78e85ceb1e86c2740c" }
|
||||
djangorestframework = { git = "https://github.com/goauthentik/django-rest-framework", rev = "896722bab969fabc74a08b827da59409cf9f1a4e" }
|
||||
|
||||
[project.scripts]
|
||||
ak = "lifecycle.ak:main"
|
||||
|
474
schema.yml
474
schema.yml
@ -4460,6 +4460,15 @@ paths:
|
||||
name: branding_title
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: client_certificates
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
format: uuid
|
||||
explode: true
|
||||
style: form
|
||||
- in: query
|
||||
name: default
|
||||
schema:
|
||||
@ -24978,6 +24987,7 @@ paths:
|
||||
- authentik_stages_identification.identificationstage
|
||||
- authentik_stages_invitation.invitation
|
||||
- authentik_stages_invitation.invitationstage
|
||||
- authentik_stages_mtls.mutualtlsstage
|
||||
- authentik_stages_password.passwordstage
|
||||
- authentik_stages_prompt.prompt
|
||||
- authentik_stages_prompt.promptstage
|
||||
@ -25226,6 +25236,7 @@ paths:
|
||||
- authentik_stages_identification.identificationstage
|
||||
- authentik_stages_invitation.invitation
|
||||
- authentik_stages_invitation.invitationstage
|
||||
- authentik_stages_mtls.mutualtlsstage
|
||||
- authentik_stages_password.passwordstage
|
||||
- authentik_stages_prompt.prompt
|
||||
- authentik_stages_prompt.promptstage
|
||||
@ -37718,6 +37729,311 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/stages/mtls/:
|
||||
get:
|
||||
operationId: stages_mtls_list
|
||||
description: MutualTLSStage Viewset
|
||||
parameters:
|
||||
- in: query
|
||||
name: cert_attribute
|
||||
schema:
|
||||
type: string
|
||||
enum:
|
||||
- common_name
|
||||
- email
|
||||
- subject
|
||||
- in: query
|
||||
name: certificate_authorities
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
format: uuid
|
||||
explode: true
|
||||
style: form
|
||||
- in: query
|
||||
name: mode
|
||||
schema:
|
||||
type: string
|
||||
enum:
|
||||
- optional
|
||||
- required
|
||||
- in: query
|
||||
name: name
|
||||
schema:
|
||||
type: string
|
||||
- name: ordering
|
||||
required: false
|
||||
in: query
|
||||
description: Which field to use when ordering the results.
|
||||
schema:
|
||||
type: string
|
||||
- name: page
|
||||
required: false
|
||||
in: query
|
||||
description: A page number within the paginated result set.
|
||||
schema:
|
||||
type: integer
|
||||
- name: page_size
|
||||
required: false
|
||||
in: query
|
||||
description: Number of results to return per page.
|
||||
schema:
|
||||
type: integer
|
||||
- name: search
|
||||
required: false
|
||||
in: query
|
||||
description: A search term.
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: stage_uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
- in: query
|
||||
name: user_attribute
|
||||
schema:
|
||||
type: string
|
||||
enum:
|
||||
- email
|
||||
- username
|
||||
tags:
|
||||
- stages
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PaginatedMutualTLSStageList'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
post:
|
||||
operationId: stages_mtls_create
|
||||
description: MutualTLSStage Viewset
|
||||
tags:
|
||||
- stages
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/MutualTLSStageRequest'
|
||||
required: true
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'201':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/MutualTLSStage'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/stages/mtls/{stage_uuid}/:
|
||||
get:
|
||||
operationId: stages_mtls_retrieve
|
||||
description: MutualTLSStage Viewset
|
||||
parameters:
|
||||
- in: path
|
||||
name: stage_uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this Mutual TLS Stage.
|
||||
required: true
|
||||
tags:
|
||||
- stages
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/MutualTLSStage'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
put:
|
||||
operationId: stages_mtls_update
|
||||
description: MutualTLSStage Viewset
|
||||
parameters:
|
||||
- in: path
|
||||
name: stage_uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this Mutual TLS Stage.
|
||||
required: true
|
||||
tags:
|
||||
- stages
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/MutualTLSStageRequest'
|
||||
required: true
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/MutualTLSStage'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
patch:
|
||||
operationId: stages_mtls_partial_update
|
||||
description: MutualTLSStage Viewset
|
||||
parameters:
|
||||
- in: path
|
||||
name: stage_uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this Mutual TLS Stage.
|
||||
required: true
|
||||
tags:
|
||||
- stages
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PatchedMutualTLSStageRequest'
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/MutualTLSStage'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
delete:
|
||||
operationId: stages_mtls_destroy
|
||||
description: MutualTLSStage Viewset
|
||||
parameters:
|
||||
- in: path
|
||||
name: stage_uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this Mutual TLS Stage.
|
||||
required: true
|
||||
tags:
|
||||
- stages
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'204':
|
||||
description: No response body
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/stages/mtls/{stage_uuid}/used_by/:
|
||||
get:
|
||||
operationId: stages_mtls_used_by_list
|
||||
description: Get a list of all objects that use this object
|
||||
parameters:
|
||||
- in: path
|
||||
name: stage_uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this Mutual TLS Stage.
|
||||
required: true
|
||||
tags:
|
||||
- stages
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/UsedBy'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/stages/password/:
|
||||
get:
|
||||
operationId: stages_password_list
|
||||
@ -40946,6 +41262,7 @@ components:
|
||||
- authentik.enterprise.providers.microsoft_entra
|
||||
- authentik.enterprise.providers.ssf
|
||||
- authentik.enterprise.stages.authenticator_endpoint_gdtc
|
||||
- authentik.enterprise.stages.mtls
|
||||
- authentik.enterprise.stages.source
|
||||
- authentik.events
|
||||
type: string
|
||||
@ -42609,6 +42926,12 @@ components:
|
||||
format: uuid
|
||||
nullable: true
|
||||
description: Web Certificate used by the authentik Core webserver.
|
||||
client_certificates:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
format: uuid
|
||||
description: Certificates used for client authentication.
|
||||
attributes: {}
|
||||
required:
|
||||
- brand_uuid
|
||||
@ -42673,6 +42996,12 @@ components:
|
||||
format: uuid
|
||||
nullable: true
|
||||
description: Web Certificate used by the authentik Core webserver.
|
||||
client_certificates:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
format: uuid
|
||||
description: Certificates used for client authentication.
|
||||
attributes: {}
|
||||
required:
|
||||
- domain
|
||||
@ -42842,6 +43171,12 @@ components:
|
||||
- name
|
||||
- private_key
|
||||
- public_key
|
||||
CertAttributeEnum:
|
||||
enum:
|
||||
- subject
|
||||
- common_name
|
||||
- email
|
||||
type: string
|
||||
CertificateData:
|
||||
type: object
|
||||
description: Get CertificateKeyPair's data
|
||||
@ -48368,6 +48703,7 @@ components:
|
||||
- authentik_providers_microsoft_entra.microsoftentraprovidermapping
|
||||
- authentik_providers_ssf.ssfprovider
|
||||
- authentik_stages_authenticator_endpoint_gdtc.authenticatorendpointgdtcstage
|
||||
- authentik_stages_mtls.mutualtlsstage
|
||||
- authentik_stages_source.sourcestage
|
||||
- authentik_events.event
|
||||
- authentik_events.notificationtransport
|
||||
@ -48375,6 +48711,96 @@ components:
|
||||
- authentik_events.notificationrule
|
||||
- authentik_events.notificationwebhookmapping
|
||||
type: string
|
||||
MutualTLSStage:
|
||||
type: object
|
||||
description: MutualTLSStage Serializer
|
||||
properties:
|
||||
pk:
|
||||
type: string
|
||||
format: uuid
|
||||
readOnly: true
|
||||
title: Stage uuid
|
||||
name:
|
||||
type: string
|
||||
component:
|
||||
type: string
|
||||
description: Get object type so that we know how to edit the object
|
||||
readOnly: true
|
||||
verbose_name:
|
||||
type: string
|
||||
description: Return object's verbose_name
|
||||
readOnly: true
|
||||
verbose_name_plural:
|
||||
type: string
|
||||
description: Return object's plural verbose_name
|
||||
readOnly: true
|
||||
meta_model_name:
|
||||
type: string
|
||||
description: Return internal model name
|
||||
readOnly: true
|
||||
flow_set:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/FlowSet'
|
||||
mode:
|
||||
$ref: '#/components/schemas/MutualTLSStageModeEnum'
|
||||
certificate_authorities:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
format: uuid
|
||||
description: Configure certificate authorities to validate the certificate
|
||||
against. This option has a higher priority than the `client_certificate`
|
||||
option on `Brand`.
|
||||
cert_attribute:
|
||||
$ref: '#/components/schemas/CertAttributeEnum'
|
||||
user_attribute:
|
||||
$ref: '#/components/schemas/UserAttributeEnum'
|
||||
required:
|
||||
- cert_attribute
|
||||
- component
|
||||
- meta_model_name
|
||||
- mode
|
||||
- name
|
||||
- pk
|
||||
- user_attribute
|
||||
- verbose_name
|
||||
- verbose_name_plural
|
||||
MutualTLSStageModeEnum:
|
||||
enum:
|
||||
- optional
|
||||
- required
|
||||
type: string
|
||||
MutualTLSStageRequest:
|
||||
type: object
|
||||
description: MutualTLSStage Serializer
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
minLength: 1
|
||||
flow_set:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/FlowSetRequest'
|
||||
mode:
|
||||
$ref: '#/components/schemas/MutualTLSStageModeEnum'
|
||||
certificate_authorities:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
format: uuid
|
||||
description: Configure certificate authorities to validate the certificate
|
||||
against. This option has a higher priority than the `client_certificate`
|
||||
option on `Brand`.
|
||||
cert_attribute:
|
||||
$ref: '#/components/schemas/CertAttributeEnum'
|
||||
user_attribute:
|
||||
$ref: '#/components/schemas/UserAttributeEnum'
|
||||
required:
|
||||
- cert_attribute
|
||||
- mode
|
||||
- name
|
||||
- user_attribute
|
||||
NameIdPolicyEnum:
|
||||
enum:
|
||||
- urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
|
||||
@ -50220,6 +50646,18 @@ components:
|
||||
required:
|
||||
- pagination
|
||||
- results
|
||||
PaginatedMutualTLSStageList:
|
||||
type: object
|
||||
properties:
|
||||
pagination:
|
||||
$ref: '#/components/schemas/Pagination'
|
||||
results:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/MutualTLSStage'
|
||||
required:
|
||||
- pagination
|
||||
- results
|
||||
PaginatedNotificationList:
|
||||
type: object
|
||||
properties:
|
||||
@ -51896,6 +52334,12 @@ components:
|
||||
format: uuid
|
||||
nullable: true
|
||||
description: Web Certificate used by the authentik Core webserver.
|
||||
client_certificates:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
format: uuid
|
||||
description: Certificates used for client authentication.
|
||||
attributes: {}
|
||||
PatchedCaptchaStageRequest:
|
||||
type: object
|
||||
@ -53079,6 +53523,31 @@ components:
|
||||
type: boolean
|
||||
description: When enabled, provider will not modify or create objects in
|
||||
the remote system.
|
||||
PatchedMutualTLSStageRequest:
|
||||
type: object
|
||||
description: MutualTLSStage Serializer
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
minLength: 1
|
||||
flow_set:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/FlowSetRequest'
|
||||
mode:
|
||||
$ref: '#/components/schemas/MutualTLSStageModeEnum'
|
||||
certificate_authorities:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
format: uuid
|
||||
description: Configure certificate authorities to validate the certificate
|
||||
against. This option has a higher priority than the `client_certificate`
|
||||
option on `Brand`.
|
||||
cert_attribute:
|
||||
$ref: '#/components/schemas/CertAttributeEnum'
|
||||
user_attribute:
|
||||
$ref: '#/components/schemas/UserAttributeEnum'
|
||||
PatchedNotificationRequest:
|
||||
type: object
|
||||
description: Notification Serializer
|
||||
@ -59793,6 +60262,11 @@ components:
|
||||
- pk
|
||||
- uid
|
||||
- username
|
||||
UserAttributeEnum:
|
||||
enum:
|
||||
- username
|
||||
- email
|
||||
type: string
|
||||
UserConsent:
|
||||
type: object
|
||||
description: UserConsent Serializer
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
set -e -x -o pipefail
|
||||
hash="$(git rev-parse HEAD || openssl rand -base64 36 | sha256sum)"
|
||||
|
||||
|
@ -6,7 +6,7 @@ services:
|
||||
network_mode: host
|
||||
restart: always
|
||||
mailpit:
|
||||
image: docker.io/axllent/mailpit:v1.24.2
|
||||
image: docker.io/axllent/mailpit:v1.25.1
|
||||
ports:
|
||||
- 1025:1025
|
||||
- 8025:8025
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
from dataclasses import asdict
|
||||
from time import sleep
|
||||
from unittest.mock import patch
|
||||
|
||||
from guardian.shortcuts import assign_perm
|
||||
from ldap3 import ALL, ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES, SUBTREE, Connection, Server
|
||||
@ -15,10 +16,12 @@ from authentik.flows.models import Flow
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.outposts.apps import MANAGED_OUTPOST
|
||||
from authentik.outposts.models import Outpost, OutpostConfig, OutpostType
|
||||
from authentik.outposts.tests.test_ws import patched__get_ct_cached
|
||||
from authentik.providers.ldap.models import APIAccessMode, LDAPProvider
|
||||
from tests.e2e.utils import SeleniumTestCase, retry
|
||||
|
||||
|
||||
@patch("guardian.shortcuts._get_ct_cached", patched__get_ct_cached)
|
||||
class TestProviderLDAP(SeleniumTestCase):
|
||||
"""LDAP and Outpost e2e tests"""
|
||||
|
||||
|
@ -6,6 +6,7 @@ from json import loads
|
||||
from sys import platform
|
||||
from time import sleep
|
||||
from unittest.case import skip, skipUnless
|
||||
from unittest.mock import patch
|
||||
|
||||
from channels.testing import ChannelsLiveServerTestCase
|
||||
from jwt import decode
|
||||
@ -17,10 +18,12 @@ from authentik.flows.models import Flow
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.outposts.models import DockerServiceConnection, Outpost, OutpostConfig, OutpostType
|
||||
from authentik.outposts.tasks import outpost_connection_discovery
|
||||
from authentik.outposts.tests.test_ws import patched__get_ct_cached
|
||||
from authentik.providers.proxy.models import ProxyProvider
|
||||
from tests.e2e.utils import SeleniumTestCase, retry
|
||||
|
||||
|
||||
@patch("guardian.shortcuts._get_ct_cached", patched__get_ct_cached)
|
||||
class TestProviderProxy(SeleniumTestCase):
|
||||
"""Proxy and Outpost e2e tests"""
|
||||
|
||||
|
@ -4,6 +4,7 @@ from json import loads
|
||||
from pathlib import Path
|
||||
from time import sleep
|
||||
from unittest import skip
|
||||
from unittest.mock import patch
|
||||
|
||||
from selenium.webdriver.common.by import By
|
||||
|
||||
@ -12,10 +13,12 @@ from authentik.core.models import Application
|
||||
from authentik.flows.models import Flow
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.outposts.models import Outpost, OutpostType
|
||||
from authentik.outposts.tests.test_ws import patched__get_ct_cached
|
||||
from authentik.providers.proxy.models import ProxyMode, ProxyProvider
|
||||
from tests.e2e.utils import SeleniumTestCase, retry
|
||||
|
||||
|
||||
@patch("guardian.shortcuts._get_ct_cached", patched__get_ct_cached)
|
||||
class TestProviderProxyForward(SeleniumTestCase):
|
||||
"""Proxy and Outpost e2e tests"""
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
from dataclasses import asdict
|
||||
from time import sleep
|
||||
from unittest.mock import patch
|
||||
|
||||
from pyrad.client import Client
|
||||
from pyrad.dictionary import Dictionary
|
||||
@ -12,10 +13,12 @@ from authentik.core.models import Application, User
|
||||
from authentik.flows.models import Flow
|
||||
from authentik.lib.generators import generate_id, generate_key
|
||||
from authentik.outposts.models import Outpost, OutpostConfig, OutpostType
|
||||
from authentik.outposts.tests.test_ws import patched__get_ct_cached
|
||||
from authentik.providers.radius.models import RadiusProvider
|
||||
from tests.e2e.utils import SeleniumTestCase, retry
|
||||
|
||||
|
||||
@patch("guardian.shortcuts._get_ct_cached", patched__get_ct_cached)
|
||||
class TestProviderRadius(SeleniumTestCase):
|
||||
"""Radius Outpost e2e tests"""
|
||||
|
||||
|
129
uv.lock
generated
129
uv.lock
generated
@ -170,7 +170,7 @@ dependencies = [
|
||||
{ name = "argon2-cffi" },
|
||||
{ name = "celery" },
|
||||
{ name = "channels" },
|
||||
{ name = "channels-redis" },
|
||||
{ name = "channels-postgres" },
|
||||
{ name = "cryptography" },
|
||||
{ name = "dacite" },
|
||||
{ name = "deepmerge" },
|
||||
@ -268,8 +268,8 @@ requires-dist = [
|
||||
{ name = "argon2-cffi", specifier = "==23.1.0" },
|
||||
{ name = "celery", specifier = "==5.5.2" },
|
||||
{ name = "channels", specifier = "==4.2.2" },
|
||||
{ name = "channels-redis", specifier = "==4.2.1" },
|
||||
{ name = "cryptography", specifier = "==44.0.3" },
|
||||
{ name = "channels-postgres", specifier = "==1.1.2" },
|
||||
{ name = "cryptography", specifier = "==45.0.3" },
|
||||
{ name = "dacite", specifier = "==1.9.2" },
|
||||
{ name = "deepmerge", specifier = "==2.0" },
|
||||
{ name = "defusedxml", specifier = "==0.7.1" },
|
||||
@ -277,25 +277,25 @@ requires-dist = [
|
||||
{ name = "django-countries", specifier = "==7.6.1" },
|
||||
{ name = "django-cte", specifier = "==1.3.3" },
|
||||
{ name = "django-filter", specifier = "==25.1" },
|
||||
{ name = "django-guardian", specifier = "<3.0.0" },
|
||||
{ name = "django-guardian", specifier = "==3.0.0" },
|
||||
{ name = "django-model-utils", specifier = "==5.0.0" },
|
||||
{ name = "django-pglock", specifier = "==1.7.2" },
|
||||
{ name = "django-prometheus", specifier = "==2.3.1" },
|
||||
{ name = "django-redis", specifier = "==5.4.0" },
|
||||
{ name = "django-storages", extras = ["s3"], specifier = "==1.14.6" },
|
||||
{ name = "django-tenants", git = "https://github.com/rissson/django-tenants.git?branch=authentik-fixes" },
|
||||
{ name = "djangorestframework", git = "https://github.com/authentik-community/django-rest-framework?rev=896722bab969fabc74a08b827da59409cf9f1a4e" },
|
||||
{ name = "django-tenants", specifier = "==3.8.0" },
|
||||
{ name = "djangorestframework", git = "https://github.com/goauthentik/django-rest-framework?rev=896722bab969fabc74a08b827da59409cf9f1a4e" },
|
||||
{ name = "djangorestframework-guardian", specifier = "==0.3.0" },
|
||||
{ name = "docker", specifier = "==7.1.0" },
|
||||
{ name = "drf-orjson-renderer", specifier = "==1.7.3" },
|
||||
{ name = "drf-spectacular", specifier = "==0.28.0" },
|
||||
{ name = "dumb-init", specifier = "==1.2.5.post1" },
|
||||
{ name = "duo-client", specifier = "==5.5.0" },
|
||||
{ name = "fido2", specifier = "==1.2.0" },
|
||||
{ name = "fido2", specifier = "==2.0.0" },
|
||||
{ name = "flower", specifier = "==2.0.1" },
|
||||
{ name = "geoip2", specifier = "==5.1.0" },
|
||||
{ name = "geopy", specifier = "==2.4.1" },
|
||||
{ name = "google-api-python-client", specifier = "==2.169.0" },
|
||||
{ name = "google-api-python-client", specifier = "==2.170.0" },
|
||||
{ name = "gssapi", specifier = "==1.9.0" },
|
||||
{ name = "gunicorn", specifier = "==23.0.0" },
|
||||
{ name = "jsonpatch", specifier = "==1.33" },
|
||||
@ -304,11 +304,11 @@ requires-dist = [
|
||||
{ name = "ldap3", specifier = "==2.9.1" },
|
||||
{ name = "lxml", specifier = "==5.4.0" },
|
||||
{ name = "msgraph-sdk", specifier = "==1.30.0" },
|
||||
{ name = "opencontainers", git = "https://github.com/BeryJu/oci-python?rev=c791b19056769cd67957322806809ab70f5bead8" },
|
||||
{ name = "opencontainers", git = "https://github.com/vsoch/oci-python?rev=ceb4fcc090851717a3069d78e85ceb1e86c2740c" },
|
||||
{ name = "packaging", specifier = "==25.0" },
|
||||
{ name = "paramiko", specifier = "==3.5.1" },
|
||||
{ name = "psycopg", extras = ["c", "pool"], specifier = "==3.2.9" },
|
||||
{ name = "pydantic", specifier = "==2.11.4" },
|
||||
{ name = "pydantic", specifier = "==2.11.5" },
|
||||
{ name = "pydantic-scim", specifier = "==0.0.8" },
|
||||
{ name = "pyjwt", specifier = "==2.10.1" },
|
||||
{ name = "pyrad", specifier = "==2.4" },
|
||||
@ -316,7 +316,7 @@ requires-dist = [
|
||||
{ name = "pyyaml", specifier = "==6.0.2" },
|
||||
{ name = "requests-oauthlib", specifier = "==2.0.0" },
|
||||
{ name = "scim2-filter-parser", specifier = "==0.7.0" },
|
||||
{ name = "sentry-sdk", specifier = "==2.28.0" },
|
||||
{ name = "sentry-sdk", specifier = "==2.29.1" },
|
||||
{ name = "service-identity", specifier = "==24.2.0" },
|
||||
{ name = "setproctitle", specifier = "==1.3.6" },
|
||||
{ name = "structlog", specifier = "==25.3.0" },
|
||||
@ -712,18 +712,18 @@ daphne = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "channels-redis"
|
||||
version = "4.2.1"
|
||||
name = "channels-postgres"
|
||||
version = "1.1.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "asgiref" },
|
||||
{ name = "channels" },
|
||||
{ name = "msgpack" },
|
||||
{ name = "redis" },
|
||||
{ name = "psycopg", extra = ["pool"] },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c7/6d/c379c9feea4522cbdb4eba9b3d23a6270ba8cbd94e847b21834d898109d6/channels_redis-4.2.1.tar.gz", hash = "sha256:8375e81493e684792efe6e6eca60ef3d7782ef76c6664057d2e5c31e80d636dd", size = 31152, upload-time = "2024-11-15T12:58:49.836Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f5/21/c74be89ec2f93fa832f9c5b47f08c73e5e07279aab176a1f7ef4a240722d/channels_postgres-1.1.2.tar.gz", hash = "sha256:bfdf580ba65aab0d5e944d38ddd807d3521939153b786b53d6f932b6f3defd8e", size = 19696, upload-time = "2025-04-20T16:09:01.83Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/aa/981d08ae9627c3b9d8dd150f0fe644122a351abc1f47bcf53d2bfff80d91/channels_redis-4.2.1-py3-none-any.whl", hash = "sha256:2ca33105b3a04b5a327a9c47dd762b546f30b76a0cd3f3f593a23d91d346b6f4", size = 20487, upload-time = "2024-11-15T12:58:47.847Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/0b/1ec2c7357ebff7ff457f39d51ccb74be56ac796595dfc228658ba23974a1/channels_postgres-1.1.2-py3-none-any.whl", hash = "sha256:301e1980f23e325e289ac93265baf54ceae289c19f1893d1b55e40fe73149f67", size = 16176, upload-time = "2025-04-20T16:09:00.383Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -869,37 +869,37 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "cryptography"
|
||||
version = "44.0.3"
|
||||
version = "45.0.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/53/d6/1411ab4d6108ab167d06254c5be517681f1e331f90edf1379895bcb87020/cryptography-44.0.3.tar.gz", hash = "sha256:fe19d8bc5536a91a24a8133328880a41831b6c5df54599a8417b62fe015d3053", size = 711096, upload-time = "2025-05-02T19:36:04.667Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/13/1f/9fa001e74a1993a9cadd2333bb889e50c66327b8594ac538ab8a04f915b7/cryptography-45.0.3.tar.gz", hash = "sha256:ec21313dd335c51d7877baf2972569f40a4291b76a0ce51391523ae358d05899", size = 744738, upload-time = "2025-05-25T14:17:24.777Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/08/53/c776d80e9d26441bb3868457909b4e74dd9ccabd182e10b2b0ae7a07e265/cryptography-44.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:962bc30480a08d133e631e8dfd4783ab71cc9e33d5d7c1e192f0b7c06397bb88", size = 6670281, upload-time = "2025-05-02T19:34:50.665Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/06/af2cf8d56ef87c77319e9086601bef621bedf40f6f59069e1b6d1ec498c5/cryptography-44.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffc61e8f3bf5b60346d89cd3d37231019c17a081208dfbbd6e1605ba03fa137", size = 3959305, upload-time = "2025-05-02T19:34:53.042Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/01/80de3bec64627207d030f47bf3536889efee8913cd363e78ca9a09b13c8e/cryptography-44.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58968d331425a6f9eedcee087f77fd3c927c88f55368f43ff7e0a19891f2642c", size = 4171040, upload-time = "2025-05-02T19:34:54.675Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/48/bb16b7541d207a19d9ae8b541c70037a05e473ddc72ccb1386524d4f023c/cryptography-44.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e28d62e59a4dbd1d22e747f57d4f00c459af22181f0b2f787ea83f5a876d7c76", size = 3963411, upload-time = "2025-05-02T19:34:56.61Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/42/b2/7d31f2af5591d217d71d37d044ef5412945a8a8e98d5a2a8ae4fd9cd4489/cryptography-44.0.3-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af653022a0c25ef2e3ffb2c673a50e5a0d02fecc41608f4954176f1933b12359", size = 3689263, upload-time = "2025-05-02T19:34:58.591Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/25/50/c0dfb9d87ae88ccc01aad8eb93e23cfbcea6a6a106a9b63a7b14c1f93c75/cryptography-44.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:157f1f3b8d941c2bd8f3ffee0af9b049c9665c39d3da9db2dc338feca5e98a43", size = 4196198, upload-time = "2025-05-02T19:35:00.988Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/c9/55c6b8794a74da652690c898cb43906310a3e4e4f6ee0b5f8b3b3e70c441/cryptography-44.0.3-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:c6cd67722619e4d55fdb42ead64ed8843d64638e9c07f4011163e46bc512cf01", size = 3966502, upload-time = "2025-05-02T19:35:03.091Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/f7/7cb5488c682ca59a02a32ec5f975074084db4c983f849d47b7b67cc8697a/cryptography-44.0.3-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:b424563394c369a804ecbee9b06dfb34997f19d00b3518e39f83a5642618397d", size = 4196173, upload-time = "2025-05-02T19:35:05.018Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d2/0b/2f789a8403ae089b0b121f8f54f4a3e5228df756e2146efdf4a09a3d5083/cryptography-44.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c91fc8e8fd78af553f98bc7f2a1d8db977334e4eea302a4bfd75b9461c2d8904", size = 4087713, upload-time = "2025-05-02T19:35:07.187Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1d/aa/330c13655f1af398fc154089295cf259252f0ba5df93b4bc9d9c7d7f843e/cryptography-44.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:25cd194c39fa5a0aa4169125ee27d1172097857b27109a45fadc59653ec06f44", size = 4299064, upload-time = "2025-05-02T19:35:08.879Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/a8/8c540a421b44fd267a7d58a1fd5f072a552d72204a3f08194f98889de76d/cryptography-44.0.3-cp37-abi3-win32.whl", hash = "sha256:3be3f649d91cb182c3a6bd336de8b61a0a71965bd13d1a04a0e15b39c3d5809d", size = 2773887, upload-time = "2025-05-02T19:35:10.41Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b9/0d/c4b1657c39ead18d76bbd122da86bd95bdc4095413460d09544000a17d56/cryptography-44.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:3883076d5c4cc56dbef0b898a74eb6992fdac29a7b9013870b34efe4ddb39a0d", size = 3209737, upload-time = "2025-05-02T19:35:12.12Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/34/a3/ad08e0bcc34ad436013458d7528e83ac29910943cea42ad7dd4141a27bbb/cryptography-44.0.3-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:5639c2b16764c6f76eedf722dbad9a0914960d3489c0cc38694ddf9464f1bb2f", size = 6673501, upload-time = "2025-05-02T19:35:13.775Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/f0/7491d44bba8d28b464a5bc8cc709f25a51e3eac54c0a4444cf2473a57c37/cryptography-44.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3ffef566ac88f75967d7abd852ed5f182da252d23fac11b4766da3957766759", size = 3960307, upload-time = "2025-05-02T19:35:15.917Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f7/c8/e5c5d0e1364d3346a5747cdcd7ecbb23ca87e6dea4f942a44e88be349f06/cryptography-44.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:192ed30fac1728f7587c6f4613c29c584abdc565d7417c13904708db10206645", size = 4170876, upload-time = "2025-05-02T19:35:18.138Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/73/96/025cb26fc351d8c7d3a1c44e20cf9a01e9f7cf740353c9c7a17072e4b264/cryptography-44.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7d5fe7195c27c32a64955740b949070f21cba664604291c298518d2e255931d2", size = 3964127, upload-time = "2025-05-02T19:35:19.864Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/01/44/eb6522db7d9f84e8833ba3bf63313f8e257729cf3a8917379473fcfd6601/cryptography-44.0.3-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3f07943aa4d7dad689e3bb1638ddc4944cc5e0921e3c227486daae0e31a05e54", size = 3689164, upload-time = "2025-05-02T19:35:21.449Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/fb/d61a4defd0d6cee20b1b8a1ea8f5e25007e26aeb413ca53835f0cae2bcd1/cryptography-44.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cb90f60e03d563ca2445099edf605c16ed1d5b15182d21831f58460c48bffb93", size = 4198081, upload-time = "2025-05-02T19:35:23.187Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1b/50/457f6911d36432a8811c3ab8bd5a6090e8d18ce655c22820994913dd06ea/cryptography-44.0.3-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:ab0b005721cc0039e885ac3503825661bd9810b15d4f374e473f8c89b7d5460c", size = 3967716, upload-time = "2025-05-02T19:35:25.426Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/6e/dca39d553075980ccb631955c47b93d87d27f3596da8d48b1ae81463d915/cryptography-44.0.3-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:3bb0847e6363c037df8f6ede57d88eaf3410ca2267fb12275370a76f85786a6f", size = 4197398, upload-time = "2025-05-02T19:35:27.678Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/9d/d1f2fe681eabc682067c66a74addd46c887ebacf39038ba01f8860338d3d/cryptography-44.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b0cc66c74c797e1db750aaa842ad5b8b78e14805a9b5d1348dc603612d3e3ff5", size = 4087900, upload-time = "2025-05-02T19:35:29.312Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/f5/3599e48c5464580b73b236aafb20973b953cd2e7b44c7c2533de1d888446/cryptography-44.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6866df152b581f9429020320e5eb9794c8780e90f7ccb021940d7f50ee00ae0b", size = 4301067, upload-time = "2025-05-02T19:35:31.547Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/6c/d2c48c8137eb39d0c193274db5c04a75dab20d2f7c3f81a7dcc3a8897701/cryptography-44.0.3-cp39-abi3-win32.whl", hash = "sha256:c138abae3a12a94c75c10499f1cbae81294a6f983b3af066390adee73f433028", size = 2775467, upload-time = "2025-05-02T19:35:33.805Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/ad/51f212198681ea7b0deaaf8846ee10af99fba4e894f67b353524eab2bbe5/cryptography-44.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:5d186f32e52e66994dce4f766884bcb9c68b8da62d61d9d215bfe5fb56d21334", size = 3210375, upload-time = "2025-05-02T19:35:35.369Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/82/b2/2345dc595998caa6f68adf84e8f8b50d18e9fc4638d32b22ea8daedd4b7a/cryptography-45.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:7573d9eebaeceeb55285205dbbb8753ac1e962af3d9640791d12b36864065e71", size = 7056239, upload-time = "2025-05-25T14:16:12.22Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/3d/ac361649a0bfffc105e2298b720d8b862330a767dab27c06adc2ddbef96a/cryptography-45.0.3-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d377dde61c5d67eb4311eace661c3efda46c62113ff56bf05e2d679e02aebb5b", size = 4205541, upload-time = "2025-05-25T14:16:14.333Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/70/3e/c02a043750494d5c445f769e9c9f67e550d65060e0bfce52d91c1362693d/cryptography-45.0.3-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fae1e637f527750811588e4582988932c222f8251f7b7ea93739acb624e1487f", size = 4433275, upload-time = "2025-05-25T14:16:16.421Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/7a/9af0bfd48784e80eef3eb6fd6fde96fe706b4fc156751ce1b2b965dada70/cryptography-45.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ca932e11218bcc9ef812aa497cdf669484870ecbcf2d99b765d6c27a86000942", size = 4209173, upload-time = "2025-05-25T14:16:18.163Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/31/5f/d6f8753c8708912df52e67969e80ef70b8e8897306cd9eb8b98201f8c184/cryptography-45.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af3f92b1dc25621f5fad065288a44ac790c5798e986a34d393ab27d2b27fcff9", size = 3898150, upload-time = "2025-05-25T14:16:20.34Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/50/f256ab79c671fb066e47336706dc398c3b1e125f952e07d54ce82cf4011a/cryptography-45.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2f8f8f0b73b885ddd7f3d8c2b2234a7d3ba49002b0223f58cfde1bedd9563c56", size = 4466473, upload-time = "2025-05-25T14:16:22.605Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/e7/312428336bb2df0848d0768ab5a062e11a32d18139447a76dfc19ada8eed/cryptography-45.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9cc80ce69032ffa528b5e16d217fa4d8d4bb7d6ba8659c1b4d74a1b0f4235fca", size = 4211890, upload-time = "2025-05-25T14:16:24.738Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/53/8a130e22c1e432b3c14896ec5eb7ac01fb53c6737e1d705df7e0efb647c6/cryptography-45.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:c824c9281cb628015bfc3c59335163d4ca0540d49de4582d6c2637312907e4b1", size = 4466300, upload-time = "2025-05-25T14:16:26.768Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/75/6bb6579688ef805fd16a053005fce93944cdade465fc92ef32bbc5c40681/cryptography-45.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:5833bb4355cb377ebd880457663a972cd044e7f49585aee39245c0d592904578", size = 4332483, upload-time = "2025-05-25T14:16:28.316Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/11/2538f4e1ce05c6c4f81f43c1ef2bd6de7ae5e24ee284460ff6c77e42ca77/cryptography-45.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9bb5bf55dcb69f7067d80354d0a348368da907345a2c448b0babc4215ccd3497", size = 4573714, upload-time = "2025-05-25T14:16:30.474Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/bb/e86e9cf07f73a98d84a4084e8fd420b0e82330a901d9cac8149f994c3417/cryptography-45.0.3-cp311-abi3-win32.whl", hash = "sha256:3ad69eeb92a9de9421e1f6685e85a10fbcfb75c833b42cc9bc2ba9fb00da4710", size = 2934752, upload-time = "2025-05-25T14:16:32.204Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/75/063bc9ddc3d1c73e959054f1fc091b79572e716ef74d6caaa56e945b4af9/cryptography-45.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:97787952246a77d77934d41b62fb1b6f3581d83f71b44796a4158d93b8f5c490", size = 3412465, upload-time = "2025-05-25T14:16:33.888Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/9b/04ead6015229a9396890d7654ee35ef630860fb42dc9ff9ec27f72157952/cryptography-45.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:c92519d242703b675ccefd0f0562eb45e74d438e001f8ab52d628e885751fb06", size = 7031892, upload-time = "2025-05-25T14:16:36.214Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/c7/c7d05d0e133a09fc677b8a87953815c522697bdf025e5cac13ba419e7240/cryptography-45.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5edcb90da1843df85292ef3a313513766a78fbbb83f584a5a58fb001a5a9d57", size = 4196181, upload-time = "2025-05-25T14:16:37.934Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/7a/6ad3aa796b18a683657cef930a986fac0045417e2dc428fd336cfc45ba52/cryptography-45.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38deed72285c7ed699864f964a3f4cf11ab3fb38e8d39cfcd96710cd2b5bb716", size = 4423370, upload-time = "2025-05-25T14:16:39.502Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/58/ec1461bfcb393525f597ac6a10a63938d18775b7803324072974b41a926b/cryptography-45.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5555365a50efe1f486eed6ac7062c33b97ccef409f5970a0b6f205a7cfab59c8", size = 4197839, upload-time = "2025-05-25T14:16:41.322Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d4/3d/5185b117c32ad4f40846f579369a80e710d6146c2baa8ce09d01612750db/cryptography-45.0.3-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9e4253ed8f5948a3589b3caee7ad9a5bf218ffd16869c516535325fece163dcc", size = 3886324, upload-time = "2025-05-25T14:16:43.041Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/85/caba91a57d291a2ad46e74016d1f83ac294f08128b26e2a81e9b4f2d2555/cryptography-45.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cfd84777b4b6684955ce86156cfb5e08d75e80dc2585e10d69e47f014f0a5342", size = 4450447, upload-time = "2025-05-25T14:16:44.759Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/d1/164e3c9d559133a38279215c712b8ba38e77735d3412f37711b9f8f6f7e0/cryptography-45.0.3-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:a2b56de3417fd5f48773ad8e91abaa700b678dc7fe1e0c757e1ae340779acf7b", size = 4200576, upload-time = "2025-05-25T14:16:46.438Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/7a/e002d5ce624ed46dfc32abe1deff32190f3ac47ede911789ee936f5a4255/cryptography-45.0.3-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:57a6500d459e8035e813bd8b51b671977fb149a8c95ed814989da682314d0782", size = 4450308, upload-time = "2025-05-25T14:16:48.228Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/87/ad/3fbff9c28cf09b0a71e98af57d74f3662dea4a174b12acc493de00ea3f28/cryptography-45.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f22af3c78abfbc7cbcdf2c55d23c3e022e1a462ee2481011d518c7fb9c9f3d65", size = 4325125, upload-time = "2025-05-25T14:16:49.844Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/b4/51417d0cc01802304c1984d76e9592f15e4801abd44ef7ba657060520bf0/cryptography-45.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:232954730c362638544758a8160c4ee1b832dc011d2c41a306ad8f7cccc5bb0b", size = 4560038, upload-time = "2025-05-25T14:16:51.398Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/80/38/d572f6482d45789a7202fb87d052deb7a7b136bf17473ebff33536727a2c/cryptography-45.0.3-cp37-abi3-win32.whl", hash = "sha256:cb6ab89421bc90e0422aca911c69044c2912fc3debb19bb3c1bfe28ee3dff6ab", size = 2924070, upload-time = "2025-05-25T14:16:53.472Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/91/5a/61f39c0ff4443651cc64e626fa97ad3099249152039952be8f344d6b0c86/cryptography-45.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:d54ae41e6bd70ea23707843021c778f151ca258081586f0cfa31d936ae43d1b2", size = 3395005, upload-time = "2025-05-25T14:16:55.134Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1027,14 +1027,14 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "django-guardian"
|
||||
version = "2.4.0"
|
||||
version = "3.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "django" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6f/4c/d1f6923a0ad7f16c403a54c09e94acb76ac6c3765e02523fb09b2b03e1a8/django-guardian-2.4.0.tar.gz", hash = "sha256:c58a68ae76922d33e6bdc0e69af1892097838de56e93e78a8361090bcd9f89a0", size = 159008, upload-time = "2021-05-23T22:11:26.23Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/30/82/2c76cdf77eae3cb0c3df394686daf8f84bcd604c0da7a26fa19f5fe74ed4/django_guardian-3.0.0.tar.gz", hash = "sha256:0c79d55c4af2cfc14fbd19539846a1ebfed2a38198b7697e0f5177b7f654e1cd", size = 79895, upload-time = "2025-05-07T19:33:23.328Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/25/869df12e544b51f583254aadbba6c1a95e11d2d08edeb9e58dd715112db5/django_guardian-2.4.0-py3-none-any.whl", hash = "sha256:440ca61358427e575323648b25f8384739e54c38b3d655c81d75e0cd0d61b697", size = 106107, upload-time = "2021-05-23T22:11:22.75Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/81/a2f3d3245d1f4cf446d78863526fba0b1b140d60784095a5cc2d4e8ac709/django_guardian-3.0.0-py3-none-any.whl", hash = "sha256:f3ebe3cc7f486e267041b780c3429ad5db72c909df40c2f74adb1b059582a3cd", size = 112672, upload-time = "2025-05-07T19:33:21.719Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1118,16 +1118,17 @@ s3 = [
|
||||
|
||||
[[package]]
|
||||
name = "django-tenants"
|
||||
version = "3.7.0"
|
||||
source = { git = "https://github.com/rissson/django-tenants.git?branch=authentik-fixes#156e53a6f5902d74b73dd9d0192fffaa2587a740" }
|
||||
version = "3.8.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "django" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a8/7b/22e3bb79d48e5a4fdcacdcdc27bbc5c2523a2b7892b440bfe229f313d823/django_tenants-3.8.0.tar.gz", hash = "sha256:07d009d5d01be2d65c3f5ddbf323d58d1228838fc1a64fded15c8e5c6f41cf8f", size = 154307, upload-time = "2025-05-23T16:07:24.307Z" }
|
||||
|
||||
[[package]]
|
||||
name = "djangorestframework"
|
||||
version = "3.16.0"
|
||||
source = { git = "https://github.com/authentik-community/django-rest-framework?rev=896722bab969fabc74a08b827da59409cf9f1a4e#896722bab969fabc74a08b827da59409cf9f1a4e" }
|
||||
source = { git = "https://github.com/goauthentik/django-rest-framework?rev=896722bab969fabc74a08b827da59409cf9f1a4e#896722bab969fabc74a08b827da59409cf9f1a4e" }
|
||||
dependencies = [
|
||||
{ name = "django" },
|
||||
]
|
||||
@ -1262,14 +1263,14 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "fido2"
|
||||
version = "1.2.0"
|
||||
version = "2.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cryptography" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/eb/cc/4529123364d41f342145f2fd775307eaed817cd22810895dea10e15a4d06/fido2-1.2.0.tar.gz", hash = "sha256:e39f95920122d64283fda5e5581d95a206e704fa42846bfa4662f86aa0d3333b", size = 266369, upload-time = "2024-11-27T09:08:21.071Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8d/b9/6ec8d8ec5715efc6ae39e8694bd48d57c189906f0628558f56688d0447b2/fido2-2.0.0.tar.gz", hash = "sha256:3061cd05e73b3a0ef6afc3b803d57c826aa2d6a9732d16abd7277361f58e7964", size = 274942, upload-time = "2025-05-20T09:45:00.974Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/4c/48/e9b99d66f27d3416a619324568739fd6603e093b2f79138d6f47ccf727b6/fido2-1.2.0-py3-none-any.whl", hash = "sha256:f7c8ee62e359aa980a45773f9493965bb29ede1b237a9218169dbfe60c80e130", size = 219418, upload-time = "2024-11-27T09:08:18.932Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4c/7d/a1dba174d7ec4b6b8d6360eed0ac3a4a4e2aa45f234e903592d3184c6c3f/fido2-2.0.0-py3-none-any.whl", hash = "sha256:685f54a50a57e019c6156e2dd699802a603e3abf70bab334f26affdd4fb8d4f7", size = 224761, upload-time = "2025-05-20T09:44:59.029Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1396,7 +1397,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "google-api-python-client"
|
||||
version = "2.169.0"
|
||||
version = "2.170.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "google-api-core" },
|
||||
@ -1405,9 +1406,9 @@ dependencies = [
|
||||
{ name = "httplib2" },
|
||||
{ name = "uritemplate" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/4f/e6/787c24738fc7c99de9289abe60bd64591800ae1cdf60db7b87e0e6ef9cdd/google_api_python_client-2.169.0.tar.gz", hash = "sha256:0585bb97bd5f5bf3ed8d4bf624593e4c5a14d06c811d1952b07a1f94b4d12c51", size = 12811341, upload-time = "2025-04-29T15:46:05.603Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/db/86/1bd09aea2664a46bc65713cb7876381ec8949a4b1e71be97dfc359c79781/google_api_python_client-2.170.0.tar.gz", hash = "sha256:75f3a1856f11418ea3723214e0abc59d9b217fd7ed43dcf743aab7f06ab9e2b1", size = 12971933, upload-time = "2025-05-22T20:39:52.802Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2d/bd/6aa93c38756cc9fc63262e0dc3d3f1ff7241ce6f413a25ad6e4a9c98b473/google_api_python_client-2.169.0-py3-none-any.whl", hash = "sha256:dae3e882dc0e6f28e60cf09c1f13fedfd881db84f824dd418aa9e44def2fe00d", size = 13323742, upload-time = "2025-04-29T15:46:02.521Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/ab/928fb4551ce9c791de96b0681924d46de9a5b50931394fd19850383a08a1/google_api_python_client-2.170.0-py3-none-any.whl", hash = "sha256:7bf518a0527ad23322f070fa69f4f24053170d5c766821dc970ff0571ec22748", size = 13490660, upload-time = "2025-05-22T20:39:49.834Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2153,7 +2154,7 @@ wheels = [
|
||||
[[package]]
|
||||
name = "opencontainers"
|
||||
version = "0.0.14"
|
||||
source = { git = "https://github.com/BeryJu/oci-python?rev=c791b19056769cd67957322806809ab70f5bead8#c791b19056769cd67957322806809ab70f5bead8" }
|
||||
source = { git = "https://github.com/vsoch/oci-python?rev=ceb4fcc090851717a3069d78e85ceb1e86c2740c#ceb4fcc090851717a3069d78e85ceb1e86c2740c" }
|
||||
|
||||
[[package]]
|
||||
name = "opentelemetry-api"
|
||||
@ -2473,7 +2474,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.11.4"
|
||||
version = "2.11.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "annotated-types" },
|
||||
@ -2481,9 +2482,9 @@ dependencies = [
|
||||
{ name = "typing-extensions" },
|
||||
{ name = "typing-inspection" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/77/ab/5250d56ad03884ab5efd07f734203943c8a8ab40d551e208af81d0257bf2/pydantic-2.11.4.tar.gz", hash = "sha256:32738d19d63a226a52eed76645a98ee07c1f410ee41d93b4afbfa85ed8111c2d", size = 786540, upload-time = "2025-04-29T20:38:55.02Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f0/86/8ce9040065e8f924d642c58e4a344e33163a07f6b57f836d0d734e0ad3fb/pydantic-2.11.5.tar.gz", hash = "sha256:7f853db3d0ce78ce8bbb148c401c2cdd6431b3473c0cdff2755c7690952a7b7a", size = 787102, upload-time = "2025-05-22T21:18:08.761Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/12/46b65f3534d099349e38ef6ec98b1a5a81f42536d17e0ba382c28c67ba67/pydantic-2.11.4-py3-none-any.whl", hash = "sha256:d9615eaa9ac5a063471da949c8fc16376a84afb5024688b3ff885693506764eb", size = 443900, upload-time = "2025-04-29T20:38:52.724Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/69/831ed22b38ff9b4b64b66569f0e5b7b97cf3638346eb95a2147fdb49ad5f/pydantic-2.11.5-py3-none-any.whl", hash = "sha256:f9c26ba06f9747749ca1e5c94d6a85cb84254577553c8785576fd38fa64dc0f7", size = 444229, upload-time = "2025-05-22T21:18:06.329Z" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
@ -2576,14 +2577,14 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "pyopenssl"
|
||||
version = "25.0.0"
|
||||
version = "25.1.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cryptography" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/9f/26/e25b4a374b4639e0c235527bbe31c0524f26eda701d79456a7e1877f4cc5/pyopenssl-25.0.0.tar.gz", hash = "sha256:cd2cef799efa3936bb08e8ccb9433a575722b9dd986023f1cabc4ae64e9dac16", size = 179573, upload-time = "2025-01-12T17:22:48.897Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/04/8c/cd89ad05804f8e3c17dea8f178c3f40eeab5694c30e0c9f5bcd49f576fc3/pyopenssl-25.1.0.tar.gz", hash = "sha256:8d031884482e0c67ee92bf9a4d8cceb08d92aba7136432ffb0703c5280fc205b", size = 179937, upload-time = "2025-05-17T16:28:31.31Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/d7/eb76863d2060dcbe7c7e6cccfd95ac02ea0b9acc37745a0d99ff6457aefb/pyOpenSSL-25.0.0-py3-none-any.whl", hash = "sha256:424c247065e46e76a37411b9ab1782541c23bb658bf003772c3405fbaa128e90", size = 56453, upload-time = "2025-01-12T17:22:43.44Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/80/28/2659c02301b9500751f8d42f9a6632e1508aa5120de5e43042b8b30f8d5d/pyopenssl-25.1.0-py3-none-any.whl", hash = "sha256:2b11f239acc47ac2e5aca04fd7fa829800aeee22a2eb30d744572a157bd8a1ab", size = 56771, upload-time = "2025-05-17T16:28:29.197Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2940,15 +2941,15 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "sentry-sdk"
|
||||
version = "2.28.0"
|
||||
version = "2.29.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
{ name = "urllib3" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5e/bb/6a41b2e0e9121bed4d2ec68d50568ab95c49f4744156a9bbb789c866c66d/sentry_sdk-2.28.0.tar.gz", hash = "sha256:14d2b73bc93afaf2a9412490329099e6217761cbab13b6ee8bc0e82927e1504e", size = 325052, upload-time = "2025-05-12T07:53:12.785Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/22/67/d552a5f8e5a6a56b2feea6529e2d8ccd54349084c84176d5a1f7295044bc/sentry_sdk-2.29.1.tar.gz", hash = "sha256:8d4a0206b95fa5fe85e5e7517ed662e3888374bdc342c00e435e10e6d831aa6d", size = 325518, upload-time = "2025-05-19T14:27:38.512Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/4e/b1575833094c088dfdef63fbca794518860fcbc8002aadf51ebe8b6a387f/sentry_sdk-2.28.0-py2.py3-none-any.whl", hash = "sha256:51496e6cb3cb625b99c8e08907c67a9112360259b0ef08470e532c3ab184a232", size = 341693, upload-time = "2025-05-12T07:53:10.882Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/e5/da07b0bd832cefd52d16f2b9bbbe31624d57552602c06631686b93ccb1bd/sentry_sdk-2.29.1-py2.py3-none-any.whl", hash = "sha256:90862fe0616ded4572da6c9dadb363121a1ae49a49e21c418f0634e9d10b4c19", size = 341553, upload-time = "2025-05-19T14:27:36.882Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3,13 +3,10 @@
|
||||
* @import { StorybookConfig } from "@storybook/web-components-vite";
|
||||
* @import { InlineConfig, Plugin } from "vite";
|
||||
*/
|
||||
import { cwd } from "process";
|
||||
import postcssLit from "rollup-plugin-postcss-lit";
|
||||
import tsconfigPaths from "vite-tsconfig-paths";
|
||||
|
||||
const NODE_ENV = process.env.NODE_ENV || "development";
|
||||
|
||||
const CSSImportPattern = /import [\w\$]+ from .+\.(css)/g;
|
||||
const CSSImportPattern = /import [\w$]+ from .+\.(css)/g;
|
||||
const JavaScriptFilePattern = /\.m?(js|ts|tsx)$/;
|
||||
|
||||
/**
|
||||
@ -30,6 +27,11 @@ const inlineCSSPlugin = {
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @satisfies {InlineConfig}
|
||||
*/
|
||||
// const viteFinal = ;
|
||||
|
||||
/**
|
||||
* @satisfies {StorybookConfig}
|
||||
*/
|
||||
@ -48,22 +50,21 @@ const config = {
|
||||
docs: {
|
||||
autodocs: "tag",
|
||||
},
|
||||
viteFinal({ plugins = [], ...config }) {
|
||||
async viteFinal(config) {
|
||||
const [{ mergeConfig }, { createBundleDefinitions }] = await Promise.all([
|
||||
import("vite"),
|
||||
import("@goauthentik/web/bundler/utils/node"),
|
||||
]);
|
||||
|
||||
/**
|
||||
* @satisfies {InlineConfig}
|
||||
*/
|
||||
const mergedConfig = {
|
||||
...config,
|
||||
define: {
|
||||
"process.env.NODE_ENV": JSON.stringify(NODE_ENV),
|
||||
"process.env.CWD": JSON.stringify(cwd()),
|
||||
"process.env.AK_API_BASE_PATH": JSON.stringify(process.env.AK_API_BASE_PATH || ""),
|
||||
},
|
||||
plugins: [inlineCSSPlugin, ...plugins, postcssLit(), tsconfigPaths()],
|
||||
const overrides = {
|
||||
define: createBundleDefinitions(),
|
||||
plugins: [inlineCSSPlugin, postcssLit(), tsconfigPaths()],
|
||||
};
|
||||
|
||||
return mergedConfig;
|
||||
return mergeConfig(config, overrides);
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
96
web/bundler/mdx-plugin/node.js
Normal file
96
web/bundler/mdx-plugin/node.js
Normal file
@ -0,0 +1,96 @@
|
||||
/**
|
||||
* @file MDX plugin for ESBuild.
|
||||
*
|
||||
* @import {
|
||||
* OnLoadArgs,
|
||||
* OnLoadResult,
|
||||
* OnResolveArgs,
|
||||
* OnResolveResult,
|
||||
* Plugin,
|
||||
* PluginBuild
|
||||
* } from "esbuild"
|
||||
*/
|
||||
import { MonoRepoRoot } from "@goauthentik/core/paths/node";
|
||||
import * as fs from "node:fs/promises";
|
||||
import * as path from "node:path";
|
||||
|
||||
/**
|
||||
* @typedef {Omit<OnLoadArgs, 'pluginData'> & LoadDataFields} LoadData Data passed to `onload`.
|
||||
*
|
||||
* @typedef LoadDataFields Extra fields given in `data` to `onload`.
|
||||
* @property {PluginData | null | undefined} [pluginData] Plugin data.
|
||||
*
|
||||
* @typedef PluginData Extra data passed.
|
||||
* @property {Buffer | string | null | undefined} [contents] File contents.
|
||||
*/
|
||||
|
||||
const pluginName = "mdx-plugin";
|
||||
|
||||
/**
|
||||
* @typedef MDXPluginOptions
|
||||
*
|
||||
* @property {string} root Root directory.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Bundle MDX into JSON modules.
|
||||
*
|
||||
* @param {MDXPluginOptions} options
|
||||
* @returns {Plugin}
|
||||
*/
|
||||
export function mdxPlugin({ root }) {
|
||||
// TODO: Replace with `resolvePackage` after NPM Workspaces support is added.
|
||||
const docsPackageRoot = path.resolve(MonoRepoRoot, "website");
|
||||
|
||||
/**
|
||||
* @param {PluginBuild} build
|
||||
*/
|
||||
function setup(build) {
|
||||
/**
|
||||
* @param {OnResolveArgs} args
|
||||
* @returns {Promise<OnResolveResult>}
|
||||
*/
|
||||
async function resolveListener(args) {
|
||||
if (!args.path.startsWith("~")) return args;
|
||||
|
||||
return {
|
||||
path: path.resolve(docsPackageRoot, args.path.slice(1)),
|
||||
pluginName,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {LoadData} data
|
||||
* @returns {Promise<OnLoadResult>}
|
||||
*/
|
||||
async function loadListener(data) {
|
||||
const content = String(
|
||||
data.pluginData &&
|
||||
data.pluginData.contents !== null &&
|
||||
data.pluginData.contents !== undefined
|
||||
? data.pluginData.contents
|
||||
: await fs.readFile(data.path),
|
||||
);
|
||||
|
||||
const publicPath = path.resolve(
|
||||
"/",
|
||||
path.relative(path.join(root, "website"), data.path),
|
||||
);
|
||||
const publicDirectory = path.dirname(publicPath);
|
||||
|
||||
return {
|
||||
contents: JSON.stringify({ content, publicPath, publicDirectory }),
|
||||
loader: "file",
|
||||
pluginName,
|
||||
};
|
||||
}
|
||||
|
||||
build.onResolve({ filter: /\.mdx?$/ }, resolveListener);
|
||||
build.onLoad({ filter: /\.mdx?$/ }, loadListener);
|
||||
}
|
||||
|
||||
return {
|
||||
name: pluginName,
|
||||
setup,
|
||||
};
|
||||
}
|
29
web/bundler/utils/node.js
Normal file
29
web/bundler/utils/node.js
Normal file
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* @file Bundler utilities.
|
||||
*/
|
||||
import { NodeEnvironment, serializeEnvironmentVars } from "@goauthentik/core/environment/node";
|
||||
import { AuthentikVersion } from "@goauthentik/core/version/node";
|
||||
|
||||
/**
|
||||
* Creates a mapping of environment variables to their respective runtime constants.
|
||||
*/
|
||||
export function createBundleDefinitions() {
|
||||
const SerializedNodeEnvironment = /** @type {`"development"` | `"production"`} */ (
|
||||
JSON.stringify(NodeEnvironment)
|
||||
);
|
||||
|
||||
/**
|
||||
* @satisfies {Record<ESBuildImportEnvKey, string>}
|
||||
*/
|
||||
const envRecord = {
|
||||
AK_VERSION: AuthentikVersion,
|
||||
AK_API_BASE_PATH: process.env.AK_API_BASE_PATH ?? "",
|
||||
};
|
||||
|
||||
return {
|
||||
...serializeEnvironmentVars(envRecord),
|
||||
// We need to explicitly set this for NPM packages that use `process`
|
||||
// to determine their environment.
|
||||
"process.env.NODE_ENV": SerializedNodeEnvironment,
|
||||
};
|
||||
}
|
38
web/commands.mjs
Normal file
38
web/commands.mjs
Normal file
@ -0,0 +1,38 @@
|
||||
/// <reference types="@wdio/globals/types" />
|
||||
/// <reference types="./types/webdriver.js" />
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {WebdriverIO.Browser} browser
|
||||
*/
|
||||
export function addCommands(browser) {
|
||||
/**
|
||||
* @file Custom WDIO browser commands
|
||||
*/
|
||||
|
||||
browser.addCommand(
|
||||
"focus",
|
||||
/**
|
||||
* @this {HTMLElement}
|
||||
*/
|
||||
function () {
|
||||
this.focus();
|
||||
|
||||
return this;
|
||||
},
|
||||
/* attachToElement */ true,
|
||||
);
|
||||
|
||||
browser.addCommand(
|
||||
"blur",
|
||||
/**
|
||||
* @this {HTMLElement}
|
||||
*/
|
||||
function () {
|
||||
this.blur();
|
||||
|
||||
return this;
|
||||
},
|
||||
/* attachToElement */ true,
|
||||
);
|
||||
}
|
@ -1,90 +1,56 @@
|
||||
import eslint from "@eslint/js";
|
||||
import tsparser from "@typescript-eslint/parser";
|
||||
import litconf from "eslint-plugin-lit";
|
||||
import wcconf from "eslint-plugin-wc";
|
||||
import globals from "globals";
|
||||
import { createESLintPackageConfig } from "@goauthentik/eslint-config";
|
||||
import tseslint from "typescript-eslint";
|
||||
|
||||
export default [
|
||||
// You would not believe how much this change has frustrated users: ["if an ignores key is used
|
||||
// without any other keys in the configuration object, then the patterns act as global
|
||||
// ignores"](https://eslint.org/docs/latest/use/configure/ignore)
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* ESLint configuration for authentik's monorepo.
|
||||
*/
|
||||
const ESLintConfig = createESLintPackageConfig({
|
||||
ignorePatterns: [
|
||||
"**/dist/**",
|
||||
"**/out/**",
|
||||
"**/.wireit/**",
|
||||
"**/node_modules/",
|
||||
"**/.storybook/*",
|
||||
"coverage/",
|
||||
"src/locale-codes.ts",
|
||||
"storybook-static/",
|
||||
"src/locales/",
|
||||
],
|
||||
});
|
||||
|
||||
export default tseslint.config(
|
||||
...ESLintConfig,
|
||||
{
|
||||
ignores: [
|
||||
"dist/",
|
||||
// don't lint the cache
|
||||
".wireit/",
|
||||
// let packages have their own configurations
|
||||
"packages/",
|
||||
// don't ever lint node_modules
|
||||
"node_modules/",
|
||||
".storybook/*",
|
||||
// don't lint build output (make sure it's set to your correct build folder name)
|
||||
// don't lint nyc coverage output
|
||||
"coverage/",
|
||||
"src/locale-codes.ts",
|
||||
"storybook-static/",
|
||||
"src/locales/",
|
||||
],
|
||||
},
|
||||
eslint.configs.recommended,
|
||||
wcconf.configs["flat/recommended"],
|
||||
litconf.configs["flat/recommended"],
|
||||
...tseslint.configs.recommended,
|
||||
{
|
||||
languageOptions: {
|
||||
parser: tsparser,
|
||||
parserOptions: {
|
||||
ecmaVersion: 12,
|
||||
sourceType: "module",
|
||||
},
|
||||
},
|
||||
files: ["src/**"],
|
||||
rules: {
|
||||
"lit/attribute-names": "off",
|
||||
// "lit/attribute-names": "error",
|
||||
"lit/no-private-properties": "error",
|
||||
// "lit/prefer-nothing": "warn",
|
||||
"lit/no-template-bind": "error",
|
||||
"no-unused-vars": "off",
|
||||
"no-console": ["error", { allow: ["debug", "warn", "error"] }],
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
argsIgnorePattern: "^_",
|
||||
varsIgnorePattern: "^_",
|
||||
caughtErrorsIgnorePattern: "^_",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
languageOptions: {
|
||||
parser: tsparser,
|
||||
parserOptions: {
|
||||
ecmaVersion: 12,
|
||||
sourceType: "module",
|
||||
},
|
||||
globals: {
|
||||
...globals.nodeBuiltin,
|
||||
...globals.node,
|
||||
},
|
||||
},
|
||||
files: ["scripts/**/*.mjs", "*.ts", "*.mjs"],
|
||||
rules: {
|
||||
"no-unused-vars": "off",
|
||||
// We WANT our scripts to output to the console!
|
||||
"no-console": "off",
|
||||
},
|
||||
files: ["packages/**/*"],
|
||||
},
|
||||
{
|
||||
rules: {
|
||||
"no-void": "off",
|
||||
"no-implicit-coercion": "off",
|
||||
"prefer-template": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
argsIgnorePattern: "^_",
|
||||
varsIgnorePattern: "^_",
|
||||
caughtErrorsIgnorePattern: "^_",
|
||||
},
|
||||
],
|
||||
"@typescript-eslint/no-unused-vars": "off",
|
||||
"@typescript-eslint/no-use-before-define": "off",
|
||||
"array-callback-return": "off",
|
||||
"block-scoped-var": "off",
|
||||
"consistent-return": "off",
|
||||
"func-names": "off",
|
||||
"guard-for-in": "off",
|
||||
"no-bitwise": "off",
|
||||
"no-div-regex": "off",
|
||||
"no-else-return": "off",
|
||||
"no-empty-function": "off",
|
||||
"no-param-reassign": "off",
|
||||
"no-throw-literal": "off",
|
||||
// "no-var": "off",
|
||||
"prefer-arrow-callback": "off",
|
||||
"react/jsx-no-leaked-render": "off",
|
||||
"vars-on-top": "off",
|
||||
},
|
||||
},
|
||||
];
|
||||
);
|
||||
|
7254
web/package-lock.json
generated
7254
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user