diff --git a/.github/workflows/ci-outpost.yml b/.github/workflows/ci-outpost.yml index 3514efe09..196fa0b3b 100644 --- a/.github/workflows/ci-outpost.yml +++ b/.github/workflows/ci-outpost.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-go@v4 + - uses: actions/setup-go@v5 with: go-version-file: "go.mod" - name: Prepare and generate API @@ -37,7 +37,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-go@v4 + - uses: actions/setup-go@v5 with: go-version-file: "go.mod" - name: Setup authentik env @@ -125,7 +125,7 @@ jobs: - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} - - uses: actions/setup-go@v4 + - uses: actions/setup-go@v5 with: go-version-file: "go.mod" - uses: actions/setup-node@v4 diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index 4d75af0d8..c3c6a0d48 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -67,7 +67,7 @@ jobs: - radius steps: - uses: actions/checkout@v4 - - uses: actions/setup-go@v4 + - uses: actions/setup-go@v5 with: go-version-file: "go.mod" - name: Set up QEMU @@ -126,7 +126,7 @@ jobs: goarch: [amd64, arm64] steps: - uses: actions/checkout@v4 - - uses: actions/setup-go@v4 + - uses: actions/setup-go@v5 with: go-version-file: "go.mod" - uses: actions/setup-node@v4 diff --git a/Dockerfile b/Dockerfile index e715c0931..3b7beb6f0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,7 +35,7 @@ COPY ./gen-ts-api /work/web/node_modules/@goauthentik/api RUN npm run build # Stage 3: Build go proxy -FROM --platform=${BUILDPLATFORM} docker.io/golang:1.21.4-bookworm AS go-builder +FROM --platform=${BUILDPLATFORM} docker.io/golang:1.21.5-bookworm AS go-builder ARG TARGETOS ARG TARGETARCH diff --git a/authentik/core/api/sources.py b/authentik/core/api/sources.py index 292f38cd3..eff2c9211 100644 --- a/authentik/core/api/sources.py +++ b/authentik/core/api/sources.py @@ -38,7 +38,7 @@ class SourceSerializer(ModelSerializer, MetaNameSerializer): managed = ReadOnlyField() component = SerializerMethodField() - icon = ReadOnlyField(source="get_icon") + icon = ReadOnlyField(source="icon_url") def get_component(self, obj: Source) -> str: """Get object component so that we know how to edit the object""" diff --git a/authentik/events/api/events.py b/authentik/events/api/events.py index 25ef5f4b4..758d03e53 100644 --- a/authentik/events/api/events.py +++ b/authentik/events/api/events.py @@ -5,7 +5,7 @@ from json import loads import django_filters from django.db.models.aggregates import Count from django.db.models.fields.json import KeyTextTransform, KeyTransform -from django.db.models.functions import ExtractDay +from django.db.models.functions import ExtractDay, ExtractHour from drf_spectacular.types import OpenApiTypes from drf_spectacular.utils import OpenApiParameter, extend_schema from guardian.shortcuts import get_objects_for_user @@ -149,7 +149,15 @@ class EventViewSet(ModelViewSet): return Response(EventTopPerUserSerializer(instance=events, many=True).data) @extend_schema( - methods=["GET"], + responses={200: CoordinateSerializer(many=True)}, + ) + @action(detail=False, methods=["GET"], pagination_class=None) + def volume(self, request: Request) -> Response: + """Get event volume for specified filters and timeframe""" + queryset = self.filter_queryset(self.get_queryset()) + return Response(queryset.get_events_per(timedelta(days=7), ExtractHour, 7 * 3)) + + @extend_schema( responses={200: CoordinateSerializer(many=True)}, filters=[], parameters=[ diff --git a/authentik/flows/stage.py b/authentik/flows/stage.py index d9fa75893..528a7bef5 100644 --- a/authentik/flows/stage.py +++ b/authentik/flows/stage.py @@ -167,7 +167,11 @@ class ChallengeStageView(StageView): stage_type=self.__class__.__name__, method="get_challenge" ).time(), ): - challenge = self.get_challenge(*args, **kwargs) + try: + challenge = self.get_challenge(*args, **kwargs) + except StageInvalidException as exc: + self.logger.debug("Got StageInvalidException", exc=exc) + return self.executor.stage_invalid() with Hub.current.start_span( op="authentik.flow.stage._get_challenge", description=self.__class__.__name__, diff --git a/authentik/stages/email/stage.py b/authentik/stages/email/stage.py index a9570190a..0fa36bfbe 100644 --- a/authentik/stages/email/stage.py +++ b/authentik/stages/email/stage.py @@ -5,6 +5,7 @@ from uuid import uuid4 from django.contrib import messages from django.http import HttpRequest, HttpResponse from django.http.request import QueryDict +from django.template.exceptions import TemplateSyntaxError from django.urls import reverse from django.utils.text import slugify from django.utils.timezone import now @@ -12,11 +13,14 @@ from django.utils.translation import gettext as _ from rest_framework.fields import CharField from rest_framework.serializers import ValidationError +from authentik.events.models import Event, EventAction from authentik.flows.challenge import Challenge, ChallengeResponse, ChallengeTypes +from authentik.flows.exceptions import StageInvalidException from authentik.flows.models import FlowDesignation, FlowToken from authentik.flows.planner import PLAN_CONTEXT_IS_RESTORED, PLAN_CONTEXT_PENDING_USER from authentik.flows.stage import ChallengeStageView from authentik.flows.views.executor import QS_KEY_TOKEN, QS_QUERY +from authentik.lib.utils.errors import exception_to_string from authentik.stages.email.models import EmailStage from authentik.stages.email.tasks import send_mails from authentik.stages.email.utils import TemplateEmailMessage @@ -103,18 +107,27 @@ class EmailStageView(ChallengeStageView): current_stage: EmailStage = self.executor.current_stage token = self.get_token() # Send mail to user - message = TemplateEmailMessage( - subject=_(current_stage.subject), - to=[email], - language=pending_user.locale(self.request), - template_name=current_stage.template, - template_context={ - "url": self.get_full_url(**{QS_KEY_TOKEN: token.key}), - "user": pending_user, - "expires": token.expires, - }, - ) - send_mails(current_stage, message) + try: + message = TemplateEmailMessage( + subject=_(current_stage.subject), + to=[email], + language=pending_user.locale(self.request), + template_name=current_stage.template, + template_context={ + "url": self.get_full_url(**{QS_KEY_TOKEN: token.key}), + "user": pending_user, + "expires": token.expires, + }, + ) + send_mails(current_stage, message) + except TemplateSyntaxError as exc: + Event.new( + EventAction.CONFIGURATION_ERROR, + message=_("Exception occurred while rendering E-mail template"), + error=exception_to_string(exc), + template=current_stage.template, + ).from_http(self.request) + raise StageInvalidException from exc def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: # Check if the user came back from the email link to verify @@ -135,7 +148,11 @@ class EmailStageView(ChallengeStageView): return self.executor.stage_invalid() # Check if we've already sent the initial e-mail if PLAN_CONTEXT_EMAIL_SENT not in self.executor.plan.context: - self.send_email() + try: + self.send_email() + except StageInvalidException as exc: + self.logger.debug("Got StageInvalidException", exc=exc) + return self.executor.stage_invalid() self.executor.plan.context[PLAN_CONTEXT_EMAIL_SENT] = True return super().get(request, *args, **kwargs) diff --git a/authentik/stages/email/tests/test_templates.py b/authentik/stages/email/tests/test_templates.py index 2bd4d0c5e..f8531b078 100644 --- a/authentik/stages/email/tests/test_templates.py +++ b/authentik/stages/email/tests/test_templates.py @@ -4,11 +4,20 @@ from pathlib import Path from shutil import rmtree from tempfile import mkdtemp, mkstemp from typing import Any +from unittest.mock import PropertyMock, patch from django.conf import settings -from django.test import TestCase +from django.core.mail.backends.locmem import EmailBackend +from django.urls import reverse -from authentik.stages.email.models import get_template_choices +from authentik.core.tests.utils import create_test_admin_user, create_test_flow +from authentik.events.models import Event, EventAction +from authentik.flows.markers import StageMarker +from authentik.flows.models import FlowDesignation, FlowStageBinding +from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan +from authentik.flows.tests import FlowTestCase +from authentik.flows.views.executor import SESSION_KEY_PLAN +from authentik.stages.email.models import EmailStage, get_template_choices def get_templates_setting(temp_dir: str) -> dict[str, Any]: @@ -18,11 +27,18 @@ def get_templates_setting(temp_dir: str) -> dict[str, Any]: return templates_setting -class TestEmailStageTemplates(TestCase): +class TestEmailStageTemplates(FlowTestCase): """Email tests""" def setUp(self) -> None: - self.dir = mkdtemp() + self.dir = Path(mkdtemp()) + self.user = create_test_admin_user() + + self.flow = create_test_flow(FlowDesignation.AUTHENTICATION) + self.stage = EmailStage.objects.create( + name="email", + ) + self.binding = FlowStageBinding.objects.create(target=self.flow, stage=self.stage, order=2) def tearDown(self) -> None: rmtree(self.dir) @@ -38,3 +54,37 @@ class TestEmailStageTemplates(TestCase): self.assertEqual(len(choices), 3) unlink(file) unlink(file2) + + def test_custom_template_invalid_syntax(self): + """Test with custom template""" + with open(self.dir / Path("invalid.html"), "w+", encoding="utf-8") as _invalid: + _invalid.write("{% blocktranslate %}") + with self.settings(TEMPLATES=get_templates_setting(self.dir)): + self.stage.template = "invalid.html" + plan = FlowPlan( + flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()] + ) + plan.context[PLAN_CONTEXT_PENDING_USER] = self.user + session = self.client.session + session[SESSION_KEY_PLAN] = plan + session.save() + + url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}) + with patch( + "authentik.stages.email.models.EmailStage.backend_class", + PropertyMock(return_value=EmailBackend), + ): + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + self.assertStageResponse( + response, + self.flow, + error_message="Unknown error", + ) + events = Event.objects.filter(action=EventAction.CONFIGURATION_ERROR) + self.assertEqual(len(events), 1) + event = events.first() + self.assertEqual( + event.context["message"], "Exception occurred while rendering E-mail template" + ) + self.assertEqual(event.context["template"], "invalid.html") diff --git a/go.mod b/go.mod index c7e703a61..99e88c97b 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1 github.com/go-ldap/ldap/v3 v3.4.6 github.com/go-openapi/runtime v0.26.0 - github.com/go-openapi/strfmt v0.21.7 + github.com/go-openapi/strfmt v0.21.8 github.com/golang-jwt/jwt v3.2.2+incompatible github.com/google/uuid v1.4.0 github.com/gorilla/handlers v1.5.2 diff --git a/go.sum b/go.sum index 78aee8833..c442c4c84 100644 --- a/go.sum +++ b/go.sum @@ -123,8 +123,8 @@ github.com/go-openapi/spec v0.20.8/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6 github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg= github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg= -github.com/go-openapi/strfmt v0.21.7 h1:rspiXgNWgeUzhjo1YU01do6qsahtJNByjLVbPLNHb8k= -github.com/go-openapi/strfmt v0.21.7/go.mod h1:adeGTkxE44sPyLk0JV235VQAO/ZXUr8KAzYjclFs3ew= +github.com/go-openapi/strfmt v0.21.8 h1:VYBUoKYRLAlgKDrIxR/I0lKrztDQ0tuTDrbhLVP8Erg= +github.com/go-openapi/strfmt v0.21.8/go.mod h1:adeGTkxE44sPyLk0JV235VQAO/ZXUr8KAzYjclFs3ew= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= diff --git a/internal/outpost/ldap/search/memory/memory.go b/internal/outpost/ldap/search/memory/memory.go index 2b80bbbbb..198739122 100644 --- a/internal/outpost/ldap/search/memory/memory.go +++ b/internal/outpost/ldap/search/memory/memory.go @@ -147,7 +147,11 @@ func (ms *MemorySearcher) Search(req *search.Request) (ldap.ServerSearchResult, fg := api.NewGroup(g.Pk, g.NumPk, g.Name, g.ParentName, []api.GroupMember{u}, []api.Role{}) fg.SetUsers([]int32{flag.UserPk}) if g.Parent.IsSet() { - fg.SetParent(*g.Parent.Get()) + if p := g.Parent.Get(); p != nil { + fg.SetParent(*p) + } else { + fg.SetParentNil() + } } fg.SetAttributes(g.Attributes) fg.SetIsSuperuser(*g.IsSuperuser) diff --git a/ldap.Dockerfile b/ldap.Dockerfile index 6bb161a97..7b1b9fbb1 100644 --- a/ldap.Dockerfile +++ b/ldap.Dockerfile @@ -1,5 +1,5 @@ # Stage 1: Build -FROM --platform=${BUILDPLATFORM} docker.io/golang:1.21.4-bookworm AS builder +FROM --platform=${BUILDPLATFORM} docker.io/golang:1.21.5-bookworm AS builder ARG TARGETOS ARG TARGETARCH diff --git a/locale/en/LC_MESSAGES/django.po b/locale/en/LC_MESSAGES/django.po index 597f17cc9..07031190c 100644 --- a/locale/en/LC_MESSAGES/django.po +++ b/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-10-02 12:46+0000\n" +"POT-Creation-Date: 2023-12-06 16:55+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,7 +18,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: authentik/admin/api/tasks.py:125 +#: authentik/admin/api/tasks.py:127 #, python-format msgid "Successfully re-scheduled Task %(name)s!" msgstr "" @@ -31,16 +31,16 @@ msgstr "" msgid "Validation Error" msgstr "" -#: authentik/blueprints/api.py:44 +#: authentik/blueprints/api.py:43 msgid "Blueprint file does not exist" msgstr "" -#: authentik/blueprints/api.py:55 +#: authentik/blueprints/api.py:54 #, python-format msgid "Failed to validate blueprint: %(logs)s" msgstr "" -#: authentik/blueprints/api.py:60 +#: authentik/blueprints/api.py:59 msgid "Either path or content must be set." msgstr "" @@ -82,11 +82,11 @@ msgstr "" msgid "Create a SAML Provider by importing its Metadata." msgstr "" -#: authentik/core/api/users.py:158 +#: authentik/core/api/users.py:156 msgid "No leading or trailing slashes allowed." msgstr "" -#: authentik/core/api/users.py:161 +#: authentik/core/api/users.py:159 msgid "No empty segments in user path allowed." msgstr "" @@ -98,151 +98,180 @@ msgstr "" msgid "Users added to this group will be superusers." msgstr "" -#: authentik/core/models.py:142 +#: authentik/core/models.py:162 +msgid "Group" +msgstr "" + +#: authentik/core/models.py:163 +msgid "Groups" +msgstr "" + +#: authentik/core/models.py:178 msgid "User's display name." msgstr "" -#: authentik/core/models.py:268 authentik/providers/oauth2/models.py:295 +#: authentik/core/models.py:274 authentik/providers/oauth2/models.py:295 msgid "User" msgstr "" -#: authentik/core/models.py:269 +#: authentik/core/models.py:275 msgid "Users" msgstr "" -#: authentik/core/models.py:282 +#: authentik/core/models.py:277 +#: authentik/stages/email/templates/email/password_reset.html:28 +msgid "Reset Password" +msgstr "" + +#: authentik/core/models.py:278 +msgid "Can impersonate other users" +msgstr "" + +#: authentik/core/models.py:279 authentik/rbac/models.py:54 +msgid "Can assign permissions to users" +msgstr "" + +#: authentik/core/models.py:280 authentik/rbac/models.py:55 +msgid "Can unassign permissions from users" +msgstr "" + +#: authentik/core/models.py:294 msgid "" "Flow used for authentication when the associated application is accessed by " "an un-authenticated user." msgstr "" -#: authentik/core/models.py:292 +#: authentik/core/models.py:304 msgid "Flow used when authorizing this provider." msgstr "" -#: authentik/core/models.py:304 +#: authentik/core/models.py:316 msgid "" "Accessed from applications; optional backchannel providers for protocols " "like LDAP and SCIM." msgstr "" -#: authentik/core/models.py:359 +#: authentik/core/models.py:371 msgid "Application's display Name." msgstr "" -#: authentik/core/models.py:360 +#: authentik/core/models.py:372 msgid "Internal application name, used in URLs." msgstr "" -#: authentik/core/models.py:372 +#: authentik/core/models.py:384 msgid "Open launch URL in a new browser tab or window." msgstr "" -#: authentik/core/models.py:436 +#: authentik/core/models.py:448 msgid "Application" msgstr "" -#: authentik/core/models.py:437 +#: authentik/core/models.py:449 msgid "Applications" msgstr "" -#: authentik/core/models.py:443 +#: authentik/core/models.py:455 msgid "Use the source-specific identifier" msgstr "" -#: authentik/core/models.py:445 +#: authentik/core/models.py:457 msgid "" "Link to a user with identical email address. Can have security implications " "when a source doesn't validate email addresses." msgstr "" -#: authentik/core/models.py:449 +#: authentik/core/models.py:461 msgid "" "Use the user's email address, but deny enrollment when the email address " "already exists." msgstr "" -#: authentik/core/models.py:452 +#: authentik/core/models.py:464 msgid "" "Link to a user with identical username. Can have security implications when " "a username is used with another source." msgstr "" -#: authentik/core/models.py:456 +#: authentik/core/models.py:468 msgid "" "Use the user's username, but deny enrollment when the username already " "exists." msgstr "" -#: authentik/core/models.py:463 +#: authentik/core/models.py:475 msgid "Source's display Name." msgstr "" -#: authentik/core/models.py:464 +#: authentik/core/models.py:476 msgid "Internal source name, used in URLs." msgstr "" -#: authentik/core/models.py:483 +#: authentik/core/models.py:495 msgid "Flow to use when authenticating existing users." msgstr "" -#: authentik/core/models.py:492 +#: authentik/core/models.py:504 msgid "Flow to use when enrolling new users." msgstr "" -#: authentik/core/models.py:500 +#: authentik/core/models.py:512 msgid "" "How the source determines if an existing user should be authenticated or a " "new user enrolled." msgstr "" -#: authentik/core/models.py:672 +#: authentik/core/models.py:684 msgid "Token" msgstr "" -#: authentik/core/models.py:673 +#: authentik/core/models.py:685 msgid "Tokens" msgstr "" -#: authentik/core/models.py:714 +#: authentik/core/models.py:690 +msgid "View token's key" +msgstr "" + +#: authentik/core/models.py:726 msgid "Property Mapping" msgstr "" -#: authentik/core/models.py:715 +#: authentik/core/models.py:727 msgid "Property Mappings" msgstr "" -#: authentik/core/models.py:750 +#: authentik/core/models.py:762 msgid "Authenticated Session" msgstr "" -#: authentik/core/models.py:751 +#: authentik/core/models.py:763 msgid "Authenticated Sessions" msgstr "" -#: authentik/core/sources/flow_manager.py:189 +#: authentik/core/sources/flow_manager.py:190 #, python-format msgid "" "Request to authenticate with %(source)s has been denied. Please authenticate " "with the source you've previously signed up with." msgstr "" -#: authentik/core/sources/flow_manager.py:241 +#: authentik/core/sources/flow_manager.py:242 msgid "Configured flow does not exist." msgstr "" -#: authentik/core/sources/flow_manager.py:271 -#: authentik/core/sources/flow_manager.py:323 +#: authentik/core/sources/flow_manager.py:272 +#: authentik/core/sources/flow_manager.py:324 #, python-format msgid "Successfully authenticated with %(source)s!" msgstr "" -#: authentik/core/sources/flow_manager.py:295 +#: authentik/core/sources/flow_manager.py:296 #, python-format msgid "Successfully linked %(source)s!" msgstr "" -#: authentik/core/sources/flow_manager.py:314 +#: authentik/core/sources/flow_manager.py:315 msgid "Source is not configured for enrollment." msgstr "" @@ -262,8 +291,8 @@ msgstr "" msgid "" "\n" " You've logged out of %(application)s. You can go back to the " -"overview to launch another application, or log out of your authentik " -"account.\n" +"overview to launch another application, or log out of your " +"%(branding_title)s account.\n" " " msgstr "" @@ -325,113 +354,121 @@ msgstr "" msgid "Certificate-Key Pairs" msgstr "" -#: authentik/enterprise/models.py:193 +#: authentik/enterprise/models.py:183 +msgid "License" +msgstr "" + +#: authentik/enterprise/models.py:184 +msgid "Licenses" +msgstr "" + +#: authentik/enterprise/models.py:206 msgid "License Usage" msgstr "" -#: authentik/enterprise/models.py:194 +#: authentik/enterprise/models.py:207 msgid "License Usage Records" msgstr "" -#: authentik/events/models.py:290 +#: authentik/events/models.py:291 msgid "Event" msgstr "" -#: authentik/events/models.py:291 +#: authentik/events/models.py:292 msgid "Events" msgstr "" -#: authentik/events/models.py:297 +#: authentik/events/models.py:298 msgid "authentik inbuilt notifications" msgstr "" -#: authentik/events/models.py:298 +#: authentik/events/models.py:299 msgid "Generic Webhook" msgstr "" -#: authentik/events/models.py:299 +#: authentik/events/models.py:300 msgid "Slack Webhook (Slack/Discord)" msgstr "" -#: authentik/events/models.py:300 +#: authentik/events/models.py:301 msgid "Email" msgstr "" -#: authentik/events/models.py:318 +#: authentik/events/models.py:319 msgid "" "Only send notification once, for example when sending a webhook into a chat " "channel." msgstr "" -#: authentik/events/models.py:383 +#: authentik/events/models.py:384 msgid "Severity" msgstr "" -#: authentik/events/models.py:388 +#: authentik/events/models.py:389 msgid "Dispatched for user" msgstr "" -#: authentik/events/models.py:397 +#: authentik/events/models.py:398 msgid "Event user" msgstr "" -#: authentik/events/models.py:491 +#: authentik/events/models.py:492 msgid "Notification Transport" msgstr "" -#: authentik/events/models.py:492 +#: authentik/events/models.py:493 msgid "Notification Transports" msgstr "" -#: authentik/events/models.py:498 +#: authentik/events/models.py:499 msgid "Notice" msgstr "" -#: authentik/events/models.py:499 +#: authentik/events/models.py:500 msgid "Warning" msgstr "" -#: authentik/events/models.py:500 +#: authentik/events/models.py:501 msgid "Alert" msgstr "" -#: authentik/events/models.py:525 +#: authentik/events/models.py:526 msgid "Notification" msgstr "" -#: authentik/events/models.py:526 +#: authentik/events/models.py:527 msgid "Notifications" msgstr "" -#: authentik/events/models.py:536 +#: authentik/events/models.py:537 msgid "" "Select which transports should be used to notify the user. If none are " "selected, the notification will only be shown in the authentik UI." msgstr "" -#: authentik/events/models.py:544 +#: authentik/events/models.py:545 msgid "Controls which severity level the created notifications will have." msgstr "" -#: authentik/events/models.py:549 +#: authentik/events/models.py:550 msgid "" "Define which group of users this notification should be sent and shown to. " "If left empty, Notification won't ben sent." msgstr "" -#: authentik/events/models.py:567 +#: authentik/events/models.py:568 msgid "Notification Rule" msgstr "" -#: authentik/events/models.py:568 +#: authentik/events/models.py:569 msgid "Notification Rules" msgstr "" -#: authentik/events/models.py:588 +#: authentik/events/models.py:589 msgid "Webhook Mapping" msgstr "" -#: authentik/events/models.py:589 +#: authentik/events/models.py:590 msgid "Webhook Mappings" msgstr "" @@ -541,15 +578,31 @@ msgstr "" msgid "Flows" msgstr "" -#: authentik/flows/models.py:215 +#: authentik/flows/models.py:197 +msgid "Can export a Flow" +msgstr "" + +#: authentik/flows/models.py:198 +msgid "Can inspect a Flow's execution" +msgstr "" + +#: authentik/flows/models.py:199 +msgid "View Flow's cache metrics" +msgstr "" + +#: authentik/flows/models.py:200 +msgid "Clear Flow's cache metrics" +msgstr "" + +#: authentik/flows/models.py:216 msgid "Evaluate policies during the Flow planning process." msgstr "" -#: authentik/flows/models.py:219 +#: authentik/flows/models.py:220 msgid "Evaluate policies when the Stage is present to the user." msgstr "" -#: authentik/flows/models.py:226 +#: authentik/flows/models.py:227 msgid "" "Configure how the flow executor should handle an invalid response to a " "challenge. RETRY returns the error message and a similar challenge to the " @@ -557,25 +610,25 @@ msgid "" "RESTART_WITH_CONTEXT restarts the flow while keeping the current context." msgstr "" -#: authentik/flows/models.py:249 +#: authentik/flows/models.py:250 msgid "Flow Stage Binding" msgstr "" -#: authentik/flows/models.py:250 +#: authentik/flows/models.py:251 msgid "Flow Stage Bindings" msgstr "" -#: authentik/flows/models.py:265 +#: authentik/flows/models.py:266 msgid "" "Flow used by an authenticated user to configure this Stage. If empty, user " "will not be able to configure this stage." msgstr "" -#: authentik/flows/models.py:305 +#: authentik/flows/models.py:306 msgid "Flow Token" msgstr "" -#: authentik/flows/models.py:306 +#: authentik/flows/models.py:307 msgid "Flow Tokens" msgstr "" @@ -584,6 +637,11 @@ msgstr "" msgid "%(value)s is not in the correct format of 'hours=3;minutes=1'." msgstr "" +#: authentik/lib/validators.py:16 +#, python-brace-format +msgid "The fields {field_names} must be used together." +msgstr "" + #: authentik/outposts/api/service_connections.py:127 msgid "" "You can only use an empty kubeconfig when connecting to a local cluster." @@ -657,6 +715,14 @@ msgid "" "empty if authentik should not handle the deployment." msgstr "" +#: authentik/outposts/models.py:419 +msgid "Outpost" +msgstr "" + +#: authentik/outposts/models.py:420 +msgid "Outposts" +msgstr "" + #: authentik/policies/denied.py:24 msgid "Access denied" msgstr "" @@ -782,6 +848,14 @@ msgstr "" msgid "Policies" msgstr "" +#: authentik/policies/models.py:193 +msgid "View Policy's cache metrics" +msgstr "" + +#: authentik/policies/models.py:194 +msgid "Clear Policy's cache metrics" +msgstr "" + #: authentik/policies/password/models.py:27 msgid "Field key to check, field keys defined in Prompt stages are available." msgstr "" @@ -899,6 +973,7 @@ msgid "" msgstr "" #: authentik/providers/ldap/models.py:76 +#: authentik/providers/radius/models.py:34 msgid "" "When enabled, code-based multi-factor authentication can be used by " "appending a semicolon and the TOTP code to the password. This should only be " @@ -1258,19 +1333,19 @@ msgid "" "specified CIDR will be dropped." msgstr "" -#: authentik/providers/radius/models.py:49 +#: authentik/providers/radius/models.py:60 msgid "Radius Provider" msgstr "" -#: authentik/providers/radius/models.py:50 +#: authentik/providers/radius/models.py:61 msgid "Radius Providers" msgstr "" -#: authentik/providers/saml/api/providers.py:257 +#: authentik/providers/saml/api/providers.py:258 msgid "Invalid XML Syntax" msgstr "" -#: authentik/providers/saml/api/providers.py:267 +#: authentik/providers/saml/api/providers.py:268 #, python-format msgid "Failed to import Metadata: %(message)s" msgstr "" @@ -1381,19 +1456,23 @@ msgstr "" msgid "Signing Keypair" msgstr "" -#: authentik/providers/saml/models.py:167 +#: authentik/providers/saml/models.py:142 +msgid "Default relay_state value for IDP-initiated logins" +msgstr "" + +#: authentik/providers/saml/models.py:171 msgid "SAML Provider" msgstr "" -#: authentik/providers/saml/models.py:168 +#: authentik/providers/saml/models.py:172 msgid "SAML Providers" msgstr "" -#: authentik/providers/saml/models.py:192 +#: authentik/providers/saml/models.py:196 msgid "SAML Property Mapping" msgstr "" -#: authentik/providers/saml/models.py:193 +#: authentik/providers/saml/models.py:197 msgid "SAML Property Mappings" msgstr "" @@ -1405,7 +1484,7 @@ msgstr "" msgid "Authentication token" msgstr "" -#: authentik/providers/scim/models.py:27 authentik/sources/ldap/models.py:94 +#: authentik/providers/scim/models.py:27 authentik/sources/ldap/models.py:98 msgid "Property mappings used for group creation/updating." msgstr "" @@ -1454,6 +1533,38 @@ msgstr "" msgid "Failed to sync group %(group_name)s due to remote error: %(error)s" msgstr "" +#: authentik/rbac/models.py:51 +msgid "Role" +msgstr "" + +#: authentik/rbac/models.py:52 +msgid "Roles" +msgstr "" + +#: authentik/rbac/models.py:66 +msgid "System permission" +msgstr "" + +#: authentik/rbac/models.py:67 +msgid "System permissions" +msgstr "" + +#: authentik/rbac/models.py:69 +msgid "Can view system info" +msgstr "" + +#: authentik/rbac/models.py:70 +msgid "Can view system tasks" +msgstr "" + +#: authentik/rbac/models.py:71 +msgid "Can run system tasks" +msgstr "" + +#: authentik/rbac/models.py:72 +msgid "Can access admin interface" +msgstr "" + #: authentik/recovery/management/commands/create_admin_group.py:11 msgid "Create admin group if the default group gets deleted." msgstr "" @@ -1466,92 +1577,92 @@ msgstr "" msgid "Used recovery-link to authenticate." msgstr "" -#: authentik/sources/ldap/models.py:37 +#: authentik/sources/ldap/models.py:41 msgid "Server URI" msgstr "" -#: authentik/sources/ldap/models.py:46 +#: authentik/sources/ldap/models.py:50 msgid "" "Optionally verify the LDAP Server's Certificate against the CA Chain in this " "keypair." msgstr "" -#: authentik/sources/ldap/models.py:55 +#: authentik/sources/ldap/models.py:59 msgid "" "Client certificate to authenticate against the LDAP Server's Certificate." msgstr "" -#: authentik/sources/ldap/models.py:58 +#: authentik/sources/ldap/models.py:62 msgid "Bind CN" msgstr "" -#: authentik/sources/ldap/models.py:60 +#: authentik/sources/ldap/models.py:64 msgid "Enable Start TLS" msgstr "" -#: authentik/sources/ldap/models.py:61 +#: authentik/sources/ldap/models.py:65 msgid "Use Server URI for SNI verification" msgstr "" -#: authentik/sources/ldap/models.py:63 +#: authentik/sources/ldap/models.py:67 msgid "Base DN" msgstr "" -#: authentik/sources/ldap/models.py:65 +#: authentik/sources/ldap/models.py:69 msgid "Prepended to Base DN for User-queries." msgstr "" -#: authentik/sources/ldap/models.py:66 +#: authentik/sources/ldap/models.py:70 msgid "Addition User DN" msgstr "" -#: authentik/sources/ldap/models.py:70 +#: authentik/sources/ldap/models.py:74 msgid "Prepended to Base DN for Group-queries." msgstr "" -#: authentik/sources/ldap/models.py:71 +#: authentik/sources/ldap/models.py:75 msgid "Addition Group DN" msgstr "" -#: authentik/sources/ldap/models.py:77 +#: authentik/sources/ldap/models.py:81 msgid "Consider Objects matching this filter to be Users." msgstr "" -#: authentik/sources/ldap/models.py:80 +#: authentik/sources/ldap/models.py:84 msgid "Field which contains members of a group." msgstr "" -#: authentik/sources/ldap/models.py:84 +#: authentik/sources/ldap/models.py:88 msgid "Consider Objects matching this filter to be Groups." msgstr "" -#: authentik/sources/ldap/models.py:87 +#: authentik/sources/ldap/models.py:91 msgid "Field which contains a unique Identifier." msgstr "" -#: authentik/sources/ldap/models.py:101 +#: authentik/sources/ldap/models.py:105 msgid "" "When a user changes their password, sync it back to LDAP. This can only be " "enabled on a single LDAP source." msgstr "" -#: authentik/sources/ldap/models.py:190 +#: authentik/sources/ldap/models.py:248 msgid "LDAP Source" msgstr "" -#: authentik/sources/ldap/models.py:191 +#: authentik/sources/ldap/models.py:249 msgid "LDAP Sources" msgstr "" -#: authentik/sources/ldap/models.py:213 +#: authentik/sources/ldap/models.py:271 msgid "LDAP Property Mapping" msgstr "" -#: authentik/sources/ldap/models.py:214 +#: authentik/sources/ldap/models.py:272 msgid "LDAP Property Mappings" msgstr "" -#: authentik/sources/ldap/signals.py:50 +#: authentik/sources/ldap/signals.py:52 msgid "Password does not match Active Directory Complexity." msgstr "" @@ -1596,123 +1707,123 @@ msgstr "" msgid "Additional Scopes" msgstr "" -#: authentik/sources/oauth/models.py:108 +#: authentik/sources/oauth/models.py:107 msgid "OAuth Source" msgstr "" -#: authentik/sources/oauth/models.py:109 +#: authentik/sources/oauth/models.py:108 msgid "OAuth Sources" msgstr "" -#: authentik/sources/oauth/models.py:117 +#: authentik/sources/oauth/models.py:116 msgid "GitHub OAuth Source" msgstr "" -#: authentik/sources/oauth/models.py:118 +#: authentik/sources/oauth/models.py:117 msgid "GitHub OAuth Sources" msgstr "" -#: authentik/sources/oauth/models.py:126 +#: authentik/sources/oauth/models.py:125 msgid "Twitch OAuth Source" msgstr "" -#: authentik/sources/oauth/models.py:127 +#: authentik/sources/oauth/models.py:126 msgid "Twitch OAuth Sources" msgstr "" -#: authentik/sources/oauth/models.py:135 +#: authentik/sources/oauth/models.py:134 msgid "Mailcow OAuth Source" msgstr "" -#: authentik/sources/oauth/models.py:136 +#: authentik/sources/oauth/models.py:135 msgid "Mailcow OAuth Sources" msgstr "" -#: authentik/sources/oauth/models.py:144 +#: authentik/sources/oauth/models.py:143 msgid "Twitter OAuth Source" msgstr "" -#: authentik/sources/oauth/models.py:145 +#: authentik/sources/oauth/models.py:144 msgid "Twitter OAuth Sources" msgstr "" -#: authentik/sources/oauth/models.py:153 +#: authentik/sources/oauth/models.py:152 msgid "Facebook OAuth Source" msgstr "" -#: authentik/sources/oauth/models.py:154 +#: authentik/sources/oauth/models.py:153 msgid "Facebook OAuth Sources" msgstr "" -#: authentik/sources/oauth/models.py:162 +#: authentik/sources/oauth/models.py:161 msgid "Discord OAuth Source" msgstr "" -#: authentik/sources/oauth/models.py:163 +#: authentik/sources/oauth/models.py:162 msgid "Discord OAuth Sources" msgstr "" -#: authentik/sources/oauth/models.py:171 +#: authentik/sources/oauth/models.py:170 msgid "Patreon OAuth Source" msgstr "" -#: authentik/sources/oauth/models.py:172 +#: authentik/sources/oauth/models.py:171 msgid "Patreon OAuth Sources" msgstr "" -#: authentik/sources/oauth/models.py:180 +#: authentik/sources/oauth/models.py:179 msgid "Google OAuth Source" msgstr "" -#: authentik/sources/oauth/models.py:181 +#: authentik/sources/oauth/models.py:180 msgid "Google OAuth Sources" msgstr "" -#: authentik/sources/oauth/models.py:189 +#: authentik/sources/oauth/models.py:188 msgid "Azure AD OAuth Source" msgstr "" -#: authentik/sources/oauth/models.py:190 +#: authentik/sources/oauth/models.py:189 msgid "Azure AD OAuth Sources" msgstr "" -#: authentik/sources/oauth/models.py:198 +#: authentik/sources/oauth/models.py:197 msgid "OpenID OAuth Source" msgstr "" -#: authentik/sources/oauth/models.py:199 +#: authentik/sources/oauth/models.py:198 msgid "OpenID OAuth Sources" msgstr "" -#: authentik/sources/oauth/models.py:207 +#: authentik/sources/oauth/models.py:206 msgid "Apple OAuth Source" msgstr "" -#: authentik/sources/oauth/models.py:208 +#: authentik/sources/oauth/models.py:207 msgid "Apple OAuth Sources" msgstr "" -#: authentik/sources/oauth/models.py:216 +#: authentik/sources/oauth/models.py:215 msgid "Okta OAuth Source" msgstr "" -#: authentik/sources/oauth/models.py:217 +#: authentik/sources/oauth/models.py:216 msgid "Okta OAuth Sources" msgstr "" -#: authentik/sources/oauth/models.py:225 +#: authentik/sources/oauth/models.py:224 msgid "Reddit OAuth Source" msgstr "" -#: authentik/sources/oauth/models.py:226 +#: authentik/sources/oauth/models.py:225 msgid "Reddit OAuth Sources" msgstr "" -#: authentik/sources/oauth/models.py:248 +#: authentik/sources/oauth/models.py:247 msgid "User OAuth Source Connection" msgstr "" -#: authentik/sources/oauth/models.py:249 +#: authentik/sources/oauth/models.py:248 msgid "User OAuth Source Connections" msgstr "" @@ -1885,13 +1996,13 @@ msgstr "" msgid "SMS Devices" msgstr "" -#: authentik/stages/authenticator_sms/stage.py:55 +#: authentik/stages/authenticator_sms/stage.py:57 #: authentik/stages/authenticator_totp/stage.py:41 #: authentik/stages/authenticator_totp/stage.py:44 msgid "Code does not match" msgstr "" -#: authentik/stages/authenticator_sms/stage.py:71 +#: authentik/stages/authenticator_sms/stage.py:73 msgid "Invalid phone number" msgstr "" @@ -1904,11 +2015,19 @@ msgid "Static Authenticator Stages" msgstr "" #: authentik/stages/authenticator_static/models.py:98 -msgid "Static device" +msgid "Static Device" msgstr "" #: authentik/stages/authenticator_static/models.py:99 -msgid "Static devices" +msgid "Static Devices" +msgstr "" + +#: authentik/stages/authenticator_static/models.py:129 +msgid "Static Token" +msgstr "" + +#: authentik/stages/authenticator_static/models.py:130 +msgid "Static Tokens" msgstr "" #: authentik/stages/authenticator_totp/models.py:25 @@ -1928,11 +2047,11 @@ msgid "TOTP Authenticator Setup Stages" msgstr "" #: authentik/stages/authenticator_totp/models.py:244 -msgid "TOTP device" +msgid "TOTP Device" msgstr "" #: authentik/stages/authenticator_totp/models.py:245 -msgid "TOTP devices" +msgid "TOTP Devices" msgstr "" #: authentik/stages/authenticator_validate/challenge.py:131 @@ -2041,11 +2160,11 @@ msgstr "" msgid "User Consents" msgstr "" -#: authentik/stages/deny/models.py:30 +#: authentik/stages/deny/models.py:32 msgid "Deny Stage" msgstr "" -#: authentik/stages/deny/models.py:31 +#: authentik/stages/deny/models.py:33 msgid "Deny Stages" msgstr "" @@ -2087,18 +2206,26 @@ msgstr "" msgid "Email Stages" msgstr "" -#: authentik/stages/email/stage.py:117 +#: authentik/stages/email/stage.py:126 +msgid "Exception occurred while rendering E-mail template" +msgstr "" + +#: authentik/stages/email/stage.py:140 msgid "Successfully verified Email." msgstr "" -#: authentik/stages/email/stage.py:124 authentik/stages/email/stage.py:146 +#: authentik/stages/email/stage.py:147 authentik/stages/email/stage.py:173 msgid "No pending user." msgstr "" -#: authentik/stages/email/stage.py:136 +#: authentik/stages/email/stage.py:163 msgid "Email sent." msgstr "" +#: authentik/stages/email/stage.py:176 +msgid "Email Successfully sent." +msgstr "" + #: authentik/stages/email/templates/email/account_confirmation.html:10 msgid "Welcome!" msgstr "" @@ -2147,10 +2274,6 @@ msgid "" " " msgstr "" -#: authentik/stages/email/templates/email/password_reset.html:28 -msgid "Reset Password" -msgstr "" - #: authentik/stages/email/templates/email/password_reset.html:39 #, python-format msgid "" @@ -2193,27 +2316,33 @@ msgid "" "user entered will be shown" msgstr "" -#: authentik/stages/identification/models.py:65 +#: authentik/stages/identification/models.py:60 +msgid "" +"When enabled, the stage will succeed and continue even when incorrect user " +"info is entered." +msgstr "" + +#: authentik/stages/identification/models.py:72 msgid "Optional enrollment flow, which is linked at the bottom of the page." msgstr "" -#: authentik/stages/identification/models.py:74 +#: authentik/stages/identification/models.py:81 msgid "Optional recovery flow, which is linked at the bottom of the page." msgstr "" -#: authentik/stages/identification/models.py:83 +#: authentik/stages/identification/models.py:90 msgid "Optional passwordless flow, which is linked at the bottom of the page." msgstr "" -#: authentik/stages/identification/models.py:87 +#: authentik/stages/identification/models.py:94 msgid "Specify which sources should be shown." msgstr "" -#: authentik/stages/identification/models.py:108 +#: authentik/stages/identification/models.py:115 msgid "Identification Stage" msgstr "" -#: authentik/stages/identification/models.py:109 +#: authentik/stages/identification/models.py:116 msgid "Identification Stages" msgstr "" @@ -2459,24 +2588,24 @@ msgstr "" msgid "Optionally add newly created users to this group." msgstr "" -#: authentik/stages/user_write/models.py:64 +#: authentik/stages/user_write/models.py:68 msgid "User Write Stage" msgstr "" -#: authentik/stages/user_write/models.py:65 +#: authentik/stages/user_write/models.py:69 msgid "User Write Stages" msgstr "" -#: authentik/stages/user_write/stage.py:130 +#: authentik/stages/user_write/stage.py:141 msgid "No Pending data." msgstr "" -#: authentik/stages/user_write/stage.py:136 +#: authentik/stages/user_write/stage.py:147 msgid "No user found and can't create new user." msgstr "" -#: authentik/stages/user_write/stage.py:153 -#: authentik/stages/user_write/stage.py:167 +#: authentik/stages/user_write/stage.py:164 +#: authentik/stages/user_write/stage.py:178 msgid "Failed to update user. Please try again later." msgstr "" diff --git a/proxy.Dockerfile b/proxy.Dockerfile index e8bb5a3f8..ca4f7bc1a 100644 --- a/proxy.Dockerfile +++ b/proxy.Dockerfile @@ -15,7 +15,7 @@ COPY web . RUN npm run build-proxy # Stage 2: Build -FROM --platform=${BUILDPLATFORM} docker.io/golang:1.21.4-bookworm AS builder +FROM --platform=${BUILDPLATFORM} docker.io/golang:1.21.5-bookworm AS builder ARG TARGETOS ARG TARGETARCH diff --git a/radius.Dockerfile b/radius.Dockerfile index 7e6b8fd42..bef91e084 100644 --- a/radius.Dockerfile +++ b/radius.Dockerfile @@ -1,5 +1,5 @@ # Stage 1: Build -FROM --platform=${BUILDPLATFORM} docker.io/golang:1.21.4-bookworm AS builder +FROM --platform=${BUILDPLATFORM} docker.io/golang:1.21.5-bookworm AS builder ARG TARGETOS ARG TARGETARCH diff --git a/schema.yml b/schema.yml index 7a53cf53c..c33d37c56 100644 --- a/schema.yml +++ b/schema.yml @@ -6365,6 +6365,86 @@ paths: schema: $ref: '#/components/schemas/GenericError' description: '' + /events/events/volume/: + get: + operationId: events_events_volume_list + description: Get event volume for specified filters and timeframe + parameters: + - in: query + name: action + schema: + type: string + - in: query + name: client_ip + schema: + type: string + - in: query + name: context_authorized_app + schema: + type: string + description: Context Authorized application + - in: query + name: context_model_app + schema: + type: string + description: Context Model App + - in: query + name: context_model_name + schema: + type: string + description: Context Model Name + - in: query + name: context_model_pk + schema: + type: string + description: Context Model Primary Key + - name: ordering + required: false + in: query + description: Which field to use when ordering the results. + schema: + type: string + - name: search + required: false + in: query + description: A search term. + schema: + type: string + - in: query + name: tenant_name + schema: + type: string + description: Tenant name + - in: query + name: username + schema: + type: string + description: Username + tags: + - events + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Coordinate' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' /events/notifications/: get: operationId: events_notifications_list diff --git a/tests/wdio/package-lock.json b/tests/wdio/package-lock.json index db45a38c6..483c15312 100644 --- a/tests/wdio/package-lock.json +++ b/tests/wdio/package-lock.json @@ -7,13 +7,13 @@ "name": "@goauthentik/web-tests", "devDependencies": { "@trivago/prettier-plugin-sort-imports": "^4.3.0", - "@typescript-eslint/eslint-plugin": "^6.13.1", - "@typescript-eslint/parser": "^6.13.1", - "@wdio/cli": "^8.24.6", - "@wdio/local-runner": "^8.24.6", - "@wdio/mocha-framework": "^8.24.6", - "@wdio/spec-reporter": "^8.24.2", - "eslint": "^8.54.0", + "@typescript-eslint/eslint-plugin": "^6.13.2", + "@typescript-eslint/parser": "^6.13.2", + "@wdio/cli": "^8.24.13", + "@wdio/local-runner": "^8.24.12", + "@wdio/mocha-framework": "^8.24.12", + "@wdio/spec-reporter": "^8.24.12", + "eslint": "^8.55.0", "eslint-config-google": "^0.14.0", "eslint-plugin-sonarjs": "^0.23.0", "npm-run-all": "^4.1.5", @@ -332,9 +332,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz", - "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -382,9 +382,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.54.0.tgz", - "integrity": "sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz", + "integrity": "sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -946,16 +946,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.1.tgz", - "integrity": "sha512-5bQDGkXaxD46bPvQt08BUz9YSaO4S0fB1LB5JHQuXTfkGPI3+UUeS387C/e9jRie5GqT8u5kFTrMvAjtX4O5kA==", + "version": "6.13.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.2.tgz", + "integrity": "sha512-3+9OGAWHhk4O1LlcwLBONbdXsAhLjyCFogJY/cWy2lxdVJ2JrcTF2pTGMaLl2AE7U1l31n8Py4a8bx5DLf/0dQ==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.13.1", - "@typescript-eslint/type-utils": "6.13.1", - "@typescript-eslint/utils": "6.13.1", - "@typescript-eslint/visitor-keys": "6.13.1", + "@typescript-eslint/scope-manager": "6.13.2", + "@typescript-eslint/type-utils": "6.13.2", + "@typescript-eslint/utils": "6.13.2", + "@typescript-eslint/visitor-keys": "6.13.2", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -981,15 +981,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.13.1.tgz", - "integrity": "sha512-fs2XOhWCzRhqMmQf0eicLa/CWSaYss2feXsy7xBD/pLyWke/jCIVc2s1ikEAtSW7ina1HNhv7kONoEfVNEcdDQ==", + "version": "6.13.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.13.2.tgz", + "integrity": "sha512-MUkcC+7Wt/QOGeVlM8aGGJZy1XV5YKjTpq9jK6r6/iLsGXhBVaGP5N0UYvFsu9BFlSpwY9kMretzdBH01rkRXg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.13.1", - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/typescript-estree": "6.13.1", - "@typescript-eslint/visitor-keys": "6.13.1", + "@typescript-eslint/scope-manager": "6.13.2", + "@typescript-eslint/types": "6.13.2", + "@typescript-eslint/typescript-estree": "6.13.2", + "@typescript-eslint/visitor-keys": "6.13.2", "debug": "^4.3.4" }, "engines": { @@ -1009,13 +1009,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.1.tgz", - "integrity": "sha512-BW0kJ7ceiKi56GbT2KKzZzN+nDxzQK2DS6x0PiSMPjciPgd/JRQGMibyaN2cPt2cAvuoH0oNvn2fwonHI+4QUQ==", + "version": "6.13.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.2.tgz", + "integrity": "sha512-CXQA0xo7z6x13FeDYCgBkjWzNqzBn8RXaE3QVQVIUm74fWJLkJkaHmHdKStrxQllGh6Q4eUGyNpMe0b1hMkXFA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/visitor-keys": "6.13.1" + "@typescript-eslint/types": "6.13.2", + "@typescript-eslint/visitor-keys": "6.13.2" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1026,13 +1026,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.13.1.tgz", - "integrity": "sha512-A2qPlgpxx2v//3meMqQyB1qqTg1h1dJvzca7TugM3Yc2USDY+fsRBiojAEo92HO7f5hW5mjAUF6qobOPzlBCBQ==", + "version": "6.13.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.13.2.tgz", + "integrity": "sha512-Qr6ssS1GFongzH2qfnWKkAQmMUyZSyOr0W54nZNU1MDfo+U4Mv3XveeLZzadc/yq8iYhQZHYT+eoXJqnACM1tw==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.13.1", - "@typescript-eslint/utils": "6.13.1", + "@typescript-eslint/typescript-estree": "6.13.2", + "@typescript-eslint/utils": "6.13.2", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -1053,9 +1053,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.1.tgz", - "integrity": "sha512-gjeEskSmiEKKFIbnhDXUyiqVma1gRCQNbVZ1C8q7Zjcxh3WZMbzWVfGE9rHfWd1msQtPS0BVD9Jz9jded44eKg==", + "version": "6.13.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.2.tgz", + "integrity": "sha512-7sxbQ+EMRubQc3wTfTsycgYpSujyVbI1xw+3UMRUcrhSy+pN09y/lWzeKDbvhoqcRbHdc+APLs/PWYi/cisLPg==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1066,13 +1066,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.1.tgz", - "integrity": "sha512-sBLQsvOC0Q7LGcUHO5qpG1HxRgePbT6wwqOiGLpR8uOJvPJbfs0mW3jPA3ujsDvfiVwVlWUDESNXv44KtINkUQ==", + "version": "6.13.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.2.tgz", + "integrity": "sha512-SuD8YLQv6WHnOEtKv8D6HZUzOub855cfPnPMKvdM/Bh1plv1f7Q/0iFUDLKKlxHcEstQnaUU4QZskgQq74t+3w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/visitor-keys": "6.13.1", + "@typescript-eslint/types": "6.13.2", + "@typescript-eslint/visitor-keys": "6.13.2", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1093,17 +1093,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.1.tgz", - "integrity": "sha512-ouPn/zVoan92JgAegesTXDB/oUp6BP1v8WpfYcqh649ejNc9Qv+B4FF2Ff626kO1xg0wWwwG48lAJ4JuesgdOw==", + "version": "6.13.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.2.tgz", + "integrity": "sha512-b9Ptq4eAZUym4idijCRzl61oPCwwREcfDI8xGk751Vhzig5fFZR9CyzDz4Sp/nxSLBYxUPyh4QdIDqWykFhNmQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.13.1", - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/typescript-estree": "6.13.1", + "@typescript-eslint/scope-manager": "6.13.2", + "@typescript-eslint/types": "6.13.2", + "@typescript-eslint/typescript-estree": "6.13.2", "semver": "^7.5.4" }, "engines": { @@ -1118,12 +1118,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.1.tgz", - "integrity": "sha512-NDhQUy2tg6XGNBGDRm1XybOHSia8mcXmlbKWoQP+nm1BIIMxa55shyJfZkHpEBN62KNPLrocSM2PdPcaLgDKMQ==", + "version": "6.13.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.2.tgz", + "integrity": "sha512-OGznFs0eAQXJsp+xSd6k/O1UbFi/K/L7WjqeRoFE7vadjAF9y0uppXhYNQNEqygjou782maGClOoZwPqF0Drlw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.1", + "@typescript-eslint/types": "6.13.2", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -1141,34 +1141,33 @@ "dev": true }, "node_modules/@wdio/cli": { - "version": "8.24.6", - "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-8.24.6.tgz", - "integrity": "sha512-QXRiP1FeGaSmUO24pFhyzP6lZY/FsZAhXyofl3r6TGwTlnw9i4S7C4Te2qQcccgAQq03rdSK058YURPwbiKhmg==", + "version": "8.24.13", + "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-8.24.13.tgz", + "integrity": "sha512-UG4dvnT6KfnKDsNVn/GeUidi21Pso6N6eu1O5oin9+fP612zpPFrx3/TuYrAfjJb+qmy1QkKq3zX99y+xlp7og==", "dev": true, "dependencies": { "@types/node": "^20.1.1", - "@wdio/config": "8.24.6", - "@wdio/globals": "8.24.6", - "@wdio/logger": "8.16.17", - "@wdio/protocols": "8.23.0", - "@wdio/types": "8.24.2", - "@wdio/utils": "8.24.6", + "@wdio/config": "8.24.12", + "@wdio/globals": "8.24.12", + "@wdio/logger": "8.24.12", + "@wdio/protocols": "8.24.12", + "@wdio/types": "8.24.12", + "@wdio/utils": "8.24.12", "async-exit-hook": "^2.0.1", "chalk": "^5.2.0", "chokidar": "^3.5.3", "cli-spinners": "^2.9.0", - "detect-package-manager": "^3.0.1", "dotenv": "^16.3.1", "ejs": "^3.1.9", "execa": "^8.0.1", - "import-meta-resolve": "^3.0.0", + "import-meta-resolve": "^4.0.0", "inquirer": "9.2.12", "lodash.flattendeep": "^4.4.0", "lodash.pickby": "^4.6.0", "lodash.union": "^4.6.0", "read-pkg-up": "^10.0.0", "recursive-readdir": "^2.2.3", - "webdriverio": "8.24.6", + "webdriverio": "8.24.12", "yargs": "^17.7.2" }, "bin": { @@ -1191,47 +1190,47 @@ } }, "node_modules/@wdio/config": { - "version": "8.24.6", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.24.6.tgz", - "integrity": "sha512-ZFmd6rB1kgL4k/SjLXbtFTCxvxSf1qzdt/losiTqkqFBYznkTRUBGSoGaVTlkMtHAReiVSK92sICc15JWaCdEA==", + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.24.12.tgz", + "integrity": "sha512-3HW7qG1rIHzOIybV6oHR1CqLghsN0G3Xzs90ZciGL8dYhtcLtYCHwuWmBw4mkaB5xViU4AmZDuj7ChiG8Cr6Qw==", "dev": true, "dependencies": { - "@wdio/logger": "8.16.17", - "@wdio/types": "8.24.2", - "@wdio/utils": "8.24.6", + "@wdio/logger": "8.24.12", + "@wdio/types": "8.24.12", + "@wdio/utils": "8.24.12", "decamelize": "^6.0.0", "deepmerge-ts": "^5.0.0", "glob": "^10.2.2", - "import-meta-resolve": "^3.0.0" + "import-meta-resolve": "^4.0.0" }, "engines": { "node": "^16.13 || >=18" } }, "node_modules/@wdio/globals": { - "version": "8.24.6", - "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.24.6.tgz", - "integrity": "sha512-v5Sjyix9ddrxPM8DCf0vADUxr21Fx7nWVYS6Z/gkTEhuQbi5svjs6EGjMmErO6tp3CY4SNTUiz+ZFJw9YH4Swg==", + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.24.12.tgz", + "integrity": "sha512-uF26a89Q+6DdqzSfK9suXJNdWYJnsazjzPuq4Xtz6nKdjgmBufSeX1JHV4LxErEu5b/IdzVcMCUKKEvsZPc9vA==", "dev": true, "engines": { "node": "^16.13 || >=18" }, "optionalDependencies": { "expect-webdriverio": "^4.6.1", - "webdriverio": "8.24.6" + "webdriverio": "8.24.12" } }, "node_modules/@wdio/local-runner": { - "version": "8.24.6", - "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.24.6.tgz", - "integrity": "sha512-fd91CxlVpOpSxg+QuqgdFl66kEtY7R/ohdKBXNhdMXtXFb4EQIGp/igiMBvuTHcHUMHOw3N8KaHfe6YXo+6Qyw==", + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.24.12.tgz", + "integrity": "sha512-Q1lfdSPDEgKwuE1gNucJrkVfgOJLTjtnYGb7Fe7oYUHGDwjkudjSBJYmyx30qFZKfZ4zRqXtaEdys54/0TxibA==", "dev": true, "dependencies": { "@types/node": "^20.1.0", - "@wdio/logger": "8.16.17", - "@wdio/repl": "8.23.1", - "@wdio/runner": "8.24.6", - "@wdio/types": "8.24.2", + "@wdio/logger": "8.24.12", + "@wdio/repl": "8.24.12", + "@wdio/runner": "8.24.12", + "@wdio/types": "8.24.12", "async-exit-hook": "^2.0.1", "split2": "^4.1.0", "stream-buffers": "^3.0.2" @@ -1241,9 +1240,9 @@ } }, "node_modules/@wdio/logger": { - "version": "8.16.17", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-8.16.17.tgz", - "integrity": "sha512-zeQ41z3T+b4IsrriZZipayXxLNDuGsm7TdExaviNGojPVrIsQUCSd/FvlLHM32b7ZrMyInHenu/zx1cjAZO71g==", + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-8.24.12.tgz", + "integrity": "sha512-QisOiVIWKTUCf1H7S+DOtC+gruhlpimQrUXfWMTeeh672PvAJYnTpOJDWA+BtXfsikkUYFAzAaq8SeMJk8rqKg==", "dev": true, "dependencies": { "chalk": "^5.1.2", @@ -1268,16 +1267,16 @@ } }, "node_modules/@wdio/mocha-framework": { - "version": "8.24.6", - "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-8.24.6.tgz", - "integrity": "sha512-qTRU7trzPJKjdlO6r4+YnyauEQ/cTvCJYRl5t2jqsG8y2OoCRsw4qUydzGTxX3YEkmgZjSN845hMNtyWuZUjcg==", + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-8.24.12.tgz", + "integrity": "sha512-SHN7CYZnDkVUNYxLp8iMV92xcmU/4gq5dqA0pRrK4m5nIU7BoL0flm0kA+ydYUQyNedQh2ru1V63uNyTOyCKAg==", "dev": true, "dependencies": { "@types/mocha": "^10.0.0", "@types/node": "^20.1.0", - "@wdio/logger": "8.16.17", - "@wdio/types": "8.24.2", - "@wdio/utils": "8.24.6", + "@wdio/logger": "8.24.12", + "@wdio/types": "8.24.12", + "@wdio/utils": "8.24.12", "mocha": "^10.0.0" }, "engines": { @@ -1285,15 +1284,15 @@ } }, "node_modules/@wdio/protocols": { - "version": "8.23.0", - "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-8.23.0.tgz", - "integrity": "sha512-2XTzD+lqQP3g8BWn+Bn5BTFzjHqzZNwq7DjlYrb27Bq8nOA+1DEcj3WzQ6V6CktTnKI/LAYKA1IFAF//Azrp/Q==", + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-8.24.12.tgz", + "integrity": "sha512-QnVj3FkapmVD3h2zoZk+ZQ8gevSj9D9MiIQIy8eOnY4FAneYZ9R9GvoW+mgNcCZO8S8++S/jZHetR8n+8Q808g==", "dev": true }, "node_modules/@wdio/repl": { - "version": "8.23.1", - "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-8.23.1.tgz", - "integrity": "sha512-u6zG2cgBm67V5/WlQzadWqLGXs3moH8MOsgoljULQncelSBBZGZ5DyLB4p7jKcUAsKtMjgmFQmIvpQoqmyvdfg==", + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-8.24.12.tgz", + "integrity": "sha512-321F3sWafnlw93uRTSjEBVuvWCxTkWNDs7ektQS15drrroL3TMeFOynu4rDrIz0jXD9Vas0HCD2Tq/P0uxFLdw==", "dev": true, "dependencies": { "@types/node": "^20.1.0" @@ -1303,14 +1302,14 @@ } }, "node_modules/@wdio/reporter": { - "version": "8.24.2", - "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-8.24.2.tgz", - "integrity": "sha512-10hTEk4JIIXW8hnwyxTNgby1ZxoJAbXH9d/eMbkEoAwxx/eqaM+ghPs1GSrzOIjjZ3lwz369POEYfJcLkw3g2w==", + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-8.24.12.tgz", + "integrity": "sha512-FtLzDTBXdgxXf4T9HJQ2bNpYYSKEw//jojFm9XzB4fPwzPeFY3HC+dbePucVW1SSLrVzVxqIOyHiwCLqQ/4cQw==", "dev": true, "dependencies": { "@types/node": "^20.1.0", - "@wdio/logger": "8.16.17", - "@wdio/types": "8.24.2", + "@wdio/logger": "8.24.12", + "@wdio/types": "8.24.12", "diff": "^5.0.0", "object-inspect": "^1.12.0" }, @@ -1319,35 +1318,35 @@ } }, "node_modules/@wdio/runner": { - "version": "8.24.6", - "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.24.6.tgz", - "integrity": "sha512-2dt5F9scy0klYwB/E4JztLo04OaPsqcuZP9WKn+NSIBNug0UrgUcBv5ARJEuE3iUyPWpTeczWkU3UtcdMmjagQ==", + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.24.12.tgz", + "integrity": "sha512-wiwXZWG12YDe7GCYBnZ1xEg3UKi18Rvh4RNQiumjypDOErJit1hOCppbJ37LqLqQu+tfWGfN73j46yR7fQOCHw==", "dev": true, "dependencies": { "@types/node": "^20.1.0", - "@wdio/config": "8.24.6", - "@wdio/globals": "8.24.6", - "@wdio/logger": "8.16.17", - "@wdio/types": "8.24.2", - "@wdio/utils": "8.24.6", + "@wdio/config": "8.24.12", + "@wdio/globals": "8.24.12", + "@wdio/logger": "8.24.12", + "@wdio/types": "8.24.12", + "@wdio/utils": "8.24.12", "deepmerge-ts": "^5.0.0", "expect-webdriverio": "^4.6.1", "gaze": "^1.1.2", - "webdriver": "8.24.6", - "webdriverio": "8.24.6" + "webdriver": "8.24.12", + "webdriverio": "8.24.12" }, "engines": { "node": "^16.13 || >=18" } }, "node_modules/@wdio/spec-reporter": { - "version": "8.24.2", - "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-8.24.2.tgz", - "integrity": "sha512-FJ1+/kgxlw4ong+5PJJcOzLf8B/TzPL9hGeOF2xoWkk2uMsmNBJN2r27pjtfFxA41lw4q+Oav0Vb/RhdCB601Q==", + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-8.24.12.tgz", + "integrity": "sha512-Ng3ErWK8eESamCYwIr2Uv49+46RvmT8FnmGaJ6irJoAp101K8zENEs1pyqYHJReucN+ka/wM87blfc2k8NEHCA==", "dev": true, "dependencies": { - "@wdio/reporter": "8.24.2", - "@wdio/types": "8.24.2", + "@wdio/reporter": "8.24.12", + "@wdio/types": "8.24.12", "chalk": "^5.1.2", "easy-table": "^1.2.0", "pretty-ms": "^7.0.0" @@ -1369,9 +1368,9 @@ } }, "node_modules/@wdio/types": { - "version": "8.24.2", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.24.2.tgz", - "integrity": "sha512-x7iWF5NM8NfVxziGwLdQ+3sstgSxRoqfmmFEDTDps0oFrN5CgkqcoLkqXJ5u166gvpxpEq0gxZwxkbPC/Lp0cw==", + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.24.12.tgz", + "integrity": "sha512-SaD3OacDiW06DvSgAQ7sDBbpiI9qZRg7eoVYeBg3uSGVtUq84vTETRhhV7D6xTC00IqZu+mmN2TY5/q+7Gqy7w==", "dev": true, "dependencies": { "@types/node": "^20.1.0" @@ -1381,21 +1380,20 @@ } }, "node_modules/@wdio/utils": { - "version": "8.24.6", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.24.6.tgz", - "integrity": "sha512-qwcshLH9iKnhK0jXoXjPw3G02UhyShT0I+ljC0hMybJEBsra92TYFa47Cp6n1fdvM3+/BTuhsgtzRz0anObicQ==", + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.24.12.tgz", + "integrity": "sha512-uzwZyBVgqz0Wz1KL3aOUaQsxT8TNkzxti4NNTSMrU256qAPqc/n75rB7V73QASapCMpy70mZZTsuPgQYYj4ytQ==", "dev": true, "dependencies": { "@puppeteer/browsers": "^1.6.0", - "@wdio/logger": "8.16.17", - "@wdio/types": "8.24.2", + "@wdio/logger": "8.24.12", + "@wdio/types": "8.24.12", "decamelize": "^6.0.0", "deepmerge-ts": "^5.1.0", "edgedriver": "^5.3.5", "geckodriver": "^4.2.0", "get-port": "^7.0.0", - "got": "^13.0.0", - "import-meta-resolve": "^3.0.0", + "import-meta-resolve": "^4.0.0", "locate-app": "^2.1.0", "safaridriver": "^0.1.0", "split2": "^4.2.0", @@ -2457,129 +2455,10 @@ "node": ">=6" } }, - "node_modules/detect-package-manager": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/detect-package-manager/-/detect-package-manager-3.0.1.tgz", - "integrity": "sha512-qoHDH6+lMcpJPAScE7+5CYj91W0mxZNXTwZPrCqi1KMk+x+AoQScQ2V1QyqTln1rHU5Haq5fikvOGHv+leKD8A==", - "dev": true, - "dependencies": { - "execa": "^5.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/detect-package-manager/node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/detect-package-manager/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/detect-package-manager/node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/detect-package-manager/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/detect-package-manager/node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/detect-package-manager/node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-package-manager/node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/detect-package-manager/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/detect-package-manager/node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/devtools-protocol": { - "version": "0.0.1213968", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1213968.tgz", - "integrity": "sha512-o4n/beY+3CcZwFctYapjGelKptR4AuQT5gXS1Kvgbig+ArwkxK7f8wDVuD1wsoswiJWCwV6OK+Qb7vhNzNmABQ==", + "version": "0.0.1233178", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1233178.tgz", + "integrity": "sha512-jmMfyaqlzddwmDaSR1AQ+5ek+f7rupZdxKuPdkRcoxrZoF70Idg/4dTgXA08TLPmwAwB54gh49Wm2l/gRM0eUg==", "dev": true }, "node_modules/diff": { @@ -2927,15 +2806,15 @@ } }, "node_modules/eslint": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.54.0.tgz", - "integrity": "sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz", + "integrity": "sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.3", - "@eslint/js": "8.54.0", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.55.0", "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -4008,43 +3887,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/got": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/got/-/got-13.0.0.tgz", - "integrity": "sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==", - "dev": true, - "dependencies": { - "@sindresorhus/is": "^5.2.0", - "@szmarczak/http-timer": "^5.0.1", - "cacheable-lookup": "^7.0.0", - "cacheable-request": "^10.2.8", - "decompress-response": "^6.0.0", - "form-data-encoder": "^2.1.2", - "get-stream": "^6.0.1", - "http2-wrapper": "^2.1.10", - "lowercase-keys": "^3.0.0", - "p-cancelable": "^3.0.0", - "responselike": "^3.0.0" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, - "node_modules/got/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -4271,9 +4113,9 @@ } }, "node_modules/import-meta-resolve": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-3.0.0.tgz", - "integrity": "sha512-4IwhLhNNA8yy445rPjD/lWh++7hMDOml2eHtd58eG7h+qK3EryMuuRbsHGPikCoAgIkkDnckKfWSk2iDla/ejg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz", + "integrity": "sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==", "dev": true, "funding": { "type": "github", @@ -8616,20 +8458,20 @@ } }, "node_modules/webdriver": { - "version": "8.24.6", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.24.6.tgz", - "integrity": "sha512-k5XI2/SHd/14h4ElPQH8EzSUXujZIGbBEi+3dTS2H457KFR5Q8QYfIazDs/YnEdooOp8b6Oe9N7qI99LP8K6bQ==", + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.24.12.tgz", + "integrity": "sha512-03DQIClHoaAqTsmDkxGwo4HwHfkn9LzJ1wfNyUerzKg8DnyXeiT6ILqj6EXLfsvh5zddU2vhYGLFXSerPgkuOQ==", "dev": true, "dependencies": { "@types/node": "^20.1.0", "@types/ws": "^8.5.3", - "@wdio/config": "8.24.6", - "@wdio/logger": "8.16.17", - "@wdio/protocols": "8.23.0", - "@wdio/types": "8.24.2", - "@wdio/utils": "8.24.6", + "@wdio/config": "8.24.12", + "@wdio/logger": "8.24.12", + "@wdio/protocols": "8.24.12", + "@wdio/types": "8.24.12", + "@wdio/utils": "8.24.12", "deepmerge-ts": "^5.1.0", - "got": "^ 12.6.1", + "got": "^12.6.1", "ky": "^0.33.0", "ws": "^8.8.0" }, @@ -8675,25 +8517,25 @@ } }, "node_modules/webdriverio": { - "version": "8.24.6", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.24.6.tgz", - "integrity": "sha512-gJMAJiErbXe/oFJbV+H9lXp9GPxnUgHrbtxkG6SCKQlk1zPFho9FZ3fQWl/ty84w5n9ZMhAdnQIfZM9aytxIBQ==", + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.24.12.tgz", + "integrity": "sha512-Ddu0NNRMVkTzRzqvm3m0wt2eLUn+Plz2Cj+1QXDnVpddYJvk9J3elZC2hqNyscEtecQ+h2y3r36OcJqkl9jPag==", "dev": true, "dependencies": { "@types/node": "^20.1.0", - "@wdio/config": "8.24.6", - "@wdio/logger": "8.16.17", - "@wdio/protocols": "8.23.0", - "@wdio/repl": "8.23.1", - "@wdio/types": "8.24.2", - "@wdio/utils": "8.24.6", + "@wdio/config": "8.24.12", + "@wdio/logger": "8.24.12", + "@wdio/protocols": "8.24.12", + "@wdio/repl": "8.24.12", + "@wdio/types": "8.24.12", + "@wdio/utils": "8.24.12", "archiver": "^6.0.0", "aria-query": "^5.0.0", "css-shorthand-properties": "^1.1.1", "css-value": "^0.0.1", - "devtools-protocol": "^0.0.1213968", + "devtools-protocol": "^0.0.1233178", "grapheme-splitter": "^1.0.2", - "import-meta-resolve": "^3.0.0", + "import-meta-resolve": "^4.0.0", "is-plain-obj": "^4.1.0", "lodash.clonedeep": "^4.5.0", "lodash.zip": "^4.2.0", @@ -8703,7 +8545,7 @@ "resq": "^1.9.1", "rgb2hex": "0.2.5", "serialize-error": "^11.0.1", - "webdriver": "8.24.6" + "webdriver": "8.24.12" }, "engines": { "node": "^16.13 || >=18" diff --git a/tests/wdio/package.json b/tests/wdio/package.json index df6063633..6b9b5c1a2 100644 --- a/tests/wdio/package.json +++ b/tests/wdio/package.json @@ -4,13 +4,13 @@ "type": "module", "devDependencies": { "@trivago/prettier-plugin-sort-imports": "^4.3.0", - "@typescript-eslint/eslint-plugin": "^6.13.1", - "@typescript-eslint/parser": "^6.13.1", - "@wdio/cli": "^8.24.6", - "@wdio/local-runner": "^8.24.6", - "@wdio/mocha-framework": "^8.24.6", - "@wdio/spec-reporter": "^8.24.2", - "eslint": "^8.54.0", + "@typescript-eslint/eslint-plugin": "^6.13.2", + "@typescript-eslint/parser": "^6.13.2", + "@wdio/cli": "^8.24.13", + "@wdio/local-runner": "^8.24.12", + "@wdio/mocha-framework": "^8.24.12", + "@wdio/spec-reporter": "^8.24.12", + "eslint": "^8.55.0", "eslint-config-google": "^0.14.0", "eslint-plugin-sonarjs": "^0.23.0", "npm-run-all": "^4.1.5", diff --git a/web/package-lock.json b/web/package-lock.json index 132d277a7..a8be14feb 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -17,22 +17,22 @@ "@codemirror/theme-one-dark": "^6.1.2", "@formatjs/intl-listformat": "^7.5.3", "@fortawesome/fontawesome-free": "^6.5.1", - "@goauthentik/api": "^2023.10.4-1700591367", + "@goauthentik/api": "^2023.10.4-1701882394", "@lit-labs/context": "^0.4.0", "@lit-labs/task": "^3.1.0", "@lit/localize": "^0.11.4", "@open-wc/lit-helpers": "^0.6.0", "@patternfly/elements": "^2.4.0", "@patternfly/patternfly": "^4.224.2", - "@sentry/browser": "^7.84.0", - "@sentry/tracing": "^7.84.0", + "@sentry/browser": "^7.85.0", + "@sentry/tracing": "^7.85.0", "@webcomponents/webcomponentsjs": "^2.8.0", "base64-js": "^1.5.1", - "chart.js": "^4.4.0", + "chart.js": "^4.4.1", "chartjs-adapter-moment": "^1.0.1", "codemirror": "^6.0.1", "construct-style-sheets-polyfill": "^3.1.0", - "core-js": "^3.33.3", + "core-js": "^3.34.0", "country-flag-icons": "^1.5.9", "fuse.js": "^7.0.0", "lit": "^2.8.0", @@ -70,12 +70,12 @@ "@types/chart.js": "^2.9.41", "@types/codemirror": "5.60.15", "@types/grecaptcha": "^3.0.7", - "@typescript-eslint/eslint-plugin": "^6.13.1", - "@typescript-eslint/parser": "^6.13.1", + "@typescript-eslint/eslint-plugin": "^6.13.2", + "@typescript-eslint/parser": "^6.13.2", "babel-plugin-macros": "^3.1.0", "babel-plugin-tsconfig-paths": "^1.0.3", "cross-env": "^7.0.3", - "eslint": "^8.54.0", + "eslint": "^8.55.0", "eslint-config-google": "^0.14.0", "eslint-plugin-custom-elements": "0.0.8", "eslint-plugin-lit": "^1.10.1", @@ -2790,9 +2790,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz", - "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -2858,9 +2858,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.54.0.tgz", - "integrity": "sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz", + "integrity": "sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2944,9 +2944,9 @@ } }, "node_modules/@goauthentik/api": { - "version": "2023.10.4-1700591367", - "resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2023.10.4-1700591367.tgz", - "integrity": "sha512-ljC/SHH6ZgGC2qjvuA3gley8sRz9wVzr5FgRGKeqd1mi6G6TfnFYeA7tuuqgQc6WGN2MVMG17FnBraTI77Rl/A==" + "version": "2023.10.4-1701882394", + "resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2023.10.4-1701882394.tgz", + "integrity": "sha512-rYfJRl4IGQN6OGdWy6f+yMuNRPgmPjkPTI+6V1otR5NwgKrvZeEzy/8Vx1tEkz3CIix8rgZCmu3SmJVsep0ggQ==" }, "node_modules/@hcaptcha/types": { "version": "1.0.3", @@ -4768,85 +4768,99 @@ "win32" ] }, - "node_modules/@sentry-internal/tracing": { - "version": "7.84.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.84.0.tgz", - "integrity": "sha512-y9bGYA0OM6PEREfd+nk4UURZy29tpIw+7vQwpxWfEVs2fqq0/5TBFX/tKFb8AKUI9lVM8v0bcF0bNSCnuPQZHQ==", + "node_modules/@sentry-internal/feedback": { + "version": "7.85.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-7.85.0.tgz", + "integrity": "sha512-MlbIN+N8CWFJBjbqMmARe4+UPo9QRhRar0YoOfmNA2Xqk/EwXcjHWkealosHznXH7tqVbjB25QJpHtDystft/Q==", "dependencies": { - "@sentry/core": "7.84.0", - "@sentry/types": "7.84.0", - "@sentry/utils": "7.84.0" + "@sentry/core": "7.85.0", + "@sentry/types": "7.85.0", + "@sentry/utils": "7.85.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@sentry-internal/tracing": { + "version": "7.85.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.85.0.tgz", + "integrity": "sha512-p3YMUwkPCy2su9cm/3+7QYR4RiMI0+07DU1BZtht9NLTzY2O87/yvUbn1v2yHR3vJQTy/+7N0ud9/mPBFznRQQ==", + "dependencies": { + "@sentry/core": "7.85.0", + "@sentry/types": "7.85.0", + "@sentry/utils": "7.85.0" }, "engines": { "node": ">=8" } }, "node_modules/@sentry/browser": { - "version": "7.84.0", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.84.0.tgz", - "integrity": "sha512-X50TlTKY9WzAnHsYc4FYrCWgm+CdVo0h02ggmodVBUpRLUBjj+cs5Q1plov/v/XeorSwmorNEMUu/n+XZNSsrA==", + "version": "7.85.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.85.0.tgz", + "integrity": "sha512-x4sH7vTQnZQgy1U7NuN8XwhleAw7YMQitccHeC5m+kpIKGUO7w4Mdvu8rD3dnjmVmZvASpnwocAxy57/vCU6Ww==", "dependencies": { - "@sentry-internal/tracing": "7.84.0", - "@sentry/core": "7.84.0", - "@sentry/replay": "7.84.0", - "@sentry/types": "7.84.0", - "@sentry/utils": "7.84.0" + "@sentry-internal/feedback": "7.85.0", + "@sentry-internal/tracing": "7.85.0", + "@sentry/core": "7.85.0", + "@sentry/replay": "7.85.0", + "@sentry/types": "7.85.0", + "@sentry/utils": "7.85.0" }, "engines": { "node": ">=8" } }, "node_modules/@sentry/core": { - "version": "7.84.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.84.0.tgz", - "integrity": "sha512-tbuwunbBx2kSex15IHCqHDnrMfIlqPc6w/76fwkGqokz3oh9GSEGlLICwmBWL8AypWimUg13IDtFpD0TJTriWA==", + "version": "7.85.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.85.0.tgz", + "integrity": "sha512-DFDAc4tWmHN5IWhr7XbHCiyF1Xgb95jz8Uj/JTX9atlgodId1UIbER77qpEmH3eQGid/QBdqrlR98zCixgSbwg==", "dependencies": { - "@sentry/types": "7.84.0", - "@sentry/utils": "7.84.0" + "@sentry/types": "7.85.0", + "@sentry/utils": "7.85.0" }, "engines": { "node": ">=8" } }, "node_modules/@sentry/replay": { - "version": "7.84.0", - "resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.84.0.tgz", - "integrity": "sha512-c4PxT0ZpvkR9zXNfmAk3ojkm6eZ9+NlDze09RFBOCNo69QwIN90hnvbjXFC1+vRIJsfgo78Zr0ya/Wzb3Rog7Q==", + "version": "7.85.0", + "resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.85.0.tgz", + "integrity": "sha512-zVtTKfO+lu5qTwHpETI/oGo8hU3rdKHr3CdI1vRLw+d60PcAa/pWVlXsQeLRTw8PFwE358gHcpFZezj/11afew==", "dependencies": { - "@sentry-internal/tracing": "7.84.0", - "@sentry/core": "7.84.0", - "@sentry/types": "7.84.0", - "@sentry/utils": "7.84.0" + "@sentry-internal/tracing": "7.85.0", + "@sentry/core": "7.85.0", + "@sentry/types": "7.85.0", + "@sentry/utils": "7.85.0" }, "engines": { "node": ">=12" } }, "node_modules/@sentry/tracing": { - "version": "7.84.0", - "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.84.0.tgz", - "integrity": "sha512-NhBX28vUmCu/5avyGKX6B4UTm4MTOfbdg9ZzCnS7hPuWDfEAUIVHZVryi2q8bqp2DNGJvS9qIq/TSf39JIpdJg==", + "version": "7.85.0", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.85.0.tgz", + "integrity": "sha512-L3bpqiM+zu5f3o6zh6hx3xEzVENyhrkuMlpUOyDo0mUytqp763HqF1xz+R+trzze7R5VWrxJaRPARsCKlXu4Ig==", "dependencies": { - "@sentry-internal/tracing": "7.84.0" + "@sentry-internal/tracing": "7.85.0" }, "engines": { "node": ">=8" } }, "node_modules/@sentry/types": { - "version": "7.84.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.84.0.tgz", - "integrity": "sha512-VqGLIF3JOUrk7yIXjLXJvAORkZL1e3dDX0Q1okRehwyt/5CRE+mdUTeJZkBo9P9mBwgMyvtwklzOGGrzjb4eMA==", + "version": "7.85.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.85.0.tgz", + "integrity": "sha512-R5jR4XkK5tBU2jDiPdSVqzkmjYRr666bcGaFGUHB/xDQCjPsjk+pEmCCL+vpuWoaZmQJUE1hVU7rgnVX81w8zg==", "engines": { "node": ">=8" } }, "node_modules/@sentry/utils": { - "version": "7.84.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.84.0.tgz", - "integrity": "sha512-qdUVuxnRBvaf05AU+28R+xYtZmi/Ymf8os3Njq9g4XuA+QEkZLbzmIpRK5W9Ja7vUtjOeg29Xgg43A8znde9LQ==", + "version": "7.85.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.85.0.tgz", + "integrity": "sha512-JZ7seNOLvhjAQ8GeB3GYknPQJkuhF88xAYOaESZP3xPOWBMFUN+IO4RqjMqMLFDniOwsVQS7GB/MfP+hxufieg==", "dependencies": { - "@sentry/types": "7.84.0" + "@sentry/types": "7.85.0" }, "engines": { "node": ">=8" @@ -10526,16 +10540,16 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.1.tgz", - "integrity": "sha512-5bQDGkXaxD46bPvQt08BUz9YSaO4S0fB1LB5JHQuXTfkGPI3+UUeS387C/e9jRie5GqT8u5kFTrMvAjtX4O5kA==", + "version": "6.13.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.2.tgz", + "integrity": "sha512-3+9OGAWHhk4O1LlcwLBONbdXsAhLjyCFogJY/cWy2lxdVJ2JrcTF2pTGMaLl2AE7U1l31n8Py4a8bx5DLf/0dQ==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.13.1", - "@typescript-eslint/type-utils": "6.13.1", - "@typescript-eslint/utils": "6.13.1", - "@typescript-eslint/visitor-keys": "6.13.1", + "@typescript-eslint/scope-manager": "6.13.2", + "@typescript-eslint/type-utils": "6.13.2", + "@typescript-eslint/utils": "6.13.2", + "@typescript-eslint/visitor-keys": "6.13.2", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -10594,15 +10608,15 @@ "dev": true }, "node_modules/@typescript-eslint/parser": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.13.1.tgz", - "integrity": "sha512-fs2XOhWCzRhqMmQf0eicLa/CWSaYss2feXsy7xBD/pLyWke/jCIVc2s1ikEAtSW7ina1HNhv7kONoEfVNEcdDQ==", + "version": "6.13.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.13.2.tgz", + "integrity": "sha512-MUkcC+7Wt/QOGeVlM8aGGJZy1XV5YKjTpq9jK6r6/iLsGXhBVaGP5N0UYvFsu9BFlSpwY9kMretzdBH01rkRXg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.13.1", - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/typescript-estree": "6.13.1", - "@typescript-eslint/visitor-keys": "6.13.1", + "@typescript-eslint/scope-manager": "6.13.2", + "@typescript-eslint/types": "6.13.2", + "@typescript-eslint/typescript-estree": "6.13.2", + "@typescript-eslint/visitor-keys": "6.13.2", "debug": "^4.3.4" }, "engines": { @@ -10622,13 +10636,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.1.tgz", - "integrity": "sha512-BW0kJ7ceiKi56GbT2KKzZzN+nDxzQK2DS6x0PiSMPjciPgd/JRQGMibyaN2cPt2cAvuoH0oNvn2fwonHI+4QUQ==", + "version": "6.13.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.2.tgz", + "integrity": "sha512-CXQA0xo7z6x13FeDYCgBkjWzNqzBn8RXaE3QVQVIUm74fWJLkJkaHmHdKStrxQllGh6Q4eUGyNpMe0b1hMkXFA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/visitor-keys": "6.13.1" + "@typescript-eslint/types": "6.13.2", + "@typescript-eslint/visitor-keys": "6.13.2" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -10639,13 +10653,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.13.1.tgz", - "integrity": "sha512-A2qPlgpxx2v//3meMqQyB1qqTg1h1dJvzca7TugM3Yc2USDY+fsRBiojAEo92HO7f5hW5mjAUF6qobOPzlBCBQ==", + "version": "6.13.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.13.2.tgz", + "integrity": "sha512-Qr6ssS1GFongzH2qfnWKkAQmMUyZSyOr0W54nZNU1MDfo+U4Mv3XveeLZzadc/yq8iYhQZHYT+eoXJqnACM1tw==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.13.1", - "@typescript-eslint/utils": "6.13.1", + "@typescript-eslint/typescript-estree": "6.13.2", + "@typescript-eslint/utils": "6.13.2", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -10666,9 +10680,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.1.tgz", - "integrity": "sha512-gjeEskSmiEKKFIbnhDXUyiqVma1gRCQNbVZ1C8q7Zjcxh3WZMbzWVfGE9rHfWd1msQtPS0BVD9Jz9jded44eKg==", + "version": "6.13.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.2.tgz", + "integrity": "sha512-7sxbQ+EMRubQc3wTfTsycgYpSujyVbI1xw+3UMRUcrhSy+pN09y/lWzeKDbvhoqcRbHdc+APLs/PWYi/cisLPg==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -10679,13 +10693,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.1.tgz", - "integrity": "sha512-sBLQsvOC0Q7LGcUHO5qpG1HxRgePbT6wwqOiGLpR8uOJvPJbfs0mW3jPA3ujsDvfiVwVlWUDESNXv44KtINkUQ==", + "version": "6.13.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.2.tgz", + "integrity": "sha512-SuD8YLQv6WHnOEtKv8D6HZUzOub855cfPnPMKvdM/Bh1plv1f7Q/0iFUDLKKlxHcEstQnaUU4QZskgQq74t+3w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/visitor-keys": "6.13.1", + "@typescript-eslint/types": "6.13.2", + "@typescript-eslint/visitor-keys": "6.13.2", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -10739,17 +10753,17 @@ "dev": true }, "node_modules/@typescript-eslint/utils": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.1.tgz", - "integrity": "sha512-ouPn/zVoan92JgAegesTXDB/oUp6BP1v8WpfYcqh649ejNc9Qv+B4FF2Ff626kO1xg0wWwwG48lAJ4JuesgdOw==", + "version": "6.13.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.2.tgz", + "integrity": "sha512-b9Ptq4eAZUym4idijCRzl61oPCwwREcfDI8xGk751Vhzig5fFZR9CyzDz4Sp/nxSLBYxUPyh4QdIDqWykFhNmQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.13.1", - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/typescript-estree": "6.13.1", + "@typescript-eslint/scope-manager": "6.13.2", + "@typescript-eslint/types": "6.13.2", + "@typescript-eslint/typescript-estree": "6.13.2", "semver": "^7.5.4" }, "engines": { @@ -10797,12 +10811,12 @@ "dev": true }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.1.tgz", - "integrity": "sha512-NDhQUy2tg6XGNBGDRm1XybOHSia8mcXmlbKWoQP+nm1BIIMxa55shyJfZkHpEBN62KNPLrocSM2PdPcaLgDKMQ==", + "version": "6.13.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.2.tgz", + "integrity": "sha512-OGznFs0eAQXJsp+xSd6k/O1UbFi/K/L7WjqeRoFE7vadjAF9y0uppXhYNQNEqygjou782maGClOoZwPqF0Drlw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.1", + "@typescript-eslint/types": "6.13.2", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -11687,9 +11701,9 @@ } }, "node_modules/chart.js": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.0.tgz", - "integrity": "sha512-vQEj6d+z0dcsKLlQvbKIMYFHd3t8W/7L2vfJIbYcfyPcRx92CsHqECpueN8qVGNlKyDcr5wBrYAYKnfu/9Q1hQ==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.1.tgz", + "integrity": "sha512-C74QN1bxwV1v2PEujhmKjOZ7iUM4w6BWs23Md/6aOZZSlwMzeCIDGuZay++rBgChYru7/+QFeoQW0fQoP534Dg==", "dependencies": { "@kurkle/color": "^0.3.0" }, @@ -12092,9 +12106,9 @@ "dev": true }, "node_modules/core-js": { - "version": "3.33.3", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.33.3.tgz", - "integrity": "sha512-lo0kOocUlLKmm6kv/FswQL8zbkH7mVsLJ/FULClOhv8WRVmKLVcs6XPNQAzstfeJTCHMyButEwG+z1kHxHoDZw==", + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.34.0.tgz", + "integrity": "sha512-aDdvlDder8QmY91H88GzNi9EtQi2TjvQhpCX6B1v/dAZHU1AuLgHvRh54RiOerpEhEW46Tkf+vgAViB/CWC0ag==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -13362,15 +13376,15 @@ } }, "node_modules/eslint": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.54.0.tgz", - "integrity": "sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz", + "integrity": "sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.3", - "@eslint/js": "8.54.0", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.55.0", "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", diff --git a/web/package.json b/web/package.json index 1b3bb898b..7ad4ace20 100644 --- a/web/package.json +++ b/web/package.json @@ -38,22 +38,22 @@ "@codemirror/theme-one-dark": "^6.1.2", "@formatjs/intl-listformat": "^7.5.3", "@fortawesome/fontawesome-free": "^6.5.1", - "@goauthentik/api": "^2023.10.4-1700591367", + "@goauthentik/api": "^2023.10.4-1701882394", "@lit-labs/context": "^0.4.0", "@lit-labs/task": "^3.1.0", "@lit/localize": "^0.11.4", "@open-wc/lit-helpers": "^0.6.0", "@patternfly/elements": "^2.4.0", "@patternfly/patternfly": "^4.224.2", - "@sentry/browser": "^7.84.0", - "@sentry/tracing": "^7.84.0", + "@sentry/browser": "^7.85.0", + "@sentry/tracing": "^7.85.0", "@webcomponents/webcomponentsjs": "^2.8.0", "base64-js": "^1.5.1", - "chart.js": "^4.4.0", + "chart.js": "^4.4.1", "chartjs-adapter-moment": "^1.0.1", "codemirror": "^6.0.1", "construct-style-sheets-polyfill": "^3.1.0", - "core-js": "^3.33.3", + "core-js": "^3.34.0", "country-flag-icons": "^1.5.9", "fuse.js": "^7.0.0", "lit": "^2.8.0", @@ -91,12 +91,12 @@ "@types/chart.js": "^2.9.41", "@types/codemirror": "5.60.15", "@types/grecaptcha": "^3.0.7", - "@typescript-eslint/eslint-plugin": "^6.13.1", - "@typescript-eslint/parser": "^6.13.1", + "@typescript-eslint/eslint-plugin": "^6.13.2", + "@typescript-eslint/parser": "^6.13.2", "babel-plugin-macros": "^3.1.0", "babel-plugin-tsconfig-paths": "^1.0.3", "cross-env": "^7.0.3", - "eslint": "^8.54.0", + "eslint": "^8.55.0", "eslint-config-google": "^0.14.0", "eslint-plugin-custom-elements": "0.0.8", "eslint-plugin-lit": "^1.10.1", diff --git a/web/src/admin/applications/ApplicationListPage.ts b/web/src/admin/applications/ApplicationListPage.ts index 928e48b9b..fd8306c3f 100644 --- a/web/src/admin/applications/ApplicationListPage.ts +++ b/web/src/admin/applications/ApplicationListPage.ts @@ -89,17 +89,15 @@ export class ApplicationListPage extends TablePage { ]; } + renderSectionBefore(): TemplateResult { + return html``; + } + renderSidebarAfter(): TemplateResult { // Rendering the wizard with .open here, as if we set the attribute in // renderObjectCreate() it'll open two wizards, since that function gets called twice - /* Re-enable the wizard later: - */ - - return html`
+ return html`
diff --git a/web/src/admin/applications/wizard/BasePanel.ts b/web/src/admin/applications/wizard/BasePanel.ts index a395fc4b3..1b2db5bd4 100644 --- a/web/src/admin/applications/wizard/BasePanel.ts +++ b/web/src/admin/applications/wizard/BasePanel.ts @@ -1,5 +1,7 @@ import { WizardPanel } from "@goauthentik/components/ak-wizard-main/types"; import { AKElement } from "@goauthentik/elements/Base"; +import { KeyUnknown, serializeForm } from "@goauthentik/elements/forms/Form"; +import { HorizontalFormElement } from "@goauthentik/elements/forms/HorizontalFormElement"; import { CustomEmitterElement } from "@goauthentik/elements/utils/eventEmitter"; import { consume } from "@lit-labs/context"; @@ -10,6 +12,15 @@ import { styles as AwadStyles } from "./BasePanel.css"; import { applicationWizardContext } from "./ContextIdentity"; import type { ApplicationWizardState, ApplicationWizardStateUpdate } from "./types"; +/** + * Application Wizard Base Panel + * + * All of the displays in our system inherit from this object, which supplies the basic CSS for all + * the inputs we display, as well as the values and validity state for the form currently being + * displayed. + * + */ + export class ApplicationWizardPageBase extends CustomEmitterElement(AKElement) implements WizardPanel @@ -18,15 +29,41 @@ export class ApplicationWizardPageBase return AwadStyles; } - @query("form") - form!: HTMLFormElement; - - rendered = false; - @consume({ context: applicationWizardContext }) public wizard!: ApplicationWizardState; - // This used to be more complex; now it just establishes the event name. + @query("form") + form!: HTMLFormElement; + + /** + * Provide access to the values on the current form. Child implementations use this to craft the + * update that will be sent using `dispatchWizardUpdate` below. + */ + get formValues(): KeyUnknown | undefined { + const elements = [ + ...Array.from( + this.form.querySelectorAll("ak-form-element-horizontal"), + ), + ...Array.from(this.form.querySelectorAll("[data-ak-control=true]")), + ]; + return serializeForm(elements as unknown as NodeListOf); + } + + /** + * Provide access to the validity of the current form. Child implementations use this to craft + * the update that will be sent using `dispatchWizardUpdate` below. + */ + get valid() { + return this.form.checkValidity(); + } + + rendered = false; + + /** + * Provide a single source of truth for the token used to notify the orchestrator that an event + * happens. The token `ak-wizard-update` is used by the Wizard framework's reactive controller + * to route "data on the current step has changed" events to the orchestrator. + */ dispatchWizardUpdate(update: ApplicationWizardStateUpdate) { this.dispatchCustomEvent("ak-wizard-update", update); } diff --git a/web/src/admin/applications/wizard/ContextIdentity.ts b/web/src/admin/applications/wizard/ContextIdentity.ts index f03f147a6..629b65cd8 100644 --- a/web/src/admin/applications/wizard/ContextIdentity.ts +++ b/web/src/admin/applications/wizard/ContextIdentity.ts @@ -5,5 +5,3 @@ import { ApplicationWizardState } from "./types"; export const applicationWizardContext = createContext( Symbol("ak-application-wizard-state-context"), ); - -export default applicationWizardContext; diff --git a/web/src/admin/applications/wizard/ak-application-wizard.ts b/web/src/admin/applications/wizard/ak-application-wizard.ts index d15570f13..50cf750d7 100644 --- a/web/src/admin/applications/wizard/ak-application-wizard.ts +++ b/web/src/admin/applications/wizard/ak-application-wizard.ts @@ -1,4 +1,3 @@ -import { merge } from "@goauthentik/common/merge"; import { AkWizard } from "@goauthentik/components/ak-wizard-main/AkWizard"; import { CustomListenerElement } from "@goauthentik/elements/utils/eventEmitter"; @@ -6,7 +5,7 @@ import { ContextProvider } from "@lit-labs/context"; import { msg } from "@lit/localize"; import { customElement, state } from "lit/decorators.js"; -import applicationWizardContext from "./ContextIdentity"; +import { applicationWizardContext } from "./ContextIdentity"; import { newSteps } from "./steps"; import { ApplicationStep, @@ -15,10 +14,11 @@ import { OneOfProvider, } from "./types"; -const freshWizardState = () => ({ +const freshWizardState = (): ApplicationWizardState => ({ providerModel: "", app: {}, provider: {}, + errors: {}, }); @customElement("ak-application-wizard") @@ -56,28 +56,6 @@ export class ApplicationWizard extends CustomListenerElement( */ providerCache: Map = new Map(); - maybeProviderSwap(providerModel: string | undefined): boolean { - if ( - providerModel === undefined || - typeof providerModel !== "string" || - providerModel === this.wizardState.providerModel - ) { - return false; - } - - this.providerCache.set(this.wizardState.providerModel, this.wizardState.provider); - const prevProvider = this.providerCache.get(providerModel); - this.wizardState.provider = prevProvider ?? { - name: `Provider for ${this.wizardState.app.name}`, - }; - const method = this.steps.find(({ id }) => id === "provider-details"); - if (!method) { - throw new Error("Could not find Authentication Method page?"); - } - method.disabled = false; - return true; - } - // And this is where all the special cases go... handleUpdate(detail: ApplicationWizardStateUpdate) { if (detail.status === "submitted") { @@ -87,17 +65,26 @@ export class ApplicationWizard extends CustomListenerElement( } this.step.valid = this.step.valid || detail.status === "valid"; - const update = detail.update; + if (!update) { return; } - if (this.maybeProviderSwap(update.providerModel)) { - this.requestUpdate(); + // When the providerModel enum changes, retrieve the customer's prior work for *this* wizard + // session (and only this wizard session) or provide an empty model with a default provider + // name. + if (update.providerModel && update.providerModel !== this.wizardState.providerModel) { + const requestedProvider = this.providerCache.get(update.providerModel) ?? { + name: `Provider for ${this.wizardState.app.name}`, + }; + if (this.wizardState.providerModel) { + this.providerCache.set(this.wizardState.providerModel, this.wizardState.provider); + } + update.provider = requestedProvider; } - this.wizardState = merge(this.wizardState, update) as ApplicationWizardState; + this.wizardState = update as ApplicationWizardState; this.wizardStateProvider.setValue(this.wizardState); this.requestUpdate(); } diff --git a/web/src/admin/applications/wizard/ak-wizard-title.ts b/web/src/admin/applications/wizard/ak-wizard-title.ts new file mode 100644 index 000000000..cd2157a69 --- /dev/null +++ b/web/src/admin/applications/wizard/ak-wizard-title.ts @@ -0,0 +1,30 @@ +import { AKElement } from "@goauthentik/elements/Base"; + +import { css, html } from "lit"; +import { customElement } from "lit/decorators.js"; + +import PFContent from "@patternfly/patternfly/components/Content/content.css"; +import PFTitle from "@patternfly/patternfly/components/Title/title.css"; + +@customElement("ak-wizard-title") +export class AkWizardTitle extends AKElement { + static get styles() { + return [ + PFContent, + PFTitle, + css` + .ak-bottom-spacing { + padding-bottom: var(--pf-global--spacer--lg); + } + `, + ]; + } + + render() { + return html`
+

