Compare commits

..

2 Commits

Author SHA1 Message Date
Connor Peshek
2f107b17bd add notes from meeting 2026-05-04 12:32:37 -05:00
Connor Peshek
b5439407b7 begin release 2026-04-30 19:08:50 -05:00
226 changed files with 3410 additions and 8303 deletions

View File

@@ -64,7 +64,7 @@ runs:
rustflags: ""
- name: Setup rust dependencies
if: ${{ contains(inputs.dependencies, 'rust') }}
uses: taiki-e/install-action@db5fb34fa772531a3ece57ca434f579eb334e0fb # v2
uses: taiki-e/install-action@481c34c1cf3a84c68b5e46f4eccfc82af798415a # v2
with:
tool: cargo-deny cargo-machete cargo-llvm-cov nextest
- name: Setup node (web)

View File

@@ -90,7 +90,7 @@ jobs:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: int128/docker-manifest-create-action@fa55f72001a6c74b0f4997dca65c70d334905180 # v2
- uses: int128/docker-manifest-create-action@7df7f9e221d927eaadf87db231ddf728047308a4 # v2
id: build
with:
tags: ${{ matrix.tag }}

View File

@@ -282,18 +282,10 @@ jobs:
fail-fast: false
matrix:
job:
- name: oidc_basic
glob: tests/openid_conformance/test_oidc_basic.py
- name: oidc_implicit
glob: tests/openid_conformance/test_oidc_implicit.py
- name: oidc_rp-initiated
glob: tests/openid_conformance/test_oidc_rp_initiated.py
- name: oidc_frontchannel
glob: tests/openid_conformance/test_oidc_frontchannel.py
- name: oidc_backchannel
glob: tests/openid_conformance/test_oidc_backchannel.py
- name: ssf_transmitter
glob: tests/openid_conformance/test_ssf_transmitter.py
- name: basic
glob: tests/openid_conformance/test_basic.py
- name: implicit
glob: tests/openid_conformance/test_implicit.py
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- name: Setup authentik env

5
.gitignore vendored
View File

