mirror of
https://github.com/goauthentik/authentik
synced 2026-05-05 22:52:42 +02:00
Compare commits
23 Commits
sdko/lit-a
...
docs/invit
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
731163200a | ||
|
|
a8db2882ec | ||
|
|
befc15ad92 | ||
|
|
2b48c27760 | ||
|
|
6be7b2f7b7 | ||
|
|
7cffbb4d07 | ||
|
|
5d629bec9b | ||
|
|
5357f42029 | ||
|
|
716bc6e136 | ||
|
|
60355fdf80 | ||
|
|
828a380569 | ||
|
|
b04f8a6177 | ||
|
|
ff190847f2 | ||
|
|
a7339c7f87 | ||
|
|
38ae472f6c | ||
|
|
7d0656c6fa | ||
|
|
0bbe415b5b | ||
|
|
e52c1b2bdc | ||
|
|
5064167f28 | ||
|
|
bca0f51b53 | ||
|
|
67c197e5a5 | ||
|
|
32b17da699 | ||
|
|
c75eed630a |
2
.github/actions/setup/action.yml
vendored
2
.github/actions/setup/action.yml
vendored
@@ -64,7 +64,7 @@ runs:
|
||||
rustflags: ""
|
||||
- name: Setup rust dependencies
|
||||
if: ${{ contains(inputs.dependencies, 'rust') }}
|
||||
uses: taiki-e/install-action@51cd0b8c0499559d9a4d75c0f5c67bec3a894ec8 # v2
|
||||
uses: taiki-e/install-action@b5fddbb5361bce8a06fb168c9d403a6cc552b084 # v2
|
||||
with:
|
||||
tool: cargo-deny cargo-machete cargo-llvm-cov nextest
|
||||
- name: Setup node (web)
|
||||
|
||||
67
Cargo.lock
generated
67
Cargo.lock
generated
@@ -1003,6 +1003,17 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "evmap"
|
||||
version = "11.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b8874945f036109c72242964c1174cf99434e30cfa45bf45fedc983f50046f8"
|
||||
dependencies = [
|
||||
"hashbag",
|
||||
"left-right",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "eyre"
|
||||
version = "0.6.12"
|
||||
@@ -1219,6 +1230,21 @@ dependencies = [
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generator"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52f04ae4152da20c76fe800fa48659201d5cf627c5149ca0b707b69d7eef6cf9"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"log",
|
||||
"rustversion",
|
||||
"windows-link",
|
||||
"windows-result",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
@@ -1300,6 +1326,12 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbag"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7040a10f52cba493ddb09926e15d10a9d8a28043708a405931fe4c6f19fac064"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
@@ -1857,6 +1889,17 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||
|
||||
[[package]]
|
||||
name = "left-right"
|
||||
version = "0.11.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f0c21e4c8ff95f487fb34e6f9182875f42c84cef966d29216bf115d9bba835a"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
"loom",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.183"
|
||||
@@ -1928,6 +1971,19 @@ version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "loom"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"generator",
|
||||
"scoped-tls",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lru-slab"
|
||||
version = "0.1.2"
|
||||
@@ -1977,11 +2033,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "metrics-exporter-prometheus"
|
||||
version = "0.18.1"
|
||||
version = "0.18.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3589659543c04c7dc5526ec858591015b87cd8746583b51b48ef4353f99dbcda"
|
||||
checksum = "1db0d8f1fc9e62caebd0319e11eaec5822b0186c171568f0480b46a0137f9108"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"evmap",
|
||||
"indexmap",
|
||||
"metrics",
|
||||
"metrics-util",
|
||||
@@ -3103,6 +3160,12 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scoped-tls"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
|
||||
@@ -44,7 +44,7 @@ hyper-util = "= 0.1.20"
|
||||
ipnet = { version = "= 2.12.0", features = ["serde"] }
|
||||
json-subscriber = "= 0.2.8"
|
||||
metrics = "= 0.24.5"
|
||||
metrics-exporter-prometheus = { version = "= 0.18.1", default-features = false }
|
||||
metrics-exporter-prometheus = { version = "= 0.18.3", default-features = false }
|
||||
nix = { version = "= 0.31.2", features = ["hostname", "signal"] }
|
||||
notify = "= 8.2.0"
|
||||
pin-project-lite = "= 0.2.17"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"""Serializer mixin for managed models"""
|
||||
|
||||
from json import JSONDecodeError, loads
|
||||
from typing import cast
|
||||
|
||||
from django.conf import settings
|
||||
@@ -44,6 +45,7 @@ class BlueprintUploadSerializer(PassiveSerializer):
|
||||
|
||||
file = FileField(required=False)
|
||||
path = CharField(required=False)
|
||||
context = CharField(required=False, allow_blank=True)
|
||||
|
||||
def validate_path(self, path: str) -> str:
|
||||
"""Ensure the path (if set) specified is retrievable"""
|
||||
@@ -54,6 +56,18 @@ class BlueprintUploadSerializer(PassiveSerializer):
|
||||
raise ValidationError(_("Blueprint file does not exist"))
|
||||
return path
|
||||
|
||||
def validate_context(self, context: str) -> dict:
|
||||
"""Parse context as a JSON object"""
|
||||
if not context:
|
||||
return {}
|
||||
try:
|
||||
parsed = loads(context)
|
||||
except JSONDecodeError as exc:
|
||||
raise ValidationError(_("Context must be valid JSON")) from exc
|
||||
if not isinstance(parsed, dict):
|
||||
raise ValidationError(_("Context must be a JSON object"))
|
||||
return parsed
|
||||
|
||||
|
||||
class ManagedSerializer:
|
||||
"""Managed Serializer"""
|
||||
@@ -224,7 +238,8 @@ class BlueprintInstanceViewSet(UsedByMixin, ModelViewSet):
|
||||
).retrieve_file()
|
||||
else:
|
||||
raise ValidationError("Either path or file must be set")
|
||||
importer = Importer.from_string(string_contents)
|
||||
context = body.validated_data.get("context") or {}
|
||||
importer = Importer.from_string(string_contents, context)
|
||||
|
||||
check_blueprint_perms(importer.blueprint, request.user)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Test blueprints v1 api"""
|
||||
|
||||
from json import loads
|
||||
from json import dumps, loads
|
||||
from tempfile import NamedTemporaryFile, mkdtemp
|
||||
|
||||
from django.urls import reverse
|
||||
@@ -8,7 +8,11 @@ from rest_framework.test import APITestCase
|
||||
from yaml import dump
|
||||
|
||||
from authentik.core.tests.utils import create_test_admin_user
|
||||
from authentik.flows.models import Flow
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.stages.invitation.models import InvitationStage
|
||||
from authentik.stages.user_write.models import UserWriteStage
|
||||
|
||||
TMP = mkdtemp("authentik-blueprints")
|
||||
|
||||
@@ -80,3 +84,107 @@ class TestBlueprintsV1API(APITestCase):
|
||||
res.content.decode(),
|
||||
{"content": ["Failed to validate blueprint", "- Invalid blueprint version"]},
|
||||
)
|
||||
|
||||
def test_api_import_with_context(self):
|
||||
"""Test that the import endpoint applies the supplied context to the real blueprint"""
|
||||
slug = f"invitation-enrollment-{generate_id()}"
|
||||
flow_name = f"Invitation Enrollment {generate_id()}"
|
||||
stage_name = f"invitation-stage-{generate_id()}"
|
||||
user_type = "internal"
|
||||
continue_without_invitation = True
|
||||
|
||||
res = self.client.post(
|
||||
reverse("authentik_api:blueprintinstance-import-"),
|
||||
data={
|
||||
"path": "example/flows-invitation-enrollment-minimal.yaml",
|
||||
"context": dumps(
|
||||
{
|
||||
"flow_slug": slug,
|
||||
"flow_name": flow_name,
|
||||
"stage_name": stage_name,
|
||||
"continue_flow_without_invitation": continue_without_invitation,
|
||||
"user_type": user_type,
|
||||
}
|
||||
),
|
||||
},
|
||||
format="multipart",
|
||||
)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertTrue(res.json()["success"])
|
||||
|
||||
flow = Flow.objects.get(slug=slug)
|
||||
self.assertEqual(flow.name, flow_name)
|
||||
self.assertEqual(flow.title, flow_name)
|
||||
|
||||
invitation_stage = InvitationStage.objects.get(name=stage_name)
|
||||
self.assertEqual(
|
||||
invitation_stage.continue_flow_without_invitation,
|
||||
continue_without_invitation,
|
||||
)
|
||||
|
||||
user_write_stage = UserWriteStage.objects.get(
|
||||
name=f"invitation-enrollment-user-write-{slug}"
|
||||
)
|
||||
self.assertEqual(user_write_stage.user_type, user_type)
|
||||
self.assertEqual(user_write_stage.user_path_template, f"users/{user_type}")
|
||||
|
||||
def test_api_import_blank_path(self):
|
||||
"""Validator returns empty path unchanged (covers api.py:53)."""
|
||||
with NamedTemporaryFile(mode="w+", suffix=".yaml") as file:
|
||||
file.write(dump({"version": 1, "entries": []}))
|
||||
file.flush()
|
||||
file.seek(0)
|
||||
res = self.client.post(
|
||||
reverse("authentik_api:blueprintinstance-import-"),
|
||||
data={"path": "", "file": file},
|
||||
format="multipart",
|
||||
)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_api_import_unknown_path(self):
|
||||
"""Path not in available blueprints is rejected (covers api.py:56)."""
|
||||
res = self.client.post(
|
||||
reverse("authentik_api:blueprintinstance-import-"),
|
||||
data={"path": "does/not/exist.yaml"},
|
||||
format="multipart",
|
||||
)
|
||||
self.assertEqual(res.status_code, 400)
|
||||
self.assertIn("Blueprint file does not exist", res.content.decode())
|
||||
|
||||
def test_api_import_blank_context(self):
|
||||
"""Blank context is normalized to empty dict (covers api.py:62)."""
|
||||
res = self.client.post(
|
||||
reverse("authentik_api:blueprintinstance-import-"),
|
||||
data={
|
||||
"path": "example/flows-invitation-enrollment-minimal.yaml",
|
||||
"context": "",
|
||||
},
|
||||
format="multipart",
|
||||
)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_api_import_invalid_json_context(self):
|
||||
"""Malformed JSON context raises ValidationError (covers api.py:65-66)."""
|
||||
res = self.client.post(
|
||||
reverse("authentik_api:blueprintinstance-import-"),
|
||||
data={
|
||||
"path": "example/flows-invitation-enrollment-minimal.yaml",
|
||||
"context": "{not json",
|
||||
},
|
||||
format="multipart",
|
||||
)
|
||||
self.assertEqual(res.status_code, 400)
|
||||
self.assertIn("Context must be valid JSON", res.content.decode())
|
||||
|
||||
def test_api_import_non_object_context(self):
|
||||
"""JSON context that isn't an object is rejected (covers api.py:68)."""
|
||||
res = self.client.post(
|
||||
reverse("authentik_api:blueprintinstance-import-"),
|
||||
data={
|
||||
"path": "example/flows-invitation-enrollment-minimal.yaml",
|
||||
"context": "[1, 2, 3]",
|
||||
},
|
||||
format="multipart",
|
||||
)
|
||||
self.assertEqual(res.status_code, 400)
|
||||
self.assertIn("Context must be a JSON object", res.content.decode())
|
||||
|
||||
@@ -32,19 +32,19 @@ from authentik.rbac.decorators import permission_required
|
||||
class UserAgentDeviceDict(TypedDict):
|
||||
"""User agent device"""
|
||||
|
||||
brand: str
|
||||
brand: str | None = None
|
||||
family: str
|
||||
model: str
|
||||
model: str | None = None
|
||||
|
||||
|
||||
class UserAgentOSDict(TypedDict):
|
||||
"""User agent os"""
|
||||
|
||||
family: str
|
||||
major: str
|
||||
minor: str
|
||||
patch: str
|
||||
patch_minor: str
|
||||
major: str | None = None
|
||||
minor: str | None = None
|
||||
patch: str | None = None
|
||||
patch_minor: str | None = None
|
||||
|
||||
|
||||
class UserAgentBrowserDict(TypedDict):
|
||||
|
||||
@@ -29,6 +29,7 @@ class RefreshOtherFlowsAfterAuthentication(Flag[bool], key="flows_refresh_others
|
||||
default = False
|
||||
visibility = "public"
|
||||
description = _("Refresh other tabs after successful authentication.")
|
||||
deprecated = True
|
||||
|
||||
|
||||
class ContinuousLogin(Flag[bool], key="flows_continuous_login"):
|
||||
|
||||
@@ -187,6 +187,7 @@ SPECTACULAR_SETTINGS = {
|
||||
"PolicyEngineMode": "authentik.policies.models.PolicyEngineMode",
|
||||
"PromptTypeEnum": "authentik.stages.prompt.models.FieldTypes",
|
||||
"ProxyMode": "authentik.providers.proxy.models.ProxyMode",
|
||||
"RedirectURITypeEnum": "authentik.providers.oauth2.models.RedirectURIType",
|
||||
"SAMLBindingsEnum": "authentik.providers.saml.models.SAMLBindings",
|
||||
"SAMLLogoutMethods": "authentik.providers.saml.models.SAMLLogoutMethods",
|
||||
"SAMLNameIDPolicyEnum": "authentik.sources.saml.models.SAMLNameIDPolicy",
|
||||
|
||||
@@ -16,7 +16,7 @@ class RedirectMode(models.TextChoices):
|
||||
|
||||
|
||||
class RedirectStage(Stage):
|
||||
"""Redirect the user to another flow, potentially with all gathered context."""
|
||||
"""Redirect the user to a static URL or another flow, optionally with all gathered context."""
|
||||
|
||||
keep_context = models.BooleanField(default=True)
|
||||
mode = models.TextField(choices=RedirectMode.choices)
|
||||
|
||||
@@ -59,6 +59,8 @@ class FlagsJSONExtension(OpenApiSerializerFieldExtension):
|
||||
props[_flag.key] = build_basic_type(get_args(_flag.__orig_bases__[0])[0])
|
||||
if _flag.description:
|
||||
props[_flag.key]["description"] = _flag.description
|
||||
if _flag.deprecated:
|
||||
props[_flag.key]["deprecated"] = _flag.deprecated
|
||||
return build_object_type(props, required=props.keys())
|
||||
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ class Flag[T]:
|
||||
Literal["none"] | Literal["public"] | Literal["authenticated"] | Literal["system"]
|
||||
) = "none"
|
||||
description: str | None = None
|
||||
deprecated = False
|
||||
|
||||
def __init_subclass__(cls, key: str, **kwargs):
|
||||
cls.__key = key
|
||||
|
||||
211
blueprints/example/flows-invitation-enrollment-minimal.yaml
Normal file
211
blueprints/example/flows-invitation-enrollment-minimal.yaml
Normal file
@@ -0,0 +1,211 @@
|
||||
# Minimal Invitation-based Enrollment Blueprint
|
||||
#
|
||||
# Companion to flows-invitation-enrollment.yaml, intended for the "New Invitation"
|
||||
# wizard in the admin UI. Creates a single enrollment flow with an invitation stage
|
||||
# bound to it, plus the supporting prompt/user-write/user-login stages.
|
||||
#
|
||||
# All user-facing fields are parameterized via !Context with fallback defaults, so
|
||||
# this blueprint can be imported directly (without context) or through the wizard
|
||||
# with custom values.
|
||||
#
|
||||
# Context keys (all optional):
|
||||
# flow_name Display name of the enrollment flow.
|
||||
# flow_slug URL slug of the flow and suffix for sub-entity
|
||||
# identifiers (so repeated imports with different
|
||||
# slugs don't overwrite each other).
|
||||
# stage_name Name of the invitation stage.
|
||||
# continue_flow_without_invitation Whether the flow continues when no invitation
|
||||
# is supplied (default: false).
|
||||
# user_type "external" or "internal" (default: "external").
|
||||
# Drives the user-write stage's user_type and
|
||||
# user_path_template.
|
||||
version: 1
|
||||
metadata:
|
||||
labels:
|
||||
blueprints.goauthentik.io/instantiate: "false"
|
||||
name: Invitation-based Enrollment (minimal)
|
||||
entries:
|
||||
- identifiers:
|
||||
slug: !Context [flow_slug, invitation-enrollment-flow]
|
||||
model: authentik_flows.flow
|
||||
id: flow
|
||||
attrs:
|
||||
name: !Context [flow_name, Invitation Enrollment Flow]
|
||||
title: !Context [flow_name, Invitation Enrollment Flow]
|
||||
designation: enrollment
|
||||
authentication: require_unauthenticated
|
||||
|
||||
- identifiers:
|
||||
name: !Context [stage_name, invitation-stage]
|
||||
id: invitation-stage
|
||||
model: authentik_stages_invitation.invitationstage
|
||||
attrs:
|
||||
continue_flow_without_invitation: !Context [continue_flow_without_invitation, false]
|
||||
|
||||
- identifiers:
|
||||
name:
|
||||
!Format [
|
||||
"invitation-enrollment-field-username-%s",
|
||||
!Context [flow_slug, invitation-enrollment-flow],
|
||||
]
|
||||
id: prompt-field-username
|
||||
model: authentik_stages_prompt.prompt
|
||||
attrs:
|
||||
field_key: username
|
||||
label: Username
|
||||
type: username
|
||||
required: true
|
||||
placeholder: Username
|
||||
placeholder_expression: false
|
||||
order: 0
|
||||
|
||||
- identifiers:
|
||||
name:
|
||||
!Format [
|
||||
"invitation-enrollment-field-password-%s",
|
||||
!Context [flow_slug, invitation-enrollment-flow],
|
||||
]
|
||||
id: prompt-field-password
|
||||
model: authentik_stages_prompt.prompt
|
||||
attrs:
|
||||
field_key: password
|
||||
label: Password
|
||||
type: password
|
||||
required: true
|
||||
placeholder: Password
|
||||
placeholder_expression: false
|
||||
order: 1
|
||||
|
||||
- identifiers:
|
||||
name:
|
||||
!Format [
|
||||
"invitation-enrollment-field-password-repeat-%s",
|
||||
!Context [flow_slug, invitation-enrollment-flow],
|
||||
]
|
||||
id: prompt-field-password-repeat
|
||||
model: authentik_stages_prompt.prompt
|
||||
attrs:
|
||||
field_key: password_repeat
|
||||
label: Password (repeat)
|
||||
type: password
|
||||
required: true
|
||||
placeholder: Password (repeat)
|
||||
placeholder_expression: false
|
||||
order: 2
|
||||
|
||||
- identifiers:
|
||||
name:
|
||||
!Format [
|
||||
"invitation-enrollment-field-name-%s",
|
||||
!Context [flow_slug, invitation-enrollment-flow],
|
||||
]
|
||||
id: prompt-field-name
|
||||
model: authentik_stages_prompt.prompt
|
||||
attrs:
|
||||
field_key: name
|
||||
label: Name
|
||||
type: text
|
||||
required: true
|
||||
placeholder: Name
|
||||
placeholder_expression: false
|
||||
order: 0
|
||||
|
||||
- identifiers:
|
||||
name:
|
||||
!Format [
|
||||
"invitation-enrollment-field-email-%s",
|
||||
!Context [flow_slug, invitation-enrollment-flow],
|
||||
]
|
||||
id: prompt-field-email
|
||||
model: authentik_stages_prompt.prompt
|
||||
attrs:
|
||||
field_key: email
|
||||
label: Email
|
||||
type: email
|
||||
required: true
|
||||
placeholder: Email
|
||||
placeholder_expression: false
|
||||
order: 1
|
||||
|
||||
- identifiers:
|
||||
name:
|
||||
!Format [
|
||||
"invitation-enrollment-prompt-credentials-%s",
|
||||
!Context [flow_slug, invitation-enrollment-flow],
|
||||
]
|
||||
id: prompt-stage-credentials
|
||||
model: authentik_stages_prompt.promptstage
|
||||
attrs:
|
||||
fields:
|
||||
- !KeyOf prompt-field-username
|
||||
- !KeyOf prompt-field-password
|
||||
- !KeyOf prompt-field-password-repeat
|
||||
|
||||
- identifiers:
|
||||
name:
|
||||
!Format [
|
||||
"invitation-enrollment-prompt-details-%s",
|
||||
!Context [flow_slug, invitation-enrollment-flow],
|
||||
]
|
||||
id: prompt-stage-details
|
||||
model: authentik_stages_prompt.promptstage
|
||||
attrs:
|
||||
fields:
|
||||
- !KeyOf prompt-field-name
|
||||
- !KeyOf prompt-field-email
|
||||
|
||||
- identifiers:
|
||||
name:
|
||||
!Format [
|
||||
"invitation-enrollment-user-write-%s",
|
||||
!Context [flow_slug, invitation-enrollment-flow],
|
||||
]
|
||||
id: user-write-stage
|
||||
model: authentik_stages_user_write.userwritestage
|
||||
attrs:
|
||||
user_creation_mode: always_create
|
||||
user_type: !Context [user_type, external]
|
||||
user_path_template:
|
||||
!Format ["users/%s", !Context [user_type, external]]
|
||||
|
||||
- identifiers:
|
||||
name:
|
||||
!Format [
|
||||
"invitation-enrollment-user-login-%s",
|
||||
!Context [flow_slug, invitation-enrollment-flow],
|
||||
]
|
||||
id: user-login-stage
|
||||
model: authentik_stages_user_login.userloginstage
|
||||
|
||||
- identifiers:
|
||||
target: !KeyOf flow
|
||||
stage: !KeyOf invitation-stage
|
||||
order: 5
|
||||
model: authentik_flows.flowstagebinding
|
||||
attrs:
|
||||
evaluate_on_plan: true
|
||||
re_evaluate_policies: true
|
||||
|
||||
- identifiers:
|
||||
target: !KeyOf flow
|
||||
stage: !KeyOf prompt-stage-credentials
|
||||
order: 10
|
||||
model: authentik_flows.flowstagebinding
|
||||
|
||||
- identifiers:
|
||||
target: !KeyOf flow
|
||||
stage: !KeyOf prompt-stage-details
|
||||
order: 15
|
||||
model: authentik_flows.flowstagebinding
|
||||
|
||||
- identifiers:
|
||||
target: !KeyOf flow
|
||||
stage: !KeyOf user-write-stage
|
||||
order: 20
|
||||
model: authentik_flows.flowstagebinding
|
||||
|
||||
- identifiers:
|
||||
target: !KeyOf flow
|
||||
stage: !KeyOf user-login-stage
|
||||
order: 100
|
||||
model: authentik_flows.flowstagebinding
|
||||
Binary file not shown.
5
packages/client-ts/src/apis/ManagedApi.ts
generated
5
packages/client-ts/src/apis/ManagedApi.ts
generated
@@ -47,6 +47,7 @@ export interface ManagedBlueprintsDestroyRequest {
|
||||
export interface ManagedBlueprintsImportCreateRequest {
|
||||
file?: Blob;
|
||||
path?: string;
|
||||
context?: string;
|
||||
}
|
||||
|
||||
export interface ManagedBlueprintsListRequest {
|
||||
@@ -369,6 +370,10 @@ export class ManagedApi extends runtime.BaseAPI {
|
||||
formParams.append("path", requestParameters["path"] as any);
|
||||
}
|
||||
|
||||
if (requestParameters["context"] != null) {
|
||||
formParams.append("context", requestParameters["context"] as any);
|
||||
}
|
||||
|
||||
let urlPath = `/managed/blueprints/import/`;
|
||||
|
||||
return {
|
||||
|
||||
@@ -23,7 +23,7 @@ export interface AuthenticatedSessionUserAgentDevice {
|
||||
* @type {string}
|
||||
* @memberof AuthenticatedSessionUserAgentDevice
|
||||
*/
|
||||
brand: string;
|
||||
brand: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
@@ -35,7 +35,7 @@ export interface AuthenticatedSessionUserAgentDevice {
|
||||
* @type {string}
|
||||
* @memberof AuthenticatedSessionUserAgentDevice
|
||||
*/
|
||||
model: string;
|
||||
model: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,25 +29,25 @@ export interface AuthenticatedSessionUserAgentOs {
|
||||
* @type {string}
|
||||
* @memberof AuthenticatedSessionUserAgentOs
|
||||
*/
|
||||
major: string;
|
||||
major: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof AuthenticatedSessionUserAgentOs
|
||||
*/
|
||||
minor: string;
|
||||
minor: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof AuthenticatedSessionUserAgentOs
|
||||
*/
|
||||
patch: string;
|
||||
patch: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof AuthenticatedSessionUserAgentOs
|
||||
*/
|
||||
patchMinor: string;
|
||||
patchMinor: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -40,6 +40,7 @@ export interface CurrentBrandFlags {
|
||||
* Refresh other tabs after successful authentication.
|
||||
* @type {boolean}
|
||||
* @memberof CurrentBrandFlags
|
||||
* @deprecated
|
||||
*/
|
||||
flowsRefreshOthers: boolean;
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ export interface PatchedSettingsRequestFlags {
|
||||
* Refresh other tabs after successful authentication.
|
||||
* @type {boolean}
|
||||
* @memberof PatchedSettingsRequestFlags
|
||||
* @deprecated
|
||||
*/
|
||||
flowsRefreshOthers: boolean;
|
||||
}
|
||||
|
||||
12
packages/client-ts/src/models/RedirectURI.ts
generated
12
packages/client-ts/src/models/RedirectURI.ts
generated
@@ -14,8 +14,8 @@
|
||||
|
||||
import type { MatchingModeEnum } from "./MatchingModeEnum";
|
||||
import { MatchingModeEnumFromJSON, MatchingModeEnumToJSON } from "./MatchingModeEnum";
|
||||
import type { RedirectUriTypeEnum } from "./RedirectUriTypeEnum";
|
||||
import { RedirectUriTypeEnumFromJSON, RedirectUriTypeEnumToJSON } from "./RedirectUriTypeEnum";
|
||||
import type { RedirectURITypeEnum } from "./RedirectURITypeEnum";
|
||||
import { RedirectURITypeEnumFromJSON, RedirectURITypeEnumToJSON } from "./RedirectURITypeEnum";
|
||||
|
||||
/**
|
||||
* A single allowed redirect URI entry
|
||||
@@ -37,10 +37,10 @@ export interface RedirectURI {
|
||||
url: string;
|
||||
/**
|
||||
*
|
||||
* @type {RedirectUriTypeEnum}
|
||||
* @type {RedirectURITypeEnum}
|
||||
* @memberof RedirectURI
|
||||
*/
|
||||
redirectUriType?: RedirectUriTypeEnum;
|
||||
redirectUriType?: RedirectURITypeEnum;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,7 +66,7 @@ export function RedirectURIFromJSONTyped(json: any, ignoreDiscriminator: boolean
|
||||
redirectUriType:
|
||||
json["redirect_uri_type"] == null
|
||||
? undefined
|
||||
: RedirectUriTypeEnumFromJSON(json["redirect_uri_type"]),
|
||||
: RedirectURITypeEnumFromJSON(json["redirect_uri_type"]),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -85,6 +85,6 @@ export function RedirectURIToJSONTyped(
|
||||
return {
|
||||
matching_mode: MatchingModeEnumToJSON(value["matchingMode"]),
|
||||
url: value["url"],
|
||||
redirect_uri_type: RedirectUriTypeEnumToJSON(value["redirectUriType"]),
|
||||
redirect_uri_type: RedirectURITypeEnumToJSON(value["redirectUriType"]),
|
||||
};
|
||||
}
|
||||
|
||||
12
packages/client-ts/src/models/RedirectURIRequest.ts
generated
12
packages/client-ts/src/models/RedirectURIRequest.ts
generated
@@ -14,8 +14,8 @@
|
||||
|
||||
import type { MatchingModeEnum } from "./MatchingModeEnum";
|
||||
import { MatchingModeEnumFromJSON, MatchingModeEnumToJSON } from "./MatchingModeEnum";
|
||||
import type { RedirectUriTypeEnum } from "./RedirectUriTypeEnum";
|
||||
import { RedirectUriTypeEnumFromJSON, RedirectUriTypeEnumToJSON } from "./RedirectUriTypeEnum";
|
||||
import type { RedirectURITypeEnum } from "./RedirectURITypeEnum";
|
||||
import { RedirectURITypeEnumFromJSON, RedirectURITypeEnumToJSON } from "./RedirectURITypeEnum";
|
||||
|
||||
/**
|
||||
* A single allowed redirect URI entry
|
||||
@@ -37,10 +37,10 @@ export interface RedirectURIRequest {
|
||||
url: string;
|
||||
/**
|
||||
*
|
||||
* @type {RedirectUriTypeEnum}
|
||||
* @type {RedirectURITypeEnum}
|
||||
* @memberof RedirectURIRequest
|
||||
*/
|
||||
redirectUriType?: RedirectUriTypeEnum;
|
||||
redirectUriType?: RedirectURITypeEnum;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,7 +69,7 @@ export function RedirectURIRequestFromJSONTyped(
|
||||
redirectUriType:
|
||||
json["redirect_uri_type"] == null
|
||||
? undefined
|
||||
: RedirectUriTypeEnumFromJSON(json["redirect_uri_type"]),
|
||||
: RedirectURITypeEnumFromJSON(json["redirect_uri_type"]),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -88,6 +88,6 @@ export function RedirectURIRequestToJSONTyped(
|
||||
return {
|
||||
matching_mode: MatchingModeEnumToJSON(value["matchingMode"]),
|
||||
url: value["url"],
|
||||
redirect_uri_type: RedirectUriTypeEnumToJSON(value["redirectUriType"]),
|
||||
redirect_uri_type: RedirectURITypeEnumToJSON(value["redirectUriType"]),
|
||||
};
|
||||
}
|
||||
|
||||
57
packages/client-ts/src/models/RedirectURITypeEnum.ts
generated
Normal file
57
packages/client-ts/src/models/RedirectURITypeEnum.ts
generated
Normal file
@@ -0,0 +1,57 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* authentik
|
||||
* Making authentication simple.
|
||||
*
|
||||
* The version of the OpenAPI document: 2026.5.0-rc1
|
||||
* Contact: hello@goauthentik.io
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const RedirectURITypeEnum = {
|
||||
Authorization: "authorization",
|
||||
Logout: "logout",
|
||||
UnknownDefaultOpenApi: "11184809",
|
||||
} as const;
|
||||
export type RedirectURITypeEnum = (typeof RedirectURITypeEnum)[keyof typeof RedirectURITypeEnum];
|
||||
|
||||
export function instanceOfRedirectURITypeEnum(value: any): boolean {
|
||||
for (const key in RedirectURITypeEnum) {
|
||||
if (Object.prototype.hasOwnProperty.call(RedirectURITypeEnum, key)) {
|
||||
if (RedirectURITypeEnum[key as keyof typeof RedirectURITypeEnum] === value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function RedirectURITypeEnumFromJSON(json: any): RedirectURITypeEnum {
|
||||
return RedirectURITypeEnumFromJSONTyped(json, false);
|
||||
}
|
||||
|
||||
export function RedirectURITypeEnumFromJSONTyped(
|
||||
json: any,
|
||||
ignoreDiscriminator: boolean,
|
||||
): RedirectURITypeEnum {
|
||||
return json as RedirectURITypeEnum;
|
||||
}
|
||||
|
||||
export function RedirectURITypeEnumToJSON(value?: RedirectURITypeEnum | null): any {
|
||||
return value as any;
|
||||
}
|
||||
|
||||
export function RedirectURITypeEnumToJSONTyped(
|
||||
value: any,
|
||||
ignoreDiscriminator: boolean,
|
||||
): RedirectURITypeEnum {
|
||||
return value as RedirectURITypeEnum;
|
||||
}
|
||||
57
packages/client-ts/src/models/RedirectUriTypeEnum.ts
generated
57
packages/client-ts/src/models/RedirectUriTypeEnum.ts
generated
@@ -1,57 +0,0 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* authentik
|
||||
* Making authentication simple.
|
||||
*
|
||||
* The version of the OpenAPI document: 2026.5.0-rc1
|
||||
* Contact: hello@goauthentik.io
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const RedirectUriTypeEnum = {
|
||||
Authorization: "authorization",
|
||||
Logout: "logout",
|
||||
UnknownDefaultOpenApi: "11184809",
|
||||
} as const;
|
||||
export type RedirectUriTypeEnum = (typeof RedirectUriTypeEnum)[keyof typeof RedirectUriTypeEnum];
|
||||
|
||||
export function instanceOfRedirectUriTypeEnum(value: any): boolean {
|
||||
for (const key in RedirectUriTypeEnum) {
|
||||
if (Object.prototype.hasOwnProperty.call(RedirectUriTypeEnum, key)) {
|
||||
if (RedirectUriTypeEnum[key as keyof typeof RedirectUriTypeEnum] === value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function RedirectUriTypeEnumFromJSON(json: any): RedirectUriTypeEnum {
|
||||
return RedirectUriTypeEnumFromJSONTyped(json, false);
|
||||
}
|
||||
|
||||
export function RedirectUriTypeEnumFromJSONTyped(
|
||||
json: any,
|
||||
ignoreDiscriminator: boolean,
|
||||
): RedirectUriTypeEnum {
|
||||
return json as RedirectUriTypeEnum;
|
||||
}
|
||||
|
||||
export function RedirectUriTypeEnumToJSON(value?: RedirectUriTypeEnum | null): any {
|
||||
return value as any;
|
||||
}
|
||||
|
||||
export function RedirectUriTypeEnumToJSONTyped(
|
||||
value: any,
|
||||
ignoreDiscriminator: boolean,
|
||||
): RedirectUriTypeEnum {
|
||||
return value as RedirectUriTypeEnum;
|
||||
}
|
||||
2
packages/client-ts/src/models/index.ts
generated
2
packages/client-ts/src/models/index.ts
generated
@@ -707,7 +707,7 @@ export * from "./RedirectStageModeEnum";
|
||||
export * from "./RedirectStageRequest";
|
||||
export * from "./RedirectURI";
|
||||
export * from "./RedirectURIRequest";
|
||||
export * from "./RedirectUriTypeEnum";
|
||||
export * from "./RedirectURITypeEnum";
|
||||
export * from "./RelatedGroup";
|
||||
export * from "./RelatedRule";
|
||||
export * from "./Reputation";
|
||||
|
||||
@@ -9,7 +9,7 @@ dependencies = [
|
||||
"argon2-cffi==25.1.0",
|
||||
"cachetools==7.0.6",
|
||||
"channels==4.3.2",
|
||||
"cryptography==47.0.0",
|
||||
"cryptography==48.0.0",
|
||||
"dacite==1.9.2",
|
||||
"deepmerge==2.0",
|
||||
"defusedxml==0.7.1",
|
||||
@@ -25,7 +25,7 @@ dependencies = [
|
||||
"django-prometheus==2.4.1",
|
||||
"django-storages[s3]==1.14.6",
|
||||
"django-tenants==3.10.1",
|
||||
"django==5.2.13",
|
||||
"django==5.2.14",
|
||||
"djangoql==0.19.1",
|
||||
"djangorestframework==3.17.1",
|
||||
"docker==7.1.0",
|
||||
@@ -48,7 +48,7 @@ dependencies = [
|
||||
"opencontainers==0.0.15",
|
||||
"packaging==26.2",
|
||||
"paramiko==4.0.0",
|
||||
"psycopg[c,pool]==3.3.3",
|
||||
"psycopg[c,pool]==3.3.4",
|
||||
"pydantic-scim==0.0.8",
|
||||
"pydantic==2.13.3",
|
||||
"pyjwt==2.11.0",
|
||||
|
||||
18
schema.yml
18
schema.yml
@@ -34608,10 +34608,12 @@ components:
|
||||
properties:
|
||||
brand:
|
||||
type: string
|
||||
nullable: true
|
||||
family:
|
||||
type: string
|
||||
model:
|
||||
type: string
|
||||
nullable: true
|
||||
required:
|
||||
- brand
|
||||
- family
|
||||
@@ -34624,12 +34626,16 @@ components:
|
||||
type: string
|
||||
major:
|
||||
type: string
|
||||
nullable: true
|
||||
minor:
|
||||
type: string
|
||||
nullable: true
|
||||
patch:
|
||||
type: string
|
||||
nullable: true
|
||||
patch_minor:
|
||||
type: string
|
||||
nullable: true
|
||||
required:
|
||||
- family
|
||||
- major
|
||||
@@ -36044,6 +36050,8 @@ components:
|
||||
path:
|
||||
type: string
|
||||
minLength: 1
|
||||
context:
|
||||
type: string
|
||||
Brand:
|
||||
type: object
|
||||
description: Brand Serializer
|
||||
@@ -37183,6 +37191,7 @@ components:
|
||||
flows_refresh_others:
|
||||
type: boolean
|
||||
description: Refresh other tabs after successful authentication.
|
||||
deprecated: true
|
||||
required:
|
||||
- core_default_app_access
|
||||
- enterprise_audit_include_expanded_diff
|
||||
@@ -51193,6 +51202,7 @@ components:
|
||||
flows_refresh_others:
|
||||
type: boolean
|
||||
description: Refresh other tabs after successful authentication.
|
||||
deprecated: true
|
||||
required:
|
||||
- core_default_app_access
|
||||
- enterprise_audit_include_expanded_diff
|
||||
@@ -53633,7 +53643,7 @@ components:
|
||||
type: string
|
||||
redirect_uri_type:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/RedirectUriTypeEnum'
|
||||
- $ref: '#/components/schemas/RedirectURITypeEnum'
|
||||
default: authorization
|
||||
required:
|
||||
- matching_mode
|
||||
@@ -53649,12 +53659,12 @@ components:
|
||||
minLength: 1
|
||||
redirect_uri_type:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/RedirectUriTypeEnum'
|
||||
- $ref: '#/components/schemas/RedirectURITypeEnum'
|
||||
default: authorization
|
||||
required:
|
||||
- matching_mode
|
||||
- url
|
||||
RedirectUriTypeEnum:
|
||||
RedirectURITypeEnum:
|
||||
enum:
|
||||
- authorization
|
||||
- logout
|
||||
@@ -55975,6 +55985,7 @@ components:
|
||||
flows_refresh_others:
|
||||
type: boolean
|
||||
description: Refresh other tabs after successful authentication.
|
||||
deprecated: true
|
||||
required:
|
||||
- core_default_app_access
|
||||
- enterprise_audit_include_expanded_diff
|
||||
@@ -56063,6 +56074,7 @@ components:
|
||||
flows_refresh_others:
|
||||
type: boolean
|
||||
description: Refresh other tabs after successful authentication.
|
||||
deprecated: true
|
||||
required:
|
||||
- core_default_app_access
|
||||
- enterprise_audit_include_expanded_diff
|
||||
|
||||
116
uv.lock
generated
116
uv.lock
generated
@@ -318,11 +318,11 @@ requires-dist = [
|
||||
{ name = "argon2-cffi", specifier = "==25.1.0" },
|
||||
{ name = "cachetools", specifier = "==7.0.6" },
|
||||
{ name = "channels", specifier = "==4.3.2" },
|
||||
{ name = "cryptography", specifier = "==47.0.0" },
|
||||
{ name = "cryptography", specifier = "==48.0.0" },
|
||||
{ name = "dacite", specifier = "==1.9.2" },
|
||||
{ name = "deepmerge", specifier = "==2.0" },
|
||||
{ name = "defusedxml", specifier = "==0.7.1" },
|
||||
{ name = "django", specifier = "==5.2.13" },
|
||||
{ name = "django", specifier = "==5.2.14" },
|
||||
{ name = "django-channels-postgres", editable = "packages/django-channels-postgres" },
|
||||
{ name = "django-countries", specifier = "==8.2.0" },
|
||||
{ name = "django-dramatiq-postgres", editable = "packages/django-dramatiq-postgres" },
|
||||
@@ -357,7 +357,7 @@ requires-dist = [
|
||||
{ name = "opencontainers", git = "https://github.com/vsoch/oci-python?rev=ceb4fcc090851717a3069d78e85ceb1e86c2740c" },
|
||||
{ name = "packaging", specifier = "==26.2" },
|
||||
{ name = "paramiko", specifier = "==4.0.0" },
|
||||
{ name = "psycopg", extras = ["c", "pool"], specifier = "==3.3.3" },
|
||||
{ name = "psycopg", extras = ["c", "pool"], specifier = "==3.3.4" },
|
||||
{ name = "pydantic", specifier = "==2.13.3" },
|
||||
{ name = "pydantic-scim", specifier = "==0.0.8" },
|
||||
{ name = "pyjwt", specifier = "==2.11.0" },
|
||||
@@ -917,55 +917,55 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "cryptography"
|
||||
version = "47.0.0"
|
||||
version = "48.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ef/b2/7ffa7fe8207a8c42147ffe70c3e360b228160c1d85dc3faff16aaa3244c0/cryptography-47.0.0.tar.gz", hash = "sha256:9f8e55fe4e63613a5e1cc5819030f27b97742d720203a087802ce4ce9ceb52bb", size = 830863, upload-time = "2026-04-24T19:54:57.056Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/9f/a9/db8f313fdcd85d767d4973515e1db101f9c71f95fced83233de224673757/cryptography-48.0.0.tar.gz", hash = "sha256:5c3932f4436d1cccb036cb0eaef46e6e2db91035166f1ad6505c3c9d5a635920", size = 832984, upload-time = "2026-05-04T22:59:38.133Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/98/40dfe932134bdcae4f6ab5927c87488754bf9eb79297d7e0070b78dd58e9/cryptography-47.0.0-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:160ad728f128972d362e714054f6ba0067cab7fb350c5202a9ae8ae4ce3ef1a0", size = 7912214, upload-time = "2026-04-24T19:53:03.864Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/34/c6/2733531243fba725f58611b918056b277692f1033373dcc8bd01af1c05d4/cryptography-47.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b9a8943e359b7615db1a3ba587994618e094ff3d6fa5a390c73d079ce18b3973", size = 4644617, upload-time = "2026-04-24T19:53:06.909Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/e3/b27be1a670a9b87f855d211cf0e1174a5d721216b7616bd52d8581d912ed/cryptography-47.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f5c15764f261394b22aef6b00252f5195f46f2ca300bec57149474e2538b31f8", size = 4668186, upload-time = "2026-04-24T19:53:09.053Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/81/b9/8443cfe5d17d482d348cee7048acf502bb89a51b6382f06240fd290d4ca3/cryptography-47.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:9c59ab0e0fa3a180a5a9c59f3a5abe3ef90d474bc56d7fadfbe80359491b615b", size = 4651244, upload-time = "2026-04-24T19:53:11.217Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/5e/13ed0cdd0eb88ba159d6dd5ebfece8cb901dbcf1ae5ac4072e28b55d3153/cryptography-47.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:34b4358b925a5ea3e14384ca781a2c0ef7ac219b57bb9eacc4457078e2b19f92", size = 5252906, upload-time = "2026-04-24T19:53:13.532Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/64/16/ed058e1df0f33d440217cd120d41d5dda9dd215a80b8187f68483185af82/cryptography-47.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0024b87d47ae2399165a6bfb20d24888881eeab83ae2566d62467c5ff0030ce7", size = 4701842, upload-time = "2026-04-24T19:53:15.618Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/e0/3d30986b30fdbd9e969abbdf8ba00ed0618615144341faeb57f395a084fe/cryptography-47.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:1e47422b5557bb82d3fff997e8d92cff4e28b9789576984f08c248d2b3535d93", size = 4289313, upload-time = "2026-04-24T19:53:17.755Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/fd/32db38e3ad0cb331f0691cb4c7a8a6f176f679124dee746b3af6633db4d9/cryptography-47.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:6f29f36582e6151d9686235e586dd35bb67491f024767d10b842e520dc6a07ac", size = 4650964, upload-time = "2026-04-24T19:53:20.062Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/53/5395d944dfd48cb1f67917f533c609c34347185ef15eb4308024c876f274/cryptography-47.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:a9b761f012a943b7de0e828843c5688d0de94a0578d44d6c85a1bae32f87791f", size = 5207817, upload-time = "2026-04-24T19:53:22.498Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/34/4f/e5711b28e1901f7d480a2b1b688b645aa4c77c73f10731ed17e7f7db3f0d/cryptography-47.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4e1de79e047e25d6e9f8cea71c86b4a53aced64134f0f003bbcbf3655fd172c8", size = 4701544, upload-time = "2026-04-24T19:53:24.356Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/22/c8ddc25de3010fc8da447648f5a092c40e7a8fadf01dd6d255d9c0b9373d/cryptography-47.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef6b3634087f18d2155b1e8ce264e5345a753da2c5fa9815e7d41315c90f8318", size = 4783536, upload-time = "2026-04-24T19:53:26.665Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/b6/d4a68f4ea999c6d89e8498579cba1c5fcba4276284de7773b17e4fa69293/cryptography-47.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:11dbb9f50a0f1bb9757b3d8c27c1101780efb8f0bdecfb12439c22a74d64c001", size = 4926106, upload-time = "2026-04-24T19:53:28.686Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/54/ed/5f524db1fade9c013aa618e1c99c6ed05e8ffc9ceee6cda22fed22dda3f4/cryptography-47.0.0-cp311-abi3-win32.whl", hash = "sha256:7fda2f02c9015db3f42bb8a22324a454516ed10a8c29ca6ece6cdbb5efe2a203", size = 3258581, upload-time = "2026-04-24T19:53:31.058Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b2/dc/1b901990b174786569029f67542b3edf72ac068b6c3c8683c17e6a2f5363/cryptography-47.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:f5c3296dab66202f1b18a91fa266be93d6aa0c2806ea3d67762c69f60adc71aa", size = 3775309, upload-time = "2026-04-24T19:53:33.054Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/88/7aa18ad9c11bc87689affa5ce4368d884b517502d75739d475fc6f4a03c7/cryptography-47.0.0-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:be12cb6a204f77ed968bcefe68086eb061695b540a3dd05edac507a3111b25f0", size = 7904299, upload-time = "2026-04-24T19:53:35.003Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/55/c18f75724544872f234678fdedc871391722cb34a2aee19faa9f63100bb2/cryptography-47.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2ebd84adf0728c039a3be2700289378e1c164afc6748df1a5ed456767bef9ba7", size = 4631180, upload-time = "2026-04-24T19:53:37.517Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/65/31a5cc0eaca99cec5bafffe155d407115d96136bb161e8b49e0ef73f09a7/cryptography-47.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f68d6fbc7fbbcfb0939fea72c3b96a9f9a6edfc0e1b1d29778a2066030418b1", size = 4653529, upload-time = "2026-04-24T19:53:39.775Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/bc/641c0519a495f3bfd0421b48d7cd325c4336578523ccd76ea322b6c29c7a/cryptography-47.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:6651d32eff255423503aa276739da98c30f26c40cbeffcc6048e0d54ef704c0c", size = 4638570, upload-time = "2026-04-24T19:53:42.129Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/f2/300327b0a47f6dc94dd8b71b57052aefe178bb51745073d73d80604f11ab/cryptography-47.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:3fb8fa48075fad7193f2e5496135c6a76ac4b2aa5a38433df0a539296b377829", size = 5238019, upload-time = "2026-04-24T19:53:44.577Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/5a/5b5cf994391d4bf9d9c7efd4c66aabe4d95227256627f8fea6cff7dfadbd/cryptography-47.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:11438c7518132d95f354fa01a4aa2f806d172a061a7bed18cf18cbdacdb204d7", size = 4686832, upload-time = "2026-04-24T19:53:47.015Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/2c/ae950e28fd6475c852fc21a44db3e6b5bcc1261d1e370f2b6e42fa800fef/cryptography-47.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:8c1a736bbb3288005796c3f7ccb9453360d7fed483b13b9f468aea5171432923", size = 4269301, upload-time = "2026-04-24T19:53:48.97Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/fb/6a39782e150ffe5cc1b0018cb6ddc48bf7ca62b498d7539ffc8a758e977d/cryptography-47.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:f1557695e5c2b86e204f6ce9470497848634100787935ab7adc5397c54abd7ab", size = 4638110, upload-time = "2026-04-24T19:53:51.011Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/d7/0b3c71090a76e5c203164a47688b697635ece006dcd2499ab3a4dbd3f0bd/cryptography-47.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:f9a034b642b960767fb343766ae5ba6ad653f2e890ddd82955aef288ffea8736", size = 5194988, upload-time = "2026-04-24T19:53:52.962Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/63/33/63a961498a9df51721ab578c5a2622661411fc520e00bd83b0cc64eb20c4/cryptography-47.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:b1c76fca783aa7698eb21eb14f9c4aa09452248ee54a627d125025a43f83e7a7", size = 4686563, upload-time = "2026-04-24T19:53:55.274Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/bf/5ee5b145248f92250de86145d1c1d6edebbd57a7fe7caa4dedb5d4cf06a1/cryptography-47.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4f7722c97826770bab8ae92959a2e7b20a5e9e9bf4deae68fd86c3ca457bab52", size = 4770094, upload-time = "2026-04-24T19:53:57.753Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/43/21d220b2da5d517773894dacdcdb5c682c28d3fffce65548cb06e87d5501/cryptography-47.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:09f6d7bf6724f8db8b32f11eccf23efc8e759924bc5603800335cf8859a3ddbd", size = 4913811, upload-time = "2026-04-24T19:54:00.236Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/31/98/dc4ad376ac5f1a1a7d4a83f7b0c6f2bcad36b5d2d8f30aeb482d3a7d9582/cryptography-47.0.0-cp314-cp314t-win32.whl", hash = "sha256:6eebcaf0df1d21ce1f90605c9b432dd2c4f4ab665ac29a40d5e3fc68f51b5e63", size = 3237158, upload-time = "2026-04-24T19:54:02.606Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/da/97f62d18306b5133468bc3f8cc73a3111e8cdc8cf8d3e69474d6e5fd2d1b/cryptography-47.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:51c9313e90bd1690ec5a75ed047c27c0b8e6c570029712943d6116ef9a90620b", size = 3758706, upload-time = "2026-04-24T19:54:04.433Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/34/a4fae8ae7c3bc227460c9ae43f56abf1b911da0ec29e0ebac53bb0a4b6b7/cryptography-47.0.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:14432c8a9bcb37009784f9594a62fae211a2ae9543e96c92b2a8e4c3cd5cd0c4", size = 7904072, upload-time = "2026-04-24T19:54:06.411Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/01/64/d7b1e54fdb69f22d24a64bb3e88dc718b31c7fb10ef0b9691a3cf7eeea6e/cryptography-47.0.0-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:07efe86201817e7d3c18781ca9770bc0db04e1e48c994be384e4602bc38f8f27", size = 4635767, upload-time = "2026-04-24T19:54:08.519Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/7b/cca826391fb2a94efdcdfe4631eb69306ee1cff0b22f664a412c90713877/cryptography-47.0.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b45761c6ec22b7c726d6a829558777e32d0f1c8be7c3f3480f9c912d5ee8a10", size = 4654350, upload-time = "2026-04-24T19:54:10.795Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4c/65/4b57bcc823f42a991627c51c2f68c9fd6eb1393c1756aac876cba2accae2/cryptography-47.0.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:edd4da498015da5b9f26d38d3bfc2e90257bfa9cbed1f6767c282a0025ae649b", size = 4643394, upload-time = "2026-04-24T19:54:13.275Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/c4/2c5fbeea70adbbca2bbae865e1d605d6a4a7f8dbd9d33eaf69645087f06c/cryptography-47.0.0-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:9af828c0d5a65c70ec729cd7495a4bf1a67ecb66417b8f02ff125ab8a6326a74", size = 5225777, upload-time = "2026-04-24T19:54:15.18Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/b8/ac57107ef32749d2b244e36069bb688792a363aaaa3acc9e3cf84c130315/cryptography-47.0.0-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:256d07c78a04d6b276f5df935a9923275f53bd1522f214447fdf365494e2d515", size = 4688771, upload-time = "2026-04-24T19:54:17.835Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/56/fc/9f1de22ff8be99d991f240a46863c52d475404c408886c5a38d2b5c3bb26/cryptography-47.0.0-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:5d0e362ff51041b0c0d219cc7d6924d7b8996f57ce5712bdcef71eb3c65a59cc", size = 4270753, upload-time = "2026-04-24T19:54:19.963Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/68/d70c852797aa68e8e48d12e5a87170c43f67bb4a59403627259dd57d15de/cryptography-47.0.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:1581aef4219f7ca2849d0250edaa3866212fb74bf5667284f46aa92f9e65c1ca", size = 4642911, upload-time = "2026-04-24T19:54:21.818Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/51/661cbee74f594c5d97ff82d34f10d5551c085ca4668645f4606ebd22bd5d/cryptography-47.0.0-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:a49a3eb5341b9503fa3000a9a0db033161db90d47285291f53c2a9d2cd1b7f76", size = 5181411, upload-time = "2026-04-24T19:54:24.376Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/87/f2b6c374a82cf076cfa1416992ac8e8ec94d79facc37aec87c1a5cb72352/cryptography-47.0.0-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:2207a498b03275d0051589e326b79d4cf59985c99031b05bb292ac52631c37fe", size = 4688262, upload-time = "2026-04-24T19:54:26.946Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/e2/8b7462f4acf21ec509616f0245018bb197194ab0b65c2ea21a0bdd53c0eb/cryptography-47.0.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7a02675e2fabd0c0fc04c868b8781863cbf1967691543c22f5470500ff840b31", size = 4775506, upload-time = "2026-04-24T19:54:28.926Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/70/75/158e494e4c08dc05e039da5bb48553826bd26c23930cf8d3cd5f21fa8921/cryptography-47.0.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80887c5cbd1774683cb126f0ab4184567f080071d5acf62205acb354b4b753b7", size = 4912060, upload-time = "2026-04-24T19:54:30.869Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/bd/0a9d3edbf5eadbac926d7b9b3cd0c4be584eeeae4a003d24d9eda4affbbd/cryptography-47.0.0-cp38-abi3-win32.whl", hash = "sha256:ed67ea4e0cfb5faa5bc7ecb6e2b8838f3807a03758eec239d6c21c8769355310", size = 3248487, upload-time = "2026-04-24T19:54:33.494Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/60/80/5681af756d0da3a599b7bdb586fac5a1540f1bcefd2717a20e611ddade45/cryptography-47.0.0-cp38-abi3-win_amd64.whl", hash = "sha256:835d2d7f47cdc53b3224e90810fb1d36ca94ea29cc1801fb4c1bc43876735769", size = 3755737, upload-time = "2026-04-24T19:54:35.408Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/3d/01f6dd9190170a5a241e0e98c2d04be3664a9e6f5b9b872cde63aff1c3dd/cryptography-48.0.0-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:0c558d2cdffd8f4bbb30fc7134c74d2ca9a476f830bb053074498fbc86f41ed6", size = 8001587, upload-time = "2026-05-04T22:57:36.803Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b2/6e/e90527eef33f309beb811cf7c982c3aeffcce8e3edb178baa4ca3ae4a6fa/cryptography-48.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f5333311663ea94f75dd408665686aaf426563556bb5283554a3539177e03b8c", size = 4690433, upload-time = "2026-05-04T22:57:40.373Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/90/04/673510ed51ddff56575f306cf1617d80411ee76831ccd3097599140efdfe/cryptography-48.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7995ef305d7165c3f11ae07f2517e5a4f1d5c18da1376a0a9ed496336b69e5f3", size = 4710620, upload-time = "2026-05-04T22:57:42.935Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/d5/e9c4ef932c8d800490c34d8bd589d64a31d5890e27ec9e9ad532be893294/cryptography-48.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:40ba1f85eaa6959837b1d51c9767e230e14612eea4ef110ee8854ada22da1bf5", size = 4696283, upload-time = "2026-05-04T22:57:45.294Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/29/174b9dfb60b12d59ecfc6cfa04bc88c21b42a54f01b8aae09bb6e51e4c7f/cryptography-48.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:369a6348999f94bbd53435c894377b20ab95f25a9065c283570e70150d8abc3c", size = 5296573, upload-time = "2026-05-04T22:57:47.933Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/95/38/0d29a6fd7d0d1373f0c0c88a04ba20e359b257753ac497564cd660fc1d55/cryptography-48.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a0e692c683f4df67815a2d258b324e66f4738bd7a96a218c826dce4f4bd05d8f", size = 4743677, upload-time = "2026-05-04T22:57:50.067Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/be/eef653013d5c63b6a490529e0316f9ac14a37602965d4903efed1399f32b/cryptography-48.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:18349bbc56f4743c8b12dc32e2bccb2cf83ee8b69a3bba74ef8ae857e26b3d25", size = 4330808, upload-time = "2026-05-04T22:57:52.301Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/84/9e/500463e87abb7a0a0f9f256ec21123ecde0a7b5541a15e840ea54551fd81/cryptography-48.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e8eac43dfca5c4cccc6dad9a80504436fca53bb9bc3100a2386d730fbe6b602", size = 4695941, upload-time = "2026-05-04T22:57:54.603Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/dc/7303087450c2ec9e7fbb750e17c2abfbc658f23cbd0e54009509b7cc4091/cryptography-48.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9ccdac7d40688ecb5a3b4a604b8a88c8002e3442d6c60aead1db2a89a041560c", size = 5252579, upload-time = "2026-05-04T22:57:57.207Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/c0/7101d3b7215edcdc90c45da544961fd8ed2d6448f77577460fa75a8443f7/cryptography-48.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:bd72e68b06bb1e96913f97dd4901119bc17f39d4586a5adf2d3e47bc2b9d58b5", size = 4743326, upload-time = "2026-05-04T22:57:59.535Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ac/d8/5b833bad13016f562ab9d063d68199a4bd121d18458e439515601d3357ec/cryptography-48.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:59baa2cb386c4f0b9905bd6eb4c2a79a69a128408fd31d32ca4d7102d4156321", size = 4826672, upload-time = "2026-05-04T22:58:01.996Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/98/e1/7074eb8bf3c135558c73fc2bcf0f5633f912e6fb87e868a55c454080ef09/cryptography-48.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9249e3cd978541d665967ac2cb2787fd6a62bddf1e75b3e347a594d7dacf4f74", size = 4972574, upload-time = "2026-05-04T22:58:03.968Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/70/e5a1b41d325f797f39427aa44ef8baf0be500065ab6d8e10369d850d4a4f/cryptography-48.0.0-cp311-abi3-win32.whl", hash = "sha256:9c459db21422be75e2809370b829a87eb37f74cd785fc4aa9ea1e5f43b47cda4", size = 3294868, upload-time = "2026-05-04T22:58:06.467Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/ac/8ac51b4a5fc5932eb7ee5c517ba7dc8cd834f0048962b6b352f00f41ebf9/cryptography-48.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:5b012212e08b8dd5edc78ef54da83dd9892fd9105323b3993eff6bea65dc21d7", size = 3817107, upload-time = "2026-05-04T22:58:08.845Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/84/70e3feea9feea87fd7cbe77efb2712ae1e3e6edf10749dc6e95f4e60e455/cryptography-48.0.0-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:3cb07a3ed6431663cd321ea8a000a1314c74211f823e4177fefa2255e057d1ec", size = 7986556, upload-time = "2026-05-04T22:58:11.172Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/6e/18e07a618bb5442ba10cf4df16e99c071365528aa570dfcb8c02e25a303b/cryptography-48.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8c7378637d7d88016fa6791c159f698b3d3eed28ebf844ac36b9dc04a14dae18", size = 4684776, upload-time = "2026-05-04T22:58:13.712Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/6a/4ea3b4c6c6759794d5ee2103c304a5076dc4b19ae1f9fe47dba439e159e9/cryptography-48.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc90c0b39b2e3c65ef52c804b72e3c58f8a04ab2a1871272798e5f9572c17d20", size = 4698121, upload-time = "2026-05-04T22:58:16.448Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/59/6ff6ad6cae03bb887da2a5860b2c9805f8dac969ef01ce563336c49bd1d1/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:76341972e1eff8b4bea859f09c0d3e64b96ce931b084f9b9b7db8ef364c30eff", size = 4690042, upload-time = "2026-05-04T22:58:18.544Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/b4/fc334ed8cfd705aca282fe4d8f5ae64a8e0f74932e9feecb344610cf6e4d/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:55b7718303bf06a5753dcdccf2f3945cf18ad7bffde41b61226e4db31ab89a9c", size = 5282526, upload-time = "2026-05-04T22:58:20.75Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/11/08/9f8c5386cc4cd90d8255c7cdd0f5baf459a08502a09de30dc51f553d38dc/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:a64697c641c7b1b2178e573cbc31c7c6684cd56883a478d75143dbb7118036db", size = 4733116, upload-time = "2026-05-04T22:58:23.627Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/77/99307d7574045699f8805aa500fa0fb83422d115b5400a064ddd306d7750/cryptography-48.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:561215ea3879cb1cbbf272867e2efda62476f240fb58c64de6b393ae19246741", size = 4316030, upload-time = "2026-05-04T22:58:25.581Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/36/a608b98337af3cb2aff4818e406649d30572b7031918b04c87d979495348/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ad64688338ed4bc1a6618076ba75fd7194a5f1797ac60b47afe926285adb3166", size = 4689640, upload-time = "2026-05-04T22:58:27.747Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dd/a6/825010a291b4438aecc1f568bc428189fc1175515223632477c07dc0a6df/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:906cbf0670286c6e0044156bc7d4af9cbb0ef6db9f73e52c3ec56ba6bdde5336", size = 5237657, upload-time = "2026-05-04T22:58:29.848Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b9/09/4e76a09b4caa29aad535ddc806f5d4c5d01885bd978bd984fbc6ca032cae/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:ea8990436d914540a40ab24b6a77c0969695ed52f4a4874c5137ccf7045a7057", size = 4732362, upload-time = "2026-05-04T22:58:32.009Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/18/78/444fa04a77d0cb95f417dda20d450e13c56ba8e5220fc892a1658f44f882/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c18684a7f0cc9a3cb60328f496b8e3372def7c5d2df39ac267878b05565aaaae", size = 4819580, upload-time = "2026-05-04T22:58:34.254Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/38/85/ea67067c70a1fd4be2c63d35eeed82658023021affccc7b17705f8527dd2/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9be5aafa5736574f8f15f262adc81b2a9869e2cfe9014d52a44633905b40d52c", size = 4963283, upload-time = "2026-05-04T22:58:36.376Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/75/54/cc6d0f3deac3e81c7f847e8a189a12b6cdd65059b43dad25d4316abd849a/cryptography-48.0.0-cp314-cp314t-win32.whl", hash = "sha256:c17dfe85494deaeddc5ce251aebd1d60bbe6afc8b62071bb0b469431a000124f", size = 3270954, upload-time = "2026-05-04T22:58:38.791Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/67/cc947e288c0758a4e5473d1dcb743037ab7785541265a969240b8885441a/cryptography-48.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27241b1dc9962e056062a8eef1991d02c3a24569c95975bd2322a8a52c6e5e12", size = 3797313, upload-time = "2026-05-04T22:58:40.746Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/63/61d4a4e1c6b6bab6ce1e213cd36a24c415d90e76d78c5eb8577c5541d2e8/cryptography-48.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:58d00498e8933e4a194f3076aee1b4a97dfec1a6da444535755822fe5d8b0b86", size = 7983482, upload-time = "2026-05-04T22:58:43.769Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/ac/f5b5995b87770c693e2596559ffafe195b4033a57f14a82268a2842953f3/cryptography-48.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:614d0949f4790582d2cc25553abd09dd723025f0c0e7c67376a1d77196743d6e", size = 4683266, upload-time = "2026-05-04T22:58:46.064Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/c6/8b14f67e18338fbc4adb76f66c001f5c3610b3e2d1837f268f47a347dbbb/cryptography-48.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ce4bfae76319a532a2dc68f82cc32f5676ee792a983187dac07183690e5c66f", size = 4696228, upload-time = "2026-05-04T22:58:48.22Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ea/73/f808fbae9514bd91b47875b003f13e284c8c6bdfd904b7944e803937eec1/cryptography-48.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:2eb992bbd4661238c5a397594c83f5b4dc2bc5b848c365c8f991b6780efcc5c7", size = 4689097, upload-time = "2026-05-04T22:58:50.9Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/93/01/d86632d7d28db8ae83221995752eeb6639ffb374c2d22955648cf8d52797/cryptography-48.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:22a5cb272895dce158b2cacdfdc3debd299019659f42947dbdac6f32d68fe832", size = 5283582, upload-time = "2026-05-04T22:58:53.017Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/e1/50edc7a50334807cc4791fc4a0ce7468b4a1416d9138eab358bfc9a3d70b/cryptography-48.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2b4d59804e8408e2fea7d1fbaf218e5ec984325221db76e6a241a9abd6cdd95c", size = 4730479, upload-time = "2026-05-04T22:58:55.611Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/af/99a582b1b1641ff5911ac559beb45097cf79efd4ead4657f578ef1af2d47/cryptography-48.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:984a20b0f62a26f48a3396c72e4bc34c66e356d356bf370053066b3b6d54634a", size = 4326481, upload-time = "2026-05-04T22:58:57.607Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/90/ee/89aa26a06ef0a7d7611788ffd571a7c50e368cc6a4d5eef8b4884e866edb/cryptography-48.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5a5ed8fde7a1d09376ca0b40e68cd59c69fe23b1f9768bd5824f54681626032a", size = 4688713, upload-time = "2026-05-04T22:59:00.077Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/70/ba/bcb1b0bb7a33d4c7c0c4d4c7874b4a62ae4f56113a5f4baefa362dfb1f0f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:8cd666227ef7af430aa5914a9910e0ddd703e75f039cef0825cd0da71b6b711a", size = 5238165, upload-time = "2026-05-04T22:59:02.317Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/70/ca4003b1ce5ca3dc3186ada51908c8a9b9ff7d5cab83cc0d43ee14ec144f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9071196d81abc88b3516ac8cdfad32e2b66dd4a5393a8e68a961e9161ddc6239", size = 4729947, upload-time = "2026-05-04T22:59:05.255Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/a0/4ec7cf774207905aef1a8d11c3750d5a1db805eb380ee4e16df317870128/cryptography-48.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1e2d54c8be6152856a36f0882ab231e70f8ec7f14e93cf87db8a2ed056bf160c", size = 4822059, upload-time = "2026-05-04T22:59:07.802Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/75/a2e55f99c16fcac7b5d6c1eb19ad8e00799854d6be5ca845f9259eae1681/cryptography-48.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a5da777e32ffed6f85a7b2b3f7c5cbc88c146bfcd0a1d7baf5fcc6c52ee35dd4", size = 4960575, upload-time = "2026-05-04T22:59:09.851Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/23/6e6f32143ab5d8b36ca848a502c4bcd477ae75b9e1677e3530d669062578/cryptography-48.0.0-cp39-abi3-win32.whl", hash = "sha256:77a2ccbbe917f6710e05ba9adaa25fb5075620bf3ea6fb751997875aff4ae4bd", size = 3279117, upload-time = "2026-05-04T22:59:12.019Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/9a/0fea98a70cf1749d41d738836f6349d97945f7c89433a259a6c2642eefeb/cryptography-48.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:16cd65b9330583e4619939b3a3843eec1e6e789744bb01e7c7e2e62e33c239c8", size = 3792100, upload-time = "2026-05-04T22:59:14.884Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1075,16 +1075,16 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "django"
|
||||
version = "5.2.13"
|
||||
version = "5.2.14"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "asgiref" },
|
||||
{ name = "sqlparse" },
|
||||
{ name = "tzdata", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1f/c5/c69e338eb2959f641045802e5ea87ca4bf5ac90c5fd08953ca10742fad51/django-5.2.13.tar.gz", hash = "sha256:a31589db5188d074c63f0945c3888fad104627dfcc236fb2b97f71f89da33bc4", size = 10890368, upload-time = "2026-04-07T14:02:15.072Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/65/95/95f7faa0950867afaa0bef2460c6263afd6a2c78cc9434046ed28160b015/django-5.2.14.tar.gz", hash = "sha256:58a63ba841662e5c686b57ba1fec52ddd68c0b93bd96ac3029d55728f00bf8a2", size = 10895118, upload-time = "2026-05-05T13:57:31.104Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/59/b1/51ab36b2eefcf8cdb9338c7188668a157e29e30306bfc98a379704c9e10d/django-5.2.13-py3-none-any.whl", hash = "sha256:5788fce61da23788a8ce6f02583765ab060d396720924789f97fa42119d37f7a", size = 8310982, upload-time = "2026-04-07T14:02:08.883Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/44/f172870cf87aa25afef48fb72adba89ee8b77fcab6f3b23d240b923f1528/django-5.2.14-py3-none-any.whl", hash = "sha256:6f712143bd3064310d1f50fac859c3e9a274bdcfc9595339853be7779297fc76", size = 8311320, upload-time = "2026-05-05T13:57:25.795Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2733,14 +2733,14 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "psycopg"
|
||||
version = "3.3.3"
|
||||
version = "3.3.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "tzdata", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d3/b6/379d0a960f8f435ec78720462fd94c4863e7a31237cf81bf76d0af5883bf/psycopg-3.3.3.tar.gz", hash = "sha256:5e9a47458b3c1583326513b2556a2a9473a1001a56c9efe9e587245b43148dd9", size = 165624, upload-time = "2026-02-18T16:52:16.546Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/db/2f/cb91e5502ec9de1de6f1b76cfbf69531932725361168bb06963620c77e2e/psycopg-3.3.4.tar.gz", hash = "sha256:e21207764952cff81b6b8bdacad9a3939f2793367fdac2987b3aac36a651b5bc", size = 165799, upload-time = "2026-05-01T23:31:55.179Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/5b/181e2e3becb7672b502f0ed7f16ed7352aca7c109cfb94cf3878a9186db9/psycopg-3.3.3-py3-none-any.whl", hash = "sha256:f96525a72bcfade6584ab17e89de415ff360748c766f0106959144dcbb38c698", size = 212768, upload-time = "2026-02-18T16:46:27.365Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/e0/7b3dee031daae7743609ce3c746565d4a3ed7c2c186479eb48e34e838c64/psycopg-3.3.4-py3-none-any.whl", hash = "sha256:b6bbc25ccf05c8fad3b061d9db2ef0909a555171b84b07f29458a447253d679a", size = 213001, upload-time = "2026-05-01T23:20:50.816Z" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
@@ -2753,9 +2753,9 @@ pool = [
|
||||
|
||||
[[package]]
|
||||
name = "psycopg-c"
|
||||
version = "3.3.3"
|
||||
version = "3.3.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/cb/a0/8feb0ca8c7c20a8b9ac4d46b335ddd57e48e593b714262f006880f34fee5/psycopg_c-3.3.3.tar.gz", hash = "sha256:86ef6f4424348247828e83fb0882c9f8acb33e64d0a5ce66c1b4a5107ee73edd", size = 631965, upload-time = "2026-02-18T16:52:18.084Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/21/7c/c08364f2eab2913e4068b3b955d963e7a3491986a85429990969525def30/psycopg_c-3.3.4.tar.gz", hash = "sha256:ed8106128b2d04359c185fc9641b4409abfce4d0b6fb1d1ff6800646e27f1a22", size = 647111, upload-time = "2026-05-01T23:31:58.032Z" }
|
||||
|
||||
[[package]]
|
||||
name = "psycopg-pool"
|
||||
@@ -2947,14 +2947,14 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "pyopenssl"
|
||||
version = "26.1.0"
|
||||
version = "26.2.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cryptography" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8c/a8/26d36401e3ab8eed9030ad33f381da7856fcfad5691780fccd1b019718fc/pyopenssl-26.1.0.tar.gz", hash = "sha256:737f0a2275c5bc54f3b02137687e1a765931fb3949b9a92a825e4d33b9eec08b", size = 186181, upload-time = "2026-04-24T20:23:48.115Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1a/51/27a5ad5f939d08f690a326ef9582cda7140555180db71695f6fb747d6a36/pyopenssl-26.2.0.tar.gz", hash = "sha256:8c6fcecd1183a7fc897548dfe388b0cdb7f37e018200d8409cf33959dbe35387", size = 182195, upload-time = "2026-05-04T23:06:09.72Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/41/52f3a3e812b816a91e89aa504199d8bf989a1f873192b10762be66cf2009/pyopenssl-26.1.0-py3-none-any.whl", hash = "sha256:115563879b2c8ccb207975705d3e491434d8c9d7c79667c902ecbf5f3bbd2ece", size = 58109, upload-time = "2026-04-24T20:23:46.273Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/73/b8/a0e2790ae249d6f38c9f66de7a211621a7ab2650217bcd04e1262f578a56/pyopenssl-26.2.0-py3-none-any.whl", hash = "sha256:4f9d971bc5298b8bc1fab282803da04bf000c755d4ad9d99b52de2569ca19a70", size = 55823, upload-time = "2026-05-04T23:06:08.395Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
298
web/package-lock.json
generated
298
web/package-lock.json
generated
@@ -44,10 +44,10 @@
|
||||
"@patternfly/patternfly": "^4.224.2",
|
||||
"@playwright/test": "^1.59.1",
|
||||
"@sentry/browser": "^10.50.0",
|
||||
"@storybook/addon-docs": "^10.3.5",
|
||||
"@storybook/addon-links": "^10.3.5",
|
||||
"@storybook/web-components": "^10.3.5",
|
||||
"@storybook/web-components-vite": "^10.3.5",
|
||||
"@storybook/addon-docs": "^10.3.6",
|
||||
"@storybook/addon-links": "^10.3.6",
|
||||
"@storybook/web-components": "^10.3.6",
|
||||
"@storybook/web-components-vite": "^10.3.6",
|
||||
"@types/codemirror": "^5.60.17",
|
||||
"@types/grecaptcha": "^3.0.9",
|
||||
"@types/guacamole-common-js": "^1.5.5",
|
||||
@@ -114,7 +114,7 @@
|
||||
"typescript": "^6.0.3",
|
||||
"typescript-eslint": "^8.57.2",
|
||||
"unist-util-visit": "^5.1.0",
|
||||
"vite": "^8.0.8",
|
||||
"vite": "^8.0.10",
|
||||
"vitest": "^4.1.1",
|
||||
"webcomponent-qr-code": "^1.3.0",
|
||||
"wireit": "^0.14.12",
|
||||
@@ -2895,9 +2895,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@rolldown/binding-android-arm64": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA==",
|
||||
"version": "1.0.0-rc.17",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.17.tgz",
|
||||
"integrity": "sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2911,9 +2911,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-darwin-arm64": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg==",
|
||||
"version": "1.0.0-rc.17",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.17.tgz",
|
||||
"integrity": "sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2927,9 +2927,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-darwin-x64": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw==",
|
||||
"version": "1.0.0-rc.17",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.17.tgz",
|
||||
"integrity": "sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2943,9 +2943,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-freebsd-x64": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw==",
|
||||
"version": "1.0.0-rc.17",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.17.tgz",
|
||||
"integrity": "sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2959,9 +2959,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA==",
|
||||
"version": "1.0.0-rc.17",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.17.tgz",
|
||||
"integrity": "sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -2975,9 +2975,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-arm64-gnu": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w==",
|
||||
"version": "1.0.0-rc.17",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.17.tgz",
|
||||
"integrity": "sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2991,9 +2991,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-arm64-musl": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ==",
|
||||
"version": "1.0.0-rc.17",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.17.tgz",
|
||||
"integrity": "sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -3007,9 +3007,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ==",
|
||||
"version": "1.0.0-rc.17",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.17.tgz",
|
||||
"integrity": "sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -3023,9 +3023,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-s390x-gnu": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ==",
|
||||
"version": "1.0.0-rc.17",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.17.tgz",
|
||||
"integrity": "sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@@ -3039,9 +3039,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-x64-gnu": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA==",
|
||||
"version": "1.0.0-rc.17",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.17.tgz",
|
||||
"integrity": "sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -3055,9 +3055,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-x64-musl": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw==",
|
||||
"version": "1.0.0-rc.17",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.17.tgz",
|
||||
"integrity": "sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -3071,9 +3071,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-openharmony-arm64": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg==",
|
||||
"version": "1.0.0-rc.17",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.17.tgz",
|
||||
"integrity": "sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -3087,27 +3087,48 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-wasm32-wasi": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q==",
|
||||
"version": "1.0.0-rc.17",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.17.tgz",
|
||||
"integrity": "sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA==",
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/core": "1.9.2",
|
||||
"@emnapi/runtime": "1.9.2",
|
||||
"@napi-rs/wasm-runtime": "^1.1.3"
|
||||
"@emnapi/core": "1.10.0",
|
||||
"@emnapi/runtime": "1.10.0",
|
||||
"@napi-rs/wasm-runtime": "^1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/core": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
|
||||
"integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/wasi-threads": "1.2.1",
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/runtime": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
|
||||
"integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-win32-arm64-msvc": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA==",
|
||||
"version": "1.0.0-rc.17",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.17.tgz",
|
||||
"integrity": "sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -3121,9 +3142,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-win32-x64-msvc": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g==",
|
||||
"version": "1.0.0-rc.17",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.17.tgz",
|
||||
"integrity": "sha512-3hkiolcUAvPB9FLb3UZdfjVVNWherN1f/skkGWJP/fgSQhYUZpSIRr0/I8ZK9TkF3F7kxvJAk0+IcKvPHk9qQg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -3137,9 +3158,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/pluginutils": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g==",
|
||||
"version": "1.0.0-rc.17",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.17.tgz",
|
||||
"integrity": "sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@rollup/plugin-commonjs": {
|
||||
@@ -3698,15 +3719,15 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@storybook/addon-docs": {
|
||||
"version": "10.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-10.3.5.tgz",
|
||||
"integrity": "sha512-WuHbxia/o5TX4Rg/IFD0641K5qId/Nk0dxhmAUNoFs5L0+yfZUwh65XOBbzXqrkYmYmcVID4v7cgDRmzstQNkA==",
|
||||
"version": "10.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-10.3.6.tgz",
|
||||
"integrity": "sha512-TvIdADVPtauxW0LzXIpIv7X6GxwetorhyNh+6+7MHC27XSBCWVxxRUwL63YeLlHTuXsIk0quG3b1xgwVRzWOJA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@mdx-js/react": "^3.0.0",
|
||||
"@storybook/csf-plugin": "10.3.5",
|
||||
"@storybook/csf-plugin": "10.3.6",
|
||||
"@storybook/icons": "^2.0.1",
|
||||
"@storybook/react-dom-shim": "10.3.5",
|
||||
"@storybook/react-dom-shim": "10.3.6",
|
||||
"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"
|
||||
@@ -3716,13 +3737,13 @@
|
||||
"url": "https://opencollective.com/storybook"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"storybook": "^10.3.5"
|
||||
"storybook": "^10.3.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/addon-links": {
|
||||
"version": "10.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-10.3.5.tgz",
|
||||
"integrity": "sha512-Xe2wCGZ+hpZ0cDqAIBHk+kPc8nODNbu585ghd5bLrlYJMDVXoNM/fIlkrLgjIDVbfpgeJLUEg7vldJrn+FyOLw==",
|
||||
"version": "10.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-10.3.6.tgz",
|
||||
"integrity": "sha512-tv9Xd68qRGBAvEubaxNo3FuFq4GwuMiBriD+gLGuFK0+/u3cnkuA264aoR1v6YCH3sT3er3+MBimuyKM3jLDxg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@storybook/global": "^5.0.0"
|
||||
@@ -3733,7 +3754,7 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"storybook": "^10.3.5"
|
||||
"storybook": "^10.3.6"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react": {
|
||||
@@ -3742,12 +3763,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/builder-vite": {
|
||||
"version": "10.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-10.3.5.tgz",
|
||||
"integrity": "sha512-i4KwCOKbhtlbQIbhm53+Kk7bMnxa0cwTn1pxmtA/x5wm1Qu7FrrBQV0V0DNjkUqzcSKo1CjspASJV/HlY0zYlw==",
|
||||
"version": "10.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-10.3.6.tgz",
|
||||
"integrity": "sha512-gpvR/sE4BcrFtmQZ+Ker7zD23oQzoVeqD9nF6cK6yzY+Q0svJXyX2EPmFG4y+EwygD5/vNzDpP84gGMut8VRwg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@storybook/csf-plugin": "10.3.5",
|
||||
"@storybook/csf-plugin": "10.3.6",
|
||||
"ts-dedent": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
@@ -3755,14 +3776,14 @@
|
||||
"url": "https://opencollective.com/storybook"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"storybook": "^10.3.5",
|
||||
"storybook": "^10.3.6",
|
||||
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/csf-plugin": {
|
||||
"version": "10.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-10.3.5.tgz",
|
||||
"integrity": "sha512-qlEzNKxOjq86pvrbuMwiGD/bylnsXk1dg7ve0j77YFjEEchqtl7qTlrXvFdNaLA89GhW6D/EV6eOCu/eobPDgw==",
|
||||
"version": "10.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-10.3.6.tgz",
|
||||
"integrity": "sha512-9kBf7VRdRqTSIYo+rPtVn5yjYYyK8kP2QhEYx3oiXvfwy4RexmbJnhk/tXa/lNiTqukA1TqaWQ2+5MqF4fu6YQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"unplugin": "^2.3.5"
|
||||
@@ -3774,7 +3795,7 @@
|
||||
"peerDependencies": {
|
||||
"esbuild": "*",
|
||||
"rollup": "*",
|
||||
"storybook": "^10.3.5",
|
||||
"storybook": "^10.3.6",
|
||||
"vite": "*",
|
||||
"webpack": "*"
|
||||
},
|
||||
@@ -3810,9 +3831,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/react-dom-shim": {
|
||||
"version": "10.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-10.3.5.tgz",
|
||||
"integrity": "sha512-Gw8R7XZm0zSUH0XAuxlQJhmizsLzyD6x00KOlP6l7oW9eQHXGfxg3seNDG3WrSAcW07iP1/P422kuiriQlOv7g==",
|
||||
"version": "10.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-10.3.6.tgz",
|
||||
"integrity": "sha512-/Tu1gPu+Fw+zOnAGmxRmOD30FX3a04LxcTAKflEtdpmtIMVR5bA3qpjy+f5YhoyDCecbXyKmL1OeIU2FIIZHqQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
@@ -3821,13 +3842,13 @@
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"storybook": "^10.3.5"
|
||||
"storybook": "^10.3.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/web-components": {
|
||||
"version": "10.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/web-components/-/web-components-10.3.5.tgz",
|
||||
"integrity": "sha512-tSppZagFCeZ+bWsaHUvdiw17ATWgfGDBz0mFicgEj0/eNuxQH2OvXyRIQUXY39b/55TBwSGeoIX3tOW91WIqpw==",
|
||||
"version": "10.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/web-components/-/web-components-10.3.6.tgz",
|
||||
"integrity": "sha512-femDZGYBGQDckL7F6ZCl2S+dNNBjvd9lp6rQrwBdbNprjctLd6d3EB4HyNM502QxtdEo7laq8y1goDu8KwIV3A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@storybook/global": "^5.0.0",
|
||||
@@ -3840,24 +3861,24 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"lit": "^2.0.0 || ^3.0.0",
|
||||
"storybook": "^10.3.5"
|
||||
"storybook": "^10.3.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/web-components-vite": {
|
||||
"version": "10.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/web-components-vite/-/web-components-vite-10.3.5.tgz",
|
||||
"integrity": "sha512-6uAw6KAUXFsAPzp8KchcMp3gatEnEAd8ylIvzoMzvsIMiHmzXwvDNmoFZnAJ2tmsQGvF4dZRDCBg7PvWdTx8Rg==",
|
||||
"version": "10.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/web-components-vite/-/web-components-vite-10.3.6.tgz",
|
||||
"integrity": "sha512-VeDEAJuOOQV6VAqEF0pilXucS6kp+1ILJVkI+ets6Ku2D+RKeu167YrQAzh1NwzRTv0e5H0anDDNke+sWvg2dg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@storybook/builder-vite": "10.3.5",
|
||||
"@storybook/web-components": "10.3.5"
|
||||
"@storybook/builder-vite": "10.3.6",
|
||||
"@storybook/web-components": "10.3.6"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/storybook"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"storybook": "^10.3.5"
|
||||
"storybook": "^10.3.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@swagger-api/apidom-ast": {
|
||||
@@ -6527,12 +6548,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz",
|
||||
"integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==",
|
||||
"version": "1.16.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.16.0.tgz",
|
||||
"integrity": "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.11",
|
||||
"follow-redirects": "^1.16.0",
|
||||
"form-data": "^4.0.5",
|
||||
"proxy-from-env": "^2.1.0"
|
||||
}
|
||||
@@ -15811,13 +15832,13 @@
|
||||
"license": "Unlicense"
|
||||
},
|
||||
"node_modules/rolldown": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g==",
|
||||
"version": "1.0.0-rc.17",
|
||||
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.17.tgz",
|
||||
"integrity": "sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@oxc-project/types": "=0.124.0",
|
||||
"@rolldown/pluginutils": "1.0.0-rc.15"
|
||||
"@oxc-project/types": "=0.127.0",
|
||||
"@rolldown/pluginutils": "1.0.0-rc.17"
|
||||
},
|
||||
"bin": {
|
||||
"rolldown": "bin/cli.mjs"
|
||||
@@ -15826,30 +15847,21 @@
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rolldown/binding-android-arm64": "1.0.0-rc.15",
|
||||
"@rolldown/binding-darwin-arm64": "1.0.0-rc.15",
|
||||
"@rolldown/binding-darwin-x64": "1.0.0-rc.15",
|
||||
"@rolldown/binding-freebsd-x64": "1.0.0-rc.15",
|
||||
"@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.15",
|
||||
"@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.15",
|
||||
"@rolldown/binding-linux-arm64-musl": "1.0.0-rc.15",
|
||||
"@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.15",
|
||||
"@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.15",
|
||||
"@rolldown/binding-linux-x64-gnu": "1.0.0-rc.15",
|
||||
"@rolldown/binding-linux-x64-musl": "1.0.0-rc.15",
|
||||
"@rolldown/binding-openharmony-arm64": "1.0.0-rc.15",
|
||||
"@rolldown/binding-wasm32-wasi": "1.0.0-rc.15",
|
||||
"@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.15",
|
||||
"@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15"
|
||||
}
|
||||
},
|
||||
"node_modules/rolldown/node_modules/@oxc-project/types": {
|
||||
"version": "0.124.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.124.0.tgz",
|
||||
"integrity": "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/Boshen"
|
||||
"@rolldown/binding-android-arm64": "1.0.0-rc.17",
|
||||
"@rolldown/binding-darwin-arm64": "1.0.0-rc.17",
|
||||
"@rolldown/binding-darwin-x64": "1.0.0-rc.17",
|
||||
"@rolldown/binding-freebsd-x64": "1.0.0-rc.17",
|
||||
"@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.17",
|
||||
"@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.17",
|
||||
"@rolldown/binding-linux-arm64-musl": "1.0.0-rc.17",
|
||||
"@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.17",
|
||||
"@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.17",
|
||||
"@rolldown/binding-linux-x64-gnu": "1.0.0-rc.17",
|
||||
"@rolldown/binding-linux-x64-musl": "1.0.0-rc.17",
|
||||
"@rolldown/binding-openharmony-arm64": "1.0.0-rc.17",
|
||||
"@rolldown/binding-wasm32-wasi": "1.0.0-rc.17",
|
||||
"@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.17",
|
||||
"@rolldown/binding-win32-x64-msvc": "1.0.0-rc.17"
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
@@ -16598,9 +16610,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/storybook": {
|
||||
"version": "10.3.5",
|
||||
"resolved": "https://registry.npmjs.org/storybook/-/storybook-10.3.5.tgz",
|
||||
"integrity": "sha512-uBSZu/GZa9aEIW3QMGvdQPMZWhGxSe4dyRWU8B3/Vd47Gy/XLC7tsBxRr13txmmPOEDHZR94uLuq0H50fvuqBw==",
|
||||
"version": "10.3.6",
|
||||
"resolved": "https://registry.npmjs.org/storybook/-/storybook-10.3.6.tgz",
|
||||
"integrity": "sha512-vbSz7g/1rGMC1uAULqMZjALkIuLu2QABqfhRYhyr/11kzyesi+vAmwyJLukZP1FfecxGOgMwOh6GS0YsGpHAvQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@storybook/global": "^5.0.0",
|
||||
@@ -16625,11 +16637,15 @@
|
||||
"url": "https://opencollective.com/storybook"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prettier": "^2 || ^3"
|
||||
"prettier": "^2 || ^3",
|
||||
"vite-plus": "^0.1.15"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"prettier": {
|
||||
"optional": true
|
||||
},
|
||||
"vite-plus": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -18400,16 +18416,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "8.0.8",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.8.tgz",
|
||||
"integrity": "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==",
|
||||
"version": "8.0.10",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.10.tgz",
|
||||
"integrity": "sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lightningcss": "^1.32.0",
|
||||
"picomatch": "^4.0.4",
|
||||
"postcss": "^8.5.8",
|
||||
"rolldown": "1.0.0-rc.15",
|
||||
"tinyglobby": "^0.2.15"
|
||||
"postcss": "^8.5.10",
|
||||
"rolldown": "1.0.0-rc.17",
|
||||
"tinyglobby": "^0.2.16"
|
||||
},
|
||||
"bin": {
|
||||
"vite": "bin/vite.js"
|
||||
@@ -18490,6 +18506,22 @@
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/tinyglobby": {
|
||||
"version": "0.2.16",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
|
||||
"integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fdir": "^6.5.0",
|
||||
"picomatch": "^4.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/SuperchupuDev"
|
||||
}
|
||||
},
|
||||
"node_modules/vitest": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.5.tgz",
|
||||
|
||||
@@ -120,10 +120,10 @@
|
||||
"@patternfly/patternfly": "^4.224.2",
|
||||
"@playwright/test": "^1.59.1",
|
||||
"@sentry/browser": "^10.50.0",
|
||||
"@storybook/addon-docs": "^10.3.5",
|
||||
"@storybook/addon-links": "^10.3.5",
|
||||
"@storybook/web-components": "^10.3.5",
|
||||
"@storybook/web-components-vite": "^10.3.5",
|
||||
"@storybook/addon-docs": "^10.3.6",
|
||||
"@storybook/addon-links": "^10.3.6",
|
||||
"@storybook/web-components": "^10.3.6",
|
||||
"@storybook/web-components-vite": "^10.3.6",
|
||||
"@types/codemirror": "^5.60.17",
|
||||
"@types/grecaptcha": "^3.0.9",
|
||||
"@types/guacamole-common-js": "^1.5.5",
|
||||
@@ -190,7 +190,7 @@
|
||||
"typescript": "^6.0.3",
|
||||
"typescript-eslint": "^8.57.2",
|
||||
"unist-util-visit": "^5.1.0",
|
||||
"vite": "^8.0.8",
|
||||
"vite": "^8.0.10",
|
||||
"vitest": "^4.1.1",
|
||||
"webcomponent-qr-code": "^1.3.0",
|
||||
"wireit": "^0.14.12",
|
||||
|
||||
@@ -8,6 +8,7 @@ import "#elements/forms/Radio";
|
||||
import "#elements/forms/SearchSelect/index";
|
||||
import "#elements/utils/TimeDeltaHelp";
|
||||
import "./AdminSettingsFooterLinks.js";
|
||||
import "#elements/Alert";
|
||||
|
||||
import { akFooterLinkInput, IFooterLinkInput } from "./AdminSettingsFooterLinks.js";
|
||||
|
||||
@@ -287,6 +288,9 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
|
||||
help=${msg(
|
||||
"When enabled, other flow tabs in a session will refresh upon a successful authentication.",
|
||||
)}
|
||||
.bighelp=${html`<ak-alert class="pf-c-radio__description" inline plain>
|
||||
${msg("This flag is deprecated.")}
|
||||
</ak-alert>`}
|
||||
>
|
||||
</ak-switch-input>
|
||||
<ak-switch-input
|
||||
|
||||
@@ -52,10 +52,6 @@ export class AboutModal extends WithLicenseSummary(WithBrandConfig(AKModal)) {
|
||||
...AKModal.styles,
|
||||
PFAbout,
|
||||
css`
|
||||
:host {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.pf-c-about-modal-box {
|
||||
--pf-c-about-modal-box--BackgroundColor: var(--ak-c-dialog--BackgroundColor);
|
||||
width: unset;
|
||||
|
||||
@@ -135,7 +135,7 @@ export class AdminInterface extends WithCapabilitiesConfig(
|
||||
|
||||
WebsocketClient.connect();
|
||||
|
||||
this.#sidebarMatcher = window.matchMedia("(width >= 1200px)");
|
||||
this.#sidebarMatcher = window.matchMedia("(width > 1210px)");
|
||||
this.sidebarOpen = this.#sidebarMatcher.matches;
|
||||
}
|
||||
|
||||
|
||||
@@ -197,15 +197,17 @@ export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Applicatio
|
||||
?checked=${this.instance?.openInNewTab ?? false}
|
||||
label=${msg("Open in new tab")}
|
||||
help=${msg(
|
||||
"Whether the launch URL will open in a new browser tab or window from the user's application library.",
|
||||
"If checked, the launch URL will open in a new browser tab or window from the user's application library.",
|
||||
)}
|
||||
>
|
||||
</ak-switch-input>
|
||||
<ak-switch-input
|
||||
name="metaHide"
|
||||
?checked=${this.instance?.metaHide ?? false}
|
||||
label=${msg("Hide from User Dashboard")}
|
||||
help=${msg("Whether this application will be shown on the User Dashboard.")}
|
||||
label=${msg("Hide from My applications")}
|
||||
help=${msg(
|
||||
"If checked, this application will not be shown on the user's My applications page.",
|
||||
)}
|
||||
>
|
||||
</ak-switch-input>
|
||||
<ak-file-search-input
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
RACProvider,
|
||||
RadiusProvider,
|
||||
RedirectURI,
|
||||
RedirectUriTypeEnum,
|
||||
RedirectURITypeEnum,
|
||||
SAMLProvider,
|
||||
SCIMProvider,
|
||||
WSFederationProvider,
|
||||
@@ -87,7 +87,7 @@ function formatRedirectUris(uris: RedirectURI[] = []) {
|
||||
(${uri.matchingMode === MatchingModeEnum.Strict
|
||||
? msg("strict")
|
||||
: msg("regexp")},
|
||||
${uri.redirectUriType === RedirectUriTypeEnum.Logout
|
||||
${uri.redirectUriType === RedirectURITypeEnum.Logout
|
||||
? msg("post logout")
|
||||
: msg("authorization")})
|
||||
</li>`,
|
||||
|
||||
@@ -183,16 +183,16 @@ export class ApplicationWizardApplicationStep extends ApplicationWizardStep {
|
||||
?checked=${app.openInNewTab ?? false}
|
||||
label=${msg("Open in new tab")}
|
||||
help=${msg(
|
||||
"Whether the launch URL will open in a new browser tab or window from the user's application library.",
|
||||
"If checked, the launch URL will open in a new browser tab or window from the user's application library.",
|
||||
)}
|
||||
>
|
||||
</ak-switch-input>
|
||||
<ak-switch-input
|
||||
name="metaHide"
|
||||
?checked=${app.metaHide ?? false}
|
||||
label=${msg("Hide from User Dashboard")}
|
||||
label=${msg("Hide from My applications")}
|
||||
help=${msg(
|
||||
"Whether this application will be shown on the User Dashboard.",
|
||||
"If checked, this application will not be shown on the user's My applications page.",
|
||||
)}
|
||||
>
|
||||
</ak-switch-input>
|
||||
|
||||
@@ -124,19 +124,22 @@ export class ApplicationWizardBindingsStep extends ApplicationWizardStep {
|
||||
order="order"
|
||||
.columns=${COLUMNS}
|
||||
.content=${[]}
|
||||
></ak-select-table>
|
||||
<ak-empty-state icon="pf-icon-module"
|
||||
><span>${msg("No bound policies.")}</span>
|
||||
<div slot="body">${msg("No policies are currently bound to this object.")}</div>
|
||||
<div slot="primary">
|
||||
<button
|
||||
@click=${() => this.onBindingEvent()}
|
||||
class="pf-c-button pf-m-primary"
|
||||
>
|
||||
${msg("Bind policy/group/user")}
|
||||
</button>
|
||||
</div>
|
||||
</ak-empty-state>
|
||||
>
|
||||
<ak-empty-state slot="empty-table" icon="pf-icon-module"
|
||||
><span>${msg("No bound policies.")}</span>
|
||||
<div slot="body">
|
||||
${msg("No policies are currently bound to this object.")}
|
||||
</div>
|
||||
<div slot="primary">
|
||||
<button
|
||||
@click=${() => this.onBindingEvent()}
|
||||
class="pf-c-button pf-m-primary"
|
||||
>
|
||||
${msg("Bind policy/group/user")}
|
||||
</button>
|
||||
</div>
|
||||
</ak-empty-state>
|
||||
</ak-select-table>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
|
||||
@@ -381,7 +381,7 @@ export class ApplicationWizardSubmitStep extends CustomEmitterElement(Applicatio
|
||||
return html`<h2 class="pf-c-wizard__main-title">
|
||||
${msg("Review the Application and Provider")}
|
||||
</h2>
|
||||
<fieldset>
|
||||
<fieldset class="ak-c-fieldset" name="application-details">
|
||||
<legend>${msg("Application Details")}</legend>
|
||||
<dl class="pf-c-description-list">
|
||||
<div class="pf-c-description-list__group">
|
||||
@@ -419,7 +419,7 @@ export class ApplicationWizardSubmitStep extends CustomEmitterElement(Applicatio
|
||||
|
||||
${
|
||||
renderer
|
||||
? html`<fieldset>
|
||||
? html`<fieldset class="ak-c-fieldset" name="provider-details">
|
||||
<legend>${msg("Provider Details")}</legend>
|
||||
${renderer(provider)}
|
||||
</fieldset>`
|
||||
|
||||
@@ -86,7 +86,7 @@ export class ConfigModal extends ModalButton {
|
||||
></ak-codemirror>
|
||||
</ak-expand>
|
||||
</div>
|
||||
<fieldset class="pf-c-modal-box__footer">
|
||||
<fieldset class="ak-c-fieldset pf-c-modal-box__footer">
|
||||
<legend class="sr-only">${msg("Form actions")}</legend>
|
||||
<button
|
||||
class="pf-c-button pf-m-plain"
|
||||
|
||||
@@ -65,7 +65,7 @@ export class DeviceAddHowTo extends ModalButton {
|
||||
})}
|
||||
</ak-tabs>`}
|
||||
</div>
|
||||
<fieldset class="pf-c-modal-box__footer">
|
||||
<fieldset class="ak-c-fieldset pf-c-modal-box__footer">
|
||||
<legend class="sr-only">${msg("Form actions")}</legend>
|
||||
<button
|
||||
class="pf-c-button pf-m-primary"
|
||||
|
||||
@@ -36,7 +36,7 @@ import {
|
||||
OAuth2Provider,
|
||||
OAuth2ProviderLogoutMethodEnum,
|
||||
RedirectURI,
|
||||
RedirectUriTypeEnum,
|
||||
RedirectURITypeEnum,
|
||||
SubModeEnum,
|
||||
ValidationError,
|
||||
} from "@goauthentik/api";
|
||||
@@ -270,7 +270,7 @@ export function renderForm({
|
||||
.newItem=${() => ({
|
||||
matchingMode: MatchingModeEnum.Strict,
|
||||
url: "",
|
||||
redirectUriType: RedirectUriTypeEnum.Authorization,
|
||||
redirectUriType: RedirectURITypeEnum.Authorization,
|
||||
})}
|
||||
.row=${(redirectURI: RedirectURI, idx: number) => {
|
||||
return html`<ak-provider-oauth2-redirect-uri
|
||||
|
||||
@@ -4,7 +4,7 @@ import { AKControlElement } from "#elements/ControlElement";
|
||||
import { LitPropertyRecord } from "#elements/types";
|
||||
import { ifPresent } from "#elements/utils/attributes";
|
||||
|
||||
import { MatchingModeEnum, RedirectURI, RedirectUriTypeEnum } from "@goauthentik/api";
|
||||
import { MatchingModeEnum, RedirectURI, RedirectURITypeEnum } from "@goauthentik/api";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { css, html } from "lit";
|
||||
@@ -37,7 +37,7 @@ export class OAuth2ProviderRedirectURI extends AKControlElement<RedirectURI> {
|
||||
public redirectURI: RedirectURI = {
|
||||
matchingMode: MatchingModeEnum.Strict,
|
||||
url: "",
|
||||
redirectUriType: RedirectUriTypeEnum.Authorization,
|
||||
redirectUriType: RedirectURITypeEnum.Authorization,
|
||||
};
|
||||
|
||||
@property({ type: String, useDefault: true })
|
||||
@@ -89,15 +89,15 @@ export class OAuth2ProviderRedirectURI extends AKControlElement<RedirectURI> {
|
||||
@change=${onChange}
|
||||
>
|
||||
<option
|
||||
value="${RedirectUriTypeEnum.Authorization}"
|
||||
value="${RedirectURITypeEnum.Authorization}"
|
||||
?selected=${(this.redirectURI.redirectUriType ??
|
||||
RedirectUriTypeEnum.Authorization) === RedirectUriTypeEnum.Authorization}
|
||||
RedirectURITypeEnum.Authorization) === RedirectURITypeEnum.Authorization}
|
||||
>
|
||||
${msg("Authorization")}
|
||||
</option>
|
||||
<option
|
||||
value="${RedirectUriTypeEnum.Logout}"
|
||||
?selected=${this.redirectURI.redirectUriType === RedirectUriTypeEnum.Logout}
|
||||
value="${RedirectURITypeEnum.Logout}"
|
||||
?selected=${this.redirectURI.redirectUriType === RedirectURITypeEnum.Logout}
|
||||
>
|
||||
${msg("Post Logout")}
|
||||
</option>
|
||||
|
||||
@@ -158,24 +158,33 @@ export class AuthenticatorWebAuthnStageForm extends BaseStageForm<AuthenticatorW
|
||||
<ak-radio
|
||||
.options=${[
|
||||
{
|
||||
label: msg("No preference is sent"),
|
||||
label: msg(
|
||||
"No preference: the browser may offer any available authenticator",
|
||||
),
|
||||
value: null,
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
label: msg(
|
||||
"A non-removable authenticator, like TouchID or Windows Hello",
|
||||
"Platform: a non-removable authenticator built into the device, such as Touch ID, Face ID, or Windows Hello",
|
||||
),
|
||||
value: AuthenticatorAttachmentEnum.Platform,
|
||||
},
|
||||
{
|
||||
label: msg('A "roaming" authenticator, like a YubiKey'),
|
||||
label: msg(
|
||||
"Cross-platform: a roaming authenticator, such as a YubiKey or Google Titan",
|
||||
),
|
||||
value: AuthenticatorAttachmentEnum.CrossPlatform,
|
||||
},
|
||||
]}
|
||||
.value=${this.instance?.authenticatorAttachment}
|
||||
>
|
||||
</ak-radio>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg(
|
||||
"Controls the authenticatorAttachment parameter sent to the browser during WebAuthn registration. If Hints are configured and this is left as 'No preference', a value is inferred from the selected hints for backward compatibility with older browsers.",
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Hints")} name="hints">
|
||||
<ak-dual-select-provider
|
||||
|
||||
@@ -10,7 +10,7 @@ import { AKElement } from "#elements/Base";
|
||||
import { Invitation, StagesApi } from "@goauthentik/api";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, html, nothing, TemplateResult } from "lit";
|
||||
import { css, CSSResult, html, nothing, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { until } from "lit/directives/until.js";
|
||||
|
||||
@@ -27,7 +27,30 @@ export class InvitationListLink extends AKElement {
|
||||
@property()
|
||||
selectedFlow?: string;
|
||||
|
||||
static styles: CSSResult[] = [PFForm, PFFormControl, PFDescriptionList, PFButton];
|
||||
/**
|
||||
* When true, the "Send via Email" button dispatches the
|
||||
* `ak-invitation-send-email-inline` event instead of opening the nested
|
||||
* email modal. Used by the invitation wizard's success step so the email
|
||||
* form can be rendered as its own wizard step.
|
||||
*/
|
||||
@property({ type: Boolean, attribute: "inline-send-email" })
|
||||
inlineSendEmail = false;
|
||||
|
||||
static styles: CSSResult[] = [
|
||||
PFForm,
|
||||
PFFormControl,
|
||||
PFDescriptionList,
|
||||
PFButton,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
input.pf-c-form-control {
|
||||
width: 100%;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
renderLink(): string {
|
||||
if (this.invitation?.flowObj) {
|
||||
@@ -103,6 +126,7 @@ export class InvitationListLink extends AKElement {
|
||||
class="pf-c-form-control"
|
||||
readonly
|
||||
type="text"
|
||||
style="width: 100%;"
|
||||
value=${this.renderLink()}
|
||||
/>
|
||||
</div>
|
||||
@@ -122,18 +146,32 @@ export class InvitationListLink extends AKElement {
|
||||
>
|
||||
${msg("Copy Link")}
|
||||
</button>
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">${msg("Send")}</span>
|
||||
<span slot="header">${msg("Send Invitation via Email")}</span>
|
||||
<ak-invitation-send-email-form
|
||||
slot="form"
|
||||
.invitation=${this.invitation}
|
||||
>
|
||||
</ak-invitation-send-email-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${msg("Send via Email")}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
${this.inlineSendEmail
|
||||
? html`<button
|
||||
class="pf-c-button pf-m-secondary"
|
||||
@click=${() => {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("ak-invitation-send-email-inline", {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
}}
|
||||
>
|
||||
${msg("Send via Email")}
|
||||
</button>`
|
||||
: html`<ak-forms-modal>
|
||||
<span slot="submit">${msg("Send")}</span>
|
||||
<span slot="header">${msg("Send Invitation via Email")}</span>
|
||||
<ak-invitation-send-email-form
|
||||
slot="form"
|
||||
.invitation=${this.invitation}
|
||||
>
|
||||
</ak-invitation-send-email-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${msg("Send via Email")}
|
||||
</button>
|
||||
</ak-forms-modal>`}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import "#admin/rbac/ObjectPermissionModal";
|
||||
import "#admin/stages/invitation/InvitationForm";
|
||||
import "#admin/stages/invitation/InvitationListLink";
|
||||
import "#admin/stages/invitation/wizard/InvitationWizard";
|
||||
import "#elements/buttons/Dropdown";
|
||||
import "#elements/buttons/ModalButton";
|
||||
import "#elements/buttons/SpinnerButton/ak-spinner-button";
|
||||
import "#elements/forms/DeleteBulkForm";
|
||||
@@ -9,7 +11,7 @@ import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
|
||||
|
||||
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
|
||||
import { IconEditButton, ModalInvokerButton } from "#elements/dialogs";
|
||||
import { IconEditButton, modalInvoker } from "#elements/dialogs";
|
||||
import { PFColor } from "#elements/Label";
|
||||
import { PaginatedResponse, TableColumn } from "#elements/table/Table";
|
||||
import { TablePage } from "#elements/table/TablePage";
|
||||
@@ -18,11 +20,12 @@ import { SlottedTemplateResult } from "#elements/types";
|
||||
import { setPageDetails } from "#components/ak-page-navbar";
|
||||
|
||||
import { InvitationForm } from "#admin/stages/invitation/InvitationForm";
|
||||
import { InvitationWizard } from "#admin/stages/invitation/wizard/InvitationWizard";
|
||||
|
||||
import { FlowDesignationEnum, Invitation, ModelEnum, StagesApi } from "@goauthentik/api";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, html, PropertyValues } from "lit";
|
||||
import { CSSResult, html, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
|
||||
import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";
|
||||
@@ -139,7 +142,66 @@ export class InvitationListPage extends TablePage<Invitation> {
|
||||
}
|
||||
|
||||
protected override renderObjectCreate(): SlottedTemplateResult {
|
||||
return ModalInvokerButton(InvitationForm);
|
||||
return html`${this.renderNewInvitationDropdown()}`;
|
||||
}
|
||||
|
||||
protected renderNewInvitationDropdown(): TemplateResult {
|
||||
return html`<ak-dropdown class="pf-c-dropdown">
|
||||
<div class="pf-c-dropdown__toggle pf-m-primary pf-m-split-button pf-m-action">
|
||||
<button
|
||||
class="pf-c-dropdown__toggle-button"
|
||||
type="button"
|
||||
${modalInvoker(InvitationWizard, { mode: "existing" })}
|
||||
>
|
||||
${msg("New Invitation")}
|
||||
</button>
|
||||
<button
|
||||
class="pf-c-dropdown__toggle-button"
|
||||
type="button"
|
||||
id="new-invitation-toggle"
|
||||
aria-haspopup="menu"
|
||||
aria-controls="new-invitation-menu"
|
||||
tabindex="0"
|
||||
aria-label=${msg("New Invitation options")}
|
||||
>
|
||||
<i class="fas fa-caret-down" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<menu
|
||||
class="pf-c-dropdown__menu"
|
||||
hidden
|
||||
id="new-invitation-menu"
|
||||
aria-labelledby="new-invitation-toggle"
|
||||
tabindex="-1"
|
||||
>
|
||||
<li role="presentation">
|
||||
<button
|
||||
type="button"
|
||||
role="menuitem"
|
||||
class="pf-c-dropdown__menu-item"
|
||||
${modalInvoker(InvitationWizard, { mode: "existing" })}
|
||||
aria-description=${msg(
|
||||
"Opens the new invitation wizard and binds the invitation to an existing enrollment flow.",
|
||||
)}
|
||||
>
|
||||
${msg("with Existing Enrollment Flow...")}
|
||||
</button>
|
||||
</li>
|
||||
<li role="presentation">
|
||||
<button
|
||||
type="button"
|
||||
role="menuitem"
|
||||
class="pf-c-dropdown__menu-item"
|
||||
${modalInvoker(InvitationWizard, { mode: "create" })}
|
||||
aria-description=${msg(
|
||||
"Opens the new invitation wizard, which will create a new enrollment flow and invitation stage.",
|
||||
)}
|
||||
>
|
||||
${msg("with New Enrollment Flow and Invitation Stage...")}
|
||||
</button>
|
||||
</li>
|
||||
</menu>
|
||||
</ak-dropdown>`;
|
||||
}
|
||||
|
||||
protected override render(): SlottedTemplateResult {
|
||||
|
||||
62
web/src/admin/stages/invitation/wizard/InvitationWizard.ts
Normal file
62
web/src/admin/stages/invitation/wizard/InvitationWizard.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import "#admin/stages/invitation/wizard/InvitationWizardDetailsStep";
|
||||
import "#admin/stages/invitation/wizard/InvitationWizardEmailStep";
|
||||
import "#admin/stages/invitation/wizard/InvitationWizardFlowStep";
|
||||
import "#admin/stages/invitation/wizard/InvitationWizardSuccessStep";
|
||||
import "#elements/wizard/Wizard";
|
||||
|
||||
import { AKElement } from "#elements/Base";
|
||||
import { TransclusionChildElement, TransclusionChildSymbol } from "#elements/dialogs";
|
||||
import { SlottedTemplateResult } from "#elements/types";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { customElement } from "@lit/reactive-element/decorators/custom-element.js";
|
||||
import { property } from "@lit/reactive-element/decorators/property.js";
|
||||
import { html } from "lit";
|
||||
|
||||
export type InvitationWizardFlowMode = "existing" | "create";
|
||||
|
||||
@customElement("ak-invitation-wizard")
|
||||
export class InvitationWizard extends AKElement implements TransclusionChildElement {
|
||||
public static verboseName = msg("Invitation");
|
||||
|
||||
public [TransclusionChildSymbol] = true;
|
||||
|
||||
@property({ type: String })
|
||||
public mode: InvitationWizardFlowMode = "existing";
|
||||
|
||||
protected override createRenderRoot(): HTMLElement | DocumentFragment {
|
||||
return this;
|
||||
}
|
||||
|
||||
protected override render(): SlottedTemplateResult {
|
||||
return html`<ak-wizard
|
||||
entity-singular=${msg("Invitation")}
|
||||
description=${msg("Create a new invitation with an enrollment flow.")}
|
||||
.initialSteps=${["flow-step", "details-step", "success-step"]}
|
||||
>
|
||||
<ak-invitation-wizard-flow-step
|
||||
slot="flow-step"
|
||||
headline=${msg("Enrollment Flow")}
|
||||
.mode=${this.mode}
|
||||
></ak-invitation-wizard-flow-step>
|
||||
<ak-invitation-wizard-details-step
|
||||
slot="details-step"
|
||||
headline=${msg("Invitation Details")}
|
||||
></ak-invitation-wizard-details-step>
|
||||
<ak-invitation-wizard-success-step
|
||||
slot="success-step"
|
||||
headline=${msg("Invitation Link")}
|
||||
></ak-invitation-wizard-success-step>
|
||||
<ak-invitation-wizard-email-step
|
||||
slot="email-step"
|
||||
headline=${msg("Send via Email")}
|
||||
></ak-invitation-wizard-email-step>
|
||||
</ak-wizard>`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ak-invitation-wizard": InvitationWizard;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
import "#components/ak-switch-input";
|
||||
import "#elements/CodeMirror";
|
||||
import "#elements/forms/HorizontalFormElement";
|
||||
|
||||
import type { InvitationWizardState } from "./types";
|
||||
|
||||
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
import {
|
||||
parseAPIResponseError,
|
||||
pluckErrorDetail,
|
||||
pluckFallbackFieldErrors,
|
||||
} from "#common/errors/network";
|
||||
import { MessageLevel } from "#common/messages";
|
||||
import { dateTimeLocal } from "#common/temporal";
|
||||
|
||||
import { showMessage } from "#elements/messages/MessageContainer";
|
||||
import { WizardPage } from "#elements/wizard/WizardPage";
|
||||
|
||||
import { FlowsApi, ManagedApi, StagesApi } from "@goauthentik/api";
|
||||
|
||||
import YAML from "yaml";
|
||||
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { CSSResult, html, TemplateResult } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
const MINIMAL_BLUEPRINT_PATH = "example/flows-invitation-enrollment-minimal.yaml";
|
||||
|
||||
@customElement("ak-invitation-wizard-details-step")
|
||||
export class InvitationWizardDetailsStep extends WizardPage {
|
||||
static styles: CSSResult[] = [PFBase, PFForm, PFFormControl];
|
||||
|
||||
@state()
|
||||
invitationName = "";
|
||||
|
||||
@state()
|
||||
invitationExpires: string = dateTimeLocal(new Date(Date.now() + 48 * 60 * 60 * 1000));
|
||||
|
||||
@state()
|
||||
fixedDataRaw = "{}";
|
||||
|
||||
@state()
|
||||
singleUse = true;
|
||||
|
||||
activeCallback = async (): Promise<void> => {
|
||||
this.host.valid = this.invitationName.length > 0;
|
||||
};
|
||||
|
||||
async #fail(step: string, err: unknown): Promise<false> {
|
||||
const parsed = await parseAPIResponseError(err);
|
||||
const fieldErrors = pluckFallbackFieldErrors(parsed);
|
||||
const detail = fieldErrors.length > 0 ? fieldErrors.join(" ") : pluckErrorDetail(parsed);
|
||||
showMessage({
|
||||
level: MessageLevel.error,
|
||||
message: msg(str`${step} failed`),
|
||||
description: detail,
|
||||
});
|
||||
this.logger.error("Invitation wizard step failed", { step, error: err });
|
||||
return false;
|
||||
}
|
||||
|
||||
validate(): void {
|
||||
let validYaml = true;
|
||||
try {
|
||||
YAML.parse(this.fixedDataRaw);
|
||||
} catch {
|
||||
validYaml = false;
|
||||
}
|
||||
this.host.valid =
|
||||
this.invitationName.length > 0 && this.invitationExpires.length > 0 && validYaml;
|
||||
}
|
||||
|
||||
nextCallback = async (): Promise<boolean> => {
|
||||
if (!this.invitationName) return false;
|
||||
|
||||
let fixedData: Record<string, unknown> = {};
|
||||
try {
|
||||
fixedData = YAML.parse(this.fixedDataRaw) || {};
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
|
||||
const wizardState = this.host.state as unknown as InvitationWizardState;
|
||||
|
||||
if (wizardState.createdInvitationPk) {
|
||||
return true;
|
||||
}
|
||||
|
||||
wizardState.invitationName = this.invitationName;
|
||||
wizardState.invitationExpires = this.invitationExpires;
|
||||
wizardState.invitationFixedData = fixedData;
|
||||
wizardState.invitationSingleUse = this.singleUse;
|
||||
|
||||
if (wizardState.needsFlow) {
|
||||
try {
|
||||
const result = await new ManagedApi(DEFAULT_CONFIG).managedBlueprintsImportCreate({
|
||||
path: MINIMAL_BLUEPRINT_PATH,
|
||||
context: JSON.stringify({
|
||||
flow_name: wizardState.newFlowName,
|
||||
flow_slug: wizardState.newFlowSlug,
|
||||
stage_name: wizardState.newStageName,
|
||||
continue_flow_without_invitation: wizardState.continueFlowWithoutInvitation,
|
||||
user_type: wizardState.newUserType,
|
||||
}),
|
||||
});
|
||||
if (!result.success) {
|
||||
const logs = (result.logs || [])
|
||||
.map((l) => l.event)
|
||||
.filter((m) => !!m)
|
||||
.join("\n");
|
||||
return this.#fail(
|
||||
msg("Importing enrollment flow blueprint"),
|
||||
new Error(logs || msg("Blueprint validation failed")),
|
||||
);
|
||||
}
|
||||
|
||||
const slugToLookup = wizardState.newFlowSlug!;
|
||||
const flows = await new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({
|
||||
slug: slugToLookup,
|
||||
});
|
||||
const createdFlow = flows.results[0];
|
||||
if (!createdFlow) {
|
||||
return this.#fail(
|
||||
msg("Importing enrollment flow blueprint"),
|
||||
new Error(
|
||||
msg(str`Flow with slug "${slugToLookup}" not found after import`),
|
||||
),
|
||||
);
|
||||
}
|
||||
wizardState.createdFlowPk = createdFlow.pk;
|
||||
wizardState.createdFlowSlug = createdFlow.slug;
|
||||
wizardState.needsFlow = false;
|
||||
wizardState.needsStage = false;
|
||||
wizardState.needsBinding = false;
|
||||
} catch (err) {
|
||||
return this.#fail(msg("Importing enrollment flow blueprint"), err);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const flowPk = wizardState.createdFlowPk || wizardState.selectedFlowPk || undefined;
|
||||
const invitation = await new StagesApi(
|
||||
DEFAULT_CONFIG,
|
||||
).stagesInvitationInvitationsCreate({
|
||||
invitationRequest: {
|
||||
name: wizardState.invitationName!,
|
||||
expires: wizardState.invitationExpires
|
||||
? new Date(wizardState.invitationExpires)
|
||||
: undefined,
|
||||
fixedData: wizardState.invitationFixedData,
|
||||
singleUse: wizardState.invitationSingleUse,
|
||||
flow: flowPk || null,
|
||||
},
|
||||
});
|
||||
wizardState.createdInvitationPk = invitation.pk;
|
||||
wizardState.createdInvitation = invitation;
|
||||
} catch (err) {
|
||||
return this.#fail(msg("Creating invitation"), err);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
override reset(): void {
|
||||
this.invitationName = "";
|
||||
this.invitationExpires = dateTimeLocal(new Date(Date.now() + 48 * 60 * 60 * 1000));
|
||||
this.fixedDataRaw = "{}";
|
||||
this.singleUse = true;
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
const wizardState = this.host.state as unknown as InvitationWizardState;
|
||||
const flowDisplay =
|
||||
wizardState.flowMode === "existing"
|
||||
? wizardState.selectedFlowSlug
|
||||
: wizardState.newFlowSlug;
|
||||
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal label=${msg("Name")} required>
|
||||
<input
|
||||
type="text"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
.value=${this.invitationName}
|
||||
@input=${(ev: InputEvent) => {
|
||||
const target = ev.target as HTMLInputElement;
|
||||
this.invitationName = target.value.replace(/[^a-z0-9-]/g, "");
|
||||
target.value = this.invitationName;
|
||||
this.validate();
|
||||
}}
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg(
|
||||
"The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.",
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Expires")} required>
|
||||
<input
|
||||
type="datetime-local"
|
||||
data-type="datetime-local"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
.value=${this.invitationExpires}
|
||||
@input=${(ev: InputEvent) => {
|
||||
this.invitationExpires = (ev.target as HTMLInputElement).value;
|
||||
this.validate();
|
||||
}}
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Flow")}>
|
||||
<input
|
||||
type="text"
|
||||
class="pf-c-form-control"
|
||||
readonly
|
||||
disabled
|
||||
.value=${flowDisplay || ""}
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg(
|
||||
"The flow selected in the previous step. The invitation will be bound to this flow.",
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Custom attributes")}>
|
||||
<ak-codemirror
|
||||
mode="yaml"
|
||||
.value=${this.fixedDataRaw}
|
||||
@change=${(ev: CustomEvent) => {
|
||||
this.fixedDataRaw = ev.detail.value;
|
||||
this.validate();
|
||||
}}
|
||||
>
|
||||
</ak-codemirror>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg(
|
||||
"Optional data which is loaded into the flow's 'prompt_data' context variable. YAML or JSON.",
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-switch-input
|
||||
label=${msg("Single use")}
|
||||
?checked=${this.singleUse}
|
||||
@change=${(ev: Event) => {
|
||||
this.singleUse = (ev.target as HTMLInputElement).checked;
|
||||
}}
|
||||
help=${msg("When enabled, the invitation will be deleted after usage.")}
|
||||
></ak-switch-input>
|
||||
</form>`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ak-invitation-wizard-details-step": InvitationWizardDetailsStep;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
import "#components/ak-textarea-input";
|
||||
import "#elements/forms/HorizontalFormElement";
|
||||
|
||||
import type { InvitationWizardState } from "./types";
|
||||
|
||||
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
import {
|
||||
parseAPIResponseError,
|
||||
pluckErrorDetail,
|
||||
pluckFallbackFieldErrors,
|
||||
} from "#common/errors/network";
|
||||
import { AKRefreshEvent } from "#common/events";
|
||||
import { MessageLevel } from "#common/messages";
|
||||
|
||||
import { showMessage } from "#elements/messages/MessageContainer";
|
||||
import { SlottedTemplateResult } from "#elements/types";
|
||||
import { WizardPage } from "#elements/wizard/WizardPage";
|
||||
|
||||
import { StagesApi, TypeCreate } from "@goauthentik/api";
|
||||
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { CSSResult, html, TemplateResult } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
@customElement("ak-invitation-wizard-email-step")
|
||||
export class InvitationWizardEmailStep extends WizardPage {
|
||||
static styles: CSSResult[] = [PFBase, PFForm, PFFormControl];
|
||||
|
||||
@state()
|
||||
toAddresses = "";
|
||||
|
||||
@state()
|
||||
ccAddresses = "";
|
||||
|
||||
@state()
|
||||
bccAddresses = "";
|
||||
|
||||
@state()
|
||||
template = "email/invitation.html";
|
||||
|
||||
@state()
|
||||
availableTemplates: TypeCreate[] = [];
|
||||
|
||||
override formatNextLabel(): SlottedTemplateResult {
|
||||
return html`${msg("Send")}
|
||||
<span class="pf-c-button__icon pf-m-end">
|
||||
<i class="fas fa-paper-plane" aria-hidden="true"></i>
|
||||
</span>`;
|
||||
}
|
||||
|
||||
activeCallback = async (): Promise<void> => {
|
||||
this.host.valid = this.toAddresses.trim().length > 0;
|
||||
try {
|
||||
this.availableTemplates = await new StagesApi(
|
||||
DEFAULT_CONFIG,
|
||||
).stagesEmailTemplatesList();
|
||||
} catch {
|
||||
this.availableTemplates = [];
|
||||
}
|
||||
};
|
||||
|
||||
parseEmailAddresses(raw: string): string[] {
|
||||
return raw
|
||||
.split(/[\n,;]/)
|
||||
.map((value) => value.trim())
|
||||
.filter((value) => value.length > 0);
|
||||
}
|
||||
|
||||
validate(): void {
|
||||
this.host.valid = this.parseEmailAddresses(this.toAddresses).length > 0;
|
||||
}
|
||||
|
||||
nextCallback = async (): Promise<boolean> => {
|
||||
const wizardState = this.host.state as unknown as InvitationWizardState;
|
||||
const invitationPk = wizardState.createdInvitationPk;
|
||||
if (!invitationPk) {
|
||||
showMessage({
|
||||
level: MessageLevel.error,
|
||||
message: msg("No invitation available to send"),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const to = this.parseEmailAddresses(this.toAddresses);
|
||||
if (to.length === 0) {
|
||||
showMessage({
|
||||
level: MessageLevel.error,
|
||||
message: msg("Please enter at least one email address"),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
const cc = this.parseEmailAddresses(this.ccAddresses);
|
||||
const bcc = this.parseEmailAddresses(this.bccAddresses);
|
||||
|
||||
try {
|
||||
await new StagesApi(DEFAULT_CONFIG).stagesInvitationInvitationsSendEmailCreate({
|
||||
inviteUuid: invitationPk,
|
||||
invitationSendEmailRequest: {
|
||||
emailAddresses: to,
|
||||
ccAddresses: cc.length > 0 ? cc : undefined,
|
||||
bccAddresses: bcc.length > 0 ? bcc : undefined,
|
||||
template: this.template,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
const parsed = await parseAPIResponseError(err);
|
||||
const fieldErrors = pluckFallbackFieldErrors(parsed);
|
||||
const detail =
|
||||
fieldErrors.length > 0 ? fieldErrors.join(" ") : pluckErrorDetail(parsed);
|
||||
showMessage({
|
||||
level: MessageLevel.error,
|
||||
message: msg("Failed to queue invitation emails"),
|
||||
description: detail,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
showMessage({
|
||||
level: MessageLevel.success,
|
||||
message: msg(
|
||||
str`Invitation emails queued for sending to ${to.length} recipient(s). Check the System Tasks for more information.`,
|
||||
),
|
||||
});
|
||||
this.dispatchEvent(new AKRefreshEvent());
|
||||
return true;
|
||||
};
|
||||
|
||||
override reset(): void {
|
||||
this.toAddresses = "";
|
||||
this.ccAddresses = "";
|
||||
this.bccAddresses = "";
|
||||
this.template = "email/invitation.html";
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal label=${msg("To")} required>
|
||||
<textarea
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
rows="3"
|
||||
.value=${this.toAddresses}
|
||||
@input=${(ev: InputEvent) => {
|
||||
this.toAddresses = (ev.target as HTMLTextAreaElement).value;
|
||||
this.validate();
|
||||
}}
|
||||
></textarea>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg(
|
||||
"One email address per line, or comma/semicolon separated. Each recipient will receive a separate email with an invitation link.",
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("CC")}>
|
||||
<textarea
|
||||
class="pf-c-form-control"
|
||||
rows="2"
|
||||
.value=${this.ccAddresses}
|
||||
@input=${(ev: InputEvent) => {
|
||||
this.ccAddresses = (ev.target as HTMLTextAreaElement).value;
|
||||
}}
|
||||
></textarea>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg(
|
||||
"A comma-separated list of addresses to receive copies of the invitation. Recipients will receive the full list of other addresses in this list.",
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("BCC")}>
|
||||
<textarea
|
||||
class="pf-c-form-control"
|
||||
rows="2"
|
||||
.value=${this.bccAddresses}
|
||||
@input=${(ev: InputEvent) => {
|
||||
this.bccAddresses = (ev.target as HTMLTextAreaElement).value;
|
||||
}}
|
||||
></textarea>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg(
|
||||
"A comma-separated list of addresses to receive copies of the invitation. Recipients will not receive the addresses of other recipients.",
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Template")} required>
|
||||
<select
|
||||
class="pf-c-form-control"
|
||||
@change=${(ev: Event) => {
|
||||
this.template = (ev.target as HTMLSelectElement).value;
|
||||
}}
|
||||
>
|
||||
${this.availableTemplates.map(
|
||||
(template) =>
|
||||
html`<option
|
||||
value=${template.name}
|
||||
?selected=${template.name === this.template}
|
||||
>
|
||||
${template.description}
|
||||
</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Select the email template to use for sending invitations.")}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</form>`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ak-invitation-wizard-email-step": InvitationWizardEmailStep;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
import "#components/ak-radio-input";
|
||||
import "#components/ak-switch-input";
|
||||
import "#elements/forms/HorizontalFormElement";
|
||||
import "#elements/forms/SearchSelect/index";
|
||||
|
||||
import type { InvitationWizardState } from "./types";
|
||||
|
||||
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
|
||||
import { WizardPage } from "#elements/wizard/WizardPage";
|
||||
|
||||
import {
|
||||
FlowDesignationEnum,
|
||||
type FlowSet,
|
||||
type InvitationStage,
|
||||
StagesApi,
|
||||
} from "@goauthentik/api";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, html, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
|
||||
import PFAlert from "@patternfly/patternfly/components/Alert/alert.css";
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
interface EnrollmentFlow {
|
||||
slug: string;
|
||||
pk: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
@customElement("ak-invitation-wizard-flow-step")
|
||||
export class InvitationWizardFlowStep extends WizardPage {
|
||||
static styles: CSSResult[] = [PFBase, PFForm, PFFormControl, PFButton, PFAlert];
|
||||
|
||||
@property({ type: String })
|
||||
public mode: "existing" | "create" = "existing";
|
||||
|
||||
@state()
|
||||
enrollmentFlows: EnrollmentFlow[] = [];
|
||||
|
||||
@state()
|
||||
loading = true;
|
||||
|
||||
@state()
|
||||
selectedFlowSlug?: string;
|
||||
|
||||
@state()
|
||||
selectedFlowPk?: string;
|
||||
|
||||
@state()
|
||||
newFlowName = "Enrollment with invitation";
|
||||
|
||||
@state()
|
||||
newFlowSlug = "enrollment-with-invitation";
|
||||
|
||||
@state()
|
||||
newStageName = "invitation-stage";
|
||||
|
||||
@state()
|
||||
newUserType: "external" | "internal" = "external";
|
||||
|
||||
@state()
|
||||
continueFlowWithoutInvitation = true;
|
||||
|
||||
activeCallback = async (): Promise<void> => {
|
||||
this.host.valid = false;
|
||||
|
||||
if (this.mode === "create") {
|
||||
this.loading = false;
|
||||
this.validate();
|
||||
return;
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
|
||||
try {
|
||||
const stages = await new StagesApi(DEFAULT_CONFIG).stagesInvitationStagesList({
|
||||
noFlows: false,
|
||||
});
|
||||
|
||||
const flowMap = new Map<string, EnrollmentFlow>();
|
||||
|
||||
stages.results.forEach((stage: InvitationStage) => {
|
||||
(stage.flowSet || [])
|
||||
.filter((flow: FlowSet) => flow.designation === FlowDesignationEnum.Enrollment)
|
||||
.forEach((flow: FlowSet) => {
|
||||
if (!flowMap.has(flow.slug)) {
|
||||
flowMap.set(flow.slug, {
|
||||
slug: flow.slug,
|
||||
pk: flow.pk,
|
||||
name: flow.name,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this.enrollmentFlows = Array.from(flowMap.values());
|
||||
|
||||
if (this.enrollmentFlows.length > 0) {
|
||||
this.selectedFlowSlug = this.enrollmentFlows[0].slug;
|
||||
this.selectedFlowPk = this.enrollmentFlows[0].pk;
|
||||
this.host.valid = true;
|
||||
}
|
||||
} catch {
|
||||
this.enrollmentFlows = [];
|
||||
}
|
||||
|
||||
this.loading = false;
|
||||
|
||||
// If there's exactly one eligible flow, skip this step so the user goes
|
||||
// straight to the invitation details. Drop ourselves from the step list
|
||||
// so the back button from the next step doesn't bounce back here.
|
||||
if (this.mode === "existing" && this.enrollmentFlows.length === 1) {
|
||||
const currentSlot = this.slot;
|
||||
const advanced = await this.host.navigateNext();
|
||||
if (advanced) {
|
||||
this.host.steps = this.host.steps.filter((s) => s !== currentSlot);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
validate(): void {
|
||||
if (this.mode === "existing") {
|
||||
this.host.valid = !!this.selectedFlowSlug;
|
||||
} else {
|
||||
this.host.valid =
|
||||
this.newFlowName.length > 0 &&
|
||||
this.newFlowSlug.length > 0 &&
|
||||
this.newStageName.length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
nextCallback = async (): Promise<boolean> => {
|
||||
const state = this.host.state as unknown as InvitationWizardState;
|
||||
|
||||
state.flowMode = this.mode;
|
||||
|
||||
if (this.mode === "existing") {
|
||||
if (!this.selectedFlowSlug) return false;
|
||||
state.selectedFlowSlug = this.selectedFlowSlug;
|
||||
state.selectedFlowPk = this.selectedFlowPk;
|
||||
state.needsFlow = false;
|
||||
state.needsStage = false;
|
||||
state.needsBinding = false;
|
||||
} else {
|
||||
if (!this.newFlowName || !this.newFlowSlug || !this.newStageName) return false;
|
||||
state.newFlowName = this.newFlowName;
|
||||
state.newFlowSlug = this.newFlowSlug;
|
||||
state.newStageName = this.newStageName;
|
||||
state.newUserType = this.newUserType;
|
||||
state.continueFlowWithoutInvitation = this.continueFlowWithoutInvitation;
|
||||
state.needsFlow = true;
|
||||
state.needsStage = true;
|
||||
state.needsBinding = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
override reset(): void {
|
||||
this.enrollmentFlows = [];
|
||||
this.loading = true;
|
||||
this.selectedFlowSlug = undefined;
|
||||
this.selectedFlowPk = undefined;
|
||||
this.newFlowName = "Enrollment with invitation";
|
||||
this.newFlowSlug = "enrollment-with-invitation";
|
||||
this.newStageName = "invitation-stage";
|
||||
this.newUserType = "external";
|
||||
this.continueFlowWithoutInvitation = true;
|
||||
}
|
||||
|
||||
slugify(value: string): string {
|
||||
return value
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, "-")
|
||||
.replace(/^-|-$/g, "");
|
||||
}
|
||||
|
||||
renderExistingFlowSelector(): TemplateResult {
|
||||
if (this.enrollmentFlows.length === 0) {
|
||||
return html`
|
||||
<div class="pf-c-alert pf-m-warning pf-m-inline">
|
||||
<div class="pf-c-alert__icon">
|
||||
<i class="fas fa-exclamation-triangle" aria-hidden="true"></i>
|
||||
</div>
|
||||
<h4 class="pf-c-alert__title">
|
||||
${msg("No enrollment flows with invitation stages found")}
|
||||
</h4>
|
||||
<div class="pf-c-alert__description">
|
||||
<p>
|
||||
${msg(
|
||||
"You can create a new enrollment flow and invitation stage right here, or cancel and bind an invitation stage to an existing flow manually.",
|
||||
)}
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
class="pf-c-button pf-m-primary"
|
||||
@click=${() => {
|
||||
this.mode = "create";
|
||||
this.validate();
|
||||
}}
|
||||
>
|
||||
${msg("Create a new enrollment flow")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ak-form-element-horizontal label=${msg("Enrollment flow")} required>
|
||||
<ak-search-select
|
||||
.fetchObjects=${async (query?: string): Promise<EnrollmentFlow[]> => {
|
||||
if (!query) return this.enrollmentFlows;
|
||||
const needle = query.toLowerCase();
|
||||
return this.enrollmentFlows.filter(
|
||||
(flow) =>
|
||||
flow.name.toLowerCase().includes(needle) ||
|
||||
flow.slug.toLowerCase().includes(needle),
|
||||
);
|
||||
}}
|
||||
.renderElement=${(flow: EnrollmentFlow): string => flow.name}
|
||||
.renderDescription=${(flow: EnrollmentFlow): TemplateResult =>
|
||||
html`${flow.slug}`}
|
||||
.value=${(flow: EnrollmentFlow | undefined): string | undefined => flow?.pk}
|
||||
.selected=${(flow: EnrollmentFlow): boolean => flow.pk === this.selectedFlowPk}
|
||||
@ak-change=${(ev: CustomEvent<{ value: EnrollmentFlow | null }>) => {
|
||||
const flow = ev.detail.value;
|
||||
this.selectedFlowSlug = flow?.slug;
|
||||
this.selectedFlowPk = flow?.pk;
|
||||
this.validate();
|
||||
}}
|
||||
style="display: block; width: 100%;"
|
||||
></ak-search-select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg(
|
||||
"Only enrollment flows that have an invitation stage bound to them are listed here.",
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
`;
|
||||
}
|
||||
|
||||
renderCreateForm(): TemplateResult {
|
||||
return html`
|
||||
<ak-form-element-horizontal label=${msg("Flow name")} required>
|
||||
<input
|
||||
type="text"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
.value=${this.newFlowName}
|
||||
@input=${(ev: InputEvent) => {
|
||||
const target = ev.target as HTMLInputElement;
|
||||
this.newFlowName = target.value;
|
||||
this.newFlowSlug = this.slugify(target.value);
|
||||
this.validate();
|
||||
}}
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">${msg("Name for the new enrollment flow.")}</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Flow slug")} required>
|
||||
<input
|
||||
type="text"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
.value=${this.newFlowSlug}
|
||||
@input=${(ev: InputEvent) => {
|
||||
const target = ev.target as HTMLInputElement;
|
||||
this.newFlowSlug = target.value.replace(/[^a-z0-9-]/g, "");
|
||||
this.validate();
|
||||
}}
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">${msg("Visible in the URL.")}</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Invitation stage name")} required>
|
||||
<input
|
||||
type="text"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
.value=${this.newStageName}
|
||||
@input=${(ev: InputEvent) => {
|
||||
this.newStageName = (ev.target as HTMLInputElement).value;
|
||||
this.validate();
|
||||
}}
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">${msg("Name for the new invitation stage.")}</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-radio-input
|
||||
label=${msg("User type")}
|
||||
.value=${this.newUserType}
|
||||
.options=${[
|
||||
{
|
||||
label: msg("External"),
|
||||
value: "external",
|
||||
description: html`${msg(
|
||||
"Enrolled users are created as external (e.g. customers, guests). New users will be placed under users/external.",
|
||||
)}`,
|
||||
},
|
||||
{
|
||||
label: msg("Internal"),
|
||||
value: "internal",
|
||||
description: html`${msg(
|
||||
"Enrolled users are created as internal (e.g. employees). New users will be placed under users/internal.",
|
||||
)}`,
|
||||
},
|
||||
]}
|
||||
@input=${(ev: CustomEvent<{ value: "external" | "internal" }>) => {
|
||||
this.newUserType = ev.detail.value;
|
||||
}}
|
||||
></ak-radio-input>
|
||||
<ak-switch-input
|
||||
label=${msg("Continue flow without invitation")}
|
||||
?checked=${this.continueFlowWithoutInvitation}
|
||||
@change=${(ev: Event) => {
|
||||
this.continueFlowWithoutInvitation = (ev.target as HTMLInputElement).checked;
|
||||
}}
|
||||
help=${msg(
|
||||
"If enabled, the stage will jump to the next stage when no invitation is given. If disabled, the flow will be cancelled without a valid invitation.",
|
||||
)}
|
||||
></ak-switch-input>
|
||||
`;
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
if (this.loading) {
|
||||
return html`<div class="pf-c-form">
|
||||
<p>${msg("Loading...")}</p>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
${this.mode === "existing"
|
||||
? this.renderExistingFlowSelector()
|
||||
: this.renderCreateForm()}
|
||||
</form>`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ak-invitation-wizard-flow-step": InvitationWizardFlowStep;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import "#admin/stages/invitation/InvitationListLink";
|
||||
|
||||
import type { InvitationWizardState } from "./types";
|
||||
|
||||
import { AKRefreshEvent } from "#common/events";
|
||||
import { MessageLevel } from "#common/messages";
|
||||
|
||||
import { showMessage } from "#elements/messages/MessageContainer";
|
||||
import { WizardPage } from "#elements/wizard/WizardPage";
|
||||
|
||||
import { Invitation } from "@goauthentik/api";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { css, CSSResult, html, TemplateResult } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
|
||||
import PFAlert from "@patternfly/patternfly/components/Alert/alert.css";
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
@customElement("ak-invitation-wizard-success-step")
|
||||
export class InvitationWizardSuccessStep extends WizardPage {
|
||||
static styles: CSSResult[] = [
|
||||
PFBase,
|
||||
PFForm,
|
||||
PFAlert,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
ak-stage-invitation-list-link {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@state()
|
||||
invitation?: Invitation;
|
||||
|
||||
#notified = false;
|
||||
|
||||
activeCallback = async (): Promise<void> => {
|
||||
const wizardState = this.host.state as unknown as InvitationWizardState;
|
||||
this.invitation = wizardState.createdInvitation;
|
||||
this.host.valid = true;
|
||||
|
||||
if (this.invitation && !this.#notified) {
|
||||
showMessage({
|
||||
level: MessageLevel.success,
|
||||
message: msg("Successfully created invitation."),
|
||||
});
|
||||
this.#notified = true;
|
||||
}
|
||||
};
|
||||
|
||||
nextCallback = async (): Promise<boolean> => {
|
||||
this.dispatchEvent(new AKRefreshEvent());
|
||||
return true;
|
||||
};
|
||||
|
||||
override reset(): void {
|
||||
this.invitation = undefined;
|
||||
this.#notified = false;
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
const invitation = this.invitation;
|
||||
|
||||
if (!invitation) {
|
||||
return html`<div class="pf-c-alert pf-m-warning pf-m-inline">
|
||||
<div class="pf-c-alert__icon">
|
||||
<i class="fas fa-exclamation-triangle" aria-hidden="true"></i>
|
||||
</div>
|
||||
<h4 class="pf-c-alert__title">${msg("No invitation was created.")}</h4>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ak-stage-invitation-list-link
|
||||
.invitation=${invitation}
|
||||
?inline-send-email=${true}
|
||||
@ak-invitation-send-email-inline=${this.onSendViaEmail}
|
||||
></ak-stage-invitation-list-link>
|
||||
`;
|
||||
}
|
||||
|
||||
onSendViaEmail = async (): Promise<void> => {
|
||||
const steps = this.host.steps;
|
||||
if (!steps.includes("email-step")) {
|
||||
this.host.steps = [...steps, "email-step"];
|
||||
}
|
||||
await this.host.navigateNext();
|
||||
};
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ak-invitation-wizard-success-step": InvitationWizardSuccessStep;
|
||||
}
|
||||
}
|
||||
31
web/src/admin/stages/invitation/wizard/types.ts
Normal file
31
web/src/admin/stages/invitation/wizard/types.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import type { Invitation } from "@goauthentik/api";
|
||||
|
||||
export interface InvitationWizardState {
|
||||
// Step 1: Flow selection
|
||||
flowMode: "existing" | "create";
|
||||
selectedFlowSlug?: string;
|
||||
selectedFlowPk?: string;
|
||||
newFlowName?: string;
|
||||
newFlowSlug?: string;
|
||||
newStageName?: string;
|
||||
newUserType?: "external" | "internal";
|
||||
continueFlowWithoutInvitation: boolean;
|
||||
|
||||
// Flags for which API calls to make
|
||||
needsFlow: boolean;
|
||||
needsStage: boolean;
|
||||
needsBinding: boolean;
|
||||
|
||||
// Step 2: Invitation details
|
||||
invitationName?: string;
|
||||
invitationExpires?: string;
|
||||
invitationFixedData?: Record<string, unknown>;
|
||||
invitationSingleUse: boolean;
|
||||
|
||||
// Results from API calls
|
||||
createdStagePk?: string;
|
||||
createdFlowPk?: string;
|
||||
createdFlowSlug?: string;
|
||||
createdInvitationPk?: string;
|
||||
createdInvitation?: Invitation;
|
||||
}
|
||||
@@ -50,7 +50,9 @@ export class RedirectStageForm extends BaseStageForm<RedirectStage> {
|
||||
|
||||
protected override renderForm(): TemplateResult {
|
||||
return html`<span>
|
||||
${msg("Redirect the user to another flow, potentially with all gathered context")}
|
||||
${msg(
|
||||
"Redirect the user to a static URL or another flow, optionally with all gathered context.",
|
||||
)}
|
||||
</span>
|
||||
<ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input
|
||||
|
||||
@@ -159,7 +159,7 @@ export class UserBulkRevokeSessionsForm extends ModalButton {
|
||||
>
|
||||
</ak-user-bulk-revoke-sessions-table>
|
||||
</section>
|
||||
<fieldset class="pf-c-modal-box__footer">
|
||||
<fieldset class="ak-c-fieldset pf-c-modal-box__footer">
|
||||
<legend class="sr-only">${msg("Form actions")}</legend>
|
||||
<ak-spinner-button
|
||||
.callAction=${async () => {
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (width < 1200px) {
|
||||
@media (width <= 1210px) {
|
||||
column-gap: calc(var(--pf-global--spacer--md) / 2);
|
||||
}
|
||||
}
|
||||
@@ -137,7 +137,7 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (width < 1200px) {
|
||||
@media (width <= 1210px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -164,7 +164,7 @@
|
||||
grid-area: toggle;
|
||||
}
|
||||
|
||||
@media (width >= 1200px) {
|
||||
@media (width > 1210px) {
|
||||
slot[name="toggle"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -22,11 +22,12 @@ function resolvePath(...args: string[]): string {
|
||||
* - Intercepts local links and scrolls to the target element.
|
||||
*/
|
||||
export const MDXAnchor = ({
|
||||
href,
|
||||
href: initialHref,
|
||||
children,
|
||||
...props
|
||||
}: React.AnchorHTMLAttributes<HTMLAnchorElement>) => {
|
||||
const { publicDirectory } = useMDXModule();
|
||||
let href = initialHref;
|
||||
|
||||
if (href?.startsWith(".") && publicDirectory) {
|
||||
const nextPathname = resolvePath(publicDirectory, href);
|
||||
|
||||
@@ -74,6 +74,10 @@ svg[id^="mermaid-svg-"] {
|
||||
}
|
||||
}
|
||||
|
||||
ak-alert + :is(h2, p) {
|
||||
padding-top: var(--pf-global--spacer--md);
|
||||
}
|
||||
|
||||
/* #region Dark Theme */
|
||||
|
||||
:host([theme="dark"]) {
|
||||
|
||||
@@ -211,9 +211,11 @@ export class SimpleTable
|
||||
);
|
||||
|
||||
return html`<tr role="presentation">
|
||||
<td role="presentation" colspan=${columnCount}>
|
||||
<td role="presentation" colspan=${columnCount + 1}>
|
||||
<div class="pf-l-bullseye">
|
||||
<ak-empty-state><span>${message}</span></ak-empty-state>
|
||||
<slot name="empty-table">
|
||||
<ak-empty-state><span>${message}</span></ak-empty-state>
|
||||
</slot>
|
||||
</div>
|
||||
</td>
|
||||
</tr>`;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
);
|
||||
--ak-c-command-palette__group--Color: var(--pf-global--palette--purple-100);
|
||||
|
||||
--ak-fieldset--BorderColor: transparent;
|
||||
--ak-c-fieldset--BorderColor: transparent;
|
||||
|
||||
--ak-c-command-palette__item--BackgroundColor: transparent;
|
||||
--ak-c-command-palette__item--Color: var(--pf-global--palette--purple-50);
|
||||
@@ -37,7 +37,7 @@
|
||||
transform: translate3d(0, 0, 0); /* Fixes rendering artifacts. */
|
||||
|
||||
@media (prefers-contrast: more) {
|
||||
--ak-fieldset--BorderColor: var(--pf-global--palette--purple-500);
|
||||
--ak-c-fieldset--BorderColor: var(--pf-global--palette--purple-500);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
transition-duration: 0.2s;
|
||||
|
||||
legend {
|
||||
--ak-fieldset__legend--PaddingInlineBase: var(--pf-global--spacer--md);
|
||||
--ak-c-fieldset__legend--PaddingInlineBase: var(--pf-global--spacer--md);
|
||||
|
||||
cursor: pointer;
|
||||
color: var(--ak-c-command-palette__group--Color);
|
||||
|
||||
@@ -524,7 +524,7 @@ export class AKCommandPaletteModal extends AKModal {
|
||||
Object.entries(grouped),
|
||||
(_group, groupIdx) => `group-${groupIdx}`,
|
||||
([groupLabel, commands], groupIdx) => html`
|
||||
<fieldset part="results-group">
|
||||
<fieldset class="ak-c-fieldset" part="results-group">
|
||||
<legend
|
||||
class="${!groupLabel ? "sr-only more-contrast-only" : ""}"
|
||||
data-label=${ifPresent(groupLabel)}
|
||||
|
||||
@@ -115,8 +115,8 @@
|
||||
/* #region Footer */
|
||||
|
||||
fieldset.ak-c-dialog__footer {
|
||||
--ak-fieldset__legend--PaddingInlineBase: var(--pf-global--spacer--md);
|
||||
padding-block: calc(var(--ak-fieldset__legend--PaddingInlineBase) / 2);
|
||||
--ak-c-fieldset__legend--PaddingInlineBase: var(--pf-global--spacer--md);
|
||||
padding-block: calc(var(--ak-c-fieldset__legend--PaddingInlineBase) / 2);
|
||||
border-inline: none;
|
||||
border-block-end: none;
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { checkObjectShallowEquality } from "#common/collections";
|
||||
import { AKElement } from "#elements/Base";
|
||||
import { asInvoker, type ModalTemplate } from "#elements/dialogs/invokers";
|
||||
import type { DialogInit, TransclusionElementConstructor } from "#elements/dialogs/shared";
|
||||
import { ElementConstructorBoundary } from "#elements/errors/boundaries";
|
||||
import type { LitPropertyRecord } from "#elements/types";
|
||||
import { isAKElementConstructor, StrictUnsafe } from "#elements/utils/unsafe";
|
||||
|
||||
@@ -159,10 +160,22 @@ export function lookupElementConstructor<T extends CustomElementConstructor>(
|
||||
tagName: string,
|
||||
registry: CustomElementRegistry = window.customElements,
|
||||
): T {
|
||||
if (!tagName) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.trace(
|
||||
"No tag name provided for lookup. Did this value come from a different version of authentik?",
|
||||
);
|
||||
|
||||
return ElementConstructorBoundary as unknown as T;
|
||||
}
|
||||
|
||||
const ElementConstructor = registry.get(tagName);
|
||||
|
||||
if (!ElementConstructor) {
|
||||
throw new TypeError(`No custom element defined for tag name: ${tagName}`);
|
||||
// eslint-disable-next-line no-console
|
||||
console.trace(`No custom element defined for tag name: ${tagName}`);
|
||||
|
||||
return ElementConstructorBoundary as unknown as T;
|
||||
}
|
||||
|
||||
return ElementConstructor as unknown as T;
|
||||
|
||||
41
web/src/elements/errors/boundaries.ts
Normal file
41
web/src/elements/errors/boundaries.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { globalAK } from "#common/global";
|
||||
|
||||
import { AKElement } from "#elements/Base";
|
||||
import { SlottedTemplateResult } from "#elements/types";
|
||||
|
||||
import { CapabilitiesEnum } from "@goauthentik/api";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { customElement } from "@lit/reactive-element/decorators/custom-element.js";
|
||||
import { html } from "lit-html";
|
||||
|
||||
import PFAlert from "@patternfly/patternfly/components/Alert/alert.css";
|
||||
|
||||
/**
|
||||
* A fallback element to render when a custom element fails to load, either due to a missing import,
|
||||
* or a version mismatch between the element's definition and its usage.
|
||||
*/
|
||||
@customElement("ak-element-missing")
|
||||
export class ElementConstructorBoundary extends AKElement {
|
||||
public styles = [PFAlert];
|
||||
|
||||
protected override render(): SlottedTemplateResult {
|
||||
const debug = globalAK().config.capabilities.includes(CapabilitiesEnum.CanDebug);
|
||||
|
||||
const description = debug
|
||||
? msg(
|
||||
"The element could not be loaded. This may be due to a missing import or a version mismatch.",
|
||||
)
|
||||
: msg(
|
||||
"An element could not be loaded. Please try refreshing the page or clearing your cache.",
|
||||
);
|
||||
|
||||
return html`<div class="pf-c-alert pf-m-danger" role="alert">
|
||||
<div class="pf-c-alert__icon">
|
||||
<i class="fas fa-exclamation-triangle" aria-hidden="true"></i>
|
||||
</div>
|
||||
<h4 class="pf-c-alert__title">${msg("Failed to load element")}</h4>
|
||||
<div class="pf-c-alert__description">${description}</div>
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
@@ -77,7 +77,7 @@ export class ConfirmationForm extends ModalButton {
|
||||
<slot class="pf-c-content" name="body"></slot>
|
||||
</form>
|
||||
</section>
|
||||
<fieldset class="pf-c-modal-box__footer">
|
||||
<fieldset class="ak-c-fieldset pf-c-modal-box__footer">
|
||||
<legend class="sr-only">${msg("Form actions")}</legend>
|
||||
<ak-spinner-button
|
||||
.callAction=${async () => {
|
||||
|
||||
@@ -119,7 +119,7 @@ export class DeleteBulkForm<T> extends ModalButton {
|
||||
>
|
||||
</ak-used-by-table>
|
||||
</section>
|
||||
<fieldset class="pf-c-modal-box__footer">
|
||||
<fieldset class="ak-c-fieldset pf-c-modal-box__footer">
|
||||
<legend class="sr-only">${msg("Form actions")}</legend>
|
||||
<ak-spinner-button
|
||||
.callAction=${async () => {
|
||||
|
||||
@@ -218,7 +218,7 @@ export class ModalForm extends ModalButton {
|
||||
}
|
||||
|
||||
protected renderActions(): SlottedTemplateResult {
|
||||
return html`<fieldset class="pf-c-modal-box__footer">
|
||||
return html`<fieldset class="ak-c-fieldset pf-c-modal-box__footer">
|
||||
<legend class="sr-only">${msg("Form actions")}</legend>
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -20,6 +20,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* P5 puts a line separating the entries, but this looks odd in our stacked usage. Specifying
|
||||
* `.pf-m-stack` here also raises the specificity above the P4 default.
|
||||
*/
|
||||
.pf-m-stack label.pf-c-radio:not(:last-child) {
|
||||
--pf-c-radio--BoxShadowColor: transparent;
|
||||
}
|
||||
|
||||
.pf-c-radio__description {
|
||||
text-wrap: balance;
|
||||
text-wrap: pretty;
|
||||
|
||||
@@ -996,7 +996,9 @@ export abstract class Table<T extends object, D = T>
|
||||
* A simple pagination display, shown at both the top and bottom of the page.
|
||||
*/
|
||||
protected renderTablePagination(): SlottedTemplateResult {
|
||||
if (!this.paginated) return nothing;
|
||||
if (!this.paginated || !this.data || this.data?.pagination.totalPages < 2) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const handler = (page: number) => {
|
||||
this.page = page;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
.empty-state-primary {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: var(--pf-global--spacer--sm);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@@ -182,8 +182,31 @@ export class AKWizard<S = Record<string, unknown>> extends AKElement {
|
||||
/**
|
||||
* Actions to display at the end of the wizard.
|
||||
*/
|
||||
private _actions: WizardAction[] = [];
|
||||
|
||||
@property({ attribute: false })
|
||||
public actions: WizardAction[] = [];
|
||||
public get actions(): WizardAction[] {
|
||||
return this._actions;
|
||||
}
|
||||
|
||||
public set actions(value: WizardAction[]) {
|
||||
const oldValue = this._actions;
|
||||
this._actions = value;
|
||||
|
||||
if (this._actions.length > 0) {
|
||||
if (!this.querySelector(`[slot="ak-wizard-page-action"]`)) {
|
||||
const actionPage = document.createElement("ak-wizard-page-action");
|
||||
actionPage.slot = "ak-wizard-page-action";
|
||||
actionPage.dataset.wizardmanaged = "true";
|
||||
this.appendChild(actionPage);
|
||||
}
|
||||
if (!this.steps.includes("ak-wizard-page-action")) {
|
||||
this.steps = [...this.steps, "ak-wizard-page-action"];
|
||||
}
|
||||
}
|
||||
|
||||
this.requestUpdate("actions", oldValue);
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
public finalHandler?: () => Promise<void>;
|
||||
@@ -530,12 +553,14 @@ export class AKWizard<S = Record<string, unknown>> extends AKElement {
|
||||
return guard(
|
||||
[activeStepIndex, lastPage, canBack, cancelable, valid, childElementCount],
|
||||
() => {
|
||||
const customLabel = this.activeStepElement?.formatNextLabel();
|
||||
const nextLabel =
|
||||
lastPage && activeStepIndex > 0
|
||||
customLabel ??
|
||||
(lastPage && activeStepIndex > 0
|
||||
? this.cancelable
|
||||
? ButtonKindLabelRecord.create()
|
||||
: ButtonKindLabelRecord.finish()
|
||||
: ButtonKindLabelRecord.next();
|
||||
: ButtonKindLabelRecord.next());
|
||||
|
||||
return [
|
||||
cancelable
|
||||
|
||||
@@ -70,6 +70,16 @@ export abstract class WizardPage<S = WizardPageState> extends AKElement {
|
||||
return html`<div part="sidebar-label-headline">${this.headline ?? msg("UNNAMED")}</div>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional override for the wizard's next-button label while this page is active.
|
||||
*
|
||||
* Return `null` (the default) to keep the wizard's default labeling
|
||||
* (Next/Finish/Create).
|
||||
*/
|
||||
public formatNextLabel(): SlottedTemplateResult | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the `next` button on the wizard is pressed. For forms, results in the submission
|
||||
* of the current form to the back-end before being allowed to proceed to the next page. This is
|
||||
|
||||
@@ -126,7 +126,7 @@ export class FlowInspector extends AKElement {
|
||||
|
||||
protected renderNextStage({ currentPlan, isCompleted }: FlowInspection): TemplateResult {
|
||||
return html`<div class="pf-c-card">
|
||||
<fieldset>
|
||||
<fieldset class="ak-c-fieldset">
|
||||
<legend class="pf-c-card__title">${msg("Next stage")}</legend>
|
||||
<div class="pf-c-card__body">
|
||||
<dl class="pf-c-description-list">
|
||||
@@ -184,7 +184,7 @@ ${stringify(this.getStage(currentPlan?.nextPlannedStage?.stageObj))}</pre
|
||||
currentPlan,
|
||||
}: FlowInspection): TemplateResult {
|
||||
return html`<div class="pf-c-card">
|
||||
<fieldset>
|
||||
<fieldset class="ak-c-fieldset">
|
||||
<legend class="pf-c-card__title">${msg("Plan history")}</legend>
|
||||
<div class="pf-c-card__body">
|
||||
<ol class="pf-c-progress-stepper pf-m-vertical">
|
||||
@@ -248,7 +248,7 @@ ${stringify(this.getStage(currentPlan?.nextPlannedStage?.stageObj))}</pre
|
||||
|
||||
protected renderCurrentPlan({ currentPlan }: FlowInspection): TemplateResult {
|
||||
return html`<div class="pf-c-card">
|
||||
<fieldset>
|
||||
<fieldset class="ak-c-fieldset">
|
||||
<legend class="pf-c-card__title">${msg("Current plan context")}</legend>
|
||||
<pre class="pf-c-card__body"><code>${stringify(
|
||||
currentPlan?.planContext,
|
||||
@@ -259,7 +259,7 @@ ${stringify(this.getStage(currentPlan?.nextPlannedStage?.stageObj))}</pre
|
||||
|
||||
protected renderSession({ currentPlan }: FlowInspection): TemplateResult {
|
||||
return html`<div class="pf-c-card">
|
||||
<fieldset>
|
||||
<fieldset class="ak-c-fieldset">
|
||||
<legend class="pf-c-card__title">${msg("Session ID")}</legend>
|
||||
<div class="pf-c-card__body">
|
||||
<code class="break"> ${currentPlan?.sessionId} </code>
|
||||
|
||||
@@ -118,7 +118,7 @@ export class RedirectStage extends BaseStage<RedirectChallenge, FlowChallengeRes
|
||||
<p>${msg("You're about to be redirected to the following URL.")}</p>
|
||||
<code>${this.getURL()}</code>
|
||||
</div>
|
||||
<fieldset class="pf-c-form__group pf-m-action">
|
||||
<fieldset class="ak-c-fieldset pf-c-form__group pf-m-action">
|
||||
<legend class="sr-only">${msg("Form actions")}</legend>
|
||||
<a
|
||||
type="submit"
|
||||
|
||||
@@ -38,7 +38,7 @@ export class AccessDeniedStage extends BaseStage<
|
||||
: nothing}
|
||||
</ak-empty-state>
|
||||
${this.challenge?.flowInfo?.cancelUrl
|
||||
? html`<fieldset class="pf-c-form__group pf-m-action">
|
||||
? html`<fieldset class="ak-c-fieldset pf-c-form__group pf-m-action">
|
||||
<legend class="sr-only">${msg("Form actions")}</legend>
|
||||
<a
|
||||
class="pf-c-button pf-m-primary pf-m-block"
|
||||
|
||||
@@ -87,7 +87,7 @@ export class AuthenticatorDuoStage extends BaseStage<
|
||||
</p>
|
||||
<a href=${this.challenge.activationCode}>${msg("Duo activation")}</a>
|
||||
|
||||
<fieldset class="pf-c-form__group pf-m-action">
|
||||
<fieldset class="ak-c-fieldset pf-c-form__group pf-m-action">
|
||||
<legend class="sr-only">${msg("Form actions")}</legend>
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -64,7 +64,7 @@ export class AuthenticatorEmailStage extends BaseStage<
|
||||
${AKFormErrors({ errors: this.challenge?.responseErrors?.email })}
|
||||
</div>
|
||||
${this.renderNonFieldErrors()}
|
||||
<fieldset class="pf-c-form__group pf-m-action">
|
||||
<fieldset class="ak-c-fieldset pf-c-form__group pf-m-action">
|
||||
<legend class="sr-only">${msg("Form actions")}</legend>
|
||||
<button
|
||||
name="continue"
|
||||
@@ -120,7 +120,7 @@ export class AuthenticatorEmailStage extends BaseStage<
|
||||
${AKFormErrors({ errors: this.challenge.responseErrors?.code })}
|
||||
</div>
|
||||
${this.renderNonFieldErrors()}
|
||||
<fieldset class="pf-c-form__group pf-m-action">
|
||||
<fieldset class="ak-c-fieldset pf-c-form__group pf-m-action">
|
||||
<legend class="sr-only">${msg("Form actions")}</legend>
|
||||
<button
|
||||
name="continue"
|
||||
|
||||
@@ -67,7 +67,7 @@ export class AuthenticatorSMSStage extends BaseStage<
|
||||
${AKFormErrors({ errors: this.challenge.responseErrors?.phone_number })}
|
||||
</div>
|
||||
${this.renderNonFieldErrors()}
|
||||
<fieldset class="pf-c-form__group pf-m-action">
|
||||
<fieldset class="ak-c-fieldset pf-c-form__group pf-m-action">
|
||||
<legend class="sr-only">${msg("Form actions")}</legend>
|
||||
<button
|
||||
name="continue"
|
||||
@@ -106,7 +106,7 @@ export class AuthenticatorSMSStage extends BaseStage<
|
||||
${AKFormErrors({ errors: this.challenge.responseErrors?.code })}
|
||||
</div>
|
||||
${this.renderNonFieldErrors()}
|
||||
<fieldset class="pf-c-form__group pf-m-action">
|
||||
<fieldset class="ak-c-fieldset pf-c-form__group pf-m-action">
|
||||
<legend class="sr-only">${msg("Form actions")}</legend>
|
||||
<button
|
||||
name="continue"
|
||||
|
||||
@@ -66,7 +66,7 @@ export class AuthenticatorStaticStage extends BaseStage<
|
||||
</ul>
|
||||
<p>${msg("Make sure to keep these tokens in a safe place.")}</p>
|
||||
|
||||
<fieldset class="pf-c-form__group pf-m-action">
|
||||
<fieldset class="ak-c-fieldset pf-c-form__group pf-m-action">
|
||||
<legend class="sr-only">${msg("Form actions")}</legend>
|
||||
<button
|
||||
name="continue"
|
||||
|
||||
@@ -175,7 +175,7 @@ export class AuthenticatorTOTPStage extends BaseStage<
|
||||
${AKFormErrors({ errors: this.challenge.responseErrors?.code })}
|
||||
</div>
|
||||
|
||||
<fieldset class="pf-c-form__group pf-m-action">
|
||||
<fieldset class="ak-c-fieldset pf-c-form__group pf-m-action">
|
||||
<legend class="sr-only">${msg("Form actions")}</legend>
|
||||
<button
|
||||
name="continue"
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
.ak-c-fieldset.ak-c-fieldset.pf-c-form__group.pf-m-action[name="device-challenges"] {
|
||||
--ak-c-fieldset--BorderWidth: thin;
|
||||
|
||||
--ak-c-fieldset__legend--MarginInlineBase: var(--pf-global--spacer--sm);
|
||||
--ak-c-fieldset__legend--PaddingInlineBase: var(--pf-global--spacer--sm);
|
||||
|
||||
--ak-c-fieldset--RowGap: 0;
|
||||
--ak-c-fieldset--ColumnGap: var(--pf-global--spacer--sm);
|
||||
}
|
||||
|
||||
.authenticator-button,
|
||||
ak-stage-authenticator-validate.style-scope .authenticator-button {
|
||||
align-items: center;
|
||||
@@ -5,6 +15,7 @@ ak-stage-authenticator-validate.style-scope .authenticator-button {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(auto, 2rem) minmax(33%, max-content);
|
||||
gap: var(--pf-global--spacer--lg);
|
||||
padding-block: calc(var(--pf-global--spacer--form-element) * 2);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--pf-global--BackgroundColor--200);
|
||||
|
||||
@@ -267,7 +267,10 @@ export class AuthenticatorValidateStage
|
||||
},
|
||||
);
|
||||
|
||||
return html`<fieldset class="pf-c-form__group pf-m-action" name="device-challenges">
|
||||
return html`<fieldset
|
||||
class="ak-c-fieldset pf-c-form__group pf-m-action"
|
||||
name="device-challenges"
|
||||
>
|
||||
<legend class="pf-c-title">${msg("Select an authentication method")}</legend>
|
||||
${deviceChallengeButtons}
|
||||
</fieldset>`;
|
||||
@@ -300,7 +303,7 @@ export class AuthenticatorValidateStage
|
||||
},
|
||||
);
|
||||
|
||||
return html`<fieldset class="pf-c-form__group pf-m-action" name="stages">
|
||||
return html`<fieldset class="ak-c-fieldset pf-c-form__group pf-m-action" name="stages">
|
||||
<legend class="sr-only">${msg("Select a configuration stage")}</legend>
|
||||
${stageButtons}
|
||||
</fieldset>`;
|
||||
|
||||
@@ -30,7 +30,7 @@ export class AuthenticatorValidateStageWebCode extends BaseDeviceStage<
|
||||
|
||||
return html`<form class="pf-c-form" @submit=${this.submitForm}>
|
||||
${this.renderUserInfo()}
|
||||
<fieldset class="pf-c-form__group">
|
||||
<fieldset class="ak-c-fieldset pf-c-form__group">
|
||||
<legend class="sr-only">${msg("Authentication code")}</legend>
|
||||
${AKLabel(
|
||||
{
|
||||
@@ -62,7 +62,7 @@ export class AuthenticatorValidateStageWebCode extends BaseDeviceStage<
|
||||
${AKFormErrors({ errors: this.challenge?.responseErrors?.code })}
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="pf-c-form__group pf-m-action">
|
||||
<fieldset class="ak-c-fieldset pf-c-form__group pf-m-action">
|
||||
<legend class="sr-only">${msg("Form actions")}</legend>
|
||||
<button name="continue" type="submit" class="pf-c-button pf-m-primary pf-m-block">
|
||||
${msg("Continue")}
|
||||
|
||||
@@ -63,7 +63,7 @@ export class AuthenticatorValidateStageWebDuo extends BaseDeviceStage<
|
||||
>
|
||||
</ak-empty-state>
|
||||
${this.showBackButton
|
||||
? html`<fieldset class="pf-c-form__group pf-m-action">
|
||||
? html`<fieldset class="ak-c-fieldset pf-c-form__group pf-m-action">
|
||||
<legend class="sr-only">${msg("Form actions")}</legend>
|
||||
${this.renderReturnToDevicePicker()}
|
||||
</fieldset>`
|
||||
|
||||
@@ -133,7 +133,7 @@ export class AuthenticatorValidateStageWebAuthn extends BaseDeviceStage<
|
||||
>
|
||||
</ak-empty-state>
|
||||
${!this.authenticating || this.showBackButton
|
||||
? html`<fieldset class="pf-c-form__group pf-m-action">
|
||||
? html`<fieldset class="ak-c-fieldset pf-c-form__group pf-m-action">
|
||||
<legend class="sr-only">${msg("Form actions")}</legend>
|
||||
${!this.authenticating
|
||||
? html`<button
|
||||
|
||||
@@ -153,7 +153,7 @@ export class WebAuthnAuthenticatorRegisterStage extends BaseStage<
|
||||
${this.challenge?.responseErrors
|
||||
? html`<p>${this.challenge.responseErrors.response[0].string}</p>`
|
||||
: nothing}
|
||||
<fieldset class="pf-c-form__group pf-m-action">
|
||||
<fieldset class="ak-c-fieldset pf-c-form__group pf-m-action">
|
||||
<legend class="sr-only">${msg("Form actions")}</legend>
|
||||
${!this.registerRunning
|
||||
? html` <button
|
||||
|
||||
@@ -126,7 +126,7 @@ export class ConsentStage extends BaseStage<ConsentChallenge, ConsentChallengeRe
|
||||
? this.renderAdditional()
|
||||
: this.renderNoPrevious()}
|
||||
|
||||
<fieldset class="pf-c-form__group pf-m-action">
|
||||
<fieldset class="ak-c-fieldset pf-c-form__group pf-m-action">
|
||||
<legend class="sr-only">${msg("Form actions")}</legend>
|
||||
<button
|
||||
name="continue"
|
||||
|
||||
@@ -23,7 +23,7 @@ export class DummyStage extends BaseStage<DummyChallenge, DummyChallengeResponse
|
||||
return html`<ak-flow-card .challenge=${this.challenge}>
|
||||
<form class="pf-c-form" @submit=${this.submitForm}>
|
||||
<p>${msg(str`Stage name: ${this.challenge?.name}`)}</p>
|
||||
<fieldset class="pf-c-form__group pf-m-action">
|
||||
<fieldset class="ak-c-fieldset pf-c-form__group pf-m-action">
|
||||
<legend class="sr-only">${msg("Form actions")}</legend>
|
||||
<button
|
||||
name="continue"
|
||||
|
||||
@@ -25,7 +25,7 @@ export class EmailStage extends BaseStage<EmailChallenge, EmailChallengeResponse
|
||||
<p>${msg("Check your Inbox for a verification email.")}</p>
|
||||
</div>
|
||||
|
||||
<fieldset class="pf-c-form__group pf-m-action">
|
||||
<fieldset class="ak-c-fieldset pf-c-form__group pf-m-action">
|
||||
<legend class="sr-only">${msg("Form actions")}</legend>
|
||||
<button
|
||||
name="continue"
|
||||
|
||||
@@ -296,8 +296,15 @@ export class IdentificationStage extends BaseStage<
|
||||
type: string,
|
||||
label: string,
|
||||
initialUserIdentification: string | null,
|
||||
autocomplete: string,
|
||||
passwordFields?: boolean,
|
||||
) {
|
||||
// When webauthn is enabled, add "webauthn" to autocomplete to enable passkey autofill
|
||||
let autocomplete: AutoFill = type === "email" ? "email" : "username";
|
||||
|
||||
if (this.#webauthn.live) {
|
||||
autocomplete = `${autocomplete} webauthn`;
|
||||
}
|
||||
|
||||
return html`<input
|
||||
${ref(this.autofocusTarget.reference)}
|
||||
id=${id}
|
||||
@@ -307,6 +314,9 @@ export class IdentificationStage extends BaseStage<
|
||||
autofocus
|
||||
autocomplete=${autocomplete}
|
||||
spellcheck="false"
|
||||
inputmode=${type === "email" ? "email" : "text"}
|
||||
autocapitalize="none"
|
||||
enterkeyhint=${passwordFields ? "next" : "go"}
|
||||
class="pf-c-form-control"
|
||||
value=${initialUserIdentification ?? ""}
|
||||
required
|
||||
@@ -345,19 +355,11 @@ export class IdentificationStage extends BaseStage<
|
||||
const type = fields.length === 1 && fields[0] === UserFieldsEnum.Email ? "email" : "text";
|
||||
const label = OR_LIST_FORMATTERS.format(fields.map((f) => UI_FIELDS[f]));
|
||||
|
||||
// When webauthn is enabled, add "webauthn" to autocomplete to enable passkey autofill
|
||||
const autocomplete: AutoFill = this.#webauthn.live ? "username webauthn" : "username";
|
||||
|
||||
console.debug(
|
||||
"Rendering identification stage with fields:",
|
||||
fields,
|
||||
initialUserIdentification,
|
||||
);
|
||||
// prettier-ignore
|
||||
return html`${offerRecovery ? this.renderRecoveryMessage() : nothing}
|
||||
<div class="pf-c-form__group">
|
||||
${AKLabel({ required: true, htmlFor: inputID }, label)}
|
||||
${this.renderUidField(inputID, type, label, initialUserIdentification, autocomplete)}
|
||||
${this.renderUidField(inputID, type, label, initialUserIdentification, passwordFields)}
|
||||
${rememberMeController?.renderToggleInput() ?? null}
|
||||
${AKFormErrors({ errors: challenge.responseErrors?.uid_field })}
|
||||
</div>
|
||||
@@ -433,9 +435,8 @@ export class IdentificationStage extends BaseStage<
|
||||
return html`<fieldset
|
||||
slot="footer"
|
||||
part="source-list"
|
||||
role="group"
|
||||
name="login-sources"
|
||||
class="pf-c-form__group"
|
||||
class="ak-c-fieldset pf-c-form__group"
|
||||
>
|
||||
<legend class="sr-only">${msg("Login sources")}</legend>
|
||||
${repeat(
|
||||
@@ -467,7 +468,8 @@ export class IdentificationStage extends BaseStage<
|
||||
return html`<fieldset
|
||||
slot="footer-band"
|
||||
part="additional-actions"
|
||||
class="pf-c-login__main-footer-band"
|
||||
name="additional-actions"
|
||||
class="ak-c-fieldset pf-c-login__main-footer-band"
|
||||
>
|
||||
<legend class="sr-only">${msg("Additional actions")}</legend>
|
||||
${enrollUrl
|
||||
|
||||
@@ -52,7 +52,7 @@ export class PasswordStage extends BaseStage<PasswordChallenge, PasswordChalleng
|
||||
?allow-show-password=${!!this.challenge?.allowShowPassword}
|
||||
prefill=${PasswordManagerPrefill.password ?? ""}
|
||||
></ak-flow-input-password>
|
||||
<fieldset class="pf-c-form__group pf-m-action">
|
||||
<fieldset class="ak-c-fieldset pf-c-form__group pf-m-action">
|
||||
<legend class="sr-only">${msg("Form actions")}</legend>
|
||||
<button
|
||||
name="continue"
|
||||
@@ -67,7 +67,8 @@ export class PasswordStage extends BaseStage<PasswordChallenge, PasswordChalleng
|
||||
? html`<fieldset
|
||||
slot="footer-band"
|
||||
part="additional-actions"
|
||||
class="pf-c-login__main-footer-band"
|
||||
name="additional-actions"
|
||||
class="ak-c-fieldset pf-c-login__main-footer-band"
|
||||
>
|
||||
<legend class="sr-only">${msg("Additional actions")}</legend>
|
||||
<div class="pf-c-login__main-footer-band-item">
|
||||
|
||||
@@ -322,7 +322,7 @@ ${prompt.initialValue}</textarea
|
||||
}
|
||||
|
||||
protected renderContinue(): SlottedTemplateResult {
|
||||
return html`<fieldset class="pf-c-form__group pf-m-action">
|
||||
return html`<fieldset class="ak-c-fieldset pf-c-form__group pf-m-action">
|
||||
<legend class="sr-only">${msg("Form actions")}</legend>
|
||||
<button name="continue" type="submit" class="pf-c-button pf-m-primary pf-m-block">
|
||||
${msg("Continue")}
|
||||
|
||||
@@ -50,7 +50,7 @@ export class PasswordStage extends BaseStage<
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<fieldset class="pf-c-form__group pf-m-action">
|
||||
<fieldset class="ak-c-fieldset pf-c-form__group pf-m-action">
|
||||
<legend class="sr-only">${msg("Form actions")}</legend>
|
||||
<button name="remember-me" type="submit" class="pf-c-button pf-m-primary">
|
||||
${msg("Yes")}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
@import "./components/Content/content.css";
|
||||
@import "./components/Table/table.css";
|
||||
@import "./components/Form/form.css";
|
||||
@import "./components/Fieldset/fieldset.css";
|
||||
@import "./components/Switch/switch.css";
|
||||
@import "./components/Select/select.css";
|
||||
@import "./components/Modal/modal.css";
|
||||
|
||||
@@ -245,3 +245,94 @@ html[data-theme="dark"],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
.ak-c-fieldset {
|
||||
--ak-c-fieldset--BorderWidth: thin;
|
||||
--ak-c-fieldset__legend--MarginInlineBase: var(--pf-global--spacer--sm);
|
||||
--ak-c-fieldset__legend--PaddingInlineBase: var(--pf-global--spacer--sm);
|
||||
|
||||
border-color: var(--ak-c-fieldset--BorderColor, var(--pf-global--BackgroundColor--light-100));
|
||||
|
||||
@media (prefers-contrast: more) {
|
||||
border-color: var(--ak-c-fieldset--BorderColor, var(--pf-global--BorderColor--200));
|
||||
}
|
||||
|
||||
@media (prefers-contrast: less) {
|
||||
border-color: var(--ak-c-fieldset--BorderColor, transparent);
|
||||
}
|
||||
|
||||
border-width: var(--ak-c-fieldset--BorderWidth);
|
||||
|
||||
padding: var(--ak-c-fieldset__legend--PaddingInlineBase) !important;
|
||||
|
||||
& > legend {
|
||||
line-height: 1;
|
||||
padding: var(--ak-c-fieldset__legend--PaddingInlineBase) !important;
|
||||
margin-inline-start: var(
|
||||
--ak-c-fieldset__legend--MarginInlineStart,
|
||||
var(--ak-c-fieldset__legend--MarginInlineBase)
|
||||
) !important;
|
||||
margin-inline-end: var(
|
||||
--ak-legend-margin-inline-end,
|
||||
var(--ak-c-fieldset__legend--MarginInlineBase)
|
||||
) !important;
|
||||
}
|
||||
|
||||
&:has(legend.sr-only:not(.more-contrast-only)) {
|
||||
border-width: 0;
|
||||
|
||||
&:not(.pf-c-modal-box__footer) {
|
||||
--ak-c-fieldset__legend--PaddingInlineBase: 0;
|
||||
--ak-c-fieldset__legend--MarginInlineBase: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.pf-c-form__group {
|
||||
border-radius: var(--pf-global--BorderRadius--sm);
|
||||
}
|
||||
|
||||
&.pf-c-form__group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
&.pf-m-action {
|
||||
gap: var(--pf-global--spacer--md) var(--pf-global--spacer--sm);
|
||||
margin-block-start: 0;
|
||||
|
||||
/* Fallback for action-only fieldsets when :has() does not suppress
|
||||
* the browser's native groove border. Keep the device picker
|
||||
* bordered because it has a visible legend.
|
||||
*/
|
||||
&:not([name="device-challenges"]) {
|
||||
border-width: 0;
|
||||
--ak-c-fieldset__legend--PaddingInlineBase: 0;
|
||||
--ak-c-fieldset__legend--MarginInlineBase: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.pf-c-login__main-footer-band {
|
||||
& > *:last-child {
|
||||
padding-block-end: var(--pf-c-login__main-footer-band-item--PaddingTop);
|
||||
}
|
||||
}
|
||||
|
||||
&.pf-c-modal-box__footer {
|
||||
--ak-c-fieldset__legend--PaddingInlineBase: var(--pf-global--spacer--lg);
|
||||
--pf-c-modal-box__footer--c-button--MarginRight: 0;
|
||||
|
||||
gap: var(--pf-global--spacer--sm);
|
||||
justify-content: end;
|
||||
padding-block: calc(var(--ak-c-fieldset__legend--PaddingInlineBase) / 2);
|
||||
border-inline: none;
|
||||
border-block-end: none;
|
||||
|
||||
--pf-c-modal-box__footer--c-button--sm--MarginRight: var(
|
||||
--pf-c-modal-box__footer--c-button--MarginRight
|
||||
);
|
||||
|
||||
& > ak-spinner-button:not(:last-child) {
|
||||
margin-right: var(--pf-c-modal-box__footer--c-button--MarginRight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
.pf-c-card > fieldset {
|
||||
.pf-c-card > .ak-c-fieldset {
|
||||
margin-inline: var(--pf-global--spacer--md);
|
||||
margin-block-end: var(--pf-global--spacer--md);
|
||||
|
||||
@media not (prefers-contrast: more) {
|
||||
--ak-fieldset__legend--MarginInlineStart: calc(
|
||||
var(--pf-c-card--child--PaddingLeft) - var(--ak-fieldset__legend--PaddingInlineBase)
|
||||
--ak-c-fieldset__legend--MarginInlineStart: calc(
|
||||
var(--pf-c-card--child--PaddingLeft) - var(--ak-c-fieldset__legend--PaddingInlineBase)
|
||||
);
|
||||
--ak-legend-margin-inline-end: calc(
|
||||
var(--pf-c-card--child--PaddingRight) - var(--ak-fieldset__legend--PaddingInlineBase)
|
||||
var(--pf-c-card--child--PaddingRight) - var(--ak-c-fieldset__legend--PaddingInlineBase)
|
||||
);
|
||||
|
||||
border-width: 0;
|
||||
|
||||
102
web/src/styles/authentik/components/Fieldset/fieldset.css
Normal file
102
web/src/styles/authentik/components/Fieldset/fieldset.css
Normal file
@@ -0,0 +1,102 @@
|
||||
.ak-c-fieldset {
|
||||
--ak-c-fieldset--BorderWidth: thin;
|
||||
|
||||
--ak-c-fieldset--RowGap: var(--pf-global--spacer--md);
|
||||
--ak-c-fieldset--ColumnGap: var(--pf-global--spacer--sm);
|
||||
|
||||
--ak-c-fieldset__legend--MarginInlineBase: var(--pf-global--spacer--sm);
|
||||
--ak-c-fieldset__legend--PaddingInlineBase: var(--pf-global--spacer--sm);
|
||||
|
||||
border-color: var(--ak-c-fieldset--BorderColor, var(--pf-global--BackgroundColor--light-100));
|
||||
border-width: var(--ak-c-fieldset--BorderWidth);
|
||||
padding: var(--ak-c-fieldset__legend--PaddingInlineBase) !important;
|
||||
|
||||
@media (prefers-contrast: more) {
|
||||
border-color: var(--ak-c-fieldset--BorderColor, var(--pf-global--BorderColor--200));
|
||||
}
|
||||
|
||||
@media (prefers-contrast: less) {
|
||||
border-color: var(--ak-c-fieldset--BorderColor, transparent);
|
||||
}
|
||||
}
|
||||
|
||||
.ak-c-fieldset > legend {
|
||||
line-height: 1;
|
||||
padding: var(--ak-c-fieldset__legend--PaddingInlineBase) !important;
|
||||
margin-inline-start: var(
|
||||
--ak-c-fieldset__legend--MarginInlineStart,
|
||||
var(--ak-c-fieldset__legend--MarginInlineBase)
|
||||
) !important;
|
||||
margin-inline-end: var(
|
||||
--ak-legend-margin-inline-end,
|
||||
var(--ak-c-fieldset__legend--MarginInlineBase)
|
||||
) !important;
|
||||
}
|
||||
|
||||
.ak-c-fieldset:has(legend.sr-only:not(.more-contrast-only)) {
|
||||
border-width: 0;
|
||||
|
||||
&:not(.pf-c-modal-box__footer) {
|
||||
--ak-c-fieldset__legend--PaddingInlineBase: 0;
|
||||
--ak-c-fieldset__legend--MarginInlineBase: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ak-c-fieldset.pf-c-form__group {
|
||||
border-radius: var(--pf-global--BorderRadius--sm);
|
||||
}
|
||||
|
||||
.ak-c-fieldset.pf-c-form__group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
&.pf-m-action {
|
||||
--ak-c-fieldset__legend--PaddingInlineBase: 0;
|
||||
--ak-c-fieldset__legend--MarginInlineBase: 0;
|
||||
--ak-c-fieldset--BorderWidth: 0;
|
||||
|
||||
row-gap: var(--ak-c-fieldset--RowGap);
|
||||
column-gap: var(--ak-c-fieldset--ColumnGap);
|
||||
margin-block-start: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ak-c-fieldset.pf-c-login__main-footer-band > *:last-child {
|
||||
padding-block-end: var(--pf-c-login__main-footer-band-item--PaddingTop);
|
||||
}
|
||||
|
||||
/* TODO: Remove after ak-modal migration. */
|
||||
.ak-c-fieldset.pf-c-modal-box__footer {
|
||||
--ak-c-fieldset__legend--PaddingInlineBase: var(--pf-global--spacer--lg);
|
||||
|
||||
--pf-c-modal-box__footer--c-button--MarginRight: 0;
|
||||
--pf-c-modal-box__footer--c-button--sm--MarginRight: var(
|
||||
--pf-c-modal-box__footer--c-button--MarginRight
|
||||
);
|
||||
|
||||
gap: var(--pf-global--spacer--sm);
|
||||
justify-content: end;
|
||||
padding-block: calc(var(--ak-c-fieldset__legend--PaddingInlineBase) / 2);
|
||||
border-inline: none;
|
||||
border-block-end: none;
|
||||
|
||||
& > ak-spinner-button:not(:last-child) {
|
||||
margin-right: var(--pf-c-modal-box__footer--c-button--MarginRight);
|
||||
}
|
||||
}
|
||||
|
||||
[data-theme="dark"] .ak-c-fieldset,
|
||||
:host([theme="dark"]) .ak-c-fieldset {
|
||||
border-color: var(
|
||||
--ak-c-fieldset--BorderColor,
|
||||
var(--pf-global--BackgroundColor--dark-transparent-200)
|
||||
);
|
||||
|
||||
@media (prefers-contrast: more) {
|
||||
border-color: var(--ak-c-fieldset--BorderColor, var(--pf-global--BorderColor--300));
|
||||
}
|
||||
|
||||
@media (prefers-contrast: less) {
|
||||
border-color: var(--ak-c-fieldset--BorderColor, transparent);
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,11 @@
|
||||
);
|
||||
}
|
||||
|
||||
.pf-c-form__alert {
|
||||
display: grid;
|
||||
gap: var(--pf-global--spacer--form-element);
|
||||
}
|
||||
|
||||
.pf-c-form.ak-m-content-center {
|
||||
--pf-c-form--GridGap: var(--pf-global--spacer--sm);
|
||||
|
||||
@@ -90,91 +95,6 @@ ak-form-element-horizontal:has(.pf-c-form__helper-text + ak-checkbox-group) {
|
||||
}
|
||||
}
|
||||
|
||||
/* #region Fields */
|
||||
|
||||
fieldset {
|
||||
--ak-fieldset--BorderWidth: thin;
|
||||
--ak-fieldset__legend--MarginInlineBase: var(--pf-global--spacer--sm);
|
||||
--ak-fieldset__legend--PaddingInlineBase: var(--pf-global--spacer--sm);
|
||||
|
||||
border-color: var(--ak-fieldset--BorderColor, var(--pf-global--BackgroundColor--light-100));
|
||||
|
||||
@media (prefers-contrast: more) {
|
||||
border-color: var(--ak-fieldset--BorderColor, var(--pf-global--BorderColor--200));
|
||||
}
|
||||
|
||||
@media (prefers-contrast: less) {
|
||||
border-color: var(--ak-fieldset--BorderColor, transparent);
|
||||
}
|
||||
|
||||
border-width: var(--ak-fieldset--BorderWidth);
|
||||
|
||||
padding: var(--ak-fieldset__legend--PaddingInlineBase) !important;
|
||||
|
||||
& > legend {
|
||||
line-height: 1;
|
||||
padding: var(--ak-fieldset__legend--PaddingInlineBase) !important;
|
||||
margin-inline-start: var(
|
||||
--ak-fieldset__legend--MarginInlineStart,
|
||||
var(--ak-fieldset__legend--MarginInlineBase)
|
||||
) !important;
|
||||
margin-inline-end: var(
|
||||
--ak-legend-margin-inline-end,
|
||||
var(--ak-fieldset__legend--MarginInlineBase)
|
||||
) !important;
|
||||
}
|
||||
|
||||
&:has(legend.sr-only:not(.more-contrast-only)) {
|
||||
border-width: 0;
|
||||
|
||||
&:not(.pf-c-modal-box__footer) {
|
||||
--ak-fieldset__legend--PaddingInlineBase: 0;
|
||||
--ak-fieldset__legend--MarginInlineBase: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.pf-c-form__group {
|
||||
border-radius: var(--pf-global--BorderRadius--sm);
|
||||
}
|
||||
|
||||
&.pf-c-form__group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
&.pf-m-action {
|
||||
gap: var(--pf-global--spacer--md) var(--pf-global--spacer--sm);
|
||||
margin-block-start: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.pf-c-login__main-footer-band {
|
||||
& > *:last-child {
|
||||
padding-block-end: var(--pf-c-login__main-footer-band-item--PaddingTop);
|
||||
}
|
||||
}
|
||||
|
||||
&.pf-c-modal-box__footer {
|
||||
--ak-fieldset__legend--PaddingInlineBase: var(--pf-global--spacer--lg);
|
||||
--pf-c-modal-box__footer--c-button--MarginRight: 0;
|
||||
|
||||
gap: var(--pf-global--spacer--sm);
|
||||
justify-content: end;
|
||||
padding-block: calc(var(--ak-fieldset__legend--PaddingInlineBase) / 2);
|
||||
border-inline: none;
|
||||
border-block-end: none;
|
||||
|
||||
--pf-c-modal-box__footer--c-button--sm--MarginRight: var(
|
||||
--pf-c-modal-box__footer--c-button--MarginRight
|
||||
);
|
||||
|
||||
& > ak-spinner-button:not(:last-child) {
|
||||
margin-right: var(--pf-c-modal-box__footer--c-button--MarginRight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* #endregion */
|
||||
|
||||
/* #region Radio */
|
||||
|
||||
.pf-c-radio {
|
||||
@@ -402,21 +322,6 @@ ak-switch-input {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
border-color: var(
|
||||
--ak-fieldset--BorderColor,
|
||||
var(--pf-global--BackgroundColor--dark-transparent-200)
|
||||
);
|
||||
|
||||
@media (prefers-contrast: more) {
|
||||
border-color: var(--ak-fieldset--BorderColor, var(--pf-global--BorderColor--300));
|
||||
}
|
||||
|
||||
@media (prefers-contrast: less) {
|
||||
border-color: var(--ak-fieldset--BorderColor, transparent);
|
||||
}
|
||||
}
|
||||
|
||||
/* #endregion */
|
||||
|
||||
/* #region Radio */
|
||||
|
||||
@@ -16,13 +16,13 @@
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@media (width >= 1200px) {
|
||||
@media (width > 1210px) {
|
||||
.pf-c-page__sidebar.pf-m-expanded {
|
||||
--pf-c-page__sidebar--BoxShadow: var(--pf-global--BoxShadow--sm-right);
|
||||
}
|
||||
}
|
||||
|
||||
@media (width < 1200px) {
|
||||
@media (width <= 1210px) {
|
||||
.pf-c-page__sidebar.pf-m-expanded ~ .pf-c-page__drawer .pf-c-page__sidebar-backdrop::after {
|
||||
background-color: var(--pf-global--BackgroundColor--dark-transparent-100);
|
||||
content: "";
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
}
|
||||
|
||||
.pf-c-wizard__main-body {
|
||||
--ak-fieldset--BorderColor: var(--pf-global--BackgroundColor--150);
|
||||
--ak-c-fieldset--BorderColor: var(--pf-global--BackgroundColor--150);
|
||||
|
||||
gap: var(--pf-global--spacer--lg);
|
||||
|
||||
@@ -44,6 +44,8 @@
|
||||
.pf-c-wizard__footer {
|
||||
justify-content: end;
|
||||
align-items: center;
|
||||
/** Approximation of the height of navigation buttons to avoid excessive layout shifts when they are added or removed. */
|
||||
min-height: calc(var(--pf-global--spacer--3xl) + (var(--pf-global--spacer--form-element) * 2));
|
||||
}
|
||||
|
||||
.pf-c-wizard__nav-link {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
@import "@patternfly/patternfly/utilities/Text/text.css";
|
||||
@import "./components/Drawer/drawer.css";
|
||||
@import "./components/Form/form.css";
|
||||
@import "./components/Fieldset/fieldset.css";
|
||||
@import "./components/Login/login.css";
|
||||
@import "./components/Icon/icon.css";
|
||||
@import "#elements/locale/ak-locale-select.css";
|
||||
|
||||
@@ -132,7 +132,7 @@ ak-app-icon {
|
||||
|
||||
[part="app-group-header"] {
|
||||
@media not (prefers-contrast: more) {
|
||||
--ak-fieldset__legend--PaddingInlineBase: 1rem;
|
||||
--ak-c-fieldset__legend--PaddingInlineBase: 1rem;
|
||||
padding-block-start: 0 !important;
|
||||
padding-inline: 0 !important;
|
||||
margin-inline: 0 !important;
|
||||
|
||||
@@ -63,6 +63,7 @@ export const AKLibraryApplicationList: LitFC<AKLibraryApplicationListProps> = ({
|
||||
const groupID = kebabCase(groupLabel);
|
||||
|
||||
return html`<fieldset
|
||||
class="ak-c-fieldset"
|
||||
data-group-id=${ifPresent(groupID)}
|
||||
part="app-group"
|
||||
data-group-index=${groupIndex}
|
||||
|
||||
@@ -178,7 +178,7 @@ export class LibraryPage extends WithSession(AKElement) {
|
||||
threshold: 0.3,
|
||||
});
|
||||
|
||||
public pageTitle = msg("User Dashboard - Applications");
|
||||
public pageTitle = msg("My Applications");
|
||||
|
||||
//#region Lifecycle
|
||||
|
||||
@@ -432,7 +432,7 @@ export class LibraryPage extends WithSession(AKElement) {
|
||||
protected override render() {
|
||||
return html`<div class="pf-c-page__main">
|
||||
<div class="pf-c-page__header pf-c-content">
|
||||
<h1 class="pf-c-page__title">${msg("User Dashboard")}</h1>
|
||||
<h1 class="pf-c-page__title">${msg("My applications")}</h1>
|
||||
${this.searchEnabled ? this.renderSearch() : nothing}
|
||||
</div>
|
||||
<main
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user