+
`; + } +} + +export default AkWizardTitle; diff --git a/web/src/admin/applications/wizard/application/ak-application-wizard-application-details.ts b/web/src/admin/applications/wizard/application/ak-application-wizard-application-details.ts index 13e439d76..c89c555a0 100644 --- a/web/src/admin/applications/wizard/application/ak-application-wizard-application-details.ts +++ b/web/src/admin/applications/wizard/application/ak-application-wizard-application-details.ts @@ -5,7 +5,6 @@ import "@goauthentik/components/ak-slug-input"; import "@goauthentik/components/ak-switch-input"; import "@goauthentik/components/ak-text-input"; import "@goauthentik/elements/forms/FormGroup"; -import "@goauthentik/elements/forms/FormGroup"; import "@goauthentik/elements/forms/HorizontalFormElement"; import { msg } from "@lit/localize"; @@ -17,28 +16,20 @@ import BasePanel from "../BasePanel"; @customElement("ak-application-wizard-application-details") export class ApplicationWizardApplicationDetails extends BasePanel { - handleChange(ev: Event) { - if (!ev.target) { - console.warn(`Received event with no target: ${ev}`); - return; + handleChange(_ev: Event) { + const formValues = this.formValues; + if (!formValues) { + throw new Error("No application values on form?"); } - - const target = ev.target as HTMLInputElement; - const value = target.type === "checkbox" ? target.checked : target.value; this.dispatchWizardUpdate({ update: { - app: { - [target.name]: value, - }, + ...this.wizard, + app: formValues, }, - status: this.form.checkValidity() ? "valid" : "invalid", + status: this.valid ? "valid" : "invalid", }); } - validator() { - return this.form.reportValidity(); - } - render(): TemplateResult { return html`
${msg("UI Settings")} @@ -82,6 +77,7 @@ export class ApplicationWizardApplicationDetails extends BasePanel { help=${msg( "If left empty, authentik will try to extract the launch URL based on the selected provider.", )} + .errorMessages=${this.wizard.errors.app?.metaLaunchUrl ?? []} > TemplateResult; type ModelConverter = (provider: OneOfProvider) => ModelRequest; +/** + * There's an internal key and an API key because "Proxy" has three different subtypes. + */ +// prettier-ignore type ProviderType = [ - string, - string, - string, - ProviderRenderer, - ProviderModelEnumType, - ModelConverter, + string, // internal key used by the wizard to distinguish between providers + string, // Name of the provider + string, // Description + ProviderRenderer, // Function that returns the provider's wizard panel as a TemplateResult + ProviderModelEnumType, // key used by the API to distinguish between providers + ModelConverter, // Handler that takes a generic provider and returns one specifically typed to its panel ]; export type LocalTypeCreate = TypeCreate & { diff --git a/web/src/admin/applications/wizard/auth-method-choice/ak-application-wizard-authentication-method-choice.ts b/web/src/admin/applications/wizard/auth-method-choice/ak-application-wizard-authentication-method-choice.ts index 593406d66..e13e11eca 100644 --- a/web/src/admin/applications/wizard/auth-method-choice/ak-application-wizard-authentication-method-choice.ts +++ b/web/src/admin/applications/wizard/auth-method-choice/ak-application-wizard-authentication-method-choice.ts @@ -25,19 +25,15 @@ export class ApplicationWizardAuthenticationMethodChoice extends BasePanel { handleChoice(ev: InputEvent) { const target = ev.target as HTMLInputElement; this.dispatchWizardUpdate({ - update: { providerModel: target.value }, - status: this.validator() ? "valid" : "invalid", + update: { + ...this.wizard, + providerModel: target.value, + errors: {}, + }, + status: this.valid ? "valid" : "invalid", }); } - validator() { - const radios = Array.from(this.form.querySelectorAll('input[type="radio"]')); - const chosen = radios.find( - (radio: Element) => radio instanceof HTMLInputElement && radio.checked, - ); - return !!chosen; - } - renderProvider(type: LocalTypeCreate) { const method = this.wizard.providerModel; diff --git a/web/src/admin/applications/wizard/commit/ak-application-wizard-commit-application.ts b/web/src/admin/applications/wizard/commit/ak-application-wizard-commit-application.ts index aa21bd073..2258aa7b2 100644 --- a/web/src/admin/applications/wizard/commit/ak-application-wizard-commit-application.ts +++ b/web/src/admin/applications/wizard/commit/ak-application-wizard-commit-application.ts @@ -18,12 +18,14 @@ import PFTitle from "@patternfly/patternfly/components/Title/title.css"; import PFBullseye from "@patternfly/patternfly/layouts/Bullseye/bullseye.css"; import { - ApplicationRequest, + type ApplicationRequest, CoreApi, - TransactionApplicationRequest, - TransactionApplicationResponse, + type ModelRequest, + type TransactionApplicationRequest, + type TransactionApplicationResponse, + ValidationError, + ValidationErrorFromJSON, } from "@goauthentik/api"; -import type { ModelRequest } from "@goauthentik/api"; import BasePanel from "../BasePanel"; import providerModelsList from "../auth-method-choice/ak-application-wizard-authentication-method-choice.choices"; @@ -88,7 +90,7 @@ export class ApplicationWizardCommitApplication extends BasePanel { commitState: State = idleState; @state() - errors: string[] = []; + errors?: ValidationError; response?: TransactionApplicationResponse; @@ -121,27 +123,10 @@ export class ApplicationWizardCommitApplication extends BasePanel { } } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - decodeErrors(body: Record) { - const spaceify = (src: Record) => - Object.values(src).map((msg) => `\u00a0\u00a0\u00a0\u00a0${msg}`); - - let errs: string[] = []; - if (body["app"] !== undefined) { - errs = [...errs, msg("In the Application:"), ...spaceify(body["app"])]; - } - if (body["provider"] !== undefined) { - errs = [...errs, msg("In the Provider:"), ...spaceify(body["provider"])]; - } - console.log(body, errs); - return errs; - } - async send( data: TransactionApplicationRequest, ): Promise { - this.errors = []; - + this.errors = undefined; new CoreApi(DEFAULT_CONFIG) .coreTransactionalApplicationsUpdate({ transactionApplicationRequest: data, @@ -153,18 +138,57 @@ export class ApplicationWizardCommitApplication extends BasePanel { this.commitState = successState; }) // eslint-disable-next-line @typescript-eslint/no-explicit-any - .catch((resolution: any) => { - resolution.response.json().then( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (body: Record) => { - this.errors = this.decodeErrors(body); + .catch(async (resolution: any) => { + const errors = (this.errors = ValidationErrorFromJSON( + await resolution.response.json(), + )); + this.dispatchWizardUpdate({ + update: { + ...this.wizard, + errors, }, - ); + status: "failed", + }); this.commitState = errorState; }); } - render(): TemplateResult { + renderErrors(errors?: ValidationError) { + if (!errors) { + return nothing; + } + + const navTo = (step: number) => () => + this.dispatchCustomEvent("ak-wizard-nav", { + command: "goto", + step, + }); + + if (errors.app) { + return html`

${msg("There was an error in the application.")}

+

${msg("Review the application.")}

`; + } + if (errors.provider) { + return html`

${msg("There was an error in the provider.")}

+

${msg("Review the provider.")}

`; + } + if (errors.detail) { + return html`

${msg("There was an error")}: ${errors.detail}

`; + } + if ((errors?.nonFieldErrors ?? []).length > 0) { + return html`

$(msg("There was an error")}:

+
    + ${(errors.nonFieldErrors ?? []).map((e: string) => html`
  • ${e}
  • `)} +
`; + } + return html`

+ ${msg( + "There was an error creating the application, but no error message was sent. Please review the server logs.", + )} +

`; + } + + render() { const icon = classMap( this.commitState.icon.reduce((acc, icon) => ({ ...acc, [icon]: true }), {}), ); @@ -184,13 +208,7 @@ export class ApplicationWizardCommitApplication extends BasePanel { > ${this.commitState.label} - ${this.errors.length > 0 - ? html`
    - ${this.errors.map( - (msg) => html`
  • ${msg}
  • `, - )} -
` - : nothing} + ${this.renderErrors(this.errors)}
diff --git a/web/src/admin/applications/wizard/methods/BaseProviderPanel.ts b/web/src/admin/applications/wizard/methods/BaseProviderPanel.ts index ce60213e1..a8044c981 100644 --- a/web/src/admin/applications/wizard/methods/BaseProviderPanel.ts +++ b/web/src/admin/applications/wizard/methods/BaseProviderPanel.ts @@ -1,26 +1,19 @@ import BasePanel from "../BasePanel"; export class ApplicationWizardProviderPageBase extends BasePanel { - handleChange(ev: InputEvent) { - if (!ev.target) { - console.warn(`Received event with no target: ${ev}`); - return; + handleChange(_ev: InputEvent) { + const formValues = this.formValues; + if (!formValues) { + throw new Error("No provider values on form?"); } - const target = ev.target as HTMLInputElement; - const value = target.type === "checkbox" ? target.checked : target.value; this.dispatchWizardUpdate({ update: { - provider: { - [target.name]: value, - }, + ...this.wizard, + provider: formValues, }, - status: this.form.checkValidity() ? "valid" : "invalid", + status: this.valid ? "valid" : "invalid", }); } - - validator() { - return this.form.reportValidity(); - } } export default ApplicationWizardProviderPageBase; diff --git a/web/src/admin/applications/wizard/methods/ldap/ak-application-wizard-authentication-by-ldap.ts b/web/src/admin/applications/wizard/methods/ldap/ak-application-wizard-authentication-by-ldap.ts index 58405f575..f95972e0a 100644 --- a/web/src/admin/applications/wizard/methods/ldap/ak-application-wizard-authentication-by-ldap.ts +++ b/web/src/admin/applications/wizard/methods/ldap/ak-application-wizard-authentication-by-ldap.ts @@ -1,3 +1,4 @@ +import "@goauthentik/admin/applications/wizard/ak-wizard-title"; import "@goauthentik/admin/common/ak-core-group-search"; import "@goauthentik/admin/common/ak-crypto-certificate-search"; import "@goauthentik/admin/common/ak-flow-search/ak-branded-flow-search"; @@ -34,112 +35,132 @@ import { export class ApplicationWizardApplicationDetails extends BaseProviderPanel { render() { const provider = this.wizard.provider as LDAPProvider | undefined; + const errors = this.wizard.errors.provider; - return html` - - - - ${msg("Configure LDAP Provider")} + + -

${msg("Flow used for users to authenticate.")}

-
+ help=${msg("Method's display Name.")} + > - - + +

+ ${msg("Flow used for users to authenticate.")} +

+
+ + -

${groupHelp}

-
+ .errorMessages=${errors?.searchGroup ?? []} + > + +

${groupHelp}

+ - - + + - - + + - - + + - - ${msg("Protocol settings")} -
- - - - - + ${msg("Protocol settings")} +
+ - -

${cryptoCertificateHelp}

- +
- + + + +

${cryptoCertificateHelp}

+
- + - -
- - `; + + + +
+
+ `; } } diff --git a/web/src/admin/applications/wizard/methods/oauth/ak-application-wizard-authentication-by-oauth.ts b/web/src/admin/applications/wizard/methods/oauth/ak-application-wizard-authentication-by-oauth.ts index 51fb7ca28..be8300b97 100644 --- a/web/src/admin/applications/wizard/methods/oauth/ak-application-wizard-authentication-by-oauth.ts +++ b/web/src/admin/applications/wizard/methods/oauth/ak-application-wizard-authentication-by-oauth.ts @@ -1,3 +1,4 @@ +import "@goauthentik/admin/applications/wizard/ak-wizard-title"; import "@goauthentik/admin/common/ak-crypto-certificate-search"; import "@goauthentik/admin/common/ak-flow-search/ak-branded-flow-search"; import { @@ -27,10 +28,10 @@ import { PropertymappingsApi, SourcesApi, } from "@goauthentik/api"; -import type { - OAuth2Provider, - PaginatedOAuthSourceList, - PaginatedScopeMappingList, +import { + type OAuth2Provider, + type PaginatedOAuthSourceList, + type PaginatedScopeMappingList, } from "@goauthentik/api"; import BaseProviderPanel from "../BaseProviderPanel"; @@ -38,7 +39,7 @@ import BaseProviderPanel from "../BaseProviderPanel"; @customElement("ak-application-wizard-authentication-by-oauth") export class ApplicationWizardAuthenticationByOauth extends BaseProviderPanel { @state() - showClientSecret = false; + showClientSecret = true; @state() propertyMappings?: PaginatedScopeMappingList; @@ -68,234 +69,254 @@ export class ApplicationWizardAuthenticationByOauth extends BaseProviderPanel { render() { const provider = this.wizard.provider as OAuth2Provider | undefined; + const errors = this.wizard.errors.provider; - return html`
- - - - ${msg("Configure OAuth2/OpenId Provider")} + + -

- ${msg("Flow used when a user access this provider and is not authenticated.")} -

-
+ > - - -

- ${msg("Flow used when authorizing this provider.")} -

-
- - - ${msg("Protocol settings")} -
- + ) => { - this.showClientSecret = ev.detail.value !== ClientTypeEnum.Public; - }} - .options=${clientTypeOptions} - > - - - +

+ ${msg( + "Flow used when a user access this provider and is not authenticated.", + )} +

+ + + -
+ > +

+ ${msg("Flow used when authorizing this provider.")} +

+ - - - - - - - - + ${msg("Protocol settings")} +
+ ) => { + this.showClientSecret = ev.detail.value !== ClientTypeEnum.Public; + }} + .options=${clientTypeOptions} > - -

${msg("Key used to sign the tokens.")}

- -
- + - - ${msg("Advanced protocol settings")} -
- - ${msg("Configure how long access codes are valid for.")} + + + + + + + + + + + + +

+ ${msg("Key used to sign the tokens.")}

- `} - > -
+ +
+
- - ${msg("Configure how long access tokens are valid for.")} + + ${msg("Advanced protocol settings")} +
+ + ${msg("Configure how long access codes are valid for.")} +

+ `} + > +
+ + + ${msg("Configure how long access tokens are valid for.")} +

+ `} + > +
+ + + ${msg("Configure how long refresh tokens are valid for.")} +

+ `} + > +
+ + + +

+ ${msg( + "Select which scopes can be used by the client. The client still has to specify the scope to access the data.", + )}

- `} - > - - - - ${msg("Configure how long refresh tokens are valid for.")} +

+ ${msg("Hold control/command to select multiple items.")}

- `} - > -
+
- - + ${this.oauthSources?.results.map((source) => { + const selected = (provider?.jwksSources || []).some((su) => { + return su == source.pk; }); - } - return html``; - })} - -

- ${msg( - "Select which scopes can be used by the client. The client still has to specify the scope to access the data.", - )} -

-

- ${msg("Hold control/command to select multiple items.")} -

-
- - - - - - -
-
- - - ${msg("Machine-to-Machine authentication settings")} -
- - -

- ${msg( - "JWTs signed by certificates configured in the selected sources can be used to authenticate to this provider.", - )} -

-

- ${msg("Hold control/command to select multiple items.")} -

-
-
-
- `; + return html``; + })} + +