@@ -229,11 +229,6 @@ source_docs/
### Golang ###
/vendor/
server
proxy
ldap
rac
radius
### Docker ###
tests/openid_conformance/exports/*.zip

158
Cargo.lock generated
View File

@@ -17,6 +17,18 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "ahash"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "aho-corasick"
version = "1.1.4"
@@ -191,7 +203,6 @@ dependencies = [
"tokio",
"tracing",
"uuid",
"which",
]
[[package]]
@@ -1003,17 +1014,6 @@ dependencies = [
"pin-project-lite",
]
[[package]]
name = "evmap"
version = "11.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8874945f036109c72242964c1174cf99434e30cfa45bf45fedc983f50046f8"
dependencies = [
"hashbag",
"left-right",
"smallvec",
]
[[package]]
name = "eyre"
version = "0.6.12"
@@ -1230,21 +1230,6 @@ dependencies = [
"slab",
]
[[package]]
name = "generator"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52f04ae4152da20c76fe800fa48659201d5cf627c5149ca0b707b69d7eef6cf9"
dependencies = [
"cc",
"cfg-if",
"libc",
"log",
"rustversion",
"windows-link",
"windows-result",
]
[[package]]
name = "generic-array"
version = "0.14.7"
@@ -1326,12 +1311,6 @@ dependencies = [
"tracing",
]
[[package]]
name = "hashbag"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7040a10f52cba493ddb09926e15d10a9d8a28043708a405931fe4c6f19fac064"
[[package]]
name = "hashbrown"
version = "0.15.5"
@@ -1889,17 +1868,6 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]]
name = "left-right"
version = "0.11.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f0c21e4c8ff95f487fb34e6f9182875f42c84cef966d29216bf115d9bba835a"
dependencies = [
"crossbeam-utils",
"loom",
"slab",
]
[[package]]
name = "libc"
version = "0.2.183"
@@ -1971,19 +1939,6 @@ version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "loom"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca"
dependencies = [
"cfg-if",
"generator",
"scoped-tls",
"tracing",
"tracing-subscriber",
]
[[package]]
name = "lru-slab"
version = "0.1.2"
@@ -2023,22 +1978,21 @@ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
[[package]]
name = "metrics"
version = "0.24.5"
version = "0.24.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff56c2e7dce6bd462e3b8919986a617027481b1dcc703175b58cf9dd98a2f071"
checksum = "5d5312e9ba3771cfa961b585728215e3d972c950a3eed9252aa093d6301277e8"
dependencies = [
"ahash",
"portable-atomic",
"rapidhash",
]
[[package]]
name = "metrics-exporter-prometheus"
version = "0.18.3"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1db0d8f1fc9e62caebd0319e11eaec5822b0186c171568f0480b46a0137f9108"
checksum = "3589659543c04c7dc5526ec858591015b87cd8746583b51b48ef4353f99dbcda"
dependencies = [
"base64 0.22.1",
"evmap",
"indexmap",
"metrics",
"metrics-util",
@@ -2057,7 +2011,7 @@ dependencies = [
"hashbrown 0.16.1",
"metrics",
"quanta",
"rand 0.9.4",
"rand 0.9.2",
"rand_xoshiro",
"sketches-ddsketch",
]
@@ -2744,7 +2698,7 @@ dependencies = [
"bytes",
"getrandom 0.3.4",
"lru-slab",
"rand 0.9.4",
"rand 0.9.2",
"ring",
"rustc-hash",
"rustls",
@@ -2804,9 +2758,9 @@ dependencies = [
[[package]]
name = "rand"
version = "0.9.4"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea"
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [
"rand_chacha 0.9.0",
"rand_core 0.9.5",
@@ -2859,15 +2813,6 @@ dependencies = [
"rand_core 0.9.5",
]
[[package]]
name = "rapidhash"
version = "4.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e48930979c155e2f33aa36ab3119b5ee81332beb6482199a8ecd6029b80b59"
dependencies = [
"rustversion",
]
[[package]]
name = "raw-cpuid"
version = "11.6.0"
@@ -2926,9 +2871,9 @@ checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
[[package]]
name = "reqwest"
version = "0.13.3"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62e0021ea2c22aed41653bc7e1419abb2c97e038ff2c33d0e1309e49a97deec0"
checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801"
dependencies = [
"base64 0.22.1",
"bytes",
@@ -3160,12 +3105,6 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "scoped-tls"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
[[package]]
name = "scopeguard"
version = "1.2.0"
@@ -3203,9 +3142,9 @@ checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
[[package]]
name = "sentry"
version = "0.48.0"
version = "0.47.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8ac94aab850a23d7507307cc505332ed2bafd36c65930dfc5c43610f9e9b477"
checksum = "eb25f439f97d26fea01d717fa626167ceffcd981addaa670001e70505b72acbb"
dependencies = [
"cfg_aliases",
"httpdate",
@@ -3224,9 +3163,9 @@ dependencies = [
[[package]]
name = "sentry-backtrace"
version = "0.48.1"
version = "0.47.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc84c325ace9ca2388e510fe7d6672b5d60cd8b3bd0eb4bb4ee8314c323cd686"
checksum = "46a8c2c1bd5c1f735e84f28b48e7d72efcaafc362b7541bc8253e60e8fcdffc6"
dependencies = [
"backtrace",
"regex",
@@ -3235,9 +3174,9 @@ dependencies = [
[[package]]
name = "sentry-contexts"
version = "0.48.1"
version = "0.47.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "896c1ab62dbfe1746fb262bbf72e6feb2fb9dfb2c14709077bf71beb532e44b2"
checksum = "9b88a90baa654d7f0e1f4b667f6b434293d9f72c71bef16b197c76af5b7d5803"
dependencies = [
"hostname",
"libc",
@@ -3249,11 +3188,11 @@ dependencies = [
[[package]]
name = "sentry-core"
version = "0.48.1"
version = "0.47.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5f5abf20c42cb1593ec1638976e2647da55f79bccac956444c1707b6cce259a"
checksum = "0ac170a5bba8bec6e3339c90432569d89641fa7a3d3e4f44987d24f0762e6adf"
dependencies = [
"rand 0.9.4",
"rand 0.9.2",
"sentry-types",
"serde",
"serde_json",
@@ -3262,9 +3201,9 @@ dependencies = [
[[package]]
name = "sentry-debug-images"
version = "0.48.1"
version = "0.47.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b88bbe6a760d5724bb40689827e82e8db1e275947df2c59abe171bfc30bb671"
checksum = "dd9646a972b57896d4a92ed200cf76139f8e30b3cfd03b6662ae59926d26633c"
dependencies = [
"findshlibs",
"sentry-core",
@@ -3272,9 +3211,9 @@ dependencies = [
[[package]]
name = "sentry-panic"
version = "0.48.1"
version = "0.47.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0260dcb52562b6a79ae7702312a26dba94b79fb5baee7301087529e5ca4e872e"
checksum = "6127d3d304ba5ce0409401e85aae538e303a569f8dbb031bf64f9ba0f7174346"
dependencies = [
"sentry-backtrace",
"sentry-core",
@@ -3282,9 +3221,9 @@ dependencies = [
[[package]]
name = "sentry-tower"
version = "0.48.1"
version = "0.47.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d669616d5d5279b5712febfc80c343acc3695e499de0d101ed70fceacadf37f2"
checksum = "61c5253dc4ad89863a866b93aeaaac1c9d60f2f774663b5024afe2d57e0a101c"
dependencies = [
"sentry-core",
"tower-layer",
@@ -3293,9 +3232,9 @@ dependencies = [
[[package]]
name = "sentry-tracing"
version = "0.48.1"
version = "0.47.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1c035f3a0a8671ae1a231c5b457abb68b71acba2bf3054dab2a09a9d4ea487e"
checksum = "27701acc51e68db5281802b709010395bfcbcb128b1d0a4e5873680d3b47ff0c"
dependencies = [
"bitflags 2.11.0",
"sentry-backtrace",
@@ -3306,13 +3245,13 @@ dependencies = [
[[package]]
name = "sentry-types"
version = "0.48.1"
version = "0.47.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82d8e81058ec155992191f61c7b29bfa7b2cf12012131e7cdc0678020898a7c9"
checksum = "56780cb5597d676bf22e6c11d1f062eb4def46390ea3bfb047bcbcf7dfd19bdb"
dependencies = [
"debugid",
"hex",
"rand 0.9.4",
"rand 0.9.2",
"serde",
"serde_json",
"thiserror 2.0.18",
@@ -4214,7 +4153,7 @@ dependencies = [
"http",
"httparse",
"log",
"rand 0.9.4",
"rand 0.9.2",
"sha1",
"thiserror 2.0.18",
]
@@ -4576,15 +4515,6 @@ dependencies = [
"rustls-pki-types",
]
[[package]]
name = "which"
version = "8.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81995fafaaaf6ae47a7d0cc83c67caf92aeb7e5331650ae6ff856f7c0c60c459"
dependencies = [
"libc",
]
[[package]]
name = "whoami"
version = "1.6.1"

View File

@@ -43,15 +43,15 @@ hyper-unix-socket = "= 0.6.1"
hyper-util = "= 0.1.20"
ipnet = { version = "= 2.12.0", features = ["serde"] }
json-subscriber = "= 0.2.8"
metrics = "= 0.24.5"
metrics-exporter-prometheus = { version = "= 0.18.3", default-features = false }
metrics = "= 0.24.3"
metrics-exporter-prometheus = { version = "= 0.18.1", default-features = false }
nix = { version = "= 0.31.2", features = ["hostname", "signal"] }
notify = "= 8.2.0"
pin-project-lite = "= 0.2.17"
pyo3 = "= 0.28.3"
pyo3-build-config = "= 0.28.3"
regex = "= 1.12.3"
reqwest = { version = "= 0.13.3", features = [
reqwest = { version = "= 0.13.2", features = [
"form",
"json",
"multipart",
@@ -67,7 +67,7 @@ reqwest-middleware = { version = "= 0.5.1", features = [
"rustls",
] }
rustls = { version = "= 0.23.40", features = ["fips"] }
sentry = { version = "= 0.48.0", default-features = false, features = [
sentry = { version = "= 0.47.0", default-features = false, features = [
"backtrace",
"contexts",
"debug-images",
@@ -113,7 +113,6 @@ tracing-subscriber = { version = "= 0.3.23", features = [
] }
url = "= 2.5.8"
uuid = { version = "= 1.23.1", features = ["serde", "v4"] }
which = "= 8.0.2"
ak-axum = { package = "authentik-axum", version = "2026.5.0-rc1", path = "./packages/ak-axum" }
ak-client = { package = "authentik-client", version = "2026.5.0-rc1", path = "./packages/client-rust" }
@@ -283,7 +282,6 @@ sqlx = { workspace = true, optional = true }
tokio.workspace = true
tracing.workspace = true
uuid.workspace = true
which.workspace = true
[lints]
workspace = true

View File

@@ -109,11 +109,14 @@ i18n-extract: core-i18n-extract web-i18n-extract ## Extract strings that requir
aws-cfn:
cd lifecycle/aws && npm i && $(UV) run npm run aws-cfn
run: ## Run the main authentik server and worker processes
$(UV) run ak allinone
run-server: ## Run the main authentik server process
$(UV) run ak server
run-watch: ## Run the authentik server and worker, with auto reloading
watchexec --on-busy-update=restart --stop-signal=SIGINT --exts py,rs,go --no-meta --notify -- $(UV) run ak allinone
run-worker: ## Run the main authentik worker process
$(UV) run ak worker
run-worker-watch: ## Run the authentik worker, with auto reloading
watchexec --on-busy-update=restart --stop-signal=SIGINT --exts py,rs --no-meta --notify -- $(UV) run ak worker
core-i18n-extract:
$(UV) run ak makemessages \

View File

@@ -1,6 +1,5 @@
"""Serializer mixin for managed models"""
from json import JSONDecodeError, loads
from typing import cast
from django.conf import settings
@@ -45,7 +44,6 @@ class BlueprintUploadSerializer(PassiveSerializer):
file = FileField(required=False)
path = CharField(required=False)
context = CharField(required=False, allow_blank=True)
def validate_path(self, path: str) -> str:
"""Ensure the path (if set) specified is retrievable"""
@@ -56,18 +54,6 @@ class BlueprintUploadSerializer(PassiveSerializer):
raise ValidationError(_("Blueprint file does not exist"))
return path
def validate_context(self, context: str) -> dict:
"""Parse context as a JSON object"""
if not context:
return {}
try:
parsed = loads(context)
except JSONDecodeError as exc:
raise ValidationError(_("Context must be valid JSON")) from exc
if not isinstance(parsed, dict):
raise ValidationError(_("Context must be a JSON object"))
return parsed
class ManagedSerializer:
"""Managed Serializer"""
@@ -238,8 +224,7 @@ class BlueprintInstanceViewSet(UsedByMixin, ModelViewSet):
).retrieve_file()
else:
raise ValidationError("Either path or file must be set")
context = body.validated_data.get("context") or {}
importer = Importer.from_string(string_contents, context)
importer = Importer.from_string(string_contents)
check_blueprint_perms(importer.blueprint, request.user)

View File

@@ -1,6 +1,6 @@
"""Test blueprints v1 api"""
from json import dumps, loads
from json import loads
from tempfile import NamedTemporaryFile, mkdtemp
from django.urls import reverse
@@ -8,11 +8,7 @@ from rest_framework.test import APITestCase
from yaml import dump
from authentik.core.tests.utils import create_test_admin_user
from authentik.flows.models import Flow
from authentik.lib.config import CONFIG
from authentik.lib.generators import generate_id
from authentik.stages.invitation.models import InvitationStage
from authentik.stages.user_write.models import UserWriteStage
TMP = mkdtemp("authentik-blueprints")
@@ -84,107 +80,3 @@ class TestBlueprintsV1API(APITestCase):
res.content.decode(),
{"content": ["Failed to validate blueprint", "- Invalid blueprint version"]},
)
def test_api_import_with_context(self):
"""Test that the import endpoint applies the supplied context to the real blueprint"""
slug = f"invitation-enrollment-{generate_id()}"
flow_name = f"Invitation Enrollment {generate_id()}"
stage_name = f"invitation-stage-{generate_id()}"
user_type = "internal"
continue_without_invitation = True
res = self.client.post(
reverse("authentik_api:blueprintinstance-import-"),
data={
"path": "example/flows-invitation-enrollment-minimal.yaml",
"context": dumps(
{
"flow_slug": slug,
"flow_name": flow_name,
"stage_name": stage_name,
"continue_flow_without_invitation": continue_without_invitation,
"user_type": user_type,
}
),
},
format="multipart",
)
self.assertEqual(res.status_code, 200)
self.assertTrue(res.json()["success"])
flow = Flow.objects.get(slug=slug)
self.assertEqual(flow.name, flow_name)
self.assertEqual(flow.title, flow_name)
invitation_stage = InvitationStage.objects.get(name=stage_name)
self.assertEqual(
invitation_stage.continue_flow_without_invitation,
continue_without_invitation,
)
user_write_stage = UserWriteStage.objects.get(
name=f"invitation-enrollment-user-write-{slug}"
)
self.assertEqual(user_write_stage.user_type, user_type)
self.assertEqual(user_write_stage.user_path_template, f"users/{user_type}")
def test_api_import_blank_path(self):
"""Validator returns empty path unchanged (covers api.py:53)."""
with NamedTemporaryFile(mode="w+", suffix=".yaml") as file:
file.write(dump({"version": 1, "entries": []}))
file.flush()
file.seek(0)
res = self.client.post(
reverse("authentik_api:blueprintinstance-import-"),
data={"path": "", "file": file},
format="multipart",
)
self.assertEqual(res.status_code, 200)
def test_api_import_unknown_path(self):
"""Path not in available blueprints is rejected (covers api.py:56)."""
res = self.client.post(
reverse("authentik_api:blueprintinstance-import-"),
data={"path": "does/not/exist.yaml"},
format="multipart",
)
self.assertEqual(res.status_code, 400)
self.assertIn("Blueprint file does not exist", res.content.decode())
def test_api_import_blank_context(self):
"""Blank context is normalized to empty dict (covers api.py:62)."""
res = self.client.post(
reverse("authentik_api:blueprintinstance-import-"),
data={
"path": "example/flows-invitation-enrollment-minimal.yaml",
"context": "",
},
format="multipart",
)
self.assertEqual(res.status_code, 200)
def test_api_import_invalid_json_context(self):
"""Malformed JSON context raises ValidationError (covers api.py:65-66)."""
res = self.client.post(
reverse("authentik_api:blueprintinstance-import-"),
data={
"path": "example/flows-invitation-enrollment-minimal.yaml",
"context": "{not json",
},
format="multipart",
)
self.assertEqual(res.status_code, 400)
self.assertIn("Context must be valid JSON", res.content.decode())
def test_api_import_non_object_context(self):
"""JSON context that isn't an object is rejected (covers api.py:68)."""
res = self.client.post(
reverse("authentik_api:blueprintinstance-import-"),
data={
"path": "example/flows-invitation-enrollment-minimal.yaml",
"context": "[1, 2, 3]",
},
format="multipart",
)
self.assertEqual(res.status_code, 400)
self.assertIn("Context must be a JSON object", res.content.decode())

View File

@@ -32,19 +32,19 @@ from authentik.rbac.decorators import permission_required
class UserAgentDeviceDict(TypedDict):
"""User agent device"""
brand: str | None = None
brand: str
family: str
model: str | None = None
model: str
class UserAgentOSDict(TypedDict):
"""User agent os"""
family: str
major: str | None = None
minor: str | None = None
patch: str | None = None
patch_minor: str | None = None
major: str
minor: str
patch: str
patch_minor: str
class UserAgentBrowserDict(TypedDict):

View File

@@ -1,5 +1,6 @@
"""authentik core signals"""
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
from django.contrib.auth.signals import user_logged_in
from django.core.cache import cache
@@ -58,7 +59,7 @@ def user_logged_in_session(sender, request: HttpRequest, user: User, **_):
layer = get_channel_layer()
device_cookie = request.COOKIES.get("authentik_device")
if device_cookie:
layer.group_send_blocking(
async_to_sync(layer.group_send)(
build_device_group(device_cookie),
{"type": "event.session.authenticated"},
)

View File

@@ -1,7 +1,6 @@
# Generated by Django 5.2.12 on 2026-04-04 16:58
from django.db import migrations, models
import django.contrib.postgres.fields
class Migration(migrations.Migration):
@@ -41,109 +40,4 @@ class Migration(migrations.Migration):
]
),
),
migrations.AlterField(
model_name="stream",
name="events_requested",
field=django.contrib.postgres.fields.ArrayField(
base_field=models.TextField(
choices=[
(
"https://schemas.openid.net/secevent/caep/event-type/session-revoked",
"Caep Session Revoked",
),
(
"https://schemas.openid.net/secevent/caep/event-type/token-claims-change",
"Caep Token Claims Change",
),
(
"https://schemas.openid.net/secevent/caep/event-type/credential-change",
"Caep Credential Change",
),
(
"https://schemas.openid.net/secevent/caep/event-type/assurance-level-change",
"Caep Assurance Level Change",
),
(
"https://schemas.openid.net/secevent/caep/event-type/device-compliance-change",
"Caep Device Compliance Change",
),
(
"https://schemas.openid.net/secevent/caep/event-type/session-established",
"Caep Session Established",
),
(
"https://schemas.openid.net/secevent/caep/event-type/session-presented",
"Caep Session Presented",
),
(
"https://schemas.openid.net/secevent/caep/event-type/risk-level-change",
"Caep Risk Level Change",
),
(
"https://schemas.openid.net/secevent/ssf/event-type/verification",
"Set Verification",
),
]
),
default=list,
size=None,
),
),
migrations.AlterField(
model_name="stream",
name="status",
field=models.TextField(
choices=[
("enabled", "Enabled"),
("paused", "Paused"),
("disabled", "Disabled"),
("disabled_deleted", "Disabled Deleted"),
],
default="enabled",
),
),
migrations.AlterField(
model_name="streamevent",
name="type",
field=models.TextField(
choices=[
(
"https://schemas.openid.net/secevent/caep/event-type/session-revoked",
"Caep Session Revoked",
),
(
"https://schemas.openid.net/secevent/caep/event-type/token-claims-change",
"Caep Token Claims Change",
),
(
"https://schemas.openid.net/secevent/caep/event-type/credential-change",
"Caep Credential Change",
),
(
"https://schemas.openid.net/secevent/caep/event-type/assurance-level-change",
"Caep Assurance Level Change",
),
(
"https://schemas.openid.net/secevent/caep/event-type/device-compliance-change",
"Caep Device Compliance Change",
),
(
"https://schemas.openid.net/secevent/caep/event-type/session-established",
"Caep Session Established",
),
(
"https://schemas.openid.net/secevent/caep/event-type/session-presented",
"Caep Session Presented",
),
(
"https://schemas.openid.net/secevent/caep/event-type/risk-level-change",
"Caep Risk Level Change",
),
(
"https://schemas.openid.net/secevent/ssf/event-type/verification",
"Set Verification",
),
]
),
),
]

View File

@@ -24,31 +24,8 @@ class EventTypes(models.TextChoices):
"""SSF Event types supported by authentik"""
CAEP_SESSION_REVOKED = "https://schemas.openid.net/secevent/caep/event-type/session-revoked"
"""https://openid.net/specs/openid-caep-1_0-final.html#section-3.1"""
CAEP_TOKEN_CLAIMS_CHANGE = (
"https://schemas.openid.net/secevent/caep/event-type/token-claims-change"
)
"""https://openid.net/specs/openid-caep-1_0-final.html#section-3.2"""
CAEP_CREDENTIAL_CHANGE = "https://schemas.openid.net/secevent/caep/event-type/credential-change"
"""https://openid.net/specs/openid-caep-1_0-final.html#section-3.3"""
CAEP_ASSURANCE_LEVEL_CHANGE = (
"https://schemas.openid.net/secevent/caep/event-type/assurance-level-change"
)
"""https://openid.net/specs/openid-caep-1_0-final.html#section-3.4"""
CAEP_DEVICE_COMPLIANCE_CHANGE = (
"https://schemas.openid.net/secevent/caep/event-type/device-compliance-change"
)
"""https://openid.net/specs/openid-caep-1_0-final.html#section-3.5"""
CAEP_SESSION_ESTABLISHED = (
"https://schemas.openid.net/secevent/caep/event-type/session-established"
)
"""https://openid.net/specs/openid-caep-1_0-final.html#section-3.6"""
CAEP_SESSION_PRESENTED = "https://schemas.openid.net/secevent/caep/event-type/session-presented"
"""https://openid.net/specs/openid-caep-1_0-final.html#section-3.7"""
CAEP_RISK_LEVEL_CHANGE = "https://schemas.openid.net/secevent/caep/event-type/risk-level-change"
"""https://openid.net/specs/openid-caep-1_0-final.html#section-3.8"""
SET_VERIFICATION = "https://schemas.openid.net/secevent/ssf/event-type/verification"
"""https://openid.net/specs/openid-sharedsignals-framework-1_0.html#section-8.1.4.1"""
class DeliveryMethods(models.TextChoices):
@@ -69,12 +46,10 @@ class SSFEventStatus(models.TextChoices):
class StreamStatus(models.TextChoices):
"""SSF Stream status"""
ENABLED = "enabled"
PAUSED = "paused"
DISABLED = "disabled"
DISABLED_DELETED = "disabled_deleted"
class SSFProvider(TasksModel, BackchannelProvider):

View File

@@ -108,13 +108,13 @@ def send_ssf_event(stream_uuid: UUID, event_data: dict[str, Any]):
event.save()
self.info("Event successfully sent", status=response.status_code)
# Cleanup, if we were the last pending message for this stream and it has been deleted
# (status=StreamStatus.DISABLED_DELETED), then we can delete the stream
# (status=StreamStatus.DISABLED), then we can delete the stream
if (
not StreamEvent.objects.filter(
stream=stream,
status__in=[SSFEventStatus.PENDING_FAILED, SSFEventStatus.PENDING_NEW],
).exists()
and stream.status == StreamStatus.DISABLED_DELETED
and stream.status == StreamStatus.DISABLED
):
LOGGER.info(
"Deleting inactive stream as all pending messages were sent.", stream=stream

View File

@@ -62,7 +62,7 @@ class TestSSFAuth(APITestCase):
self.assertEqual(event.status, SSFEventStatus.PENDING_FAILED)
self.assertEqual(
event.payload["events"],
{"https://schemas.openid.net/secevent/ssf/event-type/verification": {}},
{"https://schemas.openid.net/secevent/ssf/event-type/verification": {"state": None}},
)
def test_stream_add_oidc(self):
@@ -115,7 +115,7 @@ class TestSSFAuth(APITestCase):
self.assertEqual(event.status, SSFEventStatus.PENDING_FAILED)
self.assertEqual(
event.payload["events"],
{"https://schemas.openid.net/secevent/ssf/event-type/verification": {}},
{"https://schemas.openid.net/secevent/ssf/event-type/verification": {"state": None}},
)
def test_token_invalid(self):

View File

@@ -54,7 +54,7 @@ class TestStream(APITestCase):
self.assertEqual(event.status, SSFEventStatus.PENDING_FAILED)
self.assertEqual(
event.payload["events"],
{"https://schemas.openid.net/secevent/ssf/event-type/verification": {}},
{"https://schemas.openid.net/secevent/ssf/event-type/verification": {"state": None}},
)
def test_stream_add_poll(self):
@@ -96,7 +96,7 @@ class TestStream(APITestCase):
)
self.assertEqual(res.status_code, 204)
stream.refresh_from_db()
self.assertEqual(stream.status, StreamStatus.DISABLED_DELETED)
self.assertEqual(stream.status, StreamStatus.DISABLED)
def test_stream_get(self):
"""get stream"""
@@ -225,26 +225,3 @@ class TestStream(APITestCase):
HTTP_AUTHORIZATION=f"Bearer {self.provider.token.key}",
)
self.assertEqual(res.status_code, 404)
def test_stream_status_update(self):
stream = Stream.objects.create(provider=self.provider)
res = self.client.post(
reverse(
"authentik_providers_ssf:stream-status",
kwargs={"application_slug": self.application.slug},
),
data={
"stream_id": str(stream.pk),
"status": StreamStatus.DISABLED,
},
HTTP_AUTHORIZATION=f"Bearer {self.provider.token.key}",
)
self.assertEqual(res.status_code, 200)
stream.refresh_from_db()
self.assertJSONEqual(
res.content,
{
"stream_id": str(stream.pk),
"status": str(stream.status),
},
)

View File

@@ -33,7 +33,7 @@ class TestTasks(APITestCase):
)
event_data = stream.prepare_event_payload(
EventTypes.SET_VERIFICATION,
{},
{"state": None},
sub_id={"format": "opaque", "id": str(stream.uuid)},
)
with Mocker() as mocker:
@@ -46,7 +46,7 @@ class TestTasks(APITestCase):
)
jwt = decode_complete(mocker.request_history[0].body, options={"verify_signature": False})
self.assertEqual(jwt["header"]["typ"], "secevent+jwt")
self.assertEqual(jwt["payload"]["events"][EventTypes.SET_VERIFICATION], {})
self.assertIsNone(jwt["payload"]["events"][EventTypes.SET_VERIFICATION]["state"])
def test_push_auth(self):
auth = generate_id()
@@ -58,7 +58,7 @@ class TestTasks(APITestCase):
)
event_data = stream.prepare_event_payload(
EventTypes.SET_VERIFICATION,
{},
{"state": None},
sub_id={"format": "opaque", "id": str(stream.uuid)},
)
with Mocker() as mocker:
@@ -72,7 +72,7 @@ class TestTasks(APITestCase):
)
jwt = decode_complete(mocker.request_history[0].body, options={"verify_signature": False})
self.assertEqual(jwt["header"]["typ"], "secevent+jwt")
self.assertEqual(jwt["payload"]["events"][EventTypes.SET_VERIFICATION], {})
self.assertIsNone(jwt["payload"]["events"][EventTypes.SET_VERIFICATION]["state"])
def test_push_stream_disable(self):
auth = generate_id()
@@ -81,11 +81,11 @@ class TestTasks(APITestCase):
delivery_method=DeliveryMethods.RFC_PUSH,
endpoint_url="http://localhost/ssf-push",
authorization_header=auth,
status=StreamStatus.DISABLED_DELETED,
status=StreamStatus.DISABLED,
)
event_data = stream.prepare_event_payload(
EventTypes.SET_VERIFICATION,
{},
{"state": None},
sub_id={"format": "opaque", "id": str(stream.uuid)},
)
with Mocker() as mocker:
@@ -95,7 +95,7 @@ class TestTasks(APITestCase):
).get_result(block=True, timeout=1)
jwt = decode_complete(mocker.request_history[0].body, options={"verify_signature": False})
self.assertEqual(jwt["header"]["typ"], "secevent+jwt")
self.assertEqual(jwt["payload"]["events"][EventTypes.SET_VERIFICATION], {})
self.assertIsNone(jwt["payload"]["events"][EventTypes.SET_VERIFICATION]["state"])
self.assertFalse(Stream.objects.filter(pk=stream.pk).exists())
def test_push_error(self):
@@ -106,7 +106,7 @@ class TestTasks(APITestCase):
)
event_data = stream.prepare_event_payload(
EventTypes.SET_VERIFICATION,
{},
{"state": None},
sub_id={"format": "opaque", "id": str(stream.uuid)},
)
with Mocker() as mocker:

View File

@@ -24,10 +24,10 @@ class SSFView(APIView):
class SSFStreamView(SSFView):
def get_object(self) -> Stream:
streams = Stream.objects.filter(provider=self.provider).exclude(
status=StreamStatus.DISABLED_DELETED
)
def get_object(self, any_status=False) -> Stream:
streams = Stream.objects.filter(provider=self.provider)
if not any_status:
streams = streams.filter(status__in=[StreamStatus.ENABLED, StreamStatus.PAUSED])
if "stream_id" in self.request.query_params:
streams = streams.filter(pk=self.request.query_params["stream_id"])
if "stream_id" in self.request.data:

View File

@@ -1,6 +1,6 @@
from uuid import uuid4
from django.http import Http404, HttpRequest
from django.http import HttpRequest
from django.urls import reverse
from rest_framework.exceptions import PermissionDenied, ValidationError
from rest_framework.fields import CharField, ChoiceField, ListField, SerializerMethodField
@@ -106,11 +106,7 @@ class StreamResponseSerializer(PassiveSerializer):
}
def get_events_supported(self, instance: Stream) -> list[str]:
return [
EventTypes.CAEP_SESSION_REVOKED,
EventTypes.CAEP_CREDENTIAL_CHANGE,
EventTypes.SET_VERIFICATION,
]
return [x.value for x in EventTypes]
class StreamView(SSFStreamView):
@@ -132,9 +128,10 @@ class StreamView(SSFStreamView):
LOGGER.info("Sending verification event", stream=instance)
send_ssf_events(
EventTypes.SET_VERIFICATION,
{},
{
"state": None,
},
stream_filter={"pk": instance.uuid},
request=request,
sub_id={"format": "opaque", "id": str(instance.uuid)},
)
response = StreamResponseSerializer(instance=instance, context={"request": request}).data
@@ -162,9 +159,7 @@ class StreamView(SSFStreamView):
def delete(self, request: Request, *args, **kwargs) -> Response:
stream = self.get_object()
if stream.status == StreamStatus.DISABLED_DELETED:
raise Http404
stream.status = StreamStatus.DISABLED_DELETED
stream.status = StreamStatus.DISABLED
stream.save()
return Response(status=204)
@@ -180,7 +175,6 @@ class StreamVerifyView(SSFStreamView):
"state": state,
},
stream_filter={"pk": stream.uuid},
request=request,
sub_id={"format": "opaque", "id": str(stream.uuid)},
)
return Response(status=204)
@@ -188,25 +182,8 @@ class StreamVerifyView(SSFStreamView):
class StreamStatusView(SSFStreamView):
class StreamStatusSerializer(PassiveSerializer):
stream_id = CharField()
status = ChoiceField(choices=StreamStatus.choices)
def get(self, request: Request, *args, **kwargs):
stream = self.get_object()
return Response(
{
"stream_id": str(stream.pk),
"status": str(stream.status),
}
)
def post(self, request: Request, *args, **kwargs):
stream = self.get_object()
serializer = self.StreamStatusSerializer(stream, data=request.data)
serializer.is_valid(raise_exception=True)
stream.status = serializer.validated_data["status"]
stream.save()
stream = self.get_object(any_status=True)
return Response(
{
"stream_id": str(stream.pk),

View File

@@ -8,6 +8,7 @@ from inspect import currentframe
from typing import Any
from uuid import uuid4
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
from django.apps import apps
from django.db import models
@@ -409,7 +410,7 @@ class NotificationTransport(TasksModel, SerializerModel):
)
notification.save()
layer = get_channel_layer()
layer.group_send_blocking(
async_to_sync(layer.group_send)(
build_user_group(notification.user),
{
"type": "event.notification",

View File

@@ -29,7 +29,6 @@ class RefreshOtherFlowsAfterAuthentication(Flag[bool], key="flows_refresh_others
default = False
visibility = "public"
description = _("Refresh other tabs after successful authentication.")
deprecated = True
class ContinuousLogin(Flag[bool], key="flows_continuous_login"):

View File

@@ -53,16 +53,6 @@ class TestEndSessionView(OAuthTestCase):
self.brand.flow_invalidation = self.invalidation_flow
self.brand.save()
def _id_token_hint(self, host: str) -> str:
"""Issue a valid id_token_hint for the test provider under the given host."""
return self.provider.encode(
{
"iss": f"http://{host}/application/o/{self.app.slug}/",
"aud": self.provider.client_id,
"sub": str(self.user.pk),
}
)
def test_post_logout_redirect_uri_strict_match(self):
"""Test strict URI matching redirects to flow"""
self.client.force_login(self.user)
@@ -71,10 +61,7 @@ class TestEndSessionView(OAuthTestCase):
"authentik_providers_oauth2:end-session",
kwargs={"application_slug": self.app.slug},
),
{
"post_logout_redirect_uri": "http://testserver/logout",
"id_token_hint": self._id_token_hint(self.brand.domain),
},
{"post_logout_redirect_uri": "http://testserver/logout"},
HTTP_HOST=self.brand.domain,
)
# Should redirect to the invalidation flow
@@ -82,12 +69,7 @@ class TestEndSessionView(OAuthTestCase):
self.assertIn(self.invalidation_flow.slug, response.url)
def test_post_logout_redirect_uri_strict_no_match(self):
"""Test strict URI not matching returns an error and does not start logout flow.
Required by OIDC RP-Initiated Logout 1.0: on an unregistered
post_logout_redirect_uri, the OP MUST NOT redirect and MUST NOT proceed with
logout that targets the RP.
"""
"""Test strict URI not matching still proceeds with flow (no redirect URI in context)"""
self.client.force_login(self.user)
invalid_uri = "http://testserver/other"
response = self.client.get(
@@ -95,14 +77,12 @@ class TestEndSessionView(OAuthTestCase):
"authentik_providers_oauth2:end-session",
kwargs={"application_slug": self.app.slug},
),
{
"post_logout_redirect_uri": invalid_uri,
"id_token_hint": self._id_token_hint(self.brand.domain),
},
{"post_logout_redirect_uri": invalid_uri},
HTTP_HOST=self.brand.domain,
)
self.assertEqual(response.status_code, 400)
self.assertNotIn(invalid_uri, response.content.decode())
# Should still redirect to flow, but invalid URI should not be in response
self.assertEqual(response.status_code, 302)
self.assertNotIn(invalid_uri, response.url)
def test_post_logout_redirect_uri_regex_match(self):
"""Test regex URI matching redirects to flow"""
@@ -112,10 +92,7 @@ class TestEndSessionView(OAuthTestCase):
"authentik_providers_oauth2:end-session",
kwargs={"application_slug": self.app.slug},
),
{
"post_logout_redirect_uri": "https://app.example.com/logout",
"id_token_hint": self._id_token_hint(self.brand.domain),
},
{"post_logout_redirect_uri": "https://app.example.com/logout"},
HTTP_HOST=self.brand.domain,
)
# Should redirect to the invalidation flow
@@ -123,7 +100,7 @@ class TestEndSessionView(OAuthTestCase):
self.assertIn(self.invalidation_flow.slug, response.url)
def test_post_logout_redirect_uri_regex_no_match(self):
"""Test regex URI not matching returns an error and does not start logout flow."""
"""Test regex URI not matching"""
self.client.force_login(self.user)
invalid_uri = "https://malicious.com/logout"
response = self.client.get(
@@ -131,14 +108,12 @@ class TestEndSessionView(OAuthTestCase):
"authentik_providers_oauth2:end-session",
kwargs={"application_slug": self.app.slug},
),
{
"post_logout_redirect_uri": invalid_uri,
"id_token_hint": self._id_token_hint(self.brand.domain),
},
{"post_logout_redirect_uri": invalid_uri},
HTTP_HOST=self.brand.domain,
)
self.assertEqual(response.status_code, 400)
self.assertNotIn(invalid_uri, response.content.decode())
# Should still proceed to flow, but invalid URI should not be in response
self.assertEqual(response.status_code, 302)
self.assertNotIn(invalid_uri, response.url)
def test_state_parameter_appended_to_uri(self):
"""Test state parameter is appended to validated redirect URI"""
@@ -148,7 +123,6 @@ class TestEndSessionView(OAuthTestCase):
{
"post_logout_redirect_uri": "http://testserver/logout",
"state": "test-state-123",
"id_token_hint": self._id_token_hint("testserver"),
},
)
request.user = self.user
@@ -158,7 +132,6 @@ class TestEndSessionView(OAuthTestCase):
view.request = request
view.kwargs = {"application_slug": self.app.slug}
view.resolve_provider_application()
view.validate()
self.assertIn("state=test-state-123", view.post_logout_redirect_uri)
@@ -173,7 +146,6 @@ class TestEndSessionView(OAuthTestCase):
{
"post_logout_redirect_uri": "http://testserver/logout",
"state": "xyz789",
"id_token_hint": self._id_token_hint(self.brand.domain),
},
HTTP_HOST=self.brand.domain,
)

View File

@@ -5,8 +5,6 @@ from urllib.parse import quote, urlparse
from django.http import Http404, HttpRequest, HttpResponse
from django.shortcuts import get_object_or_404
from jwt import PyJWTError
from jwt import decode as jwt_decode
from authentik.common.oauth.constants import (
FORBIDDEN_URI_SCHEMES,
@@ -23,14 +21,11 @@ from authentik.flows.planner import (
from authentik.flows.stage import SessionEndStage
from authentik.flows.views.executor import SESSION_KEY_PLAN
from authentik.lib.views import bad_request_message
from authentik.policies.views import PolicyAccessView
from authentik.policies.views import PolicyAccessView, RequestValidationError
from authentik.providers.iframe_logout import IframeLogoutStageView
from authentik.providers.oauth2.errors import TokenError
from authentik.providers.oauth2.models import (
AccessToken,
JWTAlgorithms,
OAuth2LogoutMethod,
OAuth2Provider,
RedirectURIMatchingMode,
)
from authentik.providers.oauth2.tasks import send_backchannel_logout_request
@@ -52,45 +47,21 @@ class EndSessionView(PolicyAccessView):
if not self.flow:
raise Http404
def validate(self):
# Parse end session parameters
query_dict = self.request.POST if self.request.method == "POST" else self.request.GET
state = query_dict.get("state")
request_redirect_uri = query_dict.get("post_logout_redirect_uri")
id_token_hint = query_dict.get("id_token_hint")
self.post_logout_redirect_uri = None
# OIDC Certification: Verify id_token_hint. If invalid or missing, throw an error
if id_token_hint:
# Load a fresh provider instance that's not part of the flow
# since it'll have the cryptography Certificate that can't be pickled
provider = OAuth2Provider.objects.get(pk=self.provider.pk)
key, alg = provider.jwt_key
if alg != JWTAlgorithms.HS256:
key = provider.signing_key.public_key
try:
jwt_decode(
id_token_hint,
key,
algorithms=[alg],
audience=provider.client_id,
issuer=provider.get_issuer(self.request),
# ID Tokens are short-lived; a logout request arriving
# after expiry is still legitimate and must succeed.
options={"verify_exp": False},
)
except PyJWTError:
raise TokenError("invalid_request").with_cause(
"id_token_hint_decode_failed"
) from None
# Validate post_logout_redirect_uri against registered URIs
if request_redirect_uri:
# OIDC Certification: id_token_hint required with post_logout_redirect_uri
if not id_token_hint:
raise TokenError("invalid_request").with_cause("id_token_hint_missing")
if urlparse(request_redirect_uri).scheme in FORBIDDEN_URI_SCHEMES:
raise TokenError("invalid_request").with_cause("post_logout_redirect_uri")
raise RequestValidationError(
bad_request_message(
self.request,
"Forbidden URI scheme in post_logout_redirect_uri",
)
)
for allowed in self.provider.post_logout_redirect_uris:
if allowed.matching_mode == RedirectURIMatchingMode.STRICT:
if request_redirect_uri == allowed.url:
@@ -100,10 +71,6 @@ class EndSessionView(PolicyAccessView):
if fullmatch(allowed.url, request_redirect_uri):
self.post_logout_redirect_uri = request_redirect_uri
break
# OIDC Certification: OP MUST NOT perform post-logout redirection
# if the supplied URI does not exactly match a registered one
if self.post_logout_redirect_uri is None:
raise TokenError("invalid_request").with_cause("invalid_post_logout_redirect_uri")
# Append state to the redirect URI if both are present
if self.post_logout_redirect_uri and state:
@@ -124,43 +91,50 @@ class EndSessionView(PolicyAccessView):
"<html><body>Logout successful</body></html>", content_type="text/html", status=200
)
# Otherwise, continue with normal policy checks
return super().dispatch(request, *args, **kwargs)
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
"""Dispatch the flow planner for the invalidation flow"""
try:
self.validate()
except TokenError as exc:
return bad_request_message(
self.request,
exc.description,
)
planner = FlowPlanner(self.flow)
planner.allow_empty_flows = True
# Build flow context with logout parameters
context = {
PLAN_CONTEXT_APPLICATION: self.application,
}
# Get session info for logout notifications and token invalidation
auth_session = AuthenticatedSession.from_request(request, request.user)
# Add validated redirect URI (with state appended) to context if available
if self.post_logout_redirect_uri:
context[PLAN_CONTEXT_POST_LOGOUT_REDIRECT_URI] = self.post_logout_redirect_uri
# Invalidate tokens for this provider/session (RP-initiated logout:
# user stays logged into authentik, only this provider's tokens are revoked)
if request.user.is_authenticated and auth_session:
AccessToken.objects.filter(
user=request.user,
provider=self.provider,
session=auth_session,
).delete()
session_key = (
auth_session.session.session_key if auth_session and auth_session.session else None
)
# Handle frontchannel logout
frontchannel_logout_url = None
if self.provider.logout_method == OAuth2LogoutMethod.FRONTCHANNEL:
frontchannel_logout_url = build_frontchannel_logout_url(
self.provider, request, session_key
)
# Handle backchannel logout
if (
self.provider.logout_method == OAuth2LogoutMethod.BACKCHANNEL
and self.provider.logout_uri
):
# Find access token to get iss and sub for the logout token
access_token = AccessToken.objects.filter(
user=request.user,
provider=self.provider,
@@ -189,16 +163,9 @@ class EndSessionView(PolicyAccessView):
}
]
access_tokens = AccessToken.objects.filter(
user=request.user,
provider=self.provider,
)
if auth_session:
access_tokens = access_tokens.filter(session=auth_session)
access_tokens.delete()
plan = planner.plan(request, context)
# Inject iframe logout stage if frontchannel logout is configured
if frontchannel_logout_url:
plan.insert_stage(in_memory_stage(IframeLogoutStageView))

View File

@@ -1,5 +1,6 @@
"""RAC Signals"""
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
from django.core.cache import cache
from django.db.models.signals import post_delete, post_save, pre_delete
@@ -17,7 +18,7 @@ from authentik.providers.rac.models import ConnectionToken, Endpoint
@receiver(pre_delete, sender=AuthenticatedSession)
def user_session_deleted(sender, instance: AuthenticatedSession, **_):
layer = get_channel_layer()
layer.group_send_blocking(
async_to_sync(layer.group_send)(
build_rac_client_group_session(instance.session.session_key),
{"type": "event.disconnect", "reason": "session_logout"},
)
@@ -27,7 +28,7 @@ def user_session_deleted(sender, instance: AuthenticatedSession, **_):
def pre_delete_connection_token_disconnect(sender, instance: ConnectionToken, **_):
"""Disconnect session when connection token is deleted"""
layer = get_channel_layer()
layer.group_send_blocking(
async_to_sync(layer.group_send)(
build_rac_client_group_token(instance.token),
{"type": "event.disconnect", "reason": "token_delete"},
)

View File

@@ -6,7 +6,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_core", "0056_user_roles"), # must run before group field is removed
("authentik_rbac", "0009_remove_initialpermissions_mode"),
]

View File

@@ -187,7 +187,6 @@ SPECTACULAR_SETTINGS = {
"PolicyEngineMode": "authentik.policies.models.PolicyEngineMode",
"PromptTypeEnum": "authentik.stages.prompt.models.FieldTypes",
"ProxyMode": "authentik.providers.proxy.models.ProxyMode",
"RedirectURITypeEnum": "authentik.providers.oauth2.models.RedirectURIType",
"SAMLBindingsEnum": "authentik.providers.saml.models.SAMLBindings",
"SAMLLogoutMethods": "authentik.providers.saml.models.SAMLLogoutMethods",
"SAMLNameIDPolicyEnum": "authentik.sources.saml.models.SAMLNameIDPolicy",

File diff suppressed because one or more lines are too long

View File

@@ -16,7 +16,7 @@ class RedirectMode(models.TextChoices):
class RedirectStage(Stage):
"""Redirect the user to a static URL or another flow, optionally with all gathered context."""
"""Redirect the user to another flow, potentially with all gathered context."""
keep_context = models.BooleanField(default=True)
mode = models.TextField(choices=RedirectMode.choices)

View File

@@ -59,8 +59,6 @@ class FlagsJSONExtension(OpenApiSerializerFieldExtension):
props[_flag.key] = build_basic_type(get_args(_flag.__orig_bases__[0])[0])
if _flag.description:
props[_flag.key]["description"] = _flag.description
if _flag.deprecated:
props[_flag.key]["deprecated"] = _flag.deprecated
return build_object_type(props, required=props.keys())

View File

@@ -18,7 +18,6 @@ class Flag[T]:
Literal["none"] | Literal["public"] | Literal["authenticated"] | Literal["system"]
) = "none"
description: str | None = None
deprecated = False
def __init_subclass__(cls, key: str, **kwargs):
cls.__key = key

View File

@@ -1,211 +0,0 @@
# Minimal Invitation-based Enrollment Blueprint
#
# Companion to flows-invitation-enrollment.yaml, intended for the "New Invitation"
# wizard in the admin UI. Creates a single enrollment flow with an invitation stage
# bound to it, plus the supporting prompt/user-write/user-login stages.
#
# All user-facing fields are parameterized via !Context with fallback defaults, so
# this blueprint can be imported directly (without context) or through the wizard
# with custom values.
#
# Context keys (all optional):
# flow_name Display name of the enrollment flow.
# flow_slug URL slug of the flow and suffix for sub-entity
# identifiers (so repeated imports with different
# slugs don't overwrite each other).
# stage_name Name of the invitation stage.
# continue_flow_without_invitation Whether the flow continues when no invitation
# is supplied (default: false).
# user_type "external" or "internal" (default: "external").
# Drives the user-write stage's user_type and
# user_path_template.
version: 1
metadata:
labels:
blueprints.goauthentik.io/instantiate: "false"
name: Invitation-based Enrollment (minimal)
entries:
- identifiers:
slug: !Context [flow_slug, invitation-enrollment-flow]
model: authentik_flows.flow
id: flow
attrs:
name: !Context [flow_name, Invitation Enrollment Flow]
title: !Context [flow_name, Invitation Enrollment Flow]
designation: enrollment
authentication: require_unauthenticated
- identifiers:
name: !Context [stage_name, invitation-stage]
id: invitation-stage
model: authentik_stages_invitation.invitationstage
attrs:
continue_flow_without_invitation: !Context [continue_flow_without_invitation, false]
- identifiers:
name:
!Format [
"invitation-enrollment-field-username-%s",
!Context [flow_slug, invitation-enrollment-flow],
]
id: prompt-field-username
model: authentik_stages_prompt.prompt
attrs:
field_key: username
label: Username
type: username
required: true
placeholder: Username
placeholder_expression: false
order: 0
- identifiers:
name:
!Format [
"invitation-enrollment-field-password-%s",
!Context [flow_slug, invitation-enrollment-flow],
]
id: prompt-field-password
model: authentik_stages_prompt.prompt
attrs:
field_key: password
label: Password
type: password
required: true
placeholder: Password
placeholder_expression: false
order: 1
- identifiers:
name:
!Format [
"invitation-enrollment-field-password-repeat-%s",
!Context [flow_slug, invitation-enrollment-flow],
]
id: prompt-field-password-repeat
model: authentik_stages_prompt.prompt
attrs:
field_key: password_repeat
label: Password (repeat)
type: password
required: true
placeholder: Password (repeat)
placeholder_expression: false
order: 2
- identifiers:
name:
!Format [
"invitation-enrollment-field-name-%s",
!Context [flow_slug, invitation-enrollment-flow],
]
id: prompt-field-name
model: authentik_stages_prompt.prompt
attrs:
field_key: name
label: Name
type: text
required: true
placeholder: Name
placeholder_expression: false
order: 0
- identifiers:
name:
!Format [
"invitation-enrollment-field-email-%s",
!Context [flow_slug, invitation-enrollment-flow],
]
id: prompt-field-email
model: authentik_stages_prompt.prompt
attrs:
field_key: email
label: Email
type: email
required: true
placeholder: Email
placeholder_expression: false
order: 1
- identifiers:
name:
!Format [
"invitation-enrollment-prompt-credentials-%s",
!Context [flow_slug, invitation-enrollment-flow],
]
id: prompt-stage-credentials
model: authentik_stages_prompt.promptstage
attrs:
fields:
- !KeyOf prompt-field-username
- !KeyOf prompt-field-password
- !KeyOf prompt-field-password-repeat
- identifiers:
name:
!Format [
"invitation-enrollment-prompt-details-%s",
!Context [flow_slug, invitation-enrollment-flow],
]
id: prompt-stage-details
model: authentik_stages_prompt.promptstage
attrs:
fields:
- !KeyOf prompt-field-name
- !KeyOf prompt-field-email
- identifiers:
name:
!Format [
"invitation-enrollment-user-write-%s",
!Context [flow_slug, invitation-enrollment-flow],
]
id: user-write-stage
model: authentik_stages_user_write.userwritestage
attrs:
user_creation_mode: always_create
user_type: !Context [user_type, external]
user_path_template:
!Format ["users/%s", !Context [user_type, external]]
- identifiers:
name:
!Format [
"invitation-enrollment-user-login-%s",
!Context [flow_slug, invitation-enrollment-flow],
]
id: user-login-stage
model: authentik_stages_user_login.userloginstage
- identifiers:
target: !KeyOf flow
stage: !KeyOf invitation-stage
order: 5
model: authentik_flows.flowstagebinding
attrs:
evaluate_on_plan: true
re_evaluate_policies: true
- identifiers:
target: !KeyOf flow
stage: !KeyOf prompt-stage-credentials
order: 10
model: authentik_flows.flowstagebinding
- identifiers:
target: !KeyOf flow
stage: !KeyOf prompt-stage-details
order: 15
model: authentik_flows.flowstagebinding
- identifiers:
target: !KeyOf flow
stage: !KeyOf user-write-stage
order: 20
model: authentik_flows.flowstagebinding
- identifiers:
target: !KeyOf flow
stage: !KeyOf user-login-stage
order: 100
model: authentik_flows.flowstagebinding

View File

@@ -73,16 +73,8 @@ entries:
redirect_uris:
- matching_mode: strict
url: https://localhost:8443/test/a/authentik/callback
redirect_uri_type: authorization
- matching_mode: strict
url: https://host.docker.internal:8443/test/a/authentik/callback
redirect_uri_type: authorization
- matching_mode: strict
url: https://localhost:8443/test/a/authentik/post_logout_redirect
redirect_uri_type: logout
- matching_mode: strict
url: https://host.docker.internal:8443/test/a/authentik/post_logout_redirect
redirect_uri_type: logout
grant_types:
- authorization_code
- implicit
@@ -116,16 +108,8 @@ entries:
redirect_uris:
- matching_mode: strict
url: https://localhost:8443/test/a/authentik/callback
redirect_uri_type: authorization
- matching_mode: strict
url: https://host.docker.internal:8443/test/a/authentik/callback
redirect_uri_type: authorization
- matching_mode: strict
url: https://localhost:8443/test/a/authentik/post_logout_redirect
redirect_uri_type: logout
- matching_mode: strict
url: https://host.docker.internal:8443/test/a/authentik/post_logout_redirect
redirect_uri_type: logout
grant_types:
- authorization_code
- implicit

View File

@@ -26,8 +26,6 @@ var healthcheckCmd = &cobra.Command{
exitCode := 1
log.WithField("mode", mode).Debug("checking health")
switch strings.ToLower(mode) {
case "allinone":
fallthrough
case "server":
exitCode = check(fmt.Sprintf("http://localhost%s-/health/live/", config.Get().Web.Path))
case "worker":

2
go.mod
View File

@@ -7,7 +7,7 @@ require (
beryju.io/radius-eap v0.1.0
github.com/avast/retry-go/v4 v4.7.0
github.com/coreos/go-oidc/v3 v3.18.0
github.com/getsentry/sentry-go v0.46.1
github.com/getsentry/sentry-go v0.46.0
github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1
github.com/go-ldap/ldap/v3 v3.4.13
github.com/go-openapi/runtime v0.29.4

4
go.sum
View File

@@ -20,8 +20,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/getsentry/sentry-go v0.46.1 h1:mZyQFaQYkPxAdDG4HR8gDg6j4CnKYVWt4TF92N7i3XY=
github.com/getsentry/sentry-go v0.46.1/go.mod h1:evVbw2qotNUdYG8KxXbAdjOQWWvWIwKxpjdZZIvcIPw=
github.com/getsentry/sentry-go v0.46.0 h1:mbdDaarbUdOt9X+dx6kDdntkShLEX3/+KyOsVDTPDj0=
github.com/getsentry/sentry-go v0.46.0/go.mod h1:evVbw2qotNUdYG8KxXbAdjOQWWvWIwKxpjdZZIvcIPw=
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=

View File

@@ -31,7 +31,7 @@ function run_authentik {
echo go run ./cmd/server "$@"
fi
;;
allinone | worker)
worker)
if [[ -x "$(command -v authentik)" ]]; then
echo authentik "$@"
else
@@ -105,7 +105,7 @@ elif [[ "$1" == "test-all" ]]; then
prepare_debug
chmod 777 /root
check_if_root_and_run manage test authentik
elif [[ "$1" == "allinone" ]] || [[ "$1" == "server" ]] || [[ "$1" == "worker" ]]; then
elif [[ "$1" == "server" ]] || [[ "$1" == "worker" ]]; then
wait_for_db
check_if_root_and_run "$@"
elif [[ "$1" == "healthcheck" ]]; then

View File

@@ -9,7 +9,7 @@
"version": "0.0.0",
"license": "MIT",
"devDependencies": {
"aws-cdk": "^2.1119.0",
"aws-cdk": "^2.1118.4",
"cross-env": "^10.1.0"
},
"engines": {
@@ -25,9 +25,9 @@
"license": "MIT"
},
"node_modules/aws-cdk": {
"version": "2.1119.0",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1119.0.tgz",
"integrity": "sha512-XBxZEKH3BY4M1EX6x0qBkmOAj8viErjpww14iH6Z3z6nI0YzjZeJ05eEl7eJwzUgv7NTGagWBS9m/eDJW5+dAg==",
"version": "2.1118.4",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1118.4.tgz",
"integrity": "sha512-wJfRQdvb+FJ2cni059mYdmjhfwhMskP+PAB59BL9jhon+jYtjy8X3pbj3uzHgAOJwNhh6jGkP8xq36Cffccbbw==",
"dev": true,
"license": "Apache-2.0",
"bin": {

View File

@@ -7,7 +7,7 @@
"aws-cfn": "cross-env CI=false cdk synth --version-reporting=false > template.yaml"
},
"devDependencies": {
"aws-cdk": "^2.1119.0",
"aws-cdk": "^2.1118.4",
"cross-env": "^10.1.0"
},
"engines": {

View File

@@ -28,45 +28,20 @@ class HttpHandler(BaseHTTPRequestHandler):
_ = db_conn.cursor()
def do_GET(self):
from django.db import (
DatabaseError,
InterfaceError,
OperationalError,
connections,
)
from psycopg.errors import AdminShutdown
from authentik.root.monitoring import monitoring_set
DATABASE_ERRORS = (
AdminShutdown,
InterfaceError,
DatabaseError,
ConnectionError,
OperationalError,
)
if self.path == "/-/metrics/":
try:
monitoring_set.send(self)
except DATABASE_ERRORS as exc:
LOGGER.warning("failed to send monitoring_set", exc=exc)
for db_conn in connections.all():
db_conn.close()
self.send_response(503)
else:
self.send_response(200)
from authentik.root.monitoring import monitoring_set
monitoring_set.send_robust(self)
self.send_response(200)
self.end_headers()
elif self.path == "/-/health/ready/":
from django.db.utils import OperationalError
try:
self.check_db()
except DATABASE_ERRORS as exc:
LOGGER.warning("failed to check database health", exc=exc)
for db_conn in connections.all():
db_conn.close()
except OperationalError:
self.send_response(503)
else:
self.send_response(200)
self.send_response(200)
self.end_headers()
else:
self.send_response(200)

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-05-01 03:47+0000\n"
"POT-Creation-Date: 2026-04-30 00:27+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"
@@ -872,6 +872,10 @@ msgstr ""
msgid "Grace period must be shorter than the interval."
msgstr ""
#: authentik/enterprise/lifecycle/api/rules.py
msgid "Only one type-wide rule for each object type is allowed."
msgstr ""
#: authentik/enterprise/lifecycle/models.py
msgid ""
"Select which transports should be used to notify the reviewers. If none are "
@@ -899,8 +903,7 @@ msgid "Go to {self._get_model_name()}"
msgstr ""
#: authentik/enterprise/lifecycle/models.py
msgid ""
"Access review is due for {self.content_type.name.lower()} {object_label}"
msgid "Access review is due for {self.content_type.name} {str(self.object)}"
msgstr ""
#: authentik/enterprise/lifecycle/models.py
@@ -913,7 +916,7 @@ msgid "Access review completed for {self.content_type.name} {str(self.object)}"
msgstr ""
#: authentik/enterprise/lifecycle/tasks.py
msgid "Dispatch tasks to apply lifecycle rules."
msgid "Dispatch tasks to validate lifecycle rules."
msgstr ""
#: authentik/enterprise/lifecycle/tasks.py
@@ -1226,78 +1229,6 @@ msgstr ""
msgid "Generate data export."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "User to lock. If omitted, locks the current user (self-service)."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "No lockdown flow configured."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Lockdown flow is not applicable."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Choose the target account, then return a flow link."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "No lockdown flow configured or the flow is not applicable"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Permission denied (when targeting another user)"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Deactivate the user account (set is_active to False)"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Set an unusable password for the user"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Delete all active sessions for the user"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid ""
"Revoke all tokens for the user (API, app password, recovery, verification, "
"OAuth)"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid ""
"Flow to redirect users to after self-service lockdown. This flow should not "
"require authentication since the user's session is deleted."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Account Lockdown Stage"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Account Lockdown Stages"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "No target user specified for account lockdown"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "You do not have permission to lock down this account."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "Account lockdown failed for this account."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "Self-service account lockdown requires a completion flow."
msgstr ""
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/models.py
msgid "Endpoint Authenticator Google Device Trust Connector Stage"
msgstr ""
@@ -4525,18 +4456,6 @@ msgstr ""
msgid "Static: Static value, displayed as-is."
msgstr ""
#: authentik/stages/prompt/models.py
msgid "Alert (Info): Static alert box with info styling"
msgstr ""
#: authentik/stages/prompt/models.py
msgid "Alert (Warning): Static alert box with warning styling"
msgstr ""
#: authentik/stages/prompt/models.py
msgid "Alert (Danger): Static alert box with danger styling"
msgstr ""
#: authentik/stages/prompt/models.py
msgid "authentik: Selection of locales authentik supports"
msgstr ""

View File

@@ -53,7 +53,6 @@ Relatedly
Sidero
snipeit
sonarqube
Technitium
Terrakube
Ueberauth
Veeam

Binary file not shown.

View File

@@ -15,7 +15,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-05-01 03:47+0000\n"
"POT-Creation-Date: 2026-04-08 00:28+0000\n"
"PO-Revision-Date: 2025-12-01 19:09+0000\n"
"Last-Translator: Sp P, 2026\n"
"Language-Team: French (France) (https://app.transifex.com/authentik/teams/119923/fr_FR/)\n"
@@ -263,18 +263,6 @@ msgstr ""
"fournisseurs backchannels sont retournés. Si faux, les fournisseurs "
"backchannels sont exclus"
#: authentik/core/api/users.py
msgid "Invalid password hash format. Must be a valid Django password hash."
msgstr ""
"Format de hachage de mot de passe invalide. Cela doit être un hachage de mot"
" de passe Django valide."
#: authentik/core/api/users.py
msgid "Cannot set both password and password_hash. Use only one."
msgstr ""
"Impossible de définir à la fois password (mot de passe) et password_hash "
"(hachage de mot de passe). N'en utiliser qu'un seul."
#: authentik/core/api/users.py
msgid "No leading or trailing slashes allowed."
msgstr ""
@@ -455,11 +443,6 @@ msgid "Open launch URL in a new browser tab or window."
msgstr ""
"Ouvrir l'URL de lancement dans une nouvelle fenêtre ou un nouvel onglet."
#: authentik/core/models.py
msgid "Hide this application from the user's My applications page."
msgstr ""
"Masquer cette application dans la page Mes applications de l'utilisateur."
#: authentik/core/models.py
msgid "Application"
msgstr "Application"
@@ -827,14 +810,6 @@ msgstr "Nonce Apple"
msgid "Apple Nonces"
msgstr "Nonces Apple"
#: authentik/endpoints/connectors/agent/models.py
msgid "Apple Independent Secure Enclave"
msgstr "Secure Enclave indépendante d'Apple"
#: authentik/endpoints/connectors/agent/models.py
msgid "Apple Independent Secure Enclaves"
msgstr "Secure Enclaves indépendantes d'Apple"
#: authentik/endpoints/facts.py
msgid "Operating System name, such as 'Server 2022' or 'Ubuntu'"
msgstr "Nom du système d'exploitation, comme 'Server 2022' ou 'Ubuntu'"
@@ -961,6 +936,12 @@ msgstr "Soit un groupe de réviseurs soit un réviseur doit être défini."
msgid "Grace period must be shorter than the interval."
msgstr "La période de grâce doit être plus courte que l'intervalle."
#: authentik/enterprise/lifecycle/api/rules.py
msgid "Only one type-wide rule for each object type is allowed."
msgstr ""
"Une seule règle pour l'ensemble du type est autorisée pour chaque type "
"d'objet."
#: authentik/enterprise/lifecycle/models.py
msgid ""
"Select which transports should be used to notify the reviewers. If none are "
@@ -991,11 +972,10 @@ msgid "Go to {self._get_model_name()}"
msgstr "Aller à {self._get_model_name()}"
#: authentik/enterprise/lifecycle/models.py
msgid ""
"Access review is due for {self.content_type.name.lower()} {object_label}"
msgid "Access review is due for {self.content_type.name} {str(self.object)}"
msgstr ""
"La révision de l'accès doit être effectuée pour "
"{self.content_type.name.lower()} {object_label}"
"La révision d'accès est attendue pour {self.content_type.name} "
"{str(self.object)}"
#: authentik/enterprise/lifecycle/models.py
msgid ""
@@ -1012,8 +992,8 @@ msgstr ""
"{str(self.object)}"
#: authentik/enterprise/lifecycle/tasks.py
msgid "Dispatch tasks to apply lifecycle rules."
msgstr "Déclencher les tâches pour appliquer les règles de cycle de vie"
msgid "Dispatch tasks to validate lifecycle rules."
msgstr "Déclenche les tâches pour valider les règles de cycle de vie"
#: authentik/enterprise/lifecycle/tasks.py
msgid "Apply lifecycle rule."
@@ -1356,86 +1336,6 @@ msgstr "Télécharger"
msgid "Generate data export."
msgstr "Générer un export de données."
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "User to lock. If omitted, locks the current user (self-service)."
msgstr ""
"Utilisateur à bloquer. Si non renseigné, bloque l'utilisateur actuel (libre "
"service)."
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "No lockdown flow configured."
msgstr "Aucun flux de blocage configuré."
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Lockdown flow is not applicable."
msgstr "Le flux de blocage n'est pas applicable."
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Choose the target account, then return a flow link."
msgstr "Choisit le compte cible, puis renvoie un lien de flux."
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "No lockdown flow configured or the flow is not applicable"
msgstr "Aucun flux de blocage configuré, ou le flux n'est pas applicable"
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Permission denied (when targeting another user)"
msgstr "Permission refusée (lors du ciblage d'un autre utilisateur)"
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Deactivate the user account (set is_active to False)"
msgstr "Désactiver le compte de l'utilisateur (définir is_active à False)."
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Set an unusable password for the user"
msgstr "Définit un mot de passe inutilisable pour cet utilisateur."
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Delete all active sessions for the user"
msgstr "Supprimer toutes les sessions actives pour cet utilisateur."
#: authentik/enterprise/stages/account_lockdown/models.py
msgid ""
"Revoke all tokens for the user (API, app password, recovery, verification, "
"OAuth)"
msgstr ""
"Révoquer tous les jetons pour cet utilisateur (API, mot de passe applicatif,"
" récupération, vérification, OAuth)"
#: authentik/enterprise/stages/account_lockdown/models.py
msgid ""
"Flow to redirect users to after self-service lockdown. This flow should not "
"require authentication since the user's session is deleted."
msgstr ""
"Flux vers lequel rediriger les utilisateurs après le blocage en libre "
"service. Ce flux ne doit pas nécessiter d'authentification car la session "
"utilisateur est supprimée."
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Account Lockdown Stage"
msgstr "Etape de blocage de compte"
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Account Lockdown Stages"
msgstr "Etapes de blocage de compte"
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "No target user specified for account lockdown"
msgstr "Aucun utilisateur ciblé défini pour le blocage de compte"
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "You do not have permission to lock down this account."
msgstr "Vous n'avez pas la permission de bloquer ce compte."
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "Account lockdown failed for this account."
msgstr "Echec du blocage de compte pour ce compte."
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "Self-service account lockdown requires a completion flow."
msgstr ""
"Le blocage de compte en libre service nécessite un flux de finalisation."
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/models.py
msgid "Endpoint Authenticator Google Device Trust Connector Stage"
msgstr ""
@@ -1569,11 +1469,11 @@ msgstr "Évènement utilisateur"
#: authentik/events/models.py
msgid "Notification Transport"
msgstr "Transport de notification"
msgstr "Transport de Notification"
#: authentik/events/models.py
msgid "Notification Transports"
msgstr "Transports de notifications"
msgstr "Transports de notification"
#: authentik/events/models.py
msgid "Notice"
@@ -1845,10 +1745,6 @@ msgstr "Jeton du flux"
msgid "Flow Tokens"
msgstr "Jetons du flux"
#: authentik/flows/planner.py
msgid "This link is invalid or has expired. Please request a new one."
msgstr "Ce lien est invalide ou a expiré. Veuillez un demander un nouveau."
#: authentik/flows/views/executor.py
msgid "Invalid next URL"
msgstr "URL suivante invalide"
@@ -2876,12 +2772,8 @@ msgstr ""
"restriction d'audience ne sera ajoutée."
#: authentik/providers/saml/models.py
msgid ""
"Also known as EntityID. Providing a value overrides the default issuer "
"generated by authentik."
msgstr ""
"Aussi appelé EntityID. Fournir une valeur remplace l'émetteur par défaut "
"généré par authentik."
msgid "Also known as EntityID"
msgstr "Aussi appelé EntityID"
#: authentik/providers/saml/models.py
msgid "SLS URL"
@@ -3102,10 +2994,6 @@ msgstr "SAML NameID pour cette session"
msgid "SAML NameID format"
msgstr "Format SAML NameID"
#: authentik/providers/saml/models.py
msgid "SAML Issuer used for this session"
msgstr "Émetteur SAML utilisé pour cette session"
#: authentik/providers/saml/models.py
msgid "SAML Session"
msgstr "Session SAML"
@@ -3138,10 +3026,6 @@ msgstr "Salesforce"
msgid "Webex"
msgstr "Webex"
#: authentik/providers/scim/models.py
msgid "vCenter"
msgstr "vCenter"
#: authentik/providers/scim/models.py
msgid "Group filters used to define sync-scope for groups."
msgstr ""
@@ -3865,8 +3749,8 @@ msgid ""
"Which servers a user has to be a member of to be granted access. Empty list "
"allows every server."
msgstr ""
"De quels serveurs un utilisateur doit être membre afin d'obtenir l'accès. "
"Une liste vide autorise tous les serveurs."
"De quels serveurs un utilisateur doit être membre afin d'être autorisé. Une "
"liste vide autorise tous les serveurs."
#: authentik/sources/plex/models.py
msgid "Allow friends to authenticate, even if you don't share a server."
@@ -4571,11 +4455,11 @@ msgstr "Activer les utilisateurs à la complétion de l'étape."
#: authentik/stages/email/models.py
msgid "Email Stage"
msgstr "Étape de Courriel"
msgstr "Étape Courriel"
#: authentik/stages/email/models.py
msgid "Email Stages"
msgstr "Étapes de Courriel"
msgstr "Étapes Courriel"
#: authentik/stages/email/stage.py
msgid "Successfully verified Email."
@@ -5049,19 +4933,6 @@ msgstr ""
msgid "Static: Static value, displayed as-is."
msgstr "Statique : valeur statique, affichée comme telle."
#: authentik/stages/prompt/models.py
msgid "Alert (Info): Static alert box with info styling"
msgstr "Alerte (Info) : message d'alerte statique au format information"
#: authentik/stages/prompt/models.py
msgid "Alert (Warning): Static alert box with warning styling"
msgstr ""
"Alerte (Avertissement) : message d'alerte statique au format avertissement"
#: authentik/stages/prompt/models.py
msgid "Alert (Danger): Static alert box with danger styling"
msgstr "Alerte (Danger) : message d'alerte statique au format danger"
#: authentik/stages/prompt/models.py
msgid "authentik: Selection of locales authentik supports"
msgstr "authentik : sélection des locales prises en charges par authentik"

View File

@@ -1,6 +1,6 @@
//! Utilities to run an axum server.
use std::{net, os::unix, path::PathBuf};
use std::{net, os::unix};
use ak_common::arbiter::{Arbiter, Tasks};
use axum::Router;
@@ -21,20 +21,26 @@ async fn run_plain(
name: &str,
router: Router,
addr: net::SocketAddr,
allow_failure: bool,
) -> Result<()> {
info!(addr = addr.to_string(), "starting {name} server");
let handle = Handle::new();
arbiter.add_net_handle(handle.clone()).await;
axum_server::Server::bind(addr)
let res = axum_server::Server::bind(addr)
.acceptor(CatchPanicAcceptor::new(
ProxyProtocolAcceptor::new().acceptor(DefaultAcceptor::new()),
arbiter.clone(),
))
.handle(handle)
.serve(router.into_make_service_with_connect_info::<net::SocketAddr>())
.await?;
.await;
if res.is_err() && allow_failure {
arbiter.shutdown().await;
return Ok(());
}
res?;
Ok(())
}
@@ -43,59 +49,60 @@ async fn run_plain(
///
/// `name` is only used for observability purposes and should describe which module is starting the
/// server.
///
/// `allow_failure` allows the server to fail silently.
pub fn start_plain(
tasks: &mut Tasks,
name: &'static str,
router: Router,
addr: net::SocketAddr,
allow_failure: bool,
) -> Result<()> {
let arbiter = tasks.arbiter();
tasks
.build_task()
.name(&format!("{}::run_plain({name}, {addr})", module_path!()))
.spawn(run_plain(arbiter, name, router, addr))?;
.spawn(run_plain(arbiter, name, router, addr, allow_failure))?;
Ok(())
}
struct UnixSocketGuard(PathBuf);
impl Drop for UnixSocketGuard {
fn drop(&mut self) {
trace!(path = ?self.0, "removing socket");
if let Err(err) = std::fs::remove_file(&self.0) {
trace!(?err, "failed to remove socket, ignoring");
}
}
}
pub(crate) async fn run_unix(
arbiter: Arbiter,
name: &str,
router: Router,
addr: unix::net::SocketAddr,
allow_failure: bool,
) -> Result<()> {
info!(?addr, "starting {name} server");
let handle = Handle::new();
arbiter.add_unix_handle(handle.clone()).await;
let _guard = if let Some(path) = addr.as_pathname() {
if !allow_failure && let Some(path) = addr.as_pathname() {
trace!(?addr, "removing socket");
if let Err(err) = std::fs::remove_file(path) {
trace!(?err, "failed to remove socket, ignoring");
}
Some(UnixSocketGuard(path.to_owned()))
} else {
None
};
axum_server::Server::bind(addr.clone())
}
let res = axum_server::Server::bind(addr.clone())
.acceptor(CatchPanicAcceptor::new(
DefaultAcceptor::new(),
arbiter.clone(),
))
.handle(handle)
.serve(router.into_make_service())
.await?;
.await;
if !allow_failure && let Some(path) = addr.as_pathname() {
trace!(?addr, "removing socket");
if let Err(err) = std::fs::remove_file(path) {
trace!(?err, "failed to remove socket, ignoring");
}
}
if res.is_err() && allow_failure {
arbiter.shutdown().await;
return Ok(());
}
res?;
Ok(())
}
@@ -104,17 +111,20 @@ pub(crate) async fn run_unix(
///
/// `name` is only used for observability purposes and should describe which module is starting the
/// server.
///
/// `allow_failure` allows the server to fail silently.
pub fn start_unix(
tasks: &mut Tasks,
name: &'static str,
router: Router,
addr: unix::net::SocketAddr,
allow_failure: bool,
) -> Result<()> {
let arbiter = tasks.arbiter();
tasks
.build_task()
.name(&format!("{}::run_unix({name}, {addr:?})", module_path!()))
.spawn(run_unix(arbiter, name, router, addr))?;
.spawn(run_unix(arbiter, name, router, addr, allow_failure))?;
Ok(())
}

View File

@@ -8,8 +8,7 @@
"url": "https://github.com/goauthentik/authentik.git"
},
"scripts": {
"clean": "tsc -b --clean tsconfig.json tsconfig.esm.json",
"build": "npm run clean && tsc -b tsconfig.json tsconfig.esm.json",
"build": "tsc && tsc -p tsconfig.esm.json",
"prepare": "npm run build"
},
"main": "./dist/index.js",

View File

@@ -47,7 +47,6 @@ export interface ManagedBlueprintsDestroyRequest {
export interface ManagedBlueprintsImportCreateRequest {
file?: Blob;
path?: string;
context?: string;
}
export interface ManagedBlueprintsListRequest {
@@ -370,10 +369,6 @@ export class ManagedApi extends runtime.BaseAPI {
formParams.append("path", requestParameters["path"] as any);
}
if (requestParameters["context"] != null) {
formParams.append("context", requestParameters["context"] as any);
}
let urlPath = `/managed/blueprints/import/`;
return {

View File

@@ -23,7 +23,7 @@ export interface AuthenticatedSessionUserAgentDevice {
* @type {string}
* @memberof AuthenticatedSessionUserAgentDevice
*/
brand: string | null;
brand: string;
/**
*
* @type {string}
@@ -35,7 +35,7 @@ export interface AuthenticatedSessionUserAgentDevice {
* @type {string}
* @memberof AuthenticatedSessionUserAgentDevice
*/
model: string | null;
model: string;
}
/**

View File

@@ -29,25 +29,25 @@ export interface AuthenticatedSessionUserAgentOs {
* @type {string}
* @memberof AuthenticatedSessionUserAgentOs
*/
major: string | null;
major: string;
/**
*
* @type {string}
* @memberof AuthenticatedSessionUserAgentOs
*/
minor: string | null;
minor: string;
/**
*
* @type {string}
* @memberof AuthenticatedSessionUserAgentOs
*/
patch: string | null;
patch: string;
/**
*
* @type {string}
* @memberof AuthenticatedSessionUserAgentOs
*/
patchMinor: string | null;
patchMinor: string;
}
/**

View File

@@ -40,7 +40,6 @@ export interface CurrentBrandFlags {
* Refresh other tabs after successful authentication.
* @type {boolean}
* @memberof CurrentBrandFlags
* @deprecated
*/
flowsRefreshOthers: boolean;
}

