Compare commits
1 Commits
consistent
...
remove-ses
Author | SHA1 | Date | |
---|---|---|---|
d8607ab324 |
14
.github/dependabot.yml
vendored
14
.github/dependabot.yml
vendored
@ -23,13 +23,7 @@ updates:
|
||||
- package-ecosystem: npm
|
||||
directories:
|
||||
- "/web"
|
||||
- "/web/packages/sfe"
|
||||
- "/web/packages/core"
|
||||
- "/web/packages/esbuild-plugin-live-reload"
|
||||
- "/packages/prettier-config"
|
||||
- "/packages/tsconfig"
|
||||
- "/packages/docusaurus-config"
|
||||
- "/packages/eslint-config"
|
||||
- "/web/sfe"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "04:00"
|
||||
@ -74,9 +68,6 @@ updates:
|
||||
wdio:
|
||||
patterns:
|
||||
- "@wdio/*"
|
||||
goauthentik:
|
||||
patterns:
|
||||
- "@goauthentik/*"
|
||||
- package-ecosystem: npm
|
||||
directory: "/website"
|
||||
schedule:
|
||||
@ -97,9 +88,6 @@ 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,7 +53,6 @@ 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,7 +37,6 @@ 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,7 +53,6 @@ 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:
|
||||
|
1
.github/workflows/packages-npm-publish.yml
vendored
1
.github/workflows/packages-npm-publish.yml
vendored
@ -7,7 +7,6 @@ on:
|
||||
- packages/eslint-config/**
|
||||
- packages/prettier-config/**
|
||||
- packages/tsconfig/**
|
||||
- packages/web/esbuild-plugin-live-reload/**
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
publish:
|
||||
|
@ -52,6 +52,3 @@ 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,7 +15,6 @@ 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:
|
||||
@ -26,13 +25,23 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
|
||||
run: |
|
||||
title=$(gh pr view ${{ github.event.pull_request.number }} --json "title" -q ".title")
|
||||
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)
|
||||
echo "title=${title}" >> "$GITHUB_OUTPUT"
|
||||
- name: Rename
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
|
||||
run: |
|
||||
gh pr edit ${{ github.event.pull_request.number }} -t "translate: ${{ steps.title.outputs.title }}" --add-label dependencies
|
||||
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 }}\"}"
|
||||
- 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:24 AS website-builder
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/node:22 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:24 AS web-builder
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/node:22 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.7 AS uv
|
||||
FROM ghcr.io/astral-sh/uv:0.7.4 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,7 +1,6 @@
|
||||
.PHONY: gen dev-reset all clean test web website
|
||||
|
||||
SHELL := /bin/bash
|
||||
.SHELLFLAGS += ${SHELLFLAGS} -e -o pipefail
|
||||
.SHELLFLAGS += ${SHELLFLAGS} -e
|
||||
PWD = $(shell pwd)
|
||||
UID = $(shell id -u)
|
||||
GID = $(shell id -g)
|
||||
@ -9,9 +8,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)
|
||||
@ -118,19 +117,14 @@ 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 ${PWD}/${GEN_API_TS}/
|
||||
rm -rf ${PWD}/web/node_modules/@goauthentik/api/
|
||||
rm -rf ./${GEN_API_TS}/
|
||||
rm -rf ./web/node_modules/@goauthentik/api/
|
||||
|
||||
gen-clean-go: ## Remove generated API client for 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
|
||||
rm -rf ./${GEN_API_GO}/
|
||||
|
||||
gen-clean-py: ## Remove generated API client for Python
|
||||
rm -rf ${PWD}/${GEN_API_PY}/
|
||||
rm -rf ./${GEN_API_PY}/
|
||||
|
||||
gen-clean: gen-clean-ts gen-clean-go gen-clean-py ## Remove generated API clients
|
||||
|
||||
@ -147,8 +141,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 ${PWD}/${GEN_API_TS} && npm i
|
||||
\cp -rf ${PWD}/${GEN_API_TS}/* web/node_modules/@goauthentik/api
|
||||
cd ./${GEN_API_TS} && npm i
|
||||
\cp -rf ./${GEN_API_TS}/* web/node_modules/@goauthentik/api
|
||||
|
||||
gen-client-py: gen-clean-py ## Build and install the authentik API for Python
|
||||
docker run \
|
||||
@ -162,17 +156,24 @@ 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 ${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
|
||||
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
|
||||
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
|
||||
@ -243,7 +244,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 ${PWD}/scripts/test_docker.sh
|
||||
BUILD=true ./scripts/test_docker.sh
|
||||
|
||||
#########################
|
||||
## CI
|
||||
|
@ -1,12 +1,9 @@
|
||||
"""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
|
||||
@ -14,17 +11,11 @@ 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, UserTypes
|
||||
from authentik.core.models import Token, TokenIntents, User
|
||||
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:
|
||||
@ -82,11 +73,6 @@ 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")
|
||||
|
||||
|
||||
@ -104,43 +90,6 @@ 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,7 +59,6 @@ class BrandSerializer(ModelSerializer):
|
||||
"flow_device_code",
|
||||
"default_application",
|
||||
"web_certificate",
|
||||
"client_certificates",
|
||||
"attributes",
|
||||
]
|
||||
extra_kwargs = {
|
||||
@ -121,7 +120,6 @@ class BrandViewSet(UsedByMixin, ModelViewSet):
|
||||
"domain",
|
||||
"branding_title",
|
||||
"web_certificate__name",
|
||||
"client_certificates__name",
|
||||
]
|
||||
filterset_fields = [
|
||||
"brand_uuid",
|
||||
@ -138,7 +136,6 @@ class BrandViewSet(UsedByMixin, ModelViewSet):
|
||||
"flow_user_settings",
|
||||
"flow_device_code",
|
||||
"web_certificate",
|
||||
"client_certificates",
|
||||
]
|
||||
ordering = ["domain"]
|
||||
|
||||
|
@ -1,37 +0,0 @@
|
||||
# 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,13 +73,6 @@ 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)
|
||||
|
||||
|
@ -1,126 +1,9 @@
|
||||
# Generated by Django 5.0.11 on 2025-01-27 12:58
|
||||
|
||||
import uuid
|
||||
import pickle # nosec
|
||||
from django.core import signing
|
||||
from django.contrib.auth import BACKEND_SESSION_KEY, HASH_SESSION_KEY, SESSION_KEY
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.contrib.sessions.backends.cache import KEY_PREFIX
|
||||
from django.utils.timezone import now, timedelta
|
||||
from authentik.lib.migrations import progress_bar
|
||||
from authentik.root.middleware import ClientIPMiddleware
|
||||
|
||||
|
||||
SESSION_CACHE_ALIAS = "default"
|
||||
|
||||
|
||||
class PickleSerializer:
|
||||
"""
|
||||
Simple wrapper around pickle to be used in signing.dumps()/loads() and
|
||||
cache backends.
|
||||
"""
|
||||
|
||||
def __init__(self, protocol=None):
|
||||
self.protocol = pickle.HIGHEST_PROTOCOL if protocol is None else protocol
|
||||
|
||||
def dumps(self, obj):
|
||||
"""Pickle data to be stored in redis"""
|
||||
return pickle.dumps(obj, self.protocol)
|
||||
|
||||
def loads(self, data):
|
||||
"""Unpickle data to be loaded from redis"""
|
||||
try:
|
||||
return pickle.loads(data) # nosec
|
||||
except Exception:
|
||||
return {}
|
||||
|
||||
|
||||
def _migrate_session(
|
||||
apps,
|
||||
db_alias,
|
||||
session_key,
|
||||
session_data,
|
||||
expires,
|
||||
):
|
||||
Session = apps.get_model("authentik_core", "Session")
|
||||
OldAuthenticatedSession = apps.get_model("authentik_core", "OldAuthenticatedSession")
|
||||
AuthenticatedSession = apps.get_model("authentik_core", "AuthenticatedSession")
|
||||
|
||||
old_auth_session = (
|
||||
OldAuthenticatedSession.objects.using(db_alias).filter(session_key=session_key).first()
|
||||
)
|
||||
|
||||
args = {
|
||||
"session_key": session_key,
|
||||
"expires": expires,
|
||||
"last_ip": ClientIPMiddleware.default_ip,
|
||||
"last_user_agent": "",
|
||||
"session_data": {},
|
||||
}
|
||||
for k, v in session_data.items():
|
||||
if k == "authentik/stages/user_login/last_ip":
|
||||
args["last_ip"] = v
|
||||
elif k in ["last_user_agent", "last_used"]:
|
||||
args[k] = v
|
||||
elif args in [SESSION_KEY, BACKEND_SESSION_KEY, HASH_SESSION_KEY]:
|
||||
pass
|
||||
else:
|
||||
args["session_data"][k] = v
|
||||
if old_auth_session:
|
||||
args["last_user_agent"] = old_auth_session.last_user_agent
|
||||
args["last_used"] = old_auth_session.last_used
|
||||
|
||||
args["session_data"] = pickle.dumps(args["session_data"])
|
||||
session = Session.objects.using(db_alias).create(**args)
|
||||
|
||||
if old_auth_session:
|
||||
AuthenticatedSession.objects.using(db_alias).create(
|
||||
session=session,
|
||||
user=old_auth_session.user,
|
||||
)
|
||||
|
||||
|
||||
def migrate_redis_sessions(apps, schema_editor):
|
||||
from django.core.cache import caches
|
||||
|
||||
db_alias = schema_editor.connection.alias
|
||||
cache = caches[SESSION_CACHE_ALIAS]
|
||||
|
||||
# Not a redis cache, skipping
|
||||
if not hasattr(cache, "keys"):
|
||||
return
|
||||
|
||||
print("\nMigrating Redis sessions to database, this might take a couple of minutes...")
|
||||
for key, session_data in progress_bar(cache.get_many(cache.keys(f"{KEY_PREFIX}*")).items()):
|
||||
_migrate_session(
|
||||
apps=apps,
|
||||
db_alias=db_alias,
|
||||
session_key=key.removeprefix(KEY_PREFIX),
|
||||
session_data=session_data,
|
||||
expires=now() + timedelta(seconds=cache.ttl(key)),
|
||||
)
|
||||
|
||||
|
||||
def migrate_database_sessions(apps, schema_editor):
|
||||
DjangoSession = apps.get_model("sessions", "Session")
|
||||
db_alias = schema_editor.connection.alias
|
||||
|
||||
print("\nMigration database sessions, this might take a couple of minutes...")
|
||||
for django_session in progress_bar(DjangoSession.objects.using(db_alias).all()):
|
||||
session_data = signing.loads(
|
||||
django_session.session_data,
|
||||
salt="django.contrib.sessions.SessionStore",
|
||||
serializer=PickleSerializer,
|
||||
)
|
||||
_migrate_session(
|
||||
apps=apps,
|
||||
db_alias=db_alias,
|
||||
session_key=django_session.session_key,
|
||||
session_data=session_data,
|
||||
expires=django_session.expire_date,
|
||||
)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
@ -230,12 +113,4 @@ class Migration(migrations.Migration):
|
||||
"verbose_name_plural": "Authenticated Sessions",
|
||||
},
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=migrate_redis_sessions,
|
||||
reverse_code=migrations.RunPython.noop,
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=migrate_database_sessions,
|
||||
reverse_code=migrations.RunPython.noop,
|
||||
),
|
||||
]
|
||||
|
@ -30,7 +30,6 @@ 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
|
||||
@ -273,12 +272,11 @@ 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()
|
||||
if request.user.type != UserTypes.INTERNAL_SERVICE_ACCOUNT:
|
||||
Event.new( # noqa # nosec
|
||||
EventAction.SECRET_VIEW,
|
||||
secret=certificate,
|
||||
type="certificate",
|
||||
).from_http(request)
|
||||
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(
|
||||
@ -304,12 +302,11 @@ 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()
|
||||
if request.user.type != UserTypes.INTERNAL_SERVICE_ACCOUNT:
|
||||
Event.new( # noqa # nosec
|
||||
EventAction.SECRET_VIEW,
|
||||
secret=certificate,
|
||||
type="private_key",
|
||||
).from_http(request)
|
||||
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_attr = "googleworkspaceprovidergroup_set"
|
||||
connection_type_query = "group"
|
||||
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_attr = "googleworkspaceprovideruser_set"
|
||||
connection_type_query = "user"
|
||||
can_discover = True
|
||||
|
||||
def __init__(self, provider: GoogleWorkspaceProvider) -> None:
|
||||
|
@ -132,11 +132,7 @@ class GoogleWorkspaceProvider(OutgoingSyncProvider, BackchannelProvider):
|
||||
if type == User:
|
||||
# Get queryset of all users with consistent ordering
|
||||
# according to the provider's settings
|
||||
base = (
|
||||
User.objects.prefetch_related("googleworkspaceprovideruser_set")
|
||||
.all()
|
||||
.exclude_anonymous()
|
||||
)
|
||||
base = User.objects.all().exclude_anonymous()
|
||||
if self.exclude_users_service_account:
|
||||
base = base.exclude(type=UserTypes.SERVICE_ACCOUNT).exclude(
|
||||
type=UserTypes.INTERNAL_SERVICE_ACCOUNT
|
||||
@ -146,11 +142,7 @@ class GoogleWorkspaceProvider(OutgoingSyncProvider, BackchannelProvider):
|
||||
return base.order_by("pk")
|
||||
if type == Group:
|
||||
# Get queryset of all groups with consistent ordering
|
||||
return (
|
||||
Group.objects.prefetch_related("googleworkspaceprovidergroup_set")
|
||||
.all()
|
||||
.order_by("pk")
|
||||
)
|
||||
return Group.objects.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_attr = "microsoftentraprovidergroup_set"
|
||||
connection_type_query = "group"
|
||||
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_attr = "microsoftentraprovideruser_set"
|
||||
connection_type_query = "user"
|
||||
can_discover = True
|
||||
|
||||
def __init__(self, provider: MicrosoftEntraProvider) -> None:
|
||||
|
@ -121,11 +121,7 @@ class MicrosoftEntraProvider(OutgoingSyncProvider, BackchannelProvider):
|
||||
if type == User:
|
||||
# Get queryset of all users with consistent ordering
|
||||
# according to the provider's settings
|
||||
base = (
|
||||
User.objects.prefetch_related("microsoftentraprovideruser_set")
|
||||
.all()
|
||||
.exclude_anonymous()
|
||||
)
|
||||
base = User.objects.all().exclude_anonymous()
|
||||
if self.exclude_users_service_account:
|
||||
base = base.exclude(type=UserTypes.SERVICE_ACCOUNT).exclude(
|
||||
type=UserTypes.INTERNAL_SERVICE_ACCOUNT
|
||||
@ -135,11 +131,7 @@ class MicrosoftEntraProvider(OutgoingSyncProvider, BackchannelProvider):
|
||||
return base.order_by("pk")
|
||||
if type == Group:
|
||||
# Get queryset of all groups with consistent ordering
|
||||
return (
|
||||
Group.objects.prefetch_related("microsoftentraprovidergroup_set")
|
||||
.all()
|
||||
.order_by("pk")
|
||||
)
|
||||
return Group.objects.all().order_by("pk")
|
||||
raise ValueError(f"Invalid type {type}")
|
||||
|
||||
def microsoft_credentials(self):
|
||||
|
@ -19,7 +19,6 @@ 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",
|
||||
]
|
||||
|
||||
|
@ -1,31 +0,0 @@
|
||||
"""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"]
|
@ -1,12 +0,0 @@
|
||||
"""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
|
@ -1,68 +0,0 @@
|
||||
# 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",),
|
||||
),
|
||||
]
|
@ -1,71 +0,0 @@
|
||||
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.")),
|
||||
]
|
@ -1,230 +0,0 @@
|
||||
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"),
|
||||
}
|
||||
)
|
@ -1,31 +0,0 @@
|
||||
-----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-----
|
@ -1,31 +0,0 @@
|
||||
-----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-----
|
@ -1,228 +0,0 @@
|
||||
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",
|
||||
},
|
||||
)
|
@ -1,5 +0,0 @@
|
||||
"""API URLs"""
|
||||
|
||||
from authentik.enterprise.stages.mtls.api import MutualTLSStageViewSet
|
||||
|
||||
api_urlpatterns = [("stages/mtls", MutualTLSStageViewSet)]
|
@ -1,10 +1,7 @@
|
||||
"""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
|
||||
@ -12,8 +9,6 @@ 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):
|
||||
@ -49,12 +44,3 @@ 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
|
||||
|
@ -23,6 +23,7 @@ if TYPE_CHECKING:
|
||||
|
||||
|
||||
class Direction(StrEnum):
|
||||
|
||||
add = "add"
|
||||
remove = "remove"
|
||||
|
||||
@ -36,16 +37,13 @@ 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_attr: str
|
||||
connection_type_query: str
|
||||
mapper: PropertyMappingManager
|
||||
|
||||
can_discover = False
|
||||
@ -65,7 +63,9 @@ 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 = getattr(obj, self.connection_attr).filter(provider=self.provider).first()
|
||||
connection = self.connection_type.objects.filter(
|
||||
provider=self.provider, **{self.connection_type_query: obj}
|
||||
).first()
|
||||
try:
|
||||
if not connection:
|
||||
connection = self.create(obj)
|
||||
|
@ -1,11 +1,9 @@
|
||||
"""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__
|
||||
@ -16,12 +14,6 @@ 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"""
|
||||
|
||||
@ -46,7 +38,6 @@ class TestOutpostWS(TransactionTestCase):
|
||||
)
|
||||
connected, _ = await communicator.connect()
|
||||
self.assertFalse(connected)
|
||||
await communicator.disconnect()
|
||||
|
||||
async def test_auth_valid(self):
|
||||
"""Test auth with token"""
|
||||
@ -57,7 +48,6 @@ class TestOutpostWS(TransactionTestCase):
|
||||
)
|
||||
connected, _ = await communicator.connect()
|
||||
self.assertTrue(connected)
|
||||
await communicator.disconnect()
|
||||
|
||||
async def test_send(self):
|
||||
"""Test sending of Hello"""
|
||||
|
@ -7,8 +7,10 @@ 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,4 +50,3 @@ AMR_PASSWORD = "pwd" # nosec
|
||||
AMR_MFA = "mfa"
|
||||
AMR_OTP = "otp"
|
||||
AMR_WEBAUTHN = "user"
|
||||
AMR_SMART_CARD = "sc"
|
||||
|
@ -16,7 +16,6 @@ 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
|
||||
@ -140,10 +139,9 @@ 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:
|
||||
amr.append(AMR_MFA)
|
||||
if len(amr) > 0:
|
||||
amr.append(AMR_MFA)
|
||||
if amr:
|
||||
id_token.amr = amr
|
||||
|
||||
|
@ -47,8 +47,6 @@ 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_attr = "scimprovidergroup_set"
|
||||
connection_type_query = "group"
|
||||
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_attr = "scimprovideruser_set"
|
||||
connection_type_query = "user"
|
||||
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.prefetch_related("scimprovideruser_set").all().exclude_anonymous()
|
||||
base = User.objects.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.prefetch_related("scimprovidergroup_set").all().order_by("pk")
|
||||
return Group.objects.all().order_by("pk")
|
||||
raise ValueError(f"Invalid type {type}")
|
||||
|
||||
@property
|
||||
|
@ -132,7 +132,7 @@ TENANT_CREATION_FAKES_MIGRATIONS = True
|
||||
TENANT_BASE_SCHEMA = "template"
|
||||
PUBLIC_SCHEMA_NAME = CONFIG.get("postgresql.default_schema")
|
||||
|
||||
GUARDIAN_MONKEY_PATCH_USER = False
|
||||
GUARDIAN_MONKEY_PATCH = False
|
||||
|
||||
SPECTACULAR_SETTINGS = {
|
||||
"TITLE": "authentik",
|
||||
|
@ -97,8 +97,7 @@ class GroupsView(SCIMObjectView):
|
||||
self.logger.warning("Invalid group member", exc=exc)
|
||||
continue
|
||||
query |= Q(uuid=member.value)
|
||||
if query:
|
||||
group.users.set(User.objects.filter(query))
|
||||
group.users.set(User.objects.filter(query))
|
||||
if not connection:
|
||||
connection, _ = SCIMSourceGroup.objects.get_or_create(
|
||||
source=self.source,
|
||||
|
@ -3921,46 +3921,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"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": [
|
||||
@ -4907,7 +4867,6 @@
|
||||
"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"
|
||||
],
|
||||
@ -5018,7 +4977,6 @@
|
||||
"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",
|
||||
@ -7519,11 +7477,6 @@
|
||||
"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",
|
||||
@ -13469,16 +13422,6 @@
|
||||
"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,
|
||||
@ -14242,11 +14185,6 @@
|
||||
"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",
|
||||
@ -15150,161 +15088,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"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,6 +19,7 @@ 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{
|
||||
@ -66,12 +67,12 @@ var rootCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
ws := web.NewWebServer()
|
||||
ws.Core().AddHealthyCallback(func() {
|
||||
ws.Core().HealthyCallback = func() {
|
||||
if config.Get().Outposts.DisableEmbeddedOutpost {
|
||||
return
|
||||
}
|
||||
go attemptProxyStart(ws, u)
|
||||
})
|
||||
}
|
||||
ws.Start()
|
||||
<-ex
|
||||
l.Info("shutting down webserver")
|
||||
@ -94,8 +95,13 @@ 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() {
|
||||
ws.BrandTLS.Check()
|
||||
tw.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.2
|
||||
goauthentik.io/api/v3 v3.2025041.1
|
||||
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.2 h1:vFYYnhcDcxL95RczZwhzt3i4LptFXMvIRN+vgf8sQYg=
|
||||
goauthentik.io/api/v3 v3.2025041.2/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
||||
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=
|
||||
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,14 +21,10 @@ func FullVersion() string {
|
||||
return ver
|
||||
}
|
||||
|
||||
func UserAgentOutpost() string {
|
||||
func OutpostUserAgent() 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
|
||||
healthyCallbacks []func()
|
||||
Healthcheck func() bool
|
||||
HealthyCallback 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,
|
||||
healthyCallbacks: []func(){},
|
||||
Healthcheck: healthcheck,
|
||||
log: logger,
|
||||
started: false,
|
||||
killed: false,
|
||||
alive: false,
|
||||
HealthyCallback: func() {},
|
||||
}
|
||||
g.initCmd()
|
||||
c := make(chan os.Signal, 1)
|
||||
@ -79,10 +79,6 @@ 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
|
||||
}
|
||||
@ -105,9 +101,7 @@ func (g *GoUnicorn) healthcheck() {
|
||||
if g.Healthcheck() {
|
||||
g.alive = true
|
||||
g.log.Debug("backend is alive, backing off with healthchecks")
|
||||
for _, cb := range g.healthyCallbacks {
|
||||
cb()
|
||||
}
|
||||
g.HealthyCallback()
|
||||
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.UserAgentOutpost(),
|
||||
constants.OutpostUserAgent(),
|
||||
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.UserAgentOutpost()},
|
||||
"User-Agent": []string{constants.OutpostUserAgent()},
|
||||
}
|
||||
|
||||
dialer := websocket.Dialer{
|
||||
|
@ -3,8 +3,6 @@ package ak
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"goauthentik.io/api/v3"
|
||||
@ -69,34 +67,16 @@ func (cs *CryptoStore) Fetch(uuid string) error {
|
||||
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,
|
||||
}
|
||||
x509cert, err := tls.X509KeyPair([]byte(cert.Data), []byte(key.Data))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cs.certificates[uuid] = &tcert
|
||||
cs.certificates[uuid] = &x509cert
|
||||
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.UserAgentOutpost(), http.DefaultTransport),
|
||||
HTTPTransport: webutils.NewUserAgentTransport(constants.OutpostUserAgent(), 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.UserAgentOutpost(), web.NewTracingTransport(rsp.Context(), ak.GetTLSTransport()))
|
||||
transport := web.NewUserAgentTransport(constants.OutpostUserAgent(), 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.UserAgentOutpost())
|
||||
headers.Set("X-authentik-meta-version", constants.OutpostUserAgent())
|
||||
|
||||
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.UserAgentOutpost()+ua,
|
||||
constants.OutpostUserAgent()+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.UserAgentOutpost()},
|
||||
"User-Agent": []string{constants.OutpostUserAgent()},
|
||||
}
|
||||
|
||||
dialer := websocket.Dialer{
|
||||
|
@ -1,7 +1,6 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
@ -10,14 +9,6 @@ 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 {
|
||||
@ -29,6 +20,7 @@ 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)
|
||||
@ -38,8 +30,7 @@ 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")
|
||||
rr := r.WithContext(context.WithValue(r.Context(), allowedProxyRequest, true))
|
||||
handlers.ProxyHeaders(h).ServeHTTP(w, rr)
|
||||
ph(h).ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package brand_tls
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -57,37 +56,22 @@ func (w *Watcher) Check() {
|
||||
return
|
||||
}
|
||||
for _, b := range brands {
|
||||
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")
|
||||
}
|
||||
kp := b.WebCertificate.Get()
|
||||
if kp == nil {
|
||||
continue
|
||||
}
|
||||
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")
|
||||
}
|
||||
}
|
||||
err := w.cs.AddKeypair(*kp)
|
||||
if err != nil {
|
||||
w.log.WithError(err).Warning("failed to add certificate")
|
||||
}
|
||||
}
|
||||
w.brands = brands
|
||||
}
|
||||
|
||||
type CertificateConfig struct {
|
||||
Web *tls.Certificate
|
||||
Client *x509.CertPool
|
||||
}
|
||||
|
||||
func (w *Watcher) GetCertificate(ch *tls.ClientHelloInfo) *CertificateConfig {
|
||||
func (w *Watcher) GetCertificate(ch *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
var bestSelection *api.Brand
|
||||
config := CertificateConfig{
|
||||
Web: w.fallback,
|
||||
}
|
||||
for _, t := range w.brands {
|
||||
if !t.WebCertificate.IsSet() && len(t.GetClientCertificates()) < 1 {
|
||||
if t.WebCertificate.Get() == nil {
|
||||
continue
|
||||
}
|
||||
if *t.Default {
|
||||
@ -98,20 +82,11 @@ func (w *Watcher) GetCertificate(ch *tls.ClientHelloInfo) *CertificateConfig {
|
||||
}
|
||||
}
|
||||
if bestSelection == nil {
|
||||
return &config
|
||||
return w.fallback, nil
|
||||
}
|
||||
if bestSelection.GetWebCertificate() != "" {
|
||||
if cert := w.cs.Get(bestSelection.GetWebCertificate()); cert != nil {
|
||||
config.Web = cert
|
||||
}
|
||||
cert := w.cs.Get(bestSelection.GetWebCertificate())
|
||||
if cert == nil {
|
||||
return w.fallback, 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
|
||||
return cert, nil
|
||||
}
|
||||
|
@ -1,11 +1,15 @@
|
||||
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"
|
||||
@ -14,6 +18,8 @@ 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",
|
||||
@ -21,6 +27,14 @@ 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)
|
||||
@ -37,7 +51,7 @@ func (ws *WebServer) runMetricsServer() {
|
||||
l.WithError(err).Warning("failed to get upstream metrics")
|
||||
return
|
||||
}
|
||||
re.Header.Set("Authorization", fmt.Sprintf("Bearer %s", ws.metricsKey))
|
||||
re.Header.Set("Authorization", fmt.Sprintf("Bearer %s", key))
|
||||
res, err := ws.upstreamHttpClient().Do(re)
|
||||
if err != nil {
|
||||
l.WithError(err).Warning("failed to get upstream metrics")
|
||||
@ -50,9 +64,13 @@ 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,29 +2,21 @@ 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) {
|
||||
@ -34,25 +26,8 @@ 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")
|
||||
}
|
||||
@ -82,7 +57,7 @@ func (ws *WebServer) configureProxy() {
|
||||
Requests.With(prometheus.Labels{
|
||||
"dest": "core",
|
||||
}).Observe(float64(elapsed) / float64(time.Second))
|
||||
r.Body = http.MaxBytesReader(rw, r.Body, maxBodyBytes)
|
||||
r.Body = http.MaxBytesReader(rw, r.Body, 32*1024*1024)
|
||||
rp.ServeHTTP(rw, r)
|
||||
}))
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package web
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
@ -14,27 +13,17 @@ 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
|
||||
@ -51,11 +40,10 @@ type WebServer struct {
|
||||
log *log.Entry
|
||||
upstreamClient *http.Client
|
||||
upstreamURL *url.URL
|
||||
|
||||
metricsKey string
|
||||
ipcKey string
|
||||
}
|
||||
|
||||
const UnixSocketName = "authentik-core.sock"
|
||||
|
||||
func NewWebServer() *WebServer {
|
||||
l := log.WithField("logger", "authentik.router")
|
||||
mainHandler := mux.NewRouter()
|
||||
@ -88,7 +76,7 @@ func NewWebServer() *WebServer {
|
||||
mainRouter: mainHandler,
|
||||
loggingRouter: loggingHandler,
|
||||
log: l,
|
||||
gunicornReady: false,
|
||||
gunicornReady: true,
|
||||
upstreamClient: upstreamClient,
|
||||
upstreamURL: u,
|
||||
}
|
||||
@ -115,59 +103,7 @@ 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()
|
||||
@ -176,23 +112,23 @@ func (ws *WebServer) Start() {
|
||||
|
||||
func (ws *WebServer) attemptStartBackend() {
|
||||
for {
|
||||
if ws.gunicornReady {
|
||||
if !ws.gunicornReady {
|
||||
return
|
||||
}
|
||||
err := ws.g.Start()
|
||||
ws.log.WithError(err).Warning("gunicorn process died, restarting")
|
||||
log.WithField("logger", "authentik.router").WithError(err).Warning("gunicorn process died, restarting")
|
||||
if err != nil {
|
||||
ws.log.WithError(err).Error("gunicorn failed to start, restarting")
|
||||
log.WithField("logger", "authentik.router").WithError(err).Error("gunicorn failed to start, restarting")
|
||||
continue
|
||||
}
|
||||
failedChecks := 0
|
||||
for range time.NewTicker(30 * time.Second).C {
|
||||
if !ws.g.IsRunning() {
|
||||
ws.log.Warningf("gunicorn process failed healthcheck %d times", failedChecks)
|
||||
log.WithField("logger", "authentik.router").Warningf("gunicorn process failed healthcheck %d times", failedChecks)
|
||||
failedChecks += 1
|
||||
}
|
||||
if failedChecks >= 3 {
|
||||
ws.log.WithError(err).Error("gunicorn process failed healthcheck three times, restarting")
|
||||
log.WithField("logger", "authentik.router").WithError(err).Error("gunicorn process failed healthcheck three times, restarting")
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -210,15 +146,6 @@ 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,57 +12,40 @@ import (
|
||||
"goauthentik.io/internal/utils/web"
|
||||
)
|
||||
|
||||
func (ws *WebServer) GetCertificate() func(ch *tls.ClientHelloInfo) (*tls.Config, error) {
|
||||
fallback, err := crypto.GenerateSelfSignedCert()
|
||||
func (ws *WebServer) GetCertificate() func(ch *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
cert, err := crypto.GenerateSelfSignedCert()
|
||||
if err != nil {
|
||||
ws.log.WithError(err).Error("failed to generate default cert")
|
||||
}
|
||||
return func(ch *tls.ClientHelloInfo) (*tls.Config, error) {
|
||||
cfg := utils.GetTLSConfig()
|
||||
return func(ch *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
if ch.ServerName == "" {
|
||||
cfg.Certificates = []tls.Certificate{fallback}
|
||||
return cfg, nil
|
||||
return &cert, nil
|
||||
}
|
||||
if ws.ProxyServer != nil {
|
||||
appCert := ws.ProxyServer.GetCertificate(ch.ServerName)
|
||||
if appCert != nil {
|
||||
cfg.Certificates = []tls.Certificate{*appCert}
|
||||
return cfg, nil
|
||||
return appCert, nil
|
||||
}
|
||||
}
|
||||
if ws.BrandTLS != nil {
|
||||
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
|
||||
return ws.BrandTLS.GetCertificate(ch)
|
||||
}
|
||||
ws.log.Trace("using default, self-signed certificate")
|
||||
cfg.Certificates = []tls.Certificate{fallback}
|
||||
return cfg, nil
|
||||
return &cert, nil
|
||||
}
|
||||
}
|
||||
|
||||
// ServeHTTPS constructs a net.Listener and starts handling HTTPS requests
|
||||
func (ws *WebServer) listenTLS() {
|
||||
tlsConfig := utils.GetTLSConfig()
|
||||
tlsConfig.GetConfigForClient = ws.GetCertificate()
|
||||
tlsConfig.GetCertificate = 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,8 +83,7 @@ if [[ "$1" == "server" ]]; then
|
||||
run_authentik
|
||||
elif [[ "$1" == "worker" ]]; then
|
||||
set_mode "worker"
|
||||
shift
|
||||
check_if_root "python -m manage worker $@"
|
||||
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.1016.1",
|
||||
"aws-cdk": "^2.1015.0",
|
||||
"cross-env": "^7.0.3"
|
||||
},
|
||||
"engines": {
|
||||
@ -17,9 +17,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk": {
|
||||
"version": "2.1016.1",
|
||||
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1016.1.tgz",
|
||||
"integrity": "sha512-248TBiluT8jHUjkpzvWJOHv2fS+An9fiII3eji8H7jwfTu5yMBk7on4B/AVNr9A1GXJk9I32qf9Q0A3rLWRYPQ==",
|
||||
"version": "2.1015.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1015.0.tgz",
|
||||
"integrity": "sha512-txd+yMVVybtLfiwT409+fahbP0SkiwhmQvQf6PVVYnWzDPSknxYlUNJHisHV4tJEcbHWn1QPsLmqqMT0bw8hBg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
|
@ -10,7 +10,7 @@
|
||||
"node": ">=20"
|
||||
},
|
||||
"devDependencies": {
|
||||
"aws-cdk": "^2.1016.1",
|
||||
"aws-cdk": "^2.1015.0",
|
||||
"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-05-20 00:10+0000\n"
|
||||
"POT-Creation-Date: 2025-04-23 09:00+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,10 +93,6 @@ 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 ""
|
||||
@ -620,32 +616,6 @@ 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-05-20 00:10+0000\n"
|
||||
"POT-Creation-Date: 2025-04-23 09:00+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,10 +113,6 @@ 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"
|
||||
@ -679,36 +675,6 @@ 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 "
|
||||
|
@ -15,7 +15,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-05-20 00:10+0000\n"
|
||||
"POT-Creation-Date: 2025-04-23 09:00+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,10 +102,6 @@ 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 "品牌"
|
||||
@ -630,33 +626,6 @@ 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 "
|
||||
|
@ -14,7 +14,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-05-20 00:10+0000\n"
|
||||
"POT-Creation-Date: 2025-04-23 09:00+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,10 +101,6 @@ 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 "品牌"
|
||||
@ -629,33 +625,6 @@ 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.1",
|
||||
"version": "2025.4.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@goauthentik/authentik",
|
||||
"version": "2025.4.1",
|
||||
"version": "2025.4.0",
|
||||
"devDependencies": {
|
||||
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
||||
"prettier": "^3.3.3",
|
||||
|
4136
packages/docusaurus-config/package-lock.json
generated
4136
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.1.0",
|
||||
"version": "1.0.6",
|
||||
"description": "authentik's Docusaurus config",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
@ -20,24 +20,23 @@
|
||||
"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-search-algolia": "^3.7.0",
|
||||
"@docusaurus/types": "^3.7.0",
|
||||
"@docusaurus/theme-mermaid": "^3.7.0",
|
||||
"@goauthentik/prettier-config": "^1.0.1",
|
||||
"@goauthentik/tsconfig": "^1.0.1",
|
||||
"@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"
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"typescript": "^5.8.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@docusaurus/core": "^3.7.0",
|
||||
"@docusaurus/preset-classic": "^3.7.0",
|
||||
"@docusaurus/theme-common": "^3.7.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"
|
||||
"@docusaurus/theme-mermaid": "^3.7.0",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.11"
|
||||
@ -50,11 +49,6 @@
|
||||
"out/**/*"
|
||||
],
|
||||
"prettier": "@goauthentik/prettier-config",
|
||||
"peerDependenciesMeta": {
|
||||
"@docusaurus/theme-search-algolia": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
|
278
packages/eslint-config/package-lock.json
generated
278
packages/eslint-config/package-lock.json
generated
@ -1,44 +1,37 @@
|
||||
{
|
||||
"name": "@goauthentik/eslint-config",
|
||||
"version": "1.0.5",
|
||||
"version": "1.0.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@goauthentik/eslint-config",
|
||||
"version": "1.0.5",
|
||||
"version": "1.0.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"eslint": "^9.27.0",
|
||||
"eslint": "^9.23.0",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint-plugin-lit": "^2.1.1",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-lit": "^1.15.0",
|
||||
"eslint-plugin-react": "^7.37.4",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-wc": "^3.0.1"
|
||||
"eslint-plugin-wc": "^2.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@goauthentik/prettier-config": "^1.0.1",
|
||||
"@goauthentik/tsconfig": "^1.0.1",
|
||||
"@types/eslint": "^9.6.1",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.32.1"
|
||||
"typescript": "^5.8.2",
|
||||
"typescript-eslint": "^8.29.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.11"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.32.1"
|
||||
"optionalDependencies": {
|
||||
"react": "^18.3.1"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
"peerDependencies": {
|
||||
"typescript": "^5.8.2",
|
||||
"typescript-eslint": "^8.29.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
@ -177,9 +170,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint-community/eslint-utils": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz",
|
||||
"integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==",
|
||||
"version": "4.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz",
|
||||
"integrity": "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"eslint-visitor-keys": "^3.4.3"
|
||||
@ -239,9 +232,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/core": {
|
||||
"version": "0.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz",
|
||||
"integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==",
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz",
|
||||
"integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.15"
|
||||
@ -274,15 +267,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "9.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.27.0.tgz",
|
||||
"integrity": "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==",
|
||||
"version": "9.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.24.0.tgz",
|
||||
"integrity": "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://eslint.org/donate"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/object-schema": {
|
||||
@ -295,18 +285,30 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/plugin-kit": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz",
|
||||
"integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==",
|
||||
"version": "0.2.8",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz",
|
||||
"integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@eslint/core": "^0.14.0",
|
||||
"@eslint/core": "^0.13.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",
|
||||
@ -576,21 +578,21 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"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==",
|
||||
"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==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.10.0",
|
||||
"@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",
|
||||
"@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",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^7.0.0",
|
||||
"ignore": "^5.3.1",
|
||||
"natural-compare": "^1.4.0",
|
||||
"ts-api-utils": "^2.1.0"
|
||||
"ts-api-utils": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -605,27 +607,17 @@
|
||||
"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.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.1.tgz",
|
||||
"integrity": "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==",
|
||||
"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==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@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",
|
||||
"@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",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@ -641,14 +633,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.32.1.tgz",
|
||||
"integrity": "sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==",
|
||||
"version": "8.29.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.29.1.tgz",
|
||||
"integrity": "sha512-2nggXGX5F3YrsGN08pw4XpMLO1Rgtnn4AzTegC2MDesv6q3QaTU5yU7IbS1tf1IwCR0Hv/1EFygLn9ms6LIpDA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.32.1",
|
||||
"@typescript-eslint/visitor-keys": "8.32.1"
|
||||
"@typescript-eslint/types": "8.29.1",
|
||||
"@typescript-eslint/visitor-keys": "8.29.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -659,16 +651,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"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==",
|
||||
"version": "8.29.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.29.1.tgz",
|
||||
"integrity": "sha512-DkDUSDwZVCYN71xA4wzySqqcZsHKic53A4BLqmrWFFpOpNSoxX233lwGu/2135ymTCR04PoKiEEEvN1gFYg4Tw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "8.32.1",
|
||||
"@typescript-eslint/utils": "8.32.1",
|
||||
"@typescript-eslint/typescript-estree": "8.29.1",
|
||||
"@typescript-eslint/utils": "8.29.1",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^2.1.0"
|
||||
"ts-api-utils": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -683,9 +675,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.32.1.tgz",
|
||||
"integrity": "sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==",
|
||||
"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==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -697,20 +689,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.1.tgz",
|
||||
"integrity": "sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==",
|
||||
"version": "8.29.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.29.1.tgz",
|
||||
"integrity": "sha512-l1enRoSaUkQxOQnbi0KPUtqeZkSiFlqrx9/3ns2rEDhGKfTa+88RmXqedC1zmVTOWrLc2e6DEJrTA51C9iLH5g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.32.1",
|
||||
"@typescript-eslint/visitor-keys": "8.32.1",
|
||||
"@typescript-eslint/types": "8.29.1",
|
||||
"@typescript-eslint/visitor-keys": "8.29.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.1.0"
|
||||
"ts-api-utils": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -750,9 +742,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||
"version": "7.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
|
||||
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
@ -763,16 +755,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.1.tgz",
|
||||
"integrity": "sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==",
|
||||
"version": "8.29.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.29.1.tgz",
|
||||
"integrity": "sha512-QAkFEbytSaB8wnmB+DflhUPz6CLbFWE2SnSCrRMEa+KnXIzDYbpsn++1HGvnfAsUY44doDXmvRkO5shlM/3UfA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@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"
|
||||
"@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"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -787,13 +779,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.1.tgz",
|
||||
"integrity": "sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==",
|
||||
"version": "8.29.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.29.1.tgz",
|
||||
"integrity": "sha512-RGLh5CRaUEf02viP5c1Vh1cMGffQscyHe7HPAzGpfmfflFg1wUz2rYxd+OZqwpeypYvZ8UxSxuIpF++fmOzEcg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.32.1",
|
||||
"@typescript-eslint/types": "8.29.1",
|
||||
"eslint-visitor-keys": "^4.2.0"
|
||||
},
|
||||
"engines": {
|
||||
@ -1513,19 +1505,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "9.27.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz",
|
||||
"integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==",
|
||||
"version": "9.24.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.24.0.tgz",
|
||||
"integrity": "sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==",
|
||||
"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.1",
|
||||
"@eslint/core": "^0.14.0",
|
||||
"@eslint/config-helpers": "^0.2.0",
|
||||
"@eslint/core": "^0.12.0",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "9.27.0",
|
||||
"@eslint/plugin-kit": "^0.3.1",
|
||||
"@eslint/js": "9.24.0",
|
||||
"@eslint/plugin-kit": "^0.2.7",
|
||||
"@humanfs/node": "^0.16.6",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
"@humanwhocodes/retry": "^0.4.2",
|
||||
@ -1661,19 +1653,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-lit": {
|
||||
"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==",
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-lit/-/eslint-plugin-lit-1.15.0.tgz",
|
||||
"integrity": "sha512-Yhr2MYNz6Ln8megKcX503aVZQln8wsywCG49g0heiJ/Qr5UjkE4pGr4Usez2anNcc7NvlvHbQWMYwWcgH3XRKA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"parse5": "^6.0.1",
|
||||
"parse5-htmlparser2-tree-adapter": "^6.0.1"
|
||||
"parse5-htmlparser2-tree-adapter": "^6.0.1",
|
||||
"requireindex": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
"node": ">= 12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": ">= 8"
|
||||
"eslint": ">= 5"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-react": {
|
||||
@ -1738,13 +1731,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-wc": {
|
||||
"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==",
|
||||
"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==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-valid-element-name": "^1.0.0",
|
||||
"js-levenshtein-esm": "^2.0.0"
|
||||
"js-levenshtein-esm": "^1.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": ">=8.40.0"
|
||||
@ -2703,9 +2696,9 @@
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/js-levenshtein-esm": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-levenshtein-esm/-/js-levenshtein-esm-2.0.0.tgz",
|
||||
"integrity": "sha512-1n4LEPOL4wRXY8rOQcuA7Iuaphe5xCMayvufCzlLAi+hRsnBRDbSS6XPuV58CBVJxj5D9ApFLyjQ7KzFToyHBw==",
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/js-levenshtein-esm/-/js-levenshtein-esm-1.2.0.tgz",
|
||||
"integrity": "sha512-fzreKVq1eD7eGcQr7MtRpQH94f8gIfhdrc7yeih38xh684TNMK9v5aAu2wxfIRMk/GpAJRrzcirMAPIaSDaByQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
@ -3280,30 +3273,18 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "19.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
|
||||
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
|
||||
"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==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
},
|
||||
"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",
|
||||
@ -3352,6 +3333,15 @@
|
||||
"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",
|
||||
@ -3468,14 +3458,6 @@
|
||||
"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",
|
||||
@ -4018,15 +4000,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-eslint": {
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.32.1.tgz",
|
||||
"integrity": "sha512-D7el+eaDHAmXvrZBy1zpzSNIRqnCOrkwTgZxTu3MUqRWk8k0q9m9Ho4+vPf7iHtgUfrK/o8IZaEApsxPlHTFCg==",
|
||||
"version": "8.29.1",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.29.1.tgz",
|
||||
"integrity": "sha512-f8cDkvndhbQMPcysk6CUSGBWV+g1utqdn71P5YKwMumVMOG/5k7cHq0KyG4O52nB0oKS4aN2Tp5+wB4APJGC+w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "8.32.1",
|
||||
"@typescript-eslint/parser": "8.32.1",
|
||||
"@typescript-eslint/utils": "8.32.1"
|
||||
"@typescript-eslint/eslint-plugin": "8.29.1",
|
||||
"@typescript-eslint/parser": "8.29.1",
|
||||
"@typescript-eslint/utils": "8.29.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@goauthentik/eslint-config",
|
||||
"version": "1.0.5",
|
||||
"version": "1.0.4",
|
||||
"description": "authentik's ESLint config",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
@ -27,25 +27,26 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"eslint": "^9.27.0",
|
||||
"eslint": "^9.23.0",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint-plugin-lit": "^2.1.1",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-lit": "^1.15.0",
|
||||
"eslint-plugin-react": "^7.37.4",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-wc": "^3.0.1"
|
||||
"eslint-plugin-wc": "^2.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@goauthentik/prettier-config": "^1.0.1",
|
||||
"@goauthentik/tsconfig": "^1.0.1",
|
||||
"@types/eslint": "^9.6.1",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.32.1"
|
||||
"typescript": "^5.8.2",
|
||||
"typescript-eslint": "^8.29.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.32.1"
|
||||
"typescript": "^5.8.2",
|
||||
"typescript-eslint": "^8.29.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"react": "^18.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.11"
|
||||
@ -57,14 +58,6 @@
|
||||
"out/**/*"
|
||||
],
|
||||
"prettier": "@goauthentik/prettier-config",
|
||||
"peerDependenciesMeta": {
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
|
@ -31,30 +31,8 @@ export const AuthentikPrettierConfig = {
|
||||
trailingComma: "all",
|
||||
useTabs: false,
|
||||
vueIndentScriptAndStyle: false,
|
||||
plugins: [
|
||||
// ---
|
||||
"prettier-plugin-packagejson",
|
||||
"@trivago/prettier-plugin-sort-imports",
|
||||
],
|
||||
importOrder: [
|
||||
// ---
|
||||
"^(@goauthentik/|#)common.+",
|
||||
"^(@goauthentik/|#)elements.+",
|
||||
"^(@goauthentik/|#)components.+",
|
||||
"^(@goauthentik/|#)user.+",
|
||||
"^(@goauthentik/|#)admin.+",
|
||||
"^(@goauthentik/|#)flow.+",
|
||||
|
||||
"<THIRD_PARTY_MODULES>",
|
||||
|
||||
"^~@goauthentik.+",
|
||||
"^@goauthentik.+",
|
||||
"^(@?)lit(.*)$",
|
||||
"\\.css$",
|
||||
"^@goauthentik/api$",
|
||||
"^[./]",
|
||||
],
|
||||
importOrderSideEffects: false,
|
||||
plugins: ["prettier-plugin-packagejson", "@trivago/prettier-plugin-sort-imports"],
|
||||
importOrder: ["^(@?)lit(.*)$", "\\.css$", "^@goauthentik/api$", "^[./]"],
|
||||
importOrderSeparation: true,
|
||||
importOrderSortSpecifiers: true,
|
||||
importOrderParserPlugins: ["typescript", "jsx", "classProperties", "decorators-legacy"],
|
||||
|
98
packages/prettier-config/package-lock.json
generated
98
packages/prettier-config/package-lock.json
generated
@ -1,19 +1,19 @@
|
||||
{
|
||||
"name": "@goauthentik/prettier-config",
|
||||
"version": "1.0.4",
|
||||
"version": "1.0.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@goauthentik/prettier-config",
|
||||
"version": "1.0.4",
|
||||
"version": "1.0.1",
|
||||
"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.14",
|
||||
"prettier-plugin-packagejson": "^2.5.10",
|
||||
"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.14"
|
||||
"prettier-plugin-packagejson": "^2.5.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
@ -206,16 +206,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@pkgr/core": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.4.tgz",
|
||||
"integrity": "sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw==",
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.2.tgz",
|
||||
"integrity": "sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/pkgr"
|
||||
"url": "https://opencollective.com/unts"
|
||||
}
|
||||
},
|
||||
"node_modules/@trivago/prettier-plugin-sort-imports": {
|
||||
@ -295,9 +295,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fdir": {
|
||||
"version": "6.4.4",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
|
||||
"integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
|
||||
"version": "6.4.3",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz",
|
||||
"integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
@ -309,10 +309,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"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": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-4.1.1.tgz",
|
||||
"integrity": "sha512-cmP497iLq54AZnv4YRAEMnEyQ1eIn4tGKbmswqwmFV4GBnAqE8NLtWxxdXa++AalfgL5EBH4IxTPyquEuGY/jA==",
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-3.2.0.tgz",
|
||||
"integrity": "sha512-ZHG9a1gEhUMX1TvGrLdyWb9kDopCBbTnI8z4JgRMYxsijWipgjSEYoPWqBuIB0DnRnvqlQSEeVmzpeuPm7NdFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
@ -437,14 +450,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier-plugin-packagejson": {
|
||||
"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==",
|
||||
"version": "2.5.10",
|
||||
"resolved": "https://registry.npmjs.org/prettier-plugin-packagejson/-/prettier-plugin-packagejson-2.5.10.tgz",
|
||||
"integrity": "sha512-LUxATI5YsImIVSaaLJlJ3aE6wTD+nvots18U3GuQMJpUyClChaZlQrqx3dBnbhF20OnKWZyx8EgyZypQtBDtgQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"sort-package-json": "3.2.1",
|
||||
"synckit": "0.11.6"
|
||||
"sort-package-json": "2.15.1",
|
||||
"synckit": "0.9.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prettier": ">= 1.16.0"
|
||||
@ -456,9 +469,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||
"version": "7.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
|
||||
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
@ -476,48 +489,50 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/sort-package-json": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-3.2.1.tgz",
|
||||
"integrity": "sha512-rTfRdb20vuoAn7LDlEtCqOkYfl2X+Qze6cLbNOzcDpbmKEhJI30tTN44d5shbKJnXsvz24QQhlCm81Bag7EOKg==",
|
||||
"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==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"detect-indent": "^7.0.1",
|
||||
"detect-newline": "^4.0.1",
|
||||
"git-hooks-list": "^4.0.0",
|
||||
"detect-newline": "^4.0.0",
|
||||
"get-stdin": "^9.0.0",
|
||||
"git-hooks-list": "^3.0.0",
|
||||
"is-plain-obj": "^4.1.0",
|
||||
"semver": "^7.7.1",
|
||||
"semver": "^7.6.0",
|
||||
"sort-object-keys": "^1.1.3",
|
||||
"tinyglobby": "^0.2.12"
|
||||
"tinyglobby": "^0.2.9"
|
||||
},
|
||||
"bin": {
|
||||
"sort-package-json": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/synckit": {
|
||||
"version": "0.11.6",
|
||||
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.6.tgz",
|
||||
"integrity": "sha512-2pR2ubZSV64f/vqm9eLPz/KOvR9Dm+Co/5ChLgeHl0yEDRc6h5hXHoxEQH8Y5Ljycozd3p1k5TTSVdzYGkPvLw==",
|
||||
"version": "0.9.2",
|
||||
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz",
|
||||
"integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@pkgr/core": "^0.2.4"
|
||||
"@pkgr/core": "^0.1.0",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/synckit"
|
||||
"url": "https://opencollective.com/unts"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby": {
|
||||
"version": "0.2.13",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",
|
||||
"integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
|
||||
"version": "0.2.12",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz",
|
||||
"integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fdir": "^6.4.4",
|
||||
"fdir": "^6.4.3",
|
||||
"picomatch": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -527,6 +542,13 @@
|
||||
"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,26 +1,32 @@
|
||||
{
|
||||
"name": "@goauthentik/prettier-config",
|
||||
"version": "1.1.0",
|
||||
"version": "1.0.4",
|
||||
"description": "authentik's Prettier config",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "tsc -p ."
|
||||
},
|
||||
"type": "module",
|
||||
"exports": "./index.js",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"import": "./index.js",
|
||||
"types": "./out/index.d.ts"
|
||||
}
|
||||
},
|
||||
"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.14",
|
||||
"prettier-plugin-packagejson": "^2.5.10",
|
||||
"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.14"
|
||||
"prettier-plugin-packagejson": "^2.5.10"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.11"
|
||||
|
@ -1,7 +1,7 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# Stage 1: Build web
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/node:24 AS web-builder
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/node:22 AS web-builder
|
||||
|
||||
ENV NODE_ENV=production
|
||||
WORKDIR /static
|
||||
|
@ -17,7 +17,7 @@ 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",
|
||||
@ -31,11 +31,11 @@ dependencies = [
|
||||
"drf-spectacular==0.28.0",
|
||||
"dumb-init==1.2.5.post1",
|
||||
"duo-client==5.5.0",
|
||||
"fido2==2.0.0",
|
||||
"fido2==1.2.0",
|
||||
"flower==2.0.1",
|
||||
"geoip2==5.1.0",
|
||||
"geopy==2.4.1",
|
||||
"google-api-python-client==2.170.0",
|
||||
"google-api-python-client==2.169.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.5",
|
||||
"pydantic==2.11.4",
|
||||
"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.29.1",
|
||||
"sentry-sdk==2.28.0",
|
||||
"service-identity==24.2.0",
|
||||
"setproctitle==1.3.6",
|
||||
"structlog==25.3.0",
|
||||
@ -114,9 +114,9 @@ no-binary-package = [
|
||||
]
|
||||
|
||||
[tool.uv.sources]
|
||||
django-tenants = { git = "https://github.com/goauthentik/django-tenants.git", branch = "authentik-fixes" }
|
||||
opencontainers = { git = "https://github.com/vsoch/oci-python", rev = "ceb4fcc090851717a3069d78e85ceb1e86c2740c" }
|
||||
djangorestframework = { git = "https://github.com/goauthentik/django-rest-framework", rev = "896722bab969fabc74a08b827da59409cf9f1a4e" }
|
||||
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" }
|
||||
|
||||
[project.scripts]
|
||||
ak = "lifecycle.ak:main"
|
||||
|
474
schema.yml
474
schema.yml
@ -4460,15 +4460,6 @@ 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:
|
||||
@ -24987,7 +24978,6 @@ 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
|
||||
@ -25236,7 +25226,6 @@ 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
|
||||
@ -37729,311 +37718,6 @@ 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
|
||||
@ -41262,7 +40946,6 @@ 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
|
||||
@ -42926,12 +42609,6 @@ 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
|
||||
@ -42996,12 +42673,6 @@ 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
|
||||
@ -43171,12 +42842,6 @@ components:
|
||||
- name
|
||||
- private_key
|
||||
- public_key
|
||||
CertAttributeEnum:
|
||||
enum:
|
||||
- subject
|
||||
- common_name
|
||||
- email
|
||||
type: string
|
||||
CertificateData:
|
||||
type: object
|
||||
description: Get CertificateKeyPair's data
|
||||
@ -48703,7 +48368,6 @@ 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
|
||||
@ -48711,96 +48375,6 @@ 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
|
||||
@ -50646,18 +50220,6 @@ 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:
|
||||
@ -52334,12 +51896,6 @@ 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
|
||||
@ -53523,31 +53079,6 @@ 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
|
||||
@ -60262,11 +59793,6 @@ components:
|
||||
- pk
|
||||
- uid
|
||||
- username
|
||||
UserAttributeEnum:
|
||||
enum:
|
||||
- username
|
||||
- email
|
||||
type: string
|
||||
UserConsent:
|
||||
type: object
|
||||
description: UserConsent Serializer
|
||||
|
@ -6,7 +6,7 @@ services:
|
||||
network_mode: host
|
||||
restart: always
|
||||
mailpit:
|
||||
image: docker.io/axllent/mailpit:v1.25.0
|
||||
image: docker.io/axllent/mailpit:v1.24.2
|
||||
ports:
|
||||
- 1025:1025
|
||||
- 8025:8025
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
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
|
||||
@ -16,12 +15,10 @@ 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,7 +6,6 @@ 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
|
||||
@ -18,12 +17,10 @@ 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,7 +4,6 @@ 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
|
||||
|
||||
@ -13,12 +12,10 @@ 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,7 +2,6 @@
|
||||
|
||||
from dataclasses import asdict
|
||||
from time import sleep
|
||||
from unittest.mock import patch
|
||||
|
||||
from pyrad.client import Client
|
||||
from pyrad.dictionary import Dictionary
|
||||
@ -13,12 +12,10 @@ 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"""
|
||||
|
||||
|
52
uv.lock
generated
52
uv.lock
generated
@ -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/goauthentik/django-tenants.git?branch=authentik-fixes" },
|
||||
{ name = "djangorestframework", git = "https://github.com/goauthentik/django-rest-framework?rev=896722bab969fabc74a08b827da59409cf9f1a4e" },
|
||||
{ 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 = "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 = "==2.0.0" },
|
||||
{ name = "fido2", specifier = "==1.2.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.170.0" },
|
||||
{ name = "google-api-python-client", specifier = "==2.169.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/vsoch/oci-python?rev=ceb4fcc090851717a3069d78e85ceb1e86c2740c" },
|
||||
{ name = "opencontainers", git = "https://github.com/BeryJu/oci-python?rev=c791b19056769cd67957322806809ab70f5bead8" },
|
||||
{ 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.5" },
|
||||
{ name = "pydantic", specifier = "==2.11.4" },
|
||||
{ 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.29.1" },
|
||||
{ name = "sentry-sdk", specifier = "==2.28.0" },
|
||||
{ name = "service-identity", specifier = "==24.2.0" },
|
||||
{ name = "setproctitle", specifier = "==1.3.6" },
|
||||
{ name = "structlog", specifier = "==25.3.0" },
|
||||
@ -1027,14 +1027,14 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "django-guardian"
|
||||
version = "3.0.0"
|
||||
version = "2.4.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "django" },
|
||||
]
|
||||
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" }
|
||||
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" }
|
||||
wheels = [
|
||||
{ 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" },
|
||||
{ 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" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1119,7 +1119,7 @@ s3 = [
|
||||
[[package]]
|
||||
name = "django-tenants"
|
||||
version = "3.7.0"
|
||||
source = { git = "https://github.com/goauthentik/django-tenants.git?branch=authentik-fixes#156e53a6f5902d74b73dd9d0192fffaa2587a740" }
|
||||
source = { git = "https://github.com/rissson/django-tenants.git?branch=authentik-fixes#156e53a6f5902d74b73dd9d0192fffaa2587a740" }
|
||||
dependencies = [
|
||||
{ name = "django" },
|
||||
]
|
||||
@ -1127,7 +1127,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "djangorestframework"
|
||||
version = "3.16.0"
|
||||
source = { git = "https://github.com/goauthentik/django-rest-framework?rev=896722bab969fabc74a08b827da59409cf9f1a4e#896722bab969fabc74a08b827da59409cf9f1a4e" }
|
||||
source = { git = "https://github.com/authentik-community/django-rest-framework?rev=896722bab969fabc74a08b827da59409cf9f1a4e#896722bab969fabc74a08b827da59409cf9f1a4e" }
|
||||
dependencies = [
|
||||
{ name = "django" },
|
||||
]
|
||||
@ -1262,14 +1262,14 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "fido2"
|
||||
version = "2.0.0"
|
||||
version = "1.2.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cryptography" },
|
||||
]
|
||||
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" }
|
||||
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" }
|
||||
wheels = [
|
||||
{ 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" },
|
||||
{ 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" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1396,7 +1396,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "google-api-python-client"
|
||||
version = "2.170.0"
|
||||
version = "2.169.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "google-api-core" },
|
||||
@ -1405,9 +1405,9 @@ dependencies = [
|
||||
{ name = "httplib2" },
|
||||
{ name = "uritemplate" },
|
||||
]
|
||||
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" }
|
||||
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" }
|
||||
wheels = [
|
||||
{ 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" },
|
||||
{ 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" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2153,7 +2153,7 @@ wheels = [
|
||||
[[package]]
|
||||
name = "opencontainers"
|
||||
version = "0.0.14"
|
||||
source = { git = "https://github.com/vsoch/oci-python?rev=ceb4fcc090851717a3069d78e85ceb1e86c2740c#ceb4fcc090851717a3069d78e85ceb1e86c2740c" }
|
||||
source = { git = "https://github.com/BeryJu/oci-python?rev=c791b19056769cd67957322806809ab70f5bead8#c791b19056769cd67957322806809ab70f5bead8" }
|
||||
|
||||
[[package]]
|
||||
name = "opentelemetry-api"
|
||||
@ -2473,7 +2473,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.11.5"
|
||||
version = "2.11.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "annotated-types" },
|
||||
@ -2481,9 +2481,9 @@ dependencies = [
|
||||
{ name = "typing-extensions" },
|
||||
{ name = "typing-inspection" },
|
||||
]
|
||||
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" }
|
||||
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" }
|
||||
wheels = [
|
||||
{ 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" },
|
||||
{ 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" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
@ -2940,15 +2940,15 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "sentry-sdk"
|
||||
version = "2.29.1"
|
||||
version = "2.28.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
{ name = "urllib3" },
|
||||
]
|
||||
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" }
|
||||
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" }
|
||||
wheels = [
|
||||
{ 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" },
|
||||
{ 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" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3,10 +3,13 @@
|
||||
* @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 CSSImportPattern = /import [\w$]+ from .+\.(css)/g;
|
||||
const NODE_ENV = process.env.NODE_ENV || "development";
|
||||
|
||||
const CSSImportPattern = /import [\w\$]+ from .+\.(css)/g;
|
||||
const JavaScriptFilePattern = /\.m?(js|ts|tsx)$/;
|
||||
|
||||
/**
|
||||
@ -27,11 +30,6 @@ const inlineCSSPlugin = {
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @satisfies {InlineConfig}
|
||||
*/
|
||||
// const viteFinal = ;
|
||||
|
||||
/**
|
||||
* @satisfies {StorybookConfig}
|
||||
*/
|
||||
@ -50,21 +48,22 @@ const config = {
|
||||
docs: {
|
||||
autodocs: "tag",
|
||||
},
|
||||
async viteFinal(config) {
|
||||
const [{ mergeConfig }, { createBundleDefinitions }] = await Promise.all([
|
||||
import("vite"),
|
||||
import("@goauthentik/web/bundler/utils/node"),
|
||||
]);
|
||||
|
||||
viteFinal({ plugins = [], ...config }) {
|
||||
/**
|
||||
* @satisfies {InlineConfig}
|
||||
*/
|
||||
const overrides = {
|
||||
define: createBundleDefinitions(),
|
||||
plugins: [inlineCSSPlugin, postcssLit(), tsconfigPaths()],
|
||||
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()],
|
||||
};
|
||||
|
||||
return mergeConfig(config, overrides);
|
||||
return mergedConfig;
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
@ -3,11 +3,10 @@
|
||||
*
|
||||
* @import { ThemeVarsPartial } from "storybook/internal/theming";
|
||||
*/
|
||||
import { createUIThemeEffect, resolveUITheme } from "@goauthentik/web/common/theme.ts";
|
||||
import { addons } from "@storybook/manager-api";
|
||||
import { create } from "@storybook/theming/create";
|
||||
|
||||
import { createUIThemeEffect, resolveUITheme } from "@goauthentik/web/common/theme.ts";
|
||||
|
||||
/**
|
||||
* @satisfies {Partial<ThemeVarsPartial>}
|
||||
*/
|
||||
|
@ -1,29 +0,0 @@
|
||||
/**
|
||||
* @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,
|
||||
};
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
/// <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,57 +1,90 @@
|
||||
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 tseslint from "typescript-eslint";
|
||||
|
||||
import { createESLintPackageConfig } from "@goauthentik/eslint-config";
|
||||
|
||||
// @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,
|
||||
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)
|
||||
{
|
||||
rules: {
|
||||
"no-console": "off",
|
||||
},
|
||||
files: ["packages/**/*"],
|
||||
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: {
|
||||
"no-void": "off",
|
||||
"no-implicit-coercion": "off",
|
||||
"prefer-template": "off",
|
||||
"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": "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",
|
||||
"@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",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
argsIgnorePattern: "^_",
|
||||
varsIgnorePattern: "^_",
|
||||
caughtErrorsIgnorePattern: "^_",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
6728
web/package-lock.json
generated
6728
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
239
web/package.json
239
web/package.json
@ -8,20 +8,21 @@
|
||||
"build-locales": "wireit",
|
||||
"build-locales:build": "wireit",
|
||||
"build-proxy": "wireit",
|
||||
"build:sfe": "npm run build -w @goauthentik/web-sfe",
|
||||
"build:sfe": "wireit",
|
||||
"esbuild:watch": "node scripts/build-web.mjs --watch",
|
||||
"extract-locales": "wireit",
|
||||
"format": "wireit",
|
||||
"lint": "eslint --fix .",
|
||||
"lint-check": "eslint --max-warnings 0 .",
|
||||
"lint:imports": "knip --config scripts/knip.config.ts",
|
||||
"lint": "wireit",
|
||||
"lint:imports": "wireit",
|
||||
"lint:lockfile": "wireit",
|
||||
"lint:nightmare": "wireit",
|
||||
"lint:precommit": "wireit",
|
||||
"lint:types": "wireit",
|
||||
"lit-analyse": "wireit",
|
||||
"precommit": "wireit",
|
||||
"prettier": "prettier --cache --write -u .",
|
||||
"prettier-check": "prettier --cache --check -u .",
|
||||
"pseudolocalize": "node ./scripts/pseudolocalize.mjs",
|
||||
"prettier": "wireit",
|
||||
"prettier-check": "wireit",
|
||||
"pseudolocalize": "wireit",
|
||||
"storybook": "storybook dev -p 6006",
|
||||
"storybook:build": "wireit",
|
||||
"test": "wireit",
|
||||
@ -34,58 +35,20 @@
|
||||
"type": "module",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
"./paths": "./paths.js",
|
||||
"./scripts/*": "./scripts/*.mjs",
|
||||
"./elements/*": "./src/elements/*",
|
||||
"./common/*": "./src/common/*",
|
||||
"./components/*": "./src/components/*",
|
||||
"./flow/*": "./src/flow/*",
|
||||
"./locales/*": "./src/locales/*",
|
||||
"./user/*": "./src/user/*",
|
||||
"./admin/*": "./src/admin/*",
|
||||
"./*/browser": {
|
||||
"types": "./out/*/browser.d.ts",
|
||||
"import": "./*/browser.js"
|
||||
},
|
||||
"./*/node": {
|
||||
"types": "./out/*/node.d.ts",
|
||||
"import": "./*/node.js"
|
||||
},
|
||||
"./*": {
|
||||
"types": "./out/*/index.d.ts",
|
||||
"import": "./*/index.js"
|
||||
}
|
||||
},
|
||||
"imports": {
|
||||
"#common/*.css": "./src/common/*.css",
|
||||
"#common/*": "./src/common/*.js",
|
||||
"#elements/*.css": "./src/elements/*.css",
|
||||
"#elements/*": "./src/elements/*.js",
|
||||
"#components/*.css": "./src/components/*.css",
|
||||
"#components/*": "./src/components/*.js",
|
||||
"#user/*.css": "./src/user/*.css",
|
||||
"#user/*": "./src/user/*.js",
|
||||
"#admin/*.css": "./src/admin/*.css",
|
||||
"#admin/*": "./src/admin/*.js",
|
||||
"#flow/*.css": "./src/flow/*.css",
|
||||
"#flow/*": "./src/flow/*.js",
|
||||
"#locales/*": "./src/locales/*.js",
|
||||
"#stories/*": "./src/stories/*.js",
|
||||
"#*/browser": {
|
||||
"types": "./out/*/browser.d.ts",
|
||||
"import": "./*/browser.js"
|
||||
},
|
||||
"#*/node": {
|
||||
"types": "./out/*/node.d.ts",
|
||||
"import": "./*/node.js"
|
||||
},
|
||||
"#*": {
|
||||
"types": "./out/*/index.d.ts",
|
||||
"import": "./*/index.js"
|
||||
}
|
||||
"./admin/*": "./src/admin/*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/lang-css": "^6.3.1",
|
||||
"@codemirror/lang-html": "^6.4.9",
|
||||
"@codemirror/lang-javascript": "^6.2.4",
|
||||
"@codemirror/lang-javascript": "^6.2.2",
|
||||
"@codemirror/lang-python": "^6.1.6",
|
||||
"@codemirror/lang-xml": "^6.1.0",
|
||||
"@codemirror/legacy-modes": "^6.4.1",
|
||||
@ -93,29 +56,29 @@
|
||||
"@floating-ui/dom": "^1.6.11",
|
||||
"@formatjs/intl-listformat": "^7.5.7",
|
||||
"@fortawesome/fontawesome-free": "^6.6.0",
|
||||
"@goauthentik/api": "^2025.4.1-1747687715",
|
||||
"@goauthentik/api": "^2025.4.1-1747332783",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@lit/localize": "^0.12.2",
|
||||
"@lit/reactive-element": "^2.0.4",
|
||||
"@lit/task": "^1.0.2",
|
||||
"@lit/task": "^1.0.1",
|
||||
"@mdx-js/mdx": "^3.1.0",
|
||||
"@open-wc/lit-helpers": "^0.7.0",
|
||||
"@patternfly/elements": "^4.1.0",
|
||||
"@patternfly/elements": "^4.0.2",
|
||||
"@patternfly/patternfly": "^4.224.2",
|
||||
"@sentry/browser": "^9.22.0",
|
||||
"@spotlightjs/spotlight": "^2.13.3",
|
||||
"@sentry/browser": "^8.32.0",
|
||||
"@spotlightjs/spotlight": "^2.4.2",
|
||||
"@webcomponents/webcomponentsjs": "^2.8.0",
|
||||
"base64-js": "^1.5.1",
|
||||
"change-case": "^5.4.4",
|
||||
"chart.js": "^4.4.9",
|
||||
"chart.js": "^4.4.4",
|
||||
"chartjs-adapter-date-fns": "^3.0.0",
|
||||
"codemirror": "^6.0.1",
|
||||
"construct-style-sheets-polyfill": "^3.1.0",
|
||||
"core-js": "^3.38.1",
|
||||
"country-flag-icons": "^1.5.19",
|
||||
"country-flag-icons": "^1.5.13",
|
||||
"date-fns": "^4.1.0",
|
||||
"deepmerge-ts": "^7.1.5",
|
||||
"dompurify": "^3.2.6",
|
||||
"dompurify": "^3.2.4",
|
||||
"fuse.js": "^7.0.0",
|
||||
"guacamole-common-js": "^1.5.0",
|
||||
"hastscript": "^9.0.1",
|
||||
@ -123,8 +86,8 @@
|
||||
"md-front-matter": "^1.0.4",
|
||||
"mermaid": "^11.4.1",
|
||||
"rapidoc": "^9.3.8",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"rehype-highlight": "^7.0.2",
|
||||
"rehype-mermaid": "^3.0.0",
|
||||
"rehype-parse": "^9.0.1",
|
||||
@ -138,49 +101,49 @@
|
||||
"ts-pattern": "^5.4.0",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"webcomponent-qr-code": "^1.2.0",
|
||||
"yaml": "^2.8.0"
|
||||
"yaml": "^2.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.27.0",
|
||||
"@goauthentik/core": "^1.0.0",
|
||||
"@eslint/js": "^9.11.1",
|
||||
"@goauthentik/esbuild-plugin-live-reload": "^1.0.4",
|
||||
"@goauthentik/eslint-config": "^1.0.4",
|
||||
"@goauthentik/monorepo": "^1.0.0",
|
||||
"@goauthentik/prettier-config": "^1.0.4",
|
||||
"@goauthentik/tsconfig": "^1.0.4",
|
||||
"@hcaptcha/types": "^1.0.4",
|
||||
"@lit/localize-tools": "^0.8.0",
|
||||
"@storybook/addon-essentials": "^8.6.14",
|
||||
"@storybook/addon-links": "^8.6.14",
|
||||
"@rollup/plugin-replace": "^6.0.1",
|
||||
"@storybook/addon-essentials": "^8.6.12",
|
||||
"@storybook/addon-links": "^8.6.12",
|
||||
"@storybook/blocks": "^8.6.12",
|
||||
"@storybook/experimental-addon-test": "^8.6.14",
|
||||
"@storybook/manager-api": "^8.6.14",
|
||||
"@storybook/test": "^8.6.14",
|
||||
"@storybook/web-components": "^8.6.14",
|
||||
"@storybook/web-components-vite": "^8.6.14",
|
||||
"@storybook/experimental-addon-test": "^8.6.12",
|
||||
"@storybook/manager-api": "^8.6.12",
|
||||
"@storybook/test": "^8.6.12",
|
||||
"@storybook/web-components": "^8.6.12",
|
||||
"@storybook/web-components-vite": "^8.6.12",
|
||||
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
||||
"@types/chart.js": "^2.9.41",
|
||||
"@types/codemirror": "^5.60.15",
|
||||
"@types/dompurify": "^3.0.5",
|
||||
"@types/eslint__js": "^8.42.3",
|
||||
"@types/grecaptcha": "^3.0.9",
|
||||
"@types/guacamole-common-js": "^1.5.3",
|
||||
"@types/guacamole-common-js": "^1.5.2",
|
||||
"@types/mocha": "^10.0.8",
|
||||
"@types/node": "^22.15.21",
|
||||
"@types/react": "^19.1.5",
|
||||
"@types/react-dom": "^19.1.5",
|
||||
"@types/node": "^22.7.4",
|
||||
"@types/react": "^18.3.13",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.8.0",
|
||||
"@typescript-eslint/parser": "^8.8.0",
|
||||
"@wdio/browser-runner": "9.4",
|
||||
"@wdio/cli": "9.4",
|
||||
"@wdio/spec-reporter": "^9.1.2",
|
||||
"@web/test-runner": "^0.20.2",
|
||||
"chromedriver": "^136.0.3",
|
||||
"esbuild": "^0.25.4",
|
||||
"chromedriver": "^131.0.1",
|
||||
"esbuild": "^0.25.0",
|
||||
"esbuild-plugin-copy": "^2.1.1",
|
||||
"esbuild-plugin-polyfill-node": "^0.3.0",
|
||||
"esbuild-plugins-node-modules-polyfill": "^1.7.0",
|
||||
"eslint": "^9.11.1",
|
||||
"eslint-plugin-lit": "^2.1.1",
|
||||
"eslint-plugin-wc": "^3.0.1",
|
||||
"eslint-plugin-lit": "^1.15.0",
|
||||
"eslint-plugin-wc": "^2.1.1",
|
||||
"github-slugger": "^2.0.0",
|
||||
"globals": "^15.10.0",
|
||||
"knip": "^5.30.6",
|
||||
@ -188,23 +151,23 @@
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.3.3",
|
||||
"pseudolocale": "^2.1.0",
|
||||
"rollup-plugin-postcss-lit": "^2.2.0",
|
||||
"storybook": "^8.6.14",
|
||||
"rollup-plugin-postcss-lit": "^2.1.0",
|
||||
"storybook": "^8.6.12",
|
||||
"storybook-addon-mock": "^5.0.0",
|
||||
"turnstile-types": "^1.2.3",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.32.1",
|
||||
"typescript": "^5.6.2",
|
||||
"typescript-eslint": "^8.8.0",
|
||||
"vite-plugin-lit-css": "^2.0.0",
|
||||
"vite-tsconfig-paths": "^5.0.1",
|
||||
"wireit": "^0.14.12"
|
||||
"wireit": "^0.14.9"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/darwin-arm64": "^0.25.4",
|
||||
"@esbuild/linux-arm64": "^0.25.4",
|
||||
"@esbuild/linux-x64": "^0.25.4",
|
||||
"@rollup/rollup-darwin-arm64": "^4.41.0",
|
||||
"@rollup/rollup-linux-arm64-gnu": "^4.41.0",
|
||||
"@rollup/rollup-linux-x64-gnu": "^4.41.0"
|
||||
"@esbuild/darwin-arm64": "^0.24.0",
|
||||
"@esbuild/linux-amd64": "^0.18.11",
|
||||
"@esbuild/linux-arm64": "^0.24.0",
|
||||
"@rollup/rollup-darwin-arm64": "4.23.0",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.23.0",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.23.0"
|
||||
},
|
||||
"wireit": {
|
||||
"build": {
|
||||
@ -239,7 +202,7 @@
|
||||
],
|
||||
"dependencies": [
|
||||
"build-locales",
|
||||
"build:sfe"
|
||||
"./packages/sfe:build"
|
||||
],
|
||||
"env": {
|
||||
"NODE_RUNNER": {
|
||||
@ -248,6 +211,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"build:sfe": {
|
||||
"dependencies": [
|
||||
"./packages/sfe:build"
|
||||
],
|
||||
"files": [
|
||||
"./packages/sfe/**/*.ts"
|
||||
]
|
||||
},
|
||||
"build-proxy": {
|
||||
"command": "node scripts/build-web.mjs --proxy",
|
||||
"dependencies": [
|
||||
@ -273,17 +244,31 @@
|
||||
"extract-locales": {
|
||||
"command": "lit-localize extract"
|
||||
},
|
||||
"format": {
|
||||
"command": "prettier --write ."
|
||||
},
|
||||
"format:packages": {
|
||||
"dependencies": [
|
||||
"./packages/sfe:prettier"
|
||||
]
|
||||
},
|
||||
"lint": {
|
||||
"command": "eslint --max-warnings 0 --fix",
|
||||
"env": {
|
||||
"NODE_OPTIONS": "--max_old_space_size=65536"
|
||||
}
|
||||
},
|
||||
"lint:components": {
|
||||
"command": "lit-analyzer src"
|
||||
},
|
||||
"lint:imports": {
|
||||
"command": "knip --config scripts/knip.config.ts"
|
||||
},
|
||||
"lint:types:tests": {
|
||||
"command": "tsc --noEmit -p ./tests"
|
||||
},
|
||||
"lint:types": {
|
||||
"command": "tsc -p .",
|
||||
"env": {
|
||||
"NODE_OPTIONS": "--max_old_space_size=8192"
|
||||
},
|
||||
"command": "NODE_OPTIONS=\"--max-old-space-size=3000\" tsc -b .",
|
||||
"dependencies": [
|
||||
"build-locales",
|
||||
"lint:types:tests"
|
||||
@ -294,18 +279,58 @@
|
||||
"shell": true,
|
||||
"command": "sh ./scripts/lint-lockfile.sh package-lock.json"
|
||||
},
|
||||
"lint:lockfiles": {
|
||||
"dependencies": [
|
||||
"./packages/sfe:lint:lockfile"
|
||||
]
|
||||
},
|
||||
"lint:nightmare": {
|
||||
"command": "${NODE_RUNNER} ./scripts/eslint.mjs --nightmare",
|
||||
"env": {
|
||||
"NODE_RUNNER": {
|
||||
"external": true,
|
||||
"default": "node"
|
||||
}
|
||||
}
|
||||
},
|
||||
"lint:precommit": {
|
||||
"command": "${NODE_RUNNER} ./scripts/eslint.mjs --precommit",
|
||||
"env": {
|
||||
"NODE_RUNNER": {
|
||||
"external": true,
|
||||
"default": "node"
|
||||
}
|
||||
}
|
||||
},
|
||||
"lint:spelling": {
|
||||
"command": "node scripts/check-spelling.mjs"
|
||||
},
|
||||
"lit-analyse": {
|
||||
"command": "lit-analyzer src"
|
||||
},
|
||||
"precommit": {
|
||||
"command": "prettier --write .",
|
||||
"dependencies": [
|
||||
"lint",
|
||||
"lint:types",
|
||||
"lint:components",
|
||||
"lint:lockfile"
|
||||
"lint:spelling",
|
||||
"lint:lockfile",
|
||||
"lint:lockfiles",
|
||||
"lint:precommit",
|
||||
"format:packages"
|
||||
]
|
||||
},
|
||||
"prettier": {
|
||||
"dependencies": [
|
||||
"format"
|
||||
]
|
||||
},
|
||||
"prettier-check": {
|
||||
"command": "prettier --check ."
|
||||
},
|
||||
"pseudolocalize": {
|
||||
"command": "node scripts/pseudolocalize.mjs"
|
||||
},
|
||||
"storybook:build": {
|
||||
"command": "storybook build",
|
||||
"env": {
|
||||
@ -348,12 +373,8 @@
|
||||
}
|
||||
},
|
||||
"tsc": {
|
||||
"command": "tsc -p .",
|
||||
"env": {
|
||||
"NODE_OPTIONS": "--max_old_space_size=8192"
|
||||
},
|
||||
"dependencies": [
|
||||
"build-locales"
|
||||
"lint:types"
|
||||
]
|
||||
}
|
||||
},
|
||||
@ -361,22 +382,16 @@
|
||||
"node": ">=20"
|
||||
},
|
||||
"workspaces": [
|
||||
".",
|
||||
"./packages/*"
|
||||
],
|
||||
"prettier": "@goauthentik/prettier-config",
|
||||
"overrides": {
|
||||
"@storybook/addon-docs": {
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0"
|
||||
},
|
||||
"chromedriver": {
|
||||
"axios": "^1.8.4"
|
||||
},
|
||||
"rapidoc": {
|
||||
"@apitools/openapi-parser@": "0.0.37"
|
||||
},
|
||||
"storybook-addon-mock": {
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0"
|
||||
"chromedriver": {
|
||||
"axios": "^1.8.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,66 +0,0 @@
|
||||
/**
|
||||
* @file Utility functions for working with environment variables.
|
||||
*/
|
||||
/// <reference types="../types/node.js" />
|
||||
|
||||
//#region Constants
|
||||
|
||||
/**
|
||||
* The current Node.js environment, defaulting to "development" when not set.
|
||||
*
|
||||
* Note, this should only be used during the build process.
|
||||
*
|
||||
* If you need to check the environment at runtime, use `process.env.NODE_ENV` to
|
||||
* ensure that module tree-shaking works correctly.
|
||||
*
|
||||
* @category Environment
|
||||
* @runtime node
|
||||
*/
|
||||
export const NodeEnvironment = process.env.NODE_ENV || "development";
|
||||
|
||||
/**
|
||||
* A source environment variable, which can be a string, number, boolean, null, or undefined.
|
||||
* @typedef {string | number | boolean | null | undefined} EnvironmentVariable
|
||||
*/
|
||||
|
||||
/**
|
||||
* A type helper for serializing environment variables.
|
||||
*
|
||||
* @category Environment
|
||||
* @template {EnvironmentVariable} T
|
||||
* @typedef {T extends string ? `"${T}"` : T} JSONify
|
||||
*/
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Utilities
|
||||
|
||||
/**
|
||||
* Given an object of environment variables, serializes them into a mapping of
|
||||
* environment variable names to their respective runtime constants.
|
||||
*
|
||||
* This is useful for defining environment variables while bundling with ESBuild, Vite, etc.
|
||||
*
|
||||
* @category Environment
|
||||
* @runtime node
|
||||
*
|
||||
* @template {Record<string, EnvironmentVariable>} EnvRecord
|
||||
* @template {string} [Prefix='import.meta.env.']
|
||||
*
|
||||
* @param {EnvRecord} input
|
||||
* @param {Prefix} [prefix='import.meta.env.']
|
||||
*
|
||||
* @returns {{[K in keyof EnvRecord as `${Prefix}${K}`]: JSONify<EnvRecord[K]>}}
|
||||
*/
|
||||
export function serializeEnvironmentVars(
|
||||
input,
|
||||
prefix = /** @type {Prefix} */ ("import.meta.env."),
|
||||
) {
|
||||
const env = Object.fromEntries(
|
||||
Object.entries(input).map(([key, value]) => [prefix + key, JSON.stringify(value ?? "")]),
|
||||
);
|
||||
|
||||
return /** @type {any} */ (env);
|
||||
}
|
||||
|
||||
//#endregion
|
@ -1,12 +0,0 @@
|
||||
/**
|
||||
* @file Dummy entry point for the `core` package.
|
||||
*
|
||||
* This exists to make TypeScript's module resolution more predictable.
|
||||
*
|
||||
* @internal
|
||||
* @ignore
|
||||
*/
|
||||
|
||||
export {};
|
||||
|
||||
export default {};
|
@ -1,59 +0,0 @@
|
||||
{
|
||||
"name": "@goauthentik/core",
|
||||
"version": "1.0.0",
|
||||
"description": "Core functionality for the authentik monorepo",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "tsc -p .",
|
||||
"prettier": "prettier --write .",
|
||||
"prettier-check": "prettier --check ."
|
||||
},
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
"./*/browser": {
|
||||
"types": "./out/*/browser.d.ts",
|
||||
"import": "./*/browser.js"
|
||||
},
|
||||
"./*/node": {
|
||||
"types": "./out/*/node.d.ts",
|
||||
"import": "./*/node.js"
|
||||
},
|
||||
"./*": {
|
||||
"types": "./out/*/index.d.ts",
|
||||
"import": "./*/index.js"
|
||||
},
|
||||
".": {
|
||||
"import": "./index.js",
|
||||
"types": "./out/index.d.ts"
|
||||
}
|
||||
},
|
||||
"imports": {
|
||||
"#*/browser": {
|
||||
"types": "./out/*/browser.d.ts",
|
||||
"import": "./*/browser.js"
|
||||
},
|
||||
"#*/node": {
|
||||
"types": "./out/*/node.d.ts",
|
||||
"import": "./*/node.js"
|
||||
},
|
||||
"#*": {
|
||||
"types": "./out/*/index.d.ts",
|
||||
"import": "./*/index.js"
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@goauthentik/prettier-config": "^1.0.4",
|
||||
"@goauthentik/tsconfig": "^1.0.4",
|
||||
"@types/node": "^22.15.21",
|
||||
"prettier": "^3.3.3",
|
||||
"typescript": "^5.6.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.11"
|
||||
},
|
||||
"types": "./out/index.d.ts",
|
||||
"prettier": "@goauthentik/prettier-config"
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user