+ ${msg( + "JWTs signed by certificates configured in the selected sources can be used to authenticate to this provider.", + )} +

+

+ ${msg("Hold control/command to select multiple items.")} +

+
+
+
+ `; } } diff --git a/web/src/admin/applications/wizard/methods/proxy/AuthenticationByProxyPage.ts b/web/src/admin/applications/wizard/methods/proxy/AuthenticationByProxyPage.ts index 4b32fe2d7..90d0f7580 100644 --- a/web/src/admin/applications/wizard/methods/proxy/AuthenticationByProxyPage.ts +++ b/web/src/admin/applications/wizard/methods/proxy/AuthenticationByProxyPage.ts @@ -1,3 +1,4 @@ +import "@goauthentik/admin/applications/wizard/ak-wizard-title"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { first } from "@goauthentik/common/utils"; import "@goauthentik/components/ak-switch-input"; @@ -61,11 +62,11 @@ export class AkTypeProxyApplicationWizardPage extends BaseProviderPanel { return nothing; } - renderProxyMode() { - return html`

This space intentionally left blank

`; + renderProxyMode(): TemplateResult { + throw new Error("Must be implemented in a child class."); } - renderHttpBasic(): TemplateResult { + renderHttpBasic() { return html``; } + scopeMappingConfiguration(provider?: ProxyProvider) { + const propertyMappings = this.propertyMappings?.results ?? []; + + const defaultScopes = () => + propertyMappings + .filter((scope) => !(scope?.managed ?? "").startsWith("goauthentik.io/providers")) + .map((pm) => pm.pk); + + const configuredScopes = (providerMappings: string[]) => + propertyMappings.map((scope) => scope.pk).filter((pk) => providerMappings.includes(pk)); + + const scopeValues = provider?.propertyMappings + ? configuredScopes(provider?.propertyMappings ?? []) + : defaultScopes(); + + const scopePairs = propertyMappings.map((scope) => [scope.pk, scope.name]); + + return { scopePairs, scopeValues }; + } + render() { - return html`
- ${this.renderModeDescription()} - + const errors = this.wizard.errors.provider; + const { scopePairs, scopeValues } = this.scopeMappingConfiguration(this.instance); - - ${msg("Configure Proxy Provider")} + + ${this.renderModeDescription()} + -