View File

@@ -19,20 +19,8 @@
export const EventsRequestedEnum = {
HttpsSchemasOpenidNetSeceventCaepEventTypeSessionRevoked:
"https://schemas.openid.net/secevent/caep/event-type/session-revoked",
HttpsSchemasOpenidNetSeceventCaepEventTypeTokenClaimsChange:
"https://schemas.openid.net/secevent/caep/event-type/token-claims-change",
HttpsSchemasOpenidNetSeceventCaepEventTypeCredentialChange:
"https://schemas.openid.net/secevent/caep/event-type/credential-change",
HttpsSchemasOpenidNetSeceventCaepEventTypeAssuranceLevelChange:
"https://schemas.openid.net/secevent/caep/event-type/assurance-level-change",
HttpsSchemasOpenidNetSeceventCaepEventTypeDeviceComplianceChange:
"https://schemas.openid.net/secevent/caep/event-type/device-compliance-change",
HttpsSchemasOpenidNetSeceventCaepEventTypeSessionEstablished:
"https://schemas.openid.net/secevent/caep/event-type/session-established",
HttpsSchemasOpenidNetSeceventCaepEventTypeSessionPresented:
"https://schemas.openid.net/secevent/caep/event-type/session-presented",
HttpsSchemasOpenidNetSeceventCaepEventTypeRiskLevelChange:
"https://schemas.openid.net/secevent/caep/event-type/risk-level-change",
HttpsSchemasOpenidNetSeceventSsfEventTypeVerification:
"https://schemas.openid.net/secevent/ssf/event-type/verification",
UnknownDefaultOpenApi: "11184809",

View File

@@ -40,7 +40,6 @@ export interface PatchedSettingsRequestFlags {
* Refresh other tabs after successful authentication.
* @type {boolean}
* @memberof PatchedSettingsRequestFlags
* @deprecated
*/
flowsRefreshOthers: boolean;
}

View File

@@ -14,8 +14,8 @@
import type { MatchingModeEnum } from "./MatchingModeEnum";
import { MatchingModeEnumFromJSON, MatchingModeEnumToJSON } from "./MatchingModeEnum";
import type { RedirectURITypeEnum } from "./RedirectURITypeEnum";
import { RedirectURITypeEnumFromJSON, RedirectURITypeEnumToJSON } from "./RedirectURITypeEnum";
import type { RedirectUriTypeEnum } from "./RedirectUriTypeEnum";
import { RedirectUriTypeEnumFromJSON, RedirectUriTypeEnumToJSON } from "./RedirectUriTypeEnum";
/**
* A single allowed redirect URI entry
@@ -37,10 +37,10 @@ export interface RedirectURI {
url: string;
/**
*
* @type {RedirectURITypeEnum}
* @type {RedirectUriTypeEnum}
* @memberof RedirectURI
*/
redirectUriType?: RedirectURITypeEnum;
redirectUriType?: RedirectUriTypeEnum;
}
/**
@@ -66,7 +66,7 @@ export function RedirectURIFromJSONTyped(json: any, ignoreDiscriminator: boolean
redirectUriType:
json["redirect_uri_type"] == null
? undefined
: RedirectURITypeEnumFromJSON(json["redirect_uri_type"]),
: RedirectUriTypeEnumFromJSON(json["redirect_uri_type"]),
};
}
@@ -85,6 +85,6 @@ export function RedirectURIToJSONTyped(
return {
matching_mode: MatchingModeEnumToJSON(value["matchingMode"]),
url: value["url"],
redirect_uri_type: RedirectURITypeEnumToJSON(value["redirectUriType"]),
redirect_uri_type: RedirectUriTypeEnumToJSON(value["redirectUriType"]),
};
}

View File

@@ -14,8 +14,8 @@
import type { MatchingModeEnum } from "./MatchingModeEnum";
import { MatchingModeEnumFromJSON, MatchingModeEnumToJSON } from "./MatchingModeEnum";
import type { RedirectURITypeEnum } from "./RedirectURITypeEnum";
import { RedirectURITypeEnumFromJSON, RedirectURITypeEnumToJSON } from "./RedirectURITypeEnum";
import type { RedirectUriTypeEnum } from "./RedirectUriTypeEnum";
import { RedirectUriTypeEnumFromJSON, RedirectUriTypeEnumToJSON } from "./RedirectUriTypeEnum";
/**
* A single allowed redirect URI entry
@@ -37,10 +37,10 @@ export interface RedirectURIRequest {
url: string;
/**
*
* @type {RedirectURITypeEnum}
* @type {RedirectUriTypeEnum}
* @memberof RedirectURIRequest
*/
redirectUriType?: RedirectURITypeEnum;
redirectUriType?: RedirectUriTypeEnum;
}
/**
@@ -69,7 +69,7 @@ export function RedirectURIRequestFromJSONTyped(
redirectUriType:
json["redirect_uri_type"] == null
? undefined
: RedirectURITypeEnumFromJSON(json["redirect_uri_type"]),
: RedirectUriTypeEnumFromJSON(json["redirect_uri_type"]),
};
}
@@ -88,6 +88,6 @@ export function RedirectURIRequestToJSONTyped(
return {
matching_mode: MatchingModeEnumToJSON(value["matchingMode"]),
url: value["url"],
redirect_uri_type: RedirectURITypeEnumToJSON(value["redirectUriType"]),
redirect_uri_type: RedirectUriTypeEnumToJSON(value["redirectUriType"]),
};
}

View File

@@ -1,57 +0,0 @@
/* tslint:disable */
/* eslint-disable */
/**
* authentik
* Making authentication simple.
*
* The version of the OpenAPI document: 2026.5.0-rc1
* Contact: hello@goauthentik.io
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
/**
*
* @export
*/
export const RedirectURITypeEnum = {
Authorization: "authorization",
Logout: "logout",
UnknownDefaultOpenApi: "11184809",
} as const;
export type RedirectURITypeEnum = (typeof RedirectURITypeEnum)[keyof typeof RedirectURITypeEnum];
export function instanceOfRedirectURITypeEnum(value: any): boolean {
for (const key in RedirectURITypeEnum) {
if (Object.prototype.hasOwnProperty.call(RedirectURITypeEnum, key)) {
if (RedirectURITypeEnum[key as keyof typeof RedirectURITypeEnum] === value) {
return true;
}
}
}
return false;
}
export function RedirectURITypeEnumFromJSON(json: any): RedirectURITypeEnum {
return RedirectURITypeEnumFromJSONTyped(json, false);
}
export function RedirectURITypeEnumFromJSONTyped(
json: any,
ignoreDiscriminator: boolean,
): RedirectURITypeEnum {
return json as RedirectURITypeEnum;
}
export function RedirectURITypeEnumToJSON(value?: RedirectURITypeEnum | null): any {
return value as any;
}
export function RedirectURITypeEnumToJSONTyped(
value: any,
ignoreDiscriminator: boolean,
): RedirectURITypeEnum {
return value as RedirectURITypeEnum;
}

