events: migrate SystemTasks to DB (#8159)

* events: migrate system tasks to save in DB

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* prefill in app startup

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* cleanup api

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* update web

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* use string for status

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix enum

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* save start and end directly in timestamp from default_timer()

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* improve metrics

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* lint

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* rename globally to system task

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* recreate migrations, better denote anonymous user

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* events: lookup actual django app instead of using module path, fallback to module path

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix logger call

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
Jens L
2024-01-24 17:23:03 +01:00
committed by GitHub
parent c0562bf860
commit 96b2a1a9ba
65 changed files with 11564 additions and 12080 deletions

View File

@ -1,134 +0,0 @@
"""Tasks API"""
from importlib import import_module
from django.contrib import messages
from django.http.response import Http404
from django.utils.translation import gettext_lazy as _
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import OpenApiParameter, OpenApiResponse, extend_schema
from rest_framework.decorators import action
from rest_framework.fields import (
CharField,
ChoiceField,
DateTimeField,
ListField,
SerializerMethodField,
)
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.viewsets import ViewSet
from structlog.stdlib import get_logger
from authentik.api.decorators import permission_required
from authentik.core.api.utils import PassiveSerializer
from authentik.events.monitored_tasks import TaskInfo, TaskResultStatus
from authentik.rbac.permissions import HasPermission
LOGGER = get_logger()
class TaskSerializer(PassiveSerializer):
"""Serialize TaskInfo and TaskResult"""
task_name = CharField()
task_description = CharField()
task_finish_timestamp = DateTimeField(source="finish_time")
task_duration = SerializerMethodField()
status = ChoiceField(
source="result.status.name",
choices=[(x.name, x.name) for x in TaskResultStatus],
)
messages = ListField(source="result.messages")
def get_task_duration(self, instance: TaskInfo) -> int:
"""Get the duration a task took to run"""
return max(instance.finish_timestamp - instance.start_timestamp, 0)
def to_representation(self, instance: TaskInfo):
"""When a new version of authentik adds fields to TaskInfo,
the API will fail with an AttributeError, as the classes
are pickled in cache. In that case, just delete the info"""
try:
return super().to_representation(instance)
# pylint: disable=broad-except
except Exception: # pragma: no cover
if isinstance(self.instance, list):
for inst in self.instance:
inst.delete()
else:
self.instance.delete()
return {}
class TaskViewSet(ViewSet):
"""Read-only view set that returns all background tasks"""
permission_classes = [HasPermission("authentik_rbac.view_system_tasks")]
serializer_class = TaskSerializer
@extend_schema(
responses={
200: TaskSerializer(many=False),
404: OpenApiResponse(description="Task not found"),
},
parameters=[
OpenApiParameter(
"id",
type=OpenApiTypes.STR,
location=OpenApiParameter.PATH,
required=True,
),
],
)
def retrieve(self, request: Request, pk=None) -> Response:
"""Get a single system task"""
task = TaskInfo.by_name(pk)
if not task:
raise Http404
return Response(TaskSerializer(task, many=False).data)
@extend_schema(responses={200: TaskSerializer(many=True)})
def list(self, request: Request) -> Response:
"""List system tasks"""
tasks = sorted(TaskInfo.all().values(), key=lambda task: task.task_name)
return Response(TaskSerializer(tasks, many=True).data)
@permission_required(None, ["authentik_rbac.run_system_tasks"])
@extend_schema(
request=OpenApiTypes.NONE,
responses={
204: OpenApiResponse(description="Task retried successfully"),
404: OpenApiResponse(description="Task not found"),
500: OpenApiResponse(description="Failed to retry task"),
},
parameters=[
OpenApiParameter(
"id",
type=OpenApiTypes.STR,
location=OpenApiParameter.PATH,
required=True,
),
],
)
@action(detail=True, methods=["post"])
def retry(self, request: Request, pk=None) -> Response:
"""Retry task"""
task = TaskInfo.by_name(pk)
if not task:
raise Http404
try:
task_module = import_module(task.task_call_module)
task_func = getattr(task_module, task.task_call_func)
LOGGER.debug("Running task", task=task_func)
task_func.delay(*task.task_call_args, **task.task_call_kwargs)
messages.success(
self.request,
_("Successfully re-scheduled Task %(name)s!" % {"name": task.task_name}),
)
return Response(status=204)
except (ImportError, AttributeError): # pragma: no cover
LOGGER.warning("Failed to run task, remove state", task=task)
# if we get an import error, the module path has probably changed
task.delete()
return Response(status=500)

View File

@ -1,7 +1,6 @@
"""admin signals"""
from django.dispatch import receiver
from authentik.admin.api.tasks import TaskInfo
from authentik.admin.apps import GAUGE_WORKERS
from authentik.root.celery import CELERY_APP
from authentik.root.monitoring import monitoring_set
@ -12,10 +11,3 @@ def monitoring_set_workers(sender, **kwargs):
"""Set worker gauge"""
count = len(CELERY_APP.control.ping(timeout=0.5))
GAUGE_WORKERS.set(count)
@receiver(monitoring_set)
def monitoring_set_tasks(sender, **kwargs):
"""Set task gauges"""
for task in TaskInfo.all().values():
task.update_metrics()

View File

@ -11,12 +11,7 @@ from structlog.stdlib import get_logger
from authentik import __version__, get_build_hash
from authentik.admin.apps import PROM_INFO
from authentik.events.models import Event, EventAction, Notification
from authentik.events.monitored_tasks import (
MonitoredTask,
TaskResult,
TaskResultStatus,
prefill_task,
)
from authentik.events.system_tasks import SystemTask, TaskStatus, prefill_task
from authentik.lib.config import CONFIG
from authentik.lib.utils.http import get_http_session
from authentik.root.celery import CELERY_APP
@ -54,13 +49,13 @@ def clear_update_notifications():
notification.delete()
@CELERY_APP.task(bind=True, base=MonitoredTask)
@CELERY_APP.task(bind=True, base=SystemTask)
@prefill_task
def update_latest_version(self: MonitoredTask):
def update_latest_version(self: SystemTask):
"""Update latest version info"""
if CONFIG.get_bool("disable_update_check"):
cache.set(VERSION_CACHE_KEY, "0.0.0", VERSION_CACHE_TIMEOUT)
self.set_status(TaskResult(TaskResultStatus.WARNING, messages=["Version check disabled."]))
self.set_status(TaskStatus.WARNING, "Version check disabled.")
return
try:
response = get_http_session().get(
@ -70,9 +65,7 @@ def update_latest_version(self: MonitoredTask):
data = response.json()
upstream_version = data.get("stable", {}).get("version")
cache.set(VERSION_CACHE_KEY, upstream_version, VERSION_CACHE_TIMEOUT)
self.set_status(
TaskResult(TaskResultStatus.SUCCESSFUL, ["Successfully updated latest Version"])
)
self.set_status(TaskStatus.SUCCESSFUL, "Successfully updated latest Version")
_set_prom_info()
# Check if upstream version is newer than what we're running,
# and if no event exists yet, create one.
@ -89,7 +82,7 @@ def update_latest_version(self: MonitoredTask):
Event.new(EventAction.UPDATE_AVAILABLE, **event_dict).save()
except (RequestException, IndexError) as exc:
cache.set(VERSION_CACHE_KEY, "0.0.0", VERSION_CACHE_TIMEOUT)
self.set_status(TaskResult(TaskResultStatus.ERROR).with_error(exc))
self.set_error(exc)
_set_prom_info()

View File

@ -7,8 +7,6 @@ from django.urls import reverse
from authentik import __version__
from authentik.blueprints.tests import reconcile_app
from authentik.core.models import Group, User
from authentik.core.tasks import clean_expired_models
from authentik.events.monitored_tasks import TaskResultStatus
from authentik.lib.generators import generate_id
@ -23,53 +21,6 @@ class TestAdminAPI(TestCase):
self.group.save()
self.client.force_login(self.user)
def test_tasks(self):
"""Test Task API"""
clean_expired_models.delay()
response = self.client.get(reverse("authentik_api:admin_system_tasks-list"))
self.assertEqual(response.status_code, 200)
body = loads(response.content)
self.assertTrue(any(task["task_name"] == "clean_expired_models" for task in body))
def test_tasks_single(self):
"""Test Task API (read single)"""
clean_expired_models.delay()
response = self.client.get(
reverse(
"authentik_api:admin_system_tasks-detail",
kwargs={"pk": "clean_expired_models"},
)
)
self.assertEqual(response.status_code, 200)
body = loads(response.content)
self.assertEqual(body["status"], TaskResultStatus.SUCCESSFUL.name)
self.assertEqual(body["task_name"], "clean_expired_models")
response = self.client.get(
reverse("authentik_api:admin_system_tasks-detail", kwargs={"pk": "qwerqwer"})
)
self.assertEqual(response.status_code, 404)
def test_tasks_retry(self):
"""Test Task API (retry)"""
clean_expired_models.delay()
response = self.client.post(
reverse(
"authentik_api:admin_system_tasks-retry",
kwargs={"pk": "clean_expired_models"},
)
)
self.assertEqual(response.status_code, 204)
def test_tasks_retry_404(self):
"""Test Task API (retry, 404)"""
response = self.client.post(
reverse(
"authentik_api:admin_system_tasks-retry",
kwargs={"pk": "qwerqewrqrqewrqewr"},
)
)
self.assertEqual(response.status_code, 404)
def test_version(self):
"""Test Version API"""
response = self.client.get(reverse("authentik_api:admin_version"))

View File

@ -4,12 +4,10 @@ from django.urls import path
from authentik.admin.api.meta import AppsViewSet, ModelViewSet
from authentik.admin.api.metrics import AdministrationMetricsViewSet
from authentik.admin.api.system import SystemView
from authentik.admin.api.tasks import TaskViewSet
from authentik.admin.api.version import VersionView
from authentik.admin.api.workers import WorkerView
api_urlpatterns = [
("admin/system_tasks", TaskViewSet, "admin_system_tasks"),
("admin/apps", AppsViewSet, "apps"),
("admin/models", ModelViewSet, "models"),
path(

View File

@ -11,14 +11,14 @@ from structlog.stdlib import BoundLogger, get_logger
class ManagedAppConfig(AppConfig):
"""Basic reconciliation logic for apps"""
_logger: BoundLogger
logger: BoundLogger
RECONCILE_GLOBAL_PREFIX: str = "reconcile_global_"
RECONCILE_TENANT_PREFIX: str = "reconcile_tenant_"
def __init__(self, app_name: str, *args, **kwargs) -> None:
super().__init__(app_name, *args, **kwargs)
self._logger = get_logger().bind(app_name=app_name)
self.logger = get_logger().bind(app_name=app_name)
def ready(self) -> None:
self.reconcile_global()
@ -38,11 +38,11 @@ class ManagedAppConfig(AppConfig):
continue
name = meth_name.replace(prefix, "")
try:
self._logger.debug("Starting reconciler", name=name)
self.logger.debug("Starting reconciler", name=name)
meth()
self._logger.debug("Successfully reconciled", name=name)
self.logger.debug("Successfully reconciled", name=name)
except (DatabaseError, ProgrammingError, InternalError) as exc:
self._logger.warning("Failed to run reconcile", name=name, exc=exc)
self.logger.warning("Failed to run reconcile", name=name, exc=exc)
def reconcile_tenant(self) -> None:
"""reconcile ourselves for tenanted methods"""
@ -51,7 +51,7 @@ class ManagedAppConfig(AppConfig):
try:
tenants = list(Tenant.objects.filter(ready=True))
except (DatabaseError, ProgrammingError, InternalError) as exc:
self._logger.debug("Failed to get tenants to run reconcile", exc=exc)
self.logger.debug("Failed to get tenants to run reconcile", exc=exc)
return
for tenant in tenants:
with tenant:

View File

@ -14,6 +14,7 @@ from django.db.models import Model
from django.db.models.query_utils import Q
from django.db.transaction import atomic
from django.db.utils import IntegrityError
from guardian.models import UserObjectPermission
from rest_framework.exceptions import ValidationError
from rest_framework.serializers import BaseSerializer, Serializer
from structlog.stdlib import BoundLogger, get_logger
@ -38,12 +39,16 @@ from authentik.core.models import (
UserSourceConnection,
)
from authentik.enterprise.models import LicenseKey, LicenseUsage
from authentik.enterprise.providers.rac.models import ConnectionToken
from authentik.events.models import SystemTask
from authentik.events.utils import cleanse_dict
from authentik.flows.models import FlowToken, Stage
from authentik.lib.models import SerializerModel
from authentik.lib.sentry import SentryIgnoredException
from authentik.outposts.models import OutpostServiceConnection
from authentik.policies.models import Policy, PolicyBindingModel
from authentik.policies.reputation.models import Reputation
from authentik.providers.oauth2.models import AccessToken, AuthorizationCode, RefreshToken
from authentik.providers.scim.models import SCIMGroup, SCIMUser
from authentik.tenants.models import Tenant
@ -65,6 +70,7 @@ def excluded_models() -> list[type[Model]]:
DjangoGroup,
ContentType,
Permission,
UserObjectPermission,
# Base classes
Provider,
Source,
@ -82,6 +88,12 @@ def excluded_models() -> list[type[Model]]:
SCIMGroup,
SCIMUser,
Tenant,
SystemTask,
ConnectionToken,
AuthorizationCode,
AccessToken,
RefreshToken,
Reputation,
)

View File

@ -29,12 +29,8 @@ from authentik.blueprints.v1.common import BlueprintLoader, BlueprintMetadata, E
from authentik.blueprints.v1.importer import Importer
from authentik.blueprints.v1.labels import LABEL_AUTHENTIK_INSTANTIATE
from authentik.blueprints.v1.oci import OCI_PREFIX
from authentik.events.monitored_tasks import (
MonitoredTask,
TaskResult,
TaskResultStatus,
prefill_task,
)
from authentik.events.models import TaskStatus
from authentik.events.system_tasks import SystemTask, prefill_task
from authentik.events.utils import sanitize_dict
from authentik.lib.config import CONFIG
from authentik.root.celery import CELERY_APP
@ -134,10 +130,10 @@ def blueprints_find() -> list[BlueprintFile]:
@CELERY_APP.task(
throws=(DatabaseError, ProgrammingError, InternalError), base=MonitoredTask, bind=True
throws=(DatabaseError, ProgrammingError, InternalError), base=SystemTask, bind=True
)
@prefill_task
def blueprints_discovery(self: MonitoredTask, path: Optional[str] = None):
def blueprints_discovery(self: SystemTask, path: Optional[str] = None):
"""Find blueprints and check if they need to be created in the database"""
count = 0
for blueprint in blueprints_find():
@ -146,10 +142,7 @@ def blueprints_discovery(self: MonitoredTask, path: Optional[str] = None):
check_blueprint_v1_file(blueprint)
count += 1
self.set_status(
TaskResult(
TaskResultStatus.SUCCESSFUL,
messages=[_("Successfully imported %(count)d files." % {"count": count})],
)
TaskStatus.SUCCESSFUL, _("Successfully imported %(count)d files." % {"count": count})
)
@ -182,9 +175,9 @@ def check_blueprint_v1_file(blueprint: BlueprintFile):
@CELERY_APP.task(
bind=True,
base=MonitoredTask,
base=SystemTask,
)
def apply_blueprint(self: MonitoredTask, instance_pk: str):
def apply_blueprint(self: SystemTask, instance_pk: str):
"""Apply single blueprint"""
self.save_on_success = False
instance: Optional[BlueprintInstance] = None
@ -202,18 +195,18 @@ def apply_blueprint(self: MonitoredTask, instance_pk: str):
if not valid:
instance.status = BlueprintInstanceStatus.ERROR
instance.save()
self.set_status(TaskResult(TaskResultStatus.ERROR, [x["event"] for x in logs]))
self.set_status(TaskStatus.ERROR, *[x["event"] for x in logs])
return
applied = importer.apply()
if not applied:
instance.status = BlueprintInstanceStatus.ERROR
instance.save()
self.set_status(TaskResult(TaskResultStatus.ERROR, "Failed to apply"))
self.set_status(TaskStatus.ERROR, "Failed to apply")
return
instance.status = BlueprintInstanceStatus.SUCCESSFUL
instance.last_applied_hash = file_hash
instance.last_applied = now()
self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL))
self.set_status(TaskStatus.SUCCESSFUL)
except (
DatabaseError,
ProgrammingError,
@ -224,7 +217,7 @@ def apply_blueprint(self: MonitoredTask, instance_pk: str):
) as exc:
if instance:
instance.status = BlueprintInstanceStatus.ERROR
self.set_status(TaskResult(TaskResultStatus.ERROR).with_error(exc))
self.set_error(exc)
finally:
if instance:
instance.save()

View File

@ -31,7 +31,7 @@ class UsedBySerializer(PassiveSerializer):
model_name = CharField()
pk = CharField()
name = CharField()
action = ChoiceField(choices=[(x.name, x.name) for x in DeleteAction])
action = ChoiceField(choices=[(x.value, x.name) for x in DeleteAction])
def get_delete_action(manager: Manager) -> str:

View File

@ -13,20 +13,15 @@ from authentik.core.models import (
ExpiringModel,
User,
)
from authentik.events.monitored_tasks import (
MonitoredTask,
TaskResult,
TaskResultStatus,
prefill_task,
)
from authentik.events.system_tasks import SystemTask, TaskStatus, prefill_task
from authentik.root.celery import CELERY_APP
LOGGER = get_logger()
@CELERY_APP.task(bind=True, base=MonitoredTask)
@CELERY_APP.task(bind=True, base=SystemTask)
@prefill_task
def clean_expired_models(self: MonitoredTask):
def clean_expired_models(self: SystemTask):
"""Remove expired objects"""
messages = []
for cls in ExpiringModel.__subclasses__():
@ -54,12 +49,12 @@ def clean_expired_models(self: MonitoredTask):
amount += 1
LOGGER.debug("Expired sessions", model=AuthenticatedSession, amount=amount)
messages.append(f"Expired {amount} {AuthenticatedSession._meta.verbose_name_plural}")
self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL, messages))
self.set_status(TaskStatus.SUCCESSFUL, *messages)
@CELERY_APP.task(bind=True, base=MonitoredTask)
@CELERY_APP.task(bind=True, base=SystemTask)
@prefill_task
def clean_temporary_users(self: MonitoredTask):
def clean_temporary_users(self: SystemTask):
"""Remove temporary users created by SAML Sources"""
_now = datetime.now()
messages = []
@ -75,4 +70,4 @@ def clean_temporary_users(self: MonitoredTask):
user.delete()
deleted_users += 1
messages.append(f"Successfully deleted {deleted_users} users.")
self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL, messages))
self.set_status(TaskStatus.SUCCESSFUL, *messages)

View File

@ -9,12 +9,8 @@ from django.utils.translation import gettext_lazy as _
from structlog.stdlib import get_logger
from authentik.crypto.models import CertificateKeyPair
from authentik.events.monitored_tasks import (
MonitoredTask,
TaskResult,
TaskResultStatus,
prefill_task,
)
from authentik.events.models import TaskStatus
from authentik.events.system_tasks import SystemTask, prefill_task
from authentik.lib.config import CONFIG
from authentik.root.celery import CELERY_APP
@ -39,9 +35,9 @@ def ensure_certificate_valid(body: str):
return body
@CELERY_APP.task(bind=True, base=MonitoredTask)
@CELERY_APP.task(bind=True, base=SystemTask)
@prefill_task
def certificate_discovery(self: MonitoredTask):
def certificate_discovery(self: SystemTask):
"""Discover, import and update certificates from the filesystem"""
certs = {}
private_keys = {}
@ -88,8 +84,5 @@ def certificate_discovery(self: MonitoredTask):
if dirty:
cert.save()
self.set_status(
TaskResult(
TaskResultStatus.SUCCESSFUL,
messages=[_("Successfully imported %(count)d files." % {"count": discovered})],
)
TaskStatus.SUCCESSFUL, _("Successfully imported %(count)d files." % {"count": discovered})
)

View File

@ -0,0 +1,107 @@
"""Tasks API"""
from datetime import datetime, timezone
from importlib import import_module
from django.contrib import messages
from django.utils.translation import gettext_lazy as _
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import OpenApiResponse, extend_schema
from rest_framework.decorators import action
from rest_framework.fields import CharField, ChoiceField, ListField, SerializerMethodField
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ReadOnlyModelViewSet
from structlog.stdlib import get_logger
from authentik.api.decorators import permission_required
from authentik.events.models import SystemTask, TaskStatus
LOGGER = get_logger()
class SystemTaskSerializer(ModelSerializer):
"""Serialize TaskInfo and TaskResult"""
name = CharField()
full_name = SerializerMethodField()
uid = CharField(required=False)
description = CharField()
start_timestamp = SerializerMethodField()
finish_timestamp = SerializerMethodField()
duration = SerializerMethodField()
status = ChoiceField(choices=[(x.value, x.name) for x in TaskStatus])
messages = ListField(child=CharField())
def get_full_name(self, instance: SystemTask) -> str:
"""Get full name with UID"""
if instance.uid:
return f"{instance.name}:{instance.uid}"
return instance.name
def get_start_timestamp(self, instance: SystemTask) -> datetime:
"""Timestamp when the task started"""
return datetime.fromtimestamp(instance.start_timestamp, tz=timezone.utc)
def get_finish_timestamp(self, instance: SystemTask) -> datetime:
"""Timestamp when the task finished"""
return datetime.fromtimestamp(instance.finish_timestamp, tz=timezone.utc)
def get_duration(self, instance: SystemTask) -> float:
"""Get the duration a task took to run"""
return max(instance.finish_timestamp - instance.start_timestamp, 0)
class Meta:
model = SystemTask
fields = [
"uuid",
"name",
"full_name",
"uid",
"description",
"start_timestamp",
"finish_timestamp",
"duration",
"status",
"messages",
]
class SystemTaskViewSet(ReadOnlyModelViewSet):
"""Read-only view set that returns all background tasks"""
queryset = SystemTask.objects.all()
serializer_class = SystemTaskSerializer
filterset_fields = ["name", "uid", "status"]
ordering = ["name", "uid", "status"]
search_fields = ["name", "description", "uid", "status"]
@permission_required(None, ["authentik_events.run_task"])
@extend_schema(
request=OpenApiTypes.NONE,
responses={
204: OpenApiResponse(description="Task retried successfully"),
404: OpenApiResponse(description="Task not found"),
500: OpenApiResponse(description="Failed to retry task"),
},
)
@action(detail=True, methods=["post"])
def run(self, request: Request, pk=None) -> Response:
"""Run task"""
task: SystemTask = self.get_object()
try:
task_module = import_module(task.task_call_module)
task_func = getattr(task_module, task.task_call_func)
LOGGER.info("Running task", task=task_func)
task_func.delay(*task.task_call_args, **task.task_call_kwargs)
messages.success(
self.request,
_("Successfully started task %(name)s." % {"name": task.name}),
)
return Response(status=204)
except (ImportError, AttributeError) as exc: # pragma: no cover
LOGGER.warning("Failed to run task, remove state", task=task.name, exc=exc)
# if we get an import error, the module path has probably changed
task.delete()
return Response(status=500)

View File

@ -1,15 +1,26 @@
"""authentik events app"""
from prometheus_client import Gauge
from prometheus_client import Gauge, Histogram
from authentik.blueprints.apps import ManagedAppConfig
from authentik.lib.config import CONFIG, ENV_PREFIX
# TODO: Deprecated metric - remove in 2024.2 or later
GAUGE_TASKS = Gauge(
"authentik_system_tasks",
"System tasks and their status",
["tenant", "task_name", "task_uid", "status"],
)
SYSTEM_TASK_TIME = Histogram(
"authentik_system_tasks_time_seconds",
"Runtime of system tasks",
)
SYSTEM_TASK_STATUS = Gauge(
"authentik_system_tasks_status",
"System task status",
["task_name", "task_uid", "status"],
)
class AuthentikEventsConfig(ManagedAppConfig):
"""authentik events app"""
@ -43,3 +54,14 @@ class AuthentikEventsConfig(ManagedAppConfig):
replacement_env=replace_env,
message=msg,
).save()
def reconcile_prefill_tasks(self):
"""Prefill tasks"""
from authentik.events.models import SystemTask
from authentik.events.system_tasks import _prefill_tasks
for task in _prefill_tasks:
if SystemTask.objects.filter(name=task.name).exists():
continue
task.save()
self.logger.debug("prefilled task", task_name=task.name)

View File

