Compare commits

..

1 Commits

Author SHA1 Message Date
Teffen Ellis
b7a478cc42 Bump package. Fix publish. 2026-02-26 16:10:16 +01:00
151 changed files with 2024 additions and 5370 deletions

View File

@@ -22,7 +22,7 @@ runs:
sudo rm -rf /usr/local/lib/android
- name: Install uv
if: ${{ contains(inputs.dependencies, 'python') }}
uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098 # v5
uses: astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # v5
with:
enable-cache: true
- name: Setup python
@@ -36,7 +36,7 @@ runs:
run: uv sync --all-extras --dev --frozen
- name: Setup node
if: ${{ contains(inputs.dependencies, 'node') }}
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v4
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v4
with:
node-version-file: web/package.json
cache: "npm"

View File

@@ -67,12 +67,12 @@ jobs:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v5
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v5
with:
node-version-file: web/package.json
cache: "npm"
cache-dependency-path: web/package-lock.json
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6
- uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6
with:
go-version-file: "go.mod"
- name: Generate API Clients
@@ -95,7 +95,7 @@ jobs:
platforms: linux/${{ inputs.image_arch }}
cache-from: type=registry,ref=${{ steps.ev.outputs.attestImageNames }}:buildcache-${{ inputs.image_arch }}
cache-to: ${{ steps.ev.outputs.cacheTo }}
- uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v3
- uses: actions/attest-build-provenance@96278af6caaf10aea03fd8d33a09a777ca52d62f # v3
id: attest
if: ${{ steps.ev.outputs.shouldPush == 'true' }}
with:

View File

@@ -97,7 +97,7 @@ jobs:
sources: |
${{ steps.ev.outputs.attestImageNames }}@${{ needs.build-server-amd64.outputs.image-digest }}
${{ steps.ev.outputs.attestImageNames }}@${{ needs.build-server-arm64.outputs.image-digest }}
- uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v3
- uses: actions/attest-build-provenance@96278af6caaf10aea03fd8d33a09a777ca52d62f # v3
id: attest
with:
subject-name: ${{ steps.ev.outputs.attestImageNames }}

View File

@@ -25,7 +25,7 @@ jobs:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
with:
token: ${{ steps.generate_token.outputs.token }}
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v5
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v5
with:
node-version-file: web/package.json
registry-url: "https://registry.npmjs.org"

View File

@@ -33,7 +33,7 @@ jobs:
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v5
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v5
with:
node-version-file: website/package.json
cache: "npm"
@@ -55,7 +55,7 @@ jobs:
env:
NODE_ENV: production
run: npm run build -w api
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v4
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v4
with:
name: api-docs
path: website/api/build
@@ -67,11 +67,11 @@ jobs:
- build
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v5
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v5
with:
name: api-docs
path: website/api/build
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v5
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v5
with:
node-version-file: website/package.json
cache: "npm"

View File

@@ -24,7 +24,7 @@ jobs:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- name: Setup authentik env
uses: ./.github/actions/setup
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v5
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v5
with:
node-version-file: lifecycle/aws/package.json
cache: "npm"

View File

@@ -36,7 +36,7 @@ jobs:
NODE_ENV: production
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v5
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v5
with:
node-version-file: website/package.json
cache: "npm"
@@ -53,7 +53,7 @@ jobs:
NODE_ENV: production
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v5
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v5
with:
node-version-file: website/package.json
cache: "npm"
@@ -105,7 +105,7 @@ jobs:
context: .
cache-from: type=registry,ref=ghcr.io/goauthentik/dev-docs:buildcache
cache-to: ${{ steps.ev.outputs.shouldPush == 'true' && 'type=registry,ref=ghcr.io/goauthentik/dev-docs:buildcache,mode=max' || '' }}
- uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v3
- uses: actions/attest-build-provenance@96278af6caaf10aea03fd8d33a09a777ca52d62f # v3
id: attest
if: ${{ steps.ev.outputs.shouldPush == 'true' }}
with:

View File

@@ -279,7 +279,7 @@ jobs:
with:
flags: conformance
- if: ${{ !cancelled() }}
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: conformance-certification-${{ matrix.job.name }}
path: tests/openid_conformance/exports/

View File

@@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6
- uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6
with:
go-version-file: "go.mod"
- name: Prepare and generate API
@@ -43,7 +43,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6
- uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6
with:
go-version-file: "go.mod"
- name: Setup authentik env
@@ -122,7 +122,7 @@ jobs:
context: .
cache-from: type=registry,ref=ghcr.io/goauthentik/dev-${{ matrix.type }}:buildcache
cache-to: ${{ steps.ev.outputs.shouldPush == 'true' && format('type=registry,ref=ghcr.io/goauthentik/dev-{0}:buildcache,mode=max', matrix.type) || '' }}
- uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v3
- uses: actions/attest-build-provenance@96278af6caaf10aea03fd8d33a09a777ca52d62f # v3
id: attest
if: ${{ steps.ev.outputs.shouldPush == 'true' }}
with:
@@ -148,10 +148,10 @@ jobs:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
with:
ref: ${{ github.event.pull_request.head.sha }}
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6
- uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6
with:
go-version-file: "go.mod"
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v5
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v5
with:
node-version-file: web/package.json
cache: "npm"

View File

@@ -32,7 +32,7 @@ jobs:
project: web
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v5
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v5
with:
node-version-file: ${{ matrix.project }}/package.json
cache: "npm"
@@ -49,7 +49,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v5
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v5
with:
node-version-file: web/package.json
cache: "npm"
@@ -77,7 +77,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v5
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v5
with:
node-version-file: web/package.json
cache: "npm"

View File

@@ -9,6 +9,7 @@ on:
- packages/eslint-config/**
- packages/prettier-config/**
- packages/docusaurus-config/**
- packages/logger-js/**
- packages/esbuild-plugin-live-reload/**
workflow_dispatch:
@@ -35,13 +36,13 @@ jobs:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
with:
fetch-depth: 2
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v5
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v5
with:
node-version-file: ${{ matrix.package }}/package.json
registry-url: "https://registry.npmjs.org"
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # 24d32ffd492484c1d75e0c0b894501ddb9d30d62
uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # 24d32ffd492484c1d75e0c0b894501ddb9d30d62
with:
files: |
${{ matrix.package }}/package.json

View File

@@ -58,7 +58,7 @@ jobs:
push: true
platforms: linux/amd64,linux/arm64
context: .
- uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v3
- uses: actions/attest-build-provenance@96278af6caaf10aea03fd8d33a09a777ca52d62f # v3
id: attest
if: true
with:
@@ -84,10 +84,10 @@ jobs:
- rac
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6
- uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6
with:
go-version-file: "go.mod"
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v5
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v5
with:
node-version-file: web/package.json
cache: "npm"
@@ -129,7 +129,7 @@ jobs:
file: lifecycle/container/${{ matrix.type }}.Dockerfile
platforms: linux/amd64,linux/arm64
context: .
- uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v3
- uses: actions/attest-build-provenance@96278af6caaf10aea03fd8d33a09a777ca52d62f # v3
id: attest
with:
subject-name: ${{ steps.ev.outputs.attestImageNames }}
@@ -152,10 +152,10 @@ jobs:
goarch: [amd64, arm64]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6
- uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6
with:
go-version-file: "go.mod"
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v5
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v5
with:
node-version-file: web/package.json
cache: "npm"
@@ -180,7 +180,7 @@ jobs:
export CGO_ENABLED=0
go build -tags=outpost_static_embed -v -o ./authentik-outpost-${{ matrix.type }}_${{ matrix.goos }}_${{ matrix.goarch }} ./cmd/${{ matrix.type }}
- name: Upload binaries to release
uses: svenstaro/upload-release-action@b98a3b12e86552593f3e4e577ca8a62aa2f3f22b # v2
uses: svenstaro/upload-release-action@6b7fa9f267e90b50a19fef07b3596790bb941741 # v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ./authentik-outpost-${{ matrix.type }}_${{ matrix.goos }}_${{ matrix.goarch }}

View File

@@ -91,7 +91,6 @@ jobs:
# ID from https://api.github.com/users/authentik-automation[bot]
git config --global user.name '${{ steps.app-token.outputs.app-slug }}[bot]'
git config --global user.email '${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com'
git pull
git commit -a -m "release: ${{ inputs.version }}" --allow-empty
git tag "version/${{ inputs.version }}" HEAD -m "version/${{ inputs.version }}"
git push --follow-tags
@@ -175,7 +174,7 @@ jobs:
if: "${{ inputs.release_reason == 'feature' }}"
run: |
changelog_url="https://docs.goauthentik.io/docs/releases/${{ needs.check-inputs.outputs.major_version }}"
reason="${{ inputs.release_reason }}"
reason="{{ inputs.release_reason }}"
jq \
--arg version "${{ inputs.version }}" \
--arg changelog "See ${changelog_url}" \
@@ -187,7 +186,7 @@ jobs:
if: "${{ inputs.release_reason != 'feature' }}"
run: |
changelog_url="https://docs.goauthentik.io/docs/releases/${{ needs.check-inputs.outputs.major_version }}#fixed-in-$(echo -n ${{ inputs.version}} | sed 's/\.//g')"
reason="${{ inputs.release_reason }}"
reason="{{ inputs.release_reason }}"
jq \
--arg version "${{ inputs.version }}" \
--arg changelog "See ${changelog_url}" \

View File

@@ -168,22 +168,12 @@ gen-build: ## Extract the schema from the database
gen-compose:
$(UV) run scripts/generate_compose.py
gen-changelog: ## (Release) generate the changelog based from the commits since the last version
# These are best-effort guesses based on commit messages
$(eval last_version := $(shell git tag --list 'version/*' --sort 'version:refname' | grep -vE 'rc\d+$$' | tail -1))
$(eval current_commit := $(shell git rev-parse HEAD))
git log --pretty=format:"- %s" $(shell git merge-base ${last_version} ${current_commit})...${current_commit} > merged_to_current
git log --pretty=format:"- %s" $(shell git merge-base ${last_version} ${current_commit})...${last_version} > merged_to_last
grep -Eo 'cherry-pick (#\d+)' merged_to_last | cut -d ' ' -f 2 | sed 's/.*/(&)$$/' > cherry_picked_to_last
grep -vf cherry_picked_to_last merged_to_current | sort > changelog.md
rm merged_to_current
rm merged_to_last
rm cherry_picked_to_last
gen-changelog: ## (Release) generate the changelog based from the commits since the last tag
git log --pretty=format:" - %s" $(shell git describe --tags $(shell git rev-list --tags --max-count=1))...$(shell git branch --show-current) | sort > changelog.md
npx prettier --write changelog.md
gen-diff: ## (Release) generate the changelog diff between the current schema and the last version
$(eval last_version := $(shell git tag --list 'version/*' --sort 'version:refname' | grep -vE 'rc\d+$$' | tail -1))
git show ${last_version}:schema.yml > schema-old.yml
gen-diff: ## (Release) generate the changelog diff between the current schema and the last tag
git show $(shell git describe --tags $(shell git rev-list --tags --max-count=1)):schema.yml > schema-old.yml
docker compose -f scripts/api/compose.yml run --rm --user "${UID}:${GID}" diff \
--markdown \
/local/diff.md \

View File

@@ -100,25 +100,13 @@ class S3Backend(ManageableBackend):
f"storage.{self.usage.value}.{self.name}.addressing_style",
CONFIG.get(f"storage.{self.name}.addressing_style", "auto"),
)
signature_version = CONFIG.get(
f"storage.{self.usage.value}.{self.name}.signature_version",
CONFIG.get(f"storage.{self.name}.signature_version", "s3v4"),
)
# Keep signature_version pass-through and let boto3/botocore handle it.
# In boto3's S3 configuration docs, `s3v4` (default) and deprecated `s3`
# are the documented values:
# https://github.com/boto/boto3/blob/791a3e8f36d83664a47b4281a0586b3546cef3ec/docs/source/guide/configuration.rst?plain=1#L398-L407
# Botocore also supports additional signer names, so we intentionally do
# not enforce a restricted allowlist here.
return self.session.client(
"s3",
endpoint_url=endpoint_url,
use_ssl=use_ssl,
region_name=region_name,
config=Config(
signature_version=signature_version, s3={"addressing_style": addressing_style}
),
config=Config(signature_version="s3v4", s3={"addressing_style": addressing_style}),
)
@property

View File

@@ -1,6 +1,5 @@
from unittest import skipUnless
from botocore.exceptions import UnsupportedSignatureVersionError
from django.test import TestCase
from authentik.admin.files.tests.utils import FileTestS3BackendMixin, s3_test_server_available
@@ -82,27 +81,6 @@ class TestS3Backend(FileTestS3BackendMixin, TestCase):
self.assertIn("X-Amz-Signature=", url)
self.assertIn("test.png", url)
def test_client_signature_version_default_v4(self):
"""Test S3 client defaults to v4 signature when not configured."""
self.assertEqual(self.media_s3_backend.client.meta.config.signature_version, "s3v4")
@CONFIG.patch("storage.s3.signature_version", "s3")
def test_client_signature_version_global_override(self):
"""Test S3 client respects globally configured signature version."""
self.assertEqual(self.media_s3_backend.client.meta.config.signature_version, "s3")
@CONFIG.patch("storage.s3.signature_version", "s3v4")
@CONFIG.patch("storage.media.s3.signature_version", "s3")
def test_client_signature_version_media_override(self):
"""Test usage-specific signature version takes precedence over global."""
self.assertEqual(self.media_s3_backend.client.meta.config.signature_version, "s3")
@CONFIG.patch("storage.media.s3.signature_version", "not-a-real-signature")
def test_client_signature_version_unsupported(self):
"""Test unsupported signature version raises botocore error."""
with self.assertRaises(UnsupportedSignatureVersionError):
self.media_s3_backend.file_url("test.png", use_cache=False)
@CONFIG.patch("storage.s3.bucket_name", "test-bucket")
def test_file_exists_true(self):
"""Test file_exists returns True for existing file"""

View File

@@ -17,6 +17,7 @@ from django.contrib.sessions.base_session import AbstractBaseSession
from django.core.validators import validate_slug
from django.db import models
from django.db.models import Q, QuerySet, options
from django.db.models.constants import LOOKUP_SEP
from django.http import HttpRequest
from django.utils.functional import cached_property
from django.utils.timezone import now
@@ -44,7 +45,6 @@ from authentik.lib.models import (
DomainlessFormattedURLValidator,
SerializerModel,
)
from authentik.lib.utils.inheritance import get_deepest_child
from authentik.lib.utils.time import timedelta_from_string
from authentik.policies.models import PolicyBindingModel
from authentik.rbac.models import Role
@@ -803,7 +803,25 @@ class Application(SerializerModel, PolicyBindingModel):
"""Get casted provider instance. Needs Application queryset with_provider"""
if not self.provider:
return None
return get_deepest_child(self.provider)
candidates = []
base_class = Provider
for subclass in base_class.objects.get_queryset()._get_subclasses_recurse(base_class):
parent = self.provider
for level in subclass.split(LOOKUP_SEP):
try:
parent = getattr(parent, level)
except AttributeError:
break
if parent in candidates:
continue
idx = subclass.count(LOOKUP_SEP)
if type(parent) is not base_class:
idx += 1
candidates.insert(idx, parent)
if not candidates:
return None
return candidates[-1]
def backchannel_provider_for[T: Provider](self, provider_type: type[T], **kwargs) -> T | None:
"""Get Backchannel provider for a specific type"""

View File

@@ -78,7 +78,7 @@ def generate_key_id_legacy(key_data: str) -> str:
"""Generate Key ID using MD5 (legacy format for backwards compatibility)."""
if not key_data:
return ""
return md5(key_data.encode("utf-8"), usedforsecurity=False).hexdigest() # nosec
return md5(key_data.encode("utf-8")).hexdigest() # nosec
class CertificateKeyPair(SerializerModel, ManagedModel, CreatedUpdatedModel):

View File