View File

@@ -0,0 +1,57 @@
/* tslint:disable */
/* eslint-disable */
/**
* authentik
* Making authentication simple.
*
* The version of the OpenAPI document: 2026.5.0-rc1
* Contact: hello@goauthentik.io
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
/**
*
* @export
*/
export const RedirectUriTypeEnum = {
Authorization: "authorization",
Logout: "logout",
UnknownDefaultOpenApi: "11184809",
} as const;
export type RedirectUriTypeEnum = (typeof RedirectUriTypeEnum)[keyof typeof RedirectUriTypeEnum];
export function instanceOfRedirectUriTypeEnum(value: any): boolean {
for (const key in RedirectUriTypeEnum) {
if (Object.prototype.hasOwnProperty.call(RedirectUriTypeEnum, key)) {
if (RedirectUriTypeEnum[key as keyof typeof RedirectUriTypeEnum] === value) {
return true;
}
}
}
return false;
}
export function RedirectUriTypeEnumFromJSON(json: any): RedirectUriTypeEnum {
return RedirectUriTypeEnumFromJSONTyped(json, false);
}
export function RedirectUriTypeEnumFromJSONTyped(
json: any,
ignoreDiscriminator: boolean,
): RedirectUriTypeEnum {
return json as RedirectUriTypeEnum;
}
export function RedirectUriTypeEnumToJSON(value?: RedirectUriTypeEnum | null): any {
return value as any;
}
export function RedirectUriTypeEnumToJSONTyped(
value: any,
ignoreDiscriminator: boolean,
): RedirectUriTypeEnum {
return value as RedirectUriTypeEnum;
}

View File

@@ -20,7 +20,6 @@ export const SSFStreamStatusEnum = {
Enabled: "enabled",
Paused: "paused",
Disabled: "disabled",
DisabledDeleted: "disabled_deleted",
UnknownDefaultOpenApi: "11184809",
} as const;
export type SSFStreamStatusEnum = (typeof SSFStreamStatusEnum)[keyof typeof SSFStreamStatusEnum];

View File

@@ -707,7 +707,7 @@ export * from "./RedirectStageModeEnum";
export * from "./RedirectStageRequest";
export * from "./RedirectURI";
export * from "./RedirectURIRequest";
export * from "./RedirectURITypeEnum";
export * from "./RedirectUriTypeEnum";
export * from "./RelatedGroup";
export * from "./RelatedRule";
export * from "./Reputation";

View File

@@ -1,7 +1,9 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"composite": true,
"isolatedModules": true,
"incremental": true,
"rootDir": "src",
"strict": true,
"newLine": "lf",

View File

@@ -1,7 +1,9 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"composite": true,
"isolatedModules": true,
"incremental": true,
"rootDir": "src",
"strict": true,
"newLine": "lf",

View File