- ${msg("Flow used when a user access this provider and is not authenticated.")} -

-
+ .errorMessages=${errors?.name ?? []} + label=${msg("Name")} + > - - -

- ${msg("Flow used when authorizing this provider.")} -

-
+ + +

+ ${msg( + "Flow used when a user access this provider and is not authenticated.", + )} +

+
- ${this.renderProxyMode()} + + +

+ ${msg("Flow used when authorizing this provider.")} +

+
- + ${this.renderProxyMode()} - - ${msg("Advanced protocol settings")} -
- - - + - - + ${this.oauthSources?.results.map((source) => { + const selected = (this.instance?.jwksSources || []).some( (su) => { - return su == scope.pk; + return su == source.pk; }, ); - return html``; })} - -

- ${msg("Additional scope mappings, which are passed to the proxy.")} -

-

- ${msg("Hold control/command to select multiple items.")} -

-
- - + +

${msg( - "Regular expressions for which authentication is not required. Each new line is interpreted as a new expression.", + "JWTs signed by certificates configured in the selected sources can be used to authenticate to this provider.", )}

- ${msg( - "When using proxy or forward auth (single application) mode, the requested URL Path is checked against the regular expressions. When using forward auth (domain mode), the full requested URL including scheme and host is matched against the regular expressions.", - )} -