@@ -81,8 +81,6 @@ class SignInProcessor:
self.sign_in_request = sign_in_request
self.saml_processor = AssertionProcessor(self.provider, self.request, AuthNRequest())
self.saml_processor.provider.audience = self.sign_in_request.wtrealm
if self.provider.signing_kp:
self.saml_processor.provider.sign_assertion = True
def create_response_token(self):
root = Element(f"{{{NS_WS_FED_TRUST}}}RequestSecurityTokenResponse", nsmap=NS_MAP)
@@ -150,8 +148,7 @@ class SignInProcessor:
def response(self) -> dict[str, str]:
root = self.create_response_token()
assertion = root.xpath("//saml:Assertion", namespaces=NS_MAP)[0]
if self.provider.signing_kp:
self.saml_processor._sign(assertion)
self.saml_processor._sign(assertion)
str_token = etree.tostring(root).decode("utf-8") # nosec
return delete_none_values(
{

View File

@@ -3,7 +3,8 @@
from django.urls import path
from authentik.enterprise.providers.ws_federation.api.providers import WSFederationProviderViewSet
from authentik.enterprise.providers.ws_federation.views import MetadataDownload, WSFedEntryView
from authentik.enterprise.providers.ws_federation.views import WSFedEntryView
from authentik.providers.saml.views.metadata import MetadataDownload
urlpatterns = [
path(

View File

@@ -29,12 +29,6 @@ class RefreshOtherFlowsAfterAuthentication(Flag[bool], key="flows_refresh_others
visibility = "public"
class ContinuousLogin(Flag[bool], key="flows_continuous_login"):
default = False
visibility = "public"
class AuthentikFlowsConfig(ManagedAppConfig):
"""authentik flows app config"""

View File

@@ -166,7 +166,6 @@ storage:
# region: "us-east-1"
# use_ssl: True
# endpoint: "https://s3.us-east-1.amazonaws.com"
# signature_version: "s3v4"
# access_key: ""
# secret_key: ""
# bucket_name: "authentik-data"

View File

@@ -1,119 +0,0 @@
"""Tests for inheritance helpers."""
from contextlib import contextmanager
from django.db import connection, models
from django.test import TransactionTestCase
from django.test.utils import isolate_apps
from authentik.lib.utils.inheritance import get_deepest_child
@contextmanager
def temporary_inheritance_models():
"""Create a temporary multi-table inheritance graph for testing."""
with isolate_apps("authentik.lib.tests"):
class GrandParent(models.Model):
class Meta:
app_label = "tests"
def __str__(self) -> str:
return f"GrandParent({self.pk})"
class Parent(GrandParent):
class Meta:
app_label = "tests"
def __str__(self) -> str:
return f"Parent({self.pk})"
class Child(Parent):
class Meta:
app_label = "tests"
def __str__(self) -> str:
return f"Child({self.pk})"
class GrandChild(Child):
class Meta:
app_label = "tests"
def __str__(self) -> str:
return f"GrandChild({self.pk})"
with connection.schema_editor() as schema_editor:
schema_editor.create_model(GrandParent)
schema_editor.create_model(Parent)
schema_editor.create_model(Child)
schema_editor.create_model(GrandChild)
try:
yield GrandParent, Parent, Child, GrandChild
finally:
with connection.schema_editor() as schema_editor:
schema_editor.delete_model(GrandChild)
schema_editor.delete_model(Child)
schema_editor.delete_model(Parent)
schema_editor.delete_model(GrandParent)
class TestInheritanceUtils(TransactionTestCase):
"""Tests for helper functions in authentik.lib.utils.inheritance."""
def test_get_deepest_child_grandparent_to_parent(self):
"""GrandParent -> Parent."""
with temporary_inheritance_models() as (GrandParent, Parent, _Child, _GrandChild):
parent = Parent.objects.create()
grandparent = GrandParent.objects.get(pk=parent.pk)
resolved = get_deepest_child(grandparent)
self.assertIsInstance(resolved, Parent)
self.assertEqual(resolved.pk, parent.pk)
def test_get_deepest_child_grandparent_to_child(self):
"""GrandParent -> Child."""
with temporary_inheritance_models() as (GrandParent, _Parent, Child, _GrandChild):
child = Child.objects.create()
grandparent = GrandParent.objects.get(pk=child.pk)
resolved = get_deepest_child(grandparent)
self.assertIsInstance(resolved, Child)
self.assertEqual(resolved.pk, child.pk)
def test_get_deepest_child_grandparent_to_grandchild(self):
"""GrandParent -> GrandChild."""
with temporary_inheritance_models() as (GrandParent, _Parent, _Child, GrandChild):
grandchild = GrandChild.objects.create()
grandparent = GrandParent.objects.get(pk=grandchild.pk)
resolved = get_deepest_child(grandparent)
self.assertIsInstance(resolved, GrandChild)
self.assertEqual(resolved.pk, grandchild.pk)
def test_get_deepest_child_parent_to_child(self):
"""Parent -> Child (start from non-root)."""
with temporary_inheritance_models() as (_GrandParent, Parent, Child, _GrandChild):
child = Child.objects.create()
parent = Parent.objects.get(pk=child.pk)
resolved = get_deepest_child(parent)
self.assertIsInstance(resolved, Child)
self.assertEqual(resolved.pk, child.pk)
def test_get_deepest_child_no_queries_with_preloaded_relations(self):
"""No extra queries when the inheritance chain is fully select_related."""
with temporary_inheritance_models() as (GrandParent, _Parent, _Child, GrandChild):
grandchild = GrandChild.objects.create()
grandparent = GrandParent.objects.select_related("parent__child__grandchild").get(
pk=grandchild.pk
)
with self.assertNumQueries(0):
resolved = get_deepest_child(grandparent)
self.assertIsInstance(resolved, GrandChild)

View File

@@ -1,41 +0,0 @@
from django.db.models import Model, OneToOneField, OneToOneRel
def get_deepest_child(parent: Model) -> Model:
"""
In multiple table inheritance, given any ancestor object, get the deepest child object.
See https://docs.djangoproject.com/en/dev/topics/db/models/#multi-table-inheritance
This function does not query the database if `select_related` has been performed on all
subclasses of `parent`'s model.
"""
# Almost verbatim copy from django-model-utils, see
# https://github.com/jazzband/django-model-utils/blob/5.0.0/model_utils/managers.py#L132
one_to_one_rels = [
field for field in parent._meta.get_fields() if isinstance(field, OneToOneRel)
]
submodel_fields = [
rel
for rel in one_to_one_rels
if isinstance(rel.field, OneToOneField)
and issubclass(rel.field.model, parent._meta.model)
and parent._meta.model is not rel.field.model
and rel.parent_link
]
submodel_accessors = [submodel_field.get_accessor_name() for submodel_field in submodel_fields]
# End Copy
child = None
for submodel in submodel_accessors:
try:
child = getattr(parent, submodel)
break
except AttributeError:
continue
if not child:
return parent
return get_deepest_child(child)

View File

@@ -89,7 +89,7 @@ class PytestTestRunner(DiscoverRunner): # pragma: no cover
sentry_init()
self.logger.debug("Test environment configured")
self.task_broker = use_test_broker()
use_test_broker()
# Send startup signals
pre_startup.send(sender=self, mode="test")
@@ -185,9 +185,7 @@ class PytestTestRunner(DiscoverRunner): # pragma: no cover
self.logger.info("Running tests", test_files=self.args)
with patch("guardian.shortcuts._get_ct_cached", patched__get_ct_cached):
try:
ret = pytest.main(self.args)
self.task_broker.close()
return ret
except Exception as exc: # noqa
self.logger.error("Error running tests", exc=exc, test_files=self.args)
return pytest.main(self.args)
except Exception as e: # noqa
self.logger.error("Error running tests", error=str(e), test_files=self.args)
return 1

View File

@@ -14,7 +14,6 @@ from django.utils.translation import gettext_lazy as _
from ldap3 import ALL, NONE, RANDOM, Connection, Server, ServerPool, Tls
from ldap3.core.exceptions import LDAPException, LDAPInsufficientAccessRightsResult, LDAPSchemaError
from rest_framework.serializers import Serializer
from structlog.stdlib import get_logger
from authentik.core.models import (
Group,
@@ -32,7 +31,6 @@ from authentik.tasks.schedules.common import ScheduleSpec
LDAP_TIMEOUT = 15
LDAP_UNIQUENESS = "ldap_uniq"
LDAP_DISTINGUISHED_NAME = "distinguishedName"
LOGGER = get_logger()
def flatten(value: Any) -> Any:
@@ -270,7 +268,6 @@ class LDAPSource(IncomingSyncSource):
)
if self.start_tls:
LOGGER.debug("Connection StartTLS", source=self)
conn.start_tls(read_server_info=False)
try:
successful = conn.bind()
@@ -281,9 +278,7 @@ class LDAPSource(IncomingSyncSource):
# See https://github.com/goauthentik/authentik/issues/4590
# See also https://github.com/goauthentik/authentik/issues/3399
if server_kwargs.get("get_info", ALL) == NONE:
LOGGER.warning("Failed to connect after schema downgrade", source=self, exc=exc)
raise exc
LOGGER.warning("Downgrading connection to no schema info", source=self, exc=exc)
server_kwargs["get_info"] = NONE
return self.connection(server, server_kwargs, connection_kwargs)
finally:

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -99,7 +99,6 @@ class IdentificationChallenge(Challenge):
password_fields = BooleanField()
allow_show_password = BooleanField(default=False)
application_pre = CharField(required=False)
application_pre_launch = CharField(required=False)
flow_designation = ChoiceField(FlowDesignation.choices)
captcha_stage = CaptchaChallenge(required=False, allow_null=True)
@@ -349,12 +348,9 @@ class IdentificationStageView(ChallengeStageView):
# If the user has been redirected to us whilst trying to access an
# application, PLAN_CONTEXT_APPLICATION is set in the flow plan
if PLAN_CONTEXT_APPLICATION in self.executor.plan.context:
app: Application = self.executor.plan.context.get(
challenge.initial_data["application_pre"] = self.executor.plan.context.get(
PLAN_CONTEXT_APPLICATION, Application()
)
challenge.initial_data["application_pre"] = app.name
if launch_url := app.get_launch_url():
challenge.initial_data["application_pre_launch"] = launch_url
).name
if (
PLAN_CONTEXT_DEVICE in self.executor.plan.context
and PLAN_CONTEXT_DEVICE_AUTH_TOKEN in self.executor.plan.context

44
authentik/tasks/forks.py Normal file
View File

@@ -0,0 +1,44 @@
from signal import pause
from structlog.stdlib import get_logger
from authentik.lib.config import CONFIG
LOGGER = get_logger()
def worker_healthcheck():
import authentik.tasks.setup # noqa
from authentik.tasks.middleware import WorkerHealthcheckMiddleware
host, _, port = CONFIG.get("listen.http").rpartition(":")
try:
port = int(port)
except ValueError:
LOGGER.error(f"Invalid port entered: {port}")
WorkerHealthcheckMiddleware.run(host, port)
pause()
def worker_status():
import authentik.tasks.setup # noqa
from authentik.tasks.middleware import WorkerStatusMiddleware
WorkerStatusMiddleware.run()
def worker_metrics():
import authentik.tasks.setup # noqa
from authentik.tasks.middleware import MetricsMiddleware
addr, _, port = CONFIG.get("listen.metrics").rpartition(":")
try:
port = int(port)
except ValueError:
LOGGER.error(f"Invalid port entered: {port}")
MetricsMiddleware.run(addr, port)
pause()

View File

@@ -1,37 +1,29 @@
import socket
from collections.abc import Callable
from http.server import BaseHTTPRequestHandler
from threading import Event as TEvent
from threading import Thread, current_thread
from time import sleep
from typing import Any, cast
import pglock
from django.db import OperationalError, connections, transaction
from django.db import OperationalError, connections
from django.utils.timezone import now
from django_dramatiq_postgres.middleware import (
CurrentTask as BaseCurrentTask,
)
from django_dramatiq_postgres.middleware import (
HTTPServer,
HTTPServerThread,
)
from django_dramatiq_postgres.middleware import HTTPServer
from django_dramatiq_postgres.middleware import (
MetricsMiddleware as BaseMetricsMiddleware,
)
from django_dramatiq_postgres.middleware import (
_MetricsHandler as BaseMetricsHandler,
)
from dramatiq import Worker
from dramatiq.broker import Broker
from dramatiq.message import Message
from dramatiq.middleware import Middleware
from psycopg.errors import Error
from setproctitle import setthreadtitle
from structlog.stdlib import get_logger
from authentik import authentik_full_version
from authentik.events.models import Event, EventAction
from authentik.lib.config import CONFIG
from authentik.lib.sentry import should_ignore_exception
from authentik.lib.utils.reflection import class_to_path
from authentik.root.monitoring import monitoring_set
@@ -221,39 +213,17 @@ class _healthcheck_handler(BaseHTTPRequestHandler):
class WorkerHealthcheckMiddleware(Middleware):
thread: HTTPServerThread | None
@property
def forks(self):
from authentik.tasks.forks import worker_healthcheck
def __init__(self):
host, _, port = CONFIG.get("listen.http").rpartition(":")
try:
port = int(port)
except ValueError:
LOGGER.error(f"Invalid port entered: {port}")
self.host, self.port = host, port
def after_worker_boot(self, broker: Broker, worker: Worker):
self.thread = HTTPServerThread(
target=WorkerHealthcheckMiddleware.run, args=(self.host, self.port)
)
self.thread.start()
def before_worker_shutdown(self, broker: Broker, worker: Worker):
server = self.thread.server
if server:
server.shutdown()
LOGGER.debug("Stopping WorkerHealthcheckMiddleware")
self.thread.join()
return [worker_healthcheck]
@staticmethod
def run(addr: str, port: int):
setthreadtitle("authentik Worker Healthcheck server")
try:
server = HTTPServer((addr, port), _healthcheck_handler)
thread = cast(HTTPServerThread, current_thread())
thread.server = server
server.serve_forever()
httpd = HTTPServer((addr, port), _healthcheck_handler)
httpd.serve_forever()
except OSError as exc:
get_logger(__name__, type(WorkerHealthcheckMiddleware)).warning(
"Port is already in use, not starting healthcheck server",
@@ -262,50 +232,36 @@ class WorkerHealthcheckMiddleware(Middleware):
class WorkerStatusMiddleware(Middleware):
thread: Thread | None
thread_event: TEvent | None
@property
def forks(self):
from authentik.tasks.forks import worker_status
def after_worker_boot(self, broker: Broker, worker: Worker):
self.thread_event = TEvent()
self.thread = Thread(target=WorkerStatusMiddleware.run, args=(self.thread_event,))
self.thread.start()
def before_worker_shutdown(self, broker: Broker, worker: Worker):
self.thread_event.set()
LOGGER.debug("Stopping WorkerStatusMiddleware")
self.thread.join()
return [worker_status]
@staticmethod
def run(event: TEvent):
setthreadtitle("authentik Worker status")
with transaction.atomic():
hostname = socket.gethostname()
WorkerStatus.objects.filter(hostname=hostname).delete()
status, _ = WorkerStatus.objects.update_or_create(
hostname=hostname,
version=authentik_full_version(),
)
while not event.is_set():
def run():
status = WorkerStatus.objects.create(
hostname=socket.gethostname(),
version=authentik_full_version(),
)
while True:
try:
WorkerStatusMiddleware.keep(event, status)
WorkerStatusMiddleware.keep(status)
except DB_ERRORS: # pragma: no cover
event.wait(10)
sleep(10)
try:
connections.close_all()
except DB_ERRORS:
pass
@staticmethod
def keep(event: TEvent, status: WorkerStatus):
def keep(status: WorkerStatus):
lock_id = f"goauthentik.io/worker/status/{status.pk}"
with pglock.advisory(lock_id, side_effect=pglock.Raise):
while not event.is_set():
status.refresh_from_db()
old_last_seen = status.last_seen
while True:
status.last_seen = now()
if old_last_seen != status.last_seen:
status.save(update_fields=("last_seen",))
event.wait(30)
status.save(update_fields=("last_seen",))
sleep(30)
class _MetricsHandler(BaseMetricsHandler):
@@ -315,26 +271,10 @@ class _MetricsHandler(BaseMetricsHandler):
class MetricsMiddleware(BaseMetricsMiddleware):
thread: HTTPServerThread | None
handler_class = _MetricsHandler
@property
def forks(self) -> list[Callable[[], None]]:
return []
def forks(self):
from authentik.tasks.forks import worker_metrics
def after_worker_boot(self, broker: Broker, worker: Worker):
addr, _, port = CONFIG.get("listen.metrics").rpartition(":")
try:
port = int(port)
except ValueError:
LOGGER.error(f"Invalid port entered: {port}")
self.thread = HTTPServerThread(target=MetricsMiddleware.run, args=(addr, port))
self.thread.start()
def before_worker_shutdown(self, broker: Broker, worker: Worker):
server = self.thread.server
if server:
server.shutdown()
LOGGER.debug("Stopping MetricsMiddleware")
self.thread.join()
return [worker_metrics]

View File

@@ -10,26 +10,24 @@ from dramatiq.results.middleware import Results
from dramatiq.worker import Worker, _ConsumerThread, _WorkerThread
from authentik.tasks.broker import PostgresBroker
from authentik.tasks.middleware import WorkerHealthcheckMiddleware
TESTING_QUEUE = "testing"
from authentik.tasks.middleware import MetricsMiddleware
class TestWorker(Worker):
def __init__(self, broker: Broker):
def __init__(self, queue_name: str, broker: Broker):
super().__init__(broker=broker)
self.work_queue = PriorityQueue()
self.consumers = {
TESTING_QUEUE: _ConsumerThread(
queue_name: _ConsumerThread(
broker=self.broker,
queue_name=TESTING_QUEUE,
queue_name=queue_name,
prefetch=2,
work_queue=self.work_queue,
worker_timeout=1,
),
}
self.consumers[TESTING_QUEUE].consumer = self.broker.consume(
queue_name=TESTING_QUEUE,
self.consumers[queue_name].consumer = self.broker.consume(
queue_name=queue_name,
prefetch=2,
timeout=1,
)
@@ -42,29 +40,18 @@ class TestWorker(Worker):
self.broker.emit_before("worker_boot", self)
self.broker.emit_after("worker_boot", self)
self.broker.emit_after("process_boot")
def process_message(self, message: MessageProxy):
self.work_queue.put((0, message))
self.consumers[TESTING_QUEUE].consumer.in_processing.add(message.message_id)
self.work_queue.put(message)
self.consumers[message.queue_name].consumer.in_processing.add(message.message_id)
self._worker.process_message(message)
class TestBroker(PostgresBroker):
worker: TestWorker | None = None
def start(self):
self.worker = TestWorker(broker=self)
def close(self):
self.emit_before("worker_shutdown", self)
return super().close()
def enqueue(self, *args, **kwargs):
message = super().enqueue(*args, **kwargs).copy(queue_name=TESTING_QUEUE)
if not self.worker:
return message
self.worker.process_message(MessageProxy(message))
message = super().enqueue(*args, **kwargs)
worker = TestWorker(message.queue_name, broker=self)
worker.process_message(MessageProxy(message))
return message
@@ -82,8 +69,8 @@ def use_test_broker():
middleware: Middleware = import_string(middleware_class)(
**middleware_kwargs,
)
if isinstance(middleware, WorkerHealthcheckMiddleware):
middleware.port = 9102
if isinstance(middleware, MetricsMiddleware):
continue
if isinstance(middleware, Retries):
middleware.max_retries = 0
if isinstance(middleware, Results):
@@ -93,6 +80,4 @@ def use_test_broker():
)
broker.add_middleware(middleware)
broker.start()
set_broker(broker)
return broker

View File

@@ -1,7 +1,10 @@
from json import loads
from django.test import TestCase
from django.urls import reverse
from authentik.core.tests.utils import create_test_admin_user
from authentik.core.models import Group, User
from authentik.lib.generators import generate_id
class TestAdminAPI(TestCase):
@@ -9,13 +12,15 @@ class TestAdminAPI(TestCase):
def setUp(self) -> None:
super().setUp()
self.user = create_test_admin_user()
self.user = User.objects.create(username=generate_id())
self.group = Group.objects.create(name=generate_id(), is_superuser=True)
self.group.users.add(self.user)
self.group.save()
self.client.force_login(self.user)
def test_workers(self):
"""Test Workers API"""
response = self.client.get(reverse("authentik_api:tasks_workers"))
self.assertEqual(response.status_code, 200)
# Disabled for flakiness
# body = loads(response.content)
# self.assertEqual(len(body), 1)
body = loads(response.content)
self.assertEqual(len(body), 0)

View File

@@ -1,52 +0,0 @@
from django.test import TestCase
from dramatiq import actor, get_broker
from authentik.tasks.middleware import CurrentTask
from authentik.tasks.models import Task, TaskLog
class TestWorkerMiddleware(TestCase):
def test_task_log(self):
@actor
def test_task():
self = CurrentTask.get_task()
self.info("foo")
test_task.send()
task = Task.objects.filter(actor_name=test_task.actor_name).first()
logs = list(
TaskLog.objects.filter(task=task).order_by("timestamp").values_list("event", flat=True)
)
self.assertEqual(
logs,
[
"Task has been queued",
"Task is being processed",
"foo",
"Task finished processing without errors",
],
)
broker = get_broker()
del broker.actors[test_task.actor_name]
def test_task_exceptions(self):
@actor
def test_task():
raise ValueError("foo")
test_task.send()
task = Task.objects.filter(actor_name=test_task.actor_name).first()
logs = list(
TaskLog.objects.filter(task=task).order_by("timestamp").values_list("event", flat=True)
)
self.assertEqual(
logs,
[
"Task has been queued",
"Task is being processed",
"foo",
],
)
broker = get_broker()
del broker.actors[test_task.actor_name]

2
go.mod
View File

@@ -30,7 +30,7 @@ require (
github.com/spf13/cobra v1.10.2
github.com/stretchr/testify v1.11.1
github.com/wwt/guac v1.3.2
goauthentik.io/api/v3 v3.2026020.17-0.20260304104333-840924fe52c4
goauthentik.io/api/v3 v3.2026020.17-0.20260223141659-4c1444ee54d9
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
golang.org/x/oauth2 v0.35.0
golang.org/x/sync v0.19.0

2
go.sum
View File

@@ -218,8 +218,6 @@ goauthentik.io/api/v3 v3.2026020.17-0.20260217173516-3a500f6eed7d h1:Gb26L41O+Q7
goauthentik.io/api/v3 v3.2026020.17-0.20260217173516-3a500f6eed7d/go.mod h1:uYa+yGMglhJy8ymyUQ8KQiJjOb3UZTuPQ24Ot2s9BCo=
goauthentik.io/api/v3 v3.2026020.17-0.20260223141659-4c1444ee54d9 h1:tuvgm4e1nV0ZPZy24wOeJcuAbMnhbJA09BuI2fzBHRk=
goauthentik.io/api/v3 v3.2026020.17-0.20260223141659-4c1444ee54d9/go.mod h1:uYa+yGMglhJy8ymyUQ8KQiJjOb3UZTuPQ24Ot2s9BCo=
goauthentik.io/api/v3 v3.2026020.17-0.20260304104333-840924fe52c4 h1:zjmi1QNVQPABt0Yx5hws1lXR3tuTI23Ae7MwXffbP/s=
goauthentik.io/api/v3 v3.2026020.17-0.20260304104333-840924fe52c4/go.mod h1:uYa+yGMglhJy8ymyUQ8KQiJjOb3UZTuPQ24Ot2s9BCo=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=

View File

@@ -26,6 +26,7 @@ import (
"goauthentik.io/api/v3"
"goauthentik.io/internal/config"
"goauthentik.io/internal/outpost/ak"
"goauthentik.io/internal/outpost/proxyv2/constants"
"goauthentik.io/internal/outpost/proxyv2/hs256"
"goauthentik.io/internal/outpost/proxyv2/metrics"
"goauthentik.io/internal/outpost/proxyv2/templates"
@@ -293,16 +294,22 @@ func (a *Application) Stop() {
func (a *Application) handleSignOut(rw http.ResponseWriter, r *http.Request) {
redirect := a.endpoint.EndSessionEndpoint
cc := a.getClaimsFromSession(rw, r)
if cc == nil {
s, err := a.sessions.Get(r, a.SessionName())
if err != nil {
a.redirectToStart(rw, r)
return
}
c, exists := s.Values[constants.SessionClaims]
if c == nil && !exists {
a.redirectToStart(rw, r)
return
}
cc := c.(types.Claims)
uv := url.Values{
"id_token_hint": []string{cc.RawToken},
}
redirect += "?" + uv.Encode()
err := a.Logout(r.Context(), func(c types.Claims) bool {
err = a.Logout(r.Context(), func(c types.Claims) bool {
return c.Sub == cc.Sub
})
if err != nil {

View File

@@ -187,7 +187,10 @@ func BuildConnConfig(cfg config.PostgreSQLConfig) (*pgx.ConnConfig, error) {
if connConfig.RuntimeParams == nil {
connConfig.RuntimeParams = make(map[string]string)
}
effectiveSearchPath := cfg.DefaultSchema
if cfg.DefaultSchema != "" {
connConfig.RuntimeParams["search_path"] = cfg.DefaultSchema
}
// Parse and apply connection options if specified
if cfg.ConnOptions != "" {
@@ -195,39 +198,12 @@ func BuildConnConfig(cfg config.PostgreSQLConfig) (*pgx.ConnConfig, error) {
if err != nil {
return nil, fmt.Errorf("failed to parse connection options: %w", err)
}
// search_path from ConnOptions is not supported here; Django controls schema selection.
// Always remove it so it cannot end up in startup RuntimeParams via applyConnOptions.
delete(connOpts, "search_path")
if err := applyConnOptions(connConfig, connOpts); err != nil {
return nil, fmt.Errorf("failed to apply connection options: %w", err)
}
}
// search_path may already be present via pgx/libpq inherited defaults (e.g. service files).
// Always remove it from startup RuntimeParams; apply it via AfterConnect instead.
if inheritedSearchPath, hasInheritedSearchPath := connConfig.RuntimeParams["search_path"]; hasInheritedSearchPath {
if effectiveSearchPath == "" {
effectiveSearchPath = inheritedSearchPath
}
delete(connConfig.RuntimeParams, "search_path")
}
// Set search_path after connection startup to avoid startup-parameter issues with PgBouncer.
if effectiveSearchPath != "" {
connConfig.AfterConnect = func(ctx context.Context, pgConn *pgconn.PgConn) error {
result := pgConn.ExecParams(
ctx,
"select pg_catalog.set_config('search_path', $1, false)",
[][]byte{[]byte(effectiveSearchPath)},
nil,
nil,
nil,
).Read()
return result.Err
}
}
return connConfig, nil
}

View File

@@ -700,7 +700,7 @@ func TestBuildConnConfig(t *testing.T) {
DefaultSchema: "custom_schema",
},
validate: func(t *testing.T, cc *pgx.ConnConfig) {
assert.NotNil(t, cc.AfterConnect)
assert.Equal(t, "custom_schema", cc.RuntimeParams["search_path"])
},
},
{
@@ -756,7 +756,7 @@ func TestBuildConnConfig(t *testing.T) {
assert.Equal(t, "admin", cc.User)
assert.Equal(t, "my super secret password!@#", cc.Password)
assert.Equal(t, "production", cc.Database)
assert.NotNil(t, cc.AfterConnect)
assert.Equal(t, "app_schema", cc.RuntimeParams["search_path"])
assert.Equal(t, "authentik", cc.RuntimeParams["application_name"])
},
},
@@ -863,7 +863,7 @@ func TestBuildConnConfig_WithSSLCertificates(t *testing.T) {
assert.Equal(t, "db.example.com", cc.TLSConfig.ServerName)
assert.NotNil(t, cc.TLSConfig.RootCAs)
assert.Len(t, cc.TLSConfig.Certificates, 1)
assert.NotNil(t, cc.AfterConnect)
assert.Equal(t, "app_schema", cc.RuntimeParams["search_path"])
assert.Equal(t, "authentik", cc.RuntimeParams["application_name"])
},
},
@@ -1357,83 +1357,6 @@ func TestBuildConnConfig_WithBase64EncodedConnOptions(t *testing.T) {
}
}
// Verifies DefaultSchema is applied via AfterConnect and never via startup RuntimeParams.
func TestBuildConnConfig_SearchPath_DefaultSchema(t *testing.T) {
cfg := config.PostgreSQLConfig{
Host: "localhost",
Port: "5432",
User: "authentik",
Name: "authentik",
DefaultSchema: "default_schema",
}
connConfig, err := BuildConnConfig(cfg)
require.NoError(t, err)
require.NotNil(t, connConfig.AfterConnect)
_, hasSearchPath := connConfig.RuntimeParams["search_path"]
assert.False(t, hasSearchPath, "search_path should not appear in RuntimeParams")
}
// Verifies ConnOptions search_path is ignored and excluded from startup RuntimeParams.
func TestBuildConnConfig_SearchPath_ConnOptions(t *testing.T) {
cfg := config.PostgreSQLConfig{
Host: "localhost",
Port: "5432",
User: "authentik",
Name: "authentik",
ConnOptions: base64.StdEncoding.EncodeToString([]byte(`{"search_path":"connopt_schema"}`)),
}
connConfig, err := BuildConnConfig(cfg)
require.NoError(t, err)
assert.Nil(t, connConfig.AfterConnect)
_, hasSearchPath := connConfig.RuntimeParams["search_path"]
assert.False(t, hasSearchPath, "search_path should not appear in RuntimeParams")
}
// Verifies ConnOptions search_path does not override DefaultSchema and other conn options still apply.
func TestBuildConnConfig_SearchPath_ConnOptionsIgnoredWhenDefaultSchemaSet(t *testing.T) {
cfg := config.PostgreSQLConfig{
Host: "localhost",
Port: "5432",
User: "authentik",
Name: "authentik",
DefaultSchema: "default_schema",
ConnOptions: base64.StdEncoding.EncodeToString([]byte(`{"search_path":"override_schema","application_name":"authentik-proxy"}`)),
}
connConfig, err := BuildConnConfig(cfg)
require.NoError(t, err)
require.NotNil(t, connConfig.AfterConnect)
assert.Equal(t, "authentik-proxy", connConfig.RuntimeParams["application_name"])
_, hasSearchPath := connConfig.RuntimeParams["search_path"]
assert.False(t, hasSearchPath, "search_path should not appear in RuntimeParams")
}
// Verifies inherited search_path from pgx/libpq defaults is removed from startup RuntimeParams.
func TestBuildConnConfig_SearchPath_InheritedServiceSetting(t *testing.T) {
serviceFile := filepath.Join(t.TempDir(), "pg_service.conf")
err := os.WriteFile(serviceFile, []byte("[authentik-test]\nsearch_path=service_schema\n"), 0o600)
require.NoError(t, err)
t.Setenv("PGSERVICE", "authentik-test")
t.Setenv("PGSERVICEFILE", serviceFile)
cfg := config.PostgreSQLConfig{
Host: "localhost",
Port: "5432",
User: "authentik",
Name: "authentik",
}
connConfig, err := BuildConnConfig(cfg)
require.NoError(t, err)
require.NotNil(t, connConfig.AfterConnect)
_, hasSearchPath := connConfig.RuntimeParams["search_path"]
assert.False(t, hasSearchPath, "search_path should not appear in RuntimeParams")
}
// TestBuildConnConfig_TargetSessionAttrs demonstrates how target_session_attrs
// should be properly handled using pgx's ValidateConnect callback
func TestBuildConnConfig_TargetSessionAttrs(t *testing.T) {

View File

@@ -9,7 +9,7 @@
"version": "0.0.0",
"license": "MIT",
"devDependencies": {
"aws-cdk": "^2.1108.0",
"aws-cdk": "^2.1107.0",
"cross-env": "^10.1.0"
},
"engines": {
@@ -25,9 +25,9 @@
"license": "MIT"
},
"node_modules/aws-cdk": {
"version": "2.1108.0",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1108.0.tgz",
"integrity": "sha512-FHnyhnYZoRc2W0C9mNzhNn6fO2vH4xNINsKfJaA7AFDuymgQ39JhEnrM4AHaoikIBqXYeNLWElvvkusY9l3ulw==",
"version": "2.1107.0",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1107.0.tgz",
"integrity": "sha512-7GKCq7p/33Jw+C+Ohwl4LnnKjvI/MzemeNZlTu/Kg8IwuZx5WEXEi32YLOlxbE1JOvleDslCWK5AIkBZ0omx/Q==",
"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.1108.0",
"aws-cdk": "^2.1107.0",
"cross-env": "^10.1.0"
},
"engines": {

View File

@@ -26,7 +26,7 @@ RUN npm run build && \
npm run build:sfe
# Stage 2: Build go proxy
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.26.0-trixie@sha256:4e603da0ea8df4a8ab10cbf0b3061f7823d277e82ea210a47c32a5fafb43cc43 AS go-builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.26.0-trixie@sha256:d0a3e4b733ecc47e92a7e7f0fa141392e5a2349e288470aad1ffd82552da5139 AS go-builder
ARG TARGETOS
ARG TARGETARCH
@@ -78,9 +78,9 @@ RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
/bin/sh -c "GEOIPUPDATE_LICENSE_KEY_FILE=/run/secrets/GEOIPUPDATE_LICENSE_KEY /usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0"
# Stage 4: Download uv
FROM ghcr.io/astral-sh/uv:0.10.8@sha256:88234bc9e09c2b2f6d176a3daf411419eb0370d450a08129257410de9cfafd2a AS uv
FROM ghcr.io/astral-sh/uv:0.10.6@sha256:2f2ccd27bbf953ec7a9e3153a4563705e41c852a5e1912b438fc44d88d6cb52c AS uv
# Stage 5: Base python image
FROM ghcr.io/goauthentik/fips-python:3.14.3-slim-trixie-fips@sha256:43260c054d91fccebf7c5f38f682d65abfc88afa7a5d1c622a938f0fbe1fdcab AS python-base
FROM ghcr.io/goauthentik/fips-python:3.14.3-slim-trixie-fips@sha256:de8ad649ed77baa64c07deb0dba2151e18dcb0408fe6ff37bdef236aabb9a576 AS python-base
ENV VENV_PATH="/ak-root/.venv" \
PATH="/lifecycle:/ak-root/.venv/bin:$PATH" \

View File

@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1
# Stage 1: Build
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.26.0-trixie@sha256:4e603da0ea8df4a8ab10cbf0b3061f7823d277e82ea210a47c32a5fafb43cc43 AS builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.26.0-trixie@sha256:d0a3e4b733ecc47e92a7e7f0fa141392e5a2349e288470aad1ffd82552da5139 AS builder
ARG TARGETOS
ARG TARGETARCH
@@ -31,7 +31,7 @@ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
go build -o /go/ldap ./cmd/ldap
# Stage 2: Run
FROM ghcr.io/goauthentik/fips-debian:trixie-slim-fips@sha256:487b9f11726364cbc83617203976b5bf61bac67ed13502b2836d64a0bd466628
FROM ghcr.io/goauthentik/fips-debian:trixie-slim-fips@sha256:7b82e2433395fed1e400120bcd1686de2faba9f59251e19b60dd7dd1ed9efe42
ARG VERSION
ARG GIT_BUILD_HASH

View File

@@ -17,7 +17,7 @@ COPY web .
RUN npm run build-proxy
# Stage 2: Build
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.26.0-trixie@sha256:4e603da0ea8df4a8ab10cbf0b3061f7823d277e82ea210a47c32a5fafb43cc43 AS builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.26.0-trixie@sha256:d0a3e4b733ecc47e92a7e7f0fa141392e5a2349e288470aad1ffd82552da5139 AS builder
ARG TARGETOS
ARG TARGETARCH
@@ -47,7 +47,7 @@ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
go build -o /go/proxy ./cmd/proxy
# Stage 3: Run
FROM ghcr.io/goauthentik/fips-debian:trixie-slim-fips@sha256:487b9f11726364cbc83617203976b5bf61bac67ed13502b2836d64a0bd466628
FROM ghcr.io/goauthentik/fips-debian:trixie-slim-fips@sha256:7b82e2433395fed1e400120bcd1686de2faba9f59251e19b60dd7dd1ed9efe42
ARG VERSION
ARG GIT_BUILD_HASH

View File

@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1
# Stage 1: Build
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.26.0-trixie@sha256:4e603da0ea8df4a8ab10cbf0b3061f7823d277e82ea210a47c32a5fafb43cc43 AS builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.26.0-trixie@sha256:d0a3e4b733ecc47e92a7e7f0fa141392e5a2349e288470aad1ffd82552da5139 AS builder
ARG TARGETOS
ARG TARGETARCH

View File

@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1
# Stage 1: Build
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.26.0-trixie@sha256:4e603da0ea8df4a8ab10cbf0b3061f7823d277e82ea210a47c32a5fafb43cc43 AS builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.26.0-trixie@sha256:d0a3e4b733ecc47e92a7e7f0fa141392e5a2349e288470aad1ffd82552da5139 AS builder
ARG TARGETOS
ARG TARGETARCH
@@ -31,7 +31,7 @@ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
go build -o /go/radius ./cmd/radius
# Stage 2: Run
FROM ghcr.io/goauthentik/fips-debian:trixie-slim-fips@sha256:487b9f11726364cbc83617203976b5bf61bac67ed13502b2836d64a0bd466628
FROM ghcr.io/goauthentik/fips-debian:trixie-slim-fips@sha256:7b82e2433395fed1e400120bcd1686de2faba9f59251e19b60dd7dd1ed9efe42
ARG VERSION
ARG GIT_BUILD_HASH

694
package-lock.json generated
View File

@@ -10,15 +10,11 @@
"dependencies": {
"@eslint/js": "^9.39.3",
"@goauthentik/eslint-config": "./packages/eslint-config",
"@goauthentik/logger-js": "./packages/logger-js",
"@goauthentik/prettier-config": "./packages/prettier-config",
"@goauthentik/tsconfig": "./packages/tsconfig",
"@typescript-eslint/eslint-plugin": "^8.56.1",
"@typescript-eslint/parser": "^8.56.1",
"eslint": "^9.39.3",
"npm-run-all": "^4.1.5",
"pino": "^10.3.1",
"pino-pretty": "^13.1.2",
"prettier": "^3.8.1",
"prettier-plugin-packagejson": "^3.0.0",
"typescript": "^5.9.3",
@@ -508,10 +504,6 @@
"resolved": "packages/eslint-config",
"link": true
},
"node_modules/@goauthentik/logger-js": {
"resolved": "packages/logger-js",
"link": true
},
"node_modules/@goauthentik/prettier-config": {
"resolved": "packages/prettier-config",
"link": true
@@ -698,12 +690,6 @@
"node": ">= 8"
}
},
"node_modules/@pinojs/redact": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz",
"integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==",
"license": "MIT"
},
"node_modules/@rtsao/scc": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
@@ -1274,15 +1260,6 @@
"node": ">= 0.4"
}
},
"node_modules/atomic-sleep": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
"integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==",
"license": "MIT",
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/available-typed-arrays": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
@@ -1495,12 +1472,6 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"license": "MIT"
},
"node_modules/colorette": {
"version": "2.0.20",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
"license": "MIT"
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -1587,15 +1558,6 @@
"node": ">=4.0"
}
},
"node_modules/dateformat": {
"version": "4.6.3",
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz",
"integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==",
"license": "MIT",
"engines": {
"node": "*"
}
},
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
@@ -1709,24 +1671,6 @@
"integrity": "sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==",
"license": "ISC"
},
"node_modules/end-of-stream": {
"version": "1.4.5",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
"integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
"license": "MIT",
"dependencies": {
"once": "^1.4.0"
}
},
"node_modules/error-ex": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
"integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
"license": "MIT",
"dependencies": {
"is-arrayish": "^0.2.1"
}
},
"node_modules/es-abstract": {
"version": "1.24.1",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz",
@@ -2395,12 +2339,6 @@
"node": ">=0.10.0"
}
},
"node_modules/fast-copy": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-4.0.2.tgz",
"integrity": "sha512-ybA6PDXIXOXivLJK/z9e+Otk7ve13I4ckBvGO5I2RRmBU1gMHLVDJYEuJYhGwez7YNlYji2M2DvVU+a9mSFDlw==",
"license": "MIT"
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -2419,12 +2357,6 @@
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
"license": "MIT"
},
"node_modules/fast-safe-stringify": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
"integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==",
"license": "MIT"
},
"node_modules/fastq": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
@@ -3110,12 +3042,6 @@
"node": ">= 0.4"
}
},
"node_modules/help-me": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz",
"integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==",
"license": "MIT"
},
"node_modules/hermes-estree": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
@@ -3131,12 +3057,6 @@
"hermes-estree": "0.25.1"
}
},
"node_modules/hosted-git-info": {
"version": "2.8.9",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
"license": "ISC"
},
"node_modules/ignore": {
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
@@ -3225,12 +3145,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
"license": "MIT"
},
"node_modules/is-async-function": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
@@ -3652,15 +3566,6 @@
"node": ">= 0.4"
}
},
"node_modules/joycon": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz",
"integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==",
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/js-levenshtein-esm": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/js-levenshtein-esm/-/js-levenshtein-esm-2.0.0.tgz",
@@ -3703,12 +3608,6 @@
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
"license": "MIT"
},
"node_modules/json-parse-better-errors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
"license": "MIT"
},
"node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
@@ -3782,21 +3681,6 @@
"node": ">= 0.8.0"
}
},
"node_modules/load-json-file": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
"integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==",
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.1.2",
"parse-json": "^4.0.0",
"pify": "^3.0.0",
"strip-bom": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/locate-path": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
@@ -3873,14 +3757,6 @@
"node": ">= 0.4"
}
},
"node_modules/memorystream": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
"integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==",
"engines": {
"node": ">= 0.10.0"
}
},
"node_modules/minimatch": {
"version": "10.2.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.3.tgz",
@@ -3935,12 +3811,6 @@
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"license": "MIT"
},
"node_modules/nice-try": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
"license": "MIT"
},
"node_modules/node-cache": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz",
@@ -3986,218 +3856,6 @@
"integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
"license": "MIT"
},
"node_modules/normalize-package-data": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
"integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
"license": "BSD-2-Clause",
"dependencies": {
"hosted-git-info": "^2.1.4",
"resolve": "^1.10.0",
"semver": "2 || 3 || 4 || 5",
"validate-npm-package-license": "^3.0.1"
}
},
"node_modules/normalize-package-data/node_modules/semver": {
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"license": "ISC",
"bin": {
"semver": "bin/semver"
}
},
"node_modules/npm-run-all": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz",
"integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==",
"license": "MIT",
"dependencies": {
"ansi-styles": "^3.2.1",
"chalk": "^2.4.1",
"cross-spawn": "^6.0.5",
"memorystream": "^0.3.1",
"minimatch": "^3.0.4",
"pidtree": "^0.3.0",
"read-pkg": "^3.0.0",
"shell-quote": "^1.6.1",
"string.prototype.padend": "^3.0.0"
},
"bin": {
"npm-run-all": "bin/npm-run-all/index.js",
"run-p": "bin/run-p/index.js",
"run-s": "bin/run-s/index.js"
},
"engines": {
"node": ">= 4"
}
},
"node_modules/npm-run-all/node_modules/ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"license": "MIT",
"dependencies": {
"color-convert": "^1.9.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/npm-run-all/node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"license": "MIT"
},
"node_modules/npm-run-all/node_modules/brace-expansion": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/npm-run-all/node_modules/chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"license": "MIT",
"dependencies": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/npm-run-all/node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"license": "MIT",
"dependencies": {
"color-name": "1.1.3"
}
},
"node_modules/npm-run-all/node_modules/color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
"license": "MIT"
},
"node_modules/npm-run-all/node_modules/cross-spawn": {
"version": "6.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz",
"integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==",
"license": "MIT",
"dependencies": {
"nice-try": "^1.0.4",
"path-key": "^2.0.1",
"semver": "^5.5.0",
"shebang-command": "^1.2.0",
"which": "^1.2.9"
},
"engines": {
"node": ">=4.8"
}
},
"node_modules/npm-run-all/node_modules/escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"license": "MIT",
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/npm-run-all/node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/npm-run-all/node_modules/minimatch": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
"license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/npm-run-all/node_modules/path-key": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
"integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==",
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/npm-run-all/node_modules/semver": {
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"license": "ISC",
"bin": {
"semver": "bin/semver"
}
},
"node_modules/npm-run-all/node_modules/shebang-command": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
"integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==",
"license": "MIT",
"dependencies": {
"shebang-regex": "^1.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/npm-run-all/node_modules/shebang-regex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
"integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/npm-run-all/node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"license": "MIT",
"dependencies": {
"has-flag": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/npm-run-all/node_modules/which": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
"integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
"license": "ISC",
"dependencies": {
"isexe": "^2.0.0"
},
"bin": {
"which": "bin/which"
}
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -4313,15 +3971,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/on-exit-leak-free": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz",
"integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==",
"license": "MIT",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -4407,19 +4056,6 @@
"node": ">=6"
}
},
"node_modules/parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==",
"license": "MIT",
"dependencies": {
"error-ex": "^1.3.1",
"json-parse-better-errors": "^1.0.1"
},
"engines": {
"node": ">=4"
}
},
"node_modules/parse5": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
@@ -4468,18 +4104,6 @@
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"license": "MIT"
},
"node_modules/path-type": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
"integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
"license": "MIT",
"dependencies": {
"pify": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -4498,100 +4122,6 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/pidtree": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz",
"integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==",
"license": "MIT",
"bin": {
"pidtree": "bin/pidtree.js"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/pify": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
"integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==",
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/pino": {
"version": "10.3.1",
"resolved": "https://registry.npmjs.org/pino/-/pino-10.3.1.tgz",
"integrity": "sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==",
"license": "MIT",
"dependencies": {
"@pinojs/redact": "^0.4.0",
"atomic-sleep": "^1.0.0",
"on-exit-leak-free": "^2.1.0",
"pino-abstract-transport": "^3.0.0",
"pino-std-serializers": "^7.0.0",
"process-warning": "^5.0.0",
"quick-format-unescaped": "^4.0.3",
"real-require": "^0.2.0",
"safe-stable-stringify": "^2.3.1",
"sonic-boom": "^4.0.1",
"thread-stream": "^4.0.0"
},
"bin": {
"pino": "bin.js"
}
},
"node_modules/pino-abstract-transport": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-3.0.0.tgz",
"integrity": "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==",
"license": "MIT",
"dependencies": {
"split2": "^4.0.0"
}
},
"node_modules/pino-pretty": {
"version": "13.1.3",
"resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-13.1.3.tgz",
"integrity": "sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==",
"license": "MIT",
"dependencies": {
"colorette": "^2.0.7",
"dateformat": "^4.6.3",
"fast-copy": "^4.0.0",
"fast-safe-stringify": "^2.1.1",
"help-me": "^5.0.0",
"joycon": "^3.1.1",
"minimist": "^1.2.6",
"on-exit-leak-free": "^2.1.0",
"pino-abstract-transport": "^3.0.0",
"pump": "^3.0.0",
"secure-json-parse": "^4.0.0",
"sonic-boom": "^4.0.1",
"strip-json-comments": "^5.0.2"
},
"bin": {
"pino-pretty": "bin.js"
}
},
"node_modules/pino-pretty/node_modules/strip-json-comments": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz",
"integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==",
"license": "MIT",
"engines": {
"node": ">=14.16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/pino-std-serializers": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz",
"integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==",
"license": "MIT"
},
"node_modules/possible-typed-array-names": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
@@ -4670,22 +4200,6 @@
}
}
},
"node_modules/process-warning": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz",
"integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fastify"
},
{
"type": "opencollective",
"url": "https://opencollective.com/fastify"
}
],
"license": "MIT"
},
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
@@ -4697,16 +4211,6 @@
"react-is": "^16.13.1"
}
},
"node_modules/pump": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
"integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
"license": "MIT",
"dependencies": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -4736,41 +4240,12 @@
],
"license": "MIT"
},
"node_modules/quick-format-unescaped": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
"integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==",
"license": "MIT"
},
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"license": "MIT"
},
"node_modules/read-pkg": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
"integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==",
"license": "MIT",
"dependencies": {
"load-json-file": "^4.0.0",
"normalize-package-data": "^2.3.2",
"path-type": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/real-require": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz",
"integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==",
"license": "MIT",
"engines": {
"node": ">= 12.13.0"
}
},
"node_modules/reflect.getprototypeof": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
@@ -4949,31 +4424,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/safe-stable-stringify": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
"integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/secure-json-parse": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.1.0.tgz",
"integrity": "sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fastify"
},
{
"type": "opencollective",
"url": "https://opencollective.com/fastify"
}
],
"license": "BSD-3-Clause"
},
"node_modules/segment-sort": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/segment-sort/-/segment-sort-1.0.9.tgz",
@@ -5059,18 +4509,6 @@
"node": ">=8"
}
},
"node_modules/shell-quote": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz",
"integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/side-channel": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
@@ -5143,15 +4581,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/sonic-boom": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz",
"integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==",
"license": "MIT",
"dependencies": {
"atomic-sleep": "^1.0.0"
}
},
"node_modules/sort-object-keys": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-2.1.0.tgz",
@@ -5188,47 +4617,6 @@
"node": ">=0.10.0"
}
},
"node_modules/spdx-correct": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
"integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==",
"license": "Apache-2.0",
"dependencies": {
"spdx-expression-parse": "^3.0.0",
"spdx-license-ids": "^3.0.0"
}
},
"node_modules/spdx-exceptions": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz",
"integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==",
"license": "CC-BY-3.0"
},
"node_modules/spdx-expression-parse": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
"integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
"license": "MIT",
"dependencies": {
"spdx-exceptions": "^2.1.0",
"spdx-license-ids": "^3.0.0"
}
},
"node_modules/spdx-license-ids": {
"version": "3.0.23",
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz",
"integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==",
"license": "CC0-1.0"
},
"node_modules/split2": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
"license": "ISC",
"engines": {
"node": ">= 10.x"
}
},
"node_modules/stop-iteration-iterator": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
@@ -5315,24 +4703,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/string.prototype.padend": {
"version": "3.1.6",
"resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz",
"integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==",
"license": "MIT",
"dependencies": {
"call-bind": "^1.0.7",
"define-properties": "^1.2.1",
"es-abstract": "^1.23.2",
"es-object-atoms": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/string.prototype.repeat": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz",
@@ -5462,18 +4832,6 @@
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"license": "MIT"
},
"node_modules/thread-stream": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-4.0.0.tgz",
"integrity": "sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA==",
"license": "MIT",
"dependencies": {
"real-require": "^0.2.0"
},
"engines": {
"node": ">=20"
}
},
"node_modules/tinyglobby": {
"version": "0.2.15",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
@@ -5751,16 +5109,6 @@
"node": ">= 4"
}
},
"node_modules/validate-npm-package-license": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
"integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
"license": "Apache-2.0",
"dependencies": {
"spdx-correct": "^3.0.0",
"spdx-expression-parse": "^3.0.0"
}
},
"node_modules/validator": {
"version": "13.15.26",
"resolved": "https://registry.npmjs.org/validator/-/validator-13.15.26.tgz",
@@ -5963,45 +5311,9 @@
}
}
},
"packages/logger-js": {
"name": "@goauthentik/logger-js",
"version": "1.0.1",
"license": "MIT",
"devDependencies": {
"@eslint/js": "^9.39.3",
"@goauthentik/prettier-config": "../prettier-config",
"@goauthentik/tsconfig": "../tsconfig",
"@types/node": "^25.3.0",
"@typescript-eslint/eslint-plugin": "^8.56.1",
"@typescript-eslint/parser": "^8.56.1",
"eslint": "^9.39.3",
"pino": "^10.3.1",
"pino-pretty": "^13.1.2",
"prettier": "^3.8.1",
"prettier-plugin-packagejson": "^3.0.0",
"typescript": "^5.9.3",
"typescript-eslint": "^8.56.1"
},
"engines": {
"node": ">=24",
"npm": ">=11.10.1"
},
"peerDependencies": {
"pino": "^10.3.1",
"pino-pretty": "^13.1.2"
},
"peerDependenciesMeta": {
"pino": {
"optional": true
},
"pino-pretty": {
"optional": true
}
}
},
"packages/prettier-config": {
"name": "@goauthentik/prettier-config",
"version": "3.4.3",
"version": "3.4.1",
"license": "MIT",
"dependencies": {
"format-imports": "^4.0.8"
@@ -6030,11 +5342,11 @@
},
"packages/tsconfig": {
"name": "@goauthentik/tsconfig",
"version": "1.0.7",
"version": "1.0.5",
"license": "MIT",
"engines": {
"node": ">=24",
"npm": ">=11.10.1"
"npm": ">=11.6.2"
}
}
}

