Merge branch 'main' into celery-2-dramatiq
This commit is contained in:
2
.github/actions/setup/action.yml
vendored
2
.github/actions/setup/action.yml
vendored
@ -36,7 +36,7 @@ runs:
|
|||||||
with:
|
with:
|
||||||
go-version-file: "go.mod"
|
go-version-file: "go.mod"
|
||||||
- name: Setup docker cache
|
- name: Setup docker cache
|
||||||
uses: ScribeMD/docker-cache@0.5.0
|
uses: AndreKurait/docker-cache@0fe76702a40db986d9663c24954fc14c6a6031b7
|
||||||
with:
|
with:
|
||||||
key: docker-images-${{ runner.os }}-${{ hashFiles('.github/actions/setup/docker-compose.yml', 'Makefile') }}-${{ inputs.postgresql_version }}
|
key: docker-images-${{ runner.os }}-${{ hashFiles('.github/actions/setup/docker-compose.yml', 'Makefile') }}-${{ inputs.postgresql_version }}
|
||||||
- name: Setup dependencies
|
- name: Setup dependencies
|
||||||
|
@ -94,7 +94,7 @@ RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
|
|||||||
/bin/sh -c "GEOIPUPDATE_LICENSE_KEY_FILE=/run/secrets/GEOIPUPDATE_LICENSE_KEY /usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0"
|
/bin/sh -c "GEOIPUPDATE_LICENSE_KEY_FILE=/run/secrets/GEOIPUPDATE_LICENSE_KEY /usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0"
|
||||||
|
|
||||||
# Stage 5: Download uv
|
# Stage 5: Download uv
|
||||||
FROM ghcr.io/astral-sh/uv:0.7.8 AS uv
|
FROM ghcr.io/astral-sh/uv:0.7.9 AS uv
|
||||||
# Stage 6: Base python image
|
# Stage 6: Base python image
|
||||||
FROM ghcr.io/goauthentik/fips-python:3.13.3-slim-bookworm-fips AS python-base
|
FROM ghcr.io/goauthentik/fips-python:3.13.3-slim-bookworm-fips AS python-base
|
||||||
|
|
||||||
|
@ -84,6 +84,7 @@ from authentik.flows.views.executor import QS_KEY_TOKEN
|
|||||||
from authentik.lib.avatars import get_avatar
|
from authentik.lib.avatars import get_avatar
|
||||||
from authentik.rbac.decorators import permission_required
|
from authentik.rbac.decorators import permission_required
|
||||||
from authentik.rbac.models import get_permission_choices
|
from authentik.rbac.models import get_permission_choices
|
||||||
|
from authentik.stages.email.flow import pickle_flow_token_for_email
|
||||||
from authentik.stages.email.models import EmailStage
|
from authentik.stages.email.models import EmailStage
|
||||||
from authentik.stages.email.tasks import send_mails
|
from authentik.stages.email.tasks import send_mails
|
||||||
from authentik.stages.email.utils import TemplateEmailMessage
|
from authentik.stages.email.utils import TemplateEmailMessage
|
||||||
@ -451,7 +452,7 @@ class UserViewSet(UsedByMixin, ModelViewSet):
|
|||||||
def list(self, request, *args, **kwargs):
|
def list(self, request, *args, **kwargs):
|
||||||
return super().list(request, *args, **kwargs)
|
return super().list(request, *args, **kwargs)
|
||||||
|
|
||||||
def _create_recovery_link(self) -> tuple[str, Token]:
|
def _create_recovery_link(self, for_email=False) -> tuple[str, Token]:
|
||||||
"""Create a recovery link (when the current brand has a recovery flow set),
|
"""Create a recovery link (when the current brand has a recovery flow set),
|
||||||
that can either be shown to an admin or sent to the user directly"""
|
that can either be shown to an admin or sent to the user directly"""
|
||||||
brand: Brand = self.request._request.brand
|
brand: Brand = self.request._request.brand
|
||||||
@ -473,12 +474,16 @@ class UserViewSet(UsedByMixin, ModelViewSet):
|
|||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
{"non_field_errors": "Recovery flow not applicable to user"}
|
{"non_field_errors": "Recovery flow not applicable to user"}
|
||||||
) from None
|
) from None
|
||||||
|
_plan = FlowToken.pickle(plan)
|
||||||
|
if for_email:
|
||||||
|
_plan = pickle_flow_token_for_email(plan)
|
||||||
token, __ = FlowToken.objects.update_or_create(
|
token, __ = FlowToken.objects.update_or_create(
|
||||||
identifier=f"{user.uid}-password-reset",
|
identifier=f"{user.uid}-password-reset",
|
||||||
defaults={
|
defaults={
|
||||||
"user": user,
|
"user": user,
|
||||||
"flow": flow,
|
"flow": flow,
|
||||||
"_plan": FlowToken.pickle(plan),
|
"_plan": _plan,
|
||||||
|
"revoke_on_execution": not for_email,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
querystring = urlencode({QS_KEY_TOKEN: token.key})
|
querystring = urlencode({QS_KEY_TOKEN: token.key})
|
||||||
@ -648,7 +653,7 @@ class UserViewSet(UsedByMixin, ModelViewSet):
|
|||||||
if for_user.email == "":
|
if for_user.email == "":
|
||||||
LOGGER.debug("User doesn't have an email address")
|
LOGGER.debug("User doesn't have an email address")
|
||||||
raise ValidationError({"non_field_errors": "User does not have an email address set."})
|
raise ValidationError({"non_field_errors": "User does not have an email address set."})
|
||||||
link, token = self._create_recovery_link()
|
link, token = self._create_recovery_link(for_email=True)
|
||||||
# Lookup the email stage to assure the current user can access it
|
# Lookup the email stage to assure the current user can access it
|
||||||
stages = get_objects_for_user(
|
stages = get_objects_for_user(
|
||||||
request.user, "authentik_stages_email.view_emailstage"
|
request.user, "authentik_stages_email.view_emailstage"
|
||||||
|
@ -79,6 +79,7 @@ def _migrate_session(
|
|||||||
AuthenticatedSession.objects.using(db_alias).create(
|
AuthenticatedSession.objects.using(db_alias).create(
|
||||||
session=session,
|
session=session,
|
||||||
user=old_auth_session.user,
|
user=old_auth_session.user,
|
||||||
|
uuid=old_auth_session.uuid,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,10 +1,81 @@
|
|||||||
# Generated by Django 5.1.9 on 2025-05-14 11:15
|
# Generated by Django 5.1.9 on 2025-05-14 11:15
|
||||||
|
|
||||||
from django.apps.registry import Apps
|
from django.apps.registry import Apps, apps as global_apps
|
||||||
from django.db import migrations
|
from django.db import migrations
|
||||||
|
from django.contrib.contenttypes.management import create_contenttypes
|
||||||
|
from django.contrib.auth.management import create_permissions
|
||||||
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
||||||
|
|
||||||
|
|
||||||
|
def migrate_authenticated_session_permissions(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
|
||||||
|
"""Migrate permissions from OldAuthenticatedSession to AuthenticatedSession"""
|
||||||
|
db_alias = schema_editor.connection.alias
|
||||||
|
|
||||||
|
# `apps` here is just an instance of `django.db.migrations.state.AppConfigStub`, we need the
|
||||||
|
# real config for creating permissions and content types
|
||||||
|
authentik_core_config = global_apps.get_app_config("authentik_core")
|
||||||
|
# These are only ran by django after all migrations, but we need them right now.
|
||||||
|
# `global_apps` is needed,
|
||||||
|
create_permissions(authentik_core_config, using=db_alias, verbosity=1)
|
||||||
|
create_contenttypes(authentik_core_config, using=db_alias, verbosity=1)
|
||||||
|
|
||||||
|
# But from now on, this is just a regular migration, so use `apps`
|
||||||
|
Permission = apps.get_model("auth", "Permission")
|
||||||
|
ContentType = apps.get_model("contenttypes", "ContentType")
|
||||||
|
|
||||||
|
try:
|
||||||
|
old_ct = ContentType.objects.using(db_alias).get(
|
||||||
|
app_label="authentik_core", model="oldauthenticatedsession"
|
||||||
|
)
|
||||||
|
new_ct = ContentType.objects.using(db_alias).get(
|
||||||
|
app_label="authentik_core", model="authenticatedsession"
|
||||||
|
)
|
||||||
|
except ContentType.DoesNotExist:
|
||||||
|
# This should exist at this point, but if not, let's cut our losses
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get all permissions for the old content type
|
||||||
|
old_perms = Permission.objects.using(db_alias).filter(content_type=old_ct)
|
||||||
|
|
||||||
|
# Create equivalent permissions for the new content type
|
||||||
|
for old_perm in old_perms:
|
||||||
|
new_perm = (
|
||||||
|
Permission.objects.using(db_alias)
|
||||||
|
.filter(
|
||||||
|
content_type=new_ct,
|
||||||
|
codename=old_perm.codename,
|
||||||
|
)
|
||||||
|
.first()
|
||||||
|
)
|
||||||
|
if not new_perm:
|
||||||
|
# This should exist at this point, but if not, let's cut our losses
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Global user permissions
|
||||||
|
User = apps.get_model("authentik_core", "User")
|
||||||
|
User.user_permissions.through.objects.using(db_alias).filter(
|
||||||
|
permission=old_perm
|
||||||
|
).all().update(permission=new_perm)
|
||||||
|
|
||||||
|
# Global role permissions
|
||||||
|
DjangoGroup = apps.get_model("auth", "Group")
|
||||||
|
DjangoGroup.permissions.through.objects.using(db_alias).filter(
|
||||||
|
permission=old_perm
|
||||||
|
).all().update(permission=new_perm)
|
||||||
|
|
||||||
|
# Object user permissions
|
||||||
|
UserObjectPermission = apps.get_model("guardian", "UserObjectPermission")
|
||||||
|
UserObjectPermission.objects.using(db_alias).filter(permission=old_perm).all().update(
|
||||||
|
permission=new_perm, content_type=new_ct
|
||||||
|
)
|
||||||
|
|
||||||
|
# Object role permissions
|
||||||
|
GroupObjectPermission = apps.get_model("guardian", "GroupObjectPermission")
|
||||||
|
GroupObjectPermission.objects.using(db_alias).filter(permission=old_perm).all().update(
|
||||||
|
permission=new_perm, content_type=new_ct
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def remove_old_authenticated_session_content_type(
|
def remove_old_authenticated_session_content_type(
|
||||||
apps: Apps, schema_editor: BaseDatabaseSchemaEditor
|
apps: Apps, schema_editor: BaseDatabaseSchemaEditor
|
||||||
):
|
):
|
||||||
@ -21,7 +92,12 @@ class Migration(migrations.Migration):
|
|||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
|
migrations.RunPython(
|
||||||
|
code=migrate_authenticated_session_permissions,
|
||||||
|
reverse_code=migrations.RunPython.noop,
|
||||||
|
),
|
||||||
migrations.RunPython(
|
migrations.RunPython(
|
||||||
code=remove_old_authenticated_session_content_type,
|
code=remove_old_authenticated_session_content_type,
|
||||||
|
reverse_code=migrations.RunPython.noop,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.1.9 on 2025-05-27 12:52
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("authentik_flows", "0027_auto_20231028_1424"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="flowtoken",
|
||||||
|
name="revoke_on_execution",
|
||||||
|
field=models.BooleanField(default=True),
|
||||||
|
),
|
||||||
|
]
|
@ -303,9 +303,10 @@ class FlowToken(Token):
|
|||||||
|
|
||||||
flow = models.ForeignKey(Flow, on_delete=models.CASCADE)
|
flow = models.ForeignKey(Flow, on_delete=models.CASCADE)
|
||||||
_plan = models.TextField()
|
_plan = models.TextField()
|
||||||
|
revoke_on_execution = models.BooleanField(default=True)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def pickle(plan) -> str:
|
def pickle(plan: "FlowPlan") -> str:
|
||||||
"""Pickle into string"""
|
"""Pickle into string"""
|
||||||
data = dumps(plan)
|
data = dumps(plan)
|
||||||
return b64encode(data).decode()
|
return b64encode(data).decode()
|
||||||
|
@ -99,9 +99,10 @@ class ChallengeStageView(StageView):
|
|||||||
self.logger.debug("Got StageInvalidException", exc=exc)
|
self.logger.debug("Got StageInvalidException", exc=exc)
|
||||||
return self.executor.stage_invalid()
|
return self.executor.stage_invalid()
|
||||||
if not challenge.is_valid():
|
if not challenge.is_valid():
|
||||||
self.logger.warning(
|
self.logger.error(
|
||||||
"f(ch): Invalid challenge",
|
"f(ch): Invalid challenge",
|
||||||
errors=challenge.errors,
|
errors=challenge.errors,
|
||||||
|
challenge=challenge.data,
|
||||||
)
|
)
|
||||||
return HttpChallengeResponse(challenge)
|
return HttpChallengeResponse(challenge)
|
||||||
|
|
||||||
|
@ -146,7 +146,8 @@ class FlowExecutorView(APIView):
|
|||||||
except (AttributeError, EOFError, ImportError, IndexError) as exc:
|
except (AttributeError, EOFError, ImportError, IndexError) as exc:
|
||||||
LOGGER.warning("f(exec): Failed to restore token plan", exc=exc)
|
LOGGER.warning("f(exec): Failed to restore token plan", exc=exc)
|
||||||
finally:
|
finally:
|
||||||
token.delete()
|
if token.revoke_on_execution:
|
||||||
|
token.delete()
|
||||||
if not isinstance(plan, FlowPlan):
|
if not isinstance(plan, FlowPlan):
|
||||||
return None
|
return None
|
||||||
plan.context[PLAN_CONTEXT_IS_RESTORED] = token
|
plan.context[PLAN_CONTEXT_IS_RESTORED] = token
|
||||||
|
@ -81,7 +81,6 @@ debugger: false
|
|||||||
|
|
||||||
log_level: info
|
log_level: info
|
||||||
|
|
||||||
session_storage: cache
|
|
||||||
sessions:
|
sessions:
|
||||||
unauthenticated_age: days=1
|
unauthenticated_age: days=1
|
||||||
|
|
||||||
|
@ -1,18 +1,21 @@
|
|||||||
|
from collections.abc import Callable
|
||||||
from dataclasses import asdict
|
from dataclasses import asdict
|
||||||
|
|
||||||
|
from celery import group
|
||||||
|
from celery.exceptions import Retry
|
||||||
|
from celery.result import allow_join_result
|
||||||
from django.core.paginator import Paginator
|
from django.core.paginator import Paginator
|
||||||
from django.db.models import Model, QuerySet
|
from django.db.models import Model, QuerySet
|
||||||
from django.db.models.query import Q
|
from django.db.models.query import Q
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from dramatiq.actor import Actor
|
|
||||||
from dramatiq.composition import group
|
|
||||||
from dramatiq.errors import Retry
|
|
||||||
from structlog.stdlib import BoundLogger, get_logger
|
from structlog.stdlib import BoundLogger, get_logger
|
||||||
|
|
||||||
from authentik.core.expression.exceptions import SkipObjectException
|
from authentik.core.expression.exceptions import SkipObjectException
|
||||||
from authentik.core.models import Group, User
|
from authentik.core.models import Group, User
|
||||||
from authentik.events.logs import LogEvent
|
from authentik.events.logs import LogEvent
|
||||||
|
from authentik.events.models import TaskStatus
|
||||||
|
from authentik.events.system_tasks import SystemTask
|
||||||
from authentik.events.utils import sanitize_item
|
from authentik.events.utils import sanitize_item
|
||||||
from authentik.lib.sync.outgoing import PAGE_SIZE, PAGE_TIMEOUT
|
from authentik.lib.sync.outgoing import PAGE_SIZE, PAGE_TIMEOUT
|
||||||
from authentik.lib.sync.outgoing.base import Direction
|
from authentik.lib.sync.outgoing.base import Direction
|
||||||
@ -24,8 +27,6 @@ from authentik.lib.sync.outgoing.exceptions import (
|
|||||||
)
|
)
|
||||||
from authentik.lib.sync.outgoing.models import OutgoingSyncProvider
|
from authentik.lib.sync.outgoing.models import OutgoingSyncProvider
|
||||||
from authentik.lib.utils.reflection import class_to_path, path_to_class
|
from authentik.lib.utils.reflection import class_to_path, path_to_class
|
||||||
from authentik.tasks.middleware import CurrentTask
|
|
||||||
from authentik.tasks.models import Task, TaskStatus
|
|
||||||
|
|
||||||
|
|
||||||
class SyncTasks:
|
class SyncTasks:
|
||||||
@ -38,35 +39,34 @@ class SyncTasks:
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
self._provider_model = provider_model
|
self._provider_model = provider_model
|
||||||
|
|
||||||
def sync_paginator(
|
def sync_all(self, single_sync: Callable[[int], None]):
|
||||||
self,
|
for provider in self._provider_model.objects.filter(
|
||||||
provider_pk: int,
|
Q(backchannel_application__isnull=False) | Q(application__isnull=False)
|
||||||
sync_objects: Actor,
|
):
|
||||||
paginator: Paginator,
|
self.trigger_single_task(provider, single_sync)
|
||||||
object_type: type[User | Group],
|
|
||||||
**options,
|
|
||||||
):
|
|
||||||
tasks = []
|
|
||||||
for page in paginator.page_range:
|
|
||||||
page_sync = sync_objects.message_with_options(
|
|
||||||
args=(class_to_path(object_type), page, provider_pk),
|
|
||||||
time_limit=PAGE_TIMEOUT * 1000,
|
|
||||||
**options,
|
|
||||||
)
|
|
||||||
tasks.append(page_sync)
|
|
||||||
return tasks
|
|
||||||
|
|
||||||
def sync(
|
def trigger_single_task(self, provider: OutgoingSyncProvider, sync_task: Callable[[int], None]):
|
||||||
|
"""Wrapper single sync task that correctly sets time limits based
|
||||||
|
on the amount of objects that will be synced"""
|
||||||
|
users_paginator = Paginator(provider.get_object_qs(User), PAGE_SIZE)
|
||||||
|
groups_paginator = Paginator(provider.get_object_qs(Group), PAGE_SIZE)
|
||||||
|
soft_time_limit = (users_paginator.num_pages + groups_paginator.num_pages) * PAGE_TIMEOUT
|
||||||
|
time_limit = soft_time_limit * 1.5
|
||||||
|
return sync_task.apply_async(
|
||||||
|
(provider.pk,), time_limit=int(time_limit), soft_time_limit=int(soft_time_limit)
|
||||||
|
)
|
||||||
|
|
||||||
|
def sync_single(
|
||||||
self,
|
self,
|
||||||
|
task: SystemTask,
|
||||||
provider_pk: int,
|
provider_pk: int,
|
||||||
sync_objects: Actor,
|
sync_objects: Callable[[int, int], list[str]],
|
||||||
):
|
):
|
||||||
task: Task = CurrentTask.get_task()
|
|
||||||
self.logger = get_logger().bind(
|
self.logger = get_logger().bind(
|
||||||
provider_type=class_to_path(self._provider_model),
|
provider_type=class_to_path(self._provider_model),
|
||||||
provider_pk=provider_pk,
|
provider_pk=provider_pk,
|
||||||
)
|
)
|
||||||
provider: OutgoingSyncProvider = self._provider_model.objects.filter(
|
provider = self._provider_model.objects.filter(
|
||||||
Q(backchannel_application__isnull=False) | Q(application__isnull=False),
|
Q(backchannel_application__isnull=False) | Q(application__isnull=False),
|
||||||
pk=provider_pk,
|
pk=provider_pk,
|
||||||
).first()
|
).first()
|
||||||
@ -78,32 +78,50 @@ class SyncTasks:
|
|||||||
self.logger.debug("Starting provider sync")
|
self.logger.debug("Starting provider sync")
|
||||||
users_paginator = Paginator(provider.get_object_qs(User), PAGE_SIZE)
|
users_paginator = Paginator(provider.get_object_qs(User), PAGE_SIZE)
|
||||||
groups_paginator = Paginator(provider.get_object_qs(Group), PAGE_SIZE)
|
groups_paginator = Paginator(provider.get_object_qs(Group), PAGE_SIZE)
|
||||||
with provider.sync_lock as lock_acquired:
|
with allow_join_result(), provider.sync_lock as lock_acquired:
|
||||||
if not lock_acquired:
|
if not lock_acquired:
|
||||||
self.logger.debug("Failed to acquire sync lock, skipping", provider=provider.name)
|
self.logger.debug("Failed to acquire sync lock, skipping", provider=provider.name)
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
tasks = group(
|
messages.append(_("Syncing users"))
|
||||||
self.sync_paginator(
|
user_results = (
|
||||||
provider_pk=provider_pk,
|
group(
|
||||||
sync_objects=sync_objects,
|
[
|
||||||
paginator=users_paginator,
|
sync_objects.signature(
|
||||||
object_type=User,
|
args=(class_to_path(User), page, provider_pk),
|
||||||
schedule_uid=task.schedule_uid,
|
time_limit=PAGE_TIMEOUT,
|
||||||
)
|
soft_time_limit=PAGE_TIMEOUT,
|
||||||
+ self.sync_paginator(
|
)
|
||||||
provider_pk=provider_pk,
|
for page in users_paginator.page_range
|
||||||
sync_objects=sync_objects,
|
]
|
||||||
paginator=groups_paginator,
|
|
||||||
object_type=Group,
|
|
||||||
schedule_uid=task.schedule_uid,
|
|
||||||
)
|
)
|
||||||
|
.apply_async()
|
||||||
|
.get()
|
||||||
)
|
)
|
||||||
tasks.run()
|
for result in user_results:
|
||||||
tasks.wait(timeout=provider.get_sync_time_limit() * 1000)
|
for msg in result:
|
||||||
|
messages.append(LogEvent(**msg))
|
||||||
|
messages.append(_("Syncing groups"))
|
||||||
|
group_results = (
|
||||||
|
group(
|
||||||
|
[
|
||||||
|
sync_objects.signature(
|
||||||
|
args=(class_to_path(Group), page, provider_pk),
|
||||||
|
time_limit=PAGE_TIMEOUT,
|
||||||
|
soft_time_limit=PAGE_TIMEOUT,
|
||||||
|
)
|
||||||
|
for page in groups_paginator.page_range
|
||||||
|
]
|
||||||
|
)
|
||||||
|
.apply_async()
|
||||||
|
.get()
|
||||||
|
)
|
||||||
|
for result in group_results:
|
||||||
|
for msg in result:
|
||||||
|
messages.append(LogEvent(**msg))
|
||||||
except TransientSyncException as exc:
|
except TransientSyncException as exc:
|
||||||
self.logger.warning("transient sync exception", exc=exc)
|
self.logger.warning("transient sync exception", exc=exc)
|
||||||
raise Retry() from exc
|
raise task.retry(exc=exc) from exc
|
||||||
except StopSync as exc:
|
except StopSync as exc:
|
||||||
task.set_error(exc)
|
task.set_error(exc)
|
||||||
return
|
return
|
||||||
@ -118,9 +136,7 @@ class SyncTasks:
|
|||||||
provider_pk=provider_pk,
|
provider_pk=provider_pk,
|
||||||
object_type=object_type,
|
object_type=object_type,
|
||||||
)
|
)
|
||||||
messages = [
|
messages = []
|
||||||
f"Syncing page {page} of {_object_type._meta.verbose_name_plural}",
|
|
||||||
]
|
|
||||||
provider = self._provider_model.objects.filter(pk=provider_pk).first()
|
provider = self._provider_model.objects.filter(pk=provider_pk).first()
|
||||||
if not provider:
|
if not provider:
|
||||||
return messages
|
return messages
|
||||||
@ -137,6 +153,15 @@ class SyncTasks:
|
|||||||
self.logger.debug("starting discover")
|
self.logger.debug("starting discover")
|
||||||
client.discover()
|
client.discover()
|
||||||
self.logger.debug("starting sync for page", page=page)
|
self.logger.debug("starting sync for page", page=page)
|
||||||
|
messages.append(
|
||||||
|
asdict(
|
||||||
|
LogEvent(
|
||||||
|
_("Syncing page {page} of groups".format(page=page)),
|
||||||
|
log_level="info",
|
||||||
|
logger=f"{provider._meta.verbose_name}@{object_type}",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
for obj in paginator.page(page).object_list:
|
for obj in paginator.page(page).object_list:
|
||||||
obj: Model
|
obj: Model
|
||||||
try:
|
try:
|
||||||
|
@ -47,15 +47,16 @@ class SCIMGroupClient(SCIMClient[Group, SCIMProviderGroup, SCIMGroupSchema]):
|
|||||||
|
|
||||||
def to_schema(self, obj: Group, connection: SCIMProviderGroup) -> SCIMGroupSchema:
|
def to_schema(self, obj: Group, connection: SCIMProviderGroup) -> SCIMGroupSchema:
|
||||||
"""Convert authentik user into SCIM"""
|
"""Convert authentik user into SCIM"""
|
||||||
raw_scim_group = super().to_schema(
|
raw_scim_group = super().to_schema(obj, connection)
|
||||||
obj,
|
|
||||||
connection,
|
|
||||||
schemas=(SCIM_GROUP_SCHEMA,),
|
|
||||||
)
|
|
||||||
try:
|
try:
|
||||||
scim_group = SCIMGroupSchema.model_validate(delete_none_values(raw_scim_group))
|
scim_group = SCIMGroupSchema.model_validate(delete_none_values(raw_scim_group))
|
||||||
except ValidationError as exc:
|
except ValidationError as exc:
|
||||||
raise StopSync(exc, obj) from exc
|
raise StopSync(exc, obj) from exc
|
||||||
|
if SCIM_GROUP_SCHEMA not in scim_group.schemas:
|
||||||
|
scim_group.schemas.insert(0, SCIM_GROUP_SCHEMA)
|
||||||
|
# As this might be unset, we need to tell pydantic it's set so ensure the schemas
|
||||||
|
# are included, even if its just the defaults
|
||||||
|
scim_group.schemas = list(scim_group.schemas)
|
||||||
if not scim_group.externalId:
|
if not scim_group.externalId:
|
||||||
scim_group.externalId = str(obj.pk)
|
scim_group.externalId = str(obj.pk)
|
||||||
|
|
||||||
|
@ -31,15 +31,16 @@ class SCIMUserClient(SCIMClient[User, SCIMProviderUser, SCIMUserSchema]):
|
|||||||
|
|
||||||
def to_schema(self, obj: User, connection: SCIMProviderUser) -> SCIMUserSchema:
|
def to_schema(self, obj: User, connection: SCIMProviderUser) -> SCIMUserSchema:
|
||||||
"""Convert authentik user into SCIM"""
|
"""Convert authentik user into SCIM"""
|
||||||
raw_scim_user = super().to_schema(
|
raw_scim_user = super().to_schema(obj, connection)
|
||||||
obj,
|
|
||||||
connection,
|
|
||||||
schemas=(SCIM_USER_SCHEMA,),
|
|
||||||
)
|
|
||||||
try:
|
try:
|
||||||
scim_user = SCIMUserSchema.model_validate(delete_none_values(raw_scim_user))
|
scim_user = SCIMUserSchema.model_validate(delete_none_values(raw_scim_user))
|
||||||
except ValidationError as exc:
|
except ValidationError as exc:
|
||||||
raise StopSync(exc, obj) from exc
|
raise StopSync(exc, obj) from exc
|
||||||
|
if SCIM_USER_SCHEMA not in scim_user.schemas:
|
||||||
|
scim_user.schemas.insert(0, SCIM_USER_SCHEMA)
|
||||||
|
# As this might be unset, we need to tell pydantic it's set so ensure the schemas
|
||||||
|
# are included, even if its just the defaults
|
||||||
|
scim_user.schemas = list(scim_user.schemas)
|
||||||
if not scim_user.externalId:
|
if not scim_user.externalId:
|
||||||
scim_user.externalId = str(obj.uid)
|
scim_user.externalId = str(obj.uid)
|
||||||
return scim_user
|
return scim_user
|
||||||
|
@ -3,14 +3,17 @@
|
|||||||
from json import loads
|
from json import loads
|
||||||
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
from django.utils.text import slugify
|
||||||
from jsonschema import validate
|
from jsonschema import validate
|
||||||
from requests_mock import Mocker
|
from requests_mock import Mocker
|
||||||
|
|
||||||
from authentik.blueprints.tests import apply_blueprint
|
from authentik.blueprints.tests import apply_blueprint
|
||||||
from authentik.core.models import Application, Group, User
|
from authentik.core.models import Application, Group, User
|
||||||
|
from authentik.events.models import SystemTask
|
||||||
from authentik.lib.generators import generate_id
|
from authentik.lib.generators import generate_id
|
||||||
from authentik.lib.sync.outgoing.base import SAFE_METHODS
|
from authentik.lib.sync.outgoing.base import SAFE_METHODS
|
||||||
from authentik.providers.scim.models import SCIMMapping, SCIMProvider
|
from authentik.providers.scim.models import SCIMMapping, SCIMProvider
|
||||||
|
from authentik.providers.scim.tasks import scim_sync, sync_tasks
|
||||||
from authentik.tenants.models import Tenant
|
from authentik.tenants.models import Tenant
|
||||||
|
|
||||||
|
|
||||||
@ -88,6 +91,57 @@ class SCIMUserTests(TestCase):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Mocker()
|
||||||
|
def test_user_create_custom_schema(self, mock: Mocker):
|
||||||
|
"""Test user creation with custom schema"""
|
||||||
|
schema = SCIMMapping.objects.create(
|
||||||
|
name="custom_schema",
|
||||||
|
expression="""return {"schemas": ["foo"]}""",
|
||||||
|
)
|
||||||
|
self.provider.property_mappings.add(schema)
|
||||||
|
scim_id = generate_id()
|
||||||
|
mock.get(
|
||||||
|
"https://localhost/ServiceProviderConfig",
|
||||||
|
json={},
|
||||||
|
)
|
||||||
|
mock.post(
|
||||||
|
"https://localhost/Users",
|
||||||
|
json={
|
||||||
|
"id": scim_id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
uid = generate_id()
|
||||||
|
user = User.objects.create(
|
||||||
|
username=uid,
|
||||||
|
name=f"{uid} {uid}",
|
||||||
|
email=f"{uid}@goauthentik.io",
|
||||||
|
)
|
||||||
|
self.assertEqual(mock.call_count, 2)
|
||||||
|
self.assertEqual(mock.request_history[0].method, "GET")
|
||||||
|
self.assertEqual(mock.request_history[1].method, "POST")
|
||||||
|
self.assertJSONEqual(
|
||||||
|
mock.request_history[1].body,
|
||||||
|
{
|
||||||
|
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User", "foo"],
|
||||||
|
"active": True,
|
||||||
|
"emails": [
|
||||||
|
{
|
||||||
|
"primary": True,
|
||||||
|
"type": "other",
|
||||||
|
"value": f"{uid}@goauthentik.io",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"externalId": user.uid,
|
||||||
|
"name": {
|
||||||
|
"familyName": uid,
|
||||||
|
"formatted": f"{uid} {uid}",
|
||||||
|
"givenName": uid,
|
||||||
|
},
|
||||||
|
"displayName": f"{uid} {uid}",
|
||||||
|
"userName": uid,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
@Mocker()
|
@Mocker()
|
||||||
def test_user_create_different_provider_same_id(self, mock: Mocker):
|
def test_user_create_different_provider_same_id(self, mock: Mocker):
|
||||||
"""Test user creation with multiple providers that happen
|
"""Test user creation with multiple providers that happen
|
||||||
@ -158,6 +212,7 @@ class SCIMUserTests(TestCase):
|
|||||||
def test_user_create_update(self, mock: Mocker):
|
def test_user_create_update(self, mock: Mocker):
|
||||||
"""Test user creation and update"""
|
"""Test user creation and update"""
|
||||||
scim_id = generate_id()
|
scim_id = generate_id()
|
||||||
|
mock: Mocker
|
||||||
mock.get(
|
mock.get(
|
||||||
"https://localhost/ServiceProviderConfig",
|
"https://localhost/ServiceProviderConfig",
|
||||||
json={},
|
json={},
|
||||||
@ -301,8 +356,7 @@ class SCIMUserTests(TestCase):
|
|||||||
email=f"{uid}@goauthentik.io",
|
email=f"{uid}@goauthentik.io",
|
||||||
)
|
)
|
||||||
|
|
||||||
for schedule in self.provider.schedules.all():
|
sync_tasks.trigger_single_task(self.provider, scim_sync).get()
|
||||||
schedule.send().get_result()
|
|
||||||
|
|
||||||
self.assertEqual(mock.call_count, 5)
|
self.assertEqual(mock.call_count, 5)
|
||||||
self.assertEqual(mock.request_history[0].method, "GET")
|
self.assertEqual(mock.request_history[0].method, "GET")
|
||||||
@ -374,17 +428,15 @@ class SCIMUserTests(TestCase):
|
|||||||
email=f"{uid}@goauthentik.io",
|
email=f"{uid}@goauthentik.io",
|
||||||
)
|
)
|
||||||
|
|
||||||
for schedule in self.provider.schedules.all():
|
sync_tasks.trigger_single_task(self.provider, scim_sync).get()
|
||||||
schedule.send().get_result()
|
|
||||||
|
|
||||||
self.assertEqual(mock.call_count, 3)
|
self.assertEqual(mock.call_count, 3)
|
||||||
for request in mock.request_history:
|
for request in mock.request_history:
|
||||||
self.assertIn(request.method, SAFE_METHODS)
|
self.assertIn(request.method, SAFE_METHODS)
|
||||||
# TODO: fixme
|
task = SystemTask.objects.filter(uid=slugify(self.provider.name)).first()
|
||||||
# task = SystemTask.objects.filter(uid=slugify(self.provider.name)).first()
|
self.assertIsNotNone(task)
|
||||||
# self.assertIsNotNone(task)
|
drop_msg = task.messages[3]
|
||||||
# drop_msg = task.messages[2]
|
self.assertEqual(drop_msg["event"], "Dropping mutating request due to dry run")
|
||||||
# self.assertEqual(drop_msg["event"], "Dropping mutating request due to dry run")
|
self.assertIsNotNone(drop_msg["attributes"]["url"])
|
||||||
# self.assertIsNotNone(drop_msg["attributes"]["url"])
|
self.assertIsNotNone(drop_msg["attributes"]["body"])
|
||||||
# self.assertIsNotNone(drop_msg["attributes"]["body"])
|
self.assertIsNotNone(drop_msg["attributes"]["method"])
|
||||||
# self.assertIsNotNone(drop_msg["attributes"]["method"])
|
|
||||||
|
@ -415,7 +415,7 @@ else:
|
|||||||
"BACKEND": "authentik.root.storages.FileStorage",
|
"BACKEND": "authentik.root.storages.FileStorage",
|
||||||
"OPTIONS": {
|
"OPTIONS": {
|
||||||
"location": Path(CONFIG.get("storage.media.file.path")),
|
"location": Path(CONFIG.get("storage.media.file.path")),
|
||||||
"base_url": "/media/",
|
"base_url": CONFIG.get("web.path", "/") + "media/",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
# Compatibility for apps not supporting top-level STORAGES
|
# Compatibility for apps not supporting top-level STORAGES
|
||||||
|
@ -32,6 +32,8 @@ class PytestTestRunner(DiscoverRunner): # pragma: no cover
|
|||||||
|
|
||||||
if kwargs.get("randomly_seed", None):
|
if kwargs.get("randomly_seed", None):
|
||||||
self.args.append(f"--randomly-seed={kwargs['randomly_seed']}")
|
self.args.append(f"--randomly-seed={kwargs['randomly_seed']}")
|
||||||
|
if kwargs.get("no_capture", False):
|
||||||
|
self.args.append("--capture=no")
|
||||||
|
|
||||||
settings.TEST = True
|
settings.TEST = True
|
||||||
settings.CELERY["task_always_eager"] = True
|
settings.CELERY["task_always_eager"] = True
|
||||||
@ -67,6 +69,11 @@ class PytestTestRunner(DiscoverRunner): # pragma: no cover
|
|||||||
"Default behaviour: use random.Random().getrandbits(32), so the seed is"
|
"Default behaviour: use random.Random().getrandbits(32), so the seed is"
|
||||||
"different on each run.",
|
"different on each run.",
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--no-capture",
|
||||||
|
action="store_true",
|
||||||
|
help="Disable any capturing of stdout/stderr during tests.",
|
||||||
|
)
|
||||||
|
|
||||||
def run_tests(self, test_labels, extra_tests=None, **kwargs):
|
def run_tests(self, test_labels, extra_tests=None, **kwargs):
|
||||||
"""Run pytest and return the exitcode.
|
"""Run pytest and return the exitcode.
|
||||||
|
@ -103,6 +103,7 @@ class LDAPSourceSerializer(SourceSerializer):
|
|||||||
"user_object_filter",
|
"user_object_filter",
|
||||||
"group_object_filter",
|
"group_object_filter",
|
||||||
"group_membership_field",
|
"group_membership_field",
|
||||||
|
"user_membership_attribute",
|
||||||
"object_uniqueness_field",
|
"object_uniqueness_field",
|
||||||
"password_login_update_internal_password",
|
"password_login_update_internal_password",
|
||||||
"sync_users",
|
"sync_users",
|
||||||
@ -111,6 +112,7 @@ class LDAPSourceSerializer(SourceSerializer):
|
|||||||
"sync_parent_group",
|
"sync_parent_group",
|
||||||
"connectivity",
|
"connectivity",
|
||||||
"lookup_groups_from_user",
|
"lookup_groups_from_user",
|
||||||
|
"delete_not_found_objects",
|
||||||
]
|
]
|
||||||
extra_kwargs = {"bind_password": {"write_only": True}}
|
extra_kwargs = {"bind_password": {"write_only": True}}
|
||||||
|
|
||||||
@ -138,6 +140,7 @@ class LDAPSourceViewSet(UsedByMixin, ModelViewSet):
|
|||||||
"user_object_filter",
|
"user_object_filter",
|
||||||
"group_object_filter",
|
"group_object_filter",
|
||||||
"group_membership_field",
|
"group_membership_field",
|
||||||
|
"user_membership_attribute",
|
||||||
"object_uniqueness_field",
|
"object_uniqueness_field",
|
||||||
"password_login_update_internal_password",
|
"password_login_update_internal_password",
|
||||||
"sync_users",
|
"sync_users",
|
||||||
@ -147,6 +150,7 @@ class LDAPSourceViewSet(UsedByMixin, ModelViewSet):
|
|||||||
"user_property_mappings",
|
"user_property_mappings",
|
||||||
"group_property_mappings",
|
"group_property_mappings",
|
||||||
"lookup_groups_from_user",
|
"lookup_groups_from_user",
|
||||||
|
"delete_not_found_objects",
|
||||||
]
|
]
|
||||||
search_fields = ["name", "slug"]
|
search_fields = ["name", "slug"]
|
||||||
ordering = ["name"]
|
ordering = ["name"]
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
# Generated by Django 5.1.9 on 2025-05-28 08:15
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("authentik_core", "0048_delete_oldauthenticatedsession_content_type"),
|
||||||
|
("authentik_sources_ldap", "0008_groupldapsourceconnection_userldapsourceconnection"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="groupldapsourceconnection",
|
||||||
|
name="validated_by",
|
||||||
|
field=models.UUIDField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Unique ID used while checking if this object still exists in the directory.",
|
||||||
|
null=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="ldapsource",
|
||||||
|
name="delete_not_found_objects",
|
||||||
|
field=models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text="Delete authentik users and groups which were previously supplied by this source, but are now missing from it.",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="userldapsourceconnection",
|
||||||
|
name="validated_by",
|
||||||
|
field=models.UUIDField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Unique ID used while checking if this object still exists in the directory.",
|
||||||
|
null=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="groupldapsourceconnection",
|
||||||
|
index=models.Index(fields=["validated_by"], name="authentik_s_validat_b70447_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="userldapsourceconnection",
|
||||||
|
index=models.Index(fields=["validated_by"], name="authentik_s_validat_ff2ebc_idx"),
|
||||||
|
),
|
||||||
|
]
|
@ -0,0 +1,32 @@
|
|||||||
|
# Generated by Django 5.1.9 on 2025-05-29 11:22
|
||||||
|
|
||||||
|
from django.apps.registry import Apps
|
||||||
|
from django.db import migrations, models
|
||||||
|
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
||||||
|
|
||||||
|
|
||||||
|
def set_user_membership_attribute(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
|
||||||
|
LDAPSource = apps.get_model("authentik_sources_ldap", "LDAPSource")
|
||||||
|
db_alias = schema_editor.connection.alias
|
||||||
|
|
||||||
|
LDAPSource.objects.using(db_alias).filter(group_membership_field="memberUid").all().update(
|
||||||
|
user_membership_attribute="ldap_uniq"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("authentik_sources_ldap", "0009_groupldapsourceconnection_validated_by_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="ldapsource",
|
||||||
|
name="user_membership_attribute",
|
||||||
|
field=models.TextField(
|
||||||
|
default="distinguishedName",
|
||||||
|
help_text="Attribute which matches the value of `group_membership_field`.",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.RunPython(set_user_membership_attribute, migrations.RunPython.noop),
|
||||||
|
]
|
@ -103,6 +103,10 @@ class LDAPSource(ScheduledModel, Source):
|
|||||||
default="(objectClass=person)",
|
default="(objectClass=person)",
|
||||||
help_text=_("Consider Objects matching this filter to be Users."),
|
help_text=_("Consider Objects matching this filter to be Users."),
|
||||||
)
|
)
|
||||||
|
user_membership_attribute = models.TextField(
|
||||||
|
default=LDAP_DISTINGUISHED_NAME,
|
||||||
|
help_text=_("Attribute which matches the value of `group_membership_field`."),
|
||||||
|
)
|
||||||
group_membership_field = models.TextField(
|
group_membership_field = models.TextField(
|
||||||
default="member", help_text=_("Field which contains members of a group.")
|
default="member", help_text=_("Field which contains members of a group.")
|
||||||
)
|
)
|
||||||
@ -140,6 +144,14 @@ class LDAPSource(ScheduledModel, Source):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
delete_not_found_objects = models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text=_(
|
||||||
|
"Delete authentik users and groups which were previously supplied by this source, "
|
||||||
|
"but are now missing from it."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def component(self) -> str:
|
def component(self) -> str:
|
||||||
return "ak-source-ldap-form"
|
return "ak-source-ldap-form"
|
||||||
@ -343,6 +355,12 @@ class LDAPSourcePropertyMapping(PropertyMapping):
|
|||||||
|
|
||||||
|
|
||||||
class UserLDAPSourceConnection(UserSourceConnection):
|
class UserLDAPSourceConnection(UserSourceConnection):
|
||||||
|
validated_by = models.UUIDField(
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
help_text=_("Unique ID used while checking if this object still exists in the directory."),
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def serializer(self) -> type[Serializer]:
|
def serializer(self) -> type[Serializer]:
|
||||||
from authentik.sources.ldap.api import (
|
from authentik.sources.ldap.api import (
|
||||||
@ -354,9 +372,18 @@ class UserLDAPSourceConnection(UserSourceConnection):
|
|||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("User LDAP Source Connection")
|
verbose_name = _("User LDAP Source Connection")
|
||||||
verbose_name_plural = _("User LDAP Source Connections")
|
verbose_name_plural = _("User LDAP Source Connections")
|
||||||
|
indexes = [
|
||||||
|
models.Index(fields=["validated_by"]),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class GroupLDAPSourceConnection(GroupSourceConnection):
|
class GroupLDAPSourceConnection(GroupSourceConnection):
|
||||||
|
validated_by = models.UUIDField(
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
help_text=_("Unique ID used while checking if this object still exists in the directory."),
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def serializer(self) -> type[Serializer]:
|
def serializer(self) -> type[Serializer]:
|
||||||
from authentik.sources.ldap.api import (
|
from authentik.sources.ldap.api import (
|
||||||
@ -368,3 +395,6 @@ class GroupLDAPSourceConnection(GroupSourceConnection):
|
|||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("Group LDAP Source Connection")
|
verbose_name = _("Group LDAP Source Connection")
|
||||||
verbose_name_plural = _("Group LDAP Source Connections")
|
verbose_name_plural = _("Group LDAP Source Connections")
|
||||||
|
indexes = [
|
||||||
|
models.Index(fields=["validated_by"]),
|
||||||
|
]
|
||||||
|
@ -9,7 +9,7 @@ from structlog.stdlib import BoundLogger, get_logger
|
|||||||
from authentik.core.sources.mapper import SourceMapper
|
from authentik.core.sources.mapper import SourceMapper
|
||||||
from authentik.lib.config import CONFIG
|
from authentik.lib.config import CONFIG
|
||||||
from authentik.lib.sync.mapper import PropertyMappingManager
|
from authentik.lib.sync.mapper import PropertyMappingManager
|
||||||
from authentik.sources.ldap.models import LDAPSource
|
from authentik.sources.ldap.models import LDAPSource, flatten
|
||||||
|
|
||||||
|
|
||||||
class BaseLDAPSynchronizer:
|
class BaseLDAPSynchronizer:
|
||||||
@ -77,6 +77,16 @@ class BaseLDAPSynchronizer:
|
|||||||
"""Get objects from LDAP, implemented in subclass"""
|
"""Get objects from LDAP, implemented in subclass"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def get_attributes(self, object):
|
||||||
|
if "attributes" not in object:
|
||||||
|
return
|
||||||
|
return object.get("attributes", {})
|
||||||
|
|
||||||
|
def get_identifier(self, attributes: dict):
|
||||||
|
if not attributes.get(self._source.object_uniqueness_field):
|
||||||
|
return
|
||||||
|
return flatten(attributes[self._source.object_uniqueness_field])
|
||||||
|
|
||||||
def search_paginator( # noqa: PLR0913
|
def search_paginator( # noqa: PLR0913
|
||||||
self,
|
self,
|
||||||
search_base,
|
search_base,
|
||||||
|
61
authentik/sources/ldap/sync/forward_delete_groups.py
Normal file
61
authentik/sources/ldap/sync/forward_delete_groups.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
from collections.abc import Generator
|
||||||
|
from itertools import batched
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
|
from ldap3 import SUBTREE
|
||||||
|
|
||||||
|
from authentik.core.models import Group
|
||||||
|
from authentik.sources.ldap.models import GroupLDAPSourceConnection
|
||||||
|
from authentik.sources.ldap.sync.base import BaseLDAPSynchronizer
|
||||||
|
from authentik.sources.ldap.sync.forward_delete_users import DELETE_CHUNK_SIZE, UPDATE_CHUNK_SIZE
|
||||||
|
|
||||||
|
|
||||||
|
class GroupLDAPForwardDeletion(BaseLDAPSynchronizer):
|
||||||
|
"""Delete LDAP Groups from authentik"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def name() -> str:
|
||||||
|
return "group_deletions"
|
||||||
|
|
||||||
|
def get_objects(self, **kwargs) -> Generator:
|
||||||
|
if not self._source.sync_groups or not self._source.delete_not_found_objects:
|
||||||
|
self.message("Group syncing is disabled for this Source")
|
||||||
|
return iter(())
|
||||||
|
|
||||||
|
uuid = uuid4()
|
||||||
|
groups = self._source.connection().extend.standard.paged_search(
|
||||||
|
search_base=self.base_dn_groups,
|
||||||
|
search_filter=self._source.group_object_filter,
|
||||||
|
search_scope=SUBTREE,
|
||||||
|
attributes=[self._source.object_uniqueness_field],
|
||||||
|
generator=True,
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
for batch in batched(groups, UPDATE_CHUNK_SIZE, strict=False):
|
||||||
|
identifiers = []
|
||||||
|
for group in batch:
|
||||||
|
if not (attributes := self.get_attributes(group)):
|
||||||
|
continue
|
||||||
|
if identifier := self.get_identifier(attributes):
|
||||||
|
identifiers.append(identifier)
|
||||||
|
GroupLDAPSourceConnection.objects.filter(identifier__in=identifiers).update(
|
||||||
|
validated_by=uuid
|
||||||
|
)
|
||||||
|
|
||||||
|
return batched(
|
||||||
|
GroupLDAPSourceConnection.objects.filter(source=self._source)
|
||||||
|
.exclude(validated_by=uuid)
|
||||||
|
.values_list("group", flat=True)
|
||||||
|
.iterator(chunk_size=DELETE_CHUNK_SIZE),
|
||||||
|
DELETE_CHUNK_SIZE,
|
||||||
|
strict=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
def sync(self, group_pks: tuple) -> int:
|
||||||
|
"""Delete authentik groups"""
|
||||||
|
if not self._source.sync_groups or not self._source.delete_not_found_objects:
|
||||||
|
self.message("Group syncing is disabled for this Source")
|
||||||
|
return -1
|
||||||
|
self._logger.debug("Deleting groups", group_pks=group_pks)
|
||||||
|
_, deleted_per_type = Group.objects.filter(pk__in=group_pks).delete()
|
||||||
|
return deleted_per_type.get(Group._meta.label, 0)
|
63
authentik/sources/ldap/sync/forward_delete_users.py
Normal file
63
authentik/sources/ldap/sync/forward_delete_users.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
from collections.abc import Generator
|
||||||
|
from itertools import batched
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
|
from ldap3 import SUBTREE
|
||||||
|
|
||||||
|
from authentik.core.models import User
|
||||||
|
from authentik.sources.ldap.models import UserLDAPSourceConnection
|
||||||
|
from authentik.sources.ldap.sync.base import BaseLDAPSynchronizer
|
||||||
|
|
||||||
|
UPDATE_CHUNK_SIZE = 10_000
|
||||||
|
DELETE_CHUNK_SIZE = 50
|
||||||
|
|
||||||
|
|
||||||
|
class UserLDAPForwardDeletion(BaseLDAPSynchronizer):
|
||||||
|
"""Delete LDAP Users from authentik"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def name() -> str:
|
||||||
|
return "user_deletions"
|
||||||
|
|
||||||
|
def get_objects(self, **kwargs) -> Generator:
|
||||||
|
if not self._source.sync_users or not self._source.delete_not_found_objects:
|
||||||
|
self.message("User syncing is disabled for this Source")
|
||||||
|
return iter(())
|
||||||
|
|
||||||
|
uuid = uuid4()
|
||||||
|
users = self._source.connection().extend.standard.paged_search(
|
||||||
|
search_base=self.base_dn_users,
|
||||||
|
search_filter=self._source.user_object_filter,
|
||||||
|
search_scope=SUBTREE,
|
||||||
|
attributes=[self._source.object_uniqueness_field],
|
||||||
|
generator=True,
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
for batch in batched(users, UPDATE_CHUNK_SIZE, strict=False):
|
||||||
|
identifiers = []
|
||||||
|
for user in batch:
|
||||||
|
if not (attributes := self.get_attributes(user)):
|
||||||
|
continue
|
||||||
|
if identifier := self.get_identifier(attributes):
|
||||||
|
identifiers.append(identifier)
|
||||||
|
UserLDAPSourceConnection.objects.filter(identifier__in=identifiers).update(
|
||||||
|
validated_by=uuid
|
||||||
|
)
|
||||||
|
|
||||||
|
return batched(
|
||||||
|
UserLDAPSourceConnection.objects.filter(source=self._source)
|
||||||
|
.exclude(validated_by=uuid)
|
||||||
|
.values_list("user", flat=True)
|
||||||
|
.iterator(chunk_size=DELETE_CHUNK_SIZE),
|
||||||
|
DELETE_CHUNK_SIZE,
|
||||||
|
strict=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
def sync(self, user_pks: tuple) -> int:
|
||||||
|
"""Delete authentik users"""
|
||||||
|
if not self._source.sync_users or not self._source.delete_not_found_objects:
|
||||||
|
self.message("User syncing is disabled for this Source")
|
||||||
|
return -1
|
||||||
|
self._logger.debug("Deleting users", user_pks=user_pks)
|
||||||
|
_, deleted_per_type = User.objects.filter(pk__in=user_pks).delete()
|
||||||
|
return deleted_per_type.get(User._meta.label, 0)
|
@ -58,18 +58,16 @@ class GroupLDAPSynchronizer(BaseLDAPSynchronizer):
|
|||||||
return -1
|
return -1
|
||||||
group_count = 0
|
group_count = 0
|
||||||
for group in page_data:
|
for group in page_data:
|
||||||
if "attributes" not in group:
|
if (attributes := self.get_attributes(group)) is None:
|
||||||
continue
|
continue
|
||||||
attributes = group.get("attributes", {})
|
|
||||||
group_dn = flatten(flatten(group.get("entryDN", group.get("dn"))))
|
group_dn = flatten(flatten(group.get("entryDN", group.get("dn"))))
|
||||||
if not attributes.get(self._source.object_uniqueness_field):
|
if not (uniq := self.get_identifier(attributes)):
|
||||||
self.message(
|
self.message(
|
||||||
f"Uniqueness field not found/not set in attributes: '{group_dn}'",
|
f"Uniqueness field not found/not set in attributes: '{group_dn}'",
|
||||||
attributes=attributes.keys(),
|
attributes=attributes.keys(),
|
||||||
dn=group_dn,
|
dn=group_dn,
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
uniq = flatten(attributes[self._source.object_uniqueness_field])
|
|
||||||
try:
|
try:
|
||||||
defaults = {
|
defaults = {
|
||||||
k: flatten(v)
|
k: flatten(v)
|
||||||
|
@ -63,25 +63,19 @@ class MembershipLDAPSynchronizer(BaseLDAPSynchronizer):
|
|||||||
group_member_dn = group_member.get("dn", {})
|
group_member_dn = group_member.get("dn", {})
|
||||||
members.append(group_member_dn)
|
members.append(group_member_dn)
|
||||||
else:
|
else:
|
||||||
if "attributes" not in group:
|
if (attributes := self.get_attributes(group)) is None:
|
||||||
continue
|
continue
|
||||||
members = group.get("attributes", {}).get(self._source.group_membership_field, [])
|
members = attributes.get(self._source.group_membership_field, [])
|
||||||
|
|
||||||
ak_group = self.get_group(group)
|
ak_group = self.get_group(group)
|
||||||
if not ak_group:
|
if not ak_group:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
membership_mapping_attribute = LDAP_DISTINGUISHED_NAME
|
|
||||||
if self._source.group_membership_field == "memberUid":
|
|
||||||
# If memberships are based on the posixGroup's 'memberUid'
|
|
||||||
# attribute we use the RDN instead of the FDN to lookup members.
|
|
||||||
membership_mapping_attribute = LDAP_UNIQUENESS
|
|
||||||
|
|
||||||
users = User.objects.filter(
|
users = User.objects.filter(
|
||||||
Q(**{f"attributes__{membership_mapping_attribute}__in": members})
|
Q(**{f"attributes__{self._source.user_membership_attribute}__in": members})
|
||||||
| Q(
|
| Q(
|
||||||
**{
|
**{
|
||||||
f"attributes__{membership_mapping_attribute}__isnull": True,
|
f"attributes__{self._source.user_membership_attribute}__isnull": True,
|
||||||
"ak_groups__in": [ak_group],
|
"ak_groups__in": [ak_group],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -60,18 +60,16 @@ class UserLDAPSynchronizer(BaseLDAPSynchronizer):
|
|||||||
return -1
|
return -1
|
||||||
user_count = 0
|
user_count = 0
|
||||||
for user in page_data:
|
for user in page_data:
|
||||||
if "attributes" not in user:
|
if (attributes := self.get_attributes(user)) is None:
|
||||||
continue
|
continue
|
||||||
attributes = user.get("attributes", {})
|
|
||||||
user_dn = flatten(user.get("entryDN", user.get("dn")))
|
user_dn = flatten(user.get("entryDN", user.get("dn")))
|
||||||
if not attributes.get(self._source.object_uniqueness_field):
|
if not (uniq := self.get_identifier(attributes)):
|
||||||
self.message(
|
self.message(
|
||||||
f"Uniqueness field not found/not set in attributes: '{user_dn}'",
|
f"Uniqueness field not found/not set in attributes: '{user_dn}'",
|
||||||
attributes=attributes.keys(),
|
attributes=attributes.keys(),
|
||||||
dn=user_dn,
|
dn=user_dn,
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
uniq = flatten(attributes[self._source.object_uniqueness_field])
|
|
||||||
try:
|
try:
|
||||||
defaults = {
|
defaults = {
|
||||||
k: flatten(v)
|
k: flatten(v)
|
||||||
|
@ -2,23 +2,26 @@
|
|||||||
|
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
|
from celery import chain, group
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from dramatiq.actor import actor
|
|
||||||
from dramatiq.composition import group
|
|
||||||
from ldap3.core.exceptions import LDAPException
|
from ldap3.core.exceptions import LDAPException
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
|
from authentik.events.models import SystemTask as DBSystemTask
|
||||||
|
from authentik.events.models import TaskStatus
|
||||||
|
from authentik.events.system_tasks import SystemTask
|
||||||
from authentik.lib.config import CONFIG
|
from authentik.lib.config import CONFIG
|
||||||
from authentik.lib.sync.outgoing.exceptions import StopSync
|
from authentik.lib.sync.outgoing.exceptions import StopSync
|
||||||
from authentik.lib.utils.errors import exception_to_string
|
from authentik.lib.utils.errors import exception_to_string
|
||||||
from authentik.lib.utils.reflection import class_to_path, path_to_class
|
from authentik.lib.utils.reflection import class_to_path, path_to_class
|
||||||
|
from authentik.root.celery import CELERY_APP
|
||||||
from authentik.sources.ldap.models import LDAPSource
|
from authentik.sources.ldap.models import LDAPSource
|
||||||
from authentik.sources.ldap.sync.base import BaseLDAPSynchronizer
|
from authentik.sources.ldap.sync.base import BaseLDAPSynchronizer
|
||||||
|
from authentik.sources.ldap.sync.forward_delete_groups import GroupLDAPForwardDeletion
|
||||||
|
from authentik.sources.ldap.sync.forward_delete_users import UserLDAPForwardDeletion
|
||||||
from authentik.sources.ldap.sync.groups import GroupLDAPSynchronizer
|
from authentik.sources.ldap.sync.groups import GroupLDAPSynchronizer
|
||||||
from authentik.sources.ldap.sync.membership import MembershipLDAPSynchronizer
|
from authentik.sources.ldap.sync.membership import MembershipLDAPSynchronizer
|
||||||
from authentik.sources.ldap.sync.users import UserLDAPSynchronizer
|
from authentik.sources.ldap.sync.users import UserLDAPSynchronizer
|
||||||
from authentik.tasks.middleware import CurrentTask
|
|
||||||
from authentik.tasks.models import Task, TaskStatus
|
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
SYNC_CLASSES = [
|
SYNC_CLASSES = [
|
||||||
@ -30,87 +33,102 @@ CACHE_KEY_PREFIX = "goauthentik.io/sources/ldap/page/"
|
|||||||
CACHE_KEY_STATUS = "goauthentik.io/sources/ldap/status/"
|
CACHE_KEY_STATUS = "goauthentik.io/sources/ldap/status/"
|
||||||
|
|
||||||
|
|
||||||
@actor
|
@CELERY_APP.task()
|
||||||
def ldap_connectivity_check(source_pk: str):
|
def ldap_sync_all():
|
||||||
|
"""Sync all sources"""
|
||||||
|
for source in LDAPSource.objects.filter(enabled=True):
|
||||||
|
ldap_sync_single.apply_async(args=[str(source.pk)])
|
||||||
|
|
||||||
|
|
||||||
|
@CELERY_APP.task()
|
||||||
|
def ldap_connectivity_check(pk: str | None = None):
|
||||||
"""Check connectivity for LDAP Sources"""
|
"""Check connectivity for LDAP Sources"""
|
||||||
# 2 hour timeout, this task should run every hour
|
# 2 hour timeout, this task should run every hour
|
||||||
timeout = 60 * 60 * 2
|
timeout = 60 * 60 * 2
|
||||||
source = LDAPSource.objects.filter(enabled=True, pk=source_pk).first()
|
sources = LDAPSource.objects.filter(enabled=True)
|
||||||
if not source:
|
if pk:
|
||||||
return
|
sources = sources.filter(pk=pk)
|
||||||
status = source.check_connection()
|
for source in sources:
|
||||||
cache.set(CACHE_KEY_STATUS + source.slug, status, timeout=timeout)
|
status = source.check_connection()
|
||||||
|
cache.set(CACHE_KEY_STATUS + source.slug, status, timeout=timeout)
|
||||||
|
|
||||||
|
|
||||||
# We take the configured hours timeout time by 2.5 as we run user and
|
@CELERY_APP.task(
|
||||||
# group in parallel and then membership, so 2x is to cover the serial tasks,
|
# We take the configured hours timeout time by 3.5 as we run user and
|
||||||
# and 0.5x on top of that to give some more leeway
|
# group in parallel and then membership, then deletions, so 3x is to cover the serial tasks,
|
||||||
@actor(time_limit=(60 * 60 * CONFIG.get_int("ldap.task_timeout_hours")) * 2.5 * 1000)
|
# and 0.5x on top of that to give some more leeway
|
||||||
def ldap_sync(source_pk: str):
|
soft_time_limit=(60 * 60 * CONFIG.get_int("ldap.task_timeout_hours")) * 3.5,
|
||||||
|
task_time_limit=(60 * 60 * CONFIG.get_int("ldap.task_timeout_hours")) * 3.5,
|
||||||
|
)
|
||||||
|
def ldap_sync_single(source_pk: str):
|
||||||
"""Sync a single source"""
|
"""Sync a single source"""
|
||||||
self: Task = CurrentTask.get_task()
|
|
||||||
source: LDAPSource = LDAPSource.objects.filter(pk=source_pk).first()
|
source: LDAPSource = LDAPSource.objects.filter(pk=source_pk).first()
|
||||||
if not source:
|
if not source:
|
||||||
return
|
return
|
||||||
# Don't sync sources when they don't have any property mappings. This will only happen if:
|
|
||||||
# - the user forgets to set them or
|
|
||||||
# - the source is newly created, the mappings are save a bit later, which might cause invalid
|
|
||||||
# data
|
|
||||||
if source.sync_users and not source.user_property_mappings.exists():
|
|
||||||
# TODO: add to task messages
|
|
||||||
LOGGER.warning(
|
|
||||||
"LDAP source has user sync enabled but does not have user property mappings configured, not syncing", # noqa: E501
|
|
||||||
source=source.slug,
|
|
||||||
)
|
|
||||||
return
|
|
||||||
if source.sync_groups and not source.group_property_mappings.exists():
|
|
||||||
# TODO: add to task messages
|
|
||||||
LOGGER.warning(
|
|
||||||
"LDAP source has group sync enabled but does not have group property mappings configured, not syncing", # noqa: E501
|
|
||||||
source=source.slug,
|
|
||||||
)
|
|
||||||
return
|
|
||||||
with source.sync_lock as lock_acquired:
|
with source.sync_lock as lock_acquired:
|
||||||
if not lock_acquired:
|
if not lock_acquired:
|
||||||
LOGGER.debug("Failed to acquire lock for LDAP sync, skipping task", source=source.slug)
|
LOGGER.debug("Failed to acquire lock for LDAP sync, skipping task", source=source.slug)
|
||||||
return
|
return
|
||||||
# User and group sync can happen at once, they have no dependencies on each other
|
# Delete all sync tasks from the cache
|
||||||
task_users_group = group(
|
DBSystemTask.objects.filter(name="ldap_sync", uid__startswith=source.slug).delete()
|
||||||
ldap_sync_paginator(source, UserLDAPSynchronizer, schedule_uid=self.schedule_uid)
|
task = chain(
|
||||||
+ ldap_sync_paginator(source, GroupLDAPSynchronizer, schedule_uid=self.schedule_uid),
|
# User and group sync can happen at once, they have no dependencies on each other
|
||||||
|
group(
|
||||||
|
ldap_sync_paginator(source, UserLDAPSynchronizer)
|
||||||
|
+ ldap_sync_paginator(source, GroupLDAPSynchronizer),
|
||||||
|
),
|
||||||
|
# Membership sync needs to run afterwards
|
||||||
|
group(
|
||||||
|
ldap_sync_paginator(source, MembershipLDAPSynchronizer),
|
||||||
|
),
|
||||||
|
# Finally, deletions. What we'd really like to do here is something like
|
||||||
|
# ```
|
||||||
|
# user_identifiers = <ldap query>
|
||||||
|
# User.objects.exclude(
|
||||||
|
# usersourceconnection__identifier__in=user_uniqueness_identifiers,
|
||||||
|
# ).delete()
|
||||||
|
# ```
|
||||||
|
# This runs into performance issues in large installations. So instead we spread the
|
||||||
|
# work out into three steps:
|
||||||
|
# 1. Get every object from the LDAP source.
|
||||||
|
# 2. Mark every object as "safe" in the database. This is quick, but any error could
|
||||||
|
# mean deleting users which should not be deleted, so we do it immediately, in
|
||||||
|
# large chunks, and only queue the deletion step afterwards.
|
||||||
|
# 3. Delete every unmarked item. This is slow, so we spread it over many tasks in
|
||||||
|
# small chunks.
|
||||||
|
group(
|
||||||
|
ldap_sync_paginator(source, UserLDAPForwardDeletion)
|
||||||
|
+ ldap_sync_paginator(source, GroupLDAPForwardDeletion),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
task_users_group.run()
|
task()
|
||||||
task_users_group.wait(timeout=60 * 60 * CONFIG.get_int("ldap.task_timeout_hours") * 1000)
|
|
||||||
# Membership sync needs to run afterwards
|
|
||||||
task_membership = group(
|
|
||||||
ldap_sync_paginator(source, MembershipLDAPSynchronizer, schedule_uid=self.schedule_uid),
|
|
||||||
)
|
|
||||||
task_membership.run()
|
|
||||||
task_membership.wait(timeout=60 * 60 * CONFIG.get_int("ldap.task_timeout_hours") * 1000)
|
|
||||||
|
|
||||||
|
|
||||||
def ldap_sync_paginator(source: LDAPSource, sync: type[BaseLDAPSynchronizer], **options) -> list:
|
def ldap_sync_paginator(source: LDAPSource, sync: type[BaseLDAPSynchronizer]) -> list:
|
||||||
"""Return a list of task signatures with LDAP pagination data"""
|
"""Return a list of task signatures with LDAP pagination data"""
|
||||||
sync_inst: BaseLDAPSynchronizer = sync(source)
|
sync_inst: BaseLDAPSynchronizer = sync(source)
|
||||||
tasks = []
|
signatures = []
|
||||||
for page in sync_inst.get_objects():
|
for page in sync_inst.get_objects():
|
||||||
page_cache_key = CACHE_KEY_PREFIX + str(uuid4())
|
page_cache_key = CACHE_KEY_PREFIX + str(uuid4())
|
||||||
cache.set(page_cache_key, page, 60 * 60 * CONFIG.get_int("ldap.task_timeout_hours"))
|
cache.set(page_cache_key, page, 60 * 60 * CONFIG.get_int("ldap.task_timeout_hours"))
|
||||||
page_sync = ldap_sync_page.message_with_options(
|
page_sync = ldap_sync.si(str(source.pk), class_to_path(sync), page_cache_key)
|
||||||
args=(source.pk, class_to_path(sync), page_cache_key),
|
signatures.append(page_sync)
|
||||||
**options,
|
return signatures
|
||||||
)
|
|
||||||
tasks.append(page_sync)
|
|
||||||
return tasks
|
|
||||||
|
|
||||||
|
|
||||||
@actor(time_limit=60 * 60 * CONFIG.get_int("ldap.task_timeout_hours") * 1000)
|
@CELERY_APP.task(
|
||||||
def ldap_sync_page(source_pk: str, sync_class: str, page_cache_key: str):
|
bind=True,
|
||||||
|
base=SystemTask,
|
||||||
|
soft_time_limit=60 * 60 * CONFIG.get_int("ldap.task_timeout_hours"),
|
||||||
|
task_time_limit=60 * 60 * CONFIG.get_int("ldap.task_timeout_hours"),
|
||||||
|
)
|
||||||
|
def ldap_sync(self: SystemTask, source_pk: str, sync_class: str, page_cache_key: str):
|
||||||
"""Synchronization of an LDAP Source"""
|
"""Synchronization of an LDAP Source"""
|
||||||
self: Task = CurrentTask.get_task()
|
self.result_timeout_hours = CONFIG.get_int("ldap.task_timeout_hours")
|
||||||
# self.result_timeout_hours = CONFIG.get_int("ldap.task_timeout_hours")
|
|
||||||
source: LDAPSource = LDAPSource.objects.filter(pk=source_pk).first()
|
source: LDAPSource = LDAPSource.objects.filter(pk=source_pk).first()
|
||||||
if not source:
|
if not source:
|
||||||
|
# Because the source couldn't be found, we don't have a UID
|
||||||
|
# to set the state with
|
||||||
return
|
return
|
||||||
sync: type[BaseLDAPSynchronizer] = path_to_class(sync_class)
|
sync: type[BaseLDAPSynchronizer] = path_to_class(sync_class)
|
||||||
uid = page_cache_key.replace(CACHE_KEY_PREFIX, "")
|
uid = page_cache_key.replace(CACHE_KEY_PREFIX, "")
|
||||||
|
@ -2,6 +2,33 @@
|
|||||||
|
|
||||||
from ldap3 import MOCK_SYNC, OFFLINE_SLAPD_2_4, Connection, Server
|
from ldap3 import MOCK_SYNC, OFFLINE_SLAPD_2_4, Connection, Server
|
||||||
|
|
||||||
|
# The mock modifies these in place, so we have to define them per string
|
||||||
|
user_in_slapd_dn = "cn=user_in_slapd_cn,ou=users,dc=goauthentik,dc=io"
|
||||||
|
user_in_slapd_cn = "user_in_slapd_cn"
|
||||||
|
user_in_slapd_uid = "user_in_slapd_uid"
|
||||||
|
user_in_slapd_object_class = "person"
|
||||||
|
user_in_slapd = {
|
||||||
|
"dn": user_in_slapd_dn,
|
||||||
|
"attributes": {
|
||||||
|
"cn": user_in_slapd_cn,
|
||||||
|
"uid": user_in_slapd_uid,
|
||||||
|
"objectClass": user_in_slapd_object_class,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
group_in_slapd_dn = "cn=user_in_slapd_cn,ou=groups,dc=goauthentik,dc=io"
|
||||||
|
group_in_slapd_cn = "group_in_slapd_cn"
|
||||||
|
group_in_slapd_uid = "group_in_slapd_uid"
|
||||||
|
group_in_slapd_object_class = "groupOfNames"
|
||||||
|
group_in_slapd = {
|
||||||
|
"dn": group_in_slapd_dn,
|
||||||
|
"attributes": {
|
||||||
|
"cn": group_in_slapd_cn,
|
||||||
|
"uid": group_in_slapd_uid,
|
||||||
|
"objectClass": group_in_slapd_object_class,
|
||||||
|
"member": [user_in_slapd["dn"]],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def mock_slapd_connection(password: str) -> Connection:
|
def mock_slapd_connection(password: str) -> Connection:
|
||||||
"""Create mock SLAPD connection"""
|
"""Create mock SLAPD connection"""
|
||||||
@ -96,5 +123,14 @@ def mock_slapd_connection(password: str) -> Connection:
|
|||||||
"objectClass": "posixAccount",
|
"objectClass": "posixAccount",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
# Known user and group
|
||||||
|
connection.strategy.add_entry(
|
||||||
|
user_in_slapd["dn"],
|
||||||
|
user_in_slapd["attributes"],
|
||||||
|
)
|
||||||
|
connection.strategy.add_entry(
|
||||||
|
group_in_slapd["dn"],
|
||||||
|
group_in_slapd["attributes"],
|
||||||
|
)
|
||||||
connection.bind()
|
connection.bind()
|
||||||
return connection
|
return connection
|
||||||
|
@ -8,15 +8,31 @@ from django.test import TestCase
|
|||||||
from authentik.blueprints.tests import apply_blueprint
|
from authentik.blueprints.tests import apply_blueprint
|
||||||
from authentik.core.models import Group, User
|
from authentik.core.models import Group, User
|
||||||
from authentik.core.tests.utils import create_test_admin_user
|
from authentik.core.tests.utils import create_test_admin_user
|
||||||
from authentik.events.models import Event, EventAction
|
from authentik.events.models import Event, EventAction, SystemTask
|
||||||
|
from authentik.events.system_tasks import TaskStatus
|
||||||
from authentik.lib.generators import generate_id, generate_key
|
from authentik.lib.generators import generate_id, generate_key
|
||||||
from authentik.lib.sync.outgoing.exceptions import StopSync
|
from authentik.lib.sync.outgoing.exceptions import StopSync
|
||||||
from authentik.sources.ldap.models import LDAPSource, LDAPSourcePropertyMapping
|
from authentik.lib.utils.reflection import class_to_path
|
||||||
|
from authentik.sources.ldap.models import (
|
||||||
|
GroupLDAPSourceConnection,
|
||||||
|
LDAPSource,
|
||||||
|
LDAPSourcePropertyMapping,
|
||||||
|
UserLDAPSourceConnection,
|
||||||
|
)
|
||||||
|
from authentik.sources.ldap.sync.forward_delete_users import DELETE_CHUNK_SIZE
|
||||||
|
from authentik.sources.ldap.sync.groups import GroupLDAPSynchronizer
|
||||||
|
from authentik.sources.ldap.sync.membership import MembershipLDAPSynchronizer
|
||||||
from authentik.sources.ldap.sync.users import UserLDAPSynchronizer
|
from authentik.sources.ldap.sync.users import UserLDAPSynchronizer
|
||||||
from authentik.sources.ldap.tasks import ldap_sync
|
from authentik.sources.ldap.tasks import ldap_sync, ldap_sync_all
|
||||||
from authentik.sources.ldap.tests.mock_ad import mock_ad_connection
|
from authentik.sources.ldap.tests.mock_ad import mock_ad_connection
|
||||||
from authentik.sources.ldap.tests.mock_freeipa import mock_freeipa_connection
|
from authentik.sources.ldap.tests.mock_freeipa import mock_freeipa_connection
|
||||||
from authentik.sources.ldap.tests.mock_slapd import mock_slapd_connection
|
from authentik.sources.ldap.tests.mock_slapd import (
|
||||||
|
group_in_slapd_cn,
|
||||||
|
group_in_slapd_uid,
|
||||||
|
mock_slapd_connection,
|
||||||
|
user_in_slapd_cn,
|
||||||
|
user_in_slapd_uid,
|
||||||
|
)
|
||||||
|
|
||||||
LDAP_PASSWORD = generate_key()
|
LDAP_PASSWORD = generate_key()
|
||||||
|
|
||||||
@ -34,14 +50,13 @@ class LDAPSyncTests(TestCase):
|
|||||||
additional_group_dn="ou=groups",
|
additional_group_dn="ou=groups",
|
||||||
)
|
)
|
||||||
|
|
||||||
# TODO: fix me
|
def test_sync_missing_page(self):
|
||||||
# def test_sync_missing_page(self):
|
"""Test sync with missing page"""
|
||||||
# """Test sync with missing page"""
|
connection = MagicMock(return_value=mock_ad_connection(LDAP_PASSWORD))
|
||||||
# connection = MagicMock(return_value=mock_ad_connection(LDAP_PASSWORD))
|
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||||
# with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
ldap_sync.delay(str(self.source.pk), class_to_path(UserLDAPSynchronizer), "foo").get()
|
||||||
# ldap_sync_page.send(str(self.source.pk), class_to_path(UserLDAPSynchronizer), "foo")
|
task = SystemTask.objects.filter(name="ldap_sync", uid="ldap:users:foo").first()
|
||||||
# task = SystemTask.objects.filter(name="ldap_sync", uid="ldap:users:foo").first()
|
self.assertEqual(task.status, TaskStatus.ERROR)
|
||||||
# self.assertEqual(task.status, TaskStatus.ERROR)
|
|
||||||
|
|
||||||
def test_sync_error(self):
|
def test_sync_error(self):
|
||||||
"""Test user sync"""
|
"""Test user sync"""
|
||||||
@ -56,9 +71,9 @@ class LDAPSyncTests(TestCase):
|
|||||||
expression="q",
|
expression="q",
|
||||||
)
|
)
|
||||||
self.source.user_property_mappings.set([mapping])
|
self.source.user_property_mappings.set([mapping])
|
||||||
|
self.source.save()
|
||||||
connection = MagicMock(return_value=mock_ad_connection(LDAP_PASSWORD))
|
connection = MagicMock(return_value=mock_ad_connection(LDAP_PASSWORD))
|
||||||
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||||
self.source.save()
|
|
||||||
user_sync = UserLDAPSynchronizer(self.source)
|
user_sync = UserLDAPSynchronizer(self.source)
|
||||||
with self.assertRaises(StopSync):
|
with self.assertRaises(StopSync):
|
||||||
user_sync.sync_full()
|
user_sync.sync_full()
|
||||||
@ -214,8 +229,11 @@ class LDAPSyncTests(TestCase):
|
|||||||
_user = create_test_admin_user()
|
_user = create_test_admin_user()
|
||||||
parent_group = Group.objects.get(name=_user.username)
|
parent_group = Group.objects.get(name=_user.username)
|
||||||
self.source.sync_parent_group = parent_group
|
self.source.sync_parent_group = parent_group
|
||||||
# Sync is run on save
|
|
||||||
self.source.save()
|
self.source.save()
|
||||||
|
group_sync = GroupLDAPSynchronizer(self.source)
|
||||||
|
group_sync.sync_full()
|
||||||
|
membership_sync = MembershipLDAPSynchronizer(self.source)
|
||||||
|
membership_sync.sync_full()
|
||||||
group: Group = Group.objects.filter(name="test-group").first()
|
group: Group = Group.objects.filter(name="test-group").first()
|
||||||
self.assertIsNotNone(group)
|
self.assertIsNotNone(group)
|
||||||
self.assertEqual(group.parent, parent_group)
|
self.assertEqual(group.parent, parent_group)
|
||||||
@ -237,8 +255,11 @@ class LDAPSyncTests(TestCase):
|
|||||||
)
|
)
|
||||||
connection = MagicMock(return_value=mock_slapd_connection(LDAP_PASSWORD))
|
connection = MagicMock(return_value=mock_slapd_connection(LDAP_PASSWORD))
|
||||||
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||||
# Sync is run on save
|
|
||||||
self.source.save()
|
self.source.save()
|
||||||
|
group_sync = GroupLDAPSynchronizer(self.source)
|
||||||
|
group_sync.sync_full()
|
||||||
|
membership_sync = MembershipLDAPSynchronizer(self.source)
|
||||||
|
membership_sync.sync_full()
|
||||||
group = Group.objects.filter(name="group1")
|
group = Group.objects.filter(name="group1")
|
||||||
self.assertTrue(group.exists())
|
self.assertTrue(group.exists())
|
||||||
|
|
||||||
@ -248,11 +269,18 @@ class LDAPSyncTests(TestCase):
|
|||||||
self.source.group_membership_field = "memberUid"
|
self.source.group_membership_field = "memberUid"
|
||||||
self.source.user_object_filter = "(objectClass=posixAccount)"
|
self.source.user_object_filter = "(objectClass=posixAccount)"
|
||||||
self.source.group_object_filter = "(objectClass=posixGroup)"
|
self.source.group_object_filter = "(objectClass=posixGroup)"
|
||||||
|
self.source.user_membership_attribute = "uid"
|
||||||
self.source.user_property_mappings.set(
|
self.source.user_property_mappings.set(
|
||||||
LDAPSourcePropertyMapping.objects.filter(
|
[
|
||||||
Q(managed__startswith="goauthentik.io/sources/ldap/default")
|
*LDAPSourcePropertyMapping.objects.filter(
|
||||||
| Q(managed__startswith="goauthentik.io/sources/ldap/openldap")
|
Q(managed__startswith="goauthentik.io/sources/ldap/default")
|
||||||
)
|
| Q(managed__startswith="goauthentik.io/sources/ldap/openldap")
|
||||||
|
).all(),
|
||||||
|
LDAPSourcePropertyMapping.objects.create(
|
||||||
|
name="name",
|
||||||
|
expression='return {"attributes": {"uid": list_flatten(ldap.get("uid"))}}',
|
||||||
|
),
|
||||||
|
]
|
||||||
)
|
)
|
||||||
self.source.group_property_mappings.set(
|
self.source.group_property_mappings.set(
|
||||||
LDAPSourcePropertyMapping.objects.filter(
|
LDAPSourcePropertyMapping.objects.filter(
|
||||||
@ -261,8 +289,51 @@ class LDAPSyncTests(TestCase):
|
|||||||
)
|
)
|
||||||
connection = MagicMock(return_value=mock_slapd_connection(LDAP_PASSWORD))
|
connection = MagicMock(return_value=mock_slapd_connection(LDAP_PASSWORD))
|
||||||
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||||
# Sync is run on save
|
|
||||||
self.source.save()
|
self.source.save()
|
||||||
|
user_sync = UserLDAPSynchronizer(self.source)
|
||||||
|
user_sync.sync_full()
|
||||||
|
group_sync = GroupLDAPSynchronizer(self.source)
|
||||||
|
group_sync.sync_full()
|
||||||
|
membership_sync = MembershipLDAPSynchronizer(self.source)
|
||||||
|
membership_sync.sync_full()
|
||||||
|
# Test if membership mapping based on memberUid works.
|
||||||
|
posix_group = Group.objects.filter(name="group-posix").first()
|
||||||
|
self.assertTrue(posix_group.users.filter(name="user-posix").exists())
|
||||||
|
|
||||||
|
def test_sync_groups_openldap_posix_group_nonstandard_membership_attribute(self):
|
||||||
|
"""Test posix group sync"""
|
||||||
|
self.source.object_uniqueness_field = "cn"
|
||||||
|
self.source.group_membership_field = "memberUid"
|
||||||
|
self.source.user_object_filter = "(objectClass=posixAccount)"
|
||||||
|
self.source.group_object_filter = "(objectClass=posixGroup)"
|
||||||
|
self.source.user_membership_attribute = "cn"
|
||||||
|
self.source.user_property_mappings.set(
|
||||||
|
[
|
||||||
|
*LDAPSourcePropertyMapping.objects.filter(
|
||||||
|
Q(managed__startswith="goauthentik.io/sources/ldap/default")
|
||||||
|
| Q(managed__startswith="goauthentik.io/sources/ldap/openldap")
|
||||||
|
).all(),
|
||||||
|
LDAPSourcePropertyMapping.objects.create(
|
||||||
|
name="name",
|
||||||
|
expression='return {"attributes": {"cn": list_flatten(ldap.get("cn"))}}',
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.source.group_property_mappings.set(
|
||||||
|
LDAPSourcePropertyMapping.objects.filter(
|
||||||
|
managed="goauthentik.io/sources/ldap/openldap-cn"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
connection = MagicMock(return_value=mock_slapd_connection(LDAP_PASSWORD))
|
||||||
|
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||||
|
self.source.save()
|
||||||
|
user_sync = UserLDAPSynchronizer(self.source)
|
||||||
|
user_sync.sync_full()
|
||||||
|
group_sync = GroupLDAPSynchronizer(self.source)
|
||||||
|
group_sync.sync_full()
|
||||||
|
membership_sync = MembershipLDAPSynchronizer(self.source)
|
||||||
|
membership_sync.sync_full()
|
||||||
|
# Test if membership mapping based on memberUid works.
|
||||||
posix_group = Group.objects.filter(name="group-posix").first()
|
posix_group = Group.objects.filter(name="group-posix").first()
|
||||||
self.assertTrue(posix_group.users.filter(name="user-posix").exists())
|
self.assertTrue(posix_group.users.filter(name="user-posix").exists())
|
||||||
|
|
||||||
@ -274,10 +345,10 @@ class LDAPSyncTests(TestCase):
|
|||||||
| Q(managed__startswith="goauthentik.io/sources/ldap/ms")
|
| Q(managed__startswith="goauthentik.io/sources/ldap/ms")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
self.source.save()
|
||||||
connection = MagicMock(return_value=mock_ad_connection(LDAP_PASSWORD))
|
connection = MagicMock(return_value=mock_ad_connection(LDAP_PASSWORD))
|
||||||
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||||
self.source.save()
|
ldap_sync_all.delay().get()
|
||||||
ldap_sync.send(self.source.pk).get_result()
|
|
||||||
|
|
||||||
def test_tasks_openldap(self):
|
def test_tasks_openldap(self):
|
||||||
"""Test Scheduled tasks"""
|
"""Test Scheduled tasks"""
|
||||||
@ -289,7 +360,164 @@ class LDAPSyncTests(TestCase):
|
|||||||
| Q(managed__startswith="goauthentik.io/sources/ldap/openldap")
|
| Q(managed__startswith="goauthentik.io/sources/ldap/openldap")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
self.source.save()
|
||||||
connection = MagicMock(return_value=mock_slapd_connection(LDAP_PASSWORD))
|
connection = MagicMock(return_value=mock_slapd_connection(LDAP_PASSWORD))
|
||||||
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||||
self.source.save()
|
ldap_sync_all.delay().get()
|
||||||
ldap_sync.send(self.source.pk).get_result()
|
|
||||||
|
def test_user_deletion(self):
|
||||||
|
"""Test user deletion"""
|
||||||
|
user = User.objects.create_user(username="not-in-the-source")
|
||||||
|
UserLDAPSourceConnection.objects.create(
|
||||||
|
user=user, source=self.source, identifier="not-in-the-source"
|
||||||
|
)
|
||||||
|
self.source.object_uniqueness_field = "uid"
|
||||||
|
self.source.group_object_filter = "(objectClass=groupOfNames)"
|
||||||
|
self.source.delete_not_found_objects = True
|
||||||
|
self.source.save()
|
||||||
|
|
||||||
|
connection = MagicMock(return_value=mock_slapd_connection(LDAP_PASSWORD))
|
||||||
|
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||||
|
ldap_sync_all.delay().get()
|
||||||
|
self.assertFalse(User.objects.filter(username="not-in-the-source").exists())
|
||||||
|
|
||||||
|
def test_user_deletion_still_in_source(self):
|
||||||
|
"""Test that user is not deleted if it's still in the source"""
|
||||||
|
username = user_in_slapd_cn
|
||||||
|
identifier = user_in_slapd_uid
|
||||||
|
user = User.objects.create_user(username=username)
|
||||||
|
UserLDAPSourceConnection.objects.create(
|
||||||
|
user=user, source=self.source, identifier=identifier
|
||||||
|
)
|
||||||
|
self.source.object_uniqueness_field = "uid"
|
||||||
|
self.source.group_object_filter = "(objectClass=groupOfNames)"
|
||||||
|
self.source.delete_not_found_objects = True
|
||||||
|
self.source.save()
|
||||||
|
|
||||||
|
connection = MagicMock(return_value=mock_slapd_connection(LDAP_PASSWORD))
|
||||||
|
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||||
|
ldap_sync_all.delay().get()
|
||||||
|
self.assertTrue(User.objects.filter(username=username).exists())
|
||||||
|
|
||||||
|
def test_user_deletion_no_sync(self):
|
||||||
|
"""Test that user is not deleted if sync_users is False"""
|
||||||
|
user = User.objects.create_user(username="not-in-the-source")
|
||||||
|
UserLDAPSourceConnection.objects.create(
|
||||||
|
user=user, source=self.source, identifier="not-in-the-source"
|
||||||
|
)
|
||||||
|
self.source.object_uniqueness_field = "uid"
|
||||||
|
self.source.group_object_filter = "(objectClass=groupOfNames)"
|
||||||
|
self.source.delete_not_found_objects = True
|
||||||
|
self.source.sync_users = False
|
||||||
|
self.source.save()
|
||||||
|
|
||||||
|
connection = MagicMock(return_value=mock_slapd_connection(LDAP_PASSWORD))
|
||||||
|
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||||
|
ldap_sync_all.delay().get()
|
||||||
|
self.assertTrue(User.objects.filter(username="not-in-the-source").exists())
|
||||||
|
|
||||||
|
def test_user_deletion_no_delete(self):
|
||||||
|
"""Test that user is not deleted if delete_not_found_objects is False"""
|
||||||
|
user = User.objects.create_user(username="not-in-the-source")
|
||||||
|
UserLDAPSourceConnection.objects.create(
|
||||||
|
user=user, source=self.source, identifier="not-in-the-source"
|
||||||
|
)
|
||||||
|
self.source.object_uniqueness_field = "uid"
|
||||||
|
self.source.group_object_filter = "(objectClass=groupOfNames)"
|
||||||
|
self.source.save()
|
||||||
|
|
||||||
|
connection = MagicMock(return_value=mock_slapd_connection(LDAP_PASSWORD))
|
||||||
|
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||||
|
ldap_sync_all.delay().get()
|
||||||
|
self.assertTrue(User.objects.filter(username="not-in-the-source").exists())
|
||||||
|
|
||||||
|
def test_group_deletion(self):
|
||||||
|
"""Test group deletion"""
|
||||||
|
group = Group.objects.create(name="not-in-the-source")
|
||||||
|
GroupLDAPSourceConnection.objects.create(
|
||||||
|
group=group, source=self.source, identifier="not-in-the-source"
|
||||||
|
)
|
||||||
|
self.source.object_uniqueness_field = "uid"
|
||||||
|
self.source.group_object_filter = "(objectClass=groupOfNames)"
|
||||||
|
self.source.delete_not_found_objects = True
|
||||||
|
self.source.save()
|
||||||
|
|
||||||
|
connection = MagicMock(return_value=mock_slapd_connection(LDAP_PASSWORD))
|
||||||
|
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||||
|
ldap_sync_all.delay().get()
|
||||||
|
self.assertFalse(Group.objects.filter(name="not-in-the-source").exists())
|
||||||
|
|
||||||
|
def test_group_deletion_still_in_source(self):
|
||||||
|
"""Test that group is not deleted if it's still in the source"""
|
||||||
|
groupname = group_in_slapd_cn
|
||||||
|
identifier = group_in_slapd_uid
|
||||||
|
group = Group.objects.create(name=groupname)
|
||||||
|
GroupLDAPSourceConnection.objects.create(
|
||||||
|
group=group, source=self.source, identifier=identifier
|
||||||
|
)
|
||||||
|
self.source.object_uniqueness_field = "uid"
|
||||||
|
self.source.group_object_filter = "(objectClass=groupOfNames)"
|
||||||
|
self.source.delete_not_found_objects = True
|
||||||
|
self.source.save()
|
||||||
|
|
||||||
|
connection = MagicMock(return_value=mock_slapd_connection(LDAP_PASSWORD))
|
||||||
|
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||||
|
ldap_sync_all.delay().get()
|
||||||
|
self.assertTrue(Group.objects.filter(name=groupname).exists())
|
||||||
|
|
||||||
|
def test_group_deletion_no_sync(self):
|
||||||
|
"""Test that group is not deleted if sync_groups is False"""
|
||||||
|
group = Group.objects.create(name="not-in-the-source")
|
||||||
|
GroupLDAPSourceConnection.objects.create(
|
||||||
|
group=group, source=self.source, identifier="not-in-the-source"
|
||||||
|
)
|
||||||
|
self.source.object_uniqueness_field = "uid"
|
||||||
|
self.source.group_object_filter = "(objectClass=groupOfNames)"
|
||||||
|
self.source.delete_not_found_objects = True
|
||||||
|
self.source.sync_groups = False
|
||||||
|
self.source.save()
|
||||||
|
|
||||||
|
connection = MagicMock(return_value=mock_slapd_connection(LDAP_PASSWORD))
|
||||||
|
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||||
|
ldap_sync_all.delay().get()
|
||||||
|
self.assertTrue(Group.objects.filter(name="not-in-the-source").exists())
|
||||||
|
|
||||||
|
def test_group_deletion_no_delete(self):
|
||||||
|
"""Test that group is not deleted if delete_not_found_objects is False"""
|
||||||
|
group = Group.objects.create(name="not-in-the-source")
|
||||||
|
GroupLDAPSourceConnection.objects.create(
|
||||||
|
group=group, source=self.source, identifier="not-in-the-source"
|
||||||
|
)
|
||||||
|
self.source.object_uniqueness_field = "uid"
|
||||||
|
self.source.group_object_filter = "(objectClass=groupOfNames)"
|
||||||
|
self.source.save()
|
||||||
|
|
||||||
|
connection = MagicMock(return_value=mock_slapd_connection(LDAP_PASSWORD))
|
||||||
|
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||||
|
ldap_sync_all.delay().get()
|
||||||
|
self.assertTrue(Group.objects.filter(name="not-in-the-source").exists())
|
||||||
|
|
||||||
|
def test_batch_deletion(self):
|
||||||
|
"""Test batch deletion"""
|
||||||
|
BATCH_SIZE = DELETE_CHUNK_SIZE + 1
|
||||||
|
for i in range(BATCH_SIZE):
|
||||||
|
user = User.objects.create_user(username=f"not-in-the-source-{i}")
|
||||||
|
group = Group.objects.create(name=f"not-in-the-source-{i}")
|
||||||
|
group.users.add(user)
|
||||||
|
UserLDAPSourceConnection.objects.create(
|
||||||
|
user=user, source=self.source, identifier=f"not-in-the-source-{i}-user"
|
||||||
|
)
|
||||||
|
GroupLDAPSourceConnection.objects.create(
|
||||||
|
group=group, source=self.source, identifier=f"not-in-the-source-{i}-group"
|
||||||
|
)
|
||||||
|
self.source.object_uniqueness_field = "uid"
|
||||||
|
self.source.group_object_filter = "(objectClass=groupOfNames)"
|
||||||
|
self.source.delete_not_found_objects = True
|
||||||
|
self.source.save()
|
||||||
|
|
||||||
|
connection = MagicMock(return_value=mock_slapd_connection(LDAP_PASSWORD))
|
||||||
|
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||||
|
ldap_sync_all.delay().get()
|
||||||
|
|
||||||
|
self.assertFalse(User.objects.filter(username__startswith="not-in-the-source").exists())
|
||||||
|
self.assertFalse(Group.objects.filter(name__startswith="not-in-the-source").exists())
|
||||||
|
@ -9,6 +9,7 @@ from django.http.response import HttpResponseBadRequest
|
|||||||
from django.shortcuts import get_object_or_404, redirect
|
from django.shortcuts import get_object_or_404, redirect
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.utils.http import urlencode
|
from django.utils.http import urlencode
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
from django.views import View
|
from django.views import View
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
@ -128,7 +129,9 @@ class InitiateView(View):
|
|||||||
# otherwise we default to POST_AUTO, with direct redirect
|
# otherwise we default to POST_AUTO, with direct redirect
|
||||||
if source.binding_type == SAMLBindingTypes.POST:
|
if source.binding_type == SAMLBindingTypes.POST:
|
||||||
injected_stages.append(in_memory_stage(ConsentStageView))
|
injected_stages.append(in_memory_stage(ConsentStageView))
|
||||||
plan_kwargs[PLAN_CONTEXT_CONSENT_HEADER] = f"Continue to {source.name}"
|
plan_kwargs[PLAN_CONTEXT_CONSENT_HEADER] = _(
|
||||||
|
"Continue to {source_name}".format(source_name=source.name)
|
||||||
|
)
|
||||||
injected_stages.append(in_memory_stage(AutosubmitStageView))
|
injected_stages.append(in_memory_stage(AutosubmitStageView))
|
||||||
return self.handle_login_flow(
|
return self.handle_login_flow(
|
||||||
source,
|
source,
|
||||||
|
@ -4,6 +4,8 @@ from uuid import uuid4
|
|||||||
|
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
from rest_framework.exceptions import ValidationError
|
||||||
from rest_framework.fields import CharField
|
from rest_framework.fields import CharField
|
||||||
|
|
||||||
from authentik.core.api.utils import PassiveSerializer
|
from authentik.core.api.utils import PassiveSerializer
|
||||||
@ -47,6 +49,11 @@ class ConsentChallengeResponse(ChallengeResponse):
|
|||||||
component = CharField(default="ak-stage-consent")
|
component = CharField(default="ak-stage-consent")
|
||||||
token = CharField(required=True)
|
token = CharField(required=True)
|
||||||
|
|
||||||
|
def validate_token(self, token: str):
|
||||||
|
if token != self.stage.executor.request.session[SESSION_KEY_CONSENT_TOKEN]:
|
||||||
|
raise ValidationError(_("Invalid consent token, re-showing prompt"))
|
||||||
|
return token
|
||||||
|
|
||||||
|
|
||||||
class ConsentStageView(ChallengeStageView):
|
class ConsentStageView(ChallengeStageView):
|
||||||
"""Simple consent checker."""
|
"""Simple consent checker."""
|
||||||
@ -120,9 +127,6 @@ class ConsentStageView(ChallengeStageView):
|
|||||||
return super().get(request, *args, **kwargs)
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
def challenge_valid(self, response: ChallengeResponse) -> HttpResponse:
|
def challenge_valid(self, response: ChallengeResponse) -> HttpResponse:
|
||||||
if response.data["token"] != self.request.session[SESSION_KEY_CONSENT_TOKEN]:
|
|
||||||
self.logger.info("Invalid consent token, re-showing prompt")
|
|
||||||
return self.get(self.request)
|
|
||||||
if self.should_always_prompt():
|
if self.should_always_prompt():
|
||||||
return self.executor.stage_ok()
|
return self.executor.stage_ok()
|
||||||
current_stage: ConsentStage = self.executor.current_stage
|
current_stage: ConsentStage = self.executor.current_stage
|
||||||
|
@ -17,6 +17,7 @@ from authentik.flows.views.executor import SESSION_KEY_PLAN
|
|||||||
from authentik.lib.generators import generate_id
|
from authentik.lib.generators import generate_id
|
||||||
from authentik.stages.consent.models import ConsentMode, ConsentStage, UserConsent
|
from authentik.stages.consent.models import ConsentMode, ConsentStage, UserConsent
|
||||||
from authentik.stages.consent.stage import (
|
from authentik.stages.consent.stage import (
|
||||||
|
PLAN_CONTEXT_CONSENT_HEADER,
|
||||||
PLAN_CONTEXT_CONSENT_PERMISSIONS,
|
PLAN_CONTEXT_CONSENT_PERMISSIONS,
|
||||||
SESSION_KEY_CONSENT_TOKEN,
|
SESSION_KEY_CONSENT_TOKEN,
|
||||||
)
|
)
|
||||||
@ -33,6 +34,40 @@ class TestConsentStage(FlowTestCase):
|
|||||||
slug=generate_id(),
|
slug=generate_id(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_mismatched_token(self):
|
||||||
|
"""Test incorrect token"""
|
||||||
|
flow = create_test_flow(FlowDesignation.AUTHENTICATION)
|
||||||
|
stage = ConsentStage.objects.create(name=generate_id(), mode=ConsentMode.ALWAYS_REQUIRE)
|
||||||
|
binding = FlowStageBinding.objects.create(target=flow, stage=stage, order=2)
|
||||||
|
|
||||||
|
plan = FlowPlan(flow_pk=flow.pk.hex, bindings=[binding], markers=[StageMarker()])
|
||||||
|
session = self.client.session
|
||||||
|
session[SESSION_KEY_PLAN] = plan
|
||||||
|
session.save()
|
||||||
|
response = self.client.get(
|
||||||
|
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
session = self.client.session
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
||||||
|
{
|
||||||
|
"token": generate_id(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertStageResponse(
|
||||||
|
response,
|
||||||
|
flow,
|
||||||
|
component="ak-stage-consent",
|
||||||
|
response_errors={
|
||||||
|
"token": [{"string": "Invalid consent token, re-showing prompt", "code": "invalid"}]
|
||||||
|
},
|
||||||
|
)
|
||||||
|
self.assertFalse(UserConsent.objects.filter(user=self.user).exists())
|
||||||
|
|
||||||
def test_always_required(self):
|
def test_always_required(self):
|
||||||
"""Test always required consent"""
|
"""Test always required consent"""
|
||||||
flow = create_test_flow(FlowDesignation.AUTHENTICATION)
|
flow = create_test_flow(FlowDesignation.AUTHENTICATION)
|
||||||
@ -158,6 +193,7 @@ class TestConsentStage(FlowTestCase):
|
|||||||
context={
|
context={
|
||||||
PLAN_CONTEXT_APPLICATION: self.application,
|
PLAN_CONTEXT_APPLICATION: self.application,
|
||||||
PLAN_CONTEXT_CONSENT_PERMISSIONS: [PermissionDict(id="foo", name="foo-desc")],
|
PLAN_CONTEXT_CONSENT_PERMISSIONS: [PermissionDict(id="foo", name="foo-desc")],
|
||||||
|
PLAN_CONTEXT_CONSENT_HEADER: "test header",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
|
38
authentik/stages/email/flow.py
Normal file
38
authentik/stages/email/flow.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
from base64 import b64encode
|
||||||
|
from copy import deepcopy
|
||||||
|
from pickle import dumps # nosec
|
||||||
|
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
|
from authentik.flows.models import FlowToken, in_memory_stage
|
||||||
|
from authentik.flows.planner import PLAN_CONTEXT_IS_RESTORED, FlowPlan
|
||||||
|
from authentik.stages.consent.stage import PLAN_CONTEXT_CONSENT_HEADER, ConsentStageView
|
||||||
|
|
||||||
|
|
||||||
|
def pickle_flow_token_for_email(plan: FlowPlan):
|
||||||
|
"""Insert a consent stage into the flow plan and pickle it for a FlowToken,
|
||||||
|
to be sent via Email. This is to prevent automated email scanners, which sometimes
|
||||||
|
open links in emails in a full browser from breaking the link."""
|
||||||
|
plan_copy = deepcopy(plan)
|
||||||
|
plan_copy.insert_stage(in_memory_stage(EmailTokenRevocationConsentStageView), index=0)
|
||||||
|
plan_copy.context[PLAN_CONTEXT_CONSENT_HEADER] = _("Continue to confirm this email address.")
|
||||||
|
data = dumps(plan_copy)
|
||||||
|
return b64encode(data).decode()
|
||||||
|
|
||||||
|
|
||||||
|
class EmailTokenRevocationConsentStageView(ConsentStageView):
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
token: FlowToken = self.executor.plan.context[PLAN_CONTEXT_IS_RESTORED]
|
||||||
|
try:
|
||||||
|
token.refresh_from_db()
|
||||||
|
except FlowToken.DoesNotExist:
|
||||||
|
return self.executor.stage_invalid(
|
||||||
|
_("Link was already used, please request a new link.")
|
||||||
|
)
|
||||||
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def challenge_valid(self, response):
|
||||||
|
token: FlowToken = self.executor.plan.context[PLAN_CONTEXT_IS_RESTORED]
|
||||||
|
token.delete()
|
||||||
|
return super().challenge_valid(response)
|
@ -23,6 +23,7 @@ from authentik.flows.stage import ChallengeStageView
|
|||||||
from authentik.flows.views.executor import QS_KEY_TOKEN, QS_QUERY
|
from authentik.flows.views.executor import QS_KEY_TOKEN, QS_QUERY
|
||||||
from authentik.lib.utils.errors import exception_to_string
|
from authentik.lib.utils.errors import exception_to_string
|
||||||
from authentik.lib.utils.time import timedelta_from_string
|
from authentik.lib.utils.time import timedelta_from_string
|
||||||
|
from authentik.stages.email.flow import pickle_flow_token_for_email
|
||||||
from authentik.stages.email.models import EmailStage
|
from authentik.stages.email.models import EmailStage
|
||||||
from authentik.stages.email.tasks import send_mails
|
from authentik.stages.email.tasks import send_mails
|
||||||
from authentik.stages.email.utils import TemplateEmailMessage
|
from authentik.stages.email.utils import TemplateEmailMessage
|
||||||
@ -86,7 +87,8 @@ class EmailStageView(ChallengeStageView):
|
|||||||
user=pending_user,
|
user=pending_user,
|
||||||
identifier=identifier,
|
identifier=identifier,
|
||||||
flow=self.executor.flow,
|
flow=self.executor.flow,
|
||||||
_plan=FlowToken.pickle(self.executor.plan),
|
_plan=pickle_flow_token_for_email(self.executor.plan),
|
||||||
|
revoke_on_execution=False,
|
||||||
)
|
)
|
||||||
token = tokens.first()
|
token = tokens.first()
|
||||||
# Check if token is expired and rotate key if so
|
# Check if token is expired and rotate key if so
|
||||||
|
@ -174,5 +174,5 @@ class TestEmailStageSending(FlowTestCase):
|
|||||||
response = self.client.post(url)
|
response = self.client.post(url)
|
||||||
response = self.client.post(url)
|
response = self.client.post(url)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertTrue(len(mail.outbox) >= 1)
|
self.assertGreaterEqual(len(mail.outbox), 1)
|
||||||
self.assertEqual(mail.outbox[0].subject, "authentik")
|
self.assertEqual(mail.outbox[0].subject, "authentik")
|
||||||
|
@ -17,6 +17,7 @@ from authentik.flows.tests import FlowTestCase
|
|||||||
from authentik.flows.views.executor import QS_KEY_TOKEN, SESSION_KEY_PLAN, FlowExecutorView
|
from authentik.flows.views.executor import QS_KEY_TOKEN, SESSION_KEY_PLAN, FlowExecutorView
|
||||||
from authentik.lib.config import CONFIG
|
from authentik.lib.config import CONFIG
|
||||||
from authentik.lib.generators import generate_id
|
from authentik.lib.generators import generate_id
|
||||||
|
from authentik.stages.consent.stage import SESSION_KEY_CONSENT_TOKEN
|
||||||
from authentik.stages.email.models import EmailStage
|
from authentik.stages.email.models import EmailStage
|
||||||
from authentik.stages.email.stage import PLAN_CONTEXT_EMAIL_OVERRIDE, EmailStageView
|
from authentik.stages.email.stage import PLAN_CONTEXT_EMAIL_OVERRIDE, EmailStageView
|
||||||
|
|
||||||
@ -164,6 +165,17 @@ class TestEmailStage(FlowTestCase):
|
|||||||
kwargs={"flow_slug": self.flow.slug},
|
kwargs={"flow_slug": self.flow.slug},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
self.assertStageResponse(response, self.flow, component="ak-stage-consent")
|
||||||
|
response = self.client.post(
|
||||||
|
reverse(
|
||||||
|
"authentik_api:flow-executor",
|
||||||
|
kwargs={"flow_slug": self.flow.slug},
|
||||||
|
),
|
||||||
|
data={
|
||||||
|
"token": self.client.session[SESSION_KEY_CONSENT_TOKEN],
|
||||||
|
},
|
||||||
|
follow=True,
|
||||||
|
)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||||
@ -186,6 +198,7 @@ class TestEmailStage(FlowTestCase):
|
|||||||
# Set flow token user to a different user
|
# Set flow token user to a different user
|
||||||
token: FlowToken = FlowToken.objects.get(user=self.user)
|
token: FlowToken = FlowToken.objects.get(user=self.user)
|
||||||
token.user = create_test_admin_user()
|
token.user = create_test_admin_user()
|
||||||
|
token.revoke_on_execution = True
|
||||||
token.save()
|
token.save()
|
||||||
|
|
||||||
with patch("authentik.flows.views.executor.FlowExecutorView.cancel", MagicMock()):
|
with patch("authentik.flows.views.executor.FlowExecutorView.cancel", MagicMock()):
|
||||||
|
@ -11,7 +11,7 @@ from rest_framework.fields import BooleanField, CharField
|
|||||||
from authentik.core.models import Session, User
|
from authentik.core.models import Session, User
|
||||||
from authentik.events.middleware import audit_ignore
|
from authentik.events.middleware import audit_ignore
|
||||||
from authentik.flows.challenge import ChallengeResponse, WithUserInfoChallenge
|
from authentik.flows.challenge import ChallengeResponse, WithUserInfoChallenge
|
||||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, PLAN_CONTEXT_SOURCE
|
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
|
||||||
from authentik.flows.stage import ChallengeStageView
|
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.root.middleware import ClientIPMiddleware
|
from authentik.root.middleware import ClientIPMiddleware
|
||||||
@ -108,10 +108,6 @@ class UserLoginStageView(ChallengeStageView):
|
|||||||
flow_slug=self.executor.flow.slug,
|
flow_slug=self.executor.flow.slug,
|
||||||
session_duration=delta,
|
session_duration=delta,
|
||||||
)
|
)
|
||||||
# Only show success message if we don't have a source in the flow
|
|
||||||
# as sources show their own success messages
|
|
||||||
if not self.executor.plan.context.get(PLAN_CONTEXT_SOURCE, None):
|
|
||||||
messages.success(self.request, _("Successfully logged in!"))
|
|
||||||
if self.executor.current_stage.terminate_other_sessions:
|
if self.executor.current_stage.terminate_other_sessions:
|
||||||
Session.objects.filter(
|
Session.objects.filter(
|
||||||
authenticatedsession__user=user,
|
authenticatedsession__user=user,
|
||||||
|
@ -8575,6 +8575,12 @@
|
|||||||
"title": "Group membership field",
|
"title": "Group membership field",
|
||||||
"description": "Field which contains members of a group."
|
"description": "Field which contains members of a group."
|
||||||
},
|
},
|
||||||
|
"user_membership_attribute": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1,
|
||||||
|
"title": "User membership attribute",
|
||||||
|
"description": "Attribute which matches the value of `group_membership_field`."
|
||||||
|
},
|
||||||
"object_uniqueness_field": {
|
"object_uniqueness_field": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"minLength": 1,
|
"minLength": 1,
|
||||||
@ -8608,6 +8614,11 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"title": "Lookup groups from user",
|
"title": "Lookup groups from user",
|
||||||
"description": "Lookup group membership based on a user attribute instead of a group attribute. This allows nested group resolution on systems like FreeIPA and Active Directory"
|
"description": "Lookup group membership based on a user attribute instead of a group attribute. This allows nested group resolution on systems like FreeIPA and Active Directory"
|
||||||
|
},
|
||||||
|
"delete_not_found_objects": {
|
||||||
|
"type": "boolean",
|
||||||
|
"title": "Delete not found objects",
|
||||||
|
"description": "Delete authentik users and groups which were previously supplied by this source, but are now missing from it."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": []
|
"required": []
|
||||||
|
4
go.mod
4
go.mod
@ -21,13 +21,13 @@ require (
|
|||||||
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484
|
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484
|
||||||
github.com/pires/go-proxyproto v0.8.1
|
github.com/pires/go-proxyproto v0.8.1
|
||||||
github.com/prometheus/client_golang v1.22.0
|
github.com/prometheus/client_golang v1.22.0
|
||||||
github.com/redis/go-redis/v9 v9.8.0
|
github.com/redis/go-redis/v9 v9.9.0
|
||||||
github.com/sethvargo/go-envconfig v1.3.0
|
github.com/sethvargo/go-envconfig v1.3.0
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/spf13/cobra v1.9.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.2025041.2
|
goauthentik.io/api/v3 v3.2025041.4
|
||||||
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
|
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
|
||||||
golang.org/x/oauth2 v0.30.0
|
golang.org/x/oauth2 v0.30.0
|
||||||
golang.org/x/sync v0.14.0
|
golang.org/x/sync v0.14.0
|
||||||
|
8
go.sum
8
go.sum
@ -245,8 +245,8 @@ github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ
|
|||||||
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
||||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||||
github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI=
|
github.com/redis/go-redis/v9 v9.9.0 h1:URbPQ4xVQSQhZ27WMQVmZSo3uT3pL+4IdHVcYq2nVfM=
|
||||||
github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
|
github.com/redis/go-redis/v9 v9.9.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||||
@ -290,8 +290,8 @@ go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y
|
|||||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
goauthentik.io/api/v3 v3.2025041.2 h1:vFYYnhcDcxL95RczZwhzt3i4LptFXMvIRN+vgf8sQYg=
|
goauthentik.io/api/v3 v3.2025041.4 h1:cGqzWYnUHrWDoaXWDpIL/kWnX9sFrIhkYDye0P0OEAo=
|
||||||
goauthentik.io/api/v3 v3.2025041.2/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
goauthentik.io/api/v3 v3.2025041.4/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
@ -28,16 +28,18 @@ func NewSessionBinder(si server.LDAPServerInstance, oldBinder bind.Binder) *Sess
|
|||||||
si: si,
|
si: si,
|
||||||
log: log.WithField("logger", "authentik.outpost.ldap.binder.session"),
|
log: log.WithField("logger", "authentik.outpost.ldap.binder.session"),
|
||||||
}
|
}
|
||||||
if oldSb, ok := oldBinder.(*SessionBinder); ok {
|
if oldBinder != nil {
|
||||||
sb.DirectBinder = oldSb.DirectBinder
|
if oldSb, ok := oldBinder.(*SessionBinder); ok {
|
||||||
sb.sessions = oldSb.sessions
|
sb.DirectBinder = oldSb.DirectBinder
|
||||||
sb.log.Debug("re-initialised session binder")
|
sb.sessions = oldSb.sessions
|
||||||
} else {
|
sb.log.Debug("re-initialised session binder")
|
||||||
sb.sessions = ttlcache.New(ttlcache.WithDisableTouchOnHit[Credentials, ldap.LDAPResultCode]())
|
return sb
|
||||||
sb.DirectBinder = *direct.NewDirectBinder(si)
|
}
|
||||||
go sb.sessions.Start()
|
|
||||||
sb.log.Debug("initialised session binder")
|
|
||||||
}
|
}
|
||||||
|
sb.sessions = ttlcache.New(ttlcache.WithDisableTouchOnHit[Credentials, ldap.LDAPResultCode]())
|
||||||
|
sb.DirectBinder = *direct.NewDirectBinder(si)
|
||||||
|
go sb.sessions.Start()
|
||||||
|
sb.log.Debug("initialised session binder")
|
||||||
return sb
|
return sb
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ import (
|
|||||||
memorybind "goauthentik.io/internal/outpost/ldap/bind/memory"
|
memorybind "goauthentik.io/internal/outpost/ldap/bind/memory"
|
||||||
"goauthentik.io/internal/outpost/ldap/constants"
|
"goauthentik.io/internal/outpost/ldap/constants"
|
||||||
"goauthentik.io/internal/outpost/ldap/flags"
|
"goauthentik.io/internal/outpost/ldap/flags"
|
||||||
|
"goauthentik.io/internal/outpost/ldap/search"
|
||||||
directsearch "goauthentik.io/internal/outpost/ldap/search/direct"
|
directsearch "goauthentik.io/internal/outpost/ldap/search/direct"
|
||||||
memorysearch "goauthentik.io/internal/outpost/ldap/search/memory"
|
memorysearch "goauthentik.io/internal/outpost/ldap/search/memory"
|
||||||
)
|
)
|
||||||
@ -85,7 +86,11 @@ func (ls *LDAPServer) Refresh() error {
|
|||||||
providers[idx].certUUID = *kp
|
providers[idx].certUUID = *kp
|
||||||
}
|
}
|
||||||
if *provider.SearchMode.Ptr() == api.LDAPAPIACCESSMODE_CACHED {
|
if *provider.SearchMode.Ptr() == api.LDAPAPIACCESSMODE_CACHED {
|
||||||
providers[idx].searcher = memorysearch.NewMemorySearcher(providers[idx])
|
var oldSearcher search.Searcher
|
||||||
|
if existing != nil {
|
||||||
|
oldSearcher = existing.searcher
|
||||||
|
}
|
||||||
|
providers[idx].searcher = memorysearch.NewMemorySearcher(providers[idx], oldSearcher)
|
||||||
} else if *provider.SearchMode.Ptr() == api.LDAPAPIACCESSMODE_DIRECT {
|
} else if *provider.SearchMode.Ptr() == api.LDAPAPIACCESSMODE_DIRECT {
|
||||||
providers[idx].searcher = directsearch.NewDirectSearcher(providers[idx])
|
providers[idx].searcher = directsearch.NewDirectSearcher(providers[idx])
|
||||||
}
|
}
|
||||||
|
@ -31,13 +31,26 @@ type MemorySearcher struct {
|
|||||||
groups []api.Group
|
groups []api.Group
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMemorySearcher(si server.LDAPServerInstance) *MemorySearcher {
|
func NewMemorySearcher(si server.LDAPServerInstance, existing search.Searcher) *MemorySearcher {
|
||||||
ms := &MemorySearcher{
|
ms := &MemorySearcher{
|
||||||
si: si,
|
si: si,
|
||||||
log: log.WithField("logger", "authentik.outpost.ldap.searcher.memory"),
|
log: log.WithField("logger", "authentik.outpost.ldap.searcher.memory"),
|
||||||
ds: direct.NewDirectSearcher(si),
|
ds: direct.NewDirectSearcher(si),
|
||||||
}
|
}
|
||||||
|
if existing != nil {
|
||||||
|
if ems, ok := existing.(*MemorySearcher); ok {
|
||||||
|
ems.si = si
|
||||||
|
ems.fetch()
|
||||||
|
ems.log.Debug("re-initialised memory searcher")
|
||||||
|
return ems
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ms.fetch()
|
||||||
ms.log.Debug("initialised memory searcher")
|
ms.log.Debug("initialised memory searcher")
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *MemorySearcher) fetch() {
|
||||||
// Error is not handled here, we get an empty/truncated list and the error is logged
|
// Error is not handled here, we get an empty/truncated list and the error is logged
|
||||||
users, _ := ak.Paginator(ms.si.GetAPIClient().CoreApi.CoreUsersList(context.TODO()).IncludeGroups(true), ak.PaginatorOptions{
|
users, _ := ak.Paginator(ms.si.GetAPIClient().CoreApi.CoreUsersList(context.TODO()).IncludeGroups(true), ak.PaginatorOptions{
|
||||||
PageSize: 100,
|
PageSize: 100,
|
||||||
@ -49,7 +62,6 @@ func NewMemorySearcher(si server.LDAPServerInstance) *MemorySearcher {
|
|||||||
Logger: ms.log,
|
Logger: ms.log,
|
||||||
})
|
})
|
||||||
ms.groups = groups
|
ms.groups = groups
|
||||||
return ms
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ms *MemorySearcher) SearchBase(req *search.Request) (ldap.ServerSearchResult, error) {
|
func (ms *MemorySearcher) SearchBase(req *search.Request) (ldap.ServerSearchResult, error) {
|
||||||
|
@ -67,11 +67,15 @@ func (ws *WebServer) configureStatic() {
|
|||||||
|
|
||||||
// Media files, if backend is file
|
// Media files, if backend is file
|
||||||
if config.Get().Storage.Media.Backend == "file" {
|
if config.Get().Storage.Media.Backend == "file" {
|
||||||
fsMedia := http.StripPrefix("/media", http.FileServer(http.Dir(config.Get().Storage.Media.File.Path)))
|
fsMedia := http.FileServer(http.Dir(config.Get().Storage.Media.File.Path))
|
||||||
indexLessRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/media/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
indexLessRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/media/").Handler(pathStripper(
|
||||||
w.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox")
|
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
fsMedia.ServeHTTP(w, r)
|
w.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox")
|
||||||
})
|
fsMedia.ServeHTTP(w, r)
|
||||||
|
}),
|
||||||
|
"media/",
|
||||||
|
config.Get().Web.Path,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
staticRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/if/help/").Handler(pathStripper(
|
staticRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/if/help/").Handler(pathStripper(
|
||||||
|
8
lifecycle/aws/package-lock.json
generated
8
lifecycle/aws/package-lock.json
generated
@ -9,7 +9,7 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"aws-cdk": "^2.1016.1",
|
"aws-cdk": "^2.1017.1",
|
||||||
"cross-env": "^7.0.3"
|
"cross-env": "^7.0.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -17,9 +17,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/aws-cdk": {
|
"node_modules/aws-cdk": {
|
||||||
"version": "2.1016.1",
|
"version": "2.1017.1",
|
||||||
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1016.1.tgz",
|
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1017.1.tgz",
|
||||||
"integrity": "sha512-248TBiluT8jHUjkpzvWJOHv2fS+An9fiII3eji8H7jwfTu5yMBk7on4B/AVNr9A1GXJk9I32qf9Q0A3rLWRYPQ==",
|
"integrity": "sha512-KtDdkMhfVjDeexjpMrVoSlz2mTYI5BE/KotvJ7iFbZy1G0nkpW1ImZ54TdBefeeFmZ+8DAjU3I6nUFtymyOI1A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
"node": ">=20"
|
"node": ">=20"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"aws-cdk": "^2.1016.1",
|
"aws-cdk": "^2.1017.1",
|
||||||
"cross-env": "^7.0.3"
|
"cross-env": "^7.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
@ -32,15 +32,17 @@
|
|||||||
# datenschmutz, 2025
|
# datenschmutz, 2025
|
||||||
# 97cce0ae0cad2a2cc552d3165d04643e_de3d740, 2025
|
# 97cce0ae0cad2a2cc552d3165d04643e_de3d740, 2025
|
||||||
# Dominic Wagner <mail@dominic-wagner.de>, 2025
|
# Dominic Wagner <mail@dominic-wagner.de>, 2025
|
||||||
|
# Till-Frederik Riechard, 2025
|
||||||
|
# Alexander Mnich, 2025
|
||||||
#
|
#
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
|
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
|
||||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||||
"Last-Translator: Dominic Wagner <mail@dominic-wagner.de>, 2025\n"
|
"Last-Translator: Alexander Mnich, 2025\n"
|
||||||
"Language-Team: German (https://app.transifex.com/authentik/teams/119923/de/)\n"
|
"Language-Team: German (https://app.transifex.com/authentik/teams/119923/de/)\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"
|
||||||
@ -132,6 +134,10 @@ msgstr ""
|
|||||||
msgid "Web Certificate used by the authentik Core webserver."
|
msgid "Web Certificate used by the authentik Core webserver."
|
||||||
msgstr "Vom Authentik-Core-Webserver verwendetes Zertifikat."
|
msgstr "Vom Authentik-Core-Webserver verwendetes Zertifikat."
|
||||||
|
|
||||||
|
#: authentik/brands/models.py
|
||||||
|
msgid "Certificates used for client authentication."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/brands/models.py
|
#: authentik/brands/models.py
|
||||||
msgid "Brand"
|
msgid "Brand"
|
||||||
msgstr "Marke"
|
msgstr "Marke"
|
||||||
@ -405,7 +411,7 @@ msgstr "Eigenschaften"
|
|||||||
|
|
||||||
#: authentik/core/models.py
|
#: authentik/core/models.py
|
||||||
msgid "session data"
|
msgid "session data"
|
||||||
msgstr ""
|
msgstr "Sitzungsdaten"
|
||||||
|
|
||||||
#: authentik/core/models.py
|
#: authentik/core/models.py
|
||||||
msgid "Session"
|
msgid "Session"
|
||||||
@ -533,7 +539,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: authentik/enterprise/policies/unique_password/models.py
|
#: authentik/enterprise/policies/unique_password/models.py
|
||||||
msgid "Number of passwords to check against."
|
msgid "Number of passwords to check against."
|
||||||
msgstr ""
|
msgstr "Anzahl Passwörter, gegen die geprüft wird."
|
||||||
|
|
||||||
#: authentik/enterprise/policies/unique_password/models.py
|
#: authentik/enterprise/policies/unique_password/models.py
|
||||||
#: authentik/policies/password/models.py
|
#: authentik/policies/password/models.py
|
||||||
@ -543,18 +549,20 @@ msgstr "Passwort nicht im Kontext festgelegt"
|
|||||||
#: authentik/enterprise/policies/unique_password/models.py
|
#: authentik/enterprise/policies/unique_password/models.py
|
||||||
msgid "This password has been used previously. Please choose a different one."
|
msgid "This password has been used previously. Please choose a different one."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"Dieses Passwort wurde in Vergangenheit bereits verwendet. Bitte nutzen Sie "
|
||||||
|
"ein anderes."
|
||||||
|
|
||||||
#: authentik/enterprise/policies/unique_password/models.py
|
#: authentik/enterprise/policies/unique_password/models.py
|
||||||
msgid "Password Uniqueness Policy"
|
msgid "Password Uniqueness Policy"
|
||||||
msgstr ""
|
msgstr "Passwort-Einzigartigkeits-Richtlinie"
|
||||||
|
|
||||||
#: authentik/enterprise/policies/unique_password/models.py
|
#: authentik/enterprise/policies/unique_password/models.py
|
||||||
msgid "Password Uniqueness Policies"
|
msgid "Password Uniqueness Policies"
|
||||||
msgstr ""
|
msgstr "Passwort-Einzigartigkeits-Richtlinien"
|
||||||
|
|
||||||
#: authentik/enterprise/policies/unique_password/models.py
|
#: authentik/enterprise/policies/unique_password/models.py
|
||||||
msgid "User Password History"
|
msgid "User Password History"
|
||||||
msgstr ""
|
msgstr "Nutzer-Passwort-Historie"
|
||||||
|
|
||||||
#: authentik/enterprise/policy.py
|
#: authentik/enterprise/policy.py
|
||||||
msgid "Enterprise required to access this feature."
|
msgid "Enterprise required to access this feature."
|
||||||
@ -693,6 +701,33 @@ msgstr "Endgeräte"
|
|||||||
msgid "Verifying your browser..."
|
msgid "Verifying your browser..."
|
||||||
msgstr "Verifiziere deinen Browser..."
|
msgstr "Verifiziere deinen Browser..."
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid ""
|
||||||
|
"Configure certificate authorities to validate the certificate against. This "
|
||||||
|
"option has a higher priority than the `client_certificate` option on "
|
||||||
|
"`Brand`."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Mutual TLS Stage"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Mutual TLS Stages"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Permissions to pass Certificates for outposts."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/stage.py
|
||||||
|
msgid "Certificate required but no certificate was given."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/stage.py
|
||||||
|
msgid "No user found for certificate."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/enterprise/stages/source/models.py
|
#: authentik/enterprise/stages/source/models.py
|
||||||
msgid ""
|
msgid ""
|
||||||
"Amount of time a user can take to return from the source to continue the "
|
"Amount of time a user can take to return from the source to continue the "
|
||||||
@ -988,7 +1023,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 "Richtlinien auswerten, wenn die Phase dem Benutzer angezeigt wird."
|
||||||
|
|
||||||
#: authentik/flows/models.py
|
#: authentik/flows/models.py
|
||||||
msgid ""
|
msgid ""
|
||||||
@ -1043,9 +1078,12 @@ msgid "Starting full provider sync"
|
|||||||
msgstr "Starte komplette Provider Synchronisation."
|
msgstr "Starte komplette Provider Synchronisation."
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
#, python-brace-format
|
msgid "Syncing users"
|
||||||
msgid "Syncing page {page} of users"
|
msgstr ""
|
||||||
msgstr "Synchonisiere Benutzer Seite {page}"
|
|
||||||
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
|
msgid "Syncing groups"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
@ -1593,11 +1631,11 @@ msgstr "ES256 (Asymmetrische Verschlüsselung)"
|
|||||||
|
|
||||||
#: authentik/providers/oauth2/models.py
|
#: authentik/providers/oauth2/models.py
|
||||||
msgid "ES384 (Asymmetric Encryption)"
|
msgid "ES384 (Asymmetric Encryption)"
|
||||||
msgstr ""
|
msgstr "ES384 (Asymmetrische Verschlüsselung)"
|
||||||
|
|
||||||
#: authentik/providers/oauth2/models.py
|
#: authentik/providers/oauth2/models.py
|
||||||
msgid "ES512 (Asymmetric Encryption)"
|
msgid "ES512 (Asymmetric Encryption)"
|
||||||
msgstr ""
|
msgstr "ES5122 (Asymmetrische Verschlüsselung)"
|
||||||
|
|
||||||
#: authentik/providers/oauth2/models.py
|
#: authentik/providers/oauth2/models.py
|
||||||
msgid "Scope used by the client"
|
msgid "Scope used by the client"
|
||||||
@ -2183,11 +2221,11 @@ msgstr "Standard"
|
|||||||
|
|
||||||
#: authentik/providers/scim/models.py
|
#: authentik/providers/scim/models.py
|
||||||
msgid "AWS"
|
msgid "AWS"
|
||||||
msgstr ""
|
msgstr "AWS"
|
||||||
|
|
||||||
#: authentik/providers/scim/models.py
|
#: authentik/providers/scim/models.py
|
||||||
msgid "Slack"
|
msgid "Slack"
|
||||||
msgstr ""
|
msgstr "Slack"
|
||||||
|
|
||||||
#: authentik/providers/scim/models.py
|
#: authentik/providers/scim/models.py
|
||||||
msgid "Base URL to SCIM requests, usually ends in /v2"
|
msgid "Base URL to SCIM requests, usually ends in /v2"
|
||||||
@ -2199,7 +2237,7 @@ msgstr "Authentifizierungstoken"
|
|||||||
|
|
||||||
#: authentik/providers/scim/models.py
|
#: authentik/providers/scim/models.py
|
||||||
msgid "SCIM Compatibility Mode"
|
msgid "SCIM Compatibility Mode"
|
||||||
msgstr ""
|
msgstr "SCIM Kompatibilitätsmodus"
|
||||||
|
|
||||||
#: authentik/providers/scim/models.py
|
#: authentik/providers/scim/models.py
|
||||||
msgid "Alter authentik behavior for vendor-specific SCIM implementations."
|
msgid "Alter authentik behavior for vendor-specific SCIM implementations."
|
||||||
@ -2231,7 +2269,7 @@ msgstr "Rollen"
|
|||||||
|
|
||||||
#: authentik/rbac/models.py
|
#: authentik/rbac/models.py
|
||||||
msgid "Initial Permissions"
|
msgid "Initial Permissions"
|
||||||
msgstr ""
|
msgstr "Initiale Berechtigungen"
|
||||||
|
|
||||||
#: authentik/rbac/models.py
|
#: authentik/rbac/models.py
|
||||||
msgid "System permission"
|
msgid "System permission"
|
||||||
@ -2487,6 +2525,12 @@ msgid ""
|
|||||||
"Active Directory"
|
"Active Directory"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid ""
|
||||||
|
"Delete authentik users and groups which were previously supplied by this "
|
||||||
|
"source, but are now missing from it."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "LDAP Source"
|
msgid "LDAP Source"
|
||||||
msgstr "LDAP Quelle"
|
msgstr "LDAP Quelle"
|
||||||
@ -2504,20 +2548,25 @@ msgid "LDAP Source Property Mappings"
|
|||||||
msgstr "LDAP Quelle Eigenschafts-Zuordnungen"
|
msgstr "LDAP Quelle Eigenschafts-Zuordnungen"
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "User LDAP Source Connection"
|
msgid ""
|
||||||
|
"Unique ID used while checking if this object still exists in the directory."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid "User LDAP Source Connection"
|
||||||
|
msgstr "Benutzer LDAP-Quellverbindung"
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "User LDAP Source Connections"
|
msgid "User LDAP Source Connections"
|
||||||
msgstr ""
|
msgstr "Benutzer LDAP-Quellverbindungen"
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "Group LDAP Source Connection"
|
msgid "Group LDAP Source Connection"
|
||||||
msgstr ""
|
msgstr "LDAP Gruppen Quellverbindung"
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "Group LDAP Source Connections"
|
msgid "Group LDAP Source Connections"
|
||||||
msgstr ""
|
msgstr "LDAP Gruppen Quellverbindungen"
|
||||||
|
|
||||||
#: authentik/sources/ldap/signals.py
|
#: authentik/sources/ldap/signals.py
|
||||||
msgid "Password does not match Active Directory Complexity."
|
msgid "Password does not match Active Directory Complexity."
|
||||||
@ -2530,7 +2579,7 @@ msgstr "Kein Token empfangen."
|
|||||||
|
|
||||||
#: authentik/sources/oauth/models.py
|
#: authentik/sources/oauth/models.py
|
||||||
msgid "HTTP Basic Authentication"
|
msgid "HTTP Basic Authentication"
|
||||||
msgstr ""
|
msgstr "HTTP Basic Authentifizierung"
|
||||||
|
|
||||||
#: authentik/sources/oauth/models.py
|
#: authentik/sources/oauth/models.py
|
||||||
msgid "Include the client ID and secret as request parameters"
|
msgid "Include the client ID and secret as request parameters"
|
||||||
@ -2896,6 +2945,11 @@ msgstr "SAML Gruppen Quellverbindung"
|
|||||||
msgid "Group SAML Source Connections"
|
msgid "Group SAML Source Connections"
|
||||||
msgstr "SAML Gruppen Quellverbindungen"
|
msgstr "SAML Gruppen Quellverbindungen"
|
||||||
|
|
||||||
|
#: authentik/sources/saml/views.py
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Continue to {source_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/scim/models.py
|
#: authentik/sources/scim/models.py
|
||||||
msgid "SCIM Source"
|
msgid "SCIM Source"
|
||||||
msgstr "SCIM Quelle"
|
msgstr "SCIM Quelle"
|
||||||
@ -2930,7 +2984,7 @@ msgstr "Duo Geräte"
|
|||||||
|
|
||||||
#: authentik/stages/authenticator_email/models.py
|
#: authentik/stages/authenticator_email/models.py
|
||||||
msgid "Email OTP"
|
msgid "Email OTP"
|
||||||
msgstr ""
|
msgstr "E-Mail Einmalpasswort"
|
||||||
|
|
||||||
#: authentik/stages/authenticator_email/models.py
|
#: authentik/stages/authenticator_email/models.py
|
||||||
#: authentik/stages/email/models.py
|
#: authentik/stages/email/models.py
|
||||||
@ -2963,11 +3017,11 @@ msgstr "Beim Rendern der E-Mail-Vorlage ist ein Fehler aufgetreten"
|
|||||||
|
|
||||||
#: authentik/stages/authenticator_email/models.py
|
#: authentik/stages/authenticator_email/models.py
|
||||||
msgid "Email Device"
|
msgid "Email Device"
|
||||||
msgstr ""
|
msgstr "E-Mail Gerät"
|
||||||
|
|
||||||
#: authentik/stages/authenticator_email/models.py
|
#: authentik/stages/authenticator_email/models.py
|
||||||
msgid "Email Devices"
|
msgid "Email Devices"
|
||||||
msgstr ""
|
msgstr "E-Mail Geräte"
|
||||||
|
|
||||||
#: authentik/stages/authenticator_email/stage.py
|
#: authentik/stages/authenticator_email/stage.py
|
||||||
#: authentik/stages/authenticator_sms/stage.py
|
#: authentik/stages/authenticator_sms/stage.py
|
||||||
@ -2977,7 +3031,7 @@ msgstr "Code stimmt nicht überein"
|
|||||||
|
|
||||||
#: authentik/stages/authenticator_email/stage.py
|
#: authentik/stages/authenticator_email/stage.py
|
||||||
msgid "Invalid email"
|
msgid "Invalid email"
|
||||||
msgstr ""
|
msgstr "Ungültige E-Mail"
|
||||||
|
|
||||||
#: authentik/stages/authenticator_email/templates/email/email_otp.html
|
#: authentik/stages/authenticator_email/templates/email/email_otp.html
|
||||||
#: authentik/stages/email/templates/email/password_reset.html
|
#: authentik/stages/email/templates/email/password_reset.html
|
||||||
@ -3273,6 +3327,10 @@ msgstr "Zustimmung der Benutzer"
|
|||||||
msgid "User Consents"
|
msgid "User Consents"
|
||||||
msgstr "Zustimmungen der Benutzer"
|
msgstr "Zustimmungen der Benutzer"
|
||||||
|
|
||||||
|
#: authentik/stages/consent/stage.py
|
||||||
|
msgid "Invalid consent token, re-showing prompt"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/stages/deny/models.py
|
#: authentik/stages/deny/models.py
|
||||||
msgid "Deny Stage"
|
msgid "Deny Stage"
|
||||||
msgstr "Verweigerungsstufe"
|
msgstr "Verweigerungsstufe"
|
||||||
@ -3289,6 +3347,14 @@ msgstr "Dummy Stufe"
|
|||||||
msgid "Dummy Stages"
|
msgid "Dummy Stages"
|
||||||
msgstr "Dummy Stufen"
|
msgstr "Dummy Stufen"
|
||||||
|
|
||||||
|
#: authentik/stages/email/flow.py
|
||||||
|
msgid "Continue to confirm this email address."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/stages/email/flow.py
|
||||||
|
msgid "Link was already used, please request a new link."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/stages/email/models.py
|
#: authentik/stages/email/models.py
|
||||||
msgid "Password Reset"
|
msgid "Password Reset"
|
||||||
msgstr "Passwort zurücksetzen"
|
msgstr "Passwort zurücksetzen"
|
||||||
@ -3890,10 +3956,11 @@ msgstr ""
|
|||||||
#: authentik/tenants/models.py
|
#: authentik/tenants/models.py
|
||||||
msgid "Reputation cannot decrease lower than this value. Zero or negative."
|
msgid "Reputation cannot decrease lower than this value. Zero or negative."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"Reputation kann nicht niedriger als dieser Wert sein. Null oder negativ."
|
||||||
|
|
||||||
#: authentik/tenants/models.py
|
#: authentik/tenants/models.py
|
||||||
msgid "Reputation cannot increase higher than this value. Zero or positive."
|
msgid "Reputation cannot increase higher than this value. Zero or positive."
|
||||||
msgstr ""
|
msgstr "Reputation kann nicht höher als dieser Wert sein. Null oder positiv."
|
||||||
|
|
||||||
#: authentik/tenants/models.py
|
#: authentik/tenants/models.py
|
||||||
msgid "The option configures the footer links on the flow executor pages."
|
msgid "The option configures the footer links on the flow executor pages."
|
||||||
|
@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-05-20 00:10+0000\n"
|
"POT-Creation-Date: 2025-06-02 00:12+0000\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@ -961,8 +961,11 @@ msgid "Starting full provider sync"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
#, python-brace-format
|
msgid "Syncing users"
|
||||||
msgid "Syncing page {page} of users"
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
|
msgid "Syncing groups"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
@ -2223,6 +2226,10 @@ msgstr ""
|
|||||||
msgid "Consider Objects matching this filter to be Users."
|
msgid "Consider Objects matching this filter to be Users."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid "Attribute which matches the value of `group_membership_field`."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "Field which contains members of a group."
|
msgid "Field which contains members of a group."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -2252,6 +2259,12 @@ msgid ""
|
|||||||
"Active Directory"
|
"Active Directory"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid ""
|
||||||
|
"Delete authentik users and groups which were previously supplied by this "
|
||||||
|
"source, but are now missing from it."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "LDAP Source"
|
msgid "LDAP Source"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -2268,6 +2281,11 @@ msgstr ""
|
|||||||
msgid "LDAP Source Property Mappings"
|
msgid "LDAP Source Property Mappings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid ""
|
||||||
|
"Unique ID used while checking if this object still exists in the directory."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "User LDAP Source Connection"
|
msgid "User LDAP Source Connection"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -2639,6 +2657,11 @@ msgstr ""
|
|||||||
msgid "Group SAML Source Connections"
|
msgid "Group SAML Source Connections"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/sources/saml/views.py
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Continue to {source_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/scim/models.py
|
#: authentik/sources/scim/models.py
|
||||||
msgid "SCIM Source"
|
msgid "SCIM Source"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -2994,6 +3017,10 @@ msgstr ""
|
|||||||
msgid "User Consents"
|
msgid "User Consents"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/stages/consent/stage.py
|
||||||
|
msgid "Invalid consent token, re-showing prompt"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/stages/deny/models.py
|
#: authentik/stages/deny/models.py
|
||||||
msgid "Deny Stage"
|
msgid "Deny Stage"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -3010,6 +3037,14 @@ msgstr ""
|
|||||||
msgid "Dummy Stages"
|
msgid "Dummy Stages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/stages/email/flow.py
|
||||||
|
msgid "Continue to confirm this email address."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/stages/email/flow.py
|
||||||
|
msgid "Link was already used, please request a new link."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/stages/email/models.py
|
#: authentik/stages/email/models.py
|
||||||
msgid "Password Reset"
|
msgid "Password Reset"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -3462,10 +3497,6 @@ msgstr ""
|
|||||||
msgid "No Pending user to login."
|
msgid "No Pending user to login."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/stages/user_login/stage.py
|
|
||||||
msgid "Successfully logged in!"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: authentik/stages/user_logout/models.py
|
#: authentik/stages/user_logout/models.py
|
||||||
msgid "User Logout Stage"
|
msgid "User Logout Stage"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
Binary file not shown.
@ -15,7 +15,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
|
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
|
||||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||||
"Last-Translator: Jens L. <jens@goauthentik.io>, 2025\n"
|
"Last-Translator: Jens L. <jens@goauthentik.io>, 2025\n"
|
||||||
"Language-Team: Spanish (https://app.transifex.com/authentik/teams/119923/es/)\n"
|
"Language-Team: Spanish (https://app.transifex.com/authentik/teams/119923/es/)\n"
|
||||||
@ -109,6 +109,10 @@ msgstr ""
|
|||||||
msgid "Web Certificate used by the authentik Core webserver."
|
msgid "Web Certificate used by the authentik Core webserver."
|
||||||
msgstr "Certificado Web usado por el servidor web Core de authentik"
|
msgstr "Certificado Web usado por el servidor web Core de authentik"
|
||||||
|
|
||||||
|
#: authentik/brands/models.py
|
||||||
|
msgid "Certificates used for client authentication."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/brands/models.py
|
#: authentik/brands/models.py
|
||||||
msgid "Brand"
|
msgid "Brand"
|
||||||
msgstr "Marca"
|
msgstr "Marca"
|
||||||
@ -671,6 +675,33 @@ msgstr "Dispositivos de Punto de Conexión"
|
|||||||
msgid "Verifying your browser..."
|
msgid "Verifying your browser..."
|
||||||
msgstr "Verificando tu navegador..."
|
msgstr "Verificando tu navegador..."
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid ""
|
||||||
|
"Configure certificate authorities to validate the certificate against. This "
|
||||||
|
"option has a higher priority than the `client_certificate` option on "
|
||||||
|
"`Brand`."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Mutual TLS Stage"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Mutual TLS Stages"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Permissions to pass Certificates for outposts."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/stage.py
|
||||||
|
msgid "Certificate required but no certificate was given."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/stage.py
|
||||||
|
msgid "No user found for certificate."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/enterprise/stages/source/models.py
|
#: authentik/enterprise/stages/source/models.py
|
||||||
msgid ""
|
msgid ""
|
||||||
"Amount of time a user can take to return from the source to continue the "
|
"Amount of time a user can take to return from the source to continue the "
|
||||||
@ -1009,9 +1040,12 @@ msgid "Starting full provider sync"
|
|||||||
msgstr "Iniciando sincronización completa de proveedor"
|
msgstr "Iniciando sincronización completa de proveedor"
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
#, python-brace-format
|
msgid "Syncing users"
|
||||||
msgid "Syncing page {page} of users"
|
msgstr ""
|
||||||
msgstr "Sincronizando página {page} de usuarios"
|
|
||||||
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
|
msgid "Syncing groups"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
@ -2452,6 +2486,12 @@ msgid ""
|
|||||||
"Active Directory"
|
"Active Directory"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid ""
|
||||||
|
"Delete authentik users and groups which were previously supplied by this "
|
||||||
|
"source, but are now missing from it."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "LDAP Source"
|
msgid "LDAP Source"
|
||||||
msgstr "Fuente de LDAP"
|
msgstr "Fuente de LDAP"
|
||||||
@ -2468,6 +2508,11 @@ msgstr "Asignación de Propiedades de Fuente de LDAP"
|
|||||||
msgid "LDAP Source Property Mappings"
|
msgid "LDAP Source Property Mappings"
|
||||||
msgstr "Asignaciones de Propiedades de Fuente de LDAP"
|
msgstr "Asignaciones de Propiedades de Fuente de LDAP"
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid ""
|
||||||
|
"Unique ID used while checking if this object still exists in the directory."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "User LDAP Source Connection"
|
msgid "User LDAP Source Connection"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -2859,6 +2904,11 @@ msgstr "Conexión de Fuente de SAML de Grupo"
|
|||||||
msgid "Group SAML Source Connections"
|
msgid "Group SAML Source Connections"
|
||||||
msgstr "Conexiones de Fuente de SAML de Grupo"
|
msgstr "Conexiones de Fuente de SAML de Grupo"
|
||||||
|
|
||||||
|
#: authentik/sources/saml/views.py
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Continue to {source_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/scim/models.py
|
#: authentik/sources/scim/models.py
|
||||||
msgid "SCIM Source"
|
msgid "SCIM Source"
|
||||||
msgstr "Fuente de SCIM"
|
msgstr "Fuente de SCIM"
|
||||||
@ -3245,6 +3295,10 @@ msgstr "Consentimiento del usuario"
|
|||||||
msgid "User Consents"
|
msgid "User Consents"
|
||||||
msgstr "Consentimientos del usuario"
|
msgstr "Consentimientos del usuario"
|
||||||
|
|
||||||
|
#: authentik/stages/consent/stage.py
|
||||||
|
msgid "Invalid consent token, re-showing prompt"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/stages/deny/models.py
|
#: authentik/stages/deny/models.py
|
||||||
msgid "Deny Stage"
|
msgid "Deny Stage"
|
||||||
msgstr "Etapa de denegación"
|
msgstr "Etapa de denegación"
|
||||||
@ -3261,6 +3315,14 @@ msgstr "Escenario ficticio"
|
|||||||
msgid "Dummy Stages"
|
msgid "Dummy Stages"
|
||||||
msgstr "Etapas ficticias"
|
msgstr "Etapas ficticias"
|
||||||
|
|
||||||
|
#: authentik/stages/email/flow.py
|
||||||
|
msgid "Continue to confirm this email address."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/stages/email/flow.py
|
||||||
|
msgid "Link was already used, please request a new link."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/stages/email/models.py
|
#: authentik/stages/email/models.py
|
||||||
msgid "Password Reset"
|
msgid "Password Reset"
|
||||||
msgstr "Restablecimiento de contraseña"
|
msgstr "Restablecimiento de contraseña"
|
||||||
|
Binary file not shown.
@ -15,7 +15,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
|
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
|
||||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||||
"Last-Translator: Ville Ranki, 2025\n"
|
"Last-Translator: Ville Ranki, 2025\n"
|
||||||
"Language-Team: Finnish (https://app.transifex.com/authentik/teams/119923/fi/)\n"
|
"Language-Team: Finnish (https://app.transifex.com/authentik/teams/119923/fi/)\n"
|
||||||
@ -106,6 +106,10 @@ msgstr ""
|
|||||||
msgid "Web Certificate used by the authentik Core webserver."
|
msgid "Web Certificate used by the authentik Core webserver."
|
||||||
msgstr "Web-sertifikaatti, jota authentik Core -verkkopalvelin käyttää."
|
msgstr "Web-sertifikaatti, jota authentik Core -verkkopalvelin käyttää."
|
||||||
|
|
||||||
|
#: authentik/brands/models.py
|
||||||
|
msgid "Certificates used for client authentication."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/brands/models.py
|
#: authentik/brands/models.py
|
||||||
msgid "Brand"
|
msgid "Brand"
|
||||||
msgstr "Brändi"
|
msgstr "Brändi"
|
||||||
@ -658,6 +662,33 @@ msgstr "Päätelaitteet"
|
|||||||
msgid "Verifying your browser..."
|
msgid "Verifying your browser..."
|
||||||
msgstr "Selaintasi varmennetaan..."
|
msgstr "Selaintasi varmennetaan..."
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid ""
|
||||||
|
"Configure certificate authorities to validate the certificate against. This "
|
||||||
|
"option has a higher priority than the `client_certificate` option on "
|
||||||
|
"`Brand`."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Mutual TLS Stage"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Mutual TLS Stages"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Permissions to pass Certificates for outposts."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/stage.py
|
||||||
|
msgid "Certificate required but no certificate was given."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/stage.py
|
||||||
|
msgid "No user found for certificate."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/enterprise/stages/source/models.py
|
#: authentik/enterprise/stages/source/models.py
|
||||||
msgid ""
|
msgid ""
|
||||||
"Amount of time a user can take to return from the source to continue the "
|
"Amount of time a user can take to return from the source to continue the "
|
||||||
@ -996,9 +1027,12 @@ msgid "Starting full provider sync"
|
|||||||
msgstr "Käynnistetään palveluntarjoajan täysi synkronisointi"
|
msgstr "Käynnistetään palveluntarjoajan täysi synkronisointi"
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
#, python-brace-format
|
msgid "Syncing users"
|
||||||
msgid "Syncing page {page} of users"
|
msgstr ""
|
||||||
msgstr "Synkronoidaan käyttäjien sivua {page}"
|
|
||||||
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
|
msgid "Syncing groups"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
@ -2429,6 +2463,12 @@ msgid ""
|
|||||||
"Active Directory"
|
"Active Directory"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid ""
|
||||||
|
"Delete authentik users and groups which were previously supplied by this "
|
||||||
|
"source, but are now missing from it."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "LDAP Source"
|
msgid "LDAP Source"
|
||||||
msgstr "LDAP-lähde"
|
msgstr "LDAP-lähde"
|
||||||
@ -2445,6 +2485,11 @@ msgstr "LDAP-lähteen ominaisuuskytkentä"
|
|||||||
msgid "LDAP Source Property Mappings"
|
msgid "LDAP Source Property Mappings"
|
||||||
msgstr "LDAP-lähteen ominaisuuskytkennät"
|
msgstr "LDAP-lähteen ominaisuuskytkennät"
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid ""
|
||||||
|
"Unique ID used while checking if this object still exists in the directory."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "User LDAP Source Connection"
|
msgid "User LDAP Source Connection"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -2837,6 +2882,11 @@ msgstr "Ryhmän SAML-lähteen yhteys"
|
|||||||
msgid "Group SAML Source Connections"
|
msgid "Group SAML Source Connections"
|
||||||
msgstr "Ryhmän SAML-lähteen yhteydet"
|
msgstr "Ryhmän SAML-lähteen yhteydet"
|
||||||
|
|
||||||
|
#: authentik/sources/saml/views.py
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Continue to {source_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/scim/models.py
|
#: authentik/sources/scim/models.py
|
||||||
msgid "SCIM Source"
|
msgid "SCIM Source"
|
||||||
msgstr "SCIM-lähde"
|
msgstr "SCIM-lähde"
|
||||||
@ -3216,6 +3266,10 @@ msgstr "Käyttäjän hyväksyntä"
|
|||||||
msgid "User Consents"
|
msgid "User Consents"
|
||||||
msgstr "Käyttäjän hyväksynnät"
|
msgstr "Käyttäjän hyväksynnät"
|
||||||
|
|
||||||
|
#: authentik/stages/consent/stage.py
|
||||||
|
msgid "Invalid consent token, re-showing prompt"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/stages/deny/models.py
|
#: authentik/stages/deny/models.py
|
||||||
msgid "Deny Stage"
|
msgid "Deny Stage"
|
||||||
msgstr "Kieltovaihe"
|
msgstr "Kieltovaihe"
|
||||||
@ -3232,6 +3286,14 @@ msgstr "Valevaihe"
|
|||||||
msgid "Dummy Stages"
|
msgid "Dummy Stages"
|
||||||
msgstr "Valevaiheet"
|
msgstr "Valevaiheet"
|
||||||
|
|
||||||
|
#: authentik/stages/email/flow.py
|
||||||
|
msgid "Continue to confirm this email address."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/stages/email/flow.py
|
||||||
|
msgid "Link was already used, please request a new link."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/stages/email/models.py
|
#: authentik/stages/email/models.py
|
||||||
msgid "Password Reset"
|
msgid "Password Reset"
|
||||||
msgstr "Salasanan nollaus"
|
msgstr "Salasanan nollaus"
|
||||||
|
@ -19,7 +19,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-05-20 00:10+0000\n"
|
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
|
||||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||||
"Last-Translator: Marc Schmitt, 2025\n"
|
"Last-Translator: Marc Schmitt, 2025\n"
|
||||||
"Language-Team: French (https://app.transifex.com/authentik/teams/119923/fr/)\n"
|
"Language-Team: French (https://app.transifex.com/authentik/teams/119923/fr/)\n"
|
||||||
@ -1056,9 +1056,12 @@ msgid "Starting full provider sync"
|
|||||||
msgstr "Démarrage d'une synchronisation complète du fournisseur"
|
msgstr "Démarrage d'une synchronisation complète du fournisseur"
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
#, python-brace-format
|
msgid "Syncing users"
|
||||||
msgid "Syncing page {page} of users"
|
msgstr "Synchronisation des utilisateurs"
|
||||||
msgstr "Synchronisation de la page {page} d'utilisateurs"
|
|
||||||
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
|
msgid "Syncing groups"
|
||||||
|
msgstr "Synchronisation des groupes"
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
@ -2508,6 +2511,14 @@ msgstr ""
|
|||||||
"plutôt que sur un attribut de groupe. Cela permet la résolution des groupes "
|
"plutôt que sur un attribut de groupe. Cela permet la résolution des groupes "
|
||||||
"imbriqués sur des systèmes tels que FreeIPA et Active Directory."
|
"imbriqués sur des systèmes tels que FreeIPA et Active Directory."
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid ""
|
||||||
|
"Delete authentik users and groups which were previously supplied by this "
|
||||||
|
"source, but are now missing from it."
|
||||||
|
msgstr ""
|
||||||
|
"Supprimer les utilisateurs et les groupes authentik qui étaient auparavant "
|
||||||
|
"fournis par cette source, mais qui en sont maintenant absents."
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "LDAP Source"
|
msgid "LDAP Source"
|
||||||
msgstr "Source LDAP"
|
msgstr "Source LDAP"
|
||||||
@ -2524,6 +2535,13 @@ msgstr "Mappage de propriété source LDAP"
|
|||||||
msgid "LDAP Source Property Mappings"
|
msgid "LDAP Source Property Mappings"
|
||||||
msgstr "Mappages de propriété source LDAP"
|
msgstr "Mappages de propriété source LDAP"
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid ""
|
||||||
|
"Unique ID used while checking if this object still exists in the directory."
|
||||||
|
msgstr ""
|
||||||
|
"ID unique utilisé pour vérifier si cet objet existe toujours dans le "
|
||||||
|
"répertoire."
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "User LDAP Source Connection"
|
msgid "User LDAP Source Connection"
|
||||||
msgstr "Connexion de l'utilisateur à la source LDAP"
|
msgstr "Connexion de l'utilisateur à la source LDAP"
|
||||||
@ -2918,6 +2936,11 @@ msgstr "Connexion du groupe à la source SAML"
|
|||||||
msgid "Group SAML Source Connections"
|
msgid "Group SAML Source Connections"
|
||||||
msgstr "Connexions du groupe à la source SAML"
|
msgstr "Connexions du groupe à la source SAML"
|
||||||
|
|
||||||
|
#: authentik/sources/saml/views.py
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Continue to {source_name}"
|
||||||
|
msgstr "Continuer vers {source_name}"
|
||||||
|
|
||||||
#: authentik/sources/scim/models.py
|
#: authentik/sources/scim/models.py
|
||||||
msgid "SCIM Source"
|
msgid "SCIM Source"
|
||||||
msgstr "Source SCIM"
|
msgstr "Source SCIM"
|
||||||
@ -3308,6 +3331,10 @@ msgstr "Consentement Utilisateur"
|
|||||||
msgid "User Consents"
|
msgid "User Consents"
|
||||||
msgstr "Consentements Utilisateur"
|
msgstr "Consentements Utilisateur"
|
||||||
|
|
||||||
|
#: authentik/stages/consent/stage.py
|
||||||
|
msgid "Invalid consent token, re-showing prompt"
|
||||||
|
msgstr "Jeton de consentement invalide, réaffichage de l'invite"
|
||||||
|
|
||||||
#: authentik/stages/deny/models.py
|
#: authentik/stages/deny/models.py
|
||||||
msgid "Deny Stage"
|
msgid "Deny Stage"
|
||||||
msgstr "Étape de Refus"
|
msgstr "Étape de Refus"
|
||||||
@ -3324,6 +3351,14 @@ msgstr "Étape factice"
|
|||||||
msgid "Dummy Stages"
|
msgid "Dummy Stages"
|
||||||
msgstr "Étapes factices"
|
msgstr "Étapes factices"
|
||||||
|
|
||||||
|
#: authentik/stages/email/flow.py
|
||||||
|
msgid "Continue to confirm this email address."
|
||||||
|
msgstr "Continuer pour confirmer cette adresse courriel."
|
||||||
|
|
||||||
|
#: authentik/stages/email/flow.py
|
||||||
|
msgid "Link was already used, please request a new link."
|
||||||
|
msgstr "Ce lien a déjà été utilisé, veuillez en demander un nouveau."
|
||||||
|
|
||||||
#: authentik/stages/email/models.py
|
#: authentik/stages/email/models.py
|
||||||
msgid "Password Reset"
|
msgid "Password Reset"
|
||||||
msgstr "Réinitialiser le Mot de Passe"
|
msgstr "Réinitialiser le Mot de Passe"
|
||||||
|
@ -20,7 +20,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
|
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
|
||||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||||
"Last-Translator: Kowalski Dragon (kowalski7cc) <kowalski.7cc@gmail.com>, 2025\n"
|
"Last-Translator: Kowalski Dragon (kowalski7cc) <kowalski.7cc@gmail.com>, 2025\n"
|
||||||
"Language-Team: Italian (https://app.transifex.com/authentik/teams/119923/it/)\n"
|
"Language-Team: Italian (https://app.transifex.com/authentik/teams/119923/it/)\n"
|
||||||
@ -114,6 +114,10 @@ msgstr ""
|
|||||||
msgid "Web Certificate used by the authentik Core webserver."
|
msgid "Web Certificate used by the authentik Core webserver."
|
||||||
msgstr "Certificato Web utilizzato dal server Web authentik Core."
|
msgstr "Certificato Web utilizzato dal server Web authentik Core."
|
||||||
|
|
||||||
|
#: authentik/brands/models.py
|
||||||
|
msgid "Certificates used for client authentication."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/brands/models.py
|
#: authentik/brands/models.py
|
||||||
msgid "Brand"
|
msgid "Brand"
|
||||||
msgstr "Brand"
|
msgstr "Brand"
|
||||||
@ -672,6 +676,33 @@ msgstr "Dispositivi di Accesso"
|
|||||||
msgid "Verifying your browser..."
|
msgid "Verifying your browser..."
|
||||||
msgstr "Verifica del tuo browser..."
|
msgstr "Verifica del tuo browser..."
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid ""
|
||||||
|
"Configure certificate authorities to validate the certificate against. This "
|
||||||
|
"option has a higher priority than the `client_certificate` option on "
|
||||||
|
"`Brand`."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Mutual TLS Stage"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Mutual TLS Stages"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Permissions to pass Certificates for outposts."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/stage.py
|
||||||
|
msgid "Certificate required but no certificate was given."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/stage.py
|
||||||
|
msgid "No user found for certificate."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/enterprise/stages/source/models.py
|
#: authentik/enterprise/stages/source/models.py
|
||||||
msgid ""
|
msgid ""
|
||||||
"Amount of time a user can take to return from the source to continue the "
|
"Amount of time a user can take to return from the source to continue the "
|
||||||
@ -1018,9 +1049,12 @@ msgid "Starting full provider sync"
|
|||||||
msgstr "Avvio della sincronizzazione completa del provider"
|
msgstr "Avvio della sincronizzazione completa del provider"
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
#, python-brace-format
|
msgid "Syncing users"
|
||||||
msgid "Syncing page {page} of users"
|
msgstr ""
|
||||||
msgstr "Sincronizzando pagina {page} degli utenti"
|
|
||||||
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
|
msgid "Syncing groups"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
@ -2463,6 +2497,12 @@ msgstr ""
|
|||||||
"attributo di gruppo. Questo consente la risoluzione di gruppi nidificati su "
|
"attributo di gruppo. Questo consente la risoluzione di gruppi nidificati su "
|
||||||
"sistemi come FreeIPA e Active Directory."
|
"sistemi come FreeIPA e Active Directory."
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid ""
|
||||||
|
"Delete authentik users and groups which were previously supplied by this "
|
||||||
|
"source, but are now missing from it."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "LDAP Source"
|
msgid "LDAP Source"
|
||||||
msgstr "Sorgente LDAP"
|
msgstr "Sorgente LDAP"
|
||||||
@ -2479,6 +2519,11 @@ msgstr "Mappatura delle proprietà sorgente LDAP"
|
|||||||
msgid "LDAP Source Property Mappings"
|
msgid "LDAP Source Property Mappings"
|
||||||
msgstr "Mappature delle proprietà della sorgente LDAP"
|
msgstr "Mappature delle proprietà della sorgente LDAP"
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid ""
|
||||||
|
"Unique ID used while checking if this object still exists in the directory."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "User LDAP Source Connection"
|
msgid "User LDAP Source Connection"
|
||||||
msgstr "Connessione Sorgente LDAP Utente"
|
msgstr "Connessione Sorgente LDAP Utente"
|
||||||
@ -2872,6 +2917,11 @@ msgstr "Connessione sorgente SAML di gruppo"
|
|||||||
msgid "Group SAML Source Connections"
|
msgid "Group SAML Source Connections"
|
||||||
msgstr "Connessioni sorgente SAML di gruppo"
|
msgstr "Connessioni sorgente SAML di gruppo"
|
||||||
|
|
||||||
|
#: authentik/sources/saml/views.py
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Continue to {source_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/scim/models.py
|
#: authentik/sources/scim/models.py
|
||||||
msgid "SCIM Source"
|
msgid "SCIM Source"
|
||||||
msgstr "Sorgente SCIM"
|
msgstr "Sorgente SCIM"
|
||||||
@ -3269,6 +3319,10 @@ msgstr "Consenso utente"
|
|||||||
msgid "User Consents"
|
msgid "User Consents"
|
||||||
msgstr "Consensi utente"
|
msgstr "Consensi utente"
|
||||||
|
|
||||||
|
#: authentik/stages/consent/stage.py
|
||||||
|
msgid "Invalid consent token, re-showing prompt"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/stages/deny/models.py
|
#: authentik/stages/deny/models.py
|
||||||
msgid "Deny Stage"
|
msgid "Deny Stage"
|
||||||
msgstr "Fase di negazione"
|
msgstr "Fase di negazione"
|
||||||
@ -3285,6 +3339,14 @@ msgstr "Fase fittizia"
|
|||||||
msgid "Dummy Stages"
|
msgid "Dummy Stages"
|
||||||
msgstr "Fasi fittizie"
|
msgstr "Fasi fittizie"
|
||||||
|
|
||||||
|
#: authentik/stages/email/flow.py
|
||||||
|
msgid "Continue to confirm this email address."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/stages/email/flow.py
|
||||||
|
msgid "Link was already used, please request a new link."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/stages/email/models.py
|
#: authentik/stages/email/models.py
|
||||||
msgid "Password Reset"
|
msgid "Password Reset"
|
||||||
msgstr "Ripristino password"
|
msgstr "Ripristino password"
|
||||||
|
@ -12,7 +12,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
|
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
|
||||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||||
"Last-Translator: NavyStack, 2023\n"
|
"Last-Translator: NavyStack, 2023\n"
|
||||||
"Language-Team: Korean (https://app.transifex.com/authentik/teams/119923/ko/)\n"
|
"Language-Team: Korean (https://app.transifex.com/authentik/teams/119923/ko/)\n"
|
||||||
@ -99,6 +99,10 @@ msgstr ""
|
|||||||
msgid "Web Certificate used by the authentik Core webserver."
|
msgid "Web Certificate used by the authentik Core webserver."
|
||||||
msgstr "Authentik Core 웹서버에서 사용하는 웹 인증서."
|
msgstr "Authentik Core 웹서버에서 사용하는 웹 인증서."
|
||||||
|
|
||||||
|
#: authentik/brands/models.py
|
||||||
|
msgid "Certificates used for client authentication."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/brands/models.py
|
#: authentik/brands/models.py
|
||||||
msgid "Brand"
|
msgid "Brand"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -625,6 +629,33 @@ msgstr ""
|
|||||||
msgid "Verifying your browser..."
|
msgid "Verifying your browser..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid ""
|
||||||
|
"Configure certificate authorities to validate the certificate against. This "
|
||||||
|
"option has a higher priority than the `client_certificate` option on "
|
||||||
|
"`Brand`."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Mutual TLS Stage"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Mutual TLS Stages"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Permissions to pass Certificates for outposts."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/stage.py
|
||||||
|
msgid "Certificate required but no certificate was given."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/stage.py
|
||||||
|
msgid "No user found for certificate."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/enterprise/stages/source/models.py
|
#: authentik/enterprise/stages/source/models.py
|
||||||
msgid ""
|
msgid ""
|
||||||
"Amount of time a user can take to return from the source to continue the "
|
"Amount of time a user can take to return from the source to continue the "
|
||||||
@ -946,8 +977,11 @@ msgid "Starting full provider sync"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
#, python-brace-format
|
msgid "Syncing users"
|
||||||
msgid "Syncing page {page} of users"
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
|
msgid "Syncing groups"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
@ -2263,6 +2297,12 @@ msgid ""
|
|||||||
"Active Directory"
|
"Active Directory"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid ""
|
||||||
|
"Delete authentik users and groups which were previously supplied by this "
|
||||||
|
"source, but are now missing from it."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "LDAP Source"
|
msgid "LDAP Source"
|
||||||
msgstr "LDAP 소스"
|
msgstr "LDAP 소스"
|
||||||
@ -2279,6 +2319,11 @@ msgstr ""
|
|||||||
msgid "LDAP Source Property Mappings"
|
msgid "LDAP Source Property Mappings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid ""
|
||||||
|
"Unique ID used while checking if this object still exists in the directory."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "User LDAP Source Connection"
|
msgid "User LDAP Source Connection"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -2657,6 +2702,11 @@ msgstr ""
|
|||||||
msgid "Group SAML Source Connections"
|
msgid "Group SAML Source Connections"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/sources/saml/views.py
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Continue to {source_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/scim/models.py
|
#: authentik/sources/scim/models.py
|
||||||
msgid "SCIM Source"
|
msgid "SCIM Source"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -3017,6 +3067,10 @@ msgstr "사용자 동의"
|
|||||||
msgid "User Consents"
|
msgid "User Consents"
|
||||||
msgstr "사용자 동의"
|
msgstr "사용자 동의"
|
||||||
|
|
||||||
|
#: authentik/stages/consent/stage.py
|
||||||
|
msgid "Invalid consent token, re-showing prompt"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/stages/deny/models.py
|
#: authentik/stages/deny/models.py
|
||||||
msgid "Deny Stage"
|
msgid "Deny Stage"
|
||||||
msgstr "거부 스테이지"
|
msgstr "거부 스테이지"
|
||||||
@ -3033,6 +3087,14 @@ msgstr "더미 스테이지"
|
|||||||
msgid "Dummy Stages"
|
msgid "Dummy Stages"
|
||||||
msgstr "더미 스테이지"
|
msgstr "더미 스테이지"
|
||||||
|
|
||||||
|
#: authentik/stages/email/flow.py
|
||||||
|
msgid "Continue to confirm this email address."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/stages/email/flow.py
|
||||||
|
msgid "Link was already used, please request a new link."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/stages/email/models.py
|
#: authentik/stages/email/models.py
|
||||||
msgid "Password Reset"
|
msgid "Password Reset"
|
||||||
msgstr "비밀번호 초기화"
|
msgstr "비밀번호 초기화"
|
||||||
|
Binary file not shown.
@ -19,7 +19,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-04-11 00:10+0000\n"
|
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
|
||||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||||
"Last-Translator: Dany Sluijk, 2025\n"
|
"Last-Translator: Dany Sluijk, 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"
|
||||||
@ -113,6 +113,10 @@ msgstr ""
|
|||||||
msgid "Web Certificate used by the authentik Core webserver."
|
msgid "Web Certificate used by the authentik Core webserver."
|
||||||
msgstr "Webcertificaat gebruikt door de authentik Core-webserver."
|
msgstr "Webcertificaat gebruikt door de authentik Core-webserver."
|
||||||
|
|
||||||
|
#: authentik/brands/models.py
|
||||||
|
msgid "Certificates used for client authentication."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/brands/models.py
|
#: authentik/brands/models.py
|
||||||
msgid "Brand"
|
msgid "Brand"
|
||||||
msgstr "Merk"
|
msgstr "Merk"
|
||||||
@ -191,6 +195,7 @@ msgid "User's display name."
|
|||||||
msgstr "Weergavenaam van de gebruiker."
|
msgstr "Weergavenaam van de gebruiker."
|
||||||
|
|
||||||
#: authentik/core/models.py authentik/providers/oauth2/models.py
|
#: authentik/core/models.py authentik/providers/oauth2/models.py
|
||||||
|
#: authentik/rbac/models.py
|
||||||
msgid "User"
|
msgid "User"
|
||||||
msgstr "Gebruiker"
|
msgstr "Gebruiker"
|
||||||
|
|
||||||
@ -379,6 +384,18 @@ msgstr "Eigenschapskoppeling"
|
|||||||
msgid "Property Mappings"
|
msgid "Property Mappings"
|
||||||
msgstr "Eigenschapskoppelingen"
|
msgstr "Eigenschapskoppelingen"
|
||||||
|
|
||||||
|
#: authentik/core/models.py
|
||||||
|
msgid "session data"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/core/models.py
|
||||||
|
msgid "Session"
|
||||||
|
msgstr "Sessie"
|
||||||
|
|
||||||
|
#: authentik/core/models.py
|
||||||
|
msgid "Sessions"
|
||||||
|
msgstr "Sessies"
|
||||||
|
|
||||||
#: authentik/core/models.py
|
#: authentik/core/models.py
|
||||||
msgid "Authenticated Session"
|
msgid "Authenticated Session"
|
||||||
msgstr "Geauthenticeerde Sessie"
|
msgstr "Geauthenticeerde Sessie"
|
||||||
@ -486,6 +503,38 @@ msgstr "Licentie Gebruik"
|
|||||||
msgid "License Usage Records"
|
msgid "License Usage Records"
|
||||||
msgstr "Licentie Gebruik Records"
|
msgstr "Licentie Gebruik Records"
|
||||||
|
|
||||||
|
#: authentik/enterprise/policies/unique_password/models.py
|
||||||
|
#: authentik/policies/password/models.py
|
||||||
|
msgid "Field key to check, field keys defined in Prompt stages are available."
|
||||||
|
msgstr ""
|
||||||
|
"Veldsleutel om te controleren, veldsleutels gedefinieerd in Prompt-stadia "
|
||||||
|
"zijn beschikbaar."
|
||||||
|
|
||||||
|
#: authentik/enterprise/policies/unique_password/models.py
|
||||||
|
msgid "Number of passwords to check against."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/policies/unique_password/models.py
|
||||||
|
#: authentik/policies/password/models.py
|
||||||
|
msgid "Password not set in context"
|
||||||
|
msgstr "Wachtwoord niet ingesteld in context"
|
||||||
|
|
||||||
|
#: authentik/enterprise/policies/unique_password/models.py
|
||||||
|
msgid "This password has been used previously. Please choose a different one."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/policies/unique_password/models.py
|
||||||
|
msgid "Password Uniqueness Policy"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/policies/unique_password/models.py
|
||||||
|
msgid "Password Uniqueness Policies"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/policies/unique_password/models.py
|
||||||
|
msgid "User Password History"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/enterprise/policy.py
|
#: authentik/enterprise/policy.py
|
||||||
msgid "Enterprise required to access this feature."
|
msgid "Enterprise required to access this feature."
|
||||||
msgstr "Enterprise benodigd voor toegang tot deze functie."
|
msgstr "Enterprise benodigd voor toegang tot deze functie."
|
||||||
@ -622,6 +671,33 @@ msgstr ""
|
|||||||
msgid "Verifying your browser..."
|
msgid "Verifying your browser..."
|
||||||
msgstr "Uw browser wordt geverifieerd..."
|
msgstr "Uw browser wordt geverifieerd..."
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid ""
|
||||||
|
"Configure certificate authorities to validate the certificate against. This "
|
||||||
|
"option has a higher priority than the `client_certificate` option on "
|
||||||
|
"`Brand`."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Mutual TLS Stage"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Mutual TLS Stages"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Permissions to pass Certificates for outposts."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/stage.py
|
||||||
|
msgid "Certificate required but no certificate was given."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/stage.py
|
||||||
|
msgid "No user found for certificate."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/enterprise/stages/source/models.py
|
#: authentik/enterprise/stages/source/models.py
|
||||||
msgid ""
|
msgid ""
|
||||||
"Amount of time a user can take to return from the source to continue the "
|
"Amount of time a user can take to return from the source to continue the "
|
||||||
@ -963,8 +1039,11 @@ msgid "Starting full provider sync"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
#, python-brace-format
|
msgid "Syncing users"
|
||||||
msgid "Syncing page {page} of users"
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
|
msgid "Syncing groups"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
@ -1265,12 +1344,6 @@ msgstr ""
|
|||||||
msgid "Clear Policy's cache metrics"
|
msgid "Clear Policy's cache metrics"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/policies/password/models.py
|
|
||||||
msgid "Field key to check, field keys defined in Prompt stages are available."
|
|
||||||
msgstr ""
|
|
||||||
"Veldsleutel om te controleren, veldsleutels gedefinieerd in Prompt-stadia "
|
|
||||||
"zijn beschikbaar."
|
|
||||||
|
|
||||||
#: authentik/policies/password/models.py
|
#: authentik/policies/password/models.py
|
||||||
msgid "How many times the password hash is allowed to be on haveibeenpwned"
|
msgid "How many times the password hash is allowed to be on haveibeenpwned"
|
||||||
msgstr "Hoe vaak het wachtwoordhash op haveibeenpwned mag voorkomen"
|
msgstr "Hoe vaak het wachtwoordhash op haveibeenpwned mag voorkomen"
|
||||||
@ -1282,10 +1355,6 @@ msgstr ""
|
|||||||
"Als de zxcvbn-score gelijk is aan of lager is dan deze waarde, zal het "
|
"Als de zxcvbn-score gelijk is aan of lager is dan deze waarde, zal het "
|
||||||
"beleid falen."
|
"beleid falen."
|
||||||
|
|
||||||
#: authentik/policies/password/models.py
|
|
||||||
msgid "Password not set in context"
|
|
||||||
msgstr "Wachtwoord niet ingesteld in context"
|
|
||||||
|
|
||||||
#: authentik/policies/password/models.py
|
#: authentik/policies/password/models.py
|
||||||
msgid "Invalid password."
|
msgid "Invalid password."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -1327,20 +1396,6 @@ msgstr "Reputatie Score"
|
|||||||
msgid "Reputation Scores"
|
msgid "Reputation Scores"
|
||||||
msgstr "Reputatie Scores"
|
msgstr "Reputatie Scores"
|
||||||
|
|
||||||
#: authentik/policies/templates/policies/buffer.html
|
|
||||||
msgid "Waiting for authentication..."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: authentik/policies/templates/policies/buffer.html
|
|
||||||
msgid ""
|
|
||||||
"You're already authenticating in another tab. This page will refresh once "
|
|
||||||
"authentication is completed."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: authentik/policies/templates/policies/buffer.html
|
|
||||||
msgid "Authenticate in this tab"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: authentik/policies/templates/policies/denied.html
|
#: authentik/policies/templates/policies/denied.html
|
||||||
msgid "Permission denied"
|
msgid "Permission denied"
|
||||||
msgstr "Toestemming geweigerd"
|
msgstr "Toestemming geweigerd"
|
||||||
@ -2160,6 +2215,10 @@ msgstr ""
|
|||||||
msgid "Roles"
|
msgid "Roles"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/rbac/models.py
|
||||||
|
msgid "Initial Permissions"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/rbac/models.py
|
#: authentik/rbac/models.py
|
||||||
msgid "System permission"
|
msgid "System permission"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -2392,6 +2451,12 @@ msgid ""
|
|||||||
"Active Directory"
|
"Active Directory"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid ""
|
||||||
|
"Delete authentik users and groups which were previously supplied by this "
|
||||||
|
"source, but are now missing from it."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "LDAP Source"
|
msgid "LDAP Source"
|
||||||
msgstr "LDAP-bron"
|
msgstr "LDAP-bron"
|
||||||
@ -2408,6 +2473,27 @@ msgstr ""
|
|||||||
msgid "LDAP Source Property Mappings"
|
msgid "LDAP Source Property Mappings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid ""
|
||||||
|
"Unique ID used while checking if this object still exists in the directory."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid "User LDAP Source Connection"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid "User LDAP Source Connections"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid "Group LDAP Source Connection"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid "Group LDAP Source Connections"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/ldap/signals.py
|
#: authentik/sources/ldap/signals.py
|
||||||
msgid "Password does not match Active Directory Complexity."
|
msgid "Password does not match Active Directory Complexity."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -2417,6 +2503,14 @@ msgstr ""
|
|||||||
msgid "No token received."
|
msgid "No token received."
|
||||||
msgstr "Geen token ontvangen."
|
msgstr "Geen token ontvangen."
|
||||||
|
|
||||||
|
#: authentik/sources/oauth/models.py
|
||||||
|
msgid "HTTP Basic Authentication"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/sources/oauth/models.py
|
||||||
|
msgid "Include the client ID and secret as request parameters"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/oauth/models.py
|
#: authentik/sources/oauth/models.py
|
||||||
msgid "Request Token URL"
|
msgid "Request Token URL"
|
||||||
msgstr "URL voor aanvragen van token"
|
msgstr "URL voor aanvragen van token"
|
||||||
@ -2458,6 +2552,12 @@ msgstr ""
|
|||||||
msgid "Additional Scopes"
|
msgid "Additional Scopes"
|
||||||
msgstr "Aanvullende scopes"
|
msgstr "Aanvullende scopes"
|
||||||
|
|
||||||
|
#: authentik/sources/oauth/models.py
|
||||||
|
msgid ""
|
||||||
|
"How to perform authentication during an authorization_code token request "
|
||||||
|
"flow"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/oauth/models.py
|
#: authentik/sources/oauth/models.py
|
||||||
msgid "OAuth Source"
|
msgid "OAuth Source"
|
||||||
msgstr "OAuth-bron"
|
msgstr "OAuth-bron"
|
||||||
@ -2769,6 +2869,11 @@ msgstr ""
|
|||||||
msgid "Group SAML Source Connections"
|
msgid "Group SAML Source Connections"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/sources/saml/views.py
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Continue to {source_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/scim/models.py
|
#: authentik/sources/scim/models.py
|
||||||
msgid "SCIM Source"
|
msgid "SCIM Source"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -3142,6 +3247,10 @@ msgstr "Gebruikerstoestemming"
|
|||||||
msgid "User Consents"
|
msgid "User Consents"
|
||||||
msgstr "Gebruikersinstemmingen"
|
msgstr "Gebruikersinstemmingen"
|
||||||
|
|
||||||
|
#: authentik/stages/consent/stage.py
|
||||||
|
msgid "Invalid consent token, re-showing prompt"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/stages/deny/models.py
|
#: authentik/stages/deny/models.py
|
||||||
msgid "Deny Stage"
|
msgid "Deny Stage"
|
||||||
msgstr "Weigerfase"
|
msgstr "Weigerfase"
|
||||||
@ -3158,6 +3267,14 @@ msgstr "Dummystadium"
|
|||||||
msgid "Dummy Stages"
|
msgid "Dummy Stages"
|
||||||
msgstr "Dummystadia"
|
msgstr "Dummystadia"
|
||||||
|
|
||||||
|
#: authentik/stages/email/flow.py
|
||||||
|
msgid "Continue to confirm this email address."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/stages/email/flow.py
|
||||||
|
msgid "Link was already used, please request a new link."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/stages/email/models.py
|
#: authentik/stages/email/models.py
|
||||||
msgid "Password Reset"
|
msgid "Password Reset"
|
||||||
msgstr "Wachtwoordherstel"
|
msgstr "Wachtwoordherstel"
|
||||||
@ -3357,6 +3474,12 @@ msgstr ""
|
|||||||
"Wanneer ingeschakeld, slaagt de stap en gaat verder wanneer ongeldige "
|
"Wanneer ingeschakeld, slaagt de stap en gaat verder wanneer ongeldige "
|
||||||
"gebruikersgegevens zijn ingevoerd."
|
"gebruikersgegevens zijn ingevoerd."
|
||||||
|
|
||||||
|
#: authentik/stages/identification/models.py
|
||||||
|
msgid ""
|
||||||
|
"Show the user the 'Remember me on this device' toggle, allowing repeat users"
|
||||||
|
" to skip straight to entering their password."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/stages/identification/models.py
|
#: authentik/stages/identification/models.py
|
||||||
msgid "Optional enrollment flow, which is linked at the bottom of the page."
|
msgid "Optional enrollment flow, which is linked at the bottom of the page."
|
||||||
msgstr "Optionele inschrijvingsflow, die onderaan de pagina is gekoppeld."
|
msgstr "Optionele inschrijvingsflow, die onderaan de pagina is gekoppeld."
|
||||||
@ -3742,6 +3865,14 @@ msgstr ""
|
|||||||
"Gebeurtenissen worden verwijderd na deze duur. (Indeling: "
|
"Gebeurtenissen worden verwijderd na deze duur. (Indeling: "
|
||||||
"weken=3;dagen=2;uren=3;seconden=2)."
|
"weken=3;dagen=2;uren=3;seconden=2)."
|
||||||
|
|
||||||
|
#: authentik/tenants/models.py
|
||||||
|
msgid "Reputation cannot decrease lower than this value. Zero or negative."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/tenants/models.py
|
||||||
|
msgid "Reputation cannot increase higher than this value. Zero or positive."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/tenants/models.py
|
#: authentik/tenants/models.py
|
||||||
msgid "The option configures the footer links on the flow executor pages."
|
msgid "The option configures the footer links on the flow executor pages."
|
||||||
msgstr "De optie stelt de voettekst links in op de flow uitvoer pagina's."
|
msgstr "De optie stelt de voettekst links in op de flow uitvoer pagina's."
|
||||||
|
@ -11,7 +11,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
|
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
|
||||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||||
"Last-Translator: Hugo Bicho, 2025\n"
|
"Last-Translator: Hugo Bicho, 2025\n"
|
||||||
"Language-Team: Portuguese (https://app.transifex.com/authentik/teams/119923/pt/)\n"
|
"Language-Team: Portuguese (https://app.transifex.com/authentik/teams/119923/pt/)\n"
|
||||||
@ -105,6 +105,10 @@ msgstr ""
|
|||||||
msgid "Web Certificate used by the authentik Core webserver."
|
msgid "Web Certificate used by the authentik Core webserver."
|
||||||
msgstr "Certificado Web usado pelo servidor web authentik Core."
|
msgstr "Certificado Web usado pelo servidor web authentik Core."
|
||||||
|
|
||||||
|
#: authentik/brands/models.py
|
||||||
|
msgid "Certificates used for client authentication."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/brands/models.py
|
#: authentik/brands/models.py
|
||||||
msgid "Brand"
|
msgid "Brand"
|
||||||
msgstr "Marca"
|
msgstr "Marca"
|
||||||
@ -662,6 +666,33 @@ msgstr "Dispositivos do ponto de ligação"
|
|||||||
msgid "Verifying your browser..."
|
msgid "Verifying your browser..."
|
||||||
msgstr "A verificar o seu browser..."
|
msgstr "A verificar o seu browser..."
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid ""
|
||||||
|
"Configure certificate authorities to validate the certificate against. This "
|
||||||
|
"option has a higher priority than the `client_certificate` option on "
|
||||||
|
"`Brand`."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Mutual TLS Stage"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Mutual TLS Stages"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Permissions to pass Certificates for outposts."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/stage.py
|
||||||
|
msgid "Certificate required but no certificate was given."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/stage.py
|
||||||
|
msgid "No user found for certificate."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/enterprise/stages/source/models.py
|
#: authentik/enterprise/stages/source/models.py
|
||||||
msgid ""
|
msgid ""
|
||||||
"Amount of time a user can take to return from the source to continue the "
|
"Amount of time a user can take to return from the source to continue the "
|
||||||
@ -1007,9 +1038,12 @@ msgid "Starting full provider sync"
|
|||||||
msgstr "Iniciando a sincronização completa com o provedor"
|
msgstr "Iniciando a sincronização completa com o provedor"
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
#, python-brace-format
|
msgid "Syncing users"
|
||||||
msgid "Syncing page {page} of users"
|
msgstr ""
|
||||||
msgstr "A sincronizar a página {page} dos utilizadores"
|
|
||||||
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
|
msgid "Syncing groups"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
@ -2456,6 +2490,12 @@ msgstr ""
|
|||||||
" um atributo do grupo. Isto permite a resolução de grupos hierárquicos em "
|
" um atributo do grupo. Isto permite a resolução de grupos hierárquicos em "
|
||||||
"sistemas como o FreeIPA e Active Directory."
|
"sistemas como o FreeIPA e Active Directory."
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid ""
|
||||||
|
"Delete authentik users and groups which were previously supplied by this "
|
||||||
|
"source, but are now missing from it."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "LDAP Source"
|
msgid "LDAP Source"
|
||||||
msgstr "Fonte LDAP"
|
msgstr "Fonte LDAP"
|
||||||
@ -2472,6 +2512,11 @@ msgstr "Mapeamento de propriedades de fonte LDAP"
|
|||||||
msgid "LDAP Source Property Mappings"
|
msgid "LDAP Source Property Mappings"
|
||||||
msgstr "Mapeamentos de propriedades de fonte LDAP"
|
msgstr "Mapeamentos de propriedades de fonte LDAP"
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid ""
|
||||||
|
"Unique ID used while checking if this object still exists in the directory."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "User LDAP Source Connection"
|
msgid "User LDAP Source Connection"
|
||||||
msgstr "Ligação à fonte LDAP de Utilizador"
|
msgstr "Ligação à fonte LDAP de Utilizador"
|
||||||
@ -2865,6 +2910,11 @@ msgstr "Ligação à fonte SAML de Grupo"
|
|||||||
msgid "Group SAML Source Connections"
|
msgid "Group SAML Source Connections"
|
||||||
msgstr "Ligações à fonte SAML de Grupo"
|
msgstr "Ligações à fonte SAML de Grupo"
|
||||||
|
|
||||||
|
#: authentik/sources/saml/views.py
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Continue to {source_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/scim/models.py
|
#: authentik/sources/scim/models.py
|
||||||
msgid "SCIM Source"
|
msgid "SCIM Source"
|
||||||
msgstr "Fonte SCIM"
|
msgstr "Fonte SCIM"
|
||||||
@ -3255,6 +3305,10 @@ msgstr "Consentimento do Utilizador"
|
|||||||
msgid "User Consents"
|
msgid "User Consents"
|
||||||
msgstr "Consentimentos do Utilizador"
|
msgstr "Consentimentos do Utilizador"
|
||||||
|
|
||||||
|
#: authentik/stages/consent/stage.py
|
||||||
|
msgid "Invalid consent token, re-showing prompt"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/stages/deny/models.py
|
#: authentik/stages/deny/models.py
|
||||||
msgid "Deny Stage"
|
msgid "Deny Stage"
|
||||||
msgstr "Etapa de negação"
|
msgstr "Etapa de negação"
|
||||||
@ -3271,6 +3325,14 @@ msgstr "Etapa fictícia"
|
|||||||
msgid "Dummy Stages"
|
msgid "Dummy Stages"
|
||||||
msgstr "Etapas fictícias"
|
msgstr "Etapas fictícias"
|
||||||
|
|
||||||
|
#: authentik/stages/email/flow.py
|
||||||
|
msgid "Continue to confirm this email address."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/stages/email/flow.py
|
||||||
|
msgid "Link was already used, please request a new link."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/stages/email/models.py
|
#: authentik/stages/email/models.py
|
||||||
msgid "Password Reset"
|
msgid "Password Reset"
|
||||||
msgstr "Redefinição de Palavra-Passe"
|
msgstr "Redefinição de Palavra-Passe"
|
||||||
|
Binary file not shown.
@ -8,19 +8,19 @@
|
|||||||
# Josenivaldo Benito Junior, 2023
|
# Josenivaldo Benito Junior, 2023
|
||||||
# Caio Lima, 2023
|
# Caio Lima, 2023
|
||||||
# Hacklab, 2023
|
# Hacklab, 2023
|
||||||
# Wagner Santos, 2024
|
|
||||||
# Rafael Mundel, 2024
|
# Rafael Mundel, 2024
|
||||||
# Anderson Silva Andrade <anderson.asa89@gmail.com>, 2025
|
# Anderson Silva Andrade <anderson.asa89@gmail.com>, 2025
|
||||||
# Gil Poiares-Oliveira, 2025
|
# Gil Poiares-Oliveira, 2025
|
||||||
|
# Wagner Santos, 2025
|
||||||
#
|
#
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
|
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
|
||||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||||
"Last-Translator: Gil Poiares-Oliveira, 2025\n"
|
"Last-Translator: Wagner Santos, 2025\n"
|
||||||
"Language-Team: Portuguese (Brazil) (https://app.transifex.com/authentik/teams/119923/pt_BR/)\n"
|
"Language-Team: Portuguese (Brazil) (https://app.transifex.com/authentik/teams/119923/pt_BR/)\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"
|
||||||
@ -112,6 +112,10 @@ msgstr ""
|
|||||||
msgid "Web Certificate used by the authentik Core webserver."
|
msgid "Web Certificate used by the authentik Core webserver."
|
||||||
msgstr "Certificado da Web usado pelo servidor da web authentik Core."
|
msgstr "Certificado da Web usado pelo servidor da web authentik Core."
|
||||||
|
|
||||||
|
#: authentik/brands/models.py
|
||||||
|
msgid "Certificates used for client authentication."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/brands/models.py
|
#: authentik/brands/models.py
|
||||||
msgid "Brand"
|
msgid "Brand"
|
||||||
msgstr "Brand"
|
msgstr "Brand"
|
||||||
@ -271,11 +275,11 @@ msgstr "Aplicativos"
|
|||||||
|
|
||||||
#: authentik/core/models.py
|
#: authentik/core/models.py
|
||||||
msgid "Application Entitlement"
|
msgid "Application Entitlement"
|
||||||
msgstr ""
|
msgstr "Autorização de aplicação"
|
||||||
|
|
||||||
#: authentik/core/models.py
|
#: authentik/core/models.py
|
||||||
msgid "Application Entitlements"
|
msgid "Application Entitlements"
|
||||||
msgstr ""
|
msgstr "Autorizações de aplicação"
|
||||||
|
|
||||||
#: authentik/core/models.py
|
#: authentik/core/models.py
|
||||||
msgid "Use the source-specific identifier"
|
msgid "Use the source-specific identifier"
|
||||||
@ -379,15 +383,15 @@ msgstr "Mapeamentos de propriedades"
|
|||||||
|
|
||||||
#: authentik/core/models.py
|
#: authentik/core/models.py
|
||||||
msgid "session data"
|
msgid "session data"
|
||||||
msgstr ""
|
msgstr "dados de sessão"
|
||||||
|
|
||||||
#: authentik/core/models.py
|
#: authentik/core/models.py
|
||||||
msgid "Session"
|
msgid "Session"
|
||||||
msgstr ""
|
msgstr "Sessão"
|
||||||
|
|
||||||
#: authentik/core/models.py
|
#: authentik/core/models.py
|
||||||
msgid "Sessions"
|
msgid "Sessions"
|
||||||
msgstr ""
|
msgstr "Sessões"
|
||||||
|
|
||||||
#: authentik/core/models.py
|
#: authentik/core/models.py
|
||||||
msgid "Authenticated Session"
|
msgid "Authenticated Session"
|
||||||
@ -505,7 +509,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: authentik/enterprise/policies/unique_password/models.py
|
#: authentik/enterprise/policies/unique_password/models.py
|
||||||
msgid "Number of passwords to check against."
|
msgid "Number of passwords to check against."
|
||||||
msgstr ""
|
msgstr "Número de senhas para verificar."
|
||||||
|
|
||||||
#: authentik/enterprise/policies/unique_password/models.py
|
#: authentik/enterprise/policies/unique_password/models.py
|
||||||
#: authentik/policies/password/models.py
|
#: authentik/policies/password/models.py
|
||||||
@ -514,19 +518,19 @@ msgstr "Senha não definida no contexto"
|
|||||||
|
|
||||||
#: authentik/enterprise/policies/unique_password/models.py
|
#: authentik/enterprise/policies/unique_password/models.py
|
||||||
msgid "This password has been used previously. Please choose a different one."
|
msgid "This password has been used previously. Please choose a different one."
|
||||||
msgstr ""
|
msgstr "A senha já foi utilizada antes. Por favor, escolha uma diferente."
|
||||||
|
|
||||||
#: authentik/enterprise/policies/unique_password/models.py
|
#: authentik/enterprise/policies/unique_password/models.py
|
||||||
msgid "Password Uniqueness Policy"
|
msgid "Password Uniqueness Policy"
|
||||||
msgstr ""
|
msgstr "Política de exclusividade de senha"
|
||||||
|
|
||||||
#: authentik/enterprise/policies/unique_password/models.py
|
#: authentik/enterprise/policies/unique_password/models.py
|
||||||
msgid "Password Uniqueness Policies"
|
msgid "Password Uniqueness Policies"
|
||||||
msgstr ""
|
msgstr "Políticas de exclusividade de senha"
|
||||||
|
|
||||||
#: authentik/enterprise/policies/unique_password/models.py
|
#: authentik/enterprise/policies/unique_password/models.py
|
||||||
msgid "User Password History"
|
msgid "User Password History"
|
||||||
msgstr ""
|
msgstr "Histórico de senhas do usuário"
|
||||||
|
|
||||||
#: authentik/enterprise/policy.py
|
#: authentik/enterprise/policy.py
|
||||||
msgid "Enterprise required to access this feature."
|
msgid "Enterprise required to access this feature."
|
||||||
@ -610,39 +614,39 @@ msgstr "Chave de Assinatura"
|
|||||||
|
|
||||||
#: 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 "Chave utilizada para assinar os eventos 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 "Provedor de 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 "Provedores de 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 "Adicionar stream ao fornecedor SSF"
|
||||||
|
|
||||||
#: authentik/enterprise/providers/ssf/models.py
|
#: authentik/enterprise/providers/ssf/models.py
|
||||||
msgid "SSF Stream"
|
msgid "SSF Stream"
|
||||||
msgstr ""
|
msgstr "Stream SSF"
|
||||||
|
|
||||||
#: authentik/enterprise/providers/ssf/models.py
|
#: authentik/enterprise/providers/ssf/models.py
|
||||||
msgid "SSF Streams"
|
msgid "SSF Streams"
|
||||||
msgstr ""
|
msgstr "Streams SSF"
|
||||||
|
|
||||||
#: authentik/enterprise/providers/ssf/models.py
|
#: authentik/enterprise/providers/ssf/models.py
|
||||||
msgid "SSF Stream Event"
|
msgid "SSF Stream Event"
|
||||||
msgstr ""
|
msgstr "Evento de stream SSF"
|
||||||
|
|
||||||
#: authentik/enterprise/providers/ssf/models.py
|
#: authentik/enterprise/providers/ssf/models.py
|
||||||
msgid "SSF Stream Events"
|
msgid "SSF Stream Events"
|
||||||
msgstr ""
|
msgstr "Eventos de stream 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 "Falha ao enviar requisição"
|
||||||
|
|
||||||
#: 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"
|
||||||
@ -664,6 +668,33 @@ msgstr ""
|
|||||||
msgid "Verifying your browser..."
|
msgid "Verifying your browser..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid ""
|
||||||
|
"Configure certificate authorities to validate the certificate against. This "
|
||||||
|
"option has a higher priority than the `client_certificate` option on "
|
||||||
|
"`Brand`."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Mutual TLS Stage"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Mutual TLS Stages"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Permissions to pass Certificates for outposts."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/stage.py
|
||||||
|
msgid "Certificate required but no certificate was given."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/stage.py
|
||||||
|
msgid "No user found for certificate."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/enterprise/stages/source/models.py
|
#: authentik/enterprise/stages/source/models.py
|
||||||
msgid ""
|
msgid ""
|
||||||
"Amount of time a user can take to return from the source to continue the "
|
"Amount of time a user can take to return from the source to continue the "
|
||||||
@ -681,7 +712,7 @@ msgstr ""
|
|||||||
#: authentik/events/api/tasks.py
|
#: authentik/events/api/tasks.py
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Successfully started task {name}."
|
msgid "Successfully started task {name}."
|
||||||
msgstr ""
|
msgstr "Tarefa {name} iniciada com sucesso."
|
||||||
|
|
||||||
#: authentik/events/models.py
|
#: authentik/events/models.py
|
||||||
msgid "Event"
|
msgid "Event"
|
||||||
@ -713,12 +744,16 @@ msgid ""
|
|||||||
"Customize the body of the request. Mapping should return data that is JSON-"
|
"Customize the body of the request. Mapping should return data that is JSON-"
|
||||||
"serializable."
|
"serializable."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"Personalize o corpo do pedido. O mapeamento deve retornar dados que sejam "
|
||||||
|
"serializáveis em JSON."
|
||||||
|
|
||||||
#: authentik/events/models.py
|
#: authentik/events/models.py
|
||||||
msgid ""
|
msgid ""
|
||||||
"Configure additional headers to be sent. Mapping should return a dictionary "
|
"Configure additional headers to be sent. Mapping should return a dictionary "
|
||||||
"of key-value pairs"
|
"of key-value pairs"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"Configurar cabeçalhos adicionais a serem enviados. O mapeamento deve "
|
||||||
|
"retornar um dicionário de pares chave-valor"
|
||||||
|
|
||||||
#: authentik/events/models.py
|
#: authentik/events/models.py
|
||||||
msgid ""
|
msgid ""
|
||||||
@ -998,8 +1033,11 @@ msgid "Starting full provider sync"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
#, python-brace-format
|
msgid "Syncing users"
|
||||||
msgid "Syncing page {page} of users"
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
|
msgid "Syncing groups"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
@ -1314,7 +1352,7 @@ msgstr ""
|
|||||||
#: authentik/policies/password/models.py
|
#: authentik/policies/password/models.py
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Password exists on {count} online lists."
|
msgid "Password exists on {count} online lists."
|
||||||
msgstr ""
|
msgstr "A senha está presente em {count} listas de senhas vulneráveis."
|
||||||
|
|
||||||
#: authentik/policies/password/models.py
|
#: authentik/policies/password/models.py
|
||||||
msgid "Password is too weak."
|
msgid "Password is too weak."
|
||||||
@ -2396,6 +2434,12 @@ msgid ""
|
|||||||
"Active Directory"
|
"Active Directory"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid ""
|
||||||
|
"Delete authentik users and groups which were previously supplied by this "
|
||||||
|
"source, but are now missing from it."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "LDAP Source"
|
msgid "LDAP Source"
|
||||||
msgstr "Fonte LDAP"
|
msgstr "Fonte LDAP"
|
||||||
@ -2412,6 +2456,11 @@ msgstr ""
|
|||||||
msgid "LDAP Source Property Mappings"
|
msgid "LDAP Source Property Mappings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid ""
|
||||||
|
"Unique ID used while checking if this object still exists in the directory."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "User LDAP Source Connection"
|
msgid "User LDAP Source Connection"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -2802,6 +2851,11 @@ msgstr ""
|
|||||||
msgid "Group SAML Source Connections"
|
msgid "Group SAML Source Connections"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/sources/saml/views.py
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Continue to {source_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/scim/models.py
|
#: authentik/sources/scim/models.py
|
||||||
msgid "SCIM Source"
|
msgid "SCIM Source"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -3174,6 +3228,10 @@ msgstr "Consentimento do usuário"
|
|||||||
msgid "User Consents"
|
msgid "User Consents"
|
||||||
msgstr "Consentimentos do usuário"
|
msgstr "Consentimentos do usuário"
|
||||||
|
|
||||||
|
#: authentik/stages/consent/stage.py
|
||||||
|
msgid "Invalid consent token, re-showing prompt"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/stages/deny/models.py
|
#: authentik/stages/deny/models.py
|
||||||
msgid "Deny Stage"
|
msgid "Deny Stage"
|
||||||
msgstr "Negar Estágio"
|
msgstr "Negar Estágio"
|
||||||
@ -3190,6 +3248,14 @@ msgstr "Palco fictício"
|
|||||||
msgid "Dummy Stages"
|
msgid "Dummy Stages"
|
||||||
msgstr "Fases fictícias"
|
msgstr "Fases fictícias"
|
||||||
|
|
||||||
|
#: authentik/stages/email/flow.py
|
||||||
|
msgid "Continue to confirm this email address."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/stages/email/flow.py
|
||||||
|
msgid "Link was already used, please request a new link."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/stages/email/models.py
|
#: authentik/stages/email/models.py
|
||||||
msgid "Password Reset"
|
msgid "Password Reset"
|
||||||
msgstr "Redefinição de senha"
|
msgstr "Redefinição de senha"
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -18,7 +18,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
|
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
|
||||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||||
"Last-Translator: Marc Schmitt, 2025\n"
|
"Last-Translator: Marc Schmitt, 2025\n"
|
||||||
"Language-Team: Russian (https://app.transifex.com/authentik/teams/119923/ru/)\n"
|
"Language-Team: Russian (https://app.transifex.com/authentik/teams/119923/ru/)\n"
|
||||||
@ -111,6 +111,10 @@ msgstr ""
|
|||||||
msgid "Web Certificate used by the authentik Core webserver."
|
msgid "Web Certificate used by the authentik Core webserver."
|
||||||
msgstr "Web Certificate используемый для authentik Core webserver."
|
msgstr "Web Certificate используемый для authentik Core webserver."
|
||||||
|
|
||||||
|
#: authentik/brands/models.py
|
||||||
|
msgid "Certificates used for client authentication."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/brands/models.py
|
#: authentik/brands/models.py
|
||||||
msgid "Brand"
|
msgid "Brand"
|
||||||
msgstr "Бренд"
|
msgstr "Бренд"
|
||||||
@ -669,6 +673,33 @@ msgstr "Конечные устройства"
|
|||||||
msgid "Verifying your browser..."
|
msgid "Verifying your browser..."
|
||||||
msgstr "Проверка вашего браузера..."
|
msgstr "Проверка вашего браузера..."
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid ""
|
||||||
|
"Configure certificate authorities to validate the certificate against. This "
|
||||||
|
"option has a higher priority than the `client_certificate` option on "
|
||||||
|
"`Brand`."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Mutual TLS Stage"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Mutual TLS Stages"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Permissions to pass Certificates for outposts."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/stage.py
|
||||||
|
msgid "Certificate required but no certificate was given."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/stage.py
|
||||||
|
msgid "No user found for certificate."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/enterprise/stages/source/models.py
|
#: authentik/enterprise/stages/source/models.py
|
||||||
msgid ""
|
msgid ""
|
||||||
"Amount of time a user can take to return from the source to continue the "
|
"Amount of time a user can take to return from the source to continue the "
|
||||||
@ -1009,8 +1040,11 @@ msgid "Starting full provider sync"
|
|||||||
msgstr "Запуск полной синхронизации провайдера"
|
msgstr "Запуск полной синхронизации провайдера"
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
#, python-brace-format
|
msgid "Syncing users"
|
||||||
msgid "Syncing page {page} of users"
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
|
msgid "Syncing groups"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
@ -2430,6 +2464,12 @@ msgid ""
|
|||||||
"Active Directory"
|
"Active Directory"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid ""
|
||||||
|
"Delete authentik users and groups which were previously supplied by this "
|
||||||
|
"source, but are now missing from it."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "LDAP Source"
|
msgid "LDAP Source"
|
||||||
msgstr "Источник LDAP"
|
msgstr "Источник LDAP"
|
||||||
@ -2446,6 +2486,11 @@ msgstr "Сопоставление свойства LDAP источника"
|
|||||||
msgid "LDAP Source Property Mappings"
|
msgid "LDAP Source Property Mappings"
|
||||||
msgstr "Сопоставление свойств LDAP источника"
|
msgstr "Сопоставление свойств LDAP источника"
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid ""
|
||||||
|
"Unique ID used while checking if this object still exists in the directory."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "User LDAP Source Connection"
|
msgid "User LDAP Source Connection"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -2842,6 +2887,11 @@ msgstr "Групповое подключение к источнику SAML"
|
|||||||
msgid "Group SAML Source Connections"
|
msgid "Group SAML Source Connections"
|
||||||
msgstr "Групповые подключения к источнику SAML"
|
msgstr "Групповые подключения к источнику SAML"
|
||||||
|
|
||||||
|
#: authentik/sources/saml/views.py
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Continue to {source_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/scim/models.py
|
#: authentik/sources/scim/models.py
|
||||||
msgid "SCIM Source"
|
msgid "SCIM Source"
|
||||||
msgstr "Источник SCIM"
|
msgstr "Источник SCIM"
|
||||||
@ -3219,6 +3269,10 @@ msgstr "Согласие пользователя"
|
|||||||
msgid "User Consents"
|
msgid "User Consents"
|
||||||
msgstr "Согласия пользователя"
|
msgstr "Согласия пользователя"
|
||||||
|
|
||||||
|
#: authentik/stages/consent/stage.py
|
||||||
|
msgid "Invalid consent token, re-showing prompt"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/stages/deny/models.py
|
#: authentik/stages/deny/models.py
|
||||||
msgid "Deny Stage"
|
msgid "Deny Stage"
|
||||||
msgstr "Этап отказа"
|
msgstr "Этап отказа"
|
||||||
@ -3235,6 +3289,14 @@ msgstr "Фиктивный этап"
|
|||||||
msgid "Dummy Stages"
|
msgid "Dummy Stages"
|
||||||
msgstr "Фиктивные этапы"
|
msgstr "Фиктивные этапы"
|
||||||
|
|
||||||
|
#: authentik/stages/email/flow.py
|
||||||
|
msgid "Continue to confirm this email address."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/stages/email/flow.py
|
||||||
|
msgid "Link was already used, please request a new link."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/stages/email/models.py
|
#: authentik/stages/email/models.py
|
||||||
msgid "Password Reset"
|
msgid "Password Reset"
|
||||||
msgstr "Сброс пароля"
|
msgstr "Сброс пароля"
|
||||||
|
@ -13,7 +13,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
|
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
|
||||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||||
"Last-Translator: Jens L. <jens@goauthentik.io>, 2025\n"
|
"Last-Translator: Jens L. <jens@goauthentik.io>, 2025\n"
|
||||||
"Language-Team: Turkish (https://app.transifex.com/authentik/teams/119923/tr/)\n"
|
"Language-Team: Turkish (https://app.transifex.com/authentik/teams/119923/tr/)\n"
|
||||||
@ -107,6 +107,10 @@ msgstr ""
|
|||||||
msgid "Web Certificate used by the authentik Core webserver."
|
msgid "Web Certificate used by the authentik Core webserver."
|
||||||
msgstr "Authentik Core web sunucusu tarafından kullanılan Web Sertifikası."
|
msgstr "Authentik Core web sunucusu tarafından kullanılan Web Sertifikası."
|
||||||
|
|
||||||
|
#: authentik/brands/models.py
|
||||||
|
msgid "Certificates used for client authentication."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/brands/models.py
|
#: authentik/brands/models.py
|
||||||
msgid "Brand"
|
msgid "Brand"
|
||||||
msgstr "Marka"
|
msgstr "Marka"
|
||||||
@ -659,6 +663,33 @@ msgstr "Uç Nokta Cihazları"
|
|||||||
msgid "Verifying your browser..."
|
msgid "Verifying your browser..."
|
||||||
msgstr "Tarayıcınız doğrulanıyor..."
|
msgstr "Tarayıcınız doğrulanıyor..."
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid ""
|
||||||
|
"Configure certificate authorities to validate the certificate against. This "
|
||||||
|
"option has a higher priority than the `client_certificate` option on "
|
||||||
|
"`Brand`."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Mutual TLS Stage"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Mutual TLS Stages"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Permissions to pass Certificates for outposts."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/stage.py
|
||||||
|
msgid "Certificate required but no certificate was given."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/stage.py
|
||||||
|
msgid "No user found for certificate."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/enterprise/stages/source/models.py
|
#: authentik/enterprise/stages/source/models.py
|
||||||
msgid ""
|
msgid ""
|
||||||
"Amount of time a user can take to return from the source to continue the "
|
"Amount of time a user can take to return from the source to continue the "
|
||||||
@ -1000,8 +1031,11 @@ msgid "Starting full provider sync"
|
|||||||
msgstr "Tam sağlayıcı senkronizasyonunu başlatma"
|
msgstr "Tam sağlayıcı senkronizasyonunu başlatma"
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
#, python-brace-format
|
msgid "Syncing users"
|
||||||
msgid "Syncing page {page} of users"
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
|
msgid "Syncing groups"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
@ -2430,6 +2464,12 @@ msgid ""
|
|||||||
"Active Directory"
|
"Active Directory"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid ""
|
||||||
|
"Delete authentik users and groups which were previously supplied by this "
|
||||||
|
"source, but are now missing from it."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "LDAP Source"
|
msgid "LDAP Source"
|
||||||
msgstr "LDAP Kaynağı"
|
msgstr "LDAP Kaynağı"
|
||||||
@ -2446,6 +2486,11 @@ msgstr "LDAP Kaynak Özellik Eşlemesi"
|
|||||||
msgid "LDAP Source Property Mappings"
|
msgid "LDAP Source Property Mappings"
|
||||||
msgstr "LDAP Kaynak Özellik Eşlemeleri"
|
msgstr "LDAP Kaynak Özellik Eşlemeleri"
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid ""
|
||||||
|
"Unique ID used while checking if this object still exists in the directory."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "User LDAP Source Connection"
|
msgid "User LDAP Source Connection"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -2837,6 +2882,11 @@ msgstr "Grup SAML Kaynak Bağlantısı"
|
|||||||
msgid "Group SAML Source Connections"
|
msgid "Group SAML Source Connections"
|
||||||
msgstr "Grup SAML Kaynak Bağlantıları"
|
msgstr "Grup SAML Kaynak Bağlantıları"
|
||||||
|
|
||||||
|
#: authentik/sources/saml/views.py
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Continue to {source_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/scim/models.py
|
#: authentik/sources/scim/models.py
|
||||||
msgid "SCIM Source"
|
msgid "SCIM Source"
|
||||||
msgstr "SCIM Kaynak"
|
msgstr "SCIM Kaynak"
|
||||||
@ -3211,6 +3261,10 @@ msgstr "Kullanıcı Onayı"
|
|||||||
msgid "User Consents"
|
msgid "User Consents"
|
||||||
msgstr "Kullanıcı Onayları"
|
msgstr "Kullanıcı Onayları"
|
||||||
|
|
||||||
|
#: authentik/stages/consent/stage.py
|
||||||
|
msgid "Invalid consent token, re-showing prompt"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/stages/deny/models.py
|
#: authentik/stages/deny/models.py
|
||||||
msgid "Deny Stage"
|
msgid "Deny Stage"
|
||||||
msgstr "Aşama Alanını Reddet"
|
msgstr "Aşama Alanını Reddet"
|
||||||
@ -3227,6 +3281,14 @@ msgstr "Kukla Aşaması"
|
|||||||
msgid "Dummy Stages"
|
msgid "Dummy Stages"
|
||||||
msgstr "Kukla Aşamaları"
|
msgstr "Kukla Aşamaları"
|
||||||
|
|
||||||
|
#: authentik/stages/email/flow.py
|
||||||
|
msgid "Continue to confirm this email address."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/stages/email/flow.py
|
||||||
|
msgid "Link was already used, please request a new link."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/stages/email/models.py
|
#: authentik/stages/email/models.py
|
||||||
msgid "Password Reset"
|
msgid "Password Reset"
|
||||||
msgstr "Parola Sıfırlama"
|
msgstr "Parola Sıfırlama"
|
||||||
|
@ -15,7 +15,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-05-20 00:10+0000\n"
|
"POT-Creation-Date: 2025-05-28 11:25+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, 2025\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"
|
||||||
@ -975,9 +975,12 @@ msgid "Starting full provider sync"
|
|||||||
msgstr "开始全量提供程序同步"
|
msgstr "开始全量提供程序同步"
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
#, python-brace-format
|
msgid "Syncing users"
|
||||||
msgid "Syncing page {page} of users"
|
msgstr ""
|
||||||
msgstr "正在同步用户页面 {page}"
|
|
||||||
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
|
msgid "Syncing groups"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
@ -2285,6 +2288,12 @@ msgid ""
|
|||||||
"Active Directory"
|
"Active Directory"
|
||||||
msgstr "基于用户属性而非组属性查询组成员身份。这允许在 FreeIPA 或 Active Directory 等系统上支持嵌套组决策"
|
msgstr "基于用户属性而非组属性查询组成员身份。这允许在 FreeIPA 或 Active Directory 等系统上支持嵌套组决策"
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid ""
|
||||||
|
"Delete authentik users and groups which were previously supplied by this "
|
||||||
|
"source, but are now missing from it."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "LDAP Source"
|
msgid "LDAP Source"
|
||||||
msgstr "LDAP 源"
|
msgstr "LDAP 源"
|
||||||
@ -2301,6 +2310,11 @@ msgstr "LDAP 源属性映射"
|
|||||||
msgid "LDAP Source Property Mappings"
|
msgid "LDAP Source Property Mappings"
|
||||||
msgstr "LDAP 源属性映射"
|
msgstr "LDAP 源属性映射"
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid ""
|
||||||
|
"Unique ID used while checking if this object still exists in the directory."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "User LDAP Source Connection"
|
msgid "User LDAP Source Connection"
|
||||||
msgstr "用户 LDAP 源连接"
|
msgstr "用户 LDAP 源连接"
|
||||||
@ -2678,6 +2692,11 @@ msgstr "组 SAML 源连接"
|
|||||||
msgid "Group SAML Source Connections"
|
msgid "Group SAML Source Connections"
|
||||||
msgstr "组 SAML 源连接"
|
msgstr "组 SAML 源连接"
|
||||||
|
|
||||||
|
#: authentik/sources/saml/views.py
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Continue to {source_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/scim/models.py
|
#: authentik/sources/scim/models.py
|
||||||
msgid "SCIM Source"
|
msgid "SCIM Source"
|
||||||
msgstr "SCIM 源"
|
msgstr "SCIM 源"
|
||||||
@ -3044,6 +3063,10 @@ msgstr "用户同意授权"
|
|||||||
msgid "User Consents"
|
msgid "User Consents"
|
||||||
msgstr "用户同意授权"
|
msgstr "用户同意授权"
|
||||||
|
|
||||||
|
#: authentik/stages/consent/stage.py
|
||||||
|
msgid "Invalid consent token, re-showing prompt"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/stages/deny/models.py
|
#: authentik/stages/deny/models.py
|
||||||
msgid "Deny Stage"
|
msgid "Deny Stage"
|
||||||
msgstr "拒绝阶段"
|
msgstr "拒绝阶段"
|
||||||
@ -3060,6 +3083,14 @@ msgstr "虚拟阶段"
|
|||||||
msgid "Dummy Stages"
|
msgid "Dummy Stages"
|
||||||
msgstr "虚拟阶段"
|
msgstr "虚拟阶段"
|
||||||
|
|
||||||
|
#: authentik/stages/email/flow.py
|
||||||
|
msgid "Continue to confirm this email address."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/stages/email/flow.py
|
||||||
|
msgid "Link was already used, please request a new link."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/stages/email/models.py
|
#: authentik/stages/email/models.py
|
||||||
msgid "Password Reset"
|
msgid "Password Reset"
|
||||||
msgstr "密码重置"
|
msgstr "密码重置"
|
||||||
|
Binary file not shown.
@ -14,7 +14,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-05-20 00:10+0000\n"
|
"POT-Creation-Date: 2025-05-28 11:25+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, 2025\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"
|
||||||
@ -974,9 +974,12 @@ msgid "Starting full provider sync"
|
|||||||
msgstr "开始全量提供程序同步"
|
msgstr "开始全量提供程序同步"
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
#, python-brace-format
|
msgid "Syncing users"
|
||||||
msgid "Syncing page {page} of users"
|
msgstr "正在同步用户"
|
||||||
msgstr "正在同步用户页面 {page}"
|
|
||||||
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
|
msgid "Syncing groups"
|
||||||
|
msgstr "正在同步组"
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
@ -2284,6 +2287,12 @@ msgid ""
|
|||||||
"Active Directory"
|
"Active Directory"
|
||||||
msgstr "基于用户属性而非组属性查询组成员身份。这允许在 FreeIPA 或 Active Directory 等系统上支持嵌套组决策"
|
msgstr "基于用户属性而非组属性查询组成员身份。这允许在 FreeIPA 或 Active Directory 等系统上支持嵌套组决策"
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid ""
|
||||||
|
"Delete authentik users and groups which were previously supplied by this "
|
||||||
|
"source, but are now missing from it."
|
||||||
|
msgstr "删除之前由此源提供,但现已缺失的用户和组。"
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "LDAP Source"
|
msgid "LDAP Source"
|
||||||
msgstr "LDAP 源"
|
msgstr "LDAP 源"
|
||||||
@ -2300,6 +2309,11 @@ msgstr "LDAP 源属性映射"
|
|||||||
msgid "LDAP Source Property Mappings"
|
msgid "LDAP Source Property Mappings"
|
||||||
msgstr "LDAP 源属性映射"
|
msgstr "LDAP 源属性映射"
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid ""
|
||||||
|
"Unique ID used while checking if this object still exists in the directory."
|
||||||
|
msgstr "检查此对象是否仍在目录中时使用的唯一 ID。"
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "User LDAP Source Connection"
|
msgid "User LDAP Source Connection"
|
||||||
msgstr "用户 LDAP 源连接"
|
msgstr "用户 LDAP 源连接"
|
||||||
@ -2677,6 +2691,11 @@ msgstr "组 SAML 源连接"
|
|||||||
msgid "Group SAML Source Connections"
|
msgid "Group SAML Source Connections"
|
||||||
msgstr "组 SAML 源连接"
|
msgstr "组 SAML 源连接"
|
||||||
|
|
||||||
|
#: authentik/sources/saml/views.py
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Continue to {source_name}"
|
||||||
|
msgstr "继续前往 {source_name}"
|
||||||
|
|
||||||
#: authentik/sources/scim/models.py
|
#: authentik/sources/scim/models.py
|
||||||
msgid "SCIM Source"
|
msgid "SCIM Source"
|
||||||
msgstr "SCIM 源"
|
msgstr "SCIM 源"
|
||||||
@ -3043,6 +3062,10 @@ msgstr "用户同意授权"
|
|||||||
msgid "User Consents"
|
msgid "User Consents"
|
||||||
msgstr "用户同意授权"
|
msgstr "用户同意授权"
|
||||||
|
|
||||||
|
#: authentik/stages/consent/stage.py
|
||||||
|
msgid "Invalid consent token, re-showing prompt"
|
||||||
|
msgstr "无效的同意令牌,将重新显示输入"
|
||||||
|
|
||||||
#: authentik/stages/deny/models.py
|
#: authentik/stages/deny/models.py
|
||||||
msgid "Deny Stage"
|
msgid "Deny Stage"
|
||||||
msgstr "拒绝阶段"
|
msgstr "拒绝阶段"
|
||||||
@ -3059,6 +3082,14 @@ msgstr "虚拟阶段"
|
|||||||
msgid "Dummy Stages"
|
msgid "Dummy Stages"
|
||||||
msgstr "虚拟阶段"
|
msgstr "虚拟阶段"
|
||||||
|
|
||||||
|
#: authentik/stages/email/flow.py
|
||||||
|
msgid "Continue to confirm this email address."
|
||||||
|
msgstr "继续以确认电子邮件地址。"
|
||||||
|
|
||||||
|
#: authentik/stages/email/flow.py
|
||||||
|
msgid "Link was already used, please request a new link."
|
||||||
|
msgstr "链接已被使用,请申请一个新链接。"
|
||||||
|
|
||||||
#: authentik/stages/email/models.py
|
#: authentik/stages/email/models.py
|
||||||
msgid "Password Reset"
|
msgid "Password Reset"
|
||||||
msgstr "密码重置"
|
msgstr "密码重置"
|
||||||
|
@ -14,7 +14,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
|
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
|
||||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||||
"Last-Translator: 刘松, 2025\n"
|
"Last-Translator: 刘松, 2025\n"
|
||||||
"Language-Team: Chinese (Taiwan) (https://app.transifex.com/authentik/teams/119923/zh_TW/)\n"
|
"Language-Team: Chinese (Taiwan) (https://app.transifex.com/authentik/teams/119923/zh_TW/)\n"
|
||||||
@ -101,6 +101,10 @@ msgstr ""
|
|||||||
msgid "Web Certificate used by the authentik Core webserver."
|
msgid "Web Certificate used by the authentik Core webserver."
|
||||||
msgstr "用於 authentik Core 網頁伺服器的網頁憑證。"
|
msgstr "用於 authentik Core 網頁伺服器的網頁憑證。"
|
||||||
|
|
||||||
|
#: authentik/brands/models.py
|
||||||
|
msgid "Certificates used for client authentication."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/brands/models.py
|
#: authentik/brands/models.py
|
||||||
msgid "Brand"
|
msgid "Brand"
|
||||||
msgstr "品牌"
|
msgstr "品牌"
|
||||||
@ -625,6 +629,33 @@ msgstr ""
|
|||||||
msgid "Verifying your browser..."
|
msgid "Verifying your browser..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid ""
|
||||||
|
"Configure certificate authorities to validate the certificate against. This "
|
||||||
|
"option has a higher priority than the `client_certificate` option on "
|
||||||
|
"`Brand`."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Mutual TLS Stage"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Mutual TLS Stages"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/models.py
|
||||||
|
msgid "Permissions to pass Certificates for outposts."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/stage.py
|
||||||
|
msgid "Certificate required but no certificate was given."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/enterprise/stages/mtls/stage.py
|
||||||
|
msgid "No user found for certificate."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/enterprise/stages/source/models.py
|
#: authentik/enterprise/stages/source/models.py
|
||||||
msgid ""
|
msgid ""
|
||||||
"Amount of time a user can take to return from the source to continue the "
|
"Amount of time a user can take to return from the source to continue the "
|
||||||
@ -943,8 +974,11 @@ msgid "Starting full provider sync"
|
|||||||
msgstr "開始同步所有提供程式"
|
msgstr "開始同步所有提供程式"
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
#, python-brace-format
|
msgid "Syncing users"
|
||||||
msgid "Syncing page {page} of users"
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
|
msgid "Syncing groups"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
@ -2249,6 +2283,12 @@ msgid ""
|
|||||||
"Active Directory"
|
"Active Directory"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid ""
|
||||||
|
"Delete authentik users and groups which were previously supplied by this "
|
||||||
|
"source, but are now missing from it."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "LDAP Source"
|
msgid "LDAP Source"
|
||||||
msgstr "LDAP 來源"
|
msgstr "LDAP 來源"
|
||||||
@ -2265,6 +2305,11 @@ msgstr ""
|
|||||||
msgid "LDAP Source Property Mappings"
|
msgid "LDAP Source Property Mappings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/sources/ldap/models.py
|
||||||
|
msgid ""
|
||||||
|
"Unique ID used while checking if this object still exists in the directory."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/ldap/models.py
|
#: authentik/sources/ldap/models.py
|
||||||
msgid "User LDAP Source Connection"
|
msgid "User LDAP Source Connection"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -2642,6 +2687,11 @@ msgstr ""
|
|||||||
msgid "Group SAML Source Connections"
|
msgid "Group SAML Source Connections"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/sources/saml/views.py
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Continue to {source_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/scim/models.py
|
#: authentik/sources/scim/models.py
|
||||||
msgid "SCIM Source"
|
msgid "SCIM Source"
|
||||||
msgstr "SCIM 來源"
|
msgstr "SCIM 來源"
|
||||||
@ -2998,6 +3048,10 @@ msgstr "使用者同意"
|
|||||||
msgid "User Consents"
|
msgid "User Consents"
|
||||||
msgstr "使用者同意"
|
msgstr "使用者同意"
|
||||||
|
|
||||||
|
#: authentik/stages/consent/stage.py
|
||||||
|
msgid "Invalid consent token, re-showing prompt"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/stages/deny/models.py
|
#: authentik/stages/deny/models.py
|
||||||
msgid "Deny Stage"
|
msgid "Deny Stage"
|
||||||
msgstr "拒絕階段"
|
msgstr "拒絕階段"
|
||||||
@ -3014,6 +3068,14 @@ msgstr "假階段"
|
|||||||
msgid "Dummy Stages"
|
msgid "Dummy Stages"
|
||||||
msgstr "假階段"
|
msgstr "假階段"
|
||||||
|
|
||||||
|
#: authentik/stages/email/flow.py
|
||||||
|
msgid "Continue to confirm this email address."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/stages/email/flow.py
|
||||||
|
msgid "Link was already used, please request a new link."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/stages/email/models.py
|
#: authentik/stages/email/models.py
|
||||||
msgid "Password Reset"
|
msgid "Password Reset"
|
||||||
msgstr "重設密碼"
|
msgstr "重設密碼"
|
||||||
|
160
packages/eslint-config/package-lock.json
generated
160
packages/eslint-config/package-lock.json
generated
@ -274,9 +274,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/js": {
|
"node_modules/@eslint/js": {
|
||||||
"version": "9.27.0",
|
"version": "9.28.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.28.0.tgz",
|
||||||
"integrity": "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==",
|
"integrity": "sha512-fnqSjGWd/CoIp4EXIxWVK/sHA6DOHN4+8Ix2cX5ycOY7LG0UY8nHCU5pIp2eaE1Mc7Qd8kHspYNzYXT2ojPLzg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@ -576,17 +576,17 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
"version": "8.32.1",
|
"version": "8.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.33.0.tgz",
|
||||||
"integrity": "sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg==",
|
"integrity": "sha512-CACyQuqSHt7ma3Ns601xykeBK/rDeZa3w6IS6UtMQbixO5DWy+8TilKkviGDH6jtWCo8FGRKEK5cLLkPvEammQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/regexpp": "^4.10.0",
|
"@eslint-community/regexpp": "^4.10.0",
|
||||||
"@typescript-eslint/scope-manager": "8.32.1",
|
"@typescript-eslint/scope-manager": "8.33.0",
|
||||||
"@typescript-eslint/type-utils": "8.32.1",
|
"@typescript-eslint/type-utils": "8.33.0",
|
||||||
"@typescript-eslint/utils": "8.32.1",
|
"@typescript-eslint/utils": "8.33.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.32.1",
|
"@typescript-eslint/visitor-keys": "8.33.0",
|
||||||
"graphemer": "^1.4.0",
|
"graphemer": "^1.4.0",
|
||||||
"ignore": "^7.0.0",
|
"ignore": "^7.0.0",
|
||||||
"natural-compare": "^1.4.0",
|
"natural-compare": "^1.4.0",
|
||||||
@ -600,15 +600,15 @@
|
|||||||
"url": "https://opencollective.com/typescript-eslint"
|
"url": "https://opencollective.com/typescript-eslint"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
|
"@typescript-eslint/parser": "^8.33.0",
|
||||||
"eslint": "^8.57.0 || ^9.0.0",
|
"eslint": "^8.57.0 || ^9.0.0",
|
||||||
"typescript": ">=4.8.4 <5.9.0"
|
"typescript": ">=4.8.4 <5.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
|
"node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
|
||||||
"version": "7.0.4",
|
"version": "7.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
|
||||||
"integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==",
|
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -616,16 +616,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/parser": {
|
"node_modules/@typescript-eslint/parser": {
|
||||||
"version": "8.32.1",
|
"version": "8.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.33.0.tgz",
|
||||||
"integrity": "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==",
|
"integrity": "sha512-JaehZvf6m0yqYp34+RVnihBAChkqeH+tqqhS0GuX1qgPpwLvmTPheKEs6OeCK6hVJgXZHJ2vbjnC9j119auStQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.32.1",
|
"@typescript-eslint/scope-manager": "8.33.0",
|
||||||
"@typescript-eslint/types": "8.32.1",
|
"@typescript-eslint/types": "8.33.0",
|
||||||
"@typescript-eslint/typescript-estree": "8.32.1",
|
"@typescript-eslint/typescript-estree": "8.33.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.32.1",
|
"@typescript-eslint/visitor-keys": "8.33.0",
|
||||||
"debug": "^4.3.4"
|
"debug": "^4.3.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -640,15 +640,16 @@
|
|||||||
"typescript": ">=4.8.4 <5.9.0"
|
"typescript": ">=4.8.4 <5.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/scope-manager": {
|
"node_modules/@typescript-eslint/project-service": {
|
||||||
"version": "8.32.1",
|
"version": "8.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.32.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.33.0.tgz",
|
||||||
"integrity": "sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==",
|
"integrity": "sha512-d1hz0u9l6N+u/gcrk6s6gYdl7/+pp8yHheRTqP6X5hVDKALEaTn8WfGiit7G511yueBEL3OpOEpD+3/MBdoN+A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.32.1",
|
"@typescript-eslint/tsconfig-utils": "^8.33.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.32.1"
|
"@typescript-eslint/types": "^8.33.0",
|
||||||
|
"debug": "^4.3.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@ -658,15 +659,50 @@
|
|||||||
"url": "https://opencollective.com/typescript-eslint"
|
"url": "https://opencollective.com/typescript-eslint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/type-utils": {
|
"node_modules/@typescript-eslint/scope-manager": {
|
||||||
"version": "8.32.1",
|
"version": "8.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.32.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.33.0.tgz",
|
||||||
"integrity": "sha512-mv9YpQGA8iIsl5KyUPi+FGLm7+bA4fgXaeRcFKRDRwDMu4iwrSHeDPipwueNXhdIIZltwCJv+NkxftECbIZWfA==",
|
"integrity": "sha512-LMi/oqrzpqxyO72ltP+dBSP6V0xiUb4saY7WLtxSfiNEBI8m321LLVFU9/QDJxjDQG9/tjSqKz/E3380TEqSTw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/typescript-estree": "8.32.1",
|
"@typescript-eslint/types": "8.33.0",
|
||||||
"@typescript-eslint/utils": "8.32.1",
|
"@typescript-eslint/visitor-keys": "8.33.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/typescript-eslint"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||||
|
"version": "8.33.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.33.0.tgz",
|
||||||
|
"integrity": "sha512-sTkETlbqhEoiFmGr1gsdq5HyVbSOF0145SYDJ/EQmXHtKViCaGvnyLqWFFHtEXoS0J1yU8Wyou2UGmgW88fEug==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/typescript-eslint"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": ">=4.8.4 <5.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript-eslint/type-utils": {
|
||||||
|
"version": "8.33.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.33.0.tgz",
|
||||||
|
"integrity": "sha512-lScnHNCBqL1QayuSrWeqAL5GmqNdVUQAAMTaCwdYEdWfIrSrOGzyLGRCHXcCixa5NK6i5l0AfSO2oBSjCjf4XQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@typescript-eslint/typescript-estree": "8.33.0",
|
||||||
|
"@typescript-eslint/utils": "8.33.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"ts-api-utils": "^2.1.0"
|
"ts-api-utils": "^2.1.0"
|
||||||
},
|
},
|
||||||
@ -683,9 +719,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/types": {
|
"node_modules/@typescript-eslint/types": {
|
||||||
"version": "8.32.1",
|
"version": "8.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.32.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.33.0.tgz",
|
||||||
"integrity": "sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==",
|
"integrity": "sha512-DKuXOKpM5IDT1FA2g9x9x1Ug81YuKrzf4mYX8FAVSNu5Wo/LELHWQyM1pQaDkI42bX15PWl0vNPt1uGiIFUOpg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -697,14 +733,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/typescript-estree": {
|
"node_modules/@typescript-eslint/typescript-estree": {
|
||||||
"version": "8.32.1",
|
"version": "8.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.33.0.tgz",
|
||||||
"integrity": "sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==",
|
"integrity": "sha512-vegY4FQoB6jL97Tu/lWRsAiUUp8qJTqzAmENH2k59SJhw0Th1oszb9Idq/FyyONLuNqT1OADJPXfyUNOR8SzAQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.32.1",
|
"@typescript-eslint/project-service": "8.33.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.32.1",
|
"@typescript-eslint/tsconfig-utils": "8.33.0",
|
||||||
|
"@typescript-eslint/types": "8.33.0",
|
||||||
|
"@typescript-eslint/visitor-keys": "8.33.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"fast-glob": "^3.3.2",
|
"fast-glob": "^3.3.2",
|
||||||
"is-glob": "^4.0.3",
|
"is-glob": "^4.0.3",
|
||||||
@ -763,16 +801,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/utils": {
|
"node_modules/@typescript-eslint/utils": {
|
||||||
"version": "8.32.1",
|
"version": "8.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.33.0.tgz",
|
||||||
"integrity": "sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==",
|
"integrity": "sha512-lPFuQaLA9aSNa7D5u2EpRiqdAUhzShwGg/nhpBlc4GR6kcTABttCuyjFs8BcEZ8VWrjCBof/bePhP3Q3fS+Yrw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.7.0",
|
"@eslint-community/eslint-utils": "^4.7.0",
|
||||||
"@typescript-eslint/scope-manager": "8.32.1",
|
"@typescript-eslint/scope-manager": "8.33.0",
|
||||||
"@typescript-eslint/types": "8.32.1",
|
"@typescript-eslint/types": "8.33.0",
|
||||||
"@typescript-eslint/typescript-estree": "8.32.1"
|
"@typescript-eslint/typescript-estree": "8.33.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@ -787,13 +825,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/visitor-keys": {
|
"node_modules/@typescript-eslint/visitor-keys": {
|
||||||
"version": "8.32.1",
|
"version": "8.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.33.0.tgz",
|
||||||
"integrity": "sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==",
|
"integrity": "sha512-7RW7CMYoskiz5OOGAWjJFxgb7c5UNjTG292gYhWeOAcFmYCtVCSqjqSBj5zMhxbXo2JOW95YYrUWJfU0zrpaGQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.32.1",
|
"@typescript-eslint/types": "8.33.0",
|
||||||
"eslint-visitor-keys": "^4.2.0"
|
"eslint-visitor-keys": "^4.2.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -1513,9 +1551,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint": {
|
"node_modules/eslint": {
|
||||||
"version": "9.27.0",
|
"version": "9.28.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.28.0.tgz",
|
||||||
"integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==",
|
"integrity": "sha512-ocgh41VhRlf9+fVpe7QKzwLj9c92fDiqOj8Y3Sd4/ZmVA4Btx4PlUYPq4pp9JDyupkf1upbEXecxL2mwNV7jPQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.2.0",
|
"@eslint-community/eslint-utils": "^4.2.0",
|
||||||
@ -1524,7 +1562,7 @@
|
|||||||
"@eslint/config-helpers": "^0.2.1",
|
"@eslint/config-helpers": "^0.2.1",
|
||||||
"@eslint/core": "^0.14.0",
|
"@eslint/core": "^0.14.0",
|
||||||
"@eslint/eslintrc": "^3.3.1",
|
"@eslint/eslintrc": "^3.3.1",
|
||||||
"@eslint/js": "9.27.0",
|
"@eslint/js": "9.28.0",
|
||||||
"@eslint/plugin-kit": "^0.3.1",
|
"@eslint/plugin-kit": "^0.3.1",
|
||||||
"@humanfs/node": "^0.16.6",
|
"@humanfs/node": "^0.16.6",
|
||||||
"@humanwhocodes/module-importer": "^1.0.1",
|
"@humanwhocodes/module-importer": "^1.0.1",
|
||||||
@ -3994,15 +4032,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript-eslint": {
|
"node_modules/typescript-eslint": {
|
||||||
"version": "8.32.1",
|
"version": "8.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.32.1.tgz",
|
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.33.0.tgz",
|
||||||
"integrity": "sha512-D7el+eaDHAmXvrZBy1zpzSNIRqnCOrkwTgZxTu3MUqRWk8k0q9m9Ho4+vPf7iHtgUfrK/o8IZaEApsxPlHTFCg==",
|
"integrity": "sha512-5YmNhF24ylCsvdNW2oJwMzTbaeO4bg90KeGtMjUw0AGtHksgEPLRTUil+coHwCfiu4QjVJFnjp94DmU6zV7DhQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/eslint-plugin": "8.32.1",
|
"@typescript-eslint/eslint-plugin": "8.33.0",
|
||||||
"@typescript-eslint/parser": "8.32.1",
|
"@typescript-eslint/parser": "8.33.0",
|
||||||
"@typescript-eslint/utils": "8.32.1"
|
"@typescript-eslint/utils": "8.33.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
@ -6,7 +6,7 @@ authors = [{ name = "authentik Team", email = "hello@goauthentik.io" }]
|
|||||||
requires-python = "==3.13.*"
|
requires-python = "==3.13.*"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"argon2-cffi==23.1.0",
|
"argon2-cffi==23.1.0",
|
||||||
"celery==5.5.2",
|
"celery==5.5.3",
|
||||||
"channels==4.2.2",
|
"channels==4.2.2",
|
||||||
"channels-redis==4.2.1",
|
"channels-redis==4.2.1",
|
||||||
"cron-converter==1.2.1",
|
"cron-converter==1.2.1",
|
||||||
@ -46,7 +46,7 @@ dependencies = [
|
|||||||
"kubernetes==32.0.1",
|
"kubernetes==32.0.1",
|
||||||
"ldap3==2.9.1",
|
"ldap3==2.9.1",
|
||||||
"lxml==5.4.0",
|
"lxml==5.4.0",
|
||||||
"msgraph-sdk==1.30.0",
|
"msgraph-sdk==1.31.0",
|
||||||
"opencontainers==0.0.14",
|
"opencontainers==0.0.14",
|
||||||
"packaging==25.0",
|
"packaging==25.0",
|
||||||
"paramiko==3.5.1",
|
"paramiko==3.5.1",
|
||||||
@ -62,15 +62,15 @@ dependencies = [
|
|||||||
"sentry-sdk==2.29.1",
|
"sentry-sdk==2.29.1",
|
||||||
"service-identity==24.2.0",
|
"service-identity==24.2.0",
|
||||||
"setproctitle==1.3.6",
|
"setproctitle==1.3.6",
|
||||||
"structlog==25.3.0",
|
"structlog==25.4.0",
|
||||||
"swagger-spec-validator==3.0.4",
|
"swagger-spec-validator==3.0.4",
|
||||||
"tenacity==9.1.2",
|
"tenacity==9.1.2",
|
||||||
"tenant-schemas-celery==4.0.1",
|
"tenant-schemas-celery==4.0.1",
|
||||||
"twilio==9.6.1",
|
"twilio==9.6.2",
|
||||||
"ua-parser==1.0.1",
|
"ua-parser==1.0.1",
|
||||||
"unidecode==1.4.0",
|
"unidecode==1.4.0",
|
||||||
"urllib3<3",
|
"urllib3<3",
|
||||||
"uvicorn[standard]==0.34.2",
|
"uvicorn[standard]==0.34.3",
|
||||||
"watchdog==6.0.0",
|
"watchdog==6.0.0",
|
||||||
"webauthn==2.5.2",
|
"webauthn==2.5.2",
|
||||||
"wsproto==1.2.0",
|
"wsproto==1.2.0",
|
||||||
|
31
schema.yml
31
schema.yml
@ -28475,6 +28475,10 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
|
- in: query
|
||||||
|
name: delete_not_found_objects
|
||||||
|
schema:
|
||||||
|
type: boolean
|
||||||
- in: query
|
- in: query
|
||||||
name: enabled
|
name: enabled
|
||||||
schema:
|
schema:
|
||||||
@ -28579,6 +28583,10 @@ paths:
|
|||||||
name: sync_users_password
|
name: sync_users_password
|
||||||
schema:
|
schema:
|
||||||
type: boolean
|
type: boolean
|
||||||
|
- in: query
|
||||||
|
name: user_membership_attribute
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
- in: query
|
- in: query
|
||||||
name: user_object_filter
|
name: user_object_filter
|
||||||
schema:
|
schema:
|
||||||
@ -48204,6 +48212,9 @@ components:
|
|||||||
group_membership_field:
|
group_membership_field:
|
||||||
type: string
|
type: string
|
||||||
description: Field which contains members of a group.
|
description: Field which contains members of a group.
|
||||||
|
user_membership_attribute:
|
||||||
|
type: string
|
||||||
|
description: Attribute which matches the value of `group_membership_field`.
|
||||||
object_uniqueness_field:
|
object_uniqueness_field:
|
||||||
type: string
|
type: string
|
||||||
description: Field which contains a unique Identifier.
|
description: Field which contains a unique Identifier.
|
||||||
@ -48237,6 +48248,10 @@ components:
|
|||||||
description: Lookup group membership based on a user attribute instead of
|
description: Lookup group membership based on a user attribute instead of
|
||||||
a group attribute. This allows nested group resolution on systems like
|
a group attribute. This allows nested group resolution on systems like
|
||||||
FreeIPA and Active Directory
|
FreeIPA and Active Directory
|
||||||
|
delete_not_found_objects:
|
||||||
|
type: boolean
|
||||||
|
description: Delete authentik users and groups which were previously supplied
|
||||||
|
by this source, but are now missing from it.
|
||||||
required:
|
required:
|
||||||
- base_dn
|
- base_dn
|
||||||
- component
|
- component
|
||||||
@ -48413,6 +48428,10 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
minLength: 1
|
minLength: 1
|
||||||
description: Field which contains members of a group.
|
description: Field which contains members of a group.
|
||||||
|
user_membership_attribute:
|
||||||
|
type: string
|
||||||
|
minLength: 1
|
||||||
|
description: Attribute which matches the value of `group_membership_field`.
|
||||||
object_uniqueness_field:
|
object_uniqueness_field:
|
||||||
type: string
|
type: string
|
||||||
minLength: 1
|
minLength: 1
|
||||||
@ -48438,6 +48457,10 @@ components:
|
|||||||
description: Lookup group membership based on a user attribute instead of
|
description: Lookup group membership based on a user attribute instead of
|
||||||
a group attribute. This allows nested group resolution on systems like
|
a group attribute. This allows nested group resolution on systems like
|
||||||
FreeIPA and Active Directory
|
FreeIPA and Active Directory
|
||||||
|
delete_not_found_objects:
|
||||||
|
type: boolean
|
||||||
|
description: Delete authentik users and groups which were previously supplied
|
||||||
|
by this source, but are now missing from it.
|
||||||
required:
|
required:
|
||||||
- base_dn
|
- base_dn
|
||||||
- name
|
- name
|
||||||
@ -53771,6 +53794,10 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
minLength: 1
|
minLength: 1
|
||||||
description: Field which contains members of a group.
|
description: Field which contains members of a group.
|
||||||
|
user_membership_attribute:
|
||||||
|
type: string
|
||||||
|
minLength: 1
|
||||||
|
description: Attribute which matches the value of `group_membership_field`.
|
||||||
object_uniqueness_field:
|
object_uniqueness_field:
|
||||||
type: string
|
type: string
|
||||||
minLength: 1
|
minLength: 1
|
||||||
@ -53796,6 +53823,10 @@ components:
|
|||||||
description: Lookup group membership based on a user attribute instead of
|
description: Lookup group membership based on a user attribute instead of
|
||||||
a group attribute. This allows nested group resolution on systems like
|
a group attribute. This allows nested group resolution on systems like
|
||||||
FreeIPA and Active Directory
|
FreeIPA and Active Directory
|
||||||
|
delete_not_found_objects:
|
||||||
|
type: boolean
|
||||||
|
description: Delete authentik users and groups which were previously supplied
|
||||||
|
by this source, but are now missing from it.
|
||||||
PatchedLicenseRequest:
|
PatchedLicenseRequest:
|
||||||
type: object
|
type: object
|
||||||
description: License Serializer
|
description: License Serializer
|
||||||
|
7
scripts/api-ts-templates/tsconfig.esm.mustache
Normal file
7
scripts/api-ts-templates/tsconfig.esm.mustache
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/tsconfig",
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "dist/esm",
|
||||||
|
},
|
||||||
|
}
|
23
scripts/api-ts-templates/tsconfig.mustache
Normal file
23
scripts/api-ts-templates/tsconfig.mustache
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/tsconfig",
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"incremental": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"rootDir": "src",
|
||||||
|
"strict": true,
|
||||||
|
"newLine": "lf",
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"outDir": "dist",
|
||||||
|
"skipDefaultLibCheck": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||||
|
},
|
||||||
|
"exclude": ["node_modules", "./out/**/*", "./dist/**/*"],
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
services:
|
services:
|
||||||
chrome:
|
chrome:
|
||||||
|
platform: linux/x86_64
|
||||||
image: docker.io/selenium/standalone-chrome:136.0
|
image: docker.io/selenium/standalone-chrome:136.0
|
||||||
volumes:
|
volumes:
|
||||||
- /dev/shm:/dev/shm
|
- /dev/shm:/dev/shm
|
||||||
|
@ -10,6 +10,7 @@ from authentik.blueprints.tests import apply_blueprint
|
|||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
from authentik.flows.models import Flow
|
from authentik.flows.models import Flow
|
||||||
from authentik.lib.config import CONFIG
|
from authentik.lib.config import CONFIG
|
||||||
|
from authentik.lib.generators import generate_id
|
||||||
from authentik.stages.identification.models import IdentificationStage
|
from authentik.stages.identification.models import IdentificationStage
|
||||||
from tests.e2e.utils import SeleniumTestCase, retry
|
from tests.e2e.utils import SeleniumTestCase, retry
|
||||||
|
|
||||||
@ -17,6 +18,10 @@ from tests.e2e.utils import SeleniumTestCase, retry
|
|||||||
class TestFlowsEnroll(SeleniumTestCase):
|
class TestFlowsEnroll(SeleniumTestCase):
|
||||||
"""Test Enroll flow"""
|
"""Test Enroll flow"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
self.username = generate_id()
|
||||||
|
|
||||||
@retry()
|
@retry()
|
||||||
@apply_blueprint(
|
@apply_blueprint(
|
||||||
"default/flow-default-authentication-flow.yaml",
|
"default/flow-default-authentication-flow.yaml",
|
||||||
@ -39,8 +44,8 @@ class TestFlowsEnroll(SeleniumTestCase):
|
|||||||
self.initial_stages()
|
self.initial_stages()
|
||||||
sleep(2)
|
sleep(2)
|
||||||
|
|
||||||
user = User.objects.get(username="foo")
|
user = User.objects.get(username=self.username)
|
||||||
self.assertEqual(user.username, "foo")
|
self.assertEqual(user.username, self.username)
|
||||||
self.assertEqual(user.name, "some name")
|
self.assertEqual(user.name, "some name")
|
||||||
self.assertEqual(user.email, "foo@bar.baz")
|
self.assertEqual(user.email, "foo@bar.baz")
|
||||||
|
|
||||||
@ -87,7 +92,16 @@ class TestFlowsEnroll(SeleniumTestCase):
|
|||||||
|
|
||||||
sleep(2)
|
sleep(2)
|
||||||
|
|
||||||
self.assert_user(User.objects.get(username="foo"))
|
flow_executor = self.get_shadow_root("ak-flow-executor")
|
||||||
|
consent_stage = self.get_shadow_root("ak-stage-consent", flow_executor)
|
||||||
|
consent_stage.find_element(
|
||||||
|
By.CSS_SELECTOR,
|
||||||
|
"[type=submit]",
|
||||||
|
).click()
|
||||||
|
|
||||||
|
self.wait_for_url(self.if_user_url())
|
||||||
|
|
||||||
|
self.assert_user(User.objects.get(username=self.username))
|
||||||
|
|
||||||
def initial_stages(self):
|
def initial_stages(self):
|
||||||
"""Fill out initial stages"""
|
"""Fill out initial stages"""
|
||||||
@ -105,7 +119,7 @@ class TestFlowsEnroll(SeleniumTestCase):
|
|||||||
wait = WebDriverWait(prompt_stage, self.wait_timeout)
|
wait = WebDriverWait(prompt_stage, self.wait_timeout)
|
||||||
|
|
||||||
wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "input[name=username]")))
|
wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "input[name=username]")))
|
||||||
prompt_stage.find_element(By.CSS_SELECTOR, "input[name=username]").send_keys("foo")
|
prompt_stage.find_element(By.CSS_SELECTOR, "input[name=username]").send_keys(self.username)
|
||||||
prompt_stage.find_element(By.CSS_SELECTOR, "input[name=password]").send_keys(
|
prompt_stage.find_element(By.CSS_SELECTOR, "input[name=password]").send_keys(
|
||||||
self.user.username
|
self.user.username
|
||||||
)
|
)
|
||||||
@ -124,3 +138,82 @@ class TestFlowsEnroll(SeleniumTestCase):
|
|||||||
prompt_stage.find_element(By.CSS_SELECTOR, "input[name=name]").send_keys("some name")
|
prompt_stage.find_element(By.CSS_SELECTOR, "input[name=name]").send_keys("some name")
|
||||||
prompt_stage.find_element(By.CSS_SELECTOR, "input[name=email]").send_keys("foo@bar.baz")
|
prompt_stage.find_element(By.CSS_SELECTOR, "input[name=email]").send_keys("foo@bar.baz")
|
||||||
prompt_stage.find_element(By.CSS_SELECTOR, ".pf-c-button").click()
|
prompt_stage.find_element(By.CSS_SELECTOR, ".pf-c-button").click()
|
||||||
|
|
||||||
|
@retry()
|
||||||
|
@apply_blueprint(
|
||||||
|
"default/flow-default-authentication-flow.yaml",
|
||||||
|
"default/flow-default-invalidation-flow.yaml",
|
||||||
|
)
|
||||||
|
@apply_blueprint(
|
||||||
|
"example/flows-enrollment-email-verification.yaml",
|
||||||
|
)
|
||||||
|
@CONFIG.patch("email.port", 1025)
|
||||||
|
def test_enroll_email_pretend_email_scanner(self):
|
||||||
|
"""Test enroll with Email verification. Open the email link twice to pretend we have an
|
||||||
|
email scanner that clicks on links"""
|
||||||
|
# Attach enrollment flow to identification stage
|
||||||
|
ident_stage: IdentificationStage = IdentificationStage.objects.get(
|
||||||
|
name="default-authentication-identification"
|
||||||
|
)
|
||||||
|
ident_stage.enrollment_flow = Flow.objects.get(slug="default-enrollment-flow")
|
||||||
|
ident_stage.save()
|
||||||
|
|
||||||
|
self.driver.get(self.live_server_url)
|
||||||
|
self.initial_stages()
|
||||||
|
|
||||||
|
# Email stage
|
||||||
|
flow_executor = self.get_shadow_root("ak-flow-executor")
|
||||||
|
email_stage = self.get_shadow_root("ak-stage-email", flow_executor)
|
||||||
|
|
||||||
|
wait = WebDriverWait(email_stage, self.wait_timeout)
|
||||||
|
|
||||||
|
# Wait for the success message so we know the email is sent
|
||||||
|
wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, ".pf-c-form p")))
|
||||||
|
|
||||||
|
# Open Mailpit
|
||||||
|
self.driver.get("http://localhost:8025")
|
||||||
|
|
||||||
|
# Click on first message
|
||||||
|
self.wait.until(ec.presence_of_element_located((By.CLASS_NAME, "message")))
|
||||||
|
self.driver.find_element(By.CLASS_NAME, "message").click()
|
||||||
|
self.driver.switch_to.frame(self.driver.find_element(By.ID, "preview-html"))
|
||||||
|
confirmation_link = self.driver.find_element(By.ID, "confirm").get_attribute("href")
|
||||||
|
|
||||||
|
main_tab = self.driver.current_window_handle
|
||||||
|
|
||||||
|
self.driver.switch_to.new_window("tab")
|
||||||
|
confirm_tab = self.driver.current_window_handle
|
||||||
|
|
||||||
|
# On the new tab, check that we have the confirmation screen
|
||||||
|
self.driver.get(confirmation_link)
|
||||||
|
self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "ak-flow-executor")))
|
||||||
|
|
||||||
|
flow_executor = self.get_shadow_root("ak-flow-executor")
|
||||||
|
consent_stage = self.get_shadow_root("ak-stage-consent", flow_executor)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
"Continue to confirm this email address.",
|
||||||
|
consent_stage.find_element(By.CSS_SELECTOR, "#header-text").text,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Back on the main tab, confirm
|
||||||
|
self.driver.switch_to.window(main_tab)
|
||||||
|
self.driver.get(confirmation_link)
|
||||||
|
|
||||||
|
flow_executor = self.get_shadow_root("ak-flow-executor")
|
||||||
|
consent_stage = self.get_shadow_root("ak-stage-consent", flow_executor)
|
||||||
|
consent_stage.find_element(
|
||||||
|
By.CSS_SELECTOR,
|
||||||
|
"[type=submit]",
|
||||||
|
).click()
|
||||||
|
|
||||||
|
self.wait_for_url(self.if_user_url())
|
||||||
|
sleep(2)
|
||||||
|
|
||||||
|
self.assert_user(User.objects.get(username=self.username))
|
||||||
|
|
||||||
|
self.driver.switch_to.window(confirm_tab)
|
||||||
|
self.driver.refresh()
|
||||||
|
flow_executor = self.get_shadow_root("ak-flow-executor")
|
||||||
|
wait = WebDriverWait(flow_executor, self.wait_timeout)
|
||||||
|
wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "ak-stage-access-denied")))
|
||||||
|
@ -84,6 +84,14 @@ class TestFlowsRecovery(SeleniumTestCase):
|
|||||||
self.driver.switch_to.window(self.driver.window_handles[0])
|
self.driver.switch_to.window(self.driver.window_handles[0])
|
||||||
|
|
||||||
sleep(2)
|
sleep(2)
|
||||||
|
|
||||||
|
flow_executor = self.get_shadow_root("ak-flow-executor")
|
||||||
|
consent_stage = self.get_shadow_root("ak-stage-consent", flow_executor)
|
||||||
|
consent_stage.find_element(
|
||||||
|
By.CSS_SELECTOR,
|
||||||
|
"[type=submit]",
|
||||||
|
).click()
|
||||||
|
|
||||||
# We can now enter the new password
|
# We can now enter the new password
|
||||||
flow_executor = self.get_shadow_root("ak-flow-executor")
|
flow_executor = self.get_shadow_root("ak-flow-executor")
|
||||||
prompt_stage = self.get_shadow_root("ak-stage-prompt", flow_executor)
|
prompt_stage = self.get_shadow_root("ak-stage-prompt", flow_executor)
|
||||||
|
@ -166,30 +166,35 @@ class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase):
|
|||||||
print("::group::authentik Logs", file=stderr)
|
print("::group::authentik Logs", file=stderr)
|
||||||
apps.get_app_config("authentik_tenants").ready()
|
apps.get_app_config("authentik_tenants").ready()
|
||||||
self.wait_timeout = 60
|
self.wait_timeout = 60
|
||||||
|
self.logger = get_logger()
|
||||||
self.driver = self._get_driver()
|
self.driver = self._get_driver()
|
||||||
self.driver.implicitly_wait(30)
|
self.driver.implicitly_wait(30)
|
||||||
self.wait = WebDriverWait(self.driver, self.wait_timeout)
|
self.wait = WebDriverWait(self.driver, self.wait_timeout)
|
||||||
self.logger = get_logger()
|
|
||||||
self.user = create_test_admin_user()
|
self.user = create_test_admin_user()
|
||||||
super().setUp()
|
super().setUp()
|
||||||
|
|
||||||
def _get_driver(self) -> WebDriver:
|
def _get_driver(self) -> WebDriver:
|
||||||
count = 0
|
count = 0
|
||||||
try:
|
opts = webdriver.ChromeOptions()
|
||||||
opts = webdriver.ChromeOptions()
|
opts.add_argument("--disable-search-engine-choice-screen")
|
||||||
opts.add_argument("--disable-search-engine-choice-screen")
|
# This breaks selenium when running remotely...?
|
||||||
return webdriver.Chrome(options=opts)
|
# opts.set_capability("goog:loggingPrefs", {"browser": "ALL"})
|
||||||
except WebDriverException:
|
opts.add_experimental_option(
|
||||||
pass
|
"prefs",
|
||||||
|
{
|
||||||
|
"profile.password_manager_leak_detection": False,
|
||||||
|
},
|
||||||
|
)
|
||||||
while count < RETRIES:
|
while count < RETRIES:
|
||||||
try:
|
try:
|
||||||
driver = webdriver.Remote(
|
driver = webdriver.Remote(
|
||||||
command_executor="http://localhost:4444/wd/hub",
|
command_executor="http://localhost:4444/wd/hub",
|
||||||
options=webdriver.ChromeOptions(),
|
options=opts,
|
||||||
)
|
)
|
||||||
driver.maximize_window()
|
driver.maximize_window()
|
||||||
return driver
|
return driver
|
||||||
except WebDriverException:
|
except WebDriverException as exc:
|
||||||
|
self.logger.warning("Failed to setup webdriver", exc=exc)
|
||||||
count += 1
|
count += 1
|
||||||
raise ValueError(f"Webdriver failed after {RETRIES}.")
|
raise ValueError(f"Webdriver failed after {RETRIES}.")
|
||||||
|
|
||||||
|
40
uv.lock
generated
40
uv.lock
generated
@ -270,7 +270,7 @@ dev = [
|
|||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [
|
requires-dist = [
|
||||||
{ name = "argon2-cffi", specifier = "==23.1.0" },
|
{ name = "argon2-cffi", specifier = "==23.1.0" },
|
||||||
{ name = "celery", specifier = "==5.5.2" },
|
{ name = "celery", specifier = "==5.5.3" },
|
||||||
{ name = "channels", specifier = "==4.2.2" },
|
{ name = "channels", specifier = "==4.2.2" },
|
||||||
{ name = "channels-redis", specifier = "==4.2.1" },
|
{ name = "channels-redis", specifier = "==4.2.1" },
|
||||||
{ name = "cron-converter", specifier = "==1.2.1" },
|
{ name = "cron-converter", specifier = "==1.2.1" },
|
||||||
@ -310,7 +310,7 @@ requires-dist = [
|
|||||||
{ name = "kubernetes", specifier = "==32.0.1" },
|
{ name = "kubernetes", specifier = "==32.0.1" },
|
||||||
{ name = "ldap3", specifier = "==2.9.1" },
|
{ name = "ldap3", specifier = "==2.9.1" },
|
||||||
{ name = "lxml", specifier = "==5.4.0" },
|
{ name = "lxml", specifier = "==5.4.0" },
|
||||||
{ name = "msgraph-sdk", specifier = "==1.30.0" },
|
{ name = "msgraph-sdk", specifier = "==1.31.0" },
|
||||||
{ name = "opencontainers", git = "https://github.com/vsoch/oci-python?rev=ceb4fcc090851717a3069d78e85ceb1e86c2740c" },
|
{ name = "opencontainers", git = "https://github.com/vsoch/oci-python?rev=ceb4fcc090851717a3069d78e85ceb1e86c2740c" },
|
||||||
{ name = "packaging", specifier = "==25.0" },
|
{ name = "packaging", specifier = "==25.0" },
|
||||||
{ name = "paramiko", specifier = "==3.5.1" },
|
{ name = "paramiko", specifier = "==3.5.1" },
|
||||||
@ -326,15 +326,15 @@ requires-dist = [
|
|||||||
{ name = "sentry-sdk", specifier = "==2.29.1" },
|
{ name = "sentry-sdk", specifier = "==2.29.1" },
|
||||||
{ name = "service-identity", specifier = "==24.2.0" },
|
{ name = "service-identity", specifier = "==24.2.0" },
|
||||||
{ name = "setproctitle", specifier = "==1.3.6" },
|
{ name = "setproctitle", specifier = "==1.3.6" },
|
||||||
{ name = "structlog", specifier = "==25.3.0" },
|
{ name = "structlog", specifier = "==25.4.0" },
|
||||||
{ name = "swagger-spec-validator", specifier = "==3.0.4" },
|
{ name = "swagger-spec-validator", specifier = "==3.0.4" },
|
||||||
{ name = "tenacity", specifier = "==9.1.2" },
|
{ name = "tenacity", specifier = "==9.1.2" },
|
||||||
{ name = "tenant-schemas-celery", specifier = "==4.0.1" },
|
{ name = "tenant-schemas-celery", specifier = "==4.0.1" },
|
||||||
{ name = "twilio", specifier = "==9.6.1" },
|
{ name = "twilio", specifier = "==9.6.2" },
|
||||||
{ name = "ua-parser", specifier = "==1.0.1" },
|
{ name = "ua-parser", specifier = "==1.0.1" },
|
||||||
{ name = "unidecode", specifier = "==1.4.0" },
|
{ name = "unidecode", specifier = "==1.4.0" },
|
||||||
{ name = "urllib3", specifier = "<3" },
|
{ name = "urllib3", specifier = "<3" },
|
||||||
{ name = "uvicorn", extras = ["standard"], specifier = "==0.34.2" },
|
{ name = "uvicorn", extras = ["standard"], specifier = "==0.34.3" },
|
||||||
{ name = "watchdog", specifier = "==6.0.0" },
|
{ name = "watchdog", specifier = "==6.0.0" },
|
||||||
{ name = "webauthn", specifier = "==2.5.2" },
|
{ name = "webauthn", specifier = "==2.5.2" },
|
||||||
{ name = "wsproto", specifier = "==1.2.0" },
|
{ name = "wsproto", specifier = "==1.2.0" },
|
||||||
@ -653,7 +653,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "celery"
|
name = "celery"
|
||||||
version = "5.5.2"
|
version = "5.5.3"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "billiard" },
|
{ name = "billiard" },
|
||||||
@ -665,9 +665,9 @@ dependencies = [
|
|||||||
{ name = "python-dateutil" },
|
{ name = "python-dateutil" },
|
||||||
{ name = "vine" },
|
{ name = "vine" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/bf/03/5d9c6c449248958f1a5870e633a29d7419ff3724c452a98ffd22688a1a6a/celery-5.5.2.tar.gz", hash = "sha256:4d6930f354f9d29295425d7a37261245c74a32807c45d764bedc286afd0e724e", size = 1666892, upload-time = "2025-04-25T20:10:04.695Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/bb/7d/6c289f407d219ba36d8b384b42489ebdd0c84ce9c413875a8aae0c85f35b/celery-5.5.3.tar.gz", hash = "sha256:6c972ae7968c2b5281227f01c3a3f984037d21c5129d07bf3550cc2afc6b10a5", size = 1667144, upload-time = "2025-06-01T11:08:12.563Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/04/94/8e825ac1cf59d45d20c4345d4461e6b5263ae475f708d047c3dad0ac6401/celery-5.5.2-py3-none-any.whl", hash = "sha256:54425a067afdc88b57cd8d94ed4af2ffaf13ab8c7680041ac2c4ac44357bdf4c", size = 438626, upload-time = "2025-04-25T20:10:01.383Z" },
|
{ url = "https://files.pythonhosted.org/packages/c9/af/0dcccc7fdcdf170f9a1585e5e96b6fb0ba1749ef6be8c89a6202284759bd/celery-5.5.3-py3-none-any.whl", hash = "sha256:0b5761a07057acee94694464ca482416b959568904c9dfa41ce8413a7d65d525", size = 438775, upload-time = "2025-06-01T11:08:09.94Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2162,7 +2162,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "msgraph-sdk"
|
name = "msgraph-sdk"
|
||||||
version = "1.30.0"
|
version = "1.31.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "azure-identity" },
|
{ name = "azure-identity" },
|
||||||
@ -2172,9 +2172,9 @@ dependencies = [
|
|||||||
{ name = "microsoft-kiota-serialization-text" },
|
{ name = "microsoft-kiota-serialization-text" },
|
||||||
{ name = "msgraph-core" },
|
{ name = "msgraph-core" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/e9/4a/4ff19671f6ea06f98fb2405f73a90350e4719ccc692e85e9e0c2fa066826/msgraph_sdk-1.30.0.tar.gz", hash = "sha256:59e30af6d7244c9009146d620c331e169701b651317746b16f561e2e2452e73f", size = 6608744, upload-time = "2025-05-13T13:09:12.594Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/d3/1c/5afdf21f92840c7029f0fdb6c2ead7373b1fcdc3c4279fe556a2fc3702a2/msgraph_sdk-1.31.0.tar.gz", hash = "sha256:7ae5f29152251f61c1fc19cca6389dd03b0120b179ddf39d8ab8cdfed7952dba", size = 6626610, upload-time = "2025-05-20T13:15:08.062Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/70/95/451ec4db8a924274a1f7260809ea03fe9c2b446d84dc5238e92e49a1b522/msgraph_sdk-1.30.0-py3-none-any.whl", hash = "sha256:6748f5cdb5ddbcff9e4f3fb073dd0a604cb00e1cf285dd0fea6969c93ba8282f", size = 27140767, upload-time = "2025-05-13T13:09:07.718Z" },
|
{ url = "https://files.pythonhosted.org/packages/d9/b9/099b28478575126ec26bd61ff0931fb291263ac813afb8baf4b4cc30c6fc/msgraph_sdk-1.31.0-py3-none-any.whl", hash = "sha256:bb2edfe17c377f37bbf2e155fc915171763d49e1cf93b665bafd721a85220dc5", size = 27185846, upload-time = "2025-05-20T13:15:05.307Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3172,11 +3172,11 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "structlog"
|
name = "structlog"
|
||||||
version = "25.3.0"
|
version = "25.4.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/ff/6a/b0b6d440e429d2267076c4819300d9929563b1da959cf1f68afbcd69fe45/structlog-25.3.0.tar.gz", hash = "sha256:8dab497e6f6ca962abad0c283c46744185e0c9ba900db52a423cb6db99f7abeb", size = 1367514, upload-time = "2025-04-25T16:00:39.167Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/79/b9/6e672db4fec07349e7a8a8172c1a6ae235c58679ca29c3f86a61b5e59ff3/structlog-25.4.0.tar.gz", hash = "sha256:186cd1b0a8ae762e29417095664adf1d6a31702160a46dacb7796ea82f7409e4", size = 1369138, upload-time = "2025-06-02T08:21:12.971Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/f5/52/7a2c7a317b254af857464da3d60a0d3730c44f912f8c510c76a738a207fd/structlog-25.3.0-py3-none-any.whl", hash = "sha256:a341f5524004c158498c3127eecded091eb67d3a611e7a3093deca30db06e172", size = 68240, upload-time = "2025-04-25T16:00:37.295Z" },
|
{ url = "https://files.pythonhosted.org/packages/a0/4a/97ee6973e3a73c74c8120d59829c3861ea52210667ec3e7a16045c62b64d/structlog-25.4.0-py3-none-any.whl", hash = "sha256:fe809ff5c27e557d14e613f45ca441aabda051d119ee5a0102aaba6ce40eed2c", size = 68720, upload-time = "2025-06-02T08:21:11.43Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3266,7 +3266,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "twilio"
|
name = "twilio"
|
||||||
version = "9.6.1"
|
version = "9.6.2"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "aiohttp" },
|
{ name = "aiohttp" },
|
||||||
@ -3274,9 +3274,9 @@ dependencies = [
|
|||||||
{ name = "pyjwt" },
|
{ name = "pyjwt" },
|
||||||
{ name = "requests" },
|
{ name = "requests" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/95/78/453ff0d35442c53490c22d077f580684a2352846c721d3e01f4c6dfa85bd/twilio-9.6.1.tar.gz", hash = "sha256:bb80b31d4d9e55c33872efef7fb99373149ed4093f21c56cf582797da45862f5", size = 987002, upload-time = "2025-05-13T09:56:55.183Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/fa/c9/441a07f6552f2b504812501d56c41bd85b02afeef6c23ab8baf41ed6c70e/twilio-9.6.2.tar.gz", hash = "sha256:5da13bb497e39ece34cb9f2b3bc911f3288928612748f7688b3bda262c2767a1", size = 1041300, upload-time = "2025-05-29T12:25:04.59Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/02/f4/36fe2566a3ad7f71a89fd28ea2ebb6b2aa05c3a4d5a55b3ca6c358768c6b/twilio-9.6.1-py2.py3-none-any.whl", hash = "sha256:441fdab61b9a204eef770368380b962cbf08dc0fe9f757fe4b1d63ced37ddeed", size = 1859407, upload-time = "2025-05-13T09:56:53.094Z" },
|
{ url = "https://files.pythonhosted.org/packages/67/91/382e83e5d205a7ae4325b66d40cd2fa6ce85526f2ed8fc553265e19abbe4/twilio-9.6.2-py2.py3-none-any.whl", hash = "sha256:8d4af6f42850734a921857df42940f7fed84e3e4a508d0d6bef5b9fb7dc08357", size = 1909253, upload-time = "2025-05-29T12:25:02.521Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3406,15 +3406,15 @@ socks = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uvicorn"
|
name = "uvicorn"
|
||||||
version = "0.34.2"
|
version = "0.34.3"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "click" },
|
{ name = "click" },
|
||||||
{ name = "h11" },
|
{ name = "h11" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/a6/ae/9bbb19b9e1c450cf9ecaef06463e40234d98d95bf572fab11b4f19ae5ded/uvicorn-0.34.2.tar.gz", hash = "sha256:0e929828f6186353a80b58ea719861d2629d766293b6d19baf086ba31d4f3328", size = 76815, upload-time = "2025-04-19T06:02:50.101Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/de/ad/713be230bcda622eaa35c28f0d328c3675c371238470abdea52417f17a8e/uvicorn-0.34.3.tar.gz", hash = "sha256:35919a9a979d7a59334b6b10e05d77c1d0d574c50e0fc98b8b1a0f165708b55a", size = 76631, upload-time = "2025-06-01T07:48:17.531Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/b1/4b/4cef6ce21a2aaca9d852a6e84ef4f135d99fcd74fa75105e2fc0c8308acd/uvicorn-0.34.2-py3-none-any.whl", hash = "sha256:deb49af569084536d269fe0a6d67e3754f104cf03aba7c11c40f01aadf33c403", size = 62483, upload-time = "2025-04-19T06:02:48.42Z" },
|
{ url = "https://files.pythonhosted.org/packages/6d/0d/8adfeaa62945f90d19ddc461c55f4a50c258af7662d34b6a3d5d1f8646f6/uvicorn-0.34.3-py3-none-any.whl", hash = "sha256:16246631db62bdfbf069b0645177d6e8a77ba950cfedbfd093acef9444e4d885", size = 62431, upload-time = "2025-06-01T07:48:15.664Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.optional-dependencies]
|
[package.optional-dependencies]
|
||||||
|
1826
web/package-lock.json
generated
1826
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -93,7 +93,7 @@
|
|||||||
"@floating-ui/dom": "^1.6.11",
|
"@floating-ui/dom": "^1.6.11",
|
||||||
"@formatjs/intl-listformat": "^7.7.11",
|
"@formatjs/intl-listformat": "^7.7.11",
|
||||||
"@fortawesome/fontawesome-free": "^6.6.0",
|
"@fortawesome/fontawesome-free": "^6.6.0",
|
||||||
"@goauthentik/api": "^2025.4.1-1747687715",
|
"@goauthentik/api": "^2025.4.1-1748622869",
|
||||||
"@lit/context": "^1.1.2",
|
"@lit/context": "^1.1.2",
|
||||||
"@lit/localize": "^0.12.2",
|
"@lit/localize": "^0.12.2",
|
||||||
"@lit/reactive-element": "^2.0.4",
|
"@lit/reactive-element": "^2.0.4",
|
||||||
@ -102,7 +102,7 @@
|
|||||||
"@open-wc/lit-helpers": "^0.7.0",
|
"@open-wc/lit-helpers": "^0.7.0",
|
||||||
"@patternfly/elements": "^4.1.0",
|
"@patternfly/elements": "^4.1.0",
|
||||||
"@patternfly/patternfly": "^4.224.2",
|
"@patternfly/patternfly": "^4.224.2",
|
||||||
"@sentry/browser": "^9.22.0",
|
"@sentry/browser": "^9.24.0",
|
||||||
"@spotlightjs/spotlight": "^2.13.3",
|
"@spotlightjs/spotlight": "^2.13.3",
|
||||||
"@webcomponents/webcomponentsjs": "^2.8.0",
|
"@webcomponents/webcomponentsjs": "^2.8.0",
|
||||||
"base64-js": "^1.5.1",
|
"base64-js": "^1.5.1",
|
||||||
@ -111,7 +111,7 @@
|
|||||||
"chartjs-adapter-date-fns": "^3.0.0",
|
"chartjs-adapter-date-fns": "^3.0.0",
|
||||||
"codemirror": "^6.0.1",
|
"codemirror": "^6.0.1",
|
||||||
"construct-style-sheets-polyfill": "^3.1.0",
|
"construct-style-sheets-polyfill": "^3.1.0",
|
||||||
"core-js": "^3.38.1",
|
"core-js": "^3.42.0",
|
||||||
"country-flag-icons": "^1.5.19",
|
"country-flag-icons": "^1.5.19",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"deepmerge-ts": "^7.1.5",
|
"deepmerge-ts": "^7.1.5",
|
||||||
@ -152,6 +152,7 @@
|
|||||||
"@storybook/addon-essentials": "^8.6.14",
|
"@storybook/addon-essentials": "^8.6.14",
|
||||||
"@storybook/addon-links": "^8.6.14",
|
"@storybook/addon-links": "^8.6.14",
|
||||||
"@storybook/blocks": "^8.6.12",
|
"@storybook/blocks": "^8.6.12",
|
||||||
|
"@storybook/channels": "^8.6.14",
|
||||||
"@storybook/experimental-addon-test": "^8.6.14",
|
"@storybook/experimental-addon-test": "^8.6.14",
|
||||||
"@storybook/manager-api": "^8.6.14",
|
"@storybook/manager-api": "^8.6.14",
|
||||||
"@storybook/test": "^8.6.14",
|
"@storybook/test": "^8.6.14",
|
||||||
@ -174,11 +175,11 @@
|
|||||||
"@wdio/spec-reporter": "^9.1.2",
|
"@wdio/spec-reporter": "^9.1.2",
|
||||||
"@web/test-runner": "^0.20.2",
|
"@web/test-runner": "^0.20.2",
|
||||||
"chromedriver": "^136.0.3",
|
"chromedriver": "^136.0.3",
|
||||||
"esbuild": "^0.25.4",
|
"esbuild": "^0.25.5",
|
||||||
"esbuild-plugin-copy": "^2.1.1",
|
"esbuild-plugin-copy": "^2.1.1",
|
||||||
"esbuild-plugin-polyfill-node": "^0.3.0",
|
"esbuild-plugin-polyfill-node": "^0.3.0",
|
||||||
"esbuild-plugins-node-modules-polyfill": "^1.7.0",
|
"esbuild-plugins-node-modules-polyfill": "^1.7.0",
|
||||||
"eslint": "^9.11.1",
|
"eslint": "^9.28.0",
|
||||||
"eslint-plugin-lit": "^2.1.1",
|
"eslint-plugin-lit": "^2.1.1",
|
||||||
"eslint-plugin-wc": "^3.0.1",
|
"eslint-plugin-wc": "^3.0.1",
|
||||||
"github-slugger": "^2.0.0",
|
"github-slugger": "^2.0.0",
|
||||||
@ -193,7 +194,7 @@
|
|||||||
"storybook-addon-mock": "^5.0.0",
|
"storybook-addon-mock": "^5.0.0",
|
||||||
"turnstile-types": "^1.2.3",
|
"turnstile-types": "^1.2.3",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"typescript-eslint": "^8.32.1",
|
"typescript-eslint": "^8.33.0",
|
||||||
"vite-plugin-lit-css": "^2.0.0",
|
"vite-plugin-lit-css": "^2.0.0",
|
||||||
"vite-tsconfig-paths": "^5.0.1",
|
"vite-tsconfig-paths": "^5.0.1",
|
||||||
"wireit": "^0.14.12"
|
"wireit": "^0.14.12"
|
||||||
|
59
web/packages/esbuild-plugin-live-reload/.github/README.md
vendored
Normal file
59
web/packages/esbuild-plugin-live-reload/.github/README.md
vendored
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
_An ESBuild development plugin that watches for file changes and triggers automatic browser refreshes._
|
||||||
|
|
||||||
|
## Quick start
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install -D @goauthentik/esbuild-plugin-live-reload
|
||||||
|
# Or with Yarn:
|
||||||
|
yarn add -D @goauthentik/esbuild-plugin-live-reload
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1. Configure ESBuild
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { liveReloadPlugin } from "@goauthentik/esbuild-plugin-live-reload";
|
||||||
|
import esbuild from "esbuild";
|
||||||
|
|
||||||
|
const NodeEnvironment = process.env.NODE_ENV || "development";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {esbuild.BuildOptions}
|
||||||
|
*/
|
||||||
|
const buildOptions = {
|
||||||
|
// ... Your build options.
|
||||||
|
define: {
|
||||||
|
"process.env.NODE_ENV": JSON.stringify(NodeEnvironment),
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
/** @see {@link LiveReloadPluginOptions} */
|
||||||
|
liveReloadPlugin(),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildContext = await esbuild.context(buildOptions);
|
||||||
|
|
||||||
|
await buildContext.rebuild();
|
||||||
|
await buildContext.watch();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Connect your browser
|
||||||
|
|
||||||
|
Add the following import near the beginning of your application's entry point.
|
||||||
|
|
||||||
|
```js
|
||||||
|
if (process.env.NODE_ENV === "development") {
|
||||||
|
await import("@goauthentik/esbuild-plugin-live-reload/client");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
That's it! Your browser will now automatically refresh whenever ESBuild finishes rebuilding your code.
|
||||||
|
|
||||||
|
## About authentik
|
||||||
|
|
||||||
|
[authentik](https://goauthentik.io) is an open source Identity Provider that unifies your identity needs into a single platform, replacing Okta, Active Directory, and Auth0.
|
||||||
|
|
||||||
|
We built this plugin to streamline our development workflow, and we're sharing it with the community. If you have any questions, feature requests, or bug reports, please [open an issue](https://github.com/goauthentik/authentik/issues/new/choose).
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This code is licensed under the [MIT License](https://www.tldrlegal.com/license/mit-license)
|
3
web/packages/esbuild-plugin-live-reload/.gitignore
vendored
Normal file
3
web/packages/esbuild-plugin-live-reload/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
README.md
|
||||||
|
node_modules
|
||||||
|
_media
|
3
web/packages/esbuild-plugin-live-reload/.prettierignore
Normal file
3
web/packages/esbuild-plugin-live-reload/.prettierignore
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
node_modules
|
||||||
|
./README.md
|
||||||
|
out
|
18
web/packages/esbuild-plugin-live-reload/LICENSE.txt
Normal file
18
web/packages/esbuild-plugin-live-reload/LICENSE.txt
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2025 Authentik Security, Inc.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||||
|
associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||||
|
including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||||
|
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial
|
||||||
|
portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||||
|
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||||
|
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@ -1,40 +0,0 @@
|
|||||||
# `@goauthentik/esbuild-plugin-live-reload`
|
|
||||||
|
|
||||||
_A plugin that enables live reloading of ESBuild during development._
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
### Node.js setup
|
|
||||||
|
|
||||||
```js
|
|
||||||
import { liveReloadPlugin } from "@goauthentik/esbuild-plugin-live-reload";
|
|
||||||
import esbuild from "esbuild";
|
|
||||||
|
|
||||||
const NodeEnvironment = process.env.NODE_ENV || "development";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {esbuild.BuildOptions}
|
|
||||||
*/
|
|
||||||
const buildOptions = {
|
|
||||||
// ... Your build options.
|
|
||||||
define: {
|
|
||||||
"process.env.NODE_ENV": JSON.stringify(NodeEnvironment),
|
|
||||||
},
|
|
||||||
plugins: [liveReloadPlugin(/** @see {@link LiveReloadPluginOptions} */)],
|
|
||||||
};
|
|
||||||
|
|
||||||
const buildContext = await esbuild.context(buildOptions);
|
|
||||||
|
|
||||||
await buildContext.rebuild();
|
|
||||||
await buildContext.watch();
|
|
||||||
```
|
|
||||||
|
|
||||||
### Browser setup
|
|
||||||
|
|
||||||
```js
|
|
||||||
// Place this at the beginning of your application's entry point.
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV === "development") {
|
|
||||||
await import("@goauthentik/esbuild-plugin-live-reload/client");
|
|
||||||
}
|
|
||||||
```
|
|
@ -28,6 +28,8 @@ const log = console.debug.bind(console, logPrefix);
|
|||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* @implements {Disposable}
|
* @implements {Disposable}
|
||||||
|
* @category Plugin
|
||||||
|
* runtime browser
|
||||||
*/
|
*/
|
||||||
export class ESBuildObserver extends EventSource {
|
export class ESBuildObserver extends EventSource {
|
||||||
/**
|
/**
|
||||||
|
@ -1,2 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* @remarks Live reload plugin for ESBuild.
|
||||||
|
*/
|
||||||
|
|
||||||
export * from "./client/index.js";
|
export * from "./client/index.js";
|
||||||
export * from "./plugin/index.js";
|
export * from "./plugin/index.js";
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
"esbuild": "^0.25.4",
|
"esbuild": "^0.25.4",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"prettier-plugin-packagejson": "^2.5.14",
|
"prettier-plugin-packagejson": "^2.5.14",
|
||||||
|
"typedoc": "^0.28.5",
|
||||||
|
"typedoc-plugin-markdown": "^4.6.3",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -145,9 +147,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/aix-ppc64": {
|
"node_modules/@esbuild/aix-ppc64": {
|
||||||
"version": "0.25.4",
|
"version": "0.25.5",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz",
|
||||||
"integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==",
|
"integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@ -162,9 +164,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/android-arm": {
|
"node_modules/@esbuild/android-arm": {
|
||||||
"version": "0.25.4",
|
"version": "0.25.5",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz",
|
||||||
"integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==",
|
"integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@ -179,9 +181,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/android-arm64": {
|
"node_modules/@esbuild/android-arm64": {
|
||||||
"version": "0.25.4",
|
"version": "0.25.5",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz",
|
||||||
"integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==",
|
"integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -196,9 +198,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/android-x64": {
|
"node_modules/@esbuild/android-x64": {
|
||||||
"version": "0.25.4",
|
"version": "0.25.5",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz",
|
||||||
"integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==",
|
"integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -213,9 +215,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/darwin-arm64": {
|
"node_modules/@esbuild/darwin-arm64": {
|
||||||
"version": "0.25.4",
|
"version": "0.25.5",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz",
|
||||||
"integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==",
|
"integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -230,9 +232,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/darwin-x64": {
|
"node_modules/@esbuild/darwin-x64": {
|
||||||
"version": "0.25.4",
|
"version": "0.25.5",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz",
|
||||||
"integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==",
|
"integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -247,9 +249,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/freebsd-arm64": {
|
"node_modules/@esbuild/freebsd-arm64": {
|
||||||
"version": "0.25.4",
|
"version": "0.25.5",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz",
|
||||||
"integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==",
|
"integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -264,9 +266,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/freebsd-x64": {
|
"node_modules/@esbuild/freebsd-x64": {
|
||||||
"version": "0.25.4",
|
"version": "0.25.5",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz",
|
||||||
"integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==",
|
"integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -281,9 +283,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-arm": {
|
"node_modules/@esbuild/linux-arm": {
|
||||||
"version": "0.25.4",
|
"version": "0.25.5",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz",
|
||||||
"integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==",
|
"integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@ -298,9 +300,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-arm64": {
|
"node_modules/@esbuild/linux-arm64": {
|
||||||
"version": "0.25.4",
|
"version": "0.25.5",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz",
|
||||||
"integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==",
|
"integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -315,9 +317,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-ia32": {
|
"node_modules/@esbuild/linux-ia32": {
|
||||||
"version": "0.25.4",
|
"version": "0.25.5",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz",
|
||||||
"integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==",
|
"integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@ -332,9 +334,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-loong64": {
|
"node_modules/@esbuild/linux-loong64": {
|
||||||
"version": "0.25.4",
|
"version": "0.25.5",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz",
|
||||||
"integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==",
|
"integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"loong64"
|
"loong64"
|
||||||
],
|
],
|
||||||
@ -349,9 +351,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-mips64el": {
|
"node_modules/@esbuild/linux-mips64el": {
|
||||||
"version": "0.25.4",
|
"version": "0.25.5",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz",
|
||||||
"integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==",
|
"integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"mips64el"
|
"mips64el"
|
||||||
],
|
],
|
||||||
@ -366,9 +368,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-ppc64": {
|
"node_modules/@esbuild/linux-ppc64": {
|
||||||
"version": "0.25.4",
|
"version": "0.25.5",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz",
|
||||||
"integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==",
|
"integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@ -383,9 +385,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-riscv64": {
|
"node_modules/@esbuild/linux-riscv64": {
|
||||||
"version": "0.25.4",
|
"version": "0.25.5",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz",
|
||||||
"integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==",
|
"integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
@ -400,9 +402,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-s390x": {
|
"node_modules/@esbuild/linux-s390x": {
|
||||||
"version": "0.25.4",
|
"version": "0.25.5",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz",
|
||||||
"integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==",
|
"integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
@ -417,9 +419,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-x64": {
|
"node_modules/@esbuild/linux-x64": {
|
||||||
"version": "0.25.4",
|
"version": "0.25.5",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz",
|
||||||
"integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==",
|
"integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -434,9 +436,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/netbsd-arm64": {
|
"node_modules/@esbuild/netbsd-arm64": {
|
||||||
"version": "0.25.4",
|
"version": "0.25.5",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz",
|
||||||
"integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==",
|
"integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -451,9 +453,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/netbsd-x64": {
|
"node_modules/@esbuild/netbsd-x64": {
|
||||||
"version": "0.25.4",
|
"version": "0.25.5",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz",
|
||||||
"integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==",
|
"integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -468,9 +470,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/openbsd-arm64": {
|
"node_modules/@esbuild/openbsd-arm64": {
|
||||||
"version": "0.25.4",
|
"version": "0.25.5",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz",
|
||||||
"integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==",
|
"integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -485,9 +487,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/openbsd-x64": {
|
"node_modules/@esbuild/openbsd-x64": {
|
||||||
"version": "0.25.4",
|
"version": "0.25.5",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz",
|
||||||
"integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==",
|
"integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -502,9 +504,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/sunos-x64": {
|
"node_modules/@esbuild/sunos-x64": {
|
||||||
"version": "0.25.4",
|
"version": "0.25.5",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz",
|
||||||
"integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==",
|
"integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -519,9 +521,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/win32-arm64": {
|
"node_modules/@esbuild/win32-arm64": {
|
||||||
"version": "0.25.4",
|
"version": "0.25.5",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz",
|
||||||
"integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==",
|
"integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -536,9 +538,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/win32-ia32": {
|
"node_modules/@esbuild/win32-ia32": {
|
||||||
"version": "0.25.4",
|
"version": "0.25.5",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz",
|
||||||
"integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==",
|
"integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@ -553,9 +555,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/win32-x64": {
|
"node_modules/@esbuild/win32-x64": {
|
||||||
"version": "0.25.4",
|
"version": "0.25.5",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz",
|
||||||
"integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==",
|
"integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -569,6 +571,20 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@gerrit0/mini-shiki": {
|
||||||
|
"version": "3.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-3.4.2.tgz",
|
||||||
|
"integrity": "sha512-3jXo5bNjvvimvdbIhKGfFxSnKCX+MA8wzHv55ptzk/cx8wOzT+BRcYgj8aFN3yTiTs+zvQQiaZFr7Jce1ZG3fw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@shikijs/engine-oniguruma": "^3.4.2",
|
||||||
|
"@shikijs/langs": "^3.4.2",
|
||||||
|
"@shikijs/themes": "^3.4.2",
|
||||||
|
"@shikijs/types": "^3.4.2",
|
||||||
|
"@shikijs/vscode-textmate": "^10.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@goauthentik/prettier-config": {
|
"node_modules/@goauthentik/prettier-config": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@goauthentik/prettier-config/-/prettier-config-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@goauthentik/prettier-config/-/prettier-config-1.0.5.tgz",
|
||||||
@ -659,6 +675,55 @@
|
|||||||
"url": "https://opencollective.com/pkgr"
|
"url": "https://opencollective.com/pkgr"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@shikijs/engine-oniguruma": {
|
||||||
|
"version": "3.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.4.2.tgz",
|
||||||
|
"integrity": "sha512-zcZKMnNndgRa3ORja6Iemsr3DrLtkX3cAF7lTJkdMB6v9alhlBsX9uNiCpqofNrXOvpA3h6lHcLJxgCIhVOU5Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@shikijs/types": "3.4.2",
|
||||||
|
"@shikijs/vscode-textmate": "^10.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@shikijs/langs": {
|
||||||
|
"version": "3.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.4.2.tgz",
|
||||||
|
"integrity": "sha512-H6azIAM+OXD98yztIfs/KH5H4PU39t+SREhmM8LaNXyUrqj2mx+zVkr8MWYqjceSjDw9I1jawm1WdFqU806rMA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@shikijs/types": "3.4.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@shikijs/themes": {
|
||||||
|
"version": "3.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.4.2.tgz",
|
||||||
|
"integrity": "sha512-qAEuAQh+brd8Jyej2UDDf+b4V2g1Rm8aBIdvt32XhDPrHvDkEnpb7Kzc9hSuHUxz0Iuflmq7elaDuQAP9bHIhg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@shikijs/types": "3.4.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@shikijs/types": {
|
||||||
|
"version": "3.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.4.2.tgz",
|
||||||
|
"integrity": "sha512-zHC1l7L+eQlDXLnxvM9R91Efh2V4+rN3oMVS2swCBssbj2U/FBwybD1eeLaq8yl/iwT+zih8iUbTBCgGZOYlVg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@shikijs/vscode-textmate": "^10.0.2",
|
||||||
|
"@types/hast": "^3.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@shikijs/vscode-textmate": {
|
||||||
|
"version": "10.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz",
|
||||||
|
"integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@trivago/prettier-plugin-sort-imports": {
|
"node_modules/@trivago/prettier-plugin-sort-imports": {
|
||||||
"version": "5.2.2",
|
"version": "5.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-5.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-5.2.2.tgz",
|
||||||
@ -694,6 +759,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/hast": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/unist": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "22.15.21",
|
"version": "22.15.21",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz",
|
||||||
@ -704,6 +779,37 @@
|
|||||||
"undici-types": "~6.21.0"
|
"undici-types": "~6.21.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/unist": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/argparse": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Python-2.0"
|
||||||
|
},
|
||||||
|
"node_modules/balanced-match": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/brace-expansion": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"balanced-match": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.4.1",
|
"version": "4.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
||||||
@ -745,10 +851,23 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/entities": {
|
||||||
|
"version": "4.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||||
|
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/esbuild": {
|
"node_modules/esbuild": {
|
||||||
"version": "0.25.4",
|
"version": "0.25.5",
|
||||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz",
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz",
|
||||||
"integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==",
|
"integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -759,31 +878,31 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@esbuild/aix-ppc64": "0.25.4",
|
"@esbuild/aix-ppc64": "0.25.5",
|
||||||
"@esbuild/android-arm": "0.25.4",
|
"@esbuild/android-arm": "0.25.5",
|
||||||
"@esbuild/android-arm64": "0.25.4",
|
"@esbuild/android-arm64": "0.25.5",
|
||||||
"@esbuild/android-x64": "0.25.4",
|
"@esbuild/android-x64": "0.25.5",
|
||||||
"@esbuild/darwin-arm64": "0.25.4",
|
"@esbuild/darwin-arm64": "0.25.5",
|
||||||
"@esbuild/darwin-x64": "0.25.4",
|
"@esbuild/darwin-x64": "0.25.5",
|
||||||
"@esbuild/freebsd-arm64": "0.25.4",
|
"@esbuild/freebsd-arm64": "0.25.5",
|
||||||
"@esbuild/freebsd-x64": "0.25.4",
|
"@esbuild/freebsd-x64": "0.25.5",
|
||||||
"@esbuild/linux-arm": "0.25.4",
|
"@esbuild/linux-arm": "0.25.5",
|
||||||
"@esbuild/linux-arm64": "0.25.4",
|
"@esbuild/linux-arm64": "0.25.5",
|
||||||
"@esbuild/linux-ia32": "0.25.4",
|
"@esbuild/linux-ia32": "0.25.5",
|
||||||
"@esbuild/linux-loong64": "0.25.4",
|
"@esbuild/linux-loong64": "0.25.5",
|
||||||
"@esbuild/linux-mips64el": "0.25.4",
|
"@esbuild/linux-mips64el": "0.25.5",
|
||||||
"@esbuild/linux-ppc64": "0.25.4",
|
"@esbuild/linux-ppc64": "0.25.5",
|
||||||
"@esbuild/linux-riscv64": "0.25.4",
|
"@esbuild/linux-riscv64": "0.25.5",
|
||||||
"@esbuild/linux-s390x": "0.25.4",
|
"@esbuild/linux-s390x": "0.25.5",
|
||||||
"@esbuild/linux-x64": "0.25.4",
|
"@esbuild/linux-x64": "0.25.5",
|
||||||
"@esbuild/netbsd-arm64": "0.25.4",
|
"@esbuild/netbsd-arm64": "0.25.5",
|
||||||
"@esbuild/netbsd-x64": "0.25.4",
|
"@esbuild/netbsd-x64": "0.25.5",
|
||||||
"@esbuild/openbsd-arm64": "0.25.4",
|
"@esbuild/openbsd-arm64": "0.25.5",
|
||||||
"@esbuild/openbsd-x64": "0.25.4",
|
"@esbuild/openbsd-x64": "0.25.5",
|
||||||
"@esbuild/sunos-x64": "0.25.4",
|
"@esbuild/sunos-x64": "0.25.5",
|
||||||
"@esbuild/win32-arm64": "0.25.4",
|
"@esbuild/win32-arm64": "0.25.5",
|
||||||
"@esbuild/win32-ia32": "0.25.4",
|
"@esbuild/win32-ia32": "0.25.5",
|
||||||
"@esbuild/win32-x64": "0.25.4"
|
"@esbuild/win32-x64": "0.25.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fdir": {
|
"node_modules/fdir": {
|
||||||
@ -865,6 +984,16 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/linkify-it": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"uc.micro": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/lodash": {
|
"node_modules/lodash": {
|
||||||
"version": "4.17.21",
|
"version": "4.17.21",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
@ -872,6 +1001,54 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/lunr": {
|
||||||
|
"version": "2.3.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
|
||||||
|
"integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/markdown-it": {
|
||||||
|
"version": "14.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
|
||||||
|
"integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"argparse": "^2.0.1",
|
||||||
|
"entities": "^4.4.0",
|
||||||
|
"linkify-it": "^5.0.0",
|
||||||
|
"mdurl": "^2.0.0",
|
||||||
|
"punycode.js": "^2.3.1",
|
||||||
|
"uc.micro": "^2.1.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"markdown-it": "bin/markdown-it.mjs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mdurl": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/minimatch": {
|
||||||
|
"version": "9.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
||||||
|
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"brace-expansion": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16 || 14 >=14.17"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
@ -950,6 +1127,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/punycode.js": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/semver": {
|
"node_modules/semver": {
|
||||||
"version": "7.7.2",
|
"version": "7.7.2",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@ -1016,6 +1203,43 @@
|
|||||||
"url": "https://github.com/sponsors/SuperchupuDev"
|
"url": "https://github.com/sponsors/SuperchupuDev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/typedoc": {
|
||||||
|
"version": "0.28.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.28.5.tgz",
|
||||||
|
"integrity": "sha512-5PzUddaA9FbaarUzIsEc4wNXCiO4Ot3bJNeMF2qKpYlTmM9TTaSHQ7162w756ERCkXER/+o2purRG6YOAv6EMA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@gerrit0/mini-shiki": "^3.2.2",
|
||||||
|
"lunr": "^2.3.9",
|
||||||
|
"markdown-it": "^14.1.0",
|
||||||
|
"minimatch": "^9.0.5",
|
||||||
|
"yaml": "^2.7.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"typedoc": "bin/typedoc"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 18",
|
||||||
|
"pnpm": ">= 10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/typedoc-plugin-markdown": {
|
||||||
|
"version": "4.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-4.6.3.tgz",
|
||||||
|
"integrity": "sha512-86oODyM2zajXwLs4Wok2mwVEfCwCnp756QyhLGX2IfsdRYr1DXLCgJgnLndaMUjJD7FBhnLk2okbNE9PdLxYRw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 18"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typedoc": "0.28.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.8.3",
|
"version": "5.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
||||||
@ -1030,10 +1254,30 @@
|
|||||||
"node": ">=14.17"
|
"node": ">=14.17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/uc.micro": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/undici-types": {
|
"node_modules/undici-types": {
|
||||||
"version": "6.21.0",
|
"version": "6.21.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/yaml": {
|
||||||
|
"version": "2.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz",
|
||||||
|
"integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"bin": {
|
||||||
|
"yaml": "bin.mjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14.6"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
{
|
{
|
||||||
"name": "@goauthentik/esbuild-plugin-live-reload",
|
"name": "@goauthentik/esbuild-plugin-live-reload",
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"description": "ESBuild plugin to watch for file changes and trigger client-side reloads.",
|
"description": "ESBuild + browser refresh. Build completes, page reloads.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc -p ."
|
"build": "npm run build:types && npm run build:docs",
|
||||||
|
"build:docs": "typedoc",
|
||||||
|
"build:types": "tsc -p .",
|
||||||
|
"prettier": "prettier --cache --write -u .",
|
||||||
|
"prettier-check": "prettier --cache --check -u ."
|
||||||
},
|
},
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@ -31,17 +35,32 @@
|
|||||||
"@goauthentik/tsconfig": "^1.0.4",
|
"@goauthentik/tsconfig": "^1.0.4",
|
||||||
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
||||||
"@types/node": "^22.15.21",
|
"@types/node": "^22.15.21",
|
||||||
"esbuild": "^0.25.4",
|
"esbuild": "^0.25.5",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"prettier-plugin-packagejson": "^2.5.14",
|
"prettier-plugin-packagejson": "^2.5.14",
|
||||||
|
"typedoc": "^0.28.5",
|
||||||
|
"typedoc-plugin-markdown": "^4.6.3",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"esbuild": "^0.25.4"
|
"esbuild": "^0.25.5"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=22"
|
"node": ">=22"
|
||||||
},
|
},
|
||||||
|
"keywords": [
|
||||||
|
"esbuild",
|
||||||
|
"live-reload",
|
||||||
|
"browser",
|
||||||
|
"refresh",
|
||||||
|
"reload",
|
||||||
|
"authentik"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/goauthentik/authentik.git",
|
||||||
|
"directory": "web/packages/esbuild-plugin-live-reload"
|
||||||
|
},
|
||||||
"types": "./out/index.d.ts",
|
"types": "./out/index.d.ts",
|
||||||
"files": [
|
"files": [
|
||||||
"./index.js",
|
"./index.js",
|
||||||
|
@ -7,12 +7,18 @@
|
|||||||
*/
|
*/
|
||||||
import { findFreePorts } from "find-free-ports";
|
import { findFreePorts } from "find-free-ports";
|
||||||
import * as http from "node:http";
|
import * as http from "node:http";
|
||||||
import * as path from "node:path";
|
import { resolve as resolvePath } from "node:path";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serializes a custom event to a text stream.
|
* Serializes a custom event to a text stream.
|
||||||
|
*
|
||||||
* @param {Event} event
|
* @param {Event} event
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
|
*
|
||||||
|
* @category Server API
|
||||||
|
* @ignore
|
||||||
|
* @internal
|
||||||
|
* @runtime node
|
||||||
*/
|
*/
|
||||||
export function serializeCustomEventToStream(event) {
|
export function serializeCustomEventToStream(event) {
|
||||||
// @ts-expect-error - TS doesn't know about the detail property
|
// @ts-expect-error - TS doesn't know about the detail property
|
||||||
@ -54,17 +60,26 @@ async function findDisparatePort() {
|
|||||||
* @property {string} pathname
|
* @property {string} pathname
|
||||||
* @property {EventTarget} dispatcher
|
* @property {EventTarget} dispatcher
|
||||||
* @property {string} [logPrefix]
|
* @property {string} [logPrefix]
|
||||||
|
*
|
||||||
|
* @category Server API
|
||||||
|
* @runtime node
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {(req: http.IncomingMessage, res: http.ServerResponse) => void} RequestHandler
|
* @typedef {(req: http.IncomingMessage, res: http.ServerResponse) => void} RequestHandler
|
||||||
|
*
|
||||||
|
* @category Server API
|
||||||
|
* @runtime node
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an event request handler.
|
* Create an event request handler.
|
||||||
|
*
|
||||||
* @param {EventServerInit} options
|
* @param {EventServerInit} options
|
||||||
* @returns {RequestHandler}
|
* @returns {RequestHandler}
|
||||||
* @category ESBuild
|
*
|
||||||
|
* @category Server API
|
||||||
|
* @runtime node
|
||||||
*/
|
*/
|
||||||
export function createRequestHandler({ pathname, dispatcher, logPrefix = "Build Observer" }) {
|
export function createRequestHandler({ pathname, dispatcher, logPrefix = "Build Observer" }) {
|
||||||
const log = console.log.bind(console, `[${logPrefix}]`);
|
const log = console.log.bind(console, `[${logPrefix}]`);
|
||||||
@ -129,6 +144,9 @@ export function createRequestHandler({ pathname, dispatcher, logPrefix = "Build
|
|||||||
/**
|
/**
|
||||||
* Options for the build observer plugin.
|
* Options for the build observer plugin.
|
||||||
*
|
*
|
||||||
|
* @category Plugin API
|
||||||
|
* @runtime node
|
||||||
|
*
|
||||||
* @typedef {object} LiveReloadPluginOptions
|
* @typedef {object} LiveReloadPluginOptions
|
||||||
*
|
*
|
||||||
* @property {HTTPServer | HTTPSServer} [server] A server to listen on. If not provided, a new server will be created.
|
* @property {HTTPServer | HTTPSServer} [server] A server to listen on. If not provided, a new server will be created.
|
||||||
@ -141,8 +159,7 @@ export function createRequestHandler({ pathname, dispatcher, logPrefix = "Build
|
|||||||
/**
|
/**
|
||||||
* Creates a plugin that listens for build events and sends them to a server-sent event stream.
|
* Creates a plugin that listens for build events and sends them to a server-sent event stream.
|
||||||
*
|
*
|
||||||
* @param {
|
* @param {LiveReloadPluginOptions} [options]
|
||||||
* } [options]
|
|
||||||
* @returns {import('esbuild').Plugin}
|
* @returns {import('esbuild').Plugin}
|
||||||
*/
|
*/
|
||||||
export function liveReloadPlugin(options = {}) {
|
export function liveReloadPlugin(options = {}) {
|
||||||
@ -234,7 +251,7 @@ export function liveReloadPlugin(options = {}) {
|
|||||||
location: error.location
|
location: error.location
|
||||||
? {
|
? {
|
||||||
...error.location,
|
...error.location,
|
||||||
file: path.resolve(relativeRoot, error.location.file),
|
file: resolvePath(relativeRoot, error.location.file),
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
})),
|
})),
|
||||||
|
@ -6,5 +6,9 @@
|
|||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"checkJs": true,
|
"checkJs": true,
|
||||||
"emitDeclarationOnly": true
|
"emitDeclarationOnly": true
|
||||||
}
|
},
|
||||||
|
"exclude": [
|
||||||
|
// ---
|
||||||
|
"**/out/**/*"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
66
web/packages/esbuild-plugin-live-reload/typedoc.json
Normal file
66
web/packages/esbuild-plugin-live-reload/typedoc.json
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://typedoc-plugin-markdown.org/schema.json",
|
||||||
|
"entryPoints": ["./plugin/index.js"],
|
||||||
|
"plugin": ["typedoc-plugin-markdown"],
|
||||||
|
"name": "ESBuild Plugin Live Reload",
|
||||||
|
"formatWithPrettier": true,
|
||||||
|
"prettierConfigFile": "@goauthentik/prettier-config",
|
||||||
|
"flattenOutputFiles": true,
|
||||||
|
"readme": ".github/README.md",
|
||||||
|
"mergeReadme": true,
|
||||||
|
"enumMembersFormat": "table",
|
||||||
|
"parametersFormat": "table",
|
||||||
|
"interfacePropertiesFormat": "table",
|
||||||
|
"typeDeclarationFormat": "table",
|
||||||
|
"indexFormat": "table",
|
||||||
|
"router": "module",
|
||||||
|
"jsDocCompatibility": true,
|
||||||
|
"defaultCategory": "Plugin API",
|
||||||
|
"disableSources": true,
|
||||||
|
"out": ".",
|
||||||
|
"cleanOutputDir": false,
|
||||||
|
"blockTags": [
|
||||||
|
"@runtime",
|
||||||
|
"@file",
|
||||||
|
"@defaultValue",
|
||||||
|
"@deprecated",
|
||||||
|
"@example",
|
||||||
|
"@param",
|
||||||
|
"@privateRemarks",
|
||||||
|
"@remarks",
|
||||||
|
"@returns",
|
||||||
|
"@see",
|
||||||
|
"@throws",
|
||||||
|
"@typeParam",
|
||||||
|
"@author",
|
||||||
|
"@callback",
|
||||||
|
"@category",
|
||||||
|
"@categoryDescription",
|
||||||
|
"@default",
|
||||||
|
"@document",
|
||||||
|
"@extends",
|
||||||
|
"@augments",
|
||||||
|
"@yields",
|
||||||
|
"@group",
|
||||||
|
"@groupDescription",
|
||||||
|
"@import",
|
||||||
|
"@inheritDoc",
|
||||||
|
"@jsx",
|
||||||
|
"@license",
|
||||||
|
"@module",
|
||||||
|
"@mergeModuleWith",
|
||||||
|
"@prop",
|
||||||
|
"@property",
|
||||||
|
"@return",
|
||||||
|
"@satisfies",
|
||||||
|
"@since",
|
||||||
|
"@template",
|
||||||
|
"@type",
|
||||||
|
"@typedef",
|
||||||
|
"@summary",
|
||||||
|
"@preventInline",
|
||||||
|
"@inlineType",
|
||||||
|
"@preventExpand",
|
||||||
|
"@expandType"
|
||||||
|
]
|
||||||
|
}
|
@ -30,7 +30,6 @@
|
|||||||
"rollup-plugin-copy": "^3.5.0"
|
"rollup-plugin-copy": "^3.5.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@swc/core": "^1.7.28",
|
|
||||||
"@swc/core-darwin-arm64": "^1.6.13",
|
"@swc/core-darwin-arm64": "^1.6.13",
|
||||||
"@swc/core-darwin-x64": "^1.6.13",
|
"@swc/core-darwin-x64": "^1.6.13",
|
||||||
"@swc/core-linux-arm-gnueabihf": "^1.6.13",
|
"@swc/core-linux-arm-gnueabihf": "^1.6.13",
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
*/
|
*/
|
||||||
import { mdxPlugin } from "#bundler/mdx-plugin/node";
|
import { mdxPlugin } from "#bundler/mdx-plugin/node";
|
||||||
import { createBundleDefinitions } from "#bundler/utils/node";
|
import { createBundleDefinitions } from "#bundler/utils/node";
|
||||||
import { DistDirectoryName } from "#paths";
|
|
||||||
import { DistDirectory, EntryPoint, PackageRoot } from "#paths/node";
|
import { DistDirectory, EntryPoint, PackageRoot } from "#paths/node";
|
||||||
import { NodeEnvironment } from "@goauthentik/core/environment/node";
|
import { NodeEnvironment } from "@goauthentik/core/environment/node";
|
||||||
import { MonoRepoRoot, resolvePackage } from "@goauthentik/core/paths/node";
|
import { MonoRepoRoot, resolvePackage } from "@goauthentik/core/paths/node";
|
||||||
@ -29,7 +28,6 @@ const BASE_ESBUILD_OPTIONS = {
|
|||||||
entryNames: `[dir]/[name]-${readBuildIdentifier()}`,
|
entryNames: `[dir]/[name]-${readBuildIdentifier()}`,
|
||||||
chunkNames: "[dir]/chunks/[hash]",
|
chunkNames: "[dir]/chunks/[hash]",
|
||||||
assetNames: "assets/[dir]/[name]-[hash]",
|
assetNames: "assets/[dir]/[name]-[hash]",
|
||||||
publicPath: path.join("/static", DistDirectoryName),
|
|
||||||
outdir: DistDirectory,
|
outdir: DistDirectory,
|
||||||
bundle: true,
|
bundle: true,
|
||||||
write: true,
|
write: true,
|
||||||
|
@ -85,8 +85,8 @@ export class AdminOverviewPage extends AdminOverviewBase {
|
|||||||
render(): TemplateResult {
|
render(): TemplateResult {
|
||||||
const username = this.user?.user.name || this.user?.user.username;
|
const username = this.user?.user.name || this.user?.user.username;
|
||||||
|
|
||||||
return html` <ak-page-header
|
return html`<ak-page-header
|
||||||
header=${msg(str`Welcome, ${username || ""}.`)}
|
header=${this.user ? msg(str`Welcome, ${username || ""}.`) : msg("Welcome.")}
|
||||||
description=${msg("General system status")}
|
description=${msg("General system status")}
|
||||||
?hasIcon=${false}
|
?hasIcon=${false}
|
||||||
>
|
>
|
||||||
|
@ -1,26 +1,38 @@
|
|||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||||
|
import {
|
||||||
|
DataProvision,
|
||||||
|
DualSelectPair,
|
||||||
|
DualSelectPairSource,
|
||||||
|
} from "#elements/ak-dual-select/types";
|
||||||
|
|
||||||
import { CertificateKeyPair, CryptoApi } from "@goauthentik/api";
|
import { CertificateKeyPair, CryptoApi } from "@goauthentik/api";
|
||||||
|
|
||||||
const certToSelect = (s: CertificateKeyPair) => [s.pk, s.name, s.name, s];
|
const certToSelect = (cert: CertificateKeyPair): DualSelectPair<CertificateKeyPair> => {
|
||||||
|
return [cert.pk, cert.name, cert.name, cert];
|
||||||
|
};
|
||||||
|
|
||||||
export async function certificateProvider(page = 1, search = "") {
|
export async function certificateProvider(page = 1, search = ""): Promise<DataProvision> {
|
||||||
const certificates = await new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsList({
|
return new CryptoApi(DEFAULT_CONFIG)
|
||||||
ordering: "name",
|
.cryptoCertificatekeypairsList({
|
||||||
pageSize: 20,
|
ordering: "name",
|
||||||
search: search.trim(),
|
pageSize: 20,
|
||||||
page,
|
search: search.trim(),
|
||||||
hasKey: undefined,
|
page,
|
||||||
});
|
hasKey: undefined,
|
||||||
return {
|
})
|
||||||
pagination: certificates.pagination,
|
.then(({ pagination, results }) => {
|
||||||
options: certificates.results.map(certToSelect),
|
return {
|
||||||
};
|
pagination,
|
||||||
|
options: results.map(certToSelect),
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function certificateSelector(instanceMappings?: string[]) {
|
export function certificateSelector(
|
||||||
|
instanceMappings?: string[],
|
||||||
|
): DualSelectPairSource<CertificateKeyPair> {
|
||||||
if (!instanceMappings) {
|
if (!instanceMappings) {
|
||||||
return [];
|
return () => Promise.resolve([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return async () => {
|
return async () => {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { $PFBase } from "#common/theme";
|
||||||
import { WithLicenseSummary } from "#elements/mixins/license";
|
import { WithLicenseSummary } from "#elements/mixins/license";
|
||||||
import "@goauthentik/elements/Alert";
|
import "@goauthentik/elements/Alert";
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { AKElement } from "@goauthentik/elements/Base";
|
||||||
@ -8,6 +9,8 @@ import { customElement, property } from "lit/decorators.js";
|
|||||||
|
|
||||||
@customElement("ak-license-notice")
|
@customElement("ak-license-notice")
|
||||||
export class AkLicenceNotice extends WithLicenseSummary(AKElement) {
|
export class AkLicenceNotice extends WithLicenseSummary(AKElement) {
|
||||||
|
static styles = [$PFBase];
|
||||||
|
|
||||||
@property()
|
@property()
|
||||||
notice = msg("Enterprise only");
|
notice = msg("Enterprise only");
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import "@goauthentik/admin/flows/StageBindingForm";
|
import "@goauthentik/admin/flows/StageBindingForm";
|
||||||
import "@goauthentik/admin/policies/BoundPoliciesList";
|
import "@goauthentik/admin/policies/BoundPoliciesList";
|
||||||
|
import "@goauthentik/admin/rbac/ObjectPermissionModal";
|
||||||
import "@goauthentik/admin/stages/StageWizard";
|
import "@goauthentik/admin/stages/StageWizard";
|
||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||||
import "@goauthentik/elements/Tabs";
|
import "@goauthentik/elements/Tabs";
|
||||||
@ -14,7 +15,11 @@ import { TemplateResult, html } from "lit";
|
|||||||
import { customElement, property } from "lit/decorators.js";
|
import { customElement, property } from "lit/decorators.js";
|
||||||
import { ifDefined } from "lit/directives/if-defined.js";
|
import { ifDefined } from "lit/directives/if-defined.js";
|
||||||
|
|
||||||
import { FlowStageBinding, FlowsApi } from "@goauthentik/api";
|
import {
|
||||||
|
FlowStageBinding,
|
||||||
|
FlowsApi,
|
||||||
|
RbacPermissionsAssignedByUsersListModelEnum,
|
||||||
|
} from "@goauthentik/api";
|
||||||
|
|
||||||
@customElement("ak-bound-stages-list")
|
@customElement("ak-bound-stages-list")
|
||||||
export class BoundStagesList extends Table<FlowStageBinding> {
|
export class BoundStagesList extends Table<FlowStageBinding> {
|
||||||
@ -99,7 +104,12 @@ export class BoundStagesList extends Table<FlowStageBinding> {
|
|||||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||||
${msg("Edit Binding")}
|
${msg("Edit Binding")}
|
||||||
</button>
|
</button>
|
||||||
</ak-forms-modal>`,
|
</ak-forms-modal>
|
||||||
|
<ak-rbac-object-permission-modal
|
||||||
|
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikFlowsFlowstagebinding}
|
||||||
|
objectPk=${item.pk}
|
||||||
|
>
|
||||||
|
</ak-rbac-object-permission-modal>`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
PolicyBindingCheckTarget,
|
PolicyBindingCheckTarget,
|
||||||
PolicyBindingCheckTargetToLabel,
|
PolicyBindingCheckTargetToLabel,
|
||||||
} from "@goauthentik/admin/policies/utils";
|
} from "@goauthentik/admin/policies/utils";
|
||||||
|
import "@goauthentik/admin/rbac/ObjectPermissionModal";
|
||||||
import "@goauthentik/admin/users/UserForm";
|
import "@goauthentik/admin/users/UserForm";
|
||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||||
import { PFSize } from "@goauthentik/common/enums.js";
|
import { PFSize } from "@goauthentik/common/enums.js";
|
||||||
@ -22,7 +23,11 @@ import { TemplateResult, html, nothing } from "lit";
|
|||||||
import { customElement, property } from "lit/decorators.js";
|
import { customElement, property } from "lit/decorators.js";
|
||||||
import { ifDefined } from "lit/directives/if-defined.js";
|
import { ifDefined } from "lit/directives/if-defined.js";
|
||||||
|
|
||||||
import { PoliciesApi, PolicyBinding } from "@goauthentik/api";
|
import {
|
||||||
|
PoliciesApi,
|
||||||
|
PolicyBinding,
|
||||||
|
RbacPermissionsAssignedByUsersListModelEnum,
|
||||||
|
} from "@goauthentik/api";
|
||||||
|
|
||||||
@customElement("ak-bound-policies-list")
|
@customElement("ak-bound-policies-list")
|
||||||
export class BoundPoliciesList extends Table<PolicyBinding> {
|
export class BoundPoliciesList extends Table<PolicyBinding> {
|
||||||
@ -178,7 +183,12 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
|
|||||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||||
${msg("Edit Binding")}
|
${msg("Edit Binding")}
|
||||||
</button>
|
</button>
|
||||||
</ak-forms-modal>`,
|
</ak-forms-modal>
|
||||||
|
<ak-rbac-object-permission-modal
|
||||||
|
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikPoliciesPolicybinding}
|
||||||
|
objectPk=${item.pk}
|
||||||
|
>
|
||||||
|
</ak-rbac-object-permission-modal>`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,6 +148,26 @@ export class LDAPSourceForm extends BaseSourceForm<LDAPSource> {
|
|||||||
<span class="pf-c-switch__label">${msg("Sync groups")}</span>
|
<span class="pf-c-switch__label">${msg("Sync groups")}</span>
|
||||||
</label>
|
</label>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
|
<ak-form-element-horizontal name="deleteNotFoundObjects">
|
||||||
|
<label class="pf-c-switch">
|
||||||
|
<input
|
||||||
|
class="pf-c-switch__input"
|
||||||
|
type="checkbox"
|
||||||
|
?checked=${this.instance?.deleteNotFoundObjects ?? 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("Delete Not Found Objects")}</span>
|
||||||
|
</label>
|
||||||
|
<p class="pf-c-form__helper-text">
|
||||||
|
${msg(
|
||||||
|
"Delete authentik users and groups which were previously supplied by this source, but are now missing from it.",
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</ak-form-element-horizontal>
|
||||||
<ak-form-group .expanded=${true}>
|
<ak-form-group .expanded=${true}>
|
||||||
<span slot="header"> ${msg("Connection settings")} </span>
|
<span slot="header"> ${msg("Connection settings")} </span>
|
||||||
<div slot="body" class="pf-c-form">
|
<div slot="body" class="pf-c-form">
|
||||||
@ -409,10 +429,25 @@ export class LDAPSourceForm extends BaseSourceForm<LDAPSource> {
|
|||||||
/>
|
/>
|
||||||
<p class="pf-c-form__helper-text">
|
<p class="pf-c-form__helper-text">
|
||||||
${msg(
|
${msg(
|
||||||
"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,...'. When selecting 'Lookup using a user attribute', this should be a user attribute, otherwise a group attribute.",
|
"Field which contains members of a group. The value of this field is matched against User membership attribute.",
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
|
<ak-form-element-horizontal
|
||||||
|
label=${msg("User membership attribute")}
|
||||||
|
?required=${true}
|
||||||
|
name="userMembershipAttribute"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value="${this.instance?.userMembershipAttribute || "distinguishedName"}"
|
||||||
|
class="pf-c-form-control"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<p class="pf-c-form__helper-text">
|
||||||
|
${msg("Attribute which matches the value of Group membership field.")}
|
||||||
|
</p>
|
||||||
|
</ak-form-element-horizontal>
|
||||||
<ak-form-element-horizontal name="lookupGroupsFromUser">
|
<ak-form-element-horizontal name="lookupGroupsFromUser">
|
||||||
<label class="pf-c-switch">
|
<label class="pf-c-switch">
|
||||||
<input
|
<input
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user