`} - > -
-
-
- - ${msg("Authentication settings")} -
- - - { - const el = ev.target as HTMLInputElement; - this.showHttpBasic = el.checked; - }} - label=${msg("Send HTTP-Basic Authentication")} - help=${msg( - "Send a custom HTTP-Basic Authentication header based on values from authentik.", - )} - > - - ${this.showHttpBasic ? this.renderHttpBasic() : html``} - - - -

- ${msg( - "JWTs signed by certificates configured in the selected sources can be used to authenticate to this provider.", - )} -

-

- ${msg("Hold control/command to select multiple items.")} -

-
-
-
-
`; + ${msg("Hold control/command to select multiple items.")} +

+ +
+ + `; } } diff --git a/web/src/admin/applications/wizard/methods/proxy/ak-application-wizard-authentication-for-forward-domain-proxy.ts b/web/src/admin/applications/wizard/methods/proxy/ak-application-wizard-authentication-for-forward-domain-proxy.ts index e794cd7d4..63510ad11 100644 --- a/web/src/admin/applications/wizard/methods/proxy/ak-application-wizard-authentication-for-forward-domain-proxy.ts +++ b/web/src/admin/applications/wizard/methods/proxy/ak-application-wizard-authentication-for-forward-domain-proxy.ts @@ -5,6 +5,8 @@ import { customElement } from "@lit/reactive-element/decorators.js"; import { html } from "lit"; import { ifDefined } from "lit/directives/if-defined.js"; +import { ProxyProvider } from "@goauthentik/api"; + import AkTypeProxyApplicationWizardPage from "./AuthenticationByProxyPage"; @customElement("ak-application-wizard-authentication-for-forward-proxy-domain") @@ -28,11 +30,15 @@ export class AkForwardDomainProxyApplicationWizardPage extends AkTypeProxyApplic } renderProxyMode() { + const provider = this.wizard.provider as ProxyProvider | undefined; + const errors = this.wizard.errors.provider; + return html` diff --git a/web/src/admin/applications/wizard/methods/proxy/ak-application-wizard-authentication-for-single-forward-proxy.ts b/web/src/admin/applications/wizard/methods/proxy/ak-application-wizard-authentication-for-single-forward-proxy.ts index 0840c698f..5680d1e59 100644 --- a/web/src/admin/applications/wizard/methods/proxy/ak-application-wizard-authentication-for-single-forward-proxy.ts +++ b/web/src/admin/applications/wizard/methods/proxy/ak-application-wizard-authentication-for-single-forward-proxy.ts @@ -5,6 +5,8 @@ import { customElement } from "@lit/reactive-element/decorators.js"; import { html } from "lit"; import { ifDefined } from "lit/directives/if-defined.js"; +import { ProxyProvider } from "@goauthentik/api"; + import AkTypeProxyApplicationWizardPage from "./AuthenticationByProxyPage"; @customElement("ak-application-wizard-authentication-for-single-forward-proxy") @@ -21,11 +23,15 @@ export class AkForwardSingleProxyApplicationWizardPage extends AkTypeProxyApplic } renderProxyMode() { + const provider = this.wizard.provider as ProxyProvider | undefined; + const errors = this.wizard.errors.provider; + return html` - - - - - ${msg("Configure Radius Provider")} +
+ -

${msg("Flow used for users to authenticate.")}

- + > +
- - ${msg("Protocol settings")} -
- + - +

+ ${msg("Flow used for users to authenticate.")} +

+ + + + ${msg("Protocol settings")} +
+ + -
-
- `; + >
+
+
+ `; } } diff --git a/web/src/admin/applications/wizard/methods/saml/ak-application-wizard-authentication-by-saml-configuration.ts b/web/src/admin/applications/wizard/methods/saml/ak-application-wizard-authentication-by-saml-configuration.ts index 944a6d3de..82024157e 100644 --- a/web/src/admin/applications/wizard/methods/saml/ak-application-wizard-authentication-by-saml-configuration.ts +++ b/web/src/admin/applications/wizard/methods/saml/ak-application-wizard-authentication-by-saml-configuration.ts @@ -1,7 +1,10 @@ +import "@goauthentik/admin/applications/wizard/ak-wizard-title"; +import "@goauthentik/admin/applications/wizard/ak-wizard-title"; import "@goauthentik/admin/common/ak-core-group-search"; import "@goauthentik/admin/common/ak-crypto-certificate-search"; import "@goauthentik/admin/common/ak-flow-search/ak-branded-flow-search"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; +import "@goauthentik/components/ak-multi-select"; import "@goauthentik/components/ak-number-input"; import "@goauthentik/components/ak-radio-input"; import "@goauthentik/components/ak-switch-input"; @@ -10,7 +13,7 @@ import "@goauthentik/elements/forms/FormGroup"; import "@goauthentik/elements/forms/HorizontalFormElement"; import { msg } from "@lit/localize"; -import { customElement } from "@lit/reactive-element/decorators/custom-element.js"; +import { customElement, state } from "@lit/reactive-element/decorators.js"; import { html } from "lit"; import { ifDefined } from "lit/directives/if-defined.js"; @@ -27,9 +30,11 @@ import { signatureAlgorithmOptions, spBindingOptions, } from "./SamlProviderOptions"; +import "./saml-property-mappings-search"; @customElement("ak-application-wizard-authentication-by-saml-configuration") export class ApplicationWizardProviderSamlConfiguration extends BaseProviderPanel { + @state() propertyMappings?: PaginatedSAMLPropertyMappingList; constructor() { @@ -43,207 +48,229 @@ export class ApplicationWizardProviderSamlConfiguration extends BaseProviderPane }); } + propertyMappingConfiguration(provider?: SAMLProvider) { + const propertyMappings = this.propertyMappings?.results ?? []; + + const configuredMappings = (providerMappings: string[]) => + propertyMappings.map((pm) => pm.pk).filter((pmpk) => providerMappings.includes(pmpk)); + + const managedMappings = () => + propertyMappings + .filter((pm) => (pm?.managed ?? "").startsWith("goauthentik.io/providers/saml")) + .map((pm) => pm.pk); + + const pmValues = provider?.propertyMappings + ? configuredMappings(provider?.propertyMappings ?? []) + : managedMappings(); + + const propertyPairs = propertyMappings.map((pm) => [pm.pk, pm.name]); + + return { pmValues, propertyPairs }; + } + render() { const provider = this.wizard.provider as SAMLProvider | undefined; + const errors = this.wizard.errors.provider; - return html`
- + const { pmValues, propertyPairs } = this.propertyMappingConfiguration(provider); - - ${msg("Configure SAML Provider")} + + -

- ${msg("Flow used when a user access this provider and is not authenticated.")} -

-
+ label=${msg("Name")} + .errorMessages=${errors?.name ?? []} + > - - -

- ${msg("Flow used when authorizing this provider.")} -

-
- - - ${msg("Protocol settings")} -
- + - - - - +

+ ${msg( + "Flow used when a user access this provider and is not authenticated.", )} - > - +

+ - -
-
+ + +

+ ${msg("Flow used when authorizing this provider.")} +

+
- - ${msg("Advanced protocol settings")} -
- - -

- ${msg( - "Certificate used to sign outgoing Responses going to the Service Provider.", + + ${msg("Protocol settings")} +

+ + + + + - + > + - - -

- ${msg( - "When selected, incoming assertion's Signatures will be validated against this certificate. To allow unsigned Requests, leave on default.", - )} -

-
+ +
+ - - -

- ${msg("Hold control/command to select multiple items.")} -

-
+ + ${msg("Advanced protocol settings")} +
+ + +

+ ${msg( + "Certificate used to sign outgoing Responses going to the Service Provider.", + )} +

+
- - + +

+ ${msg( + "When selected, incoming assertion's Signatures will be validated against this certificate. To allow unsigned Requests, leave on default.", + )} +

+
+ + + ${msg("Property mappings used for user mapping.")} +

+

+ ${msg("Hold control/command to select multiple items.")} +

`} + >
+ + -

- ${msg( - "Configure how the NameID value will be created. When left empty, the NameIDPolicy of the incoming request will be respected.", + > + +

+ ${msg( + "Configure how the NameID value will be created. When left empty, the NameIDPolicy of the incoming request will be respected.", + )} +

+
+ + - + .errorMessages=${errors?.assertionValidNotBefore ?? []} + > - + - + - + + - - - - - -
-
- `; + + +
+
+ `; } } diff --git a/web/src/admin/applications/wizard/methods/saml/ak-application-wizard-authentication-by-saml-import.ts b/web/src/admin/applications/wizard/methods/saml/ak-application-wizard-authentication-by-saml-import.ts deleted file mode 100644 index 924aead76..000000000 --- a/web/src/admin/applications/wizard/methods/saml/ak-application-wizard-authentication-by-saml-import.ts +++ /dev/null @@ -1,81 +0,0 @@ -import "@goauthentik/admin/common/ak-flow-search/ak-flow-search-no-default"; -import "@goauthentik/components/ak-file-input"; -import { AkFileInput } from "@goauthentik/components/ak-file-input"; -import "@goauthentik/components/ak-text-input"; -import "@goauthentik/elements/forms/HorizontalFormElement"; - -import { msg } from "@lit/localize"; -import { customElement } from "@lit/reactive-element/decorators/custom-element.js"; -import { html } from "lit"; -import { query } from "lit/decorators.js"; -import { ifDefined } from "lit/directives/if-defined.js"; - -import { - FlowsInstancesListDesignationEnum, - ProvidersSamlImportMetadataCreateRequest, -} from "@goauthentik/api"; - -import BaseProviderPanel from "../BaseProviderPanel"; - -@customElement("ak-application-wizard-authentication-by-saml-import") -export class ApplicationWizardProviderSamlImport extends BaseProviderPanel { - @query('ak-file-input[name="metadata"]') - fileInput!: AkFileInput; - - handleChange(ev: InputEvent) { - if (!ev.target) { - console.warn(`Received event with no target: ${ev}`); - return; - } - const target = ev.target as HTMLInputElement; - if (target.type === "file") { - const file = this.fileInput.files?.[0] ?? null; - if (file) { - this.dispatchWizardUpdate({ - update: { - provider: { - file, - }, - }, - status: this.form.checkValidity() ? "valid" : "invalid", - }); - } - return; - } - super.handleChange(ev); - } - - render() { - const provider = this.wizard.provider as - | ProvidersSamlImportMetadataCreateRequest - | undefined; - - return html`
- - - - -

- ${msg("Flow used when authorizing this provider.")} -

-
- - -
`; - } -} - -export default ApplicationWizardProviderSamlImport; diff --git a/web/src/admin/applications/wizard/methods/scim/ak-application-wizard-authentication-by-scim.ts b/web/src/admin/applications/wizard/methods/scim/ak-application-wizard-authentication-by-scim.ts index 61dc9a3eb..2bc8596e2 100644 --- a/web/src/admin/applications/wizard/methods/scim/ak-application-wizard-authentication-by-scim.ts +++ b/web/src/admin/applications/wizard/methods/scim/ak-application-wizard-authentication-by-scim.ts @@ -1,7 +1,10 @@ +import "@goauthentik/admin/applications/wizard/ak-wizard-title"; +import "@goauthentik/admin/common/ak-core-group-search"; import "@goauthentik/admin/common/ak-crypto-certificate-search"; import "@goauthentik/admin/common/ak-flow-search/ak-branded-flow-search"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { first } from "@goauthentik/common/utils"; +import "@goauthentik/components/ak-multi-select"; import "@goauthentik/components/ak-switch-input"; import "@goauthentik/components/ak-text-input"; import "@goauthentik/elements/forms/FormGroup"; @@ -12,14 +15,7 @@ import { customElement, state } from "@lit/reactive-element/decorators.js"; import { html } from "lit"; import { ifDefined } from "lit/directives/if-defined.js"; -import { - CoreApi, - CoreGroupsListRequest, - type Group, - PaginatedSCIMMappingList, - PropertymappingsApi, - type SCIMProvider, -} from "@goauthentik/api"; +import { PaginatedSCIMMappingList, PropertymappingsApi, type SCIMProvider } from "@goauthentik/api"; import BaseProviderPanel from "../BaseProviderPanel"; @@ -31,158 +27,129 @@ export class ApplicationWizardAuthenticationBySCIM extends BaseProviderPanel { constructor() { super(); new PropertymappingsApi(DEFAULT_CONFIG) - .propertymappingsScopeList({ - ordering: "scope_name", + .propertymappingsScimList({ + ordering: "managed", }) .then((propertyMappings: PaginatedSCIMMappingList) => { this.propertyMappings = propertyMappings; }); } + propertyMappingConfiguration(provider?: SCIMProvider) { + const propertyMappings = this.propertyMappings?.results ?? []; + + const configuredMappings = (providerMappings: string[]) => + propertyMappings.map((pm) => pm.pk).filter((pmpk) => providerMappings.includes(pmpk)); + + const managedMappings = (key: string) => + propertyMappings + .filter((pm) => pm.managed === `goauthentik.io/providers/scim/${key}`) + .map((pm) => pm.pk); + + const pmUserValues = provider?.propertyMappings + ? configuredMappings(provider?.propertyMappings ?? []) + : managedMappings("user"); + + const pmGroupValues = provider?.propertyMappingsGroup + ? configuredMappings(provider?.propertyMappingsGroup ?? []) + : managedMappings("group"); + + const propertyPairs = propertyMappings.map((pm) => [pm.pk, pm.name]); + + return { pmUserValues, pmGroupValues, propertyPairs }; + } + render() { const provider = this.wizard.provider as SCIMProvider | undefined; + const errors = this.wizard.errors.provider; - return html`
- - - ${msg("Protocol settings")} -
- - - - -
-
- - ${msg("User filtering")} -
- - - => { - const args: CoreGroupsListRequest = { - ordering: "name", - }; - if (query !== undefined) { - args.search = query; - } - const groups = await new CoreApi(DEFAULT_CONFIG).coreGroupsList( - args, - ); - return groups.results; - }} - .renderElement=${(group: Group): string => { - return group.name; - }} - .value=${(group: Group | undefined): string | undefined => { - return group ? group.pk : undefined; - }} - .selected=${(group: Group): boolean => { - return group.pk === provider?.filterGroup; - }} - ?blankable=${true} + const { pmUserValues, pmGroupValues, propertyPairs } = + this.propertyMappingConfiguration(provider); + + return html`${msg("Configure SCIM Provider")} + + + + ${msg("Protocol settings")} +
+ - -

- ${msg("Only sync users within the selected group.")} -

- -
-
- - ${msg("Attribute mapping")} -
- - -

- ${msg("Property mappings used to user mapping.")} -

-

- ${msg("Hold control/command to select multiple items.")} -

-
- - -

- ${msg("Property mappings used to group creation.")} -

-

- ${msg("Hold control/command to select multiple items.")} -

-
-
-
- `; + + + +
+
+ + ${msg("User filtering")} +
+ + + +

+ ${msg("Only sync users within the selected group.")} +

+
+
+
+ + ${msg("Attribute mapping")} +
+ + ${msg("Property mappings used for user mapping.")} +

+

+ ${msg("Hold control/command to select multiple items.")} +

`} + >
+ + ${msg("Property mappings used for group creation.")} +

+

+ ${msg("Hold control/command to select multiple items.")} +

`} + >
+
+
+ `; } } diff --git a/web/src/admin/applications/wizard/steps.ts b/web/src/admin/applications/wizard/steps.ts index 451367bf8..fc427ed08 100644 --- a/web/src/admin/applications/wizard/steps.ts +++ b/web/src/admin/applications/wizard/steps.ts @@ -15,6 +15,12 @@ import "./commit/ak-application-wizard-commit-application"; import "./methods/ak-application-wizard-authentication-method"; import { ApplicationStep as ApplicationStepType } from "./types"; +/** + * In the current implementation, all of the child forms have access to the wizard's + * global context, into which all data is written, and which is updated by events + * flowing into the top-level orchestrator. + */ + class ApplicationStep implements ApplicationStepType { id = "application"; label = "Application Details"; diff --git a/web/src/admin/applications/wizard/stories/ak-application-context-display-for-test.ts b/web/src/admin/applications/wizard/stories/ak-application-context-display-for-test.ts index fa418199e..c21eb4199 100644 --- a/web/src/admin/applications/wizard/stories/ak-application-context-display-for-test.ts +++ b/web/src/admin/applications/wizard/stories/ak-application-context-display-for-test.ts @@ -3,7 +3,7 @@ import { customElement } from "@lit/reactive-element/decorators/custom-element.j import { state } from "@lit/reactive-element/decorators/state.js"; import { LitElement, html } from "lit"; -import applicationWizardContext from "../ContextIdentity"; +import { applicationWizardContext } from "../ContextIdentity"; import type { ApplicationWizardState } from "../types"; @customElement("ak-application-context-display-for-test") diff --git a/web/src/admin/applications/wizard/types.ts b/web/src/admin/applications/wizard/types.ts index 0ebe7aa8a..a6e86cac1 100644 --- a/web/src/admin/applications/wizard/types.ts +++ b/web/src/admin/applications/wizard/types.ts @@ -9,6 +9,7 @@ import { type RadiusProviderRequest, type SAMLProviderRequest, type SCIMProviderRequest, + type ValidationError, } from "@goauthentik/api"; export type OneOfProvider = @@ -24,12 +25,13 @@ export interface ApplicationWizardState { providerModel: string; app: Partial; provider: OneOfProvider; + errors: ValidationError; } type StatusType = "invalid" | "valid" | "submitted" | "failed"; export type ApplicationWizardStateUpdate = { - update?: Partial; + update?: ApplicationWizardState; status?: StatusType; }; diff --git a/web/src/admin/events/EventListPage.ts b/web/src/admin/events/EventListPage.ts index 6645633b7..da6a73cd5 100644 --- a/web/src/admin/events/EventListPage.ts +++ b/web/src/admin/events/EventListPage.ts @@ -1,3 +1,4 @@ +import "@goauthentik/admin/events/EventVolumeChart"; import { EventGeo } from "@goauthentik/admin/events/utils"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { EventWithContext } from "@goauthentik/common/events"; @@ -10,7 +11,7 @@ import { TablePage } from "@goauthentik/elements/table/TablePage"; import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; import { msg, str } from "@lit/localize"; -import { TemplateResult, html } from "lit"; +import { CSSResult, TemplateResult, css, html } from "lit"; import { customElement, property } from "lit/decorators.js"; import { Event, EventsApi } from "@goauthentik/api"; @@ -35,6 +36,14 @@ export class EventListPage extends TablePage { @property() order = "-created"; + static get styles(): CSSResult[] { + return super.styles.concat(css` + .pf-m-no-padding-bottom { + padding-bottom: 0; + } + `); + } + async apiEndpoint(page: number): Promise> { return new EventsApi(DEFAULT_CONFIG).eventsEventsList({ ordering: this.order, @@ -55,6 +64,19 @@ export class EventListPage extends TablePage { ]; } + renderSectionBefore(): TemplateResult { + return html` +
+ +
+ `; + } + row(item: EventWithContext): TemplateResult[] { return [ html`
${actionToLabel(item.action)}
diff --git a/web/src/admin/events/EventVolumeChart.ts b/web/src/admin/events/EventVolumeChart.ts new file mode 100644 index 000000000..da1710af0 --- /dev/null +++ b/web/src/admin/events/EventVolumeChart.ts @@ -0,0 +1,63 @@ +import { DEFAULT_CONFIG } from "@goauthentik/app/common/api/config"; +import { AKChart } from "@goauthentik/app/elements/charts/Chart"; +import { ChartData } from "chart.js"; + +import { msg } from "@lit/localize"; +import { CSSResult, TemplateResult, css, html } from "lit"; +import { customElement, property } from "lit/decorators.js"; + +import PFCard from "@patternfly/patternfly/components/Card/card.css"; + +import { Coordinate, EventsApi, EventsEventsListRequest } from "@goauthentik/api"; + +@customElement("ak-events-volume-chart") +export class EventVolumeChart extends AKChart { + _query?: EventsEventsListRequest; + + @property({ attribute: false }) + set query(value: EventsEventsListRequest | undefined) { + this._query = value; + this.refreshHandler(); + } + + static get styles(): CSSResult[] { + return super.styles.concat( + PFCard, + css` + .pf-c-card__body { + height: 12rem; + } + `, + ); + } + + apiRequest(): Promise { + return new EventsApi(DEFAULT_CONFIG).eventsEventsVolumeList(this._query); + } + + getChartData(data: Coordinate[]): ChartData { + return { + datasets: [ + { + label: msg("Events"), + backgroundColor: "rgba(189, 229, 184, .5)", + spanGaps: true, + data: + data.map((cord) => { + return { + x: cord.xCord || 0, + y: cord.yCord || 0, + }; + }) || [], + }, + ], + }; + } + + render(): TemplateResult { + return html`
+
${msg("Event volume")}
+
${super.render()}
+
`; + } +} diff --git a/web/src/common/merge.ts b/web/src/common/merge.ts deleted file mode 100644 index 4e60e856c..000000000 --- a/web/src/common/merge.ts +++ /dev/null @@ -1,120 +0,0 @@ -/** Taken from: https://github.com/zellwk/javascript/tree/master - * - * We have added some typescript annotations, but this is such a rich feature with deep nesting - * we'll just have to watch it closely for any issues. So far there don't seem to be any. - * - */ - -function objectType(value: T) { - return Object.prototype.toString.call(value); -} - -// Creates a deep clone for each value -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function cloneDescriptorValue(value: any) { - // Arrays - if (objectType(value) === "[object Array]") { - const array = []; - for (let v of value) { - v = cloneDescriptorValue(v); - array.push(v); - } - return array; - } - - // Objects - if (objectType(value) === "[object Object]") { - const obj = {}; - const props = Object.keys(value); - for (const prop of props) { - const descriptor = Object.getOwnPropertyDescriptor(value, prop); - if (!descriptor) { - continue; - } - - if (descriptor.value) { - descriptor.value = cloneDescriptorValue(descriptor.value); - } - Object.defineProperty(obj, prop, descriptor); - } - return obj; - } - - // Other Types of Objects - if (objectType(value) === "[object Date]") { - return new Date(value.getTime()); - } - - if (objectType(value) === "[object Map]") { - const map = new Map(); - for (const entry of value) { - map.set(entry[0], cloneDescriptorValue(entry[1])); - } - return map; - } - - if (objectType(value) === "[object Set]") { - const set = new Set(); - for (const entry of value.entries()) { - set.add(cloneDescriptorValue(entry[0])); - } - return set; - } - - // Types we don't need to clone or cannot clone. - // Examples: - // - Primitives don't need to clone - // - Functions cannot clone - return value; -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function _merge(output: Record, input: Record) { - const props = Object.keys(input); - - for (const prop of props) { - // Prevents Prototype Pollution - if (prop === "__proto__") continue; - - const descriptor = Object.getOwnPropertyDescriptor(input, prop); - if (!descriptor) { - continue; - } - - const value = descriptor.value; - if (value) descriptor.value = cloneDescriptorValue(value); - - // If don't have prop => Define property - // [ken@goauthentik] Using `hasOwn` is preferable over - // the basic identity test, according to Typescript. - if (!Object.hasOwn(output, prop)) { - Object.defineProperty(output, prop, descriptor); - continue; - } - - // If have prop, but type is not object => Overwrite by redefining property - if (typeof output[prop] !== "object") { - Object.defineProperty(output, prop, descriptor); - continue; - } - - // If have prop, but type is Object => Concat the arrays together. - if (objectType(descriptor.value) === "[object Array]") { - output[prop] = output[prop].concat(descriptor.value); - continue; - } - - // If have prop, but type is Object => Merge. - _merge(output[prop], descriptor.value); - } -} - -export function merge(...sources: Array) { - const result = {}; - for (const source of sources) { - _merge(result, source); - } - return result; -} - -export default merge; diff --git a/web/src/common/utils.ts b/web/src/common/utils.ts index 1ae395b4e..2b88f43dd 100644 --- a/web/src/common/utils.ts +++ b/web/src/common/utils.ts @@ -54,6 +54,13 @@ export function camelToSnake(key: string): string { return result.split(" ").join("_").toLowerCase(); } +const capitalize = (key: string) => (key.length === 0 ? "" : key[0].toUpperCase() + key.slice(1)); + +export function snakeToCamel(key: string) { + const [start, ...rest] = key.split("_"); + return [start, ...rest.map(capitalize)].join(""); +} + export function groupBy(objects: T[], callback: (obj: T) => string): Array<[string, T[]]> { const m = new Map(); objects.forEach((obj) => { diff --git a/web/src/components/HorizontalLightComponent.ts b/web/src/components/HorizontalLightComponent.ts new file mode 100644 index 000000000..7d34c833d --- /dev/null +++ b/web/src/components/HorizontalLightComponent.ts @@ -0,0 +1,72 @@ +import { AKElement } from "@goauthentik/elements/Base"; + +import { TemplateResult, html, nothing } from "lit"; +import { property } from "lit/decorators.js"; + +type HelpType = TemplateResult | typeof nothing; + +export class HorizontalLightComponent extends AKElement { + // Render into the lightDOM. This effectively erases the shadowDOM nature of this component, but + // we're not actually using that and, for the meantime, we need the form handlers to be able to + // find the children of this component. + // + // TODO: This abstraction is wrong; it's putting *more* layers in as a way of managing the + // visual clutter and legibility issues of ak-form-elemental-horizontal and patternfly in + // general. + protected createRenderRoot() { + return this; + } + + @property({ type: String }) + name!: string; + + @property({ type: String }) + label = ""; + + @property({ type: Boolean }) + required = false; + + @property({ type: String }) + help = ""; + + @property({ type: Object }) + bighelp?: TemplateResult | TemplateResult[]; + + @property({ type: Boolean }) + hidden = false; + + @property({ type: Boolean }) + invalid = false; + + @property({ attribute: false }) + errorMessages: string[] = []; + + renderControl() { + throw new Error("Must be implemented in a subclass"); + } + + renderHelp(): HelpType[] { + const bigHelp: HelpType[] = Array.isArray(this.bighelp) + ? this.bighelp + : [this.bighelp ?? nothing]; + return [ + this.help ? html`

${this.help}

` : nothing, + ...bigHelp, + ]; + } + + render() { + // prettier-ignore + return html` + ${this.renderControl()} + ${this.renderHelp()} + `; + } +} diff --git a/web/src/components/ak-multi-select.ts b/web/src/components/ak-multi-select.ts new file mode 100644 index 000000000..9efddd079 --- /dev/null +++ b/web/src/components/ak-multi-select.ts @@ -0,0 +1,150 @@ +import "@goauthentik/app/elements/forms/HorizontalFormElement"; +import { AKElement } from "@goauthentik/elements/Base"; + +import { TemplateResult, css, html, nothing } from "lit"; +import { customElement, property } from "lit/decorators.js"; +import { ifDefined } from "lit/directives/if-defined.js"; +import { map } from "lit/directives/map.js"; +import { Ref, createRef, ref } from "lit/directives/ref.js"; + +import PFForm from "@patternfly/patternfly/components/Form/form.css"; +import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css"; +import PFBase from "@patternfly/patternfly/patternfly-base.css"; + +type Pair = [string, string]; + +const selectStyles = css` + select[multiple] { + min-height: 15rem; + } +`; + +/** + * Horizontal layout control with a multi-select. + * + * @part select - The select itself, to override the height specified above. + */ +@customElement("ak-multi-select") +export class AkMultiSelect extends AKElement { + constructor() { + super(); + this.dataset.akControl = "true"; + } + + static get styles() { + return [PFBase, PFForm, PFFormControl, selectStyles]; + } + + /** + * The [name] attribute, which is also distributed to the layout manager and the input control. + */ + @property({ type: String }) + name!: string; + + /** + * The text label to display on the control + */ + @property({ type: String }) + label = ""; + + /** + * The values to be displayed in the select. The format is [Value, Label], where the label is + * what will be displayed. + */ + @property({ attribute: false }) + options: Pair[] = []; + + /** + * If true, at least one object must be selected + */ + @property({ type: Boolean }) + required = false; + + /** + * Supporting a simple help string + */ + @property({ type: String }) + help = ""; + + /** + * For more complex help instructions, provide a template result. + */ + @property({ type: Object }) + bighelp!: TemplateResult | TemplateResult[]; + + /** + * An array of strings representing the objects currently selected. + */ + @property({ type: Array }) + values: string[] = []; + + /** + * Helper accessor for older code + */ + get value() { + return this.values; + } + + /** + * One of two criteria (the other being the data-ak-control flag) that specifies this as a + * control that produces values of specific interest to our REST API. This is our modern + * accessor name. + */ + json() { + return this.values; + } + + renderHelp() { + return [ + this.help ? html`

${this.help}

` : nothing, + this.bighelp ? this.bighelp : nothing, + ]; + } + + handleChange(ev: Event) { + if (ev.type === "change") { + this.values = Array.from(this.selectRef.value!.querySelectorAll("option")) + .filter((option) => option.selected) + .map((option) => option.value); + this.dispatchEvent( + new CustomEvent("ak-select", { + detail: this.values, + composed: true, + bubbles: true, + }), + ); + } + } + + selectRef: Ref = createRef(); + + render() { + return html`
+ + + ${this.renderHelp()} + +
`; + } +} + +export default AkMultiSelect; diff --git a/web/src/components/ak-number-input.ts b/web/src/components/ak-number-input.ts index dcfef1541..65fc10b0e 100644 --- a/web/src/components/ak-number-input.ts +++ b/web/src/components/ak-number-input.ts @@ -1,51 +1,21 @@ -import { AKElement } from "@goauthentik/elements/Base"; - -import { html, nothing } from "lit"; +import { html } from "lit"; import { customElement, property } from "lit/decorators.js"; import { ifDefined } from "lit/directives/if-defined.js"; +import { HorizontalLightComponent } from "./HorizontalLightComponent"; + @customElement("ak-number-input") -export class AkNumberInput extends AKElement { - // Render into the lightDOM. This effectively erases the shadowDOM nature of this component, but - // we're not actually using that and, for the meantime, we need the form handlers to be able to - // find the children of this component. - // - // TODO: This abstraction is wrong; it's putting *more* layers in as a way of managing the - // visual clutter and legibility issues of ak-form-elemental-horizontal and patternfly in - // general. - protected createRenderRoot() { - return this; - } - - @property({ type: String }) - name!: string; - - @property({ type: String }) - label = ""; - +export class AkNumberInput extends HorizontalLightComponent { @property({ type: Number, reflect: true }) value = 0; - @property({ type: Boolean }) - required = false; - - @property({ type: String }) - help = ""; - - render() { - return html` - - ${this.help ? html`

${this.help}

` : nothing} -
`; + />`; } } diff --git a/web/src/components/ak-radio-input.ts b/web/src/components/ak-radio-input.ts index c65b8f1ae..b4899cfc5 100644 --- a/web/src/components/ak-radio-input.ts +++ b/web/src/components/ak-radio-input.ts @@ -1,35 +1,13 @@ -import { AKElement } from "@goauthentik/elements/Base"; import { RadioOption } from "@goauthentik/elements/forms/Radio"; import "@goauthentik/elements/forms/Radio"; import { html, nothing } from "lit"; import { customElement, property } from "lit/decorators.js"; +import { HorizontalLightComponent } from "./HorizontalLightComponent"; + @customElement("ak-radio-input") -export class AkRadioInput extends AKElement { - // Render into the lightDOM. This effectively erases the shadowDOM nature of this component, but - // we're not actually using that and, for the meantime, we need the form handlers to be able to - // find the children of this component. - // - // TODO: This abstraction is wrong; it's putting *more* layers in as a way of managing the - // visual clutter and legibility issues of ak-form-elemental-horizontal and patternfly in - // general. - protected createRenderRoot() { - return this; - } - - @property({ type: String }) - name!: string; - - @property({ type: String }) - label = ""; - - @property({ type: String }) - help = ""; - - @property({ type: Boolean }) - required = false; - +export class AkRadioInput extends HorizontalLightComponent { @property({ type: Object }) value!: T; @@ -37,24 +15,25 @@ export class AkRadioInput extends AKElement { options: RadioOption[] = []; handleInput(ev: CustomEvent) { - this.value = ev.detail.value; + if ("detail" in ev) { + this.value = ev.detail.value; + } } - render() { - return html` - ${this.help.trim() ? html`

${this.help}

` - : nothing} -
`; + : nothing}`; } } diff --git a/web/src/components/ak-slug-input.ts b/web/src/components/ak-slug-input.ts index b4fac3380..161a00c87 100644 --- a/web/src/components/ak-slug-input.ts +++ b/web/src/components/ak-slug-input.ts @@ -1,44 +1,16 @@ import { convertToSlug } from "@goauthentik/common/utils"; -import { AKElement } from "@goauthentik/elements/Base"; -import { TemplateResult, html, nothing } from "lit"; +import { html } from "lit"; import { customElement, property, query } from "lit/decorators.js"; import { ifDefined } from "lit/directives/if-defined.js"; +import { HorizontalLightComponent } from "./HorizontalLightComponent"; + @customElement("ak-slug-input") -export class AkSlugInput extends AKElement { - // Render into the lightDOM. This effectively erases the shadowDOM nature of this component, but - // we're not actually using that and, for the meantime, we need the form handlers to be able to - // find the children of this component. - // - // TODO: This abstraction is wrong; it's putting *more* layers in as a way of managing the - // visual clutter and legibility issues of ak-form-elemental-horizontal and patternfly in - // general. - protected createRenderRoot() { - return this; - } - - @property({ type: String }) - name!: string; - - @property({ type: String }) - label = ""; - +export class AkSlugInput extends HorizontalLightComponent { @property({ type: String, reflect: true }) value = ""; - @property({ type: Boolean }) - required = false; - - @property({ type: String }) - help = ""; - - @property({ type: Boolean }) - hidden = false; - - @property({ type: Object }) - bighelp!: TemplateResult | TemplateResult[]; - @property({ type: String }) source = ""; @@ -59,13 +31,6 @@ export class AkSlugInput extends AKElement { this.input.addEventListener("input", this.handleTouch); } - renderHelp() { - return [ - this.help ? html`

