mirror of
https://github.com/goauthentik/authentik
synced 2026-04-25 17:15:26 +02:00
Merge branch 'main' into web/stage--identification--change-login-to-log-in
This commit is contained in:
16
.github/workflows/ci-main-daily.yml
vendored
16
.github/workflows/ci-main-daily.yml
vendored
@@ -6,6 +6,10 @@ on:
|
||||
schedule:
|
||||
# Every night at 3am
|
||||
- cron: "0 3 * * *"
|
||||
pull_request:
|
||||
paths:
|
||||
# Needs to refer to itself
|
||||
- .github/workflows/ci-main-daily.yml
|
||||
|
||||
jobs:
|
||||
test-container:
|
||||
@@ -15,14 +19,14 @@ jobs:
|
||||
matrix:
|
||||
version:
|
||||
- docs
|
||||
- version-2025-4
|
||||
- version-2025-2
|
||||
- version-2025-12
|
||||
- version-2025-10
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- run: |
|
||||
current="$(pwd)"
|
||||
dir="/tmp/authentik/${{ matrix.version }}"
|
||||
mkdir -p $dir
|
||||
cd $dir
|
||||
wget https://${{ matrix.version }}.goauthentik.io/compose.yml
|
||||
${current}/scripts/test_docker.sh
|
||||
mkdir -p "${dir}/lifecycle/container"
|
||||
cd "${dir}"
|
||||
wget "https://${{ matrix.version }}.goauthentik.io/docker-compose.yml" -O "${dir}/lifecycle/container/compose.yml"
|
||||
"${current}/scripts/test_docker.sh"
|
||||
|
||||
@@ -15,6 +15,7 @@ from django.core.cache import cache
|
||||
from django.db.models.query import QuerySet
|
||||
from django.utils.timezone import now
|
||||
from jwt import PyJWTError, decode, get_unverified_header
|
||||
from jwt.algorithms import ECAlgorithm
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.fields import (
|
||||
ChoiceField,
|
||||
@@ -109,7 +110,15 @@ class LicenseKey:
|
||||
intermediate.verify_directly_issued_by(get_licensing_key())
|
||||
except InvalidSignature, TypeError, ValueError, Error:
|
||||
raise ValidationError("Unable to verify license") from None
|
||||
_validate_curve_original = ECAlgorithm._validate_curve
|
||||
try:
|
||||
# authentik's license used to be generated with `algorithm="ES512"` and signed with
|
||||
# a key of curve `secp384r1`. Starting with version 2.11.0, pyjwt enforces the spec, see
|
||||
# https://github.com/jpadilla/pyjwt/commit/5b8622773358e56d3d3c0a9acf404809ff34433a
|
||||
# New licenses are generated with `algorithm="ES384"` and signed with `secp384r1`.
|
||||
# The last license will run out by March 2027.
|
||||
# TODO: remove this in March 2027.
|
||||
ECAlgorithm._validate_curve = lambda *_: True
|
||||
body = from_dict(
|
||||
LicenseKey,
|
||||
decode(
|
||||
@@ -125,6 +134,8 @@ class LicenseKey:
|
||||
if unverified["aud"] != get_license_aud():
|
||||
raise ValidationError("Invalid Install ID in license") from None
|
||||
raise ValidationError("Unable to verify license") from None
|
||||
finally:
|
||||
ECAlgorithm._validate_curve = _validate_curve_original
|
||||
return body
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
from datetime import date
|
||||
from datetime import datetime
|
||||
|
||||
from django.db.models import BooleanField as ModelBooleanField
|
||||
from django.db.models import Case, Q, Value, When
|
||||
from django_filters.rest_framework import BooleanFilter, FilterSet
|
||||
from drf_spectacular.utils import extend_schema, extend_schema_field
|
||||
from drf_spectacular.utils import extend_schema
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.fields import DateField, IntegerField, SerializerMethodField
|
||||
from rest_framework.fields import IntegerField, SerializerMethodField
|
||||
from rest_framework.mixins import CreateModelMixin
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
@@ -21,6 +21,7 @@ from authentik.enterprise.lifecycle.utils import (
|
||||
ReviewerUserSerializer,
|
||||
admin_link_for_model,
|
||||
parse_content_type,
|
||||
start_of_day,
|
||||
)
|
||||
from authentik.lib.utils.time import timedelta_from_string
|
||||
|
||||
@@ -67,13 +68,13 @@ class LifecycleIterationSerializer(EnterpriseRequiredMixin, ModelSerializer):
|
||||
def get_object_admin_url(self, iteration: LifecycleIteration) -> str:
|
||||
return admin_link_for_model(iteration.object)
|
||||
|
||||
@extend_schema_field(DateField())
|
||||
def get_grace_period_end(self, iteration: LifecycleIteration) -> date:
|
||||
return iteration.opened_on + timedelta_from_string(iteration.rule.grace_period)
|
||||
def get_grace_period_end(self, iteration: LifecycleIteration) -> datetime:
|
||||
return start_of_day(
|
||||
iteration.opened_on + timedelta_from_string(iteration.rule.grace_period)
|
||||
)
|
||||
|
||||
@extend_schema_field(DateField())
|
||||
def get_next_review_date(self, iteration: LifecycleIteration):
|
||||
return iteration.opened_on + timedelta_from_string(iteration.rule.interval)
|
||||
def get_next_review_date(self, iteration: LifecycleIteration) -> datetime:
|
||||
return start_of_day(iteration.opened_on + timedelta_from_string(iteration.rule.interval))
|
||||
|
||||
def get_user_can_review(self, iteration: LifecycleIteration) -> bool:
|
||||
return iteration.user_can_review(self.context["request"].user)
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.2.11 on 2026-02-13 09:33
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_lifecycle", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="lifecycleiteration",
|
||||
name="opened_on",
|
||||
field=models.DateTimeField(auto_now_add=True),
|
||||
),
|
||||
]
|
||||
@@ -1,3 +1,4 @@
|
||||
from datetime import timedelta
|
||||
from uuid import uuid4
|
||||
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
@@ -13,7 +14,7 @@ from rest_framework.serializers import BaseSerializer
|
||||
|
||||
from authentik.blueprints.models import ManagedModel
|
||||
from authentik.core.models import Group, User
|
||||
from authentik.enterprise.lifecycle.utils import link_for_model
|
||||
from authentik.enterprise.lifecycle.utils import link_for_model, start_of_day
|
||||
from authentik.events.models import Event, EventAction, NotificationSeverity, NotificationTransport
|
||||
from authentik.lib.models import SerializerModel
|
||||
from authentik.lib.utils.time import timedelta_from_string, timedelta_string_validator
|
||||
@@ -98,7 +99,9 @@ class LifecycleRule(SerializerModel):
|
||||
|
||||
def _get_newly_overdue_iterations(self) -> QuerySet[LifecycleIteration]:
|
||||
return self.lifecycleiteration_set.filter(
|
||||
opened_on__lte=timezone.now() - timedelta_from_string(self.grace_period),
|
||||
opened_on__lt=start_of_day(
|
||||
timezone.now() + timedelta(days=1) - timedelta_from_string(self.grace_period)
|
||||
),
|
||||
state=ReviewState.PENDING,
|
||||
)
|
||||
|
||||
@@ -106,7 +109,9 @@ class LifecycleRule(SerializerModel):
|
||||
recent_iteration_ids = LifecycleIteration.objects.filter(
|
||||
content_type=self.content_type,
|
||||
object_id__isnull=False,
|
||||
opened_on__gte=timezone.now() - timedelta_from_string(self.interval),
|
||||
opened_on__gte=start_of_day(
|
||||
timezone.now() + timedelta(days=1) - timedelta_from_string(self.interval)
|
||||
),
|
||||
).values_list(Cast("object_id", output_field=self._get_pk_field()), flat=True)
|
||||
|
||||
return self.get_objects().exclude(pk__in=recent_iteration_ids)
|
||||
@@ -186,7 +191,7 @@ class LifecycleIteration(SerializerModel, ManagedModel):
|
||||
rule = models.ForeignKey(LifecycleRule, null=True, on_delete=models.SET_NULL)
|
||||
|
||||
state = models.CharField(max_length=10, choices=ReviewState, default=ReviewState.PENDING)
|
||||
opened_on = models.DateField(auto_now_add=True)
|
||||
opened_on = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
class Meta:
|
||||
indexes = [models.Index(fields=["content_type", "opened_on"])]
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import datetime as dt
|
||||
from datetime import timedelta
|
||||
from unittest.mock import patch
|
||||
|
||||
@@ -319,7 +320,7 @@ class TestLifecycleModels(TestCase):
|
||||
content_type=content_type, object_id=str(app_one.pk), rule=rule_overdue
|
||||
)
|
||||
LifecycleIteration.objects.filter(pk=iteration.pk).update(
|
||||
opened_on=(timezone.now().date() - timedelta(days=20))
|
||||
opened_on=(timezone.now() - timedelta(days=20))
|
||||
)
|
||||
|
||||
# Apply again to trigger overdue logic
|
||||
@@ -383,7 +384,7 @@ class TestLifecycleModels(TestCase):
|
||||
content_type=content_type, object_id=str(app_overdue.pk), rule=rule_overdue
|
||||
)
|
||||
LifecycleIteration.objects.filter(pk=overdue_iteration.pk).update(
|
||||
opened_on=(timezone.now().date() - timedelta(days=20))
|
||||
opened_on=(timezone.now() - timedelta(days=20))
|
||||
)
|
||||
|
||||
# Apply overdue rule to mark iteration as overdue
|
||||
@@ -667,3 +668,178 @@ class TestLifecycleModels(TestCase):
|
||||
reviewers = list(rule.get_reviewers())
|
||||
self.assertIn(explicit_reviewer, reviewers)
|
||||
self.assertIn(group_member, reviewers)
|
||||
|
||||
|
||||
class TestLifecycleDateBoundaries(TestCase):
|
||||
"""Verify that start_of_day normalization ensures correct overdue/due
|
||||
detection regardless of exact task execution time within a day.
|
||||
|
||||
The daily task may run at any point during the day. The start_of_day
|
||||
normalization in _get_newly_overdue_iterations and _get_newly_due_objects
|
||||
ensures that the boundary is always at midnight, so millisecond variations
|
||||
in task execution time do not affect results."""
|
||||
|
||||
def _create_rule_and_iteration(self, grace_period="days=1", interval="days=365"):
|
||||
app = Application.objects.create(name=generate_id(), slug=generate_id())
|
||||
content_type = ContentType.objects.get_for_model(Application)
|
||||
rule = LifecycleRule.objects.create(
|
||||
name=generate_id(),
|
||||
content_type=content_type,
|
||||
object_id=str(app.pk),
|
||||
interval=interval,
|
||||
grace_period=grace_period,
|
||||
)
|
||||
iteration = LifecycleIteration.objects.get(
|
||||
content_type=content_type, object_id=str(app.pk), rule=rule
|
||||
)
|
||||
return app, rule, iteration
|
||||
|
||||
def test_overdue_iteration_opened_yesterday(self):
|
||||
"""grace_period=1 day: iteration opened yesterday at any time is overdue today."""
|
||||
_, rule, iteration = self._create_rule_and_iteration(grace_period="days=1")
|
||||
fixed_now = dt.datetime(2025, 6, 15, 14, 30, 0, tzinfo=dt.UTC)
|
||||
for opened_on in [
|
||||
dt.datetime(2025, 6, 14, 0, 0, 0, tzinfo=dt.UTC),
|
||||
dt.datetime(2025, 6, 14, 12, 0, 0, tzinfo=dt.UTC),
|
||||
dt.datetime(2025, 6, 14, 23, 59, 59, 999999, tzinfo=dt.UTC),
|
||||
]:
|
||||
with self.subTest(opened_on=opened_on):
|
||||
LifecycleIteration.objects.filter(pk=iteration.pk).update(
|
||||
opened_on=opened_on, state=ReviewState.PENDING
|
||||
)
|
||||
with patch("django.utils.timezone.now", return_value=fixed_now):
|
||||
self.assertIn(iteration, list(rule._get_newly_overdue_iterations()))
|
||||
|
||||
def test_not_overdue_iteration_opened_today(self):
|
||||
"""grace_period=1 day: iteration opened today at any time is NOT overdue."""
|
||||
_, rule, iteration = self._create_rule_and_iteration(grace_period="days=1")
|
||||
fixed_now = dt.datetime(2025, 6, 15, 14, 30, 0, tzinfo=dt.UTC)
|
||||
for opened_on in [
|
||||
dt.datetime(2025, 6, 15, 0, 0, 0, tzinfo=dt.UTC),
|
||||
dt.datetime(2025, 6, 15, 14, 30, 0, tzinfo=dt.UTC),
|
||||
dt.datetime(2025, 6, 15, 23, 59, 59, 999999, tzinfo=dt.UTC),
|
||||
]:
|
||||
with self.subTest(opened_on=opened_on):
|
||||
LifecycleIteration.objects.filter(pk=iteration.pk).update(
|
||||
opened_on=opened_on, state=ReviewState.PENDING
|
||||
)
|
||||
with patch("django.utils.timezone.now", return_value=fixed_now):
|
||||
self.assertNotIn(iteration, list(rule._get_newly_overdue_iterations()))
|
||||
|
||||
def test_overdue_independent_of_task_execution_time(self):
|
||||
"""Overdue detection gives the same result whether the task runs at 00:00:01 or 23:59:59."""
|
||||
_, rule, iteration = self._create_rule_and_iteration(grace_period="days=1")
|
||||
opened_on = dt.datetime(2025, 6, 14, 18, 0, 0, tzinfo=dt.UTC)
|
||||
LifecycleIteration.objects.filter(pk=iteration.pk).update(
|
||||
opened_on=opened_on, state=ReviewState.PENDING
|
||||
)
|
||||
for task_time in [
|
||||
dt.datetime(2025, 6, 15, 0, 0, 1, tzinfo=dt.UTC),
|
||||
dt.datetime(2025, 6, 15, 12, 0, 0, tzinfo=dt.UTC),
|
||||
dt.datetime(2025, 6, 15, 23, 59, 59, tzinfo=dt.UTC),
|
||||
]:
|
||||
with self.subTest(task_time=task_time):
|
||||
with patch("django.utils.timezone.now", return_value=task_time):
|
||||
self.assertIn(iteration, list(rule._get_newly_overdue_iterations()))
|
||||
|
||||
def test_overdue_boundary_multi_day_grace_period(self):
|
||||
"""grace_period=30 days: overdue after 30 full days, not after 29."""
|
||||
_, rule, iteration = self._create_rule_and_iteration(grace_period="days=30")
|
||||
fixed_now = dt.datetime(2025, 6, 15, 14, 30, 0, tzinfo=dt.UTC)
|
||||
|
||||
# Opened 30 days ago (May 16), should go overdue
|
||||
LifecycleIteration.objects.filter(pk=iteration.pk).update(
|
||||
opened_on=dt.datetime(2025, 5, 16, 12, 0, 0, tzinfo=dt.UTC),
|
||||
state=ReviewState.PENDING,
|
||||
)
|
||||
with patch("django.utils.timezone.now", return_value=fixed_now):
|
||||
self.assertIn(iteration, list(rule._get_newly_overdue_iterations()))
|
||||
|
||||
# Opened 29 days ago (May 17), should NOT go overdue
|
||||
LifecycleIteration.objects.filter(pk=iteration.pk).update(
|
||||
opened_on=dt.datetime(2025, 5, 17, 12, 0, 0, tzinfo=dt.UTC),
|
||||
state=ReviewState.PENDING,
|
||||
)
|
||||
with patch("django.utils.timezone.now", return_value=fixed_now):
|
||||
self.assertNotIn(iteration, list(rule._get_newly_overdue_iterations()))
|
||||
|
||||
def test_due_object_iteration_opened_yesterday(self):
|
||||
"""interval=1 day: object with iteration opened yesterday is due for a new review."""
|
||||
app, rule, iteration = self._create_rule_and_iteration(interval="days=1")
|
||||
fixed_now = dt.datetime(2025, 6, 15, 14, 30, 0, tzinfo=dt.UTC)
|
||||
for opened_on in [
|
||||
dt.datetime(2025, 6, 14, 0, 0, 0, tzinfo=dt.UTC),
|
||||
dt.datetime(2025, 6, 14, 12, 0, 0, tzinfo=dt.UTC),
|
||||
dt.datetime(2025, 6, 14, 23, 59, 59, 999999, tzinfo=dt.UTC),
|
||||
]:
|
||||
with self.subTest(opened_on=opened_on):
|
||||
LifecycleIteration.objects.filter(pk=iteration.pk).update(opened_on=opened_on)
|
||||
with patch("django.utils.timezone.now", return_value=fixed_now):
|
||||
self.assertIn(app, list(rule._get_newly_due_objects()))
|
||||
|
||||
def test_not_due_object_iteration_opened_today(self):
|
||||
"""interval=1 day: object with iteration opened today is NOT due."""
|
||||
app, rule, iteration = self._create_rule_and_iteration(interval="days=1")
|
||||
fixed_now = dt.datetime(2025, 6, 15, 14, 30, 0, tzinfo=dt.UTC)
|
||||
for opened_on in [
|
||||
dt.datetime(2025, 6, 15, 0, 0, 0, tzinfo=dt.UTC),
|
||||
dt.datetime(2025, 6, 15, 14, 30, 0, tzinfo=dt.UTC),
|
||||
dt.datetime(2025, 6, 15, 23, 59, 59, 999999, tzinfo=dt.UTC),
|
||||
]:
|
||||
with self.subTest(opened_on=opened_on):
|
||||
LifecycleIteration.objects.filter(pk=iteration.pk).update(opened_on=opened_on)
|
||||
with patch("django.utils.timezone.now", return_value=fixed_now):
|
||||
self.assertNotIn(app, list(rule._get_newly_due_objects()))
|
||||
|
||||
def test_due_independent_of_task_execution_time(self):
|
||||
"""Due detection gives the same result whether the task runs at 00:00:01 or 23:59:59."""
|
||||
app, rule, iteration = self._create_rule_and_iteration(interval="days=1")
|
||||
opened_on = dt.datetime(2025, 6, 14, 18, 0, 0, tzinfo=dt.UTC)
|
||||
LifecycleIteration.objects.filter(pk=iteration.pk).update(opened_on=opened_on)
|
||||
for task_time in [
|
||||
dt.datetime(2025, 6, 15, 0, 0, 1, tzinfo=dt.UTC),
|
||||
dt.datetime(2025, 6, 15, 12, 0, 0, tzinfo=dt.UTC),
|
||||
dt.datetime(2025, 6, 15, 23, 59, 59, tzinfo=dt.UTC),
|
||||
]:
|
||||
with self.subTest(task_time=task_time):
|
||||
with patch("django.utils.timezone.now", return_value=task_time):
|
||||
self.assertIn(app, list(rule._get_newly_due_objects()))
|
||||
|
||||
def test_due_boundary_multi_day_interval(self):
|
||||
"""interval=30 days: due after 30 full days, not after 29."""
|
||||
app, rule, iteration = self._create_rule_and_iteration(interval="days=30")
|
||||
fixed_now = dt.datetime(2025, 6, 15, 14, 30, 0, tzinfo=dt.UTC)
|
||||
|
||||
# Previous review opened 30 days ago (May 16), review is due for the object
|
||||
LifecycleIteration.objects.filter(pk=iteration.pk).update(
|
||||
opened_on=dt.datetime(2025, 5, 16, 12, 0, 0, tzinfo=dt.UTC)
|
||||
)
|
||||
with patch("django.utils.timezone.now", return_value=fixed_now):
|
||||
self.assertIn(app, list(rule._get_newly_due_objects()))
|
||||
|
||||
# Previous review opened 29 days ago (May 17), new review is NOT due
|
||||
LifecycleIteration.objects.filter(pk=iteration.pk).update(
|
||||
opened_on=dt.datetime(2025, 5, 17, 12, 0, 0, tzinfo=dt.UTC)
|
||||
)
|
||||
with patch("django.utils.timezone.now", return_value=fixed_now):
|
||||
self.assertNotIn(app, list(rule._get_newly_due_objects()))
|
||||
|
||||
def test_apply_overdue_at_boundary(self):
|
||||
"""apply() marks iteration overdue when grace period just expired,
|
||||
regardless of what time the daily task runs."""
|
||||
_, rule, iteration = self._create_rule_and_iteration(
|
||||
grace_period="days=1", interval="days=365"
|
||||
)
|
||||
opened_on = dt.datetime(2025, 6, 14, 20, 0, 0, tzinfo=dt.UTC)
|
||||
for task_time in [
|
||||
dt.datetime(2025, 6, 15, 0, 0, 1, tzinfo=dt.UTC),
|
||||
dt.datetime(2025, 6, 15, 23, 59, 59, tzinfo=dt.UTC),
|
||||
]:
|
||||
with self.subTest(task_time=task_time):
|
||||
LifecycleIteration.objects.filter(pk=iteration.pk).update(
|
||||
opened_on=opened_on, state=ReviewState.PENDING
|
||||
)
|
||||
with patch("django.utils.timezone.now", return_value=task_time):
|
||||
rule.apply()
|
||||
iteration.refresh_from_db()
|
||||
self.assertEqual(iteration.state, ReviewState.OVERDUE)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from datetime import datetime
|
||||
from urllib import parse
|
||||
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
@@ -39,6 +40,10 @@ def link_for_model(model: Model) -> str:
|
||||
return f"{reverse("authentik_core:if-admin")}#{admin_link_for_model(model)}"
|
||||
|
||||
|
||||
def start_of_day(dt: datetime) -> datetime:
|
||||
return dt.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
|
||||
|
||||
class ContentTypeField(ChoiceField):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(choices=model_choices(), **kwargs)
|
||||
|
||||
@@ -78,7 +78,8 @@ class MicrosoftEntraUserClient(MicrosoftEntraSyncClient[User, MicrosoftEntraProv
|
||||
def create(self, user: User):
|
||||
"""Create user from scratch and create a connection object"""
|
||||
microsoft_user = self.to_schema(user, None)
|
||||
self.check_email_valid(microsoft_user.user_principal_name)
|
||||
if microsoft_user.user_principal_name:
|
||||
self.check_email_valid(microsoft_user.user_principal_name)
|
||||
with transaction.atomic():
|
||||
try:
|
||||
response = self._request(self.client.users.post(microsoft_user))
|
||||
@@ -118,7 +119,8 @@ class MicrosoftEntraUserClient(MicrosoftEntraSyncClient[User, MicrosoftEntraProv
|
||||
def update(self, user: User, connection: MicrosoftEntraProviderUser):
|
||||
"""Update existing user"""
|
||||
microsoft_user = self.to_schema(user, connection)
|
||||
self.check_email_valid(microsoft_user.user_principal_name)
|
||||
if microsoft_user.user_principal_name:
|
||||
self.check_email_valid(microsoft_user.user_principal_name)
|
||||
response = self._request(
|
||||
self.client.users.by_user_id(connection.microsoft_id).patch(microsoft_user)
|
||||
)
|
||||
|
||||
@@ -185,6 +185,16 @@ class PolicyEngine:
|
||||
# Only call .recv() if no result is saved, otherwise we just deadlock here
|
||||
if not proc_info.result:
|
||||
proc_info.result = proc_info.connection.recv()
|
||||
if proc_info.result and proc_info.result._exec_time:
|
||||
HIST_POLICIES_EXECUTION_TIME.labels(
|
||||
binding_order=proc_info.binding.order,
|
||||
binding_target_type=proc_info.binding.target_type,
|
||||
binding_target_name=proc_info.binding.target_name,
|
||||
object_type=(
|
||||
class_to_path(self.request.obj.__class__) if self.request.obj else ""
|
||||
),
|
||||
mode="execute_process",
|
||||
).observe(proc_info.result._exec_time)
|
||||
return self
|
||||
|
||||
@property
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from multiprocessing import get_context
|
||||
from multiprocessing.connection import Connection
|
||||
from time import perf_counter
|
||||
|
||||
from django.core.cache import cache
|
||||
from sentry_sdk import start_span
|
||||
@@ -11,8 +12,6 @@ from structlog.stdlib import get_logger
|
||||
from authentik.events.models import Event, EventAction
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.lib.utils.errors import exception_to_dict
|
||||
from authentik.lib.utils.reflection import class_to_path
|
||||
from authentik.policies.apps import HIST_POLICIES_EXECUTION_TIME
|
||||
from authentik.policies.exceptions import PolicyException
|
||||
from authentik.policies.models import PolicyBinding
|
||||
from authentik.policies.types import CACHE_PREFIX, PolicyRequest, PolicyResult
|
||||
@@ -123,18 +122,9 @@ class PolicyProcess(PROCESS_CLASS):
|
||||
|
||||
def profiling_wrapper(self):
|
||||
"""Run with profiling enabled"""
|
||||
with (
|
||||
start_span(
|
||||
op="authentik.policy.process.execute",
|
||||
) as span,
|
||||
HIST_POLICIES_EXECUTION_TIME.labels(
|
||||
binding_order=self.binding.order,
|
||||
binding_target_type=self.binding.target_type,
|
||||
binding_target_name=self.binding.target_name,
|
||||
object_type=class_to_path(self.request.obj.__class__) if self.request.obj else "",
|
||||
mode="execute_process",
|
||||
).time(),
|
||||
):
|
||||
with start_span(
|
||||
op="authentik.policy.process.execute",
|
||||
) as span:
|
||||
span: Span
|
||||
span.set_data("policy", self.binding.policy)
|
||||
span.set_data("request", self.request)
|
||||
@@ -142,8 +132,14 @@ class PolicyProcess(PROCESS_CLASS):
|
||||
|
||||
def run(self): # pragma: no cover
|
||||
"""Task wrapper to run policy checking"""
|
||||
result = None
|
||||
try:
|
||||
self.connection.send(self.profiling_wrapper())
|
||||
start = perf_counter()
|
||||
result = self.profiling_wrapper()
|
||||
end = perf_counter()
|
||||
result._exec_time = max((end - start), 0)
|
||||
except Exception as exc: # noqa
|
||||
LOGGER.warning("Policy failed to run", exc=exc)
|
||||
self.connection.send(PolicyResult(False, str(exc)))
|
||||
result = PolicyResult(False, str(exc))
|
||||
finally:
|
||||
self.connection.send(result)
|
||||
|
||||
@@ -77,6 +77,8 @@ class PolicyResult:
|
||||
|
||||
log_messages: list[LogEvent] | None
|
||||
|
||||
_exec_time: int | None
|
||||
|
||||
def __init__(self, passing: bool, *messages: str):
|
||||
self.passing = passing
|
||||
self.messages = messages
|
||||
@@ -84,6 +86,7 @@ class PolicyResult:
|
||||
self.source_binding = None
|
||||
self.source_results = []
|
||||
self.log_messages = []
|
||||
self._exec_time = None
|
||||
|
||||
def __repr__(self):
|
||||
return self.__str__()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"""Device backchannel tests"""
|
||||
|
||||
from base64 import b64encode
|
||||
from json import loads
|
||||
|
||||
from django.urls import reverse
|
||||
@@ -26,7 +27,7 @@ class TesOAuth2DeviceBackchannel(OAuthTestCase):
|
||||
provider=self.provider,
|
||||
)
|
||||
|
||||
def test_backchannel_invalid(self):
|
||||
def test_backchannel_invalid_client_id_via_post_body(self):
|
||||
"""Test backchannel"""
|
||||
res = self.client.post(
|
||||
reverse("authentik_providers_oauth2:device"),
|
||||
@@ -50,7 +51,7 @@ class TesOAuth2DeviceBackchannel(OAuthTestCase):
|
||||
)
|
||||
self.assertEqual(res.status_code, 400)
|
||||
|
||||
def test_backchannel(self):
|
||||
def test_backchannel_client_id_via_post_body(self):
|
||||
"""Test backchannel"""
|
||||
res = self.client.post(
|
||||
reverse("authentik_providers_oauth2:device"),
|
||||
@@ -61,3 +62,37 @@ class TesOAuth2DeviceBackchannel(OAuthTestCase):
|
||||
self.assertEqual(res.status_code, 200)
|
||||
body = loads(res.content.decode())
|
||||
self.assertEqual(body["expires_in"], 60)
|
||||
|
||||
def test_backchannel_invalid_client_id_via_auth_header(self):
|
||||
"""Test backchannel"""
|
||||
creds = b64encode(b"foo:").decode()
|
||||
res = self.client.post(
|
||||
reverse("authentik_providers_oauth2:device"),
|
||||
HTTP_AUTHORIZATION=f"Basic {creds}",
|
||||
)
|
||||
self.assertEqual(res.status_code, 400)
|
||||
res = self.client.post(
|
||||
reverse("authentik_providers_oauth2:device"),
|
||||
)
|
||||
self.assertEqual(res.status_code, 400)
|
||||
# test without application
|
||||
self.application.provider = None
|
||||
self.application.save()
|
||||
res = self.client.post(
|
||||
reverse("authentik_providers_oauth2:device"),
|
||||
data={
|
||||
"client_id": "test",
|
||||
},
|
||||
)
|
||||
self.assertEqual(res.status_code, 400)
|
||||
|
||||
def test_backchannel_client_id_via_auth_header(self):
|
||||
"""Test backchannel"""
|
||||
creds = b64encode(f"{self.provider.client_id}:".encode()).decode()
|
||||
res = self.client.post(
|
||||
reverse("authentik_providers_oauth2:device"),
|
||||
HTTP_AUTHORIZATION=f"Basic {creds}",
|
||||
)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
body = loads(res.content.decode())
|
||||
self.assertEqual(body["expires_in"], 60)
|
||||
|
||||
@@ -16,7 +16,7 @@ from authentik.lib.config import CONFIG
|
||||
from authentik.lib.utils.time import timedelta_from_string
|
||||
from authentik.providers.oauth2.errors import DeviceCodeError
|
||||
from authentik.providers.oauth2.models import DeviceToken, OAuth2Provider
|
||||
from authentik.providers.oauth2.utils import TokenResponse
|
||||
from authentik.providers.oauth2.utils import TokenResponse, extract_client_auth
|
||||
from authentik.providers.oauth2.views.device_init import QS_KEY_CODE
|
||||
|
||||
LOGGER = get_logger()
|
||||
@@ -32,7 +32,7 @@ class DeviceView(View):
|
||||
|
||||
def parse_request(self):
|
||||
"""Parse incoming request"""
|
||||
client_id = self.request.POST.get("client_id", None)
|
||||
client_id, _ = extract_client_auth(self.request)
|
||||
if not client_id:
|
||||
raise DeviceCodeError("invalid_client")
|
||||
provider = OAuth2Provider.objects.filter(client_id=client_id).first()
|
||||
|
||||
@@ -14,24 +14,6 @@ class SAMLException(SentryIgnoredException):
|
||||
return self.default_message
|
||||
|
||||
|
||||
class MissingSAMLResponse(SAMLException):
|
||||
"""Exception raised when request does not contain SAML Response."""
|
||||
|
||||
default_message = "Request does not contain a SAML response."
|
||||
|
||||
|
||||
class UnsupportedNameIDFormat(SAMLException):
|
||||
"""Exception raised when SAML Response contains NameID Format not supported."""
|
||||
|
||||
default_message = "The NameID Format in the SAML Response is not supported."
|
||||
|
||||
|
||||
class MismatchedRequestID(SAMLException):
|
||||
"""Exception raised when the returned request ID doesn't match the saved ID."""
|
||||
|
||||
default_message = "The SAML Response ID does not match the original request ID."
|
||||
|
||||
|
||||
class InvalidEncryption(SAMLException):
|
||||
"""Encryption of XML Object is either missing or invalid."""
|
||||
|
||||
@@ -42,3 +24,21 @@ class InvalidSignature(SAMLException):
|
||||
"""Signature of XML Object is either missing or invalid."""
|
||||
|
||||
default_message = "The signature of the SAML object is either missing or invalid."
|
||||
|
||||
|
||||
class MismatchedRequestID(SAMLException):
|
||||
"""Exception raised when the returned request ID doesn't match the saved ID."""
|
||||
|
||||
default_message = "The SAML Response ID does not match the original request ID."
|
||||
|
||||
|
||||
class MissingSAMLResponse(SAMLException):
|
||||
"""Exception raised when request does not contain SAML Response."""
|
||||
|
||||
default_message = "Request does not contain a SAML response."
|
||||
|
||||
|
||||
class UnsupportedNameIDFormat(SAMLException):
|
||||
"""Exception raised when SAML Response contains NameID Format not supported."""
|
||||
|
||||
default_message = "The NameID Format in the SAML Response is not supported."
|
||||
|
||||
@@ -4,6 +4,7 @@ from urllib.parse import parse_qsl, urlparse, urlunparse
|
||||
|
||||
from django.contrib.auth import logout
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.core.exceptions import SuspiciousOperation
|
||||
from django.http import Http404, HttpRequest, HttpResponse
|
||||
from django.http.response import HttpResponseBadRequest
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
@@ -37,7 +38,9 @@ from authentik.flows.views.executor import NEXT_ARG_NAME, SESSION_KEY_GET, SESSI
|
||||
from authentik.lib.views import bad_request_message
|
||||
from authentik.providers.saml.utils.encoding import nice64
|
||||
from authentik.sources.saml.exceptions import (
|
||||
InvalidEncryption,
|
||||
InvalidSignature,
|
||||
MismatchedRequestID,
|
||||
MissingSAMLResponse,
|
||||
UnsupportedNameIDFormat,
|
||||
)
|
||||
@@ -156,7 +159,15 @@ class ACSView(View):
|
||||
processor = ResponseProcessor(source, request)
|
||||
try:
|
||||
processor.parse()
|
||||
except (InvalidSignature, MissingSAMLResponse, VerificationError, ValueError) as exc:
|
||||
except (
|
||||
InvalidEncryption,
|
||||
InvalidSignature,
|
||||
MismatchedRequestID,
|
||||
MissingSAMLResponse,
|
||||
SuspiciousOperation,
|
||||
VerificationError,
|
||||
ValueError,
|
||||
) as exc:
|
||||
return bad_request_message(request, str(exc))
|
||||
|
||||
try:
|
||||
|
||||
@@ -6,6 +6,7 @@ from django.contrib.auth.views import redirect_to_login
|
||||
from django.http.request import HttpRequest
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.core.middleware import get_user
|
||||
from authentik.core.models import Session
|
||||
from authentik.events.context_processors.asn import ASN_CONTEXT_PROCESSOR
|
||||
from authentik.events.context_processors.geoip import GEOIP_CONTEXT_PROCESSOR
|
||||
@@ -54,11 +55,13 @@ class SessionBindingBroken(SentryIgnoredException):
|
||||
|
||||
def logout_extra(request: HttpRequest, exc: SessionBindingBroken):
|
||||
"""Similar to django's logout method, but able to carry more info to the signal"""
|
||||
# Dispatch the signal before the user is logged out so the receivers have a
|
||||
# chance to find out *who* logged out.
|
||||
user = getattr(request, "user", None)
|
||||
# Since this middleware runs before the AuthenticationMiddleware, we can't use `request.user`
|
||||
# as it hasn't been populated yet.
|
||||
user = get_user(request)
|
||||
if not getattr(user, "is_authenticated", True):
|
||||
user = None
|
||||
# Dispatch the signal before the user is logged out so the receivers have a
|
||||
# chance to find out *who* logged out.
|
||||
user_logged_out.send(
|
||||
sender=user.__class__, request=request, user=user, event_extra=exc.to_event()
|
||||
)
|
||||
|
||||
@@ -10,6 +10,8 @@ from django.utils.timezone import now
|
||||
from authentik.blueprints.tests import apply_blueprint
|
||||
from authentik.core.models import AuthenticatedSession, Session
|
||||
from authentik.core.tests.utils import create_test_flow, create_test_user
|
||||
from authentik.events.models import Event, EventAction
|
||||
from authentik.events.utils import get_user
|
||||
from authentik.flows.markers import StageMarker
|
||||
from authentik.flows.models import FlowDesignation, FlowStageBinding
|
||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
|
||||
@@ -270,6 +272,7 @@ class TestUserLoginStage(FlowTestCase):
|
||||
|
||||
def test_session_binding_broken(self):
|
||||
"""Test session binding"""
|
||||
Event.objects.all().delete()
|
||||
self.client.force_login(self.user)
|
||||
session = self.client.session
|
||||
session[Session.Keys.LAST_IP] = "192.0.2.1"
|
||||
@@ -285,3 +288,5 @@ class TestUserLoginStage(FlowTestCase):
|
||||
)
|
||||
+ f"?{NEXT_ARG_NAME}={reverse("authentik_api:user-me")}",
|
||||
)
|
||||
event = Event.objects.filter(action=EventAction.LOGOUT).first()
|
||||
self.assertEqual(event.user, get_user(self.user))
|
||||
|
||||
@@ -76,7 +76,7 @@ func (a *Application) redirectToStart(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
redirectUrl := urlJoin(a.proxyConfig.ExternalHost, r.URL.Path)
|
||||
redirectUrl := urlJoin(a.proxyConfig.ExternalHost, r.URL.EscapedPath())
|
||||
|
||||
if a.Mode() == api.PROXYMODE_FORWARD_DOMAIN {
|
||||
dom := strings.TrimPrefix(*a.proxyConfig.CookieDomain, ".")
|
||||
|
||||
@@ -27,6 +27,24 @@ func TestRedirectToStart_Proxy(t *testing.T) {
|
||||
assert.Equal(t, "https://test.goauthentik.io/foo/bar/baz", s.Values[constants.SessionRedirect])
|
||||
}
|
||||
|
||||
func TestRedirectToStart_Proxy_EncodedSlash(t *testing.T) {
|
||||
a := newTestApplication()
|
||||
a.proxyConfig.Mode = api.PROXYMODE_PROXY.Ptr()
|
||||
a.proxyConfig.ExternalHost = "https://test.goauthentik.io"
|
||||
// %2F is a URL-encoded forward slash, used by apps like RabbitMQ in queue paths
|
||||
req, _ := http.NewRequest("GET", "/api/queues/%2F/MYChannelCreated", nil)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
a.redirectToStart(rr, req)
|
||||
|
||||
assert.Equal(t, http.StatusFound, rr.Code)
|
||||
loc, _ := rr.Result().Location()
|
||||
assert.Contains(t, loc.String(), "%252F", "encoded slash %2F must be preserved in redirect URL")
|
||||
|
||||
s, _ := a.sessions.Get(req, a.SessionName())
|
||||
assert.Contains(t, s.Values[constants.SessionRedirect].(string), "%2F", "encoded slash %2F must be preserved in session redirect")
|
||||
}
|
||||
|
||||
func TestRedirectToStart_Forward(t *testing.T) {
|
||||
a := newTestApplication()
|
||||
a.proxyConfig.Mode = api.PROXYMODE_FORWARD_SINGLE.Ptr()
|
||||
|
||||
@@ -80,7 +80,7 @@ RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
|
||||
# Stage 4: Download uv
|
||||
FROM ghcr.io/astral-sh/uv:0.10.4@sha256:4cac394b6b72846f8a85a7a0e577c6d61d4e17fe2ccee65d9451a8b3c9efb4ac AS uv
|
||||
# Stage 5: Base python image
|
||||
FROM ghcr.io/goauthentik/fips-python:3.14.3-slim-trixie-fips@sha256:c2726911d327b377501adb6c1ca9ba6b9bde8feb09bc8787cd358413784fe39f AS python-base
|
||||
FROM ghcr.io/goauthentik/fips-python:3.14.3-slim-trixie-fips@sha256:bccefeecbdd7b5895053c97b7cc24327380934e959a34b1331a1489201ed3df3 AS python-base
|
||||
|
||||
ENV VENV_PATH="/ak-root/.venv" \
|
||||
PATH="/lifecycle:/ak-root/.venv/bin:$PATH" \
|
||||
|
||||
@@ -31,7 +31,7 @@ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
|
||||
go build -o /go/ldap ./cmd/ldap
|
||||
|
||||
# Stage 2: Run
|
||||
FROM ghcr.io/goauthentik/fips-debian:trixie-slim-fips@sha256:b0917afc8d7d3dea6cf8e741fee59bbd40776b9a98e6fdac16143adac2c45394
|
||||
FROM ghcr.io/goauthentik/fips-debian:trixie-slim-fips@sha256:d6def0a23db74f699199c7d72fe57e2313982a51eeedc4883039b22538f6ed02
|
||||
|
||||
ARG VERSION
|
||||
ARG GIT_BUILD_HASH
|
||||
|
||||
@@ -47,7 +47,7 @@ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
|
||||
go build -o /go/proxy ./cmd/proxy
|
||||
|
||||
# Stage 3: Run
|
||||
FROM ghcr.io/goauthentik/fips-debian:trixie-slim-fips@sha256:b0917afc8d7d3dea6cf8e741fee59bbd40776b9a98e6fdac16143adac2c45394
|
||||
FROM ghcr.io/goauthentik/fips-debian:trixie-slim-fips@sha256:d6def0a23db74f699199c7d72fe57e2313982a51eeedc4883039b22538f6ed02
|
||||
|
||||
ARG VERSION
|
||||
ARG GIT_BUILD_HASH
|
||||
|
||||
@@ -31,7 +31,7 @@ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
|
||||
go build -o /go/radius ./cmd/radius
|
||||
|
||||
# Stage 2: Run
|
||||
FROM ghcr.io/goauthentik/fips-debian:trixie-slim-fips@sha256:b0917afc8d7d3dea6cf8e741fee59bbd40776b9a98e6fdac16143adac2c45394
|
||||
FROM ghcr.io/goauthentik/fips-debian:trixie-slim-fips@sha256:d6def0a23db74f699199c7d72fe57e2313982a51eeedc4883039b22538f6ed02
|
||||
|
||||
ARG VERSION
|
||||
ARG GIT_BUILD_HASH
|
||||
|
||||
666
package-lock.json
generated
666
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
36
package.json
36
package.json
@@ -2,6 +2,12 @@
|
||||
"name": "@goauthentik/authentik",
|
||||
"version": "2026.5.0-rc1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"lint": "run-s lint:spellcheck lint:lockfile",
|
||||
"lint:lockfile": "echo 'Skipping lockfile linting'",
|
||||
"lint:node": "echo 'Skipping node linting'",
|
||||
"lint:spellcheck": "echo 'Skipping spellcheck linting'"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@eslint/js": "^9.39.1",
|
||||
@@ -12,31 +18,31 @@
|
||||
"@typescript-eslint/parser": "^8.48.1",
|
||||
"eslint": "^9.39.1",
|
||||
"prettier": "^3.7.4",
|
||||
"prettier-plugin-packagejson": "^2.5.20",
|
||||
"prettier-plugin-packagejson": "^3.0.0",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.48.1"
|
||||
},
|
||||
"workspaces": [],
|
||||
"engines": {
|
||||
"node": ">=24",
|
||||
"npm": ">=11.6.2"
|
||||
"npm": ">=11.10.1"
|
||||
},
|
||||
"devEngines": {
|
||||
"runtime": {
|
||||
"name": "node",
|
||||
"onFail": "ignore",
|
||||
"version": "24"
|
||||
},
|
||||
"packageManager": {
|
||||
"name": "npm",
|
||||
"version": "11.10.1",
|
||||
"onFail": "ignore"
|
||||
}
|
||||
},
|
||||
"workspaces": [],
|
||||
"prettier": "@goauthentik/prettier-config",
|
||||
"overrides": {
|
||||
"format-imports": {
|
||||
"eslint": "$eslint"
|
||||
}
|
||||
},
|
||||
"devEngines": {
|
||||
"runtime": {
|
||||
"name": "node",
|
||||
"onFail": "warn",
|
||||
"version": ">=24"
|
||||
},
|
||||
"packageManager": {
|
||||
"name": "npm",
|
||||
"onFail": "warn",
|
||||
"version": ">=11.6.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
98
packages/docusaurus-config/package-lock.json
generated
98
packages/docusaurus-config/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@goauthentik/docusaurus-config",
|
||||
"version": "2.3.0",
|
||||
"version": "2.4.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@goauthentik/docusaurus-config",
|
||||
"version": "2.3.0",
|
||||
"version": "2.4.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"deepmerge-ts": "^7.1.5",
|
||||
@@ -27,14 +27,14 @@
|
||||
"@typescript-eslint/parser": "^8.47.0",
|
||||
"eslint": "^9.39.1",
|
||||
"pino": "^10.1.0",
|
||||
"prettier": "^3.7.4",
|
||||
"prettier-plugin-packagejson": "^2.5.20",
|
||||
"prettier": "^3.8.1",
|
||||
"prettier-plugin-packagejson": "^3.0.0",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.49.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=24",
|
||||
"npm": ">=11.6.2"
|
||||
"npm": ">=11.10.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"react": ">=18",
|
||||
@@ -61,7 +61,7 @@
|
||||
},
|
||||
"../eslint-config": {
|
||||
"name": "@goauthentik/eslint-config",
|
||||
"version": "1.2.0",
|
||||
"version": "1.3.0",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -101,7 +101,7 @@
|
||||
},
|
||||
"../prettier-config": {
|
||||
"name": "@goauthentik/prettier-config",
|
||||
"version": "3.3.1",
|
||||
"version": "3.4.0",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -115,18 +115,18 @@
|
||||
"@typescript-eslint/eslint-plugin": "^8.49.0",
|
||||
"@typescript-eslint/parser": "^8.49.0",
|
||||
"eslint": "^9.39.1",
|
||||
"prettier": "^3.7.4",
|
||||
"prettier-plugin-packagejson": "^2.5.20",
|
||||
"prettier": "^3.8.1",
|
||||
"prettier-plugin-packagejson": "^3.0.0",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.49.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=24",
|
||||
"npm": ">=11.6.2"
|
||||
"npm": ">=11.10.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prettier": "^3.7.4",
|
||||
"prettier-plugin-packagejson": "^2.5.20"
|
||||
"prettier": "^3.8.1",
|
||||
"prettier-plugin-packagejson": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"../tsconfig": {
|
||||
@@ -4699,19 +4699,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@pkgr/core": {
|
||||
"version": "0.2.9",
|
||||
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz",
|
||||
"integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/pkgr"
|
||||
}
|
||||
},
|
||||
"node_modules/@pnpm/config.env-replace": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz",
|
||||
@@ -9426,9 +9413,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/git-hooks-list": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-4.1.1.tgz",
|
||||
"integrity": "sha512-cmP497iLq54AZnv4YRAEMnEyQ1eIn4tGKbmswqwmFV4GBnAqE8NLtWxxdXa++AalfgL5EBH4IxTPyquEuGY/jA==",
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-4.2.1.tgz",
|
||||
"integrity": "sha512-WNvqJjOxxs/8ZP9+DWdwWJ7cDsd60NHf39XnD82pDVrKO5q7xfPqpkK6hwEAmBa/ZSEE4IOoR75EzbbIuwGlMw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
@@ -16131,9 +16118,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.7.4",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz",
|
||||
"integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==",
|
||||
"version": "3.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz",
|
||||
"integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
@@ -16147,17 +16134,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier-plugin-packagejson": {
|
||||
"version": "2.5.20",
|
||||
"resolved": "https://registry.npmjs.org/prettier-plugin-packagejson/-/prettier-plugin-packagejson-2.5.20.tgz",
|
||||
"integrity": "sha512-G8cowPh+QmJJECTZlrPDKWkVVcwrFjF2rGcw546w3N8blLoc4szSs8UUPfFVxHUNLUjiru71Ah83g1lZkeK9Bw==",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/prettier-plugin-packagejson/-/prettier-plugin-packagejson-3.0.0.tgz",
|
||||
"integrity": "sha512-z8/QmPSqx/ANvvQMWJSkSq1+ihBXeuwDEYdjX3ZjRJ5Ty1k7vGbFQfhzk2eDe0rwS/TNyRjWK/qnjJEStAOtDw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"sort-package-json": "3.5.0",
|
||||
"synckit": "0.11.11"
|
||||
"sort-package-json": "3.6.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prettier": ">= 1.16.0"
|
||||
"prettier": "^3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"prettier": {
|
||||
@@ -17886,26 +17872,26 @@
|
||||
}
|
||||
},
|
||||
"node_modules/sort-object-keys": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-2.0.1.tgz",
|
||||
"integrity": "sha512-R89fO+z3x7hiKPXX5P0qim+ge6Y60AjtlW+QQpRozrrNcR1lw9Pkpm5MLB56HoNvdcLHL4wbpq16OcvGpEDJIg==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-2.1.0.tgz",
|
||||
"integrity": "sha512-SOiEnthkJKPv2L6ec6HMwhUcN0/lppkeYuN1x63PbyPRrgSPIuBJCiYxYyvWRTtjMlOi14vQUCGUJqS6PLVm8g==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/sort-package-json": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-3.5.0.tgz",
|
||||
"integrity": "sha512-moY4UtptUuP5sPuu9H9dp8xHNel7eP5/Kz/7+90jTvC0IOiPH2LigtRM/aSFSxreaWoToHUVUpEV4a2tAs2oKQ==",
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-3.6.0.tgz",
|
||||
"integrity": "sha512-fyJsPLhWvY7u2KsKPZn1PixbXp+1m7V8NWqU8CvgFRbMEX41Ffw1kD8n0CfJiGoaSfoAvbrqRRl/DcHO8omQOQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"detect-indent": "^7.0.1",
|
||||
"detect-indent": "^7.0.2",
|
||||
"detect-newline": "^4.0.1",
|
||||
"git-hooks-list": "^4.0.0",
|
||||
"git-hooks-list": "^4.1.1",
|
||||
"is-plain-obj": "^4.1.0",
|
||||
"semver": "^7.7.1",
|
||||
"sort-object-keys": "^2.0.0",
|
||||
"tinyglobby": "^0.2.12"
|
||||
"semver": "^7.7.3",
|
||||
"sort-object-keys": "^2.0.1",
|
||||
"tinyglobby": "^0.2.15"
|
||||
},
|
||||
"bin": {
|
||||
"sort-package-json": "cli.js"
|
||||
@@ -18341,22 +18327,6 @@
|
||||
"react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/synckit": {
|
||||
"version": "0.11.11",
|
||||
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz",
|
||||
"integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@pkgr/core": "^0.2.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/synckit"
|
||||
}
|
||||
},
|
||||
"node_modules/tapable": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
{
|
||||
"name": "@goauthentik/docusaurus-config",
|
||||
"version": "2.3.0",
|
||||
"version": "2.4.0",
|
||||
"description": "authentik's Docusaurus config",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/goauthentik/authentik.git",
|
||||
"directory": "packages/docusaurus-config"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc -p .",
|
||||
"lint": "eslint --fix .",
|
||||
@@ -11,11 +16,12 @@
|
||||
"prettier-check": "prettier --cache --check -u ."
|
||||
},
|
||||
"type": "module",
|
||||
"types": "./out/index.d.ts",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"import": "./index.js",
|
||||
"types": "./out/index.d.ts"
|
||||
"types": "./out/index.d.ts",
|
||||
"import": "./index.js"
|
||||
},
|
||||
"./css/*.css": "./css/*.css"
|
||||
},
|
||||
@@ -38,8 +44,8 @@
|
||||
"@typescript-eslint/parser": "^8.47.0",
|
||||
"eslint": "^9.39.1",
|
||||
"pino": "^10.1.0",
|
||||
"prettier": "^3.7.4",
|
||||
"prettier-plugin-packagejson": "^2.5.20",
|
||||
"prettier": "^3.8.1",
|
||||
"prettier-plugin-packagejson": "^3.0.0",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.49.0"
|
||||
},
|
||||
@@ -54,22 +60,28 @@
|
||||
"react": ">=18",
|
||||
"react-dom": ">=18"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=24",
|
||||
"npm": ">=11.6.2"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/goauthentik/authentik.git",
|
||||
"directory": "packages/docusaurus-config"
|
||||
},
|
||||
"types": "./out/index.d.ts",
|
||||
"files": [
|
||||
"./index.js",
|
||||
"lib/**/*",
|
||||
"css/**/*",
|
||||
"out/**/*"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=24",
|
||||
"npm": ">=11.10.0"
|
||||
},
|
||||
"devEngines": {
|
||||
"runtime": {
|
||||
"name": "node",
|
||||
"onFail": "warn",
|
||||
"version": ">=24"
|
||||
},
|
||||
"packageManager": {
|
||||
"name": "npm",
|
||||
"version": "11.10.1",
|
||||
"onFail": "warn"
|
||||
}
|
||||
},
|
||||
"prettier": "@goauthentik/prettier-config",
|
||||
"peerDependenciesMeta": {
|
||||
"@docusaurus/theme-search-algolia": {
|
||||
|
||||
105
packages/esbuild-plugin-live-reload/package-lock.json
generated
105
packages/esbuild-plugin-live-reload/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@goauthentik/esbuild-plugin-live-reload",
|
||||
"version": "1.4.0",
|
||||
"version": "1.5.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@goauthentik/esbuild-plugin-live-reload",
|
||||
"version": "1.4.0",
|
||||
"version": "1.5.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"find-free-ports": "^3.1.1"
|
||||
@@ -22,8 +22,8 @@
|
||||
"esbuild": "^0.27.1",
|
||||
"eslint": "^9.39.1",
|
||||
"pino": "^10.1.0",
|
||||
"prettier": "^3.7.4",
|
||||
"prettier-plugin-packagejson": "^2.5.20",
|
||||
"prettier": "^3.8.1",
|
||||
"prettier-plugin-packagejson": "^3.0.0",
|
||||
"typedoc": "^0.28.15",
|
||||
"typedoc-plugin-markdown": "^4.9.0",
|
||||
"typescript": "^5.9.3",
|
||||
@@ -39,7 +39,7 @@
|
||||
},
|
||||
"../eslint-config": {
|
||||
"name": "@goauthentik/eslint-config",
|
||||
"version": "1.2.0",
|
||||
"version": "1.2.1",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -60,7 +60,7 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=24",
|
||||
"npm": ">=11.6.2"
|
||||
"npm": ">=11.10.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
@@ -79,7 +79,7 @@
|
||||
},
|
||||
"../prettier-config": {
|
||||
"name": "@goauthentik/prettier-config",
|
||||
"version": "3.3.1",
|
||||
"version": "3.4.0",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -93,18 +93,18 @@
|
||||
"@typescript-eslint/eslint-plugin": "^8.49.0",
|
||||
"@typescript-eslint/parser": "^8.49.0",
|
||||
"eslint": "^9.39.1",
|
||||
"prettier": "^3.7.4",
|
||||
"prettier-plugin-packagejson": "^2.5.20",
|
||||
"prettier": "^3.8.1",
|
||||
"prettier-plugin-packagejson": "^3.0.0",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.49.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=24",
|
||||
"npm": ">=11.6.2"
|
||||
"npm": ">=11.10.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prettier": "^3.7.4",
|
||||
"prettier-plugin-packagejson": "^2.5.20"
|
||||
"prettier": "^3.8.1",
|
||||
"prettier-plugin-packagejson": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"../tsconfig": {
|
||||
@@ -833,19 +833,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@pkgr/core": {
|
||||
"version": "0.2.9",
|
||||
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz",
|
||||
"integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/pkgr"
|
||||
}
|
||||
},
|
||||
"node_modules/@shikijs/engine-oniguruma": {
|
||||
"version": "3.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.19.0.tgz",
|
||||
@@ -971,7 +958,6 @@
|
||||
"integrity": "sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.49.0",
|
||||
"@typescript-eslint/types": "8.49.0",
|
||||
@@ -1176,7 +1162,6 @@
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -1455,7 +1440,6 @@
|
||||
"integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
@@ -1761,9 +1745,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/git-hooks-list": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-4.1.1.tgz",
|
||||
"integrity": "sha512-cmP497iLq54AZnv4YRAEMnEyQ1eIn4tGKbmswqwmFV4GBnAqE8NLtWxxdXa++AalfgL5EBH4IxTPyquEuGY/jA==",
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-4.2.1.tgz",
|
||||
"integrity": "sha512-WNvqJjOxxs/8ZP9+DWdwWJ7cDsd60NHf39XnD82pDVrKO5q7xfPqpkK6hwEAmBa/ZSEE4IOoR75EzbbIuwGlMw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
@@ -2138,7 +2122,6 @@
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -2197,12 +2180,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.7.4",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz",
|
||||
"integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==",
|
||||
"version": "3.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz",
|
||||
"integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
},
|
||||
@@ -2214,17 +2196,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier-plugin-packagejson": {
|
||||
"version": "2.5.20",
|
||||
"resolved": "https://registry.npmjs.org/prettier-plugin-packagejson/-/prettier-plugin-packagejson-2.5.20.tgz",
|
||||
"integrity": "sha512-G8cowPh+QmJJECTZlrPDKWkVVcwrFjF2rGcw546w3N8blLoc4szSs8UUPfFVxHUNLUjiru71Ah83g1lZkeK9Bw==",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/prettier-plugin-packagejson/-/prettier-plugin-packagejson-3.0.0.tgz",
|
||||
"integrity": "sha512-z8/QmPSqx/ANvvQMWJSkSq1+ihBXeuwDEYdjX3ZjRJ5Ty1k7vGbFQfhzk2eDe0rwS/TNyRjWK/qnjJEStAOtDw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"sort-package-json": "3.5.0",
|
||||
"synckit": "0.11.11"
|
||||
"sort-package-json": "3.6.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prettier": ">= 1.16.0"
|
||||
"prettier": "^3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"prettier": {
|
||||
@@ -2353,26 +2334,26 @@
|
||||
}
|
||||
},
|
||||
"node_modules/sort-object-keys": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-2.0.1.tgz",
|
||||
"integrity": "sha512-R89fO+z3x7hiKPXX5P0qim+ge6Y60AjtlW+QQpRozrrNcR1lw9Pkpm5MLB56HoNvdcLHL4wbpq16OcvGpEDJIg==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-2.1.0.tgz",
|
||||
"integrity": "sha512-SOiEnthkJKPv2L6ec6HMwhUcN0/lppkeYuN1x63PbyPRrgSPIuBJCiYxYyvWRTtjMlOi14vQUCGUJqS6PLVm8g==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/sort-package-json": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-3.5.0.tgz",
|
||||
"integrity": "sha512-moY4UtptUuP5sPuu9H9dp8xHNel7eP5/Kz/7+90jTvC0IOiPH2LigtRM/aSFSxreaWoToHUVUpEV4a2tAs2oKQ==",
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-3.6.0.tgz",
|
||||
"integrity": "sha512-fyJsPLhWvY7u2KsKPZn1PixbXp+1m7V8NWqU8CvgFRbMEX41Ffw1kD8n0CfJiGoaSfoAvbrqRRl/DcHO8omQOQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"detect-indent": "^7.0.1",
|
||||
"detect-indent": "^7.0.2",
|
||||
"detect-newline": "^4.0.1",
|
||||
"git-hooks-list": "^4.0.0",
|
||||
"git-hooks-list": "^4.1.1",
|
||||
"is-plain-obj": "^4.1.0",
|
||||
"semver": "^7.7.1",
|
||||
"sort-object-keys": "^2.0.0",
|
||||
"tinyglobby": "^0.2.12"
|
||||
"semver": "^7.7.3",
|
||||
"sort-object-keys": "^2.0.1",
|
||||
"tinyglobby": "^0.2.15"
|
||||
},
|
||||
"bin": {
|
||||
"sort-package-json": "cli.js"
|
||||
@@ -2417,22 +2398,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/synckit": {
|
||||
"version": "0.11.11",
|
||||
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz",
|
||||
"integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@pkgr/core": "^0.2.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/synckit"
|
||||
}
|
||||
},
|
||||
"node_modules/thread-stream": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz",
|
||||
@@ -2492,7 +2457,6 @@
|
||||
"integrity": "sha512-mw2/2vTL7MlT+BVo43lOsufkkd2CJO4zeOSuWQQsiXoV2VuEn7f6IZp2jsUDPmBMABpgR0R5jlcJ2OGEFYmkyg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@gerrit0/mini-shiki": "^3.17.0",
|
||||
"lunr": "^2.3.9",
|
||||
@@ -2530,7 +2494,6 @@
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
{
|
||||
"name": "@goauthentik/esbuild-plugin-live-reload",
|
||||
"version": "1.4.0",
|
||||
"version": "1.5.0",
|
||||
"description": "ESBuild + browser refresh. Build completes, page reloads.",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/goauthentik/authentik.git",
|
||||
"directory": "packages/esbuild-plugin-live-reload"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "npm run build:types && npm run build:docs",
|
||||
"build:docs": "typedoc",
|
||||
@@ -14,6 +19,7 @@
|
||||
},
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"types": "./out/index.d.ts",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
@@ -47,8 +53,8 @@
|
||||
"esbuild": "^0.27.1",
|
||||
"eslint": "^9.39.1",
|
||||
"pino": "^10.1.0",
|
||||
"prettier": "^3.7.4",
|
||||
"prettier-plugin-packagejson": "^2.5.20",
|
||||
"prettier": "^3.8.1",
|
||||
"prettier-plugin-packagejson": "^3.0.0",
|
||||
"typedoc": "^0.28.15",
|
||||
"typedoc-plugin-markdown": "^4.9.0",
|
||||
"typescript": "^5.9.3",
|
||||
@@ -57,10 +63,30 @@
|
||||
"peerDependencies": {
|
||||
"esbuild": "^0.27.0"
|
||||
},
|
||||
"files": [
|
||||
"./index.js",
|
||||
"client/**/*",
|
||||
"plugin/**/*",
|
||||
"shared/**/*",
|
||||
"out/**/*"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=24",
|
||||
"npm": ">=11.6.2"
|
||||
},
|
||||
"devEngines": {
|
||||
"runtime": {
|
||||
"name": "node",
|
||||
"onFail": "warn",
|
||||
"version": ">=24"
|
||||
},
|
||||
"packageManager": {
|
||||
"name": "npm",
|
||||
"version": "11.10.1",
|
||||
"onFail": "warn"
|
||||
}
|
||||
},
|
||||
"prettier": "@goauthentik/prettier-config",
|
||||
"keywords": [
|
||||
"esbuild",
|
||||
"live-reload",
|
||||
@@ -69,20 +95,6 @@
|
||||
"reload",
|
||||
"authentik"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/goauthentik/authentik.git",
|
||||
"directory": "packages/esbuild-plugin-live-reload"
|
||||
},
|
||||
"types": "./out/index.d.ts",
|
||||
"files": [
|
||||
"./index.js",
|
||||
"client/**/*",
|
||||
"plugin/**/*",
|
||||
"shared/**/*",
|
||||
"out/**/*"
|
||||
],
|
||||
"prettier": "@goauthentik/prettier-config",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
|
||||
32
packages/eslint-config/package-lock.json
generated
32
packages/eslint-config/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@goauthentik/eslint-config",
|
||||
"version": "1.2.0",
|
||||
"version": "1.2.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@goauthentik/eslint-config",
|
||||
"version": "1.2.0",
|
||||
"version": "1.2.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"eslint": "^9.39.1",
|
||||
@@ -26,7 +26,7 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=24",
|
||||
"npm": ">=11.6.2"
|
||||
"npm": ">=11.10.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
@@ -45,7 +45,7 @@
|
||||
},
|
||||
"../prettier-config": {
|
||||
"name": "@goauthentik/prettier-config",
|
||||
"version": "3.3.1",
|
||||
"version": "3.4.0",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -59,18 +59,18 @@
|
||||
"@typescript-eslint/eslint-plugin": "^8.49.0",
|
||||
"@typescript-eslint/parser": "^8.49.0",
|
||||
"eslint": "^9.39.1",
|
||||
"prettier": "^3.7.4",
|
||||
"prettier-plugin-packagejson": "^2.5.20",
|
||||
"prettier": "^3.8.1",
|
||||
"prettier-plugin-packagejson": "^3.0.0",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.49.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=24",
|
||||
"npm": ">=11.6.2"
|
||||
"npm": ">=11.10.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prettier": "^3.7.4",
|
||||
"prettier-plugin-packagejson": "^2.5.20"
|
||||
"prettier": "^3.8.1",
|
||||
"prettier-plugin-packagejson": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"../tsconfig": {
|
||||
@@ -111,7 +111,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz",
|
||||
"integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.3",
|
||||
@@ -658,7 +657,6 @@
|
||||
"integrity": "sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.49.0",
|
||||
"@typescript-eslint/types": "8.49.0",
|
||||
@@ -888,7 +886,6 @@
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -906,9 +903,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"version": "6.14.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
|
||||
"integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
@@ -1162,7 +1159,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.8.9",
|
||||
"caniuse-lite": "^1.0.30001746",
|
||||
@@ -1648,7 +1644,6 @@
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz",
|
||||
"integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
@@ -3212,7 +3207,6 @@
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -3820,7 +3814,6 @@
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -4049,7 +4042,6 @@
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
||||
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
{
|
||||
"name": "@goauthentik/eslint-config",
|
||||
"version": "1.2.0",
|
||||
"version": "1.2.1",
|
||||
"description": "authentik's ESLint config",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/goauthentik/authentik.git",
|
||||
"directory": "packages/eslint-config"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc -p .",
|
||||
"lint": "eslint --fix .",
|
||||
@@ -11,6 +16,7 @@
|
||||
"prettier-check": "prettier --cache --check -u ."
|
||||
},
|
||||
"type": "module",
|
||||
"types": "./out/index.d.ts",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
@@ -52,21 +58,27 @@
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.49.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=24",
|
||||
"npm": ">=11.6.2"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/goauthentik/authentik.git",
|
||||
"directory": "packages/eslint-config"
|
||||
},
|
||||
"types": "./out/index.d.ts",
|
||||
"files": [
|
||||
"./index.js",
|
||||
"lib/**/*",
|
||||
"out/**/*"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=24",
|
||||
"npm": ">=11.10.1"
|
||||
},
|
||||
"devEngines": {
|
||||
"runtime": {
|
||||
"name": "node",
|
||||
"version": "24",
|
||||
"onFail": "ignore"
|
||||
},
|
||||
"packageManager": {
|
||||
"name": "npm",
|
||||
"version": "11.10.1",
|
||||
"onFail": "ignore"
|
||||
}
|
||||
},
|
||||
"prettier": "@goauthentik/prettier-config",
|
||||
"peerDependenciesMeta": {
|
||||
"react": {
|
||||
|
||||
@@ -11,10 +11,86 @@ import { fileURLToPath } from "node:url";
|
||||
* @property {string[]} [packageSortOrder] Custom ordering array.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {PrettierConfig & PackageJSONPluginConfig} ExtendedPrettierConfig
|
||||
*/
|
||||
|
||||
const CI = !!process.env.CI;
|
||||
|
||||
/**
|
||||
* @type {ExtendedPrettierConfig['plugins']}
|
||||
*/
|
||||
const plugins = [
|
||||
// ---
|
||||
fileURLToPath(import.meta.resolve("@goauthentik/prettier-config/imports-plugin")),
|
||||
];
|
||||
|
||||
/**
|
||||
* @type {ExtendedPrettierConfig['overrides']}
|
||||
*/
|
||||
const overrides = [
|
||||
{
|
||||
files: "schemas/**/*.json",
|
||||
options: {
|
||||
tabWidth: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: "tsconfig.json",
|
||||
options: {
|
||||
trailingComma: "none",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
// Sort order can be a source of false-positives in CI when this package is updated.
|
||||
if (!CI) {
|
||||
plugins.unshift("prettier-plugin-packagejson");
|
||||
overrides.push({
|
||||
files: "package.json",
|
||||
options: {
|
||||
packageSortOrder: [
|
||||
// ---
|
||||
"name",
|
||||
"version",
|
||||
"description",
|
||||
"license",
|
||||
"private",
|
||||
"author",
|
||||
"authors",
|
||||
"contributors",
|
||||
"funding",
|
||||
"repository",
|
||||
"bugs",
|
||||
"homepage",
|
||||
"scripts",
|
||||
"main",
|
||||
"type",
|
||||
"types",
|
||||
"exports",
|
||||
"imports",
|
||||
"dependencies",
|
||||
"devDependencies",
|
||||
"peerDependencies",
|
||||
"optionalDependencies",
|
||||
"workspaces",
|
||||
"files",
|
||||
"wireit",
|
||||
"resolutions",
|
||||
"engines",
|
||||
"devEngines",
|
||||
"packageManager",
|
||||
"prettier",
|
||||
"eslintConfig",
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* authentik Prettier configuration.
|
||||
*
|
||||
* @type {PrettierConfig & PackageJSONPluginConfig}
|
||||
* @type {ExtendedPrettierConfig}
|
||||
* @internal
|
||||
*/
|
||||
export const AuthentikPrettierConfig = {
|
||||
@@ -34,51 +110,6 @@ export const AuthentikPrettierConfig = {
|
||||
trailingComma: "all",
|
||||
useTabs: false,
|
||||
vueIndentScriptAndStyle: false,
|
||||
plugins: [
|
||||
// ---
|
||||
"prettier-plugin-packagejson",
|
||||
fileURLToPath(import.meta.resolve("@goauthentik/prettier-config/imports-plugin")),
|
||||
],
|
||||
|
||||
overrides: [
|
||||
{
|
||||
files: "schemas/**/*.json",
|
||||
options: {
|
||||
tabWidth: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: "tsconfig.json",
|
||||
options: {
|
||||
trailingComma: "none",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: "package.json",
|
||||
options: {
|
||||
packageSortOrder: [
|
||||
// ---
|
||||
"name",
|
||||
"version",
|
||||
"description",
|
||||
"license",
|
||||
"private",
|
||||
"author",
|
||||
"authors",
|
||||
"scripts",
|
||||
"main",
|
||||
"type",
|
||||
"exports",
|
||||
"imports",
|
||||
"dependencies",
|
||||
"devDependencies",
|
||||
"peerDependencies",
|
||||
"optionalDependencies",
|
||||
"wireit",
|
||||
"resolutions",
|
||||
"engines",
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
plugins,
|
||||
overrides,
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { format } from "prettier";
|
||||
|
||||
import { AuthentikPrettierConfig } from "./constants.js";
|
||||
|
||||
import { format } from "prettier";
|
||||
|
||||
/**
|
||||
* Format using Prettier.
|
||||
*
|
||||
|
||||
98
packages/prettier-config/package-lock.json
generated
98
packages/prettier-config/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@goauthentik/prettier-config",
|
||||
"version": "3.3.1",
|
||||
"version": "3.4.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@goauthentik/prettier-config",
|
||||
"version": "3.3.1",
|
||||
"version": "3.4.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"format-imports": "^4.0.8"
|
||||
@@ -19,23 +19,23 @@
|
||||
"@typescript-eslint/eslint-plugin": "^8.49.0",
|
||||
"@typescript-eslint/parser": "^8.49.0",
|
||||
"eslint": "^9.39.1",
|
||||
"prettier": "^3.7.4",
|
||||
"prettier-plugin-packagejson": "^2.5.20",
|
||||
"prettier": "^3.8.1",
|
||||
"prettier-plugin-packagejson": "^3.0.0",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.49.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=24",
|
||||
"npm": ">=11.6.2"
|
||||
"npm": ">=11.10.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prettier": "^3.7.4",
|
||||
"prettier-plugin-packagejson": "^2.5.20"
|
||||
"prettier": "^3.8.1",
|
||||
"prettier-plugin-packagejson": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"../eslint-config": {
|
||||
"name": "@goauthentik/eslint-config",
|
||||
"version": "1.2.0",
|
||||
"version": "1.2.1",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -56,7 +56,7 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=24",
|
||||
"npm": ">=11.6.2"
|
||||
"npm": ">=11.10.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
@@ -433,19 +433,6 @@
|
||||
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@pkgr/core": {
|
||||
"version": "0.2.9",
|
||||
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz",
|
||||
"integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/pkgr"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
||||
@@ -792,9 +779,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"version": "6.14.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
|
||||
"integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
@@ -1353,9 +1340,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/git-hooks-list": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-4.1.1.tgz",
|
||||
"integrity": "sha512-cmP497iLq54AZnv4YRAEMnEyQ1eIn4tGKbmswqwmFV4GBnAqE8NLtWxxdXa++AalfgL5EBH4IxTPyquEuGY/jA==",
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-4.2.1.tgz",
|
||||
"integrity": "sha512-WNvqJjOxxs/8ZP9+DWdwWJ7cDsd60NHf39XnD82pDVrKO5q7xfPqpkK6hwEAmBa/ZSEE4IOoR75EzbbIuwGlMw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
@@ -1799,9 +1786,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.7.4",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz",
|
||||
"integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==",
|
||||
"version": "3.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz",
|
||||
"integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
@@ -1814,17 +1801,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier-plugin-packagejson": {
|
||||
"version": "2.5.20",
|
||||
"resolved": "https://registry.npmjs.org/prettier-plugin-packagejson/-/prettier-plugin-packagejson-2.5.20.tgz",
|
||||
"integrity": "sha512-G8cowPh+QmJJECTZlrPDKWkVVcwrFjF2rGcw546w3N8blLoc4szSs8UUPfFVxHUNLUjiru71Ah83g1lZkeK9Bw==",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/prettier-plugin-packagejson/-/prettier-plugin-packagejson-3.0.0.tgz",
|
||||
"integrity": "sha512-z8/QmPSqx/ANvvQMWJSkSq1+ihBXeuwDEYdjX3ZjRJ5Ty1k7vGbFQfhzk2eDe0rwS/TNyRjWK/qnjJEStAOtDw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"sort-package-json": "3.5.0",
|
||||
"synckit": "0.11.11"
|
||||
"sort-package-json": "3.6.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prettier": ">= 1.16.0"
|
||||
"prettier": "^3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"prettier": {
|
||||
@@ -1897,26 +1883,26 @@
|
||||
}
|
||||
},
|
||||
"node_modules/sort-object-keys": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-2.0.1.tgz",
|
||||
"integrity": "sha512-R89fO+z3x7hiKPXX5P0qim+ge6Y60AjtlW+QQpRozrrNcR1lw9Pkpm5MLB56HoNvdcLHL4wbpq16OcvGpEDJIg==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-2.1.0.tgz",
|
||||
"integrity": "sha512-SOiEnthkJKPv2L6ec6HMwhUcN0/lppkeYuN1x63PbyPRrgSPIuBJCiYxYyvWRTtjMlOi14vQUCGUJqS6PLVm8g==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/sort-package-json": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-3.5.0.tgz",
|
||||
"integrity": "sha512-moY4UtptUuP5sPuu9H9dp8xHNel7eP5/Kz/7+90jTvC0IOiPH2LigtRM/aSFSxreaWoToHUVUpEV4a2tAs2oKQ==",
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-3.6.0.tgz",
|
||||
"integrity": "sha512-fyJsPLhWvY7u2KsKPZn1PixbXp+1m7V8NWqU8CvgFRbMEX41Ffw1kD8n0CfJiGoaSfoAvbrqRRl/DcHO8omQOQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"detect-indent": "^7.0.1",
|
||||
"detect-indent": "^7.0.2",
|
||||
"detect-newline": "^4.0.1",
|
||||
"git-hooks-list": "^4.0.0",
|
||||
"git-hooks-list": "^4.1.1",
|
||||
"is-plain-obj": "^4.1.0",
|
||||
"semver": "^7.7.1",
|
||||
"sort-object-keys": "^2.0.0",
|
||||
"tinyglobby": "^0.2.12"
|
||||
"semver": "^7.7.3",
|
||||
"sort-object-keys": "^2.0.1",
|
||||
"tinyglobby": "^0.2.15"
|
||||
},
|
||||
"bin": {
|
||||
"sort-package-json": "cli.js"
|
||||
@@ -2004,22 +1990,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/synckit": {
|
||||
"version": "0.11.11",
|
||||
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz",
|
||||
"integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@pkgr/core": "^0.2.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/synckit"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby": {
|
||||
"version": "0.2.15",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
{
|
||||
"name": "@goauthentik/prettier-config",
|
||||
"version": "3.3.1",
|
||||
"version": "3.4.0",
|
||||
"description": "authentik's Prettier config",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/goauthentik/authentik.git",
|
||||
"directory": "packages/prettier-config"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc -p .",
|
||||
"lint": "eslint --fix .",
|
||||
@@ -11,19 +16,20 @@
|
||||
"prettier-check": "prettier --check ."
|
||||
},
|
||||
"type": "module",
|
||||
"types": "./out/index.d.ts",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"import": "./index.js",
|
||||
"types": "./out/index.d.ts"
|
||||
"types": "./out/index.d.ts",
|
||||
"import": "./index.js"
|
||||
},
|
||||
"./imports-plugin": {
|
||||
"import": "./lib/imports.js",
|
||||
"types": "./out/lib/imports.d.ts"
|
||||
"types": "./out/lib/imports.d.ts",
|
||||
"import": "./lib/imports.js"
|
||||
},
|
||||
"./formatter": {
|
||||
"import": "./lib/formatter.js",
|
||||
"types": "./out/lib/formatter.d.ts"
|
||||
"types": "./out/lib/formatter.d.ts",
|
||||
"import": "./lib/formatter.js"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -37,30 +43,36 @@
|
||||
"@typescript-eslint/eslint-plugin": "^8.49.0",
|
||||
"@typescript-eslint/parser": "^8.49.0",
|
||||
"eslint": "^9.39.1",
|
||||
"prettier": "^3.7.4",
|
||||
"prettier-plugin-packagejson": "^2.5.20",
|
||||
"prettier": "^3.8.1",
|
||||
"prettier-plugin-packagejson": "^3.0.0",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.49.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prettier": "^3.7.4",
|
||||
"prettier-plugin-packagejson": "^2.5.20"
|
||||
"prettier": "^3.8.1",
|
||||
"prettier-plugin-packagejson": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=24",
|
||||
"npm": ">=11.6.2"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/goauthentik/authentik.git",
|
||||
"directory": "packages/prettier-config"
|
||||
},
|
||||
"types": "./out/index.d.ts",
|
||||
"files": [
|
||||
"./index.js",
|
||||
"lib/**/*",
|
||||
"out/**/*"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=24",
|
||||
"npm": ">=11.10.1"
|
||||
},
|
||||
"devEngines": {
|
||||
"runtime": {
|
||||
"name": "node",
|
||||
"version": "24",
|
||||
"onFail": "ignore"
|
||||
},
|
||||
"packageManager": {
|
||||
"name": "npm",
|
||||
"version": "11.10.1",
|
||||
"onFail": "ignore"
|
||||
}
|
||||
},
|
||||
"prettier": "./index.js",
|
||||
"overrides": {
|
||||
"format-imports": {
|
||||
|
||||
@@ -44,7 +44,7 @@ dependencies = [
|
||||
"kubernetes==35.0.0",
|
||||
"ldap3==2.9.1",
|
||||
"lxml==6.0.2",
|
||||
"msgraph-sdk==1.54.0",
|
||||
"msgraph-sdk==1.55.0",
|
||||
"opencontainers==0.0.15",
|
||||
"packaging==26.0",
|
||||
"paramiko==4.0.0",
|
||||
@@ -76,13 +76,13 @@ dependencies = [
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"aws-cdk-lib==2.238.0",
|
||||
"aws-cdk-lib==2.239.0",
|
||||
"bandit==1.9.3",
|
||||
"black==26.1.0",
|
||||
"bpython==0.26",
|
||||
"codespell==2.4.1",
|
||||
"colorama==0.4.6",
|
||||
"constructs==10.5.0",
|
||||
"constructs==10.5.1",
|
||||
"coverage[toml]==7.13.4",
|
||||
"daphne==4.2.1",
|
||||
"debugpy==1.8.20",
|
||||
@@ -102,8 +102,8 @@ dev = [
|
||||
"pytest-timeout==2.4.0",
|
||||
"pytest==9.0.2",
|
||||
"requests-mock==1.12.1",
|
||||
"ruff==0.15.1",
|
||||
"selenium==4.40.0",
|
||||
"ruff==0.15.2",
|
||||
"selenium==4.41.0",
|
||||
"types-channels==4.3.0.20250822",
|
||||
"types-docker==7.1.0.20260109",
|
||||
"types-jwcrypto==1.5.0.20251102",
|
||||
|
||||
@@ -42199,15 +42199,15 @@ components:
|
||||
readOnly: true
|
||||
opened_on:
|
||||
type: string
|
||||
format: date
|
||||
format: date-time
|
||||
readOnly: true
|
||||
grace_period_end:
|
||||
type: string
|
||||
format: date
|
||||
format: date-time
|
||||
readOnly: true
|
||||
next_review_date:
|
||||
type: string
|
||||
format: date
|
||||
format: date-time
|
||||
readOnly: true
|
||||
reviews:
|
||||
type: array
|
||||
|
||||
@@ -110,8 +110,8 @@ class TestFlowsEnroll(SeleniumTestCase):
|
||||
identification_stage = self.get_shadow_root("ak-stage-identification", flow_executor)
|
||||
wait = WebDriverWait(identification_stage, self.wait_timeout)
|
||||
|
||||
wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "a[name='enroll']")))
|
||||
identification_stage.find_element(By.CSS_SELECTOR, "a[name='enroll']").click()
|
||||
wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "a[ouiaId='enroll']")))
|
||||
identification_stage.find_element(By.CSS_SELECTOR, "a[ouiaId='enroll']").click()
|
||||
|
||||
# First prompt stage
|
||||
flow_executor = self.get_shadow_root("ak-flow-executor")
|
||||
|
||||
@@ -26,8 +26,8 @@ class TestFlowsRecovery(SeleniumTestCase):
|
||||
identification_stage = self.get_shadow_root("ak-stage-identification", flow_executor)
|
||||
wait = WebDriverWait(identification_stage, self.wait_timeout)
|
||||
|
||||
wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "a[name='recovery']")))
|
||||
identification_stage.find_element(By.CSS_SELECTOR, "a[name='recovery']").click()
|
||||
wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "a[ouiaId='recovery']")))
|
||||
identification_stage.find_element(By.CSS_SELECTOR, "a[ouiaId='recovery']").click()
|
||||
|
||||
# First prompt stage
|
||||
flow_executor = self.get_shadow_root("ak-flow-executor")
|
||||
|
||||
125
uv.lock
generated
125
uv.lock
generated
@@ -192,15 +192,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/91/be/317c2c55b8bbec407257d45f5c8d1b6867abc76d12043f2d3d58c538a4ea/asgiref-3.11.0-py3-none-any.whl", hash = "sha256:1db9021efadb0d9512ce8ffaf72fcef601c7b73a8807a1bb2ef143dc6b14846d", size = 24096, upload-time = "2025-11-19T15:32:19.004Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-generator"
|
||||
version = "1.10"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ce/b6/6fa6b3b598a03cba5e80f829e0dadbb49d7645f523d209b2fb7ea0bbb02a/async_generator-1.10.tar.gz", hash = "sha256:6ebb3d106c12920aaae42ccb6f787ef5eefdcdd166ea3d628fa8476abe712144", size = 29870, upload-time = "2018-08-01T03:36:21.69Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/71/52/39d20e03abd0ac9159c162ec24b93fbcaa111e8400308f2465432495ca2b/async_generator-1.10-py3-none-any.whl", hash = "sha256:01c7bf666359b4967d2cda0000cc2e4af16a0ae098cbffcb8472fb9e8ad6585b", size = 18857, upload-time = "2018-08-01T03:36:20.029Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "attrs"
|
||||
version = "25.4.0"
|
||||
@@ -363,7 +354,7 @@ requires-dist = [
|
||||
{ name = "kubernetes", specifier = "==35.0.0" },
|
||||
{ name = "ldap3", specifier = "==2.9.1" },
|
||||
{ name = "lxml", specifier = "==6.0.2" },
|
||||
{ name = "msgraph-sdk", specifier = "==1.54.0" },
|
||||
{ name = "msgraph-sdk", specifier = "==1.55.0" },
|
||||
{ name = "opencontainers", git = "https://github.com/vsoch/oci-python?rev=ceb4fcc090851717a3069d78e85ceb1e86c2740c" },
|
||||
{ name = "packaging", specifier = "==26.0" },
|
||||
{ name = "paramiko", specifier = "==4.0.0" },
|
||||
@@ -395,13 +386,13 @@ requires-dist = [
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
{ name = "aws-cdk-lib", specifier = "==2.238.0" },
|
||||
{ name = "aws-cdk-lib", specifier = "==2.239.0" },
|
||||
{ name = "bandit", specifier = "==1.9.3" },
|
||||
{ name = "black", specifier = "==26.1.0" },
|
||||
{ name = "bpython", specifier = "==0.26" },
|
||||
{ name = "codespell", specifier = "==2.4.1" },
|
||||
{ name = "colorama", specifier = "==0.4.6" },
|
||||
{ name = "constructs", specifier = "==10.5.0" },
|
||||
{ name = "constructs", specifier = "==10.5.1" },
|
||||
{ name = "coverage", extras = ["toml"], specifier = "==7.13.4" },
|
||||
{ name = "daphne", specifier = "==4.2.1" },
|
||||
{ name = "debugpy", specifier = "==1.8.20" },
|
||||
@@ -421,8 +412,8 @@ dev = [
|
||||
{ name = "pytest-randomly", specifier = "==4.0.1" },
|
||||
{ name = "pytest-timeout", specifier = "==2.4.0" },
|
||||
{ name = "requests-mock", specifier = "==1.12.1" },
|
||||
{ name = "ruff", specifier = "==0.15.1" },
|
||||
{ name = "selenium", specifier = "==4.40.0" },
|
||||
{ name = "ruff", specifier = "==0.15.2" },
|
||||
{ name = "selenium", specifier = "==4.41.0" },
|
||||
{ name = "types-channels", specifier = "==4.3.0.20250822" },
|
||||
{ name = "types-docker", specifier = "==7.1.0.20260109" },
|
||||
{ name = "types-jwcrypto", specifier = "==1.5.0.20251102" },
|
||||
@@ -492,21 +483,21 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-cdk-cloud-assembly-schema"
|
||||
version = "48.20.0"
|
||||
version = "50.4.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "jsii" },
|
||||
{ name = "publication" },
|
||||
{ name = "typeguard" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a7/b5/1ce2f6bff913ca8c94a001b84290ec4ce3729f54a3af0e3ff0edb303ac20/aws_cdk_cloud_assembly_schema-48.20.0.tar.gz", hash = "sha256:229aa136c26b71b0a82b5a32658eabcd30e344f7e136315fdb6e3de8ef523bfa", size = 208109, upload-time = "2025-11-19T12:19:48.206Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f8/4c/a9ac7498f2b76d7697e60367b9a8c690fc54bb3bf4e591e6fe06977c847b/aws_cdk_cloud_assembly_schema-50.4.0.tar.gz", hash = "sha256:c9aa7a108ca63f3880f26594166d3e8c16b504a50424011baf785231dc009f30", size = 208573, upload-time = "2026-02-05T16:37:54.61Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/13/08/17a35f0b668451484f2254f5e50a0105958bffe90da11c41b7629972e6a9/aws_cdk_cloud_assembly_schema-48.20.0-py3-none-any.whl", hash = "sha256:f5b6cf661cac8690add9461de13aeae3f3742eec71c066032bd045b08d0b7c3e", size = 207669, upload-time = "2025-11-19T12:19:46.614Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/37/946c9646606cf9dffa5d15a860557c9d507655c97fb2525bb8bd0c215179/aws_cdk_cloud_assembly_schema-50.4.0-py3-none-any.whl", hash = "sha256:3f98f06d99f68f5bae5c72f0f392494dd3ef4211197afd0e75cfe1d5fc487d1c", size = 208231, upload-time = "2026-02-05T16:37:52.037Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-cdk-lib"
|
||||
version = "2.238.0"
|
||||
version = "2.239.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "aws-cdk-asset-awscli-v1" },
|
||||
@@ -517,9 +508,9 @@ dependencies = [
|
||||
{ name = "publication" },
|
||||
{ name = "typeguard" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/3f/37/d9cdaf7068b5e598b2ab60687877de484adefc3927a7cf6571bff40da884/aws_cdk_lib-2.238.0.tar.gz", hash = "sha256:cef10c71e1575196df277fdac57c54010a8d28d77646da09200b1d1cb3625f8e", size = 47453139, upload-time = "2026-02-09T16:56:46.117Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e3/f8/851c18653bf6806877f5c942df7149c1d4f118063106b3c99c083d124da9/aws_cdk_lib-2.239.0.tar.gz", hash = "sha256:b5637f961e05b0d9ce28da2d759d605e23f4679f2cd0d1262efe3c32986d81f3", size = 47594912, upload-time = "2026-02-19T21:58:24.267Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/b9/47cb90169841bbbe74e2e30967132d8930a70e05340454caaa3e31b129ab/aws_cdk_lib-2.238.0-py3-none-any.whl", hash = "sha256:6602d6678597c649d80ff884972c3fa67a98fd6cacb56adcb77cd8fc4a735d43", size = 48104897, upload-time = "2026-02-09T16:56:06.79Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/05/d5293605890d315b2e91d3538b6ebdd7fa7704e9686a0beac76773ae6954/aws_cdk_lib-2.239.0-py3-none-any.whl", hash = "sha256:ef00581bb309440de8e4fbf0adc2ab53fa443a10783111a349c69bb25128ddad", size = 48242835, upload-time = "2026-02-19T21:57:38.138Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -856,16 +847,16 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "constructs"
|
||||
version = "10.5.0"
|
||||
version = "10.5.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "jsii" },
|
||||
{ name = "publication" },
|
||||
{ name = "typeguard" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/35/e3/29744d401eb6597701cba93dbedee08450a6e8412718091b986e48502e49/constructs-10.5.0.tar.gz", hash = "sha256:88d23b10361c0a8b4896f6bed66693a0f4c61f0aa4247eb33a34a4ed12e67aaf", size = 68010, upload-time = "2026-02-17T09:48:08.71Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/df/a7/1d87d7327aaa8662f7525b064b6c0524e4b3985092840e6a568f5eb4a7d6/constructs-10.5.1.tar.gz", hash = "sha256:c0e90bb2b9c2782f292017820b91714321cb78393c8965c9362b0b624bfaf23b", size = 68165, upload-time = "2026-02-19T09:56:32.925Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/4c/0a/8141f766828dc6f249bf08d4e3bb4a65018ff7a8c5caf3c5f3f46f90d8e7/constructs-10.5.0-py3-none-any.whl", hash = "sha256:01e613161f087f0432c24824a1ba4e8a3084f5af4c7fcd852c8e94bfc0110ab5", size = 66172, upload-time = "2026-02-17T09:48:07.218Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c3/20/dff420181946bcaba48dba3f0c11db202c8441eda927f281ad78912cf777/constructs-10.5.1-py3-none-any.whl", hash = "sha256:fc5c14f6b2770c8542a43e298aa29b63dee4b18701763e8c0fdce202624c3a7c", size = 66344, upload-time = "2026-02-19T09:56:31.311Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2409,7 +2400,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "msgraph-sdk"
|
||||
version = "1.54.0"
|
||||
version = "1.55.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "azure-identity" },
|
||||
@@ -2419,9 +2410,9 @@ dependencies = [
|
||||
{ name = "microsoft-kiota-serialization-text" },
|
||||
{ name = "msgraph-core" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ac/56/0c2fba87b6eee19e4589bf21c3141de290afc51d654445fc3e2908244d7b/msgraph_sdk-1.54.0.tar.gz", hash = "sha256:4dc294fc7f8a173f5bee30ccfc396b81fa1a16d7dcc95debe76fdf706920e5b3", size = 6283861, upload-time = "2026-02-06T01:29:26.975Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/10/44/0b5a188addf6341b3da10dd207e444417de255f7c1651902ba72016a2843/msgraph_sdk-1.55.0.tar.gz", hash = "sha256:6df691a31954a050d26b8a678968017e157d940fb377f2a8a4e17a9741b98756", size = 6295669, upload-time = "2026-02-20T00:32:29.378Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/7a/d9eed36c7f309eb6c81c5b72b5b8bad40203487414ad2b9e371febc79885/msgraph_sdk-1.54.0-py3-none-any.whl", hash = "sha256:2b9894fd9f21ed9a71188e3d68bd1a9a58b2d1077e96ee4cb10a9f3d9d59a58e", size = 25707515, upload-time = "2026-02-06T01:29:23.344Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/a8/de807e62f8ff93003b573aa243cdcee2da2c0618b42efbc9a8e61aa7300d/msgraph_sdk-1.55.0-py3-none-any.whl", hash = "sha256:c8e68ebc4b88af5111de312e7fa910a4e76ddf48a4534feadb1fb8a411c48cfc", size = 25758742, upload-time = "2026-02-20T00:30:40.039Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3285,27 +3276,27 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.15.1"
|
||||
version = "0.15.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/04/dc/4e6ac71b511b141cf626357a3946679abeba4cf67bc7cc5a17920f31e10d/ruff-0.15.1.tar.gz", hash = "sha256:c590fe13fb57c97141ae975c03a1aedb3d3156030cabd740d6ff0b0d601e203f", size = 4540855, upload-time = "2026-02-12T23:09:09.998Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/06/04/eab13a954e763b0606f460443fcbf6bb5a0faf06890ea3754ff16523dce5/ruff-0.15.2.tar.gz", hash = "sha256:14b965afee0969e68bb871eba625343b8673375f457af4abe98553e8bbb98342", size = 4558148, upload-time = "2026-02-19T22:32:20.271Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/23/bf/e6e4324238c17f9d9120a9d60aa99a7daaa21204c07fcd84e2ef03bb5fd1/ruff-0.15.1-py3-none-linux_armv6l.whl", hash = "sha256:b101ed7cf4615bda6ffe65bdb59f964e9f4a0d3f85cbf0e54f0ab76d7b90228a", size = 10367819, upload-time = "2026-02-12T23:09:03.598Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/ea/c8f89d32e7912269d38c58f3649e453ac32c528f93bb7f4219258be2e7ed/ruff-0.15.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:939c995e9277e63ea632cc8d3fae17aa758526f49a9a850d2e7e758bfef46602", size = 10798618, upload-time = "2026-02-12T23:09:22.928Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/0f/1d0d88bc862624247d82c20c10d4c0f6bb2f346559d8af281674cf327f15/ruff-0.15.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1d83466455fdefe60b8d9c8df81d3c1bbb2115cede53549d3b522ce2bc703899", size = 10148518, upload-time = "2026-02-12T23:08:58.339Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/c8/291c49cefaa4a9248e986256df2ade7add79388fe179e0691be06fae6f37/ruff-0.15.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9457e3c3291024866222b96108ab2d8265b477e5b1534c7ddb1810904858d16", size = 10518811, upload-time = "2026-02-12T23:09:31.865Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c3/1a/f5707440e5ae43ffa5365cac8bbb91e9665f4a883f560893829cf16a606b/ruff-0.15.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:92c92b003e9d4f7fbd33b1867bb15a1b785b1735069108dfc23821ba045b29bc", size = 10196169, upload-time = "2026-02-12T23:09:17.306Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/ff/26ddc8c4da04c8fd3ee65a89c9fb99eaa5c30394269d424461467be2271f/ruff-0.15.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fe5c41ab43e3a06778844c586251eb5a510f67125427625f9eb2b9526535779", size = 10990491, upload-time = "2026-02-12T23:09:25.503Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fc/00/50920cb385b89413f7cdb4bb9bc8fc59c1b0f30028d8bccc294189a54955/ruff-0.15.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66a6dd6df4d80dc382c6484f8ce1bcceb55c32e9f27a8b94c32f6c7331bf14fb", size = 11843280, upload-time = "2026-02-12T23:09:19.88Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/6d/2f5cad8380caf5632a15460c323ae326f1e1a2b5b90a6ee7519017a017ca/ruff-0.15.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6a4a42cbb8af0bda9bcd7606b064d7c0bc311a88d141d02f78920be6acb5aa83", size = 11274336, upload-time = "2026-02-12T23:09:14.907Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/1d/5f56cae1d6c40b8a318513599b35ea4b075d7dc1cd1d04449578c29d1d75/ruff-0.15.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ab064052c31dddada35079901592dfba2e05f5b1e43af3954aafcbc1096a5b2", size = 11137288, upload-time = "2026-02-12T23:09:07.475Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cd/20/6f8d7d8f768c93b0382b33b9306b3b999918816da46537d5a61635514635/ruff-0.15.1-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:5631c940fe9fe91f817a4c2ea4e81f47bee3ca4aa646134a24374f3c19ad9454", size = 11070681, upload-time = "2026-02-12T23:08:55.43Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/67/d640ac76069f64cdea59dba02af2e00b1fa30e2103c7f8d049c0cff4cafd/ruff-0.15.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:68138a4ba184b4691ccdc39f7795c66b3c68160c586519e7e8444cf5a53e1b4c", size = 10486401, upload-time = "2026-02-12T23:09:27.927Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/65/3d/e1429f64a3ff89297497916b88c32a5cc88eeca7e9c787072d0e7f1d3e1e/ruff-0.15.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:518f9af03bfc33c03bdb4cb63fabc935341bb7f54af500f92ac309ecfbba6330", size = 10197452, upload-time = "2026-02-12T23:09:12.147Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/83/e2c3bade17dad63bf1e1c2ffaf11490603b760be149e1419b07049b36ef2/ruff-0.15.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:da79f4d6a826caaea95de0237a67e33b81e6ec2e25fc7e1993a4015dffca7c61", size = 10693900, upload-time = "2026-02-12T23:09:34.418Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a1/27/fdc0e11a813e6338e0706e8b39bb7a1d61ea5b36873b351acee7e524a72a/ruff-0.15.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:3dd86dccb83cd7d4dcfac303ffc277e6048600dfc22e38158afa208e8bf94a1f", size = 11227302, upload-time = "2026-02-12T23:09:36.536Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f6/58/ac864a75067dcbd3b95be5ab4eb2b601d7fbc3d3d736a27e391a4f92a5c1/ruff-0.15.1-py3-none-win32.whl", hash = "sha256:660975d9cb49b5d5278b12b03bb9951d554543a90b74ed5d366b20e2c57c2098", size = 10462555, upload-time = "2026-02-12T23:09:29.899Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/5e/d4ccc8a27ecdb78116feac4935dfc39d1304536f4296168f91ed3ec00cd2/ruff-0.15.1-py3-none-win_amd64.whl", hash = "sha256:c820fef9dd5d4172a6570e5721704a96c6679b80cf7be41659ed439653f62336", size = 11599956, upload-time = "2026-02-12T23:09:01.157Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/07/5bda6a85b220c64c65686bc85bd0bbb23b29c62b3a9f9433fa55f17cda93/ruff-0.15.1-py3-none-win_arm64.whl", hash = "sha256:5ff7d5f0f88567850f45081fac8f4ec212be8d0b963e385c3f7d0d2eb4899416", size = 10874604, upload-time = "2026-02-12T23:09:05.515Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/70/3a4dc6d09b13cb3e695f28307e5d889b2e1a66b7af9c5e257e796695b0e6/ruff-0.15.2-py3-none-linux_armv6l.whl", hash = "sha256:120691a6fdae2f16d65435648160f5b81a9625288f75544dc40637436b5d3c0d", size = 10430565, upload-time = "2026-02-19T22:32:41.824Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/0b/bb8457b56185ece1305c666dc895832946d24055be90692381c31d57466d/ruff-0.15.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:a89056d831256099658b6bba4037ac6dd06f49d194199215befe2bb10457ea5e", size = 10820354, upload-time = "2026-02-19T22:32:07.366Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2d/c1/e0532d7f9c9e0b14c46f61b14afd563298b8b83f337b6789ddd987e46121/ruff-0.15.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e36dee3a64be0ebd23c86ffa3aa3fd3ac9a712ff295e192243f814a830b6bd87", size = 10170767, upload-time = "2026-02-19T22:32:13.188Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/e8/da1aa341d3af017a21c7a62fb5ec31d4e7ad0a93ab80e3a508316efbcb23/ruff-0.15.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9fb47b6d9764677f8c0a193c0943ce9a05d6763523f132325af8a858eadc2b9", size = 10529591, upload-time = "2026-02-19T22:32:02.547Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/93/74/184fbf38e9f3510231fbc5e437e808f0b48c42d1df9434b208821efcd8d6/ruff-0.15.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f376990f9d0d6442ea9014b19621d8f2aaf2b8e39fdbfc79220b7f0c596c9b80", size = 10260771, upload-time = "2026-02-19T22:32:36.938Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/ac/605c20b8e059a0bc4b42360414baa4892ff278cec1c91fff4be0dceedefd/ruff-0.15.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2dcc987551952d73cbf5c88d9fdee815618d497e4df86cd4c4824cc59d5dd75f", size = 11045791, upload-time = "2026-02-19T22:32:31.642Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/52/db6e419908f45a894924d410ac77d64bdd98ff86901d833364251bd08e22/ruff-0.15.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:42a47fd785cbe8c01b9ff45031af875d101b040ad8f4de7bbb716487c74c9a77", size = 11879271, upload-time = "2026-02-19T22:32:29.305Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/d8/7992b18f2008bdc9231d0f10b16df7dda964dbf639e2b8b4c1b4e91b83af/ruff-0.15.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cbe9f49354866e575b4c6943856989f966421870e85cd2ac94dccb0a9dcb2fea", size = 11303707, upload-time = "2026-02-19T22:32:22.492Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/02/849b46184bcfdd4b64cde61752cc9a146c54759ed036edd11857e9b8443b/ruff-0.15.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7a672c82b5f9887576087d97be5ce439f04bbaf548ee987b92d3a7dede41d3a", size = 11149151, upload-time = "2026-02-19T22:32:44.234Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/70/04/f5284e388bab60d1d3b99614a5a9aeb03e0f333847e2429bebd2aaa1feec/ruff-0.15.2-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:72ecc64f46f7019e2bcc3cdc05d4a7da958b629a5ab7033195e11a438403d956", size = 11091132, upload-time = "2026-02-19T22:32:24.691Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/ae/88d844a21110e14d92cf73d57363fab59b727ebeabe78009b9ccb23500af/ruff-0.15.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:8dcf243b15b561c655c1ef2f2b0050e5d50db37fe90115507f6ff37d865dc8b4", size = 10504717, upload-time = "2026-02-19T22:32:26.75Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/64/27/867076a6ada7f2b9c8292884ab44d08fd2ba71bd2b5364d4136f3cd537e1/ruff-0.15.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:dab6941c862c05739774677c6273166d2510d254dac0695c0e3f5efa1b5585de", size = 10263122, upload-time = "2026-02-19T22:32:10.036Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/ef/faf9321d550f8ebf0c6373696e70d1758e20ccdc3951ad7af00c0956be7c/ruff-0.15.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1b9164f57fc36058e9a6806eb92af185b0697c9fe4c7c52caa431c6554521e5c", size = 10735295, upload-time = "2026-02-19T22:32:39.227Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/55/e8089fec62e050ba84d71b70e7834b97709ca9b7aba10c1a0b196e493f97/ruff-0.15.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:80d24fcae24d42659db7e335b9e1531697a7102c19185b8dc4a028b952865fd8", size = 11241641, upload-time = "2026-02-19T22:32:34.617Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/01/1c30526460f4d23222d0fabd5888868262fd0e2b71a00570ca26483cd993/ruff-0.15.2-py3-none-win32.whl", hash = "sha256:fd5ff9e5f519a7e1bd99cbe8daa324010a74f5e2ebc97c6242c08f26f3714f6f", size = 10507885, upload-time = "2026-02-19T22:32:15.635Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/10/3d18e3bbdf8fc50bbb4ac3cc45970aa5a9753c5cb51bf9ed9a3cd8b79fa3/ruff-0.15.2-py3-none-win_amd64.whl", hash = "sha256:d20014e3dfa400f3ff84830dfb5755ece2de45ab62ecea4af6b7262d0fb4f7c5", size = 11623725, upload-time = "2026-02-19T22:32:04.947Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/78/097c0798b1dab9f8affe73da9642bb4500e098cb27fd8dc9724816ac747b/ruff-0.15.2-py3-none-win_arm64.whl", hash = "sha256:cabddc5822acdc8f7b5527b36ceac55cc51eec7b1946e60181de8fe83ca8876e", size = 10941649, upload-time = "2026-02-19T22:32:18.108Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3334,22 +3325,19 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "selenium"
|
||||
version = "4.40.0"
|
||||
version = "4.41.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
{ name = "trio" },
|
||||
{ name = "trio-typing" },
|
||||
{ name = "trio-websocket" },
|
||||
{ name = "types-certifi" },
|
||||
{ name = "types-urllib3" },
|
||||
{ name = "typing-extensions" },
|
||||
{ name = "urllib3", extra = ["socks"] },
|
||||
{ name = "websocket-client" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/66/ef/a5727fa7b33d20d296322adf851b76072d8d3513e1b151969d3228437faf/selenium-4.40.0.tar.gz", hash = "sha256:a88f5905d88ad0b84991c2386ea39e2bbde6d6c334be38df5842318ba98eaa8c", size = 930444, upload-time = "2026-01-18T23:12:31.565Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/04/7c/133d00d6d013a17d3f39199f27f1a780ec2e95d7b9aa997dc1b8ac2e62a7/selenium-4.41.0.tar.gz", hash = "sha256:003e971f805231ad63e671783a2b91a299355d10cefb9de964c36ff3819115aa", size = 937872, upload-time = "2026-02-20T03:42:06.216Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/74/eb9d6540aca1911106fa0877b8e9ef24171bc18857937a6b0ffe0586c623/selenium-4.40.0-py3-none-any.whl", hash = "sha256:c8823fc02e2c771d9ad9a0cf899cee7de1a57a6697e3d0b91f67566129f2b729", size = 9608184, upload-time = "2026-01-18T23:12:29.435Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/d6/e4160989ef6b272779af6f3e5c43c3ba9be6687bdc21c68c3fb220e555b3/selenium-4.41.0-py3-none-any.whl", hash = "sha256:b8ccde8d2e7642221ca64af184a92c19eee6accf2e27f20f30472f5efae18eb1", size = 9532858, upload-time = "2026-02-20T03:42:03.218Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3530,23 +3518,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/41/bf/945d527ff706233636c73880b22c7c953f3faeb9d6c7e2e85bfbfd0134a0/trio-0.32.0-py3-none-any.whl", hash = "sha256:4ab65984ef8370b79a76659ec87aa3a30c5c7c83ff250b4de88c29a8ab6123c5", size = 512030, upload-time = "2025-10-31T07:18:15.885Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "trio-typing"
|
||||
version = "0.10.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "async-generator" },
|
||||
{ name = "importlib-metadata" },
|
||||
{ name = "mypy-extensions" },
|
||||
{ name = "packaging" },
|
||||
{ name = "trio" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b5/74/a87aafa40ec3a37089148b859892cbe2eef08d132c816d58a60459be5337/trio-typing-0.10.0.tar.gz", hash = "sha256:065ee684296d52a8ab0e2374666301aec36ee5747ac0e7a61f230250f8907ac3", size = 38747, upload-time = "2023-12-01T02:54:55.508Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/89/ff/9bd795273eb14fac7f6a59d16cc8c4d0948a619a1193d375437c7f50f3eb/trio_typing-0.10.0-py3-none-any.whl", hash = "sha256:6d0e7ec9d837a2fe03591031a172533fbf4a1a95baf369edebfc51d5a49f0264", size = 42224, upload-time = "2023-12-01T02:54:54.1Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "trio-websocket"
|
||||
version = "0.12.2"
|
||||
@@ -3619,15 +3590,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/bb/d43e5c75054e53efce310e79d63df0ac3f25e34c926be5dffb7d283fb2a8/typeguard-2.13.3-py3-none-any.whl", hash = "sha256:5e3e3be01e887e7eafae5af63d1f36c849aaa94e3a0112097312aabfa16284f1", size = 17605, upload-time = "2021-12-10T21:09:37.844Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "types-certifi"
|
||||
version = "2021.10.8.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/52/68/943c3aeaf14624712a0357c4a67814dba5cea36d194f5c764dad7959a00c/types-certifi-2021.10.8.3.tar.gz", hash = "sha256:72cf7798d165bc0b76e1c10dd1ea3097c7063c42c21d664523b928e88b554a4f", size = 2095, upload-time = "2022-06-09T15:19:05.244Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/63/2463d89481e811f007b0e1cd0a91e52e141b47f9de724d20db7b861dcfec/types_certifi-2021.10.8.3-py3-none-any.whl", hash = "sha256:b2d1e325e69f71f7c78e5943d410e650b4707bb0ef32e4ddf3da37f54176e88a", size = 2136, upload-time = "2022-06-09T15:19:03.127Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "types-channels"
|
||||
version = "4.3.0.20250822"
|
||||
@@ -3721,15 +3683,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1c/12/709ea261f2bf91ef0a26a9eed20f2623227a8ed85610c1e54c5805692ecb/types_requests-2.32.4.20260107-py3-none-any.whl", hash = "sha256:b703fe72f8ce5b31ef031264fe9395cac8f46a04661a79f7ed31a80fb308730d", size = 20676, upload-time = "2026-01-07T03:20:52.929Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "types-urllib3"
|
||||
version = "1.26.25.14"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/73/de/b9d7a68ad39092368fb21dd6194b362b98a1daeea5dcfef5e1adb5031c7e/types-urllib3-1.26.25.14.tar.gz", hash = "sha256:229b7f577c951b8c1b92c1bc2b2fdb0b49847bd2af6d1cc2a2e3dd340f3bda8f", size = 11239, upload-time = "2023-07-20T15:19:31.307Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/11/7b/3fc711b2efea5e85a7a0bbfe269ea944aa767bbba5ec52f9ee45d362ccf3/types_urllib3-1.26.25.14-py3-none-any.whl", hash = "sha256:9683bbb7fb72e32bfe9d2be6e04875fbe1b3eeec3cbb4ea231435aa7fd6b4f0e", size = 15377, upload-time = "2023-07-20T15:19:30.379Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "types-zxcvbn"
|
||||
version = "4.5.0.20250809"
|
||||
|
||||
1832
web/package-lock.json
generated
1832
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -5,17 +5,17 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "wireit",
|
||||
"build:sfe": "npm run build -w @goauthentik/web-sfe",
|
||||
"build-locales": "node scripts/build-locales.mjs",
|
||||
"build-proxy": "wireit",
|
||||
"build:sfe": "npm run build -w @goauthentik/web-sfe",
|
||||
"bundler:watch": "node scripts/build-web.mjs --watch",
|
||||
"extract-locales": "lit-localize extract",
|
||||
"format": "wireit",
|
||||
"lint": "eslint --fix .",
|
||||
"lint-check": "eslint --max-warnings 0 .",
|
||||
"lint:imports": "knip --config scripts/knip.config.ts",
|
||||
"lint:lockfile": "wireit",
|
||||
"lint:types": "wireit",
|
||||
"lint-check": "eslint --max-warnings 0 .",
|
||||
"lit-analyse": "wireit",
|
||||
"precommit": "wireit",
|
||||
"prettier": "prettier --cache --write -u .",
|
||||
@@ -101,8 +101,8 @@
|
||||
"@goauthentik/api": "^2026.2.0-rc1-1770744803",
|
||||
"@goauthentik/core": "^1.0.0",
|
||||
"@goauthentik/esbuild-plugin-live-reload": "^1.4.0",
|
||||
"@goauthentik/eslint-config": "^1.2.0",
|
||||
"@goauthentik/prettier-config": "^3.3.1",
|
||||
"@goauthentik/eslint-config": "^1.2.1",
|
||||
"@goauthentik/prettier-config": "^3.4.0",
|
||||
"@goauthentik/tsconfig": "^1.0.5",
|
||||
"@hcaptcha/types": "^1.1.0",
|
||||
"@lit/context": "^1.1.6",
|
||||
@@ -152,7 +152,7 @@
|
||||
"globals": "^17.3.0",
|
||||
"guacamole-common-js": "^1.5.0",
|
||||
"hastscript": "^9.0.1",
|
||||
"knip": "^5.84.1",
|
||||
"knip": "^5.85.0",
|
||||
"lex": "^2025.11.0",
|
||||
"lit": "^3.3.2",
|
||||
"lit-analyzer": "^2.0.3",
|
||||
@@ -166,6 +166,7 @@
|
||||
"pino-pretty": "^13.1.2",
|
||||
"playwright": "^1.58.2",
|
||||
"prettier": "^3.8.1",
|
||||
"prettier-plugin-packagejson": "^3.0.0",
|
||||
"pseudolocale": "^2.2.0",
|
||||
"rapidoc": "^9.3.8",
|
||||
"react": "^19.2.4",
|
||||
@@ -202,6 +203,9 @@
|
||||
"@rollup/rollup-linux-x64-gnu": "^4.57.1",
|
||||
"chromedriver": "^145.0.3"
|
||||
},
|
||||
"workspaces": [
|
||||
"./packages/*"
|
||||
],
|
||||
"wireit": {
|
||||
"build": {
|
||||
"#comment": [
|
||||
@@ -299,14 +303,27 @@
|
||||
"node": ">=24",
|
||||
"npm": ">=11.6.2"
|
||||
},
|
||||
"workspaces": [
|
||||
"./packages/*"
|
||||
],
|
||||
"devEngines": {
|
||||
"runtime": {
|
||||
"name": "node",
|
||||
"onFail": "warn",
|
||||
"version": ">=24"
|
||||
},
|
||||
"packageManager": {
|
||||
"name": "npm",
|
||||
"version": "11.10.1",
|
||||
"onFail": "warn"
|
||||
}
|
||||
},
|
||||
"prettier": "@goauthentik/prettier-config",
|
||||
"overrides": {
|
||||
"@goauthentik/esbuild-plugin-live-reload": {
|
||||
"esbuild": "$esbuild"
|
||||
},
|
||||
"@goauthentik/prettier-config": {
|
||||
"prettier": "$prettier",
|
||||
"prettier-plugin-packagejson": "$prettier-plugin-packagejson"
|
||||
},
|
||||
"@mrmarble/djangoql-completion": {
|
||||
"lex": "$lex"
|
||||
},
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
},
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"types": "./out/index.d.ts",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
"./*/browser": {
|
||||
@@ -52,6 +53,5 @@
|
||||
"engines": {
|
||||
"node": ">=24",
|
||||
"npm": ">=11.6.2"
|
||||
},
|
||||
"types": "./out/index.d.ts"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import "#admin/admin-overview/cards/WorkerStatusCard";
|
||||
import "#admin/admin-overview/charts/AdminLoginAuthorizeChart";
|
||||
import "#admin/admin-overview/charts/OutpostStatusChart";
|
||||
import "#admin/admin-overview/charts/SyncStatusChart";
|
||||
import "#elements/cards/AggregatePromiseCard";
|
||||
import "#elements/cards/AggregateCard";
|
||||
import "#elements/cards/QuickActionsCard";
|
||||
|
||||
import { formatUserDisplayName } from "#common/users";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import "#admin/admin-overview/charts/AdminModelPerDay";
|
||||
import "#elements/cards/AggregatePromiseCard";
|
||||
import "#elements/cards/AggregateCard";
|
||||
|
||||
import { AKElement } from "#elements/Base";
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ export class BoundStagesList extends Table<FlowStageBinding> {
|
||||
html` <ak-forms-modal>
|
||||
${StrictUnsafe<CustomFormElementTagName>(item.stageObj?.component, {
|
||||
slot: "form",
|
||||
instancePk: item.pk,
|
||||
instancePk: item.stageObj?.pk,
|
||||
actionLabel: msg("Update"),
|
||||
headline: msg(str`Update ${item.stageObj?.verboseName}`, {
|
||||
id: "form.headline.update",
|
||||
|
||||
@@ -272,7 +272,7 @@ export class FlowViewPage extends AKElement {
|
||||
>
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<ak-bound-stages-list .target=${this.flow.pk}> </ak-bound-stages-list>
|
||||
<ak-bound-stages-list target=${this.flow.pk}> </ak-bound-stages-list>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,6 +11,15 @@ export type DeepPartial<T> = T extends object
|
||||
}
|
||||
: T;
|
||||
|
||||
/**
|
||||
* Type utility to make all properties in T recursively required.
|
||||
*/
|
||||
export type DeepRequired<T> = T extends object
|
||||
? {
|
||||
[P in keyof T]-?: DeepRequired<T[P]>;
|
||||
}
|
||||
: T;
|
||||
|
||||
/**
|
||||
* Type utility to make readonly properties mutable.
|
||||
*/
|
||||
|
||||
@@ -49,7 +49,7 @@ export class AkDualSelectAvailablePane extends CustomEmitterElement<DualSelectEv
|
||||
|
||||
/* The array of key/value pairs this pane is currently showing */
|
||||
@property({ type: Array })
|
||||
public readonly options?: DualSelectPair[];
|
||||
public options?: DualSelectPair[];
|
||||
|
||||
/**
|
||||
* A set (set being easy for lookups) of keys with all the pairs selected,
|
||||
@@ -57,7 +57,7 @@ export class AkDualSelectAvailablePane extends CustomEmitterElement<DualSelectEv
|
||||
* can be marked and their clicks ignored.
|
||||
*/
|
||||
@property({ type: Object })
|
||||
public readonly selected: Set<string | number> = new Set();
|
||||
public selected: Set<string | number> = new Set();
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ export class AkDualSelectSelectedPane extends CustomEmitterElement<DualSelectEve
|
||||
|
||||
/* The array of key/value pairs that are in the selected list. ALL of them. */
|
||||
@property({ type: Array })
|
||||
readonly selected: DualSelectPair[] = [];
|
||||
public selected: DualSelectPair[] = [];
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
import "../AggregatePromiseCard.js";
|
||||
|
||||
import { AggregatePromiseCard, type IAggregatePromiseCard } from "../AggregatePromiseCard.js";
|
||||
|
||||
import { ifPresent } from "#elements/utils/attributes";
|
||||
|
||||
import type { Meta, StoryObj } from "@storybook/web-components";
|
||||
|
||||
import { html } from "lit";
|
||||
|
||||
const metadata: Meta<AggregatePromiseCard> = {
|
||||
title: "Elements/<ak-aggregate-card-promise>",
|
||||
component: "ak-aggregate-card-promise",
|
||||
tags: ["autodocs"],
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
component: /* md */ `
|
||||
# Aggregate Promise Cards
|
||||
|
||||
Aggregate Promise Cards are Aggregate Cards that take a promise from client code and either display
|
||||
the contents of that promise or a pre-configured failure notice. The contents must be compliant with
|
||||
and produce a meaningful result via the \`.toString()\` API. HTML in the string will currently be
|
||||
escaped.
|
||||
|
||||
## Usage
|
||||
|
||||
\`\`\`Typescript
|
||||
import "#elements/cards/AggregatePromiseCard";
|
||||
\`\`\`
|
||||
|
||||
\`\`\`html
|
||||
<ak-aggregate-card-promise
|
||||
header="Some title"
|
||||
.promise="\${somePromise}"
|
||||
></ak-aggregate-card-promise>
|
||||
\`\`\`
|
||||
`,
|
||||
},
|
||||
},
|
||||
},
|
||||
argTypes: {
|
||||
icon: { control: "text" },
|
||||
label: { control: "text" },
|
||||
headerLink: { control: "text" },
|
||||
subtext: { control: "text" },
|
||||
failureMessage: { control: "text" },
|
||||
},
|
||||
};
|
||||
|
||||
export default metadata;
|
||||
|
||||
const text =
|
||||
"Curl up and sleep on the freshly laundered towels mew, but make meme, make cute face growl at dogs in my sleep. Scratch me there, elevator butt humans, humans, humans oh how much they love us felines we are the center of attention they feed, they clean hopped up on catnip mice. Kitty time flop over, for see owner, run in terror";
|
||||
|
||||
const MILLIS_PER_SECOND = 1000;
|
||||
const EXAMPLE_TIMEOUT = 8000; // 8 seconds
|
||||
|
||||
export const DefaultStory: StoryObj = {
|
||||
args: {
|
||||
icon: undefined,
|
||||
header: "Default",
|
||||
headerLink: undefined,
|
||||
subtext: `Demo has a ${EXAMPLE_TIMEOUT / MILLIS_PER_SECOND} second delay until resolution`,
|
||||
},
|
||||
render: ({ icon, label, headerLink, subtext }: IAggregatePromiseCard) => {
|
||||
const runThis = (timeout: number, value: string) =>
|
||||
new Promise((resolve) => setTimeout(resolve, timeout, value));
|
||||
|
||||
return html`>
|
||||
<style>
|
||||
ak-aggregate-card-promise {
|
||||
display: inline-block;
|
||||
width: 32rem;
|
||||
max-width: 32rem;
|
||||
}
|
||||
</style>
|
||||
<ak-aggregate-card-promise
|
||||
label=${ifPresent(label)}
|
||||
headerLink=${ifPresent(headerLink)}
|
||||
subtext=${ifPresent(subtext)}
|
||||
icon=${ifPresent(icon)}
|
||||
.promise=${runThis(EXAMPLE_TIMEOUT, text)}
|
||||
>
|
||||
</ak-aggregate-card-promise> `;
|
||||
},
|
||||
};
|
||||
|
||||
export const PromiseRejected: StoryObj = {
|
||||
args: {
|
||||
icon: undefined,
|
||||
header: "Default",
|
||||
headerLink: undefined,
|
||||
subtext: `Demo has a ${EXAMPLE_TIMEOUT / MILLIS_PER_SECOND} second delay until resolution`,
|
||||
failureMessage: undefined,
|
||||
},
|
||||
render: ({ icon, label, headerLink, subtext, failureMessage }: IAggregatePromiseCard) => {
|
||||
const runThis = (timeout: number, value: string) =>
|
||||
new Promise((_resolve, reject) => setTimeout(reject, timeout, value));
|
||||
|
||||
return html`
|
||||
<style>
|
||||
ak-aggregate-card-promise {
|
||||
display: inline-block;
|
||||
width: 32rem;
|
||||
max-width: 32rem;
|
||||
}
|
||||
</style>
|
||||
<ak-aggregate-card-promise
|
||||
label=${ifPresent(label)}
|
||||
headerLink=${ifPresent(headerLink)}
|
||||
subtext=${ifPresent(subtext)}
|
||||
icon=${ifPresent(icon)}
|
||||
failureMessage=${ifPresent(failureMessage)}
|
||||
.promise=${runThis(EXAMPLE_TIMEOUT, text)}
|
||||
>
|
||||
</ak-aggregate-card-promise>
|
||||
`;
|
||||
},
|
||||
};
|
||||
@@ -43,19 +43,23 @@ export const Prefix = {
|
||||
|
||||
export type Prefix = (typeof Prefix)[keyof typeof Prefix];
|
||||
|
||||
type WrappedPropertyDeclaration = PropertyDeclaration<unknown, unknown> & { wrapped?: boolean };
|
||||
|
||||
/**
|
||||
* Given a Lit property declaration, determine the appropriate prefix for rendering the property as either a property or an attribute, based on the declaration's type and attribute configuration.
|
||||
*
|
||||
* @param propDeclaration The Lit property declaration to analyze.
|
||||
* @returns The determined prefix for rendering the property.
|
||||
*/
|
||||
function resolvePrefix<T extends PropertyDeclaration<unknown, unknown>>(
|
||||
propDeclaration: T,
|
||||
): Prefix {
|
||||
function resolvePrefix<T extends WrappedPropertyDeclaration>(propDeclaration: T): Prefix {
|
||||
if (!propDeclaration.attribute) {
|
||||
return Prefix.Property;
|
||||
}
|
||||
|
||||
if ("wrapped" in propDeclaration && propDeclaration.wrapped && !propDeclaration.type) {
|
||||
return Prefix.Attribute;
|
||||
}
|
||||
|
||||
switch (propDeclaration.type) {
|
||||
case String:
|
||||
return Prefix.Attribute;
|
||||
@@ -71,7 +75,7 @@ function resolvePrefix<T extends PropertyDeclaration<unknown, unknown>>(
|
||||
* determine the appropriate name to use for rendering the property,
|
||||
* taking into account any custom attribute name specified in the declaration.
|
||||
*/
|
||||
function resolvePropertyName<T extends PropertyDeclaration<unknown, unknown>>(
|
||||
function resolvePropertyName<T extends WrappedPropertyDeclaration>(
|
||||
propDeclaration: T,
|
||||
prefix: Prefix,
|
||||
key: string,
|
||||
@@ -148,9 +152,7 @@ export function StrictUnsafe<T extends string>(
|
||||
if (propDeclaration) {
|
||||
const prefix = resolvePrefix(propDeclaration);
|
||||
const name = resolvePropertyName(propDeclaration, prefix, propName);
|
||||
|
||||
filteredProps[`${prefix}${name}`] = propValue;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,7 @@ ak-flow-executor.style-scope {
|
||||
flex-flow: column nowrap;
|
||||
}
|
||||
|
||||
.inspector-toggle,
|
||||
.inspector-toggle.style-scope {
|
||||
ak-flow-inspector-button {
|
||||
position: absolute;
|
||||
inset-inline-end: var(--pf-global--spacer--md);
|
||||
inset-block-start: var(--pf-global--spacer--md);
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
import "#flow/stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage";
|
||||
import "#elements/LoadingOverlay";
|
||||
import "#elements/locale/ak-locale-select";
|
||||
import "#flow/components/ak-brand-footer";
|
||||
import "#flow/components/ak-flow-card";
|
||||
import "#flow/sources/apple/AppleLoginInit";
|
||||
import "#flow/sources/plex/PlexLoginInit";
|
||||
import "#flow/sources/telegram/TelegramLogin";
|
||||
import "#flow/stages/FlowErrorStage";
|
||||
import "#flow/stages/FlowFrameStage";
|
||||
import "#flow/stages/RedirectStage";
|
||||
import "#flow/inspector/FlowInspectorButton";
|
||||
|
||||
import Styles from "./FlowExecutor.css" with { type: "bundled-text" };
|
||||
|
||||
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
import { parseAPIResponseError, pluckErrorDetail } from "#common/errors/network";
|
||||
import { APIError, parseAPIResponseError, pluckErrorDetail } from "#common/errors/network";
|
||||
import { globalAK } from "#common/global";
|
||||
import { configureSentry } from "#common/sentry/index";
|
||||
import { applyBackgroundImageProperty } from "#common/theme";
|
||||
@@ -24,36 +18,35 @@ import { listen } from "#elements/decorators/listen";
|
||||
import { Interface } from "#elements/Interface";
|
||||
import { showAPIErrorMessage } from "#elements/messages/MessageContainer";
|
||||
import { WithBrandConfig } from "#elements/mixins/branding";
|
||||
import { WithCapabilitiesConfig } from "#elements/mixins/capabilities";
|
||||
import { LitPropertyRecord, SlottedTemplateResult } from "#elements/types";
|
||||
import { exportParts } from "#elements/utils/attributes";
|
||||
import { ThemedImage } from "#elements/utils/images";
|
||||
|
||||
import { AKFlowAdvanceEvent, AKFlowInspectorChangeEvent } from "#flow/events";
|
||||
import { AKFlowAdvanceEvent } from "#flow/events";
|
||||
import { StageMapping } from "#flow/FlowExecutorStageFactory";
|
||||
import { BaseStage } from "#flow/stages/base";
|
||||
import type { StageHost, SubmitOptions } from "#flow/types";
|
||||
|
||||
import { ConsoleLogger } from "#logger/browser";
|
||||
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
ChallengeTypes,
|
||||
ContextualFlowInfo,
|
||||
FlowChallengeResponseRequest,
|
||||
FlowErrorChallenge,
|
||||
FlowLayoutEnum,
|
||||
FlowsApi,
|
||||
ShellChallenge,
|
||||
} from "@goauthentik/api";
|
||||
|
||||
import { spread } from "@open-wc/lit-helpers";
|
||||
import { match, P } from "ts-pattern";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, html, nothing, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { CSSResult, html, nothing, PropertyValues } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { guard } from "lit/directives/guard.js";
|
||||
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
||||
import { until } from "lit/directives/until.js";
|
||||
import { html as staticHTML, unsafeStatic } from "lit/static-html.js";
|
||||
|
||||
import PFBackgroundImage from "@patternfly/patternfly/components/BackgroundImage/background-image.css";
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
@@ -83,10 +76,7 @@ import PFTitle from "@patternfly/patternfly/components/Title/title.css";
|
||||
* @part locale-select-select - The select element of the locale select component.
|
||||
*/
|
||||
@customElement("ak-flow-executor")
|
||||
export class FlowExecutor
|
||||
extends WithCapabilitiesConfig(WithBrandConfig(Interface))
|
||||
implements StageHost
|
||||
{
|
||||
export class FlowExecutor extends WithBrandConfig(Interface) implements StageHost {
|
||||
public static readonly DefaultLayout: FlowLayoutEnum =
|
||||
globalAK()?.flow?.layout || FlowLayoutEnum.Stacked;
|
||||
|
||||
@@ -109,57 +99,31 @@ export class FlowExecutor
|
||||
@property({ type: String, attribute: "slug", useDefault: true })
|
||||
public flowSlug: string = window.location.pathname.split("/")[3];
|
||||
|
||||
#challenge: ChallengeTypes | null = null;
|
||||
|
||||
@property({ attribute: false })
|
||||
public set challenge(value: ChallengeTypes | null) {
|
||||
const previousValue = this.#challenge;
|
||||
const previousTitle = previousValue?.flowInfo?.title;
|
||||
const nextTitle = value?.flowInfo?.title;
|
||||
|
||||
this.#challenge = value;
|
||||
|
||||
if (value?.flowInfo) {
|
||||
this.flowInfo = value.flowInfo;
|
||||
}
|
||||
|
||||
if (!nextTitle) {
|
||||
document.title = this.brandingTitle;
|
||||
} else if (nextTitle !== previousTitle) {
|
||||
document.title = `${nextTitle} - ${this.brandingTitle}`;
|
||||
}
|
||||
|
||||
this.requestUpdate("challenge", previousValue);
|
||||
}
|
||||
|
||||
public get challenge(): ChallengeTypes | null {
|
||||
return this.#challenge;
|
||||
}
|
||||
public challenge: ChallengeTypes | null = null;
|
||||
|
||||
@property({ type: Boolean })
|
||||
public loading = false;
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region State
|
||||
|
||||
#inspectorLoaded = false;
|
||||
#logger = ConsoleLogger.prefix("flow-executor");
|
||||
|
||||
@property({ type: Boolean })
|
||||
public inspectorOpen?: boolean;
|
||||
|
||||
@property({ type: Boolean })
|
||||
public inspectorAvailable?: boolean;
|
||||
|
||||
@property({ type: String, attribute: "data-layout", useDefault: true, reflect: true })
|
||||
public layout: FlowLayoutEnum = FlowExecutor.DefaultLayout;
|
||||
|
||||
@state()
|
||||
public flowInfo?: ContextualFlowInfo;
|
||||
//#endregion
|
||||
|
||||
//#region Internal State
|
||||
|
||||
#logger = ConsoleLogger.prefix("flow-executor");
|
||||
|
||||
#api: FlowsApi;
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Accessors
|
||||
|
||||
public get flowInfo() {
|
||||
return this.challenge?.flowInfo ?? null;
|
||||
}
|
||||
|
||||
//#region Lifecycle
|
||||
|
||||
constructor() {
|
||||
@@ -169,14 +133,7 @@ export class FlowExecutor
|
||||
|
||||
WebsocketClient.connect();
|
||||
|
||||
const inspector = new URLSearchParams(window.location.search).get("inspector");
|
||||
|
||||
if (inspector === "" || inspector === "open") {
|
||||
this.inspectorOpen = true;
|
||||
this.inspectorAvailable = true;
|
||||
} else if (inspector === "available") {
|
||||
this.inspectorAvailable = true;
|
||||
}
|
||||
this.#api = new FlowsApi(DEFAULT_CONFIG);
|
||||
|
||||
window.addEventListener("message", (event) => {
|
||||
const msg: {
|
||||
@@ -233,7 +190,15 @@ export class FlowExecutor
|
||||
WebsocketClient.close();
|
||||
}
|
||||
|
||||
protected refresh = (): Promise<void> => {
|
||||
private setFlowErrorChallenge(error: APIError) {
|
||||
this.challenge = {
|
||||
component: "ak-stage-flow-error",
|
||||
error: pluckErrorDetail(error),
|
||||
requestId: "",
|
||||
} satisfies FlowErrorChallenge as ChallengeTypes;
|
||||
}
|
||||
|
||||
protected refresh = async () => {
|
||||
if (!this.flowSlug) {
|
||||
this.#logger.debug("Skipping refresh, no flow slug provided");
|
||||
return Promise.resolve();
|
||||
@@ -241,26 +206,20 @@ export class FlowExecutor
|
||||
|
||||
this.loading = true;
|
||||
|
||||
return new FlowsApi(DEFAULT_CONFIG)
|
||||
return this.#api
|
||||
.flowsExecutorGet({
|
||||
flowSlug: this.flowSlug,
|
||||
query: window.location.search.substring(1),
|
||||
})
|
||||
.then((challenge) => {
|
||||
this.challenge = challenge;
|
||||
return !!this.challenge;
|
||||
})
|
||||
.catch(async (error) => {
|
||||
const parsedError = await parseAPIResponseError(error);
|
||||
|
||||
const challenge: FlowErrorChallenge = {
|
||||
component: "ak-stage-flow-error",
|
||||
error: pluckErrorDetail(parsedError),
|
||||
requestId: "",
|
||||
};
|
||||
|
||||
showAPIErrorMessage(parsedError);
|
||||
|
||||
this.challenge = challenge as ChallengeTypes;
|
||||
this.setFlowErrorChallenge(parsedError);
|
||||
return false;
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
@@ -270,14 +229,8 @@ export class FlowExecutor
|
||||
public async firstUpdated(changed: PropertyValues<this>): Promise<void> {
|
||||
super.firstUpdated(changed);
|
||||
|
||||
if (this.can(CapabilitiesEnum.CanDebug)) {
|
||||
this.inspectorAvailable = true;
|
||||
}
|
||||
|
||||
this.refresh().then(() => {
|
||||
if (this.inspectorOpen) {
|
||||
window.dispatchEvent(new AKFlowAdvanceEvent());
|
||||
}
|
||||
window.dispatchEvent(new AKFlowAdvanceEvent());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -285,6 +238,10 @@ export class FlowExecutor
|
||||
public updated(changedProperties: PropertyValues<this>) {
|
||||
super.updated(changedProperties);
|
||||
|
||||
document.title = match(this.challenge?.flowInfo?.title)
|
||||
.with(P.nullish, () => this.brandingTitle)
|
||||
.otherwise((title) => `${title} - ${this.brandingTitle}`);
|
||||
|
||||
if (changedProperties.has("challenge") && this.challenge?.flowInfo) {
|
||||
this.layout = this.challenge?.flowInfo?.layout || FlowExecutor.DefaultLayout;
|
||||
}
|
||||
@@ -292,16 +249,6 @@ export class FlowExecutor
|
||||
if (changedProperties.has("flowInfo") || changedProperties.has("activeTheme")) {
|
||||
this.#synchronizeFlowInfo();
|
||||
}
|
||||
|
||||
if (
|
||||
changedProperties.has("inspectorOpen") &&
|
||||
this.inspectorOpen &&
|
||||
!this.#inspectorLoaded
|
||||
) {
|
||||
import("#flow/FlowInspector").then(() => {
|
||||
this.#inspectorLoaded = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
@@ -331,33 +278,19 @@ export class FlowExecutor
|
||||
this.loading = true;
|
||||
}
|
||||
|
||||
return new FlowsApi(DEFAULT_CONFIG)
|
||||
return this.#api
|
||||
.flowsExecutorSolve({
|
||||
flowSlug: this.flowSlug,
|
||||
query: window.location.search.substring(1),
|
||||
flowChallengeResponseRequest: payload,
|
||||
})
|
||||
.then((challenge) => {
|
||||
if (this.inspectorOpen) {
|
||||
window.dispatchEvent(new AKFlowAdvanceEvent());
|
||||
}
|
||||
|
||||
window.dispatchEvent(new AKFlowAdvanceEvent());
|
||||
this.challenge = challenge;
|
||||
|
||||
if (this.challenge.flowInfo) {
|
||||
this.flowInfo = this.challenge.flowInfo;
|
||||
}
|
||||
|
||||
return !this.challenge.responseErrors;
|
||||
})
|
||||
.catch((error: unknown) => {
|
||||
const challenge: FlowErrorChallenge = {
|
||||
component: "ak-stage-flow-error",
|
||||
error: pluckErrorDetail(error),
|
||||
requestId: "",
|
||||
};
|
||||
|
||||
this.challenge = challenge as ChallengeTypes;
|
||||
.catch((error: APIError) => {
|
||||
this.setFlowErrorChallenge(error);
|
||||
return false;
|
||||
})
|
||||
.finally(() => {
|
||||
@@ -367,183 +300,64 @@ export class FlowExecutor
|
||||
|
||||
//#region Render Challenge
|
||||
|
||||
protected async renderChallenge(
|
||||
component: ChallengeTypes["component"],
|
||||
): Promise<TemplateResult> {
|
||||
const { challenge, inspectorOpen } = this;
|
||||
protected async renderChallenge(challenge: ChallengeTypes) {
|
||||
const stageEntry = StageMapping.registry.get(challenge.component);
|
||||
|
||||
const stageProps: LitPropertyRecord<BaseStage<NonNullable<typeof challenge>, unknown>> = {
|
||||
".challenge": challenge!,
|
||||
".host": this,
|
||||
};
|
||||
// The special cases!
|
||||
if (!stageEntry) {
|
||||
if (challenge.component === "xak-flow-shell") {
|
||||
return html`${unsafeHTML(challenge.body)}`;
|
||||
}
|
||||
|
||||
const props = {
|
||||
...stageProps,
|
||||
return this.renderChallengeError(
|
||||
`No stage found for component: ${challenge.component}`,
|
||||
);
|
||||
}
|
||||
|
||||
const challengeProps: LitPropertyRecord<BaseStage<NonNullable<typeof challenge>, object>> =
|
||||
{
|
||||
".challenge": challenge,
|
||||
".host": this,
|
||||
};
|
||||
|
||||
const litParts = {
|
||||
part: "challenge",
|
||||
exportparts: exportParts(["additional-actions", "footer-band"], "challenge"),
|
||||
};
|
||||
|
||||
switch (component) {
|
||||
case "ak-stage-access-denied":
|
||||
await import("#flow/stages/access_denied/AccessDeniedStage");
|
||||
return html`<ak-stage-access-denied ${spread(props)}></ak-stage-access-denied>`;
|
||||
case "ak-stage-identification":
|
||||
await import("#flow/stages/identification/IdentificationStage");
|
||||
return html`<ak-stage-identification ${spread(props)}></ak-stage-identification>`;
|
||||
case "ak-stage-password":
|
||||
await import("#flow/stages/password/PasswordStage");
|
||||
return html`<ak-stage-password ${spread(props)}></ak-stage-password>`;
|
||||
case "ak-stage-captcha":
|
||||
await import("#flow/stages/captcha/CaptchaStage");
|
||||
return html`<ak-stage-captcha ${spread(props)}></ak-stage-captcha>`;
|
||||
case "ak-stage-consent":
|
||||
await import("#flow/stages/consent/ConsentStage");
|
||||
return html`<ak-stage-consent ${spread(props)}></ak-stage-consent>`;
|
||||
case "ak-stage-dummy":
|
||||
await import("#flow/stages/dummy/DummyStage");
|
||||
return html`<ak-stage-dummy ${spread(props)}></ak-stage-dummy>`;
|
||||
case "ak-stage-email":
|
||||
await import("#flow/stages/email/EmailStage");
|
||||
return html`<ak-stage-email ${spread(props)}></ak-stage-email>`;
|
||||
case "ak-stage-autosubmit":
|
||||
await import("#flow/stages/autosubmit/AutosubmitStage");
|
||||
return html`<ak-stage-autosubmit ${spread(props)}></ak-stage-autosubmit>`;
|
||||
case "ak-stage-prompt":
|
||||
await import("#flow/stages/prompt/PromptStage");
|
||||
return html`<ak-stage-prompt ${spread(props)}></ak-stage-prompt>`;
|
||||
case "ak-stage-authenticator-totp":
|
||||
await import("#flow/stages/authenticator_totp/AuthenticatorTOTPStage");
|
||||
return html`<ak-stage-authenticator-totp
|
||||
${spread(props)}
|
||||
></ak-stage-authenticator-totp>`;
|
||||
case "ak-stage-authenticator-duo":
|
||||
await import("#flow/stages/authenticator_duo/AuthenticatorDuoStage");
|
||||
return html`<ak-stage-authenticator-duo
|
||||
${spread(props)}
|
||||
></ak-stage-authenticator-duo>`;
|
||||
case "ak-stage-authenticator-static":
|
||||
await import("#flow/stages/authenticator_static/AuthenticatorStaticStage");
|
||||
return html`<ak-stage-authenticator-static
|
||||
${spread(props)}
|
||||
></ak-stage-authenticator-static>`;
|
||||
case "ak-stage-authenticator-webauthn":
|
||||
return html`<ak-stage-authenticator-webauthn
|
||||
${spread(props)}
|
||||
></ak-stage-authenticator-webauthn>`;
|
||||
case "ak-stage-authenticator-email":
|
||||
await import("#flow/stages/authenticator_email/AuthenticatorEmailStage");
|
||||
return html`<ak-stage-authenticator-email
|
||||
${spread(props)}
|
||||
></ak-stage-authenticator-email>`;
|
||||
case "ak-stage-authenticator-sms":
|
||||
await import("#flow/stages/authenticator_sms/AuthenticatorSMSStage");
|
||||
return html`<ak-stage-authenticator-sms
|
||||
${spread(props)}
|
||||
></ak-stage-authenticator-sms>`;
|
||||
case "ak-stage-authenticator-validate":
|
||||
await import("#flow/stages/authenticator_validate/AuthenticatorValidateStage");
|
||||
return html`<ak-stage-authenticator-validate
|
||||
${spread(props)}
|
||||
></ak-stage-authenticator-validate>`;
|
||||
case "ak-stage-user-login":
|
||||
await import("#flow/stages/user_login/UserLoginStage");
|
||||
return html`<ak-stage-user-login
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-user-login>`;
|
||||
case "ak-stage-endpoint-agent":
|
||||
await import("#flow/stages/endpoint/agent/EndpointAgentStage");
|
||||
return html`<ak-stage-endpoint-agent
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-endpoint-agent>`;
|
||||
// Sources
|
||||
case "ak-source-plex":
|
||||
return html`<ak-flow-source-plex ${spread(props)}></ak-flow-source-plex>`;
|
||||
case "ak-source-oauth-apple":
|
||||
return html`<ak-flow-source-oauth-apple
|
||||
${spread(props)}
|
||||
></ak-flow-source-oauth-apple>`;
|
||||
case "ak-source-telegram":
|
||||
return html`<ak-flow-source-telegram ${spread(props)}></ak-flow-source-telegram>`;
|
||||
// Providers
|
||||
case "ak-provider-oauth2-device-code":
|
||||
await import("#flow/providers/oauth2/DeviceCode");
|
||||
return html`<ak-flow-provider-oauth2-code
|
||||
${spread(props)}
|
||||
></ak-flow-provider-oauth2-code>`;
|
||||
case "ak-provider-oauth2-device-code-finish":
|
||||
await import("#flow/providers/oauth2/DeviceCodeFinish");
|
||||
return html`<ak-flow-provider-oauth2-code-finish
|
||||
${spread(props)}
|
||||
></ak-flow-provider-oauth2-code-finish>`;
|
||||
case "ak-stage-session-end":
|
||||
await import("#flow/providers/SessionEnd");
|
||||
return html`<ak-stage-session-end ${spread(props)}></ak-stage-session-end>`;
|
||||
case "ak-provider-saml-native-logout":
|
||||
await import("#flow/providers/saml/NativeLogoutStage");
|
||||
return html`<ak-provider-saml-native-logout
|
||||
${spread(props)}
|
||||
></ak-provider-saml-native-logout>`;
|
||||
case "ak-provider-iframe-logout":
|
||||
await import("#flow/providers/IFrameLogoutStage");
|
||||
return html`<ak-provider-iframe-logout
|
||||
${spread(props)}
|
||||
></ak-provider-iframe-logout>`;
|
||||
// Internal stages
|
||||
case "ak-stage-flow-error":
|
||||
return html`<ak-stage-flow-error ${spread(props)}></ak-stage-flow-error>`;
|
||||
case "xak-flow-redirect":
|
||||
return html`<ak-stage-redirect ${spread(props)} ?promptUser=${inspectorOpen}>
|
||||
</ak-stage-redirect>`;
|
||||
case "xak-flow-shell":
|
||||
return html`${unsafeHTML((this.challenge as ShellChallenge).body)}`;
|
||||
case "xak-flow-frame":
|
||||
return html`<xak-flow-frame
|
||||
.host=${this}
|
||||
.challenge=${challenge}
|
||||
></xak-flow-frame>`;
|
||||
default:
|
||||
return html`Invalid native challenge element`;
|
||||
let mapping: StageMapping;
|
||||
|
||||
try {
|
||||
mapping = await StageMapping.from(stageEntry);
|
||||
} catch (error: unknown) {
|
||||
return this.renderChallengeError(error);
|
||||
}
|
||||
|
||||
const { tag, variant } = mapping;
|
||||
|
||||
const props = spread(
|
||||
match(variant)
|
||||
.with("challenge", () => challengeProps)
|
||||
.with("standard", () => ({ ...challengeProps, ...litParts }))
|
||||
.exhaustive(),
|
||||
);
|
||||
|
||||
return staticHTML`<${unsafeStatic(tag)} ${props}></${unsafeStatic(tag)}>`;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
protected renderChallengeError(error: unknown): SlottedTemplateResult {
|
||||
const detail = pluckErrorDetail(error);
|
||||
|
||||
//#region Render Inspector
|
||||
// eslint-disable-next-line no-console
|
||||
console.trace(error);
|
||||
|
||||
@listen(AKFlowInspectorChangeEvent)
|
||||
protected toggleInspector = () => {
|
||||
this.inspectorOpen = !this.inspectorOpen;
|
||||
const errorChallenge: FlowErrorChallenge = {
|
||||
component: "ak-stage-flow-error",
|
||||
error: detail,
|
||||
requestId: "",
|
||||
};
|
||||
|
||||
const drawer = document.getElementById("flow-drawer");
|
||||
|
||||
if (!drawer) {
|
||||
return;
|
||||
}
|
||||
|
||||
drawer.classList.toggle("pf-m-expanded", this.inspectorOpen);
|
||||
drawer.classList.toggle("pf-m-collapsed", !this.inspectorOpen);
|
||||
};
|
||||
|
||||
protected renderInspectorButton() {
|
||||
return guard([this.inspectorAvailable, this.inspectorOpen], () => {
|
||||
if (!this.inspectorAvailable || this.inspectorOpen) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return html`<button
|
||||
aria-label=${this.inspectorOpen
|
||||
? msg("Close flow inspector")
|
||||
: msg("Open flow inspector")}
|
||||
aria-expanded=${this.inspectorOpen ? "true" : "false"}
|
||||
class="inspector-toggle pf-c-button pf-m-primary"
|
||||
aria-controls="flow-inspector"
|
||||
@click=${this.toggleInspector}
|
||||
>
|
||||
<i class="fa fa-search-plus" aria-hidden="true"></i>
|
||||
</button>`;
|
||||
});
|
||||
return html`<ak-stage-flow-error .challenge=${errorChallenge}></ak-stage-flow-error>`;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
@@ -555,7 +369,7 @@ export class FlowExecutor
|
||||
}
|
||||
|
||||
protected renderFrameBackground(): SlottedTemplateResult {
|
||||
return guard([this.layout, this.#challenge], () => {
|
||||
return guard([this.layout, this.challenge], () => {
|
||||
if (
|
||||
this.layout !== FlowLayoutEnum.SidebarLeftFrameBackground &&
|
||||
this.layout !== FlowLayoutEnum.SidebarRightFrameBackground
|
||||
@@ -563,7 +377,7 @@ export class FlowExecutor
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const src = this.#challenge?.flowInfo?.background;
|
||||
const src = this.challenge?.flowInfo?.background;
|
||||
|
||||
if (!src) return nothing;
|
||||
|
||||
@@ -596,15 +410,17 @@ export class FlowExecutor
|
||||
}
|
||||
|
||||
protected override render(): SlottedTemplateResult {
|
||||
const { component } = this.challenge || {};
|
||||
const { challenge, loading } = this;
|
||||
|
||||
return html`<ak-locale-select
|
||||
part="locale-select"
|
||||
exportparts="label:locale-select-label,select:locale-select-select"
|
||||
class="pf-m-dark"
|
||||
></ak-locale-select>
|
||||
${this.renderFrameBackground()}
|
||||
<header class="pf-c-login__header">${this.renderInspectorButton()}</header>
|
||||
|
||||
<header class="pf-c-login__header">
|
||||
<ak-flow-inspector-button></ak-flow-inspector-button>
|
||||
</header>
|
||||
<main
|
||||
data-layout=${this.layout}
|
||||
class="pf-c-login__main"
|
||||
@@ -620,10 +436,12 @@ export class FlowExecutor
|
||||
themedUrls: this.brandingLogoThemedUrls,
|
||||
})}
|
||||
</div>
|
||||
${this.loading && this.challenge
|
||||
? html`<ak-loading-overlay part="loading-overlay"></ak-loading-overlay>`
|
||||
: nothing}
|
||||
${component ? until(this.renderChallenge(component)) : this.renderLoading()}
|
||||
${loading && challenge ? html`<ak-loading-overlay></ak-loading-overlay>` : nothing}
|
||||
${guard([challenge], () => {
|
||||
return challenge?.component
|
||||
? until(this.renderChallenge(challenge))
|
||||
: this.renderLoading();
|
||||
})}
|
||||
</main>
|
||||
${this.renderFooter()}`;
|
||||
}
|
||||
|
||||
110
web/src/flow/FlowExecutorStageFactory.ts
Normal file
110
web/src/flow/FlowExecutorStageFactory.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* We have several patterns for the client-side components that handle a stage. In most cases, the
|
||||
* stage component-name and the client-side element-name are the same, but not always. Most stages
|
||||
* need CSS-related help to be visually attractive, but "challenge" stages do not. Most stages can
|
||||
* be imported as-needed, but some must be pre-loaded.
|
||||
*/
|
||||
|
||||
import { ResolvedDefaultESModule } from "#common/modules/types";
|
||||
import { DeepRequired } from "#common/types";
|
||||
|
||||
import { StageEntries, StageEntry } from "#flow/FlowExecutorStages";
|
||||
import type {
|
||||
BaseStageConstructor,
|
||||
FlowChallengeComponentName,
|
||||
StageModuleCallback,
|
||||
} from "#flow/types";
|
||||
|
||||
import { match, P } from "ts-pattern";
|
||||
|
||||
export type { FlowChallengeComponentName, StageModuleCallback };
|
||||
export const propVariants = ["standard", "challenge"] as const;
|
||||
export type PropVariant = (typeof propVariants)[number];
|
||||
|
||||
// The first type supports "import only."
|
||||
type StageEntryMetadata =
|
||||
| []
|
||||
| [tag: string]
|
||||
| [variant: PropVariant]
|
||||
| [tag: string, variant: PropVariant];
|
||||
|
||||
const STANDARD = propVariants[0];
|
||||
const isImport = (x: unknown): x is StageModuleCallback => typeof x === "function";
|
||||
const PVariant = P.when(
|
||||
(x): x is PropVariant => typeof x === "string" && propVariants.includes(x as PropVariant),
|
||||
);
|
||||
const PTag = P.when((x): x is string => typeof x === "string" && x.includes("-"));
|
||||
|
||||
export class StageMappingError extends TypeError {
|
||||
constructor(message: string, options?: ErrorOptions) {
|
||||
super(message, options);
|
||||
this.name = "StageMappingError";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a stage constructor to its custom element tag name.
|
||||
*/
|
||||
function resolveStageTag(module: ResolvedDefaultESModule<BaseStageConstructor>): string {
|
||||
const StageConstructor = module.default;
|
||||
const tag = window.customElements.getName(StageConstructor);
|
||||
|
||||
if (!tag) {
|
||||
const error = new StageMappingError(
|
||||
`Failed to load module: No client stage found for component`,
|
||||
);
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
interface StageMappingInit {
|
||||
token: FlowChallengeComponentName;
|
||||
variant: PropVariant;
|
||||
tag?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The metadata needed to load and invoke a stage.
|
||||
*/
|
||||
export class StageMapping {
|
||||
/**
|
||||
* A mapping of server-side stage tokens to client-side custom element tags.
|
||||
*
|
||||
* This can be used to determine if a given stage component has a corresponding client-side stage.
|
||||
*/
|
||||
public static readonly registry: ReadonlyMap<FlowChallengeComponentName, StageEntry> = new Map(
|
||||
StageEntries.map((entry) => [entry[0], entry]),
|
||||
);
|
||||
|
||||
public readonly token: FlowChallengeComponentName;
|
||||
public readonly variant: PropVariant;
|
||||
public readonly tag: string;
|
||||
|
||||
protected constructor({ token, variant, tag }: DeepRequired<StageMappingInit>) {
|
||||
this.token = token;
|
||||
this.tag = tag;
|
||||
this.variant = variant;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a `StageMapping` from a `StageEntry`.
|
||||
*/
|
||||
public static async from([token, ...rest]: StageEntry): Promise<StageMapping> {
|
||||
const last = rest.at(-1);
|
||||
const callback = isImport(last) ? last : null;
|
||||
const meta = (callback ? rest.slice(0, -1) : rest) as StageEntryMetadata;
|
||||
|
||||
const init = match<StageEntryMetadata, StageMappingInit>(meta)
|
||||
.with([], () => ({ token, variant: STANDARD }))
|
||||
.with([PTag, PVariant], ([tag, variant]) => ({ token, variant, tag }))
|
||||
.with([PVariant], ([variant]) => ({ token, variant }))
|
||||
.with([PTag], ([tag]) => ({ token, variant: STANDARD, tag }))
|
||||
.exhaustive();
|
||||
|
||||
const tag = init.tag || (await callback?.().then(resolveStageTag)) || token;
|
||||
return new StageMapping({ ...init, tag });
|
||||
}
|
||||
}
|
||||
96
web/src/flow/FlowExecutorStages.ts
Normal file
96
web/src/flow/FlowExecutorStages.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* @file Flow executor stage definitions.
|
||||
*
|
||||
* @remarks
|
||||
* The following imports must be imported statically, as they define web components that are used in stage definitions below.
|
||||
*/
|
||||
|
||||
import "#flow/sources/apple/AppleLoginInit";
|
||||
import "#flow/sources/plex/PlexLoginInit";
|
||||
import "#flow/sources/telegram/TelegramLogin";
|
||||
import "#flow/stages/FlowErrorStage";
|
||||
import "#flow/stages/FlowFrameStage";
|
||||
import "#flow/stages/RedirectStage";
|
||||
import "#flow/stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage";
|
||||
|
||||
import type {
|
||||
FlowChallengeComponentName,
|
||||
PropVariant,
|
||||
StageModuleCallback,
|
||||
} from "#flow/FlowExecutorStageFactory";
|
||||
|
||||
/**
|
||||
* A tuple representing the metadata for a stage entry in the stage mapping registry.
|
||||
*/
|
||||
// prettier-ignore
|
||||
export type StageEntry =
|
||||
| [token: FlowChallengeComponentName, tag: string, variant: PropVariant, import?: StageModuleCallback]
|
||||
| [token: FlowChallengeComponentName, variant: PropVariant, import?: StageModuleCallback]
|
||||
| [token: FlowChallengeComponentName, tag: string, import?: StageModuleCallback]
|
||||
| [token: FlowChallengeComponentName, import?: StageModuleCallback];
|
||||
|
||||
/**
|
||||
* A mapping of server-side stage tokens to client-side custom element tags, along with the variant
|
||||
* of props they consume and an optional import callback for lazy-loading.
|
||||
*
|
||||
* @remarks
|
||||
* The different ways a stage can be associated with its server-side component are listed in the
|
||||
* type declaration above. The variants are meant to reduce the amount of information you have to
|
||||
* provide:
|
||||
*
|
||||
* - If the server-side component and the client-side tag are the same, only provide the component.
|
||||
* - Variants describe the attribute needs. There are only two variant: "standard" and "challenge."
|
||||
* The "challenge" variant is for components that immediately issue redirects. "standard" is the
|
||||
* default; you don't need to specify it.
|
||||
* - If the stage needs to be live immediately, import it above. Otherwise, provide an import
|
||||
* function, following the examples already provided.
|
||||
*
|
||||
* Variants and Tags have a single strong differentiator: Tags refer to web components and so must
|
||||
* always have a dash, whereas wariants are from a limited supply of names and do not have a dash.
|
||||
* The StageFactory will not get confused. If you get confused, the type-checker will explain it.
|
||||
*
|
||||
* The resolution of the web component tag name is: tag supplied, tag received with import, tag
|
||||
* derived from component name. THIS CAN FAIL: a preloaded stage with an incongruent and non- or
|
||||
* incorrectly-specified tag will result in a stage that cannot be rendered. Pre-loaded stages must
|
||||
* be tested carefully.
|
||||
*/
|
||||
// ,---. | | , . ,---.| | |
|
||||
// |---|,---|,---| |\ |,---.. . . `---.|--- ,---.,---.,---.,---. |---|,---.,---.,---.
|
||||
// | || || | | \ ||---'| | | || ,---|| ||---'`---. | ||---'| |---'
|
||||
// ` '`---'`---' ` `'`---'`-'-' `---'`---'`---^`---|`---'`---' ` '`---'` `---'
|
||||
// `---'
|
||||
// prettier-ignore
|
||||
export const StageEntries: readonly StageEntry[] = [
|
||||
["ak-provider-iframe-logout", () => import("#flow/providers/IFrameLogoutStage")],
|
||||
["ak-provider-oauth2-device-code", () => import("#flow/providers/oauth2/DeviceCode")],
|
||||
["ak-provider-oauth2-device-code-finish", () => import("#flow/providers/oauth2/DeviceCodeFinish")],
|
||||
["ak-provider-saml-native-logout", () => import("#flow/providers/saml/NativeLogoutStage")],
|
||||
|
||||
["ak-source-oauth-apple", "ak-flow-source-oauth-apple"],
|
||||
["ak-source-plex", "ak-flow-source-plex"],
|
||||
["ak-source-telegram", "ak-flow-source-telegram"],
|
||||
|
||||
["ak-stage-access-denied", () => import("#flow/stages/access_denied/AccessDeniedStage")],
|
||||
["ak-stage-authenticator-duo", () => import("#flow/stages/authenticator_duo/AuthenticatorDuoStage")],
|
||||
["ak-stage-authenticator-email", () => import("#flow/stages/authenticator_email/AuthenticatorEmailStage")],
|
||||
["ak-stage-authenticator-sms", () => import("#flow/stages/authenticator_sms/AuthenticatorSMSStage")],
|
||||
["ak-stage-authenticator-static", () => import("#flow/stages/authenticator_static/AuthenticatorStaticStage")],
|
||||
["ak-stage-authenticator-totp", () => import("#flow/stages/authenticator_totp/AuthenticatorTOTPStage")],
|
||||
["ak-stage-authenticator-validate", () => import("#flow/stages/authenticator_validate/AuthenticatorValidateStage")],
|
||||
["ak-stage-authenticator-webauthn"],
|
||||
["ak-stage-autosubmit", () => import("#flow/stages/autosubmit/AutosubmitStage")],
|
||||
["ak-stage-captcha", () => import("#flow/stages/captcha/CaptchaStage")],
|
||||
["ak-stage-consent", () => import("#flow/stages/consent/ConsentStage")],
|
||||
["ak-stage-dummy", () => import("#flow/stages/dummy/DummyStage")],
|
||||
["ak-stage-email", () => import("#flow/stages/email/EmailStage")],
|
||||
["ak-stage-endpoint-agent", "challenge", () => import("#flow/stages/endpoint/agent/EndpointAgentStage")],
|
||||
["ak-stage-flow-error"],
|
||||
["ak-stage-identification", () => import("#flow/stages/identification/IdentificationStage")],
|
||||
["ak-stage-password", () => import("#flow/stages/password/PasswordStage")],
|
||||
["ak-stage-prompt", () => import("#flow/stages/prompt/PromptStage")],
|
||||
["ak-stage-session-end", () => import("#flow/providers/SessionEnd")],
|
||||
["ak-stage-user-login", "challenge", () => import("#flow/stages/user_login/UserLoginStage")],
|
||||
|
||||
["xak-flow-frame", "challenge"],
|
||||
["xak-flow-redirect", "ak-stage-redirect"],
|
||||
]
|
||||
@@ -33,21 +33,33 @@ export class BrandLinks extends AKElement {
|
||||
public links: FooterLink[] = globalAK().brand.uiFooterLinks || [];
|
||||
|
||||
render() {
|
||||
return html`<ul aria-label=${msg("Site links")} class="pf-c-list pf-m-inline" part="list">
|
||||
${map(this.links, (link) => {
|
||||
const links = [
|
||||
...this.links,
|
||||
{
|
||||
name: msg("Powered by authentik"),
|
||||
href: null,
|
||||
},
|
||||
];
|
||||
return html`<ul
|
||||
aria-label=${msg("Site links")}
|
||||
class="pf-c-list pf-m-inline"
|
||||
part="list"
|
||||
data-count=${links.length}
|
||||
>
|
||||
${map(links, (link, idx) => {
|
||||
const children = sanitizeHTML(BrandedHTMLPolicy, link.name);
|
||||
|
||||
if (link.href) {
|
||||
return html`<li part="list-item">
|
||||
<a part="list-item-link" href=${link.href}>${children}</a>
|
||||
</li>`;
|
||||
}
|
||||
|
||||
return html`<li part="list-item">
|
||||
<span>${children}</span>
|
||||
return html`<li
|
||||
part="list-item"
|
||||
data-index=${idx}
|
||||
data-kind=${link.href ? "link" : "text"}
|
||||
data-track-name=${idx === 0 ? "start" : idx === links.length - 1 ? "end" : idx}
|
||||
>
|
||||
${link.href
|
||||
? html`<a part="list-item-link" href=${link.href}>${children}</a>`
|
||||
: children}
|
||||
</li>`;
|
||||
})}
|
||||
<li part="list-item"><span>${msg("Powered by authentik")}</span></li>
|
||||
</ul>`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { AKElement } from "#elements/Base";
|
||||
import { listen } from "#elements/decorators/listen";
|
||||
|
||||
import { AKFlowAdvanceEvent, AKFlowInspectorChangeEvent } from "#flow/events";
|
||||
import Styles from "#flow/FlowInspector.css";
|
||||
import Styles from "#flow/inspector/FlowInspector.css";
|
||||
|
||||
import { FlowInspection, FlowsApi, Stage } from "@goauthentik/api";
|
||||
|
||||
85
web/src/flow/inspector/FlowInspectorButton.ts
Normal file
85
web/src/flow/inspector/FlowInspectorButton.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { AKElement } from "#elements/Base";
|
||||
import { listen } from "#elements/decorators/listen";
|
||||
import { CapabilitiesEnum, WithCapabilitiesConfig } from "#elements/mixins/capabilities";
|
||||
|
||||
import { AKFlowAdvanceEvent, AKFlowInspectorChangeEvent } from "#flow/events";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { html, nothing, PropertyValues } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
|
||||
// Custom implementation because there are rules for when to show this.
|
||||
|
||||
@customElement("ak-flow-inspector-button")
|
||||
export class FlowInspectorButton extends WithCapabilitiesConfig(AKElement) {
|
||||
public static readonly styles = [PFButton];
|
||||
|
||||
@property({ type: Boolean, reflect: true })
|
||||
public open = false;
|
||||
|
||||
@state()
|
||||
private available = false;
|
||||
|
||||
@state()
|
||||
private loaded = false;
|
||||
|
||||
@listen(AKFlowInspectorChangeEvent)
|
||||
protected _onInspectorToggle = (ev: AKFlowInspectorChangeEvent) => {
|
||||
this.open = ev.open;
|
||||
};
|
||||
|
||||
public override connectedCallback() {
|
||||
super.connectedCallback();
|
||||
const inspector = new URLSearchParams(window.location.search).get("inspector");
|
||||
this.available = this.can(CapabilitiesEnum.CanDebug) || inspector !== undefined;
|
||||
this.open = inspector === "" || inspector === "open";
|
||||
}
|
||||
|
||||
protected toggle = () => {
|
||||
this.open = !this.open;
|
||||
};
|
||||
|
||||
public override render() {
|
||||
return this.open || !this.available
|
||||
? nothing
|
||||
: html`<button
|
||||
aria-label=${this.open ? msg("Close flow inspector") : msg("Open flow inspector")}
|
||||
aria-expanded=${this.open ? "true" : "false"}
|
||||
class="inspector-toggle pf-c-button pf-m-primary"
|
||||
aria-controls="flow-inspector"
|
||||
@click=${this.toggle}
|
||||
>
|
||||
<i class="fa fa-search-plus" aria-hidden="true"></i>
|
||||
</button>`;
|
||||
}
|
||||
|
||||
public override firstUpdated(changed: PropertyValues<this>) {
|
||||
super.firstUpdated(changed);
|
||||
if (this.open) {
|
||||
window.dispatchEvent(new AKFlowAdvanceEvent());
|
||||
}
|
||||
}
|
||||
|
||||
// Only load the inspector if the user requests it. It should hydrate automatically
|
||||
public override updated(changed: PropertyValues<this>) {
|
||||
super.updated(changed);
|
||||
if (changed.has("open") && this.open && !this.loaded) {
|
||||
import("#flow/inspector/FlowInspector").then(() => {
|
||||
this.loaded = true;
|
||||
});
|
||||
}
|
||||
const drawer = document.getElementById("flow-drawer");
|
||||
if (changed.has("open") && drawer) {
|
||||
drawer.classList.toggle("pf-m-expanded", this.open);
|
||||
drawer.classList.toggle("pf-m-collapsed", !this.open);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ak-flow-inspector-button": FlowInspectorButton;
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import { FlowChallengeResponseRequest, RedirectChallenge } from "@goauthentik/ap
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { css, CSSResult, html, nothing, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
@@ -18,9 +18,6 @@ import PFTitle from "@patternfly/patternfly/components/Title/title.css";
|
||||
|
||||
@customElement("ak-stage-redirect")
|
||||
export class RedirectStage extends BaseStage<RedirectChallenge, FlowChallengeResponseRequest> {
|
||||
@property({ type: Boolean })
|
||||
promptUser = false;
|
||||
|
||||
@state()
|
||||
startedRedirect = false;
|
||||
|
||||
@@ -41,6 +38,14 @@ export class RedirectStage extends BaseStage<RedirectChallenge, FlowChallengeRes
|
||||
return new URL(this.challenge?.to || "", document.baseURI).toString();
|
||||
}
|
||||
|
||||
// The current implementation expects the button and the stage to share the same DOM context,
|
||||
// and the same rootNode. If that changes, this will need to be updated.
|
||||
get promptUser() {
|
||||
return !!(this.getRootNode() as Element | undefined)?.querySelector(
|
||||
"ak-flow-inspector-button",
|
||||
)?.open;
|
||||
}
|
||||
|
||||
updated(changed: PropertyValues<this>): void {
|
||||
super.updated(changed);
|
||||
|
||||
|
||||
@@ -365,13 +365,13 @@ export class IdentificationStage extends BaseStage<
|
||||
const enrollmentItem = enrollUrl
|
||||
? html`<div class="pf-c-login__main-footer-band-item">
|
||||
${msg("Need an account?")}
|
||||
<a name="enroll" href="${enrollUrl}">${msg("Sign up.")}</a>
|
||||
<a href="${enrollUrl}" ouiaId="enroll">${msg("Sign up.")}</a>
|
||||
</div>`
|
||||
: null;
|
||||
|
||||
const recoveryItem = recoveryUrl
|
||||
? html`<div class="pf-c-login__main-footer-band-item">
|
||||
<a name="recovery" href="${recoveryUrl}"
|
||||
<a href="${recoveryUrl}" ouiaId="recovery"
|
||||
>${msg("Forgot username or password?")}</a
|
||||
>
|
||||
</div>`
|
||||
@@ -450,7 +450,6 @@ export class IdentificationStage extends BaseStage<
|
||||
<ak-flow-input-password
|
||||
label=${msg("Password")}
|
||||
input-id="ak-stage-identification-password"
|
||||
required
|
||||
class="pf-c-form__group"
|
||||
.errors=${this.challenge?.responseErrors?.password}
|
||||
?allow-show-password=${this.challenge.allowShowPassword}
|
||||
@@ -510,9 +509,9 @@ export class IdentificationStage extends BaseStage<
|
||||
${this.renderInput()}
|
||||
${this.challenge?.passwordlessUrl
|
||||
? html`<a
|
||||
name="passwordless"
|
||||
href=${this.challenge.passwordlessUrl}
|
||||
class="pf-c-button pf-m-secondary pf-m-block"
|
||||
ouiaId="passwordless"
|
||||
>
|
||||
${msg("Use a security key")}
|
||||
</a> `
|
||||
|
||||
@@ -46,7 +46,6 @@ export class PasswordStage extends BaseStage<PasswordChallenge, PasswordChalleng
|
||||
/>
|
||||
<ak-flow-input-password
|
||||
label=${msg("Password")}
|
||||
required
|
||||
grab-focus
|
||||
class="pf-c-form__group"
|
||||
.errors=${this.#errors("password")}
|
||||
@@ -72,9 +71,7 @@ export class PasswordStage extends BaseStage<PasswordChallenge, PasswordChalleng
|
||||
>
|
||||
<legend class="sr-only">${msg("Additional actions")}</legend>
|
||||
<div class="pf-c-login__main-footer-band-item">
|
||||
<a name="forgot-password" href="${this.challenge.recoveryUrl}"
|
||||
>${msg("Forgot password?")}</a
|
||||
>
|
||||
<a href="${this.challenge.recoveryUrl}">${msg("Forgot password?")}</a>
|
||||
</div>
|
||||
</fieldset>`
|
||||
: null}
|
||||
|
||||
@@ -344,6 +344,10 @@
|
||||
(var(--ak-c-login__footer--PaddingBlock) * 2) + (var(--pf-global--LineHeight--md) * 1rem)
|
||||
);
|
||||
|
||||
/* Only applicable to the smallest of mobile viewports. */
|
||||
max-width: 100dvw;
|
||||
overflow: hidden;
|
||||
|
||||
color: var(--ak-c-login__footer--Color);
|
||||
|
||||
@media (max-width: 35rem) {
|
||||
|
||||
@@ -63,6 +63,36 @@
|
||||
|
||||
--ak-c-login__footer--PaddingBlock: var(--pf-global--spacer--md);
|
||||
--ak-c-login__footer--Color: var(--ak-global--BackgroundColorContrast--100);
|
||||
|
||||
--ak-c-login__footer--ColumnGap: min(var(--pf-global--spacer--2xl), 2dvw);
|
||||
--ak-c-login__footer--RowGap: var(--pf-global--spacer--md);
|
||||
|
||||
--ak-c-login__footer--Display: grid;
|
||||
|
||||
--ak-c-login__footer--MaxWidth: var(--ak-c-login--MaxWidth);
|
||||
/* Gracefully degrade to the login max width if CSS size functions are not supported. */
|
||||
--ak-c-login__footer--MaxWidth: min(100dvw, var(--ak-c-login--MaxWidth));
|
||||
|
||||
--ak-c-login__footer--TrackMin: max-content;
|
||||
--ak-c-login__footer--TrackWidth: minmax(
|
||||
var(--ak-c-login__footer--TrackMin),
|
||||
var(--ak-c-login__footer--TrackMax)
|
||||
);
|
||||
|
||||
--ak-c-login__footer--ItemMaxWidth: calc(
|
||||
var(--ak-c-login__footer--MaxWidth) - var(--ak-c-login__footer--ColumnGap)
|
||||
);
|
||||
|
||||
--ak-c-login__footer--ColumnCount: 4;
|
||||
--ak-c-login__footer--TrackMax: calc(
|
||||
(var(--ak-c-login__footer--MaxWidth) / var(--ak-c-login__footer--ColumnCount)) -
|
||||
var(--ak-c-login__footer--ColumnGap)
|
||||
);
|
||||
|
||||
@media (width <= 35rem) {
|
||||
--ak-c-login__footer--TrackWidth: 1fr;
|
||||
--ak-c-login__footer__list-item--FlexBasis: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
[data-theme="dark"] .pf-c-login {
|
||||
@@ -93,6 +123,9 @@
|
||||
--pf-c-login__main-footer-band--BackgroundColor: transparent;
|
||||
|
||||
--pf-c-login__footer--c-list--xl--PaddingTop: 0;
|
||||
--pf-c-login__footer--PaddingLeft: var(--pf-global--spacer--lg);
|
||||
--pf-c-login__footer--PaddingRight: var(--pf-global--spacer--lg);
|
||||
|
||||
--pf-c-login__container--PaddingLeft: 0 !important;
|
||||
--pf-c-login__container--PaddingRight: 0 !important;
|
||||
}
|
||||
@@ -133,9 +166,45 @@
|
||||
&::part(list) {
|
||||
--pf-c-list--m-inline--li--MarginRight: 0;
|
||||
|
||||
/* 3 entries is a unique scenario where 2 columns is visually balanced. */
|
||||
&[data-count="3"] {
|
||||
--ak-c-login__footer--ColumnCount: 2;
|
||||
}
|
||||
|
||||
justify-content: center;
|
||||
column-gap: var(--pf-global--spacer--2xl);
|
||||
row-gap: var(--pf-global--spacer--md);
|
||||
column-gap: var(--ak-c-login__footer--ColumnGap);
|
||||
row-gap: var(--ak-c-login__footer--RowGap);
|
||||
|
||||
max-width: var(--ak-c-login__footer--MaxWidth);
|
||||
place-items: center;
|
||||
display: var(--ak-c-login__footer--Display);
|
||||
|
||||
grid-template-columns: repeat(
|
||||
var(--ak-c-login__footer--ColumnCount),
|
||||
var(--ak-c-login__footer--TrackWidth)
|
||||
);
|
||||
|
||||
grid-template-rows:
|
||||
[header] max-content
|
||||
[main] max-content
|
||||
[footer];
|
||||
}
|
||||
|
||||
[part="list-item"],
|
||||
&::part(list-item) {
|
||||
/* CSS grid is preferred, but if the custom CSS overrides this, default to something reasonable. */
|
||||
flex: 1 1 var(--ak-c-login__footer__list-item--FlexBasis, auto);
|
||||
text-align: center;
|
||||
max-width: var(--ak-c-login__footer--ItemMaxWidth);
|
||||
|
||||
&[data-kind="text"] {
|
||||
&[data-track-name="start"] {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
&[data-track-name="end"] {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[part="list-item-link"],
|
||||
|
||||
@@ -25,6 +25,17 @@ client_id=application_client_id&
|
||||
scope=openid email my-other-scope
|
||||
```
|
||||
|
||||
Alternatively the client id may be sent via the HTTP Authorization header:
|
||||
|
||||
```http
|
||||
POST /application/o/device/ HTTP/1.1
|
||||
Host: authentik.company
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
Authorization: Bearer YXBwbGljYXRpb25fY2xpZW50X2lkOg==
|
||||
|
||||
scope=openid email my-other-scope
|
||||
```
|
||||
|
||||
The response contains the following fields:
|
||||
|
||||
- `device_code`: Device code, which is the code kept on the device
|
||||
|
||||
@@ -34,10 +34,6 @@ Adhering to the following guidelines will help us get your PRs merged much easie
|
||||
- [docs templates](./templates/index.md)
|
||||
- [integration guide template](https://integrations.goauthentik.io/applications#add-a-new-application)
|
||||
|
||||
:::tip
|
||||
If you encounter build check fails, or issues with your local build, you might need to run `make docs-install` in order to get the latest build tools and dependencies; we do occasionally update our build tools.
|
||||
:::
|
||||
|
||||
## Setting up a docs development environment
|
||||
|
||||
### Prerequisites
|
||||
@@ -83,7 +79,11 @@ Run the following command to install or update the build tools for both the tech
|
||||
make docs-install
|
||||
```
|
||||
|
||||
Installs or updates the build dependencies such as Docusaurus, Prettier, and ESLint. You should run this command when you are first setting up your writing environment, and also if you encounter build check fails either when you build locally or when you push your PR to the authentik repository. Running this command will grab any new dependencies that we might have added to our build tool package.
|
||||
This command installs or updates the build dependencies such as Docusaurus, Prettier, and ESLint. You should run this command when you are first setting up your writing environment, and also if you encounter build check fails either when you build locally or when you push your PR to the authentik repository. Running this command will grab any new dependencies that we might have added to our build tool package.
|
||||
|
||||
:::tip
|
||||
If you have the [full development environment](../setup/full-dev-environment.mdx) installed you can run `make install` to get all of the latest build tools and dependencies, not just those for building documentation.
|
||||
:::
|
||||
|
||||
## Writing or modifying technical docs
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ make migrate
|
||||
```
|
||||
|
||||
:::info
|
||||
If you ever want to start over, use `make dev-reset` which drops and restores the authentik PostgreSQL database to the state after `make migrate`.
|
||||
If you ever want to start over, use `make dev-reset` which drops and restores the authentik PostgreSQL database to the state it was after you ran after `make migrate`.
|
||||
:::
|
||||
|
||||
## 5. Running authentik
|
||||
@@ -174,6 +174,18 @@ make web-watch
|
||||
|
||||
When `AUTHENTIK_DEBUG` is set to `true` (the default for the development environment), the authentik server automatically reloads whenever changes are made to the code. However, due to instabilities in the reloading process of the worker, that behavior is turned off for the worker. You can enable code reloading in the worker by manually running `uv run ak worker --watch`.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Recovery key
|
||||
|
||||
If you can't login anymore or the authentication flow repeats (perhaps due to an incorrectly configured stage or a failed flow import), you can create a recovery key by running this command in your terminal:
|
||||
|
||||
`uv run ak create_recovery_key 10 akadmin`
|
||||
|
||||
Copy the generated recovery key and paste it into the URL, after the domain. For example:
|
||||
|
||||
`http://localhost:9000/recovery/use-token/ChFk2nJKJKJKY9OdIc8yv6RCgpGYp5rdndBhR6qHoHoJoWDdlvLuvU/`
|
||||
|
||||
## End-to-End (E2E) Setup
|
||||
|
||||
Start the E2E test services with the following command:
|
||||
|
||||
@@ -29,7 +29,7 @@ The fundamental steps to implement initial permissions are as follows:
|
||||
Because the new initial permissions object is coupled with the role (and that role is assigned to a group), the initial permissions object is applied automatically to any new objects (users or flows or any object) that the member user creates.
|
||||
|
||||
:::info
|
||||
Typically, initial permissions are assigned to non-super-user, non-administrator roles. In this scenario, the administrator needs to verify that the user has the `Can view Admin interface` permission (which allows the user to access the Admin interface). For details, see Step 5 below.
|
||||
Typically, initial permissions are assigned to non-super-user, non-administrator roles. In this scenario, the administrator needs to verify that the user has the `Can access Admin interface` permission (which allows the user to access the Admin interface). For details, see Step 5 below.
|
||||
|
||||
Be aware that any rights beyond viewing the Admin interface will need to be assigned as well; for example, if you want a non-administrator user to be able to create flows in the Admin interface, you need to grant those global permissions to add flows.
|
||||
:::
|
||||
@@ -53,6 +53,6 @@ To create a new set of initial permissions and apply them to a role, follow thes
|
||||
|
||||
- **Permissions**: select all permissions to add to the initial permissions object.
|
||||
|
||||
5. To ensure that the role to which you assign the initial permissions _also_ has access to the Admin interface, check to see if the users also need [the global permission `Can view admin interface`](./manage_permissions.md#assign-can-view-admin-interface-permissions). Furthermore, verify that the user(s) has the global permissions to add specific objects.
|
||||
5. To ensure that the role to which you assign the initial permissions _also_ has access to the Admin interface, check to see if the users also need [the global permission `Can access admin interface`](./manage_permissions.md#assign-can-access-admin-interface-permissions). Furthermore, verify that the user(s) has the global permissions to add specific objects.
|
||||
|
||||
6. Optionally, create new users and add them to the group. Each new user added to the group will automatically have the set of permissions included within the initial permissions object.
|
||||
|
||||
@@ -69,11 +69,11 @@ To assign or remove _global_ permissions for a role:
|
||||
1. Select the permission(s) you'd like to remove.
|
||||
2. Click **Delete Object Permission**.
|
||||
|
||||
### Assign `Can view Admin interface` permissions
|
||||
### Assign `Can access admin interface` permissions
|
||||
|
||||
You can use a role to grant regular users, who are not superusers nor Admins, the right to view the Admin interface. This can be useful in scenarios where you have a team who needs to be able to create certain objects (flows, other users, etc) but who should not have _full_ access to the Admin interface.
|
||||
|
||||
To assign the `Can view Admin interface` permission to a role:
|
||||
To assign the `Can access Admin interface` permission to a role:
|
||||
|
||||
1. Go to the Admin interface and navigate to **Directory > Role**.
|
||||
2. Select a specific role by clicking on the role's name.
|
||||
|
||||
@@ -32,7 +32,7 @@ Additionally, authentik employs _initial permissions_ to streamline the process
|
||||
|
||||
### Global permissions
|
||||
|
||||
Global permissions define coarse-grained access control. For example, a role with a global permission of "Can change Flow" can change any [flow](../../add-secure-apps/flows-stages/flow/index.md). Some permissions only make sense as global permissions, e.g. the permission to add a specific object type or whether a user [`Can view admin interface`](./manage_permissions.md#assign-can-view-admin-interface-permissions).
|
||||
Global permissions define coarse-grained access control. For example, a role with a global permission of "Can change Flow" can change any [flow](../../add-secure-apps/flows-stages/flow/index.md). Some permissions only make sense as global permissions, e.g. the permission to add a specific object type or whether a user [`Can access admin interface`](./manage_permissions.md#assign-can-access-admin-interface-permissions).
|
||||
|
||||
### Object permissions
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
---
|
||||
title: Github
|
||||
title: GitHub
|
||||
tags:
|
||||
- source
|
||||
- github
|
||||
---
|
||||
|
||||
Allows users to authenticate using their Github credentials by configuring GitHub as a federated identity provider via OAuth2.
|
||||
Allows users to authenticate using their GitHub credentials by configuring GitHub as a federated identity provider via OAuth2.
|
||||
|
||||
## Preparation
|
||||
|
||||
@@ -14,9 +14,9 @@ The following placeholders are used in this guide:
|
||||
- `authentik.company` is the FQDN of the authentik installation.
|
||||
- `www.my.company` is the Homepage URL for your site
|
||||
|
||||
## Github configuration
|
||||
## GitHub configuration
|
||||
|
||||
To integrate GitHub with authentik you will need to create an OAuth application in GitHub Developer Settings.
|
||||
To integrate GitHub with authentik, you need to create an OAuth application in GitHub Developer Settings.
|
||||
|
||||
1. Log in to GitHub and open the [Developer Settings](https://github.com/settings/developers) menu.
|
||||
2. Create an OAuth app by clicking on the **Register a new application** button and set the following values:
|
||||
@@ -70,7 +70,7 @@ from authentik.sources.oauth.models import OAuthSource
|
||||
# Set this value
|
||||
accepted_org = "your_organization"
|
||||
|
||||
# Ensure flow is only run during oauth logins via Github
|
||||
# Ensure flow is only run during OAuth logins via GitHub
|
||||
if not isinstance(context['source'], OAuthSource) or context["source"].provider_type != "github":
|
||||
return True
|
||||
|
||||
@@ -81,7 +81,7 @@ access_token = connection.access_token
|
||||
# We also access the user info authentik already retrieved, to get the correct username
|
||||
github_username = context["oauth_userinfo"]
|
||||
|
||||
# Github does not include Organizations in the userinfo endpoint, so we have to call another URL
|
||||
# GitHub does not include organizations in the userinfo endpoint, so we have to call another URL
|
||||
orgs_response = requests.get(
|
||||
"https://api.github.com/user/orgs",
|
||||
auth=(github_username["login"], access_token),
|
||||
|
||||
@@ -141,7 +141,7 @@ ChatGPT only enables the **Manage SSO** wizard after you verify ownership of you
|
||||
|
||||
To verify that authentik is correctly integrated with ChatGPT, log out, then attempt to log back in by entering your email address and clicking **Continue**. You should be redirected to authentik and upon a successful login, redirected back to ChatGPT.
|
||||
|
||||
## References
|
||||
## Resources
|
||||
|
||||
- [OpenAI Help - Configuring SSO for ChatGPT](https://help.openai.com/en/articles/9534785-configuring-sso-for-chatgpt)
|
||||
- [OpenAI Help - SSO for ChatGPT Business - FAQ](https://help.openai.com/en/articles/11489188-sso-for-chatgpt-business-faq)
|
||||
|
||||
@@ -115,6 +115,6 @@ LOCAL_AUTH_ENABLED="false"
|
||||
|
||||
To confirm that authentik is properly configured with Joplin Server, log out of Joplin and then attempt to sign in again. The login page should redirect you to authentik; after a successful authentik login you should be returned to Joplin with access to your notes.
|
||||
|
||||
## References
|
||||
## Resources
|
||||
|
||||
- [Joplin Server – SAML configuration](https://joplinapp.org/help/apps/server/saml/)
|
||||
|
||||
@@ -68,7 +68,7 @@ OIDC_SECURITY_ASSUME_EMAIL_IS_VERIFIED=true
|
||||
|
||||
Restart mastodon-web.service
|
||||
|
||||
## Additional Resources
|
||||
## Resources
|
||||
|
||||
- https://github.com/mastodon/mastodon/pull/16221
|
||||
- https://forum.fedimins.net/t/sso-fuer-verschiedene-dienste/42
|
||||
|
||||
@@ -68,6 +68,6 @@ config :ueberauth, Ueberauth.Strategy.Keycloak.OAuth,
|
||||
|
||||
Restart mobilizon.service
|
||||
|
||||
## Additional Resources
|
||||
## Resources
|
||||
|
||||
- https://docs.mobilizon.org/3.%20System%20administration/configure/auth/#oauth
|
||||
|
||||
@@ -126,7 +126,7 @@ With this setup, Dovecot can also be used with other email clients that support
|
||||
|
||||
To verify that authentik is correctly integrated with Roundcube, first log out of Roundcube. Log in to roundcube using authentik credentials. A mailbox should open and you should be able to send and receive mail.
|
||||
|
||||
## References
|
||||
## Resources
|
||||
|
||||
- [Roundcube documentation - Configuration: OAuth2](https://github.com/roundcube/roundcubemail/wiki/Configuration:-OAuth2)
|
||||
- [Dovecot documentation - Open Authentication v2.0 Database](https://doc.dovecot.org/main/core/config/auth/databases/oauth2.html)
|
||||
|
||||
@@ -99,6 +99,6 @@ If your usernames in authentik and WriteFreely are different, you might need to
|
||||
|
||||
To link the accounts, first log into Writefreely with local credentials, and then navigate to **Customize -->Account Settings**. In the option "Linked Accounts", click on "authentik".
|
||||
|
||||
## Additional Resources
|
||||
## Resources
|
||||
|
||||
- https://writefreely.org/docs/latest/admin/config
|
||||
|
||||
@@ -82,7 +82,7 @@ The certificate file name must match the idp identifier name you set in the conf
|
||||
Remember to restart Zulip.
|
||||
:::
|
||||
|
||||
## Additional Resources
|
||||
## Resources
|
||||
|
||||
Please refer to the following for further information:
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ To support the integration of DigitalOcean with authentik, you need to create a
|
||||
3. Click **Edit**, expand **UI Settings**, and set **Launch URL** to the **SSO sign-in URL** copied from the DigitalOcean control panel.
|
||||
4. Click **Update**.
|
||||
|
||||
## References
|
||||
## Resources
|
||||
|
||||
- [DigitalOcean Documentation - How to Configure Single Sign-On for Teams](https://docs.digitalocean.com/platform/teams/how-to/configure-sso/)
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ To verify that authentik is properly integrated with OVHcloud, first log out of
|
||||
|
||||
You’ll be redirected to your authentik instance to complete authentication. Once successful, you’ll be logged in to OVHcloud.
|
||||
|
||||
## References
|
||||
## Resources
|
||||
|
||||
- [OVHcloud Help Center - User management & Federation](https://help.ovhcloud.com/csm/en-ie-documentation-manage-operate-user-federation?id=kb_browse_cat&kb_id=3d4a8129a884a950f07829d7d5c75243&kb_category=21734cbe50d47d90476b12dfd60b3542&spa=1)
|
||||
- [OVHcloud US Help Center - User management & Federation](https://support.us.ovhcloud.com/hc/en-us/sections/27230986868883-Federation)
|
||||
|
||||
@@ -153,7 +153,7 @@ Configure Snipe-IT SAML settings by going to settings (the gear icon), and selec
|
||||
|
||||
All other field can be left blank.
|
||||
|
||||
## Additional Resources
|
||||
## Resources
|
||||
|
||||
- https://snipe-it.readme.io/docs/ldap-sync-login
|
||||
- https://snipe-it.readme.io/docs/saml
|
||||
|
||||
@@ -76,7 +76,7 @@ OIDC_SCOPES="openid email profile"
|
||||
|
||||
Then restart Arcane to apply the changes.
|
||||
|
||||
## References
|
||||
## Resources
|
||||
|
||||
- [Arcane Docs - OIDC Single Sign-On](https://getarcane.app/docs/configuration/sso)
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ Please note that by default, sssd returns all user accounts; active and disabled
|
||||
|
||||
:::
|
||||
|
||||
## Additional Resources
|
||||
## Resources
|
||||
|
||||
The setup of sssd might vary based on Linux distribution and version; here are some resources that can help you get this set up:
|
||||
|
||||
|
||||
@@ -85,6 +85,6 @@ To support the integration of TrueCommand with authentik, you need to create an
|
||||
- SAML Identity Provider URL: `Paste the Metadata URL from your clipboard.`
|
||||
- Click _Save_, then click _Configure_ again then select _Start the SAML service_, then click _Save_ to start the service.
|
||||
|
||||
## Additional Resources
|
||||
## Resources
|
||||
|
||||
- https://www.truenas.com/docs/truecommand/administration/settings/samlad/
|
||||
|
||||
@@ -4,6 +4,9 @@ sidebar_label: Zammad
|
||||
support_level: community
|
||||
---
|
||||
|
||||
import TabItem from "@theme/TabItem";
|
||||
import Tabs from "@theme/Tabs";
|
||||
|
||||
## What is Zammad
|
||||
|
||||
> Zammad is a web-based, open source user support/ticketing solution.
|
||||
@@ -22,6 +25,18 @@ The following placeholders are used in this guide:
|
||||
This documentation lists only the settings that you need to change from their default values. Be aware that any changes other than those explicitly mentioned in this guide could cause issues accessing your application.
|
||||
:::
|
||||
|
||||
## Configuration methods
|
||||
|
||||
There are two ways to configure single sign-on for Zammad; SAML or OIDC.
|
||||
|
||||
<Tabs
|
||||
defaultValue="saml"
|
||||
values={[
|
||||
{ label: "Log in with SAML", value: "saml" },
|
||||
{ label: "Log in with OIDC", value: "oidc" },
|
||||
]}>
|
||||
<TabItem value="saml">
|
||||
|
||||
## authentik configuration
|
||||
|
||||
To support the integration of Zammad with authentik, you need to create an application/provider pair in authentik.
|
||||
@@ -30,19 +45,18 @@ To support the integration of Zammad with authentik, you need to create an appli
|
||||
|
||||
1. Log in to authentik as an administrator and open the authentik Admin interface.
|
||||
2. Navigate to **Applications** > **Applications** and click **Create with Provider** to create an application and provider pair. (Alternatively you can first create a provider separately, then create the application and connect it with the provider.)
|
||||
|
||||
- **Application**: provide a descriptive name, an optional group for the type of application, the policy engine mode, and optional UI settings. Take note of the **slug** as it will be required later.
|
||||
- **Choose a Provider type**: select **SAML Provider** as the provider type.
|
||||
- **Configure the Provider**: provide a name (or accept the auto-provided name), the authorization flow to use for this provider, and the following required configurations.
|
||||
- Set the **ACS URL** `bd>https://zammad.company/auth/saml/callback`.
|
||||
- Set the **Issuer** to `https://zammad.company/auth/saml/metadata`.
|
||||
- Set the **Audience** to `https://zammad.company/auth/saml/metadata`.
|
||||
- Set the **Service Provider Binding** to `Post`.
|
||||
- Set the **SLS URL** to `https://zammad.company/auth/saml/slo`.
|
||||
- Set the **SLS Binding** to `Redirect`.
|
||||
- Set the **Logout Method** to `Front-channel (Iframe)`.
|
||||
- Under **Advanced protocol settings**, select an available **Signing certificate**.
|
||||
- **Configure Bindings** _(optional)_: you can create a [binding](/docs/add-secure-apps/bindings-overview/) (policy, group, or user) to manage the listing and access to applications on a user's **My applications** page.
|
||||
- **Application**: provide a descriptive name, an optional group for the type of application, the policy engine mode, and optional UI settings. Take note of the **slug** as it will be required later.
|
||||
- **Choose a Provider type**: select **SAML Provider** as the provider type.
|
||||
- **Configure the Provider**: provide a name (or accept the auto-provided name), the authorization flow to use for this provider, and the following required configurations.
|
||||
- Set the **ACS URL** to `https://zammad.company/auth/saml/callback`.
|
||||
- Set the **Issuer** to `https://zammad.company/auth/saml/metadata`.
|
||||
- Set the **Audience** to `https://zammad.company/auth/saml/metadata`.
|
||||
- Set the **Service Provider Binding** to `Post`.
|
||||
- Set the **SLS URL** to `https://zammad.company/auth/saml/slo`.
|
||||
- Set the **SLS Binding** to `Redirect`.
|
||||
- Set the **Logout Method** to `Front-channel (Iframe)`.
|
||||
- Under **Advanced protocol settings**, select an available **Signing certificate**.
|
||||
- **Configure Bindings** _(optional)_: you can create a [binding](/docs/add-secure-apps/bindings-overview/) (policy, group, or user) to manage the listing and access to applications on a user's **My applications** page.
|
||||
|
||||
3. Click **Submit** to save the new application and provider.
|
||||
|
||||
@@ -56,16 +70,68 @@ To support the integration of Zammad with authentik, you need to create an appli
|
||||
|
||||
To configure Zammad's integration with authentik, go to **Settings** (the gear icon) and select **Security** > **Third-party Applications**. Next, activate the **Authentication via SAML** toggle and change the following fields:
|
||||
|
||||
1. Set the following fields:
|
||||
- **Display name**: authentik
|
||||
- **IDP SSO target URL**: `https://authentik.company/application/saml/<application_slug>/sso/binding/post/`
|
||||
- **IDP SSO target URL**: `https://authentik.company/application/saml/<application_slug>/sso/binding/redirect/`
|
||||
- **IDP single logout target URL**: `https://authentik.company/application/saml/<application_slug>/slo/binding/redirect/`
|
||||
- **IDP Certificate**: paste the contents of your certificate file.
|
||||
- **IDP certificate fingerprint**: Leave this empty.
|
||||
- **Name Identifier Format**: `urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress`
|
||||
- **Automatic account link on initial logon**: Enable this to automatically create Zammad users when they sign in using authentik for the first time.
|
||||
2. Click **Submit** to save the authentication settings.
|
||||
|
||||
- **IDP Certificate**: paste the contents of your certificate file.
|
||||
- **IDP certificate fingerprint**: Leave this empty.
|
||||
- **Name Identifier Format**: `urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress`
|
||||
- **Automatic account link on initial logon**: Enable this to automatically create Zammad users when they sign in using authentik for the first time.
|
||||
:::info
|
||||
The **SSL verification** can fail when Zammad tries to connect to authentik directly, while accessing authentik in your browser works perfectly fine. You may have to disable the verification in order to save the configuration. See https://github.com/zammad/zammad/issues/5225 for details.
|
||||
:::
|
||||
|
||||
## Additional Resources
|
||||
</TabItem>
|
||||
|
||||
- https://admin-docs.zammad.org/en/latest/settings/security/third-party/saml.html
|
||||
- https://community.zammad.org/t/saml-authentication-with-authentik-saml-login-url-and-auto-assign-permission/10876/3
|
||||
<TabItem value="oidc">
|
||||
|
||||
## authentik configuration
|
||||
|
||||
To support the integration of Zammad with authentik, you need to create an application/provider pair in authentik.
|
||||
|
||||
### Create an application and provider in authentik
|
||||
|
||||
1. Log in to authentik as an administrator and open the authentik Admin interface.
|
||||
2. Navigate to **Applications** > **Applications** and click **Create with Provider** to create an application and provider pair. (Alternatively you can first create a provider separately, then create the application and connect it with the provider.)
|
||||
- **Application**: provide a descriptive name, an optional group for the type of application, the policy engine mode, and optional UI settings.
|
||||
- **Choose a Provider type**: select **OAuth2/OpenID Connect** as the provider type.
|
||||
- **Configure the Provider**: provide a name (or accept the auto-provided name), the authorization flow to use for this provider, and the following required configurations.
|
||||
- Set the **Client type** to `Public`.
|
||||
- Take note of the **Client ID** and **slug** values because they will be required later.
|
||||
- Set the **Redirect URIs/Origins** to `Strict` / `https://zammad.company/auth/openid_connect/callback`.
|
||||
- Select a **Signing Key**.
|
||||
- Under **Advanced protocol settings**, set **Subject mode** to **Based on the User's Email**.
|
||||
- **Configure Bindings** _(optional)_: you can create a [binding](/docs/add-secure-apps/bindings-overview/) (policy, group, or user) to manage the listing and access to applications on a user's **My applications** page.
|
||||
|
||||
3. Click **Submit** to save the new application and provider.
|
||||
|
||||
## Zammad configuration
|
||||
|
||||
To configure Zammad's integration with authentik, go to **Settings** (the gear icon) and select **Security** > **Third-party Applications**. Next, activate the **Authentication via OpenID Connect** toggle and change the following fields:
|
||||
|
||||
1. Set the following fields:
|
||||
- **Display name**: authentik
|
||||
- **Identifier**: the **Client ID** from above.
|
||||
- **Issuer**: `https://authentik.company/application/o/<application_slug>/`
|
||||
- **PKCE**: set to **yes**.
|
||||
|
||||
2. Click **Submit** to save the authentication settings.
|
||||
|
||||
At the very top of the **Third-party Applications** page are a few additional settings:
|
||||
|
||||
- **Automatic account link on initial logon**: Enable this to automatically link existing Zammad users when they sign in using authentik for the first time.
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Configuration verification
|
||||
|
||||
To verify that authentik is correctly integrated with Zammad, log out of Zammad and then log back in by clicking the SAML or OIDC button on the login screen. The button will show the **Display Name** you specified above. You should be redirected to authentik to log in, and if the process is successful, you'll be logged in to the Zammad dashboard.
|
||||
|
||||
## Resources
|
||||
|
||||
- [Zammad Admin Documentation - SAML](https://admin-docs.zammad.org/en/latest/settings/security/third-party/saml.html)
|
||||
- [Zammad Admin Documentation - OpenID Connect](https://admin-docs.zammad.org/en/latest/settings/security/third-party/openid-connect.html)
|
||||
|
||||
@@ -69,6 +69,6 @@ To bypass SSO for troubleshooting, navigate to `https://audiobookshelf.company/l
|
||||
|
||||
To confirm that authentik is properly configured with Audiobookshelf, log out and attempt to log back in using OpenID Connect. You should be redirected to authentik for authentication and then redirected back to Audiobookshelf.
|
||||
|
||||
## References
|
||||
## Resources
|
||||
|
||||
- [Audiobookshelf OIDC Authentication documentation](https://www.audiobookshelf.org/guides/oidc_authentication/)
|
||||
|
||||
@@ -101,6 +101,6 @@ If you're already signed in, go to **User Settings** > **Security** and click **
|
||||
|
||||
To confirm that authentik is properly configured with ezBookkeeping, log out of ezBookkeeping, click **Log in with authentik**, and complete the authentik sign-in flow. A successful authentication should return you to ezBookkeeping with access to your account.
|
||||
|
||||
## References
|
||||
## Resources
|
||||
|
||||
- [ezBookkeeping Documentation - Configuration](https://ezbookkeeping.mayswind.net/configuration#authentication)
|
||||
|
||||
@@ -80,6 +80,6 @@ Click on the user whose role should be increased from **Pending** to at least **
|
||||
More details on how to administer Open WebUI can be found here: `https://docs.openwebui.com/`.
|
||||
:::
|
||||
|
||||
## References
|
||||
## Resources
|
||||
|
||||
- [Open WebUI Documentation - Federated Authentication Support](https://docs.openwebui.com/features/sso/)
|
||||
|
||||
@@ -56,11 +56,11 @@ OIDC_CLIENT_SECRET=<Your Client Secret from authentik>
|
||||
```yaml showLineNumbers title="config.yaml"
|
||||
security:
|
||||
oidc:
|
||||
issuer-url: https://authentik.company/application/o/<application_slug>/
|
||||
client-id: $\{OIDC_CLIENT_ID}
|
||||
client-secret: $\{OIDC_CLIENT_SECRET}
|
||||
redirect-url: https://gatus.company/authorization-code/callback
|
||||
scopes: [openid]
|
||||
issuer-url: "https://authentik.company/application/o/<application_slug>/"
|
||||
client-id: "OIDC_CLIENT_ID"
|
||||
client-secret: "OIDC_CLIENT_SECRET"
|
||||
redirect-url: "https://gatus.company/authorization-code/callback"
|
||||
scopes: ["openid"]
|
||||
```
|
||||
|
||||
## Configuration verification
|
||||
|
||||
@@ -67,6 +67,6 @@ To hide the local login form and show only SSO, set `PULSE_AUTH_HIDE_LOCAL_LOGIN
|
||||
|
||||
To confirm that authentik is properly configured with Pulse, log out and attempt to log back in using Single Sign-On. You should be redirected to authentik for authentication and then redirected back to Pulse.
|
||||
|
||||
## References
|
||||
## Resources
|
||||
|
||||
- [Pulse OIDC Single Sign-On documentation](https://github.com/rcourtman/Pulse/blob/main/docs/OIDC.md)
|
||||
|
||||
@@ -58,7 +58,7 @@ To support the integration of Wazuh with authentik, you need to create a group,
|
||||
- **Application**: provide a descriptive name (e.g., `Wazuh`), an optional group for the type of application, the policy engine mode, and optional UI settings.
|
||||
- **Choose a Provider type**: Select **SAML Provider** as the provider type.
|
||||
- **Configure the Provider**: provide a name (or accept the auto-provided name), the authorization flow to use for this provider, and the following required configurations.
|
||||
- **ACS URL**: `https://wazuh-dashboard.company/\_opendistro/\_security/saml/acs`
|
||||
- **ACS URL**: `https://wazuh-dashboard.company/_opendistro/_security/saml/acs`
|
||||
- **Issuer**: `wazuh-saml`
|
||||
- **Service Provider Binding**: `Post`
|
||||
- Under **Advanced protocol settings**:
|
||||
|
||||
@@ -139,7 +139,7 @@ If you encounter any issues:
|
||||
- Check the FortiGate logs for SAML-related errors
|
||||
:::
|
||||
|
||||
## Additional Resources
|
||||
## Resources
|
||||
|
||||
- [FortiGate SSLVPN Documentation](https://docs.fortinet.com/document/fortigate/7.2.8/administration-guide/397719/ssl-vpn)
|
||||
- [FortiGate SAML Configuration Guide](https://docs.fortinet.com/document/fortigate/7.2.8/administration-guide/954635/saml-sp)
|
||||
|
||||
@@ -66,7 +66,7 @@ If you are developing Drupal locally with DDEV and authentik is also running loc
|
||||
|
||||
TODO
|
||||
|
||||
## Additional Resources
|
||||
## Resources
|
||||
|
||||
- [Drupal OpenID Connect Module Documentation](https://www.drupal.org/project/openid_connect)
|
||||
- [Drupal User Account Settings Documentation](https://www.drupal.org/docs/user_guide/en/user-registration.html)
|
||||
|
||||
@@ -184,7 +184,7 @@ New-MgDomainFederationConfiguration `
|
||||
|
||||
To confirm that authentik is properly configured with Microsoft365, log out of your Microsoft account, then attempt to log back in by visiting [Microsoft 365 Portal](https://m365.cloud.microsoft/) and clicking **Sign In**. Enter an email address in your federated domain, then click **Next**. You should be redirected to authentik and, once authenticated, redirected back to Microsoft and logged in.
|
||||
|
||||
## References
|
||||
## Resources
|
||||
|
||||
- [Microsoft Learn - Use a SAML 2.0 Identity Provider for Single Sign On](https://learn.microsoft.com/en-us/entra/identity/hybrid/connect/how-to-connect-fed-saml-idp)
|
||||
- [Microsoft Graph PowerShell - Domain Federation Configuration](https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.identity.directorymanagement/new-mgdomainfederationconfiguration)
|
||||
|
||||
@@ -252,7 +252,7 @@ Salesforce requires specific SCIM attributes that are not included in the defaul
|
||||
4. In the **Backchannel Providers** field, select the SCIM provider you created.
|
||||
5. Click **Update** to save the application.
|
||||
|
||||
## References
|
||||
## Resources
|
||||
|
||||
- [Salesforce Help - Configure SSO with Salesforce as a SAML Service Provider](https://help.salesforce.com/s/articleView?id=sf.sso_saml.htm&type=5)
|
||||
- [Salesforce Help - Just-in-Time SAML Assertion Fields for Salesforce](https://help.salesforce.com/s/articleView?id=sf.sso_jit_requirements.htm&type=5)
|
||||
|
||||
@@ -72,7 +72,7 @@ SSO_ENABLED=true
|
||||
SSO_AUTHORITY=https://authentik.company/application/o/<application_slug>/
|
||||
SSO_CLIENT_ID=<client_id>
|
||||
SSO_CLIENT_SECRET=<client_secret>
|
||||
SSO_SCOPES="openid email profile offline_access"
|
||||
SSO_SCOPES=email profile offline_access
|
||||
SSO_ALLOW_UNKNOWN_EMAIL_VERIFICATION=false
|
||||
SSO_CLIENT_CACHE_EXPIRATION=0
|
||||
SSO_ONLY=false # Set to true to disable email+master password login and require SSO
|
||||
@@ -81,7 +81,7 @@ SSO_SIGNUPS_MATCH_EMAIL=true # Match first SSO login to existing account by emai
|
||||
|
||||
Then restart Vaultwarden to apply the changes.
|
||||
|
||||
## References
|
||||
## Resources
|
||||
|
||||
- [Vaultwarden Wiki - SSO using OpenID Connect](https://github.com/dani-garcia/vaultwarden/wiki/Enabling-SSO-support-using-OpenId-Connect)
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ Template sentence that you can typically use here: "To confirm that authentik is
|
||||
|
||||
If there are more specific validation methods for the Service (e.g., clicking a button), include these instructions for clarity.
|
||||
|
||||
## References
|
||||
## Resources
|
||||
|
||||
List the external sources (official docs, community articles, blogs, videos) that were used to create this guide.
|
||||
|
||||
|
||||
160
website/package-lock.json
generated
160
website/package-lock.json
generated
@@ -17,8 +17,8 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@eslint/js": "^9.39.1",
|
||||
"@goauthentik/eslint-config": "^1.1.1",
|
||||
"@goauthentik/prettier-config": "^3.2.1",
|
||||
"@goauthentik/eslint-config": "^1.2.1",
|
||||
"@goauthentik/prettier-config": "^3.4.0",
|
||||
"@goauthentik/tsconfig": "^1.0.5",
|
||||
"@types/node": "^25.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.48.0",
|
||||
@@ -29,8 +29,8 @@
|
||||
"netlify-redirect-parser": "^14.4.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"postman-code-generators": "2.1.0",
|
||||
"prettier": "^3.7.3",
|
||||
"prettier-plugin-packagejson": "^2.5.20",
|
||||
"prettier": "^3.8.1",
|
||||
"prettier-plugin-packagejson": "^3.0.0",
|
||||
"typescript-eslint": "^8.48.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -138,7 +138,6 @@
|
||||
"dependencies": {
|
||||
"@docusaurus/preset-classic": "^3.9.2",
|
||||
"@goauthentik/docusaurus-config": "^2.2.2",
|
||||
"@iconify-json/logos": "^1.2.9",
|
||||
"@types/semver": "^7.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"fast-glob": "^3.3.3",
|
||||
@@ -382,7 +381,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.46.0.tgz",
|
||||
"integrity": "sha512-22SHEEVNjZfFWkFks3P6HilkR3rS7a6GjnCIqR22Zz4HNxdfT0FG+RE7efTcFVfLUkTTMQQybvaUcwMrHXYa7Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@algolia/client-common": "5.46.0",
|
||||
"@algolia/requester-browser-xhr": "5.46.0",
|
||||
@@ -538,7 +536,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz",
|
||||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.5",
|
||||
@@ -2373,7 +2370,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
@@ -2396,7 +2392,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -2506,7 +2501,6 @@
|
||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz",
|
||||
"integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"cssesc": "^3.0.0",
|
||||
"util-deprecate": "^1.0.2"
|
||||
@@ -2928,7 +2922,6 @@
|
||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz",
|
||||
"integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"cssesc": "^3.0.0",
|
||||
"util-deprecate": "^1.0.2"
|
||||
@@ -3682,7 +3675,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.9.2.tgz",
|
||||
"integrity": "sha512-HbjwKeC+pHUFBfLMNzuSjqFE/58+rLVKmOU3lxQrpsxLBOGosYco/Q0GduBb0/jEMRiyEqjNT/01rRdOMWq5pw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@docusaurus/babel": "3.9.2",
|
||||
"@docusaurus/bundler": "3.9.2",
|
||||
@@ -4002,7 +3994,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.9.2.tgz",
|
||||
"integrity": "sha512-C5wZsGuKTY8jEYsqdxhhFOe1ZDjH0uIYJ9T/jebHwkyxqnr4wW0jTkB72OMqNjsoQRcb0JN3PcSeTwFlVgzCZg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.9.2",
|
||||
"@docusaurus/logger": "3.9.2",
|
||||
@@ -4271,7 +4262,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.9.2.tgz",
|
||||
"integrity": "sha512-6c4DAbR6n6nPbnZhY2V3tzpnKnGL+6aOsLvFL26VRqhlczli9eWG0VDUNoCQEPnGwDMhPS42UhSAnz5pThm5Ag==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@docusaurus/mdx-loader": "3.9.2",
|
||||
"@docusaurus/module-type-aliases": "3.9.2",
|
||||
@@ -4414,7 +4404,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.9.2.tgz",
|
||||
"integrity": "sha512-lBSBiRruFurFKXr5Hbsl2thmGweAPmddhF3jb99U4EMDA5L+e5Y1rAkOS07Nvrup7HUMBDrCV45meaxZnt28nQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@docusaurus/logger": "3.9.2",
|
||||
"@docusaurus/types": "3.9.2",
|
||||
@@ -4460,7 +4449,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.9.2.tgz",
|
||||
"integrity": "sha512-l7yk3X5VnNmATbwijJkexdhulNsQaNDwoagiwujXoxFbWLcxHQqNQ+c/IAlzrfMMOfa/8xSBZ7KEKDesE/2J7A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@docusaurus/logger": "3.9.2",
|
||||
"@docusaurus/utils": "3.9.2",
|
||||
@@ -4801,9 +4789,9 @@
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@goauthentik/eslint-config": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/eslint-config/-/eslint-config-1.1.1.tgz",
|
||||
"integrity": "sha512-IOCQjBvD2FeUD0m1eAVhLTYxPM5pKA7UBEbub2QQYJAm7Ny8gNIA1jVoYRomYBRTundWJa5Y6iy1zN52r6Qofw==",
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/eslint-config/-/eslint-config-1.2.1.tgz",
|
||||
"integrity": "sha512-Il47UJolIPG2j671iV64QcX5DCScorm70m0rjr7wsGcSDvc/CflD04fHKwXH4Ud+Hs5khJD4LQp2usTZT/Bi/g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"eslint": "^9.39.1",
|
||||
@@ -4815,13 +4803,13 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=24",
|
||||
"npm": ">=11.6.2"
|
||||
"npm": ">=11.10.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.47.0"
|
||||
"typescript-eslint": "^8.49.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react": {
|
||||
@@ -4837,20 +4825,20 @@
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@goauthentik/prettier-config": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/prettier-config/-/prettier-config-3.2.1.tgz",
|
||||
"integrity": "sha512-Cq/z0s0LRFaDVDaNvh8cZzMJ8RHE3YG+Dwi3maLA6OJkLMg/hSQF8AXneLlwo/eF4S5TORbrd0gl7l8xiUqrcQ==",
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/prettier-config/-/prettier-config-3.4.0.tgz",
|
||||
"integrity": "sha512-00SnyvdfHhoifuqlQlS+Beyl0aFUUBWShI4Ci+QxFS1pkiyl1HnHbr20QRSR7DPqPmRMVAYmsr1Yv6/1heNhIg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"format-imports": "^4.0.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=24",
|
||||
"npm": ">=11.6.2"
|
||||
"npm": ">=11.10.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prettier": "^3.6.2",
|
||||
"prettier-plugin-packagejson": "^2.5.19"
|
||||
"prettier": "^3.8.1",
|
||||
"prettier-plugin-packagejson": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@goauthentik/tsconfig": {
|
||||
@@ -4970,15 +4958,6 @@
|
||||
"@iconify/types": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@iconify-json/logos": {
|
||||
"version": "1.2.10",
|
||||
"resolved": "https://registry.npmjs.org/@iconify-json/logos/-/logos-1.2.10.tgz",
|
||||
"integrity": "sha512-qxaXKJ6fu8jzTMPQdHtNxlfx6tBQ0jXRbHZIYy5Ilh8Lx9US9FsAdzZWUR8MXV8PnWTKGDFO4ZZee9VwerCyMA==",
|
||||
"license": "CC0-1.0",
|
||||
"dependencies": {
|
||||
"@iconify/types": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@iconify-json/mdi": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@iconify-json/mdi/-/mdi-1.2.3.tgz",
|
||||
@@ -5726,18 +5705,6 @@
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@pkgr/core": {
|
||||
"version": "0.2.9",
|
||||
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz",
|
||||
"integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/pkgr"
|
||||
}
|
||||
},
|
||||
"node_modules/@pnpm/config.env-replace": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz",
|
||||
@@ -6038,7 +6005,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@rspack/core/-/core-1.6.7.tgz",
|
||||
"integrity": "sha512-tkd4nSzTf+pDa9OAE4INi/JEa93HNszjWy5C9+trf4ZCXLLHsHxHQFbzoreuz4Vv2PlCWajgvAdiPMV1vGIkuw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@module-federation/runtime-tools": "0.21.6",
|
||||
"@rspack/binding": "1.6.7",
|
||||
@@ -6283,7 +6249,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz",
|
||||
"integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.21.3",
|
||||
"@svgr/babel-preset": "8.1.0",
|
||||
@@ -6388,7 +6353,6 @@
|
||||
"integrity": "sha512-Qd8eBPkUFL4eAONgGjycZXj1jFCBW8Fd+xF0PzdTlBCWQIV1xnUT7B93wUANtW3KGjl3TRcOyxwSx/u/jyKw/Q==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@swc/counter": "^0.1.3",
|
||||
"@swc/types": "^0.1.25"
|
||||
@@ -7361,7 +7325,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
|
||||
"integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.2.2"
|
||||
}
|
||||
@@ -7558,7 +7521,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.49.0.tgz",
|
||||
"integrity": "sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.49.0",
|
||||
"@typescript-eslint/types": "8.49.0",
|
||||
@@ -8038,7 +8000,6 @@
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -8133,7 +8094,6 @@
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
@@ -8198,7 +8158,6 @@
|
||||
"resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.46.0.tgz",
|
||||
"integrity": "sha512-7ML6fa2K93FIfifG3GMWhDEwT5qQzPTmoHKCTvhzGEwdbQ4n0yYUWZlLYT75WllTGJCJtNUI0C1ybN4BCegqvg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@algolia/abtesting": "1.12.0",
|
||||
"@algolia/client-abtesting": "5.46.0",
|
||||
@@ -8899,7 +8858,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.9.0",
|
||||
"caniuse-lite": "^1.0.30001759",
|
||||
@@ -9287,7 +9245,6 @@
|
||||
"resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz",
|
||||
"integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@chevrotain/cst-dts-gen": "11.0.3",
|
||||
"@chevrotain/gast": "11.0.3",
|
||||
@@ -10121,7 +10078,6 @@
|
||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz",
|
||||
"integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"cssesc": "^3.0.0",
|
||||
"util-deprecate": "^1.0.2"
|
||||
@@ -10441,7 +10397,6 @@
|
||||
"resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz",
|
||||
"integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
@@ -10851,7 +10806,6 @@
|
||||
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
|
||||
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -11389,7 +11343,6 @@
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
|
||||
"integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
@@ -12625,7 +12578,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz",
|
||||
"integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.15.4",
|
||||
"@types/react-redux": "^7.1.20",
|
||||
@@ -13298,7 +13250,6 @@
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz",
|
||||
"integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
@@ -14699,9 +14650,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/git-hooks-list": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-4.1.1.tgz",
|
||||
"integrity": "sha512-cmP497iLq54AZnv4YRAEMnEyQ1eIn4tGKbmswqwmFV4GBnAqE8NLtWxxdXa++AalfgL5EBH4IxTPyquEuGY/jA==",
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-4.2.1.tgz",
|
||||
"integrity": "sha512-WNvqJjOxxs/8ZP9+DWdwWJ7cDsd60NHf39XnD82pDVrKO5q7xfPqpkK6hwEAmBa/ZSEE4IOoR75EzbbIuwGlMw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/fisker/git-hooks-list?sponsor=1"
|
||||
@@ -20804,7 +20755,6 @@
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
@@ -21444,7 +21394,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.11",
|
||||
"picocolors": "^1.1.1",
|
||||
@@ -22348,7 +22297,6 @@
|
||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz",
|
||||
"integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"cssesc": "^3.0.0",
|
||||
"util-deprecate": "^1.0.2"
|
||||
@@ -22962,11 +22910,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.7.4",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz",
|
||||
"integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==",
|
||||
"version": "3.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz",
|
||||
"integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
},
|
||||
@@ -22978,17 +22925,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier-plugin-packagejson": {
|
||||
"version": "2.5.20",
|
||||
"resolved": "https://registry.npmjs.org/prettier-plugin-packagejson/-/prettier-plugin-packagejson-2.5.20.tgz",
|
||||
"integrity": "sha512-G8cowPh+QmJJECTZlrPDKWkVVcwrFjF2rGcw546w3N8blLoc4szSs8UUPfFVxHUNLUjiru71Ah83g1lZkeK9Bw==",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/prettier-plugin-packagejson/-/prettier-plugin-packagejson-3.0.0.tgz",
|
||||
"integrity": "sha512-z8/QmPSqx/ANvvQMWJSkSq1+ihBXeuwDEYdjX3ZjRJ5Ty1k7vGbFQfhzk2eDe0rwS/TNyRjWK/qnjJEStAOtDw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"sort-package-json": "3.5.0",
|
||||
"synckit": "0.11.11"
|
||||
"sort-package-json": "3.6.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prettier": ">= 1.16.0"
|
||||
"prettier": "^3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"prettier": {
|
||||
@@ -23274,7 +23219,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.1.tgz",
|
||||
"integrity": "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -23294,7 +23238,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.1.tgz",
|
||||
"integrity": "sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.27.0"
|
||||
},
|
||||
@@ -23331,7 +23274,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.68.0.tgz",
|
||||
"integrity": "sha512-oNN3fjrZ/Xo40SWlHf1yCjlMK417JxoSJVUXQjGdvdRCU07NTFei1i1f8ApUAts+IVh14e4EdakeLEA+BEAs/Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
@@ -23392,7 +23334,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-6.0.0.tgz",
|
||||
"integrity": "sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/react": "*"
|
||||
},
|
||||
@@ -24115,7 +24056,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz",
|
||||
"integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.13",
|
||||
"history": "^4.9.0",
|
||||
@@ -24285,7 +24225,6 @@
|
||||
"resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz",
|
||||
"integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.9.2"
|
||||
}
|
||||
@@ -25015,7 +24954,6 @@
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.96.0.tgz",
|
||||
"integrity": "sha512-8u4xqqUeugGNCYwr9ARNtQKTOj4KmYiJAVKXf2CTIivTCR51j96htbMKWDru8H5SaQWpyVgTfOF8Ylyf5pun1Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"chokidar": "^4.0.0",
|
||||
"immutable": "^5.0.2",
|
||||
@@ -25141,7 +25079,6 @@
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
@@ -25927,24 +25864,24 @@
|
||||
}
|
||||
},
|
||||
"node_modules/sort-object-keys": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-2.0.1.tgz",
|
||||
"integrity": "sha512-R89fO+z3x7hiKPXX5P0qim+ge6Y60AjtlW+QQpRozrrNcR1lw9Pkpm5MLB56HoNvdcLHL4wbpq16OcvGpEDJIg==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-2.1.0.tgz",
|
||||
"integrity": "sha512-SOiEnthkJKPv2L6ec6HMwhUcN0/lppkeYuN1x63PbyPRrgSPIuBJCiYxYyvWRTtjMlOi14vQUCGUJqS6PLVm8g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/sort-package-json": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-3.5.0.tgz",
|
||||
"integrity": "sha512-moY4UtptUuP5sPuu9H9dp8xHNel7eP5/Kz/7+90jTvC0IOiPH2LigtRM/aSFSxreaWoToHUVUpEV4a2tAs2oKQ==",
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-3.6.0.tgz",
|
||||
"integrity": "sha512-fyJsPLhWvY7u2KsKPZn1PixbXp+1m7V8NWqU8CvgFRbMEX41Ffw1kD8n0CfJiGoaSfoAvbrqRRl/DcHO8omQOQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"detect-indent": "^7.0.1",
|
||||
"detect-indent": "^7.0.2",
|
||||
"detect-newline": "^4.0.1",
|
||||
"git-hooks-list": "^4.0.0",
|
||||
"git-hooks-list": "^4.1.1",
|
||||
"is-plain-obj": "^4.1.0",
|
||||
"semver": "^7.7.1",
|
||||
"sort-object-keys": "^2.0.0",
|
||||
"tinyglobby": "^0.2.12"
|
||||
"semver": "^7.7.3",
|
||||
"sort-object-keys": "^2.0.1",
|
||||
"tinyglobby": "^0.2.15"
|
||||
},
|
||||
"bin": {
|
||||
"sort-package-json": "cli.js"
|
||||
@@ -26600,21 +26537,6 @@
|
||||
"react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/synckit": {
|
||||
"version": "0.11.11",
|
||||
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz",
|
||||
"integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@pkgr/core": "^0.2.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/synckit"
|
||||
}
|
||||
},
|
||||
"node_modules/tapable": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
|
||||
@@ -26829,7 +26751,6 @@
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -26995,8 +26916,7 @@
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD",
|
||||
"peer": true
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/type-check": {
|
||||
"version": "0.4.0",
|
||||
@@ -27144,7 +27064,6 @@
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -27158,7 +27077,6 @@
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.49.0.tgz",
|
||||
"integrity": "sha512-zRSVH1WXD0uXczCXw+nsdjGPUdx4dfrs5VQoHnUWmv1U3oNlAKv4FUNdLDhVUg+gYn+a5hUESqch//Rv5wVhrg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "8.49.0",
|
||||
"@typescript-eslint/parser": "8.49.0",
|
||||
@@ -27900,7 +27818,6 @@
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.103.0.tgz",
|
||||
"integrity": "sha512-HU1JOuV1OavsZ+mfigY0j8d1TgQgbZ6M+J75zDkpEAwYeXjWSqrGJtgnPblJjd/mAyTNQ7ygw0MiKOn6etz8yw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/eslint-scope": "^3.7.7",
|
||||
"@types/estree": "^1.0.8",
|
||||
@@ -28739,7 +28656,6 @@
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.1.13.tgz",
|
||||
"integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user