@ -9,19 +9,14 @@ from django.core.exceptions import SuspiciousOperation
from django.db.models import Model
from django.db.models.signals import m2m_changed, post_save, pre_delete
from django.http import HttpRequest, HttpResponse
from guardian.models import UserObjectPermission
from structlog.stdlib import BoundLogger, get_logger
from authentik.blueprints.v1.importer import excluded_models
from authentik.core.models import Group, User
from authentik.enterprise.providers.rac.models import ConnectionToken
from authentik.events.models import Event, EventAction, Notification
from authentik.events.utils import model_to_dict
from authentik.lib.sentry import before_send
from authentik.lib.utils.errors import exception_to_string
from authentik.policies.reputation.models import Reputation
from authentik.providers.oauth2.models import AccessToken, AuthorizationCode, RefreshToken
from authentik.providers.scim.models import SCIMGroup, SCIMUser
from authentik.stages.authenticator_static.models import StaticToken
IGNORED_MODELS = tuple(
@ -29,16 +24,8 @@ IGNORED_MODELS = tuple(
+ (
Event,
Notification,
UserObjectPermission,
StaticToken,
Session,
AuthorizationCode,
AccessToken,
RefreshToken,
SCIMUser,
SCIMGroup,
Reputation,
ConnectionToken,
)
)

View File

@ -0,0 +1,60 @@
# Generated by Django 5.0.1 on 2024-01-24 12:48
import uuid
from django.db import migrations, models
import authentik.core.models
class Migration(migrations.Migration):
dependencies = [
("authentik_events", "0003_rename_tenant_event_brand"),
]
operations = [
migrations.CreateModel(
name="SystemTask",
fields=[
(
"expires",
models.DateTimeField(default=authentik.core.models.default_token_duration),
),
("expiring", models.BooleanField(default=True)),
(
"uuid",
models.UUIDField(
default=uuid.uuid4, editable=False, primary_key=True, serialize=False
),
),
("name", models.TextField()),
("uid", models.TextField(null=True)),
("start_timestamp", models.FloatField()),
("finish_timestamp", models.FloatField()),
(
"status",
models.TextField(
choices=[
("unknown", "Unknown"),
("successful", "Successful"),
("warning", "Warning"),
("error", "Error"),
]
),
),
("description", models.TextField(null=True)),
("messages", models.JSONField()),
("task_call_module", models.TextField()),
("task_call_func", models.TextField()),
("task_call_args", models.JSONField(default=list)),
("task_call_kwargs", models.JSONField(default=dict)),
],
options={
"verbose_name": "System Task",
"verbose_name_plural": "System Tasks",
"permissions": [("run_task", "Run task")],
"default_permissions": ["view"],
"unique_together": {("name", "uid")},
},
),
]

View File

@ -2,11 +2,14 @@
import time
from collections import Counter
from datetime import timedelta
from difflib import get_close_matches
from functools import lru_cache
from inspect import currentframe
from smtplib import SMTPException
from typing import TYPE_CHECKING, Optional
from typing import Optional
from uuid import uuid4
from django.apps import apps
from django.db import models
from django.db.models import Count, ExpressionWrapper, F
from django.db.models.fields import DurationField
@ -18,6 +21,7 @@ from django.http.request import QueryDict
from django.utils.timezone import now
from django.utils.translation import gettext as _
from requests import RequestException
from rest_framework.serializers import Serializer
from structlog.stdlib import get_logger
from authentik import get_full_version
@ -28,6 +32,7 @@ from authentik.core.middleware import (
SESSION_KEY_IMPERSONATE_USER,
)
from authentik.core.models import ExpiringModel, Group, PropertyMapping, User
from authentik.events.apps import GAUGE_TASKS, SYSTEM_TASK_STATUS, SYSTEM_TASK_TIME
from authentik.events.context_processors.base import get_context_processors
from authentik.events.utils import (
cleanse_dict,
@ -46,8 +51,6 @@ from authentik.stages.email.utils import TemplateEmailMessage
from authentik.tenants.models import Tenant
LOGGER = get_logger()
if TYPE_CHECKING:
from rest_framework.serializers import Serializer
def default_event_duration():
@ -61,6 +64,12 @@ def default_brand():
return sanitize_dict(model_to_dict(DEFAULT_BRAND))
@lru_cache()
def django_app_names() -> list[str]:
"""Get a cached list of all django apps' names (not labels)"""
return [x.name for x in apps.app_configs.values()]
class NotificationTransportError(SentryIgnoredException):
"""Error raised when a notification fails to be delivered"""
@ -198,6 +207,11 @@ class Event(SerializerModel, ExpiringModel):
current = currentframe()
parent = current.f_back
app = parent.f_globals["__name__"]
# Attempt to match the calling module to the django app it belongs to
# if we can't find a match, keep the module name
django_apps = get_close_matches(app, django_app_names(), n=1)
if len(django_apps) > 0:
app = django_apps[0]
cleaned_kwargs = cleanse_dict(sanitize_dict(kwargs))
event = Event(action=action, app=app, context=cleaned_kwargs)
return event
@ -270,7 +284,7 @@ class Event(SerializerModel, ExpiringModel):
super().save(*args, **kwargs)
@property
def serializer(self) -> "Serializer":
def serializer(self) -> type[Serializer]:
from authentik.events.api.events import EventSerializer
return EventSerializer
@ -478,7 +492,7 @@ class NotificationTransport(SerializerModel):
raise NotificationTransportError(exc) from exc
@property
def serializer(self) -> "Serializer":
def serializer(self) -> type[Serializer]:
from authentik.events.api.notification_transports import NotificationTransportSerializer
return NotificationTransportSerializer
@ -511,7 +525,7 @@ class Notification(SerializerModel):
user = models.ForeignKey(User, on_delete=models.CASCADE)
@property
def serializer(self) -> "Serializer":
def serializer(self) -> type[Serializer]:
from authentik.events.api.notifications import NotificationSerializer
return NotificationSerializer
@ -554,7 +568,7 @@ class NotificationRule(SerializerModel, PolicyBindingModel):
)
@property
def serializer(self) -> "Serializer":
def serializer(self) -> type[Serializer]:
from authentik.events.api.notification_rules import NotificationRuleSerializer
return NotificationRuleSerializer
@ -575,7 +589,7 @@ class NotificationWebhookMapping(PropertyMapping):
return "ak-property-mapping-notification-form"
@property
def serializer(self) -> type["Serializer"]:
def serializer(self) -> type[type[Serializer]]:
from authentik.events.api.notification_mappings import NotificationWebhookMappingSerializer
return NotificationWebhookMappingSerializer
@ -586,3 +600,66 @@ class NotificationWebhookMapping(PropertyMapping):
class Meta:
verbose_name = _("Webhook Mapping")
verbose_name_plural = _("Webhook Mappings")
class TaskStatus(models.TextChoices):
"""Possible states of tasks"""
UNKNOWN = "unknown"
SUCCESSFUL = "successful"
WARNING = "warning"
ERROR = "error"
class SystemTask(SerializerModel, ExpiringModel):
"""Info about a system task running in the background along with details to restart the task"""
uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
name = models.TextField()
uid = models.TextField(null=True)
start_timestamp = models.FloatField()
finish_timestamp = models.FloatField()
status = models.TextField(choices=TaskStatus.choices)
description = models.TextField(null=True)
messages = models.JSONField()
task_call_module = models.TextField()
task_call_func = models.TextField()
task_call_args = models.JSONField(default=list)
task_call_kwargs = models.JSONField(default=dict)
@property
def serializer(self) -> type[Serializer]:
from authentik.events.api.tasks import SystemTaskSerializer
return SystemTaskSerializer
def update_metrics(self):
"""Update prometheus metrics"""
duration = max(self.finish_timestamp - self.start_timestamp, 0)
# TODO: Deprecated metric - remove in 2024.2 or later
GAUGE_TASKS.labels(
task_name=self.name,
task_uid=self.uid or "",
status=self.status.lower(),
).set(duration)
SYSTEM_TASK_TIME.observe(duration)
SYSTEM_TASK_STATUS.labels(
task_name=self.name,
task_uid=self.uid or "",
status=self.status.lower(),
).inc()
def __str__(self) -> str:
return f"System Task {self.name}"
class Meta:
unique_together = (("name", "uid"),)
# Remove "add", "change" and "delete" permissions as those are not used
default_permissions = ["view"]
permissions = [("run_task", _("Run task"))]
verbose_name = _("System Task")
verbose_name_plural = _("System Tasks")

View File

@ -1,216 +0,0 @@
"""Monitored tasks"""
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from timeit import default_timer
from typing import Any, Optional
from django.core.cache import cache
from django.db import connection
from django.utils.translation import gettext_lazy as _
from structlog.stdlib import get_logger
from tenant_schemas_celery.task import TenantTask
from authentik.events.apps import GAUGE_TASKS
from authentik.events.models import Event, EventAction
from authentik.lib.utils.errors import exception_to_string
LOGGER = get_logger()
CACHE_KEY_PREFIX = "goauthentik.io/events/tasks/"
class TaskResultStatus(Enum):
"""Possible states of tasks"""
SUCCESSFUL = 1
WARNING = 2
ERROR = 4
UNKNOWN = 8
@dataclass
class TaskResult:
"""Result of a task run, this class is created by the task itself
and used by self.set_status"""
status: TaskResultStatus
messages: list[str] = field(default_factory=list)
# Optional UID used in cache for tasks that run in different instances
uid: Optional[str] = field(default=None)
def with_error(self, exc: Exception) -> "TaskResult":
"""Since errors might not always be pickle-able, set the traceback"""
# TODO: Mark exception somehow so that is rendered as <pre> in frontend
self.messages.append(exception_to_string(exc))
return self
@dataclass
class TaskInfo:
"""Info about a task run"""
task_name: str
start_timestamp: float
finish_timestamp: float
finish_time: datetime
result: TaskResult
task_call_module: str
task_call_func: str
task_call_args: list[Any] = field(default_factory=list)
task_call_kwargs: dict[str, Any] = field(default_factory=dict)
task_description: Optional[str] = field(default=None)
@staticmethod
def all() -> dict[str, "TaskInfo"]:
"""Get all TaskInfo objects"""
return cache.get_many(cache.keys(CACHE_KEY_PREFIX + "*"))
@staticmethod
def by_name(name: str) -> Optional["TaskInfo"] | Optional[list["TaskInfo"]]:
"""Get TaskInfo Object by name"""
if "*" in name:
return cache.get_many(cache.keys(CACHE_KEY_PREFIX + name)).values()
return cache.get(CACHE_KEY_PREFIX + name, None)
@property
def full_name(self) -> str:
"""Get the full cache key with task name and UID"""
key = CACHE_KEY_PREFIX + self.task_name
if self.result.uid:
uid_suffix = f":{self.result.uid}"
key += uid_suffix
if not self.task_name.endswith(uid_suffix):
self.task_name += uid_suffix
return key
def delete(self):
"""Delete task info from cache"""
return cache.delete(self.full_name)
def update_metrics(self):
"""Update prometheus metrics"""
start = default_timer()
if hasattr(self, "start_timestamp"):
start = self.start_timestamp
try:
duration = max(self.finish_timestamp - start, 0)
except TypeError:
duration = 0
GAUGE_TASKS.labels(
tenant=connection.schema_name,
task_name=self.task_name.split(":")[0],
task_uid=self.result.uid or "",
status=self.result.status.name.lower(),
).set(duration)
def save(self, timeout_hours=6):
"""Save task into cache"""
self.update_metrics()
cache.set(self.full_name, self, timeout=timeout_hours * 60 * 60)
class MonitoredTask(TenantTask):
"""Task which can save its state to the cache"""
# For tasks that should only be listed if they failed, set this to False
save_on_success: bool
_result: Optional[TaskResult]
_uid: Optional[str]
start: Optional[float] = None
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.save_on_success = True
self._uid = None
self._result = None
self.result_timeout_hours = 6
def set_uid(self, uid: str):
"""Set UID, so in the case of an unexpected error its saved correctly"""
self._uid = uid
def set_status(self, result: TaskResult):
"""Set result for current run, will overwrite previous result."""
self._result = result
def before_start(self, task_id, args, kwargs):
self.start = default_timer()
return super().before_start(task_id, args, kwargs)
# pylint: disable=too-many-arguments
def after_return(self, status, retval, task_id, args: list[Any], kwargs: dict[str, Any], einfo):
super().after_return(status, retval, task_id, args, kwargs, einfo=einfo)
if not self._result:
return
if not self._result.uid:
self._result.uid = self._uid
info = TaskInfo(
task_name=self.__name__,
task_description=self.__doc__,
start_timestamp=self.start or default_timer(),
finish_timestamp=default_timer(),
finish_time=datetime.now(),
result=self._result,
task_call_module=self.__module__,
task_call_func=self.__name__,
task_call_args=args,
task_call_kwargs=kwargs,
)
if self._result.status == TaskResultStatus.SUCCESSFUL and not self.save_on_success:
info.delete()
return
info.save(self.result_timeout_hours)
# pylint: disable=too-many-arguments
def on_failure(self, exc, task_id, args, kwargs, einfo):
super().on_failure(exc, task_id, args, kwargs, einfo=einfo)
if not self._result:
self._result = TaskResult(status=TaskResultStatus.ERROR, messages=[str(exc)])
if not self._result.uid:
self._result.uid = self._uid
TaskInfo(
task_name=self.__name__,
task_description=self.__doc__,
start_timestamp=self.start or default_timer(),
finish_timestamp=default_timer(),
finish_time=datetime.now(),
result=self._result,
task_call_module=self.__module__,
task_call_func=self.__name__,
task_call_args=args,
task_call_kwargs=kwargs,
).save(self.result_timeout_hours)
Event.new(
EventAction.SYSTEM_TASK_EXCEPTION,
message=f"Task {self.__name__} encountered an error: {exception_to_string(exc)}",
).save()
def run(self, *args, **kwargs):
raise NotImplementedError
def prefill_task(func):
"""Ensure a task's details are always in cache, so it can always be triggered via API"""
status = TaskInfo.by_name(func.__name__)
if status:
return func
TaskInfo(
task_name=func.__name__,
task_description=func.__doc__,
result=TaskResult(TaskResultStatus.UNKNOWN, messages=[_("Task has not been run yet.")]),
task_call_module=func.__module__,
task_call_func=func.__name__,
# We don't have real values for these attributes but they cannot be null
start_timestamp=0,
finish_timestamp=0,
finish_time=datetime.now(),
).save(86400)
LOGGER.debug("prefilled task", task_name=func.__name__)
return func

View File

@ -8,11 +8,13 @@ from django.http import HttpRequest
from authentik.core.models import User
from authentik.core.signals import login_failed, password_changed
from authentik.events.models import Event, EventAction
from authentik.events.apps import SYSTEM_TASK_STATUS
from authentik.events.models import Event, EventAction, SystemTask
from authentik.events.tasks import event_notification_handler, gdpr_cleanup
from authentik.flows.models import Stage
from authentik.flows.planner import PLAN_CONTEXT_SOURCE, FlowPlan
from authentik.flows.views.executor import SESSION_KEY_PLAN
from authentik.root.monitoring import monitoring_set
from authentik.stages.invitation.models import Invitation
from authentik.stages.invitation.signals import invitation_used
from authentik.stages.password.stage import PLAN_CONTEXT_METHOD, PLAN_CONTEXT_METHOD_ARGS
@ -100,3 +102,11 @@ def event_user_pre_delete_cleanup(sender, instance: User, **_):
"""If gdpr_compliance is enabled, remove all the user's events"""
if get_current_tenant().gdpr_compliance:
gdpr_cleanup.delay(instance.pk)
@receiver(monitoring_set)
def monitoring_system_task(sender, **_):
"""Update metrics when task is saved"""
SYSTEM_TASK_STATUS.clear()
for task in SystemTask.objects.all():
task.update_metrics()

View File

@ -0,0 +1,135 @@
"""Monitored tasks"""
from datetime import timedelta
from timeit import default_timer
from typing import Any, Optional
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
from structlog.stdlib import get_logger
from tenant_schemas_celery.task import TenantTask
from authentik.events.models import Event, EventAction
from authentik.events.models import SystemTask as DBSystemTask
from authentik.events.models import TaskStatus
from authentik.events.utils import sanitize_item
from authentik.lib.utils.errors import exception_to_string
LOGGER = get_logger()
class SystemTask(TenantTask):
"""Task which can save its state to the cache"""
# For tasks that should only be listed if they failed, set this to False
save_on_success: bool
_status: Optional[TaskStatus]
_messages: list[str]
_uid: Optional[str]
_start: Optional[float] = None
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.save_on_success = True
self._uid = None
self._status = None
self._messages = []
self.result_timeout_hours = 6
def set_uid(self, uid: str):
"""Set UID, so in the case of an unexpected error its saved correctly"""
self._uid = uid
def set_status(self, status: TaskStatus, *messages: str):
"""Set result for current run, will overwrite previous result."""
self._status = status
self._messages = messages
def set_error(self, exception: Exception):
"""Set result to error and save exception"""
self._status = TaskStatus.ERROR
self._messages = [exception_to_string(exception)]
def before_start(self, task_id, args, kwargs):
self._start = default_timer()
return super().before_start(task_id, args, kwargs)
# pylint: disable=too-many-arguments
def after_return(self, status, retval, task_id, args: list[Any], kwargs: dict[str, Any], einfo):
super().after_return(status, retval, task_id, args, kwargs, einfo=einfo)
if not self._status:
return
if self._status == TaskStatus.SUCCESSFUL and not self.save_on_success:
DBSystemTask.objects.filter(
name=self.__name__,
uid=self._uid,
).delete()
return
DBSystemTask.objects.update_or_create(
name=self.__name__,
uid=self._uid,
defaults={
"description": self.__doc__,
"start_timestamp": self._start or default_timer(),
"finish_timestamp": default_timer(),
"task_call_module": self.__module__,
"task_call_func": self.__name__,
"task_call_args": args,
"task_call_kwargs": kwargs,
"status": self._status,
"messages": sanitize_item(self._messages),
"expires": now() + timedelta(hours=self.result_timeout_hours),
"expiring": True,
},
)
# pylint: disable=too-many-arguments
def on_failure(self, exc, task_id, args, kwargs, einfo):
super().on_failure(exc, task_id, args, kwargs, einfo=einfo)
if not self._status:
self._status = TaskStatus.ERROR
self._messages = exception_to_string(exc)
DBSystemTask.objects.update_or_create(
name=self.__name__,
uid=self._uid,
defaults={
"description": self.__doc__,
"start_timestamp": self._start or default_timer(),
"finish_timestamp": default_timer(),
"task_call_module": self.__module__,
"task_call_func": self.__name__,
"task_call_args": args,
"task_call_kwargs": kwargs,
"status": self._status,
"messages": sanitize_item(self._messages),
"expires": now() + timedelta(hours=self.result_timeout_hours),
"expiring": True,
},
)
Event.new(
EventAction.SYSTEM_TASK_EXCEPTION,
message=f"Task {self.__name__} encountered an error: {exception_to_string(exc)}",
).save()
def run(self, *args, **kwargs):
raise NotImplementedError
def prefill_task(func):
"""Ensure a task's details are always in cache, so it can always be triggered via API"""
_prefill_tasks.append(
DBSystemTask(
name=func.__name__,
description=func.__doc__,
status=TaskStatus.UNKNOWN,
messages=sanitize_item([_("Task has not been run yet.")]),
task_call_module=func.__module__,
task_call_func=func.__name__,
expiring=False,
)
)
return func
_prefill_tasks = []

View File

@ -13,13 +13,9 @@ from authentik.events.models import (
NotificationRule,
NotificationTransport,
NotificationTransportError,
TaskStatus,
)
from authentik.events.monitored_tasks import (
MonitoredTask,
TaskResult,
TaskResultStatus,
prefill_task,
)
from authentik.events.system_tasks import SystemTask, prefill_task
from authentik.policies.engine import PolicyEngine
from authentik.policies.models import PolicyBinding, PolicyEngineMode
from authentik.root.celery import CELERY_APP
@ -99,10 +95,10 @@ def event_trigger_handler(event_uuid: str, trigger_name: str):
bind=True,
autoretry_for=(NotificationTransportError,),
retry_backoff=True,
base=MonitoredTask,
base=SystemTask,
)
def notification_transport(
self: MonitoredTask, transport_pk: int, event_pk: str, user_pk: int, trigger_pk: str
self: SystemTask, transport_pk: int, event_pk: str, user_pk: int, trigger_pk: str
):
"""Send notification over specified transport"""
self.save_on_success = False
@ -123,9 +119,9 @@ def notification_transport(
if not transport:
return
transport.send(notification)
self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL))
self.set_status(TaskStatus.SUCCESSFUL)
except (NotificationTransportError, PropertyMappingExpressionException) as exc:
self.set_status(TaskResult(TaskResultStatus.ERROR).with_error(exc))
self.set_error(exc)
raise exc
@ -137,13 +133,13 @@ def gdpr_cleanup(user_pk: int):
events.delete()
@CELERY_APP.task(bind=True, base=MonitoredTask)
@CELERY_APP.task(bind=True, base=SystemTask)
@prefill_task
def notification_cleanup(self: MonitoredTask):
def notification_cleanup(self: SystemTask):
"""Cleanup seen notifications and notifications whose event expired."""
notifications = Notification.objects.filter(Q(event=None) | Q(seen=True))
amount = notifications.count()
for notification in notifications:
notification.delete()
LOGGER.debug("Expired notifications", amount=amount)
self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL, [f"Expired {amount} Notifications"]))
self.set_status(TaskStatus.SUCCESSFUL, f"Expired {amount} Notifications")

View File

@ -1,14 +1,26 @@
"""Test Monitored tasks"""
from django.test import TestCase
from json import loads
from authentik.events.monitored_tasks import MonitoredTask, TaskInfo, TaskResult, TaskResultStatus
from django.urls import reverse
from rest_framework.test import APITestCase
from authentik.core.tasks import clean_expired_models
from authentik.core.tests.utils import create_test_admin_user
from authentik.events.models import SystemTask as DBSystemTask
from authentik.events.models import TaskStatus
from authentik.events.system_tasks import SystemTask
from authentik.lib.generators import generate_id
from authentik.root.celery import CELERY_APP
class TestMonitoredTasks(TestCase):
class TestSystemTasks(APITestCase):
"""Test Monitored tasks"""
def setUp(self):
super().setUp()
self.user = create_test_admin_user()
self.client.force_login(self.user)
def test_failed_successful_remove_state(self):
"""Test that a task with `save_on_success` set to `False` that failed saves
a state, and upon successful completion will delete the state"""
@ -17,27 +29,74 @@ class TestMonitoredTasks(TestCase):
@CELERY_APP.task(
bind=True,
base=MonitoredTask,
base=SystemTask,
)
def test_task(self: MonitoredTask):
def test_task(self: SystemTask):
self.save_on_success = False
self.set_uid(uid)
self.set_status(
TaskResult(TaskResultStatus.ERROR if should_fail else TaskResultStatus.SUCCESSFUL)
)
self.set_status(TaskStatus.ERROR if should_fail else TaskStatus.SUCCESSFUL)
# First test successful run
should_fail = False
test_task.delay().get()
self.assertIsNone(TaskInfo.by_name(f"test_task:{uid}"))
self.assertIsNone(DBSystemTask.objects.filter(name="test_task", uid=uid).first())
# Then test failed
should_fail = True
test_task.delay().get()
info = TaskInfo.by_name(f"test_task:{uid}")
self.assertEqual(info.result.status, TaskResultStatus.ERROR)
task = DBSystemTask.objects.filter(name="test_task", uid=uid).first()
self.assertEqual(task.status, TaskStatus.ERROR)
# Then after that, the state should be removed
should_fail = False
test_task.delay().get()
self.assertIsNone(TaskInfo.by_name(f"test_task:{uid}"))
self.assertIsNone(DBSystemTask.objects.filter(name="test_task", uid=uid).first())
def test_tasks(self):
"""Test Task API"""
clean_expired_models.delay().get()
response = self.client.get(reverse("authentik_api:systemtask-list"))
self.assertEqual(response.status_code, 200)
body = loads(response.content)
self.assertTrue(any(task["name"] == "clean_expired_models" for task in body["results"]))
def test_tasks_single(self):
"""Test Task API (read single)"""
clean_expired_models.delay().get()
task = DBSystemTask.objects.filter(name="clean_expired_models").first()
response = self.client.get(
reverse(
"authentik_api:systemtask-detail",
kwargs={"pk": str(task.pk)},
)
)
self.assertEqual(response.status_code, 200)
body = loads(response.content)
self.assertEqual(body["status"], TaskStatus.SUCCESSFUL.value)
self.assertEqual(body["name"], "clean_expired_models")
response = self.client.get(
reverse("authentik_api:systemtask-detail", kwargs={"pk": "qwerqwer"})
)
self.assertEqual(response.status_code, 404)
def test_tasks_run(self):
"""Test Task API (run)"""
clean_expired_models.delay().get()
task = DBSystemTask.objects.filter(name="clean_expired_models").first()
response = self.client.post(
reverse(
"authentik_api:systemtask-run",
kwargs={"pk": str(task.pk)},
)
)
self.assertEqual(response.status_code, 204)
def test_tasks_run_404(self):
"""Test Task API (run, 404)"""
response = self.client.post(
reverse(
"authentik_api:systemtask-run",
kwargs={"pk": "qwerqewrqrqewrqewr"},
)
)
self.assertEqual(response.status_code, 404)

View File

@ -4,11 +4,13 @@ from authentik.events.api.notification_mappings import NotificationWebhookMappin
from authentik.events.api.notification_rules import NotificationRuleViewSet
from authentik.events.api.notification_transports import NotificationTransportViewSet
from authentik.events.api.notifications import NotificationViewSet
from authentik.events.api.tasks import SystemTaskViewSet
api_urlpatterns = [
("events/events", EventViewSet),
("events/notifications", NotificationViewSet),
("events/transports", NotificationTransportViewSet),
("events/rules", NotificationRuleViewSet),
("events/system_tasks", SystemTaskViewSet),
("propertymappings/notification", NotificationWebhookMappingViewSet),
]

View File

@ -18,6 +18,7 @@ from django.http.request import HttpRequest
from django.utils import timezone
from django.views.debug import SafeExceptionReporterFilter
from geoip2.models import ASN, City
from guardian.conf import settings
from guardian.utils import get_anonymous_user
from authentik.blueprints.v1.common import YAMLTag
@ -84,6 +85,8 @@ def get_user(user: User | AnonymousUser, original_user: Optional[User] = None) -
"pk": user.pk,
"email": user.email,
}
if user.username == settings.ANONYMOUS_USER_NAME:
user_data["is_anonymous"] = True
if original_user:
original_data = get_user(original_user)
original_data["on_behalf_of"] = user_data

View File

@ -19,12 +19,8 @@ from yaml import safe_load
from authentik.enterprise.providers.rac.controllers.docker import RACDockerController
from authentik.enterprise.providers.rac.controllers.kubernetes import RACKubernetesController
from authentik.events.monitored_tasks import (
MonitoredTask,
TaskResult,
TaskResultStatus,
prefill_task,
)
from authentik.events.models import TaskStatus
from authentik.events.system_tasks import SystemTask, prefill_task
from authentik.lib.config import CONFIG
from authentik.lib.utils.reflection import path_to_class
from authentik.outposts.consumer import OUTPOST_GROUP
@ -108,20 +104,18 @@ def outpost_service_connection_state(connection_pk: Any):
@CELERY_APP.task(
bind=True,
base=MonitoredTask,
base=SystemTask,
throws=(DatabaseError, ProgrammingError, InternalError),
)
@prefill_task
def outpost_service_connection_monitor(self: MonitoredTask):
def outpost_service_connection_monitor(self: SystemTask):
"""Regularly check the state of Outpost Service Connections"""
connections = OutpostServiceConnection.objects.all()
for connection in connections.iterator():
outpost_service_connection_state.delay(connection.pk)
self.set_status(
TaskResult(
TaskResultStatus.SUCCESSFUL,
[f"Successfully updated {len(connections)} connections."],
)
TaskStatus.SUCCESSFUL,
f"Successfully updated {len(connections)} connections.",
)
@ -134,9 +128,9 @@ def outpost_controller_all():
outpost_controller.delay(outpost.pk.hex, "up", from_cache=False)
@CELERY_APP.task(bind=True, base=MonitoredTask)
@CELERY_APP.task(bind=True, base=SystemTask)
def outpost_controller(
self: MonitoredTask, outpost_pk: str, action: str = "up", from_cache: bool = False
self: SystemTask, outpost_pk: str, action: str = "up", from_cache: bool = False
):
"""Create/update/monitor/delete the deployment of an Outpost"""
logs = []
@ -161,16 +155,16 @@ def outpost_controller(
LOGGER.debug(log)
LOGGER.debug("-----------------Outpost Controller logs end-------------------")
except (ControllerException, ServiceConnectionInvalid) as exc:
self.set_status(TaskResult(TaskResultStatus.ERROR).with_error(exc))
self.set_error(exc)
else:
if from_cache:
cache.delete(CACHE_KEY_OUTPOST_DOWN % outpost_pk)
self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL, logs))
self.set_status(TaskStatus.SUCCESSFUL, *logs)
@CELERY_APP.task(bind=True, base=MonitoredTask)
@CELERY_APP.task(bind=True, base=SystemTask)
@prefill_task
def outpost_token_ensurer(self: MonitoredTask):
def outpost_token_ensurer(self: SystemTask):
"""Periodically ensure that all Outposts have valid Service Accounts
and Tokens"""
all_outposts = Outpost.objects.all()
@ -178,10 +172,8 @@ def outpost_token_ensurer(self: MonitoredTask):
_ = outpost.token
outpost.build_user_permissions(outpost.user)
self.set_status(
TaskResult(
TaskResultStatus.SUCCESSFUL,
[f"Successfully checked {len(all_outposts)} Outposts."],
)
TaskStatus.SUCCESSFUL,
f"Successfully checked {len(all_outposts)} Outposts.",
)
@ -256,32 +248,32 @@ def _outpost_single_update(outpost: Outpost, layer=None):
@CELERY_APP.task(
base=MonitoredTask,
base=SystemTask,
bind=True,
)
def outpost_connection_discovery(self: MonitoredTask):
def outpost_connection_discovery(self: SystemTask):
"""Checks the local environment and create Service connections."""
status = TaskResult(TaskResultStatus.SUCCESSFUL)
messages = []
if not CONFIG.get_bool("outposts.discover"):
status.messages.append("Outpost integration discovery is disabled")
self.set_status(status)
messages.append("Outpost integration discovery is disabled")
self.set_status(TaskStatus.SUCCESSFUL, *messages)
return
# Explicitly check against token filename, as that's
# only present when the integration is enabled
if Path(SERVICE_TOKEN_FILENAME).exists():
status.messages.append("Detected in-cluster Kubernetes Config")
messages.append("Detected in-cluster Kubernetes Config")
if not KubernetesServiceConnection.objects.filter(local=True).exists():
status.messages.append("Created Service Connection for in-cluster")
messages.append("Created Service Connection for in-cluster")
KubernetesServiceConnection.objects.create(
name="Local Kubernetes Cluster", local=True, kubeconfig={}
)
# For development, check for the existence of a kubeconfig file
kubeconfig_path = Path(KUBE_CONFIG_DEFAULT_LOCATION).expanduser()
if kubeconfig_path.exists():
status.messages.append("Detected kubeconfig")
messages.append("Detected kubeconfig")
kubeconfig_local_name = f"k8s-{gethostname()}"
if not KubernetesServiceConnection.objects.filter(name=kubeconfig_local_name).exists():
status.messages.append("Creating kubeconfig Service Connection")
messages.append("Creating kubeconfig Service Connection")
with kubeconfig_path.open("r", encoding="utf8") as _kubeconfig:
KubernetesServiceConnection.objects.create(
name=kubeconfig_local_name,
@ -290,12 +282,12 @@ def outpost_connection_discovery(self: MonitoredTask):
unix_socket_path = urlparse(DEFAULT_UNIX_SOCKET).path
socket = Path(unix_socket_path)
if socket.exists() and access(socket, R_OK):
status.messages.append("Detected local docker socket")
messages.append("Detected local docker socket")
if len(DockerServiceConnection.objects.filter(local=True)) == 0:
status.messages.append("Created Service Connection for docker")
messages.append("Created Service Connection for docker")
DockerServiceConnection.objects.create(
name="Local Docker connection",
local=True,
url=unix_socket_path,
)
self.set_status(status)
self.set_status(TaskStatus.SUCCESSFUL, *messages)

View File

@ -4,12 +4,8 @@ from structlog.stdlib import get_logger
from authentik.events.context_processors.asn import ASN_CONTEXT_PROCESSOR
from authentik.events.context_processors.geoip import GEOIP_CONTEXT_PROCESSOR
from authentik.events.monitored_tasks import (
MonitoredTask,
TaskResult,
TaskResultStatus,
prefill_task,
)
from authentik.events.models import TaskStatus
from authentik.events.system_tasks import SystemTask, prefill_task
from authentik.policies.reputation.models import Reputation
from authentik.policies.reputation.signals import CACHE_KEY_PREFIX
from authentik.root.celery import CELERY_APP
@ -17,9 +13,9 @@ from authentik.root.celery import CELERY_APP
LOGGER = get_logger()
@CELERY_APP.task(bind=True, base=MonitoredTask)
@CELERY_APP.task(bind=True, base=SystemTask)
@prefill_task
def save_reputation(self: MonitoredTask):
def save_reputation(self: SystemTask):
"""Save currently cached reputation to database"""
objects_to_update = []
for _, score in cache.get_many(cache.keys(CACHE_KEY_PREFIX + "*")).items():
@ -32,4 +28,4 @@ def save_reputation(self: MonitoredTask):
rep.score = score["score"]
objects_to_update.append(rep)
Reputation.objects.bulk_update(objects_to_update, ["score", "ip_geo_data"])
self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL, ["Successfully updated Reputation"]))
self.set_status(TaskStatus.SUCCESSFUL, "Successfully updated Reputation")