${this.help}

` : nothing, - this.bighelp ? this.bighelp : nothing, - ]; - } - // Do not stop propagation of this event; it must be sent up the tree so that a parent // component, such as a custom forms manager, may receive it. handleTouch(ev: Event) { @@ -150,21 +115,13 @@ export class AkSlugInput extends AKElement { super.disconnectedCallback(); } - render() { - return html` - - ${this.renderHelp()} - `; + />`; } } diff --git a/web/src/components/ak-text-input.ts b/web/src/components/ak-text-input.ts index 2e7a9dd63..545ff9018 100644 --- a/web/src/components/ak-text-input.ts +++ b/web/src/components/ak-text-input.ts @@ -1,65 +1,21 @@ -import { AKElement } from "@goauthentik/elements/Base"; - -import { TemplateResult, html, nothing } from "lit"; +import { html } from "lit"; import { customElement, property } from "lit/decorators.js"; import { ifDefined } from "lit/directives/if-defined.js"; +import { HorizontalLightComponent } from "./HorizontalLightComponent"; + @customElement("ak-text-input") -export class AkTextInput extends AKElement { - // Render into the lightDOM. This effectively erases the shadowDOM nature of this component, but - // we're not actually using that and, for the meantime, we need the form handlers to be able to - // find the children of this component. - // - // TODO: This abstraction is wrong; it's putting *more* layers in as a way of managing the - // visual clutter and legibility issues of ak-form-elemental-horizontal and patternfly in - // general. - protected createRenderRoot() { - return this; - } - - @property({ type: String }) - name!: string; - - @property({ type: String }) - label = ""; - +export class AkTextInput extends HorizontalLightComponent { @property({ type: String, reflect: true }) value = ""; - @property({ type: Boolean }) - required = false; - - @property({ type: String }) - help = ""; - - @property({ type: Boolean }) - hidden = false; - - @property({ type: Object }) - bighelp!: TemplateResult | TemplateResult[]; - - renderHelp() { - return [ - this.help ? html`

${this.help}

` : nothing, - this.bighelp ? this.bighelp : nothing, - ]; - } - - render() { - return html` - - ${this.renderHelp()} - `; + />`; } } diff --git a/web/src/components/ak-textarea-input.ts b/web/src/components/ak-textarea-input.ts index 95b138550..9ca2efc4f 100644 --- a/web/src/components/ak-textarea-input.ts +++ b/web/src/components/ak-textarea-input.ts @@ -1,57 +1,22 @@ -import { AKElement } from "@goauthentik/elements/Base"; - -import { TemplateResult, html, nothing } from "lit"; +import { html } from "lit"; import { customElement, property } from "lit/decorators.js"; +import { HorizontalLightComponent } from "./HorizontalLightComponent"; + @customElement("ak-textarea-input") -export class AkTextareaInput extends AKElement { - // Render into the lightDOM. This effectively erases the shadowDOM nature of this component, but - // we're not actually using that and, for the meantime, we need the form handlers to be able to - // find the children of this component. - // - // TODO: This abstraction is wrong; it's putting *more* layers in as a way of managing the - // visual clutter and legibility issues of ak-form-elemental-horizontal and patternfly in - // general. - protected createRenderRoot() { - return this; - } - - @property({ type: String }) - name!: string; - - @property({ type: String }) - label = ""; - - @property({ type: String }) +export class AkTextareaInput extends HorizontalLightComponent { + @property({ type: String, reflect: true }) value = ""; - @property({ type: Boolean }) - required = false; - - @property({ type: String }) - help = ""; - - @property({ type: Object }) - bighelp!: TemplateResult | TemplateResult[]; - - renderHelp() { - return [ - this.help ? html`

${this.help}

` : nothing, - this.bighelp ? this.bighelp : nothing, - ]; - } - - render() { - return html` - - ${this.renderHelp()} - `; + >${this.value !== undefined ? this.value : ""} `; } } diff --git a/web/src/components/stories/ak-multi-select.stories.ts b/web/src/components/stories/ak-multi-select.stories.ts new file mode 100644 index 000000000..a3115c560 --- /dev/null +++ b/web/src/components/stories/ak-multi-select.stories.ts @@ -0,0 +1,79 @@ +import "@goauthentik/elements/messages/MessageContainer"; +import { Meta } from "@storybook/web-components"; + +import { TemplateResult, html, render } from "lit"; + +import "../ak-multi-select"; +import AkMultiSelect from "../ak-multi-select"; + +const metadata: Meta = { + title: "Components / MultiSelect", + component: "ak-multi-select", + parameters: { + docs: { + description: { + component: "A stylized value control for multi-select displays", + }, + }, + }, +}; + +export default metadata; + +const container = (testItem: TemplateResult) => + html`
+ + + ${testItem} + +
+
`; + +const testOptions = [ + ["funky", "Option One: Funky"], + ["strange", "Option Two: Strange"], + ["weird", "Option Three: Weird"], +]; + +export const RadioInput = () => { + const result = ""; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const displayChange = (ev: any) => { + const messagePad = document.getElementById("message-pad"); + const component: AkMultiSelect | null = document.querySelector( + 'ak-multi-select[name="ak-test-multi-select"]', + ); + + const results = html` +

Results from event:

+
    + ${ev.target.value.map((v: string) => html`
  • ${v}
  • `)} +
+

Results from component:

+
    + ${component!.json().map((v: string) => html`
  • ${v}
  • `)} +
+ `; + + render(results, messagePad!); + }; + + return container( + html` +
${result}
`, + ); +}; diff --git a/web/src/elements/forms/Form.ts b/web/src/elements/forms/Form.ts index 0c4590fac..d465a2435 100644 --- a/web/src/elements/forms/Form.ts +++ b/web/src/elements/forms/Form.ts @@ -31,10 +31,15 @@ export interface KeyUnknown { [key: string]: unknown; } +// Literally the only field `assignValue()` cares about. +type HTMLNamedElement = Pick; + +type AkControlElement = HTMLInputElement & { json: () => string | string[] }; + /** * Recursively assign `value` into `json` while interpreting the dot-path of `element.name` */ -function assignValue(element: HTMLInputElement, value: unknown, json: KeyUnknown): void { +function assignValue(element: HTMLNamedElement, value: unknown, json: KeyUnknown): void { let parent = json; if (!element.name?.includes(".")) { parent[element.name] = value; @@ -60,6 +65,16 @@ export function serializeForm( const json: { [key: string]: unknown } = {}; elements.forEach((element) => { element.requestUpdate(); + if (element.hidden) { + return; + } + + // TODO: Tighten up the typing so that we can handle both. + if ("akControl" in element.dataset) { + assignValue(element, (element as unknown as AkControlElement).json(), json); + return; + } + const inputElement = element.querySelector("[name]"); if (element.hidden || !inputElement) { return; diff --git a/web/xliff/de.xlf b/web/xliff/de.xlf index cacf5580c..85e253602 100644 --- a/web/xliff/de.xlf +++ b/web/xliff/de.xlf @@ -5800,12 +5800,6 @@ Bindings to groups/users are checked against the user of the event. Your application has been saved - - In the Application: - - - In the Provider: - Method's display Name. @@ -6075,6 +6069,54 @@ Bindings to groups/users are checked against the user of the event. When enabled, the stage will always accept the given user identifier and continue. + + + There was an error in the application. + + + Review the application. + + + There was an error in the provider. + + + Review the provider. + + + There was an error + + + There was an error creating the application, but no error message was sent. Please review the server logs. + + + Configure LDAP Provider + + + Configure OAuth2/OpenId Provider + + + Configure Proxy Provider + + + AdditionalScopes + + + Configure Radius Provider + + + Configure SAML Provider + + + Property mappings used for user mapping. + + + Configure SCIM Provider + + + Property mappings used for group creation. + + + Event volume diff --git a/web/xliff/en.xlf b/web/xliff/en.xlf index 11d8ee1a5..2b236521f 100644 --- a/web/xliff/en.xlf +++ b/web/xliff/en.xlf @@ -6077,12 +6077,6 @@ Bindings to groups/users are checked against the user of the event. Your application has been saved - - In the Application: - - - In the Provider: - Method's display Name. @@ -6352,6 +6346,54 @@ Bindings to groups/users are checked against the user of the event. When enabled, the stage will always accept the given user identifier and continue. + + + There was an error in the application. + + + Review the application. + + + There was an error in the provider. + + + Review the provider. + + + There was an error + + + There was an error creating the application, but no error message was sent. Please review the server logs. + + + Configure LDAP Provider + + + Configure OAuth2/OpenId Provider + + + Configure Proxy Provider + + + AdditionalScopes + + + Configure Radius Provider + + + Configure SAML Provider + + + Property mappings used for user mapping. + + + Configure SCIM Provider + + + Property mappings used for group creation. + + + Event volume diff --git a/web/xliff/es.xlf b/web/xliff/es.xlf index b5708c7ac..d9564b6ad 100644 --- a/web/xliff/es.xlf +++ b/web/xliff/es.xlf @@ -5716,12 +5716,6 @@ Bindings to groups/users are checked against the user of the event. Your application has been saved - - In the Application: - - - In the Provider: - Method's display Name. @@ -5991,6 +5985,54 @@ Bindings to groups/users are checked against the user of the event. When enabled, the stage will always accept the given user identifier and continue. + + + There was an error in the application. + + + Review the application. + + + There was an error in the provider. + + + Review the provider. + + + There was an error + + + There was an error creating the application, but no error message was sent. Please review the server logs. + + + Configure LDAP Provider + + + Configure OAuth2/OpenId Provider + + + Configure Proxy Provider + + + AdditionalScopes + + + Configure Radius Provider + + + Configure SAML Provider + + + Property mappings used for user mapping. + + + Configure SCIM Provider + + + Property mappings used for group creation. + + + Event volume diff --git a/web/xliff/fr.xlf b/web/xliff/fr.xlf index 37dfc4bce..3c0fd9f4d 100644 --- a/web/xliff/fr.xlf +++ b/web/xliff/fr.xlf @@ -7617,14 +7617,6 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti Your application has been saved L'application a été sauvegardée - - In the Application: - Dans l'application : - - - In the Provider: - Dans le fournisseur : - Method's display Name. Nom d'affichage de la méthode. @@ -7986,6 +7978,54 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti When enabled, the stage will always accept the given user identifier and continue. Lorsqu'activé, l'étape acceptera toujours l'identifiant utilisateur donné et continuera. + + + There was an error in the application. + + + Review the application. + + + There was an error in the provider. + + + Review the provider. + + + There was an error + + + There was an error creating the application, but no error message was sent. Please review the server logs. + + + Configure LDAP Provider + + + Configure OAuth2/OpenId Provider + + + Configure Proxy Provider + + + AdditionalScopes + + + Configure Radius Provider + + + Configure SAML Provider + + + Property mappings used for user mapping. + + + Configure SCIM Provider + + + Property mappings used for group creation. + + + Event volume diff --git a/web/xliff/pl.xlf b/web/xliff/pl.xlf index 0c8118ae8..7aae5340d 100644 --- a/web/xliff/pl.xlf +++ b/web/xliff/pl.xlf @@ -5924,12 +5924,6 @@ Bindings to groups/users are checked against the user of the event. Your application has been saved - - In the Application: - - - In the Provider: - Method's display Name. @@ -6199,6 +6193,54 @@ Bindings to groups/users are checked against the user of the event. When enabled, the stage will always accept the given user identifier and continue. + + + There was an error in the application. + + + Review the application. + + + There was an error in the provider. + + + Review the provider. + + + There was an error + + + There was an error creating the application, but no error message was sent. Please review the server logs. + + + Configure LDAP Provider + + + Configure OAuth2/OpenId Provider + + + Configure Proxy Provider + + + AdditionalScopes + + + Configure Radius Provider + + + Configure SAML Provider + + + Property mappings used for user mapping. + + + Configure SCIM Provider + + + Property mappings used for group creation. + + + Event volume diff --git a/web/xliff/pseudo-LOCALE.xlf b/web/xliff/pseudo-LOCALE.xlf index 78b560613..3839422ba 100644 --- a/web/xliff/pseudo-LOCALE.xlf +++ b/web/xliff/pseudo-LOCALE.xlf @@ -7556,14 +7556,6 @@ Bindings to groups/users are checked against the user of the event. Your application has been saved Ŷōũŕ àƥƥĺĩćàţĩōń ĥàś ƀēēń śàvēď - - In the Application: - Ĩń ţĥē Àƥƥĺĩćàţĩōń: - - - In the Provider: - Ĩń ţĥē Ƥŕōvĩďēŕ: - Method's display Name. Mēţĥōď'ś ďĩśƥĺàŷ Ńàmē. @@ -7732,149 +7724,261 @@ Bindings to groups/users are checked against the user of the event. Create With Wizard + Ćŕēàţē Ŵĩţĥ Ŵĩźàŕď One hint, 'New Application Wizard', is currently hidden + Ōńē ĥĩńţ, 'Ńēŵ Àƥƥĺĩćàţĩōń Ŵĩźàŕď', ĩś ćũŕŕēńţĺŷ ĥĩďďēń External applications that use authentik as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access. + Ēxţēŕńàĺ àƥƥĺĩćàţĩōńś ţĥàţ ũśē àũţĥēńţĩķ àś àń ĩďēńţĩţŷ ƥŕōvĩďēŕ vĩà ƥŕōţōćōĺś ĺĩķē ŌÀũţĥ2 àńď ŚÀMĹ. Àĺĺ àƥƥĺĩćàţĩōńś àŕē śĥōŵń ĥēŕē, ēvēń ōńēś ŷōũ ćàńńōţ àććēśś. Deny message + Ďēńŷ mēśśàĝē Message shown when this stage is run. + Mēśśàĝē śĥōŵń ŵĥēń ţĥĩś śţàĝē ĩś ŕũń. Open Wizard + Ōƥēń Ŵĩźàŕď Demo Wizard + Ďēmō Ŵĩźàŕď Run the demo wizard + Ŕũń ţĥē ďēmō ŵĩźàŕď OAuth2/OIDC (Open Authorization/OpenID Connect) + ŌÀũţĥ2/ŌĨĎĆ (Ōƥēń Àũţĥōŕĩźàţĩōń/ŌƥēńĨĎ Ćōńńēćţ) LDAP (Lightweight Directory Access Protocol) + ĹĎÀƤ (Ĺĩĝĥţŵēĩĝĥţ Ďĩŕēćţōŕŷ Àććēśś Ƥŕōţōćōĺ) Forward Auth (Single Application) + Ƒōŕŵàŕď Àũţĥ (Śĩńĝĺē Àƥƥĺĩćàţĩōń) Forward Auth (Domain Level) + Ƒōŕŵàŕď Àũţĥ (Ďōmàĩń Ĺēvēĺ) SAML (Security Assertion Markup Language) + ŚÀMĹ (Śēćũŕĩţŷ Àśśēŕţĩōń Màŕķũƥ Ĺàńĝũàĝē) RADIUS (Remote Authentication Dial-In User Service) + ŔÀĎĨŨŚ (Ŕēmōţē Àũţĥēńţĩćàţĩōń Ďĩàĺ-Ĩń Ũśēŕ Śēŕvĩćē) SCIM (System for Cross-domain Identity Management) + ŚĆĨM (Śŷśţēm ƒōŕ Ćŕōśś-ďōmàĩń Ĩďēńţĩţŷ Màńàĝēmēńţ) The token has been copied to your clipboard + Ţĥē ţōķēń ĥàś ƀēēń ćōƥĩēď ţō ŷōũŕ ćĺĩƥƀōàŕď The token was displayed because authentik does not have permission to write to the clipboard + Ţĥē ţōķēń ŵàś ďĩśƥĺàŷēď ƀēćàũśē àũţĥēńţĩķ ďōēś ńōţ ĥàvē ƥēŕmĩśśĩōń ţō ŵŕĩţē ţō ţĥē ćĺĩƥƀōàŕď A copy of this recovery link has been placed in your clipboard + À ćōƥŷ ōƒ ţĥĩś ŕēćōvēŕŷ ĺĩńķ ĥàś ƀēēń ƥĺàćēď ĩń ŷōũŕ ćĺĩƥƀōàŕď The current tenant must have a recovery flow configured to use a recovery link + Ţĥē ćũŕŕēńţ ţēńàńţ mũśţ ĥàvē à ŕēćōvēŕŷ ƒĺōŵ ćōńƒĩĝũŕēď ţō ũśē à ŕēćōvēŕŷ ĺĩńķ Create recovery link + Ćŕēàţē ŕēćōvēŕŷ ĺĩńķ Create Recovery Link + Ćŕēàţē Ŕēćōvēŕŷ Ĺĩńķ External + Ēxţēŕńàĺ Service account + Śēŕvĩćē àććōũńţ Service account (internal) + Śēŕvĩćē àććōũńţ (ĩńţēŕńàĺ) Check the release notes + Ćĥēćķ ţĥē ŕēĺēàśē ńōţēś User Statistics + Ũśēŕ Śţàţĩśţĩćś <No name set> + <Ńō ńàmē śēţ> For nginx's auth_request or traefik's forwardAuth + Ƒōŕ ńĝĩńx'ś àũţĥ_ŕēǫũēśţ ōŕ ţŕàēƒĩķ'ś ƒōŕŵàŕďÀũţĥ For nginx's auth_request or traefik's forwardAuth per root domain + Ƒōŕ ńĝĩńx'ś àũţĥ_ŕēǫũēśţ ōŕ ţŕàēƒĩķ'ś ƒōŕŵàŕďÀũţĥ ƥēŕ ŕōōţ ďōmàĩń RBAC is in preview. + ŔßÀĆ ĩś ĩń ƥŕēvĩēŵ. User type used for newly created users. + Ũśēŕ ţŷƥē ũśēď ƒōŕ ńēŵĺŷ ćŕēàţēď ũśēŕś. Users created + Ũśēŕś ćŕēàţēď Failed logins + Ƒàĩĺēď ĺōĝĩńś Also known as Client ID. + Àĺśō ķńōŵń àś Ćĺĩēńţ ĨĎ. Also known as Client Secret. + Àĺśō ķńōŵń àś Ćĺĩēńţ Śēćŕēţ. Global status + Ĝĺōƀàĺ śţàţũś Vendor + Vēńďōŕ No sync status. + Ńō śŷńć śţàţũś. Sync currently running. + Śŷńć ćũŕŕēńţĺŷ ŕũńńĩńĝ. Connectivity + Ćōńńēćţĩvĩţŷ 0: Too guessable: risky password. (guesses &lt; 10^3) + 0: Ţōō ĝũēśśàƀĺē: ŕĩśķŷ ƥàśśŵōŕď. (ĝũēśśēś &ĺţ; 10^3) 1: Very guessable: protection from throttled online attacks. (guesses &lt; 10^6) + 1: Vēŕŷ ĝũēśśàƀĺē: ƥŕōţēćţĩōń ƒŕōm ţĥŕōţţĺēď ōńĺĩńē àţţàćķś. (ĝũēśśēś &ĺţ; 10^6) 2: Somewhat guessable: protection from unthrottled online attacks. (guesses &lt; 10^8) + 2: Śōmēŵĥàţ ĝũēśśàƀĺē: ƥŕōţēćţĩōń ƒŕōm ũńţĥŕōţţĺēď ōńĺĩńē àţţàćķś. (ĝũēśśēś &ĺţ; 10^8) 3: Safely unguessable: moderate protection from offline slow-hash scenario. (guesses &lt; 10^10) + 3: Śàƒēĺŷ ũńĝũēśśàƀĺē: mōďēŕàţē ƥŕōţēćţĩōń ƒŕōm ōƒƒĺĩńē śĺōŵ-ĥàśĥ śćēńàŕĩō. (ĝũēśśēś &ĺţ; 10^10) 4: Very unguessable: strong protection from offline slow-hash scenario. (guesses &gt;= 10^10) + 4: Vēŕŷ ũńĝũēśśàƀĺē: śţŕōńĝ ƥŕōţēćţĩōń ƒŕōm ōƒƒĺĩńē śĺōŵ-ĥàśĥ śćēńàŕĩō. (ĝũēśśēś &ĝţ;= 10^10) Successfully created user and added to group + Śũććēśśƒũĺĺŷ ćŕēàţēď ũśēŕ àńď àďďēď ţō ĝŕōũƥ This user will be added to the group "". + Ţĥĩś ũśēŕ ŵĩĺĺ ƀē àďďēď ţō ţĥē ĝŕōũƥ "". Pretend user exists + Ƥŕēţēńď ũśēŕ ēxĩśţś When enabled, the stage will always accept the given user identifier and continue. + Ŵĥēń ēńàƀĺēď, ţĥē śţàĝē ŵĩĺĺ àĺŵàŷś àććēƥţ ţĥē ĝĩvēń ũśēŕ ĩďēńţĩƒĩēŕ àńď ćōńţĩńũē. + + + There was an error in the application. + Ţĥēŕē ŵàś àń ēŕŕōŕ ĩń ţĥē àƥƥĺĩćàţĩōń. + + + Review the application. + Ŕēvĩēŵ ţĥē àƥƥĺĩćàţĩōń. + + + There was an error in the provider. + Ţĥēŕē ŵàś àń ēŕŕōŕ ĩń ţĥē ƥŕōvĩďēŕ. + + + Review the provider. + Ŕēvĩēŵ ţĥē ƥŕōvĩďēŕ. + + + There was an error + Ţĥēŕē ŵàś àń ēŕŕōŕ + + + There was an error creating the application, but no error message was sent. Please review the server logs. + Ţĥēŕē ŵàś àń ēŕŕōŕ ćŕēàţĩńĝ ţĥē àƥƥĺĩćàţĩōń, ƀũţ ńō ēŕŕōŕ mēśśàĝē ŵàś śēńţ. Ƥĺēàśē ŕēvĩēŵ ţĥē śēŕvēŕ ĺōĝś. + + + Configure LDAP Provider + Ćōńƒĩĝũŕē ĹĎÀƤ Ƥŕōvĩďēŕ + + + Configure OAuth2/OpenId Provider + Ćōńƒĩĝũŕē ŌÀũţĥ2/ŌƥēńĨď Ƥŕōvĩďēŕ + + + Configure Proxy Provider + Ćōńƒĩĝũŕē Ƥŕōxŷ Ƥŕōvĩďēŕ + + + AdditionalScopes + ÀďďĩţĩōńàĺŚćōƥēś + + + Configure Radius Provider + Ćōńƒĩĝũŕē Ŕàďĩũś Ƥŕōvĩďēŕ + + + Configure SAML Provider + Ćōńƒĩĝũŕē ŚÀMĹ Ƥŕōvĩďēŕ + + + Property mappings used for user mapping. + Ƥŕōƥēŕţŷ màƥƥĩńĝś ũśēď ƒōŕ ũśēŕ màƥƥĩńĝ. + + + Configure SCIM Provider + Ćōńƒĩĝũŕē ŚĆĨM Ƥŕōvĩďēŕ + + + Property mappings used for group creation. + Ƥŕōƥēŕţŷ màƥƥĩńĝś ũśēď ƒōŕ ĝŕōũƥ ćŕēàţĩōń. + + + Event volume diff --git a/web/xliff/tr.xlf b/web/xliff/tr.xlf index f2eb13ba5..188474957 100644 --- a/web/xliff/tr.xlf +++ b/web/xliff/tr.xlf @@ -5709,12 +5709,6 @@ Bindings to groups/users are checked against the user of the event. Your application has been saved - - In the Application: - - - In the Provider: - Method's display Name. @@ -5984,6 +5978,54 @@ Bindings to groups/users are checked against the user of the event. When enabled, the stage will always accept the given user identifier and continue. + + + There was an error in the application. + + + Review the application. + + + There was an error in the provider. + + + Review the provider. + + + There was an error + + + There was an error creating the application, but no error message was sent. Please review the server logs. + + + Configure LDAP Provider + + + Configure OAuth2/OpenId Provider + + + Configure Proxy Provider + + + AdditionalScopes + + + Configure Radius Provider + + + Configure SAML Provider + + + Property mappings used for user mapping. + + + Configure SCIM Provider + + + Property mappings used for group creation. + + + Event volume diff --git a/web/xliff/zh-Hans.xlf b/web/xliff/zh-Hans.xlf index 96bfb9808..ad0d2c265 100644 --- a/web/xliff/zh-Hans.xlf +++ b/web/xliff/zh-Hans.xlf @@ -7619,14 +7619,6 @@ Bindings to groups/users are checked against the user of the event. Your application has been saved 您的应用程序已保存 - - In the Application: - 在应用程序中: - - - In the Provider: - 在提供程序中: - Method's display Name. 方法的显示名称。 @@ -7988,6 +7980,54 @@ Bindings to groups/users are checked against the user of the event. When enabled, the stage will always accept the given user identifier and continue. 启用时,此阶段总是会接受指定的用户 ID 并继续。 + + + There was an error in the application. + + + Review the application. + + + There was an error in the provider. + + + Review the provider. + + + There was an error + + + There was an error creating the application, but no error message was sent. Please review the server logs. + + + Configure LDAP Provider + + + Configure OAuth2/OpenId Provider + + + Configure Proxy Provider + + + AdditionalScopes + + + Configure Radius Provider + + + Configure SAML Provider + + + Property mappings used for user mapping. + + + Configure SCIM Provider + + + Property mappings used for group creation. + + + Event volume diff --git a/web/xliff/zh-Hant.xlf b/web/xliff/zh-Hant.xlf index e836104f2..1b85d0ee3 100644 --- a/web/xliff/zh-Hant.xlf +++ b/web/xliff/zh-Hant.xlf @@ -5757,12 +5757,6 @@ Bindings to groups/users are checked against the user of the event. Your application has been saved - - In the Application: - - - In the Provider: - Method's display Name. @@ -6032,6 +6026,54 @@ Bindings to groups/users are checked against the user of the event. When enabled, the stage will always accept the given user identifier and continue. + + + There was an error in the application. + + + Review the application. + + + There was an error in the provider. + + + Review the provider. + + + There was an error + + + There was an error creating the application, but no error message was sent. Please review the server logs. + + + Configure LDAP Provider + + + Configure OAuth2/OpenId Provider + + + Configure Proxy Provider + + + AdditionalScopes + + + Configure Radius Provider + + + Configure SAML Provider + + + Property mappings used for user mapping. + + + Configure SCIM Provider + + + Property mappings used for group creation. + + + Event volume diff --git a/web/xliff/zh_TW.xlf b/web/xliff/zh_TW.xlf index eccaef9f7..05818f770 100644 --- a/web/xliff/zh_TW.xlf +++ b/web/xliff/zh_TW.xlf @@ -7555,14 +7555,6 @@ Bindings to groups/users are checked against the user of the event. Your application has been saved 已經儲存您的應用程式 - - In the Application: - 在應用程式: - - - In the Provider: - 在供應商: - Method's display Name. 方法的顯示名稱。 @@ -7925,6 +7917,54 @@ Bindings to groups/users are checked against the user of the event. Are you sure you want to remove the selected users from the group ? + + + There was an error in the application. + + + Review the application. + + + There was an error in the provider. + + + Review the provider. + + + There was an error + + + There was an error creating the application, but no error message was sent. Please review the server logs. + + + Configure LDAP Provider + + + Configure OAuth2/OpenId Provider + + + Configure Proxy Provider + + + AdditionalScopes + + + Configure Radius Provider + + + Configure SAML Provider + + + Property mappings used for user mapping. + + + Configure SCIM Provider + + + Property mappings used for group creation. + + + Event volume diff --git a/website/docs/installation/docker-compose.md b/website/docs/installation/docker-compose.md index 39ada87ad..527058c8a 100644 --- a/website/docs/installation/docker-compose.md +++ b/website/docs/installation/docker-compose.md @@ -5,7 +5,7 @@ title: Docker Compose installation This installation method is for test-setups and small-scale production setups. :::info -You can also [view a video walk-through](https://youtu.be/owk1a_1xYe4) of the installation process on Docker (with bonus details about email configuration and other important options). +You can also [view a video walk-through](https://www.youtube.com/watch?v=O1qUbrk4Yc8) of the installation process on Docker (with bonus details about email configuration and other important options). ::: ## Requirements diff --git a/website/docs/installation/kubernetes.md b/website/docs/installation/kubernetes.md index 1c3f3dd85..e7caa73e0 100644 --- a/website/docs/installation/kubernetes.md +++ b/website/docs/installation/kubernetes.md @@ -5,7 +5,7 @@ title: Kubernetes installation You can install authentik to run on Kubernetes using Helm Chart. :::info -You can also [view a video walk-through](https://youtu.be/owk1a_1xYe4) of the installation process on Kubernetes (with bonus details about email configuration and other important options). +You can also [view a video walk-through](https://www.youtube.com/watch?v=O1qUbrk4Yc8) of the installation process on Kubernetes (with bonus details about email configuration and other important options). ::: ### Requirements diff --git a/website/package-lock.json b/website/package-lock.json index 8479133d5..58c698705 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -18,7 +18,7 @@ "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "disqus-react": "^1.1.5", - "postcss": "^8.4.31", + "postcss": "^8.4.32", "prism-react-renderer": "^2.3.0", "rapidoc": "^9.3.4", "react": "^18.2.0", @@ -26,14 +26,14 @@ "react-dom": "^18.2.0", "react-feather": "^2.0.10", "react-toggle": "^4.1.3", - "react-tooltip": "^5.24.0", + "react-tooltip": "^5.25.0", "remark-github": "^12.0.0" }, "devDependencies": { "@docusaurus/module-type-aliases": "3.0.1", "@docusaurus/tsconfig": "3.0.1", "@docusaurus/types": "3.0.1", - "@types/react": "^18.2.39", + "@types/react": "^18.2.42", "prettier": "3.1.0", "typescript": "~5.3.2" }, @@ -4373,9 +4373,9 @@ "integrity": "sha512-+0autS93xyXizIYiyL02FCY8N+KkKPhILhcUSA276HxzreZ16kl+cmwvV2qAM/PuCCwPXzOXOWhiPcw20uSFcA==" }, "node_modules/@types/react": { - "version": "18.2.39", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.39.tgz", - "integrity": "sha512-Oiw+ppED6IremMInLV4HXGbfbG6GyziY3kqAwJYOR0PNbkYDmLWQA3a95EhdSmamsvbkJN96ZNN+YD+fGjzSBA==", + "version": "18.2.42", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.42.tgz", + "integrity": "sha512-c1zEr96MjakLYus/wPnuWDo1/zErfdU9rNsIGmE+NV71nx88FG9Ttgo5dqorXTu/LImX2f63WBP986gJkMPNbA==", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -13137,9 +13137,9 @@ } }, "node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "version": "8.4.32", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz", + "integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==", "funding": [ { "type": "opencollective", @@ -13155,7 +13155,7 @@ } ], "dependencies": { - "nanoid": "^3.3.6", + "nanoid": "^3.3.7", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, @@ -14365,9 +14365,9 @@ } }, "node_modules/react-tooltip": { - "version": "5.24.0", - "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.24.0.tgz", - "integrity": "sha512-HjstgpOrUwP4eN6mHU4EThpbxVuKO5SvqumRt1aAcPq0ya+pIVVxlwltndtdIIMBJ7w3jnN05vNfcfh2sxE2mQ==", + "version": "5.25.0", + "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.25.0.tgz", + "integrity": "sha512-/eGhmlwbHlJrVoUe75fb58rJfAy9aZnTvQAK9ZUPM0n9mmBGpEk13vDPiQVCeUuax+fBej+7JPsUXlhzaySc7w==", "dependencies": { "@floating-ui/dom": "^1.0.0", "classnames": "^2.3.0" diff --git a/website/package.json b/website/package.json index 326c2c6a8..8153abbb2 100644 --- a/website/package.json +++ b/website/package.json @@ -25,14 +25,14 @@ "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "disqus-react": "^1.1.5", - "postcss": "^8.4.31", + "postcss": "^8.4.32", "prism-react-renderer": "^2.3.0", "rapidoc": "^9.3.4", "react-before-after-slider-component": "^1.1.8", "react-dom": "^18.2.0", "react-feather": "^2.0.10", "react-toggle": "^4.1.3", - "react-tooltip": "^5.24.0", + "react-tooltip": "^5.25.0", "react": "^18.2.0", "remark-github": "^12.0.0" }, @@ -52,7 +52,7 @@ "@docusaurus/module-type-aliases": "3.0.1", "@docusaurus/tsconfig": "3.0.1", "@docusaurus/types": "3.0.1", - "@types/react": "^18.2.39", + "@types/react": "^18.2.42", "prettier": "3.1.0", "typescript": "~5.3.2" },