View File

@@ -9,21 +9,14 @@
"lint:spellcheck": "echo 'Skipping spellcheck linting'"
},
"type": "module",
"imports": {
"#logger": "./packages/logger-js/lib/node.js"
},
"dependencies": {
"@eslint/js": "^9.39.3",
"@goauthentik/eslint-config": "./packages/eslint-config",
"@goauthentik/logger-js": "./packages/logger-js",
"@goauthentik/prettier-config": "./packages/prettier-config",
"@goauthentik/tsconfig": "./packages/tsconfig",
"@typescript-eslint/eslint-plugin": "^8.56.1",
"@typescript-eslint/parser": "^8.56.1",
"eslint": "^9.39.3",
"npm-run-all": "^4.1.5",
"pino": "^10.3.1",
"pino-pretty": "^13.1.2",
"prettier": "^3.8.1",
"prettier-plugin-packagejson": "^3.0.0",
"typescript": "^5.9.3",
@@ -37,16 +30,15 @@
"devEngines": {
"runtime": {
"name": "node",
"onFail": "warn",
"version": ">=24"
"onFail": "ignore",
"version": "24"
},
"packageManager": {
"name": "npm",
"version": ">=11.10.1",
"onFail": "warn"
"version": "11.10.1",
"onFail": "ignore"
}
},
"packageManager": "npm@11.11.0+sha512.f36811c4aae1fde639527368ae44c571d050006a608d67a191f195a801a52637a312d259186254aa3a3799b05335b7390539cf28656d18f0591a1125ba35f973",
"prettier": "@goauthentik/prettier-config",
"overrides": {
"format-imports": {

View File

@@ -235,16 +235,15 @@ class PostgresChannelLoopLayer(BaseChannelLayer):
try:
while True:
message_id, message = await q.get()
async with await self.connection() as conn:
async with conn.cursor() as cursor:
if message is None:
if message is None:
async with await self.connection() as conn:
async with conn.cursor() as cursor:
await cursor.execute(
sql.SQL("""
DELETE
SELECT {table}.{message}
FROM {table}
WHERE {table}.{id} = %s
RETURNING {table}.{message}
""").format(
""").format(
table=sql.Identifier(MESSAGE_TABLE),
id=sql.Identifier("id"),
message=sql.Identifier("message"),
@@ -255,18 +254,6 @@ class PostgresChannelLoopLayer(BaseChannelLayer):
if row is None:
continue
message = row[0]
else:
await cursor.execute(
sql.SQL("""
DELETE
FROM {table}
WHERE {table}.{id} = %s
""").format(
table=sql.Identifier(MESSAGE_TABLE),
id=sql.Identifier("id"),
),
(message_id,),
)
break
except asyncio.CancelledError, TimeoutError, GeneratorExit:
# We assume here that the reason we are cancelled is because the consumer

View File

@@ -1,7 +1,5 @@
import platform
import sys
from argparse import Namespace
from multiprocessing import set_start_method
from typing import Any
from django.apps.registry import apps
@@ -71,10 +69,7 @@ class Command(BaseCommand):
args.pid_file = pid_file
args.verbose = verbosity - 1
# > On macOS [...] the fork start method should be considered unsafe
# https://docs.python.org/3/library/multiprocessing.html#contexts-and-start-methods
if not platform.system() == "Darwin":
set_start_method("fork")
connections.close_all()
sys.exit(main(args)) # type: ignore[no-untyped-call]

View File

@@ -5,7 +5,6 @@ from collections.abc import Callable
from http.server import BaseHTTPRequestHandler
from http.server import HTTPServer as BaseHTTPServer
from ipaddress import IPv6Address, ip_address
from threading import Thread, current_thread
from typing import TYPE_CHECKING, Any, cast
from django.db import DatabaseError, close_old_connections, connections
@@ -23,13 +22,6 @@ if TYPE_CHECKING:
from django_dramatiq_postgres.broker import PostgresBroker
class HTTPServerThread(Thread):
"""Base class for a thread which runs an HTTP Server. Mainly used for typing
the `server` instance variable."""
server: HTTPServer | None
class HTTPServer(BaseHTTPServer):
def server_bind(self) -> None:
self.socket.close()
@@ -242,6 +234,9 @@ class MetricsMiddleware(Middleware):
return [worker_metrics]
def before_worker_boot(self, broker: Broker, worker: Any) -> None:
if Conf().test:
return
from prometheus_client import Counter, Gauge, Histogram
self.total_messages = Counter(
@@ -358,8 +353,6 @@ class MetricsMiddleware(Middleware):
def run(cls, addr: str, port: int) -> None:
try:
server = HTTPServer((addr, port), cls.handler_class)
thread = cast(HTTPServerThread, current_thread())
thread.server = server
server.serve_forever()
except OSError:
get_logger(__name__, type(MetricsMiddleware)).warning(

View File

@@ -1,12 +1,12 @@
{
"name": "@goauthentik/docusaurus-config",
"version": "2.5.1",
"version": "2.5.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@goauthentik/docusaurus-config",
"version": "2.5.1",
"version": "2.5.0",
"license": "MIT",
"dependencies": {
"deepmerge-ts": "^7.1.5",
@@ -34,7 +34,7 @@
},
"engines": {
"node": ">=24",
"npm": ">=11.10.1"
"npm": ">=11.10.0"
},
"optionalDependencies": {
"react": ">=18",
@@ -101,7 +101,7 @@
},
"../prettier-config": {
"name": "@goauthentik/prettier-config",
"version": "3.4.3",
"version": "3.4.1",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -131,12 +131,12 @@
},
"../tsconfig": {
"name": "@goauthentik/tsconfig",
"version": "1.0.7",
"version": "1.0.5",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=24",
"npm": ">=11.10.1"
"npm": ">=11.6.2"
}
},
"node_modules/@algolia/abtesting": {

View File

@@ -1,6 +1,6 @@
{
"name": "@goauthentik/docusaurus-config",
"version": "2.5.1",
"version": "2.5.0",
"description": "authentik's Docusaurus config",
"license": "MIT",
"repository": {
@@ -68,7 +68,7 @@
],
"engines": {
"node": ">=24",
"npm": ">=11.10.1"
"npm": ">=11.10.0"
},
"devEngines": {
"runtime": {
@@ -78,7 +78,7 @@
},
"packageManager": {
"name": "npm",
"version": ">=11.10.1",
"version": "11.10.1",
"onFail": "warn"
}
},

View File

@@ -1,12 +1,12 @@
{
"name": "@goauthentik/esbuild-plugin-live-reload",
"version": "1.6.1",
"version": "1.6.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@goauthentik/esbuild-plugin-live-reload",
"version": "1.6.1",
"version": "1.6.0",
"license": "MIT",
"dependencies": {
"find-free-ports": "^3.1.1"
@@ -31,7 +31,7 @@
},
"engines": {
"node": ">=24",
"npm": ">=11.10.1"
"npm": ">=11.6.2"
},
"peerDependencies": {
"esbuild": "^0.27.3"
@@ -79,7 +79,7 @@
},
"../prettier-config": {
"name": "@goauthentik/prettier-config",
"version": "3.4.3",
"version": "3.4.1",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -109,12 +109,12 @@
},
"../tsconfig": {
"name": "@goauthentik/tsconfig",
"version": "1.0.7",
"version": "1.0.5",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=24",
"npm": ">=11.10.1"
"npm": ">=11.6.2"
}
},
"node_modules/@esbuild/aix-ppc64": {

View File

@@ -1,6 +1,6 @@
{
"name": "@goauthentik/esbuild-plugin-live-reload",
"version": "1.6.1",
"version": "1.6.0",
"description": "ESBuild + browser refresh. Build completes, page reloads.",
"license": "MIT",
"repository": {
@@ -72,7 +72,7 @@
],
"engines": {
"node": ">=24",
"npm": ">=11.10.1"
"npm": ">=11.6.2"
},
"devEngines": {
"runtime": {
@@ -82,7 +82,7 @@
},
"packageManager": {
"name": "npm",
"version": ">=11.10.1",
"version": "11.10.1",
"onFail": "warn"
}
},

View File

@@ -45,7 +45,7 @@
},
"../prettier-config": {
"name": "@goauthentik/prettier-config",
"version": "3.4.3",
"version": "3.4.1",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -75,12 +75,12 @@
},
"../tsconfig": {
"name": "@goauthentik/tsconfig",
"version": "1.0.7",
"version": "1.0.5",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=24",
"npm": ">=11.10.1"
"npm": ">=11.6.2"
}
},
"node_modules/@babel/code-frame": {

View File

@@ -70,13 +70,13 @@
"devEngines": {
"runtime": {
"name": "node",
"version": ">=24",
"onFail": "warn"
"version": "24",
"onFail": "ignore"
},
"packageManager": {
"name": "npm",
"version": ">=11.10.1",
"onFail": "warn"
"version": "11.10.1",
"onFail": "ignore"
}
},
"prettier": "@goauthentik/prettier-config",

View File

@@ -6,6 +6,7 @@
* @import { IConsoleLogger } from "./shared.js"
*/
/// <reference types="./shared.js" />
/// <reference types="../types/node.js" />
import { fixture, prefix } from "./shared.js";

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,12 @@
{
"name": "@goauthentik/prettier-config",
"version": "3.4.2",
"version": "3.4.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@goauthentik/prettier-config",
"version": "3.4.2",
"version": "3.4.1",
"license": "MIT",
"dependencies": {
"format-imports": "^4.0.8"
@@ -75,12 +75,12 @@
},
"../tsconfig": {
"name": "@goauthentik/tsconfig",
"version": "1.0.7",
"version": "1.0.5",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=24",
"npm": ">=11.10.1"
"npm": ">=11.6.2"
}
},
"node_modules/@babel/helper-string-parser": {

View File

@@ -1,6 +1,6 @@
{
"name": "@goauthentik/prettier-config",
"version": "3.4.3",
"version": "3.4.1",
"description": "authentik's Prettier config",
"license": "MIT",
"repository": {
@@ -64,13 +64,13 @@
"devEngines": {
"runtime": {
"name": "node",
"version": ">=24",
"onFail": "warn"
"version": "24",
"onFail": "ignore"
},
"packageManager": {
"name": "npm",
"version": ">=11.10.1",
"onFail": "warn"
"version": "11.10.1",
"onFail": "ignore"
}
},
"prettier": "./index.js",

View File

@@ -1,16 +1,16 @@
{
"name": "@goauthentik/tsconfig",
"version": "1.0.7",
"version": "1.0.5",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@goauthentik/tsconfig",
"version": "1.0.7",
"version": "1.0.5",
"license": "MIT",
"engines": {
"node": ">=24",
"npm": ">=11.10.1"
"npm": ">=11.6.2"
}
}
}

View File

@@ -1,13 +1,8 @@
{
"name": "@goauthentik/tsconfig",
"version": "1.0.7",
"version": "1.0.5",
"description": "authentik's base TypeScript configuration.",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/goauthentik/authentik.git",
"directory": "packages/tsconfig"
},
"scripts": {
"build": ""
},
@@ -15,24 +10,17 @@
"type": "module",
"engines": {
"node": ">=24",
"npm": ">=11.10.1"
},
"devEngines": {
"runtime": {
"name": "node",
"version": ">=24",
"onFail": "warn"
},
"packageManager": {
"name": "npm",
"version": ">=11.10.1",
"onFail": "warn"
}
"npm": ">=11.6.2"
},
"keywords": [
"tsconfig",
"typescript"
],
"repository": {
"type": "git",
"url": "git+https://github.com/goauthentik/authentik.git",
"directory": "packages/tsconfig"
},
"publishConfig": {
"access": "public"
}

View File

@@ -7,7 +7,7 @@ requires-python = "==3.14.*"
dependencies = [
"ak-guardian==3.2.0",
"argon2-cffi==25.1.0",
"cachetools==7.0.2",
"cachetools==7.0.1",
"channels==4.3.2",
"cryptography==46.0.5",
"dacite==1.9.2",
@@ -32,11 +32,11 @@ dependencies = [
"drf-orjson-renderer==1.8.0",
"drf-spectacular==0.28.0",
"dumb-init==1.2.5.post1",
"duo-client==5.6.1",
"duo-client==5.5.0",
"fido2==2.1.1",
"geoip2==5.2.0",
"geopy==2.4.1",
"google-api-python-client==2.191.0",
"google-api-python-client==2.190.0",
"gssapi==1.11.1",
"gunicorn==25.1.0",
"jsonpatch==1.33",
@@ -57,7 +57,7 @@ dependencies = [
"pyyaml==6.0.3",
"requests-oauthlib==2.0.0",
"scim2-filter-parser==0.7.0",
"sentry-sdk==2.54.0",
"sentry-sdk==2.53.0",
"service-identity==24.2.0",
"setproctitle==1.3.7",
"structlog==25.5.0",
@@ -76,8 +76,8 @@ dependencies = [
[dependency-groups]
dev = [
"aws-cdk-lib==2.241.0",
"bandit==1.9.4",
"aws-cdk-lib==2.240.0",
"bandit==1.9.3",
"black==26.1.0",
"bpython==0.26",
"codespell==2.4.1",
@@ -97,12 +97,12 @@ dev = [
"pdoc==16.0.0",
"pytest-django==4.12.0",
"pytest-flakefinder==1.1.0",
"pytest-github-actions-annotate-failures==0.4.0",
"pytest-github-actions-annotate-failures==0.3.0",
"pytest-randomly==4.0.1",
"pytest-timeout==2.4.0",
"pytest==9.0.2",
"requests-mock==1.12.1",
"ruff==0.15.4",
"ruff==0.15.2",
"selenium==4.41.0",
"types-channels==4.3.0.20250822",
"types-docker==7.1.0.20260109",

View File

@@ -36805,13 +36805,10 @@ components:
type: boolean
policies_buffered_access_view:
type: boolean
flows_continuous_login:
type: boolean
flows_refresh_others:
type: boolean
required:
- enterprise_audit_include_expanded_diff
- flows_continuous_login
- flows_refresh_others
- policies_buffered_access_view
readOnly: true
@@ -40483,8 +40480,6 @@ components:
default: false
application_pre:
type: string
application_pre_launch:
type: string
flow_designation:
$ref: '#/components/schemas/FlowDesignationEnum'
captcha_stage:
@@ -50466,13 +50461,10 @@ components:
type: boolean
policies_buffered_access_view:
type: boolean
flows_continuous_login:
type: boolean
flows_refresh_others:
type: boolean
required:
- enterprise_audit_include_expanded_diff
- flows_continuous_login
- flows_refresh_others
- policies_buffered_access_view
PatchedSourceStageRequest:
@@ -55168,13 +55160,10 @@ components:
type: boolean
policies_buffered_access_view:
type: boolean
flows_continuous_login:
type: boolean
flows_refresh_others:
type: boolean
required:
- enterprise_audit_include_expanded_diff
- flows_continuous_login
- flows_refresh_others
- policies_buffered_access_view
required:
@@ -55249,13 +55238,10 @@ components:
type: boolean
policies_buffered_access_view:
type: boolean
flows_continuous_login:
type: boolean
flows_refresh_others:
type: boolean
required:
- enterprise_audit_include_expanded_diff
- flows_continuous_login
- flows_refresh_others
- policies_buffered_access_view
required:

View File

@@ -1,6 +1,6 @@
services:
chromium:
image: ghcr.io/goauthentik/selenium:145.0-ak-0.40.2
image: ghcr.io/goauthentik/selenium:145.0-ak-0.40.0
shm_size: 2g
network_mode: host
restart: always

114
uv.lock generated
View File

@@ -317,7 +317,7 @@ dev = [
requires-dist = [
{ name = "ak-guardian", editable = "packages/ak-guardian" },
{ name = "argon2-cffi", specifier = "==25.1.0" },
{ name = "cachetools", specifier = "==7.0.2" },
{ name = "cachetools", specifier = "==7.0.1" },
{ name = "channels", specifier = "==4.3.2" },
{ name = "cryptography", specifier = "==46.0.5" },
{ name = "dacite", specifier = "==1.9.2" },
@@ -342,11 +342,11 @@ requires-dist = [
{ name = "drf-orjson-renderer", specifier = "==1.8.0" },
{ name = "drf-spectacular", specifier = "==0.28.0" },
{ name = "dumb-init", specifier = "==1.2.5.post1" },
{ name = "duo-client", specifier = "==5.6.1" },
{ name = "duo-client", specifier = "==5.5.0" },
{ name = "fido2", specifier = "==2.1.1" },
{ name = "geoip2", specifier = "==5.2.0" },
{ name = "geopy", specifier = "==2.4.1" },
{ name = "google-api-python-client", specifier = "==2.191.0" },
{ name = "google-api-python-client", specifier = "==2.190.0" },
{ name = "gssapi", specifier = "==1.11.1" },
{ name = "gunicorn", specifier = "==25.1.0" },
{ name = "jsonpatch", specifier = "==1.33" },
@@ -367,7 +367,7 @@ requires-dist = [
{ name = "pyyaml", specifier = "==6.0.3" },
{ name = "requests-oauthlib", specifier = "==2.0.0" },
{ name = "scim2-filter-parser", specifier = "==0.7.0" },
{ name = "sentry-sdk", specifier = "==2.54.0" },
{ name = "sentry-sdk", specifier = "==2.53.0" },
{ name = "service-identity", specifier = "==24.2.0" },
{ name = "setproctitle", specifier = "==1.3.7" },
{ name = "structlog", specifier = "==25.5.0" },
@@ -386,8 +386,8 @@ requires-dist = [
[package.metadata.requires-dev]
dev = [
{ name = "aws-cdk-lib", specifier = "==2.241.0" },
{ name = "bandit", specifier = "==1.9.4" },
{ name = "aws-cdk-lib", specifier = "==2.240.0" },
{ name = "bandit", specifier = "==1.9.3" },
{ name = "black", specifier = "==26.1.0" },
{ name = "bpython", specifier = "==0.26" },
{ name = "codespell", specifier = "==2.4.1" },
@@ -408,11 +408,11 @@ dev = [
{ name = "pytest", specifier = "==9.0.2" },
{ name = "pytest-django", specifier = "==4.12.0" },
{ name = "pytest-flakefinder", specifier = "==1.1.0" },
{ name = "pytest-github-actions-annotate-failures", specifier = "==0.4.0" },
{ name = "pytest-github-actions-annotate-failures", specifier = "==0.3.0" },
{ name = "pytest-randomly", specifier = "==4.0.1" },
{ name = "pytest-timeout", specifier = "==2.4.0" },
{ name = "requests-mock", specifier = "==1.12.1" },
{ name = "ruff", specifier = "==0.15.4" },
{ name = "ruff", specifier = "==0.15.2" },
{ name = "selenium", specifier = "==4.41.0" },
{ name = "types-channels", specifier = "==4.3.0.20250822" },
{ name = "types-docker", specifier = "==7.1.0.20260109" },
@@ -469,35 +469,35 @@ wheels = [
[[package]]
name = "aws-cdk-asset-node-proxy-agent-v6"
version = "2.1.1"
version = "2.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "jsii" },
{ name = "publication" },
{ name = "typeguard" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d3/4b/0ccc9d12f66becaaf489e55d152f50376817cd808cf4068c0c5385d21c9d/aws_cdk_asset_node_proxy_agent_v6-2.1.1.tar.gz", hash = "sha256:37211fea7e308144d20666b939d8763d733000d8d837ba968dacaf4983daf574", size = 1542241, upload-time = "2026-02-26T16:38:43.95Z" }
sdist = { url = "https://files.pythonhosted.org/packages/d5/ab/09ac3ecc0067988d02398328e088d66cbe8555c991563c8ddfa1db5296ae/aws_cdk_asset_node_proxy_agent_v6-2.1.0.tar.gz", hash = "sha256:1f292c0631f86708ba4ee328b3a2b229f7e46ea1c79fbde567ee9eb119c2b0e2", size = 1540231, upload-time = "2024-09-03T09:36:51.634Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/dc/b1/775c53a017d30d6bb0c9fa19182df11672569b6c8c7c1a1b325639abff63/aws_cdk_asset_node_proxy_agent_v6-2.1.1-py3-none-any.whl", hash = "sha256:a24fab484423fcb7c729fb376817a4dbb6a09c176243fd1dcbdaa05ddf62f037", size = 1540751, upload-time = "2026-02-26T16:38:42.464Z" },
{ url = "https://files.pythonhosted.org/packages/8d/86/1817a6da223aa80aeb94a504f07f930170284694b18f6053729e9930cc6a/aws_cdk.asset_node_proxy_agent_v6-2.1.0-py3-none-any.whl", hash = "sha256:24a388b69a44d03bae6dbf864c4e25ba650d4b61c008b4568b94ffbb9a69e40e", size = 1538724, upload-time = "2024-09-03T09:36:49.8Z" },
]
[[package]]
name = "aws-cdk-cloud-assembly-schema"
version = "52.2.0"
version = "50.4.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "jsii" },
{ name = "publication" },
{ name = "typeguard" },
]
sdist = { url = "https://files.pythonhosted.org/packages/81/b3/baf6ba1d34bc4fc8affb7849613978df80b6e97b5d48a885dbc84adf1a97/aws_cdk_cloud_assembly_schema-52.2.0.tar.gz", hash = "sha256:737309e2c7c7e4b46bd669cb9fe8799a36424c9a174523b54833cf1cd12b5e3f", size = 210466, upload-time = "2026-02-23T17:08:48.404Z" }
sdist = { url = "https://files.pythonhosted.org/packages/f8/4c/a9ac7498f2b76d7697e60367b9a8c690fc54bb3bf4e591e6fe06977c847b/aws_cdk_cloud_assembly_schema-50.4.0.tar.gz", hash = "sha256:c9aa7a108ca63f3880f26594166d3e8c16b504a50424011baf785231dc009f30", size = 208573, upload-time = "2026-02-05T16:37:54.61Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0e/b3/2305e64194634b3c1f0bff38462dd59e617dc1460ac8df1616a5b193d2fe/aws_cdk_cloud_assembly_schema-52.2.0-py3-none-any.whl", hash = "sha256:87b918589f7d627f45e330726f592ec0b39056e1403558f3a1ba8a2134dacd6f", size = 210184, upload-time = "2026-02-23T17:08:46.923Z" },
{ url = "https://files.pythonhosted.org/packages/50/37/946c9646606cf9dffa5d15a860557c9d507655c97fb2525bb8bd0c215179/aws_cdk_cloud_assembly_schema-50.4.0-py3-none-any.whl", hash = "sha256:3f98f06d99f68f5bae5c72f0f392494dd3ef4211197afd0e75cfe1d5fc487d1c", size = 208231, upload-time = "2026-02-05T16:37:52.037Z" },
]
[[package]]
name = "aws-cdk-lib"
version = "2.241.0"
version = "2.240.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "aws-cdk-asset-awscli-v1" },
@@ -508,9 +508,9 @@ dependencies = [
{ name = "publication" },
{ name = "typeguard" },
]
sdist = { url = "https://files.pythonhosted.org/packages/55/8a/58fa1cc687847b5f834af45c2466a34cd68fc22489732a6f899560769c4a/aws_cdk_lib-2.241.0.tar.gz", hash = "sha256:4ea84d6caf43c3a42670a76e8a023e513ce335499e7c45f22028b2186926e7ab", size = 48209539, upload-time = "2026-03-02T14:39:12.156Z" }
sdist = { url = "https://files.pythonhosted.org/packages/97/c3/56cb53689bdaa5e4359cc76c5b3356be21d974486324a0091a32c59c3682/aws_cdk_lib-2.240.0.tar.gz", hash = "sha256:096fcb3efd41ba30cdcee0c97a54537f6cb3ab9e53a1f0ed68981a8b86860784", size = 47956951, upload-time = "2026-02-23T21:31:34.799Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cb/20/9a61e0558ff9483d0146e4f563e09d59c936761d1ac77ff25ec6a0e67d02/aws_cdk_lib-2.241.0-py3-none-any.whl", hash = "sha256:5c62f97c13a2a9e65b1d2b376f267595b7b2cc3947f6bcf710e52e39a381b5e3", size = 48855242, upload-time = "2026-03-02T14:38:30.307Z" },
{ url = "https://files.pythonhosted.org/packages/2e/7c/0fdc9ffd4a516a62f0313ecb0abb0f2b1e58f3e5c409254ecefea571749f/aws_cdk_lib-2.240.0-py3-none-any.whl", hash = "sha256:6ba3343885e0ad10cb38981ea4f7e6c721253519d2601af9d6784bb5787d1292", size = 48608831, upload-time = "2026-02-23T21:30:54.727Z" },
]
[[package]]
@@ -544,7 +544,7 @@ wheels = [
[[package]]
name = "bandit"
version = "1.9.4"
version = "1.9.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
@@ -552,9 +552,9 @@ dependencies = [
{ name = "rich" },
{ name = "stevedore" },
]
sdist = { url = "https://files.pythonhosted.org/packages/aa/c3/0cb80dfe0f3076e5da7e4c5ad8e57bac6ac357ff4a6406205501cade4965/bandit-1.9.4.tar.gz", hash = "sha256:b589e5de2afe70bd4d53fa0c1da6199f4085af666fde00e8a034f152a52cd628", size = 4242677, upload-time = "2026-02-25T06:44:15.503Z" }
sdist = { url = "https://files.pythonhosted.org/packages/89/76/a7f3e639b78601118aaa4a394db2c66ae2597fbd8c39644c32874ed11e0c/bandit-1.9.3.tar.gz", hash = "sha256:ade4b9b7786f89ef6fc7344a52b34558caec5da74cb90373aed01de88472f774", size = 4242154, upload-time = "2026-01-19T04:05:22.802Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/05/a4/a26d5b25671d27e03afb5401a0be5899d94ff8fab6a698b1ac5be3ec29ef/bandit-1.9.4-py3-none-any.whl", hash = "sha256:f89ffa663767f5a0585ea075f01020207e966a9c0f2b9ef56a57c7963a3f6f8e", size = 134741, upload-time = "2026-02-25T06:44:13.694Z" },
{ url = "https://files.pythonhosted.org/packages/e0/0b/8bdc52111c83e2dc2f97403dc87c0830b8989d9ae45732b34b686326fb2c/bandit-1.9.3-py3-none-any.whl", hash = "sha256:4745917c88d2246def79748bde5e08b9d5e9b92f877863d43fab70cd8814ce6a", size = 134451, upload-time = "2026-01-19T04:05:20.938Z" },
]
[[package]]
@@ -690,11 +690,11 @@ wheels = [
[[package]]
name = "cachetools"
version = "7.0.2"
version = "7.0.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/6c/c7/342b33cc6877eebc6c9bb45cb9f78e170e575839699f6f3cc96050176431/cachetools-7.0.2.tar.gz", hash = "sha256:7e7f09a4ca8b791d8bb4864afc71e9c17e607a28e6839ca1a644253c97dbeae0", size = 36983, upload-time = "2026-03-02T19:45:16.926Z" }
sdist = { url = "https://files.pythonhosted.org/packages/d4/07/56595285564e90777d758ebd383d6b0b971b87729bbe2184a849932a3736/cachetools-7.0.1.tar.gz", hash = "sha256:e31e579d2c5b6e2944177a0397150d312888ddf4e16e12f1016068f0c03b8341", size = 36126, upload-time = "2026-02-10T22:24:05.03Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ef/04/4b6968e77c110f12da96fdbfcb39c6557c2e5e81bd7afcf8ed893d5bc588/cachetools-7.0.2-py3-none-any.whl", hash = "sha256:938dcad184827c5e94928c4fd5526e2b46692b7fb1ae94472da9131d0299343c", size = 13793, upload-time = "2026-03-02T19:45:15.495Z" },
{ url = "https://files.pythonhosted.org/packages/ed/9e/5faefbf9db1db466d633735faceda1f94aa99ce506ac450d232536266b32/cachetools-7.0.1-py3-none-any.whl", hash = "sha256:8f086515c254d5664ae2146d14fc7f65c9a4bce75152eb247e5a9c5e6d7b2ecf", size = 13484, upload-time = "2026-02-10T22:24:03.741Z" },
]
[[package]]
@@ -1451,14 +1451,14 @@ wheels = [
[[package]]
name = "duo-client"
version = "5.6.1"
version = "5.5.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "setuptools" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c8/20/48842c3cc8b2c60129e1e52f0ab5c72ac0a30a3a345ea6d6e9aa8c94c093/duo_client-5.6.1.tar.gz", hash = "sha256:5b9bfba3bd995178cebf51db605190bdc84fec8e4b6935210e7abde4ae97cef5", size = 95447, upload-time = "2026-02-26T16:38:53.369Z" }
sdist = { url = "https://files.pythonhosted.org/packages/8b/c8/4e8416eec72bc77ad947b736f760ceedf818df0c48153816b1bfb0fab854/duo_client-5.5.0.tar.gz", hash = "sha256:303109e047fe7525ba4fc4a294c1f3deb4125066e89c10d33f7430378867b1d6", size = 93475, upload-time = "2025-03-24T15:35:19.175Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9d/db/798547ea1a4af7be4454d621f8c23dbbad153dc7d9c57285fbcde03abd42/duo_client-5.6.1-py3-none-any.whl", hash = "sha256:ec7fa30548b7b8e3b7983948d08d167ada4df7a1b54b4723e7a02ee4635bb513", size = 52001, upload-time = "2026-02-26T16:38:51.85Z" },
{ url = "https://files.pythonhosted.org/packages/7a/4b/dd927d3111e1f4c78c1f5944d3b64b4fb6ab655487c23100a3feff5cdaa4/duo_client-5.5.0-py3-none-any.whl", hash = "sha256:4fbf1e97a2b25ef64e9f88171ab817162cf45bafc1c63026af4883baf8892a12", size = 51313, upload-time = "2025-03-24T15:35:18.243Z" },
]
[[package]]
@@ -1623,7 +1623,7 @@ wheels = [
[[package]]
name = "google-api-python-client"
version = "2.191.0"
version = "2.190.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "google-api-core" },
@@ -1632,9 +1632,9 @@ dependencies = [
{ name = "httplib2" },
{ name = "uritemplate" },
]
sdist = { url = "https://files.pythonhosted.org/packages/60/58/7d0843b7419a5ac51a27965e7233ef7c26ad693b490a74ab26548d0fd231/google_api_python_client-2.191.0.tar.gz", hash = "sha256:858c22fd46f51a65cee365a78aec2054e6d47b50434bee4ba62e91ac0944aea1", size = 14195310, upload-time = "2026-03-02T16:58:59.965Z" }
sdist = { url = "https://files.pythonhosted.org/packages/e4/8d/4ab3e3516b93bb50ed7814738ea61d49cba3f72f4e331dc9518ae2731e92/google_api_python_client-2.190.0.tar.gz", hash = "sha256:5357f34552e3724d80d2604c8fa146766e0a9d6bb0afada886fafed9feafeef6", size = 14111143, upload-time = "2026-02-12T00:38:03.37Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/6e/a4/da3a66c90f94b9eb433f0844f1bf79b5789bfa0859e55c0cd02c1139fed8/google_api_python_client-2.191.0-py3-none-any.whl", hash = "sha256:0768dde3202121abb3e897c4ee5150e58a25f32ee843780f6bee636dccc7ef23", size = 14769138, upload-time = "2026-03-02T16:58:57.665Z" },
{ url = "https://files.pythonhosted.org/packages/07/ad/223d5f4b0b987669ffeb3eadd7e9f85ece633aa7fd3246f1e2f6238e1e05/google_api_python_client-2.190.0-py3-none-any.whl", hash = "sha256:d9b5266758f96c39b8c21d9bbfeb4e58c14dbfba3c931f7c5a8d7fdcd292dd57", size = 14682070, upload-time = "2026-02-12T00:38:00.974Z" },
]
[[package]]
@@ -1945,7 +1945,7 @@ wheels = [
[[package]]
name = "jsii"
version = "1.127.0"
version = "1.126.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "attrs" },
@@ -1956,9 +1956,9 @@ dependencies = [
{ name = "typeguard" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/81/e8/a163295e70ecc652bfd7960c83f9a61ee4283f2ef73d6df23ac57b430f6c/jsii-1.127.0.tar.gz", hash = "sha256:631a13d73265eaa22c0c0804e77fe59c8fcc3af3a94de9923af65380b6ad267a", size = 461782, upload-time = "2026-02-25T16:54:04.368Z" }
sdist = { url = "https://files.pythonhosted.org/packages/76/22/a3c8a8bfc6bd25b761553380d91d9b5a772a3796e78b7f6d4eecc003bced/jsii-1.126.0.tar.gz", hash = "sha256:5e4739843aab3af25472490a05a271cf7d53f01a6d46167ab0f1f2cff3a8df95", size = 626608, upload-time = "2026-01-26T10:43:16.152Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b6/91/3b58a04d06cb6abbb3806ca5fcf4b8933a85e013d24d983d3535d9fb6566/jsii-1.127.0-py3-none-any.whl", hash = "sha256:92a11f39e461f5168e2467efd53351cc32b118314b95cc6323c2d044eb299eaf", size = 436980, upload-time = "2026-02-25T16:54:02.824Z" },
{ url = "https://files.pythonhosted.org/packages/a2/7d/f058cfdc20e1536b09d165303b1996d24504fbe105b0aba62c865e60bdc9/jsii-1.126.0-py3-none-any.whl", hash = "sha256:0bb3d5423fd62a499f9ce83e98668b48424ac6ef39472bff90cdf4650aa41b41", size = 602728, upload-time = "2026-01-26T10:43:14.68Z" },
]
[[package]]
@@ -3034,14 +3034,14 @@ wheels = [
[[package]]
name = "pytest-github-actions-annotate-failures"
version = "0.4.0"
version = "0.3.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pytest" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c2/e1/8f2c242e6d75a26a8e5ddcc23f652a411e4aac3eedc4b923808ac0582685/pytest_github_actions_annotate_failures-0.4.0.tar.gz", hash = "sha256:77d6baa29c8c61c2dacc494fa76eb95a185f0ee61666714ac43fb12ea672d217", size = 10857, upload-time = "2026-03-02T18:57:40.919Z" }
sdist = { url = "https://files.pythonhosted.org/packages/39/d4/c54ee6a871eee4a7468e3a8c0dead28e634c0bc2110c694309dcb7563a66/pytest_github_actions_annotate_failures-0.3.0.tar.gz", hash = "sha256:d4c3177c98046c3900a7f8ddebb22ea54b9f6822201b5d3ab8fcdea51e010db7", size = 11248, upload-time = "2025-01-17T22:39:32.722Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0f/bd/11809f5c78d5d8da2d0e004845d2382768bc20798b3e0988bca61efd349f/pytest_github_actions_annotate_failures-0.4.0-py3-none-any.whl", hash = "sha256:285fed86e16b0b7a8eac6acdcde31913798fb739b15ef5b86895b4f5e32bf237", size = 6039, upload-time = "2026-03-02T18:57:39.991Z" },
{ url = "https://files.pythonhosted.org/packages/6d/73/7b0b15cb8605ee967b34aa1d949737ab664f94e6b0f1534e8339d9e64ab2/pytest_github_actions_annotate_failures-0.3.0-py3-none-any.whl", hash = "sha256:41ea558ba10c332c0bfc053daeee0c85187507b2034e990f21e4f7e5fef044cf", size = 6030, upload-time = "2025-01-17T22:39:31.701Z" },
]
[[package]]
@@ -3276,27 +3276,27 @@ wheels = [
[[package]]
name = "ruff"
version = "0.15.4"
version = "0.15.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/da/31/d6e536cdebb6568ae75a7f00e4b4819ae0ad2640c3604c305a0428680b0c/ruff-0.15.4.tar.gz", hash = "sha256:3412195319e42d634470cc97aa9803d07e9d5c9223b99bcb1518f0c725f26ae1", size = 4569550, upload-time = "2026-02-26T20:04:14.959Z" }
sdist = { url = "https://files.pythonhosted.org/packages/06/04/eab13a954e763b0606f460443fcbf6bb5a0faf06890ea3754ff16523dce5/ruff-0.15.2.tar.gz", hash = "sha256:14b965afee0969e68bb871eba625343b8673375f457af4abe98553e8bbb98342", size = 4558148, upload-time = "2026-02-19T22:32:20.271Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f2/82/c11a03cfec3a4d26a0ea1e571f0f44be5993b923f905eeddfc397c13d360/ruff-0.15.4-py3-none-linux_armv6l.whl", hash = "sha256:a1810931c41606c686bae8b5b9a8072adac2f611bb433c0ba476acba17a332e0", size = 10453333, upload-time = "2026-02-26T20:04:20.093Z" },
{ url = "https://files.pythonhosted.org/packages/ce/5d/6a1f271f6e31dffb31855996493641edc3eef8077b883eaf007a2f1c2976/ruff-0.15.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5a1632c66672b8b4d3e1d1782859e98d6e0b4e70829530666644286600a33992", size = 10853356, upload-time = "2026-02-26T20:04:05.808Z" },
{ url = "https://files.pythonhosted.org/packages/b1/d8/0fab9f8842b83b1a9c2bf81b85063f65e93fb512e60effa95b0be49bfc54/ruff-0.15.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a4386ba2cd6c0f4ff75252845906acc7c7c8e1ac567b7bc3d373686ac8c222ba", size = 10187434, upload-time = "2026-02-26T20:03:54.656Z" },
{ url = "https://files.pythonhosted.org/packages/85/cc/cc220fd9394eff5db8d94dec199eec56dd6c9f3651d8869d024867a91030/ruff-0.15.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2496488bdfd3732747558b6f95ae427ff066d1fcd054daf75f5a50674411e75", size = 10535456, upload-time = "2026-02-26T20:03:52.738Z" },
{ url = "https://files.pythonhosted.org/packages/fa/0f/bced38fa5cf24373ec767713c8e4cadc90247f3863605fb030e597878661/ruff-0.15.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3f1c4893841ff2d54cbda1b2860fa3260173df5ddd7b95d370186f8a5e66a4ac", size = 10287772, upload-time = "2026-02-26T20:04:08.138Z" },
{ url = "https://files.pythonhosted.org/packages/2b/90/58a1802d84fed15f8f281925b21ab3cecd813bde52a8ca033a4de8ab0e7a/ruff-0.15.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:820b8766bd65503b6c30aaa6331e8ef3a6e564f7999c844e9a547c40179e440a", size = 11049051, upload-time = "2026-02-26T20:04:03.53Z" },
{ url = "https://files.pythonhosted.org/packages/d2/ac/b7ad36703c35f3866584564dc15f12f91cb1a26a897dc2fd13d7cb3ae1af/ruff-0.15.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9fb74bab47139c1751f900f857fa503987253c3ef89129b24ed375e72873e85", size = 11890494, upload-time = "2026-02-26T20:04:10.497Z" },
{ url = "https://files.pythonhosted.org/packages/93/3d/3eb2f47a39a8b0da99faf9c54d3eb24720add1e886a5309d4d1be73a6380/ruff-0.15.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f80c98765949c518142b3a50a5db89343aa90f2c2bf7799de9986498ae6176db", size = 11326221, upload-time = "2026-02-26T20:04:12.84Z" },
{ url = "https://files.pythonhosted.org/packages/ff/90/bf134f4c1e5243e62690e09d63c55df948a74084c8ac3e48a88468314da6/ruff-0.15.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:451a2e224151729b3b6c9ffb36aed9091b2996fe4bdbd11f47e27d8f2e8888ec", size = 11168459, upload-time = "2026-02-26T20:04:00.969Z" },
{ url = "https://files.pythonhosted.org/packages/b5/e5/a64d27688789b06b5d55162aafc32059bb8c989c61a5139a36e1368285eb/ruff-0.15.4-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a8f157f2e583c513c4f5f896163a93198297371f34c04220daf40d133fdd4f7f", size = 11104366, upload-time = "2026-02-26T20:03:48.099Z" },
{ url = "https://files.pythonhosted.org/packages/f1/f6/32d1dcb66a2559763fc3027bdd65836cad9eb09d90f2ed6a63d8e9252b02/ruff-0.15.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:917cc68503357021f541e69b35361c99387cdbbf99bd0ea4aa6f28ca99ff5338", size = 10510887, upload-time = "2026-02-26T20:03:45.771Z" },
{ url = "https://files.pythonhosted.org/packages/ff/92/22d1ced50971c5b6433aed166fcef8c9343f567a94cf2b9d9089f6aa80fe/ruff-0.15.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e9737c8161da79fd7cfec19f1e35620375bd8b2a50c3e77fa3d2c16f574105cc", size = 10285939, upload-time = "2026-02-26T20:04:22.42Z" },
{ url = "https://files.pythonhosted.org/packages/e6/f4/7c20aec3143837641a02509a4668fb146a642fd1211846634edc17eb5563/ruff-0.15.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:291258c917539e18f6ba40482fe31d6f5ac023994ee11d7bdafd716f2aab8a68", size = 10765471, upload-time = "2026-02-26T20:03:58.924Z" },
{ url = "https://files.pythonhosted.org/packages/d0/09/6d2f7586f09a16120aebdff8f64d962d7c4348313c77ebb29c566cefc357/ruff-0.15.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:3f83c45911da6f2cd5936c436cf86b9f09f09165f033a99dcf7477e34041cbc3", size = 11263382, upload-time = "2026-02-26T20:04:24.424Z" },
{ url = "https://files.pythonhosted.org/packages/1b/fa/2ef715a1cd329ef47c1a050e10dee91a9054b7ce2fcfdd6a06d139afb7ec/ruff-0.15.4-py3-none-win32.whl", hash = "sha256:65594a2d557d4ee9f02834fcdf0a28daa8b3b9f6cb2cb93846025a36db47ef22", size = 10506664, upload-time = "2026-02-26T20:03:50.56Z" },
{ url = "https://files.pythonhosted.org/packages/d0/a8/c688ef7e29983976820d18710f955751d9f4d4eb69df658af3d006e2ba3e/ruff-0.15.4-py3-none-win_amd64.whl", hash = "sha256:04196ad44f0df220c2ece5b0e959c2f37c777375ec744397d21d15b50a75264f", size = 11651048, upload-time = "2026-02-26T20:04:17.191Z" },
{ url = "https://files.pythonhosted.org/packages/3e/0a/9e1be9035b37448ce2e68c978f0591da94389ade5a5abafa4cf99985d1b2/ruff-0.15.4-py3-none-win_arm64.whl", hash = "sha256:60d5177e8cfc70e51b9c5fad936c634872a74209f934c1e79107d11787ad5453", size = 10966776, upload-time = "2026-02-26T20:03:56.908Z" },
{ url = "https://files.pythonhosted.org/packages/2f/70/3a4dc6d09b13cb3e695f28307e5d889b2e1a66b7af9c5e257e796695b0e6/ruff-0.15.2-py3-none-linux_armv6l.whl", hash = "sha256:120691a6fdae2f16d65435648160f5b81a9625288f75544dc40637436b5d3c0d", size = 10430565, upload-time = "2026-02-19T22:32:41.824Z" },
{ url = "https://files.pythonhosted.org/packages/71/0b/bb8457b56185ece1305c666dc895832946d24055be90692381c31d57466d/ruff-0.15.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:a89056d831256099658b6bba4037ac6dd06f49d194199215befe2bb10457ea5e", size = 10820354, upload-time = "2026-02-19T22:32:07.366Z" },
{ url = "https://files.pythonhosted.org/packages/2d/c1/e0532d7f9c9e0b14c46f61b14afd563298b8b83f337b6789ddd987e46121/ruff-0.15.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e36dee3a64be0ebd23c86ffa3aa3fd3ac9a712ff295e192243f814a830b6bd87", size = 10170767, upload-time = "2026-02-19T22:32:13.188Z" },
{ url = "https://files.pythonhosted.org/packages/47/e8/da1aa341d3af017a21c7a62fb5ec31d4e7ad0a93ab80e3a508316efbcb23/ruff-0.15.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9fb47b6d9764677f8c0a193c0943ce9a05d6763523f132325af8a858eadc2b9", size = 10529591, upload-time = "2026-02-19T22:32:02.547Z" },
{ url = "https://files.pythonhosted.org/packages/93/74/184fbf38e9f3510231fbc5e437e808f0b48c42d1df9434b208821efcd8d6/ruff-0.15.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f376990f9d0d6442ea9014b19621d8f2aaf2b8e39fdbfc79220b7f0c596c9b80", size = 10260771, upload-time = "2026-02-19T22:32:36.938Z" },
{ url = "https://files.pythonhosted.org/packages/05/ac/605c20b8e059a0bc4b42360414baa4892ff278cec1c91fff4be0dceedefd/ruff-0.15.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2dcc987551952d73cbf5c88d9fdee815618d497e4df86cd4c4824cc59d5dd75f", size = 11045791, upload-time = "2026-02-19T22:32:31.642Z" },
{ url = "https://files.pythonhosted.org/packages/fd/52/db6e419908f45a894924d410ac77d64bdd98ff86901d833364251bd08e22/ruff-0.15.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:42a47fd785cbe8c01b9ff45031af875d101b040ad8f4de7bbb716487c74c9a77", size = 11879271, upload-time = "2026-02-19T22:32:29.305Z" },
{ url = "https://files.pythonhosted.org/packages/3e/d8/7992b18f2008bdc9231d0f10b16df7dda964dbf639e2b8b4c1b4e91b83af/ruff-0.15.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cbe9f49354866e575b4c6943856989f966421870e85cd2ac94dccb0a9dcb2fea", size = 11303707, upload-time = "2026-02-19T22:32:22.492Z" },
{ url = "https://files.pythonhosted.org/packages/d7/02/849b46184bcfdd4b64cde61752cc9a146c54759ed036edd11857e9b8443b/ruff-0.15.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7a672c82b5f9887576087d97be5ce439f04bbaf548ee987b92d3a7dede41d3a", size = 11149151, upload-time = "2026-02-19T22:32:44.234Z" },
{ url = "https://files.pythonhosted.org/packages/70/04/f5284e388bab60d1d3b99614a5a9aeb03e0f333847e2429bebd2aaa1feec/ruff-0.15.2-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:72ecc64f46f7019e2bcc3cdc05d4a7da958b629a5ab7033195e11a438403d956", size = 11091132, upload-time = "2026-02-19T22:32:24.691Z" },
{ url = "https://files.pythonhosted.org/packages/fa/ae/88d844a21110e14d92cf73d57363fab59b727ebeabe78009b9ccb23500af/ruff-0.15.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:8dcf243b15b561c655c1ef2f2b0050e5d50db37fe90115507f6ff37d865dc8b4", size = 10504717, upload-time = "2026-02-19T22:32:26.75Z" },
{ url = "https://files.pythonhosted.org/packages/64/27/867076a6ada7f2b9c8292884ab44d08fd2ba71bd2b5364d4136f3cd537e1/ruff-0.15.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:dab6941c862c05739774677c6273166d2510d254dac0695c0e3f5efa1b5585de", size = 10263122, upload-time = "2026-02-19T22:32:10.036Z" },
{ url = "https://files.pythonhosted.org/packages/e7/ef/faf9321d550f8ebf0c6373696e70d1758e20ccdc3951ad7af00c0956be7c/ruff-0.15.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1b9164f57fc36058e9a6806eb92af185b0697c9fe4c7c52caa431c6554521e5c", size = 10735295, upload-time = "2026-02-19T22:32:39.227Z" },
{ url = "https://files.pythonhosted.org/packages/2f/55/e8089fec62e050ba84d71b70e7834b97709ca9b7aba10c1a0b196e493f97/ruff-0.15.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:80d24fcae24d42659db7e335b9e1531697a7102c19185b8dc4a028b952865fd8", size = 11241641, upload-time = "2026-02-19T22:32:34.617Z" },
{ url = "https://files.pythonhosted.org/packages/23/01/1c30526460f4d23222d0fabd5888868262fd0e2b71a00570ca26483cd993/ruff-0.15.2-py3-none-win32.whl", hash = "sha256:fd5ff9e5f519a7e1bd99cbe8daa324010a74f5e2ebc97c6242c08f26f3714f6f", size = 10507885, upload-time = "2026-02-19T22:32:15.635Z" },
{ url = "https://files.pythonhosted.org/packages/5c/10/3d18e3bbdf8fc50bbb4ac3cc45970aa5a9753c5cb51bf9ed9a3cd8b79fa3/ruff-0.15.2-py3-none-win_amd64.whl", hash = "sha256:d20014e3dfa400f3ff84830dfb5755ece2de45ab62ecea4af6b7262d0fb4f7c5", size = 11623725, upload-time = "2026-02-19T22:32:04.947Z" },
{ url = "https://files.pythonhosted.org/packages/6d/78/097c0798b1dab9f8affe73da9642bb4500e098cb27fd8dc9724816ac747b/ruff-0.15.2-py3-none-win_arm64.whl", hash = "sha256:cabddc5822acdc8f7b5527b36ceac55cc51eec7b1946e60181de8fe83ca8876e", size = 10941649, upload-time = "2026-02-19T22:32:18.108Z" },
]
[[package]]
@@ -3342,15 +3342,15 @@ wheels = [
[[package]]
name = "sentry-sdk"
version = "2.54.0"
version = "2.53.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
{ name = "urllib3" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c8/e9/2e3a46c304e7fa21eaa70612f60354e32699c7102eb961f67448e222ad7c/sentry_sdk-2.54.0.tar.gz", hash = "sha256:2620c2575128d009b11b20f7feb81e4e4e8ae08ec1d36cbc845705060b45cc1b", size = 413813, upload-time = "2026-03-02T15:12:41.355Z" }
sdist = { url = "https://files.pythonhosted.org/packages/d3/06/66c8b705179bc54087845f28fd1b72f83751b6e9a195628e2e9af9926505/sentry_sdk-2.53.0.tar.gz", hash = "sha256:6520ef2c4acd823f28efc55e43eb6ce2e6d9f954a95a3aa96b6fd14871e92b77", size = 412369, upload-time = "2026-02-16T11:11:14.743Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/53/39/be412cc86bc6247b8f69e9383d7950711bd86f8d0a4a4b0fe8fad685bc21/sentry_sdk-2.54.0-py2.py3-none-any.whl", hash = "sha256:fd74e0e281dcda63afff095d23ebcd6e97006102cdc8e78a29f19ecdf796a0de", size = 439198, upload-time = "2026-03-02T15:12:39.546Z" },
{ url = "https://files.pythonhosted.org/packages/47/d4/2fdf854bc3b9c7f55219678f812600a20a138af2dd847d99004994eada8f/sentry_sdk-2.53.0-py2.py3-none-any.whl", hash = "sha256:46e1ed8d84355ae54406c924f6b290c3d61f4048625989a723fd622aab838899", size = 437908, upload-time = "2026-02-16T11:11:13.227Z" },
]
[[package]]

318
web/package-lock.json generated
View File

@@ -14,21 +14,21 @@
"dependencies": {
"@codemirror/lang-css": "^6.3.1",
"@codemirror/lang-html": "^6.4.11",
"@codemirror/lang-javascript": "^6.2.5",
"@codemirror/lang-javascript": "^6.2.4",
"@codemirror/lang-python": "^6.2.1",
"@codemirror/lang-xml": "^6.1.0",
"@codemirror/legacy-modes": "^6.5.2",
"@codemirror/theme-one-dark": "^6.1.3",
"@eslint/js": "^9.39.3",
"@floating-ui/dom": "^1.7.6",
"@floating-ui/dom": "^1.7.5",
"@formatjs/intl-listformat": "^8.2.1",
"@fortawesome/fontawesome-free": "^7.2.0",
"@goauthentik/api": "^2026.2.0-rc1-1770744803",
"@goauthentik/core": "^1.0.0",
"@goauthentik/esbuild-plugin-live-reload": "^1.6.1",
"@goauthentik/esbuild-plugin-live-reload": "^1.6.0",
"@goauthentik/eslint-config": "^1.3.0",
"@goauthentik/prettier-config": "^3.4.3",
"@goauthentik/tsconfig": "^1.0.7",
"@goauthentik/prettier-config": "^3.4.1",
"@goauthentik/tsconfig": "^1.0.5",
"@hcaptcha/types": "^1.1.0",
"@lit/context": "^1.1.6",
"@lit/localize": "^0.12.2",
@@ -43,15 +43,15 @@
"@patternfly/elements": "^4.3.1",
"@patternfly/patternfly": "^4.224.2",
"@playwright/test": "^1.58.2",
"@sentry/browser": "^10.42.0",
"@storybook/addon-docs": "^10.2.13",
"@storybook/addon-links": "^10.2.13",
"@storybook/web-components": "^10.2.13",
"@storybook/web-components-vite": "^10.2.13",
"@sentry/browser": "^10.39.0",
"@storybook/addon-docs": "^10.2.10",
"@storybook/addon-links": "^10.2.10",
"@storybook/web-components": "^10.2.10",
"@storybook/web-components-vite": "^10.2.10",
"@types/codemirror": "^5.60.17",
"@types/grecaptcha": "^3.0.9",
"@types/guacamole-common-js": "^1.5.5",
"@types/node": "^25.3.3",
"@types/node": "^25.3.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@typescript-eslint/eslint-plugin": "^8.56.1",
@@ -65,7 +65,7 @@
"chartjs-adapter-date-fns": "^3.0.0",
"codemirror": "^6.0.2",
"core-js": "^3.48.0",
"country-flag-icons": "^1.6.15",
"country-flag-icons": "^1.6.13",
"date-fns": "^4.1.0",
"deepmerge-ts": "^7.1.5",
"dompurify": "^3.3.1",
@@ -74,7 +74,7 @@
"eslint-plugin-lit": "^2.2.1",
"eslint-plugin-wc": "^3.1.0",
"fuse.js": "^7.1.0",
"globals": "^17.4.0",
"globals": "^17.3.0",
"guacamole-common-js": "^1.5.0",
"hastscript": "^9.0.1",
"knip": "^5.85.0",
@@ -534,9 +534,9 @@
}
},
"node_modules/@codemirror/lang-javascript": {
"version": "6.2.5",
"resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.5.tgz",
"integrity": "sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A==",
"version": "6.2.4",
"resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.4.tgz",
"integrity": "sha512-0WVmhp1QOqZ4Rt6GlVGwKJN3KW7Xh4H2q8ZZNGZaP6lRdxXJzmjm4FqvmOojVj6khWJHIb9sp7U/72W7xQgqAA==",
"license": "MIT",
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
@@ -1346,28 +1346,28 @@
}
},
"node_modules/@floating-ui/core": {
"version": "1.7.5",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz",
"integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==",
"version": "1.7.4",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.4.tgz",
"integrity": "sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg==",
"license": "MIT",
"dependencies": {
"@floating-ui/utils": "^0.2.11"
"@floating-ui/utils": "^0.2.10"
}
},
"node_modules/@floating-ui/dom": {
"version": "1.7.6",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz",
"integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==",
"version": "1.7.5",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.5.tgz",
"integrity": "sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg==",
"license": "MIT",
"dependencies": {
"@floating-ui/core": "^1.7.5",
"@floating-ui/utils": "^0.2.11"
"@floating-ui/core": "^1.7.4",
"@floating-ui/utils": "^0.2.10"
}
},
"node_modules/@floating-ui/utils": {
"version": "0.2.11",
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz",
"integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==",
"version": "0.2.10",
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
"integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
"license": "MIT"
},
"node_modules/@formatjs/ecma402-abstract": {
@@ -1431,16 +1431,16 @@
"link": true
},
"node_modules/@goauthentik/esbuild-plugin-live-reload": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/@goauthentik/esbuild-plugin-live-reload/-/esbuild-plugin-live-reload-1.6.1.tgz",
"integrity": "sha512-JTml8rIVMEkepbQFLlvEvb0ms+C0OlOJLLiIvAYkPrXQZ88xa9rKlj9pQj+Y8cf0QRARNZ0YEBTxjtoxZ7lLqQ==",
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@goauthentik/esbuild-plugin-live-reload/-/esbuild-plugin-live-reload-1.6.0.tgz",
"integrity": "sha512-FEMlKXFXHFBULdyAZQt07C3mNy/z1+SVPnRiuPjjgf5vqwT+nQhvEEY5cO9vMmQSOyLSn7wfweDCPcYQ1wXfPQ==",
"license": "MIT",
"dependencies": {
"find-free-ports": "^3.1.1"
},
"engines": {
"node": ">=24",
"npm": ">=11.10.1"
"npm": ">=11.6.2"
},
"peerDependencies": {
"esbuild": "^0.27.3"
@@ -1479,9 +1479,9 @@
}
},
"node_modules/@goauthentik/prettier-config": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/@goauthentik/prettier-config/-/prettier-config-3.4.3.tgz",
"integrity": "sha512-yDfEZQy+vpP6lDKsA84rs4ye+M19TXNvTBZiTEbTj1xANnMjZoV92UtCnTUkW8DywuPkKQNmKZIEjhc7jExnTw==",
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/@goauthentik/prettier-config/-/prettier-config-3.4.1.tgz",
"integrity": "sha512-jc8dHhdP3qjApZjLyRwLXB3Cg0hE7TVnyXaKLCULjfncTNYInLZK8j+UDiqWbEABxthhy/zwCdLKOKS1NuyE5A==",
"license": "MIT",
"dependencies": {
"format-imports": "^4.0.8"
@@ -1496,13 +1496,13 @@
}
},
"node_modules/@goauthentik/tsconfig": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@goauthentik/tsconfig/-/tsconfig-1.0.7.tgz",
"integrity": "sha512-ftuK337VmEqC0/HvpzYsZm/IK9wUFx0uM1oQ9YAzBJ0nYKZ213RcJA9Q1bZfNf3B/jSMOxSSqF+rE2YdQdMV3Q==",
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@goauthentik/tsconfig/-/tsconfig-1.0.5.tgz",
"integrity": "sha512-zedK2VYcsxHiNBBTCMFVz10P7S1uMDn8HgieHRlS8UpCYP1thKuvergISo31gSUFjRLbupMf4E4CipaYLJYkkg==",
"license": "MIT",
"engines": {
"node": ">=24",
"npm": ">=11.10.1"
"npm": ">=11.6.2"
}
},
"node_modules/@goauthentik/web-sfe": {
@@ -3066,75 +3066,75 @@
"license": "Apache-2.0"
},
"node_modules/@sentry-internal/browser-utils": {
"version": "10.42.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-10.42.0.tgz",
"integrity": "sha512-HCEICKvepxN4/6NYfnMMMlppcSwIEwtS66X6d1/mwaHdi2ivw0uGl52p7Nfhda/lIJArbrkWprxl0WcjZajhQA==",
"version": "10.40.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-10.40.0.tgz",
"integrity": "sha512-3CDeVNBXYOIvBVdT0SOdMZx5LzYDLuhGK/z7A14sYZz4Cd2+f4mSeFDaEOoH/g2SaY2CKR5KGkAADy8IyjZ21w==",
"license": "MIT",
"dependencies": {
"@sentry/core": "10.42.0"
"@sentry/core": "10.40.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@sentry-internal/feedback": {
"version": "10.42.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-10.42.0.tgz",
"integrity": "sha512-lpPcHsog10MVYFTWE0Pf8vQRqQWwZHJpkVl2FEb9/HDdHFyTBUhCVoWo1KyKaG7GJl9AVKMAg7bp9SSNArhFNQ==",
"version": "10.40.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-10.40.0.tgz",
"integrity": "sha512-V/ixkcdCNMo04KgsCEeNEu966xUUTD6czKT2LOAO5siZACqFjT/Rp9VR1n7QQrVo3sL7P3QNiTHtX0jaeWbwzg==",
"license": "MIT",
"dependencies": {
"@sentry/core": "10.42.0"
"@sentry/core": "10.40.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@sentry-internal/replay": {
"version": "10.42.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-10.42.0.tgz",
"integrity": "sha512-Zh3EoaH39x2lqVY1YyVB2vJEyCIrT+YLUQxYl1yvP0MJgLxaR6akVjkgxbSUJahan4cX5DxpZiEHfzdlWnYPyQ==",
"version": "10.40.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-10.40.0.tgz",
"integrity": "sha512-vsH2Ut0KIIQIHNdS3zzEGLJ2C9btbpvJIWAVk7l7oft66JzlUNC89qNaQ5SAypjLQx4Ln2V/ZTqfEoNzXOAsoQ==",
"license": "MIT",
"dependencies": {
"@sentry-internal/browser-utils": "10.42.0",
"@sentry/core": "10.42.0"
"@sentry-internal/browser-utils": "10.40.0",
"@sentry/core": "10.40.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@sentry-internal/replay-canvas": {
"version": "10.42.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-10.42.0.tgz",
"integrity": "sha512-am3m1Fj8ihoPfoYo41Qq4KeCAAICn4bySso8Oepu9dMNe9Lcnsf+reMRS2qxTPg3pZDc4JEMOcLyNCcgnAfrHw==",
"version": "10.40.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-10.40.0.tgz",
"integrity": "sha512-wzQwilFHO2baeCt0dTMf0eW+rgK8O+mkisf9sQzPXzG3Krr/iVtFg1T5T1Th3YsCsEdn6yQ3hcBPLEXjMSvccg==",
"license": "MIT",
"dependencies": {
"@sentry-internal/replay": "10.42.0",
"@sentry/core": "10.42.0"
"@sentry-internal/replay": "10.40.0",
"@sentry/core": "10.40.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@sentry/browser": {
"version": "10.42.0",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-10.42.0.tgz",
"integrity": "sha512-iXxYjXNEBwY1MH4lDSDZZUNjzPJDK7/YLwVIJq/3iBYpIQVIhaJsoJnf3clx9+NfJ8QFKyKfcvgae61zm+hgTA==",
"version": "10.40.0",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-10.40.0.tgz",
"integrity": "sha512-nCt3FKUMFad0C6xl5wCK0Jz+qT4Vev4fv6HJRn0YoNRRDQCfsUVxAz7pNyyiPNGM/WCDp9wJpGJsRvbBRd2anw==",
"license": "MIT",
"dependencies": {
"@sentry-internal/browser-utils": "10.42.0",
"@sentry-internal/feedback": "10.42.0",
"@sentry-internal/replay": "10.42.0",
"@sentry-internal/replay-canvas": "10.42.0",
"@sentry/core": "10.42.0"
"@sentry-internal/browser-utils": "10.40.0",
"@sentry-internal/feedback": "10.40.0",
"@sentry-internal/replay": "10.40.0",
"@sentry-internal/replay-canvas": "10.40.0",
"@sentry/core": "10.40.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@sentry/core": {
"version": "10.42.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.42.0.tgz",
"integrity": "sha512-L4rMrXMqUKBanpjpMT+TuAVk6xAijz6AWM6RiEYpohAr7SGcCEc1/T0+Ep1eLV8+pwWacfU27OvELIyNeOnGzA==",
"version": "10.40.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.40.0.tgz",
"integrity": "sha512-/wrcHPp9Avmgl6WBimPjS4gj810a1wU5oX9fF1bzJfeIIbF3jTsAbv0oMbgDp0cSDnkwv2+NvcPnn3+c5J6pBA==",
"license": "MIT",
"engines": {
"node": ">=18"
@@ -3171,15 +3171,15 @@
"license": "MIT"
},
"node_modules/@storybook/addon-docs": {
"version": "10.2.13",
"resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-10.2.13.tgz",
"integrity": "sha512-puMxpJbt/CuodLIbKDxWrW1ZgADYomfNHWEKp2d2l2eJjp17rADx0h3PABuNbX+YHbJwYcDdqluSnQwMysFEOA==",
"version": "10.2.12",
"resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-10.2.12.tgz",
"integrity": "sha512-LlLQSYxISugG2mEVVV/rNMyZaF1wIvIhaq5kGKbBuAz18GOtQmvaa/o3fJjlebmvTn/AOmipNlQ4ChOXSAwmdg==",
"license": "MIT",
"dependencies": {
"@mdx-js/react": "^3.0.0",
"@storybook/csf-plugin": "10.2.13",
"@storybook/csf-plugin": "10.2.12",
"@storybook/icons": "^2.0.1",
"@storybook/react-dom-shim": "10.2.13",
"@storybook/react-dom-shim": "10.2.12",
"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"
@@ -3189,13 +3189,13 @@
"url": "https://opencollective.com/storybook"
},
"peerDependencies": {
"storybook": "^10.2.13"
"storybook": "^10.2.12"
}
},
"node_modules/@storybook/addon-links": {
"version": "10.2.13",
"resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-10.2.13.tgz",
"integrity": "sha512-8wnAomGiHaUpNIc+lOzmazTrebxa64z9rihIbM/Q59vkOImHQNkGp7KP/qNgJA4GPTFtu8+fLjX2qCoAQPM0jQ==",
"version": "10.2.12",
"resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-10.2.12.tgz",
"integrity": "sha512-ybqnOhje/V75AbsGsLkk6uCcOXsfyUrkdqnp/y2e6YJRws2mlNMIYduNtqorFHm3Yh+NPH9GnzulVFaDFXBiYw==",
"license": "MIT",
"dependencies": {
"@storybook/global": "^5.0.0"
@@ -3206,7 +3206,7 @@
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
"storybook": "^10.2.13"
"storybook": "^10.2.12"
},
"peerDependenciesMeta": {
"react": {
@@ -3215,12 +3215,12 @@
}
},
"node_modules/@storybook/builder-vite": {
"version": "10.2.13",
"resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-10.2.13.tgz",
"integrity": "sha512-UMlPPPBa5ZbcaCXSKrFIi4tTEb0W72JTByqlJ5cGtDXGkN2uX69aL5n2JLIP0F4NzRRl6rNTeu9tGPPcD4r/CA==",
"version": "10.2.12",
"resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-10.2.12.tgz",
"integrity": "sha512-QaOwhNpoBs4Af8xYG1NOoyOvbapcN7EpwYq53ERpMz2uU+yOeApqy4XeJ5xM7doCvkJKZqidwLMXZQeN/j/mUw==",
"license": "MIT",
"dependencies": {
"@storybook/csf-plugin": "10.2.13",
"@storybook/csf-plugin": "10.2.12",
"ts-dedent": "^2.0.0"
},
"funding": {
@@ -3228,14 +3228,14 @@
"url": "https://opencollective.com/storybook"
},
"peerDependencies": {
"storybook": "^10.2.13",
"storybook": "^10.2.12",
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0"
}
},
"node_modules/@storybook/csf-plugin": {
"version": "10.2.13",
"resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-10.2.13.tgz",
"integrity": "sha512-gUCR7PmyrWYj3dIJJgxOm25dcXFolPIUPmug3z90Aaon7YPXw3pUN+dNDx8KqDJqRK1WDIB4HaefgYZIm5V7iA==",
"version": "10.2.12",
"resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-10.2.12.tgz",
"integrity": "sha512-+6nv7oX21K+BRmMUCfeaFeS3UFgDu5/2M4YsLYzXnx/YmAP3yg0bb09falzvCKTCncHCJUWaO8GDvavj32anaA==",
"license": "MIT",
"dependencies": {
"unplugin": "^2.3.5"
@@ -3247,7 +3247,7 @@
"peerDependencies": {
"esbuild": "*",
"rollup": "*",
"storybook": "^10.2.13",
"storybook": "^10.2.12",
"vite": "*",
"webpack": "*"
},
@@ -3283,9 +3283,9 @@
}
},
"node_modules/@storybook/react-dom-shim": {
"version": "10.2.13",
"resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-10.2.13.tgz",
"integrity": "sha512-ZSduoB10qTI0V9z22qeULmQLsvTs8d/rtJi03qbVxpPiMRor86AmyAaBrfhGGmWBxWQZpOGQQm6yIT2YLoPs7w==",
"version": "10.2.12",
"resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-10.2.12.tgz",
"integrity": "sha512-T4uHPAgtEbroRazubGDIByopvNw1WKAp1kOlNqKWR8LZPDaVyEr8X7J6zaJo1y+vE8j6fUXvk6jZVMwhi/Jeig==",
"license": "MIT",
"funding": {
"type": "opencollective",
@@ -3294,13 +3294,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.2.13"
"storybook": "^10.2.12"
}
},
"node_modules/@storybook/web-components": {
"version": "10.2.13",
"resolved": "https://registry.npmjs.org/@storybook/web-components/-/web-components-10.2.13.tgz",
"integrity": "sha512-su/f9CUdWwwc5c8+p6RvAB1LgWLbcSJ2y1bUj9QJsYyWB7cG+t76XoQ/5fuob71GgLCt7Aqz0+9oLMTUicCXgw==",
"version": "10.2.12",
"resolved": "https://registry.npmjs.org/@storybook/web-components/-/web-components-10.2.12.tgz",
"integrity": "sha512-lb57k6NkVum9vkalRnmK8qf6KRDei/yTgyPdLQoGzYl7RLtkBPrElvEV6YYnnryc8HYpmQx4vXs7cnE/SrfYVw==",
"license": "MIT",
"dependencies": {
"@storybook/global": "^5.0.0",
@@ -3313,24 +3313,24 @@
},
"peerDependencies": {
"lit": "^2.0.0 || ^3.0.0",
"storybook": "^10.2.13"
"storybook": "^10.2.12"
}
},
"node_modules/@storybook/web-components-vite": {
"version": "10.2.13",
"resolved": "https://registry.npmjs.org/@storybook/web-components-vite/-/web-components-vite-10.2.13.tgz",
"integrity": "sha512-LLXhMREvpFJmjyt1GIycHAJlI2vleXe2wJ9XkbuD6zIIArUsuB/3iNT2RDCudzFmOXqQQjsYAd4QWbIkCCYMKA==",
"version": "10.2.12",
"resolved": "https://registry.npmjs.org/@storybook/web-components-vite/-/web-components-vite-10.2.12.tgz",
"integrity": "sha512-YHeX6StEJDyrhrlrnonUQqQ8Ee68bJUCD8Sr6oSSXLQUhKVs4SNw+uMYmxKL6z/kAC0Ez0nG5z/T32UTuoVYYA==",
"license": "MIT",
"dependencies": {
"@storybook/builder-vite": "10.2.13",
"@storybook/web-components": "10.2.13"
"@storybook/builder-vite": "10.2.12",
"@storybook/web-components": "10.2.12"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/storybook"
},
"peerDependencies": {
"storybook": "^10.2.13"
"storybook": "^10.2.12"
}
},
"node_modules/@swagger-api/apidom-ast": {
@@ -3939,9 +3939,9 @@
}
},
"node_modules/@swc/core": {
"version": "1.15.18",
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.18.tgz",
"integrity": "sha512-z87aF9GphWp//fnkRsqvtY+inMVPgYW3zSlXH1kJFvRT5H/wiAn+G32qW5l3oEk63KSF1x3Ov0BfHCObAmT8RA==",
"version": "1.15.13",
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.13.tgz",
"integrity": "sha512-0l1gl/72PErwUZuavcRpRAQN9uSst+Nk++niC5IX6lmMWpXoScYx3oq/narT64/sKv/eRiPTaAjBFGDEQiWJIw==",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
@@ -3956,16 +3956,16 @@
"url": "https://opencollective.com/swc"
},
"optionalDependencies": {
"@swc/core-darwin-arm64": "1.15.18",
"@swc/core-darwin-x64": "1.15.18",
"@swc/core-linux-arm-gnueabihf": "1.15.18",
"@swc/core-linux-arm64-gnu": "1.15.18",
"@swc/core-linux-arm64-musl": "1.15.18",
"@swc/core-linux-x64-gnu": "1.15.18",
"@swc/core-linux-x64-musl": "1.15.18",
"@swc/core-win32-arm64-msvc": "1.15.18",
"@swc/core-win32-ia32-msvc": "1.15.18",
"@swc/core-win32-x64-msvc": "1.15.18"
"@swc/core-darwin-arm64": "1.15.13",
"@swc/core-darwin-x64": "1.15.13",
"@swc/core-linux-arm-gnueabihf": "1.15.13",
"@swc/core-linux-arm64-gnu": "1.15.13",
"@swc/core-linux-arm64-musl": "1.15.13",
"@swc/core-linux-x64-gnu": "1.15.13",
"@swc/core-linux-x64-musl": "1.15.13",
"@swc/core-win32-arm64-msvc": "1.15.13",
"@swc/core-win32-ia32-msvc": "1.15.13",
"@swc/core-win32-x64-msvc": "1.15.13"
},
"peerDependencies": {
"@swc/helpers": ">=0.5.17"
@@ -3977,9 +3977,9 @@
}
},
"node_modules/@swc/core-darwin-arm64": {
"version": "1.15.18",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.18.tgz",
"integrity": "sha512-+mIv7uBuSaywN3C9LNuWaX1jJJ3SKfiJuE6Lr3bd+/1Iv8oMU7oLBjYMluX1UrEPzwN2qCdY6Io0yVicABoCwQ==",
"version": "1.15.13",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.13.tgz",
"integrity": "sha512-ztXusRuC5NV2w+a6pDhX13CGioMLq8CjX5P4XgVJ21ocqz9t19288Do0y8LklplDtwcEhYGTNdMbkmUT7+lDTg==",
"cpu": [
"arm64"
],
@@ -3993,9 +3993,9 @@
}
},
"node_modules/@swc/core-darwin-x64": {
"version": "1.15.18",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.18.tgz",
"integrity": "sha512-wZle0eaQhnzxWX5V/2kEOI6Z9vl/lTFEC6V4EWcn+5pDjhemCpQv9e/TDJ0GIoiClX8EDWRvuZwh+Z3dhL1NAg==",
"version": "1.15.13",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.13.tgz",
"integrity": "sha512-cVifxQUKhaE7qcO/y9Mq6PEhoyvN9tSLzCnnFZ4EIabFHBuLtDDO6a+vLveOy98hAs5Qu1+bb5Nv0oa1Pihe3Q==",
"cpu": [
"x64"
],
@@ -4009,9 +4009,9 @@
}
},
"node_modules/@swc/core-linux-arm-gnueabihf": {
"version": "1.15.18",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.18.tgz",
"integrity": "sha512-ao61HGXVqrJFHAcPtF4/DegmwEkVCo4HApnotLU8ognfmU8x589z7+tcf3hU+qBiU1WOXV5fQX6W9Nzs6hjxDw==",
"version": "1.15.13",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.13.tgz",
"integrity": "sha512-t+xxEzZ48enl/wGGy7SRYd7kImWQ/+wvVFD7g5JZo234g6/QnIgZ+YdfIyjHB+ZJI3F7a2IQHS7RNjxF29UkWw==",
"cpu": [
"arm"
],
@@ -4025,9 +4025,9 @@
}
},
"node_modules/@swc/core-linux-arm64-gnu": {
"version": "1.15.18",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.18.tgz",
"integrity": "sha512-3xnctOBLIq3kj8PxOCgPrGjBLP/kNOddr6f5gukYt/1IZxsITQaU9TDyjeX6jG+FiCIHjCuWuffsyQDL5Ew1bg==",
"version": "1.15.13",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.13.tgz",
"integrity": "sha512-VndeGvKmTXFn6AGwjy0Kg8i7HccOCE7Jt/vmZwRxGtOfNZM1RLYRQ7MfDLo6T0h1Bq6eYzps3L5Ma4zBmjOnOg==",
"cpu": [
"arm64"
],
@@ -4041,9 +4041,9 @@
}
},
"node_modules/@swc/core-linux-arm64-musl": {
"version": "1.15.18",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.18.tgz",
"integrity": "sha512-0a+Lix+FSSHBSBOA0XznCcHo5/1nA6oLLjcnocvzXeqtdjnPb+SvchItHI+lfeiuj1sClYPDvPMLSLyXFaiIKw==",
"version": "1.15.13",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.13.tgz",
"integrity": "sha512-SmZ9m+XqCB35NddHCctvHFLqPZDAs5j8IgD36GoutufDJmeq2VNfgk5rQoqNqKmAK3Y7iFdEmI76QoHIWiCLyw==",
"cpu": [
"arm64"
],
@@ -4057,9 +4057,9 @@
}
},
"node_modules/@swc/core-linux-x64-gnu": {
"version": "1.15.18",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.18.tgz",
"integrity": "sha512-wG9J8vReUlpaHz4KOD/5UE1AUgirimU4UFT9oZmupUDEofxJKYb1mTA/DrMj0s78bkBiNI+7Fo2EgPuvOJfuAA==",
"version": "1.15.13",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.13.tgz",
"integrity": "sha512-5rij+vB9a29aNkHq72EXI2ihDZPszJb4zlApJY4aCC/q6utgqFA6CkrfTfIb+O8hxtG3zP5KERETz8mfFK6A0A==",
"cpu": [
"x64"
],
@@ -4073,9 +4073,9 @@
}
},
"node_modules/@swc/core-linux-x64-musl": {
"version": "1.15.18",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.18.tgz",
"integrity": "sha512-4nwbVvCphKzicwNWRmvD5iBaZj8JYsRGa4xOxJmOyHlMDpsvvJ2OR2cODlvWyGFH6BYL1MfIAK3qph3hp0Az6g==",
"version": "1.15.13",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.13.tgz",
"integrity": "sha512-OlSlaOK9JplQ5qn07WiBLibkOw7iml2++ojEXhhR3rbWrNEKCD7sd8+6wSavsInyFdw4PhLA+Hy6YyDBIE23Yw==",
"cpu": [
"x64"
],
@@ -4089,9 +4089,9 @@
}
},
"node_modules/@swc/core-win32-arm64-msvc": {
"version": "1.15.18",
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.18.tgz",
"integrity": "sha512-zk0RYO+LjiBCat2RTMHzAWaMky0cra9loH4oRrLKLLNuL+jarxKLFDA8xTZWEkCPLjUTwlRN7d28eDLLMgtUcQ==",
"version": "1.15.13",
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.13.tgz",
"integrity": "sha512-zwQii5YVdsfG8Ti9gIKgBKZg8qMkRZxl+OlYWUT5D93Jl4NuNBRausP20tfEkQdAPSRrMCSUZBM6FhW7izAZRg==",
"cpu": [
"arm64"
],
@@ -4105,9 +4105,9 @@
}
},
"node_modules/@swc/core-win32-ia32-msvc": {
"version": "1.15.18",
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.18.tgz",
"integrity": "sha512-yVuTrZ0RccD5+PEkpcLOBAuPbYBXS6rslENvIXfvJGXSdX5QGi1ehC4BjAMl5FkKLiam4kJECUI0l7Hq7T1vwg==",
"version": "1.15.13",
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.13.tgz",
"integrity": "sha512-hYXvyVVntqRlYoAIDwNzkS3tL2ijP3rxyWQMNKaxcCxxkCDto/w3meOK/OB6rbQSkNw0qTUcBfU9k+T0ptYdfQ==",
"cpu": [
"ia32"
],
@@ -4121,9 +4121,9 @@
}
},
"node_modules/@swc/core-win32-x64-msvc": {
"version": "1.15.18",
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.18.tgz",
"integrity": "sha512-7NRmE4hmUQNCbYU3Hn9Tz57mK9Qq4c97ZS+YlamlK6qG9Fb5g/BB3gPDe0iLlJkns/sYv2VWSkm8c3NmbEGjbg==",
"version": "1.15.13",
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.13.tgz",
"integrity": "sha512-XTzKs7c/vYCcjmcwawnQvlHHNS1naJEAzcBckMI5OJlnrcgW8UtcX9NHFYvNjGtXuKv0/9KvqL4fuahdvlNGKw==",
"cpu": [
"x64"
],
@@ -4645,9 +4645,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "25.3.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.3.tgz",
"integrity": "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==",
"version": "25.3.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.0.tgz",
"integrity": "sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A==",
"license": "MIT",
"dependencies": {
"undici-types": "~7.18.0"
@@ -6577,9 +6577,9 @@
}
},
"node_modules/country-flag-icons": {
"version": "1.6.15",
"resolved": "https://registry.npmjs.org/country-flag-icons/-/country-flag-icons-1.6.15.tgz",
"integrity": "sha512-92HoA8l6DluEidku8tKBftjuFRj4Rv3zDW1lXxCuNnqAxhUSkvso9gM/Afj4F5BnK+wneHIe3ydI+s+4NA29/Q==",
"version": "1.6.14",
"resolved": "https://registry.npmjs.org/country-flag-icons/-/country-flag-icons-1.6.14.tgz",
"integrity": "sha512-tPis+tN/esXXSuQJZCbkUhnIeUrZmtDKSjUuSI7Sss2GHsgAGvbSAQkx4Ut0qethZbDfPGYco8EjzOPElxYIFw==",
"license": "MIT"
},
"node_modules/crelt": {
@@ -9220,9 +9220,9 @@
}
},
"node_modules/globals": {
"version": "17.4.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-17.4.0.tgz",
"integrity": "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==",
"version": "17.3.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-17.3.0.tgz",
"integrity": "sha512-yMqGUQVVCkD4tqjOJf3TnrvaaHDMYp4VlUSObbkIiuCPe/ofdMBFIAcBbCSRFWOnos6qRiTVStDwqPLUclaxIw==",
"license": "MIT",
"engines": {
"node": ">=18"
@@ -15312,9 +15312,9 @@
}
},
"node_modules/storybook": {
"version": "10.2.13",
"resolved": "https://registry.npmjs.org/storybook/-/storybook-10.2.13.tgz",
"integrity": "sha512-heMfJjOfbHvL+wlCAwFZlSxcakyJ5yQDam6e9k2RRArB1veJhRnsjO6lO1hOXjJYrqxfHA/ldIugbBVlCDqfvQ==",
"version": "10.2.12",
"resolved": "https://registry.npmjs.org/storybook/-/storybook-10.2.12.tgz",
"integrity": "sha512-eT4266OqLdRE3J/pxl2dk36Rnj9vv17Y6rNMpzxohOMjRVONyKkozUd6gaZ2C4WUcwYdIw6VE+ft4LwrJXt/4Q==",
"license": "MIT",
"dependencies": {
"@storybook/global": "^5.0.0",
@@ -17351,8 +17351,8 @@
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"@goauthentik/tsconfig": "^1.0.7",
"@types/node": "^25.3.3",
"@goauthentik/tsconfig": "^1.0.5",
"@types/node": "^25.3.0",
"@types/semver": "^7.7.1",
"semver": "^7.7.4",
"typescript": "^5.9.3"
@@ -17385,7 +17385,7 @@
"@rollup/plugin-node-resolve": "^16.0.3",
"@rollup/plugin-swc": "^0.4.0",
"@swc/cli": "^0.8.0",
"@swc/core": "^1.15.18",
"@swc/core": "^1.15.11",
"base64-js": "^1.5.1",
"core-js": "^3.48.0",
"formdata-polyfill": "^2025.11.0",

View File

@@ -89,21 +89,21 @@
"dependencies": {
"@codemirror/lang-css": "^6.3.1",
"@codemirror/lang-html": "^6.4.11",
"@codemirror/lang-javascript": "^6.2.5",
"@codemirror/lang-javascript": "^6.2.4",
"@codemirror/lang-python": "^6.2.1",
"@codemirror/lang-xml": "^6.1.0",
"@codemirror/legacy-modes": "^6.5.2",
"@codemirror/theme-one-dark": "^6.1.3",
"@eslint/js": "^9.39.3",
"@floating-ui/dom": "^1.7.6",
"@floating-ui/dom": "^1.7.5",
"@formatjs/intl-listformat": "^8.2.1",
"@fortawesome/fontawesome-free": "^7.2.0",
"@goauthentik/api": "^2026.2.0-rc1-1770744803",
"@goauthentik/core": "^1.0.0",
"@goauthentik/esbuild-plugin-live-reload": "^1.6.1",
"@goauthentik/esbuild-plugin-live-reload": "^1.6.0",
"@goauthentik/eslint-config": "^1.3.0",
"@goauthentik/prettier-config": "^3.4.3",
"@goauthentik/tsconfig": "^1.0.7",
"@goauthentik/prettier-config": "^3.4.1",
"@goauthentik/tsconfig": "^1.0.5",
"@hcaptcha/types": "^1.1.0",
"@lit/context": "^1.1.6",
"@lit/localize": "^0.12.2",
@@ -118,15 +118,15 @@
"@patternfly/elements": "^4.3.1",
"@patternfly/patternfly": "^4.224.2",
"@playwright/test": "^1.58.2",
"@sentry/browser": "^10.42.0",
"@storybook/addon-docs": "^10.2.13",
"@storybook/addon-links": "^10.2.13",
"@storybook/web-components": "^10.2.13",
"@storybook/web-components-vite": "^10.2.13",
"@sentry/browser": "^10.39.0",
"@storybook/addon-docs": "^10.2.10",
"@storybook/addon-links": "^10.2.10",
"@storybook/web-components": "^10.2.10",
"@storybook/web-components-vite": "^10.2.10",
"@types/codemirror": "^5.60.17",
"@types/grecaptcha": "^3.0.9",
"@types/guacamole-common-js": "^1.5.5",
"@types/node": "^25.3.3",
"@types/node": "^25.3.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@typescript-eslint/eslint-plugin": "^8.56.1",
@@ -140,7 +140,7 @@
"chartjs-adapter-date-fns": "^3.0.0",
"codemirror": "^6.0.2",
"core-js": "^3.48.0",
"country-flag-icons": "^1.6.15",
"country-flag-icons": "^1.6.13",
"date-fns": "^4.1.0",
"deepmerge-ts": "^7.1.5",
"dompurify": "^3.3.1",
@@ -149,7 +149,7 @@
"eslint-plugin-lit": "^2.2.1",
"eslint-plugin-wc": "^3.1.0",
"fuse.js": "^7.1.0",
"globals": "^17.4.0",
"globals": "^17.3.0",
"guacamole-common-js": "^1.5.0",
"hastscript": "^9.0.1",
"knip": "^5.85.0",

View File

@@ -44,8 +44,8 @@
}
},
"dependencies": {
"@goauthentik/tsconfig": "^1.0.7",
"@types/node": "^25.3.3",
"@goauthentik/tsconfig": "^1.0.5",
"@types/node": "^25.3.0",
"@types/semver": "^7.7.1",
"semver": "^7.7.4",
"typescript": "^5.9.3"

View File

@@ -20,7 +20,7 @@
"@rollup/plugin-node-resolve": "^16.0.3",
"@rollup/plugin-swc": "^0.4.0",
"@swc/cli": "^0.8.0",
"@swc/core": "^1.15.18",
"@swc/core": "^1.15.11",
"base64-js": "^1.5.1",
"core-js": "^3.48.0",
"formdata-polyfill": "^2025.11.0",

View File

@@ -3,172 +3,137 @@ import "#elements/EmptyState";
import { DEFAULT_CONFIG } from "#common/api/config";
import { globalAK } from "#common/global";
import { ModalButton } from "#elements/buttons/ModalButton";
import { WithBrandConfig } from "#elements/mixins/branding";
import { WithLicenseSummary } from "#elements/mixins/license";
import { AKModal } from "#elements/modals/ak-modal";
import { asInvoker } from "#elements/modals/utils";
import { ThemedImage } from "#elements/utils/images";
import { AdminApi, CapabilitiesEnum, LicenseSummaryStatusEnum } from "@goauthentik/api";
import { msg } from "@lit/localize";
import { css, html, TemplateResult } from "lit";
import { ref } from "lit-html/directives/ref.js";
import { styleMap } from "lit-html/directives/style-map.js";
import { customElement, state } from "lit/decorators.js";
import { customElement } from "lit/decorators.js";
import { createRef, ref } from "lit/directives/ref.js";
import { until } from "lit/directives/until.js";
import PFAbout from "@patternfly/patternfly/components/AboutModalBox/about-modal-box.css";
const DEFAULT_BRAND_IMAGE = "/static/dist/assets/images/flow_background.jpg";
type AboutEntry = [label: string, content: string | TemplateResult];
async function fetchAboutDetails(): Promise<AboutEntry[]> {
const api = new AdminApi(DEFAULT_CONFIG);
const [status, version] = await Promise.all([
api.adminSystemRetrieve(),
api.adminVersionRetrieve(),
]);
let build: string | TemplateResult = msg("Release");
if (globalAK().config.capabilities.includes(CapabilitiesEnum.CanDebug)) {
build = msg("Development");
} else if (version.buildHash) {
build = html`<a
rel="noopener noreferrer"
href="https://github.com/goauthentik/authentik/commit/${version.buildHash}"
target="_blank"
>${version.buildHash}</a
>`;
}
return [
[msg("Version"), version.versionCurrent],
[msg("UI Version"), import.meta.env.AK_VERSION],
[msg("Build"), build],
[msg("Python version"), status.runtime.pythonVersion],
[msg("Platform"), status.runtime.platform],
[msg("Kernel"), status.runtime.uname],
[
msg("OpenSSL"),
`${status.runtime.opensslVersion} ${status.runtime.opensslFipsEnabled ? "FIPS" : ""}`,
],
];
}
@customElement("ak-about-modal")
export class AboutModal extends WithLicenseSummary(WithBrandConfig(AKModal)) {
static hostStyles = [
css`
dialog.ak-c-modal:has(ak-about-modal) {
--ak-c-modal--BackgroundColor: var(--pf-global--palette--black-900);
--ak-c-modal--BorderColor: var(--pf-global--palette--black-600);
}
`,
];
export class AboutModal extends WithLicenseSummary(WithBrandConfig(ModalButton)) {
static styles = [
...AKModal.styles,
...ModalButton.styles,
PFAbout,
css`
:host {
height: 100%;
.pf-c-about-modal-box {
--pf-c-about-modal-box--BackgroundColor: var(--pf-global--palette--black-900);
}
.pf-c-about-modal-box {
--pf-c-about-modal-box--BackgroundColor: var(--ak-c-modal--BackgroundColor);
width: unset;
height: 100%;
max-height: unset;
max-width: unset;
z-index: unset;
position: unset;
box-shadow: unset;
.pf-c-about-modal-box__hero {
background-image: url("/static/dist/assets/images/flow_background.jpg");
}
.pf-c-about-modal-box__brand {
--pf-c-about-modal-box__brand-image--Height: 6.25rem;
}
.pf-c-about-modal-box__brand i {
font-size: var(--pf-c-about-modal-box__brand-image--Height);
}
`,
];
public static open = asInvoker(AboutModal);
@state()
protected entries: AboutEntry[] | null = null;
public refresh() {
return fetchAboutDetails().then((entries) => {
this.entries = entries;
});
async getAboutEntries(): Promise<[string, string | TemplateResult][]> {
const status = await new AdminApi(DEFAULT_CONFIG).adminSystemRetrieve();
const version = await new AdminApi(DEFAULT_CONFIG).adminVersionRetrieve();
let build: string | TemplateResult = msg("Release");
if (globalAK().config.capabilities.includes(CapabilitiesEnum.CanDebug)) {
build = msg("Development");
} else if (version.buildHash !== "") {
build = html`<a
rel="noopener noreferrer"
href="https://github.com/goauthentik/authentik/commit/${version.buildHash}"
target="_blank"
>${version.buildHash}</a
>`;
}
return [
[msg("Version"), version.versionCurrent],
[msg("UI Version"), import.meta.env.AK_VERSION],
[msg("Build"), build],
[msg("Python version"), status.runtime.pythonVersion],
[msg("Platform"), status.runtime.platform],
[msg("Kernel"), status.runtime.uname],
[
msg("OpenSSL"),
`${status.runtime.opensslVersion} ${status.runtime.opensslFipsEnabled ? "FIPS" : ""}`,
],
];
}
public connectedCallback(): void {
super.connectedCallback();
this.refresh();
}
#contentRef = createRef<HTMLDivElement>();
//#region Renderers
#backdropListener = (event: PointerEvent) => {
// We only want to close the modal when the backdrop is clicked, not when it's children are clicked.
protected override renderCloseButton() {
return null;
}
if (this.#contentRef.value?.contains(event.target as Node)) {
return;
}
this.close();
};
protected override render() {
protected override renderModal() {
let product = this.brandingTitle;
if (this.licenseSummary?.status !== LicenseSummaryStatusEnum.Unlicensed) {
product += ` ${msg("Enterprise")}`;
}
return html`<div
${ref(this.scrollContainerRef)}
class="pf-c-about-modal-box"
style=${styleMap({
"--pf-c-about-modal-box__hero--sm--BackgroundImage": `url(${DEFAULT_BRAND_IMAGE})`,
})}
aria-labelledby="modal-title"
>
<div class="pf-c-about-modal-box__close">
<button
class="pf-c-button pf-m-plain"
type="button"
@click=${this.closeListener}
aria-label=${msg("Close dialog")}
return html`<div class="pf-c-backdrop" @click=${this.#backdropListener}>
<div class="pf-l-bullseye">
<div
${ref(this.#contentRef)}
class="pf-c-about-modal-box"
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
>
<i class="fas fa-times" aria-hidden="true"></i>
</button>
</div>
<div class="pf-c-about-modal-box__brand">
${ThemedImage({
src: this.brandingFavicon,
alt: msg("authentik Logo"),
className: "pf-c-about-modal-box__brand-image",
theme: this.activeTheme,
themedUrls: this.brandingFaviconThemedUrls,
})}
</div>
<div class="pf-c-about-modal-box__header">
<h1 class="pf-c-title pf-m-4xl" id="modal-title">${product}</h1>
</div>
<div class="pf-c-about-modal-box__hero"></div>
<div class="pf-c-about-modal-box__content">
<div class="pf-c-about-modal-box__body">
<div class="pf-c-content">
${this.entries
? html`<dl>
${this.entries.map(([label, value]) => {
return html`<dt>${label}</dt>
<dd>${value}</dd>`;
})}
</dl>`
: html`<ak-empty-state loading></ak-empty-state>`}
<div class="pf-c-about-modal-box__brand">
${ThemedImage({
src: this.brandingFavicon,
alt: msg("authentik Logo"),
className: "pf-c-about-modal-box__brand-image",
theme: this.activeTheme,
themedUrls: this.brandingFaviconThemedUrls,
})}
</div>
<div class="pf-c-about-modal-box__close">
<button class="pf-c-button pf-m-plain" type="button" @click=${this.close}>
<i class="fas fa-times" aria-hidden="true"></i>
</button>
</div>
<div class="pf-c-about-modal-box__header">
<h1 class="pf-c-title pf-m-4xl" id="modal-title">${product}</h1>
</div>
<div class="pf-c-about-modal-box__hero"></div>
<div class="pf-c-about-modal-box__content">
<div class="pf-c-about-modal-box__body">
<div class="pf-c-content">
${until(
this.getAboutEntries().then((entries) => {
return html`<dl>
${entries.map(([label, value]) => {
return html`<dt>${label}</dt>
<dd>${value}</dd>`;
})}
</dl>`;
}),
html`<ak-empty-state loading></ak-empty-state>`,
)}
</div>
</div>
<p class="pf-c-about-modal-box__strapline"></p>
</div>
</div>
<p class="pf-c-about-modal-box__strapline"></p>
</div>
</div>`;
}
//#endregion
}
declare global {

View File

@@ -1,3 +1,4 @@
import "#admin/AdminInterface/AboutModal";
import "#elements/banner/EnterpriseStatusBanner";
import "#elements/banner/VersionBanner";
import "#elements/messages/MessageContainer";
@@ -9,7 +10,6 @@ import {
createAdminSidebarEnterpriseEntries,
createAdminSidebarEntries,
renderSidebarItems,
SidebarEntry,
} from "./AdminSidebar.js";
import { isAPIResultReady } from "#common/api/responses";
@@ -29,16 +29,16 @@ import {
readDrawerParams,
renderNotificationDrawerPanel,
} from "#elements/notifications/utils";
import { navigate } from "#elements/router/RouterOutlet";
import type { AboutModal } from "#admin/AdminInterface/AboutModal";
import Styles from "#admin/AdminInterface/index.entrypoint.css";
import { ROUTES } from "#admin/Routes";
import { CapabilitiesEnum } from "@goauthentik/api";
import { LOCALE_STATUS_EVENT, LocaleStatusEventDetail, msg } from "@lit/localize";
import { msg } from "@lit/localize";
import { CSSResult, html, nothing, PropertyValues, TemplateResult } from "lit";
import { customElement, eventOptions, property, state } from "lit/decorators.js";
import { customElement, eventOptions, property, query, state } from "lit/decorators.js";
import { classMap } from "lit/directives/class-map.js";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
@@ -60,14 +60,14 @@ export class AdminInterface extends WithCapabilitiesConfig(
//#endregion
//#region Public Properties
//#region Properties
@query("ak-about-modal")
public aboutModal?: AboutModal;
@property({ type: Boolean, reflect: true, attribute: "sidebar" })
public sidebarOpen = false;
@property({ type: Array })
public entries: readonly SidebarEntry[] = createAdminSidebarEntries();
//#endregion
//#region Public Methods
@@ -76,14 +76,9 @@ export class AdminInterface extends WithCapabilitiesConfig(
this.sidebarOpen = !this.sidebarOpen;
};
public synchronizeSidebarEntries = () => {
this.logger.debug("Synchronizing sidebar entries with current locale");
this.entries = createAdminSidebarEntries();
};
//#endregion
//#region Event Listeners
//#region Lifecycle
#sidebarMatcher: MediaQueryList;
#sidebarMediaQueryListener = (event: MediaQueryListEvent) => {
@@ -104,17 +99,6 @@ export class AdminInterface extends WithCapabilitiesConfig(
persistDrawerParams(event.drawer);
};
@listen(LOCALE_STATUS_EVENT)
localeStatusListener = (event: CustomEvent<LocaleStatusEventDetail>) => {
if (event.detail.status === "ready") {
this.synchronizeSidebarEntries();
}
};
//#endregion
//#region Lifecycle
constructor() {
configureSentry();
@@ -126,41 +110,6 @@ export class AdminInterface extends WithCapabilitiesConfig(
this.sidebarOpen = this.#sidebarMatcher.matches;
}
#refreshCommandsFrameID = -1;
#refreshCommands = () => {
const commands = [
{
label: msg("Create a new application..."),
action: () => navigate("/core/applications", { createWizard: true }),
prefix: msg("Jump to", { id: "command-palette.prefix.jump-to" }),
group: msg("Applications"),
},
{
label: msg("Check the logs"),
action: () => navigate("/events/log"),
group: msg("Events"),
},
{
label: msg("Manage users"),
action: () => navigate("/identity/users"),
group: msg("Users"),
},
...this.entries.flatMap(([, label, , children]) => [
...(children ?? []).map(([path, childLabel]) => ({
label: childLabel,
prefix: msg("Jump to", { id: "command-palette.prefix.jump-to" }),
group: label,
action: () => {
navigate(path!);
},
})),
]),
];
this.commandPalette.modal.setCommands(commands);
};
public connectedCallback() {
super.connectedCallback();
@@ -172,19 +121,11 @@ export class AdminInterface extends WithCapabilitiesConfig(
public disconnectedCallback(): void {
super.disconnectedCallback();
cancelAnimationFrame(this.#refreshCommandsFrameID);
this.#sidebarMatcher.removeEventListener("change", this.#sidebarMediaQueryListener);
WebsocketClient.close();
}
public firstUpdated(changedProperties: PropertyValues<this>): void {
super.firstUpdated(changedProperties);
this.#refreshCommandsFrameID = requestAnimationFrame(this.#refreshCommands);
}
public override updated(changedProperties: PropertyValues<this>): void {
super.updated(changedProperties);
@@ -217,62 +158,62 @@ export class AdminInterface extends WithCapabilitiesConfig(
};
return html`<div class="pf-c-page">
<ak-page-navbar>
<button
slot="toggle"
aria-controls="global-nav"
class="pf-c-button pf-m-plain"
@click=${this.toggleSidebar}
aria-label=${this.sidebarOpen
? msg("Collapse navigation")
: msg("Expand navigation")}
aria-expanded=${this.sidebarOpen ? "true" : "false"}
>
<i aria-hidden="true" class="fas fa-bars"></i>
</button>
<ak-page-navbar>
<button
slot="toggle"
aria-controls="global-nav"
class="pf-c-button pf-m-plain"
@click=${this.toggleSidebar}
aria-label=${this.sidebarOpen
? msg("Collapse navigation")
: msg("Expand navigation")}
aria-expanded=${this.sidebarOpen ? "true" : "false"}
>
<i aria-hidden="true" class="fas fa-bars"></i>
</button>
<ak-version-banner></ak-version-banner>
<ak-enterprise-status interface="admin"></ak-enterprise-status>
</ak-page-navbar>
<ak-version-banner></ak-version-banner>
<ak-enterprise-status interface="admin"></ak-enterprise-status>
</ak-page-navbar>
<ak-sidebar ?hidden=${!this.sidebarOpen} class="${classMap(sidebarClasses)}"
>${renderSidebarItems(this.entries)}
${this.can(CapabilitiesEnum.IsEnterprise)
? renderSidebarItems(createAdminSidebarEnterpriseEntries())
: nothing}
</ak-sidebar>
<ak-sidebar ?hidden=${!this.sidebarOpen} class="${classMap(sidebarClasses)}"
>${renderSidebarItems(createAdminSidebarEntries())}
${this.can(CapabilitiesEnum.IsEnterprise)
? renderSidebarItems(createAdminSidebarEnterpriseEntries())
: nothing}
</ak-sidebar>
<div class="pf-c-page__drawer">
<div class="pf-c-drawer ${classMap(drawerClasses)}">
<div class="pf-c-drawer__main">
<div class="pf-c-drawer__content">
<div class="pf-c-drawer__body">
<ak-router-outlet
role="presentation"
class="pf-c-page__main"
tabindex="-1"
id="main-content"
default-url="/administration/overview"
.routes=${ROUTES}
@ak-route-change=${this.routeChangeListener}
>
</ak-router-outlet>
</div>
<div class="pf-c-page__drawer">
<div class="pf-c-drawer ${classMap(drawerClasses)}">
<div class="pf-c-drawer__main">
<div class="pf-c-drawer__content">
<div class="pf-c-drawer__body">
<ak-router-outlet
role="presentation"
class="pf-c-page__main"
tabindex="-1"
id="main-content"
default-url="/administration/overview"
.routes=${ROUTES}
@ak-route-change=${this.routeChangeListener}
>
</ak-router-outlet>
</div>
${renderNotificationDrawerPanel(this.drawer)}
</div>
${renderNotificationDrawerPanel(this.drawer)}
<ak-about-modal></ak-about-modal>
</div>
<div
class="pf-c-page__sidebar-backdrop"
aria-label=${this.sidebarOpen ? msg("Close sidebar") : msg("Open sidebar")}
@click=${this.toggleSidebar}
role="button"
tabindex="0"
></div>
</div>
<div
class="pf-c-page__sidebar-backdrop"
aria-label=${this.sidebarOpen ? msg("Close sidebar") : msg("Open sidebar")}
@click=${this.toggleSidebar}
role="button"
tabindex="0"
></div>
</div>
${this.commandPalette}`;
</div>`;
}
//#endregion

View File

@@ -293,12 +293,6 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
)}
>
</ak-switch-input>
<ak-switch-input
name="flags.flowsContinuousLogin"
?checked=${settings?.flags.flowsContinuousLogin ?? false}
label=${msg("Continuous Login")}
>
</ak-switch-input>
</div>
</ak-form-group>
`;

View File

@@ -40,9 +40,6 @@ import { ifDefined } from "lit/directives/if-defined.js";
export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Application, string>) {
#api = new CoreApi(DEFAULT_CONFIG);
protected entitySingular = msg("Application");
protected entityPlural = msg("Applications");
protected override async loadInstance(pk: string): Promise<Application> {
const app = await this.#api.coreApplicationsRetrieve({
slug: pk,

View File

@@ -5,29 +5,24 @@ import "#elements/ak-mdx/ak-mdx";
import "#elements/buttons/SpinnerButton/ak-spinner-button";
import "#elements/forms/DeleteBulkForm";
import "#elements/forms/ModalForm";
import "#elements/modals/ak-modal";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import "./ApplicationWizardHint.js";
import { DEFAULT_CONFIG } from "#common/api/config";
import { WithBrandConfig } from "#elements/mixins/branding";
import { asInvoker, renderModal } from "#elements/modals/utils";
import { getURLParam } from "#elements/router/RouteMatch";
import { PaginatedResponse, TableColumn } from "#elements/table/Table";
import { TablePage } from "#elements/table/TablePage";
import { SlottedTemplateResult } from "#elements/types";
import { ifPresent } from "#elements/utils/attributes";
import { ApplicationForm } from "#admin/applications/ApplicationForm";
import { AkApplicationWizard } from "#admin/applications/wizard/ak-application-wizard";
import { Application, CoreApi, PoliciesApi } from "@goauthentik/api";
import MDApplication from "~docs/add-secure-apps/applications/index.md";
import { msg, str } from "@lit/localize";
import { css, CSSResult, html, nothing, PropertyValues, TemplateResult } from "lit";
import { css, CSSResult, html, nothing, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators.js";
import PFCard from "@patternfly/patternfly/components/Card/card.css";
@@ -74,26 +69,6 @@ export class ApplicationListPage extends WithBrandConfig(TablePage<Application>)
static styles: CSSResult[] = [...TablePage.styles, PFCard, applicationListStyle];
public override firstUpdated(changed: PropertyValues<this>): void {
super.firstUpdated(changed);
if (getURLParam("createWizard", false)) {
this.#openCreateWizard();
} else if (getURLParam("createForm", false)) {
this.#openCreateModal();
}
}
#openEditModal(event: Event) {
const pk = (event.currentTarget as HTMLElement).dataset.pk;
renderModal(html`<ak-application-form .instancePk=${pk}></ak-application-form>`);
}
#openCreateWizard = AkApplicationWizard.open;
#openCreateModal = asInvoker(ApplicationForm);
protected columns: TableColumn[] = [
["", undefined, msg("Application Icon")],
[msg("Name"), "name"],
@@ -158,16 +133,21 @@ export class ApplicationListPage extends WithBrandConfig(TablePage<Application>)
: html`-`,
html`${item.providerObj?.verboseName || msg("-")}`,
html`<div>
<button
class="pf-c-button pf-m-plain"
aria-label=${msg(str`Edit "${item.name}"`)}
data-pk=${item.slug}
@click=${this.#openEditModal}
>
<pf-tooltip position="top" content=${msg("Edit")}>
<i class="fas fa-edit" aria-hidden="true"></i>
</pf-tooltip>
</button>
<ak-forms-modal>
<span slot="submit">${msg("Update")}</span>
<span slot="header">${msg("Update Application")}</span>
<ak-application-form slot="form" .instancePk=${item.slug}>
</ak-application-form>
<button
slot="trigger"
class="pf-c-button pf-m-plain"
aria-label=${msg(str`Edit "${item.name}"`)}
>
<pf-tooltip position="top" content=${msg("Edit")}>
<i class="fas fa-edit" aria-hidden="true"></i>
</pf-tooltip>
</button>
</ak-forms-modal>
${item.launchUrl
? html`<a
href=${item.launchUrl}
@@ -185,12 +165,21 @@ export class ApplicationListPage extends WithBrandConfig(TablePage<Application>)
}
renderObjectCreate(): TemplateResult {
return html`<button class="pf-c-button pf-m-primary" @click=${this.#openCreateWizard}>
${msg("Create with Provider")}
</button>
<button class="pf-c-button pf-m-primary" @click=${this.#openCreateModal}>
${msg("Create")}
</button>`;
return html` <ak-application-wizard .open=${getURLParam("createWizard", false)}>
<button
slot="trigger"
class="pf-c-button pf-m-primary"
data-ouia-component-id="start-application-wizard"
>
${msg("Create with Provider")}
</button>
</ak-application-wizard>
<ak-forms-modal .open=${getURLParam("createForm", false)}>
<span slot="submit">${msg("Create")}</span>
<span slot="header">${msg("Create Application")}</span>
<ak-application-form slot="form"> </ak-application-form>
<button slot="trigger" class="pf-c-button pf-m-primary">${msg("Create")}</button>
</ak-forms-modal>`;
}
renderToolbar(): TemplateResult {

View File

@@ -50,10 +50,6 @@ export const providerTypePriority: ProviderModelNameEnum[] = [
@customElement("ak-application-wizard-main")
export class AkApplicationWizardMain extends AKElement {
protected createRenderRoot(): HTMLElement | DocumentFragment {
return this;
}
@state()
wizard: ApplicationWizardState = freshWizardState();

View File

@@ -1,39 +1,28 @@
import "./ak-application-wizard-main.js";
import { AKModal } from "#elements/modals/ak-modal";
import { asInvoker } from "#elements/modals/utils";
import { SlottedTemplateResult } from "#elements/types";
import { ModalButton } from "#elements/buttons/ModalButton";
import { bound } from "#elements/decorators/bound";
import { WizardCloseEvent } from "#components/ak-wizard/events";
import { css, CSSResult, html } from "lit";
import { html } from "lit";
import { customElement } from "lit/decorators.js";
@customElement("ak-application-wizard")
export class AkApplicationWizard extends AKModal {
public static override styles: CSSResult[] = [
...super.styles,
css`
[part="main"] {
display: block;
}
`,
];
public static open = asInvoker(AkApplicationWizard);
export class AkApplicationWizard extends ModalButton {
constructor() {
super();
this.addEventListener(WizardCloseEvent.eventName, this.closeListener);
this.addEventListener(WizardCloseEvent.eventName, this.onCloseEvent);
}
protected renderCloseButton(): SlottedTemplateResult {
return null;
@bound
onCloseEvent(ev: WizardCloseEvent) {
ev.stopPropagation();
this.open = false;
}
render() {
return html`<ak-application-wizard-main part="main"></ak-application-wizard-main>`;
renderModalInner() {
return html` <ak-application-wizard-main> </ak-application-wizard-main>`;
}
}

View File

@@ -21,7 +21,7 @@ import {
import YAML from "yaml";
import { msg } from "@lit/localize";
import { css, CSSResult, html, nothing, TemplateResult } from "lit";
import { CSSResult, html, nothing, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
@@ -56,15 +56,7 @@ export class PolicyTestForm extends Form<PolicyTestRequest> {
return (this.result = result);
}
static styles: CSSResult[] = [
...super.styles,
PFDescriptionList,
css`
.ak-policy-test-log-messages {
width: 100%;
}
`,
];
static styles: CSSResult[] = [...super.styles, PFDescriptionList];
renderResult(): TemplateResult {
return html`
@@ -97,7 +89,7 @@ export class PolicyTestForm extends Form<PolicyTestRequest> {
<ak-form-element-horizontal label=${msg("Log messages")}>
<div class="pf-c-form__group-label">
<div class="pf-c-form__horizontal-group ak-policy-test-log-messages">
<div class="c-form__horizontal-group">
<dl class="pf-c-description-list pf-m-horizontal">
<ak-log-viewer .logs=${this.result?.logMessages}></ak-log-viewer>
</dl>

View File

@@ -1,13 +0,0 @@
/**
* Given an array or length, return logical index of the element at the given delta.
* This is effectively a modulo loop, allowing for positive and negative deltas.
*/
export function torusIndex(lengthLike: number | ArrayLike<number>, delta: number): number {
const length = typeof lengthLike === "number" ? lengthLike : lengthLike.length;
if (delta < 0) {
return (length + delta) % length;
}
return ((delta % length) + length) % length;
}

View File

@@ -371,4 +371,21 @@ export function applyBackgroundImageProperty(
target.style.setProperty(AKBackgroundImageProperty, `url("${nextURL.href}")`);
}
/**
* Returns the root interface element of the page.
*
* @deprecated Use context controllers to access the interface root instead.
*/
export function rootInterface<T extends HTMLElement = HTMLElement>(): T {
const element = document.body.querySelector<T>("[data-test-id=interface-root]");
if (!element) {
throw new Error(
`Could not find root interface element. Was this element added before the parent interface element?`,
);
}
return element;
}
//#endregion

View File

@@ -19,7 +19,6 @@ export const DefaultBrand = {
policiesBufferedAccessView: false,
flowsRefreshOthers: false,
enterpriseAuditIncludeExpandedDiff: false,
flowsContinuousLogin: false,
},
} as const satisfies CurrentBrand;

View File

@@ -1,12 +1,11 @@
import "#elements/buttons/Dropdown";
import { torusIndex } from "#common/collections";
import { StripHTMLTrustPolicy } from "#common/purify";
import { rootInterface } from "#common/theme";
import { FormAssociated, FormAssociatedElement } from "#elements/forms/form-associated-element";
import { PaginatedResponse } from "#elements/table/Table";
import { ifPresent } from "#elements/utils/attributes";
import { resolveInterface } from "#elements/utils/render-roots";
import Styles from "#components/ak-search-ql/styles.css";
@@ -34,6 +33,20 @@ export class QL extends DjangoQL {
}
}
/**
* Given an array or length, return logical index of the element at the given delta.
* This is effectively a modulo loop, allowing for positive and negative deltas.
*/
function torusIndex(lengthLike: number | ArrayLike<number>, delta: number): number {
const length = typeof lengthLike === "number" ? lengthLike : lengthLike.length;
if (delta < 0) {
return (length + delta) % length;
}
return ((delta % length) + length) % length;
}
@customElement("ak-search-ql")
export class QLSearch extends FormAssociatedElement<string> implements FormAssociated {
static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true };
@@ -130,7 +143,8 @@ export class QLSearch extends FormAssociatedElement<string> implements FormAssoc
public override connectedCallback() {
super.connectedCallback();
this.#scrollContainer = resolveInterface().renderRoot.querySelector("#main-content");
this.#scrollContainer =
rootInterface<LitElement>().renderRoot.querySelector("#main-content");
this.#scrollContainer?.addEventListener("scroll", this.#updateDropdownPosition, {
passive: true,

View File

@@ -7,7 +7,7 @@ import { AKElement } from "#elements/Base";
import { bound } from "#elements/decorators/bound";
import { Context, ContextProvider } from "@lit/context";
import { css, html, nothing } from "lit";
import { html, nothing } from "lit";
import { customElement, property } from "lit/decorators.js";
/**
@@ -25,14 +25,6 @@ import { customElement, property } from "lit/decorators.js";
@customElement("ak-wizard-steps")
export class WizardStepsManager extends AKElement {
public static styles = [
css`
:host {
display: block;
}
`,
];
@property({ type: String, attribute: true })
public currentStep?: string;
@@ -105,10 +97,6 @@ export class WizardStepsManager extends AKElement {
@bound
onSlotchange(ev: Event) {
console.debug(
"Slot change detected in WizardStepsManager; recalculating slots and step labels.",
ev,
);
ev.stopPropagation();
this.findSlots();
this.findSlot(this.currentStep);

View File

@@ -1,10 +1,5 @@
import { globalAK } from "#common/global";
import {
createCSSResult,
createStyleSheetUnsafe,
setAdoptedStyleSheets,
StyleRoot,
} from "#common/stylesheets";
import { createCSSResult, createStyleSheetUnsafe, StyleRoot } from "#common/stylesheets";
import { applyUITheme, ResolvedUITheme, resolveUITheme, ThemeChangeEvent } from "#common/theme";
import AKBase from "#styles/authentik/base.css" with { type: "bundled-text" };
@@ -39,22 +34,7 @@ export class AKElement extends LitElement implements AKElementProps {
public static styles?: Array<CSSResult | CSSModule>;
/**
* Host styles are styles that are applied to the element's render root,
* but are not scoped to the element itself.
*
* @remarks
*
* This is useful if the element is a wrapper around a third-party component
* that requires styles to be applied to the host, such as Patternfly's modals.
*/
public static hostStyles?: Array<CSSResult | CSSModule>;
private static hostStyleSheets: CSSStyleSheet[] | null = null;
protected static override finalizeStyles(styles: CSSResultGroup = []): CSSResultOrNative[] {
this.hostStyleSheets = this.hostStyles ? this.hostStyles.map(createStyleSheetUnsafe) : null;
const elementStyles = [
$PFBase,
// Route around TSC`s known-to-fail typechecking of `.flat(Infinity)`. Removes types.
@@ -122,35 +102,10 @@ export class AKElement extends LitElement implements AKElementProps {
this.activeTheme = preferredColorScheme;
}
const rootNode = this.getRootNode();
if (rootNode instanceof ShadowRoot) {
const { hostStyleSheets } = this.constructor as typeof AKElement;
if (hostStyleSheets) {
setAdoptedStyleSheets(rootNode, (currentStyleSheets) => {
return [...currentStyleSheets, ...hostStyleSheets];
});
}
}
}
public override disconnectedCallback(): void {
this.#themeAbortController?.abort();
const rootNode = this.getRootNode();
if (rootNode instanceof ShadowRoot) {
const { hostStyleSheets } = this.constructor as typeof AKElement;
if (hostStyleSheets) {
setAdoptedStyleSheets(rootNode, (currentStyleSheets) => {
return currentStyleSheets.filter((sheet) => !hostStyleSheets.includes(sheet));
});
}
}
super.disconnectedCallback();
}

View File

@@ -1,10 +1,7 @@
import "#elements/commands/ak-command-palette";
import { globalAK } from "#common/global";
import { applyDocumentTheme, createUIThemeEffect } from "#common/theme";
import { AKElement } from "#elements/Base";
import { AKCommandPalette } from "#elements/commands/ak-command-palette";
import { BrandingContextController } from "#elements/controllers/BrandContextController";
import { ConfigContextController } from "#elements/controllers/ConfigContextController";
import { ContextControllerRegistry } from "#elements/controllers/ContextControllerRegistry";
@@ -14,8 +11,6 @@ import { ReactiveContextController } from "#elements/controllers/ReactiveContext
import { BrandingContext } from "#elements/mixins/branding";
import { AuthentikConfigContext } from "#elements/mixins/config";
import { ConsoleLogger, Logger } from "#logger/browser";
import { Context, ContextType } from "@lit/context";
import { ReactiveController } from "lit";
@@ -23,8 +18,6 @@ import { ReactiveController } from "lit";
* The base interface element for the application.
*/
export abstract class Interface extends AKElement {
protected logger: Logger;
/**
* Private map of controllers to their registry keys.
*
@@ -33,17 +26,9 @@ export abstract class Interface extends AKElement {
*/
#registryKeys = new WeakMap<ReactiveController, ContextType<Context<unknown, unknown>>>();
/**
* The command palette instance. This must be inserted by the extending class,
* as the palette may depend on a context that is not available at the time of this class's construction.
*/
public readonly commandPalette: AKCommandPalette;
constructor() {
super();
this.logger = ConsoleLogger.prefix(this.tagName.toLowerCase());
const { config, brand, locale } = globalAK();
createUIThemeEffect(applyDocumentTheme);
@@ -53,8 +38,7 @@ export abstract class Interface extends AKElement {
this.addController(new BrandingContextController(this, brand), BrandingContext);
this.addController(new ModalOrchestrationController());
this.id = "interface-root";
this.commandPalette = this.ownerDocument.createElement("ak-command-palette");
this.dataset.testId = "interface-root";
}
public override addController(

View File

@@ -1,21 +1,13 @@
import { CURRENT_CLASS, EVENT_REFRESH } from "#common/constants";
import { AKElement } from "#elements/Base";
import {
CommandPaletteState,
PaletteCommandAction,
PaletteCommandDefinition,
} from "#elements/commands/shared";
import { intersectionObserver } from "#elements/decorators/intersection-observer";
import { getURLParams, updateURLParams } from "#elements/router/RouteMatch";
import Styles from "#elements/Tabs.css" with { type: "bundled-text" };
import { ifPresent } from "#elements/utils/attributes";
import { isFocusable } from "#elements/utils/focus";
import { capitalCase } from "change-case";
import { msg, str } from "@lit/localize";
import { CSSResult, html, LitElement, PropertyValues, TemplateResult } from "lit";
import { msg } from "@lit/localize";
import { CSSResult, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import { createRef, ref } from "lit/directives/ref.js";
@@ -38,59 +30,16 @@ export class Tabs extends AKElement {
@state()
protected tabs: ReadonlyMap<string, Element> = new Map();
/**
* Whether the tab is visible in the viewport.
*/
@intersectionObserver()
public visible = false;
#focusTargetRef = createRef<HTMLSlotElement>();
#observer: MutationObserver | null = null;
#commands = new CommandPaletteState<string>();
#updateTabs = (): void => {
this.tabs = new Map(
Array.from(this.querySelectorAll(":scope > [slot^='page-']"), (element) => {
return [element.getAttribute("slot") || "", element];
}),
);
requestAnimationFrame(this.#updateCommands);
};
#updateCommands = (): void => {
const commands: PaletteCommandDefinition<string>[] = [];
if (!this.visible) {
this.#commands.clear();
return;
}
const group = msg(str`Landmark: ${capitalCase(this.pageIdentifier)}`);
const prefix = msg("Switch to tab", { id: "command-palette.switch-to-tab" });
const action: PaletteCommandAction<string> = (slotName) => {
this.activateTab(slotName);
};
for (const [slotName, tabPanel] of this.tabs) {
if (this.activeTabName === slotName) {
continue;
}
const label = tabPanel.getAttribute("aria-label") || slotName;
commands.push({
label,
action,
group,
prefix,
details: slotName,
});
}
this.#commands.set(commands);
};
public override connectedCallback(): void {
@@ -129,18 +78,9 @@ export class Tabs extends AKElement {
public override disconnectedCallback(): void {
this.#observer?.disconnect();
this.#commands.clear();
super.disconnectedCallback();
}
public override updated(changedProperties: PropertyValues<this>): void {
super.updated(changedProperties);
if (changedProperties.has("visible")) {
this.#updateCommands();
}
}
public findActiveTabPanel(): Element | null {
return this.querySelector(`[slot='${this.activeTabName}']`);
}

View File

@@ -1,180 +0,0 @@
:host {
--ak-c-command-palette--PaddingBlock: calc(1em * var(--pf-global--LineHeight--sm));
--ak-c-command-palette--PaddingInline: calc(
2 * var(--pf-global--spacer--form-element) + var(--pf-global--spacer--sm)
);
--ak-c-command-palette--Translucency: 25%;
--ak-c-command-palette__group--BorderColor: var(
--pf-global--BackgroundColor--dark-transparent-200
);
--ak-fieldset--BorderColor: var(--pf-global--palette--purple-500);
--ak-c-command-palette__item--BackgroundColor: transparent;
--ak-c-command-palette__item--Color: var(--pf-global--palette--purple-700);
--ak-c-command-palette__item--Color: var(--pf-global--palette--blue-50);
--ak-c-command-palette__item--selected--BackgroundColor: var(--pf-global--palette--blue-400);
--ak-c-command-palette__item--hover--BackgroundColor: var(--pf-global--palette--purple-600);
--ak-c-command-palette__item--hover-selected--BackgroundColor: var(
--pf-global--palette--blue-300
);
--pf-global--Color--100: var(--pf-global--palette--purple-50);
@media (prefers-reduced-transparency: reduce) {
--ak-c-command-palette--Translucency: 0%;
}
will-change: opacity, text-decoration-color, background-color, color;
transform: translate3d(0, 0, 0); /* Fixes rendering artifacts. */
}
:host {
display: block;
min-height: min(10rem, 20dvh);
padding: var(--pf-global--spacer--sm);
padding-block-end: var(--pf-global--spacer--md);
color: var(--pf-global--Color--100);
}
[part="command-field"] {
border-bottom: 0.5px solid var(--pf-global--palette--black-600);
margin-block-end: var(--pf-global--spacer--xs);
padding-block-end: var(--pf-global--spacer--xs);
margin-inline: var(--pf-global--spacer--sm);
}
#command-input {
background: transparent;
display: block;
width: 100%;
font-size: var(--pf-global--FontSize--2xl);
padding-block: var(--pf-global--spacer--form-element);
padding-inline-start: calc(var(--ak-c-command-palette--PaddingInline) * 2);
padding-inline-end: var(--ak-c-command-palette--PaddingInline);
border: none;
outline: none;
&::placeholder {
color: var(--pf-global--palette--purple-100);
font-weight: 100;
font-family: var(--pf-global--FontFamily--heading--sans-serif);
}
}
[part="results-group"] {
border-width: 0.5px;
padding-inline: 0 !important;
padding-block-end: 0 !important;
legend {
padding-block: var(--pf-global--spacer--xs) !important;
}
&:not(:first-child) {
margin-block-start: var(--pf-global--spacer--md);
}
}
[part="results"] {
overflow-y: auto;
max-height: calc(100dvh - (1.75 * var(--ak-c-modal--MarginBlockStart)));
}
[part="results-list"] {
margin-block-start: calc(var(--pf-global--spacer--sm) * -1);
}
[part="command-item-label"] {
grid-row: label;
font-family: var(--pf-global--FontFamily--heading--sans-serif);
}
[part="command-item-prefix"] {
grid-area: prefix;
font-variant: all-small-caps;
font-weight: bold;
color: var(--pf-global--palette--gold-200);
}
[part="command-item-suffix"] {
grid-area: suffix;
font-variant: all-small-caps;
font-weight: bold;
display: flex;
color: var(--pf-global--palette--gold-200);
justify-content: end;
align-items: center;
}
[part="command-item-description"] {
grid-area: description;
}
[part="group-heading"] {
margin-block: var(--pf-global--spacer--sm);
margin-inline: var(--pf-global--spacer--sm);
}
.command-item {
&.selected {
--ak-c-command-palette__item--BackgroundColor: var(
--ak-c-command-palette__item--selected--BackgroundColor
);
--ak-c-command-palette__item--hover--BackgroundColor: var(
--ak-c-command-palette__item--hover-selected--BackgroundColor
);
--ak-c-command-palette__item--AccentColor: var(--ak-accent);
}
}
[part="command-button"] {
display: grid;
grid-template-areas:
"prefix prefix prefix suffix"
"icon label label suffix"
"icon description description suffix";
width: 100%;
background: transparent;
padding: var(--pf-global--spacer--md);
border: none;
column-gap: var(--pf-global--spacer--sm);
border-inline-start: 3px solid var(--ak-c-command-palette__item--AccentColor, transparent);
background-color: color-mix(
var(--ak-c-command-palette__item--BackgroundColor),
transparent var(--ak-c-command-palette--Translucency)
);
width: 100%;
border-radius: 0;
text-align: start;
color: var(--ak-c-command-palette__item--Color);
&:hover {
background-color: color-mix(
var(--ak-c-command-palette__item--hover--BackgroundColor),
transparent var(--ak-c-command-palette--Translucency)
);
}
}
[part="input-label"] {
cursor: pointer;
position: absolute;
inset-block-start: var(--ak-c-command-palette--PaddingBlock);
inset-inline-start: var(--ak-c-command-palette--PaddingInline);
z-index: 1;
pointer-events: none;
z-index: 1;
pointer-events: none;
.icon {
display: block;
height: var(--pf-global--icon--FontSize--lg);
fill: currentColor;
stroke: currentColor;
}
}

View File

@@ -1,540 +0,0 @@
import "#elements/EmptyState";
import { torusIndex } from "#common/collections";
import { PFSize } from "#common/enums";
import Styles from "#elements/commands/ak-command-palette-modal.css";
import { AKCommandChangeEvent } from "#elements/commands/events";
import { PaletteCommandDefinition } from "#elements/commands/shared";
import { listen } from "#elements/decorators/listen";
import { AKModal } from "#elements/modals/ak-modal";
import { asInvoker } from "#elements/modals/utils";
import { SlottedTemplateResult } from "#elements/types";
import { FocusTarget } from "#elements/utils/focus";
import { AboutModal } from "#admin/AdminInterface/AboutModal";
import Fuse from "fuse.js";
import { msg, str } from "@lit/localize";
import { html, PropertyValues } from "lit";
import { guard } from "lit-html/directives/guard.js";
import { createRef, ref } from "lit-html/directives/ref.js";
import { customElement, property, state } from "lit/decorators.js";
import { repeat } from "lit/directives/repeat.js";
function openDocsSearch(query: string) {
const url = new URL("/search", import.meta.env.AK_DOCS_URL);
url.searchParams.set("q", query);
window.open(url, "_ak_docs", "noopener,noreferrer");
}
function createCommonCommands(): PaletteCommandDefinition<unknown>[] {
return [
{
label: msg("Integrations"),
prefix: msg("View", { id: "command-palette.prefix.view" }),
action: () => window.open("https://integrations.goauthentik.io/", "_blank"),
group: msg("Documentation"),
},
{
label: msg("Release notes"),
action: () => window.open(import.meta.env.AK_DOCS_RELEASE_NOTES_URL, "_blank"),
prefix: msg("View", { id: "command-palette.prefix.view" }),
suffix: msg(str`New in ${import.meta.env.AK_VERSION}`, {
id: "command-palette.suffix.new-in",
}),
group: msg("authentik"),
},
{
label: msg("About authentik"),
action: AboutModal.open,
prefix: msg("View", { id: "command-palette.prefix.view" }),
group: msg("authentik"),
},
];
}
@customElement("ak-command-palette-modal")
export class AKCommandPaletteModal extends AKModal {
static openOnConnect = false;
static styles = [...AKModal.styles, Styles];
static open = asInvoker(AKCommandPaletteModal);
protected autofocusTarget = new FocusTarget<HTMLInputElement>();
protected formRef = createRef<HTMLFormElement>();
#scrollCommandFrameID = -1;
#autoFocusFrameID = -1;
// TODO: Fix form references.
declare form: null;
protected get value() {
return this.autofocusTarget.target?.value.trim() || "";
}
protected fuse = new Fuse<PaletteCommandDefinition>([], {
keys: [
// ---
{ name: "label", weight: 3 },
"description",
"group",
{
name: "keywords",
getFn: (command) => command.keywords?.join(" ") || "",
weight: 2,
},
],
findAllMatches: true,
includeScore: true,
shouldSort: true,
ignoreFieldNorm: true,
useExtendedSearch: true,
threshold: 0.3,
});
//#region Public Properties
@property({ type: Number, attribute: false, useDefault: true })
public selectionIndex = 1;
public get selectedCommand(): PaletteCommandDefinition | null {
if (this.selectionIndex === -1) {
return null;
}
return this.filteredCommands[this.selectionIndex] || null;
}
@property({ type: Number, attribute: false, useDefault: true })
public maxCount = 20;
@property({ type: Array, attribute: false, useDefault: true })
public filteredCommands: readonly PaletteCommandDefinition<unknown>[] = [];
/**
* A map of the currently filtered commands to their index in the flattened commands array,
* used to normalize the selection index while rendering groups.
*/
#filteredCommandsIndex = new Map<PaletteCommandDefinition<unknown>, number>();
/**
* A flattened array of all commands in the command palette, used for filtering and selection.
*/
#flattenedCommands: PaletteCommandDefinition<unknown>[] = [];
@state()
public commands = new Set<readonly PaletteCommandDefinition<unknown>[]>();
public override size = PFSize.Medium;
public override focus = this.autofocusTarget.focus;
//#region Public Methods
public setCommands = (
commands?: readonly PaletteCommandDefinition<unknown>[] | null,
previousCommands?: readonly PaletteCommandDefinition<unknown>[] | null,
) => {
if (previousCommands) {
this.commands.delete(previousCommands);
}
if (commands) {
this.commands.add(commands);
this.#flattenedCommands = Array.from(this.commands).reverse().flat();
}
const { target } = this.autofocusTarget;
if (target) {
target.value = "";
}
if (this.open && (commands || previousCommands)) {
this.requestUpdate("commands");
}
};
public scrollCommandIntoView = () => {
const id = `command-${this.selectionIndex}`;
const element = this.renderRoot.querySelector(`#${id}`);
if (!element) {
return;
}
const legend = element.closest("fieldset")?.querySelector("legend");
legend?.scrollIntoView({
behavior: "auto",
block: "nearest",
});
element.scrollIntoView({
behavior: "auto",
block: "nearest",
});
};
//#region Lifecycle
public override connectedCallback(): void {
super.connectedCallback();
this.addEventListener("focus", this.autofocusTarget.toEventListener());
requestAnimationFrame(() => {
this.setCommands([
{
label: msg("Documentation"),
action: () => openDocsSearch(this.value),
keywords: [msg("Docs"), msg("Readme"), msg("Help")],
prefix: msg("View", { id: "command-palette.prefix.view" }),
suffix: msg("New Tab", { id: "command-palette.suffix.view-docs" }),
group: msg("Documentation"),
},
...createCommonCommands(),
]);
});
}
public override updated(changedProperties: PropertyValues<this>): void {
super.updated(changedProperties);
if (changedProperties.has("commands")) {
this.fuse.setCollection(this.#flattenedCommands);
this.selectionIndex = 0;
this.synchronizeFilteredCommands();
}
if (changedProperties.has("open") && this.open) {
cancelAnimationFrame(this.#autoFocusFrameID);
this.#autoFocusFrameID = requestAnimationFrame(() => {
this.autofocusTarget.focus();
this.autofocusTarget.target?.select();
});
}
if (changedProperties.has("selectionIndex")) {
cancelAnimationFrame(this.#scrollCommandFrameID);
this.#scrollCommandFrameID = requestAnimationFrame(this.scrollCommandIntoView);
}
}
//#endregion
public synchronizeFilteredCommands = () => {
cancelAnimationFrame(this.#scrollCommandFrameID);
this.selectionIndex = 0;
const { value } = this;
if (value) {
const filteredCommands = this.fuse
.search(value, {
limit: this.maxCount,
})
.map((result) => result.item);
filteredCommands.push({
group: msg("Documentation"),
label: msg(str`Search the docs for "${value}"`),
prefix: msg("Open", { id: "command-palette.prefix.open" }),
suffix: msg("New Tab", { id: "command-palette.suffix.view-docs" }),
action: () => openDocsSearch(value),
});
this.filteredCommands = filteredCommands;
} else {
this.filteredCommands = this.#flattenedCommands.slice(0, this.maxCount);
}
this.#filteredCommandsIndex = new Map(
this.filteredCommands.map((command, index) => [command, index]),
);
this.#scrollCommandFrameID = requestAnimationFrame(this.scrollCommandIntoView);
};
public submit() {
const form = this.formRef.value;
if (!form) return;
const submitEvent = new SubmitEvent("submit", {
submitter: this,
bubbles: true,
composed: true,
cancelable: true,
});
form.dispatchEvent(submitEvent);
}
#submitListener = (event: SubmitEvent) => {
let commandIndex: number;
if (event.submitter instanceof HTMLElement && event.submitter.dataset.index) {
commandIndex = parseInt(event.submitter.dataset.index, 10);
} else {
commandIndex = this.selectionIndex;
}
const command = this.filteredCommands[commandIndex];
if (!command) return;
this.open = false;
command.action(command.details || null);
};
#commandClickListener = (event: MouseEvent) => {
const target = event.currentTarget as HTMLElement;
const index = parseInt(target.dataset.index!, 10);
if (isNaN(index)) return;
this.selectionIndex = index;
};
//#region Event Listeners
@listen(AKCommandChangeEvent, {
target: this,
})
protected commandChangeListener = (event: AKCommandChangeEvent) => {
this.setCommands(event.commands, event.previousCommands);
};
#keydownListener = (event: KeyboardEvent) => {
const visibleCommandsCount = this.filteredCommands.length;
if (!this.open) {
return;
}
if (event.key === "Enter" && this.form) {
this.submit();
return;
}
if (!visibleCommandsCount) {
return;
}
switch (event.key) {
case "ArrowDown":
event.preventDefault();
this.selectionIndex = torusIndex(visibleCommandsCount, this.selectionIndex + 1);
return;
case "ArrowUp":
event.preventDefault();
this.selectionIndex = torusIndex(visibleCommandsCount, this.selectionIndex - 1);
return;
case "Enter":
event.preventDefault();
this.submit();
return;
}
};
#focusListener = () => {
this.selectionIndex = this.selectionIndex === -1 ? 0 : this.selectionIndex;
this.synchronizeFilteredCommands();
};
//#region Rendering
protected override renderCloseButton(): SlottedTemplateResult {
return null;
}
protected renderCommands() {
const { selectionIndex, value, filteredCommands } = this;
return guard([filteredCommands, selectionIndex, value], () => {
const grouped = Object.groupBy(filteredCommands, (command) => command.group || "");
return html`<div
part="results"
role="listbox"
id="command-suggestions"
aria-label=${msg("Query suggestions")}
>
${repeat(
Object.entries(grouped),
(_, groupIdx) => `group-${groupIdx}`,
([groupLabel, commands], groupIdx) => html`
<fieldset part="results-group">
<legend
class="pf-c-content ${!groupLabel
? "sr-only more-contrast-only"
: ""}"
>
<h2>${groupLabel || msg("Ungrouped")}</h2>
</legend>
<ul
part="results-list"
data-group-index=${groupIdx}
role="presentation"
>
${repeat(
commands!,
(_, commandIdx) => `group-${groupIdx}-command-${commandIdx}`,
(command) => {
const absoluteIdx =
this.#filteredCommandsIndex.get(command) ?? -1;
const { label, prefix, suffix, description } = command;
const selected = selectionIndex === absoluteIdx;
return html`<li
role="presentation"
id="command-${absoluteIdx}"
aria-selected=${selected ? "true" : "false"}
class="command-item ${selected ? "selected" : ""}"
part="command-item"
>
<button
part="command-button"
type="submit"
formmethod="dialog"
data-index=${absoluteIdx}
@click=${this.#commandClickListener}
aria-labelledby="command-${absoluteIdx}-label"
aria-describedby="command-${absoluteIdx}-description"
>
${prefix
? html`<div
part="command-item-prefix"
id="command-${absoluteIdx}-prefix"
>
${prefix}
</div>`
: null}
<div
part="command-item-label"
id="command-${absoluteIdx}-label"
>
${label}
</div>
${suffix
? html`<div
part="command-item-suffix"
id="command-${absoluteIdx}-suffix"
>
${suffix}
</div>`
: null}
<div
part="command-item-description"
id="command-${absoluteIdx}-description"
>
${description || ""}
</div>
</button>
</li>`;
},
)}
</ul>
</fieldset>
`,
)}
</div>`;
});
}
protected override render() {
const { value, filteredCommands } = this;
return html`<form
${ref(this.formRef)}
method="dialog"
class="command-palette-form"
@submit=${this.#submitListener}
>
<div
class="input"
aria-expanded=${this.open ? "true" : "false"}
aria-autocomplete="list"
role="combobox"
aria-label=${msg("Command palette")}
aria-haspopup="listbox"
aria-activedescendant=${this.selectionIndex === -1
? ""
: `command-${this.selectionIndex}`}
>
<div part="command-field">
<label
part="input-label"
for="command-input"
@click=${this.show}
aria-label=${msg("Type a command...", {
id: "command-palette-placeholder",
desc: "Label for the command palette input",
})}
>
<svg
class="icon"
role="img"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 32 32"
>
<path
d="m29 27.586-7.552-7.552a11.018 11.018 0 1 0-1.414 1.414L27.586 29ZM4 13a9 9 0 1 1 9 9 9.01 9.01 0 0 1-9-9"
/>
</svg>
</label>
<input
${this.autofocusTarget.toRef()}
autofocus
id="command-input"
name="command"
aria-controls="command-suggestions"
type="search"
placeholder=${msg("What are you looking for?", {
id: "command-palette-placeholder-extended",
desc: "Placeholder for the command palette input",
})}
class="pf-c-control command-input"
autocomplete="off"
autocapitalize="off"
spellcheck="false"
@input=${this.synchronizeFilteredCommands}
@focus=${this.#focusListener}
@keydown=${this.#keydownListener}
/>
</div>
</div>
${this.renderCommands()}
${!value && !filteredCommands.length
? html`<ak-empty-state icon="pf-icon-module"
><span>${msg("No commands")}</span>
<div slot="body">${msg("No commands are currently available.")}</div>
</ak-empty-state>`
: null}
</form>`;
}
//#endregion
}
declare global {
interface HTMLElementTagNameMap {
"ak-command-palette-modal": AKCommandPaletteModal;
}
}

View File

@@ -1,27 +0,0 @@
ak-command-palette {
display: block;
}
.ak-c-modal:has(ak-command-palette-modal) {
overflow: visible;
--ak-c-modal--MarginBlockStart: 25dvh;
--ak-c-modal--BackgroundColor: var(--pf-global--palette--purple-700);
--ak-c-modal--BorderColor: var(--pf-global--BackgroundColor--dark-300);
--ak-c-modal__backdrop--active--BackdropFilter: blur(1px);
--ak-c-modal__backdrop--active--BackgroundColor: hsla(0, 0%, 0%, 0.25);
--ak-c-command-palette--Translucency: 5%;
--ak-c-command-palette--BackdropFilter: blur(4px);
border-radius: var(--pf-global--BorderRadius--sm);
backdrop-filter: var(--ak-c-command-palette--BackdropFilter);
background-color: color-mix(
var(--ak-c-modal--BackgroundColor),
transparent var(--ak-c-command-palette--Translucency)
);
@media (prefers-reduced-transparency: reduce) {
--ak-c-command-palette--Translucency: 0%;
--ak-c-command-palette--BackdropFilter: blur(0);
}
}

View File

@@ -1,66 +0,0 @@
import "#elements/commands/ak-command-palette-modal";
import HostStyles from "./ak-command-palette.css";
import { AKElement } from "#elements/Base";
import { AKCommandPaletteModal } from "#elements/commands/ak-command-palette-modal";
import { listen } from "#elements/decorators/listen";
import { ConsoleLogger, Logger } from "#logger/browser";
import { PropertyValues } from "lit";
import { customElement } from "lit/decorators.js";
@customElement("ak-command-palette")
export class AKCommandPalette extends AKElement {
public static hostStyles = [HostStyles];
protected createRenderRoot(): HTMLElement | DocumentFragment {
return this;
}
protected logger: Logger;
public readonly dialog: HTMLDialogElement;
public readonly modal: AKCommandPaletteModal;
constructor() {
super();
this.logger = ConsoleLogger.prefix(this.tagName.toLowerCase());
this.dialog = this.ownerDocument.createElement("dialog");
this.modal = this.ownerDocument.createElement("ak-command-palette-modal");
this.dialog.appendChild(this.modal);
}
@listen("keydown", { passive: false, capture: true })
protected keydownListener = (event: KeyboardEvent) => {
if (event.key !== "k" || (!event.metaKey && !event.ctrlKey)) {
return;
}
this.logger.info("Toggling command palette");
event.preventDefault();
this.modal.open = !this.modal.open;
};
protected firstUpdated(_changedProperties: PropertyValues): void {
super.firstUpdated(_changedProperties);
// DEBUGGING
this.modal.open = true;
}
protected override render() {
return this.dialog;
}
}
declare global {
interface HTMLElementTagNameMap {
"ak-command-palette": AKCommandPalette;
}
}

View File

@@ -1,28 +0,0 @@
import type { PaletteCommandDefinition } from "#elements/commands/shared";
/**
* Event dispatched when the available commands in the command palette change.
* This is used by the command palette to update the list of available commands.
*/
export class AKCommandChangeEvent<D = unknown> extends Event {
public static readonly eventName = "ak-command-change";
public readonly commands: readonly PaletteCommandDefinition<D>[];
public readonly previousCommands: readonly PaletteCommandDefinition<D>[] | null;
constructor(
commands: PaletteCommandDefinition<D>[],
previousCommands?: PaletteCommandDefinition<D>[] | null,
) {
super(AKCommandChangeEvent.eventName, { bubbles: true, composed: true });
this.commands = commands;
this.previousCommands = previousCommands ?? null;
}
}
declare global {
interface WindowEventMap {
[AKCommandChangeEvent.eventName]: AKCommandChangeEvent;
}
}

View File

@@ -1,41 +0,0 @@
import { AKCommandChangeEvent } from "#elements/commands/events";
import { SlottedTemplateResult } from "#elements/types";
export type PaletteCommandAction<D = unknown> = (data: D) => unknown | Promise<unknown>;
export interface PaletteCommandDefinition<D = unknown> {
label: SlottedTemplateResult;
keywords?: string[];
prefix?: SlottedTemplateResult;
suffix?: SlottedTemplateResult;
description?: SlottedTemplateResult;
group?: string;
details?: D;
action: PaletteCommandAction<D>;
}
export interface CommandPaletteStateInit<D = unknown> {
commands?: PaletteCommandDefinition<D>[] | null;
target?: EventTarget;
}
export class CommandPaletteState<D = unknown> {
#commands: PaletteCommandDefinition<D>[] | null = null;
#target: EventTarget;
constructor({ commands = null, target = window }: CommandPaletteStateInit<D> = {}) {
this.#commands = commands;
this.#target = target ?? window;
}
public set(nextCommands: PaletteCommandDefinition<D>[] | null): void {
const previousCommands = this.#commands;
this.#commands = nextCommands;
this.#target.dispatchEvent(new AKCommandChangeEvent(nextCommands ?? [], previousCommands));
}
public clear(): void {
this.set(null);
}
}

View File

@@ -1,30 +1,20 @@
import { DEFAULT_CONFIG } from "#common/api/config";
import { type APIResult, isAPIResultReady } from "#common/api/responses";
import { globalAK } from "#common/global";
import { applyThemeChoice, formatColorScheme } from "#common/theme";
import { createUIConfig, DefaultUIConfig } from "#common/ui/config";
import { autoDetectLanguage } from "#common/ui/locale/utils";
import { me } from "#common/users";
import { CommandPaletteState, PaletteCommandDefinition } from "#elements/commands/shared";
import { ReactiveContextController } from "#elements/controllers/ReactiveContextController";
import { AKConfigMixin, kAKConfig } from "#elements/mixins/config";
import { kAKLocale, type LocaleMixin } from "#elements/mixins/locale";
import {
canAccessAdmin,
SessionContext,
SessionMixin,
UIConfigContext,
} from "#elements/mixins/session";
import { AKDrawerChangeEvent } from "#elements/notifications/events";
import { SessionContext, SessionMixin, UIConfigContext } from "#elements/mixins/session";
import type { ReactiveElementHost } from "#elements/types";
import { CoreApi, SessionUser } from "@goauthentik/api";
import { SessionUser } from "@goauthentik/api";
import { setUser } from "@sentry/browser";
import { ContextProvider } from "@lit/context";
import { msg } from "@lit/localize";
/**
* A controller that provides the session information to the element.
@@ -62,128 +52,38 @@ export class SessionContextController extends ReactiveContextController<APIResul
return me(requestInit);
}
#refreshCommandsFrameID = -1;
#commands = new CommandPaletteState({
target: this.host,
});
protected doRefresh(session: APIResult<SessionUser>): void {
this.context.setValue(session);
this.host.session = session;
if (!isAPIResultReady(session)) return;
if (isAPIResultReady(session)) {
const localeHint: string | undefined = session.user.settings.locale;
const localeHint: string | undefined = session.user.settings.locale;
if (localeHint) {
const locale = autoDetectLanguage(localeHint);
this.logger.info(`Activating user's configured locale '${locale}'`);
this.host[kAKLocale]?.setLocale(locale);
}
if (localeHint) {
const locale = autoDetectLanguage(localeHint);
this.logger.info(`Activating user's configured locale '${locale}'`);
this.host[kAKLocale]?.setLocale(locale);
const { settings = {} } = session.user || {};
const nextUIConfig = createUIConfig(settings);
this.uiConfigContext.setValue(nextUIConfig);
this.host.uiConfig = nextUIConfig;
const colorScheme = formatColorScheme(nextUIConfig.theme.base);
applyThemeChoice(colorScheme, this.host.ownerDocument);
const config = this.host[kAKConfig];
if (config?.errorReporting.sendPii) {
this.logger.info("Sentry with PII enabled.");
setUser({ email: session.user.email });
}
}
const { settings = {} } = session.user || {};
const nextUIConfig = createUIConfig(settings);
this.uiConfigContext.setValue(nextUIConfig);
this.host.uiConfig = nextUIConfig;
const colorScheme = formatColorScheme(nextUIConfig.theme.base);
applyThemeChoice(colorScheme, this.host.ownerDocument);
const config = this.host[kAKConfig];
if (config?.errorReporting.sendPii) {
this.logger.info("Sentry with PII enabled.");
setUser({ email: session.user.email });
}
this.#refreshCommandsFrameID = requestAnimationFrame(this.#refreshCommands);
}
#refreshCommands = (): void => {
const session = this.context.value;
if (!isAPIResultReady(session)) {
this.#commands.clear();
return;
}
const base = globalAK().api.base;
const group = msg("Session");
const commands: PaletteCommandDefinition[] = [
{
label: msg("Sign out"),
suffix: msg("Reloads page", { id: "command-palette.prefix.reloads-page" }),
keywords: [msg("Logout"), msg("Log off"), msg("Sign off")],
group,
action: () => {
window.location.assign(`${base}flows/-/default/invalidation/`);
},
},
{
label: msg("User settings"),
prefix: msg("Navigate to", { id: "command-palette.prefix.navigate" }),
group,
action: () => {
window.location.assign(`${base}if/user/#/settings`);
},
},
];
const { notificationDrawer, apiDrawer } = this.host.uiConfig?.enabledFeatures ?? {};
const drawerGroup = msg("Interface");
if (apiDrawer) {
commands.push({
label: msg("API requests drawer", {
id: "command-palette.label.api-requests-drawer",
}),
prefix: msg("Toggle", { id: "command-palette.prefix.toggle" }),
group: drawerGroup,
action: AKDrawerChangeEvent.dispatchAPIToggle,
});
}
if (notificationDrawer) {
commands.push({
label: msg("Notifications drawer", {
id: "command-palette.label.notifications-drawer",
}),
prefix: msg("Toggle", { id: "command-palette.prefix.toggle" }),
group: drawerGroup,
action: AKDrawerChangeEvent.dispatchNotificationsToggle,
});
}
if (canAccessAdmin(session.user)) {
commands.push({
label: msg("Admin interface"),
prefix: msg("Navigate to", { id: "command-palette.prefix.navigate" }),
group,
action: () => {
window.location.assign(`${base}if/admin/`);
},
});
}
if (session.original) {
commands.push({
label: msg("Stop impersonation"),
suffix: msg("Reloads page", { id: "command-palette.prefix.reloads-page" }),
group,
action: async () => {
await new CoreApi(DEFAULT_CONFIG).coreUsersImpersonateEndRetrieve();
window.location.reload();
},
});
}
this.#commands.set(commands);
};
public override hostConnected() {
this.logger.debug("Host connected, refreshing session");
this.refresh();
@@ -191,7 +91,6 @@ export class SessionContextController extends ReactiveContextController<APIResul
public override hostDisconnected() {
this.context.clearCallbacks();
cancelAnimationFrame(this.#refreshCommandsFrameID);
super.hostDisconnected();
}

View File

@@ -239,11 +239,11 @@ export abstract class Form<T = Record<string, unknown>> extends AKElement {
@property({ type: String })
public autocomplete?: Exclude<AutoFillBase, "">;
@property({ type: String, useDefault: true })
public headline?: string | null = null;
@property({ type: String })
public headline?: string;
@property({ type: String, attribute: "action-label", useDefault: true })
public actionLabel: string | null = null;
@property({ type: String, attribute: "action-label" })
public actionLabel?: string;
//#endregion
@@ -254,9 +254,6 @@ export abstract class Form<T = Record<string, unknown>> extends AKElement {
@state()
protected nonFieldErrors: readonly string[] | null = null;
protected entitySingular?: string;
protected entityPlural?: string;
static styles: CSSResult[] = [
PFCard,
PFButton,
@@ -320,20 +317,6 @@ export abstract class Form<T = Record<string, unknown>> extends AKElement {
};
}
/**
* An overridable method for formatting the form headline.
*/
protected formatHeadline(headline = this.headline): string {
return headline || "";
}
/**
* An overridable method for formatting the submit button label.
*/
protected formatSubmitLabel(actionLabel = this.actionLabel): string {
return actionLabel || msg("Submit");
}
//#region Public methods
public reset(): void {
@@ -529,22 +512,20 @@ export abstract class Form<T = Record<string, unknown>> extends AKElement {
}
/**
* An overridable method for rendering the form header.
* An overridable method for rendering the form heading.
*
* @remarks
* If this form is slotted, such as in a modal, this method will not render anything,
* allowing the slot parent to provide the header in a more visually appropriate manner.
* allowing the slot parent to provide the heading in a more visually appropriate manner.
*/
public renderHeader(force?: boolean): SlottedTemplateResult {
const { assignedSlot, headline } = this;
return guard([force, assignedSlot, headline], () => {
if (!force && assignedSlot && !assignedSlot.name) {
protected renderHeading(): SlottedTemplateResult {
return guard([this.assignedSlot, this.headline], () => {
if (this.assignedSlot) {
return nothing;
}
return html`<header>
<h1 class="pf-c-title pf-m-2xl">${this.formatHeadline(headline)}</h1>
<h1 class="pf-c-title pf-m-2xl">${this.headline}</h1>
</header>`;
});
}
@@ -556,33 +537,23 @@ export abstract class Form<T = Record<string, unknown>> extends AKElement {
* If this form is slotted, such as in a modal, this method will not render anything,
* allowing the slot parent to provide the actions in a more visually appropriate manner.
*/
public renderActions(force?: boolean): SlottedTemplateResult {
const { assignedSlot, actionLabel } = this;
return guard([force, assignedSlot, actionLabel], () => {
if (!force && assignedSlot && !assignedSlot.name) {
protected renderActions(): SlottedTemplateResult {
return guard([this.assignedSlot], () => {
if (this.assignedSlot) {
return nothing;
}
return html`<fieldset part="form-actions" class="pf-c-card__footer">
<legend class="sr-only">${msg("Form actions")}</legend>
<button
type="button"
type="submit"
form="form"
class="pf-c-button pf-m-primary"
@click=${(event: Event) => {
this.submit(
new SubmitEvent("submit", {
submitter: event.currentTarget as HTMLButtonElement,
cancelable: true,
bubbles: true,
composed: true,
}),
);
}}
part="submit-button"
formmethod="dialog"
aria-description=${msg("Submit action")}
>
${this.formatSubmitLabel(actionLabel)}
${this.actionLabel || msg("Submit")}
</button>
</fieldset>`;
});
@@ -592,12 +563,8 @@ export abstract class Form<T = Record<string, unknown>> extends AKElement {
* An overridable method for rendering the form when it is visible.
*/
protected renderVisible(): SlottedTemplateResult {
return [
this.renderHeader(),
this.renderNonFieldErrors(),
this.renderFormWrapper(),
this.renderActions(),
];
return html`${this.renderHeading()}${this.renderNonFieldErrors()}
${this.renderFormWrapper()}${this.renderActions()}`;
}
protected override render(): SlottedTemplateResult {

View File

@@ -8,7 +8,6 @@ import { SlottedTemplateResult } from "#elements/types";
import { ConsoleLogger } from "#logger/browser";
import { msg, str } from "@lit/localize";
import { html } from "lit";
import { property } from "lit/decorators.js";
@@ -110,22 +109,6 @@ export abstract class ModelForm<
});
};
protected override formatSubmitLabel(): string {
return this.#instancePk ? msg("Update") : msg("Create");
}
protected override formatHeadline(): string {
const verb = this.#instancePk ? msg("Edit") : msg("New");
const noun = this.entitySingular;
if (!noun) return verb;
return msg(str`${verb} ${noun}`, {
id: "model-form.headline",
desc: "The headline for a form that creates or updates a model instance.",
});
}
public override reset(): void {
super.reset();

View File

@@ -1,5 +1,4 @@
import { randomId } from "#elements/utils/randomId";
import { resolveInterface } from "#elements/utils/render-roots";
import { autoUpdate, computePosition, flip, hide } from "@floating-ui/dom";
@@ -69,17 +68,13 @@ export class Portal extends LitElement implements IPortal {
this.setAttribute("data-ouia-component-type", "ak-portal");
this.setAttribute("data-ouia-component-id", this.getAttribute("id") || randomId());
this.dropdownContainer = this.ownerDocument.createElement("div");
this.dropdownContainer = document.createElement("div");
this.dropdownContainer.dataset.managedBy = "ak-portal";
if (this.name) {
this.dropdownContainer.dataset.managedFor = this.name;
}
const interfaceElement = resolveInterface();
const container =
interfaceElement.renderRoot.querySelector("dialog") || this.ownerDocument.body;
container.append(this.dropdownContainer);
document.body.append(this.dropdownContainer);
if (!this.anchor) {
throw new Error("Tether entrance initialized incorrectly: missing anchor");

View File

@@ -229,13 +229,11 @@ export class SearchSelectView extends AKElement implements ISearchSelectView {
this.setAttribute("data-ouia-component-safe", "true");
}
public override firstUpdated(changed: PropertyValues<this>) {
super.firstUpdated(changed);
public override firstUpdated() {
// Route around Lit's scheduling algorithm complaining about re-renders
requestAnimationFrame(() => {
window.setTimeout(() => {
this.inputRefIsAvailable = Boolean(this.#inputRef?.value);
});
}, 0);
}
connectedCallback() {

View File

@@ -1,496 +0,0 @@
import { PFSize } from "#common/enums";
import { AKElement } from "#elements/Base";
import { AKFormSubmittedEvent } from "#elements/forms/events";
import { Form } from "#elements/forms/Form";
import Styles from "#elements/modals/styles.css";
import { SlottedTemplateResult } from "#elements/types";
import { ConsoleLogger, Logger } from "#logger/browser";
import { msg } from "@lit/localize";
import { CSSResult, html, PropertyValues } from "lit";
import { createRef } from "lit-html/directives/ref.js";
import { customElement, property, state } from "lit/decorators.js";
import { guard } from "lit/directives/guard.js";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFCard from "@patternfly/patternfly/components/Card/card.css";
import PFContent from "@patternfly/patternfly/components/Content/content.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
import PFPage from "@patternfly/patternfly/components/Page/page.css";
import PFTitle from "@patternfly/patternfly/components/Title/title.css";
@customElement("ak-modal")
export class AKModal extends AKElement {
static shadowRootOptions: ShadowRootInit = {
...AKElement.shadowRootOptions,
delegatesFocus: true,
};
/**
* Whether the modal should open the parent dialog element when it is connected to the DOM.
*/
static openOnConnect = true;
public static styles: CSSResult[] = [
PFButton,
PFForm,
PFTitle,
PFFormControl,
PFPage,
PFCard,
PFContent,
Styles,
];
#hostResizeObserver: ResizeObserver;
//#region Protected Properties
protected logger: Logger;
/**
* An optional Lit ref which can automatically synchronize the modal's height with the element's height.
*/
protected scrollContainerRef = createRef<HTMLElement>();
declare parentElement: HTMLDialogElement | null;
//#region Public Properties
@property({ type: String, useDefault: true })
public headline: string | null = null;
/**
* Whether the parent dialog element is currently open.
*/
public get open(): boolean {
return this.parentElement?.open ?? false;
}
@property({ type: Boolean, attribute: false, reflect: false })
public set open(value: boolean) {
if (value) {
this.show();
} else {
this.close();
}
}
@property({ type: String })
public size: PFSize = PFSize.Large;
protected defaultSlot: HTMLSlotElement;
@state()
protected form: Form | null = null;
//#endregion
//#region Public methods
/**
* Show the modal, rendering its contents.
*/
public show() {
const dialogElement = this.parentElement;
if (!dialogElement) {
this.logger.debug("No parentElement, cannot show modal", this);
return;
}
dialogElement.addEventListener("transitionend", this.fadeInListener, {
once: true,
passive: true,
});
dialogElement.showModal();
dialogElement.classList.add("fade-in");
}
/**
* Close the modal, fading it out and then removing it from the DOM,
* optionally with a return value.
*
* @param returnValue The return value for the dialog, if any.
*/
public close(returnValue?: string) {
const dialogElement = this.parentElement;
if (!dialogElement) {
this.logger.debug("No parentElement, cannot close modal", this);
return;
}
dialogElement.addEventListener(
"transitionend",
(event) => this.delegateClose(event, returnValue),
{ once: true, passive: true },
);
dialogElement.classList.remove("fade-in", "fade-in-complete");
}
//#endregion
//#region Event listeners
/**
* A stable reference to the dialog's open event listener.
*
* This is useful for simplifying adding and removing event listeners.
*/
protected showListener = () => {
this.show();
};
#heightSyncFrameID = -1;
/**
* A map of observed elements to their expected sizes,
* used to prevent unnecessary height synchronizations.
*/
#expectedSizes = new WeakMap<Element, number>();
protected lastScrollHeight = 0;
protected synchronizeHeight: ResizeObserverCallback = ([entry]) => {
this.#heightSyncFrameID = requestAnimationFrame(() => {
const dialogElement = this.parentElement;
if (!dialogElement || !dialogElement.open) {
return;
}
const expectedSize = this.#expectedSizes.get(entry.target);
const blockSize = Math.ceil(entry.borderBoxSize[0].blockSize);
const desiredSize = Math.max(entry.target.scrollHeight, blockSize);
if (desiredSize === expectedSize) {
return;
}
dialogElement.style.height = desiredSize + "px";
this.#expectedSizes.set(entry.target, desiredSize);
});
};
#heightResetAnimationFrameID = -1;
protected resetHeight = () => {
cancelAnimationFrame(this.#heightResetAnimationFrameID);
cancelAnimationFrame(this.#heightSyncFrameID);
this.#heightResetAnimationFrameID = requestAnimationFrame(() => {
if (this.parentElement) {
this.parentElement.style.height = "";
}
this.#heightResetAnimationFrameID = requestAnimationFrame(() => {
const scrollContainer = this.scrollContainerRef.value || this;
const scrollHeight = scrollContainer.scrollHeight;
this.lastScrollHeight = scrollHeight;
});
});
};
protected fadeInListener = async (event: TransitionEvent) => {
this.logger.debug("fade-in complete", event);
this.removeEventListener("transitionend", this.fadeInListener);
const dialogElement = this.parentElement;
if (!dialogElement) {
this.logger.debug("Skipping height synchronization, no dialog element", this);
return;
}
dialogElement.classList.add("fade-in-complete");
dialogElement.style.height = dialogElement.clientHeight + "px";
const scrollContainer = this.scrollContainerRef.value || this;
this.#hostResizeObserver.observe(scrollContainer);
window.addEventListener("resize", this.resetHeight, {
passive: true,
});
};
#closing = false;
/**
* Delegate the close action to the parent dialog element,
* ensuring that the correct event listeners are triggered and the modal is properly closed.
*/
protected delegateClose(event: TransitionEvent, returnValue?: string) {
if (this.#closing) {
return;
}
this.logger.debug("Closing", event);
this.#closing = true;
this.parentElement?.close(returnValue);
requestAnimationFrame(() => {
this.#closing = false;
this.#hostResizeObserver.unobserve(this);
window.removeEventListener("resize", this.resetHeight);
this.resetHeight();
});
}
/**
* A stable reference to the dialog's close event listener.
*
* This is useful for simplifying adding and removing event listeners.
*/
protected closeListener = () => {
this.close();
};
/**
* A stable reference to the dialog's backdrop click event listener.
*
* @remarks
* Note that if the browser supports {@linkcode HTMLDialogElement}'s `closeBy` property,
* the backdrop click may trigger a "cancel" event instead of a "click" event.
*/
protected backdropClickListener = (event: Event) => {
if (event.target === this.parentElement) {
this.close();
}
};
/**
* A stable reference to the dialog's cancel event listener.
*
* This is useful for simplifying adding and removing event listeners.
*/
protected cancelListener = (event: Event) => {
if (!this.parentElement) {
return;
}
event.preventDefault();
this.close();
};
//#endregion
//#region Lifecycle
/**
* A bound render method that can be safely passed as a callback without losing the correct `this` context.
*
* @remarks
* This allows the implementing class to use `render`, reducing the mental
* overhead of which method should be used for rendering content.
*/
protected renderContent: () => unknown;
public constructor() {
super();
this.logger = ConsoleLogger.prefix(this.tagName.toLowerCase());
this.renderContent = this.render.bind(this);
this.render = this.renderInternal;
this.#hostResizeObserver = new ResizeObserver(this.synchronizeHeight);
this.defaultSlot = this.ownerDocument.createElement("slot");
}
public override connectedCallback(): void {
super.connectedCallback();
if (!this.parentElement) {
this.logger.debug("Skipping connectedCallback, no parentElement", this);
return;
}
if (!(this.parentElement instanceof HTMLDialogElement)) {
throw new TypeError(
`authentik/modal: ${this.tagName.toLowerCase()} must be placed inside a <dialog> element.`,
);
}
const tagName = this.tagName.toLowerCase();
this.parentElement.dataset.akModal = tagName;
this.parentElement.classList.add("ak-c-modal", this.size);
// eslint-disable-next-line wc/no-self-class
this.classList.add("ak-c-modal__content");
this.parentElement.addEventListener("cancel", this.cancelListener);
this.parentElement.addEventListener("click", this.backdropClickListener, { passive: true });
this.addEventListener(AKFormSubmittedEvent.eventName, this.closeListener);
const { openOnConnect } = this.constructor as typeof AKModal;
if (openOnConnect) {
this.show();
}
}
public override disconnectedCallback(): void {
super.disconnectedCallback();
this.#hostResizeObserver.disconnect();
window.removeEventListener("resize", this.resetHeight);
}
public override updated(changedProperties: PropertyValues<this>): void {
super.updated(changedProperties);
const assignedElements = this.defaultSlot.assignedElements({ flatten: true });
const form =
assignedElements.find((element): element is Form => element instanceof Form) ?? null;
if (form && form !== this.form) {
this.form = form;
this.form.viewportCheck = false;
}
const dialogElement = this.parentElement;
if (dialogElement && dialogElement.open) {
requestAnimationFrame(() => {
const scrollContainer = this.scrollContainerRef.value || this;
const scrollHeight = scrollContainer.scrollHeight;
if (scrollHeight !== this.lastScrollHeight) {
this.resetHeight();
}
});
}
}
//#endregion
//#region Render
/**
* An overridable method that determines whether the modal content should be rendered.
*
* By default, the modal content is only rendered when the modal is open,
* to avoid unnecessary rendering and potential issues with elements that
* require being in the DOM to function properly (e.g., autofocus).
*/
protected shouldRenderModalContent(): boolean {
return this.open;
}
/**
* The internal render method, including the close button and header, which are common to all modals.
*/
protected renderInternal() {
if (!this.shouldRenderModalContent()) {
return super.render();
}
return [
this.renderCloseButton(),
this.renderHeader(),
this.renderContent(),
this.renderActions(),
];
}
protected renderCloseButton(): SlottedTemplateResult {
return html`<button
@click=${this.closeListener}
class="pf-c-button pf-m-plain"
type="button"
aria-label=${msg("Close dialog")}
>
<i class="fas fa-times" aria-hidden="true"></i>
</button>`;
}
/**
* Render the modal header.
*
* This method may be overridden to customize the modal header.
*
* @protected
* @abstract
*/
protected renderHeader(): SlottedTemplateResult {
const { headline, form } = this;
const hasHeaderSlot = this.hasSlotted("header");
return guard([headline, hasHeaderSlot, form], () => {
if (!headline && !hasHeaderSlot && !form) {
return null;
}
return html`<header class="ak-c-modal__header">
<div class="ak-c-modal__title">
<h1 class="ak-c-modal__title-text" id="modal-title">
${this.headline}
<slot name="header"></slot>
${form ? form.renderHeader(true) : null}
</h1>
</div>
</header>`;
});
}
/**
* Render the modal actions.
*
* This method may be overridden to customize the modal actions.
*
* @protected
* @abstract
*/
protected renderActions(): SlottedTemplateResult {
const { form } = this;
const hasActionsSlot = this.hasSlotted("actions");
return guard([hasActionsSlot, form], () => {
if (!hasActionsSlot && !form) {
return null;
}
return html`<footer class="ak-c-modal__footer">
<slot name="actions"></slot>
${form ? form.renderActions(true) : null}
</footer>`;
});
}
/**
* Render the modal content.
*
* Note that this method is only called when the modal is open.
*
* @protected
* @abstract
*/
protected render(): unknown {
return html`<div class="ak-c-modal__body">${this.defaultSlot}</div>`;
}
//#endregion
}
declare global {
interface HTMLElementTagNameMap {
"ak-modal": AKModal;
}
}

View File

@@ -1,144 +0,0 @@
:host {
display: block;
}
/* #region Buttons */
:host > .pf-c-button {
position: absolute;
top: var(--ak-c-modal--c-button--Top);
right: var(--ak-c-modal--c-button--Right);
}
:host > .pf-c-button + .ak-c-modal__header {
margin-right: var(--ak-c-modal--c-button--sibling--MarginRight);
}
/* #endregion */
/* #region Header */
.ak-c-modal__header {
display: flex;
flex-direction: column;
flex-shrink: 0;
padding-top: var(--ak-c-modal__header--PaddingTop);
padding-bottom: var(--ak-c-modal__header--PaddingBottom);
padding-right: var(--ak-c-modal__header--PaddingRight);
padding-left: var(--ak-c-modal__header--PaddingLeft);
}
.ak-c-modal__header.pf-m-help {
display: flex;
flex-direction: row;
}
.ak-c-modal__header:last-child {
padding-bottom: var(--ak-c-modal__header--last-child--PaddingBottom);
}
.ak-c-modal__header + .ak-c-modal__body,
.ak-c-modal__header + slot + .ak-c-modal__body {
/* --ak-c-modal__body--PaddingTop: var(--ak-c-modal__header--body--PaddingTop); */
--ak-c-modal__body--PaddingTop: 0;
}
.ak-c-modal__header-main {
flex-grow: 1;
min-width: 0;
}
/* #endregion */
/* #region Title */
.ak-c-modal__title,
.ak-c-modal__title-text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.ak-c-modal__title {
flex: 0 0 auto;
font-family: var(--ak-c-modal__title--FontFamily);
font-size: var(--ak-c-modal__title--FontSize);
line-height: var(--ak-c-modal__title--LineHeight);
}
.ak-c-modal__title.pf-m-icon {
display: flex;
}
.ak-c-modal__title-icon {
margin-right: var(--ak-c-modal__title-icon--MarginRight);
color: var(--ak-c-modal__title-icon--Color);
}
.ak-c-modal__description {
padding-top: var(--ak-c-modal__description--PaddingTop);
}
/* #endregion */
/* #region Body */
.ak-c-modal__body {
flex: 1 1 auto;
max-height: var(--ak-c-modal__body--MaxHeight);
min-height: var(--ak-c-modal__body--MinHeight);
padding-top: var(--ak-c-modal__body--PaddingTop);
padding-right: var(--ak-c-modal__body--PaddingRight);
padding-left: var(--ak-c-modal__body--PaddingLeft);
overflow-x: hidden;
overflow-y: auto;
overscroll-behavior: contain;
word-break: break-word;
-webkit-overflow-scrolling: touch;
max-height: calc(
75dvh - 2rem - var(--ak-c-modal__header--PaddingTop) -
var(--ak-c-modal__footer--PaddingBottom)
);
}
.ak-c-modal__body:last-child {
padding-bottom: var(--ak-c-modal__body--last-child--PaddingBottom);
}
.ak-c-modal__footer {
display: flex;
flex: 0 0 auto;
align-items: center;
padding-top: var(--ak-c-modal__footer--PaddingTop);
padding-right: var(--ak-c-modal__footer--PaddingRight);
padding-bottom: var(--ak-c-modal__footer--PaddingBottom);
padding-left: var(--ak-c-modal__footer--PaddingLeft);
gap: var(--pf-global--spacer--xs);
@media screen and (min-width: 576px) {
gap: var(--pf-global--spacer--sm);
}
}
/* #endregion */
/* #region Footer */
fieldset.ak-c-modal__footer {
--ak-fieldset__legend--PaddingInlineBase: var(--pf-global--spacer--md);
padding-block: calc(var(--ak-fieldset__legend--PaddingInlineBase) / 2);
border-inline: none;
border-block-end: none;
--ak-c-modal__footer--c-button--sm--MarginRight: var(
--ak-c-modal__footer--c-button--MarginRight
);
& > ak-spinner-button:not(:last-child) {
margin-right: var(--ak-c-modal__footer--c-button--MarginRight);
}
}
/* #endregion */

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