View File

@ -1,17 +1,17 @@
"""SCIM Provider API Views"""
from django.utils.text import slugify
from drf_spectacular.utils import OpenApiResponse, extend_schema
from guardian.shortcuts import get_objects_for_user
from rest_framework.decorators import action
from rest_framework.fields import BooleanField
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from authentik.admin.api.tasks import TaskSerializer
from authentik.core.api.providers import ProviderSerializer
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import PassiveSerializer
from authentik.events.monitored_tasks import TaskInfo
from authentik.events.api.tasks import SystemTaskSerializer
from authentik.providers.scim.models import SCIMProvider
@ -43,7 +43,7 @@ class SCIMSyncStatusSerializer(PassiveSerializer):
"""SCIM Provider sync status"""
is_running = BooleanField(read_only=True)
tasks = TaskSerializer(many=True, read_only=True)
tasks = SystemTaskSerializer(many=True, read_only=True)
class SCIMProviderViewSet(UsedByMixin, ModelViewSet):
@ -65,8 +65,12 @@ class SCIMProviderViewSet(UsedByMixin, ModelViewSet):
def sync_status(self, request: Request, pk: int) -> Response:
"""Get provider's sync status"""
provider: SCIMProvider = self.get_object()
task = TaskInfo.by_name(f"scim_sync:{slugify(provider.name)}")
tasks = [task] if task else []
tasks = list(
get_objects_for_user(request.user, "authentik_events.view_systemtask").filter(
name="scim_sync",
uid=slugify(provider.name),
)
)
status = {
"tasks": tasks,
"is_running": provider.sync_lock.locked(),

View File

@ -10,7 +10,8 @@ from pydanticscim.responses import PatchOp
from structlog.stdlib import get_logger
from authentik.core.models import Group, User
from authentik.events.monitored_tasks import MonitoredTask, TaskResult, TaskResultStatus
from authentik.events.models import TaskStatus
from authentik.events.system_tasks import SystemTask
from authentik.lib.utils.reflection import path_to_class
from authentik.providers.scim.clients import PAGE_SIZE, PAGE_TIMEOUT
from authentik.providers.scim.clients.base import SCIMClient
@ -39,8 +40,8 @@ def scim_sync_all():
scim_sync.delay(provider.pk)
@CELERY_APP.task(bind=True, base=MonitoredTask)
def scim_sync(self: MonitoredTask, provider_pk: int) -> None:
@CELERY_APP.task(bind=True, base=SystemTask)
def scim_sync(self: SystemTask, provider_pk: int) -> None:
"""Run SCIM full sync for provider"""
provider: SCIMProvider = SCIMProvider.objects.filter(
pk=provider_pk, backchannel_application__isnull=False
@ -52,8 +53,8 @@ def scim_sync(self: MonitoredTask, provider_pk: int) -> None:
LOGGER.debug("SCIM sync locked, skipping task", source=provider.name)
return
self.set_uid(slugify(provider.name))
result = TaskResult(TaskResultStatus.SUCCESSFUL, [])
result.messages.append(_("Starting full SCIM sync"))
messages = []
messages.append(_("Starting full SCIM sync"))
LOGGER.debug("Starting SCIM sync")
users_paginator = Paginator(provider.get_user_qs(), PAGE_SIZE)
groups_paginator = Paginator(provider.get_group_qs(), PAGE_SIZE)
@ -63,17 +64,17 @@ def scim_sync(self: MonitoredTask, provider_pk: int) -> None:
with allow_join_result():
try:
for page in users_paginator.page_range:
result.messages.append(_("Syncing page %(page)d of users" % {"page": page}))
messages.append(_("Syncing page %(page)d of users" % {"page": page}))
for msg in scim_sync_users.delay(page, provider_pk).get():
result.messages.append(msg)
messages.append(msg)
for page in groups_paginator.page_range:
result.messages.append(_("Syncing page %(page)d of groups" % {"page": page}))
messages.append(_("Syncing page %(page)d of groups" % {"page": page}))
for msg in scim_sync_group.delay(page, provider_pk).get():
result.messages.append(msg)
messages.append(msg)
except StopSync as exc:
self.set_status(TaskResult(TaskResultStatus.ERROR).with_error(exc))
self.set_error(exc)
return
self.set_status(result)
self.set_status(TaskStatus.SUCCESSFUL, *messages)
@CELERY_APP.task(

View File

@ -6,6 +6,7 @@ from django_filters.filters import AllValuesMultipleFilter
from django_filters.filterset import FilterSet
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema, extend_schema_field, inline_serializer
from guardian.shortcuts import get_objects_for_user
from rest_framework.decorators import action
from rest_framework.exceptions import ValidationError
from rest_framework.fields import BooleanField, DictField, ListField, SerializerMethodField
@ -14,13 +15,12 @@ from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from authentik.admin.api.tasks import TaskSerializer
from authentik.core.api.propertymappings import PropertyMappingSerializer
from authentik.core.api.sources import SourceSerializer
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import PassiveSerializer
from authentik.crypto.models import CertificateKeyPair
from authentik.events.monitored_tasks import TaskInfo
from authentik.events.api.tasks import SystemTaskSerializer
from authentik.sources.ldap.models import LDAPPropertyMapping, LDAPSource
from authentik.sources.ldap.tasks import CACHE_KEY_STATUS, SYNC_CLASSES
@ -91,7 +91,7 @@ class LDAPSyncStatusSerializer(PassiveSerializer):
"""LDAP Source sync status"""
is_running = BooleanField(read_only=True)
tasks = TaskSerializer(many=True, read_only=True)
tasks = SystemTaskSerializer(many=True, read_only=True)
class LDAPSourceViewSet(UsedByMixin, ModelViewSet):
@ -136,7 +136,12 @@ class LDAPSourceViewSet(UsedByMixin, ModelViewSet):
def sync_status(self, request: Request, slug: str) -> Response:
"""Get source's sync status"""
source: LDAPSource = self.get_object()
tasks = TaskInfo.by_name(f"ldap_sync:{source.slug}:*") or []
tasks = list(
get_objects_for_user(request.user, "authentik_events.view_systemtask").filter(
name="ldap_sync",
uid__startswith=source.slug,
)
)
status = {
"tasks": tasks,
"is_running": source.sync_lock.locked(),

View File

@ -1,9 +1,7 @@
"""FreeIPA specific"""
from datetime import datetime
from datetime import datetime, timezone
from typing import Any, Generator
from pytz import UTC
from authentik.core.models import User
from authentik.sources.ldap.sync.base import BaseLDAPSynchronizer, flatten
@ -27,7 +25,7 @@ class FreeIPA(BaseLDAPSynchronizer):
if "krbLastPwdChange" not in attributes:
return
pwd_last_set: datetime = attributes.get("krbLastPwdChange", datetime.now())
pwd_last_set = pwd_last_set.replace(tzinfo=UTC)
pwd_last_set = pwd_last_set.replace(tzinfo=timezone.utc)
if created or pwd_last_set >= user.password_change_date:
self.message(f"'{user.username}': Reset user's password")
self._logger.debug(

View File

@ -1,10 +1,8 @@
"""Active Directory specific"""
from datetime import datetime
from datetime import datetime, timezone
from enum import IntFlag
from typing import Any, Generator
from pytz import UTC
from authentik.core.models import User
from authentik.sources.ldap.sync.base import BaseLDAPSynchronizer
@ -58,7 +56,7 @@ class MicrosoftActiveDirectory(BaseLDAPSynchronizer):
if "pwdLastSet" not in attributes:
return
pwd_last_set: datetime = attributes.get("pwdLastSet", datetime.now())
pwd_last_set = pwd_last_set.replace(tzinfo=UTC)
pwd_last_set = pwd_last_set.replace(tzinfo=timezone.utc)
if created or pwd_last_set >= user.password_change_date:
self.message(f"'{user.username}': Reset user's password")
self._logger.debug(

View File

@ -8,8 +8,9 @@ from ldap3.core.exceptions import LDAPException
from redis.exceptions import LockError
from structlog.stdlib import get_logger
from authentik.events.monitored_tasks import CACHE_KEY_PREFIX as CACHE_KEY_PREFIX_TASKS
from authentik.events.monitored_tasks import MonitoredTask, TaskResult, TaskResultStatus
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.utils.errors import exception_to_string
from authentik.lib.utils.reflection import class_to_path, path_to_class
@ -34,7 +35,7 @@ CACHE_KEY_STATUS = "goauthentik.io/sources/ldap/status/"
def ldap_sync_all():
"""Sync all sources"""
for source in LDAPSource.objects.filter(enabled=True):
ldap_sync_single.apply_async(args=[source.pk])
ldap_sync_single.apply_async(args=[str(source.pk)])
@CELERY_APP.task()
@ -69,8 +70,7 @@ def ldap_sync_single(source_pk: str):
try:
with lock:
# Delete all sync tasks from the cache
keys = cache.keys(f"{CACHE_KEY_PREFIX_TASKS}ldap_sync:{source.slug}*")
cache.delete_many(keys)
DBSystemTask.objects.filter(name="ldap_sync", uid__startswith=source.slug).delete()
task = chain(
# User and group sync can happen at once, they have no dependencies on each other
group(
@ -96,18 +96,18 @@ def ldap_sync_paginator(source: LDAPSource, sync: type[BaseLDAPSynchronizer]) ->
for page in sync_inst.get_objects():
page_cache_key = CACHE_KEY_PREFIX + str(uuid4())
cache.set(page_cache_key, page, 60 * 60 * CONFIG.get_int("ldap.task_timeout_hours"))
page_sync = ldap_sync.si(source.pk, class_to_path(sync), page_cache_key)
page_sync = ldap_sync.si(str(source.pk), class_to_path(sync), page_cache_key)
signatures.append(page_sync)
return signatures
@CELERY_APP.task(
bind=True,
base=MonitoredTask,
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: MonitoredTask, source_pk: str, sync_class: str, page_cache_key: str):
def ldap_sync(self: SystemTask, source_pk: str, sync_class: str, page_cache_key: str):
"""Synchronization of an LDAP Source"""
self.result_timeout_hours = CONFIG.get_int("ldap.task_timeout_hours")
source: LDAPSource = LDAPSource.objects.filter(pk=source_pk).first()
@ -127,20 +127,18 @@ def ldap_sync(self: MonitoredTask, source_pk: str, sync_class: str, page_cache_k
+ "Try increasing ldap.task_timeout_hours"
)
LOGGER.warning(error_message)
self.set_status(TaskResult(TaskResultStatus.ERROR, [error_message]))
self.set_status(TaskStatus.ERROR, error_message)
return
cache.touch(page_cache_key)
count = sync_inst.sync(page)
messages = sync_inst.messages
messages.append(f"Synced {count} objects.")
self.set_status(
TaskResult(
TaskResultStatus.SUCCESSFUL,
messages,
)
TaskStatus.SUCCESSFUL,
*messages,
)
cache.delete(page_cache_key)
except LDAPException as exc:
# No explicit event is created here as .set_status with an error will do that
LOGGER.warning(exception_to_string(exc))
self.set_status(TaskResult(TaskResultStatus.ERROR).with_error(exc))
self.set_error(exc)

View File

@ -7,8 +7,8 @@ from django.test import TestCase
from authentik.blueprints.tests import apply_blueprint
from authentik.core.models import Group, User
from authentik.core.tests.utils import create_test_admin_user
from authentik.events.models import Event, EventAction
from authentik.events.monitored_tasks import TaskInfo, TaskResultStatus
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.utils.reflection import class_to_path
from authentik.sources.ldap.models import LDAPPropertyMapping, LDAPSource
@ -40,9 +40,9 @@ class LDAPSyncTests(TestCase):
"""Test sync with missing page"""
connection = MagicMock(return_value=mock_ad_connection(LDAP_PASSWORD))
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
ldap_sync.delay(self.source.pk, class_to_path(UserLDAPSynchronizer), "foo").get()
status = TaskInfo.by_name("ldap_sync:ldap:users:foo")
self.assertEqual(status.result.status, TaskResultStatus.ERROR)
ldap_sync.delay(str(self.source.pk), class_to_path(UserLDAPSynchronizer), "foo").get()
task = SystemTask.objects.filter(name="ldap_sync", uid="ldap:users:foo").first()
self.assertEqual(task.status, TaskStatus.ERROR)
def test_sync_error(self):
"""Test user sync"""

View File

@ -4,7 +4,8 @@ from json import dumps
from requests import RequestException
from structlog.stdlib import get_logger
from authentik.events.monitored_tasks import MonitoredTask, TaskResult, TaskResultStatus
from authentik.events.models import TaskStatus
from authentik.events.system_tasks import SystemTask
from authentik.lib.utils.http import get_http_session
from authentik.root.celery import CELERY_APP
from authentik.sources.oauth.models import OAuthSource
@ -12,11 +13,11 @@ from authentik.sources.oauth.models import OAuthSource
LOGGER = get_logger()
@CELERY_APP.task(bind=True, base=MonitoredTask)
def update_well_known_jwks(self: MonitoredTask):
@CELERY_APP.task(bind=True, base=SystemTask)
def update_well_known_jwks(self: SystemTask):
"""Update OAuth sources' config from well_known, and JWKS info from the configured URL"""
session = get_http_session()
result = TaskResult(TaskResultStatus.SUCCESSFUL, [])
messages = []
for source in OAuthSource.objects.all().exclude(oidc_well_known_url=""):
try:
well_known_config = session.get(source.oidc_well_known_url)
@ -24,7 +25,7 @@ def update_well_known_jwks(self: MonitoredTask):
except RequestException as exc:
text = exc.response.text if exc.response else str(exc)
LOGGER.warning("Failed to update well_known", source=source, exc=exc, text=text)
result.messages.append(f"Failed to update OIDC configuration for {source.slug}")
messages.append(f"Failed to update OIDC configuration for {source.slug}")
continue
config = well_known_config.json()
try:
@ -47,7 +48,7 @@ def update_well_known_jwks(self: MonitoredTask):
source=source,
exc=exc,
)
result.messages.append(f"Failed to update OIDC configuration for {source.slug}")
messages.append(f"Failed to update OIDC configuration for {source.slug}")
continue
if dirty:
LOGGER.info("Updating sources' OpenID Configuration", source=source)
@ -60,11 +61,11 @@ def update_well_known_jwks(self: MonitoredTask):
except RequestException as exc:
text = exc.response.text if exc.response else str(exc)
LOGGER.warning("Failed to update JWKS", source=source, exc=exc, text=text)
result.messages.append(f"Failed to update JWKS for {source.slug}")
messages.append(f"Failed to update JWKS for {source.slug}")
continue
config = jwks_config.json()
if dumps(source.oidc_jwks, sort_keys=True) != dumps(config, sort_keys=True):
source.oidc_jwks = config
LOGGER.info("Updating sources' JWKS", source=source)
source.save()
self.set_status(result)
self.set_status(TaskStatus.SUCCESSFUL, *messages)

View File

@ -1,8 +1,8 @@
"""Plex tasks"""
from requests import RequestException
from authentik.events.models import Event, EventAction
from authentik.events.monitored_tasks import MonitoredTask, TaskResult, TaskResultStatus
from authentik.events.models import Event, EventAction, TaskStatus
from authentik.events.system_tasks import SystemTask
from authentik.lib.utils.errors import exception_to_string
from authentik.root.celery import CELERY_APP
from authentik.sources.plex.models import PlexSource
@ -16,8 +16,8 @@ def check_plex_token_all():
check_plex_token.delay(source.slug)
@CELERY_APP.task(bind=True, base=MonitoredTask)
def check_plex_token(self: MonitoredTask, source_slug: int):
@CELERY_APP.task(bind=True, base=SystemTask)
def check_plex_token(self: SystemTask, source_slug: int):
"""Check the validity of a Plex source."""
sources = PlexSource.objects.filter(slug=source_slug)
if not sources.exists():
@ -27,16 +27,15 @@ def check_plex_token(self: MonitoredTask, source_slug: int):
auth = PlexAuth(source, source.plex_token)
try:
auth.get_user_info()
self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL, ["Plex token is valid."]))
self.set_status(TaskStatus.SUCCESSFUL, "Plex token is valid.")
except RequestException as exc:
error = exception_to_string(exc)
if len(source.plex_token) > 0:
error = error.replace(source.plex_token, "$PLEX_TOKEN")
self.set_status(
TaskResult(
TaskResultStatus.ERROR,
["Plex token is invalid/an error occurred:", error],
)
TaskStatus.ERROR,
"Plex token is invalid/an error occurred:",
error,
)
Event.new(
EventAction.CONFIGURATION_ERROR,

View File

@ -9,8 +9,8 @@ from django.core.mail.utils import DNS_NAME
from django.utils.text import slugify
from structlog.stdlib import get_logger
from authentik.events.models import Event, EventAction
from authentik.events.monitored_tasks import MonitoredTask, TaskResult, TaskResultStatus
from authentik.events.models import Event, EventAction, TaskStatus
from authentik.events.system_tasks import SystemTask
from authentik.root.celery import CELERY_APP
from authentik.stages.email.models import EmailStage
from authentik.stages.email.utils import logo_data
@ -22,7 +22,7 @@ def send_mails(stage: EmailStage, *messages: list[EmailMultiAlternatives]):
"""Wrapper to convert EmailMessage to dict and send it from worker"""
tasks = []
for message in messages:
tasks.append(send_mail.s(message.__dict__, stage.pk))
tasks.append(send_mail.s(message.__dict__, str(stage.pk)))
lazy_group = group(*tasks)
promise = lazy_group()
return promise
@ -44,9 +44,9 @@ def get_email_body(email: EmailMultiAlternatives) -> str:
OSError,
),
retry_backoff=True,
base=MonitoredTask,
base=SystemTask,
)
def send_mail(self: MonitoredTask, message: dict[Any, Any], email_stage_pk: Optional[int] = None):
def send_mail(self: SystemTask, message: dict[Any, Any], email_stage_pk: Optional[str] = None):
"""Send Email for Email Stage. Retries are scheduled automatically."""
self.save_on_success = False
message_id = make_msgid(domain=DNS_NAME)
@ -58,10 +58,8 @@ def send_mail(self: MonitoredTask, message: dict[Any, Any], email_stage_pk: Opti
stages = EmailStage.objects.filter(pk=email_stage_pk)
if not stages.exists():
self.set_status(
TaskResult(
TaskResultStatus.WARNING,
messages=["Email stage does not exist anymore. Discarding message."],
)
TaskStatus.WARNING,
"Email stage does not exist anymore. Discarding message.",
)
return
stage: EmailStage = stages.first()
@ -69,7 +67,7 @@ def send_mail(self: MonitoredTask, message: dict[Any, Any], email_stage_pk: Opti
backend = stage.backend
except ValueError as exc:
LOGGER.warning("failed to get email backend", exc=exc)
self.set_status(TaskResult(TaskResultStatus.ERROR).with_error(exc))
self.set_error(exc)
return
backend.open()
# Since django's EmailMessage objects are not JSON serialisable,
@ -97,12 +95,10 @@ def send_mail(self: MonitoredTask, message: dict[Any, Any], email_stage_pk: Opti
to_email=message_object.to,
).save()
self.set_status(
TaskResult(
TaskResultStatus.SUCCESSFUL,
messages=["Successfully sent Mail."],
)
TaskStatus.SUCCESSFUL,
"Successfully sent Mail.",
)
except (SMTPException, ConnectionError, OSError) as exc:
LOGGER.debug("Error sending email, retrying...", exc=exc)
self.set_status(TaskResult(TaskResultStatus.ERROR).with_error(exc))
self.set_error(exc)
raise exc

View File

@ -707,43 +707,6 @@
}
}
},
{
"type": "object",
"required": [
"model",
"identifiers"
],
"properties": {
"model": {
"const": "authentik_policies_reputation.reputation"
},
"id": {
"type": "string"
},
"state": {
"type": "string",
"enum": [
"absent",
"present",
"created",
"must_created"
],
"default": "present"
},
"conditions": {
"type": "array",
"items": {
"type": "boolean"
}
},
"attrs": {
"$ref": "#/$defs/model_authentik_policies_reputation.reputation"
},
"identifiers": {
"$ref": "#/$defs/model_authentik_policies_reputation.reputation"
}
}
},
{
"type": "object",
"required": [
@ -892,117 +855,6 @@
}
}
},
{
"type": "object",
"required": [
"model",
"identifiers"
],
"properties": {
"model": {
"const": "authentik_providers_oauth2.authorizationcode"
},
"id": {
"type": "string"
},
"state": {
"type": "string",
"enum": [
"absent",
"present",
"created",
"must_created"
],
"default": "present"
},
"conditions": {
"type": "array",
"items": {
"type": "boolean"
}
},
"attrs": {
"$ref": "#/$defs/model_authentik_providers_oauth2.authorizationcode"
},
"identifiers": {
"$ref": "#/$defs/model_authentik_providers_oauth2.authorizationcode"
}
}
},
{
"type": "object",
"required": [
"model",
"identifiers"
],
"properties": {
"model": {
"const": "authentik_providers_oauth2.accesstoken"
},
"id": {
"type": "string"
},
"state": {
"type": "string",
"enum": [
"absent",
"present",
"created",
"must_created"
],
"default": "present"
},
"conditions": {
"type": "array",
"items": {
"type": "boolean"
}
},
"attrs": {
"$ref": "#/$defs/model_authentik_providers_oauth2.accesstoken"
},
"identifiers": {
"$ref": "#/$defs/model_authentik_providers_oauth2.accesstoken"
}
}
},
{
"type": "object",
"required": [
"model",
"identifiers"
],
"properties": {
"model": {
"const": "authentik_providers_oauth2.refreshtoken"
},
"id": {
"type": "string"
},
"state": {
"type": "string",
"enum": [
"absent",
"present",
"created",
"must_created"
],
"default": "present"
},
"conditions": {
"type": "array",
"items": {
"type": "boolean"
}
},
"attrs": {
"$ref": "#/$defs/model_authentik_providers_oauth2.refreshtoken"
},
"identifiers": {
"$ref": "#/$defs/model_authentik_providers_oauth2.refreshtoken"
}
}
},
{
"type": "object",
"required": [
@ -3678,14 +3530,10 @@
"authentik_policies_expression.expressionpolicy",
"authentik_policies_password.passwordpolicy",
"authentik_policies_reputation.reputationpolicy",
"authentik_policies_reputation.reputation",
"authentik_policies.policybinding",
"authentik_providers_ldap.ldapprovider",
"authentik_providers_oauth2.scopemapping",
"authentik_providers_oauth2.oauth2provider",
"authentik_providers_oauth2.authorizationcode",
"authentik_providers_oauth2.accesstoken",
"authentik_providers_oauth2.refreshtoken",
"authentik_providers_proxy.proxyprovider",
"authentik_providers_radius.radiusprovider",
"authentik_providers_saml.samlprovider",
@ -3909,43 +3757,6 @@
},
"required": []
},
"model_authentik_policies_reputation.reputation": {
"type": "object",
"properties": {
"pk": {
"type": "string",
"format": "uuid",
"title": "Reputation uuid"
},
"identifier": {
"type": "string",
"minLength": 1,
"title": "Identifier"
},
"ip": {
"type": "string",
"minLength": 1,
"title": "Ip"
},
"ip_geo_data": {
"type": "object",
"additionalProperties": true,
"title": "Ip geo data"
},
"ip_asn_data": {
"type": "object",
"additionalProperties": true,
"title": "Ip asn data"
},
"score": {
"type": "integer",
"minimum": -9223372036854775808,
"maximum": 9223372036854775807,
"title": "Score"
}
},
"required": []
},
"model_authentik_policies.policybinding": {
"type": "object",
"properties": {
@ -4224,617 +4035,6 @@
},
"required": []
},
"model_authentik_providers_oauth2.authorizationcode": {
"type": "object",
"properties": {
"provider": {
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"title": "Name"
},
"authentication_flow": {
"type": "integer",
"title": "Authentication flow",
"description": "Flow used for authentication when the associated application is accessed by an un-authenticated user."
},
"authorization_flow": {
"type": "integer",
"title": "Authorization flow",
"description": "Flow used when authorizing this provider."
},
"property_mappings": {
"type": "array",
"items": {
"type": "integer"
},
"title": "Property mappings"
},
"client_type": {
"type": "string",
"enum": [
"confidential",
"public"
],
"title": "Client Type",
"description": "Confidential clients are capable of maintaining the confidentiality of their credentials. Public clients are incapable"
},
"client_id": {
"type": "string",
"maxLength": 255,
"minLength": 1,
"title": "Client ID"
},
"client_secret": {
"type": "string",
"maxLength": 255,
"title": "Client Secret"
},
"access_code_validity": {
"type": "string",
"minLength": 1,
"title": "Access code validity",
"description": "Access codes not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
},
"access_token_validity": {
"type": "string",
"minLength": 1,
"title": "Access token validity",
"description": "Tokens not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
},
"refresh_token_validity": {
"type": "string",
"minLength": 1,
"title": "Refresh token validity",
"description": "Tokens not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
},
"include_claims_in_id_token": {
"type": "boolean",
"title": "Include claims in id_token",
"description": "Include User claims from scopes in the id_token, for applications that don't access the userinfo endpoint."
},
"signing_key": {
"type": "integer",
"title": "Signing Key",
"description": "Key used to sign the tokens. Only required when JWT Algorithm is set to RS256."
},
"redirect_uris": {
"type": "string",
"title": "Redirect URIs",
"description": "Enter each URI on a new line."
},
"sub_mode": {
"type": "string",
"enum": [
"hashed_user_id",
"user_id",
"user_uuid",
"user_username",
"user_email",
"user_upn"
],
"title": "Sub mode",
"description": "Configure what data should be used as unique User Identifier. For most cases, the default should be fine."
},
"issuer_mode": {
"type": "string",
"enum": [
"global",
"per_provider"
],
"title": "Issuer mode",
"description": "Configure how the issuer field of the ID Token should be filled."
},
"jwks_sources": {
"type": "array",
"items": {
"type": "integer",
"title": "Any JWT signed by the JWK of the selected source can be used to authenticate."
},
"title": "Any JWT signed by the JWK of the selected source can be used to authenticate."
}
},
"required": [
"name",
"authorization_flow"
],
"title": "Provider"
},
"user": {
"type": "object",
"properties": {
"username": {
"type": "string",
"maxLength": 150,
"minLength": 1,
"title": "Username"
},
"name": {
"type": "string",
"title": "Name",
"description": "User's display name."
},
"is_active": {
"type": "boolean",
"title": "Active",
"description": "Designates whether this user should be treated as active. Unselect this instead of deleting accounts."
},
"last_login": {
"type": [
"string",
"null"
],
"format": "date-time",
"title": "Last login"
},
"groups": {
"type": "array",
"items": {
"type": "integer"
},
"title": "Groups"
},
"email": {
"type": "string",
"format": "email",
"maxLength": 254,
"title": "Email address"
},
"attributes": {
"type": "object",
"additionalProperties": true,
"title": "Attributes"
},
"path": {
"type": "string",
"minLength": 1,
"title": "Path"
},
"type": {
"type": "string",
"enum": [
"internal",
"external",
"service_account",
"internal_service_account"
],
"title": "Type"
}
},
"required": [
"username",
"name"
],
"title": "User"
},
"expires": {
"type": "string",
"format": "date-time",
"title": "Expires"
},
"scope": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"title": "Scope"
}
},
"required": []
},
"model_authentik_providers_oauth2.accesstoken": {
"type": "object",
"properties": {
"provider": {
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"title": "Name"
},
"authentication_flow": {
"type": "integer",
"title": "Authentication flow",
"description": "Flow used for authentication when the associated application is accessed by an un-authenticated user."
},
"authorization_flow": {
"type": "integer",
"title": "Authorization flow",
"description": "Flow used when authorizing this provider."
},
"property_mappings": {
"type": "array",
"items": {
"type": "integer"
},
"title": "Property mappings"
},
"client_type": {
"type": "string",
"enum": [
"confidential",
"public"
],
"title": "Client Type",
"description": "Confidential clients are capable of maintaining the confidentiality of their credentials. Public clients are incapable"
},
"client_id": {
"type": "string",
"maxLength": 255,
"minLength": 1,
"title": "Client ID"
},
"client_secret": {
"type": "string",
"maxLength": 255,
"title": "Client Secret"
},
"access_code_validity": {
"type": "string",
"minLength": 1,
"title": "Access code validity",
"description": "Access codes not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
},
"access_token_validity": {
"type": "string",
"minLength": 1,
"title": "Access token validity",
"description": "Tokens not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
},
"refresh_token_validity": {
"type": "string",
"minLength": 1,
"title": "Refresh token validity",
"description": "Tokens not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
},
"include_claims_in_id_token": {
"type": "boolean",
"title": "Include claims in id_token",
"description": "Include User claims from scopes in the id_token, for applications that don't access the userinfo endpoint."
},
"signing_key": {
"type": "integer",
"title": "Signing Key",
"description": "Key used to sign the tokens. Only required when JWT Algorithm is set to RS256."
},
"redirect_uris": {
"type": "string",
"title": "Redirect URIs",
"description": "Enter each URI on a new line."
},
"sub_mode": {
"type": "string",
"enum": [
"hashed_user_id",
"user_id",
"user_uuid",
"user_username",
"user_email",
"user_upn"
],
"title": "Sub mode",
"description": "Configure what data should be used as unique User Identifier. For most cases, the default should be fine."
},
"issuer_mode": {
"type": "string",
"enum": [
"global",
"per_provider"
],
"title": "Issuer mode",
"description": "Configure how the issuer field of the ID Token should be filled."
},
"jwks_sources": {
"type": "array",
"items": {
"type": "integer",
"title": "Any JWT signed by the JWK of the selected source can be used to authenticate."
},
"title": "Any JWT signed by the JWK of the selected source can be used to authenticate."
}
},
"required": [
"name",
"authorization_flow"
],
"title": "Provider"
},
"user": {
"type": "object",
"properties": {
"username": {
"type": "string",
"maxLength": 150,
"minLength": 1,
"title": "Username"
},
"name": {
"type": "string",
"title": "Name",
"description": "User's display name."
},
"is_active": {
"type": "boolean",
"title": "Active",
"description": "Designates whether this user should be treated as active. Unselect this instead of deleting accounts."
},
"last_login": {
"type": [
"string",
"null"
],
"format": "date-time",
"title": "Last login"
},
"groups": {
"type": "array",
"items": {
"type": "integer"
},
"title": "Groups"
},
"email": {
"type": "string",
"format": "email",
"maxLength": 254,
"title": "Email address"
},
"attributes": {
"type": "object",
"additionalProperties": true,
"title": "Attributes"
},
"path": {
"type": "string",
"minLength": 1,
"title": "Path"
},
"type": {
"type": "string",
"enum": [
"internal",
"external",
"service_account",
"internal_service_account"
],
"title": "Type"
}
},
"required": [
"username",
"name"
],
"title": "User"
},
"expires": {
"type": "string",
"format": "date-time",
"title": "Expires"
},
"scope": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"title": "Scope"
},
"revoked": {
"type": "boolean",
"title": "Revoked"
}
},
"required": []
},
"model_authentik_providers_oauth2.refreshtoken": {
"type": "object",
"properties": {
"provider": {
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"title": "Name"
},
"authentication_flow": {
"type": "integer",
"title": "Authentication flow",
"description": "Flow used for authentication when the associated application is accessed by an un-authenticated user."
},
"authorization_flow": {
"type": "integer",
"title": "Authorization flow",
"description": "Flow used when authorizing this provider."
},
"property_mappings": {
"type": "array",
"items": {
"type": "integer"
},
"title": "Property mappings"
},
"client_type": {
"type": "string",
"enum": [
"confidential",
"public"
],
"title": "Client Type",
"description": "Confidential clients are capable of maintaining the confidentiality of their credentials. Public clients are incapable"
},
"client_id": {
"type": "string",
"maxLength": 255,
"minLength": 1,
"title": "Client ID"
},
"client_secret": {
"type": "string",
"maxLength": 255,
"title": "Client Secret"
},
"access_code_validity": {
"type": "string",
"minLength": 1,
"title": "Access code validity",
"description": "Access codes not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
},
"access_token_validity": {
"type": "string",
"minLength": 1,
"title": "Access token validity",
"description": "Tokens not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
},
"refresh_token_validity": {
"type": "string",
"minLength": 1,
"title": "Refresh token validity",
"description": "Tokens not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
},
"include_claims_in_id_token": {
"type": "boolean",
"title": "Include claims in id_token",
"description": "Include User claims from scopes in the id_token, for applications that don't access the userinfo endpoint."
},
"signing_key": {
"type": "integer",
"title": "Signing Key",
"description": "Key used to sign the tokens. Only required when JWT Algorithm is set to RS256."
},
"redirect_uris": {
"type": "string",
"title": "Redirect URIs",
"description": "Enter each URI on a new line."
},
"sub_mode": {
"type": "string",
"enum": [
"hashed_user_id",
"user_id",
"user_uuid",
"user_username",
"user_email",
"user_upn"
],
"title": "Sub mode",
"description": "Configure what data should be used as unique User Identifier. For most cases, the default should be fine."
},
"issuer_mode": {
"type": "string",
"enum": [
"global",
"per_provider"
],
"title": "Issuer mode",
"description": "Configure how the issuer field of the ID Token should be filled."
},
"jwks_sources": {
"type": "array",
"items": {
"type": "integer",
"title": "Any JWT signed by the JWK of the selected source can be used to authenticate."
},
"title": "Any JWT signed by the JWK of the selected source can be used to authenticate."
}
},
"required": [
"name",
"authorization_flow"
],
"title": "Provider"
},
"user": {
"type": "object",
"properties": {
"username": {
"type": "string",
"maxLength": 150,
"minLength": 1,
"title": "Username"
},
"name": {
"type": "string",
"title": "Name",
"description": "User's display name."
},
"is_active": {
"type": "boolean",
"title": "Active",
"description": "Designates whether this user should be treated as active. Unselect this instead of deleting accounts."
},
"last_login": {
"type": [
"string",
"null"
],
"format": "date-time",
"title": "Last login"
},
"groups": {
"type": "array",
"items": {
"type": "integer"
},
"title": "Groups"
},
"email": {
"type": "string",
"format": "email",
"maxLength": 254,
"title": "Email address"
},
"attributes": {
"type": "object",
"additionalProperties": true,
"title": "Attributes"
},
"path": {
"type": "string",
"minLength": 1,
"title": "Path"
},
"type": {
"type": "string",
"enum": [
"internal",
"external",
"service_account",
"internal_service_account"
],
"title": "Type"
}
},
"required": [
"username",
"name"
],
"title": "User"
},
"expires": {
"type": "string",
"format": "date-time",
"title": "Expires"
},
"scope": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"title": "Scope"
},
"revoked": {
"type": "boolean",
"title": "Revoked"
}
},
"required": []
},
"model_authentik_providers_proxy.proxyprovider": {
"type": "object",
"properties": {

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-01-23 08:02+0000\n"
"POT-Creation-Date: 2024-01-24 12:44+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -18,11 +18,6 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: authentik/admin/api/tasks.py:127
#, python-format
msgid "Successfully re-scheduled Task %(name)s!"
msgstr ""
#: authentik/api/schema.py:25
msgid "Generic API Error"
msgstr ""
@ -69,7 +64,7 @@ msgstr ""
msgid "authentik Export - %(date)s"
msgstr ""
#: authentik/blueprints/v1/tasks.py:151 authentik/crypto/tasks.py:93
#: authentik/blueprints/v1/tasks.py:145 authentik/crypto/tasks.py:87
#, python-format
msgid "Successfully imported %(count)d files."
msgstr ""
@ -108,162 +103,162 @@ msgstr ""
msgid "No empty segments in user path allowed."
msgstr ""
#: authentik/core/models.py:85
#: authentik/core/models.py:92
msgid "name"
msgstr ""
#: authentik/core/models.py:87
#: authentik/core/models.py:94
msgid "Users added to this group will be superusers."
msgstr ""
#: authentik/core/models.py:161
#: authentik/core/models.py:168
msgid "Group"
msgstr ""
#: authentik/core/models.py:162
#: authentik/core/models.py:169
msgid "Groups"
msgstr ""
#: authentik/core/models.py:177
#: authentik/core/models.py:184
msgid "User's display name."
msgstr ""
#: authentik/core/models.py:273 authentik/providers/oauth2/models.py:295
#: authentik/core/models.py:280 authentik/providers/oauth2/models.py:295
msgid "User"
msgstr ""
#: authentik/core/models.py:274
#: authentik/core/models.py:281
msgid "Users"
msgstr ""
#: authentik/core/models.py:276
#: authentik/core/models.py:283
#: authentik/stages/email/templates/email/password_reset.html:28
msgid "Reset Password"
msgstr ""
#: authentik/core/models.py:277
#: authentik/core/models.py:284
msgid "Can impersonate other users"
msgstr ""
#: authentik/core/models.py:278 authentik/rbac/models.py:54
#: authentik/core/models.py:285 authentik/rbac/models.py:54
msgid "Can assign permissions to users"
msgstr ""
#: authentik/core/models.py:279 authentik/rbac/models.py:55
#: authentik/core/models.py:286 authentik/rbac/models.py:55
msgid "Can unassign permissions from users"
msgstr ""
#: authentik/core/models.py:293
#: authentik/core/models.py:308
msgid ""
"Flow used for authentication when the associated application is accessed by "
"an un-authenticated user."
msgstr ""
#: authentik/core/models.py:303
#: authentik/core/models.py:318
msgid "Flow used when authorizing this provider."
msgstr ""
#: authentik/core/models.py:315
#: authentik/core/models.py:330
msgid ""
"Accessed from applications; optional backchannel providers for protocols "
"like LDAP and SCIM."
msgstr ""
#: authentik/core/models.py:370
#: authentik/core/models.py:385
msgid "Application's display Name."
msgstr ""
#: authentik/core/models.py:371
#: authentik/core/models.py:386
msgid "Internal application name, used in URLs."
msgstr ""
#: authentik/core/models.py:383
#: authentik/core/models.py:398
msgid "Open launch URL in a new browser tab or window."
msgstr ""
#: authentik/core/models.py:447
#: authentik/core/models.py:462
msgid "Application"
msgstr ""
#: authentik/core/models.py:448
#: authentik/core/models.py:463
msgid "Applications"
msgstr ""
#: authentik/core/models.py:454
#: authentik/core/models.py:469
msgid "Use the source-specific identifier"
msgstr ""
#: authentik/core/models.py:456
#: authentik/core/models.py:471
msgid ""
"Link to a user with identical email address. Can have security implications "
"when a source doesn't validate email addresses."
msgstr ""
#: authentik/core/models.py:460
#: authentik/core/models.py:475
msgid ""
"Use the user's email address, but deny enrollment when the email address "
"already exists."
msgstr ""
#: authentik/core/models.py:463
#: authentik/core/models.py:478
msgid ""
"Link to a user with identical username. Can have security implications when "
"a username is used with another source."
msgstr ""
#: authentik/core/models.py:467
#: authentik/core/models.py:482
msgid ""
"Use the user's username, but deny enrollment when the username already "
"exists."
msgstr ""
#: authentik/core/models.py:474
#: authentik/core/models.py:489
msgid "Source's display Name."
msgstr ""
#: authentik/core/models.py:475
#: authentik/core/models.py:490
msgid "Internal source name, used in URLs."
msgstr ""
#: authentik/core/models.py:494
#: authentik/core/models.py:509
msgid "Flow to use when authenticating existing users."
msgstr ""
#: authentik/core/models.py:503
#: authentik/core/models.py:518
msgid "Flow to use when enrolling new users."
msgstr ""
#: authentik/core/models.py:511
#: authentik/core/models.py:526
msgid ""
"How the source determines if an existing user should be authenticated or a "
"new user enrolled."
msgstr ""
#: authentik/core/models.py:683
#: authentik/core/models.py:698
msgid "Token"
msgstr ""
#: authentik/core/models.py:684
#: authentik/core/models.py:699
msgid "Tokens"
msgstr ""
#: authentik/core/models.py:689
#: authentik/core/models.py:704
msgid "View token's key"
msgstr ""
#: authentik/core/models.py:725
#: authentik/core/models.py:740
msgid "Property Mapping"
msgstr ""
#: authentik/core/models.py:726
#: authentik/core/models.py:741
msgid "Property Mappings"
msgstr ""
#: authentik/core/models.py:763
#: authentik/core/models.py:778
msgid "Authenticated Session"
msgstr ""
#: authentik/core/models.py:764
#: authentik/core/models.py:779
msgid "Authenticated Sessions"
msgstr ""
@ -439,6 +434,11 @@ msgstr ""
msgid "(You are already connected in another tab/window)"
msgstr ""
#: authentik/events/api/tasks.py:99
#, python-format
msgid "Successfully re-scheduled Task %(name)s!"
msgstr ""
#: authentik/events/models.py:289
msgid "Event"
msgstr ""
@ -541,7 +541,19 @@ msgstr ""
msgid "Webhook Mappings"
msgstr ""
#: authentik/events/monitored_tasks.py:207
#: authentik/events/models.py:642
msgid "Rerun task"
msgstr ""
#: authentik/events/models.py:643
msgid "System Task"
msgstr ""
#: authentik/events/models.py:644
msgid "System Tasks"
msgstr ""
#: authentik/events/monitored_tasks.py:128
msgid "Task has not been run yet."
msgstr ""
@ -1573,31 +1585,31 @@ msgstr ""
msgid "SCIM Mappings"
msgstr ""
#: authentik/providers/scim/tasks.py:56
#: authentik/providers/scim/tasks.py:57
msgid "Starting full SCIM sync"
msgstr ""
#: authentik/providers/scim/tasks.py:66
#: authentik/providers/scim/tasks.py:67
#, python-format
msgid "Syncing page %(page)d of users"
msgstr ""
#: authentik/providers/scim/tasks.py:70
#: authentik/providers/scim/tasks.py:71
#, python-format
msgid "Syncing page %(page)d of groups"
msgstr ""
#: authentik/providers/scim/tasks.py:102
#: authentik/providers/scim/tasks.py:103
#, python-format
msgid "Failed to sync user %(user_name)s due to remote error: %(error)s"
msgstr ""
#: authentik/providers/scim/tasks.py:113 authentik/providers/scim/tasks.py:154
#: authentik/providers/scim/tasks.py:114 authentik/providers/scim/tasks.py:155
#, python-format
msgid "Stopping sync due to error: %(error)s"
msgstr ""
#: authentik/providers/scim/tasks.py:143
#: authentik/providers/scim/tasks.py:144
#, python-format
msgid "Failed to sync group %(group_name)s due to remote error: %(error)s"
msgstr ""

View File

@ -236,103 +236,6 @@ paths:
schema:
$ref: '#/components/schemas/GenericError'
description: ''
/admin/system_tasks/:
get:
operationId: admin_system_tasks_list
description: List system tasks
tags:
- admin
security:
- authentik: []
responses:
'200':
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Task'
description: ''
'400':
content:
application/json:
schema:
$ref: '#/components/schemas/ValidationError'
description: ''
'403':
content:
application/json:
schema:
$ref: '#/components/schemas/GenericError'
description: ''
/admin/system_tasks/{id}/:
get:
operationId: admin_system_tasks_retrieve
description: Get a single system task
parameters:
- in: path
name: id
schema:
type: string
required: true
tags:
- admin
security:
- authentik: []
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/Task'
description: ''
'404':
description: Task not found
'400':
content:
application/json:
schema:
$ref: '#/components/schemas/ValidationError'
description: ''
'403':
content:
application/json:
schema:
$ref: '#/components/schemas/GenericError'
description: ''
/admin/system_tasks/{id}/retry/:
post:
operationId: admin_system_tasks_retry_create
description: Retry task
parameters:
- in: path
name: id
schema:
type: string
required: true
tags:
- admin
security:
- authentik: []
responses:
'204':
description: Task retried successfully
'404':
description: Task not found
'500':
description: Failed to retry task
'400':
content:
application/json:
schema:
$ref: '#/components/schemas/ValidationError'
description: ''
'403':
content:
application/json:
schema:
$ref: '#/components/schemas/GenericError'
description: ''
/admin/version/:
get:
operationId: admin_version_retrieve
@ -7017,6 +6920,150 @@ paths:
schema:
$ref: '#/components/schemas/GenericError'
description: ''
/events/system_tasks/:
get:
operationId: events_system_tasks_list
description: Read-only view set that returns all background tasks
parameters:
- in: query
name: name
schema:
type: string
- name: ordering
required: false
in: query
description: Which field to use when ordering the results.
schema:
type: string
- name: page
required: false
in: query
description: A page number within the paginated result set.
schema:
type: integer
- name: page_size
required: false
in: query
description: Number of results to return per page.
schema:
type: integer
- name: search
required: false
in: query
description: A search term.
schema:
type: string
- in: query
name: status
schema:
type: string
enum:
- error
- successful
- unknown
- warning
description: |-
* `unknown` - Unknown
* `successful` - Successful
* `warning` - Warning
* `error` - Error
- in: query
name: uid
schema:
type: string
tags:
- events
security:
- authentik: []
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/PaginatedSystemTaskList'
description: ''
'400':
content:
application/json:
schema:
$ref: '#/components/schemas/ValidationError'
description: ''
'403':
content:
application/json:
schema:
$ref: '#/components/schemas/GenericError'
description: ''
/events/system_tasks/{uuid}/:
get:
operationId: events_system_tasks_retrieve
description: Read-only view set that returns all background tasks
parameters:
- in: path
name: uuid
schema:
type: string
format: uuid
description: A UUID string identifying this System Task.
required: true
tags:
- events
security:
- authentik: []
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/SystemTask'
description: ''
'400':
content:
application/json:
schema:
$ref: '#/components/schemas/ValidationError'
description: ''
'403':
content:
application/json:
schema:
$ref: '#/components/schemas/GenericError'
description: ''
/events/system_tasks/{uuid}/run/:
post:
operationId: events_system_tasks_run_create
description: Run task
parameters:
- in: path
name: uuid
schema:
type: string
format: uuid
description: A UUID string identifying this System Task.
required: true
tags:
- events
security:
- authentik: []
responses:
'204':
description: Task retried successfully
'404':
description: Task not found
'500':
description: Failed to retry task
'400':
content:
application/json:
schema:
$ref: '#/components/schemas/ValidationError'
description: ''
'403':
content:
application/json:
schema:
$ref: '#/components/schemas/GenericError'
description: ''
/events/transports/:
get:
operationId: events_transports_list
@ -18172,13 +18219,9 @@ paths:
- authentik_policies_expiry.passwordexpirypolicy
- authentik_policies_expression.expressionpolicy
- authentik_policies_password.passwordpolicy
- authentik_policies_reputation.reputation
- authentik_policies_reputation.reputationpolicy
- authentik_providers_ldap.ldapprovider
- authentik_providers_oauth2.accesstoken
- authentik_providers_oauth2.authorizationcode
- authentik_providers_oauth2.oauth2provider
- authentik_providers_oauth2.refreshtoken
- authentik_providers_oauth2.scopemapping
- authentik_providers_proxy.proxyprovider
- authentik_providers_rac.endpoint
@ -18245,14 +18288,10 @@ paths:
* `authentik_policies_expression.expressionpolicy` - Expression Policy
* `authentik_policies_password.passwordpolicy` - Password Policy
* `authentik_policies_reputation.reputationpolicy` - Reputation Policy
* `authentik_policies_reputation.reputation` - Reputation Score
* `authentik_policies.policybinding` - Policy Binding
* `authentik_providers_ldap.ldapprovider` - LDAP Provider
* `authentik_providers_oauth2.scopemapping` - Scope Mapping
* `authentik_providers_oauth2.oauth2provider` - OAuth2/OpenID Provider
* `authentik_providers_oauth2.authorizationcode` - Authorization Code
* `authentik_providers_oauth2.accesstoken` - OAuth2 Access Token
* `authentik_providers_oauth2.refreshtoken` - OAuth2 Refresh Token
* `authentik_providers_proxy.proxyprovider` - Proxy Provider
* `authentik_providers_radius.radiusprovider` - Radius Provider
* `authentik_providers_saml.samlprovider` - SAML Provider
@ -18272,7 +18311,7 @@ paths:
* `authentik_stages_authenticator_duo.duodevice` - Duo Device
* `authentik_stages_authenticator_sms.authenticatorsmsstage` - SMS Authenticator Setup Stage
* `authentik_stages_authenticator_sms.smsdevice` - SMS Device
* `authentik_stages_authenticator_static.authenticatorstaticstage` - Static Authenticator Stage
* `authentik_stages_authenticator_static.authenticatorstaticstage` - Static Authenticator Setup Stage
* `authentik_stages_authenticator_static.staticdevice` - Static Device
* `authentik_stages_authenticator_totp.authenticatortotpstage` - TOTP Authenticator Setup Stage
* `authentik_stages_authenticator_totp.totpdevice` - TOTP Device
@ -18468,13 +18507,9 @@ paths:
- authentik_policies_expiry.passwordexpirypolicy
- authentik_policies_expression.expressionpolicy
- authentik_policies_password.passwordpolicy
- authentik_policies_reputation.reputation
- authentik_policies_reputation.reputationpolicy
- authentik_providers_ldap.ldapprovider
- authentik_providers_oauth2.accesstoken
- authentik_providers_oauth2.authorizationcode
- authentik_providers_oauth2.oauth2provider
- authentik_providers_oauth2.refreshtoken
- authentik_providers_oauth2.scopemapping
- authentik_providers_proxy.proxyprovider
- authentik_providers_rac.endpoint
@ -18541,14 +18576,10 @@ paths:
* `authentik_policies_expression.expressionpolicy` - Expression Policy
* `authentik_policies_password.passwordpolicy` - Password Policy
* `authentik_policies_reputation.reputationpolicy` - Reputation Policy
* `authentik_policies_reputation.reputation` - Reputation Score
* `authentik_policies.policybinding` - Policy Binding
* `authentik_providers_ldap.ldapprovider` - LDAP Provider
* `authentik_providers_oauth2.scopemapping` - Scope Mapping
* `authentik_providers_oauth2.oauth2provider` - OAuth2/OpenID Provider
* `authentik_providers_oauth2.authorizationcode` - Authorization Code
* `authentik_providers_oauth2.accesstoken` - OAuth2 Access Token
* `authentik_providers_oauth2.refreshtoken` - OAuth2 Refresh Token
* `authentik_providers_proxy.proxyprovider` - Proxy Provider
* `authentik_providers_radius.radiusprovider` - Radius Provider
* `authentik_providers_saml.samlprovider` - SAML Provider
@ -18568,7 +18599,7 @@ paths:
* `authentik_stages_authenticator_duo.duodevice` - Duo Device
* `authentik_stages_authenticator_sms.authenticatorsmsstage` - SMS Authenticator Setup Stage
* `authentik_stages_authenticator_sms.smsdevice` - SMS Device
* `authentik_stages_authenticator_static.authenticatorstaticstage` - Static Authenticator Stage
* `authentik_stages_authenticator_static.authenticatorstaticstage` - Static Authenticator Setup Stage
* `authentik_stages_authenticator_static.staticdevice` - Static Device
* `authentik_stages_authenticator_totp.authenticatortotpstage` - TOTP Authenticator Setup Stage
* `authentik_stages_authenticator_totp.totpdevice` - TOTP Device
@ -23265,7 +23296,7 @@ paths:
schema:
type: string
format: uuid
description: A UUID string identifying this Static Authenticator Stage.
description: A UUID string identifying this Static Authenticator Setup Stage.
required: true
tags:
- stages
@ -23299,7 +23330,7 @@ paths:
schema:
type: string
format: uuid
description: A UUID string identifying this Static Authenticator Stage.
description: A UUID string identifying this Static Authenticator Setup Stage.
required: true
tags:
- stages
@ -23339,7 +23370,7 @@ paths:
schema:
type: string
format: uuid
description: A UUID string identifying this Static Authenticator Stage.
description: A UUID string identifying this Static Authenticator Setup Stage.
required: true
tags:
- stages
@ -23378,7 +23409,7 @@ paths:
schema:
type: string
format: uuid
description: A UUID string identifying this Static Authenticator Stage.
description: A UUID string identifying this Static Authenticator Setup Stage.
required: true
tags:
- stages
@ -23409,7 +23440,7 @@ paths:
schema:
type: string
format: uuid
description: A UUID string identifying this Static Authenticator Stage.
description: A UUID string identifying this Static Authenticator Setup Stage.
required: true
tags:
- stages
@ -32444,14 +32475,10 @@ components:
* `authentik_policies_expression.expressionpolicy` - Expression Policy
* `authentik_policies_password.passwordpolicy` - Password Policy
* `authentik_policies_reputation.reputationpolicy` - Reputation Policy
* `authentik_policies_reputation.reputation` - Reputation Score
* `authentik_policies.policybinding` - Policy Binding
* `authentik_providers_ldap.ldapprovider` - LDAP Provider
* `authentik_providers_oauth2.scopemapping` - Scope Mapping
* `authentik_providers_oauth2.oauth2provider` - OAuth2/OpenID Provider
* `authentik_providers_oauth2.authorizationcode` - Authorization Code
* `authentik_providers_oauth2.accesstoken` - OAuth2 Access Token
* `authentik_providers_oauth2.refreshtoken` - OAuth2 Refresh Token
* `authentik_providers_proxy.proxyprovider` - Proxy Provider
* `authentik_providers_radius.radiusprovider` - Radius Provider
* `authentik_providers_saml.samlprovider` - SAML Provider
@ -32471,7 +32498,7 @@ components:
* `authentik_stages_authenticator_duo.duodevice` - Duo Device
* `authentik_stages_authenticator_sms.authenticatorsmsstage` - SMS Authenticator Setup Stage
* `authentik_stages_authenticator_sms.smsdevice` - SMS Device
* `authentik_stages_authenticator_static.authenticatorstaticstage` - Static Authenticator Stage
* `authentik_stages_authenticator_static.authenticatorstaticstage` - Static Authenticator Setup Stage
* `authentik_stages_authenticator_static.staticdevice` - Static Device
* `authentik_stages_authenticator_totp.authenticatortotpstage` - TOTP Authenticator Setup Stage
* `authentik_stages_authenticator_totp.totpdevice` - TOTP Device
@ -32647,14 +32674,10 @@ components:
* `authentik_policies_expression.expressionpolicy` - Expression Policy
* `authentik_policies_password.passwordpolicy` - Password Policy
* `authentik_policies_reputation.reputationpolicy` - Reputation Policy
* `authentik_policies_reputation.reputation` - Reputation Score
* `authentik_policies.policybinding` - Policy Binding
* `authentik_providers_ldap.ldapprovider` - LDAP Provider
* `authentik_providers_oauth2.scopemapping` - Scope Mapping
* `authentik_providers_oauth2.oauth2provider` - OAuth2/OpenID Provider
* `authentik_providers_oauth2.authorizationcode` - Authorization Code
* `authentik_providers_oauth2.accesstoken` - OAuth2 Access Token
* `authentik_providers_oauth2.refreshtoken` - OAuth2 Refresh Token
* `authentik_providers_proxy.proxyprovider` - Proxy Provider
* `authentik_providers_radius.radiusprovider` - Radius Provider
* `authentik_providers_saml.samlprovider` - SAML Provider
@ -32674,7 +32697,7 @@ components:
* `authentik_stages_authenticator_duo.duodevice` - Duo Device
* `authentik_stages_authenticator_sms.authenticatorsmsstage` - SMS Authenticator Setup Stage
* `authentik_stages_authenticator_sms.smsdevice` - SMS Device
* `authentik_stages_authenticator_static.authenticatorstaticstage` - Static Authenticator Stage
* `authentik_stages_authenticator_static.authenticatorstaticstage` - Static Authenticator Setup Stage
* `authentik_stages_authenticator_static.staticdevice` - Static Device
* `authentik_stages_authenticator_totp.authenticatortotpstage` - TOTP Authenticator Setup Stage
* `authentik_stages_authenticator_totp.totpdevice` - TOTP Device
@ -34787,7 +34810,7 @@ components:
tasks:
type: array
items:
$ref: '#/components/schemas/Task'
$ref: '#/components/schemas/SystemTask'
readOnly: true
required:
- is_running
@ -34966,14 +34989,10 @@ components:
- authentik_policies_expression.expressionpolicy
- authentik_policies_password.passwordpolicy
- authentik_policies_reputation.reputationpolicy
- authentik_policies_reputation.reputation
- authentik_policies.policybinding
- authentik_providers_ldap.ldapprovider
- authentik_providers_oauth2.scopemapping
- authentik_providers_oauth2.oauth2provider
- authentik_providers_oauth2.authorizationcode
- authentik_providers_oauth2.accesstoken
- authentik_providers_oauth2.refreshtoken
- authentik_providers_proxy.proxyprovider
- authentik_providers_radius.radiusprovider
- authentik_providers_saml.samlprovider
@ -35046,14 +35065,10 @@ components:
* `authentik_policies_expression.expressionpolicy` - Expression Policy
* `authentik_policies_password.passwordpolicy` - Password Policy
* `authentik_policies_reputation.reputationpolicy` - Reputation Policy
* `authentik_policies_reputation.reputation` - Reputation Score
* `authentik_policies.policybinding` - Policy Binding
* `authentik_providers_ldap.ldapprovider` - LDAP Provider
* `authentik_providers_oauth2.scopemapping` - Scope Mapping
* `authentik_providers_oauth2.oauth2provider` - OAuth2/OpenID Provider
* `authentik_providers_oauth2.authorizationcode` - Authorization Code
* `authentik_providers_oauth2.accesstoken` - OAuth2 Access Token
* `authentik_providers_oauth2.refreshtoken` - OAuth2 Refresh Token
* `authentik_providers_proxy.proxyprovider` - Proxy Provider
* `authentik_providers_radius.radiusprovider` - Radius Provider
* `authentik_providers_saml.samlprovider` - SAML Provider
@ -35073,7 +35088,7 @@ components:
* `authentik_stages_authenticator_duo.duodevice` - Duo Device
* `authentik_stages_authenticator_sms.authenticatorsmsstage` - SMS Authenticator Setup Stage
* `authentik_stages_authenticator_sms.smsdevice` - SMS Device
* `authentik_stages_authenticator_static.authenticatorstaticstage` - Static Authenticator Stage
* `authentik_stages_authenticator_static.authenticatorstaticstage` - Static Authenticator Setup Stage
* `authentik_stages_authenticator_static.staticdevice` - Static Device
* `authentik_stages_authenticator_totp.authenticatortotpstage` - TOTP Authenticator Setup Stage
* `authentik_stages_authenticator_totp.totpdevice` - TOTP Device
@ -37047,6 +37062,18 @@ components:
required:
- pagination
- results
PaginatedSystemTaskList:
type: object
properties:
pagination:
$ref: '#/components/schemas/Pagination'
results:
type: array
items:
$ref: '#/components/schemas/SystemTask'
required:
- pagination
- results
PaginatedTOTPDeviceList:
type: object
properties:
@ -38274,14 +38301,10 @@ components:
* `authentik_policies_expression.expressionpolicy` - Expression Policy
* `authentik_policies_password.passwordpolicy` - Password Policy
* `authentik_policies_reputation.reputationpolicy` - Reputation Policy
* `authentik_policies_reputation.reputation` - Reputation Score
* `authentik_policies.policybinding` - Policy Binding
* `authentik_providers_ldap.ldapprovider` - LDAP Provider
* `authentik_providers_oauth2.scopemapping` - Scope Mapping
* `authentik_providers_oauth2.oauth2provider` - OAuth2/OpenID Provider
* `authentik_providers_oauth2.authorizationcode` - Authorization Code
* `authentik_providers_oauth2.accesstoken` - OAuth2 Access Token
* `authentik_providers_oauth2.refreshtoken` - OAuth2 Refresh Token
* `authentik_providers_proxy.proxyprovider` - Proxy Provider
* `authentik_providers_radius.radiusprovider` - Radius Provider
* `authentik_providers_saml.samlprovider` - SAML Provider
@ -38301,7 +38324,7 @@ components:
* `authentik_stages_authenticator_duo.duodevice` - Duo Device
* `authentik_stages_authenticator_sms.authenticatorsmsstage` - SMS Authenticator Setup Stage
* `authentik_stages_authenticator_sms.smsdevice` - SMS Device
* `authentik_stages_authenticator_static.authenticatorstaticstage` - Static Authenticator Stage
* `authentik_stages_authenticator_static.authenticatorstaticstage` - Static Authenticator Setup Stage
* `authentik_stages_authenticator_static.staticdevice` - Static Device
* `authentik_stages_authenticator_totp.authenticatortotpstage` - TOTP Authenticator Setup Stage
* `authentik_stages_authenticator_totp.totpdevice` - TOTP Device
@ -42794,7 +42817,7 @@ components:
tasks:
type: array
items:
$ref: '#/components/schemas/Task'
$ref: '#/components/schemas/SystemTask'
readOnly: true
required:
- is_running
@ -43517,6 +43540,67 @@ components:
- http_is_secure
- runtime
- server_time
SystemTask:
type: object
description: Serialize TaskInfo and TaskResult
properties:
uuid:
type: string
format: uuid
readOnly: true
name:
type: string
full_name:
type: string
description: Get full name with UID
readOnly: true
uid:
type: string
description:
type: string
start_timestamp:
type: string
format: date-time
description: Timestamp when the task started
readOnly: true
finish_timestamp:
type: string
format: date-time
description: Timestamp when the task finished
readOnly: true
duration:
type: number
format: double
description: Get the duration a task took to run
readOnly: true
status:
$ref: '#/components/schemas/SystemTaskStatusEnum'
messages:
type: array
items:
type: string
required:
- description
- duration
- finish_timestamp
- full_name
- messages
- name
- start_timestamp
- status
- uuid
SystemTaskStatusEnum:
enum:
- unknown
- successful
- warning
- error
type: string
description: |-
* `unknown` - UNKNOWN
* `successful` - SUCCESSFUL
* `warning` - WARNING
* `error` - ERROR
TOTPDevice:
type: object
description: Serializer for totp authenticator devices
@ -43543,45 +43627,6 @@ components:
maxLength: 64
required:
- name
Task:
type: object
description: Serialize TaskInfo and TaskResult
properties:
task_name:
type: string
task_description:
type: string
task_finish_timestamp:
type: string
format: date-time
task_duration:
type: integer
description: Get the duration a task took to run
readOnly: true
status:
$ref: '#/components/schemas/TaskStatusEnum'
messages:
type: array
items: {}
required:
- messages
- status
- task_description
- task_duration
- task_finish_timestamp
- task_name
TaskStatusEnum:
enum:
- SUCCESSFUL
- WARNING
- ERROR
- UNKNOWN
type: string
description: |-
* `SUCCESSFUL` - SUCCESSFUL
* `WARNING` - WARNING
* `ERROR` - ERROR
* `UNKNOWN` - UNKNOWN
Tenant:
type: object
description: Tenant Serializer
@ -43853,16 +43898,16 @@ components:
- pk
UsedByActionEnum:
enum:
- CASCADE
- CASCADE_MANY
- SET_NULL
- SET_DEFAULT
- cascade
- cascade_many
- set_null
- set_default
type: string
description: |-
* `CASCADE` - CASCADE
* `CASCADE_MANY` - CASCADE_MANY
* `SET_NULL` - SET_NULL
* `SET_DEFAULT` - SET_DEFAULT
* `cascade` - CASCADE
* `cascade_many` - CASCADE_MANY
* `set_null` - SET_NULL
* `set_default` - SET_DEFAULT
User:
type: object
description: User Serializer

View File

@ -183,7 +183,12 @@ class TestProviderLDAP(SeleniumTestCase):
self.assertTrue(
Event.objects.filter(
action=EventAction.LOGIN_FAILED,
user={"pk": anon.pk, "email": anon.email, "username": anon.username},
user={
"pk": anon.pk,
"email": anon.email,
"username": anon.username,
"is_anonymous": True,
},
).exists(),
)

View File

@ -1,8 +1,7 @@
import { EventGeo } from "@goauthentik/admin/events/utils";
import { EventGeo, EventUser } from "@goauthentik/admin/events/utils";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EventWithContext } from "@goauthentik/common/events";
import { actionToLabel } from "@goauthentik/common/labels";
import { truncate } from "@goauthentik/common/utils";
import "@goauthentik/components/ak-event-info";
import "@goauthentik/elements/Tabs";
import "@goauthentik/elements/buttons/Dropdown";
@ -11,7 +10,7 @@ import "@goauthentik/elements/buttons/SpinnerButton";
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { Table, TableColumn } from "@goauthentik/elements/table/Table";
import { msg, str } from "@lit/localize";
import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, css, html } from "lit";
import { customElement, property } from "lit/decorators.js";
@ -71,20 +70,7 @@ export class RecentEventsCard extends Table<Event> {
return [
html`<div><a href="${`#/events/log/${item.pk}`}">${actionToLabel(item.action)}</a></div>
<small>${item.app}</small>`,
item.user?.username
? html`<div>
<a href="#/identity/users/${item.user.pk}"
>${truncate(item.user?.username, 15)}</a
>
</div>
${item.user.on_behalf_of
? html`<small>
<a href="#/identity/users/${item.user.on_behalf_of.pk}"
>${msg(str`On behalf of ${item.user.on_behalf_of.username}`)}</a
>
</small>`
: html``}`
: html`-`,
EventUser(item),
html`<span>${item.created?.toLocaleString()}</span>`,
html` <div>${item.clientIp || msg("-")}</div>
<small>${EventGeo(item)}</small>`,

View File

@ -6,7 +6,7 @@ import { ChartData, ChartOptions } from "chart.js";
import { msg } from "@lit/localize";
import { customElement } from "lit/decorators.js";
import { ProvidersApi, SourcesApi, TaskStatusEnum } from "@goauthentik/api";
import { ProvidersApi, SourcesApi, SystemTaskStatusEnum } from "@goauthentik/api";
export interface SyncStatus {
healthy: number;
@ -49,12 +49,12 @@ export class LDAPSyncStatusChart extends AKChart<SyncStatus[]> {
});
health.tasks.forEach((task) => {
if (task.status !== TaskStatusEnum.Successful) {
if (task.status !== SystemTaskStatusEnum.Successful) {
metrics.failed += 1;
}
const now = new Date().getTime();
const maxDelta = 3600000; // 1 hour
if (!health || now - task.taskFinishTimestamp.getTime() > maxDelta) {
if (!health || now - task.finishTimestamp.getTime() > maxDelta) {
metrics.unsynced += 1;
} else {
metrics.healthy += 1;
@ -94,12 +94,12 @@ export class LDAPSyncStatusChart extends AKChart<SyncStatus[]> {
id: element.pk,
});
health.tasks.forEach((task) => {
if (task.status !== TaskStatusEnum.Successful) {
if (task.status !== SystemTaskStatusEnum.Successful) {
sourceKey = "failed";
}
const now = new Date().getTime();
const maxDelta = 3600000; // 1 hour
if (!health || now - task.taskFinishTimestamp.getTime() > maxDelta) {
if (!health || now - task.finishTimestamp.getTime() > maxDelta) {
sourceKey = "unsynced";
}
});

View File

@ -1,5 +1,5 @@
import "@goauthentik/admin/events/EventVolumeChart";
import { EventGeo } from "@goauthentik/admin/events/utils";
import { EventGeo, EventUser } from "@goauthentik/admin/events/utils";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EventWithContext } from "@goauthentik/common/events";
import { actionToLabel } from "@goauthentik/common/labels";
@ -10,7 +10,7 @@ import { TableColumn } from "@goauthentik/elements/table/Table";
import { TablePage } from "@goauthentik/elements/table/TablePage";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg, str } from "@lit/localize";
import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, css, html } from "lit";
import { customElement, property } from "lit/decorators.js";
@ -81,18 +81,7 @@ export class EventListPage extends TablePage<Event> {
return [
html`<div>${actionToLabel(item.action)}</div>
<small>${item.app}</small>`,
item.user?.username
? html`<div>
<a href="#/identity/users/${item.user.pk}">${item.user?.username}</a>
</div>
${item.user.on_behalf_of
? html`<small>
<a href="#/identity/users/${item.user.on_behalf_of.pk}"
>${msg(str`On behalf of ${item.user.on_behalf_of.username}`)}</a
>
</small>`
: html``}`
: html`-`,
EventUser(item),
html`<span>${item.created?.toLocaleString()}</span>`,
html`<div>${item.clientIp || msg("-")}</div>

View File

@ -1,4 +1,4 @@
import { EventGeo } from "@goauthentik/admin/events/utils";
import { EventGeo, EventUser } from "@goauthentik/admin/events/utils";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EventWithContext } from "@goauthentik/common/events";
import { actionToLabel } from "@goauthentik/common/labels";
@ -87,27 +87,7 @@ export class EventViewPage extends AKElement {
</dt>
<dd class="pf-c-description-list__description">
<div class="pf-c-description-list__text">
${this.event.user?.username
? html`<div>
<a
href="#/identity/users/${this.event
.user.pk}"
>${this.event.user?.username}</a
>
</div>
${this.event.user.on_behalf_of
? html`<small>
<a
href="#/identity/users/${this
.event.user.on_behalf_of
.pk}"
>${msg(
str`On behalf of ${this.event.user.on_behalf_of.username}`,
)}</a
>
</small>`
: html``}`
: html`-`}
${EventUser(this.event)}
</div>
</dd>
</div>

View File

@ -1,6 +1,8 @@
import { truncate } from "@goauthentik/app/common/utils";
import { EventWithContext } from "@goauthentik/common/events";
import { KeyUnknown } from "@goauthentik/elements/forms/Form";
import { msg, str } from "@lit/localize";
import { TemplateResult, html } from "lit";
export function EventGeo(event: EventWithContext): TemplateResult {
@ -14,3 +16,29 @@ export function EventGeo(event: EventWithContext): TemplateResult {
}
return html``;
}
export function EventUser(event: EventWithContext, truncateUsername?: number): TemplateResult {
if (!event.user.username) {
return html`-`;
}
let body = html``;
if (event.user.is_anonymous) {
body = html`<div>${msg("Anonymous user")}</div>`;
} else {
body = html`<div>
<a href="#/identity/users/${event.user.pk}"
>${truncateUsername
? truncate(event.user?.username, truncateUsername)
: event.user?.username}</a
>
</div>`;
}
if (event.user.on_behalf_of) {
body = html`${body}<small>
<a href="#/identity/users/${event.user.on_behalf_of.pk}"
>${msg(str`On behalf of ${event.user.on_behalf_of.username}`)}</a
>
</small>`;
}
return body;
}

View File

@ -94,7 +94,7 @@ export class ReputationListPage extends TablePage<Reputation> {
html`${item.updated.toLocaleString()}`,
html`
<ak-rbac-object-permission-modal
model=${RbacPermissionsAssignedByUsersListModelEnum.PoliciesReputationReputation}
model=${RbacPermissionsAssignedByUsersListModelEnum.PoliciesReputationReputationpolicy}
objectPk=${item.pk || ""}
>
</ak-rbac-object-permission-modal>

View File

@ -32,7 +32,7 @@ import {
RbacPermissionsAssignedByUsersListModelEnum,
SCIMProvider,
SCIMSyncStatus,
TaskStatusEnum,
SystemTaskStatusEnum,
} from "@goauthentik/api";
@customElement("ak-provider-scim-view")
@ -143,15 +143,15 @@ export class SCIMProviderViewPage extends AKElement {
<ul class="pf-c-list">
${this.syncState.tasks.map((task) => {
let header = "";
if (task.status === TaskStatusEnum.Warning) {
if (task.status === SystemTaskStatusEnum.Warning) {
header = msg("Task finished with warnings");
} else if (task.status === TaskStatusEnum.Error) {
} else if (task.status === SystemTaskStatusEnum.Error) {
header = msg("Task finished with errors");
} else {
header = msg(str`Last sync: ${task.taskFinishTimestamp.toLocaleString()}`);
header = msg(str`Last sync: ${task.finishTimestamp.toLocaleString()}`);
}
return html`<li>
<p>${task.taskName}</p>
<p>${task.name}</p>
<ul class="pf-c-list">
<li>${header}</li>
${task.messages.map((m) => {

View File

@ -29,7 +29,7 @@ import {
LDAPSyncStatus,
RbacPermissionsAssignedByUsersListModelEnum,
SourcesApi,
TaskStatusEnum,
SystemTaskStatusEnum,
} from "@goauthentik/api";
@customElement("ak-source-ldap-view")
@ -77,15 +77,15 @@ export class LDAPSourceViewPage extends AKElement {
<ul class="pf-c-list">
${this.syncState.tasks.map((task) => {
let header = "";
if (task.status === TaskStatusEnum.Warning) {
if (task.status === SystemTaskStatusEnum.Warning) {
header = msg("Task finished with warnings");
} else if (task.status === TaskStatusEnum.Error) {
} else if (task.status === SystemTaskStatusEnum.Error) {
header = msg("Task finished with errors");
} else {
header = msg(str`Last sync: ${task.taskFinishTimestamp.toLocaleString()}`);
header = msg(str`Last sync: ${task.finishTimestamp.toLocaleString()}`);
}
return html`<li>
<p>${task.taskName}</p>
<p>${task.name}</p>
<ul class="pf-c-list">
<li>${header}</li>
${task.messages.map((m) => {

View File

@ -1,3 +1,4 @@
import { uiConfig } from "@goauthentik/app/common/ui/config";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EVENT_REFRESH } from "@goauthentik/common/constants";
import { PFColor } from "@goauthentik/elements/Label";
@ -14,13 +15,10 @@ import { customElement, property } from "lit/decorators.js";
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
import { AdminApi, Task, TaskStatusEnum } from "@goauthentik/api";
import { EventsApi, SystemTask, SystemTaskStatusEnum } from "@goauthentik/api";
@customElement("ak-system-task-list")
export class SystemTaskListPage extends TablePage<Task> {
searchEnabled(): boolean {
return false;
}
export class SystemTaskListPage extends TablePage<SystemTask> {
pageTitle(): string {
return msg("System Tasks");
}
@ -34,53 +32,45 @@ export class SystemTaskListPage extends TablePage<Task> {
expandable = true;
@property()
order = "slug";
order = "name";
static get styles(): CSSResult[] {
return super.styles.concat(PFDescriptionList);
}
async apiEndpoint(page: number): Promise<PaginatedResponse<Task>> {
return new AdminApi(DEFAULT_CONFIG).adminSystemTasksList().then((tasks) => {
return {
pagination: {
count: tasks.length,
totalPages: 1,
startIndex: 1,
endIndex: tasks.length,
current: page,
next: 0,
previous: 0,
},
results: tasks,
};
async apiEndpoint(page: number): Promise<PaginatedResponse<SystemTask>> {
return new EventsApi(DEFAULT_CONFIG).eventsSystemTasksList({
ordering: this.order,
page: page,
pageSize: (await uiConfig()).pagination.perPage,
search: this.search || "",
});
}
columns(): TableColumn[] {
return [
new TableColumn(msg("Identifier")),
new TableColumn(msg("Identifier"), "name"),
new TableColumn(msg("Description")),
new TableColumn(msg("Last run")),
new TableColumn(msg("Status")),
new TableColumn(msg("Status"), "status"),
new TableColumn(msg("Actions")),
];
}
taskStatus(task: Task): TemplateResult {
taskStatus(task: SystemTask): TemplateResult {
switch (task.status) {
case TaskStatusEnum.Successful:
case SystemTaskStatusEnum.Successful:
return html`<ak-label color=${PFColor.Green}>${msg("Successful")}</ak-label>`;
case TaskStatusEnum.Warning:
case SystemTaskStatusEnum.Warning:
return html`<ak-label color=${PFColor.Orange}>${msg("Warning")}</ak-label>`;
case TaskStatusEnum.Error:
case SystemTaskStatusEnum.Error:
return html`<ak-label color=${PFColor.Red}>${msg("Error")}</ak-label>`;
default:
return html`<ak-label color=${PFColor.Grey}>${msg("Unknown")}</ak-label>`;
}
}
renderExpanded(item: Task): TemplateResult {
renderExpanded(item: SystemTask): TemplateResult {
return html` <td role="cell" colspan="3">
<div class="pf-c-table__expandable-row-content">
<dl class="pf-c-description-list pf-m-horizontal">
@ -90,7 +80,7 @@ export class SystemTaskListPage extends TablePage<Task> {
</dt>
<dd class="pf-c-description-list__description">
<div class="pf-c-description-list__text">
${msg(str`${item.taskDuration.toFixed(2)} seconds`)}
${msg(str`${item.duration.toFixed(2)} seconds`)}
</div>
</dd>
</div>
@ -113,18 +103,18 @@ export class SystemTaskListPage extends TablePage<Task> {
<td></td>`;
}
row(item: Task): TemplateResult[] {
row(item: SystemTask): TemplateResult[] {
return [
html`${item.taskName}`,
html`${item.taskDescription}`,
html`${item.taskFinishTimestamp.toLocaleString()}`,
html`${item.name}${item.uid ? `:${item.uid}` : ""}`,
html`${item.description}`,
html`${item.finishTimestamp.toLocaleString()}`,
this.taskStatus(item),
html`<ak-action-button
class="pf-m-plain"
.apiRequest=${() => {
return new AdminApi(DEFAULT_CONFIG)
.adminSystemTasksRetryCreate({
id: item.taskName,
return new EventsApi(DEFAULT_CONFIG)
.eventsSystemTasksRunCreate({
uuid: item.uuid,
})
.then(() => {
this.dispatchEvent(

View File

@ -5,6 +5,7 @@ export interface EventUser {
email?: string;
username: string;
on_behalf_of?: EventUser;
is_anonymous?: boolean;
}
export interface EventContext {

View File

@ -1,4 +1,4 @@
import { EventGeo } from "@goauthentik/app/admin/events/utils";
import { EventGeo, EventUser } from "@goauthentik/app/admin/events/utils";
import { actionToLabel } from "@goauthentik/app/common/labels";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EventWithContext } from "@goauthentik/common/events";
@ -11,7 +11,7 @@ import "@goauthentik/elements/buttons/SpinnerButton";
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { Table, TableColumn } from "@goauthentik/elements/table/Table";
import { msg, str } from "@lit/localize";
import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit";
import { customElement, property } from "lit/decorators.js";
@ -76,12 +76,7 @@ export class ObjectChangelog extends Table<Event> {
row(item: EventWithContext): TemplateResult[] {
return [
html`${actionToLabel(item.action)}`,
html`<div>${item.user?.username}</div>
${item.user.on_behalf_of
? html`<small>
${msg(str`On behalf of ${item.user.on_behalf_of.username}`)}
</small>`
: html``}`,
EventUser(item),
html`<span>${item.created?.toLocaleString()}</span>`,
html`<div>${item.clientIp || msg("-")}</div>

View File

@ -1,3 +1,4 @@
import { EventUser } from "@goauthentik/app/admin/events/utils";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EventWithContext } from "@goauthentik/common/events";
import { actionToLabel } from "@goauthentik/common/labels";
@ -9,7 +10,7 @@ import "@goauthentik/elements/buttons/ModalButton";
import "@goauthentik/elements/buttons/SpinnerButton";
import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table";
import { msg, str } from "@lit/localize";
import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit";
import { customElement, property } from "lit/decorators.js";
@ -46,12 +47,7 @@ export class UserEvents extends Table<Event> {
row(item: EventWithContext): TemplateResult[] {
return [
html`${actionToLabel(item.action)}`,
html`<div>${item.user?.username}</div>
${item.user.on_behalf_of
? html`<small>
${msg(str`On behalf of ${item.user.on_behalf_of.username}`)}
</small>`
: html``}`,
EventUser(item),
html`<span>${item.created?.toLocaleString()}</span>`,
html`<span>${item.clientIp || msg("-")}</span>`,
];

View File

@ -160,7 +160,7 @@
</trans-unit>
<trans-unit id="s14622ee6de586485">
<source>Attempted to log in as <x id="0" equiv-text="${this.event.context.username}"/></source>
<target>Loginversuch als
<target>Loginversuch als
<x id="0" equiv-text="${this.event.context.username}"/></target>
</trans-unit>
<trans-unit id="sb07bf992e3d00664">
@ -308,8 +308,8 @@
<trans-unit id="s04c5a637328c9b67">
<source><x id="0" equiv-text="${this.pages?.startIndex}"/> - <x id="1" equiv-text="${this.pages?.endIndex}"/> of <x id="2" equiv-text="${this.pages?.count}"/></source>
<target>
<x id="0" equiv-text="${this.pages?.startIndex}"/>-
<x id="1" equiv-text="${this.pages?.endIndex}"/>von
<x id="0" equiv-text="${this.pages?.startIndex}"/>-
<x id="1" equiv-text="${this.pages?.endIndex}"/>von
<x id="2" equiv-text="${this.pages?.count}"/></target>
</trans-unit>
<trans-unit id="s6a89bb10338369b4">
@ -359,9 +359,9 @@
<source>Recent events</source>
</trans-unit>
<trans-unit id="sc35581d9c1cd67ff">
<source>On behalf of <x id="0" equiv-text="${item.user.on_behalf_of.username}"/></source>
<target>Im Namen von
<x id="0" equiv-text="${item.user.on_behalf_of.username}"/></target>
<source>On behalf of <x id="0" equiv-text="${event.user.on_behalf_of.username}"/></source>
<target>Im Namen von
<x id="0" equiv-text="${event.user.on_behalf_of.username}"/></target>
</trans-unit>
<trans-unit id="saf63a04c86018698">
<source>-</source>
@ -448,7 +448,7 @@
<trans-unit id="s0382d73823585617">
<source><x id="0" equiv-text="${this.errorMessage}"/>: <x id="1" equiv-text="${e.toString()}"/></source>
<target>
<x id="0" equiv-text="${this.errorMessage}"/>:
<x id="0" equiv-text="${this.errorMessage}"/>:
<x id="1" equiv-text="${e.toString()}"/></target>
</trans-unit>
<trans-unit id="s2ceb11be2290bb1b">
@ -479,7 +479,7 @@
</trans-unit>
<trans-unit id="saa0e2675da69651b">
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
<target>Die URL "
<target>Die URL "
<x id="0" equiv-text="${this.url}"/>" wurde nicht gefunden.</target>
</trans-unit>
<trans-unit id="s58cd9c2fe836d9c6">
@ -492,7 +492,7 @@
</trans-unit>
<trans-unit id="s6dfd15978586d05f">
<source>Welcome, <x id="0" equiv-text="${name}"/>.</source>
<target>Willkommen,
<target>Willkommen,
<x id="0" equiv-text="${name}"/>!</target>
</trans-unit>
<trans-unit id="sc381422c585b867f">
@ -531,7 +531,7 @@
</trans-unit>
<trans-unit id="sda5e1499f93146ad">
<source><x id="0" equiv-text="${ago}"/> days ago</source>
<target>vor
<target>vor
<x id="0" equiv-text="${ago}"/>Tagen</target>
</trans-unit>
<trans-unit id="s51ea3a244c781b1f">
@ -598,7 +598,7 @@
<source>Duration</source>
</trans-unit>
<trans-unit id="se7e1ababbc4868b8">
<source><x id="0" equiv-text="${item.taskDuration.toFixed(2)}"/> seconds</source>
<source><x id="0" equiv-text="${item.duration.toFixed(2)}"/> seconds</source>
</trans-unit>
<trans-unit id="sc25edca57df81461">
<source>Authentication</source>
@ -1299,7 +1299,7 @@
<trans-unit id="s55fa598b754cc3cc">
<source><x id="0" equiv-text="${ub.name}"/> (<x id="1" equiv-text="${consequence}"/>)</source>
<target>
<x id="0" equiv-text="${ub.name}"/>(
<x id="0" equiv-text="${ub.name}"/>(
<x id="1" equiv-text="${consequence}"/>)</target>
</trans-unit>
<trans-unit id="s09240e07b5b8d640">
@ -1311,8 +1311,8 @@
</trans-unit>
<trans-unit id="sf6eb148db23d19de">
<source>Failed to delete <x id="0" equiv-text="${this.objectLabel}"/>: <x id="1" equiv-text="${e.toString()}"/></source>
<target>Löschen von
<x id="0" equiv-text="${this.objectLabel}"/>fehlgeschlagen:
<target>Löschen von
<x id="0" equiv-text="${this.objectLabel}"/>fehlgeschlagen:
<x id="1" equiv-text="${e.toString()}"/></target>
</trans-unit>
<trans-unit id="s039b6434e8a75560">
@ -1360,7 +1360,7 @@
</trans-unit>
<trans-unit id="sc9175cb129fdc306">
<source>Update <x id="0" equiv-text="${item.verboseName}"/></source>
<target>Aktualisiere
<target>Aktualisiere
<x id="0" equiv-text="${item.verboseName}"/></target>
</trans-unit>
<trans-unit id="s398f6ba74ba8943a">
@ -2065,17 +2065,17 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s030ac0829bb50a49">
<source>Policy <x id="0" equiv-text="${item.policyObj?.name}"/></source>
<target>Richtlinie
<target>Richtlinie
<x id="0" equiv-text="${item.policyObj?.name}"/></target>
</trans-unit>
<trans-unit id="s2a64d2dca3da9b0e">
<source>Group <x id="0" equiv-text="${item.groupObj?.name}"/></source>
<target>Gruppe
<target>Gruppe
<x id="0" equiv-text="${item.groupObj?.name}"/></target>
</trans-unit>
<trans-unit id="se5dc026819a32ff8">
<source>User <x id="0" equiv-text="${item.userObj?.name}"/></source>
<target>Benutzer
<target>Benutzer
<x id="0" equiv-text="${item.userObj?.name}"/></target>
</trans-unit>
<trans-unit id="s50c312bea93b6925">
@ -2553,9 +2553,9 @@ doesn't pass when either or both of the selected options are equal or above the
<target>Aufgabe mit Fehlern beendet</target>
</trans-unit>
<trans-unit id="sbedb77365a066648">
<source>Last sync: <x id="0" equiv-text="${task.taskFinishTimestamp.toLocaleString()}"/></source>
<target>Letzte Synchronisierung:
<x id="0" equiv-text="${task.taskFinishTimestamp.toLocaleString()}"/></target>
<source>Last sync: <x id="0" equiv-text="${task.finishTimestamp.toLocaleString()}"/></source>
<target>Letzte Synchronisierung:
<x id="0" equiv-text="${task.finishTimestamp.toLocaleString()}"/></target>
</trans-unit>
<trans-unit id="sf3fec8353106ac2f">
<source>OAuth Source <x id="0" equiv-text="${this.source.name}"/></source>
@ -2900,7 +2900,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="se16ac750b81fa93d">
<source>Assigned to <x id="0" equiv-text="${item.boundTo}"/> object(s).</source>
<target>Zugewiesen zu
<target>Zugewiesen zu
<x id="0" equiv-text="${item.boundTo}"/>Objekt(en).</target>
</trans-unit>
<trans-unit id="s5a48d5171e1a1522">
@ -3000,7 +3000,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s4414164d120de61a">
<source>The following objects use <x id="0" equiv-text="${objName}"/></source>
<target>Die folgenden Objekte verwenden
<target>Die folgenden Objekte verwenden
<x id="0" equiv-text="${objName}"/></target>
</trans-unit>
<trans-unit id="s92e241c9f3c101a2">
@ -3012,14 +3012,14 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s14401ff4a0cba208">
<source>Failed to update <x id="0" equiv-text="${this.objectLabel}"/>: <x id="1" equiv-text="${e.toString()}"/></source>
<target>Aktualisieren von
<x id="0" equiv-text="${this.objectLabel}"/>fehlgeschlagen:
<target>Aktualisieren von
<x id="0" equiv-text="${this.objectLabel}"/>fehlgeschlagen:
<x id="1" equiv-text="${e.toString()}"/></target>
</trans-unit>
<trans-unit id="sa95a538bfbb86111">
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
<target>Sind Sie sicher, dass Sie
<x id="0" equiv-text="${this.objectLabel}"/>"
<target>Sind Sie sicher, dass Sie
<x id="0" equiv-text="${this.objectLabel}"/>"
<x id="1" equiv-text="${this.obj?.name}"/>" aktualisieren wollen?</target>
</trans-unit>
<trans-unit id="sc92d7cfb6ee1fec6">
@ -3150,7 +3150,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s3616cc78631f5893">
<source>Warning: You're about to delete the user you're logged in as (<x id="0" equiv-text="${shouldShowWarning.username}"/>). Proceed at your own risk.</source>
<target>Warnung: Sie sind im Begriff, den Benutzer zu löschen, als den Sie angemeldet sind (
<target>Warnung: Sie sind im Begriff, den Benutzer zu löschen, als den Sie angemeldet sind (
<x id="0" equiv-text="${shouldShowWarning.username}"/>). Fahren Sie auf eigene Gefahr fort.</target>
</trans-unit>
<trans-unit id="s510c7add9e24c306">
@ -4092,8 +4092,8 @@ doesn't pass when either or both of the selected options are equal or above the
<trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
<target>
<x id="0" equiv-text="${prompt.name}"/>("
<x id="1" equiv-text="${prompt.fieldKey}"/>", vom Typ
<x id="0" equiv-text="${prompt.name}"/>("
<x id="1" equiv-text="${prompt.fieldKey}"/>", vom Typ
<x id="2" equiv-text="${prompt.type}"/>)</target>
</trans-unit>
<trans-unit id="s3b7b519444181264">
@ -4448,7 +4448,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s65d67612999165e9">
<source>Event <x id="0" equiv-text="${this.event.pk}"/></source>
<target>Ereignis
<target>Ereignis
<x id="0" equiv-text="${this.event.pk}"/></target>
</trans-unit>
<trans-unit id="s455de2f740b073fd">
@ -4627,8 +4627,8 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s1ac2653a6492b435">
<source><x id="0" equiv-text="${this.outpostHealth.version}"/>, should be <x id="1" equiv-text="${this.outpostHealth.versionShould}"/></source>
<target>"
<x id="0" equiv-text="${this.outpostHealth.version}"/>", sollte "
<target>"
<x id="0" equiv-text="${this.outpostHealth.version}"/>", sollte "
<x id="1" equiv-text="${this.outpostHealth.versionShould}"/>" sein</target>
</trans-unit>
<trans-unit id="s1e176e35c828318c">
@ -4640,7 +4640,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s02b632a9ac24a824">
<source>Last seen: <x id="0" equiv-text="${this.outpostHealth.lastSeen?.toLocaleTimeString()}"/></source>
<target>Überprüft:
<target>Überprüft:
<x id="0" equiv-text="${this.outpostHealth.lastSeen?.toLocaleTimeString()}"/></target>
</trans-unit>
<trans-unit id="sa43153d53ae65063">
@ -4664,7 +4664,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sbf5f4c5ba679e847">
<source>Logging in via <x id="0" equiv-text="${item.config.authentik_host}"/>.</source>
<target>Anmelden über
<target>Anmelden über
<x id="0" equiv-text="${item.config.authentik_host}"/>.</target>
</trans-unit>
<trans-unit id="s59b6028f19d15cda">
@ -4819,7 +4819,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sef50d248448e0df1">
<source>Yes (<x id="0" equiv-text="${item.privateKeyType?.toUpperCase()}"/>)</source>
<target>Ja (
<target>Ja (
<x id="0" equiv-text="${item.privateKeyType?.toUpperCase()}"/>)</target>
</trans-unit>
<trans-unit id="s09205907b5b56cda">
@ -4944,7 +4944,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s96b3cddf33e1c853">
<source>You're currently impersonating <x id="0" equiv-text="${this.impersonation}"/>. Click to stop.</source>
<target>Sie geben sich gerade als
<target>Sie geben sich gerade als
<x id="0" equiv-text="${this.impersonation}"/>aus. Klicken Sie zum Stoppen.</target>
</trans-unit>
<trans-unit id="s7031e6928c44cedd">
@ -5042,7 +5042,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s670ad066cc0e50a3">
<source>Login to continue to <x id="0" equiv-text="${this.challenge.applicationPre}"/>.</source>
<target>Anmelden um mit
<target>Anmelden um mit
<x id="0" equiv-text="${this.challenge.applicationPre}"/>fortzufahren.</target>
</trans-unit>
<trans-unit id="scf5ce91bfba10a61">
@ -5147,12 +5147,12 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sbbb7318812d64e51">
<source>Error when creating credential: <x id="0" equiv-text="${err}"/></source>
<target>Fehler beim Erstellen der Anmeldedaten:
<target>Fehler beim Erstellen der Anmeldedaten:
<x id="0" equiv-text="${err}"/></target>
</trans-unit>
<trans-unit id="sfe199b2564b66054">
<source>Error when validating assertion on server: <x id="0" equiv-text="${err}"/></source>
<target>Fehler beim Validieren der Assertion auf dem Server:
<target>Fehler beim Validieren der Assertion auf dem Server:
<x id="0" equiv-text="${err}"/></target>
</trans-unit>
<trans-unit id="se409d01b52c4e12f">
@ -5288,12 +5288,12 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s7fa4e5e409d43573">
<source>Error creating credential: <x id="0" equiv-text="${err}"/></source>
<target>Fehler beim Erstellen der Anmeldedaten:
<target>Fehler beim Erstellen der Anmeldedaten:
<x id="0" equiv-text="${err}"/></target>
</trans-unit>
<trans-unit id="s9d95f09deb601f34">
<source>Server validation of credential failed: <x id="0" equiv-text="${err}"/></source>
<target>Servervalidierung der Anmeldedaten fehlgeschlagen:
<target>Servervalidierung der Anmeldedaten fehlgeschlagen:
<x id="0" equiv-text="${err}"/></target>
</trans-unit>
<trans-unit id="s6c8f05e3be04f62a">
@ -5362,7 +5362,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s67dedada007d4067">
<source>Failed to disconnected source: <x id="0" equiv-text="${exc}"/></source>
<target>Quelle konnte nicht getrennt werden:
<target>Quelle konnte nicht getrennt werden:
<x id="0" equiv-text="${exc}"/></target>
</trans-unit>
<trans-unit id="sd2208cd1a767644b">
@ -5375,7 +5375,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sababff57115130a0">
<source>Error: unsupported source settings: <x id="0" equiv-text="${source.component}"/></source>
<target>Fehler: nicht unterstützte Quelleinstellungen:
<target>Fehler: nicht unterstützte Quelleinstellungen:
<x id="0" equiv-text="${source.component}"/></target>
</trans-unit>
<trans-unit id="sd1031bddc66dc495">
@ -6328,6 +6328,24 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="see1eb81c1f734079">
<source>System settings</source>
</trans-unit>
<trans-unit id="s47fb5504f693775b">
<source>Changes made:</source>
</trans-unit>
<trans-unit id="s506b6a19d12f414c">
<source>Key</source>
</trans-unit>
<trans-unit id="s4fc9dc73245eab09">
<source>Previous value</source>
</trans-unit>
<trans-unit id="scb3b0671d7b7f640">
<source>New value</source>
</trans-unit>
<trans-unit id="s4a642406b0745917">
<source>Raw event info</source>
</trans-unit>
<trans-unit id="sa65e7bc7ddd3484d">
<source>Anonymous user</source>
</trans-unit>
</body>
</file>

View File

@ -160,7 +160,7 @@
</trans-unit>
<trans-unit id="s14622ee6de586485">
<source>Attempted to log in as <x id="0" equiv-text="${this.event.context.username}"/></source>
<target>Attempted to log in as
<target>Attempted to log in as
<x id="0" equiv-text="${this.event.context.username}"/></target>
</trans-unit>
<trans-unit id="sb07bf992e3d00664">
@ -310,8 +310,8 @@
<trans-unit id="s04c5a637328c9b67">
<source><x id="0" equiv-text="${this.pages?.startIndex}"/> - <x id="1" equiv-text="${this.pages?.endIndex}"/> of <x id="2" equiv-text="${this.pages?.count}"/></source>
<target>
<x id="0" equiv-text="${this.pages?.startIndex}"/>-
<x id="1" equiv-text="${this.pages?.endIndex}"/>of
<x id="0" equiv-text="${this.pages?.startIndex}"/>-
<x id="1" equiv-text="${this.pages?.endIndex}"/>of
<x id="2" equiv-text="${this.pages?.count}"/></target>
</trans-unit>
<trans-unit id="s6a89bb10338369b4">
@ -363,9 +363,9 @@
<target>Recent events</target>
</trans-unit>
<trans-unit id="sc35581d9c1cd67ff">
<source>On behalf of <x id="0" equiv-text="${item.user.on_behalf_of.username}"/></source>
<target>On behalf of
<x id="0" equiv-text="${item.user.on_behalf_of.username}"/></target>
<source>On behalf of <x id="0" equiv-text="${event.user.on_behalf_of.username}"/></source>
<target>On behalf of
<x id="0" equiv-text="${event.user.on_behalf_of.username}"/></target>
</trans-unit>
<trans-unit id="saf63a04c86018698">
<source>-</source>
@ -409,7 +409,7 @@
</trans-unit>
<trans-unit id="scefe482c547fb3f3">
<source>Based on <x id="0" equiv-text="${value.versionCurrent}"/></source>
<target>Based on
<target>Based on
<x id="0" equiv-text="${value.versionCurrent}"/></target>
</trans-unit>
<trans-unit id="s68a50b1ee6efee7b">
@ -458,7 +458,7 @@
<trans-unit id="s0382d73823585617">
<source><x id="0" equiv-text="${this.errorMessage}"/>: <x id="1" equiv-text="${e.toString()}"/></source>
<target>
<x id="0" equiv-text="${this.errorMessage}"/>:
<x id="0" equiv-text="${this.errorMessage}"/>:
<x id="1" equiv-text="${e.toString()}"/></target>
</trans-unit>
<trans-unit id="s2ceb11be2290bb1b">
@ -491,7 +491,7 @@
</trans-unit>
<trans-unit id="saa0e2675da69651b">
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
<target>The URL "
<target>The URL "
<x id="0" equiv-text="${this.url}"/>" was not found.</target>
</trans-unit>
<trans-unit id="s58cd9c2fe836d9c6">
@ -504,7 +504,7 @@
</trans-unit>
<trans-unit id="s6dfd15978586d05f">
<source>Welcome, <x id="0" equiv-text="${name}"/>.</source>
<target>Welcome,
<target>Welcome,
<x id="0" equiv-text="${name}"/>.</target>
</trans-unit>
<trans-unit id="sc381422c585b867f">
@ -613,9 +613,9 @@
<target>Duration</target>
</trans-unit>
<trans-unit id="se7e1ababbc4868b8">
<source><x id="0" equiv-text="${item.taskDuration.toFixed(2)}"/> seconds</source>
<source><x id="0" equiv-text="${item.duration.toFixed(2)}"/> seconds</source>
<target>
<x id="0" equiv-text="${item.taskDuration.toFixed(2)}"/>seconds</target>
<x id="0" equiv-text="${item.duration.toFixed(2)}"/>seconds</target>
</trans-unit>
<trans-unit id="sc25edca57df81461">
<source>Authentication</source>
@ -1275,7 +1275,7 @@
</trans-unit>
<trans-unit id="s5d6af4c100ad321b">
<source>Create <x id="0" equiv-text="${type.name}"/></source>
<target>Create
<target>Create
<x id="0" equiv-text="${type.name}"/></target>
</trans-unit>
<trans-unit id="sb95baab425322600">
@ -1366,7 +1366,7 @@
<trans-unit id="s55fa598b754cc3cc">
<source><x id="0" equiv-text="${ub.name}"/> (<x id="1" equiv-text="${consequence}"/>)</source>
<target>
<x id="0" equiv-text="${ub.name}"/>(
<x id="0" equiv-text="${ub.name}"/>(
<x id="1" equiv-text="${consequence}"/>)</target>
</trans-unit>
<trans-unit id="s09240e07b5b8d640">
@ -1378,13 +1378,13 @@
</trans-unit>
<trans-unit id="sf6eb148db23d19de">
<source>Failed to delete <x id="0" equiv-text="${this.objectLabel}"/>: <x id="1" equiv-text="${e.toString()}"/></source>
<target>Failed to delete
<x id="0" equiv-text="${this.objectLabel}"/>:
<target>Failed to delete
<x id="0" equiv-text="${this.objectLabel}"/>:
<x id="1" equiv-text="${e.toString()}"/></target>
</trans-unit>
<trans-unit id="s039b6434e8a75560">
<source>Delete <x id="0" equiv-text="${this.objectLabel}"/></source>
<target>Delete
<target>Delete
<x id="0" equiv-text="${this.objectLabel}"/></target>
</trans-unit>
<trans-unit id="s5819a49638f6d7cb">
@ -1428,7 +1428,7 @@
</trans-unit>
<trans-unit id="sc9175cb129fdc306">
<source>Update <x id="0" equiv-text="${item.verboseName}"/></source>
<target>Update
<target>Update
<x id="0" equiv-text="${item.verboseName}"/></target>
</trans-unit>
<trans-unit id="s398f6ba74ba8943a">
@ -2173,17 +2173,17 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s030ac0829bb50a49">
<source>Policy <x id="0" equiv-text="${item.policyObj?.name}"/></source>
<target>Policy
<target>Policy
<x id="0" equiv-text="${item.policyObj?.name}"/></target>
</trans-unit>
<trans-unit id="s2a64d2dca3da9b0e">
<source>Group <x id="0" equiv-text="${item.groupObj?.name}"/></source>
<target>Group
<target>Group
<x id="0" equiv-text="${item.groupObj?.name}"/></target>
</trans-unit>
<trans-unit id="se5dc026819a32ff8">
<source>User <x id="0" equiv-text="${item.userObj?.name}"/></source>
<target>User
<target>User
<x id="0" equiv-text="${item.userObj?.name}"/></target>
</trans-unit>
<trans-unit id="s50c312bea93b6925">
@ -2675,13 +2675,13 @@ doesn't pass when either or both of the selected options are equal or above the
<target>Task finished with errors</target>
</trans-unit>
<trans-unit id="sbedb77365a066648">
<source>Last sync: <x id="0" equiv-text="${task.taskFinishTimestamp.toLocaleString()}"/></source>
<target>Last sync:
<x id="0" equiv-text="${task.taskFinishTimestamp.toLocaleString()}"/></target>
<source>Last sync: <x id="0" equiv-text="${task.finishTimestamp.toLocaleString()}"/></source>
<target>Last sync:
<x id="0" equiv-text="${task.finishTimestamp.toLocaleString()}"/></target>
</trans-unit>
<trans-unit id="sf3fec8353106ac2f">
<source>OAuth Source <x id="0" equiv-text="${this.source.name}"/></source>
<target>OAuth Source
<target>OAuth Source
<x id="0" equiv-text="${this.source.name}"/></target>
</trans-unit>
<trans-unit id="se09d055771f3a11d">
@ -3034,7 +3034,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="se16ac750b81fa93d">
<source>Assigned to <x id="0" equiv-text="${item.boundTo}"/> object(s).</source>
<target>Assigned to
<target>Assigned to
<x id="0" equiv-text="${item.boundTo}"/>object(s).</target>
</trans-unit>
<trans-unit id="s5a48d5171e1a1522">
@ -3134,7 +3134,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s4414164d120de61a">
<source>The following objects use <x id="0" equiv-text="${objName}"/></source>
<target>The following objects use
<target>The following objects use
<x id="0" equiv-text="${objName}"/></target>
</trans-unit>
<trans-unit id="s92e241c9f3c101a2">
@ -3146,14 +3146,14 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s14401ff4a0cba208">
<source>Failed to update <x id="0" equiv-text="${this.objectLabel}"/>: <x id="1" equiv-text="${e.toString()}"/></source>
<target>Failed to update
<x id="0" equiv-text="${this.objectLabel}"/>:
<target>Failed to update
<x id="0" equiv-text="${this.objectLabel}"/>:
<x id="1" equiv-text="${e.toString()}"/></target>
</trans-unit>
<trans-unit id="sa95a538bfbb86111">
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
<target>Are you sure you want to update
<x id="0" equiv-text="${this.objectLabel}"/>"
<target>Are you sure you want to update
<x id="0" equiv-text="${this.objectLabel}"/>"
<x id="1" equiv-text="${this.obj?.name}"/>"?</target>
</trans-unit>
<trans-unit id="sc92d7cfb6ee1fec6">
@ -3186,7 +3186,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s5d7748b1d2363478">
<source>Are you sure you want to remove the selected users from the group <x id="0" equiv-text="${this.targetGroup?.name}"/>?</source>
<target>Are you sure you want to remove the selected users from the group
<target>Are you sure you want to remove the selected users from the group
<x id="0" equiv-text="${this.targetGroup?.name}"/>?</target>
</trans-unit>
<trans-unit id="sea4f08110bb8f15d">
@ -3295,7 +3295,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s3616cc78631f5893">
<source>Warning: You're about to delete the user you're logged in as (<x id="0" equiv-text="${shouldShowWarning.username}"/>). Proceed at your own risk.</source>
<target>Warning: You're about to delete the user you're logged in as (
<target>Warning: You're about to delete the user you're logged in as (
<x id="0" equiv-text="${shouldShowWarning.username}"/>). Proceed at your own risk.</target>
</trans-unit>
<trans-unit id="s510c7add9e24c306">
@ -3320,7 +3320,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="sb4c9ed2a487b238f">
<source>Are you sure you want to remove user <x id="0" equiv-text="${this.targetUser?.username}"/> from the following groups?</source>
<target>Are you sure you want to remove user
<target>Are you sure you want to remove user
<x id="0" equiv-text="${this.targetUser?.username}"/>from the following groups?</target>
</trans-unit>
<trans-unit id="s964f6725aeb7662f">
@ -4284,8 +4284,8 @@ doesn't pass when either or both of the selected options are equal or above the
<trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
<target>
<x id="0" equiv-text="${prompt.name}"/>("
<x id="1" equiv-text="${prompt.fieldKey}"/>", of type
<x id="0" equiv-text="${prompt.name}"/>("
<x id="1" equiv-text="${prompt.fieldKey}"/>", of type
<x id="2" equiv-text="${prompt.type}"/>)</target>
</trans-unit>
<trans-unit id="s3b7b519444181264">
@ -4417,7 +4417,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s77299a9d3dd932cd">
<source>Successfully imported <x id="0" equiv-text="${res.count}"/> devices.</source>
<target>Successfully imported
<target>Successfully imported
<x id="0" equiv-text="${res.count}"/>devices.</target>
</trans-unit>
<trans-unit id="s6a615f6165ef01c9">
@ -4678,7 +4678,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s65d67612999165e9">
<source>Event <x id="0" equiv-text="${this.event.pk}"/></source>
<target>Event
<target>Event
<x id="0" equiv-text="${this.event.pk}"/></target>
</trans-unit>
<trans-unit id="s455de2f740b073fd">
@ -4864,7 +4864,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s1ac2653a6492b435">
<source><x id="0" equiv-text="${this.outpostHealth.version}"/>, should be <x id="1" equiv-text="${this.outpostHealth.versionShould}"/></source>
<target>
<x id="0" equiv-text="${this.outpostHealth.version}"/>, should be
<x id="0" equiv-text="${this.outpostHealth.version}"/>, should be
<x id="1" equiv-text="${this.outpostHealth.versionShould}"/></target>
</trans-unit>
<trans-unit id="s1e176e35c828318c">
@ -4877,7 +4877,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s02b632a9ac24a824">
<source>Last seen: <x id="0" equiv-text="${this.outpostHealth.lastSeen?.toLocaleTimeString()}"/></source>
<target>Last seen:
<target>Last seen:
<x id="0" equiv-text="${this.outpostHealth.lastSeen?.toLocaleTimeString()}"/></target>
</trans-unit>
<trans-unit id="sa43153d53ae65063">
@ -4902,7 +4902,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sbf5f4c5ba679e847">
<source>Logging in via <x id="0" equiv-text="${item.config.authentik_host}"/>.</source>
<target>Logging in via
<target>Logging in via
<x id="0" equiv-text="${item.config.authentik_host}"/>.</target>
</trans-unit>
<trans-unit id="s59b6028f19d15cda">
@ -5059,7 +5059,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sef50d248448e0df1">
<source>Yes (<x id="0" equiv-text="${item.privateKeyType?.toUpperCase()}"/>)</source>
<target>Yes (
<target>Yes (
<x id="0" equiv-text="${item.privateKeyType?.toUpperCase()}"/>)</target>
</trans-unit>
<trans-unit id="s09205907b5b56cda">
@ -5201,7 +5201,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s96b3cddf33e1c853">
<source>You're currently impersonating <x id="0" equiv-text="${this.impersonation}"/>. Click to stop.</source>
<target>You're currently impersonating
<target>You're currently impersonating
<x id="0" equiv-text="${this.impersonation}"/>. Click to stop.</target>
</trans-unit>
<trans-unit id="s7031e6928c44cedd">
@ -5302,7 +5302,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s670ad066cc0e50a3">
<source>Login to continue to <x id="0" equiv-text="${this.challenge.applicationPre}"/>.</source>
<target>Login to continue to
<target>Login to continue to
<x id="0" equiv-text="${this.challenge.applicationPre}"/>.</target>
</trans-unit>
<trans-unit id="scf5ce91bfba10a61">
@ -5415,12 +5415,12 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sbbb7318812d64e51">
<source>Error when creating credential: <x id="0" equiv-text="${err}"/></source>
<target>Error when creating credential:
<target>Error when creating credential:
<x id="0" equiv-text="${err}"/></target>
</trans-unit>
<trans-unit id="sfe199b2564b66054">
<source>Error when validating assertion on server: <x id="0" equiv-text="${err}"/></source>
<target>Error when validating assertion on server:
<target>Error when validating assertion on server:
<x id="0" equiv-text="${err}"/></target>
</trans-unit>
<trans-unit id="se409d01b52c4e12f">
@ -5561,12 +5561,12 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s7fa4e5e409d43573">
<source>Error creating credential: <x id="0" equiv-text="${err}"/></source>
<target>Error creating credential:
<target>Error creating credential:
<x id="0" equiv-text="${err}"/></target>
</trans-unit>
<trans-unit id="s9d95f09deb601f34">
<source>Server validation of credential failed: <x id="0" equiv-text="${err}"/></source>
<target>Server validation of credential failed:
<target>Server validation of credential failed:
<x id="0" equiv-text="${err}"/></target>
</trans-unit>
<trans-unit id="s6c8f05e3be04f62a">
@ -5635,7 +5635,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s67dedada007d4067">
<source>Failed to disconnected source: <x id="0" equiv-text="${exc}"/></source>
<target>Failed to disconnected source:
<target>Failed to disconnected source:
<x id="0" equiv-text="${exc}"/></target>
</trans-unit>
<trans-unit id="sd2208cd1a767644b">
@ -5648,7 +5648,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sababff57115130a0">
<source>Error: unsupported source settings: <x id="0" equiv-text="${source.component}"/></source>
<target>Error: unsupported source settings:
<target>Error: unsupported source settings:
<x id="0" equiv-text="${source.component}"/></target>
</trans-unit>
<trans-unit id="sd1031bddc66dc495">
@ -6603,6 +6603,24 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="see1eb81c1f734079">
<source>System settings</source>
</trans-unit>
<trans-unit id="s47fb5504f693775b">
<source>Changes made:</source>
</trans-unit>
<trans-unit id="s506b6a19d12f414c">
<source>Key</source>
</trans-unit>
<trans-unit id="s4fc9dc73245eab09">
<source>Previous value</source>
</trans-unit>
<trans-unit id="scb3b0671d7b7f640">
<source>New value</source>
</trans-unit>
<trans-unit id="s4a642406b0745917">
<source>Raw event info</source>
</trans-unit>
<trans-unit id="sa65e7bc7ddd3484d">
<source>Anonymous user</source>
</trans-unit>
</body>
</file>

View File

@ -154,7 +154,7 @@
</trans-unit>
<trans-unit id="s14622ee6de586485">
<source>Attempted to log in as <x id="0" equiv-text="${this.event.context.username}"/></source>
<target>Se intentó iniciar sesión como
<target>Se intentó iniciar sesión como
<x id="0" equiv-text="${this.event.context.username}"/></target>
</trans-unit>
<trans-unit id="sb07bf992e3d00664">
@ -301,8 +301,8 @@
<trans-unit id="s04c5a637328c9b67">
<source><x id="0" equiv-text="${this.pages?.startIndex}"/> - <x id="1" equiv-text="${this.pages?.endIndex}"/> of <x id="2" equiv-text="${this.pages?.count}"/></source>
<target>
<x id="0" equiv-text="${this.pages?.startIndex}"/>-
<x id="1" equiv-text="${this.pages?.endIndex}"/>de
<x id="0" equiv-text="${this.pages?.startIndex}"/>-
<x id="1" equiv-text="${this.pages?.endIndex}"/>de
<x id="2" equiv-text="${this.pages?.count}"/></target>
</trans-unit>
<trans-unit id="s6a89bb10338369b4">
@ -352,9 +352,9 @@
<source>Recent events</source>
</trans-unit>
<trans-unit id="sc35581d9c1cd67ff">
<source>On behalf of <x id="0" equiv-text="${item.user.on_behalf_of.username}"/></source>
<target>En nombre de
<x id="0" equiv-text="${item.user.on_behalf_of.username}"/></target>
<source>On behalf of <x id="0" equiv-text="${event.user.on_behalf_of.username}"/></source>
<target>En nombre de
<x id="0" equiv-text="${event.user.on_behalf_of.username}"/></target>
</trans-unit>
<trans-unit id="saf63a04c86018698">
<source>-</source>
@ -441,7 +441,7 @@
<trans-unit id="s0382d73823585617">
<source><x id="0" equiv-text="${this.errorMessage}"/>: <x id="1" equiv-text="${e.toString()}"/></source>
<target>
<x id="0" equiv-text="${this.errorMessage}"/>:
<x id="0" equiv-text="${this.errorMessage}"/>:
<x id="1" equiv-text="${e.toString()}"/></target>
</trans-unit>
<trans-unit id="s2ceb11be2290bb1b">
@ -472,7 +472,7 @@
</trans-unit>
<trans-unit id="saa0e2675da69651b">
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
<target>No se encontró la URL «
<target>No se encontró la URL «
<x id="0" equiv-text="${this.url}"/>».</target>
</trans-unit>
<trans-unit id="s58cd9c2fe836d9c6">
@ -485,7 +485,7 @@
</trans-unit>
<trans-unit id="s6dfd15978586d05f">
<source>Welcome, <x id="0" equiv-text="${name}"/>.</source>
<target>Bienvenido,
<target>Bienvenido,
<x id="0" equiv-text="${name}"/>.</target>
</trans-unit>
<trans-unit id="sc381422c585b867f">
@ -590,7 +590,7 @@
<source>Duration</source>
</trans-unit>
<trans-unit id="se7e1ababbc4868b8">
<source><x id="0" equiv-text="${item.taskDuration.toFixed(2)}"/> seconds</source>
<source><x id="0" equiv-text="${item.duration.toFixed(2)}"/> seconds</source>
</trans-unit>
<trans-unit id="sc25edca57df81461">
<source>Authentication</source>
@ -1192,7 +1192,7 @@
</trans-unit>
<trans-unit id="s5d6af4c100ad321b">
<source>Create <x id="0" equiv-text="${type.name}"/></source>
<target>Crear
<target>Crear
<x id="0" equiv-text="${type.name}"/></target>
</trans-unit>
<trans-unit id="sb95baab425322600">
@ -1273,7 +1273,7 @@
<trans-unit id="s55fa598b754cc3cc">
<source><x id="0" equiv-text="${ub.name}"/> (<x id="1" equiv-text="${consequence}"/>)</source>
<target>
<x id="0" equiv-text="${ub.name}"/>(
<x id="0" equiv-text="${ub.name}"/>(
<x id="1" equiv-text="${consequence}"/>)</target>
</trans-unit>
<trans-unit id="s09240e07b5b8d640">
@ -1285,13 +1285,13 @@
</trans-unit>
<trans-unit id="sf6eb148db23d19de">
<source>Failed to delete <x id="0" equiv-text="${this.objectLabel}"/>: <x id="1" equiv-text="${e.toString()}"/></source>
<target>No se pudo eliminar
<x id="0" equiv-text="${this.objectLabel}"/>:
<target>No se pudo eliminar
<x id="0" equiv-text="${this.objectLabel}"/>:
<x id="1" equiv-text="${e.toString()}"/></target>
</trans-unit>
<trans-unit id="s039b6434e8a75560">
<source>Delete <x id="0" equiv-text="${this.objectLabel}"/></source>
<target>Eliminar
<target>Eliminar
<x id="0" equiv-text="${this.objectLabel}"/></target>
</trans-unit>
<trans-unit id="s5819a49638f6d7cb">
@ -1334,7 +1334,7 @@
</trans-unit>
<trans-unit id="sc9175cb129fdc306">
<source>Update <x id="0" equiv-text="${item.verboseName}"/></source>
<target>Actualización
<target>Actualización
<x id="0" equiv-text="${item.verboseName}"/></target>
</trans-unit>
<trans-unit id="s398f6ba74ba8943a">
@ -2031,17 +2031,17 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s030ac0829bb50a49">
<source>Policy <x id="0" equiv-text="${item.policyObj?.name}"/></source>
<target>Política
<target>Política
<x id="0" equiv-text="${item.policyObj?.name}"/></target>
</trans-unit>
<trans-unit id="s2a64d2dca3da9b0e">
<source>Group <x id="0" equiv-text="${item.groupObj?.name}"/></source>
<target>Grupo
<target>Grupo
<x id="0" equiv-text="${item.groupObj?.name}"/></target>
</trans-unit>
<trans-unit id="se5dc026819a32ff8">
<source>User <x id="0" equiv-text="${item.userObj?.name}"/></source>
<target>Usuario
<target>Usuario
<x id="0" equiv-text="${item.userObj?.name}"/></target>
</trans-unit>
<trans-unit id="s50c312bea93b6925">
@ -2513,9 +2513,9 @@ doesn't pass when either or both of the selected options are equal or above the
<target>La tarea ha finalizado con errores</target>
</trans-unit>
<trans-unit id="sbedb77365a066648">
<source>Last sync: <x id="0" equiv-text="${task.taskFinishTimestamp.toLocaleString()}"/></source>
<target>Última sincronización:
<x id="0" equiv-text="${task.taskFinishTimestamp.toLocaleString()}"/></target>
<source>Last sync: <x id="0" equiv-text="${task.finishTimestamp.toLocaleString()}"/></source>
<target>Última sincronización:
<x id="0" equiv-text="${task.finishTimestamp.toLocaleString()}"/></target>
</trans-unit>
<trans-unit id="sf3fec8353106ac2f">
<source>OAuth Source <x id="0" equiv-text="${this.source.name}"/></source>
@ -2854,7 +2854,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="se16ac750b81fa93d">
<source>Assigned to <x id="0" equiv-text="${item.boundTo}"/> object(s).</source>
<target>Se asigna a
<target>Se asigna a
<x id="0" equiv-text="${item.boundTo}"/>objetos.</target>
</trans-unit>
<trans-unit id="s5a48d5171e1a1522">
@ -2954,7 +2954,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s4414164d120de61a">
<source>The following objects use <x id="0" equiv-text="${objName}"/></source>
<target>Los siguientes objetos usan
<target>Los siguientes objetos usan
<x id="0" equiv-text="${objName}"/></target>
</trans-unit>
<trans-unit id="s92e241c9f3c101a2">
@ -2966,14 +2966,14 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s14401ff4a0cba208">
<source>Failed to update <x id="0" equiv-text="${this.objectLabel}"/>: <x id="1" equiv-text="${e.toString()}"/></source>
<target>No se pudo actualizar
<x id="0" equiv-text="${this.objectLabel}"/>:
<target>No se pudo actualizar
<x id="0" equiv-text="${this.objectLabel}"/>:
<x id="1" equiv-text="${e.toString()}"/></target>
</trans-unit>
<trans-unit id="sa95a538bfbb86111">
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
<target>¿Seguro que quieres actualizar
<x id="0" equiv-text="${this.objectLabel}"/>«
<target>¿Seguro que quieres actualizar
<x id="0" equiv-text="${this.objectLabel}"/>«
<x id="1" equiv-text="${this.obj?.name}"/>»?</target>
</trans-unit>
<trans-unit id="sc92d7cfb6ee1fec6">
@ -3101,7 +3101,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s3616cc78631f5893">
<source>Warning: You're about to delete the user you're logged in as (<x id="0" equiv-text="${shouldShowWarning.username}"/>). Proceed at your own risk.</source>
<target>Advertencia: Vas a eliminar el usuario con el que iniciaste sesión (
<target>Advertencia: Vas a eliminar el usuario con el que iniciaste sesión (
<x id="0" equiv-text="${shouldShowWarning.username}"/>). Proceda bajo su propio riesgo.</target>
</trans-unit>
<trans-unit id="s510c7add9e24c306">
@ -4026,8 +4026,8 @@ doesn't pass when either or both of the selected options are equal or above the
<trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
<target>
<x id="0" equiv-text="${prompt.name}"/>(«
<x id="1" equiv-text="${prompt.fieldKey}"/>», de tipo
<x id="0" equiv-text="${prompt.name}"/>(«
<x id="1" equiv-text="${prompt.fieldKey}"/>», de tipo
<x id="2" equiv-text="${prompt.type}"/>)</target>
</trans-unit>
<trans-unit id="s3b7b519444181264">
@ -4376,7 +4376,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s65d67612999165e9">
<source>Event <x id="0" equiv-text="${this.event.pk}"/></source>
<target>Evento
<target>Evento
<x id="0" equiv-text="${this.event.pk}"/></target>
</trans-unit>
<trans-unit id="s455de2f740b073fd">
@ -4555,7 +4555,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s1ac2653a6492b435">
<source><x id="0" equiv-text="${this.outpostHealth.version}"/>, should be <x id="1" equiv-text="${this.outpostHealth.versionShould}"/></source>
<target>
<x id="0" equiv-text="${this.outpostHealth.version}"/>, debe ser
<x id="0" equiv-text="${this.outpostHealth.version}"/>, debe ser
<x id="1" equiv-text="${this.outpostHealth.versionShould}"/></target>
</trans-unit>
<trans-unit id="s1e176e35c828318c">
@ -4567,7 +4567,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s02b632a9ac24a824">
<source>Last seen: <x id="0" equiv-text="${this.outpostHealth.lastSeen?.toLocaleTimeString()}"/></source>
<target>Visto por última vez:
<target>Visto por última vez:
<x id="0" equiv-text="${this.outpostHealth.lastSeen?.toLocaleTimeString()}"/></target>
</trans-unit>
<trans-unit id="sa43153d53ae65063">
@ -4591,7 +4591,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sbf5f4c5ba679e847">
<source>Logging in via <x id="0" equiv-text="${item.config.authentik_host}"/>.</source>
<target>Iniciar sesión a través de
<target>Iniciar sesión a través de
<x id="0" equiv-text="${item.config.authentik_host}"/>.</target>
</trans-unit>
<trans-unit id="s59b6028f19d15cda">
@ -4744,7 +4744,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sef50d248448e0df1">
<source>Yes (<x id="0" equiv-text="${item.privateKeyType?.toUpperCase()}"/>)</source>
<target>Sí (
<target>Sí (
<x id="0" equiv-text="${item.privateKeyType?.toUpperCase()}"/>)</target>
</trans-unit>
<trans-unit id="s09205907b5b56cda">
@ -4869,7 +4869,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s96b3cddf33e1c853">
<source>You're currently impersonating <x id="0" equiv-text="${this.impersonation}"/>. Click to stop.</source>
<target>Estás suplantando a
<target>Estás suplantando a
<x id="0" equiv-text="${this.impersonation}"/>. Haga clic para parar.</target>
</trans-unit>
<trans-unit id="s7031e6928c44cedd">
@ -4967,7 +4967,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s670ad066cc0e50a3">
<source>Login to continue to <x id="0" equiv-text="${this.challenge.applicationPre}"/>.</source>
<target>Inicie sesión para continuar en
<target>Inicie sesión para continuar en
<x id="0" equiv-text="${this.challenge.applicationPre}"/>.</target>
</trans-unit>
<trans-unit id="scf5ce91bfba10a61">
@ -5072,12 +5072,12 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sbbb7318812d64e51">
<source>Error when creating credential: <x id="0" equiv-text="${err}"/></source>
<target>Error al crear la credencial:
<target>Error al crear la credencial:
<x id="0" equiv-text="${err}"/></target>
</trans-unit>
<trans-unit id="sfe199b2564b66054">
<source>Error when validating assertion on server: <x id="0" equiv-text="${err}"/></source>
<target>Error al validar la afirmación en el servidor:
<target>Error al validar la afirmación en el servidor:
<x id="0" equiv-text="${err}"/></target>
</trans-unit>
<trans-unit id="se409d01b52c4e12f">
@ -5211,12 +5211,12 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s7fa4e5e409d43573">
<source>Error creating credential: <x id="0" equiv-text="${err}"/></source>
<target>Error creando la credencial:
<target>Error creando la credencial:
<x id="0" equiv-text="${err}"/></target>
</trans-unit>
<trans-unit id="s9d95f09deb601f34">
<source>Server validation of credential failed: <x id="0" equiv-text="${err}"/></source>
<target>No se pudo validar la credencial en el servidor:
<target>No se pudo validar la credencial en el servidor:
<x id="0" equiv-text="${err}"/></target>
</trans-unit>
<trans-unit id="s6c8f05e3be04f62a">
@ -5292,7 +5292,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sababff57115130a0">
<source>Error: unsupported source settings: <x id="0" equiv-text="${source.component}"/></source>
<target>Error: configuración de origen no admitida:
<target>Error: configuración de origen no admitida:
<x id="0" equiv-text="${source.component}"/></target>
</trans-unit>
<trans-unit id="sd1031bddc66dc495">
@ -6244,6 +6244,24 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="see1eb81c1f734079">
<source>System settings</source>
</trans-unit>
<trans-unit id="s47fb5504f693775b">
<source>Changes made:</source>
</trans-unit>
<trans-unit id="s506b6a19d12f414c">
<source>Key</source>
</trans-unit>
<trans-unit id="s4fc9dc73245eab09">
<source>Previous value</source>
</trans-unit>
<trans-unit id="scb3b0671d7b7f640">
<source>New value</source>
</trans-unit>
<trans-unit id="s4a642406b0745917">
<source>Raw event info</source>
</trans-unit>
<trans-unit id="sa65e7bc7ddd3484d">
<source>Anonymous user</source>
</trans-unit>
</body>
</file>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -160,7 +160,7 @@
</trans-unit>
<trans-unit id="s14622ee6de586485">
<source>Attempted to log in as <x id="0" equiv-text="${this.event.context.username}"/></source>
<target>Próbowano zalogować się jako
<target>Próbowano zalogować się jako
<x id="0" equiv-text="${this.event.context.username}"/></target>
</trans-unit>
<trans-unit id="sb07bf992e3d00664">
@ -309,8 +309,8 @@
<trans-unit id="s04c5a637328c9b67">
<source><x id="0" equiv-text="${this.pages?.startIndex}"/> - <x id="1" equiv-text="${this.pages?.endIndex}"/> of <x id="2" equiv-text="${this.pages?.count}"/></source>
<target>
<x id="0" equiv-text="${this.pages?.startIndex}"/>-
<x id="1" equiv-text="${this.pages?.endIndex}"/>z
<x id="0" equiv-text="${this.pages?.startIndex}"/>-
<x id="1" equiv-text="${this.pages?.endIndex}"/>z
<x id="2" equiv-text="${this.pages?.count}"/></target>
</trans-unit>
<trans-unit id="s6a89bb10338369b4">
@ -361,9 +361,9 @@
<source>Recent events</source>
</trans-unit>
<trans-unit id="sc35581d9c1cd67ff">
<source>On behalf of <x id="0" equiv-text="${item.user.on_behalf_of.username}"/></source>
<target>W imieniu
<x id="0" equiv-text="${item.user.on_behalf_of.username}"/></target>
<source>On behalf of <x id="0" equiv-text="${event.user.on_behalf_of.username}"/></source>
<target>W imieniu
<x id="0" equiv-text="${event.user.on_behalf_of.username}"/></target>
</trans-unit>
<trans-unit id="saf63a04c86018698">
<source>-</source>
@ -450,7 +450,7 @@
<trans-unit id="s0382d73823585617">
<source><x id="0" equiv-text="${this.errorMessage}"/>: <x id="1" equiv-text="${e.toString()}"/></source>
<target>
<x id="0" equiv-text="${this.errorMessage}"/>:
<x id="0" equiv-text="${this.errorMessage}"/>:
<x id="1" equiv-text="${e.toString()}"/></target>
</trans-unit>
<trans-unit id="s2ceb11be2290bb1b">
@ -481,7 +481,7 @@
</trans-unit>
<trans-unit id="saa0e2675da69651b">
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
<target>Nie znaleziono adresu URL „
<target>Nie znaleziono adresu URL „
<x id="0" equiv-text="${this.url}"/>”.</target>
</trans-unit>
<trans-unit id="s58cd9c2fe836d9c6">
@ -494,7 +494,7 @@
</trans-unit>
<trans-unit id="s6dfd15978586d05f">
<source>Welcome, <x id="0" equiv-text="${name}"/>.</source>
<target>Witaj,
<target>Witaj,
<x id="0" equiv-text="${name}"/>.</target>
</trans-unit>
<trans-unit id="sc381422c585b867f">
@ -600,7 +600,7 @@
<source>Duration</source>
</trans-unit>
<trans-unit id="se7e1ababbc4868b8">
<source><x id="0" equiv-text="${item.taskDuration.toFixed(2)}"/> seconds</source>
<source><x id="0" equiv-text="${item.duration.toFixed(2)}"/> seconds</source>
</trans-unit>
<trans-unit id="sc25edca57df81461">
<source>Authentication</source>
@ -1231,7 +1231,7 @@
</trans-unit>
<trans-unit id="s5d6af4c100ad321b">
<source>Create <x id="0" equiv-text="${type.name}"/></source>
<target>Utwórz
<target>Utwórz
<x id="0" equiv-text="${type.name}"/></target>
</trans-unit>
<trans-unit id="sb95baab425322600">
@ -1313,7 +1313,7 @@
<trans-unit id="s55fa598b754cc3cc">
<source><x id="0" equiv-text="${ub.name}"/> (<x id="1" equiv-text="${consequence}"/>)</source>
<target>
<x id="0" equiv-text="${ub.name}"/>(
<x id="0" equiv-text="${ub.name}"/>(
<x id="1" equiv-text="${consequence}"/>)</target>
</trans-unit>
<trans-unit id="s09240e07b5b8d640">
@ -1325,13 +1325,13 @@
</trans-unit>
<trans-unit id="sf6eb148db23d19de">
<source>Failed to delete <x id="0" equiv-text="${this.objectLabel}"/>: <x id="1" equiv-text="${e.toString()}"/></source>
<target>Nie udało się usunąć
<x id="0" equiv-text="${this.objectLabel}"/>:
<target>Nie udało się usunąć
<x id="0" equiv-text="${this.objectLabel}"/>:
<x id="1" equiv-text="${e.toString()}"/></target>
</trans-unit>
<trans-unit id="s039b6434e8a75560">
<source>Delete <x id="0" equiv-text="${this.objectLabel}"/></source>
<target>Usuń
<target>Usuń
<x id="0" equiv-text="${this.objectLabel}"/></target>
</trans-unit>
<trans-unit id="s5819a49638f6d7cb">
@ -1374,7 +1374,7 @@
</trans-unit>
<trans-unit id="sc9175cb129fdc306">
<source>Update <x id="0" equiv-text="${item.verboseName}"/></source>
<target>Zaktualizuj
<target>Zaktualizuj
<x id="0" equiv-text="${item.verboseName}"/></target>
</trans-unit>
<trans-unit id="s398f6ba74ba8943a">
@ -2103,17 +2103,17 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s030ac0829bb50a49">
<source>Policy <x id="0" equiv-text="${item.policyObj?.name}"/></source>
<target>Zasada
<target>Zasada
<x id="0" equiv-text="${item.policyObj?.name}"/></target>
</trans-unit>
<trans-unit id="s2a64d2dca3da9b0e">
<source>Group <x id="0" equiv-text="${item.groupObj?.name}"/></source>
<target>Grupa
<target>Grupa
<x id="0" equiv-text="${item.groupObj?.name}"/></target>
</trans-unit>
<trans-unit id="se5dc026819a32ff8">
<source>User <x id="0" equiv-text="${item.userObj?.name}"/></source>
<target>Użytkownik
<target>Użytkownik
<x id="0" equiv-text="${item.userObj?.name}"/></target>
</trans-unit>
<trans-unit id="s50c312bea93b6925">
@ -2597,13 +2597,13 @@ doesn't pass when either or both of the selected options are equal or above the
<target>Zadanie zakończone z błędami</target>
</trans-unit>
<trans-unit id="sbedb77365a066648">
<source>Last sync: <x id="0" equiv-text="${task.taskFinishTimestamp.toLocaleString()}"/></source>
<target>Ostatnia synchronizacja:
<x id="0" equiv-text="${task.taskFinishTimestamp.toLocaleString()}"/></target>
<source>Last sync: <x id="0" equiv-text="${task.finishTimestamp.toLocaleString()}"/></source>
<target>Ostatnia synchronizacja:
<x id="0" equiv-text="${task.finishTimestamp.toLocaleString()}"/></target>
</trans-unit>
<trans-unit id="sf3fec8353106ac2f">
<source>OAuth Source <x id="0" equiv-text="${this.source.name}"/></source>
<target>Źródło OAuth
<target>Źródło OAuth
<x id="0" equiv-text="${this.source.name}"/></target>
</trans-unit>
<trans-unit id="se09d055771f3a11d">
@ -2950,7 +2950,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="se16ac750b81fa93d">
<source>Assigned to <x id="0" equiv-text="${item.boundTo}"/> object(s).</source>
<target>Przypisany do
<target>Przypisany do
<x id="0" equiv-text="${item.boundTo}"/>obiektu(ów).</target>
</trans-unit>
<trans-unit id="s5a48d5171e1a1522">
@ -3050,7 +3050,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s4414164d120de61a">
<source>The following objects use <x id="0" equiv-text="${objName}"/></source>
<target>Następujące obiekty używają
<target>Następujące obiekty używają
<x id="0" equiv-text="${objName}"/></target>
</trans-unit>
<trans-unit id="s92e241c9f3c101a2">
@ -3062,14 +3062,14 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s14401ff4a0cba208">
<source>Failed to update <x id="0" equiv-text="${this.objectLabel}"/>: <x id="1" equiv-text="${e.toString()}"/></source>
<target>Nie udało się zaktualizować
<x id="0" equiv-text="${this.objectLabel}"/>:
<target>Nie udało się zaktualizować
<x id="0" equiv-text="${this.objectLabel}"/>:
<x id="1" equiv-text="${e.toString()}"/></target>
</trans-unit>
<trans-unit id="sa95a538bfbb86111">
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
<target>Czy na pewno chcesz zaktualizować
<x id="0" equiv-text="${this.objectLabel}"/>"
<target>Czy na pewno chcesz zaktualizować
<x id="0" equiv-text="${this.objectLabel}"/>"
<x id="1" equiv-text="${this.obj?.name}"/>”?</target>
</trans-unit>
<trans-unit id="sc92d7cfb6ee1fec6">
@ -3102,7 +3102,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s5d7748b1d2363478">
<source>Are you sure you want to remove the selected users from the group <x id="0" equiv-text="${this.targetGroup?.name}"/>?</source>
<target>Czy na pewno chcesz usunąć wybranych użytkowników z grupy
<target>Czy na pewno chcesz usunąć wybranych użytkowników z grupy
<x id="0" equiv-text="${this.targetGroup?.name}"/>?</target>
</trans-unit>
<trans-unit id="sea4f08110bb8f15d">
@ -3208,7 +3208,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s3616cc78631f5893">
<source>Warning: You're about to delete the user you're logged in as (<x id="0" equiv-text="${shouldShowWarning.username}"/>). Proceed at your own risk.</source>
<target>Ostrzeżenie: masz zamiar usunąć użytkownika, na którym jesteś zalogowany jako (
<target>Ostrzeżenie: masz zamiar usunąć użytkownika, na którym jesteś zalogowany jako (
<x id="0" equiv-text="${shouldShowWarning.username}"/>). Kontynuuj na własne ryzyko.</target>
</trans-unit>
<trans-unit id="s510c7add9e24c306">
@ -3232,7 +3232,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="sb4c9ed2a487b238f">
<source>Are you sure you want to remove user <x id="0" equiv-text="${this.targetUser?.username}"/> from the following groups?</source>
<target>Czy na pewno chcesz usunąć użytkownika
<target>Czy na pewno chcesz usunąć użytkownika
<x id="0" equiv-text="${this.targetUser?.username}"/>z następujących grup?</target>
</trans-unit>
<trans-unit id="s964f6725aeb7662f">
@ -4169,8 +4169,8 @@ doesn't pass when either or both of the selected options are equal or above the
<trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
<target>
<x id="0" equiv-text="${prompt.name}"/>(„
<x id="1" equiv-text="${prompt.fieldKey}"/>”, typu
<x id="0" equiv-text="${prompt.name}"/>(„
<x id="1" equiv-text="${prompt.fieldKey}"/>”, typu
<x id="2" equiv-text="${prompt.type}"/>)</target>
</trans-unit>
<trans-unit id="s3b7b519444181264">
@ -4292,7 +4292,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s77299a9d3dd932cd">
<source>Successfully imported <x id="0" equiv-text="${res.count}"/> devices.</source>
<target>Pomyślnie zaimportowano
<target>Pomyślnie zaimportowano
<x id="0" equiv-text="${res.count}"/>urządzenia.</target>
</trans-unit>
<trans-unit id="s6a615f6165ef01c9">
@ -4544,7 +4544,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s65d67612999165e9">
<source>Event <x id="0" equiv-text="${this.event.pk}"/></source>
<target>Zdarzenie
<target>Zdarzenie
<x id="0" equiv-text="${this.event.pk}"/></target>
</trans-unit>
<trans-unit id="s455de2f740b073fd">
@ -4727,7 +4727,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s1ac2653a6492b435">
<source><x id="0" equiv-text="${this.outpostHealth.version}"/>, should be <x id="1" equiv-text="${this.outpostHealth.versionShould}"/></source>
<target>
<x id="0" equiv-text="${this.outpostHealth.version}"/>, powinno być
<x id="0" equiv-text="${this.outpostHealth.version}"/>, powinno być
<x id="1" equiv-text="${this.outpostHealth.versionShould}"/></target>
</trans-unit>
<trans-unit id="s1e176e35c828318c">
@ -4740,7 +4740,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s02b632a9ac24a824">
<source>Last seen: <x id="0" equiv-text="${this.outpostHealth.lastSeen?.toLocaleTimeString()}"/></source>
<target>Ostatnio widziany:
<target>Ostatnio widziany:
<x id="0" equiv-text="${this.outpostHealth.lastSeen?.toLocaleTimeString()}"/></target>
</trans-unit>
<trans-unit id="sa43153d53ae65063">
@ -4765,7 +4765,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sbf5f4c5ba679e847">
<source>Logging in via <x id="0" equiv-text="${item.config.authentik_host}"/>.</source>
<target>Logowanie przez
<target>Logowanie przez
<x id="0" equiv-text="${item.config.authentik_host}"/>.</target>
</trans-unit>
<trans-unit id="s59b6028f19d15cda">
@ -4920,7 +4920,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sef50d248448e0df1">
<source>Yes (<x id="0" equiv-text="${item.privateKeyType?.toUpperCase()}"/>)</source>
<target>Tak (
<target>Tak (
<x id="0" equiv-text="${item.privateKeyType?.toUpperCase()}"/>)</target>
</trans-unit>
<trans-unit id="s09205907b5b56cda">
@ -5058,7 +5058,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s96b3cddf33e1c853">
<source>You're currently impersonating <x id="0" equiv-text="${this.impersonation}"/>. Click to stop.</source>
<target>Obecnie podszywasz się pod
<target>Obecnie podszywasz się pod
<x id="0" equiv-text="${this.impersonation}"/>. Kliknij, aby zatrzymać.</target>
</trans-unit>
<trans-unit id="s7031e6928c44cedd">
@ -5158,7 +5158,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s670ad066cc0e50a3">
<source>Login to continue to <x id="0" equiv-text="${this.challenge.applicationPre}"/>.</source>
<target>Zaloguj się, aby przejść do
<target>Zaloguj się, aby przejść do
<x id="0" equiv-text="${this.challenge.applicationPre}"/>.</target>
</trans-unit>
<trans-unit id="scf5ce91bfba10a61">
@ -5267,12 +5267,12 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sbbb7318812d64e51">
<source>Error when creating credential: <x id="0" equiv-text="${err}"/></source>
<target>Błąd podczas tworzenia poświadczeń:
<target>Błąd podczas tworzenia poświadczeń:
<x id="0" equiv-text="${err}"/></target>
</trans-unit>
<trans-unit id="sfe199b2564b66054">
<source>Error when validating assertion on server: <x id="0" equiv-text="${err}"/></source>
<target>Błąd podczas walidacji asercji na serwerze:
<target>Błąd podczas walidacji asercji na serwerze:
<x id="0" equiv-text="${err}"/></target>
</trans-unit>
<trans-unit id="se409d01b52c4e12f">
@ -5411,12 +5411,12 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s7fa4e5e409d43573">
<source>Error creating credential: <x id="0" equiv-text="${err}"/></source>
<target>Błąd podczas tworzenia poświadczenia:
<target>Błąd podczas tworzenia poświadczenia:
<x id="0" equiv-text="${err}"/></target>
</trans-unit>
<trans-unit id="s9d95f09deb601f34">
<source>Server validation of credential failed: <x id="0" equiv-text="${err}"/></source>
<target>Weryfikacja poświadczeń serwera nie powiodła się:
<target>Weryfikacja poświadczeń serwera nie powiodła się:
<x id="0" equiv-text="${err}"/></target>
</trans-unit>
<trans-unit id="s6c8f05e3be04f62a">
@ -5485,7 +5485,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s67dedada007d4067">
<source>Failed to disconnected source: <x id="0" equiv-text="${exc}"/></source>
<target>Nie udało się odłączyć źródła:
<target>Nie udało się odłączyć źródła:
<x id="0" equiv-text="${exc}"/></target>
</trans-unit>
<trans-unit id="sd2208cd1a767644b">
@ -5498,7 +5498,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sababff57115130a0">
<source>Error: unsupported source settings: <x id="0" equiv-text="${source.component}"/></source>
<target>Błąd: nieobsługiwane ustawienia źródła:
<target>Błąd: nieobsługiwane ustawienia źródła:
<x id="0" equiv-text="${source.component}"/></target>
</trans-unit>
<trans-unit id="sd1031bddc66dc495">
@ -6451,6 +6451,24 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="see1eb81c1f734079">
<source>System settings</source>
</trans-unit>
<trans-unit id="s47fb5504f693775b">
<source>Changes made:</source>
</trans-unit>
<trans-unit id="s506b6a19d12f414c">
<source>Key</source>
</trans-unit>
<trans-unit id="s4fc9dc73245eab09">
<source>Previous value</source>
</trans-unit>
<trans-unit id="scb3b0671d7b7f640">
<source>New value</source>
</trans-unit>
<trans-unit id="s4a642406b0745917">
<source>Raw event info</source>
</trans-unit>
<trans-unit id="sa65e7bc7ddd3484d">
<source>Anonymous user</source>
</trans-unit>
</body>
</file>

File diff suppressed because it is too large Load Diff

View File

@ -301,8 +301,8 @@
<trans-unit id="s04c5a637328c9b67">
<source><x id="0" equiv-text="${this.pages?.startIndex}"/> - <x id="1" equiv-text="${this.pages?.endIndex}"/> of <x id="2" equiv-text="${this.pages?.count}"/></source>
<target>
<x id="0" equiv-text="${this.pages?.count}"/>içinden
<x id="1" equiv-text="${this.pages?.startIndex}"/>-
<x id="0" equiv-text="${this.pages?.count}"/>içinden
<x id="1" equiv-text="${this.pages?.startIndex}"/>-
<x id="2" equiv-text="${this.pages?.endIndex}"/></target>
</trans-unit>
<trans-unit id="s6a89bb10338369b4">
@ -352,9 +352,9 @@
<source>Recent events</source>
</trans-unit>
<trans-unit id="sc35581d9c1cd67ff">
<source>On behalf of <x id="0" equiv-text="${item.user.on_behalf_of.username}"/></source>
<source>On behalf of <x id="0" equiv-text="${event.user.on_behalf_of.username}"/></source>
<target>
<x id="0" equiv-text="${item.user.on_behalf_of.username}"/>adına</target>
<x id="0" equiv-text="${event.user.on_behalf_of.username}"/>adına</target>
</trans-unit>
<trans-unit id="saf63a04c86018698">
<source>-</source>
@ -441,7 +441,7 @@
<trans-unit id="s0382d73823585617">
<source><x id="0" equiv-text="${this.errorMessage}"/>: <x id="1" equiv-text="${e.toString()}"/></source>
<target>
<x id="0" equiv-text="${this.errorMessage}"/>:
<x id="0" equiv-text="${this.errorMessage}"/>:
<x id="1" equiv-text="${e.toString()}"/></target>
</trans-unit>
<trans-unit id="s2ceb11be2290bb1b">
@ -472,7 +472,7 @@
</trans-unit>
<trans-unit id="saa0e2675da69651b">
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
<target>“
<target>“
<x id="0" equiv-text="${this.url}"/>” URL'si bulunamadı.</target>
</trans-unit>
<trans-unit id="s58cd9c2fe836d9c6">
@ -485,7 +485,7 @@
</trans-unit>
<trans-unit id="s6dfd15978586d05f">
<source>Welcome, <x id="0" equiv-text="${name}"/>.</source>
<target>Hoş geldiniz,
<target>Hoş geldiniz,
<x id="0" equiv-text="${name}"/>.</target>
</trans-unit>
<trans-unit id="sc381422c585b867f">
@ -590,7 +590,7 @@
<source>Duration</source>
</trans-unit>
<trans-unit id="se7e1ababbc4868b8">
<source><x id="0" equiv-text="${item.taskDuration.toFixed(2)}"/> seconds</source>
<source><x id="0" equiv-text="${item.duration.toFixed(2)}"/> seconds</source>
</trans-unit>
<trans-unit id="sc25edca57df81461">
<source>Authentication</source>
@ -1191,7 +1191,7 @@
</trans-unit>
<trans-unit id="s5d6af4c100ad321b">
<source>Create <x id="0" equiv-text="${type.name}"/></source>
<target>Oluştur
<target>Oluştur
<x id="0" equiv-text="${type.name}"/></target>
</trans-unit>
<trans-unit id="sb95baab425322600">
@ -1272,7 +1272,7 @@
<trans-unit id="s55fa598b754cc3cc">
<source><x id="0" equiv-text="${ub.name}"/> (<x id="1" equiv-text="${consequence}"/>)</source>
<target>
<x id="0" equiv-text="${ub.name}"/>(
<x id="0" equiv-text="${ub.name}"/>(
<x id="1" equiv-text="${consequence}"/>)</target>
</trans-unit>
<trans-unit id="s09240e07b5b8d640">
@ -1285,7 +1285,7 @@
<trans-unit id="sf6eb148db23d19de">
<source>Failed to delete <x id="0" equiv-text="${this.objectLabel}"/>: <x id="1" equiv-text="${e.toString()}"/></source>
<target>
<x id="0" equiv-text="${this.objectLabel}"/>silinemedi:
<x id="0" equiv-text="${this.objectLabel}"/>silinemedi:
<x id="1" equiv-text="${e.toString()}"/></target>
</trans-unit>
<trans-unit id="s039b6434e8a75560">
@ -1333,7 +1333,7 @@
</trans-unit>
<trans-unit id="sc9175cb129fdc306">
<source>Update <x id="0" equiv-text="${item.verboseName}"/></source>
<target>Güncelleme
<target>Güncelleme
<x id="0" equiv-text="${item.verboseName}"/></target>
</trans-unit>
<trans-unit id="s398f6ba74ba8943a">
@ -2030,17 +2030,17 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s030ac0829bb50a49">
<source>Policy <x id="0" equiv-text="${item.policyObj?.name}"/></source>
<target>İlke
<target>İlke
<x id="0" equiv-text="${item.policyObj?.name}"/></target>
</trans-unit>
<trans-unit id="s2a64d2dca3da9b0e">
<source>Group <x id="0" equiv-text="${item.groupObj?.name}"/></source>
<target>Grup
<target>Grup
<x id="0" equiv-text="${item.groupObj?.name}"/></target>
</trans-unit>
<trans-unit id="se5dc026819a32ff8">
<source>User <x id="0" equiv-text="${item.userObj?.name}"/></source>
<target>Kullanıcı
<target>Kullanıcı
<x id="0" equiv-text="${item.userObj?.name}"/></target>
</trans-unit>
<trans-unit id="s50c312bea93b6925">
@ -2512,9 +2512,9 @@ doesn't pass when either or both of the selected options are equal or above the
<target>Görev hatalarla tamamlandı</target>
</trans-unit>
<trans-unit id="sbedb77365a066648">
<source>Last sync: <x id="0" equiv-text="${task.taskFinishTimestamp.toLocaleString()}"/></source>
<target>Son senkronizasyon:
<x id="0" equiv-text="${task.taskFinishTimestamp.toLocaleString()}"/></target>
<source>Last sync: <x id="0" equiv-text="${task.finishTimestamp.toLocaleString()}"/></source>
<target>Son senkronizasyon:
<x id="0" equiv-text="${task.finishTimestamp.toLocaleString()}"/></target>
</trans-unit>
<trans-unit id="sf3fec8353106ac2f">
<source>OAuth Source <x id="0" equiv-text="${this.source.name}"/></source>
@ -2953,7 +2953,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s4414164d120de61a">
<source>The following objects use <x id="0" equiv-text="${objName}"/></source>
<target>Aşağıdaki nesneler
<target>Aşağıdaki nesneler
<x id="0" equiv-text="${objName}"/></target>
</trans-unit>
<trans-unit id="s92e241c9f3c101a2">
@ -2966,13 +2966,13 @@ doesn't pass when either or both of the selected options are equal or above the
<trans-unit id="s14401ff4a0cba208">
<source>Failed to update <x id="0" equiv-text="${this.objectLabel}"/>: <x id="1" equiv-text="${e.toString()}"/></source>
<target>
<x id="0" equiv-text="${this.objectLabel}"/>güncellenemedi:
<x id="0" equiv-text="${this.objectLabel}"/>güncellenemedi:
<x id="1" equiv-text="${e.toString()}"/></target>
</trans-unit>
<trans-unit id="sa95a538bfbb86111">
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
<target>
<x id="0" equiv-text="${this.objectLabel}"/>“
<x id="0" equiv-text="${this.objectLabel}"/>“
<x id="1" equiv-text="${this.obj?.name}"/>” güncellemesini istediğinizden emin misiniz?</target>
</trans-unit>
<trans-unit id="sc92d7cfb6ee1fec6">
@ -3100,7 +3100,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s3616cc78631f5893">
<source>Warning: You're about to delete the user you're logged in as (<x id="0" equiv-text="${shouldShowWarning.username}"/>). Proceed at your own risk.</source>
<target>Uyarı: Oturum açtığınız kullanıcıyı (
<target>Uyarı: Oturum açtığınız kullanıcıyı (
<x id="0" equiv-text="${shouldShowWarning.username}"/>) silmek üzeresiniz. Kendi sorumluluğunuzdadır.</target>
</trans-unit>
<trans-unit id="s510c7add9e24c306">
@ -4020,8 +4020,8 @@ doesn't pass when either or both of the selected options are equal or above the
<trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
<target>
<x id="0" equiv-text="${prompt.name}"/>(“
<x id="1" equiv-text="${prompt.fieldKey}"/>”,
<x id="0" equiv-text="${prompt.name}"/>(“
<x id="1" equiv-text="${prompt.fieldKey}"/>”,
<x id="2" equiv-text="${prompt.type}"/>türünde)</target>
</trans-unit>
<trans-unit id="s3b7b519444181264">
@ -4370,7 +4370,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s65d67612999165e9">
<source>Event <x id="0" equiv-text="${this.event.pk}"/></source>
<target>Olay
<target>Olay
<x id="0" equiv-text="${this.event.pk}"/></target>
</trans-unit>
<trans-unit id="s455de2f740b073fd">
@ -4548,7 +4548,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s1ac2653a6492b435">
<source><x id="0" equiv-text="${this.outpostHealth.version}"/>, should be <x id="1" equiv-text="${this.outpostHealth.versionShould}"/></source>
<target>
<x id="0" equiv-text="${this.outpostHealth.version}"/>,
<x id="0" equiv-text="${this.outpostHealth.version}"/>,
<x id="1" equiv-text="${this.outpostHealth.versionShould}"/>olmalıdır</target>
</trans-unit>
<trans-unit id="s1e176e35c828318c">
@ -4560,7 +4560,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s02b632a9ac24a824">
<source>Last seen: <x id="0" equiv-text="${this.outpostHealth.lastSeen?.toLocaleTimeString()}"/></source>
<target>Son görüldü:
<target>Son görüldü:
<x id="0" equiv-text="${this.outpostHealth.lastSeen?.toLocaleTimeString()}"/></target>
</trans-unit>
<trans-unit id="sa43153d53ae65063">
@ -4737,7 +4737,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sef50d248448e0df1">
<source>Yes (<x id="0" equiv-text="${item.privateKeyType?.toUpperCase()}"/>)</source>
<target>Evet (
<target>Evet (
<x id="0" equiv-text="${item.privateKeyType?.toUpperCase()}"/>)</target>
</trans-unit>
<trans-unit id="s09205907b5b56cda">
@ -4862,7 +4862,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s96b3cddf33e1c853">
<source>You're currently impersonating <x id="0" equiv-text="${this.impersonation}"/>. Click to stop.</source>
<target>Şu anda
<target>Şu anda
<x id="0" equiv-text="${this.impersonation}"/>kimliğine bürünüyorsunuz. Durdurmak için tıklayın.</target>
</trans-unit>
<trans-unit id="s7031e6928c44cedd">
@ -5065,12 +5065,12 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sbbb7318812d64e51">
<source>Error when creating credential: <x id="0" equiv-text="${err}"/></source>
<target>Kimlik bilgisi oluşturulurken hata oluştu:
<target>Kimlik bilgisi oluşturulurken hata oluştu:
<x id="0" equiv-text="${err}"/></target>
</trans-unit>
<trans-unit id="sfe199b2564b66054">
<source>Error when validating assertion on server: <x id="0" equiv-text="${err}"/></source>
<target>Sunucuda onaylama işlemi doğrulanırken hata oluştu:
<target>Sunucuda onaylama işlemi doğrulanırken hata oluştu:
<x id="0" equiv-text="${err}"/></target>
</trans-unit>
<trans-unit id="se409d01b52c4e12f">
@ -5204,12 +5204,12 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s7fa4e5e409d43573">
<source>Error creating credential: <x id="0" equiv-text="${err}"/></source>
<target>Kimlik bilgisi oluşturulurken hata oluştu:
<target>Kimlik bilgisi oluşturulurken hata oluştu:
<x id="0" equiv-text="${err}"/></target>
</trans-unit>
<trans-unit id="s9d95f09deb601f34">
<source>Server validation of credential failed: <x id="0" equiv-text="${err}"/></source>
<target>Kimlik bilgisi sunucu doğrulaması başarısız oldu:
<target>Kimlik bilgisi sunucu doğrulaması başarısız oldu:
<x id="0" equiv-text="${err}"/></target>
</trans-unit>
<trans-unit id="s6c8f05e3be04f62a">
@ -5285,7 +5285,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sababff57115130a0">
<source>Error: unsupported source settings: <x id="0" equiv-text="${source.component}"/></source>
<target>Hata: desteklenmeyen kaynak ayarları:
<target>Hata: desteklenmeyen kaynak ayarları:
<x id="0" equiv-text="${source.component}"/></target>
</trans-unit>
<trans-unit id="sd1031bddc66dc495">
@ -6237,6 +6237,24 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="see1eb81c1f734079">
<source>System settings</source>
</trans-unit>
<trans-unit id="s47fb5504f693775b">
<source>Changes made:</source>
</trans-unit>
<trans-unit id="s506b6a19d12f414c">
<source>Key</source>
</trans-unit>
<trans-unit id="s4fc9dc73245eab09">
<source>Previous value</source>
</trans-unit>
<trans-unit id="scb3b0671d7b7f640">
<source>New value</source>
</trans-unit>
<trans-unit id="s4a642406b0745917">
<source>Raw event info</source>
</trans-unit>
<trans-unit id="sa65e7bc7ddd3484d">
<source>Anonymous user</source>
</trans-unit>
</body>
</file>

View File

@ -294,7 +294,7 @@
<source>Recent events</source>
</trans-unit>
<trans-unit id="sc35581d9c1cd67ff">
<source>On behalf of <x id="0" equiv-text="${item.user.on_behalf_of.username}"/></source>
<source>On behalf of <x id="0" equiv-text="${event.user.on_behalf_of.username}"/></source>
</trans-unit>
<trans-unit id="saf63a04c86018698">
<source>-</source>
@ -498,7 +498,7 @@
<source>Duration</source>
</trans-unit>
<trans-unit id="se7e1ababbc4868b8">
<source><x id="0" equiv-text="${item.taskDuration.toFixed(2)}"/> seconds</source>
<source><x id="0" equiv-text="${item.duration.toFixed(2)}"/> seconds</source>
</trans-unit>
<trans-unit id="s14bf17e2a1a2c381">
<source>Restart task</source>
@ -2029,7 +2029,7 @@ doesn't pass when either or both of the selected options are equal or above the
<source>Task finished with errors</source>
</trans-unit>
<trans-unit id="sbedb77365a066648">
<source>Last sync: <x id="0" equiv-text="${task.taskFinishTimestamp.toLocaleString()}"/></source>
<source>Last sync: <x id="0" equiv-text="${task.finishTimestamp.toLocaleString()}"/></source>
</trans-unit>
<trans-unit id="s2f0f6691de0b0388">
<source>Warning: Provider is not assigned to an application as backchannel provider.</source>
@ -5146,6 +5146,24 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="see1eb81c1f734079">
<source>System settings</source>
</trans-unit>
<trans-unit id="s47fb5504f693775b">
<source>Changes made:</source>
</trans-unit>
<trans-unit id="s506b6a19d12f414c">
<source>Key</source>
</trans-unit>
<trans-unit id="s4fc9dc73245eab09">
<source>Previous value</source>
</trans-unit>
<trans-unit id="scb3b0671d7b7f640">
<source>New value</source>
</trans-unit>
<trans-unit id="s4a642406b0745917">
<source>Raw event info</source>
</trans-unit>
<trans-unit id="sa65e7bc7ddd3484d">
<source>Anonymous user</source>
</trans-unit>
</body>
</file>
</xliff>

File diff suppressed because it is too large Load Diff

View File

@ -160,7 +160,7 @@
</trans-unit>
<trans-unit id="s14622ee6de586485">
<source>Attempted to log in as <x id="0" equiv-text="${this.event.context.username}"/></source>
<target>已尝试以
<target>已尝试以
<x id="0" equiv-text="${this.event.context.username}"/>身份登入</target>
</trans-unit>
<trans-unit id="sb07bf992e3d00664">
@ -308,8 +308,8 @@
<trans-unit id="s04c5a637328c9b67">
<source><x id="0" equiv-text="${this.pages?.startIndex}"/> - <x id="1" equiv-text="${this.pages?.endIndex}"/> of <x id="2" equiv-text="${this.pages?.count}"/></source>
<target>
<x id="0" equiv-text="${this.pages?.startIndex}"/>-
<x id="1" equiv-text="${this.pages?.endIndex}"/>of
<x id="0" equiv-text="${this.pages?.startIndex}"/>-
<x id="1" equiv-text="${this.pages?.endIndex}"/>of
<x id="2" equiv-text="${this.pages?.count}"/></target>
</trans-unit>
<trans-unit id="s6a89bb10338369b4">
@ -359,9 +359,9 @@
<source>Recent events</source>
</trans-unit>
<trans-unit id="sc35581d9c1cd67ff">
<source>On behalf of <x id="0" equiv-text="${item.user.on_behalf_of.username}"/></source>
<target>代表
<x id="0" equiv-text="${item.user.on_behalf_of.username}"/></target>
<source>On behalf of <x id="0" equiv-text="${event.user.on_behalf_of.username}"/></source>
<target>代表
<x id="0" equiv-text="${event.user.on_behalf_of.username}"/></target>
</trans-unit>
<trans-unit id="saf63a04c86018698">
<source>-</source>
@ -448,7 +448,7 @@
<trans-unit id="s0382d73823585617">
<source><x id="0" equiv-text="${this.errorMessage}"/>: <x id="1" equiv-text="${e.toString()}"/></source>
<target>
<x id="0" equiv-text="${this.errorMessage}"/>:
<x id="0" equiv-text="${this.errorMessage}"/>:
<x id="1" equiv-text="${e.toString()}"/></target>
</trans-unit>
<trans-unit id="s2ceb11be2290bb1b">
@ -479,7 +479,7 @@
</trans-unit>
<trans-unit id="saa0e2675da69651b">
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
<target>找不到网址 “
<target>找不到网址 “
<x id="0" equiv-text="${this.url}"/>”。</target>
</trans-unit>
<trans-unit id="s58cd9c2fe836d9c6">
@ -492,7 +492,7 @@
</trans-unit>
<trans-unit id="s6dfd15978586d05f">
<source>Welcome, <x id="0" equiv-text="${name}"/>.</source>
<target>欢迎,
<target>欢迎,
<x id="0" equiv-text="${name}"/>。</target>
</trans-unit>
<trans-unit id="sc381422c585b867f">
@ -597,7 +597,7 @@
<source>Duration</source>
</trans-unit>
<trans-unit id="se7e1ababbc4868b8">
<source><x id="0" equiv-text="${item.taskDuration.toFixed(2)}"/> seconds</source>
<source><x id="0" equiv-text="${item.duration.toFixed(2)}"/> seconds</source>
</trans-unit>
<trans-unit id="sc25edca57df81461">
<source>Authentication</source>
@ -1204,7 +1204,7 @@
</trans-unit>
<trans-unit id="s5d6af4c100ad321b">
<source>Create <x id="0" equiv-text="${type.name}"/></source>
<target>创建
<target>创建
<x id="0" equiv-text="${type.name}"/></target>
</trans-unit>
<trans-unit id="sb95baab425322600">
@ -1285,7 +1285,7 @@
<trans-unit id="s55fa598b754cc3cc">
<source><x id="0" equiv-text="${ub.name}"/> (<x id="1" equiv-text="${consequence}"/>)</source>
<target>
<x id="0" equiv-text="${ub.name}"/>(
<x id="0" equiv-text="${ub.name}"/>(
<x id="1" equiv-text="${consequence}"/>)</target>
</trans-unit>
<trans-unit id="s09240e07b5b8d640">
@ -1297,13 +1297,13 @@
</trans-unit>
<trans-unit id="sf6eb148db23d19de">
<source>Failed to delete <x id="0" equiv-text="${this.objectLabel}"/>: <x id="1" equiv-text="${e.toString()}"/></source>
<target>无法删除
<x id="0" equiv-text="${this.objectLabel}"/>:
<target>无法删除
<x id="0" equiv-text="${this.objectLabel}"/>:
<x id="1" equiv-text="${e.toString()}"/></target>
</trans-unit>
<trans-unit id="s039b6434e8a75560">
<source>Delete <x id="0" equiv-text="${this.objectLabel}"/></source>
<target>删除
<target>删除
<x id="0" equiv-text="${this.objectLabel}"/></target>
</trans-unit>
<trans-unit id="s5819a49638f6d7cb">
@ -1346,7 +1346,7 @@
</trans-unit>
<trans-unit id="sc9175cb129fdc306">
<source>Update <x id="0" equiv-text="${item.verboseName}"/></source>
<target>更新
<target>更新
<x id="0" equiv-text="${item.verboseName}"/></target>
</trans-unit>
<trans-unit id="s398f6ba74ba8943a">
@ -2050,17 +2050,17 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s030ac0829bb50a49">
<source>Policy <x id="0" equiv-text="${item.policyObj?.name}"/></source>
<target>策略
<target>策略
<x id="0" equiv-text="${item.policyObj?.name}"/></target>
</trans-unit>
<trans-unit id="s2a64d2dca3da9b0e">
<source>Group <x id="0" equiv-text="${item.groupObj?.name}"/></source>
<target>组
<target>组
<x id="0" equiv-text="${item.groupObj?.name}"/></target>
</trans-unit>
<trans-unit id="se5dc026819a32ff8">
<source>User <x id="0" equiv-text="${item.userObj?.name}"/></source>
<target>用户
<target>用户
<x id="0" equiv-text="${item.userObj?.name}"/></target>
</trans-unit>
<trans-unit id="s50c312bea93b6925">
@ -2534,9 +2534,9 @@ doesn't pass when either or both of the selected options are equal or above the
<target>任务已完成,但出现错误</target>
</trans-unit>
<trans-unit id="sbedb77365a066648">
<source>Last sync: <x id="0" equiv-text="${task.taskFinishTimestamp.toLocaleString()}"/></source>
<target>上次同步:
<x id="0" equiv-text="${task.taskFinishTimestamp.toLocaleString()}"/></target>
<source>Last sync: <x id="0" equiv-text="${task.finishTimestamp.toLocaleString()}"/></source>
<target>上次同步:
<x id="0" equiv-text="${task.finishTimestamp.toLocaleString()}"/></target>
</trans-unit>
<trans-unit id="sf3fec8353106ac2f">
<source>OAuth Source <x id="0" equiv-text="${this.source.name}"/></source>
@ -2879,7 +2879,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="se16ac750b81fa93d">
<source>Assigned to <x id="0" equiv-text="${item.boundTo}"/> object(s).</source>
<target>已分配给
<target>已分配给
<x id="0" equiv-text="${item.boundTo}"/>个对象。</target>
</trans-unit>
<trans-unit id="s5a48d5171e1a1522">
@ -2979,7 +2979,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s4414164d120de61a">
<source>The following objects use <x id="0" equiv-text="${objName}"/></source>
<target>以下对象使用
<target>以下对象使用
<x id="0" equiv-text="${objName}"/></target>
</trans-unit>
<trans-unit id="s92e241c9f3c101a2">
@ -2991,14 +2991,14 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s14401ff4a0cba208">
<source>Failed to update <x id="0" equiv-text="${this.objectLabel}"/>: <x id="1" equiv-text="${e.toString()}"/></source>
<target>更新失败
<x id="0" equiv-text="${this.objectLabel}"/>
<target>更新失败
<x id="0" equiv-text="${this.objectLabel}"/>
<x id="1" equiv-text="${e.toString()}"/></target>
</trans-unit>
<trans-unit id="sa95a538bfbb86111">
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
<target>你确定要更新
<x id="0" equiv-text="${this.objectLabel}"/>"
<target>你确定要更新
<x id="0" equiv-text="${this.objectLabel}"/>"
<x id="1" equiv-text="${this.obj?.name}"/>" 吗?</target>
</trans-unit>
<trans-unit id="sc92d7cfb6ee1fec6">
@ -3127,7 +3127,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s3616cc78631f5893">
<source>Warning: You're about to delete the user you're logged in as (<x id="0" equiv-text="${shouldShowWarning.username}"/>). Proceed at your own risk.</source>
<target>警告:你即将删除登录的用户 (
<target>警告:你即将删除登录的用户 (
<x id="0" equiv-text="${shouldShowWarning.username}"/>)。继续,风险自负。</target>
</trans-unit>
<trans-unit id="s510c7add9e24c306">
@ -4058,8 +4058,8 @@ doesn't pass when either or both of the selected options are equal or above the
<trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
<target>
<x id="0" equiv-text="${prompt.name}"/>(“
<x id="1" equiv-text="${prompt.fieldKey}"/>”, 类型为
<x id="0" equiv-text="${prompt.name}"/>(“
<x id="1" equiv-text="${prompt.fieldKey}"/>”, 类型为
<x id="2" equiv-text="${prompt.type}"/>)</target>
</trans-unit>
<trans-unit id="s3b7b519444181264">
@ -4411,7 +4411,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s65d67612999165e9">
<source>Event <x id="0" equiv-text="${this.event.pk}"/></source>
<target>事件
<target>事件
<x id="0" equiv-text="${this.event.pk}"/></target>
</trans-unit>
<trans-unit id="s455de2f740b073fd">
@ -4590,7 +4590,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s1ac2653a6492b435">
<source><x id="0" equiv-text="${this.outpostHealth.version}"/>, should be <x id="1" equiv-text="${this.outpostHealth.versionShould}"/></source>
<target>
<x id="0" equiv-text="${this.outpostHealth.version}"/>,应该是
<x id="0" equiv-text="${this.outpostHealth.version}"/>,应该是
<x id="1" equiv-text="${this.outpostHealth.versionShould}"/></target>
</trans-unit>
<trans-unit id="s1e176e35c828318c">
@ -4602,7 +4602,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s02b632a9ac24a824">
<source>Last seen: <x id="0" equiv-text="${this.outpostHealth.lastSeen?.toLocaleTimeString()}"/></source>
<target>最后显示:
<target>最后显示:
<x id="0" equiv-text="${this.outpostHealth.lastSeen?.toLocaleTimeString()}"/></target>
</trans-unit>
<trans-unit id="sa43153d53ae65063">
@ -4626,7 +4626,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sbf5f4c5ba679e847">
<source>Logging in via <x id="0" equiv-text="${item.config.authentik_host}"/>.</source>
<target>通过
<target>通过
<x id="0" equiv-text="${item.config.authentik_host}"/>登录。</target>
</trans-unit>
<trans-unit id="s59b6028f19d15cda">
@ -4781,7 +4781,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sef50d248448e0df1">
<source>Yes (<x id="0" equiv-text="${item.privateKeyType?.toUpperCase()}"/>)</source>
<target>Yes (
<target>Yes (
<x id="0" equiv-text="${item.privateKeyType?.toUpperCase()}"/>)</target>
</trans-unit>
<trans-unit id="s09205907b5b56cda">
@ -4906,7 +4906,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s96b3cddf33e1c853">
<source>You're currently impersonating <x id="0" equiv-text="${this.impersonation}"/>. Click to stop.</source>
<target>你目前正在模拟
<target>你目前正在模拟
<x id="0" equiv-text="${this.impersonation}"/>。单击停止。</target>
</trans-unit>
<trans-unit id="s7031e6928c44cedd">
@ -5004,7 +5004,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s670ad066cc0e50a3">
<source>Login to continue to <x id="0" equiv-text="${this.challenge.applicationPre}"/>.</source>
<target>登入以继续
<target>登入以继续
<x id="0" equiv-text="${this.challenge.applicationPre}"/>。</target>
</trans-unit>
<trans-unit id="scf5ce91bfba10a61">
@ -5109,12 +5109,12 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sbbb7318812d64e51">
<source>Error when creating credential: <x id="0" equiv-text="${err}"/></source>
<target>创建凭证时出错:
<target>创建凭证时出错:
<x id="0" equiv-text="${err}"/></target>
</trans-unit>
<trans-unit id="sfe199b2564b66054">
<source>Error when validating assertion on server: <x id="0" equiv-text="${err}"/></source>
<target>在服务器上验证断言时出错:
<target>在服务器上验证断言时出错:
<x id="0" equiv-text="${err}"/></target>
</trans-unit>
<trans-unit id="se409d01b52c4e12f">
@ -5248,12 +5248,12 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s7fa4e5e409d43573">
<source>Error creating credential: <x id="0" equiv-text="${err}"/></source>
<target>创建凭证时出错:
<target>创建凭证时出错:
<x id="0" equiv-text="${err}"/></target>
</trans-unit>
<trans-unit id="s9d95f09deb601f34">
<source>Server validation of credential failed: <x id="0" equiv-text="${err}"/></source>
<target>服务器验证凭据失败:
<target>服务器验证凭据失败:
<x id="0" equiv-text="${err}"/></target>
</trans-unit>
<trans-unit id="s6c8f05e3be04f62a">
@ -5332,7 +5332,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sababff57115130a0">
<source>Error: unsupported source settings: <x id="0" equiv-text="${source.component}"/></source>
<target>错误:不支持的源设置:
<target>错误:不支持的源设置:
<x id="0" equiv-text="${source.component}"/></target>
</trans-unit>
<trans-unit id="sd1031bddc66dc495">
@ -6285,6 +6285,24 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="see1eb81c1f734079">
<source>System settings</source>
</trans-unit>
<trans-unit id="s47fb5504f693775b">
<source>Changes made:</source>
</trans-unit>
<trans-unit id="s506b6a19d12f414c">
<source>Key</source>
</trans-unit>
<trans-unit id="s4fc9dc73245eab09">
<source>Previous value</source>
</trans-unit>
<trans-unit id="scb3b0671d7b7f640">
<source>New value</source>
</trans-unit>
<trans-unit id="s4a642406b0745917">
<source>Raw event info</source>
</trans-unit>
<trans-unit id="sa65e7bc7ddd3484d">
<source>Anonymous user</source>
</trans-unit>
</body>
</file>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff