Merge branch 'main' into dev

* main: (24 commits)
  core: add additional RBAC permission to restrict setting the superuser status on groups (#12900)
  web: bump API Client version (#13089)
  core: bump github.com/spf13/cobra from 1.8.1 to 1.9.1 (#13085)
  stages/authenticator_email: Email OTP (#12630)
  website: bump dompurify and mermaid in /website (#13077)
  web: bump dompurify and mermaid in /web (#13078)
  core: bump django-filter from 24.3 to 25.1 (#13086)
  enterprise/audit: fix diff being created when not enabled (#13084)
  core, web: update translations (#13088)
  translate: Updates for file locale/en/LC_MESSAGES/django.po in zh_CN (#13080)
  translate: Updates for file locale/en/LC_MESSAGES/django.po in zh-Hans (#13081)
  translate: Updates for file web/xliff/en.xlf in zh-Hans (#13082)
  translate: Updates for file web/xliff/en.xlf in zh_CN (#13083)
  core: bump django-storages from 1.14.4 to 1.14.5 (#13087)
  web/user: fix redirects back to user settings (#13076)
  ci: parallelize unit tests (#13036)
  core, web: update translations (#13072)
  stages/authenticator_webauthn: Update FIDO MDS3 & Passkey aaguid blobs (#13073)
  root: Improve debugging experience (#12961)
  core, web: update translations (#13071)
  ...
This commit is contained in:
Ken Sternberg
2025-02-17 08:48:25 -08:00
78 changed files with 8078 additions and 2366 deletions

View File

@ -43,15 +43,26 @@ jobs:
uses: ./.github/actions/setup uses: ./.github/actions/setup
- name: run migrations - name: run migrations
run: poetry run python -m lifecycle.migrate run: poetry run python -m lifecycle.migrate
test-migrations-from-stable: test-make-seed:
name: test-migrations-from-stable - PostgreSQL ${{ matrix.psql }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps:
- id: seed
run: |
echo "seed=$(printf "%d\n" "0x$(openssl rand -hex 4)")" >> "$GITHUB_OUTPUT"
outputs:
seed: ${{ steps.seed.outputs.seed }}
test-migrations-from-stable:
name: test-migrations-from-stable - PostgreSQL ${{ matrix.psql }} - Run ${{ matrix.run_id }}/5
runs-on: ubuntu-latest
timeout-minutes: 20
needs: test-make-seed
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
psql: psql:
- 15-alpine - 15-alpine
- 16-alpine - 16-alpine
run_id: [1, 2, 3, 4, 5]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
@ -93,18 +104,23 @@ jobs:
env: env:
# Test in the main database that we just migrated from the previous stable version # Test in the main database that we just migrated from the previous stable version
AUTHENTIK_POSTGRESQL__TEST__NAME: authentik AUTHENTIK_POSTGRESQL__TEST__NAME: authentik
CI_TEST_SEED: ${{ needs.test-make-seed.outputs.seed }}
CI_RUN_ID: ${{ matrix.run_id }}
CI_TOTAL_RUNS: "5"
run: | run: |
poetry run make test poetry run make ci-test
test-unittest: test-unittest:
name: test-unittest - PostgreSQL ${{ matrix.psql }} name: test-unittest - PostgreSQL ${{ matrix.psql }} - Run ${{ matrix.run_id }}/5
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 30 timeout-minutes: 20
needs: test-make-seed
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
psql: psql:
- 15-alpine - 15-alpine
- 16-alpine - 16-alpine
run_id: [1, 2, 3, 4, 5]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Setup authentik env - name: Setup authentik env
@ -112,9 +128,12 @@ jobs:
with: with:
postgresql_version: ${{ matrix.psql }} postgresql_version: ${{ matrix.psql }}
- name: run unittest - name: run unittest
env:
CI_TEST_SEED: ${{ needs.test-make-seed.outputs.seed }}
CI_RUN_ID: ${{ matrix.run_id }}
CI_TOTAL_RUNS: "5"
run: | run: |
poetry run make test poetry run make ci-test
poetry run coverage xml
- if: ${{ always() }} - if: ${{ always() }}
uses: codecov/codecov-action@v5 uses: codecov/codecov-action@v5
with: with:

3
.gitignore vendored
View File

@ -209,3 +209,6 @@ source_docs/
### Golang ### ### Golang ###
/vendor/ /vendor/
### Docker ###
docker-compose.override.yml

4
.vscode/launch.json vendored
View File

@ -12,7 +12,7 @@
"pathMappings": [ "pathMappings": [
{ {
"localRoot": "${workspaceFolder}", "localRoot": "${workspaceFolder}",
"remoteRoot": "/" "remoteRoot": "."
} }
], ],
"django": true "django": true
@ -28,7 +28,7 @@
"pathMappings": [ "pathMappings": [
{ {
"localRoot": "${workspaceFolder}", "localRoot": "${workspaceFolder}",
"remoteRoot": "/" "remoteRoot": "."
} }
], ],
"django": true "django": true

View File

@ -284,3 +284,8 @@ ci-bandit: ci--meta-debug
ci-pending-migrations: ci--meta-debug ci-pending-migrations: ci--meta-debug
ak makemigrations --check ak makemigrations --check
ci-test: ci--meta-debug
coverage run manage.py test --keepdb --randomly-seed ${CI_TEST_SEED} authentik
coverage report
coverage xml

View File

@ -4,6 +4,7 @@ from json import loads
from django.db.models import Prefetch from django.db.models import Prefetch
from django.http import Http404 from django.http import Http404
from django.utils.translation import gettext as _
from django_filters.filters import CharFilter, ModelMultipleChoiceFilter from django_filters.filters import CharFilter, ModelMultipleChoiceFilter
from django_filters.filterset import FilterSet from django_filters.filterset import FilterSet
from drf_spectacular.utils import ( from drf_spectacular.utils import (
@ -81,9 +82,37 @@ class GroupSerializer(ModelSerializer):
if not self.instance or not parent: if not self.instance or not parent:
return parent return parent
if str(parent.group_uuid) == str(self.instance.group_uuid): if str(parent.group_uuid) == str(self.instance.group_uuid):
raise ValidationError("Cannot set group as parent of itself.") raise ValidationError(_("Cannot set group as parent of itself."))
return parent return parent
def validate_is_superuser(self, superuser: bool):
"""Ensure that the user creating this group has permissions to set the superuser flag"""
request: Request = self.context.get("request", None)
if not request:
return superuser
# If we're updating an instance, and the state hasn't changed, we don't need to check perms
if self.instance and superuser == self.instance.is_superuser:
return superuser
user: User = request.user
perm = (
"authentik_core.enable_group_superuser"
if superuser
else "authentik_core.disable_group_superuser"
)
has_perm = user.has_perm(perm)
if self.instance and not has_perm:
has_perm = user.has_perm(perm, self.instance)
if not has_perm:
raise ValidationError(
_(
(
"User does not have permission to set "
"superuser status to {superuser_status}."
).format_map({"superuser_status": superuser})
)
)
return superuser
class Meta: class Meta:
model = Group model = Group
fields = [ fields = [

View File

@ -0,0 +1,26 @@
# Generated by Django 5.0.11 on 2025-01-30 23:55
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("authentik_core", "0042_authenticatedsession_authentik_c_expires_08251d_idx_and_more"),
]
operations = [
migrations.AlterModelOptions(
name="group",
options={
"permissions": [
("add_user_to_group", "Add user to group"),
("remove_user_from_group", "Remove user from group"),
("enable_group_superuser", "Enable superuser status"),
("disable_group_superuser", "Disable superuser status"),
],
"verbose_name": "Group",
"verbose_name_plural": "Groups",
},
),
]

View File

@ -204,6 +204,8 @@ class Group(SerializerModel, AttributesMixin):
permissions = [ permissions = [
("add_user_to_group", _("Add user to group")), ("add_user_to_group", _("Add user to group")),
("remove_user_from_group", _("Remove user from group")), ("remove_user_from_group", _("Remove user from group")),
("enable_group_superuser", _("Enable superuser status")),
("disable_group_superuser", _("Disable superuser status")),
] ]
def __str__(self): def __str__(self):

View File

@ -4,7 +4,7 @@ from django.urls.base import reverse
from guardian.shortcuts import assign_perm from guardian.shortcuts import assign_perm
from rest_framework.test import APITestCase from rest_framework.test import APITestCase
from authentik.core.models import Group, User from authentik.core.models import Group
from authentik.core.tests.utils import create_test_admin_user, create_test_user from authentik.core.tests.utils import create_test_admin_user, create_test_user
from authentik.lib.generators import generate_id from authentik.lib.generators import generate_id
@ -14,7 +14,7 @@ class TestGroupsAPI(APITestCase):
def setUp(self) -> None: def setUp(self) -> None:
self.login_user = create_test_user() self.login_user = create_test_user()
self.user = User.objects.create(username="test-user") self.user = create_test_user()
def test_list_with_users(self): def test_list_with_users(self):
"""Test listing with users""" """Test listing with users"""
@ -109,3 +109,57 @@ class TestGroupsAPI(APITestCase):
}, },
) )
self.assertEqual(res.status_code, 400) self.assertEqual(res.status_code, 400)
def test_superuser_no_perm(self):
"""Test creating a superuser group without permission"""
assign_perm("authentik_core.add_group", self.login_user)
self.client.force_login(self.login_user)
res = self.client.post(
reverse("authentik_api:group-list"),
data={"name": generate_id(), "is_superuser": True},
)
self.assertEqual(res.status_code, 400)
self.assertJSONEqual(
res.content,
{"is_superuser": ["User does not have permission to set superuser status to True."]},
)
def test_superuser_update_no_perm(self):
"""Test updating a superuser group without permission"""
group = Group.objects.create(name=generate_id(), is_superuser=True)
assign_perm("view_group", self.login_user, group)
assign_perm("change_group", self.login_user, group)
self.client.force_login(self.login_user)
res = self.client.patch(
reverse("authentik_api:group-detail", kwargs={"pk": group.pk}),
data={"is_superuser": False},
)
self.assertEqual(res.status_code, 400)
self.assertJSONEqual(
res.content,
{"is_superuser": ["User does not have permission to set superuser status to False."]},
)
def test_superuser_update_no_change(self):
"""Test updating a superuser group without permission
and without changing the superuser status"""
group = Group.objects.create(name=generate_id(), is_superuser=True)
assign_perm("view_group", self.login_user, group)
assign_perm("change_group", self.login_user, group)
self.client.force_login(self.login_user)
res = self.client.patch(
reverse("authentik_api:group-detail", kwargs={"pk": group.pk}),
data={"name": generate_id(), "is_superuser": True},
)
self.assertEqual(res.status_code, 200)
def test_superuser_create(self):
"""Test creating a superuser group with permission"""
assign_perm("authentik_core.add_group", self.login_user)
assign_perm("authentik_core.enable_group_superuser", self.login_user)
self.client.force_login(self.login_user)
res = self.client.post(
reverse("authentik_api:group-list"),
data={"name": generate_id(), "is_superuser": True},
)
self.assertEqual(res.status_code, 201)

View File

@ -97,6 +97,8 @@ class EnterpriseAuditMiddleware(AuditMiddleware):
thread_kwargs: dict | None = None, thread_kwargs: dict | None = None,
**_, **_,
): ):
if not self.enabled:
return super().post_save_handler(request, sender, instance, created, thread_kwargs, **_)
if not should_log_model(instance): if not should_log_model(instance):
return None return None
thread_kwargs = {} thread_kwargs = {}
@ -122,6 +124,8 @@ class EnterpriseAuditMiddleware(AuditMiddleware):
): ):
thread_kwargs = {} thread_kwargs = {}
m2m_field = None m2m_field = None
if not self.enabled:
return super().m2m_changed_handler(request, sender, instance, action, thread_kwargs)
# For the audit log we don't care about `pre_` or `post_` so we trim that part off # For the audit log we don't care about `pre_` or `post_` so we trim that part off
_, _, action_direction = action.partition("_") _, _, action_direction = action.partition("_")
# resolve the "through" model to an actual field # resolve the "through" model to an actual field

View File

@ -0,0 +1,54 @@
"""Email utility functions"""
def mask_email(email: str | None) -> str | None:
"""Mask email address for privacy
Args:
email: Email address to mask
Returns:
Masked email address or None if input is None
Example:
mask_email("myname@company.org")
'm*****@c******.org'
"""
if not email:
return None
# Basic email format validation
if email.count("@") != 1:
raise ValueError("Invalid email format: Must contain exactly one '@' symbol")
local, domain = email.split("@")
if not local or not domain:
raise ValueError("Invalid email format: Local and domain parts cannot be empty")
domain_parts = domain.split(".")
if len(domain_parts) < 2: # noqa: PLR2004
raise ValueError("Invalid email format: Domain must contain at least one dot")
limit = 2
# Mask local part (keep first char)
if len(local) <= limit:
masked_local = "*" * len(local)
else:
masked_local = local[0] + "*" * (len(local) - 1)
# Mask each domain part except the last one (TLD)
masked_domain_parts = []
for _i, part in enumerate(domain_parts[:-1]): # Process all parts except TLD
if not part: # Check for empty parts (consecutive dots)
raise ValueError("Invalid email format: Domain parts cannot be empty")
if len(part) <= limit:
masked_part = "*" * len(part)
else:
masked_part = part[0] + "*" * (len(part) - 1)
masked_domain_parts.append(masked_part)
# Add TLD unchanged
if not domain_parts[-1]: # Check if TLD is empty
raise ValueError("Invalid email format: TLD cannot be empty")
masked_domain_parts.append(domain_parts[-1])
return f"{masked_local}@{'.'.join(masked_domain_parts)}"

View File

@ -100,6 +100,7 @@ TENANT_APPS = [
"authentik.sources.scim", "authentik.sources.scim",
"authentik.stages.authenticator", "authentik.stages.authenticator",
"authentik.stages.authenticator_duo", "authentik.stages.authenticator_duo",
"authentik.stages.authenticator_email",
"authentik.stages.authenticator_sms", "authentik.stages.authenticator_sms",
"authentik.stages.authenticator_static", "authentik.stages.authenticator_static",
"authentik.stages.authenticator_totp", "authentik.stages.authenticator_totp",

View File

@ -1,3 +1,4 @@
import math
from os import environ from os import environ
from ssl import OPENSSL_VERSION from ssl import OPENSSL_VERSION
@ -24,3 +25,20 @@ def pytest_report_header(*_, **__):
f"authentik version: {get_full_version()}", f"authentik version: {get_full_version()}",
f"OpenSSL version: {OPENSSL_VERSION}, FIPS: {backend._fips_enabled}", f"OpenSSL version: {OPENSSL_VERSION}, FIPS: {backend._fips_enabled}",
] ]
def pytest_collection_modifyitems(config: pytest.Config, items: list[pytest.Item]) -> None:
current_id = int(environ.get("CI_RUN_ID", 0)) - 1
total_ids = int(environ.get("CI_TOTAL_RUNS", 0))
if total_ids:
num_tests = len(items)
matrix_size = math.ceil(num_tests / total_ids)
start = current_id * matrix_size
end = (current_id + 1) * matrix_size
deselected_items = items[:start] + items[end:]
config.hook.pytest_deselected(items=deselected_items)
items[:] = items[start:end]
print(f" Executing {start} - {end} tests")

View File

@ -0,0 +1,85 @@
"""AuthenticatorEmailStage API Views"""
from rest_framework import mixins
from rest_framework.viewsets import GenericViewSet, ModelViewSet
from authentik.core.api.groups import GroupMemberSerializer
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import ModelSerializer
from authentik.flows.api.stages import StageSerializer
from authentik.stages.authenticator_email.models import AuthenticatorEmailStage, EmailDevice
class AuthenticatorEmailStageSerializer(StageSerializer):
"""AuthenticatorEmailStage Serializer"""
class Meta:
model = AuthenticatorEmailStage
fields = StageSerializer.Meta.fields + [
"configure_flow",
"friendly_name",
"use_global_settings",
"host",
"port",
"username",
"password",
"use_tls",
"use_ssl",
"timeout",
"from_address",
"subject",
"token_expiry",
"template",
]
class AuthenticatorEmailStageViewSet(UsedByMixin, ModelViewSet):
"""AuthenticatorEmailStage Viewset"""
queryset = AuthenticatorEmailStage.objects.all()
serializer_class = AuthenticatorEmailStageSerializer
filterset_fields = "__all__"
ordering = ["name"]
search_fields = ["name"]
class EmailDeviceSerializer(ModelSerializer):
"""Serializer for email authenticator devices"""
user = GroupMemberSerializer(read_only=True)
class Meta:
model = EmailDevice
fields = ["name", "pk", "email", "user"]
depth = 2
extra_kwargs = {
"email": {"read_only": True},
}
class EmailDeviceViewSet(
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
UsedByMixin,
mixins.ListModelMixin,
GenericViewSet,
):
"""Viewset for email authenticator devices"""
queryset = EmailDevice.objects.all()
serializer_class = EmailDeviceSerializer
search_fields = ["name"]
filterset_fields = ["name"]
ordering = ["name"]
owner_field = "user"
class EmailAdminDeviceViewSet(ModelViewSet):
"""Viewset for email authenticator devices (for admins)"""
queryset = EmailDevice.objects.all()
serializer_class = EmailDeviceSerializer
search_fields = ["name"]
filterset_fields = ["name"]
ordering = ["name"]

View File

@ -0,0 +1,12 @@
"""Email Authenticator"""
from authentik.blueprints.apps import ManagedAppConfig
class AuthentikStageAuthenticatorEmailConfig(ManagedAppConfig):
"""Email Authenticator App config"""
name = "authentik.stages.authenticator_email"
label = "authentik_stages_authenticator_email"
verbose_name = "authentik Stages.Authenticator.Email"
default = True

View File

@ -0,0 +1,132 @@
# Generated by Django 5.0.10 on 2025-01-27 20:05
import django.db.models.deletion
import django.utils.timezone
from django.conf import settings
from django.db import migrations, models
import authentik.lib.utils.time
class Migration(migrations.Migration):
initial = True
dependencies = [
("authentik_flows", "0027_auto_20231028_1424"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name="AuthenticatorEmailStage",
fields=[
(
"stage_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="authentik_flows.stage",
),
),
("friendly_name", models.TextField(null=True)),
(
"use_global_settings",
models.BooleanField(
default=False,
help_text="When enabled, global Email connection settings will be used and connection settings below will be ignored.",
),
),
("host", models.TextField(default="localhost")),
("port", models.IntegerField(default=25)),
("username", models.TextField(blank=True, default="")),
("password", models.TextField(blank=True, default="")),
("use_tls", models.BooleanField(default=False)),
("use_ssl", models.BooleanField(default=False)),
("timeout", models.IntegerField(default=10)),
(
"from_address",
models.EmailField(default="system@authentik.local", max_length=254),
),
(
"token_expiry",
models.TextField(
default="minutes=30",
help_text="Time the token sent is valid (Format: hours=3,minutes=17,seconds=300).",
validators=[authentik.lib.utils.time.timedelta_string_validator],
),
),
("subject", models.TextField(default="authentik Sign-in code")),
("template", models.TextField(default="email/email_otp.html")),
(
"configure_flow",
models.ForeignKey(
blank=True,
help_text="Flow used by an authenticated user to configure this Stage. If empty, user will not be able to configure this stage.",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="authentik_flows.flow",
),
),
],
options={
"verbose_name": "Email Authenticator Setup Stage",
"verbose_name_plural": "Email Authenticator Setup Stages",
},
bases=("authentik_flows.stage", models.Model),
),
migrations.CreateModel(
name="EmailDevice",
fields=[
(
"id",
models.AutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
("created", models.DateTimeField(auto_now_add=True)),
("last_updated", models.DateTimeField(auto_now=True)),
(
"name",
models.CharField(
help_text="The human-readable name of this device.", max_length=64
),
),
(
"confirmed",
models.BooleanField(default=True, help_text="Is this device ready for use?"),
),
("token", models.CharField(blank=True, max_length=16, null=True)),
(
"valid_until",
models.DateTimeField(
default=django.utils.timezone.now,
help_text="The timestamp of the moment of expiry of the saved token.",
),
),
("email", models.EmailField(max_length=254)),
("last_used", models.DateTimeField(auto_now=True)),
(
"stage",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="authentik_stages_authenticator_email.authenticatoremailstage",
),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL
),
),
],
options={
"verbose_name": "Email Device",
"verbose_name_plural": "Email Devices",
"unique_together": {("user", "email")},
},
),
]

View File

@ -0,0 +1,167 @@
from django.contrib.auth import get_user_model
from django.core.mail.backends.base import BaseEmailBackend
from django.core.mail.backends.smtp import EmailBackend
from django.db import models
from django.template import TemplateSyntaxError
from django.utils.translation import gettext_lazy as _
from django.views import View
from rest_framework.serializers import BaseSerializer
from authentik.events.models import Event, EventAction
from authentik.flows.exceptions import StageInvalidException
from authentik.flows.models import ConfigurableStage, FriendlyNamedStage, Stage
from authentik.lib.config import CONFIG
from authentik.lib.models import SerializerModel
from authentik.lib.utils.errors import exception_to_string
from authentik.lib.utils.time import timedelta_string_validator
from authentik.stages.authenticator.models import SideChannelDevice
from authentik.stages.email.utils import TemplateEmailMessage
class EmailTemplates(models.TextChoices):
"""Templates used for rendering the Email"""
EMAIL_OTP = (
"email/email_otp.html",
_("Email OTP"),
) # nosec
class AuthenticatorEmailStage(ConfigurableStage, FriendlyNamedStage, Stage):
"""Use Email-based authentication instead of authenticator-based."""
use_global_settings = models.BooleanField(
default=False,
help_text=_(
"When enabled, global Email connection settings will be used and "
"connection settings below will be ignored."
),
)
host = models.TextField(default="localhost")
port = models.IntegerField(default=25)
username = models.TextField(default="", blank=True)
password = models.TextField(default="", blank=True)
use_tls = models.BooleanField(default=False)
use_ssl = models.BooleanField(default=False)
timeout = models.IntegerField(default=10)
from_address = models.EmailField(default="system@authentik.local")
token_expiry = models.TextField(
default="minutes=30",
validators=[timedelta_string_validator],
help_text=_("Time the token sent is valid (Format: hours=3,minutes=17,seconds=300)."),
)
subject = models.TextField(default="authentik Sign-in code")
template = models.TextField(default=EmailTemplates.EMAIL_OTP)
@property
def serializer(self) -> type[BaseSerializer]:
from authentik.stages.authenticator_email.api import AuthenticatorEmailStageSerializer
return AuthenticatorEmailStageSerializer
@property
def view(self) -> type[View]:
from authentik.stages.authenticator_email.stage import AuthenticatorEmailStageView
return AuthenticatorEmailStageView
@property
def component(self) -> str:
return "ak-stage-authenticator-email-form"
@property
def backend_class(self) -> type[BaseEmailBackend]:
"""Get the email backend class to use"""
return EmailBackend
@property
def backend(self) -> BaseEmailBackend:
"""Get fully configured Email Backend instance"""
if self.use_global_settings:
CONFIG.refresh("email.password")
return self.backend_class(
host=CONFIG.get("email.host"),
port=CONFIG.get_int("email.port"),
username=CONFIG.get("email.username"),
password=CONFIG.get("email.password"),
use_tls=CONFIG.get_bool("email.use_tls", False),
use_ssl=CONFIG.get_bool("email.use_ssl", False),
timeout=CONFIG.get_int("email.timeout"),
)
return self.backend_class(
host=self.host,
port=self.port,
username=self.username,
password=self.password,
use_tls=self.use_tls,
use_ssl=self.use_ssl,
timeout=self.timeout,
)
def send(self, device: "EmailDevice"):
# Lazy import here to avoid circular import
from authentik.stages.email.tasks import send_mails
# Compose the message using templates
message = device._compose_email()
return send_mails(device.stage, message)
def __str__(self):
return f"Email Authenticator Stage {self.name}"
class Meta:
verbose_name = _("Email Authenticator Setup Stage")
verbose_name_plural = _("Email Authenticator Setup Stages")
class EmailDevice(SerializerModel, SideChannelDevice):
"""Email Device"""
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
email = models.EmailField()
stage = models.ForeignKey(AuthenticatorEmailStage, on_delete=models.CASCADE)
last_used = models.DateTimeField(auto_now=True)
@property
def serializer(self) -> type[BaseSerializer]:
from authentik.stages.authenticator_email.api import EmailDeviceSerializer
return EmailDeviceSerializer
def _compose_email(self) -> TemplateEmailMessage:
try:
pending_user = self.user
stage = self.stage
email = self.email
message = TemplateEmailMessage(
subject=_(stage.subject),
to=[(pending_user.name, email)],
template_name=stage.template,
template_context={
"user": pending_user,
"expires": self.valid_until,
"token": self.token,
},
)
return message
except TemplateSyntaxError as exc:
Event.new(
EventAction.CONFIGURATION_ERROR,
message=_("Exception occurred while rendering E-mail template"),
error=exception_to_string(exc),
template=stage.template,
).from_http(self.request)
raise StageInvalidException from exc
def __str__(self):
if not self.pk:
return "New Email Device"
return f"Email Device for {self.user_id}"
class Meta:
verbose_name = _("Email Device")
verbose_name_plural = _("Email Devices")
unique_together = (("user", "email"),)

View File

@ -0,0 +1,177 @@
"""Email Setup stage"""
from django.db.models import Q
from django.http import HttpRequest, HttpResponse
from django.http.request import QueryDict
from django.template.exceptions import TemplateSyntaxError
from django.utils.translation import gettext_lazy as _
from rest_framework.exceptions import ValidationError
from rest_framework.fields import BooleanField, CharField, IntegerField
from authentik.events.models import Event, EventAction
from authentik.flows.challenge import (
Challenge,
ChallengeResponse,
WithUserInfoChallenge,
)
from authentik.flows.exceptions import StageInvalidException
from authentik.flows.stage import ChallengeStageView
from authentik.lib.utils.email import mask_email
from authentik.lib.utils.errors import exception_to_string
from authentik.lib.utils.time import timedelta_from_string
from authentik.stages.authenticator_email.models import (
AuthenticatorEmailStage,
EmailDevice,
)
from authentik.stages.email.tasks import send_mails
from authentik.stages.email.utils import TemplateEmailMessage
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
SESSION_KEY_EMAIL_DEVICE = "authentik/stages/authenticator_email/email_device"
PLAN_CONTEXT_EMAIL = "email"
PLAN_CONTEXT_EMAIL_SENT = "email_sent"
PLAN_CONTEXT_EMAIL_OVERRIDE = "email"
class AuthenticatorEmailChallenge(WithUserInfoChallenge):
"""Authenticator Email Setup challenge"""
# Set to true if no previous prompt stage set the email
# this stage will also check prompt_data.email
email = CharField(default=None, allow_blank=True, allow_null=True)
email_required = BooleanField(default=True)
component = CharField(default="ak-stage-authenticator-email")
class AuthenticatorEmailChallengeResponse(ChallengeResponse):
"""Authenticator Email Challenge response, device is set by get_response_instance"""
device: EmailDevice
code = IntegerField(required=False)
email = CharField(required=False)
component = CharField(default="ak-stage-authenticator-email")
def validate(self, attrs: dict) -> dict:
"""Check"""
if "code" not in attrs:
if "email" not in attrs:
raise ValidationError("email required")
self.device.email = attrs["email"]
self.stage.validate_and_send(attrs["email"])
return super().validate(attrs)
if not self.device.verify_token(str(attrs["code"])):
raise ValidationError(_("Code does not match"))
self.device.confirmed = True
return super().validate(attrs)
class AuthenticatorEmailStageView(ChallengeStageView):
"""Authenticator Email Setup stage"""
response_class = AuthenticatorEmailChallengeResponse
def validate_and_send(self, email: str):
"""Validate email and send message"""
pending_user = self.get_pending_user()
stage: AuthenticatorEmailStage = self.executor.current_stage
if EmailDevice.objects.filter(Q(email=email), stage=stage.pk).exists():
raise ValidationError(_("Invalid email"))
device: EmailDevice = self.request.session[SESSION_KEY_EMAIL_DEVICE]
try:
message = TemplateEmailMessage(
subject=_(stage.subject),
to=[(pending_user.name, email)],
language=pending_user.locale(self.request),
template_name=stage.template,
template_context={
"user": pending_user,
"expires": device.valid_until,
"token": device.token,
},
)
send_mails(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=stage.template,
).from_http(self.request)
raise StageInvalidException from exc
def _has_email(self) -> str | None:
context = self.executor.plan.context
# Check user's email attribute
user = self.get_pending_user()
if user.email:
self.logger.debug("got email from user attributes")
return user.email
# Check plan context for email
if PLAN_CONTEXT_EMAIL in context.get(PLAN_CONTEXT_PROMPT, {}):
self.logger.debug("got email from plan context")
return context.get(PLAN_CONTEXT_PROMPT, {}).get(PLAN_CONTEXT_EMAIL)
# Check device for email
if SESSION_KEY_EMAIL_DEVICE in self.request.session:
self.logger.debug("got email from device in session")
device: EmailDevice = self.request.session[SESSION_KEY_EMAIL_DEVICE]
if device.email == "":
return None
return device.email
return None
def get_challenge(self, *args, **kwargs) -> Challenge:
email = self._has_email()
return AuthenticatorEmailChallenge(
data={
"email": mask_email(email),
"email_required": email is None,
}
)
def get_response_instance(self, data: QueryDict) -> ChallengeResponse:
response = super().get_response_instance(data)
response.device = self.request.session[SESSION_KEY_EMAIL_DEVICE]
return response
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
user = self.get_pending_user()
stage: AuthenticatorEmailStage = self.executor.current_stage
if SESSION_KEY_EMAIL_DEVICE not in self.request.session:
device = EmailDevice(user=user, confirmed=False, stage=stage, name="Email Device")
valid_secs: int = timedelta_from_string(stage.token_expiry).total_seconds()
device.generate_token(valid_secs=valid_secs, commit=False)
self.request.session[SESSION_KEY_EMAIL_DEVICE] = device
if email := self._has_email():
device.email = email
try:
self.validate_and_send(email)
except ValidationError as exc:
# We had an email given already (at this point only possible from flow
# context), but an error occurred while sending (most likely)
# due to a duplicate device, so delete the email we got given, reset the state
# (ish) and retry
device.email = ""
self.executor.plan.context.get(PLAN_CONTEXT_PROMPT, {}).pop(
PLAN_CONTEXT_EMAIL, None
)
self.request.session.pop(SESSION_KEY_EMAIL_DEVICE, None)
self.logger.warning("failed to send email to pre-set address", exc=exc)
return self.get(request, *args, **kwargs)
return super().get(request, *args, **kwargs)
def challenge_valid(self, response: ChallengeResponse) -> HttpResponse:
"""Email Token is validated by challenge"""
device: EmailDevice = self.request.session[SESSION_KEY_EMAIL_DEVICE]
if not device.confirmed:
return self.challenge_invalid(response)
device.save()
del self.request.session[SESSION_KEY_EMAIL_DEVICE]
return self.executor.stage_ok()

View File

@ -0,0 +1,44 @@
{% extends "email/base.html" %}
{% load i18n %}
{% load humanize %}
{% block content %}
<tr>
<td align="center">
<h1>
{% blocktrans with username=user.username %}
Hi {{ username }},
{% endblocktrans %}
</h1>
</td>
</tr>
<tr>
<td align="center">
<table border="0">
<tr>
<td align="center" style="max-width: 300px; padding: 20px 0; color: #212124;">
{% blocktrans %}
Email MFA code.
{% endblocktrans %}
</td>
</tr>
<tr>
<td align="center" class="btn btn-primary">
{{ token }}
</td>
</tr>
</table>
</td>
</tr>
{% endblock %}
{% block sub_content %}
<tr>
<td style="padding: 20px; font-size: 12px; color: #212124;" align="center">
{% blocktrans with expires=expires|timeuntil %}
If you did not request this code, please ignore this email. The code above is valid for {{ expires }}.
{% endblocktrans %}
</td>
</tr>
{% endblock %}

View File

@ -0,0 +1,13 @@
{% load i18n %}{% load humanize %}{% autoescape off %}{% blocktrans with username=user.username %}Hi {{ username }},{% endblocktrans %}
{% blocktrans %}
Email MFA code
{% endblocktrans %}
{{ token }}
{% blocktrans with expires=expires|timeuntil %}
If you did not request this code, please ignore this email. The code above is valid for {{ expires }}.
{% endblocktrans %}
--
Powered by goauthentik.io.
{% endautoescape %}

View File

@ -0,0 +1,340 @@
"""Test Email Authenticator API"""
from datetime import timedelta
from unittest.mock import MagicMock, PropertyMock, patch
from django.core import mail
from django.core.mail.backends.smtp import EmailBackend
from django.db.utils import IntegrityError
from django.template.exceptions import TemplateDoesNotExist
from django.urls import reverse
from django.utils.timezone import now
from authentik.core.tests.utils import create_test_admin_user, create_test_flow, create_test_user
from authentik.flows.models import FlowStageBinding
from authentik.flows.tests import FlowTestCase
from authentik.lib.config import CONFIG
from authentik.lib.utils.email import mask_email
from authentik.stages.authenticator_email.api import (
AuthenticatorEmailStageSerializer,
EmailDeviceSerializer,
)
from authentik.stages.authenticator_email.models import AuthenticatorEmailStage, EmailDevice
from authentik.stages.authenticator_email.stage import (
SESSION_KEY_EMAIL_DEVICE,
)
from authentik.stages.email.utils import TemplateEmailMessage
class TestAuthenticatorEmailStage(FlowTestCase):
"""Test Email Authenticator stage"""
def setUp(self):
super().setUp()
self.flow = create_test_flow()
self.user = create_test_admin_user()
self.user_noemail = create_test_user(email="")
self.stage = AuthenticatorEmailStage.objects.create(
name="email-authenticator",
use_global_settings=True,
from_address="test@authentik.local",
configure_flow=self.flow,
token_expiry="minutes=30",
) # nosec
self.binding = FlowStageBinding.objects.create(target=self.flow, stage=self.stage, order=0)
self.device = EmailDevice.objects.create(
user=self.user,
stage=self.stage,
email="test@authentik.local",
)
self.client.force_login(self.user)
def test_device_str(self):
"""Test string representation of device"""
self.assertEqual(str(self.device), f"Email Device for {self.user.pk}")
# Test unsaved device
unsaved_device = EmailDevice(
user=self.user,
stage=self.stage,
email="test@authentik.local",
)
self.assertEqual(str(unsaved_device), "New Email Device")
def test_stage_str(self):
"""Test string representation of stage"""
self.assertEqual(str(self.stage), f"Email Authenticator Stage {self.stage.name}")
def test_token_lifecycle(self):
"""Test token generation, validation and expiry"""
# Initially no token
self.assertIsNone(self.device.token)
# Generate token
self.device.generate_token()
token = self.device.token
self.assertIsNotNone(token)
self.assertIsNotNone(self.device.valid_until)
self.assertTrue(self.device.valid_until > now())
# Verify invalid token
self.assertFalse(self.device.verify_token("000000"))
# Verify correct token (should clear token after verification)
self.assertTrue(self.device.verify_token(token))
self.assertIsNone(self.device.token)
def test_stage_no_prefill(self):
"""Test stage without prefilled email"""
self.client.force_login(self.user_noemail)
with patch(
"authentik.stages.authenticator_email.models.AuthenticatorEmailStage.backend_class",
PropertyMock(return_value=EmailBackend),
):
response = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
)
self.assertStageResponse(
response,
self.flow,
self.user_noemail,
component="ak-stage-authenticator-email",
email_required=True,
)
def test_stage_submit(self):
"""Test stage email submission"""
# Initialize the flow
response = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
)
self.assertStageResponse(
response,
self.flow,
self.user,
component="ak-stage-authenticator-email",
email_required=False,
)
# Test email submission with locmem backend
def mock_send_mails(stage, *messages):
"""Mock send_mails to send directly"""
for message in messages:
message.send()
with (
patch(
"authentik.stages.authenticator_email.models.AuthenticatorEmailStage.backend_class",
return_value=EmailBackend,
),
patch(
"authentik.stages.authenticator_email.stage.send_mails",
side_effect=mock_send_mails,
),
):
response = self.client.post(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
data={"component": "ak-stage-authenticator-email", "email": "test@example.com"},
)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(mail.outbox), 1)
sent_mail = mail.outbox[0]
self.assertEqual(sent_mail.subject, self.stage.subject)
self.assertEqual(sent_mail.to, [f"{self.user} <test@example.com>"])
# Get from_address from global email config to test if global settings are being used
from_address_global = CONFIG.get("email.from")
self.assertEqual(sent_mail.from_email, from_address_global)
self.assertStageResponse(
response,
self.flow,
self.user,
component="ak-stage-authenticator-email",
response_errors={},
email_required=False,
)
def test_email_template(self):
"""Test email template rendering"""
self.device.generate_token()
message = self.device._compose_email()
self.assertIsInstance(message, TemplateEmailMessage)
self.assertEqual(message.subject, self.stage.subject)
self.assertEqual(message.to, [f"{self.user.name} <{self.device.email}>"])
self.assertTrue(self.device.token in message.body)
def test_duplicate_email(self):
"""Test attempting to use same email twice"""
email = "test2@authentik.local"
# First device
EmailDevice.objects.create(
user=self.user,
stage=self.stage,
email=email,
)
# Attempt to create second device with same email
with self.assertRaises(IntegrityError):
EmailDevice.objects.create(
user=self.user,
stage=self.stage,
email=email,
)
def test_token_expiry(self):
"""Test token expiration behavior"""
self.device.generate_token()
token = self.device.token
# Set token as expired
self.device.valid_until = now() - timedelta(minutes=1)
self.device.save()
# Verify expired token fails
self.assertFalse(self.device.verify_token(token))
def test_template_errors(self):
"""Test handling of template errors"""
self.stage.template = "{% invalid template %}"
with self.assertRaises(TemplateDoesNotExist):
self.stage.send(self.device)
def test_challenge_response_validation(self):
"""Test challenge response validation"""
# Initialize the flow
self.client.force_login(self.user_noemail)
with patch(
"authentik.stages.authenticator_email.models.AuthenticatorEmailStage.backend_class",
PropertyMock(return_value=EmailBackend),
):
response = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
)
# Test missing code and email
response = self.client.post(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
data={"component": "ak-stage-authenticator-email"},
)
self.assertIn("email required", str(response.content))
# Test invalid code
response = self.client.post(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
data={"component": "ak-stage-authenticator-email", "code": "000000"},
)
self.assertIn("Code does not match", str(response.content))
# Test valid code
self.client.force_login(self.user)
response = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
)
device = self.device
token = device.token
response = self.client.post(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
data={"component": "ak-stage-authenticator-email", "code": token},
)
self.assertEqual(response.status_code, 200)
self.assertTrue(device.confirmed)
def test_challenge_generation(self):
"""Test challenge generation"""
# Test with masked email
with patch(
"authentik.stages.authenticator_email.models.AuthenticatorEmailStage.backend_class",
PropertyMock(return_value=EmailBackend),
):
response = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
)
self.assertStageResponse(
response,
self.flow,
self.user,
component="ak-stage-authenticator-email",
email_required=False,
)
masked_email = mask_email(self.user.email)
self.assertEqual(masked_email, response.json()["email"])
# Test without email
self.client.force_login(self.user_noemail)
response = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
)
self.assertStageResponse(
response,
self.flow,
self.user_noemail,
component="ak-stage-authenticator-email",
email_required=True,
)
self.assertIsNone(response.json()["email"])
def test_session_management(self):
"""Test session device management"""
# Test device creation in session
with patch(
"authentik.stages.authenticator_email.models.AuthenticatorEmailStage.backend_class",
PropertyMock(return_value=EmailBackend),
):
# Delete any existing devices for this test
EmailDevice.objects.filter(user=self.user).delete()
response = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
)
self.assertIn(SESSION_KEY_EMAIL_DEVICE, self.client.session)
device = self.client.session[SESSION_KEY_EMAIL_DEVICE]
self.assertIsInstance(device, EmailDevice)
self.assertFalse(device.confirmed)
self.assertEqual(device.user, self.user)
# Test device confirmation and cleanup
device.confirmed = True
device.email = "new_test@authentik.local" # Use a different email
self.client.session[SESSION_KEY_EMAIL_DEVICE] = device
self.client.session.save()
response = self.client.post(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
data={"component": "ak-stage-authenticator-email", "code": device.token},
)
self.assertEqual(response.status_code, 200)
self.assertTrue(device.confirmed)
# Session key should be removed after device is saved
device.save()
self.assertNotIn(SESSION_KEY_EMAIL_DEVICE, self.client.session)
def test_model_properties_and_methods(self):
"""Test model properties"""
device = self.device
stage = self.stage
self.assertEqual(stage.serializer, AuthenticatorEmailStageSerializer)
self.assertIsInstance(stage.backend, EmailBackend)
self.assertEqual(device.serializer, EmailDeviceSerializer)
# Test AuthenticatorEmailStage send method
with patch(
"authentik.stages.authenticator_email.models.AuthenticatorEmailStage.backend_class",
return_value=EmailBackend,
):
self.device.generate_token()
# Test EmailDevice _compose_email method
message = self.device._compose_email()
self.assertIsInstance(message, TemplateEmailMessage)
self.assertEqual(message.subject, self.stage.subject)
self.assertEqual(message.to, [f"{self.user.name} <{self.device.email}>"])
self.assertTrue(self.device.token in message.body)
# Test AuthenticatorEmailStage send method
self.stage.send(device)
def test_email_tasks(self):
email_send_mock = MagicMock()
with patch(
"authentik.stages.email.tasks.send_mails",
email_send_mock,
):
# Test AuthenticatorEmailStage send method
self.stage.send(self.device)
email_send_mock.assert_called_once()

View File

@ -0,0 +1,17 @@
"""API URLs"""
from authentik.stages.authenticator_email.api import (
AuthenticatorEmailStageViewSet,
EmailAdminDeviceViewSet,
EmailDeviceViewSet,
)
api_urlpatterns = [
("authenticators/email", EmailDeviceViewSet),
(
"authenticators/admin/email",
EmailAdminDeviceViewSet,
"admin-emaildevice",
),
("stages/authenticator/email", AuthenticatorEmailStageViewSet),
]

View File

@ -26,10 +26,13 @@ from authentik.events.middleware import audit_ignore
from authentik.events.models import Event, EventAction from authentik.events.models import Event, EventAction
from authentik.flows.stage import StageView from authentik.flows.stage import StageView
from authentik.flows.views.executor import SESSION_KEY_APPLICATION_PRE from authentik.flows.views.executor import SESSION_KEY_APPLICATION_PRE
from authentik.lib.utils.email import mask_email
from authentik.lib.utils.time import timedelta_from_string
from authentik.root.middleware import ClientIPMiddleware from authentik.root.middleware import ClientIPMiddleware
from authentik.stages.authenticator import match_token from authentik.stages.authenticator import match_token
from authentik.stages.authenticator.models import Device from authentik.stages.authenticator.models import Device
from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice
from authentik.stages.authenticator_email.models import EmailDevice
from authentik.stages.authenticator_sms.models import SMSDevice from authentik.stages.authenticator_sms.models import SMSDevice
from authentik.stages.authenticator_validate.models import AuthenticatorValidateStage, DeviceClasses from authentik.stages.authenticator_validate.models import AuthenticatorValidateStage, DeviceClasses
from authentik.stages.authenticator_webauthn.models import UserVerification, WebAuthnDevice from authentik.stages.authenticator_webauthn.models import UserVerification, WebAuthnDevice
@ -54,6 +57,8 @@ def get_challenge_for_device(
"""Generate challenge for a single device""" """Generate challenge for a single device"""
if isinstance(device, WebAuthnDevice): if isinstance(device, WebAuthnDevice):
return get_webauthn_challenge(request, stage, device) return get_webauthn_challenge(request, stage, device)
if isinstance(device, EmailDevice):
return {"email": mask_email(device.email)}
# Code-based challenges have no hints # Code-based challenges have no hints
return {} return {}
@ -103,6 +108,8 @@ def select_challenge(request: HttpRequest, device: Device):
"""Callback when the user selected a challenge in the frontend.""" """Callback when the user selected a challenge in the frontend."""
if isinstance(device, SMSDevice): if isinstance(device, SMSDevice):
select_challenge_sms(request, device) select_challenge_sms(request, device)
elif isinstance(device, EmailDevice):
select_challenge_email(request, device)
def select_challenge_sms(request: HttpRequest, device: SMSDevice): def select_challenge_sms(request: HttpRequest, device: SMSDevice):
@ -111,6 +118,13 @@ def select_challenge_sms(request: HttpRequest, device: SMSDevice):
device.stage.send(device.token, device) device.stage.send(device.token, device)
def select_challenge_email(request: HttpRequest, device: EmailDevice):
"""Send Email"""
valid_secs: int = timedelta_from_string(device.stage.token_expiry).total_seconds()
device.generate_token(valid_secs=valid_secs)
device.stage.send(device)
def validate_challenge_code(code: str, stage_view: StageView, user: User) -> Device: def validate_challenge_code(code: str, stage_view: StageView, user: User) -> Device:
"""Validate code-based challenges. We test against every device, on purpose, as """Validate code-based challenges. We test against every device, on purpose, as
the user mustn't choose between totp and static devices.""" the user mustn't choose between totp and static devices."""

View File

@ -0,0 +1,37 @@
# Generated by Django 5.0.10 on 2025-01-16 02:48
import authentik.stages.authenticator_validate.models
import django.contrib.postgres.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
(
"authentik_stages_authenticator_validate",
"0013_authenticatorvalidatestage_webauthn_allowed_device_types",
),
]
operations = [
migrations.AlterField(
model_name="authenticatorvalidatestage",
name="device_classes",
field=django.contrib.postgres.fields.ArrayField(
base_field=models.TextField(
choices=[
("static", "Static"),
("totp", "TOTP"),
("webauthn", "WebAuthn"),
("duo", "Duo"),
("sms", "SMS"),
("email", "Email"),
]
),
default=authentik.stages.authenticator_validate.models.default_device_classes,
help_text="Device classes which can be used to authenticate",
size=None,
),
),
]

View File

@ -20,6 +20,7 @@ class DeviceClasses(models.TextChoices):
WEBAUTHN = "webauthn", _("WebAuthn") WEBAUTHN = "webauthn", _("WebAuthn")
DUO = "duo", _("Duo") DUO = "duo", _("Duo")
SMS = "sms", _("SMS") SMS = "sms", _("SMS")
EMAIL = "email", _("Email")
def default_device_classes() -> list: def default_device_classes() -> list:
@ -30,6 +31,7 @@ def default_device_classes() -> list:
DeviceClasses.WEBAUTHN, DeviceClasses.WEBAUTHN,
DeviceClasses.DUO, DeviceClasses.DUO,
DeviceClasses.SMS, DeviceClasses.SMS,
DeviceClasses.EMAIL,
] ]

View File

@ -23,6 +23,7 @@ from authentik.flows.stage import ChallengeStageView
from authentik.lib.utils.time import timedelta_from_string from authentik.lib.utils.time import timedelta_from_string
from authentik.stages.authenticator import devices_for_user from authentik.stages.authenticator import devices_for_user
from authentik.stages.authenticator.models import Device from authentik.stages.authenticator.models import Device
from authentik.stages.authenticator_email.models import EmailDevice
from authentik.stages.authenticator_sms.models import SMSDevice from authentik.stages.authenticator_sms.models import SMSDevice
from authentik.stages.authenticator_validate.challenge import ( from authentik.stages.authenticator_validate.challenge import (
DeviceChallenge, DeviceChallenge,
@ -84,7 +85,9 @@ class AuthenticatorValidationChallengeResponse(ChallengeResponse):
def validate_code(self, code: str) -> str: def validate_code(self, code: str) -> str:
"""Validate code-based response, raise error if code isn't allowed""" """Validate code-based response, raise error if code isn't allowed"""
self._challenge_allowed([DeviceClasses.TOTP, DeviceClasses.STATIC, DeviceClasses.SMS]) self._challenge_allowed(
[DeviceClasses.TOTP, DeviceClasses.STATIC, DeviceClasses.SMS, DeviceClasses.EMAIL]
)
self.device = validate_challenge_code(code, self.stage, self.stage.get_pending_user()) self.device = validate_challenge_code(code, self.stage, self.stage.get_pending_user())
return code return code
@ -117,12 +120,17 @@ class AuthenticatorValidationChallengeResponse(ChallengeResponse):
if not allowed: if not allowed:
raise ValidationError("invalid challenge selected") raise ValidationError("invalid challenge selected")
if challenge.get("device_class", "") != "sms": device_class = challenge.get("device_class", "")
return challenge if device_class == "sms":
devices = SMSDevice.objects.filter(pk=int(challenge.get("device_uid", "0"))) devices = SMSDevice.objects.filter(pk=int(challenge.get("device_uid", "0")))
if not devices.exists(): if not devices.exists():
raise ValidationError("invalid challenge selected") raise ValidationError("invalid challenge selected")
select_challenge(self.stage.request, devices.first()) select_challenge(self.stage.request, devices.first())
elif device_class == "email":
devices = EmailDevice.objects.filter(pk=int(challenge.get("device_uid", "0")))
if not devices.exists():
raise ValidationError("invalid challenge selected")
select_challenge(self.stage.request, devices.first())
return challenge return challenge
def validate_selected_stage(self, stage_pk: str) -> str: def validate_selected_stage(self, stage_pk: str) -> str:

View File

@ -0,0 +1,183 @@
"""Test validator stage for Email devices"""
from django.test.client import RequestFactory
from django.urls.base import reverse
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
from authentik.flows.models import FlowStageBinding, NotConfiguredAction
from authentik.flows.tests import FlowTestCase
from authentik.lib.generators import generate_id
from authentik.lib.utils.email import mask_email
from authentik.stages.authenticator_email.models import AuthenticatorEmailStage, EmailDevice
from authentik.stages.authenticator_validate.models import AuthenticatorValidateStage, DeviceClasses
from authentik.stages.identification.models import IdentificationStage, UserFields
class AuthenticatorValidateStageEmailTests(FlowTestCase):
"""Test validator stage for Email devices"""
def setUp(self) -> None:
self.user = create_test_admin_user()
self.request_factory = RequestFactory()
# Create email authenticator stage
self.stage = AuthenticatorEmailStage.objects.create(
name="email-authenticator",
use_global_settings=True,
from_address="test@authentik.local",
)
# Create identification stage
self.ident_stage = IdentificationStage.objects.create(
name=generate_id(),
user_fields=[UserFields.USERNAME],
)
# Create validation stage
self.validate_stage = AuthenticatorValidateStage.objects.create(
name=generate_id(),
device_classes=[DeviceClasses.EMAIL],
)
# Create flow with both stages
self.flow = create_test_flow()
FlowStageBinding.objects.create(target=self.flow, stage=self.ident_stage, order=0)
FlowStageBinding.objects.create(target=self.flow, stage=self.validate_stage, order=1)
def _identify_user(self):
"""Helper to identify user in flow"""
response = self.client.post(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
{"uid_field": self.user.username},
follow=True,
)
self.assertEqual(response.status_code, 200)
return response
def _send_challenge(self, device):
"""Helper to send challenge for device"""
response = self.client.post(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
{
"component": "ak-stage-authenticator-validate",
"selected_challenge": {
"device_class": "email",
"device_uid": str(device.pk),
"challenge": {},
"last_used": device.last_used.isoformat() if device.last_used else None,
},
},
)
self.assertEqual(response.status_code, 200)
return response
def test_happy_path(self):
"""Test validator stage with valid code"""
# Create a device for our user
device = EmailDevice.objects.create(
user=self.user,
confirmed=True,
stage=self.stage,
email="xx@0.co",
) # Short email for testing purposes
# First identify the user
self._identify_user()
# Send the challenge
response = self._send_challenge(device)
response_data = self.assertStageResponse(
response,
flow=self.flow,
component="ak-stage-authenticator-validate",
)
# Get the device challenge from the response and verify it matches
device_challenge = response_data["device_challenges"][0]
self.assertEqual(device_challenge["device_class"], "email")
self.assertEqual(device_challenge["device_uid"], str(device.pk))
self.assertEqual(device_challenge["challenge"], {"email": mask_email(device.email)})
# Generate a token for the device
device.generate_token()
# Submit the valid code
response = self.client.post(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
{"component": "ak-stage-authenticator-validate", "code": device.token},
)
# Should redirect to root since this is the last stage
self.assertStageRedirects(response, "/")
def test_no_device(self):
"""Test validator stage without configured device"""
configuration_stage = AuthenticatorEmailStage.objects.create(
name=generate_id(),
use_global_settings=True,
from_address="test@authentik.local",
)
stage = AuthenticatorValidateStage.objects.create(
name=generate_id(),
not_configured_action=NotConfiguredAction.CONFIGURE,
device_classes=[DeviceClasses.EMAIL],
)
stage.configuration_stages.set([configuration_stage])
flow = create_test_flow()
FlowStageBinding.objects.create(target=flow, stage=stage, order=2)
response = self.client.post(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
{"component": "ak-stage-authenticator-validate"},
)
self.assertEqual(response.status_code, 200)
response_data = self.assertStageResponse(
response,
flow=flow,
component="ak-stage-authenticator-validate",
)
self.assertEqual(response_data["configuration_stages"], [])
self.assertEqual(response_data["device_challenges"], [])
self.assertEqual(
response_data["response_errors"],
{"non_field_errors": [{"code": "invalid", "string": "Empty response"}]},
)
def test_invalid_code(self):
"""Test validator stage with invalid code"""
# Create a device for our user
device = EmailDevice.objects.create(
user=self.user,
confirmed=True,
stage=self.stage,
email="test@authentik.local",
)
# First identify the user
self._identify_user()
# Send the challenge
self._send_challenge(device)
# Generate a token for the device
device.generate_token()
# Try invalid code and verify error message
response = self.client.post(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
{"component": "ak-stage-authenticator-validate", "code": "invalid"},
)
response_data = self.assertStageResponse(
response,
flow=self.flow,
component="ak-stage-authenticator-validate",
)
self.assertEqual(
response_data["response_errors"],
{
"code": [
{
"code": "invalid",
"string": (
"Invalid Token. Please ensure the time on your device "
"is accurate and try again."
),
}
],
},
)

File diff suppressed because one or more lines are too long

View File

@ -13,17 +13,28 @@ from structlog.stdlib import get_logger
from authentik.events.models import Event, EventAction, TaskStatus from authentik.events.models import Event, EventAction, TaskStatus
from authentik.events.system_tasks import SystemTask from authentik.events.system_tasks import SystemTask
from authentik.root.celery import CELERY_APP from authentik.root.celery import CELERY_APP
from authentik.stages.authenticator_email.models import AuthenticatorEmailStage
from authentik.stages.email.models import EmailStage from authentik.stages.email.models import EmailStage
from authentik.stages.email.utils import logo_data from authentik.stages.email.utils import logo_data
LOGGER = get_logger() LOGGER = get_logger()
def send_mails(stage: EmailStage, *messages: list[EmailMultiAlternatives]): def send_mails(
"""Wrapper to convert EmailMessage to dict and send it from worker""" stage: EmailStage | AuthenticatorEmailStage, *messages: list[EmailMultiAlternatives]
):
"""Wrapper to convert EmailMessage to dict and send it from worker
Args:
stage: Either an EmailStage or AuthenticatorEmailStage instance
messages: List of email messages to send
Returns:
Celery group promise for the email sending tasks
"""
tasks = [] tasks = []
stage_class = stage.__class__
for message in messages: for message in messages:
tasks.append(send_mail.s(message.__dict__, str(stage.pk))) tasks.append(send_mail.s(message.__dict__, stage_class, str(stage.pk)))
lazy_group = group(*tasks) lazy_group = group(*tasks)
promise = lazy_group() promise = lazy_group()
return promise return promise
@ -47,23 +58,28 @@ def get_email_body(email: EmailMultiAlternatives) -> str:
retry_backoff=True, retry_backoff=True,
base=SystemTask, base=SystemTask,
) )
def send_mail(self: SystemTask, message: dict[Any, Any], email_stage_pk: str | None = None): def send_mail(
self: SystemTask,
message: dict[Any, Any],
stage_class: EmailStage | AuthenticatorEmailStage = EmailStage,
email_stage_pk: str | None = None,
):
"""Send Email for Email Stage. Retries are scheduled automatically.""" """Send Email for Email Stage. Retries are scheduled automatically."""
self.save_on_success = False self.save_on_success = False
message_id = make_msgid(domain=DNS_NAME) message_id = make_msgid(domain=DNS_NAME)
self.set_uid(slugify(message_id.replace(".", "_").replace("@", "_"))) self.set_uid(slugify(message_id.replace(".", "_").replace("@", "_")))
try: try:
if not email_stage_pk: if not email_stage_pk:
stage: EmailStage = EmailStage(use_global_settings=True) stage: EmailStage | AuthenticatorEmailStage = stage_class(use_global_settings=True)
else: else:
stages = EmailStage.objects.filter(pk=email_stage_pk) stages = stage_class.objects.filter(pk=email_stage_pk)
if not stages.exists(): if not stages.exists():
self.set_status( self.set_status(
TaskStatus.WARNING, TaskStatus.WARNING,
"Email stage does not exist anymore. Discarding message.", "Email stage does not exist anymore. Discarding message.",
) )
return return
stage: EmailStage = stages.first() stage: EmailStage | AuthenticatorEmailStage = stages.first()
try: try:
backend = stage.backend backend = stage.backend
except ValueError as exc: except ValueError as exc:

View File

@ -0,0 +1,30 @@
version: 1
metadata:
labels:
blueprints.goauthentik.io/instantiate: "false"
name: Example - Email MFA setup flow
entries:
- attrs:
designation: stage_configuration
name: Default Email Authenticator Flow
title: Setup Email Two-Factor Authentication
authentication: require_authenticated
identifiers:
slug: default-authenticator-email-setup
model: authentik_flows.flow
id: flow
- attrs:
configure_flow: !KeyOf flow
friendly_name: Email Authenticator
use_global_settings: true
token_expiry: minutes=30
subject: authentik Sign-in code
identifiers:
name: default-authenticator-email-setup
id: default-authenticator-email-setup
model: authentik_stages_authenticator_email.authenticatoremailstage
- identifiers:
order: 0
stage: !KeyOf default-authenticator-email-setup
target: !KeyOf flow
model: authentik_flows.flowstagebinding

View File

@ -1961,6 +1961,86 @@
} }
} }
}, },
{
"type": "object",
"required": [
"model",
"identifiers"
],
"properties": {
"model": {
"const": "authentik_stages_authenticator_email.authenticatoremailstage"
},
"id": {
"type": "string"
},
"state": {
"type": "string",
"enum": [
"absent",
"present",
"created",
"must_created"
],
"default": "present"
},
"conditions": {
"type": "array",
"items": {
"type": "boolean"
}
},
"permissions": {
"$ref": "#/$defs/model_authentik_stages_authenticator_email.authenticatoremailstage_permissions"
},
"attrs": {
"$ref": "#/$defs/model_authentik_stages_authenticator_email.authenticatoremailstage"
},
"identifiers": {
"$ref": "#/$defs/model_authentik_stages_authenticator_email.authenticatoremailstage"
}
}
},
{
"type": "object",
"required": [
"model",
"identifiers"
],
"properties": {
"model": {
"const": "authentik_stages_authenticator_email.emaildevice"
},
"id": {
"type": "string"
},
"state": {
"type": "string",
"enum": [
"absent",
"present",
"created",
"must_created"
],
"default": "present"
},
"conditions": {
"type": "array",
"items": {
"type": "boolean"
}
},
"permissions": {
"$ref": "#/$defs/model_authentik_stages_authenticator_email.emaildevice_permissions"
},
"attrs": {
"$ref": "#/$defs/model_authentik_stages_authenticator_email.emaildevice"
},
"identifiers": {
"$ref": "#/$defs/model_authentik_stages_authenticator_email.emaildevice"
}
}
},
{ {
"type": "object", "type": "object",
"required": [ "required": [
@ -4596,6 +4676,7 @@
"authentik.sources.scim", "authentik.sources.scim",
"authentik.stages.authenticator", "authentik.stages.authenticator",
"authentik.stages.authenticator_duo", "authentik.stages.authenticator_duo",
"authentik.stages.authenticator_email",
"authentik.stages.authenticator_sms", "authentik.stages.authenticator_sms",
"authentik.stages.authenticator_static", "authentik.stages.authenticator_static",
"authentik.stages.authenticator_totp", "authentik.stages.authenticator_totp",
@ -4686,6 +4767,8 @@
"authentik_sources_scim.scimsourcepropertymapping", "authentik_sources_scim.scimsourcepropertymapping",
"authentik_stages_authenticator_duo.authenticatorduostage", "authentik_stages_authenticator_duo.authenticatorduostage",
"authentik_stages_authenticator_duo.duodevice", "authentik_stages_authenticator_duo.duodevice",
"authentik_stages_authenticator_email.authenticatoremailstage",
"authentik_stages_authenticator_email.emaildevice",
"authentik_stages_authenticator_sms.authenticatorsmsstage", "authentik_stages_authenticator_sms.authenticatorsmsstage",
"authentik_stages_authenticator_sms.smsdevice", "authentik_stages_authenticator_sms.smsdevice",
"authentik_stages_authenticator_static.authenticatorstaticstage", "authentik_stages_authenticator_static.authenticatorstaticstage",
@ -6482,6 +6565,8 @@
"authentik_core.delete_token", "authentik_core.delete_token",
"authentik_core.delete_user", "authentik_core.delete_user",
"authentik_core.delete_usersourceconnection", "authentik_core.delete_usersourceconnection",
"authentik_core.disable_group_superuser",
"authentik_core.enable_group_superuser",
"authentik_core.impersonate", "authentik_core.impersonate",
"authentik_core.preview_user", "authentik_core.preview_user",
"authentik_core.remove_user_from_group", "authentik_core.remove_user_from_group",
@ -6847,6 +6932,14 @@
"authentik_stages_authenticator_duo.delete_duodevice", "authentik_stages_authenticator_duo.delete_duodevice",
"authentik_stages_authenticator_duo.view_authenticatorduostage", "authentik_stages_authenticator_duo.view_authenticatorduostage",
"authentik_stages_authenticator_duo.view_duodevice", "authentik_stages_authenticator_duo.view_duodevice",
"authentik_stages_authenticator_email.add_authenticatoremailstage",
"authentik_stages_authenticator_email.add_emaildevice",
"authentik_stages_authenticator_email.change_authenticatoremailstage",
"authentik_stages_authenticator_email.change_emaildevice",
"authentik_stages_authenticator_email.delete_authenticatoremailstage",
"authentik_stages_authenticator_email.delete_emaildevice",
"authentik_stages_authenticator_email.view_authenticatoremailstage",
"authentik_stages_authenticator_email.view_emaildevice",
"authentik_stages_authenticator_endpoint_gdtc.add_authenticatorendpointgdtcstage", "authentik_stages_authenticator_endpoint_gdtc.add_authenticatorendpointgdtcstage",
"authentik_stages_authenticator_endpoint_gdtc.add_endpointdevice", "authentik_stages_authenticator_endpoint_gdtc.add_endpointdevice",
"authentik_stages_authenticator_endpoint_gdtc.add_endpointdeviceconnection", "authentik_stages_authenticator_endpoint_gdtc.add_endpointdeviceconnection",
@ -8972,6 +9065,239 @@
} }
} }
}, },
"model_authentik_stages_authenticator_email.authenticatoremailstage": {
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"title": "Name"
},
"flow_set": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"title": "Name"
},
"slug": {
"type": "string",
"maxLength": 50,
"minLength": 1,
"pattern": "^[-a-zA-Z0-9_]+$",
"title": "Slug",
"description": "Visible in the URL."
},
"title": {
"type": "string",
"minLength": 1,
"title": "Title",
"description": "Shown as the Title in Flow pages."
},
"designation": {
"type": "string",
"enum": [
"authentication",
"authorization",
"invalidation",
"enrollment",
"unenrollment",
"recovery",
"stage_configuration"
],
"title": "Designation",
"description": "Decides what this Flow is used for. For example, the Authentication flow is redirect to when an un-authenticated user visits authentik."
},
"policy_engine_mode": {
"type": "string",
"enum": [
"all",
"any"
],
"title": "Policy engine mode"
},
"compatibility_mode": {
"type": "boolean",
"title": "Compatibility mode",
"description": "Enable compatibility mode, increases compatibility with password managers on mobile devices."
},
"layout": {
"type": "string",
"enum": [
"stacked",
"content_left",
"content_right",
"sidebar_left",
"sidebar_right"
],
"title": "Layout"
},
"denied_action": {
"type": "string",
"enum": [
"message_continue",
"message",
"continue"
],
"title": "Denied action",
"description": "Configure what should happen when a flow denies access to a user."
}
},
"required": [
"name",
"slug",
"title",
"designation"
]
},
"title": "Flow set"
},
"configure_flow": {
"type": "string",
"format": "uuid",
"title": "Configure flow",
"description": "Flow used by an authenticated user to configure this Stage. If empty, user will not be able to configure this stage."
},
"friendly_name": {
"type": [
"string",
"null"
],
"minLength": 1,
"title": "Friendly name"
},
"use_global_settings": {
"type": "boolean",
"title": "Use global settings",
"description": "When enabled, global Email connection settings will be used and connection settings below will be ignored."
},
"host": {
"type": "string",
"minLength": 1,
"title": "Host"
},
"port": {
"type": "integer",
"minimum": -2147483648,
"maximum": 2147483647,
"title": "Port"
},
"username": {
"type": "string",
"title": "Username"
},
"password": {
"type": "string",
"title": "Password"
},
"use_tls": {
"type": "boolean",
"title": "Use tls"
},
"use_ssl": {
"type": "boolean",
"title": "Use ssl"
},
"timeout": {
"type": "integer",
"minimum": -2147483648,
"maximum": 2147483647,
"title": "Timeout"
},
"from_address": {
"type": "string",
"format": "email",
"maxLength": 254,
"minLength": 1,
"title": "From address"
},
"subject": {
"type": "string",
"minLength": 1,
"title": "Subject"
},
"token_expiry": {
"type": "string",
"minLength": 1,
"title": "Token expiry",
"description": "Time the token sent is valid (Format: hours=3,minutes=17,seconds=300)."
},
"template": {
"type": "string",
"minLength": 1,
"title": "Template"
}
},
"required": []
},
"model_authentik_stages_authenticator_email.authenticatoremailstage_permissions": {
"type": "array",
"items": {
"type": "object",
"required": [
"permission"
],
"properties": {
"permission": {
"type": "string",
"enum": [
"add_authenticatoremailstage",
"change_authenticatoremailstage",
"delete_authenticatoremailstage",
"view_authenticatoremailstage"
]
},
"user": {
"type": "integer"
},
"role": {
"type": "string"
}
}
}
},
"model_authentik_stages_authenticator_email.emaildevice": {
"type": "object",
"properties": {
"name": {
"type": "string",
"maxLength": 64,
"minLength": 1,
"title": "Name",
"description": "The human-readable name of this device."
}
},
"required": []
},
"model_authentik_stages_authenticator_email.emaildevice_permissions": {
"type": "array",
"items": {
"type": "object",
"required": [
"permission"
],
"properties": {
"permission": {
"type": "string",
"enum": [
"add_emaildevice",
"change_emaildevice",
"delete_emaildevice",
"view_emaildevice"
]
},
"user": {
"type": "integer"
},
"role": {
"type": "string"
}
}
}
},
"model_authentik_stages_authenticator_sms.authenticatorsmsstage": { "model_authentik_stages_authenticator_sms.authenticatorsmsstage": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -9661,7 +9987,8 @@
"totp", "totp",
"webauthn", "webauthn",
"duo", "duo",
"sms" "sms",
"email"
], ],
"title": "Device classes" "title": "Device classes"
}, },
@ -12611,6 +12938,8 @@
"enum": [ "enum": [
"add_user_to_group", "add_user_to_group",
"remove_user_from_group", "remove_user_from_group",
"enable_group_superuser",
"disable_group_superuser",
"add_group", "add_group",
"change_group", "change_group",
"delete_group", "delete_group",
@ -12743,6 +13072,8 @@
"authentik_core.delete_token", "authentik_core.delete_token",
"authentik_core.delete_user", "authentik_core.delete_user",
"authentik_core.delete_usersourceconnection", "authentik_core.delete_usersourceconnection",
"authentik_core.disable_group_superuser",
"authentik_core.enable_group_superuser",
"authentik_core.impersonate", "authentik_core.impersonate",
"authentik_core.preview_user", "authentik_core.preview_user",
"authentik_core.remove_user_from_group", "authentik_core.remove_user_from_group",
@ -13108,6 +13439,14 @@
"authentik_stages_authenticator_duo.delete_duodevice", "authentik_stages_authenticator_duo.delete_duodevice",
"authentik_stages_authenticator_duo.view_authenticatorduostage", "authentik_stages_authenticator_duo.view_authenticatorduostage",
"authentik_stages_authenticator_duo.view_duodevice", "authentik_stages_authenticator_duo.view_duodevice",
"authentik_stages_authenticator_email.add_authenticatoremailstage",
"authentik_stages_authenticator_email.add_emaildevice",
"authentik_stages_authenticator_email.change_authenticatoremailstage",
"authentik_stages_authenticator_email.change_emaildevice",
"authentik_stages_authenticator_email.delete_authenticatoremailstage",
"authentik_stages_authenticator_email.delete_emaildevice",
"authentik_stages_authenticator_email.view_authenticatoremailstage",
"authentik_stages_authenticator_email.view_emaildevice",
"authentik_stages_authenticator_endpoint_gdtc.add_authenticatorendpointgdtcstage", "authentik_stages_authenticator_endpoint_gdtc.add_authenticatorendpointgdtcstage",
"authentik_stages_authenticator_endpoint_gdtc.add_endpointdevice", "authentik_stages_authenticator_endpoint_gdtc.add_endpointdevice",
"authentik_stages_authenticator_endpoint_gdtc.add_endpointdeviceconnection", "authentik_stages_authenticator_endpoint_gdtc.add_endpointdeviceconnection",

4
go.mod
View File

@ -26,7 +26,7 @@ require (
github.com/redis/go-redis/v9 v9.7.0 github.com/redis/go-redis/v9 v9.7.0
github.com/sethvargo/go-envconfig v1.1.1 github.com/sethvargo/go-envconfig v1.1.1
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.8.1 github.com/spf13/cobra v1.9.1
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.10.0
github.com/wwt/guac v1.3.2 github.com/wwt/guac v1.3.2
goauthentik.io/api/v3 v3.2024123.4 goauthentik.io/api/v3 v3.2024123.4
@ -71,7 +71,7 @@ require (
github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect github.com/prometheus/procfs v0.15.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.6 // indirect
go.mongodb.org/mongo-driver v1.14.0 // indirect go.mongodb.org/mongo-driver v1.14.0 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect go.opentelemetry.io/otel/metric v1.24.0 // indirect

10
go.sum
View File

@ -57,7 +57,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/coreos/go-oidc/v3 v3.12.0 h1:sJk+8G2qq94rDI6ehZ71Bol3oUHy63qNYmkiSjrc/Jo= github.com/coreos/go-oidc/v3 v3.12.0 h1:sJk+8G2qq94rDI6ehZ71Bol3oUHy63qNYmkiSjrc/Jo=
github.com/coreos/go-oidc/v3 v3.12.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0= github.com/coreos/go-oidc/v3 v3.12.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -259,10 +259,10 @@ github.com/sethvargo/go-envconfig v1.1.1/go.mod h1:JLd0KFWQYzyENqnEPWWZ49i4vzZo/
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -11,6 +11,7 @@
# Alex Kruidenberg <alexkruidenberg@hotmail.com>, 2024 # Alex Kruidenberg <alexkruidenberg@hotmail.com>, 2024
# Wilmer, 2024 # Wilmer, 2024
# Sjors Wortelboer, 2024 # Sjors Wortelboer, 2024
# Marc Schmitt, 2025
# #
#, fuzzy #, fuzzy
msgid "" msgid ""
@ -19,7 +20,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-02-14 14:49+0000\n" "POT-Creation-Date: 2025-02-14 14:49+0000\n"
"PO-Revision-Date: 2022-09-26 16:47+0000\n" "PO-Revision-Date: 2022-09-26 16:47+0000\n"
"Last-Translator: Sjors Wortelboer, 2024\n" "Last-Translator: Marc Schmitt, 2025\n"
"Language-Team: Dutch (https://app.transifex.com/authentik/teams/119923/nl/)\n" "Language-Team: Dutch (https://app.transifex.com/authentik/teams/119923/nl/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@ -801,7 +802,7 @@ msgstr "Taak is nog niet uitgevoerd."
#: authentik/flows/api/flows.py #: authentik/flows/api/flows.py
#, python-brace-format #, python-brace-format
msgid "Flow not applicable to current user/request: {messages}" msgid "Flow not applicable to current user/request: {messages}"
msgstr "Flow is niet toepasbaar op huidige gebruiker/verzoek: {message}" msgstr "Flow is niet toepasbaar op huidige gebruiker/verzoek: {messages}"
#: authentik/flows/api/flows_diagram.py #: authentik/flows/api/flows_diagram.py
#, python-brace-format #, python-brace-format
@ -3024,6 +3025,7 @@ msgid ""
msgstr "" msgstr ""
"\n" "\n"
" Als dat niet werkt, kopieer en plak de volgende link in uw browser: %(url)s\n" " Als dat niet werkt, kopieer en plak de volgende link in uw browser: %(url)s\n"
" "
#: authentik/stages/email/templates/email/account_confirmation.txt #: authentik/stages/email/templates/email/account_confirmation.txt
msgid "" msgid ""
@ -3111,6 +3113,7 @@ msgid ""
msgstr "" msgstr ""
"\n" "\n"
" Dit is een test-e-mail om u te informeren dat u authentik-e-mails succesvol heeft geconfigureerd.\n" " Dit is een test-e-mail om u te informeren dat u authentik-e-mails succesvol heeft geconfigureerd.\n"
" "
#: authentik/stages/email/templates/email/setup.txt #: authentik/stages/email/templates/email/setup.txt
msgid "" msgid ""

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -8,7 +8,7 @@
# 刘松, 2022 # 刘松, 2022
# Tianhao Chai <cth451@gmail.com>, 2024 # Tianhao Chai <cth451@gmail.com>, 2024
# Jens L. <jens@goauthentik.io>, 2024 # Jens L. <jens@goauthentik.io>, 2024
# deluxghost, 2024 # deluxghost, 2025
# #
#, fuzzy #, fuzzy
msgid "" msgid ""
@ -17,7 +17,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-02-14 14:49+0000\n" "POT-Creation-Date: 2025-02-14 14:49+0000\n"
"PO-Revision-Date: 2022-09-26 16:47+0000\n" "PO-Revision-Date: 2022-09-26 16:47+0000\n"
"Last-Translator: deluxghost, 2024\n" "Last-Translator: deluxghost, 2025\n"
"Language-Team: Chinese Simplified (https://app.transifex.com/authentik/teams/119923/zh-Hans/)\n" "Language-Team: Chinese Simplified (https://app.transifex.com/authentik/teams/119923/zh-Hans/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@ -568,39 +568,39 @@ msgstr "签名密钥"
#: authentik/enterprise/providers/ssf/models.py #: authentik/enterprise/providers/ssf/models.py
msgid "Key used to sign the SSF Events." msgid "Key used to sign the SSF Events."
msgstr "" msgstr "用于签名 SSF 时间的密钥。"
#: authentik/enterprise/providers/ssf/models.py #: authentik/enterprise/providers/ssf/models.py
msgid "Shared Signals Framework Provider" msgid "Shared Signals Framework Provider"
msgstr "" msgstr "Shared Signals Framework 提供程序"
#: authentik/enterprise/providers/ssf/models.py #: authentik/enterprise/providers/ssf/models.py
msgid "Shared Signals Framework Providers" msgid "Shared Signals Framework Providers"
msgstr "" msgstr "Shared Signals Framework 提供程序"
#: authentik/enterprise/providers/ssf/models.py #: authentik/enterprise/providers/ssf/models.py
msgid "Add stream to SSF provider" msgid "Add stream to SSF provider"
msgstr "" msgstr "向 SSF 提供程序添加流"
#: authentik/enterprise/providers/ssf/models.py #: authentik/enterprise/providers/ssf/models.py
msgid "SSF Stream" msgid "SSF Stream"
msgstr "" msgstr "SSF 流"
#: authentik/enterprise/providers/ssf/models.py #: authentik/enterprise/providers/ssf/models.py
msgid "SSF Streams" msgid "SSF Streams"
msgstr "" msgstr "SSF 流"
#: authentik/enterprise/providers/ssf/models.py #: authentik/enterprise/providers/ssf/models.py
msgid "SSF Stream Event" msgid "SSF Stream Event"
msgstr "" msgstr "SSF 流事件"
#: authentik/enterprise/providers/ssf/models.py #: authentik/enterprise/providers/ssf/models.py
msgid "SSF Stream Events" msgid "SSF Stream Events"
msgstr "" msgstr "SSF 流事件"
#: authentik/enterprise/providers/ssf/tasks.py #: authentik/enterprise/providers/ssf/tasks.py
msgid "Failed to send request" msgid "Failed to send request"
msgstr "" msgstr "发送请求失败"
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/models.py #: authentik/enterprise/stages/authenticator_endpoint_gdtc/models.py
msgid "Endpoint Authenticator Google Device Trust Connector Stage" msgid "Endpoint Authenticator Google Device Trust Connector Stage"
@ -878,7 +878,7 @@ msgstr "在流程规划过程中评估策略。"
#: authentik/flows/models.py #: authentik/flows/models.py
msgid "Evaluate policies when the Stage is presented to the user." msgid "Evaluate policies when the Stage is presented to the user."
msgstr "" msgstr "在阶段呈现给用户时评估策略。"
#: authentik/flows/models.py #: authentik/flows/models.py
msgid "" msgid ""

Binary file not shown.

View File

@ -7,7 +7,7 @@
# Chen Zhikai, 2022 # Chen Zhikai, 2022
# 刘松, 2022 # 刘松, 2022
# Jens L. <jens@goauthentik.io>, 2024 # Jens L. <jens@goauthentik.io>, 2024
# deluxghost, 2024 # deluxghost, 2025
# #
#, fuzzy #, fuzzy
msgid "" msgid ""
@ -16,7 +16,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-02-14 14:49+0000\n" "POT-Creation-Date: 2025-02-14 14:49+0000\n"
"PO-Revision-Date: 2022-09-26 16:47+0000\n" "PO-Revision-Date: 2022-09-26 16:47+0000\n"
"Last-Translator: deluxghost, 2024\n" "Last-Translator: deluxghost, 2025\n"
"Language-Team: Chinese (China) (https://app.transifex.com/authentik/teams/119923/zh_CN/)\n" "Language-Team: Chinese (China) (https://app.transifex.com/authentik/teams/119923/zh_CN/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@ -567,39 +567,39 @@ msgstr "签名密钥"
#: authentik/enterprise/providers/ssf/models.py #: authentik/enterprise/providers/ssf/models.py
msgid "Key used to sign the SSF Events." msgid "Key used to sign the SSF Events."
msgstr "" msgstr "用于签名 SSF 时间的密钥。"
#: authentik/enterprise/providers/ssf/models.py #: authentik/enterprise/providers/ssf/models.py
msgid "Shared Signals Framework Provider" msgid "Shared Signals Framework Provider"
msgstr "" msgstr "Shared Signals Framework 提供程序"
#: authentik/enterprise/providers/ssf/models.py #: authentik/enterprise/providers/ssf/models.py
msgid "Shared Signals Framework Providers" msgid "Shared Signals Framework Providers"
msgstr "" msgstr "Shared Signals Framework 提供程序"
#: authentik/enterprise/providers/ssf/models.py #: authentik/enterprise/providers/ssf/models.py
msgid "Add stream to SSF provider" msgid "Add stream to SSF provider"
msgstr "" msgstr "向 SSF 提供程序添加流"
#: authentik/enterprise/providers/ssf/models.py #: authentik/enterprise/providers/ssf/models.py
msgid "SSF Stream" msgid "SSF Stream"
msgstr "" msgstr "SSF 流"
#: authentik/enterprise/providers/ssf/models.py #: authentik/enterprise/providers/ssf/models.py
msgid "SSF Streams" msgid "SSF Streams"
msgstr "" msgstr "SSF 流"
#: authentik/enterprise/providers/ssf/models.py #: authentik/enterprise/providers/ssf/models.py
msgid "SSF Stream Event" msgid "SSF Stream Event"
msgstr "" msgstr "SSF 流事件"
#: authentik/enterprise/providers/ssf/models.py #: authentik/enterprise/providers/ssf/models.py
msgid "SSF Stream Events" msgid "SSF Stream Events"
msgstr "" msgstr "SSF 流事件"
#: authentik/enterprise/providers/ssf/tasks.py #: authentik/enterprise/providers/ssf/tasks.py
msgid "Failed to send request" msgid "Failed to send request"
msgstr "" msgstr "发送请求失败"
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/models.py #: authentik/enterprise/stages/authenticator_endpoint_gdtc/models.py
msgid "Endpoint Authenticator Google Device Trust Connector Stage" msgid "Endpoint Authenticator Google Device Trust Connector Stage"
@ -877,7 +877,7 @@ msgstr "在流程规划过程中评估策略。"
#: authentik/flows/models.py #: authentik/flows/models.py
msgid "Evaluate policies when the Stage is presented to the user." msgid "Evaluate policies when the Stage is presented to the user."
msgstr "" msgstr "在阶段呈现给用户时评估策略。"
#: authentik/flows/models.py #: authentik/flows/models.py
msgid "" msgid ""

Binary file not shown.

16
poetry.lock generated
View File

@ -1417,13 +1417,13 @@ files = [
[[package]] [[package]]
name = "django-filter" name = "django-filter"
version = "24.3" version = "25.1"
description = "Django-filter is a reusable Django application for allowing users to filter querysets dynamically." description = "Django-filter is a reusable Django application for allowing users to filter querysets dynamically."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.9"
files = [ files = [
{file = "django_filter-24.3-py3-none-any.whl", hash = "sha256:c4852822928ce17fb699bcfccd644b3574f1a2d80aeb2b4ff4f16b02dd49dc64"}, {file = "django_filter-25.1-py3-none-any.whl", hash = "sha256:4fa48677cf5857b9b1347fed23e355ea792464e0fe07244d1fdfb8a806215b80"},
{file = "django_filter-24.3.tar.gz", hash = "sha256:d8ccaf6732afd21ca0542f6733b11591030fa98669f8d15599b358e24a2cd9c3"}, {file = "django_filter-25.1.tar.gz", hash = "sha256:1ec9eef48fa8da1c0ac9b411744b16c3f4c31176c867886e4c48da369c407153"},
] ]
[package.dependencies] [package.dependencies]
@ -1520,13 +1520,13 @@ hiredis = ["redis[hiredis] (>=3,!=4.0.0,!=4.0.1)"]
[[package]] [[package]]
name = "django-storages" name = "django-storages"
version = "1.14.4" version = "1.14.5"
description = "Support for many storage backends in Django" description = "Support for many storage backends in Django"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "django-storages-1.14.4.tar.gz", hash = "sha256:69aca94d26e6714d14ad63f33d13619e697508ee33ede184e462ed766dc2a73f"}, {file = "django_storages-1.14.5-py3-none-any.whl", hash = "sha256:5ce9c69426f24f379821fd688442314e4aa03de87ae43183c4e16915f4c165d4"},
{file = "django_storages-1.14.4-py3-none-any.whl", hash = "sha256:d61930acb4a25e3aebebc6addaf946a3b1df31c803a6bf1af2f31c9047febaa3"}, {file = "django_storages-1.14.5.tar.gz", hash = "sha256:ace80dbee311258453e30cd5cfd91096b834180ccf09bc1f4d2cb6d38d68571a"},
] ]
[package.dependencies] [package.dependencies]
@ -1537,7 +1537,7 @@ Django = ">=3.2"
azure = ["azure-core (>=1.13)", "azure-storage-blob (>=12)"] azure = ["azure-core (>=1.13)", "azure-storage-blob (>=12)"]
boto3 = ["boto3 (>=1.4.4)"] boto3 = ["boto3 (>=1.4.4)"]
dropbox = ["dropbox (>=7.2.1)"] dropbox = ["dropbox (>=7.2.1)"]
google = ["google-cloud-storage (>=1.27)"] google = ["google-cloud-storage (>=1.32)"]
libcloud = ["apache-libcloud"] libcloud = ["apache-libcloud"]
s3 = ["boto3 (>=1.4.4)"] s3 = ["boto3 (>=1.4.4)"]
sftp = ["paramiko (>=1.15)"] sftp = ["paramiko (>=1.15)"]

1113
schema.yml

File diff suppressed because it is too large Load Diff

350
web/package-lock.json generated
View File

@ -23,7 +23,7 @@
"@floating-ui/dom": "^1.6.11", "@floating-ui/dom": "^1.6.11",
"@formatjs/intl-listformat": "^7.5.7", "@formatjs/intl-listformat": "^7.5.7",
"@fortawesome/fontawesome-free": "^6.6.0", "@fortawesome/fontawesome-free": "^6.6.0",
"@goauthentik/api": "^2024.12.3-1739449824", "@goauthentik/api": "^2024.12.3-1739801838",
"@lit-labs/ssr": "^3.2.2", "@lit-labs/ssr": "^3.2.2",
"@lit/context": "^1.1.2", "@lit/context": "^1.1.2",
"@lit/localize": "^0.12.2", "@lit/localize": "^0.12.2",
@ -42,12 +42,12 @@
"construct-style-sheets-polyfill": "^3.1.0", "construct-style-sheets-polyfill": "^3.1.0",
"core-js": "^3.38.1", "core-js": "^3.38.1",
"country-flag-icons": "^1.5.13", "country-flag-icons": "^1.5.13",
"dompurify": "^3.1.7", "dompurify": "^3.2.4",
"fuse.js": "^7.0.0", "fuse.js": "^7.0.0",
"guacamole-common-js": "^1.5.0", "guacamole-common-js": "^1.5.0",
"lit": "^3.2.0", "lit": "^3.2.0",
"md-front-matter": "^1.0.4", "md-front-matter": "^1.0.4",
"mermaid": "^11.2.1", "mermaid": "^11.4.1",
"rapidoc": "^9.3.7", "rapidoc": "^9.3.7",
"showdown": "^2.1.0", "showdown": "^2.1.0",
"style-mod": "^4.1.2", "style-mod": "^4.1.2",
@ -1814,9 +1814,9 @@
} }
}, },
"node_modules/@goauthentik/api": { "node_modules/@goauthentik/api": {
"version": "2024.12.3-1739449824", "version": "2024.12.3-1739801838",
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2024.12.3-1739449824.tgz", "resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2024.12.3-1739801838.tgz",
"integrity": "sha512-0M2SkvqpdjYgWOtaRLO41gTTyo43WPXlWbcfqCxfCJUoi1c3VGT5mozFCgRM21mY6+a3tKPHh4O28qDuz5gthw==" "integrity": "sha512-gK+chjueX2MbHyjeYczx+plapSRmzaTkZDH6PFwulcvxIFb3awfMkw4cWDjwHZunfDgu8EoP50cPlF3AA/PNoQ=="
}, },
"node_modules/@goauthentik/web": { "node_modules/@goauthentik/web": {
"resolved": "", "resolved": "",
@ -5728,6 +5728,259 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/d3": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz",
"integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==",
"license": "MIT",
"dependencies": {
"@types/d3-array": "*",
"@types/d3-axis": "*",
"@types/d3-brush": "*",
"@types/d3-chord": "*",
"@types/d3-color": "*",
"@types/d3-contour": "*",
"@types/d3-delaunay": "*",
"@types/d3-dispatch": "*",
"@types/d3-drag": "*",
"@types/d3-dsv": "*",
"@types/d3-ease": "*",
"@types/d3-fetch": "*",
"@types/d3-force": "*",
"@types/d3-format": "*",
"@types/d3-geo": "*",
"@types/d3-hierarchy": "*",
"@types/d3-interpolate": "*",
"@types/d3-path": "*",
"@types/d3-polygon": "*",
"@types/d3-quadtree": "*",
"@types/d3-random": "*",
"@types/d3-scale": "*",
"@types/d3-scale-chromatic": "*",
"@types/d3-selection": "*",
"@types/d3-shape": "*",
"@types/d3-time": "*",
"@types/d3-time-format": "*",
"@types/d3-timer": "*",
"@types/d3-transition": "*",
"@types/d3-zoom": "*"
}
},
"node_modules/@types/d3-array": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
"integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==",
"license": "MIT"
},
"node_modules/@types/d3-axis": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz",
"integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==",
"license": "MIT",
"dependencies": {
"@types/d3-selection": "*"
}
},
"node_modules/@types/d3-brush": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz",
"integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==",
"license": "MIT",
"dependencies": {
"@types/d3-selection": "*"
}
},
"node_modules/@types/d3-chord": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz",
"integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==",
"license": "MIT"
},
"node_modules/@types/d3-color": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
"integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
"license": "MIT"
},
"node_modules/@types/d3-contour": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz",
"integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==",
"license": "MIT",
"dependencies": {
"@types/d3-array": "*",
"@types/geojson": "*"
}
},
"node_modules/@types/d3-delaunay": {
"version": "6.0.4",
"resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
"integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==",
"license": "MIT"
},
"node_modules/@types/d3-dispatch": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz",
"integrity": "sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==",
"license": "MIT"
},
"node_modules/@types/d3-drag": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz",
"integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==",
"license": "MIT",
"dependencies": {
"@types/d3-selection": "*"
}
},
"node_modules/@types/d3-dsv": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz",
"integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==",
"license": "MIT"
},
"node_modules/@types/d3-ease": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
"integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==",
"license": "MIT"
},
"node_modules/@types/d3-fetch": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz",
"integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==",
"license": "MIT",
"dependencies": {
"@types/d3-dsv": "*"
}
},
"node_modules/@types/d3-force": {
"version": "3.0.10",
"resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz",
"integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==",
"license": "MIT"
},
"node_modules/@types/d3-format": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz",
"integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==",
"license": "MIT"
},
"node_modules/@types/d3-geo": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz",
"integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==",
"license": "MIT",
"dependencies": {
"@types/geojson": "*"
}
},
"node_modules/@types/d3-hierarchy": {
"version": "3.1.7",
"resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz",
"integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==",
"license": "MIT"
},
"node_modules/@types/d3-interpolate": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
"integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
"license": "MIT",
"dependencies": {
"@types/d3-color": "*"
}
},
"node_modules/@types/d3-path": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
"integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==",
"license": "MIT"
},
"node_modules/@types/d3-polygon": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz",
"integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==",
"license": "MIT"
},
"node_modules/@types/d3-quadtree": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz",
"integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==",
"license": "MIT"
},
"node_modules/@types/d3-random": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz",
"integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==",
"license": "MIT"
},
"node_modules/@types/d3-scale": {
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
"integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
"license": "MIT",
"dependencies": {
"@types/d3-time": "*"
}
},
"node_modules/@types/d3-scale-chromatic": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
"integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==",
"license": "MIT"
},
"node_modules/@types/d3-selection": {
"version": "3.0.11",
"resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz",
"integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==",
"license": "MIT"
},
"node_modules/@types/d3-shape": {
"version": "3.1.7",
"resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz",
"integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==",
"license": "MIT",
"dependencies": {
"@types/d3-path": "*"
}
},
"node_modules/@types/d3-time": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
"integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==",
"license": "MIT"
},
"node_modules/@types/d3-time-format": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz",
"integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==",
"license": "MIT"
},
"node_modules/@types/d3-timer": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
"integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",
"license": "MIT"
},
"node_modules/@types/d3-transition": {
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz",
"integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==",
"license": "MIT",
"dependencies": {
"@types/d3-selection": "*"
}
},
"node_modules/@types/d3-zoom": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz",
"integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==",
"license": "MIT",
"dependencies": {
"@types/d3-interpolate": "*",
"@types/d3-selection": "*"
}
},
"node_modules/@types/dompurify": { "node_modules/@types/dompurify": {
"version": "3.0.5", "version": "3.0.5",
"resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz", "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz",
@ -5802,6 +6055,12 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/geojson": {
"version": "7946.0.16",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
"integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
"license": "MIT"
},
"node_modules/@types/glob": { "node_modules/@types/glob": {
"version": "7.2.0", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
@ -9982,6 +10241,7 @@
"version": "7.9.0", "version": "7.9.0",
"resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz",
"integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==",
"license": "ISC",
"dependencies": { "dependencies": {
"d3-array": "3", "d3-array": "3",
"d3-axis": "3", "d3-axis": "3",
@ -10022,6 +10282,7 @@
"version": "3.2.4", "version": "3.2.4",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
"license": "ISC",
"dependencies": { "dependencies": {
"internmap": "1 - 2" "internmap": "1 - 2"
}, },
@ -10033,6 +10294,7 @@
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz",
"integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==",
"license": "ISC",
"engines": { "engines": {
"node": ">=12" "node": ">=12"
} }
@ -10041,6 +10303,7 @@
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz",
"integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
"license": "ISC",
"dependencies": { "dependencies": {
"d3-dispatch": "1 - 3", "d3-dispatch": "1 - 3",
"d3-drag": "2 - 3", "d3-drag": "2 - 3",
@ -10056,6 +10319,7 @@
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz",
"integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
"license": "ISC",
"dependencies": { "dependencies": {
"d3-path": "1 - 3" "d3-path": "1 - 3"
}, },
@ -10067,6 +10331,7 @@
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
"license": "ISC",
"engines": { "engines": {
"node": ">=12" "node": ">=12"
} }
@ -10075,6 +10340,7 @@
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz",
"integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==",
"license": "ISC",
"dependencies": { "dependencies": {
"d3-array": "^3.2.0" "d3-array": "^3.2.0"
}, },
@ -10086,6 +10352,7 @@
"version": "6.0.4", "version": "6.0.4",
"resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
"integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
"license": "ISC",
"dependencies": { "dependencies": {
"delaunator": "5" "delaunator": "5"
}, },
@ -10097,6 +10364,7 @@
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
"integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
"license": "ISC",
"engines": { "engines": {
"node": ">=12" "node": ">=12"
} }
@ -10105,6 +10373,7 @@
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
"integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
"license": "ISC",
"dependencies": { "dependencies": {
"d3-dispatch": "1 - 3", "d3-dispatch": "1 - 3",
"d3-selection": "3" "d3-selection": "3"
@ -10117,6 +10386,7 @@
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
"integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
"license": "ISC",
"dependencies": { "dependencies": {
"commander": "7", "commander": "7",
"iconv-lite": "0.6", "iconv-lite": "0.6",
@ -10141,6 +10411,7 @@
"version": "7.2.0", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
"license": "MIT",
"engines": { "engines": {
"node": ">= 10" "node": ">= 10"
} }
@ -10149,6 +10420,7 @@
"version": "0.6.3", "version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"license": "MIT",
"dependencies": { "dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0" "safer-buffer": ">= 2.1.2 < 3.0.0"
}, },
@ -10160,6 +10432,7 @@
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
"license": "BSD-3-Clause",
"engines": { "engines": {
"node": ">=12" "node": ">=12"
} }
@ -10168,6 +10441,7 @@
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz",
"integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
"license": "ISC",
"dependencies": { "dependencies": {
"d3-dsv": "1 - 3" "d3-dsv": "1 - 3"
}, },
@ -10179,6 +10453,7 @@
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
"integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
"license": "ISC",
"dependencies": { "dependencies": {
"d3-dispatch": "1 - 3", "d3-dispatch": "1 - 3",
"d3-quadtree": "1 - 3", "d3-quadtree": "1 - 3",
@ -10192,6 +10467,7 @@
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
"integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
"license": "ISC",
"engines": { "engines": {
"node": ">=12" "node": ">=12"
} }
@ -10200,6 +10476,7 @@
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz",
"integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==",
"license": "ISC",
"dependencies": { "dependencies": {
"d3-array": "2.5.0 - 3" "d3-array": "2.5.0 - 3"
}, },
@ -10211,6 +10488,7 @@
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
"integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==",
"license": "ISC",
"engines": { "engines": {
"node": ">=12" "node": ">=12"
} }
@ -10219,6 +10497,7 @@
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
"license": "ISC",
"dependencies": { "dependencies": {
"d3-color": "1 - 3" "d3-color": "1 - 3"
}, },
@ -10230,6 +10509,7 @@
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
"integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
"license": "ISC",
"engines": { "engines": {
"node": ">=12" "node": ">=12"
} }
@ -10238,6 +10518,7 @@
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz",
"integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==",
"license": "ISC",
"engines": { "engines": {
"node": ">=12" "node": ">=12"
} }
@ -10246,6 +10527,7 @@
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
"integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
"license": "ISC",
"engines": { "engines": {
"node": ">=12" "node": ">=12"
} }
@ -10254,6 +10536,7 @@
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",
"integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==",
"license": "ISC",
"engines": { "engines": {
"node": ">=12" "node": ">=12"
} }
@ -10297,6 +10580,7 @@
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
"license": "ISC",
"dependencies": { "dependencies": {
"d3-array": "2.10.0 - 3", "d3-array": "2.10.0 - 3",
"d3-format": "1 - 3", "d3-format": "1 - 3",
@ -10312,6 +10596,7 @@
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
"integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==",
"license": "ISC",
"dependencies": { "dependencies": {
"d3-color": "1 - 3", "d3-color": "1 - 3",
"d3-interpolate": "1 - 3" "d3-interpolate": "1 - 3"
@ -10324,6 +10609,7 @@
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
"license": "ISC",
"engines": { "engines": {
"node": ">=12" "node": ">=12"
} }
@ -10332,6 +10618,7 @@
"version": "3.2.0", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
"integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
"license": "ISC",
"dependencies": { "dependencies": {
"d3-path": "^3.1.0" "d3-path": "^3.1.0"
}, },
@ -10343,6 +10630,7 @@
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
"integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
"license": "ISC",
"dependencies": { "dependencies": {
"d3-array": "2 - 3" "d3-array": "2 - 3"
}, },
@ -10354,6 +10642,7 @@
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
"license": "ISC",
"dependencies": { "dependencies": {
"d3-time": "1 - 3" "d3-time": "1 - 3"
}, },
@ -10365,6 +10654,7 @@
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
"license": "ISC",
"engines": { "engines": {
"node": ">=12" "node": ">=12"
} }
@ -10373,6 +10663,7 @@
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
"integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
"license": "ISC",
"dependencies": { "dependencies": {
"d3-color": "1 - 3", "d3-color": "1 - 3",
"d3-dispatch": "1 - 3", "d3-dispatch": "1 - 3",
@ -10391,6 +10682,7 @@
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
"integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
"license": "ISC",
"dependencies": { "dependencies": {
"d3-dispatch": "1 - 3", "d3-dispatch": "1 - 3",
"d3-drag": "2 - 3", "d3-drag": "2 - 3",
@ -10403,11 +10695,12 @@
} }
}, },
"node_modules/dagre-d3-es": { "node_modules/dagre-d3-es": {
"version": "7.0.10", "version": "7.0.11",
"resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.10.tgz", "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.11.tgz",
"integrity": "sha512-qTCQmEhcynucuaZgY5/+ti3X/rnszKZhEQH/ZdWdtP1tA/y3VoHJzcVrO9pjjJCNpigfscAtoUB5ONcd2wNn0A==", "integrity": "sha512-tvlJLyQf834SylNKax8Wkzco/1ias1OPw8DcUMDE7oUIoSEW25riQVuiu/0OWEFqT0cxHT3Pa9/D82Jr47IONw==",
"license": "MIT",
"dependencies": { "dependencies": {
"d3": "^7.8.2", "d3": "^7.9.0",
"lodash-es": "^4.17.21" "lodash-es": "^4.17.21"
} }
}, },
@ -10646,6 +10939,7 @@
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz",
"integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==",
"license": "ISC",
"dependencies": { "dependencies": {
"robust-predicates": "^3.0.2" "robust-predicates": "^3.0.2"
} }
@ -10797,10 +11091,13 @@
} }
}, },
"node_modules/dompurify": { "node_modules/dompurify": {
"version": "3.1.7", "version": "3.2.4",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.7.tgz", "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.4.tgz",
"integrity": "sha512-VaTstWtsneJY8xzy7DekmYWEOZcmzIe3Qb3zPd4STve1OBTa+e+WmS1ITQec1fZYXI3HCsOZZiSMpG6oxoWMWQ==", "integrity": "sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg==",
"license": "(MPL-2.0 OR Apache-2.0)" "license": "(MPL-2.0 OR Apache-2.0)",
"optionalDependencies": {
"@types/trusted-types": "^2.0.7"
}
}, },
"node_modules/domutils": { "node_modules/domutils": {
"version": "3.1.0", "version": "3.1.0",
@ -13658,6 +13955,7 @@
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
"license": "ISC",
"engines": { "engines": {
"node": ">=12" "node": ">=12"
} }
@ -15782,21 +16080,23 @@
} }
}, },
"node_modules/mermaid": { "node_modules/mermaid": {
"version": "11.3.0", "version": "11.4.1",
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.3.0.tgz", "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.4.1.tgz",
"integrity": "sha512-fFmf2gRXLtlGzug4wpIGN+rQdZ30M8IZEB1D3eZkXNqC7puhqeURBcD/9tbwXsqBO+A6Nzzo3MSSepmnw5xSeg==", "integrity": "sha512-Mb01JT/x6CKDWaxigwfZYuYmDZ6xtrNwNlidKZwkSrDaY9n90tdrJTV5Umk+wP1fZscGptmKFXHsXMDEVZ+Q6A==",
"license": "MIT",
"dependencies": { "dependencies": {
"@braintree/sanitize-url": "^7.0.1", "@braintree/sanitize-url": "^7.0.1",
"@iconify/utils": "^2.1.32", "@iconify/utils": "^2.1.32",
"@mermaid-js/parser": "^0.3.0", "@mermaid-js/parser": "^0.3.0",
"@types/d3": "^7.4.3",
"cytoscape": "^3.29.2", "cytoscape": "^3.29.2",
"cytoscape-cose-bilkent": "^4.1.0", "cytoscape-cose-bilkent": "^4.1.0",
"cytoscape-fcose": "^2.2.0", "cytoscape-fcose": "^2.2.0",
"d3": "^7.9.0", "d3": "^7.9.0",
"d3-sankey": "^0.12.3", "d3-sankey": "^0.12.3",
"dagre-d3-es": "7.0.10", "dagre-d3-es": "7.0.11",
"dayjs": "^1.11.10", "dayjs": "^1.11.10",
"dompurify": "^3.0.11 <3.1.7", "dompurify": "^3.2.1",
"katex": "^0.16.9", "katex": "^0.16.9",
"khroma": "^2.1.0", "khroma": "^2.1.0",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
@ -15807,12 +16107,6 @@
"uuid": "^9.0.1" "uuid": "^9.0.1"
} }
}, },
"node_modules/mermaid/node_modules/dompurify": {
"version": "3.1.6",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.6.tgz",
"integrity": "sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ==",
"license": "(MPL-2.0 OR Apache-2.0)"
},
"node_modules/methods": { "node_modules/methods": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
@ -18984,7 +19278,8 @@
"node_modules/robust-predicates": { "node_modules/robust-predicates": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz",
"integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==",
"license": "Unlicense"
}, },
"node_modules/rollup": { "node_modules/rollup": {
"version": "4.24.0", "version": "4.24.0",
@ -19254,7 +19549,8 @@
"node_modules/rw": { "node_modules/rw": {
"version": "1.3.3", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
"integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==",
"license": "BSD-3-Clause"
}, },
"node_modules/rxjs": { "node_modules/rxjs": {
"version": "7.8.1", "version": "7.8.1",

View File

@ -11,7 +11,7 @@
"@floating-ui/dom": "^1.6.11", "@floating-ui/dom": "^1.6.11",
"@formatjs/intl-listformat": "^7.5.7", "@formatjs/intl-listformat": "^7.5.7",
"@fortawesome/fontawesome-free": "^6.6.0", "@fortawesome/fontawesome-free": "^6.6.0",
"@goauthentik/api": "^2024.12.3-1739449824", "@goauthentik/api": "^2024.12.3-1739801838",
"@lit-labs/ssr": "^3.2.2", "@lit-labs/ssr": "^3.2.2",
"@lit/context": "^1.1.2", "@lit/context": "^1.1.2",
"@lit/localize": "^0.12.2", "@lit/localize": "^0.12.2",
@ -30,12 +30,12 @@
"construct-style-sheets-polyfill": "^3.1.0", "construct-style-sheets-polyfill": "^3.1.0",
"core-js": "^3.38.1", "core-js": "^3.38.1",
"country-flag-icons": "^1.5.13", "country-flag-icons": "^1.5.13",
"dompurify": "^3.1.7", "dompurify": "^3.2.4",
"fuse.js": "^7.0.0", "fuse.js": "^7.0.0",
"guacamole-common-js": "^1.5.0", "guacamole-common-js": "^1.5.0",
"lit": "^3.2.0", "lit": "^3.2.0",
"md-front-matter": "^1.0.4", "md-front-matter": "^1.0.4",
"mermaid": "^11.2.1", "mermaid": "^11.4.1",
"rapidoc": "^9.3.7", "rapidoc": "^9.3.7",
"showdown": "^2.1.0", "showdown": "^2.1.0",
"style-mod": "^4.1.2", "style-mod": "^4.1.2",

View File

@ -2,6 +2,7 @@ import "@goauthentik/admin/rbac/ObjectPermissionModal";
import "@goauthentik/admin/stages/StageWizard"; import "@goauthentik/admin/stages/StageWizard";
import "@goauthentik/admin/stages/authenticator_duo/AuthenticatorDuoStageForm"; import "@goauthentik/admin/stages/authenticator_duo/AuthenticatorDuoStageForm";
import "@goauthentik/admin/stages/authenticator_duo/DuoDeviceImportForm"; import "@goauthentik/admin/stages/authenticator_duo/DuoDeviceImportForm";
import "@goauthentik/admin/stages/authenticator_email/AuthenticatorEmailStageForm";
import "@goauthentik/admin/stages/authenticator_endpoint_gdtc/AuthenticatorEndpointGDTCStageForm"; import "@goauthentik/admin/stages/authenticator_endpoint_gdtc/AuthenticatorEndpointGDTCStageForm";
import "@goauthentik/admin/stages/authenticator_sms/AuthenticatorSMSStageForm"; import "@goauthentik/admin/stages/authenticator_sms/AuthenticatorSMSStageForm";
import "@goauthentik/admin/stages/authenticator_static/AuthenticatorStaticStageForm"; import "@goauthentik/admin/stages/authenticator_static/AuthenticatorStaticStageForm";

View File

@ -1,6 +1,7 @@
import "@goauthentik/admin/common/ak-license-notice"; import "@goauthentik/admin/common/ak-license-notice";
import { StageBindingForm } from "@goauthentik/admin/flows/StageBindingForm"; import { StageBindingForm } from "@goauthentik/admin/flows/StageBindingForm";
import "@goauthentik/admin/stages/authenticator_duo/AuthenticatorDuoStageForm"; import "@goauthentik/admin/stages/authenticator_duo/AuthenticatorDuoStageForm";
import "@goauthentik/admin/stages/authenticator_email/AuthenticatorEmailStageForm";
import "@goauthentik/admin/stages/authenticator_sms/AuthenticatorSMSStageForm"; import "@goauthentik/admin/stages/authenticator_sms/AuthenticatorSMSStageForm";
import "@goauthentik/admin/stages/authenticator_static/AuthenticatorStaticStageForm"; import "@goauthentik/admin/stages/authenticator_static/AuthenticatorStaticStageForm";
import "@goauthentik/admin/stages/authenticator_totp/AuthenticatorTOTPStageForm"; import "@goauthentik/admin/stages/authenticator_totp/AuthenticatorTOTPStageForm";

View File

@ -0,0 +1,283 @@
import { RenderFlowOption } from "@goauthentik/admin/flows/utils";
import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
import "@goauthentik/elements/forms/Radio";
import "@goauthentik/elements/forms/SearchSelect";
import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit";
import { customElement, property } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import {
AuthenticatorEmailStage,
Flow,
FlowsApi,
FlowsInstancesListDesignationEnum,
FlowsInstancesListRequest,
StagesApi,
} from "@goauthentik/api";
@customElement("ak-stage-authenticator-email-form")
export class AuthenticatorEmailStageForm extends BaseStageForm<AuthenticatorEmailStage> {
async loadInstance(pk: string): Promise<AuthenticatorEmailStage> {
const stage = await new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorEmailRetrieve({
stageUuid: pk,
});
this.showConnectionSettings = !stage.useGlobalSettings;
return stage;
}
@property({ type: Boolean })
showConnectionSettings = false;
async send(data: AuthenticatorEmailStage): Promise<AuthenticatorEmailStage> {
if (this.instance) {
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorEmailUpdate({
stageUuid: this.instance.pk || "",
authenticatorEmailStageRequest: data,
});
} else {
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorEmailCreate({
authenticatorEmailStageRequest: data,
});
}
}
renderConnectionSettings(): TemplateResult {
if (!this.showConnectionSettings) {
return html``;
}
return html`<ak-form-group .expanded=${true}>
<span slot="header"> ${msg("Connection settings")} </span>
<div slot="body" class="pf-c-form">
<ak-form-element-horizontal label=${msg("SMTP Host")} ?required=${true} name="host">
<input
type="text"
value="${ifDefined(this.instance?.host || "")}"
class="pf-c-form-control"
required
/>
</ak-form-element-horizontal>
<ak-form-element-horizontal label=${msg("SMTP Port")} ?required=${true} name="port">
<input
type="number"
value="${first(this.instance?.port, 25)}"
class="pf-c-form-control"
required
/>
</ak-form-element-horizontal>
<ak-form-element-horizontal label=${msg("SMTP Username")} name="username">
<input
type="text"
value="${ifDefined(this.instance?.username || "")}"
class="pf-c-form-control"
/>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("SMTP Password")}
?writeOnly=${this.instance !== undefined}
name="password"
>
<input type="text" value="" class="pf-c-form-control" />
</ak-form-element-horizontal>
<ak-form-element-horizontal name="useTls">
<label class="pf-c-switch">
<input
class="pf-c-switch__input"
type="checkbox"
?checked=${first(this.instance?.useTls, true)}
/>
<span class="pf-c-switch__toggle">
<span class="pf-c-switch__toggle-icon">
<i class="fas fa-check" aria-hidden="true"></i>
</span>
</span>
<span class="pf-c-switch__label">${msg("Use TLS")}</span>
</label>
</ak-form-element-horizontal>
<ak-form-element-horizontal name="useSsl">
<label class="pf-c-switch">
<input
class="pf-c-switch__input"
type="checkbox"
?checked=${first(this.instance?.useSsl, false)}
/>
<span class="pf-c-switch__toggle">
<span class="pf-c-switch__toggle-icon">
<i class="fas fa-check" aria-hidden="true"></i>
</span>
</span>
<span class="pf-c-switch__label">${msg("Use SSL")}</span>
</label>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("Timeout")}
?required=${true}
name="timeout"
>
<input
type="number"
value="${first(this.instance?.timeout, 30)}"
class="pf-c-form-control"
required
/>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("From address")}
?required=${true}
name="fromAddress"
>
<input
type="text"
value="${ifDefined(this.instance?.fromAddress || "system@authentik.local")}"
class="pf-c-form-control"
required
/>
<p class="pf-c-form__helper-text">
${msg("Email address the verification email will be sent from.")}
</p>
</ak-form-element-horizontal>
</div>
</ak-form-group>`;
}
renderForm(): TemplateResult {
return html` <span> ${msg("Stage used to configure an email-based authenticator.")} </span>
<ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
<input
type="text"
value="${first(this.instance?.name, "")}"
class="pf-c-form-control"
required
/>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("Authenticator type name")}
?required=${false}
name="friendlyName"
>
<input
type="text"
value="${first(this.instance?.friendlyName, "")}"
class="pf-c-form-control"
/>
<p class="pf-c-form__helper-text">
${msg(
"Display name of this authenticator, used by users when they enroll an authenticator.",
)}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal name="useGlobalSettings">
<label class="pf-c-switch">
<input
class="pf-c-switch__input"
type="checkbox"
?checked=${first(this.instance?.useGlobalSettings, true)}
@change=${(ev: Event) => {
const target = ev.target as HTMLInputElement;
this.showConnectionSettings = !target.checked;
}}
/>
<span class="pf-c-switch__toggle">
<span class="pf-c-switch__toggle-icon">
<i class="fas fa-check" aria-hidden="true"></i>
</span>
</span>
<span class="pf-c-switch__label">${msg("Use global connection settings")}</span>
</label>
<p class="pf-c-form__helper-text">
${msg(
"When enabled, global email connection settings will be used and connection settings below will be ignored.",
)}
</p>
</ak-form-element-horizontal>
${this.renderConnectionSettings()}
<ak-form-group .expanded=${true}>
<span slot="header"> ${msg("Stage-specific settings")} </span>
<div slot="body" class="pf-c-form">
<ak-form-element-horizontal
label=${msg("Subject")}
?required=${true}
name="subject"
>
<input
type="text"
value="${first(this.instance?.subject, "authentik Sign-in code")}"
class="pf-c-form-control"
required
/>
<p class="pf-c-form__helper-text">
${msg("Subject of the verification email.")}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("Token expiration")}
?required=${true}
name="tokenExpiry"
>
<input
type="text"
value="${first(this.instance?.tokenExpiry, "minutes=15")}"
class="pf-c-form-control"
required
/>
<p class="pf-c-form__helper-text">
${msg(
"Time the token sent is valid (Format: hours=3,minutes=17,seconds=300).",
)}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("Configuration flow")}
name="configureFlow"
>
<ak-search-select
.fetchObjects=${async (query?: string): Promise<Flow[]> => {
const args: FlowsInstancesListRequest = {
ordering: "slug",
designation:
FlowsInstancesListDesignationEnum.StageConfiguration,
};
if (query !== undefined) {
args.search = query;
}
const flows = await new FlowsApi(DEFAULT_CONFIG).flowsInstancesList(
args,
);
return flows.results;
}}
.renderElement=${(flow: Flow): string => {
return RenderFlowOption(flow);
}}
.renderDescription=${(flow: Flow): TemplateResult => {
return html`${flow.name}`;
}}
.value=${(flow: Flow | undefined): string | undefined => {
return flow?.pk;
}}
.selected=${(flow: Flow): boolean => {
return this.instance?.configureFlow === flow.pk;
}}
?blankable=${true}
>
</ak-search-select>
<p class="pf-c-form__helper-text">
${msg(
"Flow used by an authenticated user to configure this Stage. If empty, user will not be able to configure this stage.",
)}
</p>
</ak-form-element-horizontal>
</div>
</ak-form-group>`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ak-stage-authenticator-email-form": AuthenticatorEmailStageForm;
}
}

View File

@ -79,6 +79,7 @@ export class AuthenticatorValidateStageForm extends BaseStageForm<AuthenticatorV
[DeviceClassesEnum.Webauthn, msg("WebAuthn Authenticators")], [DeviceClassesEnum.Webauthn, msg("WebAuthn Authenticators")],
[DeviceClassesEnum.Duo, msg("Duo Authenticators")], [DeviceClassesEnum.Duo, msg("Duo Authenticators")],
[DeviceClassesEnum.Sms, msg("SMS-based Authenticators")], [DeviceClassesEnum.Sms, msg("SMS-based Authenticators")],
[DeviceClassesEnum.Email, msg("Email-based Authenticators")],
]; ];
return html` return html`

View File

@ -58,6 +58,8 @@ export class UserDeviceTable extends Table<Device> {
switch (device.type) { switch (device.type) {
case "authentik_stages_authenticator_duo.DuoDevice": case "authentik_stages_authenticator_duo.DuoDevice":
return api.authenticatorsAdminDuoDestroy({ id: parseInt(device.pk, 10) }); return api.authenticatorsAdminDuoDestroy({ id: parseInt(device.pk, 10) });
case "authentik_stages_authenticator_email.EmailDevice":
return api.authenticatorsAdminEmailDestroy({ id: parseInt(device.pk, 10) });
case "authentik_stages_authenticator_sms.SMSDevice": case "authentik_stages_authenticator_sms.SMSDevice":
return api.authenticatorsAdminSmsDestroy({ id: parseInt(device.pk, 10) }); return api.authenticatorsAdminSmsDestroy({ id: parseInt(device.pk, 10) });
case "authentik_stages_authenticator_totp.TOTPDevice": case "authentik_stages_authenticator_totp.TOTPDevice":

View File

@ -392,6 +392,14 @@ export class FlowExecutor extends Interface implements StageHost {
.host=${this as StageHost} .host=${this as StageHost}
.challenge=${this.challenge} .challenge=${this.challenge}
></ak-stage-authenticator-webauthn>`; ></ak-stage-authenticator-webauthn>`;
case "ak-stage-authenticator-email":
await import(
"@goauthentik/flow/stages/authenticator_email/AuthenticatorEmailStage"
);
return html`<ak-stage-authenticator-email
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-authenticator-email>`;
case "ak-stage-authenticator-sms": case "ak-stage-authenticator-sms":
await import("@goauthentik/flow/stages/authenticator_sms/AuthenticatorSMSStage"); await import("@goauthentik/flow/stages/authenticator_sms/AuthenticatorSMSStage");
return html`<ak-stage-authenticator-sms return html`<ak-stage-authenticator-sms

View File

@ -0,0 +1,173 @@
import "@goauthentik/elements/EmptyState";
import "@goauthentik/elements/forms/FormElement";
import "@goauthentik/flow/FormStatic";
import { BaseStage } from "@goauthentik/flow/stages/base";
import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, html } from "lit";
import { customElement } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import PFAlert from "@patternfly/patternfly/components/Alert/alert.css";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
import PFTitle from "@patternfly/patternfly/components/Title/title.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
import {
AuthenticatorEmailChallenge,
AuthenticatorEmailChallengeResponseRequest,
} from "@goauthentik/api";
@customElement("ak-stage-authenticator-email")
export class AuthenticatorEmailStage extends BaseStage<
AuthenticatorEmailChallenge,
AuthenticatorEmailChallengeResponseRequest
> {
static get styles(): CSSResult[] {
return [PFBase, PFAlert, PFLogin, PFForm, PFFormControl, PFTitle, PFButton];
}
renderEmailInput(): TemplateResult {
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo?.title}</h1>
</header>
<div class="pf-c-login__main-body">
<form
class="pf-c-form"
@submit=${(e: Event) => {
this.submitForm(e);
}}
>
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}
>
<div slot="link">
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}"
>${msg("Not you?")}</a
>
</div>
</ak-form-static>
<ak-form-element
label="${msg("Configure your email")}"
required
class="pf-c-form__group"
.errors=${(this.challenge?.responseErrors || {})["email"]}
>
<input
type="email"
name="email"
placeholder="${msg("Please enter your email address.")}"
autofocus=""
autocomplete="email"
class="pf-c-form-control"
required
/>
</ak-form-element>
${this.renderNonFieldErrors()}
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
${msg("Continue")}
</button>
</div>
</form>
</div>
<footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links"></ul>
</footer>`;
}
renderEmailOTPInput(): TemplateResult {
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo?.title}</h1>
</header>
<div class="pf-c-login__main-body">
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}
>
<div slot="link">
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}"
>${msg("Not you?")}</a
>
</div>
</ak-form-static>
A verification token has been sent to your configured email address
${ifDefined(this.challenge.email)}
<form
class="pf-c-form"
@submit=${(e: Event) => {
this.submitForm(e);
}}
>
<ak-form-element
label="${msg("Code")}"
required
class="pf-c-form__group"
.errors=${(this.challenge?.responseErrors || {})["code"]}
>
<input
type="text"
name="code"
inputmode="numeric"
pattern="[0-9]*"
placeholder="${msg("Please enter the code you received via email")}"
autofocus=""
autocomplete="one-time-code"
class="pf-c-form-control"
required
/>
</ak-form-element>
${this.renderNonFieldErrors()}
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
${msg("Continue")}
</button>
</div>
</form>
</div>
<footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links"></ul>
</footer>`;
}
render(): TemplateResult {
console.debug(
"authentik/stages/authenticator_email:",
this.challenge ? this.challenge.emailRequired : undefined,
);
if (!this.challenge) {
console.debug(
"authentik/stages/authenticator_email: AuthenticatorEmailStage.render() called without challenge",
);
return html`<ak-empty-state loading> </ak-empty-state>`;
}
if (this.challenge.emailRequired) {
console.debug(
"authentik/stages/authenticator_email: AuthenticatorEmailStage.render() called with challenge",
this.challenge,
);
return this.renderEmailInput();
}
console.debug(
"authentik/stages/authenticator_email: AuthenticatorEmailStage.render() called without emailRequired challenge",
this.challenge,
);
return this.renderEmailOTPInput();
}
}
declare global {
interface HTMLElementTagNameMap {
"ak-stage-authenticator-email": AuthenticatorEmailStage;
}
}

View File

@ -185,6 +185,12 @@ export class AuthenticatorValidateStage
<p>${msg("SMS")}</p> <p>${msg("SMS")}</p>
<small>${msg("Tokens sent via SMS.")}</small> <small>${msg("Tokens sent via SMS.")}</small>
</div>`; </div>`;
case DeviceClassesEnum.Email:
return html`<i class="fas fa-envelope-o"></i>
<div class="right">
<p>${msg("Email")}</p>
<small>${msg("Tokens sent via email.")}</small>
</div>`;
default: default:
break; break;
} }
@ -240,6 +246,7 @@ export class AuthenticatorValidateStage
switch (this.selectedDeviceChallenge?.deviceClass) { switch (this.selectedDeviceChallenge?.deviceClass) {
case DeviceClassesEnum.Static: case DeviceClassesEnum.Static:
case DeviceClassesEnum.Totp: case DeviceClassesEnum.Totp:
case DeviceClassesEnum.Email:
case DeviceClassesEnum.Sms: case DeviceClassesEnum.Sms:
return html` <ak-stage-authenticator-validate-code return html` <ak-stage-authenticator-validate-code
.host=${this} .host=${this}

View File

@ -33,6 +33,10 @@ export class AuthenticatorValidateStageWebCode extends BaseDeviceStage<
deviceMessage(): string { deviceMessage(): string {
switch (this.deviceChallenge?.deviceClass) { switch (this.deviceChallenge?.deviceClass) {
case DeviceClassesEnum.Email: {
const email = this.deviceChallenge.challenge?.email;
return msg(`A code has been sent to you via email${email ? ` ${email}` : ""}`);
}
case DeviceClassesEnum.Sms: case DeviceClassesEnum.Sms:
return msg("A code has been sent to you via SMS."); return msg("A code has been sent to you via SMS.");
case DeviceClassesEnum.Totp: case DeviceClassesEnum.Totp:
@ -48,12 +52,14 @@ export class AuthenticatorValidateStageWebCode extends BaseDeviceStage<
deviceIcon(): string { deviceIcon(): string {
switch (this.deviceChallenge?.deviceClass) { switch (this.deviceChallenge?.deviceClass) {
case DeviceClassesEnum.Email:
return "fa-envelope-o";
case DeviceClassesEnum.Sms: case DeviceClassesEnum.Sms:
return "fa-key";
case DeviceClassesEnum.Totp:
return "fa-mobile-alt"; return "fa-mobile-alt";
case DeviceClassesEnum.Totp:
return "fa-clock";
case DeviceClassesEnum.Static: case DeviceClassesEnum.Static:
return "fa-sticky-note"; return "fa-key";
} }
return "fa-mobile-alt"; return "fa-mobile-alt";

View File

@ -1,4 +1,5 @@
import { AndNext } from "@goauthentik/common/api/config"; import { AndNext } from "@goauthentik/common/api/config";
import { globalAK } from "@goauthentik/common/global";
import { AKElement } from "@goauthentik/elements/Base"; import { AKElement } from "@goauthentik/elements/Base";
import { msg } from "@lit/localize"; import { msg } from "@lit/localize";
@ -31,7 +32,7 @@ export class UserSettingsPassword extends AKElement {
<div class="pf-c-card__body"> <div class="pf-c-card__body">
<a <a
href="${ifDefined(this.configureUrl)}${AndNext( href="${ifDefined(this.configureUrl)}${AndNext(
`/if/user/#/settings;${JSON.stringify({ page: "page-details" })}`, `${globalAK().api.base}if/user/#/settings;${JSON.stringify({ page: "page-details" })}`,
)}" )}"
class="pf-c-button pf-m-primary" class="pf-c-button pf-m-primary"
> >

View File

@ -34,6 +34,12 @@ export class MFADeviceForm extends ModelForm<Device, string> {
duoDeviceRequest: device, duoDeviceRequest: device,
}); });
break; break;
case "authentik_stages_authenticator_email.EmailDevice":
await new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsEmailUpdate({
id: parseInt(this.instance?.pk, 10),
emailDeviceRequest: device,
});
break;
case "authentik_stages_authenticator_sms.SMSDevice": case "authentik_stages_authenticator_sms.SMSDevice":
await new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsSmsUpdate({ await new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsSmsUpdate({
id: parseInt(this.instance?.pk, 10), id: parseInt(this.instance?.pk, 10),

View File

@ -1,5 +1,6 @@
import { AndNext, DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { AndNext, DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { SentryIgnoredError } from "@goauthentik/common/errors"; import { SentryIgnoredError } from "@goauthentik/common/errors";
import { globalAK } from "@goauthentik/common/global";
import { deviceTypeName } from "@goauthentik/common/labels"; import { deviceTypeName } from "@goauthentik/common/labels";
import { getRelativeTime } from "@goauthentik/common/utils"; import { getRelativeTime } from "@goauthentik/common/utils";
import "@goauthentik/elements/buttons/Dropdown"; import "@goauthentik/elements/buttons/Dropdown";
@ -73,7 +74,7 @@ export class MFADevicesPage extends Table<Device> {
return html`<li> return html`<li>
<a <a
href="${ifDefined(stage.configureUrl)}${AndNext( href="${ifDefined(stage.configureUrl)}${AndNext(
`/if/user/#/settings;${JSON.stringify({ `${globalAK().api.base}if/user/#/settings;${JSON.stringify({
page: "page-mfa", page: "page-mfa",
})}`, })}`,
)}" )}"
@ -94,6 +95,8 @@ export class MFADevicesPage extends Table<Device> {
switch (device.type) { switch (device.type) {
case "authentik_stages_authenticator_duo.DuoDevice": case "authentik_stages_authenticator_duo.DuoDevice":
return api.authenticatorsDuoDestroy(id); return api.authenticatorsDuoDestroy(id);
case "authentik_stages_authenticator_email.EmailDevice":
return api.authenticatorsEmailDestroy(id);
case "authentik_stages_authenticator_sms.SMSDevice": case "authentik_stages_authenticator_sms.SMSDevice":
return api.authenticatorsSmsDestroy(id); return api.authenticatorsSmsDestroy(id);
case "authentik_stages_authenticator_totp.TOTPDevice": case "authentik_stages_authenticator_totp.TOTPDevice":

File diff suppressed because it is too large Load Diff

View File

@ -596,9 +596,9 @@
</trans-unit> </trans-unit>
<trans-unit id="saa0e2675da69651b"> <trans-unit id="saa0e2675da69651b">
<source>The URL &quot;<x id="0" equiv-text="${this.url}"/>&quot; was not found.</source> <source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
<target>El URL &quot; <target>El URL "
<x id="0" equiv-text="${this.url}"/>&quot; no fue encontrado.</target> <x id="0" equiv-text="${this.url}"/>" no fue encontrado.</target>
</trans-unit> </trans-unit>
<trans-unit id="s58cd9c2fe836d9c6"> <trans-unit id="s58cd9c2fe836d9c6">
@ -1702,7 +1702,7 @@
</trans-unit> </trans-unit>
<trans-unit id="sa90b7809586c35ce"> <trans-unit id="sa90b7809586c35ce">
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon &quot;fa-test&quot;.</source> <source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
<target>Ingrese una URL completa, una ruta relativa o use 'fa: //fa-test' para usar el ícono Font Awesome «fa-test».</target> <target>Ingrese una URL completa, una ruta relativa o use 'fa: //fa-test' para usar el ícono Font Awesome «fa-test».</target>
</trans-unit> </trans-unit>
@ -2839,7 +2839,7 @@ no se aprueba cuando una o ambas de las opciones seleccionadas son iguales o sup
</trans-unit> </trans-unit>
<trans-unit id="s76768bebabb7d543"> <trans-unit id="s76768bebabb7d543">
<source>Field which contains members of a group. Note that if using the &quot;memberUid&quot; field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source> <source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source>
<target>Campo que contiene los miembros de un grupo. Tenga en cuenta que si se utiliza el campo «MemberUID», se supone que el valor contiene un nombre distintivo relativo. Por ejemplo, 'memberUid=alguno-usuario' en lugar de 'memberUid=CN=alguno-usuario, ou=grupos,... '</target> <target>Campo que contiene los miembros de un grupo. Tenga en cuenta que si se utiliza el campo «MemberUID», se supone que el valor contiene un nombre distintivo relativo. Por ejemplo, 'memberUid=alguno-usuario' en lugar de 'memberUid=CN=alguno-usuario, ou=grupos,... '</target>
</trans-unit> </trans-unit>
@ -3510,7 +3510,7 @@ no se aprueba cuando una o ambas de las opciones seleccionadas son iguales o sup
</trans-unit> </trans-unit>
<trans-unit id="sd216b08bafb297ee"> <trans-unit id="sd216b08bafb297ee">
<source>Flow used to authenticate users. If left empty, the first applicable flow sorted by the slug is used.</source> <source>Flow used to authenticate users. If left empty, the first applicable flow sorted by the slug is used.</source>
<target>Flujo utilizado para autenticar a los usuarios. Si se deja vacío, se usa el primer flujo aplicable clasificado por el &quot;slug&quot;.</target> <target>Flujo utilizado para autenticar a los usuarios. Si se deja vacío, se usa el primer flujo aplicable clasificado por el "slug".</target>
</trans-unit> </trans-unit>
<trans-unit id="s35e6e60e83a8c003"> <trans-unit id="s35e6e60e83a8c003">
@ -3520,7 +3520,7 @@ no se aprueba cuando una o ambas de las opciones seleccionadas son iguales o sup
</trans-unit> </trans-unit>
<trans-unit id="s7989db5f4819af89"> <trans-unit id="s7989db5f4819af89">
<source>Flow used to logout. If left empty, the first applicable flow sorted by the slug is used.</source> <source>Flow used to logout. If left empty, the first applicable flow sorted by the slug is used.</source>
<target>Flujo utilizado para cerrar sesión. Si se deja vacío, se usa el primer flujo aplicable clasificado por el &quot;slug&quot;.</target> <target>Flujo utilizado para cerrar sesión. Si se deja vacío, se usa el primer flujo aplicable clasificado por el "slug".</target>
</trans-unit> </trans-unit>
<trans-unit id="sfeb779d4ccbc5a0e"> <trans-unit id="sfeb779d4ccbc5a0e">
@ -3747,10 +3747,10 @@ no se aprueba cuando una o ambas de las opciones seleccionadas son iguales o sup
</trans-unit> </trans-unit>
<trans-unit id="sa95a538bfbb86111"> <trans-unit id="sa95a538bfbb86111">
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> &quot;<x id="1" equiv-text="${this.obj?.name}"/>&quot;?</source> <source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
<target>¿Estás seguro de que deseas actualizar <target>¿Estás seguro de que deseas actualizar
<x id="0" equiv-text="${this.objectLabel}"/>&quot; <x id="0" equiv-text="${this.objectLabel}"/>"
<x id="1" equiv-text="${this.obj?.name}"/>&quot;?</target> <x id="1" equiv-text="${this.obj?.name}"/>"?</target>
</trans-unit> </trans-unit>
<trans-unit id="sc92d7cfb6ee1fec6"> <trans-unit id="sc92d7cfb6ee1fec6">
@ -4792,8 +4792,8 @@ no se aprueba cuando una o ambas de las opciones seleccionadas son iguales o sup
</trans-unit> </trans-unit>
<trans-unit id="sdf1d8edef27236f0"> <trans-unit id="sdf1d8edef27236f0">
<source>A &quot;roaming&quot; authenticator, like a YubiKey</source> <source>A "roaming" authenticator, like a YubiKey</source>
<target>Un autenticador &quot;roaming&quot;, como una YubiKey</target> <target>Un autenticador "roaming", como una YubiKey</target>
</trans-unit> </trans-unit>
<trans-unit id="sfffba7b23d8fb40c"> <trans-unit id="sfffba7b23d8fb40c">
@ -5154,7 +5154,7 @@ no se aprueba cuando una o ambas de las opciones seleccionadas son iguales o sup
</trans-unit> </trans-unit>
<trans-unit id="s1608b2f94fa0dbd4"> <trans-unit id="s1608b2f94fa0dbd4">
<source>If set to a duration above 0, the user will have the option to choose to &quot;stay signed in&quot;, which will extend their session by the time specified here.</source> <source>If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here.</source>
</trans-unit> </trans-unit>
<trans-unit id="s542a71bb8f41e057"> <trans-unit id="s542a71bb8f41e057">
@ -7366,7 +7366,7 @@ Las vinculaciones a grupos o usuarios se comparan con el usuario del evento.</ta
<target>Usuario creado correctamente y agregado al grupo <x id="0" equiv-text="${this.group.name}"/></target> <target>Usuario creado correctamente y agregado al grupo <x id="0" equiv-text="${this.group.name}"/></target>
</trans-unit> </trans-unit>
<trans-unit id="s824e0943a7104668"> <trans-unit id="s824e0943a7104668">
<source>This user will be added to the group &quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&quot;.</source> <source>This user will be added to the group "<x id="0" equiv-text="${this.targetGroup.name}"/>".</source>
</trans-unit> </trans-unit>
<trans-unit id="s62e7f6ed7d9cb3ca"> <trans-unit id="s62e7f6ed7d9cb3ca">
<source>Pretend user exists</source> <source>Pretend user exists</source>
@ -8506,7 +8506,7 @@ Las vinculaciones a grupos o usuarios se comparan con el usuario del evento.</ta
<source>Sync Group</source> <source>Sync Group</source>
</trans-unit> </trans-unit>
<trans-unit id="s2d5f69929bb7221d"> <trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${p.name}"/> (&quot;<x id="1" equiv-text="${p.fieldKey}"/>&quot;, of type <x id="2" equiv-text="${p.type}"/>)</source> <source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
</trans-unit> </trans-unit>
<trans-unit id="s25bacc19d98b444e"> <trans-unit id="s25bacc19d98b444e">
<source>Parent Group</source> <source>Parent Group</source>
@ -8701,7 +8701,7 @@ Las vinculaciones a grupos o usuarios se comparan con el usuario del evento.</ta
<source>Valid redirect URIs after a successful authorization flow. Also specify any origins here for Implicit flows.</source> <source>Valid redirect URIs after a successful authorization flow. Also specify any origins here for Implicit flows.</source>
</trans-unit> </trans-unit>
<trans-unit id="s4c49d27de60a532b"> <trans-unit id="s4c49d27de60a532b">
<source>To allow any redirect URI, set the mode to Regex and the value to &quot;.*&quot;. Be aware of the possible security implications this can have.</source> <source>To allow any redirect URI, set the mode to Regex and the value to ".*". Be aware of the possible security implications this can have.</source>
</trans-unit> </trans-unit>
<trans-unit id="sa52bf79fe1ccb13e"> <trans-unit id="sa52bf79fe1ccb13e">
<source>Federated OIDC Sources</source> <source>Federated OIDC Sources</source>

View File

@ -596,9 +596,9 @@
</trans-unit> </trans-unit>
<trans-unit id="saa0e2675da69651b"> <trans-unit id="saa0e2675da69651b">
<source>The URL &quot;<x id="0" equiv-text="${this.url}"/>&quot; was not found.</source> <source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
<target>L'URL &quot; <target>L'URL "
<x id="0" equiv-text="${this.url}"/>&quot; n'a pas été trouvée.</target> <x id="0" equiv-text="${this.url}"/>" n'a pas été trouvée.</target>
</trans-unit> </trans-unit>
<trans-unit id="s58cd9c2fe836d9c6"> <trans-unit id="s58cd9c2fe836d9c6">
@ -1547,7 +1547,7 @@
</trans-unit> </trans-unit>
<trans-unit id="s33ed903c210a6209"> <trans-unit id="s33ed903c210a6209">
<source>Token to authenticate with. Currently only bearer authentication is supported.</source> <source>Token to authenticate with. Currently only bearer authentication is supported.</source>
<target>Jeton d'authentification à utiliser. Actuellement, seule l'authentification &quot;bearer authentication&quot; est prise en charge.</target> <target>Jeton d'authentification à utiliser. Actuellement, seule l'authentification "bearer authentication" est prise en charge.</target>
</trans-unit> </trans-unit>
<trans-unit id="sfc8bb104e2c05af8"> <trans-unit id="sfc8bb104e2c05af8">
@ -1715,8 +1715,8 @@
</trans-unit> </trans-unit>
<trans-unit id="sa90b7809586c35ce"> <trans-unit id="sa90b7809586c35ce">
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon &quot;fa-test&quot;.</source> <source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
<target>Entrez une URL complète, un chemin relatif ou utilisez 'fa://fa-test' pour utiliser l'icône Font Awesome &quot;fa-test&quot;.</target> <target>Entrez une URL complète, un chemin relatif ou utilisez 'fa://fa-test' pour utiliser l'icône Font Awesome "fa-test".</target>
</trans-unit> </trans-unit>
<trans-unit id="s0410779cb47de312"> <trans-unit id="s0410779cb47de312">
@ -2779,7 +2779,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit> </trans-unit>
<trans-unit id="s33683c3b1dbaf264"> <trans-unit id="s33683c3b1dbaf264">
<source>To use SSL instead, use 'ldaps://' and disable this option.</source> <source>To use SSL instead, use 'ldaps://' and disable this option.</source>
<target>Pour utiliser SSL à la base, utilisez &quot;ldaps://&quot; et désactviez cette option.</target> <target>Pour utiliser SSL à la base, utilisez "ldaps://" et désactviez cette option.</target>
</trans-unit> </trans-unit>
<trans-unit id="s2221fef80f4753a2"> <trans-unit id="s2221fef80f4753a2">
@ -2863,8 +2863,8 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit> </trans-unit>
<trans-unit id="s76768bebabb7d543"> <trans-unit id="s76768bebabb7d543">
<source>Field which contains members of a group. Note that if using the &quot;memberUid&quot; field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source> <source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source>
<target>Champ qui contient les membres d'un groupe. Si vous utilisez le champ &quot;memberUid&quot;, la valeur est censée contenir un nom distinctif relatif, par exemple 'memberUid=un-utilisateur' au lieu de 'memberUid=cn=un-utilisateur,ou=groups,...'</target> <target>Champ qui contient les membres d'un groupe. Si vous utilisez le champ "memberUid", la valeur est censée contenir un nom distinctif relatif, par exemple 'memberUid=un-utilisateur' au lieu de 'memberUid=cn=un-utilisateur,ou=groups,...'</target>
</trans-unit> </trans-unit>
<trans-unit id="s026555347e589f0e"> <trans-unit id="s026555347e589f0e">
@ -3159,7 +3159,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit> </trans-unit>
<trans-unit id="s3198c384c2f68b08"> <trans-unit id="s3198c384c2f68b08">
<source>Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually.</source> <source>Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually.</source>
<target>Moment où les utilisateurs temporaires doivent être supprimés. Cela ne s'applique que si votre IDP utilise le format NameID &quot;transient&quot; et que l'utilisateur ne se déconnecte pas manuellement.</target> <target>Moment où les utilisateurs temporaires doivent être supprimés. Cela ne s'applique que si votre IDP utilise le format NameID "transient" et que l'utilisateur ne se déconnecte pas manuellement.</target>
</trans-unit> </trans-unit>
<trans-unit id="sb32e9c1faa0b8673"> <trans-unit id="sb32e9c1faa0b8673">
@ -3301,7 +3301,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit> </trans-unit>
<trans-unit id="s9f8aac89fe318acc"> <trans-unit id="s9f8aac89fe318acc">
<source>Optionally set the 'FriendlyName' value of the Assertion attribute.</source> <source>Optionally set the 'FriendlyName' value of the Assertion attribute.</source>
<target>Indiquer la valeur &quot;FriendlyName&quot; de l'attribut d'assertion (optionnel)</target> <target>Indiquer la valeur "FriendlyName" de l'attribut d'assertion (optionnel)</target>
</trans-unit> </trans-unit>
<trans-unit id="s851c108679653d2a"> <trans-unit id="s851c108679653d2a">
@ -3782,10 +3782,10 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit> </trans-unit>
<trans-unit id="sa95a538bfbb86111"> <trans-unit id="sa95a538bfbb86111">
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> &quot;<x id="1" equiv-text="${this.obj?.name}"/>&quot;?</source> <source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
<target>Êtes-vous sûr de vouloir mettre à jour <target>Êtes-vous sûr de vouloir mettre à jour
<x id="0" equiv-text="${this.objectLabel}"/>&quot; <x id="0" equiv-text="${this.objectLabel}"/>"
<x id="1" equiv-text="${this.obj?.name}"/>&quot;?</target> <x id="1" equiv-text="${this.obj?.name}"/>"?</target>
</trans-unit> </trans-unit>
<trans-unit id="sc92d7cfb6ee1fec6"> <trans-unit id="sc92d7cfb6ee1fec6">
@ -4856,8 +4856,8 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit> </trans-unit>
<trans-unit id="sdf1d8edef27236f0"> <trans-unit id="sdf1d8edef27236f0">
<source>A &quot;roaming&quot; authenticator, like a YubiKey</source> <source>A "roaming" authenticator, like a YubiKey</source>
<target>Un authentificateur &quot;itinérant&quot;, comme une YubiKey</target> <target>Un authentificateur "itinérant", comme une YubiKey</target>
</trans-unit> </trans-unit>
<trans-unit id="sfffba7b23d8fb40c"> <trans-unit id="sfffba7b23d8fb40c">
@ -5172,7 +5172,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit> </trans-unit>
<trans-unit id="s5170f9ef331949c0"> <trans-unit id="s5170f9ef331949c0">
<source>Show arbitrary input fields to the user, for example during enrollment. Data is saved in the flow context under the 'prompt_data' variable.</source> <source>Show arbitrary input fields to the user, for example during enrollment. Data is saved in the flow context under the 'prompt_data' variable.</source>
<target>Afficher des champs de saisie arbitraires à l'utilisateur, par exemple pendant l'inscription. Les données sont enregistrées dans le contexte du flux sous la variable &quot;prompt_data&quot;.</target> <target>Afficher des champs de saisie arbitraires à l'utilisateur, par exemple pendant l'inscription. Les données sont enregistrées dans le contexte du flux sous la variable "prompt_data".</target>
</trans-unit> </trans-unit>
<trans-unit id="s36cb242ac90353bc"> <trans-unit id="s36cb242ac90353bc">
@ -5225,8 +5225,8 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit> </trans-unit>
<trans-unit id="s1608b2f94fa0dbd4"> <trans-unit id="s1608b2f94fa0dbd4">
<source>If set to a duration above 0, the user will have the option to choose to &quot;stay signed in&quot;, which will extend their session by the time specified here.</source> <source>If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here.</source>
<target>Si défini à une durée supérieure à 0, l'utilisateur aura la possibilité de choisir de &quot;rester connecté&quot;, ce qui prolongera sa session jusqu'à la durée spécifiée ici.</target> <target>Si défini à une durée supérieure à 0, l'utilisateur aura la possibilité de choisir de "rester connecté", ce qui prolongera sa session jusqu'à la durée spécifiée ici.</target>
</trans-unit> </trans-unit>
<trans-unit id="s542a71bb8f41e057"> <trans-unit id="s542a71bb8f41e057">
@ -7216,7 +7216,7 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
</trans-unit> </trans-unit>
<trans-unit id="sff0ac1ace2d90709"> <trans-unit id="sff0ac1ace2d90709">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a managed outpost, this is done for you).</source> <source>Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a managed outpost, this is done for you).</source>
<target>Utilisez ce fournisseur avec l'option &quot;auth_request&quot; de Nginx ou &quot;forwardAuth&quot; de Traefik. Chaque application/domaine a besoin de son propre fournisseur. De plus, sur chaque domaine, &quot;/outpost.goauthentik.io&quot; doit être routé vers le poste avancé (lorsque vous utilisez un poste avancé géré, cela est fait pour vous).</target> <target>Utilisez ce fournisseur avec l'option "auth_request" de Nginx ou "forwardAuth" de Traefik. Chaque application/domaine a besoin de son propre fournisseur. De plus, sur chaque domaine, "/outpost.goauthentik.io" doit être routé vers le poste avancé (lorsque vous utilisez un poste avancé géré, cela est fait pour vous).</target>
</trans-unit> </trans-unit>
<trans-unit id="scb58b8a60cad8762"> <trans-unit id="scb58b8a60cad8762">
<source>Default relay state</source> <source>Default relay state</source>
@ -7520,7 +7520,7 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
<target>Utilisateur créé et ajouté au groupe <x id="0" equiv-text="${this.group.name}"/> avec succès</target> <target>Utilisateur créé et ajouté au groupe <x id="0" equiv-text="${this.group.name}"/> avec succès</target>
</trans-unit> </trans-unit>
<trans-unit id="s824e0943a7104668"> <trans-unit id="s824e0943a7104668">
<source>This user will be added to the group &quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&quot;.</source> <source>This user will be added to the group "<x id="0" equiv-text="${this.targetGroup.name}"/>".</source>
<target>Cet utilisateur sera ajouté au groupe &amp;quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&amp;quot;.</target> <target>Cet utilisateur sera ajouté au groupe &amp;quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&amp;quot;.</target>
</trans-unit> </trans-unit>
<trans-unit id="s62e7f6ed7d9cb3ca"> <trans-unit id="s62e7f6ed7d9cb3ca">
@ -8814,7 +8814,7 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
<target>Synchroniser le groupe</target> <target>Synchroniser le groupe</target>
</trans-unit> </trans-unit>
<trans-unit id="s2d5f69929bb7221d"> <trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${p.name}"/> (&quot;<x id="1" equiv-text="${p.fieldKey}"/>&quot;, of type <x id="2" equiv-text="${p.type}"/>)</source> <source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
<target><x id="0" equiv-text="${p.name}"/> (&amp;quot;<x id="1" equiv-text="${p.fieldKey}"/>&amp;quot;, de type <x id="2" equiv-text="${p.type}"/>)</target> <target><x id="0" equiv-text="${p.name}"/> (&amp;quot;<x id="1" equiv-text="${p.fieldKey}"/>&amp;quot;, de type <x id="2" equiv-text="${p.type}"/>)</target>
</trans-unit> </trans-unit>
<trans-unit id="s25bacc19d98b444e"> <trans-unit id="s25bacc19d98b444e">
@ -9062,8 +9062,8 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
<target>URLs de redirection autorisées après un flux d'autorisation réussi. Indiquez également toute origine ici pour les flux implicites.</target> <target>URLs de redirection autorisées après un flux d'autorisation réussi. Indiquez également toute origine ici pour les flux implicites.</target>
</trans-unit> </trans-unit>
<trans-unit id="s4c49d27de60a532b"> <trans-unit id="s4c49d27de60a532b">
<source>To allow any redirect URI, set the mode to Regex and the value to &quot;.*&quot;. Be aware of the possible security implications this can have.</source> <source>To allow any redirect URI, set the mode to Regex and the value to ".*". Be aware of the possible security implications this can have.</source>
<target>Pour permettre n'importe quelle URI de redirection, définissez cette valeur sur &quot;.*&quot;. Soyez conscient des possibles implications de sécurité que cela peut avoir.</target> <target>Pour permettre n'importe quelle URI de redirection, définissez cette valeur sur ".*". Soyez conscient des possibles implications de sécurité que cela peut avoir.</target>
</trans-unit> </trans-unit>
<trans-unit id="sa52bf79fe1ccb13e"> <trans-unit id="sa52bf79fe1ccb13e">
<source>Federated OIDC Sources</source> <source>Federated OIDC Sources</source>

View File

@ -3991,7 +3991,10 @@ doesn't pass when either or both of the selected options are equal or above the
<target>Rimuovi dal gruppo(i)</target> <target>Rimuovi dal gruppo(i)</target>
</trans-unit> </trans-unit>
<trans-unit id="sb4c9ed2a487b238f">
<source>Are you sure you want to remove user <x id="0" equiv-text="${this.targetUser?.username}"/> from the following groups?</source>
</trans-unit>
<trans-unit id="s964f6725aeb7662f"> <trans-unit id="s964f6725aeb7662f">
<source>Add Group</source> <source>Add Group</source>
<target>Aggiungi Gruppo</target> <target>Aggiungi Gruppo</target>
@ -4823,7 +4826,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit> </trans-unit>
<trans-unit id="s7e5af9c6ba6f5cc6"> <trans-unit id="s7e5af9c6ba6f5cc6">
<source>When multiple stages are selected, the user can choose which one they want to enroll.</source> <source>When multiple stages are selected, the user can choose which one they want to enroll.</source>
<target>Quando vengono selezionate più fasi, l'utente può scegliere quale desidera iscriversi.</target> <target>Quando vengono selezionate più fasi, l'utente può scegliere a quale desidera iscriversi.</target>
</trans-unit> </trans-unit>
<trans-unit id="s34b23ebbac9f6ab9"> <trans-unit id="s34b23ebbac9f6ab9">
@ -5874,6 +5877,10 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Last seen</source> <source>Last seen</source>
<target>Ultima volta visto</target> <target>Ultima volta visto</target>
</trans-unit>
<trans-unit id="s1ac2653a6492b435">
<source><x id="0" equiv-text="${this.outpostHealth.version}"/>, should be <x id="1" equiv-text="${this.outpostHealth.versionShould}"/></source>
</trans-unit> </trans-unit>
<trans-unit id="s1e176e35c828318c"> <trans-unit id="s1e176e35c828318c">
<source>Hostname</source> <source>Hostname</source>
@ -6679,6 +6686,10 @@ Bindings to groups/users are checked against the user of the event.</source>
<target>Errore durante la creazione della credenziale: <target>Errore durante la creazione della credenziale:
<x id="0" equiv-text="${err}"/></target> <x id="0" equiv-text="${err}"/></target>
</trans-unit>
<trans-unit id="s9d95f09deb601f34">
<source>Server validation of credential failed: <x id="0" equiv-text="${err}"/></source>
</trans-unit> </trans-unit>
<trans-unit id="s3fb39fc45e840f78"> <trans-unit id="s3fb39fc45e840f78">
<source>Refer to documentation</source> <source>Refer to documentation</source>
@ -6715,8 +6726,6 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="saf63d34c8601dd41"> <trans-unit id="saf63d34c8601dd41">
<source><x id="0" equiv-text="${prompt.label}"/></source> <source><x id="0" equiv-text="${prompt.label}"/></source>
<target><x id="0" equiv-text="${prompt.label}"/>
</target>
</trans-unit> </trans-unit>
<trans-unit id="s33f85f24c0f5f008"> <trans-unit id="s33f85f24c0f5f008">
@ -6754,7 +6763,10 @@ Bindings to groups/users are checked against the user of the event.</source>
<target>Riuscita disconnessione sorgente</target> <target>Riuscita disconnessione sorgente</target>
</trans-unit> </trans-unit>
<trans-unit id="s67dedada007d4067">
<source>Failed to disconnected source: <x id="0" equiv-text="${exc}"/></source>
</trans-unit>
<trans-unit id="sd2208cd1a767644b"> <trans-unit id="sd2208cd1a767644b">
<source>Disconnect</source> <source>Disconnect</source>
<target>Disconnetti</target> <target>Disconnetti</target>
@ -6764,6 +6776,10 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Connect</source> <source>Connect</source>
<target>Connetti</target> <target>Connetti</target>
</trans-unit>
<trans-unit id="sababff57115130a0">
<source>Error: unsupported source settings: <x id="0" equiv-text="${source.component}"/></source>
</trans-unit> </trans-unit>
<trans-unit id="sd1031bddc66dc495"> <trans-unit id="sd1031bddc66dc495">
<source>Connect your user account to the services listed below, to allow you to login using the service instead of traditional credentials.</source> <source>Connect your user account to the services listed below, to allow you to login using the service instead of traditional credentials.</source>
@ -6977,10 +6993,16 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Forecast internal users</source> <source>Forecast internal users</source>
<target>Previsione utenti interni</target> <target>Previsione utenti interni</target>
</trans-unit> </trans-unit>
<trans-unit id="sde9a3f41977ec1f8">
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.internalUsers}"/> current internal users and <x id="1" equiv-text="${this.forecast?.forecastedInternalUsers}"/> forecasted internal users.</source>
</trans-unit>
<trans-unit id="s4557b6b9da258643"> <trans-unit id="s4557b6b9da258643">
<source>Forecast external users</source> <source>Forecast external users</source>
<target>Previsione utenti esterni</target> <target>Previsione utenti esterni</target>
</trans-unit> </trans-unit>
<trans-unit id="sf52479d6daa0a4a8">
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.externalUsers}"/> current external users and <x id="1" equiv-text="${this.forecast?.forecastedExternalUsers}"/> forecasted external users.</source>
</trans-unit>
<trans-unit id="s6196153c4b0c1ea0"> <trans-unit id="s6196153c4b0c1ea0">
<source>Install</source> <source>Install</source>
<target>Installa</target> <target>Installa</target>
@ -7218,7 +7240,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Method's display Name.</source> <source>Method's display Name.</source>
<target>Nome visualizzato del metodo.</target> <target>Nome visualizzato del metodo.</target>
</trans-unit> </trans-unit>
<trans-unit id="sd18b18f91b804c3f"> <trans-unit id="sd18b18f91b804c3f">
<source>Custom attributes</source> <source>Custom attributes</source>
<target>Attributi personalizzati</target> <target>Attributi personalizzati</target>
@ -7479,6 +7500,12 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>4: Very unguessable: strong protection from offline slow-hash scenario. (guesses &amp;gt;= 10^10)</source> <source>4: Very unguessable: strong protection from offline slow-hash scenario. (guesses &amp;gt;= 10^10)</source>
<target>4: Molto difficilmente indovinabile: forte protezione dallo scenario offline con hash lento. (tentativi &amp;gt;= 10^10)</target> <target>4: Molto difficilmente indovinabile: forte protezione dallo scenario offline con hash lento. (tentativi &amp;gt;= 10^10)</target>
</trans-unit> </trans-unit>
<trans-unit id="s3d2a8b86a4f5a810">
<source>Successfully created user and added to group <x id="0" equiv-text="${this.group.name}"/></source>
</trans-unit>
<trans-unit id="s824e0943a7104668">
<source>This user will be added to the group "<x id="0" equiv-text="${this.targetGroup.name}"/>".</source>
</trans-unit>
<trans-unit id="s62e7f6ed7d9cb3ca"> <trans-unit id="s62e7f6ed7d9cb3ca">
<source>Pretend user exists</source> <source>Pretend user exists</source>
<target>Fingere che l'utente esista</target> <target>Fingere che l'utente esista</target>
@ -7791,10 +7818,21 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>The user's UPN, if set (otherwise an empty string)</source> <source>The user's UPN, if set (otherwise an empty string)</source>
<target>L'utente è UPN, se impostato (altrimenti una stringa vuota)</target> <target>L'utente è UPN, se impostato (altrimenti una stringa vuota)</target>
</trans-unit> </trans-unit>
<trans-unit id="h4963ed14d7e239a9">
<source>An attribute path like
<x id="0" equiv-text="&lt;code&gt;"/>attributes.something.avatar<x id="1" equiv-text="&lt;/code&gt;"/>, which can be used in
combination with the file field to allow users to upload custom
avatars for themselves.</source>
</trans-unit>
<trans-unit id="s4c80c34a67a6f1c9"> <trans-unit id="s4c80c34a67a6f1c9">
<source>Multiple values can be set, comma-separated, and authentik will fallback to the next mode when no avatar could be found.</source> <source>Multiple values can be set, comma-separated, and authentik will fallback to the next mode when no avatar could be found.</source>
<target>È possibile impostare più valori, separati da virgole e Authenk scenderà alla modalità successiva quando non è stato possibile trovare un avatar.</target> <target>È possibile impostare più valori, separati da virgole e Authenk scenderà alla modalità successiva quando non è stato possibile trovare un avatar.</target>
</trans-unit> </trans-unit>
<trans-unit id="h2fafcc3ebafea2f8">
<source>For example, setting this to <x id="0" equiv-text="&lt;code&gt;"/>gravatar,initials<x id="1" equiv-text="&lt;/code&gt;"/> will
attempt to get an avatar from Gravatar, and if the user has not
configured on there, it will fallback to a generated avatar.</source>
</trans-unit>
<trans-unit id="s5faec5eb5faf62ac"> <trans-unit id="s5faec5eb5faf62ac">
<source>Allow users to change name</source> <source>Allow users to change name</source>
<target>Permetti agli utenti di cambiare nome</target> <target>Permetti agli utenti di cambiare nome</target>
@ -8635,6 +8673,9 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Russian</source> <source>Russian</source>
<target>Russo</target> <target>Russo</target>
</trans-unit> </trans-unit>
<trans-unit id="s354405ae02cb262d">
<source>Last seen: <x id="0" equiv-text="${getRelativeTime(lastSeen)}"/> (<x id="1" equiv-text="${lastSeen.toLocaleTimeString()}"/>)</source>
</trans-unit>
<trans-unit id="s5aebe06e3ddf6ca9"> <trans-unit id="s5aebe06e3ddf6ca9">
<source>Sign assertions</source> <source>Sign assertions</source>
<target>Segnale</target> <target>Segnale</target>
@ -8727,6 +8768,10 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Internal user usage</source> <source>Internal user usage</source>
<target>Utilizzo degli utenti interni</target> <target>Utilizzo degli utenti interni</target>
</trans-unit> </trans-unit>
<trans-unit id="s087d6f07b52b30ec">
<source><x id="0" equiv-text="${internalUserPercentage &lt; Infinity ? internalUserPercentage : &quot;∞&quot;}"/>%</source>
<target><x id="0" equiv-text="${internalUserPercentage &lt; Infinity ? internalUserPercentage : &quot;∞&quot;}"/>%</target>
</trans-unit>
<trans-unit id="sae72e1569d34fb02"> <trans-unit id="sae72e1569d34fb02">
<source>External user usage</source> <source>External user usage</source>
<target>Utilizzo dell'utente esterno</target> <target>Utilizzo dell'utente esterno</target>
@ -8743,6 +8788,9 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Sync Group</source> <source>Sync Group</source>
<target>Gruppo di sincronizzazione</target> <target>Gruppo di sincronizzazione</target>
</trans-unit> </trans-unit>
<trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
</trans-unit>
<trans-unit id="s25bacc19d98b444e"> <trans-unit id="s25bacc19d98b444e">
<source>Parent Group</source> <source>Parent Group</source>
<target>Gruppo genitore</target> <target>Gruppo genitore</target>
@ -8763,10 +8811,19 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Verify SCIM server's certificates</source> <source>Verify SCIM server's certificates</source>
<target>Verifica i certificati del server Scim</target> <target>Verifica i certificati del server Scim</target>
</trans-unit> </trans-unit>
<trans-unit id="sac88482c48453fc8">
<source>You've logged out of <x id="0" equiv-text="${this.challenge.applicationName}"/>. You can go back to the overview to launch another application, or log out of your authentik account.</source>
</trans-unit>
<trans-unit id="s3108167b562674e2"> <trans-unit id="s3108167b562674e2">
<source>Go back to overview</source> <source>Go back to overview</source>
<target>Torna alla panoramica</target> <target>Torna alla panoramica</target>
</trans-unit> </trans-unit>
<trans-unit id="sdb749e793de55478">
<source>Log out of <x id="0" equiv-text="${this.challenge.brandName}"/></source>
</trans-unit>
<trans-unit id="s521681ed1d5ff814">
<source>Log back into <x id="0" equiv-text="${this.challenge.applicationName}"/></source>
</trans-unit>
<trans-unit id="sc991a35f5e88d1d3"> <trans-unit id="sc991a35f5e88d1d3">
<source>Encryption Key</source> <source>Encryption Key</source>
<target>Chiave di crittografia</target> <target>Chiave di crittografia</target>
@ -8775,6 +8832,9 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Key used to encrypt the tokens.</source> <source>Key used to encrypt the tokens.</source>
<target>Chiave utilizzato per crittografare i token.</target> <target>Chiave utilizzato per crittografare i token.</target>
</trans-unit> </trans-unit>
<trans-unit id="sbfee780fa0a2c83e">
<source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be deleted</source>
</trans-unit>
<trans-unit id="s336936629cdeb3e5"> <trans-unit id="s336936629cdeb3e5">
<source>Stage used to verify users' browsers using Google Chrome Device Trust. This stage can be used in authentication/authorization flows.</source> <source>Stage used to verify users' browsers using Google Chrome Device Trust. This stage can be used in authentication/authorization flows.</source>
<target>Fase utilizzata per verificare i browser degli utenti utilizzando la fiducia del dispositivo Google Chrome. Questa fase può essere utilizzata nei flussi di autenticazione/autorizzazione.</target> <target>Fase utilizzata per verificare i browser degli utenti utilizzando la fiducia del dispositivo Google Chrome. Questa fase può essere utilizzata nei flussi di autenticazione/autorizzazione.</target>
@ -8783,6 +8843,9 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Google Verified Access API</source> <source>Google Verified Access API</source>
<target>Google Verified Access API</target> <target>Google Verified Access API</target>
</trans-unit> </trans-unit>
<trans-unit id="s013620384af7c8b4">
<source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be edited</source>
</trans-unit>
<trans-unit id="s4347135696fc7cde"> <trans-unit id="s4347135696fc7cde">
<source>Advanced flow settings</source> <source>Advanced flow settings</source>
<target>Impostazioni di flusso avanzate</target> <target>Impostazioni di flusso avanzate</target>
@ -8911,106 +8974,56 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Endpoint Google Chrome Device Trust is in preview.</source> <source>Endpoint Google Chrome Device Trust is in preview.</source>
<target>anteprima dell'Endpoint Google Chrome Device Trust</target> <target>anteprima dell'Endpoint Google Chrome Device Trust</target>
</trans-unit> </trans-unit>
<trans-unit id="s3d2a8b86a4f5a810">
<source>Successfully created user and added to group <x id="0" equiv-text="${this.group.name}"/></source>
</trans-unit>
<trans-unit id="s824e0943a7104668">
<source>This user will be added to the group "<x id="0" equiv-text="${this.targetGroup.name}"/>".</source>
</trans-unit>
<trans-unit id="sd48cc52117f4d68b">
<source>Italian</source>
</trans-unit>
<trans-unit id="sb4c9ed2a487b238f">
<source>Are you sure you want to remove user <x id="0" equiv-text="${this.targetUser?.username}"/> from the following groups?</source>
</trans-unit>
<trans-unit id="s67dedada007d4067">
<source>Failed to disconnected source: <x id="0" equiv-text="${exc}"/></source>
</trans-unit>
<trans-unit id="sababff57115130a0">
<source>Error: unsupported source settings: <x id="0" equiv-text="${source.component}"/></source>
</trans-unit>
<trans-unit id="sbfee780fa0a2c83e">
<source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be deleted</source>
</trans-unit>
<trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
</trans-unit>
<trans-unit id="s1ac2653a6492b435">
<source><x id="0" equiv-text="${this.outpostHealth.version}"/>, should be <x id="1" equiv-text="${this.outpostHealth.versionShould}"/></source>
</trans-unit>
<trans-unit id="s354405ae02cb262d">
<source>Last seen: <x id="0" equiv-text="${getRelativeTime(lastSeen)}"/> (<x id="1" equiv-text="${lastSeen.toLocaleTimeString()}"/>)</source>
</trans-unit>
<trans-unit id="h4963ed14d7e239a9">
<source>An attribute path like
<x id="0" equiv-text="&lt;code&gt;"/>attributes.something.avatar<x id="1" equiv-text="&lt;/code&gt;"/>, which can be used in
combination with the file field to allow users to upload custom
avatars for themselves.</source>
</trans-unit>
<trans-unit id="h2fafcc3ebafea2f8">
<source>For example, setting this to <x id="0" equiv-text="&lt;code&gt;"/>gravatar,initials<x id="1" equiv-text="&lt;/code&gt;"/> will
attempt to get an avatar from Gravatar, and if the user has not
configured on there, it will fallback to a generated avatar.</source>
</trans-unit>
<trans-unit id="s087d6f07b52b30ec">
<source><x id="0" equiv-text="${internalUserPercentage &lt; Infinity ? internalUserPercentage : &quot;∞&quot;}"/>%</source>
</trans-unit>
<trans-unit id="sde9a3f41977ec1f8">
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.internalUsers}"/> current internal users and <x id="1" equiv-text="${this.forecast?.forecastedInternalUsers}"/> forecasted internal users.</source>
</trans-unit>
<trans-unit id="sf52479d6daa0a4a8">
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.externalUsers}"/> current external users and <x id="1" equiv-text="${this.forecast?.forecastedExternalUsers}"/> forecasted external users.</source>
</trans-unit>
<trans-unit id="sac88482c48453fc8">
<source>You've logged out of <x id="0" equiv-text="${this.challenge.applicationName}"/>. You can go back to the overview to launch another application, or log out of your authentik account.</source>
</trans-unit>
<trans-unit id="sdb749e793de55478">
<source>Log out of <x id="0" equiv-text="${this.challenge.brandName}"/></source>
</trans-unit>
<trans-unit id="s521681ed1d5ff814">
<source>Log back into <x id="0" equiv-text="${this.challenge.applicationName}"/></source>
</trans-unit>
<trans-unit id="s9d95f09deb601f34">
<source>Server validation of credential failed: <x id="0" equiv-text="${err}"/></source>
</trans-unit>
<trans-unit id="s013620384af7c8b4">
<source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be edited</source>
</trans-unit>
<trans-unit id="saa29a2ac03cd9d19">
<source>Reason</source>
</trans-unit>
<trans-unit id="se9fd92a824994eba">
<source>Reason for impersonating the user</source>
</trans-unit>
<trans-unit id="s6f4f35a5a4b9b3cb"> <trans-unit id="s6f4f35a5a4b9b3cb">
<source>Interactive</source> <source>Interactive</source>
<target>Interattivo</target>
</trans-unit> </trans-unit>
<trans-unit id="sd020240e41e4c207"> <trans-unit id="sd020240e41e4c207">
<source>Enable this flag if the configured captcha requires User-interaction. Required for reCAPTCHA v2, hCaptcha and Cloudflare Turnstile.</source> <source>Enable this flag if the configured captcha requires User-interaction. Required for reCAPTCHA v2, hCaptcha and Cloudflare Turnstile.</source>
<target>Abilita questa vocese il captcha configurato richiede interazione con l'utente. Richiesto per reCAPTCHA v2m hCaptcha eCloudflare Turnstile</target>
</trans-unit> </trans-unit>
<trans-unit id="s6cee92a3b310e650"> <trans-unit id="saa29a2ac03cd9d19">
<source>Add entry</source> <source>Reason</source>
<target>Motivo</target>
</trans-unit> </trans-unit>
<trans-unit id="s8f36fb59c31d33eb"> <trans-unit id="se9fd92a824994eba">
<source>Link Title</source> <source>Reason for impersonating the user</source>
</trans-unit> <target>Motivo per impersonare un utente</target>
<trans-unit id="s92205c10ba1f0f4c">
<source>This option configures the footer links on the flow executor pages. The URL is limited to web and mail addresses. If the name is left blank, the URL will be shown.</source>
</trans-unit> </trans-unit>
<trans-unit id="s29fab8887734192f"> <trans-unit id="s29fab8887734192f">
<source>Require reason for impersonation</source> <source>Require reason for impersonation</source>
<target>Richiedi un motivo per impersonare un utente</target>
</trans-unit> </trans-unit>
<trans-unit id="sc24af6de78468cfa"> <trans-unit id="sc24af6de78468cfa">
<source>Require administrators to provide a reason for impersonating a user.</source> <source>Require administrators to provide a reason for impersonating a user.</source>
<target>Richiedi agli amministratori un motivo per impersonare un utente</target>
</trans-unit>
<trans-unit id="sd48cc52117f4d68b">
<source>Italian</source>
<target>Italiano</target>
</trans-unit>
<trans-unit id="s6cee92a3b310e650">
<source>Add entry</source>
<target>Aggiungi voce</target>
</trans-unit>
<trans-unit id="s8f36fb59c31d33eb">
<source>Link Title</source>
<target>nome del link</target>
</trans-unit>
<trans-unit id="s92205c10ba1f0f4c">
<source>This option configures the footer links on the flow executor pages. The URL is limited to web and mail addresses. If the name is left blank, the URL will be shown.</source>
<target>Questo opzione configura il link in basso nel flusso delle pagine di esecuzione. L'URL e' limitato a web e indirizzo mail-Se il nome viene lasciato vuoto, verra' visualizzato l'URL</target>
</trans-unit> </trans-unit>
<trans-unit id="s66f572bec2bde9c4"> <trans-unit id="s66f572bec2bde9c4">
<source>External applications that use <x id="0" equiv-text="${this.brand.brandingTitle || &quot;authentik&quot;}"/> as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source> <source>External applications that use <x id="0" equiv-text="${this.brand.brandingTitle || &quot;authentik&quot;}"/> as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
<target>Applicazioni esterne che usano <x id="0" equiv-text="${this.brand.brandingTitle || &quot;authentik&quot;}"/> come identity provider tramite protocolli come OAuth2 e SAML. Sono mostrate tutte le applicazioni, anche quelle alle quali non hai accesso.</target>
</trans-unit> </trans-unit>
<trans-unit id="s58bec0ecd4f3ccd4"> <trans-unit id="s58bec0ecd4f3ccd4">
<source>Strict</source> <source>Strict</source>
</trans-unit> </trans-unit>
<trans-unit id="sd27a8f54aca5d278"> <trans-unit id="sd27a8f54aca5d278">
<source>Regex</source> <source>Regex</source>
<target>Regex</target>
</trans-unit> </trans-unit>
<trans-unit id="seb411b22a8f262d6"> <trans-unit id="seb411b22a8f262d6">
<source>Valid redirect URIs after a successful authorization flow. Also specify any origins here for Implicit flows.</source> <source>Valid redirect URIs after a successful authorization flow. Also specify any origins here for Implicit flows.</source>
@ -9044,6 +9057,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sc9e494c8346b7cb5"> <trans-unit id="sc9e494c8346b7cb5">
<source>Other</source> <source>Other</source>
<target>Altro</target>
</trans-unit> </trans-unit>
<trans-unit id="sbf6c78047e8ec8f8"> <trans-unit id="sbf6c78047e8ec8f8">
<source>Other type of kadmin</source> <source>Other type of kadmin</source>
@ -9059,21 +9073,26 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="s1cc032bcc50b2942"> <trans-unit id="s1cc032bcc50b2942">
<source>Available Policies</source> <source>Available Policies</source>
<target>Policy disponibili</target>
</trans-unit> </trans-unit>
<trans-unit id="s3ad64193ad5f4a5e"> <trans-unit id="s3ad64193ad5f4a5e">
<source>Selected Policies</source> <source>Selected Policies</source>
<target>Criteri selezionati</target>
</trans-unit> </trans-unit>
<trans-unit id="sc487e11d5987dbb4"> <trans-unit id="sc487e11d5987dbb4">
<source>Redirect the user to another flow, potentially with all gathered context</source> <source>Redirect the user to another flow, potentially with all gathered context</source>
</trans-unit> </trans-unit>
<trans-unit id="sad9d5481474d4f5b"> <trans-unit id="sad9d5481474d4f5b">
<source>Static</source> <source>Static</source>
<target>Statico</target>
</trans-unit> </trans-unit>
<trans-unit id="se87a96950464bc89"> <trans-unit id="se87a96950464bc89">
<source>Target URL</source> <source>Target URL</source>
<target>URL di destinazione</target>
</trans-unit> </trans-unit>
<trans-unit id="s7f3097955b19736a"> <trans-unit id="s7f3097955b19736a">
<source>Redirect the user to a static URL.</source> <source>Redirect the user to a static URL.</source>
<target>Reindirizzare l'utente a un URL statico.</target>
</trans-unit> </trans-unit>
<trans-unit id="s9bdee1c5130c8240"> <trans-unit id="s9bdee1c5130c8240">
<source>Target Flow</source> <source>Target Flow</source>
@ -9089,6 +9108,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="s66f533986ba6182c"> <trans-unit id="s66f533986ba6182c">
<source>Require superuser</source> <source>Require superuser</source>
<target>Richiede super-utente</target>
</trans-unit> </trans-unit>
<trans-unit id="s26c0a8789930b5fd"> <trans-unit id="s26c0a8789930b5fd">
<source>Require being redirected from another flow</source> <source>Require being redirected from another flow</source>
@ -9098,15 +9118,18 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sb80507bed88545a6"> <trans-unit id="sb80507bed88545a6">
<source>An application name is required</source> <source>An application name is required</source>
<target>È richiesto un nome dell'applicazione</target>
</trans-unit> </trans-unit>
<trans-unit id="s39c052006b2b141a"> <trans-unit id="s39c052006b2b141a">
<source>Not a valid URL</source> <source>Not a valid URL</source>
<target>Non è un URL valido</target>
</trans-unit> </trans-unit>
<trans-unit id="s317f3ab0da3d068a"> <trans-unit id="s317f3ab0da3d068a">
<source>Not a valid slug</source> <source>Not a valid slug</source>
</trans-unit> </trans-unit>
<trans-unit id="s177577c23ed3ca7e"> <trans-unit id="s177577c23ed3ca7e">
<source>Configure The Application</source> <source>Configure The Application</source>
<target>Configurare l'applicazione</target>
</trans-unit> </trans-unit>
<trans-unit id="s030ed8a3f12c23ff"> <trans-unit id="s030ed8a3f12c23ff">
<source>Configure Bindings</source> <source>Configure Bindings</source>
@ -9128,21 +9151,26 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="s54a37223ce938349"> <trans-unit id="s54a37223ce938349">
<source>Save Binding</source> <source>Save Binding</source>
<target>Salva associazione</target>
</trans-unit> </trans-unit>
<trans-unit id="s931754aabd4a6a47"> <trans-unit id="s931754aabd4a6a47">
<source>Create a Policy/User/Group Binding</source> <source>Create a Policy/User/Group Binding</source>
</trans-unit> </trans-unit>
<trans-unit id="s10ba15343fe64890"> <trans-unit id="s10ba15343fe64890">
<source>Choose A Provider</source> <source>Choose A Provider</source>
<target>Scegli un provider</target>
</trans-unit> </trans-unit>
<trans-unit id="sb7be945aa799b8d7"> <trans-unit id="sb7be945aa799b8d7">
<source>Please choose a provider type before proceeding.</source> <source>Please choose a provider type before proceeding.</source>
<target>Selezionare un tipo di provider prima di procedere.</target>
</trans-unit> </trans-unit>
<trans-unit id="s2289ff3b411251d8"> <trans-unit id="s2289ff3b411251d8">
<source>Choose a Provider Type</source> <source>Choose a Provider Type</source>
<target>Scegli un tipo di provider</target>
</trans-unit> </trans-unit>
<trans-unit id="sde0ad51b14f77cf6"> <trans-unit id="sde0ad51b14f77cf6">
<source>Redirect URIs/Origins (RegEx)</source> <source>Redirect URIs/Origins (RegEx)</source>
<target>Reindirizzamento URI/Origini (RegEx)</target>
</trans-unit> </trans-unit>
<trans-unit id="s89ae85c3a9df8a6f"> <trans-unit id="s89ae85c3a9df8a6f">
<source>Configure OAuth2 Provider</source> <source>Configure OAuth2 Provider</source>
@ -9161,15 +9189,18 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="s5e57ca23b946c1b8"> <trans-unit id="s5e57ca23b946c1b8">
<source>regexp</source> <source>regexp</source>
<target>regexp</target>
</trans-unit> </trans-unit>
<trans-unit id="sd226dc54c414585a"> <trans-unit id="sd226dc54c414585a">
<source>Review and Submit Application</source> <source>Review and Submit Application</source>
</trans-unit> </trans-unit>
<trans-unit id="s828ded9ade500ffc"> <trans-unit id="s828ded9ade500ffc">
<source>There was an error. Please go back and review the application.</source> <source>There was an error. Please go back and review the application.</source>
<target>Si è verificato un errore. Torna indietro e rivedi l'applicazione.</target>
</trans-unit> </trans-unit>
<trans-unit id="s9fdda682893624a1"> <trans-unit id="s9fdda682893624a1">
<source>There was an error:</source> <source>There was an error:</source>
<target>Si è verificato un errore:</target>
</trans-unit> </trans-unit>
<trans-unit id="s201ee6bc6d9f97da"> <trans-unit id="s201ee6bc6d9f97da">
<source>Please go back and review the application.</source> <source>Please go back and review the application.</source>
@ -9179,15 +9210,19 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="s0a0a21f7bcf9d865"> <trans-unit id="s0a0a21f7bcf9d865">
<source>Saving application...</source> <source>Saving application...</source>
<target>Salvataggio dell'applicazione...</target>
</trans-unit> </trans-unit>
<trans-unit id="se669a4b939be0891"> <trans-unit id="se669a4b939be0891">
<source>authentik was unable to complete this process.</source> <source>authentik was unable to complete this process.</source>
<target>authentik non è riuscito a completare questo processo.</target>
</trans-unit> </trans-unit>
<trans-unit id="s8aac845c63f45ca2"> <trans-unit id="s8aac845c63f45ca2">
<source>Create with wizard</source> <source>Create with wizard</source>
<target>Crea con la procedura guidata</target>
</trans-unit> </trans-unit>
<trans-unit id="s080af72866b69b3d"> <trans-unit id="s080af72866b69b3d">
<source>Bind existing <x id="0" equiv-text="${this.allowedTypesLabel}"/></source> <source>Bind existing <x id="0" equiv-text="${this.allowedTypesLabel}"/></source>
<target>Associa esistente <x id="0" equiv-text="${this.allowedTypesLabel}"/></target>
</trans-unit> </trans-unit>
<trans-unit id="sa36117e97f49c86e"> <trans-unit id="sa36117e97f49c86e">
<source>Successfully updated entitlement.</source> <source>Successfully updated entitlement.</source>
@ -9197,9 +9232,11 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="s8a48455905fc14e6"> <trans-unit id="s8a48455905fc14e6">
<source>Application entitlement(s)</source> <source>Application entitlement(s)</source>
<target>Privilegio(i) applicazione</target>
</trans-unit> </trans-unit>
<trans-unit id="sae971cbff672090b"> <trans-unit id="sae971cbff672090b">
<source>Update Entitlement</source> <source>Update Entitlement</source>
<target>Aggiorna Privilegio</target>
</trans-unit> </trans-unit>
<trans-unit id="s537a58ae0ff50f1c"> <trans-unit id="s537a58ae0ff50f1c">
<source>These bindings control which users have access to this entitlement.</source> <source>These bindings control which users have access to this entitlement.</source>
@ -9212,15 +9249,19 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sf0bd204ce3fea1de"> <trans-unit id="sf0bd204ce3fea1de">
<source>Create Entitlement</source> <source>Create Entitlement</source>
<target>Crea Privilegio</target>
</trans-unit> </trans-unit>
<trans-unit id="s55d8e42d0a1c057e"> <trans-unit id="s55d8e42d0a1c057e">
<source>Create entitlement</source> <source>Create entitlement</source>
<target>Crea privilegio</target>
</trans-unit> </trans-unit>
<trans-unit id="sc6479479c936939f"> <trans-unit id="sc6479479c936939f">
<source>Application entitlements</source> <source>Application entitlements</source>
<target>Privilegi applicazione</target>
</trans-unit> </trans-unit>
<trans-unit id="sfafbd9bddb38d172"> <trans-unit id="sfafbd9bddb38d172">
<source>Application entitlements are in preview.</source> <source>Application entitlements are in preview.</source>
<target>Privilegi applicazione sono in anteprima</target>
</trans-unit> </trans-unit>
<trans-unit id="s8e1c375a007d1839"> <trans-unit id="s8e1c375a007d1839">
<source>These entitlements can be used to configure user access in this application.</source> <source>These entitlements can be used to configure user access in this application.</source>

View File

@ -583,7 +583,7 @@
</trans-unit> </trans-unit>
<trans-unit id="saa0e2675da69651b"> <trans-unit id="saa0e2675da69651b">
<source>The URL &quot;<x id="0" equiv-text="${this.url}"/>&quot; was not found.</source> <source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
</trans-unit> </trans-unit>
<trans-unit id="s58cd9c2fe836d9c6"> <trans-unit id="s58cd9c2fe836d9c6">
@ -1684,8 +1684,8 @@
</trans-unit> </trans-unit>
<trans-unit id="sa90b7809586c35ce"> <trans-unit id="sa90b7809586c35ce">
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon &quot;fa-test&quot;.</source> <source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
<target>전체 URL, 상대 경로를 입력하거나, 또는 'fa://fa-test'를 사용하여 Font Awesome 아이콘 &quot;fa-test&quot;를 사용합니다.</target> <target>전체 URL, 상대 경로를 입력하거나, 또는 'fa://fa-test'를 사용하여 Font Awesome 아이콘 "fa-test"를 사용합니다.</target>
</trans-unit> </trans-unit>
<trans-unit id="s0410779cb47de312"> <trans-unit id="s0410779cb47de312">
@ -2826,7 +2826,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit> </trans-unit>
<trans-unit id="s76768bebabb7d543"> <trans-unit id="s76768bebabb7d543">
<source>Field which contains members of a group. Note that if using the &quot;memberUid&quot; field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source> <source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source>
<target>그룹 구성원이 포함된 필드입니다. 'memberUid' 필드를 사용하는 경우 값에 상대적인 고유 이름이 포함된 것으로 가정합니다 (예:'memberUid=some-user' 대신 'memberUid=cn=some-user,ou=groups,...').</target> <target>그룹 구성원이 포함된 필드입니다. 'memberUid' 필드를 사용하는 경우 값에 상대적인 고유 이름이 포함된 것으로 가정합니다 (예:'memberUid=some-user' 대신 'memberUid=cn=some-user,ou=groups,...').</target>
</trans-unit> </trans-unit>
@ -3737,7 +3737,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit> </trans-unit>
<trans-unit id="sa95a538bfbb86111"> <trans-unit id="sa95a538bfbb86111">
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> &quot;<x id="1" equiv-text="${this.obj?.name}"/>&quot;?</source> <source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
</trans-unit> </trans-unit>
<trans-unit id="sc92d7cfb6ee1fec6"> <trans-unit id="sc92d7cfb6ee1fec6">
@ -4802,8 +4802,8 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit> </trans-unit>
<trans-unit id="sdf1d8edef27236f0"> <trans-unit id="sdf1d8edef27236f0">
<source>A &quot;roaming&quot; authenticator, like a YubiKey</source> <source>A "roaming" authenticator, like a YubiKey</source>
<target>YubiKey 같은 &quot;로밍&quot; 인증기</target> <target>YubiKey 같은 "로밍" 인증기</target>
</trans-unit> </trans-unit>
<trans-unit id="sfffba7b23d8fb40c"> <trans-unit id="sfffba7b23d8fb40c">
@ -5171,7 +5171,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit> </trans-unit>
<trans-unit id="s1608b2f94fa0dbd4"> <trans-unit id="s1608b2f94fa0dbd4">
<source>If set to a duration above 0, the user will have the option to choose to &quot;stay signed in&quot;, which will extend their session by the time specified here.</source> <source>If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here.</source>
<target>기간을 0 이상으로 설정하면, 사용자에게 '로그인 상태 유지'를 선택할 수 있는 옵션이 제공되며, 이 경우 세션이 여기에 지정된 시간만큼 연장됩니다.</target> <target>기간을 0 이상으로 설정하면, 사용자에게 '로그인 상태 유지'를 선택할 수 있는 옵션이 제공되며, 이 경우 세션이 여기에 지정된 시간만큼 연장됩니다.</target>
</trans-unit> </trans-unit>
@ -7444,8 +7444,8 @@ Bindings to groups/users are checked against the user of the event.</source>
<target>사용자 생성과 <x id="0" equiv-text="${this.group.name}"/> 그룹 추가에 성공했습니다.</target> <target>사용자 생성과 <x id="0" equiv-text="${this.group.name}"/> 그룹 추가에 성공했습니다.</target>
</trans-unit> </trans-unit>
<trans-unit id="s824e0943a7104668"> <trans-unit id="s824e0943a7104668">
<source>This user will be added to the group &quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&quot;.</source> <source>This user will be added to the group "<x id="0" equiv-text="${this.targetGroup.name}"/>".</source>
<target>이 사용자는 &quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&quot; 그룹에 추가됩니다.</target> <target>이 사용자는 "<x id="0" equiv-text="${this.targetGroup.name}"/>" 그룹에 추가됩니다.</target>
</trans-unit> </trans-unit>
<trans-unit id="s62e7f6ed7d9cb3ca"> <trans-unit id="s62e7f6ed7d9cb3ca">
<source>Pretend user exists</source> <source>Pretend user exists</source>
@ -8426,7 +8426,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Sync Group</source> <source>Sync Group</source>
</trans-unit> </trans-unit>
<trans-unit id="s2d5f69929bb7221d"> <trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${p.name}"/> (&quot;<x id="1" equiv-text="${p.fieldKey}"/>&quot;, of type <x id="2" equiv-text="${p.type}"/>)</source> <source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
</trans-unit> </trans-unit>
<trans-unit id="s25bacc19d98b444e"> <trans-unit id="s25bacc19d98b444e">
<source>Parent Group</source> <source>Parent Group</source>
@ -8612,7 +8612,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Valid redirect URIs after a successful authorization flow. Also specify any origins here for Implicit flows.</source> <source>Valid redirect URIs after a successful authorization flow. Also specify any origins here for Implicit flows.</source>
</trans-unit> </trans-unit>
<trans-unit id="s4c49d27de60a532b"> <trans-unit id="s4c49d27de60a532b">
<source>To allow any redirect URI, set the mode to Regex and the value to &quot;.*&quot;. Be aware of the possible security implications this can have.</source> <source>To allow any redirect URI, set the mode to Regex and the value to ".*". Be aware of the possible security implications this can have.</source>
</trans-unit> </trans-unit>
<trans-unit id="sa52bf79fe1ccb13e"> <trans-unit id="sa52bf79fe1ccb13e">
<source>Federated OIDC Sources</source> <source>Federated OIDC Sources</source>

View File

@ -591,7 +591,7 @@
</trans-unit> </trans-unit>
<trans-unit id="saa0e2675da69651b"> <trans-unit id="saa0e2675da69651b">
<source>The URL &quot;<x id="0" equiv-text="${this.url}"/>&quot; was not found.</source> <source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
</trans-unit> </trans-unit>
<trans-unit id="s58cd9c2fe836d9c6"> <trans-unit id="s58cd9c2fe836d9c6">
@ -1695,8 +1695,8 @@
</trans-unit> </trans-unit>
<trans-unit id="sa90b7809586c35ce"> <trans-unit id="sa90b7809586c35ce">
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon &quot;fa-test&quot;.</source> <source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
<target>Voer een volledige URL, een relatief pad in, of gebruik 'fa://fa-test' om het Font Awesome-pictogram &quot;fa-test&quot; te gebruiken.</target> <target>Voer een volledige URL, een relatief pad in, of gebruik 'fa://fa-test' om het Font Awesome-pictogram "fa-test" te gebruiken.</target>
</trans-unit> </trans-unit>
<trans-unit id="s0410779cb47de312"> <trans-unit id="s0410779cb47de312">
@ -2835,12 +2835,12 @@ slaagt niet wanneer een of beide geselecteerde opties gelijk zijn aan of boven d
</trans-unit> </trans-unit>
<trans-unit id="sf325a4adba4d6278"> <trans-unit id="sf325a4adba4d6278">
<source>Group membership field</source> <source>Group membership field</source>
<target>Veld dat leden van een groep bevat. Let op dat als het veld &quot;memberUid&quot; wordt gebruikt, de waarde wordt verondersteld een relatieve Distinguished Name te bevatten. Bijv. 'memberUid=some-user' in plaats van 'memberUid=cn=some-user,ou=groups,...'</target> <target>Veld dat leden van een groep bevat. Let op dat als het veld "memberUid" wordt gebruikt, de waarde wordt verondersteld een relatieve Distinguished Name te bevatten. Bijv. 'memberUid=some-user' in plaats van 'memberUid=cn=some-user,ou=groups,...'</target>
</trans-unit> </trans-unit>
<trans-unit id="s76768bebabb7d543"> <trans-unit id="s76768bebabb7d543">
<source>Field which contains members of a group. Note that if using the &quot;memberUid&quot; field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source> <source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source>
<target>Veld dat leden van een groep bevat. Let op dat als het veld &quot;memberUid&quot; wordt gebruikt, de waarde wordt verondersteld een relatieve Distinguished Name te bevatten. Bijv. 'memberUid=some-user' in plaats van 'memberUid=cn=some-user,ou=groups,...'</target> <target>Veld dat leden van een groep bevat. Let op dat als het veld "memberUid" wordt gebruikt, de waarde wordt verondersteld een relatieve Distinguished Name te bevatten. Bijv. 'memberUid=some-user' in plaats van 'memberUid=cn=some-user,ou=groups,...'</target>
</trans-unit> </trans-unit>
<trans-unit id="s026555347e589f0e"> <trans-unit id="s026555347e589f0e">
@ -3749,7 +3749,7 @@ slaagt niet wanneer een of beide geselecteerde opties gelijk zijn aan of boven d
</trans-unit> </trans-unit>
<trans-unit id="sa95a538bfbb86111"> <trans-unit id="sa95a538bfbb86111">
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> &quot;<x id="1" equiv-text="${this.obj?.name}"/>&quot;?</source> <source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
</trans-unit> </trans-unit>
<trans-unit id="sc92d7cfb6ee1fec6"> <trans-unit id="sc92d7cfb6ee1fec6">
@ -4812,8 +4812,8 @@ slaagt niet wanneer een of beide geselecteerde opties gelijk zijn aan of boven d
</trans-unit> </trans-unit>
<trans-unit id="sdf1d8edef27236f0"> <trans-unit id="sdf1d8edef27236f0">
<source>A &quot;roaming&quot; authenticator, like a YubiKey</source> <source>A "roaming" authenticator, like a YubiKey</source>
<target>Een &quot;roaming&quot; authenticator, zoals een YubiKey</target> <target>Een "roaming" authenticator, zoals een YubiKey</target>
</trans-unit> </trans-unit>
<trans-unit id="sfffba7b23d8fb40c"> <trans-unit id="sfffba7b23d8fb40c">
@ -4888,7 +4888,7 @@ slaagt niet wanneer een of beide geselecteerde opties gelijk zijn aan of boven d
</trans-unit> </trans-unit>
<trans-unit id="s22b10ed263b96194"> <trans-unit id="s22b10ed263b96194">
<source>Dummy stage used for testing. Shows a simple continue button and always passes.</source> <source>Dummy stage used for testing. Shows a simple continue button and always passes.</source>
<target>Dummyfase voor testdoeleinden. Toont een eenvoudige knop &quot;Doorgaan&quot; en slaagt altijd.</target> <target>Dummyfase voor testdoeleinden. Toont een eenvoudige knop "Doorgaan" en slaagt altijd.</target>
</trans-unit> </trans-unit>
<trans-unit id="sdb861d9906f18ac2"> <trans-unit id="sdb861d9906f18ac2">
@ -5181,8 +5181,8 @@ slaagt niet wanneer een of beide geselecteerde opties gelijk zijn aan of boven d
</trans-unit> </trans-unit>
<trans-unit id="s1608b2f94fa0dbd4"> <trans-unit id="s1608b2f94fa0dbd4">
<source>If set to a duration above 0, the user will have the option to choose to &quot;stay signed in&quot;, which will extend their session by the time specified here.</source> <source>If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here.</source>
<target>Als ingesteld op een duur boven 0, heeft de gebruiker de mogelijkheid om te kiezen voor &quot;ingelogd blijven&quot;, wat hun sessie zal verlengen met de hier opgegeven tijd.</target> <target>Als ingesteld op een duur boven 0, heeft de gebruiker de mogelijkheid om te kiezen voor "ingelogd blijven", wat hun sessie zal verlengen met de hier opgegeven tijd.</target>
</trans-unit> </trans-unit>
<trans-unit id="s542a71bb8f41e057"> <trans-unit id="s542a71bb8f41e057">
@ -7359,7 +7359,7 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
<source>Successfully created user and added to group <x id="0" equiv-text="${this.group.name}"/></source> <source>Successfully created user and added to group <x id="0" equiv-text="${this.group.name}"/></source>
</trans-unit> </trans-unit>
<trans-unit id="s824e0943a7104668"> <trans-unit id="s824e0943a7104668">
<source>This user will be added to the group &quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&quot;.</source> <source>This user will be added to the group "<x id="0" equiv-text="${this.targetGroup.name}"/>".</source>
</trans-unit> </trans-unit>
<trans-unit id="s62e7f6ed7d9cb3ca"> <trans-unit id="s62e7f6ed7d9cb3ca">
<source>Pretend user exists</source> <source>Pretend user exists</source>
@ -8327,7 +8327,7 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
<source>Sync Group</source> <source>Sync Group</source>
</trans-unit> </trans-unit>
<trans-unit id="s2d5f69929bb7221d"> <trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${p.name}"/> (&quot;<x id="1" equiv-text="${p.fieldKey}"/>&quot;, of type <x id="2" equiv-text="${p.type}"/>)</source> <source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
</trans-unit> </trans-unit>
<trans-unit id="s25bacc19d98b444e"> <trans-unit id="s25bacc19d98b444e">
<source>Parent Group</source> <source>Parent Group</source>
@ -8513,7 +8513,7 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
<source>Valid redirect URIs after a successful authorization flow. Also specify any origins here for Implicit flows.</source> <source>Valid redirect URIs after a successful authorization flow. Also specify any origins here for Implicit flows.</source>
</trans-unit> </trans-unit>
<trans-unit id="s4c49d27de60a532b"> <trans-unit id="s4c49d27de60a532b">
<source>To allow any redirect URI, set the mode to Regex and the value to &quot;.*&quot;. Be aware of the possible security implications this can have.</source> <source>To allow any redirect URI, set the mode to Regex and the value to ".*". Be aware of the possible security implications this can have.</source>
</trans-unit> </trans-unit>
<trans-unit id="sa52bf79fe1ccb13e"> <trans-unit id="sa52bf79fe1ccb13e">
<source>Federated OIDC Sources</source> <source>Federated OIDC Sources</source>

View File

@ -596,9 +596,9 @@
</trans-unit> </trans-unit>
<trans-unit id="saa0e2675da69651b"> <trans-unit id="saa0e2675da69651b">
<source>The URL &quot;<x id="0" equiv-text="${this.url}"/>&quot; was not found.</source> <source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
<target>Nie znaleziono &quot; <target>Nie znaleziono "
<x id="0" equiv-text="${this.url}"/>&quot; adresu URL.</target> <x id="0" equiv-text="${this.url}"/>" adresu URL.</target>
</trans-unit> </trans-unit>
<trans-unit id="s58cd9c2fe836d9c6"> <trans-unit id="s58cd9c2fe836d9c6">
@ -1715,7 +1715,7 @@
</trans-unit> </trans-unit>
<trans-unit id="sa90b7809586c35ce"> <trans-unit id="sa90b7809586c35ce">
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon &quot;fa-test&quot;.</source> <source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
<target>Wprowadź pełny adres URL, ścieżkę względną lub użyj „fa://fa-test”, aby użyć ikony Font Awesome „fa-test”.</target> <target>Wprowadź pełny adres URL, ścieżkę względną lub użyj „fa://fa-test”, aby użyć ikony Font Awesome „fa-test”.</target>
</trans-unit> </trans-unit>
@ -2865,7 +2865,7 @@ nie przechodzi, gdy jedna lub obie wybrane opcje są równe lub wyższe od progu
</trans-unit> </trans-unit>
<trans-unit id="s76768bebabb7d543"> <trans-unit id="s76768bebabb7d543">
<source>Field which contains members of a group. Note that if using the &quot;memberUid&quot; field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source> <source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source>
<target>Pole zawierające członków grupy. Należy zauważyć, że w przypadku korzystania z pola „memberUid” zakłada się, że wartość zawiera względną nazwę wyróżniającą. np. 'memberUid=jakiś-użytkownik' zamiast 'memberUid=cn=jakiś-użytkownik,ou=grupy,...'</target> <target>Pole zawierające członków grupy. Należy zauważyć, że w przypadku korzystania z pola „memberUid” zakłada się, że wartość zawiera względną nazwę wyróżniającą. np. 'memberUid=jakiś-użytkownik' zamiast 'memberUid=cn=jakiś-użytkownik,ou=grupy,...'</target>
</trans-unit> </trans-unit>
@ -3784,9 +3784,9 @@ Można tu używać tylko zasad, ponieważ dostęp jest sprawdzany przed uwierzyt
</trans-unit> </trans-unit>
<trans-unit id="sa95a538bfbb86111"> <trans-unit id="sa95a538bfbb86111">
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> &quot;<x id="1" equiv-text="${this.obj?.name}"/>&quot;?</source> <source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
<target>Czy na pewno chcesz zaktualizować <target>Czy na pewno chcesz zaktualizować
<x id="0" equiv-text="${this.objectLabel}"/>&quot; <x id="0" equiv-text="${this.objectLabel}"/>"
<x id="1" equiv-text="${this.obj?.name}"/>”?</target> <x id="1" equiv-text="${this.obj?.name}"/>”?</target>
</trans-unit> </trans-unit>
@ -4858,7 +4858,7 @@ Można tu używać tylko zasad, ponieważ dostęp jest sprawdzany przed uwierzyt
</trans-unit> </trans-unit>
<trans-unit id="sdf1d8edef27236f0"> <trans-unit id="sdf1d8edef27236f0">
<source>A &quot;roaming&quot; authenticator, like a YubiKey</source> <source>A "roaming" authenticator, like a YubiKey</source>
<target>„Mobilne” uwierzytelniacz, taki jak YubiKey</target> <target>„Mobilne” uwierzytelniacz, taki jak YubiKey</target>
</trans-unit> </trans-unit>
@ -5227,7 +5227,7 @@ Można tu używać tylko zasad, ponieważ dostęp jest sprawdzany przed uwierzyt
</trans-unit> </trans-unit>
<trans-unit id="s1608b2f94fa0dbd4"> <trans-unit id="s1608b2f94fa0dbd4">
<source>If set to a duration above 0, the user will have the option to choose to &quot;stay signed in&quot;, which will extend their session by the time specified here.</source> <source>If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here.</source>
<target>W przypadku ustawienia czasu trwania powyżej 0, użytkownik będzie mógł wybrać opcję „pozostania zalogowanym”, co spowoduje przedłużenie jego sesji o określony tutaj czas.</target> <target>W przypadku ustawienia czasu trwania powyżej 0, użytkownik będzie mógł wybrać opcję „pozostania zalogowanym”, co spowoduje przedłużenie jego sesji o określony tutaj czas.</target>
</trans-unit> </trans-unit>
@ -7522,7 +7522,7 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
<target>Pomyślnie utworzono użytkownika i dodano go do grupy <x id="0" equiv-text="${this.group.name}"/></target> <target>Pomyślnie utworzono użytkownika i dodano go do grupy <x id="0" equiv-text="${this.group.name}"/></target>
</trans-unit> </trans-unit>
<trans-unit id="s824e0943a7104668"> <trans-unit id="s824e0943a7104668">
<source>This user will be added to the group &quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&quot;.</source> <source>This user will be added to the group "<x id="0" equiv-text="${this.targetGroup.name}"/>".</source>
<target>Ten użytkownik zostanie dodany do grupy &amp;quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&amp;quot;.</target> <target>Ten użytkownik zostanie dodany do grupy &amp;quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&amp;quot;.</target>
</trans-unit> </trans-unit>
<trans-unit id="s62e7f6ed7d9cb3ca"> <trans-unit id="s62e7f6ed7d9cb3ca">
@ -8650,7 +8650,7 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
</trans-unit> </trans-unit>
<trans-unit id="s9dda0789d278f5c5"> <trans-unit id="s9dda0789d278f5c5">
<source>Provide users with a 'show password' button.</source> <source>Provide users with a 'show password' button.</source>
<target>Udostępnij użytkownikom przycisk &quot;pokaż hasło&quot;.</target> <target>Udostępnij użytkownikom przycisk "pokaż hasło".</target>
</trans-unit> </trans-unit>
<trans-unit id="s2f7f35f6a5b733f5"> <trans-unit id="s2f7f35f6a5b733f5">
<source>Show password</source> <source>Show password</source>
@ -8756,7 +8756,7 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
<source>Sync Group</source> <source>Sync Group</source>
</trans-unit> </trans-unit>
<trans-unit id="s2d5f69929bb7221d"> <trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${p.name}"/> (&quot;<x id="1" equiv-text="${p.fieldKey}"/>&quot;, of type <x id="2" equiv-text="${p.type}"/>)</source> <source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
</trans-unit> </trans-unit>
<trans-unit id="s25bacc19d98b444e"> <trans-unit id="s25bacc19d98b444e">
<source>Parent Group</source> <source>Parent Group</source>
@ -8942,7 +8942,7 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
<source>Valid redirect URIs after a successful authorization flow. Also specify any origins here for Implicit flows.</source> <source>Valid redirect URIs after a successful authorization flow. Also specify any origins here for Implicit flows.</source>
</trans-unit> </trans-unit>
<trans-unit id="s4c49d27de60a532b"> <trans-unit id="s4c49d27de60a532b">
<source>To allow any redirect URI, set the mode to Regex and the value to &quot;.*&quot;. Be aware of the possible security implications this can have.</source> <source>To allow any redirect URI, set the mode to Regex and the value to ".*". Be aware of the possible security implications this can have.</source>
</trans-unit> </trans-unit>
<trans-unit id="sa52bf79fe1ccb13e"> <trans-unit id="sa52bf79fe1ccb13e">
<source>Federated OIDC Sources</source> <source>Federated OIDC Sources</source>

View File

@ -596,9 +596,9 @@
</trans-unit> </trans-unit>
<trans-unit id="saa0e2675da69651b"> <trans-unit id="saa0e2675da69651b">
<source>The URL &quot;<x id="0" equiv-text="${this.url}"/>&quot; was not found.</source> <source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
<target>URL &quot; <target>URL "
<x id="0" equiv-text="${this.url}"/>&quot; не найден.</target> <x id="0" equiv-text="${this.url}"/>" не найден.</target>
</trans-unit> </trans-unit>
<trans-unit id="s58cd9c2fe836d9c6"> <trans-unit id="s58cd9c2fe836d9c6">
@ -1715,8 +1715,8 @@
</trans-unit> </trans-unit>
<trans-unit id="sa90b7809586c35ce"> <trans-unit id="sa90b7809586c35ce">
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon &quot;fa-test&quot;.</source> <source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
<target>Введите полный URL-адрес, относительный путь или используйте 'fa://fa-test', чтобы использовать иконку Font Awesome &quot;fa-test&quot;.</target> <target>Введите полный URL-адрес, относительный путь или используйте 'fa://fa-test', чтобы использовать иконку Font Awesome "fa-test".</target>
</trans-unit> </trans-unit>
<trans-unit id="s0410779cb47de312"> <trans-unit id="s0410779cb47de312">
@ -2864,8 +2864,8 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit> </trans-unit>
<trans-unit id="s76768bebabb7d543"> <trans-unit id="s76768bebabb7d543">
<source>Field which contains members of a group. Note that if using the &quot;memberUid&quot; field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source> <source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source>
<target>Поле, содержащее членов группы. Обратите внимание, что при использовании поля &quot;memberUid&quot; предполагается, что значение содержит относительное отличительное имя. Например, 'memberUid=some-user' вместо 'memberUid=cn=some-user,ou=groups,...'.</target> <target>Поле, содержащее членов группы. Обратите внимание, что при использовании поля "memberUid" предполагается, что значение содержит относительное отличительное имя. Например, 'memberUid=some-user' вместо 'memberUid=cn=some-user,ou=groups,...'.</target>
</trans-unit> </trans-unit>
<trans-unit id="s026555347e589f0e"> <trans-unit id="s026555347e589f0e">
@ -3747,7 +3747,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit> </trans-unit>
<trans-unit id="s6b6e6eb037aef7da"> <trans-unit id="s6b6e6eb037aef7da">
<source>Use the username and password below to authenticate. The password can be retrieved later on the Tokens page.</source> <source>Use the username and password below to authenticate. The password can be retrieved later on the Tokens page.</source>
<target>Для аутентификации используйте указанные ниже имя пользователя и пароль. Пароль можно получить позже на странице &quot;Токены&quot;.</target> <target>Для аутентификации используйте указанные ниже имя пользователя и пароль. Пароль можно получить позже на странице "Токены".</target>
</trans-unit> </trans-unit>
<trans-unit id="sf6e1665c7022a1f8"> <trans-unit id="sf6e1665c7022a1f8">
@ -3783,10 +3783,10 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit> </trans-unit>
<trans-unit id="sa95a538bfbb86111"> <trans-unit id="sa95a538bfbb86111">
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> &quot;<x id="1" equiv-text="${this.obj?.name}"/>&quot;?</source> <source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
<target>Вы уверены, что хотите обновить <target>Вы уверены, что хотите обновить
<x id="0" equiv-text="${this.objectLabel}"/>&quot; <x id="0" equiv-text="${this.objectLabel}"/>"
<x id="1" equiv-text="${this.obj?.name}"/>&quot;?</target> <x id="1" equiv-text="${this.obj?.name}"/>"?</target>
</trans-unit> </trans-unit>
<trans-unit id="sc92d7cfb6ee1fec6"> <trans-unit id="sc92d7cfb6ee1fec6">
@ -4857,7 +4857,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit> </trans-unit>
<trans-unit id="sdf1d8edef27236f0"> <trans-unit id="sdf1d8edef27236f0">
<source>A &quot;roaming&quot; authenticator, like a YubiKey</source> <source>A "roaming" authenticator, like a YubiKey</source>
<target>Переносной аутентификатор, например YubiKey</target> <target>Переносной аутентификатор, например YubiKey</target>
</trans-unit> </trans-unit>
@ -5222,12 +5222,12 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit> </trans-unit>
<trans-unit id="s2512334108f06a5a"> <trans-unit id="s2512334108f06a5a">
<source>Stay signed in offset</source> <source>Stay signed in offset</source>
<target>Смещение &quot;Оставаться в системе&quot;</target> <target>Смещение "Оставаться в системе"</target>
</trans-unit> </trans-unit>
<trans-unit id="s1608b2f94fa0dbd4"> <trans-unit id="s1608b2f94fa0dbd4">
<source>If set to a duration above 0, the user will have the option to choose to &quot;stay signed in&quot;, which will extend their session by the time specified here.</source> <source>If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here.</source>
<target>Если значение продолжительности больше 0, у пользователя будет возможность выбрать опцию &quot;оставаться в системе&quot;, что продлит его сеанс на указанное здесь время.</target> <target>Если значение продолжительности больше 0, у пользователя будет возможность выбрать опцию "оставаться в системе", что продлит его сеанс на указанное здесь время.</target>
</trans-unit> </trans-unit>
<trans-unit id="s542a71bb8f41e057"> <trans-unit id="s542a71bb8f41e057">
@ -7402,7 +7402,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="s070fdfb03034ca9b"> <trans-unit id="s070fdfb03034ca9b">
<source>One hint, 'New Application Wizard', is currently hidden</source> <source>One hint, 'New Application Wizard', is currently hidden</source>
<target>Одна подсказка, &quot;Мастер создания нового приложения&quot;, в настоящее время скрыта</target> <target>Одна подсказка, "Мастер создания нового приложения", в настоящее время скрыта</target>
</trans-unit> </trans-unit>
<trans-unit id="s1cc306d8e28c4464"> <trans-unit id="s1cc306d8e28c4464">
<source>Deny message</source> <source>Deny message</source>
@ -7521,7 +7521,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<target>Пользователь успешно создан и добавлен в группу <x id="0" equiv-text="${this.group.name}"/></target> <target>Пользователь успешно создан и добавлен в группу <x id="0" equiv-text="${this.group.name}"/></target>
</trans-unit> </trans-unit>
<trans-unit id="s824e0943a7104668"> <trans-unit id="s824e0943a7104668">
<source>This user will be added to the group &quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&quot;.</source> <source>This user will be added to the group "<x id="0" equiv-text="${this.targetGroup.name}"/>".</source>
<target>Этот пользователь будет добавлен в группу &amp;quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&amp;quot;.</target> <target>Этот пользователь будет добавлен в группу &amp;quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&amp;quot;.</target>
</trans-unit> </trans-unit>
<trans-unit id="s62e7f6ed7d9cb3ca"> <trans-unit id="s62e7f6ed7d9cb3ca">
@ -8652,7 +8652,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sd8a5f7269b7ac7a3"> <trans-unit id="sd8a5f7269b7ac7a3">
<source>Caution: This authentik instance has entered read-only mode due to expired/exceeded licenses.</source> <source>Caution: This authentik instance has entered read-only mode due to expired/exceeded licenses.</source>
<target>Внимание: Этот экземпляр authentik перешел в режим &quot;только чтение&quot; из-за истекших/превышенных лицензий.</target> <target>Внимание: Этот экземпляр authentik перешел в режим "только чтение" из-за истекших/превышенных лицензий.</target>
</trans-unit> </trans-unit>
<trans-unit id="se8c58e65674b6847"> <trans-unit id="se8c58e65674b6847">
<source>This authentik instance uses a Trial license.</source> <source>This authentik instance uses a Trial license.</source>
@ -8680,7 +8680,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="s9dda0789d278f5c5"> <trans-unit id="s9dda0789d278f5c5">
<source>Provide users with a 'show password' button.</source> <source>Provide users with a 'show password' button.</source>
<target>Предоставить пользователям кнопку &quot;показать пароль&quot;.</target> <target>Предоставить пользователям кнопку "показать пароль".</target>
</trans-unit> </trans-unit>
<trans-unit id="s2f7f35f6a5b733f5"> <trans-unit id="s2f7f35f6a5b733f5">
<source>Show password</source> <source>Show password</source>
@ -8789,7 +8789,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Sync Group</source> <source>Sync Group</source>
</trans-unit> </trans-unit>
<trans-unit id="s2d5f69929bb7221d"> <trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${p.name}"/> (&quot;<x id="1" equiv-text="${p.fieldKey}"/>&quot;, of type <x id="2" equiv-text="${p.type}"/>)</source> <source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
</trans-unit> </trans-unit>
<trans-unit id="s25bacc19d98b444e"> <trans-unit id="s25bacc19d98b444e">
<source>Parent Group</source> <source>Parent Group</source>
@ -8975,7 +8975,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Valid redirect URIs after a successful authorization flow. Also specify any origins here for Implicit flows.</source> <source>Valid redirect URIs after a successful authorization flow. Also specify any origins here for Implicit flows.</source>
</trans-unit> </trans-unit>
<trans-unit id="s4c49d27de60a532b"> <trans-unit id="s4c49d27de60a532b">
<source>To allow any redirect URI, set the mode to Regex and the value to &quot;.*&quot;. Be aware of the possible security implications this can have.</source> <source>To allow any redirect URI, set the mode to Regex and the value to ".*". Be aware of the possible security implications this can have.</source>
</trans-unit> </trans-unit>
<trans-unit id="sa52bf79fe1ccb13e"> <trans-unit id="sa52bf79fe1ccb13e">
<source>Federated OIDC Sources</source> <source>Federated OIDC Sources</source>

View File

@ -6075,6 +6075,12 @@ Gruplara/kullanıcılara yapılan bağlamalar, etkinliğin kullanıcısına kar
<source>Managed by authentik (Discovered)</source> <source>Managed by authentik (Discovered)</source>
<target>Auentik tarafından yönetilen (Keşfedildi)</target> <target>Auentik tarafından yönetilen (Keşfedildi)</target>
</trans-unit>
<trans-unit id="sef50d248448e0df1">
<source>Yes (<x id="0" equiv-text="${item.privateKeyType?.toUpperCase()}"/>)</source>
<target>Evet (
<x id="0" equiv-text="${item.privateKeyType?.toUpperCase()}"/>)</target>
</trans-unit> </trans-unit>
<trans-unit id="s09205907b5b56cda"> <trans-unit id="s09205907b5b56cda">
<source>No</source> <source>No</source>
@ -8767,7 +8773,6 @@ Gruplara/kullanıcılara yapılan bağlamalar, etkinliğin kullanıcısına kar
</trans-unit> </trans-unit>
<trans-unit id="s2d5f69929bb7221d"> <trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source> <source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
<target><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</target>
</trans-unit> </trans-unit>
<trans-unit id="s25bacc19d98b444e"> <trans-unit id="s25bacc19d98b444e">
<source>Parent Group</source> <source>Parent Group</source>
@ -8957,9 +8962,6 @@ Gruplara/kullanıcılara yapılan bağlamalar, etkinliğin kullanıcısına kar
<source>Endpoint Google Chrome Device Trust is in preview.</source> <source>Endpoint Google Chrome Device Trust is in preview.</source>
<target>Uç nokta Google Chrome Cihaz Güvenilirliği önizleme aşamasındadır.</target> <target>Uç nokta Google Chrome Cihaz Güvenilirliği önizleme aşamasındadır.</target>
</trans-unit> </trans-unit>
<trans-unit id="sef50d248448e0df1">
<source>Yes (<x id="0" equiv-text="${item.privateKeyType?.toUpperCase()}"/>)</source>
</trans-unit>
<trans-unit id="s6f4f35a5a4b9b3cb"> <trans-unit id="s6f4f35a5a4b9b3cb">
<source>Interactive</source> <source>Interactive</source>
</trans-unit> </trans-unit>
@ -9130,6 +9132,7 @@ Gruplara/kullanıcılara yapılan bağlamalar, etkinliğin kullanıcısına kar
</trans-unit> </trans-unit>
<trans-unit id="sde0ad51b14f77cf6"> <trans-unit id="sde0ad51b14f77cf6">
<source>Redirect URIs/Origins (RegEx)</source> <source>Redirect URIs/Origins (RegEx)</source>
<target>Yeniden Yönlendirme URI'leri/Kökenleri (RegEx)</target>
</trans-unit> </trans-unit>
<trans-unit id="s89ae85c3a9df8a6f"> <trans-unit id="s89ae85c3a9df8a6f">
<source>Configure OAuth2 Provider</source> <source>Configure OAuth2 Provider</source>

View File

@ -596,9 +596,9 @@
</trans-unit> </trans-unit>
<trans-unit id="saa0e2675da69651b"> <trans-unit id="saa0e2675da69651b">
<source>The URL &quot;<x id="0" equiv-text="${this.url}"/>&quot; was not found.</source> <source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
<target>未找到 URL &quot; <target>未找到 URL "
<x id="0" equiv-text="${this.url}"/>&quot;。</target> <x id="0" equiv-text="${this.url}"/>"。</target>
</trans-unit> </trans-unit>
<trans-unit id="s58cd9c2fe836d9c6"> <trans-unit id="s58cd9c2fe836d9c6">
@ -1715,8 +1715,8 @@
</trans-unit> </trans-unit>
<trans-unit id="sa90b7809586c35ce"> <trans-unit id="sa90b7809586c35ce">
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon &quot;fa-test&quot;.</source> <source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
<target>输入完整 URL、相对路径或者使用 'fa://fa-test' 来使用 Font Awesome 图标 &quot;fa-test&quot;。</target> <target>输入完整 URL、相对路径或者使用 'fa://fa-test' 来使用 Font Awesome 图标 "fa-test"。</target>
</trans-unit> </trans-unit>
<trans-unit id="s0410779cb47de312"> <trans-unit id="s0410779cb47de312">
@ -2864,8 +2864,8 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit> </trans-unit>
<trans-unit id="s76768bebabb7d543"> <trans-unit id="s76768bebabb7d543">
<source>Field which contains members of a group. Note that if using the &quot;memberUid&quot; field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source> <source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source>
<target>包含组成员的字段。请注意,如果使用 &quot;memberUid&quot; 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...'</target> <target>包含组成员的字段。请注意,如果使用 "memberUid" 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...'</target>
</trans-unit> </trans-unit>
<trans-unit id="s026555347e589f0e"> <trans-unit id="s026555347e589f0e">
@ -3783,10 +3783,10 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit> </trans-unit>
<trans-unit id="sa95a538bfbb86111"> <trans-unit id="sa95a538bfbb86111">
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> &quot;<x id="1" equiv-text="${this.obj?.name}"/>&quot;?</source> <source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
<target>您确定要更新 <target>您确定要更新
<x id="0" equiv-text="${this.objectLabel}"/>&quot; <x id="0" equiv-text="${this.objectLabel}"/>"
<x id="1" equiv-text="${this.obj?.name}"/>&quot; 吗?</target> <x id="1" equiv-text="${this.obj?.name}"/>" 吗?</target>
</trans-unit> </trans-unit>
<trans-unit id="sc92d7cfb6ee1fec6"> <trans-unit id="sc92d7cfb6ee1fec6">
@ -4857,7 +4857,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit> </trans-unit>
<trans-unit id="sdf1d8edef27236f0"> <trans-unit id="sdf1d8edef27236f0">
<source>A &quot;roaming&quot; authenticator, like a YubiKey</source> <source>A "roaming" authenticator, like a YubiKey</source>
<target>像 YubiKey 这样的“漫游”身份验证器</target> <target>像 YubiKey 这样的“漫游”身份验证器</target>
</trans-unit> </trans-unit>
@ -5226,7 +5226,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit> </trans-unit>
<trans-unit id="s1608b2f94fa0dbd4"> <trans-unit id="s1608b2f94fa0dbd4">
<source>If set to a duration above 0, the user will have the option to choose to &quot;stay signed in&quot;, which will extend their session by the time specified here.</source> <source>If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here.</source>
<target>如果设置时长大于 0用户可以选择“保持登录”选项这将使用户的会话延长此处设置的时间。</target> <target>如果设置时长大于 0用户可以选择“保持登录”选项这将使用户的会话延长此处设置的时间。</target>
</trans-unit> </trans-unit>
@ -7521,7 +7521,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<target>成功创建用户并添加到组 <x id="0" equiv-text="${this.group.name}"/></target> <target>成功创建用户并添加到组 <x id="0" equiv-text="${this.group.name}"/></target>
</trans-unit> </trans-unit>
<trans-unit id="s824e0943a7104668"> <trans-unit id="s824e0943a7104668">
<source>This user will be added to the group &quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&quot;.</source> <source>This user will be added to the group "<x id="0" equiv-text="${this.targetGroup.name}"/>".</source>
<target>此用户将会被添加到组 &amp;quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&amp;quot;。</target> <target>此用户将会被添加到组 &amp;quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&amp;quot;。</target>
</trans-unit> </trans-unit>
<trans-unit id="s62e7f6ed7d9cb3ca"> <trans-unit id="s62e7f6ed7d9cb3ca">
@ -8815,7 +8815,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<target>同步组</target> <target>同步组</target>
</trans-unit> </trans-unit>
<trans-unit id="s2d5f69929bb7221d"> <trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${p.name}"/> (&quot;<x id="1" equiv-text="${p.fieldKey}"/>&quot;, of type <x id="2" equiv-text="${p.type}"/>)</source> <source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
<target><x id="0" equiv-text="${p.name}"/>&amp;quot;<x id="1" equiv-text="${p.fieldKey}"/>&amp;quot;,类型为 <x id="2" equiv-text="${p.type}"/></target> <target><x id="0" equiv-text="${p.name}"/>&amp;quot;<x id="1" equiv-text="${p.fieldKey}"/>&amp;quot;,类型为 <x id="2" equiv-text="${p.type}"/></target>
</trans-unit> </trans-unit>
<trans-unit id="s25bacc19d98b444e"> <trans-unit id="s25bacc19d98b444e">
@ -9063,8 +9063,8 @@ Bindings to groups/users are checked against the user of the event.</source>
<target>授权流程成功后有效的重定向 URI。还可以在此处为隐式流程指定任何来源。</target> <target>授权流程成功后有效的重定向 URI。还可以在此处为隐式流程指定任何来源。</target>
</trans-unit> </trans-unit>
<trans-unit id="s4c49d27de60a532b"> <trans-unit id="s4c49d27de60a532b">
<source>To allow any redirect URI, set the mode to Regex and the value to &quot;.*&quot;. Be aware of the possible security implications this can have.</source> <source>To allow any redirect URI, set the mode to Regex and the value to ".*". Be aware of the possible security implications this can have.</source>
<target>要允许任何重定向 URI请设置模式为正则表达式并将此值设置为 &quot;.*&quot;。请注意这可能带来的安全影响。</target> <target>要允许任何重定向 URI请设置模式为正则表达式并将此值设置为 ".*"。请注意这可能带来的安全影响。</target>
</trans-unit> </trans-unit>
<trans-unit id="sa52bf79fe1ccb13e"> <trans-unit id="sa52bf79fe1ccb13e">
<source>Federated OIDC Sources</source> <source>Federated OIDC Sources</source>
@ -9344,108 +9344,145 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="s82cced87e15e0665"> <trans-unit id="s82cced87e15e0665">
<source>Worker with incorrect version connected.</source> <source>Worker with incorrect version connected.</source>
<target>错误版本的 Worker 已连接。</target>
</trans-unit> </trans-unit>
<trans-unit id="ha8e91858b300a3b7"> <trans-unit id="ha8e91858b300a3b7">
<source>(Format: <x id="0" equiv-text="&lt;code&gt;"/>hours=-1;minutes=-2;seconds=-3)<x id="1" equiv-text="&lt;/code&gt;"/>.</source> <source>(Format: <x id="0" equiv-text="&lt;code&gt;"/>hours=-1;minutes=-2;seconds=-3)<x id="1" equiv-text="&lt;/code&gt;"/>.</source>
<target>(格式:<x id="0" equiv-text="&lt;code&gt;"/>hours=-1;minutes=-2;seconds=-3<x id="1" equiv-text="&lt;/code&gt;"/>。</target>
</trans-unit> </trans-unit>
<trans-unit id="hbe8e6353371ecb22"> <trans-unit id="hbe8e6353371ecb22">
<source>(Format: <x id="0" equiv-text="&lt;code&gt;"/>hours=1;minutes=2;seconds=3).<x id="1" equiv-text="&lt;/code&gt;"/></source> <source>(Format: <x id="0" equiv-text="&lt;code&gt;"/>hours=1;minutes=2;seconds=3).<x id="1" equiv-text="&lt;/code&gt;"/></source>
<target>(格式:<x id="0" equiv-text="&lt;code&gt;"/>hours=1;minutes=2;seconds=3。<x id="1" equiv-text="&lt;/code&gt;"/></target>
</trans-unit> </trans-unit>
<trans-unit id="se8dd47dfaf01d6c9"> <trans-unit id="se8dd47dfaf01d6c9">
<source>Key used to sign the events.</source> <source>Key used to sign the events.</source>
<target>用于签名事件的密钥。</target>
</trans-unit> </trans-unit>
<trans-unit id="sd1b31f8fba05d429"> <trans-unit id="sd1b31f8fba05d429">
<source>Event Retention</source> <source>Event Retention</source>
<target>事件保留</target>
</trans-unit> </trans-unit>
<trans-unit id="sd760b123cced4675"> <trans-unit id="sd760b123cced4675">
<source>Determines how long events are stored for. If an event could not be sent correctly, its expiration is also increased by this duration.</source> <source>Determines how long events are stored for. If an event could not be sent correctly, its expiration is also increased by this duration.</source>
<target>设置事件存储多久时间。如果无法成功发送事件,则此时长也会添加到事件的过期时间。</target>
</trans-unit> </trans-unit>
<trans-unit id="s1aa62e1bf258e88a"> <trans-unit id="s1aa62e1bf258e88a">
<source>OIDC Providers</source> <source>OIDC Providers</source>
<target>OIDC 提供程序</target>
</trans-unit> </trans-unit>
<trans-unit id="sb9d947872397ca07"> <trans-unit id="sb9d947872397ca07">
<source>SSF Provider is in preview.</source> <source>SSF Provider is in preview.</source>
<target>SSF 提供程序处于预览状态。</target>
</trans-unit> </trans-unit>
<trans-unit id="s78302d097a608b99"> <trans-unit id="s78302d097a608b99">
<source>Update SSF Provider</source> <source>Update SSF Provider</source>
<target>更新 SSF 提供程序</target>
</trans-unit> </trans-unit>
<trans-unit id="sa412f34efd88e962"> <trans-unit id="sa412f34efd88e962">
<source>Streams</source> <source>Streams</source>
<target>流</target>
</trans-unit> </trans-unit>
<trans-unit id="s91ae4b6bf981682b"> <trans-unit id="s91ae4b6bf981682b">
<source>authentik Logo</source> <source>authentik Logo</source>
<target>authentik 图标</target>
</trans-unit> </trans-unit>
<trans-unit id="s1cdeecda0baf365e"> <trans-unit id="s1cdeecda0baf365e">
<source>Release</source> <source>Release</source>
<target>发布版</target>
</trans-unit> </trans-unit>
<trans-unit id="s35429c9dc2319aea"> <trans-unit id="s35429c9dc2319aea">
<source>Development</source> <source>Development</source>
<target>开发版</target>
</trans-unit> </trans-unit>
<trans-unit id="s5937938934a777ab"> <trans-unit id="s5937938934a777ab">
<source>UI Version</source> <source>UI Version</source>
<target>界面版本</target>
</trans-unit> </trans-unit>
<trans-unit id="s3fe3ff490adaaa63"> <trans-unit id="s3fe3ff490adaaa63">
<source>Build</source> <source>Build</source>
<target>构建</target>
</trans-unit> </trans-unit>
<trans-unit id="s42aea5c6d3d1fce7"> <trans-unit id="s42aea5c6d3d1fce7">
<source>Python version</source> <source>Python version</source>
<target>Python 版本</target>
</trans-unit> </trans-unit>
<trans-unit id="s5c67b6c66029e5b2"> <trans-unit id="s5c67b6c66029e5b2">
<source>Platform</source> <source>Platform</source>
<target>平台</target>
</trans-unit> </trans-unit>
<trans-unit id="se8574bce64d9c1f8"> <trans-unit id="se8574bce64d9c1f8">
<source>Kernel</source> <source>Kernel</source>
<target>内核</target>
</trans-unit> </trans-unit>
<trans-unit id="s3fbe63b7fcfe3e91"> <trans-unit id="s3fbe63b7fcfe3e91">
<source>OpenSSL</source> <source>OpenSSL</source>
<target>OpenSSL</target>
</trans-unit> </trans-unit>
<trans-unit id="s1381314790b50b1f"> <trans-unit id="s1381314790b50b1f">
<source>A newer version (<x id="0" equiv-text="${this.version.versionCurrent}"/>) of the UI is available.</source> <source>A newer version (<x id="0" equiv-text="${this.version.versionCurrent}"/>) of the UI is available.</source>
<target>新版本界面(<x id="0" equiv-text="${this.version.versionCurrent}"/>)可用。</target>
</trans-unit> </trans-unit>
<trans-unit id="s61ffea061fae0af4"> <trans-unit id="s61ffea061fae0af4">
<source>No notifications found.</source> <source>No notifications found.</source>
<target>未找到通知。</target>
</trans-unit> </trans-unit>
<trans-unit id="scf8d5cdc8b434982"> <trans-unit id="scf8d5cdc8b434982">
<source>You don't have any notifications currently.</source> <source>You don't have any notifications currently.</source>
<target>您当前没有任何通知。</target>
</trans-unit> </trans-unit>
<trans-unit id="s358e08de4fbebf51"> <trans-unit id="s358e08de4fbebf51">
<source>Version <x id="0" equiv-text="${this.version?.versionCurrent || &quot;&quot;}"/></source> <source>Version <x id="0" equiv-text="${this.version?.versionCurrent || &quot;&quot;}"/></source>
<target>版本 <x id="0" equiv-text="${this.version?.versionCurrent || &quot;&quot;}"/></target>
</trans-unit> </trans-unit>
<trans-unit id="sfb95ad8c25db95ba"> <trans-unit id="sfb95ad8c25db95ba">
<source>Last password change</source> <source>Last password change</source>
<target>上次修改密码</target>
</trans-unit> </trans-unit>
<trans-unit id="s37e1bad4bf0e9241"> <trans-unit id="s37e1bad4bf0e9241">
<source>Evaluate policies before the Stage is presented to the user.</source> <source>Evaluate policies before the Stage is presented to the user.</source>
<target>在阶段即将呈现给用户时评估策略。</target>
</trans-unit> </trans-unit>
<trans-unit id="h8c1aa2d523f94f36"> <trans-unit id="h8c1aa2d523f94f36">
<source>Can be in the format of <x id="0" equiv-text="&lt;code&gt;"/>unix://<x id="1" equiv-text="&lt;/code&gt;"/> when connecting to a local <source>Can be in the format of <x id="0" equiv-text="&lt;code&gt;"/>unix://<x id="1" equiv-text="&lt;/code&gt;"/> when connecting to a local
docker daemon, using <x id="2" equiv-text="&lt;code&gt;"/>ssh://<x id="3" equiv-text="&lt;/code&gt;"/> to connect via SSH, or docker daemon, using <x id="2" equiv-text="&lt;code&gt;"/>ssh://<x id="3" equiv-text="&lt;/code&gt;"/> to connect via SSH, or
<x id="4" equiv-text="&lt;code&gt;"/>https://:2376<x id="5" equiv-text="&lt;/code&gt;"/> when connecting to a remote system.</source> <x id="4" equiv-text="&lt;code&gt;"/>https://:2376<x id="5" equiv-text="&lt;/code&gt;"/> when connecting to a remote system.</source>
<target>可以使用 <x id="0" equiv-text="&lt;code&gt;"/>unix://<x id="1" equiv-text="&lt;/code&gt;"/> 格式连接本地 Docker 守护程序,
使用 <x id="2" equiv-text="&lt;code&gt;"/>ssh://<x id="3" equiv-text="&lt;/code&gt;"/> 通过 SSH 连接,或者
使用 <x id="4" equiv-text="&lt;code&gt;"/>https://:2376<x id="5" equiv-text="&lt;/code&gt;"/> 连接到远程系统。</target>
</trans-unit> </trans-unit>
<trans-unit id="h3ac79dc5cc553329"> <trans-unit id="h3ac79dc5cc553329">
<source>When using an external logging solution for archiving, this can be <source>When using an external logging solution for archiving, this can be
set to <x id="0" equiv-text="&lt;code&gt;"/>minutes=5<x id="1" equiv-text="&lt;/code&gt;"/>.</source> set to <x id="0" equiv-text="&lt;code&gt;"/>minutes=5<x id="1" equiv-text="&lt;/code&gt;"/>.</source>
<target>当使用外部日志解决方案进行存档时,可以
设置为 <x id="0" equiv-text="&lt;code&gt;"/>minutes=5<x id="1" equiv-text="&lt;/code&gt;"/>。</target>
</trans-unit> </trans-unit>
<trans-unit id="sc92905d14a9421f3"> <trans-unit id="sc92905d14a9421f3">
<source>Idle</source> <source>Idle</source>
<target>闲置</target>
</trans-unit> </trans-unit>
<trans-unit id="s4b052cb287d81da7"> <trans-unit id="s4b052cb287d81da7">
<source>Connecting</source> <source>Connecting</source>
<target>正在连接</target>
</trans-unit> </trans-unit>
<trans-unit id="s123b7254b44dd148"> <trans-unit id="s123b7254b44dd148">
<source>Waiting</source> <source>Waiting</source>
<target>正在等待</target>
</trans-unit> </trans-unit>
<trans-unit id="s82a4e28ab5b56670"> <trans-unit id="s82a4e28ab5b56670">
<source>Connected</source> <source>Connected</source>
<target>已连接</target>
</trans-unit> </trans-unit>
<trans-unit id="sd669c8ff49c068ad"> <trans-unit id="sd669c8ff49c068ad">
<source>Disconnecting</source> <source>Disconnecting</source>
<target>正在断开连接</target>
</trans-unit> </trans-unit>
<trans-unit id="sfc2c3bd345c0128a"> <trans-unit id="sfc2c3bd345c0128a">
<source>Disconnected</source> <source>Disconnected</source>
<target>已断开连接</target>
</trans-unit> </trans-unit>
<trans-unit id="s47b7ce63a543564c"> <trans-unit id="s47b7ce63a543564c">
<source>Fewer details</source> <source>Fewer details</source>
<target>显示更少</target>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -9344,108 +9344,145 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="s82cced87e15e0665"> <trans-unit id="s82cced87e15e0665">
<source>Worker with incorrect version connected.</source> <source>Worker with incorrect version connected.</source>
<target>错误版本的 Worker 已连接。</target>
</trans-unit> </trans-unit>
<trans-unit id="ha8e91858b300a3b7"> <trans-unit id="ha8e91858b300a3b7">
<source>(Format: <x id="0" equiv-text="&lt;code&gt;"/>hours=-1;minutes=-2;seconds=-3)<x id="1" equiv-text="&lt;/code&gt;"/>.</source> <source>(Format: <x id="0" equiv-text="&lt;code&gt;"/>hours=-1;minutes=-2;seconds=-3)<x id="1" equiv-text="&lt;/code&gt;"/>.</source>
<target>(格式:<x id="0" equiv-text="&lt;code&gt;"/>hours=-1;minutes=-2;seconds=-3<x id="1" equiv-text="&lt;/code&gt;"/>。</target>
</trans-unit> </trans-unit>
<trans-unit id="hbe8e6353371ecb22"> <trans-unit id="hbe8e6353371ecb22">
<source>(Format: <x id="0" equiv-text="&lt;code&gt;"/>hours=1;minutes=2;seconds=3).<x id="1" equiv-text="&lt;/code&gt;"/></source> <source>(Format: <x id="0" equiv-text="&lt;code&gt;"/>hours=1;minutes=2;seconds=3).<x id="1" equiv-text="&lt;/code&gt;"/></source>
<target>(格式:<x id="0" equiv-text="&lt;code&gt;"/>hours=1;minutes=2;seconds=3。<x id="1" equiv-text="&lt;/code&gt;"/></target>
</trans-unit> </trans-unit>
<trans-unit id="se8dd47dfaf01d6c9"> <trans-unit id="se8dd47dfaf01d6c9">
<source>Key used to sign the events.</source> <source>Key used to sign the events.</source>
<target>用于签名事件的密钥。</target>
</trans-unit> </trans-unit>
<trans-unit id="sd1b31f8fba05d429"> <trans-unit id="sd1b31f8fba05d429">
<source>Event Retention</source> <source>Event Retention</source>
<target>事件保留</target>
</trans-unit> </trans-unit>
<trans-unit id="sd760b123cced4675"> <trans-unit id="sd760b123cced4675">
<source>Determines how long events are stored for. If an event could not be sent correctly, its expiration is also increased by this duration.</source> <source>Determines how long events are stored for. If an event could not be sent correctly, its expiration is also increased by this duration.</source>
<target>设置事件存储多久时间。如果无法成功发送事件,则此时长也会添加到事件的过期时间。</target>
</trans-unit> </trans-unit>
<trans-unit id="s1aa62e1bf258e88a"> <trans-unit id="s1aa62e1bf258e88a">
<source>OIDC Providers</source> <source>OIDC Providers</source>
<target>OIDC 提供程序</target>
</trans-unit> </trans-unit>
<trans-unit id="sb9d947872397ca07"> <trans-unit id="sb9d947872397ca07">
<source>SSF Provider is in preview.</source> <source>SSF Provider is in preview.</source>
<target>SSF 提供程序处于预览状态。</target>
</trans-unit> </trans-unit>
<trans-unit id="s78302d097a608b99"> <trans-unit id="s78302d097a608b99">
<source>Update SSF Provider</source> <source>Update SSF Provider</source>
<target>更新 SSF 提供程序</target>
</trans-unit> </trans-unit>
<trans-unit id="sa412f34efd88e962"> <trans-unit id="sa412f34efd88e962">
<source>Streams</source> <source>Streams</source>
<target>流</target>
</trans-unit> </trans-unit>
<trans-unit id="s91ae4b6bf981682b"> <trans-unit id="s91ae4b6bf981682b">
<source>authentik Logo</source> <source>authentik Logo</source>
<target>authentik 图标</target>
</trans-unit> </trans-unit>
<trans-unit id="s1cdeecda0baf365e"> <trans-unit id="s1cdeecda0baf365e">
<source>Release</source> <source>Release</source>
<target>发布版</target>
</trans-unit> </trans-unit>
<trans-unit id="s35429c9dc2319aea"> <trans-unit id="s35429c9dc2319aea">
<source>Development</source> <source>Development</source>
<target>开发版</target>
</trans-unit> </trans-unit>
<trans-unit id="s5937938934a777ab"> <trans-unit id="s5937938934a777ab">
<source>UI Version</source> <source>UI Version</source>
<target>界面版本</target>
</trans-unit> </trans-unit>
<trans-unit id="s3fe3ff490adaaa63"> <trans-unit id="s3fe3ff490adaaa63">
<source>Build</source> <source>Build</source>
<target>构建</target>
</trans-unit> </trans-unit>
<trans-unit id="s42aea5c6d3d1fce7"> <trans-unit id="s42aea5c6d3d1fce7">
<source>Python version</source> <source>Python version</source>
<target>Python 版本</target>
</trans-unit> </trans-unit>
<trans-unit id="s5c67b6c66029e5b2"> <trans-unit id="s5c67b6c66029e5b2">
<source>Platform</source> <source>Platform</source>
<target>平台</target>
</trans-unit> </trans-unit>
<trans-unit id="se8574bce64d9c1f8"> <trans-unit id="se8574bce64d9c1f8">
<source>Kernel</source> <source>Kernel</source>
<target>内核</target>
</trans-unit> </trans-unit>
<trans-unit id="s3fbe63b7fcfe3e91"> <trans-unit id="s3fbe63b7fcfe3e91">
<source>OpenSSL</source> <source>OpenSSL</source>
<target>OpenSSL</target>
</trans-unit> </trans-unit>
<trans-unit id="s1381314790b50b1f"> <trans-unit id="s1381314790b50b1f">
<source>A newer version (<x id="0" equiv-text="${this.version.versionCurrent}"/>) of the UI is available.</source> <source>A newer version (<x id="0" equiv-text="${this.version.versionCurrent}"/>) of the UI is available.</source>
<target>新版本界面(<x id="0" equiv-text="${this.version.versionCurrent}"/>)可用。</target>
</trans-unit> </trans-unit>
<trans-unit id="s61ffea061fae0af4"> <trans-unit id="s61ffea061fae0af4">
<source>No notifications found.</source> <source>No notifications found.</source>
<target>未找到通知。</target>
</trans-unit> </trans-unit>
<trans-unit id="scf8d5cdc8b434982"> <trans-unit id="scf8d5cdc8b434982">
<source>You don't have any notifications currently.</source> <source>You don't have any notifications currently.</source>
<target>您当前没有任何通知。</target>
</trans-unit> </trans-unit>
<trans-unit id="s358e08de4fbebf51"> <trans-unit id="s358e08de4fbebf51">
<source>Version <x id="0" equiv-text="${this.version?.versionCurrent || &quot;&quot;}"/></source> <source>Version <x id="0" equiv-text="${this.version?.versionCurrent || &quot;&quot;}"/></source>
<target>版本 <x id="0" equiv-text="${this.version?.versionCurrent || &quot;&quot;}"/></target>
</trans-unit> </trans-unit>
<trans-unit id="sfb95ad8c25db95ba"> <trans-unit id="sfb95ad8c25db95ba">
<source>Last password change</source> <source>Last password change</source>
<target>上次修改密码</target>
</trans-unit> </trans-unit>
<trans-unit id="s37e1bad4bf0e9241"> <trans-unit id="s37e1bad4bf0e9241">
<source>Evaluate policies before the Stage is presented to the user.</source> <source>Evaluate policies before the Stage is presented to the user.</source>
<target>在阶段即将呈现给用户时评估策略。</target>
</trans-unit> </trans-unit>
<trans-unit id="h8c1aa2d523f94f36"> <trans-unit id="h8c1aa2d523f94f36">
<source>Can be in the format of <x id="0" equiv-text="&lt;code&gt;"/>unix://<x id="1" equiv-text="&lt;/code&gt;"/> when connecting to a local <source>Can be in the format of <x id="0" equiv-text="&lt;code&gt;"/>unix://<x id="1" equiv-text="&lt;/code&gt;"/> when connecting to a local
docker daemon, using <x id="2" equiv-text="&lt;code&gt;"/>ssh://<x id="3" equiv-text="&lt;/code&gt;"/> to connect via SSH, or docker daemon, using <x id="2" equiv-text="&lt;code&gt;"/>ssh://<x id="3" equiv-text="&lt;/code&gt;"/> to connect via SSH, or
<x id="4" equiv-text="&lt;code&gt;"/>https://:2376<x id="5" equiv-text="&lt;/code&gt;"/> when connecting to a remote system.</source> <x id="4" equiv-text="&lt;code&gt;"/>https://:2376<x id="5" equiv-text="&lt;/code&gt;"/> when connecting to a remote system.</source>
<target>可以使用 <x id="0" equiv-text="&lt;code&gt;"/>unix://<x id="1" equiv-text="&lt;/code&gt;"/> 格式连接本地 Docker 守护程序,
使用 <x id="2" equiv-text="&lt;code&gt;"/>ssh://<x id="3" equiv-text="&lt;/code&gt;"/> 通过 SSH 连接,或者
使用 <x id="4" equiv-text="&lt;code&gt;"/>https://:2376<x id="5" equiv-text="&lt;/code&gt;"/> 连接到远程系统。</target>
</trans-unit> </trans-unit>
<trans-unit id="h3ac79dc5cc553329"> <trans-unit id="h3ac79dc5cc553329">
<source>When using an external logging solution for archiving, this can be <source>When using an external logging solution for archiving, this can be
set to <x id="0" equiv-text="&lt;code&gt;"/>minutes=5<x id="1" equiv-text="&lt;/code&gt;"/>.</source> set to <x id="0" equiv-text="&lt;code&gt;"/>minutes=5<x id="1" equiv-text="&lt;/code&gt;"/>.</source>
<target>当使用外部日志解决方案进行存档时,可以
设置为 <x id="0" equiv-text="&lt;code&gt;"/>minutes=5<x id="1" equiv-text="&lt;/code&gt;"/>。</target>
</trans-unit> </trans-unit>
<trans-unit id="sc92905d14a9421f3"> <trans-unit id="sc92905d14a9421f3">
<source>Idle</source> <source>Idle</source>
<target>闲置</target>
</trans-unit> </trans-unit>
<trans-unit id="s4b052cb287d81da7"> <trans-unit id="s4b052cb287d81da7">
<source>Connecting</source> <source>Connecting</source>
<target>正在连接</target>
</trans-unit> </trans-unit>
<trans-unit id="s123b7254b44dd148"> <trans-unit id="s123b7254b44dd148">
<source>Waiting</source> <source>Waiting</source>
<target>正在等待</target>
</trans-unit> </trans-unit>
<trans-unit id="s82a4e28ab5b56670"> <trans-unit id="s82a4e28ab5b56670">
<source>Connected</source> <source>Connected</source>
<target>已连接</target>
</trans-unit> </trans-unit>
<trans-unit id="sd669c8ff49c068ad"> <trans-unit id="sd669c8ff49c068ad">
<source>Disconnecting</source> <source>Disconnecting</source>
<target>正在断开连接</target>
</trans-unit> </trans-unit>
<trans-unit id="sfc2c3bd345c0128a"> <trans-unit id="sfc2c3bd345c0128a">
<source>Disconnected</source> <source>Disconnected</source>
<target>已断开连接</target>
</trans-unit> </trans-unit>
<trans-unit id="s47b7ce63a543564c"> <trans-unit id="s47b7ce63a543564c">
<source>Fewer details</source> <source>Fewer details</source>
<target>显示更少</target>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -582,7 +582,7 @@
</trans-unit> </trans-unit>
<trans-unit id="saa0e2675da69651b"> <trans-unit id="saa0e2675da69651b">
<source>The URL &quot;<x id="0" equiv-text="${this.url}"/>&quot; was not found.</source> <source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
</trans-unit> </trans-unit>
<trans-unit id="s58cd9c2fe836d9c6"> <trans-unit id="s58cd9c2fe836d9c6">
@ -1682,7 +1682,7 @@
</trans-unit> </trans-unit>
<trans-unit id="sa90b7809586c35ce"> <trans-unit id="sa90b7809586c35ce">
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon &quot;fa-test&quot;.</source> <source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
<target>輸入完整網址、相對路徑,或者使用 'fa://fa-test' 來使用 Font Awesome 圖示 「fa-test」。</target> <target>輸入完整網址、相對路徑,或者使用 'fa://fa-test' 來使用 Font Awesome 圖示 「fa-test」。</target>
</trans-unit> </trans-unit>
@ -2824,7 +2824,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit> </trans-unit>
<trans-unit id="s76768bebabb7d543"> <trans-unit id="s76768bebabb7d543">
<source>Field which contains members of a group. Note that if using the &quot;memberUid&quot; field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source> <source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source>
<target>包含群組成員的欄位。注意如果使用「memberUid」欄位則假設其值包含相對可分辨的名稱。例如「memberUID=some-user」而不是「memberuid=cn=some-user,ou=groups,... 」</target> <target>包含群組成員的欄位。注意如果使用「memberUid」欄位則假設其值包含相對可分辨的名稱。例如「memberUID=some-user」而不是「memberuid=cn=some-user,ou=groups,... 」</target>
</trans-unit> </trans-unit>
@ -3733,7 +3733,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit> </trans-unit>
<trans-unit id="sa95a538bfbb86111"> <trans-unit id="sa95a538bfbb86111">
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> &quot;<x id="1" equiv-text="${this.obj?.name}"/>&quot;?</source> <source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
</trans-unit> </trans-unit>
<trans-unit id="sc92d7cfb6ee1fec6"> <trans-unit id="sc92d7cfb6ee1fec6">
@ -4793,7 +4793,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit> </trans-unit>
<trans-unit id="sdf1d8edef27236f0"> <trans-unit id="sdf1d8edef27236f0">
<source>A &quot;roaming&quot; authenticator, like a YubiKey</source> <source>A "roaming" authenticator, like a YubiKey</source>
<target>外接式的身分認證器,例如 YubiKey</target> <target>外接式的身分認證器,例如 YubiKey</target>
</trans-unit> </trans-unit>
@ -5162,7 +5162,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit> </trans-unit>
<trans-unit id="s1608b2f94fa0dbd4"> <trans-unit id="s1608b2f94fa0dbd4">
<source>If set to a duration above 0, the user will have the option to choose to &quot;stay signed in&quot;, which will extend their session by the time specified here.</source> <source>If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here.</source>
<target>如果持續時間大於零,使用者介面上將會有「保持登入」選項。這將會依照設定的時間延長會談。</target> <target>如果持續時間大於零,使用者介面上將會有「保持登入」選項。這將會依照設定的時間延長會談。</target>
</trans-unit> </trans-unit>
@ -7432,7 +7432,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<target>成功建立使用者並加入到群組 <x id="0" equiv-text="${this.group.name}"/></target> <target>成功建立使用者並加入到群組 <x id="0" equiv-text="${this.group.name}"/></target>
</trans-unit> </trans-unit>
<trans-unit id="s824e0943a7104668"> <trans-unit id="s824e0943a7104668">
<source>This user will be added to the group &quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&quot;.</source> <source>This user will be added to the group "<x id="0" equiv-text="${this.targetGroup.name}"/>".</source>
<target>這個使用者將會被加入到「<x id="0" equiv-text="${this.targetGroup.name}"/>」群組。</target> <target>這個使用者將會被加入到「<x id="0" equiv-text="${this.targetGroup.name}"/>」群組。</target>
</trans-unit> </trans-unit>
<trans-unit id="s62e7f6ed7d9cb3ca"> <trans-unit id="s62e7f6ed7d9cb3ca">
@ -8403,7 +8403,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Sync Group</source> <source>Sync Group</source>
</trans-unit> </trans-unit>
<trans-unit id="s2d5f69929bb7221d"> <trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${p.name}"/> (&quot;<x id="1" equiv-text="${p.fieldKey}"/>&quot;, of type <x id="2" equiv-text="${p.type}"/>)</source> <source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
</trans-unit> </trans-unit>
<trans-unit id="s25bacc19d98b444e"> <trans-unit id="s25bacc19d98b444e">
<source>Parent Group</source> <source>Parent Group</source>
@ -8589,7 +8589,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Valid redirect URIs after a successful authorization flow. Also specify any origins here for Implicit flows.</source> <source>Valid redirect URIs after a successful authorization flow. Also specify any origins here for Implicit flows.</source>
</trans-unit> </trans-unit>
<trans-unit id="s4c49d27de60a532b"> <trans-unit id="s4c49d27de60a532b">
<source>To allow any redirect URI, set the mode to Regex and the value to &quot;.*&quot;. Be aware of the possible security implications this can have.</source> <source>To allow any redirect URI, set the mode to Regex and the value to ".*". Be aware of the possible security implications this can have.</source>
</trans-unit> </trans-unit>
<trans-unit id="sa52bf79fe1ccb13e"> <trans-unit id="sa52bf79fe1ccb13e">
<source>Federated OIDC Sources</source> <source>Federated OIDC Sources</source>

1188
website/package-lock.json generated

File diff suppressed because it is too large Load Diff