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:
@ -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)
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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"))
|
||||
|
@ -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(
|
||||
|
@ -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:
|
||||
|
@ -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,
|
||||
)
|
||||
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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})
|
||||
)
|
||||
|
107
authentik/events/api/tasks.py
Normal file
107
authentik/events/api/tasks.py
Normal 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)
|
@ -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)
|
||||
|
@ -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,
|
||||
)
|
||||
)
|
||||
|
||||
|
60
authentik/events/migrations/0004_systemtask.py
Normal file
60
authentik/events/migrations/0004_systemtask.py
Normal 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")},
|
||||
},
|
||||
),
|
||||
]
|
@ -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")
|
||||
|
@ -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
|
@ -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()
|
||||
|
135
authentik/events/system_tasks.py
Normal file
135
authentik/events/system_tasks.py
Normal 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 = []
|
@ -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")
|
||||
|
@ -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)
|
||||
|
@ -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),
|
||||
]
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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")
|
||||
|
@ -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(),
|
||||
|
@ -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(
|
||||
|
@ -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(),
|
||||
|
@ -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(
|
||||
|
6
authentik/sources/ldap/sync/vendor/ms_ad.py
vendored
6
authentik/sources/ldap/sync/vendor/ms_ad.py
vendored
@ -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(
|
||||
|
@ -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)
|
||||
|
@ -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"""
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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": {
|
||||
|
@ -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 ""
|
||||
|
431
schema.yml
431
schema.yml
@ -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
|
||||
|
@ -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(),
|
||||
)
|
||||
|
||||
|
@ -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>`,
|
||||
|
@ -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";
|
||||
}
|
||||
});
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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) => {
|
||||
|
@ -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) => {
|
||||
|
@ -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(
|
||||
|
@ -5,6 +5,7 @@ export interface EventUser {
|
||||
email?: string;
|
||||
username: string;
|
||||
on_behalf_of?: EventUser;
|
||||
is_anonymous?: boolean;
|
||||
}
|
||||
|
||||
export interface EventContext {
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>`,
|
||||
];
|
||||
|
106
web/xliff/de.xlf
106
web/xliff/de.xlf
@ -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>
|
||||
|
118
web/xliff/en.xlf
118
web/xliff/en.xlf
@ -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>
|
||||
|
104
web/xliff/es.xlf
104
web/xliff/es.xlf
@ -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>
|
||||
|
2960
web/xliff/fr.xlf
2960
web/xliff/fr.xlf
File diff suppressed because it is too large
Load Diff
2842
web/xliff/ko.xlf
2842
web/xliff/ko.xlf
File diff suppressed because it is too large
Load Diff
2830
web/xliff/nl.xlf
2830
web/xliff/nl.xlf
File diff suppressed because it is too large
Load Diff
114
web/xliff/pl.xlf
114
web/xliff/pl.xlf
@ -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
@ -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>
|
||||
|
@ -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
@ -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>
|
||||
|
2902
web/xliff/zh_CN.xlf
2902
web/xliff/zh_CN.xlf
File diff suppressed because it is too large
Load Diff
2830
web/xliff/zh_TW.xlf
2830
web/xliff/zh_TW.xlf
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user