@@ -9,7 +9,7 @@ dependencies = [
"argon2-cffi==25.1.0",
"cachetools==7.0.6",
"channels==4.3.2",
"cryptography==48.0.0",
"cryptography==47.0.0",
"dacite==1.9.2",
"deepmerge==2.0",
"defusedxml==0.7.1",
@@ -25,7 +25,7 @@ dependencies = [
"django-prometheus==2.4.1",
"django-storages[s3]==1.14.6",
"django-tenants==3.10.1",
"django==5.2.14",
"django==5.2.13",
"djangoql==0.19.1",
"djangorestframework==3.17.1",
"docker==7.1.0",
@@ -46,9 +46,9 @@ dependencies = [
"lxml==6.1.0",
"msgraph-sdk==1.56.0",
"opencontainers==0.0.15",
"packaging==26.2",
"packaging==26.1",
"paramiko==4.0.0",
"psycopg[c,pool]==3.3.4",
"psycopg[c,pool]==3.3.3",
"pydantic-scim==0.0.8",
"pydantic==2.13.3",
"pyjwt==2.11.0",
@@ -66,7 +66,7 @@ dependencies = [
"ua-parser==1.0.2",
"unidecode==1.4.0",
"urllib3<3",
"uvicorn[standard]==0.46.0",
"uvicorn[standard]==0.45.0",
"watchdog==6.0.0",
"webauthn==2.7.1",
"wsproto==1.3.2",
@@ -76,7 +76,7 @@ dependencies = [
[dependency-groups]
dev = [
"aws-cdk-lib==2.251.0",
"aws-cdk-lib==2.250.0",
"bandit==1.9.4",
"black==26.3.1",
"bpython==0.26",

View File

@@ -34608,12 +34608,10 @@ components:
properties:
brand:
type: string
nullable: true
family:
type: string
model:
type: string
nullable: true
required:
- brand
- family
@@ -34626,16 +34624,12 @@ components:
type: string
major:
type: string
nullable: true
minor:
type: string
nullable: true
patch:
type: string
nullable: true
patch_minor:
type: string
nullable: true
required:
- family
- major
@@ -36050,8 +36044,6 @@ components:
path:
type: string
minLength: 1
context:
type: string
Brand:
type: object
description: Brand Serializer
@@ -37191,7 +37183,6 @@ components:
flows_refresh_others:
type: boolean
description: Refresh other tabs after successful authentication.
deprecated: true
required:
- core_default_app_access
- enterprise_audit_include_expanded_diff
@@ -39040,13 +39031,7 @@ components:
EventsRequestedEnum:
enum:
- https://schemas.openid.net/secevent/caep/event-type/session-revoked
- https://schemas.openid.net/secevent/caep/event-type/token-claims-change
- https://schemas.openid.net/secevent/caep/event-type/credential-change
- https://schemas.openid.net/secevent/caep/event-type/assurance-level-change
- https://schemas.openid.net/secevent/caep/event-type/device-compliance-change
- https://schemas.openid.net/secevent/caep/event-type/session-established
- https://schemas.openid.net/secevent/caep/event-type/session-presented
- https://schemas.openid.net/secevent/caep/event-type/risk-level-change
- https://schemas.openid.net/secevent/ssf/event-type/verification
type: string
ExpiringBaseGrantModel:
@@ -51202,7 +51187,6 @@ components:
flows_refresh_others:
type: boolean
description: Refresh other tabs after successful authentication.
deprecated: true
required:
- core_default_app_access
- enterprise_audit_include_expanded_diff
@@ -53643,7 +53627,7 @@ components:
type: string
redirect_uri_type:
allOf:
- $ref: '#/components/schemas/RedirectURITypeEnum'
- $ref: '#/components/schemas/RedirectUriTypeEnum'
default: authorization
required:
- matching_mode
@@ -53659,12 +53643,12 @@ components:
minLength: 1
redirect_uri_type:
allOf:
- $ref: '#/components/schemas/RedirectURITypeEnum'
- $ref: '#/components/schemas/RedirectUriTypeEnum'
default: authorization
required:
- matching_mode
- url
RedirectURITypeEnum:
RedirectUriTypeEnum:
enum:
- authorization
- logout
@@ -55634,7 +55618,6 @@ components:
- enabled
- paused
- disabled
- disabled_deleted
type: string
Schedule:
type: object
@@ -55985,7 +55968,6 @@ components:
flows_refresh_others:
type: boolean
description: Refresh other tabs after successful authentication.
deprecated: true
required:
- core_default_app_access
- enterprise_audit_include_expanded_diff
@@ -56074,7 +56056,6 @@ components:
flows_refresh_others:
type: boolean
description: Refresh other tabs after successful authentication.
deprecated: true
required:
- core_default_app_access
- enterprise_audit_include_expanded_diff

View File

@@ -23,23 +23,10 @@ struct Cli {
#[derive(Debug, FromArgs, PartialEq)]
#[argh(subcommand)]
enum Command {
#[cfg(feature = "core")]
AllInOne(AllInOne),
#[cfg(feature = "core")]
Server(server::Cli),
#[cfg(feature = "core")]
Worker(worker::Cli),
}
#[derive(Debug, FromArgs, PartialEq)]
/// Run both the authentik server and worker.
#[argh(subcommand, name = "allinone")]
#[expect(
clippy::empty_structs_with_brackets,
reason = "argh doesn't support unit structs"
)]
pub(crate) struct AllInOne {}
fn main() -> Result<()> {
let tracing_crude = ak_tracing::install_crude();
info!(version = authentik_full_version(), "authentik is starting");
@@ -47,10 +34,6 @@ fn main() -> Result<()> {
let cli: Cli = argh::from_env();
match &cli.command {
#[cfg(feature = "core")]
Command::AllInOne(_) => Mode::set(Mode::AllInOne)?,
#[cfg(feature = "core")]
Command::Server(_) => Mode::set(Mode::Server)?,
#[cfg(feature = "core")]
Command::Worker(_) => Mode::set(Mode::Worker)?,
}
@@ -93,16 +76,6 @@ fn main() -> Result<()> {
}
match cli.command {
#[cfg(feature = "core")]
Command::AllInOne(_) => {
server::start(server::Cli::default(), &mut tasks).await?;
let workers = worker::start(worker::Cli::default(), &mut tasks)?;
metrics.workers.store(Some(workers));
}
#[cfg(feature = "core")]
Command::Server(args) => {
server::start(args, &mut tasks).await?;
}
#[cfg(feature = "core")]
Command::Worker(args) => {
let workers = worker::start(args, &mut tasks)?;

View File

@@ -2,7 +2,6 @@ use std::{env::temp_dir, os::unix, path::PathBuf, sync::Arc};
use ak_axum::{router::wrap_router, server};
use ak_common::{
Mode,
arbiter::{Arbiter, Tasks},
config,
};
@@ -78,20 +77,25 @@ pub(crate) fn start(tasks: &mut Tasks) -> Result<Arc<Metrics>> {
.name(&format!("{}::run_upkeep", module_path!()))
.spawn(run_upkeep(arbiter, Arc::clone(&metrics)))?;
// Only run HTTP server in worker mode, in server or allinone mode, they're handled by the
// server.
if Mode::get() == Mode::Worker {
for addr in config::get().listen.metrics.iter().copied() {
server::start_plain(tasks, "metrics", router.clone(), addr)?;
}
server::start_unix(
for addr in config::get().listen.metrics.iter().copied() {
server::start_plain(
tasks,
"metrics",
router,
unix::net::SocketAddr::from_pathname(socket_path())?,
router.clone(),
addr,
config::get().debug, /* Allow failure in case the server is running on the same
* machine, like in dev */
)?;
}
server::start_unix(
tasks,
"metrics",
router,
unix::net::SocketAddr::from_pathname(socket_path())?,
config::get().debug, /* Allow failure in case the server is running on the same machine,
* like in dev */
)?;
Ok(metrics)
}

View File

@@ -1,124 +1,5 @@
use std::{env::temp_dir, path::PathBuf, process::Stdio, sync::Arc};
use ak_common::{Arbiter, Tasks, config};
use argh::FromArgs;
use eyre::{Result, eyre};
use nix::{
sys::signal::{Signal, kill},
unistd::Pid,
};
use tokio::{
process::{Child, Command},
sync::Mutex,
time::{Duration, sleep, timeout},
};
use tracing::{info, warn};
#[derive(Debug, Default, FromArgs, PartialEq, Eq)]
/// Run the authentik server.
#[argh(subcommand, name = "server")]
#[expect(
clippy::empty_structs_with_brackets,
reason = "argh doesn't support unit structs"
)]
pub(crate) struct Cli {}
use std::{env::temp_dir, path::PathBuf};
pub(crate) fn socket_path() -> PathBuf {
temp_dir().join("authentik.sock")
}
pub(crate) struct Server {
server: Mutex<Child>,
}
impl Server {
async fn new() -> Result<Self> {
info!("starting server");
let server = if config::get().debug && which::which("authentik-server").is_err() {
let build_status = Command::new("go")
.args(["build", "-o", "server", "./cmd/server"])
.stdin(Stdio::null())
.status()
.await?;
if !build_status.success() {
return Err(eyre!("golang server failed to compile"));
}
Command::new("./server")
.kill_on_drop(true)
.stdin(Stdio::null())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.spawn()?
} else {
Command::new("authentik-server")
.kill_on_drop(true)
.stdin(Stdio::null())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.spawn()?
};
Ok(Self {
server: Mutex::new(server),
})
}
async fn shutdown(&self) -> Result<()> {
info!("shutting down server");
let mut server = self.server.lock().await;
if let Some(id) = server.id() {
kill(Pid::from_raw(id.cast_signed()), Signal::SIGINT)?;
}
timeout(Duration::from_secs(1), server.wait()).await??;
Ok(())
}
async fn is_alive(&self) -> bool {
let try_wait = self.server.lock().await.try_wait();
match try_wait {
Ok(Some(code)) => {
warn!(?code, "server has exited");
false
}
Ok(None) => true,
Err(err) => {
warn!(
?err,
"failed to check the status of server process, ignoring"
);
true
}
}
}
}
async fn watch_server(arbiter: Arbiter, server: Arc<Server>) -> Result<()> {
info!("starting server watcher");
loop {
tokio::select! {
() = sleep(Duration::from_secs(5)) => {
if !server.is_alive().await {
return Err(eyre!("server has exited unexpectedly"));
}
}
() = arbiter.shutdown() => {
server.shutdown().await?;
return Ok(());
}
}
}
}
pub(crate) async fn start(_cli: Cli, tasks: &mut Tasks) -> Result<Arc<Server>> {
let arbiter = tasks.arbiter();
let server = Arc::new(Server::new().await?);
tasks
.build_task()
.name(&format!("{}::watch_server", module_path!()))
.spawn(watch_server(arbiter.clone(), Arc::clone(&server)))?;
Ok(server)
}

View File

@@ -54,7 +54,6 @@ const INITIAL_WORKER_ID: usize = 1000;
static INITIAL_WORKER_READY: AtomicBool = AtomicBool::new(false);
pub(crate) struct Worker {
worker_id: usize,
worker: Child,
client: Client<UnixSocketConnector<PathBuf>, Body>,
socket_path: PathBuf,
@@ -76,7 +75,6 @@ impl Worker {
.build(UnixSocketConnector::new(socket_path.clone()));
Ok(Self {
worker_id,
worker: cmd
.kill_on_drop(true)
.stdin(Stdio::null())
@@ -110,7 +108,7 @@ impl Worker {
self.shutdown(Signal::SIGINT).await
}
#[instrument(skip(self), fields(worker_id = self.worker_id))]
#[instrument(skip_all)]
fn is_alive(&mut self) -> bool {
let try_wait = self.worker.try_wait();
match try_wait {
@@ -135,52 +133,34 @@ impl Worker {
result.is_ok()
}
#[instrument(skip(self), fields(worker_id = self.worker_id))]
#[instrument(skip_all)]
async fn health_live(&self) -> Result<bool> {
trace!("sending health live request to worker");
let req = Request::builder()
.method("GET")
.uri("http://localhost:8000/-/health/live/")
.header(HOST, "localhost")
.body(Body::from(""))?;
Ok(self
.client
.request(req)
.await
.inspect_err(|err| warn!(?err, "failed to send health live request to worker"))?
.status()
.is_success())
Ok(self.client.request(req).await?.status().is_success())
}
#[instrument(skip(self), fields(worker_id = self.worker_id))]
#[instrument(skip_all)]
async fn health_ready(&self) -> Result<bool> {
trace!("sending health ready request to worker");
let req = Request::builder()
.method("GET")
.uri("http://localhost:8000/-/health/ready/")
.header(HOST, "localhost")
.body(Body::from(""))?;
Ok(self
.client
.request(req)
.await
.inspect_err(|err| warn!(?err, "failed to send health ready request to worker"))?
.status()
.is_success())
Ok(self.client.request(req).await?.status().is_success())
}
#[instrument(skip(self), fields(worker_id = self.worker_id))]
#[instrument(skip_all)]
async fn notify_metrics(&self) -> Result<()> {
trace!("sending metrics request to worker");
let req = Request::builder()
.method("GET")
.uri("http://localhost:8000/-/metrics/")
.header(HOST, "localhost")
.body(Body::from(""))?;
self.client
.request(req)
.await
.inspect_err(|err| warn!(?err, "failed to send metrics request to worker"))?;
self.client.request(req).await?;
Ok(())
}
}
@@ -343,7 +323,14 @@ pub(crate) fn start(_cli: Cli, tasks: &mut Tasks) -> Result<Arc<Workers>> {
let router = healthcheck::build_router(Arc::clone(&workers));
for addr in config::get().listen.http.iter().copied() {
ak_axum::server::start_plain(tasks, "worker", router.clone(), addr)?;
ak_axum::server::start_plain(
tasks,
"worker",
router.clone(),
addr,
config::get().debug, /* Allow failure in case the server is running on the same
* machine, like in dev. */
)?;
}
ak_axum::server::start_unix(
@@ -351,6 +338,8 @@ pub(crate) fn start(_cli: Cli, tasks: &mut Tasks) -> Result<Arc<Workers>> {
"worker",
router,
unix::net::SocketAddr::from_pathname(socket_path())?,
config::get().debug, /* Allow failure in case the server is running on the same
* machine, like in dev. */
)?;
}

View File

@@ -111,12 +111,8 @@ class TestFlowsEnroll(SeleniumTestCase):
identification_stage = self.get_shadow_root("ak-stage-identification", flow_executor)
wait = WebDriverWait(identification_stage, self.wait_timeout)
wait.until(
ec.presence_of_element_located((By.CSS_SELECTOR, "a[data-ouia-component-id='enroll']"))
)
identification_stage.find_element(
By.CSS_SELECTOR, "a[data-ouia-component-id='enroll']"
).click()
wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "a[ouiaId='enroll']")))
identification_stage.find_element(By.CSS_SELECTOR, "a[ouiaId='enroll']").click()
# First prompt stage
flow_executor = self.get_shadow_root("ak-flow-executor")

View File

@@ -27,14 +27,8 @@ class TestFlowsRecovery(SeleniumTestCase):
identification_stage = self.get_shadow_root("ak-stage-identification", flow_executor)
wait = WebDriverWait(identification_stage, self.wait_timeout)
wait.until(
ec.presence_of_element_located(
(By.CSS_SELECTOR, "a[data-ouia-component-id='recovery']")
)
)
identification_stage.find_element(
By.CSS_SELECTOR, "a[data-ouia-component-id='recovery']"
).click()
wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "a[ouiaId='recovery']")))
identification_stage.find_element(By.CSS_SELECTOR, "a[ouiaId='recovery']").click()
# First prompt stage
flow_executor = self.get_shadow_root("ak-flow-executor")

View File

@@ -1,16 +1,10 @@
from os import unlink, write
from sys import stderr
from tempfile import mkstemp
from urllib.parse import urlencode
from channels.testing import ChannelsLiveServerTestCase
from django.apps import apps
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from django.urls import reverse
from docker.types import Healthcheck
from dramatiq import get_broker
from structlog.stdlib import get_logger
from yaml import safe_dump
from authentik.core.apps import Setup
from authentik.core.models import User
@@ -53,84 +47,6 @@ class E2ETestMixin(DockerTestCase):
print("::endgroup::", file=stderr)
super().tearDown()
def url(self, view: str, query: dict | None = None, **kwargs) -> str:
"""reverse `view` with `**kwargs` into full URL using live_server_url"""
url = self.live_server_url + reverse(view, kwargs=kwargs)
if query:
return url + "?" + urlencode(query)
return url
class SSLLiveMixin(DockerTestCase):
"""Mixin to provide an SSL-enabled webserver for integration/e2e tests that require it.
Overrides `live_server_url` and as such other all usual helper functions will return an HTTPS
URL. Certificate is self-signed and random on each run."""
def setUp(self):
super().setUp()
self._setup_traefik()
def tearDown(self):
super().tearDown()
unlink(self._traefik_config)
@property
def live_server_url(self):
return f"https://{self.host}:{self._traefik_port}"
def _setup_traefik(self):
config = {
"http": {
"routers": {
"authentik": {
"rule": "PathPrefix(`/`)",
"entryPoints": ["websecure"],
"service": "authentik",
"tls": {},
}
},
"services": {
"authentik": {"loadBalancer": {"servers": [{"url": super().live_server_url}]}}
},
}
}
fd, self._traefik_config = mkstemp()
write(fd, safe_dump(config).encode())
traefik = self.run_container(
image="docker.io/library/traefik:3.1",
command=[
"--providers.file.filename=/etc/traefik/dynamic.yml",
"--providers.file.watch=true",
"--entrypoints.websecure.address=:9443",
"--log.level=DEBUG",
"--api=true",
"--api.dashboard=true",
"--api.insecure=true",
"--ping=true",
],
healthcheck=Healthcheck(
test=["CMD", "traefik", "healthcheck", "--ping"],
interval=5 * 1_000 * 1_000_000,
start_period=1 * 1_000 * 1_000_000,
),
ports={
"9443": None,
},
volumes={
self._traefik_config: {
"bind": "/etc/traefik/dynamic.yml",
}
},
)
# {
# "8443/tcp": [
# {"HostIp": "0.0.0.0", "HostPort": "8443"},
# {"HostIp": "::", "HostPort": "8443"},
# ],
# }
self._traefik_port = traefik.ports["9443/tcp"][0]["HostPort"]
class E2ETestCase(E2ETestMixin, StaticLiveServerTestCase):
"""E2E Test case with django static live server"""

View File

@@ -1,19 +1,17 @@
from os import makedirs
from pathlib import Path
from time import sleep
from typing import Any
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as ec
from authentik.blueprints.tests import apply_blueprint, reconcile_app
from authentik.providers.oauth2.models import OAuth2Provider
from tests.live import SSLLiveMixin
from tests.openid_conformance.conformance import Conformance
from tests.selenium import SeleniumTestCase
class TestOpenIDConformance(SSLLiveMixin, SeleniumTestCase):
class TestOpenIDConformance(SeleniumTestCase):
conformance: Conformance
@@ -61,28 +59,32 @@ class TestOpenIDConformance(SSLLiveMixin, SeleniumTestCase):
},
"consent": {},
}
self.test_variant = {
"server_metadata": "discovery",
"client_registration": "static_client",
}
def run_test(
self, test_name: str, test_plan_config: dict[str, Any], test_variant: dict[str, Any]
):
def run_test(self, test_name: str, test_plan_config: dict):
# Create a Conformance instance...
self.conformance = Conformance(f"https://{self.host}:8443/", None, verify_ssl=False)
test_plan = self.conformance.create_test_plan(
test_name,
test_plan_config,
test_variant,
self.test_variant,
)
plan_id = test_plan["id"]
for test in test_plan["modules"]:
# Fetch name and variant of the next test to run
module_name = test["testModule"]
variant = test["variant"]
module_instance = self.conformance.create_test_from_plan_with_variant(
plan_id, module_name, variant
)
module_id = module_instance["id"]
self.run_single_test(module_id)
self.conformance.wait_for_state(module_id, ["FINISHED"], timeout=self.wait_timeout)
with self.subTest(test["testModule"], **test["variant"]):
# Fetch name and variant of the next test to run
module_name = test["testModule"]
variant = test["variant"]
module_instance = self.conformance.create_test_from_plan_with_variant(
plan_id, module_name, variant
)
module_id = module_instance["id"]
self.run_single_test(module_id)
self.conformance.wait_for_state(module_id, ["FINISHED"], timeout=self.wait_timeout)
sleep(2)
self.conformance.export_html(plan_id, Path(__file__).parent / "exports")

View File

@@ -2,14 +2,14 @@ services:
mongodb:
image: mongo:6.0.13
nginx:
image: ghcr.io/beryju/oidc-conformance-suite-nginx:v5.1.43
image: ghcr.io/beryju/oidc-conformance-suite-nginx:v5.1.41
ports:
- "8443:8443"
- "8444:8444"
depends_on:
- server
server:
image: ghcr.io/beryju/oidc-conformance-suite-server:v5.1.43
image: ghcr.io/beryju/oidc-conformance-suite-server:v5.1.41
ports:
- "9999:9999"
extra_hosts:
@@ -19,8 +19,8 @@ services:
-Xdebug -Xrunjdwp:transport=dt_socket,address=*:9999,server=y,suspend=n
-jar /server/fapi-test-suite.jar
-Djdk.tls.maxHandshakeMessageSize=65536
--fintechlabs.base_url=https://localhost:8443
--fintechlabs.base_mtls_url=https://localhost:8444
--fintechlabs.base_url=https://host.docker.internal:8443
--fintechlabs.base_mtls_url=https://host.docker.internal:8444
--fintechlabs.devmode=true
--fintechlabs.startredir=true
links:

View File

@@ -0,0 +1,10 @@
from tests.decorators import retry
from tests.openid_conformance.base import TestOpenIDConformance
class TestOpenIDConformanceBasic(TestOpenIDConformance):
@retry()
def test_oidcc_basic_certification_test(self):
test_plan_name = "oidcc-basic-certification-test-plan"
self.run_test(test_plan_name, self.test_plan_config)

View File

@@ -0,0 +1,10 @@
from tests.decorators import retry
from tests.openid_conformance.base import TestOpenIDConformance
class TestOpenIDConformanceImplicit(TestOpenIDConformance):
@retry()
def test_oidcc_implicit_certification_test_plan(self):
test_plan_name = "oidcc-implicit-certification-test-plan"
self.run_test(test_plan_name, self.test_plan_config)

View File

@@ -1,39 +0,0 @@
from unittest.mock import patch
import urllib3
from authentik.flows.models import Flow
from authentik.lib.utils.http import get_http_session as real_get_http_session
from authentik.providers.oauth2.models import OAuth2LogoutMethod, OAuth2Provider
from tests.decorators import retry
from tests.openid_conformance.base import TestOpenIDConformance
def _insecure_http_session():
session = real_get_http_session()
session.verify = False
return session
@patch("authentik.providers.oauth2.tasks.get_http_session", _insecure_http_session)
class TestOpenIDConformanceBackchannel(TestOpenIDConformance):
def setUp(self):
super().setUp()
OAuth2Provider.objects.filter(name__startswith="oidc-conformance-").update(
invalidation_flow=Flow.objects.get(slug="default-invalidation-flow"),
logout_method=OAuth2LogoutMethod.BACKCHANNEL,
logout_uri="https://localhost:8443/test/a/authentik/backchannel_logout",
)
# We are unable to use https for this at the current time
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
@retry()
def test_oidcc_backchannel_logout_certification_test_plan(self):
self.run_test(
"oidcc-backchannel-rp-initiated-logout-certification-test-plan",
self.test_plan_config,
{
"client_registration": "static_client",
"response_type": "code",
},
)

View File

@@ -1,16 +0,0 @@
from tests.decorators import retry
from tests.openid_conformance.base import TestOpenIDConformance
class TestOpenIDConformanceBasic(TestOpenIDConformance):
@retry()
def test_oidcc_basic_certification_test(self):
self.run_test(
"oidcc-basic-certification-test-plan",
self.test_plan_config,
{
"server_metadata": "discovery",
"client_registration": "static_client",
},
)

View File

@@ -1,26 +0,0 @@
from authentik.flows.models import Flow
from authentik.providers.oauth2.models import OAuth2LogoutMethod, OAuth2Provider
from tests.decorators import retry
from tests.openid_conformance.base import TestOpenIDConformance
class TestOpenIDConformanceFrontchannel(TestOpenIDConformance):
def setUp(self):
super().setUp()
OAuth2Provider.objects.filter(name__startswith="oidc-conformance-").update(
invalidation_flow=Flow.objects.get(slug="default-invalidation-flow"),
logout_method=OAuth2LogoutMethod.FRONTCHANNEL,
logout_uri="https://localhost:8443/test/a/authentik/frontchannel_logout",
)
@retry()
def test_oidcc_frontchannel_logout_certification_test_plan(self):
self.run_test(
"oidcc-frontchannel-rp-initiated-logout-certification-test-plan",
self.test_plan_config,
{
"client_registration": "static_client",
"response_type": "code",
},
)

View File

@@ -1,16 +0,0 @@
from tests.decorators import retry
from tests.openid_conformance.base import TestOpenIDConformance
class TestOpenIDConformanceImplicit(TestOpenIDConformance):
@retry()
def test_oidcc_implicit_certification_test_plan(self):
self.run_test(
"oidcc-implicit-certification-test-plan",
self.test_plan_config,
{
"server_metadata": "discovery",
"client_registration": "static_client",
},
)

View File

@@ -1,24 +0,0 @@
from authentik.flows.models import Flow
from authentik.providers.oauth2.models import OAuth2Provider
from tests.decorators import retry
from tests.openid_conformance.base import TestOpenIDConformance
class TestOpenIDConformanceRPInitiated(TestOpenIDConformance):
def setUp(self):
super().setUp()
OAuth2Provider.objects.filter(name__startswith="oidc-conformance-").update(
invalidation_flow=Flow.objects.get(slug="default-invalidation-flow"),
)
@retry()
def test_oidcc_rp_initiated_certification_test_plan(self):
self.run_test(
"oidcc-rp-initiated-logout-certification-test-plan",
self.test_plan_config,
{
"client_registration": "static_client",
"response_type": "code",
},
)

View File

@@ -1,49 +0,0 @@
from authentik.core.models import Application
from authentik.crypto.models import CertificateKeyPair
from authentik.enterprise.providers.ssf.models import SSFProvider
from authentik.lib.generators import generate_id
from tests.decorators import retry
from tests.live import SSLLiveMixin
from tests.openid_conformance.base import TestOpenIDConformance
class TestOpenIDConformanceSSFTransmitter(TestOpenIDConformance, SSLLiveMixin):
def setUp(self):
super().setUp()
self.provider = SSFProvider.objects.create(
name=generate_id(),
signing_key=CertificateKeyPair.objects.get(name="authentik Self-signed Certificate"),
backchannel_application=Application.objects.get(slug="oidc-conformance-1"),
push_verify_certificates=False,
)
@retry()
def test_openid_ssf_transmitter_test_plan(self):
iss = self.url(
"authentik_providers_ssf:configuration",
application_slug="oidc-conformance-1",
)
self.run_test(
"openid-ssf-transmitter-test-plan",
{
"alias": "authentik",
"description": "authentik",
"ssf": {
"transmitter": {
"issuer": iss,
"configuration_metadata_endpoint": iss,
"access_token": self.provider.token.key,
}
},
},
test_variant={
"client_auth_type": "client_secret_post",
"ssf_server_metadata": "static",
"server_metadata": "static",
"ssf_auth_mode": "static",
"ssf_delivery_mode": "push",
"ssf_profile": "caep_interop",
"client_registration": "static_client",
},
)

View File

@@ -5,8 +5,10 @@ from json import JSONDecodeError, dumps, loads
from pathlib import Path
from tempfile import gettempdir
from time import sleep
from urllib.parse import urlencode
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from django.urls import reverse
from docker.models.containers import Container
from requests import RequestException
from selenium import webdriver
@@ -129,6 +131,13 @@ class SeleniumTestMixin(E2ETestMixin):
f"HTML: {self.driver.page_source[:1000]}"
) from exc
def url(self, view: str, query: dict | None = None, **kwargs) -> str:
"""reverse `view` with `**kwargs` into full URL using live_server_url"""
url = self.live_server_url + reverse(view, kwargs=kwargs)
if query:
return url + "?" + urlencode(query)
return url
def if_user_url(self, path: str | None = None) -> str:
"""same as self.url() but show URL in shell"""
url = self.url("authentik_core:if-user")

146
uv.lock generated
View File

@@ -318,11 +318,11 @@ requires-dist = [
{ name = "argon2-cffi", specifier = "==25.1.0" },
{ name = "cachetools", specifier = "==7.0.6" },
{ name = "channels", specifier = "==4.3.2" },
{ name = "cryptography", specifier = "==48.0.0" },
{ name = "cryptography", specifier = "==47.0.0" },
{ name = "dacite", specifier = "==1.9.2" },
{ name = "deepmerge", specifier = "==2.0" },
{ name = "defusedxml", specifier = "==0.7.1" },
{ name = "django", specifier = "==5.2.14" },
{ name = "django", specifier = "==5.2.13" },
{ name = "django-channels-postgres", editable = "packages/django-channels-postgres" },
{ name = "django-countries", specifier = "==8.2.0" },
{ name = "django-dramatiq-postgres", editable = "packages/django-dramatiq-postgres" },
@@ -355,9 +355,9 @@ requires-dist = [
{ name = "lxml", specifier = "==6.1.0" },
{ name = "msgraph-sdk", specifier = "==1.56.0" },
{ name = "opencontainers", git = "https://github.com/vsoch/oci-python?rev=ceb4fcc090851717a3069d78e85ceb1e86c2740c" },
{ name = "packaging", specifier = "==26.2" },
{ name = "packaging", specifier = "==26.1" },
{ name = "paramiko", specifier = "==4.0.0" },
{ name = "psycopg", extras = ["c", "pool"], specifier = "==3.3.4" },
{ name = "psycopg", extras = ["c", "pool"], specifier = "==3.3.3" },
{ name = "pydantic", specifier = "==2.13.3" },
{ name = "pydantic-scim", specifier = "==0.0.8" },
{ name = "pyjwt", specifier = "==2.11.0" },
@@ -375,7 +375,7 @@ requires-dist = [
{ name = "ua-parser", specifier = "==1.0.2" },
{ name = "unidecode", specifier = "==1.4.0" },
{ name = "urllib3", specifier = "<3" },
{ name = "uvicorn", extras = ["standard"], specifier = "==0.46.0" },
{ name = "uvicorn", extras = ["standard"], specifier = "==0.45.0" },
{ name = "watchdog", specifier = "==6.0.0" },
{ name = "webauthn", specifier = "==2.7.1" },
{ name = "wsproto", specifier = "==1.3.2" },
@@ -385,7 +385,7 @@ requires-dist = [
[package.metadata.requires-dev]
dev = [
{ name = "aws-cdk-lib", specifier = "==2.251.0" },
{ name = "aws-cdk-lib", specifier = "==2.250.0" },
{ name = "bandit", specifier = "==1.9.4" },
{ name = "black", specifier = "==26.3.1" },
{ name = "bpython", specifier = "==0.26" },
@@ -481,21 +481,21 @@ wheels = [
[[package]]
name = "aws-cdk-cloud-assembly-schema"
version = "53.20.0"
version = "53.9.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "jsii" },
{ name = "publication" },
{ name = "typeguard" },
]
sdist = { url = "https://files.pythonhosted.org/packages/bf/8a/3b94fbba0d8ca4123eb015ea12a1c8fc5193a1eddcc4d69d31b9575546c8/aws_cdk_cloud_assembly_schema-53.20.0.tar.gz", hash = "sha256:c5d884f7211fd18cc0ce8c4349902ab6a6b3cd8f3c2259c56616a59218c221eb", size = 212292, upload-time = "2026-04-30T11:34:29.29Z" }
sdist = { url = "https://files.pythonhosted.org/packages/7c/95/afd7bd310b7eb64bfa51700e283a297a57692d8ca5a3a289d9bfe6ca92f6/aws_cdk_cloud_assembly_schema-53.9.0.tar.gz", hash = "sha256:0a9a537c6cdfebbf3e97f250aaff92d811a6be94394a4673784a50660889a3cb", size = 210905, upload-time = "2026-03-26T18:44:01.266Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b5/eb/7bf100ad3603d5fbbebb49b3d48add58abda59c3fa44a4a7eae40da5b8d0/aws_cdk_cloud_assembly_schema-53.20.0-py3-none-any.whl", hash = "sha256:b68ea0754ec830751d4a375ebe84d4c077dc488a2498c3607a2f998bc7e91d73", size = 212140, upload-time = "2026-04-30T11:34:27.044Z" },
{ url = "https://files.pythonhosted.org/packages/6e/d0/4dbd2020f8fa6991f6469ed1851981f3160965393ea4cef19a59bf8c938b/aws_cdk_cloud_assembly_schema-53.9.0-py3-none-any.whl", hash = "sha256:41e75541d3ea3d46edbe67ade491d974f0472ac62c3856596e3029fe384a9a44", size = 210649, upload-time = "2026-03-26T18:43:59.951Z" },
]
[[package]]
name = "aws-cdk-lib"
version = "2.251.0"
version = "2.250.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "aws-cdk-asset-awscli-v1" },
@@ -506,9 +506,9 @@ dependencies = [
{ name = "publication" },
{ name = "typeguard" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b8/6c/d60d96e1848aabf1882e6a1d30a27de4a592affc9437d6918848f0e06497/aws_cdk_lib-2.251.0.tar.gz", hash = "sha256:ed69e7ea6896c62ac2ce01857083601baf541d5d875370bee6d213d641e8921e", size = 49353237, upload-time = "2026-04-24T23:21:04.805Z" }
sdist = { url = "https://files.pythonhosted.org/packages/d5/7b/98c66b6ea6d4f84b70d86ec391bbcd2856a82b384a97adc223f36b36dfd6/aws_cdk_lib-2.250.0.tar.gz", hash = "sha256:6e5cb8def9208a45cede1376a81d7508b3889879ccc7e9cddaa4fd807da0b144", size = 49146123, upload-time = "2026-04-14T21:43:07.309Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d2/fb/ab682b518e3ca5d18b23b252832e0fade4e6617a2c0f2b0ae0d8d2e74312/aws_cdk_lib-2.251.0-py3-none-any.whl", hash = "sha256:a684f3461d096443ac688adbf559abe1af2d50dd5c8e0fa7dbf4a5f361702db8", size = 50035969, upload-time = "2026-04-24T23:20:18.952Z" },
{ url = "https://files.pythonhosted.org/packages/e9/e7/b64389b59215a42d0d7538a1d9a8006edb2eefb297ccbdcd4c55ff2cffba/aws_cdk_lib-2.250.0-py3-none-any.whl", hash = "sha256:427c9a062f350c16e301326fd6ca0440428f9cc0e85421aaa69a9afa57228acc", size = 49827287, upload-time = "2026-04-14T21:42:21.21Z" },
]
[[package]]
@@ -917,55 +917,55 @@ wheels = [
[[package]]
name = "cryptography"
version = "48.0.0"
version = "47.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9f/a9/db8f313fdcd85d767d4973515e1db101f9c71f95fced83233de224673757/cryptography-48.0.0.tar.gz", hash = "sha256:5c3932f4436d1cccb036cb0eaef46e6e2db91035166f1ad6505c3c9d5a635920", size = 832984, upload-time = "2026-05-04T22:59:38.133Z" }
sdist = { url = "https://files.pythonhosted.org/packages/ef/b2/7ffa7fe8207a8c42147ffe70c3e360b228160c1d85dc3faff16aaa3244c0/cryptography-47.0.0.tar.gz", hash = "sha256:9f8e55fe4e63613a5e1cc5819030f27b97742d720203a087802ce4ce9ceb52bb", size = 830863, upload-time = "2026-04-24T19:54:57.056Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/df/3d/01f6dd9190170a5a241e0e98c2d04be3664a9e6f5b9b872cde63aff1c3dd/cryptography-48.0.0-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:0c558d2cdffd8f4bbb30fc7134c74d2ca9a476f830bb053074498fbc86f41ed6", size = 8001587, upload-time = "2026-05-04T22:57:36.803Z" },
{ url = "https://files.pythonhosted.org/packages/b2/6e/e90527eef33f309beb811cf7c982c3aeffcce8e3edb178baa4ca3ae4a6fa/cryptography-48.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f5333311663ea94f75dd408665686aaf426563556bb5283554a3539177e03b8c", size = 4690433, upload-time = "2026-05-04T22:57:40.373Z" },
{ url = "https://files.pythonhosted.org/packages/90/04/673510ed51ddff56575f306cf1617d80411ee76831ccd3097599140efdfe/cryptography-48.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7995ef305d7165c3f11ae07f2517e5a4f1d5c18da1376a0a9ed496336b69e5f3", size = 4710620, upload-time = "2026-05-04T22:57:42.935Z" },
{ url = "https://files.pythonhosted.org/packages/14/d5/e9c4ef932c8d800490c34d8bd589d64a31d5890e27ec9e9ad532be893294/cryptography-48.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:40ba1f85eaa6959837b1d51c9767e230e14612eea4ef110ee8854ada22da1bf5", size = 4696283, upload-time = "2026-05-04T22:57:45.294Z" },
{ url = "https://files.pythonhosted.org/packages/0c/29/174b9dfb60b12d59ecfc6cfa04bc88c21b42a54f01b8aae09bb6e51e4c7f/cryptography-48.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:369a6348999f94bbd53435c894377b20ab95f25a9065c283570e70150d8abc3c", size = 5296573, upload-time = "2026-05-04T22:57:47.933Z" },
{ url = "https://files.pythonhosted.org/packages/95/38/0d29a6fd7d0d1373f0c0c88a04ba20e359b257753ac497564cd660fc1d55/cryptography-48.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a0e692c683f4df67815a2d258b324e66f4738bd7a96a218c826dce4f4bd05d8f", size = 4743677, upload-time = "2026-05-04T22:57:50.067Z" },
{ url = "https://files.pythonhosted.org/packages/30/be/eef653013d5c63b6a490529e0316f9ac14a37602965d4903efed1399f32b/cryptography-48.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:18349bbc56f4743c8b12dc32e2bccb2cf83ee8b69a3bba74ef8ae857e26b3d25", size = 4330808, upload-time = "2026-05-04T22:57:52.301Z" },
{ url = "https://files.pythonhosted.org/packages/84/9e/500463e87abb7a0a0f9f256ec21123ecde0a7b5541a15e840ea54551fd81/cryptography-48.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e8eac43dfca5c4cccc6dad9a80504436fca53bb9bc3100a2386d730fbe6b602", size = 4695941, upload-time = "2026-05-04T22:57:54.603Z" },
{ url = "https://files.pythonhosted.org/packages/e3/dc/7303087450c2ec9e7fbb750e17c2abfbc658f23cbd0e54009509b7cc4091/cryptography-48.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9ccdac7d40688ecb5a3b4a604b8a88c8002e3442d6c60aead1db2a89a041560c", size = 5252579, upload-time = "2026-05-04T22:57:57.207Z" },
{ url = "https://files.pythonhosted.org/packages/d0/c0/7101d3b7215edcdc90c45da544961fd8ed2d6448f77577460fa75a8443f7/cryptography-48.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:bd72e68b06bb1e96913f97dd4901119bc17f39d4586a5adf2d3e47bc2b9d58b5", size = 4743326, upload-time = "2026-05-04T22:57:59.535Z" },
{ url = "https://files.pythonhosted.org/packages/ac/d8/5b833bad13016f562ab9d063d68199a4bd121d18458e439515601d3357ec/cryptography-48.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:59baa2cb386c4f0b9905bd6eb4c2a79a69a128408fd31d32ca4d7102d4156321", size = 4826672, upload-time = "2026-05-04T22:58:01.996Z" },
{ url = "https://files.pythonhosted.org/packages/98/e1/7074eb8bf3c135558c73fc2bcf0f5633f912e6fb87e868a55c454080ef09/cryptography-48.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9249e3cd978541d665967ac2cb2787fd6a62bddf1e75b3e347a594d7dacf4f74", size = 4972574, upload-time = "2026-05-04T22:58:03.968Z" },
{ url = "https://files.pythonhosted.org/packages/04/70/e5a1b41d325f797f39427aa44ef8baf0be500065ab6d8e10369d850d4a4f/cryptography-48.0.0-cp311-abi3-win32.whl", hash = "sha256:9c459db21422be75e2809370b829a87eb37f74cd785fc4aa9ea1e5f43b47cda4", size = 3294868, upload-time = "2026-05-04T22:58:06.467Z" },
{ url = "https://files.pythonhosted.org/packages/f4/ac/8ac51b4a5fc5932eb7ee5c517ba7dc8cd834f0048962b6b352f00f41ebf9/cryptography-48.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:5b012212e08b8dd5edc78ef54da83dd9892fd9105323b3993eff6bea65dc21d7", size = 3817107, upload-time = "2026-05-04T22:58:08.845Z" },
{ url = "https://files.pythonhosted.org/packages/6b/84/70e3feea9feea87fd7cbe77efb2712ae1e3e6edf10749dc6e95f4e60e455/cryptography-48.0.0-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:3cb07a3ed6431663cd321ea8a000a1314c74211f823e4177fefa2255e057d1ec", size = 7986556, upload-time = "2026-05-04T22:58:11.172Z" },
{ url = "https://files.pythonhosted.org/packages/89/6e/18e07a618bb5442ba10cf4df16e99c071365528aa570dfcb8c02e25a303b/cryptography-48.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8c7378637d7d88016fa6791c159f698b3d3eed28ebf844ac36b9dc04a14dae18", size = 4684776, upload-time = "2026-05-04T22:58:13.712Z" },
{ url = "https://files.pythonhosted.org/packages/be/6a/4ea3b4c6c6759794d5ee2103c304a5076dc4b19ae1f9fe47dba439e159e9/cryptography-48.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc90c0b39b2e3c65ef52c804b72e3c58f8a04ab2a1871272798e5f9572c17d20", size = 4698121, upload-time = "2026-05-04T22:58:16.448Z" },
{ url = "https://files.pythonhosted.org/packages/2f/59/6ff6ad6cae03bb887da2a5860b2c9805f8dac969ef01ce563336c49bd1d1/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:76341972e1eff8b4bea859f09c0d3e64b96ce931b084f9b9b7db8ef364c30eff", size = 4690042, upload-time = "2026-05-04T22:58:18.544Z" },
{ url = "https://files.pythonhosted.org/packages/ca/b4/fc334ed8cfd705aca282fe4d8f5ae64a8e0f74932e9feecb344610cf6e4d/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:55b7718303bf06a5753dcdccf2f3945cf18ad7bffde41b61226e4db31ab89a9c", size = 5282526, upload-time = "2026-05-04T22:58:20.75Z" },
{ url = "https://files.pythonhosted.org/packages/11/08/9f8c5386cc4cd90d8255c7cdd0f5baf459a08502a09de30dc51f553d38dc/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:a64697c641c7b1b2178e573cbc31c7c6684cd56883a478d75143dbb7118036db", size = 4733116, upload-time = "2026-05-04T22:58:23.627Z" },
{ url = "https://files.pythonhosted.org/packages/b8/77/99307d7574045699f8805aa500fa0fb83422d115b5400a064ddd306d7750/cryptography-48.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:561215ea3879cb1cbbf272867e2efda62476f240fb58c64de6b393ae19246741", size = 4316030, upload-time = "2026-05-04T22:58:25.581Z" },
{ url = "https://files.pythonhosted.org/packages/fd/36/a608b98337af3cb2aff4818e406649d30572b7031918b04c87d979495348/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ad64688338ed4bc1a6618076ba75fd7194a5f1797ac60b47afe926285adb3166", size = 4689640, upload-time = "2026-05-04T22:58:27.747Z" },
{ url = "https://files.pythonhosted.org/packages/dd/a6/825010a291b4438aecc1f568bc428189fc1175515223632477c07dc0a6df/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:906cbf0670286c6e0044156bc7d4af9cbb0ef6db9f73e52c3ec56ba6bdde5336", size = 5237657, upload-time = "2026-05-04T22:58:29.848Z" },
{ url = "https://files.pythonhosted.org/packages/b9/09/4e76a09b4caa29aad535ddc806f5d4c5d01885bd978bd984fbc6ca032cae/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:ea8990436d914540a40ab24b6a77c0969695ed52f4a4874c5137ccf7045a7057", size = 4732362, upload-time = "2026-05-04T22:58:32.009Z" },
{ url = "https://files.pythonhosted.org/packages/18/78/444fa04a77d0cb95f417dda20d450e13c56ba8e5220fc892a1658f44f882/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c18684a7f0cc9a3cb60328f496b8e3372def7c5d2df39ac267878b05565aaaae", size = 4819580, upload-time = "2026-05-04T22:58:34.254Z" },
{ url = "https://files.pythonhosted.org/packages/38/85/ea67067c70a1fd4be2c63d35eeed82658023021affccc7b17705f8527dd2/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9be5aafa5736574f8f15f262adc81b2a9869e2cfe9014d52a44633905b40d52c", size = 4963283, upload-time = "2026-05-04T22:58:36.376Z" },
{ url = "https://files.pythonhosted.org/packages/75/54/cc6d0f3deac3e81c7f847e8a189a12b6cdd65059b43dad25d4316abd849a/cryptography-48.0.0-cp314-cp314t-win32.whl", hash = "sha256:c17dfe85494deaeddc5ce251aebd1d60bbe6afc8b62071bb0b469431a000124f", size = 3270954, upload-time = "2026-05-04T22:58:38.791Z" },
{ url = "https://files.pythonhosted.org/packages/49/67/cc947e288c0758a4e5473d1dcb743037ab7785541265a969240b8885441a/cryptography-48.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27241b1dc9962e056062a8eef1991d02c3a24569c95975bd2322a8a52c6e5e12", size = 3797313, upload-time = "2026-05-04T22:58:40.746Z" },
{ url = "https://files.pythonhosted.org/packages/f2/63/61d4a4e1c6b6bab6ce1e213cd36a24c415d90e76d78c5eb8577c5541d2e8/cryptography-48.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:58d00498e8933e4a194f3076aee1b4a97dfec1a6da444535755822fe5d8b0b86", size = 7983482, upload-time = "2026-05-04T22:58:43.769Z" },
{ url = "https://files.pythonhosted.org/packages/d5/ac/f5b5995b87770c693e2596559ffafe195b4033a57f14a82268a2842953f3/cryptography-48.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:614d0949f4790582d2cc25553abd09dd723025f0c0e7c67376a1d77196743d6e", size = 4683266, upload-time = "2026-05-04T22:58:46.064Z" },
{ url = "https://files.pythonhosted.org/packages/ec/c6/8b14f67e18338fbc4adb76f66c001f5c3610b3e2d1837f268f47a347dbbb/cryptography-48.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ce4bfae76319a532a2dc68f82cc32f5676ee792a983187dac07183690e5c66f", size = 4696228, upload-time = "2026-05-04T22:58:48.22Z" },
{ url = "https://files.pythonhosted.org/packages/ea/73/f808fbae9514bd91b47875b003f13e284c8c6bdfd904b7944e803937eec1/cryptography-48.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:2eb992bbd4661238c5a397594c83f5b4dc2bc5b848c365c8f991b6780efcc5c7", size = 4689097, upload-time = "2026-05-04T22:58:50.9Z" },
{ url = "https://files.pythonhosted.org/packages/93/01/d86632d7d28db8ae83221995752eeb6639ffb374c2d22955648cf8d52797/cryptography-48.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:22a5cb272895dce158b2cacdfdc3debd299019659f42947dbdac6f32d68fe832", size = 5283582, upload-time = "2026-05-04T22:58:53.017Z" },
{ url = "https://files.pythonhosted.org/packages/02/e1/50edc7a50334807cc4791fc4a0ce7468b4a1416d9138eab358bfc9a3d70b/cryptography-48.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2b4d59804e8408e2fea7d1fbaf218e5ec984325221db76e6a241a9abd6cdd95c", size = 4730479, upload-time = "2026-05-04T22:58:55.611Z" },
{ url = "https://files.pythonhosted.org/packages/6f/af/99a582b1b1641ff5911ac559beb45097cf79efd4ead4657f578ef1af2d47/cryptography-48.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:984a20b0f62a26f48a3396c72e4bc34c66e356d356bf370053066b3b6d54634a", size = 4326481, upload-time = "2026-05-04T22:58:57.607Z" },
{ url = "https://files.pythonhosted.org/packages/90/ee/89aa26a06ef0a7d7611788ffd571a7c50e368cc6a4d5eef8b4884e866edb/cryptography-48.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5a5ed8fde7a1d09376ca0b40e68cd59c69fe23b1f9768bd5824f54681626032a", size = 4688713, upload-time = "2026-05-04T22:59:00.077Z" },
{ url = "https://files.pythonhosted.org/packages/70/ba/bcb1b0bb7a33d4c7c0c4d4c7874b4a62ae4f56113a5f4baefa362dfb1f0f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:8cd666227ef7af430aa5914a9910e0ddd703e75f039cef0825cd0da71b6b711a", size = 5238165, upload-time = "2026-05-04T22:59:02.317Z" },
{ url = "https://files.pythonhosted.org/packages/c9/70/ca4003b1ce5ca3dc3186ada51908c8a9b9ff7d5cab83cc0d43ee14ec144f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9071196d81abc88b3516ac8cdfad32e2b66dd4a5393a8e68a961e9161ddc6239", size = 4729947, upload-time = "2026-05-04T22:59:05.255Z" },
{ url = "https://files.pythonhosted.org/packages/44/a0/4ec7cf774207905aef1a8d11c3750d5a1db805eb380ee4e16df317870128/cryptography-48.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1e2d54c8be6152856a36f0882ab231e70f8ec7f14e93cf87db8a2ed056bf160c", size = 4822059, upload-time = "2026-05-04T22:59:07.802Z" },
{ url = "https://files.pythonhosted.org/packages/1e/75/a2e55f99c16fcac7b5d6c1eb19ad8e00799854d6be5ca845f9259eae1681/cryptography-48.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a5da777e32ffed6f85a7b2b3f7c5cbc88c146bfcd0a1d7baf5fcc6c52ee35dd4", size = 4960575, upload-time = "2026-05-04T22:59:09.851Z" },
{ url = "https://files.pythonhosted.org/packages/b8/23/6e6f32143ab5d8b36ca848a502c4bcd477ae75b9e1677e3530d669062578/cryptography-48.0.0-cp39-abi3-win32.whl", hash = "sha256:77a2ccbbe917f6710e05ba9adaa25fb5075620bf3ea6fb751997875aff4ae4bd", size = 3279117, upload-time = "2026-05-04T22:59:12.019Z" },
{ url = "https://files.pythonhosted.org/packages/9d/9a/0fea98a70cf1749d41d738836f6349d97945f7c89433a259a6c2642eefeb/cryptography-48.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:16cd65b9330583e4619939b3a3843eec1e6e789744bb01e7c7e2e62e33c239c8", size = 3792100, upload-time = "2026-05-04T22:59:14.884Z" },
{ url = "https://files.pythonhosted.org/packages/a4/98/40dfe932134bdcae4f6ab5927c87488754bf9eb79297d7e0070b78dd58e9/cryptography-47.0.0-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:160ad728f128972d362e714054f6ba0067cab7fb350c5202a9ae8ae4ce3ef1a0", size = 7912214, upload-time = "2026-04-24T19:53:03.864Z" },
{ url = "https://files.pythonhosted.org/packages/34/c6/2733531243fba725f58611b918056b277692f1033373dcc8bd01af1c05d4/cryptography-47.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b9a8943e359b7615db1a3ba587994618e094ff3d6fa5a390c73d079ce18b3973", size = 4644617, upload-time = "2026-04-24T19:53:06.909Z" },
{ url = "https://files.pythonhosted.org/packages/00/e3/b27be1a670a9b87f855d211cf0e1174a5d721216b7616bd52d8581d912ed/cryptography-47.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f5c15764f261394b22aef6b00252f5195f46f2ca300bec57149474e2538b31f8", size = 4668186, upload-time = "2026-04-24T19:53:09.053Z" },
{ url = "https://files.pythonhosted.org/packages/81/b9/8443cfe5d17d482d348cee7048acf502bb89a51b6382f06240fd290d4ca3/cryptography-47.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:9c59ab0e0fa3a180a5a9c59f3a5abe3ef90d474bc56d7fadfbe80359491b615b", size = 4651244, upload-time = "2026-04-24T19:53:11.217Z" },
{ url = "https://files.pythonhosted.org/packages/5d/5e/13ed0cdd0eb88ba159d6dd5ebfece8cb901dbcf1ae5ac4072e28b55d3153/cryptography-47.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:34b4358b925a5ea3e14384ca781a2c0ef7ac219b57bb9eacc4457078e2b19f92", size = 5252906, upload-time = "2026-04-24T19:53:13.532Z" },
{ url = "https://files.pythonhosted.org/packages/64/16/ed058e1df0f33d440217cd120d41d5dda9dd215a80b8187f68483185af82/cryptography-47.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0024b87d47ae2399165a6bfb20d24888881eeab83ae2566d62467c5ff0030ce7", size = 4701842, upload-time = "2026-04-24T19:53:15.618Z" },
{ url = "https://files.pythonhosted.org/packages/02/e0/3d30986b30fdbd9e969abbdf8ba00ed0618615144341faeb57f395a084fe/cryptography-47.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:1e47422b5557bb82d3fff997e8d92cff4e28b9789576984f08c248d2b3535d93", size = 4289313, upload-time = "2026-04-24T19:53:17.755Z" },
{ url = "https://files.pythonhosted.org/packages/df/fd/32db38e3ad0cb331f0691cb4c7a8a6f176f679124dee746b3af6633db4d9/cryptography-47.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:6f29f36582e6151d9686235e586dd35bb67491f024767d10b842e520dc6a07ac", size = 4650964, upload-time = "2026-04-24T19:53:20.062Z" },
{ url = "https://files.pythonhosted.org/packages/86/53/5395d944dfd48cb1f67917f533c609c34347185ef15eb4308024c876f274/cryptography-47.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:a9b761f012a943b7de0e828843c5688d0de94a0578d44d6c85a1bae32f87791f", size = 5207817, upload-time = "2026-04-24T19:53:22.498Z" },
{ url = "https://files.pythonhosted.org/packages/34/4f/e5711b28e1901f7d480a2b1b688b645aa4c77c73f10731ed17e7f7db3f0d/cryptography-47.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4e1de79e047e25d6e9f8cea71c86b4a53aced64134f0f003bbcbf3655fd172c8", size = 4701544, upload-time = "2026-04-24T19:53:24.356Z" },
{ url = "https://files.pythonhosted.org/packages/22/22/c8ddc25de3010fc8da447648f5a092c40e7a8fadf01dd6d255d9c0b9373d/cryptography-47.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef6b3634087f18d2155b1e8ce264e5345a753da2c5fa9815e7d41315c90f8318", size = 4783536, upload-time = "2026-04-24T19:53:26.665Z" },
{ url = "https://files.pythonhosted.org/packages/66/b6/d4a68f4ea999c6d89e8498579cba1c5fcba4276284de7773b17e4fa69293/cryptography-47.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:11dbb9f50a0f1bb9757b3d8c27c1101780efb8f0bdecfb12439c22a74d64c001", size = 4926106, upload-time = "2026-04-24T19:53:28.686Z" },
{ url = "https://files.pythonhosted.org/packages/54/ed/5f524db1fade9c013aa618e1c99c6ed05e8ffc9ceee6cda22fed22dda3f4/cryptography-47.0.0-cp311-abi3-win32.whl", hash = "sha256:7fda2f02c9015db3f42bb8a22324a454516ed10a8c29ca6ece6cdbb5efe2a203", size = 3258581, upload-time = "2026-04-24T19:53:31.058Z" },
{ url = "https://files.pythonhosted.org/packages/b2/dc/1b901990b174786569029f67542b3edf72ac068b6c3c8683c17e6a2f5363/cryptography-47.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:f5c3296dab66202f1b18a91fa266be93d6aa0c2806ea3d67762c69f60adc71aa", size = 3775309, upload-time = "2026-04-24T19:53:33.054Z" },
{ url = "https://files.pythonhosted.org/packages/14/88/7aa18ad9c11bc87689affa5ce4368d884b517502d75739d475fc6f4a03c7/cryptography-47.0.0-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:be12cb6a204f77ed968bcefe68086eb061695b540a3dd05edac507a3111b25f0", size = 7904299, upload-time = "2026-04-24T19:53:35.003Z" },
{ url = "https://files.pythonhosted.org/packages/07/55/c18f75724544872f234678fdedc871391722cb34a2aee19faa9f63100bb2/cryptography-47.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2ebd84adf0728c039a3be2700289378e1c164afc6748df1a5ed456767bef9ba7", size = 4631180, upload-time = "2026-04-24T19:53:37.517Z" },
{ url = "https://files.pythonhosted.org/packages/ee/65/31a5cc0eaca99cec5bafffe155d407115d96136bb161e8b49e0ef73f09a7/cryptography-47.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f68d6fbc7fbbcfb0939fea72c3b96a9f9a6edfc0e1b1d29778a2066030418b1", size = 4653529, upload-time = "2026-04-24T19:53:39.775Z" },
{ url = "https://files.pythonhosted.org/packages/e5/bc/641c0519a495f3bfd0421b48d7cd325c4336578523ccd76ea322b6c29c7a/cryptography-47.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:6651d32eff255423503aa276739da98c30f26c40cbeffcc6048e0d54ef704c0c", size = 4638570, upload-time = "2026-04-24T19:53:42.129Z" },
{ url = "https://files.pythonhosted.org/packages/2b/f2/300327b0a47f6dc94dd8b71b57052aefe178bb51745073d73d80604f11ab/cryptography-47.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:3fb8fa48075fad7193f2e5496135c6a76ac4b2aa5a38433df0a539296b377829", size = 5238019, upload-time = "2026-04-24T19:53:44.577Z" },
{ url = "https://files.pythonhosted.org/packages/e9/5a/5b5cf994391d4bf9d9c7efd4c66aabe4d95227256627f8fea6cff7dfadbd/cryptography-47.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:11438c7518132d95f354fa01a4aa2f806d172a061a7bed18cf18cbdacdb204d7", size = 4686832, upload-time = "2026-04-24T19:53:47.015Z" },
{ url = "https://files.pythonhosted.org/packages/dc/2c/ae950e28fd6475c852fc21a44db3e6b5bcc1261d1e370f2b6e42fa800fef/cryptography-47.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:8c1a736bbb3288005796c3f7ccb9453360d7fed483b13b9f468aea5171432923", size = 4269301, upload-time = "2026-04-24T19:53:48.97Z" },
{ url = "https://files.pythonhosted.org/packages/67/fb/6a39782e150ffe5cc1b0018cb6ddc48bf7ca62b498d7539ffc8a758e977d/cryptography-47.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:f1557695e5c2b86e204f6ce9470497848634100787935ab7adc5397c54abd7ab", size = 4638110, upload-time = "2026-04-24T19:53:51.011Z" },
{ url = "https://files.pythonhosted.org/packages/8e/d7/0b3c71090a76e5c203164a47688b697635ece006dcd2499ab3a4dbd3f0bd/cryptography-47.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:f9a034b642b960767fb343766ae5ba6ad653f2e890ddd82955aef288ffea8736", size = 5194988, upload-time = "2026-04-24T19:53:52.962Z" },
{ url = "https://files.pythonhosted.org/packages/63/33/63a961498a9df51721ab578c5a2622661411fc520e00bd83b0cc64eb20c4/cryptography-47.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:b1c76fca783aa7698eb21eb14f9c4aa09452248ee54a627d125025a43f83e7a7", size = 4686563, upload-time = "2026-04-24T19:53:55.274Z" },
{ url = "https://files.pythonhosted.org/packages/b7/bf/5ee5b145248f92250de86145d1c1d6edebbd57a7fe7caa4dedb5d4cf06a1/cryptography-47.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4f7722c97826770bab8ae92959a2e7b20a5e9e9bf4deae68fd86c3ca457bab52", size = 4770094, upload-time = "2026-04-24T19:53:57.753Z" },
{ url = "https://files.pythonhosted.org/packages/92/43/21d220b2da5d517773894dacdcdb5c682c28d3fffce65548cb06e87d5501/cryptography-47.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:09f6d7bf6724f8db8b32f11eccf23efc8e759924bc5603800335cf8859a3ddbd", size = 4913811, upload-time = "2026-04-24T19:54:00.236Z" },
{ url = "https://files.pythonhosted.org/packages/31/98/dc4ad376ac5f1a1a7d4a83f7b0c6f2bcad36b5d2d8f30aeb482d3a7d9582/cryptography-47.0.0-cp314-cp314t-win32.whl", hash = "sha256:6eebcaf0df1d21ce1f90605c9b432dd2c4f4ab665ac29a40d5e3fc68f51b5e63", size = 3237158, upload-time = "2026-04-24T19:54:02.606Z" },
{ url = "https://files.pythonhosted.org/packages/bc/da/97f62d18306b5133468bc3f8cc73a3111e8cdc8cf8d3e69474d6e5fd2d1b/cryptography-47.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:51c9313e90bd1690ec5a75ed047c27c0b8e6c570029712943d6116ef9a90620b", size = 3758706, upload-time = "2026-04-24T19:54:04.433Z" },
{ url = "https://files.pythonhosted.org/packages/e0/34/a4fae8ae7c3bc227460c9ae43f56abf1b911da0ec29e0ebac53bb0a4b6b7/cryptography-47.0.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:14432c8a9bcb37009784f9594a62fae211a2ae9543e96c92b2a8e4c3cd5cd0c4", size = 7904072, upload-time = "2026-04-24T19:54:06.411Z" },
{ url = "https://files.pythonhosted.org/packages/01/64/d7b1e54fdb69f22d24a64bb3e88dc718b31c7fb10ef0b9691a3cf7eeea6e/cryptography-47.0.0-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:07efe86201817e7d3c18781ca9770bc0db04e1e48c994be384e4602bc38f8f27", size = 4635767, upload-time = "2026-04-24T19:54:08.519Z" },
{ url = "https://files.pythonhosted.org/packages/8b/7b/cca826391fb2a94efdcdfe4631eb69306ee1cff0b22f664a412c90713877/cryptography-47.0.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b45761c6ec22b7c726d6a829558777e32d0f1c8be7c3f3480f9c912d5ee8a10", size = 4654350, upload-time = "2026-04-24T19:54:10.795Z" },
{ url = "https://files.pythonhosted.org/packages/4c/65/4b57bcc823f42a991627c51c2f68c9fd6eb1393c1756aac876cba2accae2/cryptography-47.0.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:edd4da498015da5b9f26d38d3bfc2e90257bfa9cbed1f6767c282a0025ae649b", size = 4643394, upload-time = "2026-04-24T19:54:13.275Z" },
{ url = "https://files.pythonhosted.org/packages/f4/c4/2c5fbeea70adbbca2bbae865e1d605d6a4a7f8dbd9d33eaf69645087f06c/cryptography-47.0.0-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:9af828c0d5a65c70ec729cd7495a4bf1a67ecb66417b8f02ff125ab8a6326a74", size = 5225777, upload-time = "2026-04-24T19:54:15.18Z" },
{ url = "https://files.pythonhosted.org/packages/7e/b8/ac57107ef32749d2b244e36069bb688792a363aaaa3acc9e3cf84c130315/cryptography-47.0.0-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:256d07c78a04d6b276f5df935a9923275f53bd1522f214447fdf365494e2d515", size = 4688771, upload-time = "2026-04-24T19:54:17.835Z" },
{ url = "https://files.pythonhosted.org/packages/56/fc/9f1de22ff8be99d991f240a46863c52d475404c408886c5a38d2b5c3bb26/cryptography-47.0.0-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:5d0e362ff51041b0c0d219cc7d6924d7b8996f57ce5712bdcef71eb3c65a59cc", size = 4270753, upload-time = "2026-04-24T19:54:19.963Z" },
{ url = "https://files.pythonhosted.org/packages/00/68/d70c852797aa68e8e48d12e5a87170c43f67bb4a59403627259dd57d15de/cryptography-47.0.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:1581aef4219f7ca2849d0250edaa3866212fb74bf5667284f46aa92f9e65c1ca", size = 4642911, upload-time = "2026-04-24T19:54:21.818Z" },
{ url = "https://files.pythonhosted.org/packages/a5/51/661cbee74f594c5d97ff82d34f10d5551c085ca4668645f4606ebd22bd5d/cryptography-47.0.0-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:a49a3eb5341b9503fa3000a9a0db033161db90d47285291f53c2a9d2cd1b7f76", size = 5181411, upload-time = "2026-04-24T19:54:24.376Z" },
{ url = "https://files.pythonhosted.org/packages/94/87/f2b6c374a82cf076cfa1416992ac8e8ec94d79facc37aec87c1a5cb72352/cryptography-47.0.0-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:2207a498b03275d0051589e326b79d4cf59985c99031b05bb292ac52631c37fe", size = 4688262, upload-time = "2026-04-24T19:54:26.946Z" },
{ url = "https://files.pythonhosted.org/packages/14/e2/8b7462f4acf21ec509616f0245018bb197194ab0b65c2ea21a0bdd53c0eb/cryptography-47.0.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7a02675e2fabd0c0fc04c868b8781863cbf1967691543c22f5470500ff840b31", size = 4775506, upload-time = "2026-04-24T19:54:28.926Z" },
{ url = "https://files.pythonhosted.org/packages/70/75/158e494e4c08dc05e039da5bb48553826bd26c23930cf8d3cd5f21fa8921/cryptography-47.0.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80887c5cbd1774683cb126f0ab4184567f080071d5acf62205acb354b4b753b7", size = 4912060, upload-time = "2026-04-24T19:54:30.869Z" },
{ url = "https://files.pythonhosted.org/packages/06/bd/0a9d3edbf5eadbac926d7b9b3cd0c4be584eeeae4a003d24d9eda4affbbd/cryptography-47.0.0-cp38-abi3-win32.whl", hash = "sha256:ed67ea4e0cfb5faa5bc7ecb6e2b8838f3807a03758eec239d6c21c8769355310", size = 3248487, upload-time = "2026-04-24T19:54:33.494Z" },
{ url = "https://files.pythonhosted.org/packages/60/80/5681af756d0da3a599b7bdb586fac5a1540f1bcefd2717a20e611ddade45/cryptography-47.0.0-cp38-abi3-win_amd64.whl", hash = "sha256:835d2d7f47cdc53b3224e90810fb1d36ca94ea29cc1801fb4c1bc43876735769", size = 3755737, upload-time = "2026-04-24T19:54:35.408Z" },
]
[[package]]
@@ -1075,16 +1075,16 @@ wheels = [
[[package]]
name = "django"
version = "5.2.14"
version = "5.2.13"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "asgiref" },
{ name = "sqlparse" },
{ name = "tzdata", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/65/95/95f7faa0950867afaa0bef2460c6263afd6a2c78cc9434046ed28160b015/django-5.2.14.tar.gz", hash = "sha256:58a63ba841662e5c686b57ba1fec52ddd68c0b93bd96ac3029d55728f00bf8a2", size = 10895118, upload-time = "2026-05-05T13:57:31.104Z" }
sdist = { url = "https://files.pythonhosted.org/packages/1f/c5/c69e338eb2959f641045802e5ea87ca4bf5ac90c5fd08953ca10742fad51/django-5.2.13.tar.gz", hash = "sha256:a31589db5188d074c63f0945c3888fad104627dfcc236fb2b97f71f89da33bc4", size = 10890368, upload-time = "2026-04-07T14:02:15.072Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/14/44/f172870cf87aa25afef48fb72adba89ee8b77fcab6f3b23d240b923f1528/django-5.2.14-py3-none-any.whl", hash = "sha256:6f712143bd3064310d1f50fac859c3e9a274bdcfc9595339853be7779297fc76", size = 8311320, upload-time = "2026-05-05T13:57:25.795Z" },
{ url = "https://files.pythonhosted.org/packages/59/b1/51ab36b2eefcf8cdb9338c7188668a157e29e30306bfc98a379704c9e10d/django-5.2.13-py3-none-any.whl", hash = "sha256:5788fce61da23788a8ce6f02583765ab060d396720924789f97fa42119d37f7a", size = 8310982, upload-time = "2026-04-07T14:02:08.883Z" },
]
[[package]]
@@ -2583,11 +2583,11 @@ wheels = [
[[package]]
name = "packaging"
version = "26.2"
version = "26.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" }
sdist = { url = "https://files.pythonhosted.org/packages/df/de/0d2b39fb4af88a0258f3bac87dfcbb48e73fbdea4a2ed0e2213f9a4c2f9a/packaging-26.1.tar.gz", hash = "sha256:f042152b681c4bfac5cae2742a55e103d27ab2ec0f3d88037136b6bfe7c9c5de", size = 215519, upload-time = "2026-04-14T21:12:49.362Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" },
{ url = "https://files.pythonhosted.org/packages/7a/c2/920ef838e2f0028c8262f16101ec09ebd5969864e5a64c4c05fad0617c56/packaging-26.1-py3-none-any.whl", hash = "sha256:5d9c0669c6285e491e0ced2eee587eaf67b670d94a19e94e3984a481aba6802f", size = 95831, upload-time = "2026-04-14T21:12:47.56Z" },
]
[[package]]
@@ -2733,14 +2733,14 @@ wheels = [
[[package]]
name = "psycopg"
version = "3.3.4"
version = "3.3.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "tzdata", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/db/2f/cb91e5502ec9de1de6f1b76cfbf69531932725361168bb06963620c77e2e/psycopg-3.3.4.tar.gz", hash = "sha256:e21207764952cff81b6b8bdacad9a3939f2793367fdac2987b3aac36a651b5bc", size = 165799, upload-time = "2026-05-01T23:31:55.179Z" }
sdist = { url = "https://files.pythonhosted.org/packages/d3/b6/379d0a960f8f435ec78720462fd94c4863e7a31237cf81bf76d0af5883bf/psycopg-3.3.3.tar.gz", hash = "sha256:5e9a47458b3c1583326513b2556a2a9473a1001a56c9efe9e587245b43148dd9", size = 165624, upload-time = "2026-02-18T16:52:16.546Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5c/e0/7b3dee031daae7743609ce3c746565d4a3ed7c2c186479eb48e34e838c64/psycopg-3.3.4-py3-none-any.whl", hash = "sha256:b6bbc25ccf05c8fad3b061d9db2ef0909a555171b84b07f29458a447253d679a", size = 213001, upload-time = "2026-05-01T23:20:50.816Z" },
{ url = "https://files.pythonhosted.org/packages/c8/5b/181e2e3becb7672b502f0ed7f16ed7352aca7c109cfb94cf3878a9186db9/psycopg-3.3.3-py3-none-any.whl", hash = "sha256:f96525a72bcfade6584ab17e89de415ff360748c766f0106959144dcbb38c698", size = 212768, upload-time = "2026-02-18T16:46:27.365Z" },
]
[package.optional-dependencies]
@@ -2753,9 +2753,9 @@ pool = [
[[package]]
name = "psycopg-c"
version = "3.3.4"
version = "3.3.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/21/7c/c08364f2eab2913e4068b3b955d963e7a3491986a85429990969525def30/psycopg_c-3.3.4.tar.gz", hash = "sha256:ed8106128b2d04359c185fc9641b4409abfce4d0b6fb1d1ff6800646e27f1a22", size = 647111, upload-time = "2026-05-01T23:31:58.032Z" }
sdist = { url = "https://files.pythonhosted.org/packages/cb/a0/8feb0ca8c7c20a8b9ac4d46b335ddd57e48e593b714262f006880f34fee5/psycopg_c-3.3.3.tar.gz", hash = "sha256:86ef6f4424348247828e83fb0882c9f8acb33e64d0a5ce66c1b4a5107ee73edd", size = 631965, upload-time = "2026-02-18T16:52:18.084Z" }
[[package]]
name = "psycopg-pool"
@@ -2947,14 +2947,14 @@ wheels = [
[[package]]
name = "pyopenssl"
version = "26.2.0"
version = "26.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cryptography" },
]
sdist = { url = "https://files.pythonhosted.org/packages/1a/51/27a5ad5f939d08f690a326ef9582cda7140555180db71695f6fb747d6a36/pyopenssl-26.2.0.tar.gz", hash = "sha256:8c6fcecd1183a7fc897548dfe388b0cdb7f37e018200d8409cf33959dbe35387", size = 182195, upload-time = "2026-05-04T23:06:09.72Z" }
sdist = { url = "https://files.pythonhosted.org/packages/8c/a8/26d36401e3ab8eed9030ad33f381da7856fcfad5691780fccd1b019718fc/pyopenssl-26.1.0.tar.gz", hash = "sha256:737f0a2275c5bc54f3b02137687e1a765931fb3949b9a92a825e4d33b9eec08b", size = 186181, upload-time = "2026-04-24T20:23:48.115Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/73/b8/a0e2790ae249d6f38c9f66de7a211621a7ab2650217bcd04e1262f578a56/pyopenssl-26.2.0-py3-none-any.whl", hash = "sha256:4f9d971bc5298b8bc1fab282803da04bf000c755d4ad9d99b52de2569ca19a70", size = 55823, upload-time = "2026-05-04T23:06:08.395Z" },
{ url = "https://files.pythonhosted.org/packages/a8/41/52f3a3e812b816a91e89aa504199d8bf989a1f873192b10762be66cf2009/pyopenssl-26.1.0-py3-none-any.whl", hash = "sha256:115563879b2c8ccb207975705d3e491434d8c9d7c79667c902ecbf5f3bbd2ece", size = 58109, upload-time = "2026-04-24T20:23:46.273Z" },
]
[[package]]
@@ -3808,15 +3808,15 @@ socks = [
[[package]]
name = "uvicorn"
version = "0.46.0"
version = "0.45.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "click" },
{ name = "h11" },
]
sdist = { url = "https://files.pythonhosted.org/packages/1f/93/041fca8274050e40e6791f267d82e0e2e27dd165627bd640d3e0e378d877/uvicorn-0.46.0.tar.gz", hash = "sha256:fb9da0926999cc6cb22dc7cd71a94a632f078e6ae47ff683c5c420750fb7413d", size = 88758, upload-time = "2026-04-23T07:16:00.151Z" }
sdist = { url = "https://files.pythonhosted.org/packages/eb/2e/62b0d9a2cfc8b4de6771322dae30f2db76c66dae9ec32e94e176a44ad563/uvicorn-0.45.0.tar.gz", hash = "sha256:3fe650df136c5bd2b9b06efc5980636344a2fbb840e9ddd86437d53144fa335d", size = 87818, upload-time = "2026-04-21T10:43:46.815Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/31/a3/5b1562db76a5a488274b2332a97199b32d0442aca0ed193697fd47786316/uvicorn-0.46.0-py3-none-any.whl", hash = "sha256:bbebbcbed972d162afca128605223022bedd345b7bc7855ce66deb31487a9048", size = 70926, upload-time = "2026-04-23T07:15:58.355Z" },
{ url = "https://files.pythonhosted.org/packages/c1/88/d0f7512465b166a4e931ccf7e77792be60fb88466a43964c7566cbaff752/uvicorn-0.45.0-py3-none-any.whl", hash = "sha256:2db26f588131aeec7439de00f2dd52d5f210710c1f01e407a52c90b880d1fd4f", size = 69838, upload-time = "2026-04-21T10:43:45.029Z" },
]
[package.optional-dependencies]

524
web/package-lock.json generated
View File

@@ -21,7 +21,7 @@
"@codemirror/theme-one-dark": "^6.1.3",
"@eslint/js": "^9.39.3",
"@floating-ui/dom": "^1.7.6",
"@formatjs/intl-listformat": "^8.3.4",
"@formatjs/intl-listformat": "^8.3.2",
"@fortawesome/fontawesome-free": "^7.2.0",
"@goauthentik/api": "0.0.0",
"@goauthentik/core": "^1.0.0",
@@ -43,11 +43,11 @@
"@patternfly/elements": "^4.4.0",
"@patternfly/patternfly": "^4.224.2",
"@playwright/test": "^1.59.1",
"@sentry/browser": "^10.50.0",
"@storybook/addon-docs": "^10.3.6",
"@storybook/addon-links": "^10.3.6",
"@storybook/web-components": "^10.3.6",
"@storybook/web-components-vite": "^10.3.6",
"@sentry/browser": "^10.49.0",
"@storybook/addon-docs": "^10.3.5",
"@storybook/addon-links": "^10.3.5",
"@storybook/web-components": "^10.3.5",
"@storybook/web-components-vite": "^10.3.5",
"@types/codemirror": "^5.60.17",
"@types/grecaptcha": "^3.0.9",
"@types/guacamole-common-js": "^1.5.5",
@@ -66,10 +66,10 @@
"chartjs-adapter-date-fns": "^3.0.0",
"codemirror": "^6.0.2",
"core-js": "^3.49.0",
"country-flag-icons": "^1.6.17",
"country-flag-icons": "^1.6.16",
"date-fns": "^4.1.0",
"deepmerge-ts": "^7.1.5",
"dompurify": "^3.4.2",
"dompurify": "^3.4.1",
"esbuild": "^0.28.0",
"eslint": "^9.39.3",
"eslint-plugin-lit": "^2.2.1",
@@ -78,7 +78,7 @@
"globals": "^17.5.0",
"guacamole-common-js": "^1.5.0",
"hastscript": "^9.0.1",
"knip": "^6.7.0",
"knip": "^6.6.3",
"lex": "^2025.11.0",
"lit": "^3.3.2",
"lit-analyzer": "^2.0.3",
@@ -114,11 +114,11 @@
"typescript": "^6.0.3",
"typescript-eslint": "^8.57.2",
"unist-util-visit": "^5.1.0",
"vite": "^8.0.10",
"vite": "^8.0.8",
"vitest": "^4.1.1",
"webcomponent-qr-code": "^1.3.0",
"wireit": "^0.14.12",
"yaml": "^2.8.4"
"yaml": "^2.8.3"
},
"engines": {
"node": ">=24",
@@ -1305,27 +1305,27 @@
"license": "MIT"
},
"node_modules/@formatjs/fast-memoize": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-3.1.3.tgz",
"integrity": "sha512-Ocd1vPuD68rW6BJDuAOtnnc1GPeVepY5kZXML1psGVFQ+1Q8CfkftT3Tnam+Mxx97Pz08jIEDCotl/GV+Naccg==",
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-3.1.2.tgz",
"integrity": "sha512-vPnriihkfK0lzoQGaXq+qXH23VsYyansRTkTgo2aTG0k1NjLFyZimFVdfj4C9JkSE5dm7CEngcQ5TTc1yAyBfQ==",
"license": "MIT"
},
"node_modules/@formatjs/intl-listformat": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-8.3.4.tgz",
"integrity": "sha512-q7WskvO6C/Cyq7ryyM9maDL2FJzt6u39MMBrxmTHZtpTMZukG5Lw0kl9sZaCOR9tYP34xOdWp4JNUrfrkdLGXQ==",
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-8.3.2.tgz",
"integrity": "sha512-PH8V1YVSm+AcrQ1D9Th5wxWo74vDs1V1rRGz9PCFm1HTBtZLVMN3P/+tDcpizQBvvdGNwUqHBOdxN5ZZtlq2bQ==",
"license": "MIT",
"dependencies": {
"@formatjs/intl-localematcher": "0.8.5"
"@formatjs/intl-localematcher": "0.8.3"
}
},
"node_modules/@formatjs/intl-localematcher": {
"version": "0.8.5",
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.8.5.tgz",
"integrity": "sha512-TEW/NR367c3PcQ2AXfkNig9jC740+qbkM0LgKl7UCE7Xtv7C5Uk1mvlu86MjQZBmscUai8HSWjcEETpwaVvJ6A==",
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.8.3.tgz",
"integrity": "sha512-pHUjWb9NuhnMs8+PxQdzBtZRFJHlGhrURGAbm6Ltwl82BFajeuiIR3jblSa7ia3r62rXe/0YtVpUG3xWr5bFCA==",
"license": "MIT",
"dependencies": {
"@formatjs/fast-memoize": "3.1.3"
"@formatjs/fast-memoize": "3.1.2"
}
},
"node_modules/@fortawesome/fontawesome-free": {
@@ -2895,9 +2895,9 @@
"license": "MIT"
},
"node_modules/@rolldown/binding-android-arm64": {
"version": "1.0.0-rc.17",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.17.tgz",
"integrity": "sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.15.tgz",
"integrity": "sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA==",
"cpu": [
"arm64"
],
@@ -2911,9 +2911,9 @@
}
},
"node_modules/@rolldown/binding-darwin-arm64": {
"version": "1.0.0-rc.17",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.17.tgz",
"integrity": "sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.15.tgz",
"integrity": "sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg==",
"cpu": [
"arm64"
],
@@ -2927,9 +2927,9 @@
}
},
"node_modules/@rolldown/binding-darwin-x64": {
"version": "1.0.0-rc.17",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.17.tgz",
"integrity": "sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.15.tgz",
"integrity": "sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw==",
"cpu": [
"x64"
],
@@ -2943,9 +2943,9 @@
}
},
"node_modules/@rolldown/binding-freebsd-x64": {
"version": "1.0.0-rc.17",
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.17.tgz",
"integrity": "sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.15.tgz",
"integrity": "sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw==",
"cpu": [
"x64"
],
@@ -2959,9 +2959,9 @@
}
},
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
"version": "1.0.0-rc.17",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.17.tgz",
"integrity": "sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.15.tgz",
"integrity": "sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA==",
"cpu": [
"arm"
],
@@ -2975,9 +2975,9 @@
}
},
"node_modules/@rolldown/binding-linux-arm64-gnu": {
"version": "1.0.0-rc.17",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.17.tgz",
"integrity": "sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.15.tgz",
"integrity": "sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w==",
"cpu": [
"arm64"
],
@@ -2991,9 +2991,9 @@
}
},
"node_modules/@rolldown/binding-linux-arm64-musl": {
"version": "1.0.0-rc.17",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.17.tgz",
"integrity": "sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.15.tgz",
"integrity": "sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ==",
"cpu": [
"arm64"
],
@@ -3007,9 +3007,9 @@
}
},
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
"version": "1.0.0-rc.17",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.17.tgz",
"integrity": "sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.15.tgz",
"integrity": "sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ==",
"cpu": [
"ppc64"
],
@@ -3023,9 +3023,9 @@
}
},
"node_modules/@rolldown/binding-linux-s390x-gnu": {
"version": "1.0.0-rc.17",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.17.tgz",
"integrity": "sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.15.tgz",
"integrity": "sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ==",
"cpu": [
"s390x"
],
@@ -3039,9 +3039,9 @@
}
},
"node_modules/@rolldown/binding-linux-x64-gnu": {
"version": "1.0.0-rc.17",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.17.tgz",
"integrity": "sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.15.tgz",
"integrity": "sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA==",
"cpu": [
"x64"
],
@@ -3055,9 +3055,9 @@
}
},
"node_modules/@rolldown/binding-linux-x64-musl": {
"version": "1.0.0-rc.17",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.17.tgz",
"integrity": "sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.15.tgz",
"integrity": "sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw==",
"cpu": [
"x64"
],
@@ -3071,9 +3071,9 @@
}
},
"node_modules/@rolldown/binding-openharmony-arm64": {
"version": "1.0.0-rc.17",
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.17.tgz",
"integrity": "sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.15.tgz",
"integrity": "sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg==",
"cpu": [
"arm64"
],
@@ -3087,48 +3087,27 @@
}
},
"node_modules/@rolldown/binding-wasm32-wasi": {
"version": "1.0.0-rc.17",
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.17.tgz",
"integrity": "sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.15.tgz",
"integrity": "sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q==",
"cpu": [
"wasm32"
],
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/core": "1.10.0",
"@emnapi/runtime": "1.10.0",
"@napi-rs/wasm-runtime": "^1.1.4"
"@emnapi/core": "1.9.2",
"@emnapi/runtime": "1.9.2",
"@napi-rs/wasm-runtime": "^1.1.3"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/core": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
"integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/wasi-threads": "1.2.1",
"tslib": "^2.4.0"
}
},
"node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/runtime": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
"integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
"node": ">=14.0.0"
}
},
"node_modules/@rolldown/binding-win32-arm64-msvc": {
"version": "1.0.0-rc.17",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.17.tgz",
"integrity": "sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.15.tgz",
"integrity": "sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA==",
"cpu": [
"arm64"
],
@@ -3142,9 +3121,9 @@
}
},
"node_modules/@rolldown/binding-win32-x64-msvc": {
"version": "1.0.0-rc.17",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.17.tgz",
"integrity": "sha512-3hkiolcUAvPB9FLb3UZdfjVVNWherN1f/skkGWJP/fgSQhYUZpSIRr0/I8ZK9TkF3F7kxvJAk0+IcKvPHk9qQg==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.15.tgz",
"integrity": "sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g==",
"cpu": [
"x64"
],
@@ -3158,9 +3137,9 @@
}
},
"node_modules/@rolldown/pluginutils": {
"version": "1.0.0-rc.17",
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.17.tgz",
"integrity": "sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.15.tgz",
"integrity": "sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g==",
"license": "MIT"
},
"node_modules/@rollup/plugin-commonjs": {
@@ -3614,75 +3593,75 @@
"license": "MIT"
},
"node_modules/@sentry-internal/browser-utils": {
"version": "10.50.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-10.50.0.tgz",
"integrity": "sha512-42bxyRTxnCmYlWnvz4CxikuQNanw8UNma2WJrtxJ0f1MAJV2GhQGSHDLnA+lvFlmiz6qct3pfen/NXGyOTegTA==",
"version": "10.49.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-10.49.0.tgz",
"integrity": "sha512-n0QRx0Ysx6mPfIydTkz7VP0FmwM+/EqMZiRqdsU3aTYsngE9GmEDV0OL1bAy6a8N/C1xf9vntkuAtj6N/8Z51w==",
"license": "MIT",
"dependencies": {
"@sentry/core": "10.50.0"
"@sentry/core": "10.49.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@sentry-internal/feedback": {
"version": "10.50.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-10.50.0.tgz",
"integrity": "sha512-0k9XZF0wn86f77mIO2U3gNNyDZooy139CnEanRzHinrN106vVzvBZ6TUEQoHtoO1fqQxr+nWWVrqV/PXUqk47w==",
"version": "10.49.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-10.49.0.tgz",
"integrity": "sha512-JNsUBGv0faCFE7MeZUH99Y9lU9qq3LBALbLxpE1x7ngNrQnVYRlcFgdqaD/btNBKr8awjYL8gmcSkHBWskGqLQ==",
"license": "MIT",
"dependencies": {
"@sentry/core": "10.50.0"
"@sentry/core": "10.49.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@sentry-internal/replay": {
"version": "10.50.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-10.50.0.tgz",
"integrity": "sha512-51FYNfnvVLAWw1rrEWPFfwHuMRb9mkVCFGA4J9/un7SpeGBsQDziGB0Di4fsCxI7+EdSBpfLHPF0csKtCCw0oQ==",
"version": "10.49.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-10.49.0.tgz",
"integrity": "sha512-IEy4lwHVMiRE3JAcn+kFKjsTgalDOCSTf20SoFd+nkt6rN/k1RDyr4xpdfF//Kj3UdeTmbuibYjK5H/FLhhnGg==",
"license": "MIT",
"dependencies": {
"@sentry-internal/browser-utils": "10.50.0",
"@sentry/core": "10.50.0"
"@sentry-internal/browser-utils": "10.49.0",
"@sentry/core": "10.49.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@sentry-internal/replay-canvas": {
"version": "10.50.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-10.50.0.tgz",
"integrity": "sha512-jx6RKBmcJSWdI92qDGS/sBv1w+7Cww879Z/moX7bw7ipHa/Ts3iDcB3rgZwvhmi17U+mvYsbJeL2DXkPo3TjPw==",
"version": "10.49.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-10.49.0.tgz",
"integrity": "sha512-7D/NrgH1Qwx5trDYaaTSSJmCb1yVQQLqFG4G/S9x2ltzl9876lSGJL8UeW8ReNQgF3CDAcwbmm/9aXaVSBUNZA==",
"license": "MIT",
"dependencies": {
"@sentry-internal/replay": "10.50.0",
"@sentry/core": "10.50.0"
"@sentry-internal/replay": "10.49.0",
"@sentry/core": "10.49.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@sentry/browser": {
"version": "10.50.0",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-10.50.0.tgz",
"integrity": "sha512-1f6rAvET6myiTaSeYqvaaBwvq1LfxqWjAPIoAW/NVC9bPMkeEcuvgDajHrnZMrBeWoJ81NMyoLkyX+iOc7MoFA==",
"version": "10.49.0",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-10.49.0.tgz",
"integrity": "sha512-bGCHc+wK2Dx67YoSbmtlt04alqWfQ+dasD/GVipVOq50gvw/BBIDHTEWRJEjACl+LrvszeY54V+24p8z4IgysA==",
"license": "MIT",
"dependencies": {
"@sentry-internal/browser-utils": "10.50.0",
"@sentry-internal/feedback": "10.50.0",
"@sentry-internal/replay": "10.50.0",
"@sentry-internal/replay-canvas": "10.50.0",
"@sentry/core": "10.50.0"
"@sentry-internal/browser-utils": "10.49.0",
"@sentry-internal/feedback": "10.49.0",
"@sentry-internal/replay": "10.49.0",
"@sentry-internal/replay-canvas": "10.49.0",
"@sentry/core": "10.49.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@sentry/core": {
"version": "10.50.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.50.0.tgz",
"integrity": "sha512-J4A+vzUO3adl0TkFCjaN1+4miamrjHiEIYuLHiuu1lmAjq5WIVw32ObvAh4yMwNtxyaEMosTrrh5M6f12XSJFg==",
"version": "10.49.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.49.0.tgz",
"integrity": "sha512-UaFeum3LUM1mB0d67jvKnqId1yWQjyqmaDV6kWngG03x+jqXb08tJdGpSoxjXZe13jFBbiBL/wKDDYIK7rCK4g==",
"license": "MIT",
"engines": {
"node": ">=18"
@@ -3719,15 +3698,15 @@
"license": "MIT"
},
"node_modules/@storybook/addon-docs": {
"version": "10.3.6",
"resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-10.3.6.tgz",
"integrity": "sha512-TvIdADVPtauxW0LzXIpIv7X6GxwetorhyNh+6+7MHC27XSBCWVxxRUwL63YeLlHTuXsIk0quG3b1xgwVRzWOJA==",
"version": "10.3.5",
"resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-10.3.5.tgz",
"integrity": "sha512-WuHbxia/o5TX4Rg/IFD0641K5qId/Nk0dxhmAUNoFs5L0+yfZUwh65XOBbzXqrkYmYmcVID4v7cgDRmzstQNkA==",
"license": "MIT",
"dependencies": {
"@mdx-js/react": "^3.0.0",
"@storybook/csf-plugin": "10.3.6",
"@storybook/csf-plugin": "10.3.5",
"@storybook/icons": "^2.0.1",
"@storybook/react-dom-shim": "10.3.6",
"@storybook/react-dom-shim": "10.3.5",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
"ts-dedent": "^2.0.0"
@@ -3737,13 +3716,13 @@
"url": "https://opencollective.com/storybook"
},
"peerDependencies": {
"storybook": "^10.3.6"
"storybook": "^10.3.5"
}
},
"node_modules/@storybook/addon-links": {
"version": "10.3.6",
"resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-10.3.6.tgz",
"integrity": "sha512-tv9Xd68qRGBAvEubaxNo3FuFq4GwuMiBriD+gLGuFK0+/u3cnkuA264aoR1v6YCH3sT3er3+MBimuyKM3jLDxg==",
"version": "10.3.5",
"resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-10.3.5.tgz",
"integrity": "sha512-Xe2wCGZ+hpZ0cDqAIBHk+kPc8nODNbu585ghd5bLrlYJMDVXoNM/fIlkrLgjIDVbfpgeJLUEg7vldJrn+FyOLw==",
"license": "MIT",
"dependencies": {
"@storybook/global": "^5.0.0"
@@ -3754,7 +3733,7 @@
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
"storybook": "^10.3.6"
"storybook": "^10.3.5"
},
"peerDependenciesMeta": {
"react": {
@@ -3763,12 +3742,12 @@
}
},
"node_modules/@storybook/builder-vite": {
"version": "10.3.6",
"resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-10.3.6.tgz",
"integrity": "sha512-gpvR/sE4BcrFtmQZ+Ker7zD23oQzoVeqD9nF6cK6yzY+Q0svJXyX2EPmFG4y+EwygD5/vNzDpP84gGMut8VRwg==",
"version": "10.3.5",
"resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-10.3.5.tgz",
"integrity": "sha512-i4KwCOKbhtlbQIbhm53+Kk7bMnxa0cwTn1pxmtA/x5wm1Qu7FrrBQV0V0DNjkUqzcSKo1CjspASJV/HlY0zYlw==",
"license": "MIT",
"dependencies": {
"@storybook/csf-plugin": "10.3.6",
"@storybook/csf-plugin": "10.3.5",
"ts-dedent": "^2.0.0"
},
"funding": {
@@ -3776,14 +3755,14 @@
"url": "https://opencollective.com/storybook"
},
"peerDependencies": {
"storybook": "^10.3.6",
"storybook": "^10.3.5",
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
"node_modules/@storybook/csf-plugin": {
"version": "10.3.6",
"resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-10.3.6.tgz",
"integrity": "sha512-9kBf7VRdRqTSIYo+rPtVn5yjYYyK8kP2QhEYx3oiXvfwy4RexmbJnhk/tXa/lNiTqukA1TqaWQ2+5MqF4fu6YQ==",
"version": "10.3.5",
"resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-10.3.5.tgz",
"integrity": "sha512-qlEzNKxOjq86pvrbuMwiGD/bylnsXk1dg7ve0j77YFjEEchqtl7qTlrXvFdNaLA89GhW6D/EV6eOCu/eobPDgw==",
"license": "MIT",
"dependencies": {
"unplugin": "^2.3.5"
@@ -3795,7 +3774,7 @@
"peerDependencies": {
"esbuild": "*",
"rollup": "*",
"storybook": "^10.3.6",
"storybook": "^10.3.5",
"vite": "*",
"webpack": "*"
},
@@ -3831,9 +3810,9 @@
}
},
"node_modules/@storybook/react-dom-shim": {
"version": "10.3.6",
"resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-10.3.6.tgz",
"integrity": "sha512-/Tu1gPu+Fw+zOnAGmxRmOD30FX3a04LxcTAKflEtdpmtIMVR5bA3qpjy+f5YhoyDCecbXyKmL1OeIU2FIIZHqQ==",
"version": "10.3.5",
"resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-10.3.5.tgz",
"integrity": "sha512-Gw8R7XZm0zSUH0XAuxlQJhmizsLzyD6x00KOlP6l7oW9eQHXGfxg3seNDG3WrSAcW07iP1/P422kuiriQlOv7g==",
"license": "MIT",
"funding": {
"type": "opencollective",
@@ -3842,13 +3821,13 @@
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
"storybook": "^10.3.6"
"storybook": "^10.3.5"
}
},
"node_modules/@storybook/web-components": {
"version": "10.3.6",
"resolved": "https://registry.npmjs.org/@storybook/web-components/-/web-components-10.3.6.tgz",
"integrity": "sha512-femDZGYBGQDckL7F6ZCl2S+dNNBjvd9lp6rQrwBdbNprjctLd6d3EB4HyNM502QxtdEo7laq8y1goDu8KwIV3A==",
"version": "10.3.5",
"resolved": "https://registry.npmjs.org/@storybook/web-components/-/web-components-10.3.5.tgz",
"integrity": "sha512-tSppZagFCeZ+bWsaHUvdiw17ATWgfGDBz0mFicgEj0/eNuxQH2OvXyRIQUXY39b/55TBwSGeoIX3tOW91WIqpw==",
"license": "MIT",
"dependencies": {
"@storybook/global": "^5.0.0",
@@ -3861,24 +3840,24 @@
},
"peerDependencies": {
"lit": "^2.0.0 || ^3.0.0",
"storybook": "^10.3.6"
"storybook": "^10.3.5"
}
},
"node_modules/@storybook/web-components-vite": {
"version": "10.3.6",
"resolved": "https://registry.npmjs.org/@storybook/web-components-vite/-/web-components-vite-10.3.6.tgz",
"integrity": "sha512-VeDEAJuOOQV6VAqEF0pilXucS6kp+1ILJVkI+ets6Ku2D+RKeu167YrQAzh1NwzRTv0e5H0anDDNke+sWvg2dg==",
"version": "10.3.5",
"resolved": "https://registry.npmjs.org/@storybook/web-components-vite/-/web-components-vite-10.3.5.tgz",
"integrity": "sha512-6uAw6KAUXFsAPzp8KchcMp3gatEnEAd8ylIvzoMzvsIMiHmzXwvDNmoFZnAJ2tmsQGvF4dZRDCBg7PvWdTx8Rg==",
"license": "MIT",
"dependencies": {
"@storybook/builder-vite": "10.3.6",
"@storybook/web-components": "10.3.6"
"@storybook/builder-vite": "10.3.5",
"@storybook/web-components": "10.3.5"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/storybook"
},
"peerDependencies": {
"storybook": "^10.3.6"
"storybook": "^10.3.5"
}
},
"node_modules/@swagger-api/apidom-ast": {
@@ -4577,9 +4556,9 @@
}
},
"node_modules/@swc/core": {
"version": "1.15.33",
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.33.tgz",
"integrity": "sha512-jOlwnFV2xhuuZeAUILGFULeR6vDPfijEJ57evfocwznQldLU3w2cZ9bSDryY9ip+AsM3r1NJKzf47V2NXebkeQ==",
"version": "1.15.30",
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.30.tgz",
"integrity": "sha512-R8VQbQY1BZcbIF2p3gjlTCwAQzx1A194ugWfwld5y+WgVVWqVKm7eURGGOVbQVubgKWzidP2agomBbg96rZilQ==",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
@@ -4594,18 +4573,18 @@
"url": "https://opencollective.com/swc"
},
"optionalDependencies": {
"@swc/core-darwin-arm64": "1.15.33",
"@swc/core-darwin-x64": "1.15.33",
"@swc/core-linux-arm-gnueabihf": "1.15.33",
"@swc/core-linux-arm64-gnu": "1.15.33",
"@swc/core-linux-arm64-musl": "1.15.33",
"@swc/core-linux-ppc64-gnu": "1.15.33",
"@swc/core-linux-s390x-gnu": "1.15.33",
"@swc/core-linux-x64-gnu": "1.15.33",
"@swc/core-linux-x64-musl": "1.15.33",
"@swc/core-win32-arm64-msvc": "1.15.33",
"@swc/core-win32-ia32-msvc": "1.15.33",
"@swc/core-win32-x64-msvc": "1.15.33"
"@swc/core-darwin-arm64": "1.15.30",
"@swc/core-darwin-x64": "1.15.30",
"@swc/core-linux-arm-gnueabihf": "1.15.30",
"@swc/core-linux-arm64-gnu": "1.15.30",
"@swc/core-linux-arm64-musl": "1.15.30",
"@swc/core-linux-ppc64-gnu": "1.15.30",
"@swc/core-linux-s390x-gnu": "1.15.30",
"@swc/core-linux-x64-gnu": "1.15.30",
"@swc/core-linux-x64-musl": "1.15.30",
"@swc/core-win32-arm64-msvc": "1.15.30",
"@swc/core-win32-ia32-msvc": "1.15.30",
"@swc/core-win32-x64-msvc": "1.15.30"
},
"peerDependencies": {
"@swc/helpers": ">=0.5.17"
@@ -4617,9 +4596,9 @@
}
},
"node_modules/@swc/core-darwin-arm64": {
"version": "1.15.33",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.33.tgz",
"integrity": "sha512-N+L0uXhuO7FIfzqwgxmzv0zIpV0qEp8wPX3QQs2p4atjMoywup2JTeDlXPw+z9pWJGCae3JjM+tZ6myclI+2gA==",
"version": "1.15.30",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.30.tgz",
"integrity": "sha512-VvpP+vq08HmGYewMWvrdsxh9s2lthz/808zXm8Yu5kaqeR8Yia2b0eYXleHQ3VAjoStUDk6LzTheBW9KXYQdMA==",
"cpu": [
"arm64"
],
@@ -4633,9 +4612,9 @@
}
},
"node_modules/@swc/core-darwin-x64": {
"version": "1.15.33",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.33.tgz",
"integrity": "sha512-/Il4QHSOhV4FekbsDtkrNmKbsX26oSysvgrRswa/RYOHXAkwXDbB4jaeKq6PsJLSPkzJ2KzQ061gtBnk0vNHfA==",
"version": "1.15.30",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.30.tgz",
"integrity": "sha512-WiJA0hiZI3nwQAO6mu5RqigtWGDtth4Hiq6rbZxAaQyhIcqKIg5IoMRc1Y071lrNJn29eEDMC86Rq58xgUxlDg==",
"cpu": [
"x64"
],
@@ -4649,9 +4628,9 @@
}
},
"node_modules/@swc/core-linux-arm-gnueabihf": {
"version": "1.15.33",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.33.tgz",
"integrity": "sha512-C64hBnBxq4viOPQ8hlx+2lJ23bzZBGnjw7ryALmS+0Q3zHmwO8lw1/DArLENw4Q18/0w5wdEO1k3m1wWNtKGqQ==",
"version": "1.15.30",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.30.tgz",
"integrity": "sha512-YANuFUo48kIT6plJgCD0keae9HFXfjxsbvsgevqc0hr/07X/p7sAWTFOGYEc2SXcASaK7UvuQqzlbW8pr7R79g==",
"cpu": [
"arm"
],
@@ -4665,9 +4644,9 @@
}
},
"node_modules/@swc/core-linux-arm64-gnu": {
"version": "1.15.33",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.33.tgz",
"integrity": "sha512-TRJfnJbX3jqpxRDRoieMzRiCBS5jOmXNb3iQXmcgjFEHKLnAgK1RZRU8Cq1MsPqO4jAJp/ld1G4O3fXuxv85uw==",
"version": "1.15.30",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.30.tgz",
"integrity": "sha512-VndG8jaR4ugY6u+iVOT0Q+d2fZd7sLgjPgN8W/Le+3EbZKl+cRfFxV7Eoz4gfLqhmneZPdcIzf9T3LkgkmqNLg==",
"cpu": [
"arm64"
],
@@ -4681,9 +4660,9 @@
}
},
"node_modules/@swc/core-linux-arm64-musl": {
"version": "1.15.33",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.33.tgz",
"integrity": "sha512-il7tYM+CpUNzieQbwAjFT1P8zqAhmGWNAGhQZBnxurXZ0aNn+5nqYFTEUKNZl7QibtT0uQXzTZrNGHCIj6Y1Og==",
"version": "1.15.30",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.30.tgz",
"integrity": "sha512-1SYGs2l0Yyyi0pR/P/NKz/x0kqxkoiw+BXeJjLUdecSk/KasncWlJrc6hOvFSgKHOBrzgM5jwuluKtlT8dnrcA==",
"cpu": [
"arm64"
],
@@ -4697,9 +4676,9 @@
}
},
"node_modules/@swc/core-linux-ppc64-gnu": {
"version": "1.15.33",
"resolved": "https://registry.npmjs.org/@swc/core-linux-ppc64-gnu/-/core-linux-ppc64-gnu-1.15.33.tgz",
"integrity": "sha512-ZtNBwN0Z7CFj9Il0FcPaKdjgP7URyKu/3RfH46vq+0paOBqLj4NYldD6Qo//Duif/7IOtAraUfDOmp0PLAufog==",
"version": "1.15.30",
"resolved": "https://registry.npmjs.org/@swc/core-linux-ppc64-gnu/-/core-linux-ppc64-gnu-1.15.30.tgz",
"integrity": "sha512-TXREtiXeRhbfDFbmhnkIsXpKfzbfT73YkV2ZF6w0sfxgjC5zI2ZAbaCOq25qxvegofj2K93DtOpm9RLaBgqR2g==",
"cpu": [
"ppc64"
],
@@ -4713,9 +4692,9 @@
}
},
"node_modules/@swc/core-linux-s390x-gnu": {
"version": "1.15.33",
"resolved": "https://registry.npmjs.org/@swc/core-linux-s390x-gnu/-/core-linux-s390x-gnu-1.15.33.tgz",
"integrity": "sha512-De1IyajoOmhOYYjw/lx66bKlyDpHZTueqwpDrWgf5O7T6d1ODeJJO9/OqMBmrBQc5C+dNnlmIufHsp4QVCWufA==",
"version": "1.15.30",
"resolved": "https://registry.npmjs.org/@swc/core-linux-s390x-gnu/-/core-linux-s390x-gnu-1.15.30.tgz",
"integrity": "sha512-DCR2YYeyd6DQE4OuDhImouuNcjXEiEdnn1Y0DyGteugPEDvVuvYk8Xddi+4o2SgWH6jiW8/I+3emZvbep1NC+g==",
"cpu": [
"s390x"
],
@@ -4729,9 +4708,9 @@
}
},
"node_modules/@swc/core-linux-x64-gnu": {
"version": "1.15.33",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.33.tgz",
"integrity": "sha512-mGTH0YxmUN+x6vRN/I6NOk5X0ogNktkwPnJ94IMvR7QjhRDwL0O8RXEDhyUM0YtwWrryBOqaJQBX4zruxEPRGw==",
"version": "1.15.30",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.30.tgz",
"integrity": "sha512-5Pizw3NgfOJ5BJOBK8TIRa59xFW2avESTOBDPTAYwZYa1JNDs+KMF9lUfjJiJLM5HiMs/wPheA9eiT0q9m2AoA==",
"cpu": [
"x64"
],
@@ -4745,9 +4724,9 @@
}
},
"node_modules/@swc/core-linux-x64-musl": {
"version": "1.15.33",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.33.tgz",
"integrity": "sha512-hj628ZkSEJf6zMf5VMbYrG2O6QqyTIp2qwY6VlCjvIa9lAEZ5c2lfPblCLVGYubTeLJDxadLB/CxqQYOQABeEQ==",
"version": "1.15.30",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.30.tgz",
"integrity": "sha512-qyqydP/wyH8alcIP4a2hnGSjHLJjm9H7yDFup+CPy9oTahFgLLwnNcv5UHXqO2Qs3AIND+cls5f/Bb6hqpxdgA==",
"cpu": [
"x64"
],
@@ -4761,9 +4740,9 @@
}
},
"node_modules/@swc/core-win32-arm64-msvc": {
"version": "1.15.33",
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.33.tgz",
"integrity": "sha512-GV2oohtN2/5+KSccl86VULu3aT+LrISC8uzgSq0FRnikpD+Zwc+sBlXmoKQ+Db6jI57ITUOIB8jRkdGMABC29g==",
"version": "1.15.30",
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.30.tgz",
"integrity": "sha512-CaQENgDHVGOg1mSF5sQVgvfFHG9kjMor2rkLMLeLOkfZYNj13ppnJ9+lfaBZLZUMMbnlGQnavCJb8PVBUOso7Q==",
"cpu": [
"arm64"
],
@@ -4777,9 +4756,9 @@
}
},
"node_modules/@swc/core-win32-ia32-msvc": {
"version": "1.15.33",
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.33.tgz",
"integrity": "sha512-gtyvzSNR8DHKfFEA2uqb8Ld1myqi6uEg2jyeUq3ikn5ytYs7H8RpZYC8mdy4NXr8hfcdJfCLXPlYaqqfBXpoEQ==",
"version": "1.15.30",
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.30.tgz",
"integrity": "sha512-30VdLeGk6fugiUs/kUdJ/pAg7z/zpvVbR11RH60jZ0Z42WIeIniYx0rLEWN7h/pKJ3CopqsQ3RsogCAkRKiA2g==",
"cpu": [
"ia32"
],
@@ -4793,9 +4772,9 @@
}
},
"node_modules/@swc/core-win32-x64-msvc": {
"version": "1.15.33",
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.33.tgz",
"integrity": "sha512-d6fRqQSkJI+kmMEBWaDQ7TMl8+YjLYbwRUPZQ9DY0ORBJeTzOrG0twvfvlZ2xgw6jA0ScQKgfBm4vHLSLl5Hqg==",
"version": "1.15.30",
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.30.tgz",
"integrity": "sha512-4iObHPR+Q4oDY110EF5SF5eIaaVJNpMdG9C0q3Q92BsJ5y467uHz7sYQhP60WYlLFsLQ1el2YrIPUItUAQGOKg==",
"cpu": [
"x64"
],
@@ -6548,12 +6527,12 @@
}
},
"node_modules/axios": {
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.16.0.tgz",
"integrity": "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==",
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz",
"integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.16.0",
"follow-redirects": "^1.15.11",
"form-data": "^4.0.5",
"proxy-from-env": "^2.1.0"
}
@@ -7475,9 +7454,9 @@
}
},
"node_modules/country-flag-icons": {
"version": "1.6.17",
"resolved": "https://registry.npmjs.org/country-flag-icons/-/country-flag-icons-1.6.17.tgz",
"integrity": "sha512-Nmik0289ZVZSI3c7mJR/amg6DyY7Z59b0sTFSKayeX72mHfPzCPJygwJs2pYgQULzuAyWeCUgwAJ+Dq8OR+JFw==",
"version": "1.6.16",
"resolved": "https://registry.npmjs.org/country-flag-icons/-/country-flag-icons-1.6.16.tgz",
"integrity": "sha512-HxJVoE/aaZGcUMx1vK/u9430uKGB3ODZDDZJJOqVJQzoHk5v42c0fSp1rk4tDfyr1dVOJjwxRiaBPliBMo2Liw==",
"license": "MIT"
},
"node_modules/crelt": {
@@ -8401,9 +8380,9 @@
"peer": true
},
"node_modules/dompurify": {
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.2.tgz",
"integrity": "sha512-lHeS9SA/IKeIFFyYciHBr2n0v1VMPlSj843HdLOwjb2OxNwdq9Xykxqhk+FE42MzAdHvInbAolSE4mhahPpjXA==",
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.1.tgz",
"integrity": "sha512-JahakDAIg1gyOm7dlgWSDjV4n7Ip2PKR55NIT6jrMfIgLFgWo81vdr1/QGqWtFNRqXP9UV71oVePtjqS2ebnPw==",
"license": "(MPL-2.0 OR Apache-2.0)",
"optionalDependencies": {
"@types/trusted-types": "^2.0.7"
@@ -10748,9 +10727,9 @@
}
},
"node_modules/ip-address": {
"version": "10.2.0",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz",
"integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==",
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
"integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
"license": "MIT",
"optional": true,
"engines": {
@@ -11590,9 +11569,9 @@
}
},
"node_modules/knip": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/knip/-/knip-6.7.0.tgz",
"integrity": "sha512-ckL51NDH1YJxnv1kNB0iUdDngB4f/e9Igz8uIqYfmNDoyOFmmk1V0WFv3LQ7/hzC63b2Z9X41gGUE9eOWrZpaA==",
"version": "6.6.3",
"resolved": "https://registry.npmjs.org/knip/-/knip-6.6.3.tgz",
"integrity": "sha512-7HSf5bLx6r66+sjXwSvSiDEE9RjRzHuAkrEFLE6XXHqaPDY97tdzNvyRVF9DeusbiV72kStAFiNnhj72rxJNGQ==",
"funding": [
{
"type": "github",
@@ -15832,13 +15811,13 @@
"license": "Unlicense"
},
"node_modules/rolldown": {
"version": "1.0.0-rc.17",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.17.tgz",
"integrity": "sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.15.tgz",
"integrity": "sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g==",
"license": "MIT",
"dependencies": {
"@oxc-project/types": "=0.127.0",
"@rolldown/pluginutils": "1.0.0-rc.17"
"@oxc-project/types": "=0.124.0",
"@rolldown/pluginutils": "1.0.0-rc.15"
},
"bin": {
"rolldown": "bin/cli.mjs"
@@ -15847,21 +15826,30 @@
"node": "^20.19.0 || >=22.12.0"
},
"optionalDependencies": {
"@rolldown/binding-android-arm64": "1.0.0-rc.17",
"@rolldown/binding-darwin-arm64": "1.0.0-rc.17",
"@rolldown/binding-darwin-x64": "1.0.0-rc.17",
"@rolldown/binding-freebsd-x64": "1.0.0-rc.17",
"@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.17",
"@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.17",
"@rolldown/binding-linux-arm64-musl": "1.0.0-rc.17",
"@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.17",
"@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.17",
"@rolldown/binding-linux-x64-gnu": "1.0.0-rc.17",
"@rolldown/binding-linux-x64-musl": "1.0.0-rc.17",
"@rolldown/binding-openharmony-arm64": "1.0.0-rc.17",
"@rolldown/binding-wasm32-wasi": "1.0.0-rc.17",
"@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.17",
"@rolldown/binding-win32-x64-msvc": "1.0.0-rc.17"
"@rolldown/binding-android-arm64": "1.0.0-rc.15",
"@rolldown/binding-darwin-arm64": "1.0.0-rc.15",
"@rolldown/binding-darwin-x64": "1.0.0-rc.15",
"@rolldown/binding-freebsd-x64": "1.0.0-rc.15",
"@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.15",
"@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.15",
"@rolldown/binding-linux-arm64-musl": "1.0.0-rc.15",
"@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.15",
"@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.15",
"@rolldown/binding-linux-x64-gnu": "1.0.0-rc.15",
"@rolldown/binding-linux-x64-musl": "1.0.0-rc.15",
"@rolldown/binding-openharmony-arm64": "1.0.0-rc.15",
"@rolldown/binding-wasm32-wasi": "1.0.0-rc.15",
"@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.15",
"@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15"
}
},
"node_modules/rolldown/node_modules/@oxc-project/types": {
"version": "0.124.0",
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.124.0.tgz",
"integrity": "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/Boshen"
}
},
"node_modules/rollup": {
@@ -16610,9 +16598,9 @@
}
},
"node_modules/storybook": {
"version": "10.3.6",
"resolved": "https://registry.npmjs.org/storybook/-/storybook-10.3.6.tgz",
"integrity": "sha512-vbSz7g/1rGMC1uAULqMZjALkIuLu2QABqfhRYhyr/11kzyesi+vAmwyJLukZP1FfecxGOgMwOh6GS0YsGpHAvQ==",
"version": "10.3.5",
"resolved": "https://registry.npmjs.org/storybook/-/storybook-10.3.5.tgz",
"integrity": "sha512-uBSZu/GZa9aEIW3QMGvdQPMZWhGxSe4dyRWU8B3/Vd47Gy/XLC7tsBxRr13txmmPOEDHZR94uLuq0H50fvuqBw==",
"license": "MIT",
"dependencies": {
"@storybook/global": "^5.0.0",
@@ -16637,15 +16625,11 @@
"url": "https://opencollective.com/storybook"
},
"peerDependencies": {
"prettier": "^2 || ^3",
"vite-plus": "^0.1.15"
"prettier": "^2 || ^3"
},
"peerDependenciesMeta": {
"prettier": {
"optional": true
},
"vite-plus": {
"optional": true
}
}
},
@@ -18416,16 +18400,16 @@
}
},
"node_modules/vite": {
"version": "8.0.10",
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.10.tgz",
"integrity": "sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==",
"version": "8.0.8",
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.8.tgz",
"integrity": "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==",
"license": "MIT",
"dependencies": {
"lightningcss": "^1.32.0",
"picomatch": "^4.0.4",
"postcss": "^8.5.10",
"rolldown": "1.0.0-rc.17",
"tinyglobby": "^0.2.16"
"postcss": "^8.5.8",
"rolldown": "1.0.0-rc.15",
"tinyglobby": "^0.2.15"
},
"bin": {
"vite": "bin/vite.js"
@@ -18506,22 +18490,6 @@
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/vite/node_modules/tinyglobby": {
"version": "0.2.16",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
"integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==",
"license": "MIT",
"dependencies": {
"fdir": "^6.5.0",
"picomatch": "^4.0.4"
},
"engines": {
"node": ">=12.0.0"
},
"funding": {
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
"node_modules/vitest": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.5.tgz",
@@ -19093,9 +19061,9 @@
"license": "ISC"
},
"node_modules/yaml": {
"version": "2.8.4",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.4.tgz",
"integrity": "sha512-ml/JPOj9fOQK8RNnWojA67GbZ0ApXAUlN2UQclwv2eVgTgn7O9gg9o7paZWKMp4g0H3nTLtS9LVzhkpOFIKzog==",
"version": "2.8.3",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz",
"integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==",
"license": "ISC",
"bin": {
"yaml": "bin.mjs"
@@ -19280,7 +19248,7 @@
"@rollup/plugin-node-resolve": "^16.0.3",
"@rollup/plugin-swc": "^0.4.0",
"@swc/cli": "^0.8.1",
"@swc/core": "^1.15.33",
"@swc/core": "^1.15.30",
"@webcomponents/template": "^1.5.1",
"base64-js": "^1.5.1",
"core-js": "^3.49.0",

View File

@@ -97,7 +97,7 @@
"@codemirror/theme-one-dark": "^6.1.3",
"@eslint/js": "^9.39.3",
"@floating-ui/dom": "^1.7.6",
"@formatjs/intl-listformat": "^8.3.4",
"@formatjs/intl-listformat": "^8.3.2",
"@fortawesome/fontawesome-free": "^7.2.0",
"@goauthentik/api": "0.0.0",
"@goauthentik/core": "^1.0.0",
@@ -119,11 +119,11 @@
"@patternfly/elements": "^4.4.0",
"@patternfly/patternfly": "^4.224.2",
"@playwright/test": "^1.59.1",
"@sentry/browser": "^10.50.0",
"@storybook/addon-docs": "^10.3.6",
"@storybook/addon-links": "^10.3.6",
"@storybook/web-components": "^10.3.6",
"@storybook/web-components-vite": "^10.3.6",
"@sentry/browser": "^10.49.0",
"@storybook/addon-docs": "^10.3.5",
"@storybook/addon-links": "^10.3.5",
"@storybook/web-components": "^10.3.5",
"@storybook/web-components-vite": "^10.3.5",
"@types/codemirror": "^5.60.17",
"@types/grecaptcha": "^3.0.9",
"@types/guacamole-common-js": "^1.5.5",
@@ -142,10 +142,10 @@
"chartjs-adapter-date-fns": "^3.0.0",
"codemirror": "^6.0.2",
"core-js": "^3.49.0",
"country-flag-icons": "^1.6.17",
"country-flag-icons": "^1.6.16",
"date-fns": "^4.1.0",
"deepmerge-ts": "^7.1.5",
"dompurify": "^3.4.2",
"dompurify": "^3.4.1",
"esbuild": "^0.28.0",
"eslint": "^9.39.3",
"eslint-plugin-lit": "^2.2.1",
@@ -154,7 +154,7 @@
"globals": "^17.5.0",
"guacamole-common-js": "^1.5.0",
"hastscript": "^9.0.1",
"knip": "^6.7.0",
"knip": "^6.6.3",
"lex": "^2025.11.0",
"lit": "^3.3.2",
"lit-analyzer": "^2.0.3",
@@ -190,11 +190,11 @@
"typescript": "^6.0.3",
"typescript-eslint": "^8.57.2",
"unist-util-visit": "^5.1.0",
"vite": "^8.0.10",
"vite": "^8.0.8",
"vitest": "^4.1.1",
"webcomponent-qr-code": "^1.3.0",
"wireit": "^0.14.12",
"yaml": "^2.8.4"
"yaml": "^2.8.3"
},
"optionalDependencies": {
"@esbuild/darwin-arm64": "^0.28.0",

View File

@@ -20,7 +20,7 @@
"@rollup/plugin-node-resolve": "^16.0.3",
"@rollup/plugin-swc": "^0.4.0",
"@swc/cli": "^0.8.1",
"@swc/core": "^1.15.33",
"@swc/core": "^1.15.30",
"@webcomponents/template": "^1.5.1",
"base64-js": "^1.5.1",
"core-js": "^3.49.0",

View File

@@ -8,7 +8,6 @@ import "#elements/forms/Radio";
import "#elements/forms/SearchSelect/index";
import "#elements/utils/TimeDeltaHelp";
import "./AdminSettingsFooterLinks.js";
import "#elements/Alert";
import { akFooterLinkInput, IFooterLinkInput } from "./AdminSettingsFooterLinks.js";
@@ -288,9 +287,6 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
help=${msg(
"When enabled, other flow tabs in a session will refresh upon a successful authentication.",
)}
.bighelp=${html`<ak-alert class="pf-c-radio__description" inline plain>
${msg("This flag is deprecated.")}
</ak-alert>`}
>
</ak-switch-input>
<ak-switch-input

View File

@@ -52,6 +52,10 @@ export class AboutModal extends WithLicenseSummary(WithBrandConfig(AKModal)) {
...AKModal.styles,
PFAbout,
css`
:host {
height: 100%;
}
.pf-c-about-modal-box {
--pf-c-about-modal-box--BackgroundColor: var(--ak-c-dialog--BackgroundColor);
width: unset;

View File

@@ -135,7 +135,7 @@ export class AdminInterface extends WithCapabilitiesConfig(
WebsocketClient.connect();
this.#sidebarMatcher = window.matchMedia("(width > 1210px)");
this.#sidebarMatcher = window.matchMedia("(width >= 1200px)");
this.sidebarOpen = this.#sidebarMatcher.matches;
}

View File

@@ -322,6 +322,30 @@ export class ApplicationViewPage extends WithLicenseSummary(AKElement) {
id="page-app-entitlements"
aria-label="${msg("Application entitlements")}"
>
<div
slot="header"
class="pf-c-banner pf-m-info"
role="status"
aria-live="polite"
>
<div class="pf-l-flex pf-m-space-items-sm">
<div class="pf-l-flex__item">
<i class="fas fa-info-circle" aria-hidden="true"></i>
</div>
<div class="pf-l-flex__item">
${msg("Application entitlements are in preview.", {
id: "application.entitlements.preview.info",
})}
</div>
<div class="pf-l-flex__item">
<a href="mailto:hello+feature/app-ent@goauthentik.io"
>${msg("Send us feedback!", {
id: "preview.send-us-feedback",
})}</a
>
</div>
</div>
</div>
<div class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">
<div class="pf-c-card__title">

View File

@@ -18,7 +18,7 @@ import {
RACProvider,
RadiusProvider,
RedirectURI,
RedirectURITypeEnum,
RedirectUriTypeEnum,
SAMLProvider,
SCIMProvider,
WSFederationProvider,
@@ -87,7 +87,7 @@ function formatRedirectUris(uris: RedirectURI[] = []) {
(${uri.matchingMode === MatchingModeEnum.Strict
? msg("strict")
: msg("regexp")},
${uri.redirectUriType === RedirectURITypeEnum.Logout
${uri.redirectUriType === RedirectUriTypeEnum.Logout
? msg("post logout")
: msg("authorization")})
</li>`,

View File

@@ -124,22 +124,19 @@ export class ApplicationWizardBindingsStep extends ApplicationWizardStep {
order="order"
.columns=${COLUMNS}
.content=${[]}
>
<ak-empty-state slot="empty-table" icon="pf-icon-module"
><span>${msg("No bound policies.")}</span>
<div slot="body">
${msg("No policies are currently bound to this object.")}
</div>
<div slot="primary">
<button
@click=${() => this.onBindingEvent()}
class="pf-c-button pf-m-primary"
>
${msg("Bind policy/group/user")}
</button>
</div>
</ak-empty-state>
</ak-select-table>
></ak-select-table>
<ak-empty-state icon="pf-icon-module"
><span>${msg("No bound policies.")}</span>
<div slot="body">${msg("No policies are currently bound to this object.")}</div>
<div slot="primary">
<button
@click=${() => this.onBindingEvent()}
class="pf-c-button pf-m-primary"
>
${msg("Bind policy/group/user")}
</button>
</div>
</ak-empty-state>
</div>`;
}

View File

@@ -201,7 +201,7 @@ export class ApplicationWizardEditBindingStep extends ApplicationWizardStep<Poli
<ak-switch-input
name="negate"
?checked=${instance?.negate ?? false}
label=${msg("Negate Result")}
label=${msg("Negate result")}
help=${msg("Negates the outcome of the binding. Messages are unaffected.")}
></ak-switch-input>
<ak-number-input
@@ -218,9 +218,8 @@ export class ApplicationWizardEditBindingStep extends ApplicationWizardStep<Poli
></ak-number-input>
<ak-radio-input
name="failureResult"
label=${msg("Failure Result")}
label=${msg("Failure result")}
.options=${createPassFailOptions}
required
></ak-radio-input>
</form>`;
}

View File

@@ -381,7 +381,7 @@ export class ApplicationWizardSubmitStep extends CustomEmitterElement(Applicatio
return html`<h2 class="pf-c-wizard__main-title">
${msg("Review the Application and Provider")}
</h2>
<fieldset class="ak-c-fieldset" name="application-details">
<fieldset>
<legend>${msg("Application Details")}</legend>
<dl class="pf-c-description-list">
<div class="pf-c-description-list__group">
@@ -419,7 +419,7 @@ export class ApplicationWizardSubmitStep extends CustomEmitterElement(Applicatio
${
renderer
? html`<fieldset class="ak-c-fieldset" name="provider-details">
? html`<fieldset>
<legend>${msg("Provider Details")}</legend>
${renderer(provider)}
</fieldset>`

View File

@@ -80,7 +80,7 @@ export class BrandListPage extends TablePage<Brand> {
return [
item.domain,
item.brandingTitle || msg("-"),
html`<ak-status-label ?good=${item._default} type="neutral"></ak-status-label>`,
html`<ak-status-label ?good=${item._default}></ak-status-label>`,
html`<div class="ak-c-table__actions">
${IconEditButton(BrandForm, item.brandUuid, item.brandingTitle)}

View File

@@ -86,7 +86,7 @@ export class ConfigModal extends ModalButton {
></ak-codemirror>
</ak-expand>
</div>
<fieldset class="ak-c-fieldset pf-c-modal-box__footer">
<fieldset class="pf-c-modal-box__footer">
<legend class="sr-only">${msg("Form actions")}</legend>
<button
class="pf-c-button pf-m-plain"

View File

@@ -65,7 +65,7 @@ export class DeviceAddHowTo extends ModalButton {
})}
</ak-tabs>`}
</div>
<fieldset class="ak-c-fieldset pf-c-modal-box__footer">
<fieldset class="pf-c-modal-box__footer">
<legend class="sr-only">${msg("Form actions")}</legend>
<button
class="pf-c-button pf-m-primary"

View File

@@ -175,21 +175,6 @@ export class BoundPoliciesList<T extends PolicyBinding = PolicyBinding> extends
}
protected renderNewPolicyButton(): SlottedTemplateResult {
if (!this.allowedTypes.includes(PolicyBindingCheckTarget.Policy)) {
return html`<button
type="button"
class="pf-c-button pf-m-primary"
${modalInvoker(() => {
return StrictUnsafe<PolicyBindingForm>(this.bindingEditForm, {
allowedTypes: this.allowedTypes,
typeNotices: this.typeNotices,
targetPk: this.target || "",
});
})}
>
${msg("Bind existing group/user")}
</button>`;
}
return html`<button
class="pf-c-button pf-m-primary"
type="button"

View File

@@ -272,7 +272,7 @@ export class PolicyBindingForm<T extends PolicyBinding = PolicyBinding> extends
</ak-switch-input>
<ak-switch-input
name="negate"
label=${msg("Negate Result")}
label=${msg("Negate result")}
?checked=${this.instance?.negate ?? false}
help=${msg("Negates the outcome of the binding. Messages are unaffected.")}
>
@@ -293,11 +293,7 @@ export class PolicyBindingForm<T extends PolicyBinding = PolicyBinding> extends
required
/>
</ak-form-element-horizontal>
<ak-form-element-horizontal
name="failureResult"
label=${msg("Failure Result")}
required
>
<ak-form-element-horizontal name="failureResult" label=${msg("Failure result")}>
<ak-radio .options=${createPassFailOptions} .value=${this.instance?.failureResult}>
</ak-radio>
<p class="pf-c-form__helper-text">

View File

@@ -46,7 +46,7 @@ export class PolicyWizard extends CreateWizard {
@property()
public bindingTarget: string | null = null;
public override groupLabel = msg("Choose Policy Type");
public override groupLabel = msg("Bind New Policy");
public override groupDescription = msg("Select the type of policy you want to create.");
public override initialSteps = this.showBindingPage

View File

@@ -36,7 +36,7 @@ import {
OAuth2Provider,
OAuth2ProviderLogoutMethodEnum,
RedirectURI,
RedirectURITypeEnum,
RedirectUriTypeEnum,
SubModeEnum,
ValidationError,
} from "@goauthentik/api";
@@ -270,7 +270,7 @@ export function renderForm({
.newItem=${() => ({
matchingMode: MatchingModeEnum.Strict,
url: "",
redirectUriType: RedirectURITypeEnum.Authorization,
redirectUriType: RedirectUriTypeEnum.Authorization,
})}
.row=${(redirectURI: RedirectURI, idx: number) => {
return html`<ak-provider-oauth2-redirect-uri

View File

@@ -4,7 +4,7 @@ import { AKControlElement } from "#elements/ControlElement";
import { LitPropertyRecord } from "#elements/types";
import { ifPresent } from "#elements/utils/attributes";
import { MatchingModeEnum, RedirectURI, RedirectURITypeEnum } from "@goauthentik/api";
import { MatchingModeEnum, RedirectURI, RedirectUriTypeEnum } from "@goauthentik/api";
import { msg } from "@lit/localize";
import { css, html } from "lit";
@@ -37,7 +37,7 @@ export class OAuth2ProviderRedirectURI extends AKControlElement<RedirectURI> {
public redirectURI: RedirectURI = {
matchingMode: MatchingModeEnum.Strict,
url: "",
redirectUriType: RedirectURITypeEnum.Authorization,
redirectUriType: RedirectUriTypeEnum.Authorization,
};
@property({ type: String, useDefault: true })
@@ -89,15 +89,15 @@ export class OAuth2ProviderRedirectURI extends AKControlElement<RedirectURI> {
@change=${onChange}
>
<option
value="${RedirectURITypeEnum.Authorization}"
value="${RedirectUriTypeEnum.Authorization}"
?selected=${(this.redirectURI.redirectUriType ??
RedirectURITypeEnum.Authorization) === RedirectURITypeEnum.Authorization}
RedirectUriTypeEnum.Authorization) === RedirectUriTypeEnum.Authorization}
>
${msg("Authorization")}
</option>
<option
value="${RedirectURITypeEnum.Logout}"
?selected=${this.redirectURI.redirectUriType === RedirectURITypeEnum.Logout}
value="${RedirectUriTypeEnum.Logout}"
?selected=${this.redirectURI.redirectUriType === RedirectUriTypeEnum.Logout}
>
${msg("Post Logout")}
</option>

View File

@@ -174,7 +174,7 @@ export class LDAPSourceForm extends BaseSourceForm<LDAPSource> {
></ak-crypto-certificate-search>
<p class="pf-c-form__helper-text">
${msg(
"Leave empty to skip certificate validation, or select a certificate/keypair containing the LDAP server CA chain to validate the remote certificate.",
"When connecting to an LDAP Server with TLS, certificates are not checked by default. Specify a keypair to validate the remote certificate.",
)}
</p>
</ak-form-element-horizontal>

Some files were not shown because too many files have changed in this diff Show More