diff --git a/authentik/api/v3/config.py b/authentik/api/v3/config.py index bbc676647..0defd1a5b 100644 --- a/authentik/api/v3/config.py +++ b/authentik/api/v3/config.py @@ -93,10 +93,10 @@ class ConfigView(APIView): "traces_sample_rate": float(CONFIG.get("error_reporting.sample_rate", 0.4)), }, "capabilities": self.get_capabilities(), - "cache_timeout": CONFIG.get_int("redis.cache_timeout"), - "cache_timeout_flows": CONFIG.get_int("redis.cache_timeout_flows"), - "cache_timeout_policies": CONFIG.get_int("redis.cache_timeout_policies"), - "cache_timeout_reputation": CONFIG.get_int("redis.cache_timeout_reputation"), + "cache_timeout": CONFIG.get_int("cache.timeout"), + "cache_timeout_flows": CONFIG.get_int("cache.timeout_flows"), + "cache_timeout_policies": CONFIG.get_int("cache.timeout_policies"), + "cache_timeout_reputation": CONFIG.get_int("cache.timeout_reputation"), } ) diff --git a/authentik/flows/planner.py b/authentik/flows/planner.py index 4cf3c6aad..f80461490 100644 --- a/authentik/flows/planner.py +++ b/authentik/flows/planner.py @@ -33,7 +33,7 @@ PLAN_CONTEXT_SOURCE = "source" # Is set by the Flow Planner when a FlowToken was used, and the currently active flow plan # was restored. PLAN_CONTEXT_IS_RESTORED = "is_restored" -CACHE_TIMEOUT = CONFIG.get_int("redis.cache_timeout_flows") +CACHE_TIMEOUT = CONFIG.get_int("cache.timeout_flows") CACHE_PREFIX = "goauthentik.io/flows/planner/" diff --git a/authentik/lib/config.py b/authentik/lib/config.py index 63aa3493a..dd5500f8e 100644 --- a/authentik/lib/config.py +++ b/authentik/lib/config.py @@ -1,4 +1,6 @@ """authentik core config loader""" +import base64 +import json import os from collections.abc import Mapping from contextlib import contextmanager @@ -22,6 +24,25 @@ SEARCH_PATHS = ["authentik/lib/default.yml", "/etc/authentik/config.yml", ""] + ENV_PREFIX = "AUTHENTIK" ENVIRONMENT = os.getenv(f"{ENV_PREFIX}_ENV", "local") +REDIS_ENV_KEYS = [ + f"{ENV_PREFIX}_REDIS__HOST", + f"{ENV_PREFIX}_REDIS__PORT", + f"{ENV_PREFIX}_REDIS__DB", + f"{ENV_PREFIX}_REDIS__USERNAME", + f"{ENV_PREFIX}_REDIS__PASSWORD", + f"{ENV_PREFIX}_REDIS__TLS", + f"{ENV_PREFIX}_REDIS__TLS_REQS", +] + +DEPRECATIONS = { + "redis.broker_url": "broker.url", + "redis.broker_transport_options": "broker.transport_options", + "redis.cache_timeout": "cache.timeout", + "redis.cache_timeout_flows": "cache.timeout_flows", + "redis.cache_timeout_policies": "cache.timeout_policies", + "redis.cache_timeout_reputation": "cache.timeout_reputation", +} + def get_path_from_dict(root: dict, path: str, sep=".", default=None) -> Any: """Recursively walk through `root`, checking each part of `path` separated by `sep`. @@ -81,6 +102,10 @@ class AttrEncoder(JSONEncoder): return super().default(o) +class UNSET: + """Used to test whether configuration key has not been set.""" + + class ConfigLoader: """Search through SEARCH_PATHS and load configuration. Environment variables starting with `ENV_PREFIX` are also applied. @@ -113,6 +138,40 @@ class ConfigLoader: self.update_from_file(env_file) self.update_from_env() self.update(self.__config, kwargs) + self.check_deprecations() + + def check_deprecations(self): + """Warn if any deprecated configuration options are used""" + + def _pop_deprecated_key(current_obj, dot_parts, index): + """Recursive function to remove deprecated keys in configuration""" + dot_part = dot_parts[index] + if index == len(dot_parts) - 1: + return current_obj.pop(dot_part) + value = _pop_deprecated_key(current_obj[dot_part], dot_parts, index + 1) + if not current_obj[dot_part]: + current_obj.pop(dot_part) + return value + + for deprecation, replacement in DEPRECATIONS.items(): + if self.get(deprecation, default=UNSET) is not UNSET: + message = ( + f"'{deprecation}' has been deprecated in favor of '{replacement}'! " + + "Please update your configuration." + ) + self.log( + "warning", + message, + ) + try: + from authentik.events.models import Event, EventAction + + Event.new(EventAction.CONFIGURATION_ERROR, message=message).save() + except ImportError: + continue + + deprecated_attr = _pop_deprecated_key(self.__config, deprecation.split("."), 0) + self.set(replacement, deprecated_attr.value) def log(self, level: str, message: str, **kwargs): """Custom Log method, we want to ensure ConfigLoader always logs JSON even when @@ -180,6 +239,10 @@ class ConfigLoader: error=str(exc), ) + def update_from_dict(self, update: dict): + """Update config from dict""" + self.__config.update(update) + def update_from_env(self): """Check environment variables""" outer = {} @@ -188,19 +251,13 @@ class ConfigLoader: if not key.startswith(ENV_PREFIX): continue relative_key = key.replace(f"{ENV_PREFIX}_", "", 1).replace("__", ".").lower() - # Recursively convert path from a.b.c into outer[a][b][c] - current_obj = outer - dot_parts = relative_key.split(".") - for dot_part in dot_parts[:-1]: - if dot_part not in current_obj: - current_obj[dot_part] = {} - current_obj = current_obj[dot_part] # Check if the value is json, and try to load it try: value = loads(value) except JSONDecodeError: pass - current_obj[dot_parts[-1]] = Attr(value, Attr.Source.ENV, key) + attr_value = Attr(value, Attr.Source.ENV, relative_key) + set_path_in_dict(outer, relative_key, attr_value) idx += 1 if idx > 0: self.log("debug", "Loaded environment variables", count=idx) @@ -241,6 +298,23 @@ class ConfigLoader: """Wrapper for get that converts value into boolean""" return str(self.get(path, default)).lower() == "true" + def get_dict_from_b64_json(self, path: str, default=None) -> dict: + """Wrapper for get that converts value from Base64 encoded string into dictionary""" + config_value = self.get(path) + if config_value is None: + return {} + try: + b64decoded_str = base64.b64decode(config_value).decode("utf-8") + b64decoded_str = b64decoded_str.strip().lstrip("{").rstrip("}") + b64decoded_str = "{" + b64decoded_str + "}" + return json.loads(b64decoded_str) + except (JSONDecodeError, TypeError, ValueError) as exc: + self.log( + "warning", + f"Ignored invalid configuration for '{path}' due to exception: {str(exc)}", + ) + return default if isinstance(default, dict) else {} + def set(self, path: str, value: Any, sep="."): """Set value using same syntax as get()""" set_path_in_dict(self.raw, path, Attr(value), sep=sep) diff --git a/authentik/lib/default.yml b/authentik/lib/default.yml index 793bece13..83bd3c7c9 100644 --- a/authentik/lib/default.yml +++ b/authentik/lib/default.yml @@ -28,14 +28,28 @@ listen: redis: host: localhost port: 6379 + db: 0 + username: "" password: "" tls: false tls_reqs: "none" - db: 0 - cache_timeout: 300 - cache_timeout_flows: 300 - cache_timeout_policies: 300 - cache_timeout_reputation: 300 + +# broker: + # url: "" + # transport_options: "" + +cache: + # url: "" + timeout: 300 + timeout_flows: 300 + timeout_policies: 300 + timeout_reputation: 300 + +# channel: + # url: "" + +# result_backend: + # url: "" paths: media: ./media diff --git a/authentik/lib/tests/test_config.py b/authentik/lib/tests/test_config.py index d95ff8fb8..3c253663f 100644 --- a/authentik/lib/tests/test_config.py +++ b/authentik/lib/tests/test_config.py @@ -1,20 +1,32 @@ """Test config loader""" +import base64 +from json import dumps from os import chmod, environ, unlink, write from tempfile import mkstemp +from unittest import mock from django.conf import ImproperlyConfigured from django.test import TestCase -from authentik.lib.config import ENV_PREFIX, ConfigLoader +from authentik.lib.config import ENV_PREFIX, UNSET, Attr, AttrEncoder, ConfigLoader class TestConfig(TestCase): """Test config loader""" + check_deprecations_env_vars = { + ENV_PREFIX + "_REDIS__BROKER_URL": "redis://myredis:8327/43", + ENV_PREFIX + "_REDIS__BROKER_TRANSPORT_OPTIONS": "bWFzdGVybmFtZT1teW1hc3Rlcg==", + ENV_PREFIX + "_REDIS__CACHE_TIMEOUT": "124s", + ENV_PREFIX + "_REDIS__CACHE_TIMEOUT_FLOWS": "32m", + ENV_PREFIX + "_REDIS__CACHE_TIMEOUT_POLICIES": "3920ns", + ENV_PREFIX + "_REDIS__CACHE_TIMEOUT_REPUTATION": "298382us", + } + + @mock.patch.dict(environ, {ENV_PREFIX + "_test__test": "bar"}) def test_env(self): """Test simple instance""" config = ConfigLoader() - environ[ENV_PREFIX + "_test__test"] = "bar" config.update_from_env() self.assertEqual(config.get("test.test"), "bar") @@ -27,12 +39,20 @@ class TestConfig(TestCase): self.assertEqual(config.get("foo.bar"), "baz") self.assertEqual(config.get("foo.bar"), "bar") + @mock.patch.dict(environ, {"foo": "bar"}) def test_uri_env(self): """Test URI parsing (environment)""" config = ConfigLoader() - environ["foo"] = "bar" - self.assertEqual(config.parse_uri("env://foo").value, "bar") - self.assertEqual(config.parse_uri("env://foo?bar").value, "bar") + foo_uri = "env://foo" + foo_parsed = config.parse_uri(foo_uri) + self.assertEqual(foo_parsed.value, "bar") + self.assertEqual(foo_parsed.source_type, Attr.Source.URI) + self.assertEqual(foo_parsed.source, foo_uri) + foo_bar_uri = "env://foo?bar" + foo_bar_parsed = config.parse_uri(foo_bar_uri) + self.assertEqual(foo_bar_parsed.value, "bar") + self.assertEqual(foo_bar_parsed.source_type, Attr.Source.URI) + self.assertEqual(foo_bar_parsed.source, foo_bar_uri) def test_uri_file(self): """Test URI parsing (file load)""" @@ -91,3 +111,60 @@ class TestConfig(TestCase): config = ConfigLoader() config.set("foo", "bar") self.assertEqual(config.get_int("foo", 1234), 1234) + + def test_get_dict_from_b64_json(self): + """Test get_dict_from_b64_json""" + config = ConfigLoader() + test_value = ' { "foo": "bar" } '.encode("utf-8") + b64_value = base64.b64encode(test_value) + config.set("foo", b64_value) + self.assertEqual(config.get_dict_from_b64_json("foo"), {"foo": "bar"}) + + def test_get_dict_from_b64_json_missing_brackets(self): + """Test get_dict_from_b64_json with missing brackets""" + config = ConfigLoader() + test_value = ' "foo": "bar" '.encode("utf-8") + b64_value = base64.b64encode(test_value) + config.set("foo", b64_value) + self.assertEqual(config.get_dict_from_b64_json("foo"), {"foo": "bar"}) + + def test_get_dict_from_b64_json_invalid(self): + """Test get_dict_from_b64_json with invalid value""" + config = ConfigLoader() + config.set("foo", "bar") + self.assertEqual(config.get_dict_from_b64_json("foo"), {}) + + def test_attr_json_encoder(self): + """Test AttrEncoder""" + test_attr = Attr("foo", Attr.Source.ENV, "AUTHENTIK_REDIS__USERNAME") + json_attr = dumps(test_attr, indent=4, cls=AttrEncoder) + self.assertEqual(json_attr, '"foo"') + + def test_attr_json_encoder_no_attr(self): + """Test AttrEncoder if no Attr is passed""" + + class Test: + """Non Attr class""" + + with self.assertRaises(TypeError): + test_obj = Test() + dumps(test_obj, indent=4, cls=AttrEncoder) + + @mock.patch.dict(environ, check_deprecations_env_vars) + def test_check_deprecations(self): + """Test config key re-write for deprecated env vars""" + config = ConfigLoader() + config.update_from_env() + config.check_deprecations() + self.assertEqual(config.get("redis.broker_url", UNSET), UNSET) + self.assertEqual(config.get("redis.broker_transport_options", UNSET), UNSET) + self.assertEqual(config.get("redis.cache_timeout", UNSET), UNSET) + self.assertEqual(config.get("redis.cache_timeout_flows", UNSET), UNSET) + self.assertEqual(config.get("redis.cache_timeout_policies", UNSET), UNSET) + self.assertEqual(config.get("redis.cache_timeout_reputation", UNSET), UNSET) + self.assertEqual(config.get("broker.url"), "redis://myredis:8327/43") + self.assertEqual(config.get("broker.transport_options"), "bWFzdGVybmFtZT1teW1hc3Rlcg==") + self.assertEqual(config.get("cache.timeout"), "124s") + self.assertEqual(config.get("cache.timeout_flows"), "32m") + self.assertEqual(config.get("cache.timeout_policies"), "3920ns") + self.assertEqual(config.get("cache.timeout_reputation"), "298382us") diff --git a/authentik/outposts/consumer.py b/authentik/outposts/consumer.py index e8c2ee127..dda3feed0 100644 --- a/authentik/outposts/consumer.py +++ b/authentik/outposts/consumer.py @@ -93,7 +93,7 @@ class OutpostConsumer(AuthJsonConsumer): expected=self.outpost.config.kubernetes_replicas, ).dec() - def receive_json(self, content: Data): + def receive_json(self, content: Data, **kwargs): msg = from_dict(WebsocketMessage, content) uid = msg.args.get("uuid", self.channel_name) self.last_uid = uid diff --git a/authentik/policies/process.py b/authentik/policies/process.py index aa3ed9ff5..61ada16b7 100644 --- a/authentik/policies/process.py +++ b/authentik/policies/process.py @@ -20,7 +20,7 @@ from authentik.policies.types import CACHE_PREFIX, PolicyRequest, PolicyResult LOGGER = get_logger() FORK_CTX = get_context("fork") -CACHE_TIMEOUT = CONFIG.get_int("redis.cache_timeout_policies") +CACHE_TIMEOUT = CONFIG.get_int("cache.timeout_policies") PROCESS_CLASS = FORK_CTX.Process diff --git a/authentik/policies/reputation/signals.py b/authentik/policies/reputation/signals.py index 2ee6045df..49b8cf011 100644 --- a/authentik/policies/reputation/signals.py +++ b/authentik/policies/reputation/signals.py @@ -13,7 +13,7 @@ from authentik.policies.reputation.tasks import save_reputation from authentik.stages.identification.signals import identification_failed LOGGER = get_logger() -CACHE_TIMEOUT = CONFIG.get_int("redis.cache_timeout_reputation") +CACHE_TIMEOUT = CONFIG.get_int("cache.timeout_reputation") def update_score(request: HttpRequest, identifier: str, amount: int): diff --git a/authentik/root/settings.py b/authentik/root/settings.py index ee31f2cc6..5c9e5bf45 100644 --- a/authentik/root/settings.py +++ b/authentik/root/settings.py @@ -1,5 +1,4 @@ """root settings for authentik""" - import importlib import os from hashlib import sha512 @@ -195,8 +194,8 @@ _redis_url = ( CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", - "LOCATION": f"{_redis_url}/{CONFIG.get('redis.db')}", - "TIMEOUT": CONFIG.get_int("redis.cache_timeout", 300), + "LOCATION": CONFIG.get("cache.url") or f"{_redis_url}/{CONFIG.get('redis.db')}", + "TIMEOUT": CONFIG.get_int("cache.timeout", 300), "OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient"}, "KEY_PREFIX": "authentik_cache", } @@ -256,7 +255,7 @@ CHANNEL_LAYERS = { "default": { "BACKEND": "channels_redis.pubsub.RedisPubSubChannelLayer", "CONFIG": { - "hosts": [f"{_redis_url}/{CONFIG.get('redis.db')}"], + "hosts": [CONFIG.get("channel.url", f"{_redis_url}/{CONFIG.get('redis.db')}")], "prefix": "authentik_channels_", }, }, @@ -349,8 +348,11 @@ CELERY = { }, "task_create_missing_queues": True, "task_default_queue": "authentik", - "broker_url": f"{_redis_url}/{CONFIG.get('redis.db')}{_redis_celery_tls_requirements}", - "result_backend": f"{_redis_url}/{CONFIG.get('redis.db')}{_redis_celery_tls_requirements}", + "broker_url": CONFIG.get("broker.url") + or f"{_redis_url}/{CONFIG.get('redis.db')}{_redis_celery_tls_requirements}", + "broker_transport_options": CONFIG.get_dict_from_b64_json("broker.transport_options"), + "result_backend": CONFIG.get("result_backend.url") + or f"{_redis_url}/{CONFIG.get('redis.db')}{_redis_celery_tls_requirements}", } # Sentry integration diff --git a/authentik/sources/ldap/api.py b/authentik/sources/ldap/api.py index 1914c66da..08e530bbb 100644 --- a/authentik/sources/ldap/api.py +++ b/authentik/sources/ldap/api.py @@ -1,13 +1,14 @@ """Source API Views""" -from typing import Any +from typing import Any, Optional +from django.core.cache import cache 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 rest_framework.decorators import action from rest_framework.exceptions import ValidationError -from rest_framework.fields import DictField, ListField +from rest_framework.fields import BooleanField, DictField, ListField, SerializerMethodField from rest_framework.relations import PrimaryKeyRelatedField from rest_framework.request import Request from rest_framework.response import Response @@ -17,15 +18,17 @@ 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.sources.ldap.models import LDAPPropertyMapping, LDAPSource -from authentik.sources.ldap.tasks import SYNC_CLASSES +from authentik.sources.ldap.tasks import CACHE_KEY_STATUS, SYNC_CLASSES class LDAPSourceSerializer(SourceSerializer): """LDAP Source Serializer""" + connectivity = SerializerMethodField() client_certificate = PrimaryKeyRelatedField( allow_null=True, help_text="Client certificate to authenticate against the LDAP Server's Certificate.", @@ -35,6 +38,10 @@ class LDAPSourceSerializer(SourceSerializer): required=False, ) + def get_connectivity(self, source: LDAPSource) -> Optional[dict[str, dict[str, str]]]: + """Get cached source connectivity""" + return cache.get(CACHE_KEY_STATUS + source.slug, None) + def validate(self, attrs: dict[str, Any]) -> dict[str, Any]: """Check that only a single source has password_sync on""" sync_users_password = attrs.get("sync_users_password", True) @@ -75,10 +82,18 @@ class LDAPSourceSerializer(SourceSerializer): "sync_parent_group", "property_mappings", "property_mappings_group", + "connectivity", ] extra_kwargs = {"bind_password": {"write_only": True}} +class LDAPSyncStatusSerializer(PassiveSerializer): + """LDAP Source sync status""" + + is_running = BooleanField(read_only=True) + tasks = TaskSerializer(many=True, read_only=True) + + class LDAPSourceViewSet(UsedByMixin, ModelViewSet): """LDAP Source Viewset""" @@ -114,19 +129,19 @@ class LDAPSourceViewSet(UsedByMixin, ModelViewSet): @extend_schema( responses={ - 200: TaskSerializer(many=True), + 200: LDAPSyncStatusSerializer(), } ) @action(methods=["GET"], detail=True, pagination_class=None, filter_backends=[]) def sync_status(self, request: Request, slug: str) -> Response: """Get source's sync status""" - source = self.get_object() - results = [] - tasks = TaskInfo.by_name(f"ldap_sync:{source.slug}:*") - if tasks: - for task in tasks: - results.append(task) - return Response(TaskSerializer(results, many=True).data) + source: LDAPSource = self.get_object() + tasks = TaskInfo.by_name(f"ldap_sync:{source.slug}:*") or [] + status = { + "tasks": tasks, + "is_running": source.sync_lock.locked(), + } + return Response(LDAPSyncStatusSerializer(status).data) @extend_schema( responses={ diff --git a/authentik/sources/ldap/management/commands/ldap_check_connection.py b/authentik/sources/ldap/management/commands/ldap_check_connection.py new file mode 100644 index 000000000..6da316aa4 --- /dev/null +++ b/authentik/sources/ldap/management/commands/ldap_check_connection.py @@ -0,0 +1,24 @@ +"""LDAP Connection check""" +from json import dumps + +from django.core.management.base import BaseCommand +from structlog.stdlib import get_logger + +from authentik.sources.ldap.models import LDAPSource + +LOGGER = get_logger() + + +class Command(BaseCommand): + """Check connectivity to LDAP servers for a source""" + + def add_arguments(self, parser): + parser.add_argument("source_slugs", nargs="?", type=str) + + def handle(self, **options): + sources = LDAPSource.objects.filter(enabled=True) + if options["source_slugs"]: + sources = LDAPSource.objects.filter(slug__in=options["source_slugs"]) + for source in sources.order_by("slug"): + status = source.check_connection() + self.stdout.write(dumps(status, indent=4)) diff --git a/authentik/sources/ldap/models.py b/authentik/sources/ldap/models.py index ac7f32aca..a09791593 100644 --- a/authentik/sources/ldap/models.py +++ b/authentik/sources/ldap/models.py @@ -4,10 +4,12 @@ from ssl import CERT_REQUIRED from tempfile import NamedTemporaryFile, mkdtemp from typing import Optional +from django.core.cache import cache from django.db import models from django.utils.translation import gettext_lazy as _ from ldap3 import ALL, NONE, RANDOM, Connection, Server, ServerPool, Tls -from ldap3.core.exceptions import LDAPInsufficientAccessRightsResult, LDAPSchemaError +from ldap3.core.exceptions import LDAPException, LDAPInsufficientAccessRightsResult, LDAPSchemaError +from redis.lock import Lock from rest_framework.serializers import Serializer from authentik.core.models import Group, PropertyMapping, Source @@ -117,7 +119,7 @@ class LDAPSource(Source): return LDAPSourceSerializer - def server(self, **kwargs) -> Server: + def server(self, **kwargs) -> ServerPool: """Get LDAP Server/ServerPool""" servers = [] tls_kwargs = {} @@ -154,7 +156,10 @@ class LDAPSource(Source): return ServerPool(servers, RANDOM, active=5, exhaust=True) def connection( - self, server_kwargs: Optional[dict] = None, connection_kwargs: Optional[dict] = None + self, + server: Optional[Server] = None, + server_kwargs: Optional[dict] = None, + connection_kwargs: Optional[dict] = None, ) -> Connection: """Get a fully connected and bound LDAP Connection""" server_kwargs = server_kwargs or {} @@ -164,7 +169,7 @@ class LDAPSource(Source): if self.bind_password is not None: connection_kwargs.setdefault("password", self.bind_password) connection = Connection( - self.server(**server_kwargs), + server or self.server(**server_kwargs), raise_exceptions=True, receive_timeout=LDAP_TIMEOUT, **connection_kwargs, @@ -183,9 +188,55 @@ class LDAPSource(Source): if server_kwargs.get("get_info", ALL) == NONE: raise exc server_kwargs["get_info"] = NONE - return self.connection(server_kwargs, connection_kwargs) + return self.connection(server, server_kwargs, connection_kwargs) return RuntimeError("Failed to bind") + @property + def sync_lock(self) -> Lock: + """Redis lock for syncing LDAP to prevent multiple parallel syncs happening""" + return Lock( + cache.client.get_client(), + name=f"goauthentik.io/sources/ldap/sync-{self.slug}", + # Convert task timeout hours to seconds, and multiply times 3 + # (see authentik/sources/ldap/tasks.py:54) + # multiply by 3 to add even more leeway + timeout=(60 * 60 * CONFIG.get_int("ldap.task_timeout_hours")) * 3, + ) + + def check_connection(self) -> dict[str, dict[str, str]]: + """Check LDAP Connection""" + from authentik.sources.ldap.sync.base import flatten + + servers = self.server() + server_info = {} + # Check each individual server + for server in servers.servers: + server: Server + try: + connection = self.connection(server=server) + server_info[server.host] = { + "vendor": str(flatten(connection.server.info.vendor_name)), + "version": str(flatten(connection.server.info.vendor_version)), + "status": "ok", + } + except LDAPException as exc: + server_info[server.host] = { + "status": str(exc), + } + # Check server pool + try: + connection = self.connection() + server_info["__all__"] = { + "vendor": str(flatten(connection.server.info.vendor_name)), + "version": str(flatten(connection.server.info.vendor_version)), + "status": "ok", + } + except LDAPException as exc: + server_info["__all__"] = { + "status": str(exc), + } + return server_info + class Meta: verbose_name = _("LDAP Source") verbose_name_plural = _("LDAP Sources") diff --git a/authentik/sources/ldap/settings.py b/authentik/sources/ldap/settings.py index 6b526b357..b141687f5 100644 --- a/authentik/sources/ldap/settings.py +++ b/authentik/sources/ldap/settings.py @@ -8,5 +8,10 @@ CELERY_BEAT_SCHEDULE = { "task": "authentik.sources.ldap.tasks.ldap_sync_all", "schedule": crontab(minute=fqdn_rand("sources_ldap_sync"), hour="*/2"), "options": {"queue": "authentik_scheduled"}, - } + }, + "sources_ldap_connectivity_check": { + "task": "authentik.sources.ldap.tasks.ldap_connectivity_check", + "schedule": crontab(minute=fqdn_rand("sources_ldap_connectivity_check"), hour="*"), + "options": {"queue": "authentik_scheduled"}, + }, } diff --git a/authentik/sources/ldap/signals.py b/authentik/sources/ldap/signals.py index 5af97376d..f95662e33 100644 --- a/authentik/sources/ldap/signals.py +++ b/authentik/sources/ldap/signals.py @@ -14,7 +14,7 @@ from authentik.events.models import Event, EventAction from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER from authentik.sources.ldap.models import LDAPSource from authentik.sources.ldap.password import LDAPPasswordChanger -from authentik.sources.ldap.tasks import ldap_sync_single +from authentik.sources.ldap.tasks import ldap_connectivity_check, ldap_sync_single from authentik.stages.prompt.signals import password_validate LOGGER = get_logger() @@ -32,6 +32,7 @@ def sync_ldap_source_on_save(sender, instance: LDAPSource, **_): if not instance.property_mappings.exists() or not instance.property_mappings_group.exists(): return ldap_sync_single.delay(instance.pk) + ldap_connectivity_check.delay(instance.pk) @receiver(password_validate) diff --git a/authentik/sources/ldap/sync/base.py b/authentik/sources/ldap/sync/base.py index 7490449ec..d3ae11f32 100644 --- a/authentik/sources/ldap/sync/base.py +++ b/authentik/sources/ldap/sync/base.py @@ -17,6 +17,15 @@ from authentik.sources.ldap.models import LDAPPropertyMapping, LDAPSource LDAP_UNIQUENESS = "ldap_uniq" +def flatten(value: Any) -> Any: + """Flatten `value` if its a list""" + if isinstance(value, list): + if len(value) < 1: + return None + return value[0] + return value + + class BaseLDAPSynchronizer: """Sync LDAP Users and groups into authentik""" @@ -122,14 +131,6 @@ class BaseLDAPSynchronizer: cookie = None yield self._connection.response - def _flatten(self, value: Any) -> Any: - """Flatten `value` if its a list""" - if isinstance(value, list): - if len(value) < 1: - return None - return value[0] - return value - def build_user_properties(self, user_dn: str, **kwargs) -> dict[str, Any]: """Build attributes for User object based on property mappings.""" props = self._build_object_properties(user_dn, self._source.property_mappings, **kwargs) @@ -163,10 +164,10 @@ class BaseLDAPSynchronizer: object_field = mapping.object_field if object_field.startswith("attributes."): # Because returning a list might desired, we can't - # rely on self._flatten here. Instead, just save the result as-is + # rely on flatten here. Instead, just save the result as-is set_path_in_dict(properties, object_field, value) else: - properties[object_field] = self._flatten(value) + properties[object_field] = flatten(value) except PropertyMappingExpressionException as exc: Event.new( EventAction.CONFIGURATION_ERROR, @@ -177,7 +178,7 @@ class BaseLDAPSynchronizer: self._logger.warning("Mapping failed to evaluate", exc=exc, mapping=mapping) continue if self._source.object_uniqueness_field in kwargs: - properties["attributes"][LDAP_UNIQUENESS] = self._flatten( + properties["attributes"][LDAP_UNIQUENESS] = flatten( kwargs.get(self._source.object_uniqueness_field) ) properties["attributes"][LDAP_DISTINGUISHED_NAME] = object_dn diff --git a/authentik/sources/ldap/sync/groups.py b/authentik/sources/ldap/sync/groups.py index 68eedcc34..92781c3ac 100644 --- a/authentik/sources/ldap/sync/groups.py +++ b/authentik/sources/ldap/sync/groups.py @@ -7,7 +7,7 @@ from ldap3 import ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES, SUBTREE from authentik.core.models import Group from authentik.events.models import Event, EventAction -from authentik.sources.ldap.sync.base import LDAP_UNIQUENESS, BaseLDAPSynchronizer +from authentik.sources.ldap.sync.base import LDAP_UNIQUENESS, BaseLDAPSynchronizer, flatten class GroupLDAPSynchronizer(BaseLDAPSynchronizer): @@ -39,7 +39,7 @@ class GroupLDAPSynchronizer(BaseLDAPSynchronizer): if "attributes" not in group: continue attributes = group.get("attributes", {}) - group_dn = self._flatten(self._flatten(group.get("entryDN", group.get("dn")))) + group_dn = flatten(flatten(group.get("entryDN", group.get("dn")))) if self._source.object_uniqueness_field not in attributes: self.message( f"Cannot find uniqueness field in attributes: '{group_dn}'", @@ -47,7 +47,7 @@ class GroupLDAPSynchronizer(BaseLDAPSynchronizer): dn=group_dn, ) continue - uniq = self._flatten(attributes[self._source.object_uniqueness_field]) + uniq = flatten(attributes[self._source.object_uniqueness_field]) try: defaults = self.build_group_properties(group_dn, **attributes) defaults["parent"] = self._source.sync_parent_group diff --git a/authentik/sources/ldap/sync/users.py b/authentik/sources/ldap/sync/users.py index 68d966022..6c4d3bd0e 100644 --- a/authentik/sources/ldap/sync/users.py +++ b/authentik/sources/ldap/sync/users.py @@ -7,7 +7,7 @@ from ldap3 import ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES, SUBTREE from authentik.core.models import User from authentik.events.models import Event, EventAction -from authentik.sources.ldap.sync.base import LDAP_UNIQUENESS, BaseLDAPSynchronizer +from authentik.sources.ldap.sync.base import LDAP_UNIQUENESS, BaseLDAPSynchronizer, flatten from authentik.sources.ldap.sync.vendor.freeipa import FreeIPA from authentik.sources.ldap.sync.vendor.ms_ad import MicrosoftActiveDirectory @@ -41,7 +41,7 @@ class UserLDAPSynchronizer(BaseLDAPSynchronizer): if "attributes" not in user: continue attributes = user.get("attributes", {}) - user_dn = self._flatten(user.get("entryDN", user.get("dn"))) + user_dn = flatten(user.get("entryDN", user.get("dn"))) if self._source.object_uniqueness_field not in attributes: self.message( f"Cannot find uniqueness field in attributes: '{user_dn}'", @@ -49,7 +49,7 @@ class UserLDAPSynchronizer(BaseLDAPSynchronizer): dn=user_dn, ) continue - uniq = self._flatten(attributes[self._source.object_uniqueness_field]) + uniq = flatten(attributes[self._source.object_uniqueness_field]) try: defaults = self.build_user_properties(user_dn, **attributes) self._logger.debug("Writing user with attributes", **defaults) diff --git a/authentik/sources/ldap/sync/vendor/freeipa.py b/authentik/sources/ldap/sync/vendor/freeipa.py index fd42c001c..d0bce0584 100644 --- a/authentik/sources/ldap/sync/vendor/freeipa.py +++ b/authentik/sources/ldap/sync/vendor/freeipa.py @@ -5,7 +5,7 @@ from typing import Any, Generator from pytz import UTC from authentik.core.models import User -from authentik.sources.ldap.sync.base import BaseLDAPSynchronizer +from authentik.sources.ldap.sync.base import BaseLDAPSynchronizer, flatten class FreeIPA(BaseLDAPSynchronizer): @@ -47,7 +47,7 @@ class FreeIPA(BaseLDAPSynchronizer): return # For some reason, nsaccountlock is not defined properly in the schema as bool # hence we get it as a list of strings - _is_locked = str(self._flatten(attributes.get("nsaccountlock", ["FALSE"]))) + _is_locked = str(flatten(attributes.get("nsaccountlock", ["FALSE"]))) # So we have to attempt to convert it to a bool is_locked = _is_locked.lower() == "true" # And then invert it since freeipa saves locked and we save active diff --git a/authentik/sources/ldap/tasks.py b/authentik/sources/ldap/tasks.py index 9c4d6af73..7f00d6bb3 100644 --- a/authentik/sources/ldap/tasks.py +++ b/authentik/sources/ldap/tasks.py @@ -1,13 +1,14 @@ """LDAP Sync tasks""" +from typing import Optional from uuid import uuid4 from celery import chain, group from django.core.cache import cache from ldap3.core.exceptions import LDAPException from redis.exceptions import LockError -from redis.lock import Lock 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.lib.config import CONFIG from authentik.lib.utils.errors import exception_to_string @@ -26,6 +27,7 @@ SYNC_CLASSES = [ MembershipLDAPSynchronizer, ] CACHE_KEY_PREFIX = "goauthentik.io/sources/ldap/page/" +CACHE_KEY_STATUS = "goauthentik.io/sources/ldap/status/" @CELERY_APP.task() @@ -35,6 +37,19 @@ def ldap_sync_all(): ldap_sync_single.apply_async(args=[source.pk]) +@CELERY_APP.task() +def ldap_connectivity_check(pk: Optional[str] = None): + """Check connectivity for LDAP Sources""" + # 2 hour timeout, this task should run every hour + timeout = 60 * 60 * 2 + sources = LDAPSource.objects.filter(enabled=True) + if pk: + sources = sources.filter(pk=pk) + for source in sources: + status = source.check_connection() + cache.set(CACHE_KEY_STATUS + source.slug, status, timeout=timeout) + + @CELERY_APP.task( # We take the configured hours timeout time by 2.5 as we run user and # group in parallel and then membership, so 2x is to cover the serial tasks, @@ -47,12 +62,15 @@ def ldap_sync_single(source_pk: str): source: LDAPSource = LDAPSource.objects.filter(pk=source_pk).first() if not source: return - lock = Lock(cache.client.get_client(), name=f"goauthentik.io/sources/ldap/sync-{source.slug}") + lock = source.sync_lock if lock.locked(): LOGGER.debug("LDAP sync locked, skipping task", source=source.slug) return 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) task = chain( # User and group sync can happen at once, they have no dependencies on each other group( diff --git a/go.mod b/go.mod index 381bc58f1..348fe36d9 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.8.0 github.com/stretchr/testify v1.8.4 - goauthentik.io/api/v3 v3.2023102.1 + goauthentik.io/api/v3 v3.2023103.1 golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab golang.org/x/oauth2 v0.14.0 golang.org/x/sync v0.5.0 diff --git a/go.sum b/go.sum index 3271b54ef..c2ad62fd3 100644 --- a/go.sum +++ b/go.sum @@ -358,8 +358,8 @@ go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyK go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= -goauthentik.io/api/v3 v3.2023102.1 h1:TinB3fzh17iw92Mak0pxVdVSMJbL2CxZkQSvd98C4+U= -goauthentik.io/api/v3 v3.2023102.1/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw= +goauthentik.io/api/v3 v3.2023103.1 h1:KqZny4BPDEQ6cIDuZ9pn6/kpvyu+o6o/EekAfujffow= +goauthentik.io/api/v3 v3.2023103.1/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= diff --git a/internal/config/struct.go b/internal/config/struct.go index 27ad926b0..c42fb2c7a 100644 --- a/internal/config/struct.go +++ b/internal/config/struct.go @@ -27,14 +27,11 @@ type Config struct { type RedisConfig struct { Host string `yaml:"host" env:"AUTHENTIK_REDIS__HOST"` Port int `yaml:"port" env:"AUTHENTIK_REDIS__PORT"` + DB int `yaml:"db" env:"AUTHENTIK_REDIS__DB"` + Username string `yaml:"username" env:"AUTHENTIK_REDIS__USERNAME"` Password string `yaml:"password" env:"AUTHENTIK_REDIS__PASSWORD"` TLS bool `yaml:"tls" env:"AUTHENTIK_REDIS__TLS"` TLSReqs string `yaml:"tls_reqs" env:"AUTHENTIK_REDIS__TLS_REQS"` - DB int `yaml:"cache_db" env:"AUTHENTIK_REDIS__DB"` - CacheTimeout int `yaml:"cache_timeout" env:"AUTHENTIK_REDIS__CACHE_TIMEOUT"` - CacheTimeoutFlows int `yaml:"cache_timeout_flows" env:"AUTHENTIK_REDIS__CACHE_TIMEOUT_FLOWS"` - CacheTimeoutPolicies int `yaml:"cache_timeout_policies" env:"AUTHENTIK_REDIS__CACHE_TIMEOUT_POLICIES"` - CacheTimeoutReputation int `yaml:"cache_timeout_reputation" env:"AUTHENTIK_REDIS__CACHE_TIMEOUT_REPUTATION"` } type ListenConfig struct { diff --git a/internal/outpost/flow/executor.go b/internal/outpost/flow/executor.go index 998249e5c..10dca6bb5 100644 --- a/internal/outpost/flow/executor.go +++ b/internal/outpost/flow/executor.go @@ -29,16 +29,6 @@ var ( Name: "authentik_outpost_flow_timing_post_seconds", Help: "Duration it took to send a challenge in seconds", }, []string{"stage", "flow"}) - - // NOTE: the following metrics are kept for compatibility purpose - FlowTimingGetLegacy = promauto.NewHistogramVec(prometheus.HistogramOpts{ - Name: "authentik_outpost_flow_timing_get", - Help: "Duration it took to get a challenge", - }, []string{"stage", "flow"}) - FlowTimingPostLegacy = promauto.NewHistogramVec(prometheus.HistogramOpts{ - Name: "authentik_outpost_flow_timing_post", - Help: "Duration it took to send a challenge", - }, []string{"stage", "flow"}) ) type SolverFunction func(*api.ChallengeTypes, api.ApiFlowsExecutorSolveRequest) (api.FlowChallengeResponseRequest, error) @@ -198,10 +188,6 @@ func (fe *FlowExecutor) getInitialChallenge() (*api.ChallengeTypes, error) { "stage": ch.GetComponent(), "flow": fe.flowSlug, }).Observe(float64(gcsp.EndTime.Sub(gcsp.StartTime)) / float64(time.Second)) - FlowTimingGetLegacy.With(prometheus.Labels{ - "stage": ch.GetComponent(), - "flow": fe.flowSlug, - }).Observe(float64(gcsp.EndTime.Sub(gcsp.StartTime))) return challenge, nil } @@ -259,10 +245,6 @@ func (fe *FlowExecutor) solveFlowChallenge(challenge *api.ChallengeTypes, depth "stage": ch.GetComponent(), "flow": fe.flowSlug, }).Observe(float64(scsp.EndTime.Sub(scsp.StartTime)) / float64(time.Second)) - FlowTimingPostLegacy.With(prometheus.Labels{ - "stage": ch.GetComponent(), - "flow": fe.flowSlug, - }).Observe(float64(scsp.EndTime.Sub(scsp.StartTime))) if depth >= 10 { return false, errors.New("exceeded stage recursion depth") diff --git a/internal/outpost/ldap/bind.go b/internal/outpost/ldap/bind.go index 5ab1e42c1..050dea646 100644 --- a/internal/outpost/ldap/bind.go +++ b/internal/outpost/ldap/bind.go @@ -22,11 +22,6 @@ func (ls *LDAPServer) Bind(bindDN string, bindPW string, conn net.Conn) (ldap.LD "type": "bind", "app": selectedApp, }).Observe(float64(span.EndTime.Sub(span.StartTime)) / float64(time.Second)) - metrics.RequestsLegacy.With(prometheus.Labels{ - "outpost_name": ls.ac.Outpost.Name, - "type": "bind", - "app": selectedApp, - }).Observe(float64(span.EndTime.Sub(span.StartTime))) req.Log().WithField("took-ms", span.EndTime.Sub(span.StartTime).Milliseconds()).Info("Bind request") }() @@ -55,12 +50,6 @@ func (ls *LDAPServer) Bind(bindDN string, bindPW string, conn net.Conn) (ldap.LD "reason": "no_provider", "app": "", }).Inc() - metrics.RequestsRejectedLegacy.With(prometheus.Labels{ - "outpost_name": ls.ac.Outpost.Name, - "type": "bind", - "reason": "no_provider", - "app": "", - }).Inc() return ldap.LDAPResultInsufficientAccessRights, nil } diff --git a/internal/outpost/ldap/bind/direct/bind.go b/internal/outpost/ldap/bind/direct/bind.go index f6e49ccfb..cffa0cf36 100644 --- a/internal/outpost/ldap/bind/direct/bind.go +++ b/internal/outpost/ldap/bind/direct/bind.go @@ -47,12 +47,6 @@ func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResul "reason": "flow_error", "app": db.si.GetAppSlug(), }).Inc() - metrics.RequestsRejectedLegacy.With(prometheus.Labels{ - "outpost_name": db.si.GetOutpostName(), - "type": "bind", - "reason": "flow_error", - "app": db.si.GetAppSlug(), - }).Inc() req.Log().WithError(err).Warning("failed to execute flow") return ldap.LDAPResultInvalidCredentials, nil } @@ -63,12 +57,6 @@ func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResul "reason": "invalid_credentials", "app": db.si.GetAppSlug(), }).Inc() - metrics.RequestsRejectedLegacy.With(prometheus.Labels{ - "outpost_name": db.si.GetOutpostName(), - "type": "bind", - "reason": "invalid_credentials", - "app": db.si.GetAppSlug(), - }).Inc() req.Log().Info("Invalid credentials") return ldap.LDAPResultInvalidCredentials, nil } @@ -82,12 +70,6 @@ func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResul "reason": "access_denied", "app": db.si.GetAppSlug(), }).Inc() - metrics.RequestsRejectedLegacy.With(prometheus.Labels{ - "outpost_name": db.si.GetOutpostName(), - "type": "bind", - "reason": "access_denied", - "app": db.si.GetAppSlug(), - }).Inc() return ldap.LDAPResultInsufficientAccessRights, nil } if err != nil { @@ -97,12 +79,6 @@ func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResul "reason": "access_check_fail", "app": db.si.GetAppSlug(), }).Inc() - metrics.RequestsRejectedLegacy.With(prometheus.Labels{ - "outpost_name": db.si.GetOutpostName(), - "type": "bind", - "reason": "access_check_fail", - "app": db.si.GetAppSlug(), - }).Inc() req.Log().WithError(err).Warning("failed to check access") return ldap.LDAPResultOperationsError, nil } @@ -117,12 +93,6 @@ func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResul "reason": "user_info_fail", "app": db.si.GetAppSlug(), }).Inc() - metrics.RequestsRejectedLegacy.With(prometheus.Labels{ - "outpost_name": db.si.GetOutpostName(), - "type": "bind", - "reason": "user_info_fail", - "app": db.si.GetAppSlug(), - }).Inc() req.Log().WithError(err).Warning("failed to get user info") return ldap.LDAPResultOperationsError, nil } diff --git a/internal/outpost/ldap/metrics/metrics.go b/internal/outpost/ldap/metrics/metrics.go index 150f89e7f..8c36b2f88 100644 --- a/internal/outpost/ldap/metrics/metrics.go +++ b/internal/outpost/ldap/metrics/metrics.go @@ -22,16 +22,6 @@ var ( Name: "authentik_outpost_ldap_requests_rejected_total", Help: "Total number of rejected requests", }, []string{"outpost_name", "type", "reason", "app"}) - - // NOTE: the following metrics are kept for compatibility purpose - RequestsLegacy = promauto.NewHistogramVec(prometheus.HistogramOpts{ - Name: "authentik_outpost_ldap_requests", - Help: "The total number of configured providers", - }, []string{"outpost_name", "type", "app"}) - RequestsRejectedLegacy = promauto.NewCounterVec(prometheus.CounterOpts{ - Name: "authentik_outpost_ldap_requests_rejected", - Help: "Total number of rejected requests", - }, []string{"outpost_name", "type", "reason", "app"}) ) func RunServer() { diff --git a/internal/outpost/ldap/search.go b/internal/outpost/ldap/search.go index a330e3fb8..b26df5023 100644 --- a/internal/outpost/ldap/search.go +++ b/internal/outpost/ldap/search.go @@ -23,11 +23,6 @@ func (ls *LDAPServer) Search(bindDN string, searchReq ldap.SearchRequest, conn n "type": "search", "app": selectedApp, }).Observe(float64(span.EndTime.Sub(span.StartTime)) / float64(time.Second)) - metrics.RequestsLegacy.With(prometheus.Labels{ - "outpost_name": ls.ac.Outpost.Name, - "type": "search", - "app": selectedApp, - }).Observe(float64(span.EndTime.Sub(span.StartTime))) req.Log().WithField("attributes", searchReq.Attributes).WithField("took-ms", span.EndTime.Sub(span.StartTime).Milliseconds()).Info("Search request") }() diff --git a/internal/outpost/ldap/search/direct/direct.go b/internal/outpost/ldap/search/direct/direct.go index 9f7ccd124..7ac59f834 100644 --- a/internal/outpost/ldap/search/direct/direct.go +++ b/internal/outpost/ldap/search/direct/direct.go @@ -45,12 +45,6 @@ func (ds *DirectSearcher) Search(req *search.Request) (ldap.ServerSearchResult, "reason": "empty_bind_dn", "app": ds.si.GetAppSlug(), }).Inc() - metrics.RequestsRejectedLegacy.With(prometheus.Labels{ - "outpost_name": ds.si.GetOutpostName(), - "type": "search", - "reason": "empty_bind_dn", - "app": ds.si.GetAppSlug(), - }).Inc() return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("Search Error: Anonymous BindDN not allowed %s", req.BindDN) } if !utils.HasSuffixNoCase(req.BindDN, ","+baseDN) { @@ -60,12 +54,6 @@ func (ds *DirectSearcher) Search(req *search.Request) (ldap.ServerSearchResult, "reason": "invalid_bind_dn", "app": ds.si.GetAppSlug(), }).Inc() - metrics.RequestsRejectedLegacy.With(prometheus.Labels{ - "outpost_name": ds.si.GetOutpostName(), - "type": "search", - "reason": "invalid_bind_dn", - "app": ds.si.GetAppSlug(), - }).Inc() return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("Search Error: BindDN %s not in our BaseDN %s", req.BindDN, ds.si.GetBaseDN()) } @@ -78,12 +66,6 @@ func (ds *DirectSearcher) Search(req *search.Request) (ldap.ServerSearchResult, "reason": "user_info_not_cached", "app": ds.si.GetAppSlug(), }).Inc() - metrics.RequestsRejectedLegacy.With(prometheus.Labels{ - "outpost_name": ds.si.GetOutpostName(), - "type": "search", - "reason": "user_info_not_cached", - "app": ds.si.GetAppSlug(), - }).Inc() return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, errors.New("access denied") } accsp.Finish() @@ -96,12 +78,6 @@ func (ds *DirectSearcher) Search(req *search.Request) (ldap.ServerSearchResult, "reason": "filter_parse_fail", "app": ds.si.GetAppSlug(), }).Inc() - metrics.RequestsRejectedLegacy.With(prometheus.Labels{ - "outpost_name": ds.si.GetOutpostName(), - "type": "search", - "reason": "filter_parse_fail", - "app": ds.si.GetAppSlug(), - }).Inc() return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("Search Error: error parsing filter: %s", req.Filter) } diff --git a/internal/outpost/ldap/search/memory/memory.go b/internal/outpost/ldap/search/memory/memory.go index 177099f7e..2b80bbbbb 100644 --- a/internal/outpost/ldap/search/memory/memory.go +++ b/internal/outpost/ldap/search/memory/memory.go @@ -62,12 +62,6 @@ func (ms *MemorySearcher) Search(req *search.Request) (ldap.ServerSearchResult, "reason": "empty_bind_dn", "app": ms.si.GetAppSlug(), }).Inc() - metrics.RequestsRejectedLegacy.With(prometheus.Labels{ - "outpost_name": ms.si.GetOutpostName(), - "type": "search", - "reason": "empty_bind_dn", - "app": ms.si.GetAppSlug(), - }).Inc() return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("Search Error: Anonymous BindDN not allowed %s", req.BindDN) } if !utils.HasSuffixNoCase(req.BindDN, ","+baseDN) { @@ -77,12 +71,6 @@ func (ms *MemorySearcher) Search(req *search.Request) (ldap.ServerSearchResult, "reason": "invalid_bind_dn", "app": ms.si.GetAppSlug(), }).Inc() - metrics.RequestsRejectedLegacy.With(prometheus.Labels{ - "outpost_name": ms.si.GetOutpostName(), - "type": "search", - "reason": "invalid_bind_dn", - "app": ms.si.GetAppSlug(), - }).Inc() return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("Search Error: BindDN %s not in our BaseDN %s", req.BindDN, ms.si.GetBaseDN()) } @@ -95,12 +83,6 @@ func (ms *MemorySearcher) Search(req *search.Request) (ldap.ServerSearchResult, "reason": "user_info_not_cached", "app": ms.si.GetAppSlug(), }).Inc() - metrics.RequestsRejectedLegacy.With(prometheus.Labels{ - "outpost_name": ms.si.GetOutpostName(), - "type": "search", - "reason": "user_info_not_cached", - "app": ms.si.GetAppSlug(), - }).Inc() return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, errors.New("access denied") } accsp.Finish() diff --git a/internal/outpost/ldap/unbind.go b/internal/outpost/ldap/unbind.go index 35c608c32..b293cb2c0 100644 --- a/internal/outpost/ldap/unbind.go +++ b/internal/outpost/ldap/unbind.go @@ -22,11 +22,6 @@ func (ls *LDAPServer) Unbind(boundDN string, conn net.Conn) (ldap.LDAPResultCode "type": "unbind", "app": selectedApp, }).Observe(float64(span.EndTime.Sub(span.StartTime)) / float64(time.Second)) - metrics.RequestsLegacy.With(prometheus.Labels{ - "outpost_name": ls.ac.Outpost.Name, - "type": "unbind", - "app": selectedApp, - }).Observe(float64(span.EndTime.Sub(span.StartTime))) req.Log().WithField("took-ms", span.EndTime.Sub(span.StartTime).Milliseconds()).Info("Unbind request") }() @@ -55,11 +50,5 @@ func (ls *LDAPServer) Unbind(boundDN string, conn net.Conn) (ldap.LDAPResultCode "reason": "no_provider", "app": "", }).Inc() - metrics.RequestsRejectedLegacy.With(prometheus.Labels{ - "outpost_name": ls.ac.Outpost.Name, - "type": "unbind", - "reason": "no_provider", - "app": "", - }).Inc() return ldap.LDAPResultOperationsError, nil } diff --git a/internal/outpost/proxyv2/application/application.go b/internal/outpost/proxyv2/application/application.go index eae4c6774..03dc4289d 100644 --- a/internal/outpost/proxyv2/application/application.go +++ b/internal/outpost/proxyv2/application/application.go @@ -173,12 +173,6 @@ func NewApplication(p api.ProxyOutpostConfig, c *http.Client, server Server) (*A "method": r.Method, "host": web.GetHost(r), }).Observe(float64(elapsed) / float64(time.Second)) - metrics.RequestsLegacy.With(prometheus.Labels{ - "outpost_name": a.outpostName, - "type": "app", - "method": r.Method, - "host": web.GetHost(r), - }).Observe(float64(elapsed)) }) }) if server.API().GlobalConfig.ErrorReporting.Enabled { @@ -241,7 +235,10 @@ func (a *Application) Mode() api.ProxyMode { return *a.proxyConfig.Mode } -func (a *Application) HasQuerySignature(r *http.Request) bool { +func (a *Application) ShouldHandleURL(r *http.Request) bool { + if strings.HasPrefix(r.URL.Path, "/outpost.goauthentik.io") { + return true + } if strings.EqualFold(r.URL.Query().Get(CallbackSignature), "true") { return true } diff --git a/internal/outpost/proxyv2/application/mode_proxy.go b/internal/outpost/proxyv2/application/mode_proxy.go index 07a53f2d6..d987cd72b 100644 --- a/internal/outpost/proxyv2/application/mode_proxy.go +++ b/internal/outpost/proxyv2/application/mode_proxy.go @@ -64,13 +64,6 @@ func (a *Application) configureProxy() error { "scheme": r.URL.Scheme, "host": web.GetHost(r), }).Observe(float64(elapsed) / float64(time.Second)) - metrics.UpstreamTimingLegacy.With(prometheus.Labels{ - "outpost_name": a.outpostName, - "upstream_host": r.URL.Host, - "method": r.Method, - "scheme": r.URL.Scheme, - "host": web.GetHost(r), - }).Observe(float64(elapsed)) }) return nil } diff --git a/internal/outpost/proxyv2/application/session.go b/internal/outpost/proxyv2/application/session.go index 73755cf54..5ffa77b04 100644 --- a/internal/outpost/proxyv2/application/session.go +++ b/internal/outpost/proxyv2/application/session.go @@ -71,7 +71,7 @@ func (a *Application) getStore(p api.ProxyOutpostConfig, externalHost *url.URL) cs.Options.Domain = *p.CookieDomain cs.Options.SameSite = http.SameSiteLaxMode cs.Options.MaxAge = maxAge - cs.Options.Path = externalHost.Path + cs.Options.Path = "/" a.log.WithField("dir", dir).Trace("using filesystem session backend") return cs } diff --git a/internal/outpost/proxyv2/handlers.go b/internal/outpost/proxyv2/handlers.go index 97d86a9fb..5ea61060b 100644 --- a/internal/outpost/proxyv2/handlers.go +++ b/internal/outpost/proxyv2/handlers.go @@ -26,12 +26,6 @@ func (ps *ProxyServer) HandlePing(rw http.ResponseWriter, r *http.Request) { "host": web.GetHost(r), "type": "ping", }).Observe(float64(elapsed) / float64(time.Second)) - metrics.RequestsLegacy.With(prometheus.Labels{ - "outpost_name": ps.akAPI.Outpost.Name, - "method": r.Method, - "host": web.GetHost(r), - "type": "ping", - }).Observe(float64(elapsed)) } func (ps *ProxyServer) HandleStatic(rw http.ResponseWriter, r *http.Request) { @@ -44,12 +38,6 @@ func (ps *ProxyServer) HandleStatic(rw http.ResponseWriter, r *http.Request) { "host": web.GetHost(r), "type": "static", }).Observe(float64(elapsed) / float64(time.Second)) - metrics.RequestsLegacy.With(prometheus.Labels{ - "outpost_name": ps.akAPI.Outpost.Name, - "method": r.Method, - "host": web.GetHost(r), - "type": "static", - }).Observe(float64(elapsed)) } func (ps *ProxyServer) lookupApp(r *http.Request) (*application.Application, string) { diff --git a/internal/outpost/proxyv2/metrics/metrics.go b/internal/outpost/proxyv2/metrics/metrics.go index 564a010d0..6f589025f 100644 --- a/internal/outpost/proxyv2/metrics/metrics.go +++ b/internal/outpost/proxyv2/metrics/metrics.go @@ -22,16 +22,6 @@ var ( Name: "authentik_outpost_proxy_upstream_response_duration_seconds", Help: "Proxy upstream response latencies in seconds", }, []string{"outpost_name", "method", "scheme", "host", "upstream_host"}) - - // NOTE: the following metric is kept for compatibility purpose - RequestsLegacy = promauto.NewHistogramVec(prometheus.HistogramOpts{ - Name: "authentik_outpost_proxy_requests", - Help: "The total number of configured providers", - }, []string{"outpost_name", "method", "host", "type"}) - UpstreamTimingLegacy = promauto.NewHistogramVec(prometheus.HistogramOpts{ - Name: "authentik_outpost_proxy_upstream_time", - Help: "A summary of the duration we wait for the upstream reply", - }, []string{"outpost_name", "method", "scheme", "host", "upstream_host"}) ) func RunServer() { diff --git a/internal/outpost/proxyv2/proxyv2.go b/internal/outpost/proxyv2/proxyv2.go index 70364957f..cd0129038 100644 --- a/internal/outpost/proxyv2/proxyv2.go +++ b/internal/outpost/proxyv2/proxyv2.go @@ -74,7 +74,7 @@ func (ps *ProxyServer) HandleHost(rw http.ResponseWriter, r *http.Request) bool if a == nil { return false } - if a.HasQuerySignature(r) || a.Mode() == api.PROXYMODE_PROXY { + if a.ShouldHandleURL(r) || a.Mode() == api.PROXYMODE_PROXY { a.ServeHTTP(rw, r) return true } diff --git a/internal/outpost/radius/handle_access_request.go b/internal/outpost/radius/handle_access_request.go index 6ea387f53..1c5f32202 100644 --- a/internal/outpost/radius/handle_access_request.go +++ b/internal/outpost/radius/handle_access_request.go @@ -35,11 +35,6 @@ func (rs *RadiusServer) Handle_AccessRequest(w radius.ResponseWriter, r *RadiusR "reason": "flow_error", "app": r.pi.appSlug, }).Inc() - metrics.RequestsRejectedLegacy.With(prometheus.Labels{ - "outpost_name": rs.ac.Outpost.Name, - "reason": "flow_error", - "app": r.pi.appSlug, - }).Inc() _ = w.Write(r.Response(radius.CodeAccessReject)) return } @@ -49,11 +44,6 @@ func (rs *RadiusServer) Handle_AccessRequest(w radius.ResponseWriter, r *RadiusR "reason": "invalid_credentials", "app": r.pi.appSlug, }).Inc() - metrics.RequestsRejectedLegacy.With(prometheus.Labels{ - "outpost_name": rs.ac.Outpost.Name, - "reason": "invalid_credentials", - "app": r.pi.appSlug, - }).Inc() _ = w.Write(r.Response(radius.CodeAccessReject)) return } @@ -66,11 +56,6 @@ func (rs *RadiusServer) Handle_AccessRequest(w radius.ResponseWriter, r *RadiusR "reason": "access_check_fail", "app": r.pi.appSlug, }).Inc() - metrics.RequestsRejectedLegacy.With(prometheus.Labels{ - "outpost_name": rs.ac.Outpost.Name, - "reason": "access_check_fail", - "app": r.pi.appSlug, - }).Inc() return } if !access { @@ -81,11 +66,6 @@ func (rs *RadiusServer) Handle_AccessRequest(w radius.ResponseWriter, r *RadiusR "reason": "access_denied", "app": r.pi.appSlug, }).Inc() - metrics.RequestsRejectedLegacy.With(prometheus.Labels{ - "outpost_name": rs.ac.Outpost.Name, - "reason": "access_denied", - "app": r.pi.appSlug, - }).Inc() return } _ = w.Write(r.Response(radius.CodeAccessAccept)) diff --git a/internal/outpost/radius/handler.go b/internal/outpost/radius/handler.go index e1d3cb531..958e88b70 100644 --- a/internal/outpost/radius/handler.go +++ b/internal/outpost/radius/handler.go @@ -47,10 +47,6 @@ func (rs *RadiusServer) ServeRADIUS(w radius.ResponseWriter, r *radius.Request) "outpost_name": rs.ac.Outpost.Name, "app": selectedApp, }).Observe(float64(span.EndTime.Sub(span.StartTime)) / float64(time.Second)) - metrics.RequestsLegacy.With(prometheus.Labels{ - "outpost_name": rs.ac.Outpost.Name, - "app": selectedApp, - }).Observe(float64(span.EndTime.Sub(span.StartTime))) }() nr := &RadiusRequest{ diff --git a/internal/outpost/radius/metrics/metrics.go b/internal/outpost/radius/metrics/metrics.go index 1bcecfdc2..c741c04ed 100644 --- a/internal/outpost/radius/metrics/metrics.go +++ b/internal/outpost/radius/metrics/metrics.go @@ -22,16 +22,6 @@ var ( Name: "authentik_outpost_radius_requests_rejected_total", Help: "Total number of rejected requests", }, []string{"outpost_name", "reason", "app"}) - - // NOTE: the following metric is kept for compatibility purpose - RequestsLegacy = promauto.NewHistogramVec(prometheus.HistogramOpts{ - Name: "authentik_outpost_radius_requests", - Help: "The total number of successful requests", - }, []string{"outpost_name", "app"}) - RequestsRejectedLegacy = promauto.NewCounterVec(prometheus.CounterOpts{ - Name: "authentik_outpost_radius_requests_rejected", - Help: "Total number of rejected requests", - }, []string{"outpost_name", "reason", "app"}) ) func RunServer() { diff --git a/internal/web/metrics.go b/internal/web/metrics.go index 0b8670b61..2e6b0def1 100644 --- a/internal/web/metrics.go +++ b/internal/web/metrics.go @@ -19,12 +19,6 @@ var ( Name: "authentik_main_request_duration_seconds", Help: "API request latencies in seconds", }, []string{"dest"}) - - // NOTE: the following metric is kept for compatibility purpose - RequestsLegacy = promauto.NewHistogramVec(prometheus.HistogramOpts{ - Name: "authentik_main_requests", - Help: "The total number of configured providers", - }, []string{"dest"}) ) func (ws *WebServer) runMetricsServer() { diff --git a/internal/web/proxy.go b/internal/web/proxy.go index b52d24c3b..56f81a072 100644 --- a/internal/web/proxy.go +++ b/internal/web/proxy.go @@ -32,21 +32,6 @@ func (ws *WebServer) configureProxy() { } rp.ErrorHandler = ws.proxyErrorHandler rp.ModifyResponse = ws.proxyModifyResponse - ws.m.PathPrefix("/outpost.goauthentik.io").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - if ws.ProxyServer != nil { - before := time.Now() - ws.ProxyServer.Handle(rw, r) - elapsed := time.Since(before) - Requests.With(prometheus.Labels{ - "dest": "embedded_outpost", - }).Observe(float64(elapsed) / float64(time.Second)) - RequestsLegacy.With(prometheus.Labels{ - "dest": "embedded_outpost", - }).Observe(float64(elapsed)) - return - } - ws.proxyErrorHandler(rw, r, errors.New("proxy not running")) - }) ws.m.Path("/-/health/live/").HandlerFunc(sentry.SentryNoSample(func(rw http.ResponseWriter, r *http.Request) { rw.WriteHeader(204) })) @@ -56,25 +41,17 @@ func (ws *WebServer) configureProxy() { return } before := time.Now() - if ws.ProxyServer != nil { - if ws.ProxyServer.HandleHost(rw, r) { - elapsed := time.Since(before) - Requests.With(prometheus.Labels{ - "dest": "embedded_outpost", - }).Observe(float64(elapsed) / float64(time.Second)) - RequestsLegacy.With(prometheus.Labels{ - "dest": "embedded_outpost", - }).Observe(float64(elapsed)) - return - } + if ws.ProxyServer != nil && ws.ProxyServer.HandleHost(rw, r) { + elapsed := time.Since(before) + Requests.With(prometheus.Labels{ + "dest": "embedded_outpost", + }).Observe(float64(elapsed) / float64(time.Second)) + return } elapsed := time.Since(before) Requests.With(prometheus.Labels{ "dest": "core", }).Observe(float64(elapsed) / float64(time.Second)) - RequestsLegacy.With(prometheus.Labels{ - "dest": "core", - }).Observe(float64(elapsed)) r.Body = http.MaxBytesReader(rw, r.Body, 32*1024*1024) rp.ServeHTTP(rw, r) })) diff --git a/locale/zh_CN/LC_MESSAGES/django.mo b/locale/zh_CN/LC_MESSAGES/django.mo index 64dba5430..221747fab 100644 Binary files a/locale/zh_CN/LC_MESSAGES/django.mo and b/locale/zh_CN/LC_MESSAGES/django.mo differ diff --git a/locale/zh_TW/LC_MESSAGES/django.po b/locale/zh_TW/LC_MESSAGES/django.po index b89dbd140..3f66e1e82 100644 --- a/locale/zh_TW/LC_MESSAGES/django.po +++ b/locale/zh_TW/LC_MESSAGES/django.po @@ -4,172 +4,258 @@ # FIRST AUTHOR , YEAR. # # Translators: -# Chen Zhikai, 2022 # 刘松, 2022 +# Chen Zhikai, 2022 +# Passerby Dreamer, 2023 # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-01-03 12:29+0000\n" -"PO-Revision-Date: 2021-10-09 18:10+0000\n" -"Last-Translator: 刘松, 2022\n" -"Language-Team: Chinese (Taiwan) (https://www.transifex.com/authentik/teams/119923/zh_TW/)\n" +"POT-Creation-Date: 2023-10-02 12:46+0000\n" +"PO-Revision-Date: 2022-09-26 16:47+0000\n" +"Last-Translator: Passerby Dreamer, 2023\n" +"Language-Team: Chinese (Taiwan) (https://app.transifex.com/authentik/teams/119923/zh_TW/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: zh_TW\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: authentik/admin/api/tasks.py:95 +#: authentik/admin/api/tasks.py:125 #, python-format msgid "Successfully re-scheduled Task %(name)s!" -msgstr "已成功重新安排任务%(name)s!" +msgstr "已成功重新安排任務%(name)s!" -#: authentik/api/schema.py:21 +#: authentik/api/schema.py:25 msgid "Generic API Error" -msgstr "通用 API 错误" +msgstr "通用 API 錯誤" -#: authentik/api/schema.py:29 +#: authentik/api/schema.py:33 msgid "Validation Error" -msgstr "校验错误" +msgstr "驗證錯誤" -#: authentik/core/api/providers.py:89 +#: authentik/blueprints/api.py:44 +msgid "Blueprint file does not exist" +msgstr "藍圖檔案不存在" + +#: authentik/blueprints/api.py:55 +#, python-format +msgid "Failed to validate blueprint: %(logs)s" +msgstr "無法驗證以下藍圖:%(logs)s" + +#: authentik/blueprints/api.py:60 +msgid "Either path or content must be set." +msgstr "必須設定路徑或內容其一。" + +#: authentik/blueprints/models.py:30 +msgid "Managed by authentik" +msgstr "由 authentik 管理" + +#: authentik/blueprints/models.py:32 +msgid "" +"Objects that are managed by authentik. These objects are created and updated" +" automatically. This flag only indicates that an object can be overwritten " +"by migrations. You can still modify the objects via the API, but expect " +"changes to be overwritten in a later update." +msgstr "" +"由 authentik 管理的物件。這些物件會自動地被建立和更新。這個旗標僅表示物件可以因遷移而被改寫。您仍然可以透過 API " +"修改物件,但預期在後續的更新中變更會被改寫。" + +#: authentik/blueprints/models.py:112 +msgid "Blueprint Instance" +msgstr "藍圖執行個體" + +#: authentik/blueprints/models.py:113 +msgid "Blueprint Instances" +msgstr "藍圖執行個體" + +#: authentik/blueprints/v1/exporter.py:62 +#, python-format +msgid "authentik Export - %(date)s" +msgstr "authentik 匯出 - %(date)s" + +#: authentik/blueprints/v1/tasks.py:150 authentik/crypto/tasks.py:93 +#, python-format +msgid "Successfully imported %(count)d files." +msgstr "已成功导入 %(count)d 个文件。" + +#: authentik/core/api/providers.py:120 msgid "SAML Provider from Metadata" -msgstr "基于Metadata的SAML请求发起端" +msgstr "從中繼資料取得 SAML 供應商" -#: authentik/core/api/providers.py:90 +#: authentik/core/api/providers.py:121 msgid "Create a SAML Provider by importing its Metadata." -msgstr "通过导入SAML请求发起端的Metadata来创建SAML请求发起端。" +msgstr "透過匯入中繼資料來建立 SAML 供應商。" -#: authentik/core/models.py:69 +#: authentik/core/api/users.py:158 +msgid "No leading or trailing slashes allowed." +msgstr "不允許使用前導或尾隨的斜線。" + +#: authentik/core/api/users.py:161 +msgid "No empty segments in user path allowed." +msgstr "使用者路徑中不允許有空字串。" + +#: authentik/core/models.py:86 msgid "name" -msgstr "名称" +msgstr "名稱" -#: authentik/core/models.py:71 +#: authentik/core/models.py:88 msgid "Users added to this group will be superusers." -msgstr "添加到该组的用户均为超级用户。" +msgstr "加入到該群組的使用者將會成為超級使用者。" -#: authentik/core/models.py:129 +#: authentik/core/models.py:142 msgid "User's display name." -msgstr "用户的显示名称。" +msgstr "使用者的顯示名稱。" -#: authentik/core/models.py:212 authentik/providers/oauth2/models.py:299 +#: authentik/core/models.py:268 authentik/providers/oauth2/models.py:295 msgid "User" -msgstr "用户" +msgstr "使用者" -#: authentik/core/models.py:213 +#: authentik/core/models.py:269 msgid "Users" -msgstr "用户" +msgstr "使用者" -#: authentik/core/models.py:224 +#: authentik/core/models.py:282 +msgid "" +"Flow used for authentication when the associated application is accessed by " +"an un-authenticated user." +msgstr "當未經認證的使用者存取相關應用程式時,用於認證的流程。" + +#: authentik/core/models.py:292 msgid "Flow used when authorizing this provider." -msgstr "授权此请求发起端时使用的Flow。" +msgstr "授權此供應商時所使用的流程。" -#: authentik/core/models.py:257 +#: authentik/core/models.py:304 +msgid "" +"Accessed from applications; optional backchannel providers for protocols " +"like LDAP and SCIM." +msgstr "從應用程式存取;對於像是 LDAP 和 SCIM 這類協定,提供可選的後端通道供應商。" + +#: authentik/core/models.py:359 msgid "Application's display Name." -msgstr "应用的显示名称。" +msgstr "應用程式的顯示名稱。" -#: authentik/core/models.py:258 +#: authentik/core/models.py:360 msgid "Internal application name, used in URLs." -msgstr "应用的内部名称,在URL中使用。" +msgstr "應用程式的內部名稱,將會用在網址中。" -#: authentik/core/models.py:311 +#: authentik/core/models.py:372 +msgid "Open launch URL in a new browser tab or window." +msgstr "從新瀏覽器分頁或視窗開啟網址。" + +#: authentik/core/models.py:436 msgid "Application" -msgstr "应用程序" +msgstr "應用程式" -#: authentik/core/models.py:312 +#: authentik/core/models.py:437 msgid "Applications" -msgstr "应用程序" +msgstr "應用程式" -#: authentik/core/models.py:318 +#: authentik/core/models.py:443 msgid "Use the source-specific identifier" -msgstr "使用源特定的标识符" +msgstr "使用基於來源的識別碼" -#: authentik/core/models.py:326 +#: authentik/core/models.py:445 +msgid "" +"Link to a user with identical email address. Can have security implications " +"when a source doesn't validate email addresses." +msgstr "連接到具有相同電子郵件地址的使用者。當來源不驗證電子郵件地址時,可能會有安全風險。" + +#: authentik/core/models.py:449 msgid "" "Use the user's email address, but deny enrollment when the email address " "already exists." -msgstr "使用用户的电子邮件地址,但在电子邮件地址已存在时拒绝注册。" +msgstr "使用使用者的電子郵件地址,但在電子郵件地址已存在時拒絕註冊。" -#: authentik/core/models.py:335 +#: authentik/core/models.py:452 +msgid "" +"Link to a user with identical username. Can have security implications when " +"a username is used with another source." +msgstr "連接到具有相同使用者名稱的使用者。當使用者名稱與其他來源一同使用時,可能會有安全風險。" + +#: authentik/core/models.py:456 msgid "" "Use the user's username, but deny enrollment when the username already " "exists." -msgstr "使用用户的用户名,但在用户名已存在时拒绝注册。" +msgstr "使用使用者的使用者名稱,但在使用者名稱已存在時拒絕註冊。" -#: authentik/core/models.py:342 +#: authentik/core/models.py:463 msgid "Source's display Name." -msgstr "源的显示名称。" +msgstr "來源的顯示名稱。" -#: authentik/core/models.py:343 +#: authentik/core/models.py:464 msgid "Internal source name, used in URLs." -msgstr "用于 URL的内部源名称。" +msgstr "用於網址的內部來源名稱。" -#: authentik/core/models.py:354 +#: authentik/core/models.py:483 msgid "Flow to use when authenticating existing users." -msgstr "认证已存在用户时所使用的流程。" +msgstr "認證已存在使用者時所使用的流程。" -#: authentik/core/models.py:363 +#: authentik/core/models.py:492 msgid "Flow to use when enrolling new users." -msgstr "新用户注册时所使用的流程。" +msgstr "新使用者註冊時所使用的流程。" -#: authentik/core/models.py:501 +#: authentik/core/models.py:500 +msgid "" +"How the source determines if an existing user should be authenticated or a " +"new user enrolled." +msgstr "來源如何確定應對現有使用者進行身份驗證,還是註冊新使用者。" + +#: authentik/core/models.py:672 msgid "Token" -msgstr "令牌" +msgstr "權杖" -#: authentik/core/models.py:502 +#: authentik/core/models.py:673 msgid "Tokens" -msgstr "令牌" +msgstr "權杖" -#: authentik/core/models.py:545 +#: authentik/core/models.py:714 msgid "Property Mapping" -msgstr "属性映射" +msgstr "屬性對應" -#: authentik/core/models.py:546 +#: authentik/core/models.py:715 msgid "Property Mappings" -msgstr "属性映射" +msgstr "屬性對應" -#: authentik/core/models.py:582 +#: authentik/core/models.py:750 msgid "Authenticated Session" -msgstr "已认证会话" +msgstr "已認證會談" -#: authentik/core/models.py:583 +#: authentik/core/models.py:751 msgid "Authenticated Sessions" -msgstr "已认证会话" +msgstr "已認證會談" -#: authentik/core/sources/flow_manager.py:166 -msgid "source" -msgstr "来源" +#: authentik/core/sources/flow_manager.py:189 +#, python-format +msgid "" +"Request to authenticate with %(source)s has been denied. Please authenticate" +" with the source you've previously signed up with." +msgstr "使用來自 %(source)s 的身份驗證請求已被拒絕。請使用您之前註冊過的來源進行身份驗證。" -#: authentik/core/sources/flow_manager.py:220 -#: authentik/core/sources/flow_manager.py:258 +#: authentik/core/sources/flow_manager.py:241 +msgid "Configured flow does not exist." +msgstr "已設定的流程不存在。" + +#: authentik/core/sources/flow_manager.py:271 +#: authentik/core/sources/flow_manager.py:323 #, python-format msgid "Successfully authenticated with %(source)s!" -msgstr "成功认证 %(source)s !" +msgstr "成功認證 %(source)s !" -#: authentik/core/sources/flow_manager.py:239 +#: authentik/core/sources/flow_manager.py:295 #, python-format msgid "Successfully linked %(source)s!" -msgstr "成功链接 %(source)s!" +msgstr "成功連結 %(source)s!" -#: authentik/core/templates/error/generic.html:27 -msgid "Go to home" -msgstr "返回主页" - -#: authentik/core/templates/if/admin.html:18 -#: authentik/core/templates/if/admin.html:24 -#: authentik/core/templates/if/flow.html:28 -#: authentik/core/templates/if/flow.html:34 -#: authentik/core/templates/if/user.html:18 -#: authentik/core/templates/if/user.html:24 -msgid "Loading..." -msgstr "载入中……" +#: authentik/core/sources/flow_manager.py:314 +msgid "Source is not configured for enrollment." +msgstr "來源未設定為用於註冊。" #: authentik/core/templates/if/end_session.html:7 msgid "End session" -msgstr "结束会话" +msgstr "結束會談" #: authentik/core/templates/if/end_session.html:11 #, python-format @@ -188,17 +274,23 @@ msgid "" " " msgstr "" "\n" -"您已成功登出 %(application)s 。 现在你可以返回总览并运行其他程序,或者登出的你的 authentik 账户。" +"您已成功登出 %(application)s 。 現在您可以返回總覽並啟動其他應用程式,或者登出您的 authentik 帳戶。" -#: authentik/core/templates/if/end_session.html:24 +#: authentik/core/templates/if/end_session.html:25 msgid "Go back to overview" -msgstr "返回总览" +msgstr "返回總覽" -#: authentik/core/templates/if/end_session.html:26 -msgid "Log out of authentik" -msgstr "登出 authentik" +#: authentik/core/templates/if/end_session.html:29 +#, python-format +msgid "" +"\n" +" Log out of %(branding_title)s\n" +" " +msgstr "" +"\n" +" 從 %(branding_title)s 登出" -#: authentik/core/templates/if/end_session.html:30 +#: authentik/core/templates/if/end_session.html:36 #, python-format msgid "" "\n" @@ -209,381 +301,585 @@ msgstr "" " 重新登入 %(application)s\n" " " -#: authentik/core/templates/login/base_full.html:65 +#: authentik/core/templates/if/error.html:18 +msgid "Go home" +msgstr "回到首頁" + +#: authentik/core/templates/login/base_full.html:89 msgid "Powered by authentik" -msgstr "由 authentik 强力驱动" +msgstr "由 authentik 技術支援" -#: authentik/crypto/api.py:132 +#: authentik/core/views/apps.py:53 +#: authentik/providers/oauth2/views/authorize.py:393 +#: authentik/providers/oauth2/views/device_init.py:70 +#: authentik/providers/saml/views/sso.py:70 +#, python-format +msgid "You're about to sign into %(application)s." +msgstr "您即將登入 %(application)s。" + +#: authentik/crypto/api.py:179 msgid "Subject-alt name" -msgstr "替代名称" +msgstr "替代名稱" -#: authentik/crypto/models.py:34 +#: authentik/crypto/models.py:30 msgid "PEM-encoded Certificate data" -msgstr "PEM 编码的证书数据" +msgstr "PEM 編碼的憑證資料" -#: authentik/crypto/models.py:37 +#: authentik/crypto/models.py:33 msgid "" "Optional Private Key. If this is set, you can use this keypair for " "encryption." -msgstr "可选私钥。如果设置了此设置,则可以使用此密钥对进行加密。" - -#: authentik/crypto/models.py:100 -msgid "Certificate-Key Pair" -msgstr "证书密钥对" +msgstr "可選:私鑰。如果設定了此選項,則可以使用此金鑰對進行加密。" #: authentik/crypto/models.py:101 +msgid "Certificate-Key Pair" +msgstr "憑證金鑰對" + +#: authentik/crypto/models.py:102 msgid "Certificate-Key Pairs" -msgstr "证书密钥对" +msgstr "憑證金鑰對" -#: authentik/crypto/tasks.py:93 -#, python-format -msgid "Successfully imported %(count)d files." -msgstr "已成功导入 %(count)d 个文件。" +#: authentik/enterprise/models.py:193 +msgid "License Usage" +msgstr "授權使用情況" -#: authentik/events/models.py:285 +#: authentik/enterprise/models.py:194 +msgid "License Usage Records" +msgstr "授權使用紀錄" + +#: authentik/events/models.py:290 msgid "Event" msgstr "事件" -#: authentik/events/models.py:286 +#: authentik/events/models.py:291 msgid "Events" msgstr "事件" -#: authentik/events/models.py:292 +#: authentik/events/models.py:297 +msgid "authentik inbuilt notifications" +msgstr "authentik 內建通知功能" + +#: authentik/events/models.py:298 msgid "Generic Webhook" msgstr "通用 Webhook" -#: authentik/events/models.py:293 +#: authentik/events/models.py:299 msgid "Slack Webhook (Slack/Discord)" msgstr "Slack Webhook(Slack/Discord)" -#: authentik/events/models.py:294 +#: authentik/events/models.py:300 msgid "Email" -msgstr "电子邮箱" +msgstr "電子郵件" -#: authentik/events/models.py:312 +#: authentik/events/models.py:318 msgid "" "Only send notification once, for example when sending a webhook into a chat " "channel." -msgstr "仅发送一次通知,例如在向聊天频道发送 Webhook 时。" +msgstr "僅發送一次通知,例如在向聊天頻道發送 Webhook 時。" -#: authentik/events/models.py:357 +#: authentik/events/models.py:383 msgid "Severity" -msgstr "严重程度" +msgstr "嚴重程度" -#: authentik/events/models.py:362 +#: authentik/events/models.py:388 msgid "Dispatched for user" -msgstr "为用户分派" +msgstr "為使用者分派" -#: authentik/events/models.py:439 +#: authentik/events/models.py:397 +msgid "Event user" +msgstr "事件使用者" + +#: authentik/events/models.py:491 msgid "Notification Transport" -msgstr "通知传输" +msgstr "通知通道" -#: authentik/events/models.py:440 +#: authentik/events/models.py:492 msgid "Notification Transports" -msgstr "通知传输" +msgstr "通知通道" -#: authentik/events/models.py:446 +#: authentik/events/models.py:498 msgid "Notice" msgstr "注意" -#: authentik/events/models.py:447 +#: authentik/events/models.py:499 msgid "Warning" msgstr "警告" -#: authentik/events/models.py:448 +#: authentik/events/models.py:500 msgid "Alert" -msgstr "注意" +msgstr "警報" -#: authentik/events/models.py:468 +#: authentik/events/models.py:525 msgid "Notification" msgstr "通知" -#: authentik/events/models.py:469 +#: authentik/events/models.py:526 msgid "Notifications" msgstr "通知" -#: authentik/events/models.py:488 +#: authentik/events/models.py:536 +msgid "" +"Select which transports should be used to notify the user. If none are " +"selected, the notification will only be shown in the authentik UI." +msgstr "選擇使用哪些傳輸通道來通知使用者。如果沒有選擇任何方式,通知將僅在 authentik 使用者介面中顯示。" + +#: authentik/events/models.py:544 msgid "Controls which severity level the created notifications will have." -msgstr "控制被创建的通知的严重性级别。" +msgstr "控制已建立通知的嚴重性等級。" -#: authentik/events/models.py:508 +#: authentik/events/models.py:549 +msgid "" +"Define which group of users this notification should be sent and shown to. " +"If left empty, Notification won't ben sent." +msgstr "定義應該向哪個群組的使用者發送和顯示此通知。如果未設定則不會發送通知。" + +#: authentik/events/models.py:567 msgid "Notification Rule" -msgstr "通知规则" +msgstr "通知規則" -#: authentik/events/models.py:509 +#: authentik/events/models.py:568 msgid "Notification Rules" -msgstr "通知规则" +msgstr "通知規則" -#: authentik/events/models.py:530 -msgid "Notification Webhook Mapping" -msgstr "通知 Webhook 映射" +#: authentik/events/models.py:588 +msgid "Webhook Mapping" +msgstr "Webhook 對應" -#: authentik/events/models.py:531 -msgid "Notification Webhook Mappings" -msgstr "通知 Webhook 映射" +#: authentik/events/models.py:589 +msgid "Webhook Mappings" +msgstr "Webhook 對應" -#: authentik/events/monitored_tasks.py:197 +#: authentik/events/monitored_tasks.py:205 msgid "Task has not been run yet." -msgstr "任务尚未运行。" +msgstr "任務尚未執行。" -#: authentik/flows/api/flows.py:350 +#: authentik/flows/api/flows.py:295 #, python-format msgid "Flow not applicable to current user/request: %(messages)s" -msgstr "Flow不适用于当前用户/请求: %(messages)s" +msgstr "流程不適用於當前使用者或請求: %(messages)s" -#: authentik/flows/models.py:107 -msgid "Visible in the URL." -msgstr "在 URL 中可见。" +#: authentik/flows/api/flows_diagram.py:68 +#: authentik/flows/api/flows_diagram.py:94 +#, python-format +msgid "Policy (%(type)s)" +msgstr "政策(%(type)s)" -#: authentik/flows/models.py:109 -msgid "Shown as the Title in Flow pages." -msgstr "显示为 “Flow” 页面中的标题。" +#: authentik/flows/api/flows_diagram.py:71 +#, python-format +msgid "Binding %(order)d" +msgstr "附加%(order)d" -#: authentik/flows/models.py:126 -msgid "Background shown during execution" -msgstr "执行时的背景" +#: authentik/flows/api/flows_diagram.py:118 +msgid "Policy passed" +msgstr "政策已通過" -#: authentik/flows/models.py:133 -msgid "" -"Enable compatibility mode, increases compatibility with password managers on" -" mobile devices." -msgstr "启用兼容模式,增加与移动设备上密码管理器的兼容性。" +#: authentik/flows/api/flows_diagram.py:122 +#, python-format +msgid "Stage (%(type)s)" +msgstr "階段(%(type)s)" -#: authentik/flows/models.py:178 +#: authentik/flows/api/flows_diagram.py:146 +#: authentik/flows/api/flows_diagram.py:206 +msgid "Policy denied" +msgstr "政策被拒" + +#: authentik/flows/api/flows_diagram.py:156 +#: authentik/flows/api/flows_diagram.py:168 +#: authentik/flows/api/flows_diagram.py:205 +#: authentik/flows/api/flows_diagram.py:227 +msgid "End of the flow" +msgstr "流程的結束點" + +#: authentik/flows/api/flows_diagram.py:169 +msgid "Requirement not fulfilled" +msgstr "需求未滿足" + +#: authentik/flows/api/flows_diagram.py:177 +msgid "Flow authentication requirement" +msgstr "流程身份驗證要求" + +#: authentik/flows/api/flows_diagram.py:183 +msgid "Requirement fulfilled" +msgstr "需求已滿足" + +#: authentik/flows/api/flows_diagram.py:196 +msgid "Pre-flow policies" +msgstr "在流程之前的政策" + +#: authentik/flows/api/flows_diagram.py:214 authentik/flows/models.py:193 msgid "Flow" msgstr "流程" -#: authentik/flows/models.py:179 +#: authentik/flows/exceptions.py:19 +msgid "Flow does not apply to current user." +msgstr "流程並未套用到目前的使用者。" + +#: authentik/flows/models.py:114 +#, python-format +msgid "Dynamic In-memory stage: %(doc)s" +msgstr "動態記憶體內儲存階段:%(doc)s" + +#: authentik/flows/models.py:129 +msgid "Visible in the URL." +msgstr "在網址列中可見。" + +#: authentik/flows/models.py:131 +msgid "Shown as the Title in Flow pages." +msgstr "做為在「流程」分頁中的標題。" + +#: authentik/flows/models.py:138 +msgid "" +"Decides what this Flow is used for. For example, the Authentication flow is " +"redirect to when an un-authenticated user visits authentik." +msgstr "決定此流程用於何種目的。例如,當未經驗證的使用者存取 authentik 時,會重新導向到身份驗證流程。" + +#: authentik/flows/models.py:147 +msgid "Background shown during execution" +msgstr "執行時顯示的背景" + +#: authentik/flows/models.py:154 +msgid "" +"Enable compatibility mode, increases compatibility with password managers on" +" mobile devices." +msgstr "啟用相容模式,增加與行動裝置上密碼管理器的相容性。" + +#: authentik/flows/models.py:162 +msgid "Configure what should happen when a flow denies access to a user." +msgstr "設定當流程拒絕某個使用者存取時,應該發生什麼事情。" + +#: authentik/flows/models.py:168 +msgid "Required level of authentication and authorization to access a flow." +msgstr "存取流程時所需的身份驗證和授權等級。" + +#: authentik/flows/models.py:194 msgid "Flows" msgstr "流程" -#: authentik/flows/models.py:209 -msgid "Evaluate policies when the Stage is present to the user." -msgstr "当阶段出现在用户面前时,评估策略。" +#: authentik/flows/models.py:215 +msgid "Evaluate policies during the Flow planning process." +msgstr "在流程規劃過程中的評估政策。" -#: authentik/flows/models.py:216 +#: authentik/flows/models.py:219 +msgid "Evaluate policies when the Stage is present to the user." +msgstr "當階段出現在使用者面前時的評估政策。" + +#: authentik/flows/models.py:226 msgid "" "Configure how the flow executor should handle an invalid response to a " "challenge. RETRY returns the error message and a similar challenge to the " "executor. RESTART restarts the flow from the beginning, and " "RESTART_WITH_CONTEXT restarts the flow while keeping the current context." msgstr "" -"配置流程执行器应如何处理对质询的无效响应。RETRY 向执行器返回错误消息和类似的质询。RESTART 从头开始重新启动流程。 " -"RESTART_WITH_CONTEXT 在保留当前上下文的同时重新启动流程。" +"設定流程執行器應如何處理對挑戰的無效回應。RETRY 向執行器回應錯誤訊息和類似的挑戰。RESTART " +"從頭開始重新啟動流程,RESTART_WITH_CONTEXT 在保留當前上下文的同時重新啟動流程。" -#: authentik/flows/models.py:240 +#: authentik/flows/models.py:249 msgid "Flow Stage Binding" -msgstr "流阶段绑定" +msgstr "流程階段附加" -#: authentik/flows/models.py:241 +#: authentik/flows/models.py:250 msgid "Flow Stage Bindings" -msgstr "流阶段绑定" +msgstr "流程階段附加" -#: authentik/flows/models.py:291 +#: authentik/flows/models.py:265 +msgid "" +"Flow used by an authenticated user to configure this Stage. If empty, user " +"will not be able to configure this stage." +msgstr "由已認證的使用者用來設定此階段的流程。如果未設定則使用者將無法設定此階段。" + +#: authentik/flows/models.py:305 msgid "Flow Token" -msgstr "流程令牌" +msgstr "流程權杖" -#: authentik/flows/models.py:292 +#: authentik/flows/models.py:306 msgid "Flow Tokens" -msgstr "流程令牌" +msgstr "流程權杖" -#: authentik/flows/templates/flows/error.html:12 -msgid "Whoops!" -msgstr "噢!" - -#: authentik/flows/templates/flows/error.html:17 -msgid "Something went wrong! Please try again later." -msgstr "发生错误,请稍后重试。" - -#: authentik/lib/utils/time.py:24 +#: authentik/lib/utils/time.py:27 #, python-format msgid "%(value)s is not in the correct format of 'hours=3;minutes=1'." -msgstr "%(value)s 的格式不正确,应为 'hours=3;minutes=1'。" +msgstr "%(value)s 不符合 'hours=3;minutes=1' 的正確格式。" -#: authentik/managed/models.py:12 -msgid "Managed by authentik" -msgstr "由 authentik 管理" - -#: authentik/outposts/api/service_connections.py:131 +#: authentik/outposts/api/service_connections.py:127 msgid "" "You can only use an empty kubeconfig when connecting to a local cluster." -msgstr "你只能在连接到本地集群时使用空的 kubeconfig。" +msgstr "您只能在連接到本機叢集時使用空的 kubeconfig。" -#: authentik/outposts/api/service_connections.py:139 +#: authentik/outposts/api/service_connections.py:135 msgid "Invalid kubeconfig" -msgstr "无效 Kubeconfig " +msgstr "無效的 kubeconfig" -#: authentik/outposts/models.py:151 -msgid "Outpost Service-Connection" -msgstr "Outpost Service-连接" +#: authentik/outposts/models.py:122 +msgid "" +"If enabled, use the local connection. Required Docker socket/Kubernetes " +"Integration" +msgstr "如果啟用將使用本機連線。需要 Docker socket / Kubernetes 整合。" #: authentik/outposts/models.py:152 -msgid "Outpost Service-Connections" -msgstr "Outpost Service-连接" +msgid "Outpost Service-Connection" +msgstr "Outpost 服務連線" -#: authentik/outposts/models.py:188 +#: authentik/outposts/models.py:153 +msgid "Outpost Service-Connections" +msgstr "Outpost 服務連線" + +#: authentik/outposts/models.py:161 +msgid "" +"Can be in the format of 'unix://' when connecting to a local docker " +"daemon, or 'https://:2376' when connecting to a remote system." +msgstr "" +"當連接到本地 Docker 常駐程式時,將會是 'unix://' 的格式,當連接到遠端系統時,將會是 " +"'https://:2376' 的格式。" + +#: authentik/outposts/models.py:173 +msgid "" +"CA which the endpoint's Certificate is verified against. Can be left empty " +"for no validation." +msgstr "用於驗證端點憑證的 CA(數位憑證認證機構)。若不需要驗證,可以留空。" + +#: authentik/outposts/models.py:185 msgid "" "Certificate/Key used for authentication. Can be left empty for no " "authentication." -msgstr "用于身份验证的证书/密钥。可以留空,留空表示不进行身份验证。" +msgstr "用於身份驗證的憑證/金鑰。可以留空表示不進行身份驗證。" -#: authentik/outposts/models.py:201 +#: authentik/outposts/models.py:203 msgid "Docker Service-Connection" -msgstr "Docker服务连接" +msgstr "Docker 服務連線" -#: authentik/outposts/models.py:202 +#: authentik/outposts/models.py:204 msgid "Docker Service-Connections" -msgstr "Docker服务连接" +msgstr "Docker 服務連線" -#: authentik/outposts/models.py:227 +#: authentik/outposts/models.py:212 +msgid "" +"Paste your kubeconfig here. authentik will automatically use the currently " +"selected context." +msgstr "在這裡貼上您的 kubeconfig。authentik 將自動使用當前選定的上下文。" + +#: authentik/outposts/models.py:218 +msgid "Verify SSL Certificates of the Kubernetes API endpoint" +msgstr "驗證 Kubernetes API 端點的 SSL 憑證" + +#: authentik/outposts/models.py:235 msgid "Kubernetes Service-Connection" -msgstr "Kubernetes 服务连接" +msgstr "Kubernetes 服務連線" -#: authentik/outposts/models.py:228 +#: authentik/outposts/models.py:236 msgid "Kubernetes Service-Connections" -msgstr "Kubernetes 服务连接" +msgstr "Kubernetes 服務連線" + +#: authentik/outposts/models.py:252 +msgid "" +"Select Service-Connection authentik should use to manage this outpost. Leave" +" empty if authentik should not handle the deployment." +msgstr "選擇 authentik 用於管理此前哨站的服務連線。請留空如果 authentik 不應處理此部署。" #: authentik/policies/denied.py:24 msgid "Access denied" -msgstr "访问被拒绝" +msgstr "存取被拒" + +#: authentik/policies/dummy/models.py:44 +msgid "Dummy Policy" +msgstr "假政策" #: authentik/policies/dummy/models.py:45 -msgid "Dummy Policy" -msgstr "虚拟策略" - -#: authentik/policies/dummy/models.py:46 msgid "Dummy Policies" -msgstr "虚拟策略" +msgstr "假政策" -#: authentik/policies/event_matcher/models.py:80 +#: authentik/policies/event_matcher/api.py:20 +#: authentik/policies/event_matcher/models.py:56 +msgid "" +"Match events created by selected application. When left empty, all " +"applications are matched." +msgstr "匹配由所選應用程式產生的事件。如果未設定則所有應用程式都將符合。" + +#: authentik/policies/event_matcher/api.py:29 +#: authentik/policies/event_matcher/models.py:64 +msgid "" +"Match events created by selected model. When left empty, all models are " +"matched. When an app is selected, all the application's models are matched." +msgstr "當未選擇任何模型時,會匹配所有模型產生的事件。如果選擇了某一個應用程式,則會匹配該應用程式下所有模型產生的事件。" + +#: authentik/policies/event_matcher/api.py:42 +msgid "At least one criteria must be set." +msgstr "必須設定至少一個條件。" + +#: authentik/policies/event_matcher/models.py:48 +msgid "" +"Match created events with this action type. When left empty, all action " +"types will be matched." +msgstr "匹配由此動作類型產生的事件。如果未設定則所有動作類型都將符合。" + +#: authentik/policies/event_matcher/models.py:73 +msgid "" +"Matches Event's Client IP (strict matching, for network matching use an " +"Expression Policy)" +msgstr "匹配事件的客戶端 IP(嚴格配對,若需進行網路層級配對,請使用表達式政策)" + +#: authentik/policies/event_matcher/models.py:143 msgid "Event Matcher Policy" -msgstr "事件匹配策略" +msgstr "事件配對政策" -#: authentik/policies/event_matcher/models.py:81 +#: authentik/policies/event_matcher/models.py:144 msgid "Event Matcher Policies" -msgstr "事件匹配策略" +msgstr "事件配對政策" -#: authentik/policies/expiry/models.py:46 -msgid "days" -msgstr "天" +#: authentik/policies/expiry/models.py:45 +#, python-format +msgid "Password expired %(days)d days ago. Please update your password." +msgstr "您的密碼在%(days)d天前已過期。請更新您的密碼。" #: authentik/policies/expiry/models.py:49 msgid "Password has expired." -msgstr "密码已过期。" +msgstr "密碼已過期。" + +#: authentik/policies/expiry/models.py:53 +msgid "Password Expiry Policy" +msgstr "密碼到期政策" #: authentik/policies/expiry/models.py:54 -msgid "Password Expiry Policy" -msgstr "密码过期策略" - -#: authentik/policies/expiry/models.py:55 msgid "Password Expiry Policies" -msgstr "密码过期策略" +msgstr "密碼到期政策" + +#: authentik/policies/expression/models.py:40 +msgid "Expression Policy" +msgstr "表達式政策" #: authentik/policies/expression/models.py:41 -msgid "Expression Policy" -msgstr "表达策略" - -#: authentik/policies/expression/models.py:42 msgid "Expression Policies" -msgstr "表达策略" +msgstr "表達式政策" -#: authentik/policies/hibp/models.py:22 -#: authentik/policies/password/models.py:24 -msgid "Field key to check, field keys defined in Prompt stages are available." -msgstr "要检查的字段键,提示阶段中定义的字段键可用。" - -#: authentik/policies/hibp/models.py:47 -#: authentik/policies/password/models.py:57 -msgid "Password not set in context" -msgstr "未在上下文中设置密码" - -#: authentik/policies/hibp/models.py:60 -#, python-format -msgid "Password exists on %(count)d online lists." -msgstr "在线列表%(count)d 中存在密码。" - -#: authentik/policies/hibp/models.py:66 -msgid "Have I Been Pwned Policy" -msgstr "我被控制了吗政策" - -#: authentik/policies/hibp/models.py:67 -msgid "Have I Been Pwned Policies" -msgstr "我被控制了吗政策" +#: authentik/policies/models.py:22 +msgid "all, all policies must pass" +msgstr "all, all 政策必須通過" #: authentik/policies/models.py:23 -msgid "ALL, all policies must pass" -msgstr "全部,必须通过所有策略" - -#: authentik/policies/models.py:25 -msgid "ANY, any policy must pass" -msgstr "任意,任一策略通过" - -#: authentik/policies/models.py:45 -msgid "Policy Binding Model" -msgstr "策略绑定模型" +msgid "any, any policy must pass" +msgstr "any, any 政策必須通過" #: authentik/policies/models.py:46 +msgid "Policy Binding Model" +msgstr "政策附加模型" + +#: authentik/policies/models.py:47 msgid "Policy Binding Models" -msgstr "策略绑定模型" +msgstr "政策附加模型" -#: authentik/policies/models.py:85 +#: authentik/policies/models.py:86 msgid "Negates the outcome of the policy. Messages are unaffected." -msgstr "否定政策的结果。消息不受影响。" +msgstr "否定政策的結果。訊息不受影響。" -#: authentik/policies/models.py:88 +#: authentik/policies/models.py:89 msgid "Timeout after which Policy execution is terminated." -msgstr "超时后,策略执行将终止。" +msgstr "逾時後,政策的執行將終止。" -#: authentik/policies/models.py:141 +#: authentik/policies/models.py:92 +msgid "Result if the Policy execution fails." +msgstr "如果政策執行失敗的結果。" + +#: authentik/policies/models.py:145 msgid "Policy Binding" -msgstr "策略绑定" +msgstr "政策附加" -#: authentik/policies/models.py:142 +#: authentik/policies/models.py:146 msgid "Policy Bindings" -msgstr "策略绑定" +msgstr "政策附加" -#: authentik/policies/models.py:181 +#: authentik/policies/models.py:167 +msgid "" +"When this option is enabled, all executions of this policy will be logged. " +"By default, only execution errors are logged." +msgstr "當啟用此選項時,將記錄此政策的所有執行操作。預設情況下,僅記錄執行錯誤。" + +#: authentik/policies/models.py:189 msgid "Policy" -msgstr "策略" +msgstr "政策" -#: authentik/policies/models.py:182 +#: authentik/policies/models.py:190 msgid "Policies" -msgstr "策略" +msgstr "政策" -#: authentik/policies/password/models.py:89 +#: authentik/policies/password/models.py:27 +msgid "Field key to check, field keys defined in Prompt stages are available." +msgstr "要檢查的欄位鍵,在提示階段中有可用的已定義欄位鍵。" + +#: authentik/policies/password/models.py:44 +msgid "How many times the password hash is allowed to be on haveibeenpwned" +msgstr "密碼雜湊在 haveibeenpwned 上允許出現的次數" + +#: authentik/policies/password/models.py:49 +msgid "" +"If the zxcvbn score is equal or less than this value, the policy will fail." +msgstr "如果 zxcvbn 分數等於或小於此值,則該政策將失敗。" + +#: authentik/policies/password/models.py:72 +msgid "Password not set in context" +msgstr "未在上下文中設定密碼" + +#: authentik/policies/password/models.py:134 +#, python-format +msgid "Password exists on %(count)d online lists." +msgstr "密碼存在於線上清單%(count)d 中。" + +#: authentik/policies/password/models.py:154 +msgid "Password is too weak." +msgstr "密碼強度太弱。" + +#: authentik/policies/password/models.py:162 msgid "Password Policy" -msgstr "密码策略" +msgstr "密碼政策" -#: authentik/policies/password/models.py:90 +#: authentik/policies/password/models.py:163 msgid "Password Policies" -msgstr "密码策略" +msgstr "密碼政策" -#: authentik/policies/reputation/models.py:54 +#: authentik/policies/reputation/api.py:18 +msgid "Either IP or Username must be checked" +msgstr "至少選取IP或使用者名稱其一" + +#: authentik/policies/reputation/models.py:67 msgid "Reputation Policy" -msgstr "信誉政策" +msgstr "信譽政策" -#: authentik/policies/reputation/models.py:55 +#: authentik/policies/reputation/models.py:68 msgid "Reputation Policies" -msgstr "信誉政策" +msgstr "信譽政策" + +#: authentik/policies/reputation/models.py:95 +msgid "Reputation Score" +msgstr "信譽分數" + +#: authentik/policies/reputation/models.py:96 +msgid "Reputation Scores" +msgstr "信譽分數" #: authentik/policies/templates/policies/denied.html:7 #: authentik/policies/templates/policies/denied.html:11 msgid "Permission denied" -msgstr "没有权限。" +msgstr "權限不足。" -#: authentik/policies/templates/policies/denied.html:20 +#: authentik/policies/templates/policies/denied.html:21 +msgid "User's avatar" +msgstr "個人檔案圖片" + +#: authentik/policies/templates/policies/denied.html:25 +msgid "Not you?" +msgstr "不是您嗎?" + +#: authentik/policies/templates/policies/denied.html:33 msgid "Request has been denied." -msgstr "请求被拒绝。" +msgstr "請求被拒。" -#: authentik/policies/templates/policies/denied.html:31 +#: authentik/policies/templates/policies/denied.html:44 msgid "Messages:" -msgstr "消息:" +msgstr "訊息:" -#: authentik/policies/templates/policies/denied.html:41 +#: authentik/policies/templates/policies/denied.html:54 msgid "Explanation:" -msgstr "解释:" +msgstr "解釋:" -#: authentik/policies/templates/policies/denied.html:45 +#: authentik/policies/templates/policies/denied.html:58 #, python-format msgid "" "\n" @@ -591,879 +887,1267 @@ msgid "" " " msgstr "" "\n" -" 策略绑定 '%(name)s' 返回结果 '%(result)s'\n" +" 附加政策 '%(name)s' 的回傳結果為 '%(result)s'\n" " " #: authentik/policies/views.py:68 msgid "Failed to resolve application" -msgstr "解析应用程序失败" +msgstr "解析應用程式失敗" #: authentik/providers/ldap/models.py:25 msgid "DN under which objects are accessible." -msgstr "在其下可以访问对象的 DN。" +msgstr "可連接到物件的 DN 位置。" #: authentik/providers/ldap/models.py:34 msgid "" "Users in this group can do search queries. If not set, every user can " "execute search queries." -msgstr "该组中的用户可以执行搜索查询。如果未设置,则每个用户都可以执行搜索查询。" +msgstr "該群組中的使用者可以執行搜尋查詢。如果未設定則每個使用者都可以執行搜尋查詢。" #: authentik/providers/ldap/models.py:53 msgid "" -"The start for uidNumbers, this number is added to the user.Pk to make sure " +"The start for uidNumbers, this number is added to the user.pk to make sure " "that the numbers aren't too low for POSIX users. Default is 2000 to ensure " "that we don't collide with local users uidNumber" msgstr "" -"对于UIDNumbers来说,这个数字被添加到User.pk中,以确保对于POSIX用户来说,这个数字不会太低。默认值为 " -"2000,以确保我们不会与本地用户 uidNumber 发生冲突" +"uidNumbers 的起始值,這個數值會和 user.pk 產生的數值相加,以確保 POSIX 使用者編號起始值不會太低。預設值為 " +"2000,以避免與本機使用者編號 uidNumber 發生衝突。" #: authentik/providers/ldap/models.py:62 msgid "" "The start for gidNumbers, this number is added to a number generated from " -"the group.Pk to make sure that the numbers aren't too low for POSIX groups. " +"the group.pk to make sure that the numbers aren't too low for POSIX groups. " "Default is 4000 to ensure that we don't collide with local groups or users " "primary groups gidNumber" msgstr "" -"对于 GIDNumbers 来说,这个数字被添加到从 group.pk 生成的数字中,以确保对于 POSIX 组来说,这个数字不会太低。默认值为 " -"4000,以确保我们不会与本地组或用户主组 GIDNumber 发生冲突" +"gidNumbers 的起始值,這個數值會和 group.pk 產生的數值相加,以確保 POSIX 群組編號起始值不會太低。預設值為 " +"4000,以避免與本機群組或使用者的主要群組編號 gidNumber 發生衝突。" -#: authentik/providers/ldap/models.py:97 +#: authentik/providers/ldap/models.py:76 +msgid "" +"When enabled, code-based multi-factor authentication can be used by " +"appending a semicolon and the TOTP code to the password. This should only be" +" enabled if all users that will bind to this provider have a TOTP device " +"configured, as otherwise a password may incorrectly be rejected if it " +"contains a semicolon." +msgstr "" +"當啟用此功能時,可以透過在密碼後加上分號及TOTP授權碼(範例:password:totp授權碼)來使用多重要素驗證。您應只有在所有要連線到此服務的使用者都已設定TOTP裝置的情況下,才啟用此功能。如果用戶的密碼中恰好包含分號,可能會因誤判而被拒絕存取。" + +#: authentik/providers/ldap/models.py:108 msgid "LDAP Provider" -msgstr "LDAP 提供商" +msgstr "LDAP 供應商" -#: authentik/providers/ldap/models.py:98 +#: authentik/providers/ldap/models.py:109 msgid "LDAP Providers" -msgstr "LDAP 提供商" +msgstr "LDAP 供應商" -#: authentik/providers/oauth2/models.py:36 -msgid "Confidential" -msgstr "机密" - -#: authentik/providers/oauth2/models.py:37 -msgid "Public" -msgstr "公开" - -#: authentik/providers/oauth2/models.py:51 +#: authentik/providers/oauth2/id_token.py:27 msgid "Based on the Hashed User ID" -msgstr "基于经过哈希处理的用户 ID" +msgstr "基於經過雜湊處理的使用者 ID" -#: authentik/providers/oauth2/models.py:52 +#: authentik/providers/oauth2/id_token.py:28 +msgid "Based on user ID" +msgstr "基於使用者 ID" + +#: authentik/providers/oauth2/id_token.py:29 +msgid "Based on user UUID" +msgstr "基於使用者 UUID" + +#: authentik/providers/oauth2/id_token.py:30 msgid "Based on the username" -msgstr "基于用户名" +msgstr "基於使用者名稱" -#: authentik/providers/oauth2/models.py:55 +#: authentik/providers/oauth2/id_token.py:33 msgid "Based on the User's Email. This is recommended over the UPN method." -msgstr "基于用户的电子邮件。 建议在 UPN 方法上使用此方法。" +msgstr "基於使用者的電子郵件。比起使用 UPN 更推薦此方法。" -#: authentik/providers/oauth2/models.py:71 +#: authentik/providers/oauth2/id_token.py:38 +msgid "" +"Based on the User's UPN, only works if user has a 'upn' attribute set. Use " +"this method only if you have different UPN and Mail domains." +msgstr "基於使用者的 UPN,只有當使用者設定了 「upn」 屬性時才有效。只有在您的 UPN 和 Mail 有不同網域時才使用這個方法。" + +#: authentik/providers/oauth2/models.py:43 +msgid "Confidential" +msgstr "機密" + +#: authentik/providers/oauth2/models.py:44 +msgid "Public" +msgstr "公開" + +#: authentik/providers/oauth2/models.py:66 msgid "Same identifier is used for all providers" -msgstr "所有提供商都使用相同的标识符" +msgstr "所有供應商都使用相同的識別碼" -#: authentik/providers/oauth2/models.py:73 +#: authentik/providers/oauth2/models.py:68 msgid "Each provider has a different issuer, based on the application slug." -msgstr "根据应用程序 slug,每个提供商都有不同的颁发者。" +msgstr "每個供應商都有一個不同的發行者,基於應用程式的縮寫(Slug)。" + +#: authentik/providers/oauth2/models.py:75 +msgid "code (Authorization Code Flow)" +msgstr "code(授權碼流程)" + +#: authentik/providers/oauth2/models.py:76 +msgid "id_token (Implicit Flow)" +msgstr "id_token(隱式流程)" + +#: authentik/providers/oauth2/models.py:77 +msgid "id_token token (Implicit Flow)" +msgstr "id_token 權杖(隱式流程)" + +#: authentik/providers/oauth2/models.py:78 +msgid "code token (Hybrid Flow)" +msgstr "授權碼權杖(混合流程)" + +#: authentik/providers/oauth2/models.py:79 +msgid "code id_token (Hybrid Flow)" +msgstr "授權碼 id_token(混合流程)" #: authentik/providers/oauth2/models.py:80 -msgid "code (Authorization Code Flow)" -msgstr "code(授权码流)" - -#: authentik/providers/oauth2/models.py:81 -msgid "id_token (Implicit Flow)" -msgstr "id_token(隐式流)" - -#: authentik/providers/oauth2/models.py:82 -msgid "id_token token (Implicit Flow)" -msgstr "id_token 令牌(隐式流)" - -#: authentik/providers/oauth2/models.py:83 -msgid "code token (Hybrid Flow)" -msgstr "代码令牌(混合流)" - -#: authentik/providers/oauth2/models.py:84 -msgid "code id_token (Hybrid Flow)" -msgstr "code id_token(混合流)" - -#: authentik/providers/oauth2/models.py:85 msgid "code id_token token (Hybrid Flow)" -msgstr "code id_token 令牌(混合流)" +msgstr "授權碼 id_token 權杖(混合流程)" -#: authentik/providers/oauth2/models.py:91 +#: authentik/providers/oauth2/models.py:86 msgid "HS256 (Symmetric Encryption)" -msgstr "HS256(对称加密)" +msgstr "HS256(對稱加密)" -#: authentik/providers/oauth2/models.py:92 +#: authentik/providers/oauth2/models.py:87 msgid "RS256 (Asymmetric Encryption)" -msgstr "RS256(非对称加密)" +msgstr "RS256(非對稱加密)" -#: authentik/providers/oauth2/models.py:93 +#: authentik/providers/oauth2/models.py:88 msgid "ES256 (Asymmetric Encryption)" -msgstr "" +msgstr "ES256(非對稱加密)" -#: authentik/providers/oauth2/models.py:99 +#: authentik/providers/oauth2/models.py:94 msgid "Scope used by the client" -msgstr "客户端使用的作用域(Scope)" +msgstr "用戶端使用的範疇" -#: authentik/providers/oauth2/models.py:125 +#: authentik/providers/oauth2/models.py:98 +msgid "" +"Description shown to the user when consenting. If left empty, the user won't" +" be informed." +msgstr "當需要使用者同意時顯示的說明。如果未設定將不會顯示。" + +#: authentik/providers/oauth2/models.py:117 msgid "Scope Mapping" -msgstr "作用域映射" +msgstr "範疇對應" -#: authentik/providers/oauth2/models.py:126 +#: authentik/providers/oauth2/models.py:118 msgid "Scope Mappings" -msgstr "作用域映射" +msgstr "範疇對應" -#: authentik/providers/oauth2/models.py:136 +#: authentik/providers/oauth2/models.py:128 msgid "Client Type" -msgstr "客户端类型" +msgstr "用戶端類型" -#: authentik/providers/oauth2/models.py:142 +#: authentik/providers/oauth2/models.py:130 +msgid "" +"Confidential clients are capable of maintaining the confidentiality of their" +" credentials. Public clients are incapable" +msgstr "機密用戶端能夠維護其憑證的機密性。公開用戶端則無法做到這一點。" + +#: authentik/providers/oauth2/models.py:137 msgid "Client ID" -msgstr "客户端 ID" +msgstr "用戶端 ID" -#: authentik/providers/oauth2/models.py:148 +#: authentik/providers/oauth2/models.py:143 msgid "Client Secret" -msgstr "客户端密钥" +msgstr "用戶端金鑰" -#: authentik/providers/oauth2/models.py:154 +#: authentik/providers/oauth2/models.py:149 msgid "Redirect URIs" -msgstr "重定向 URI" +msgstr "重新導向 URI" + +#: authentik/providers/oauth2/models.py:150 +msgid "Enter each URI on a new line." +msgstr "每行輸入一個網址。" #: authentik/providers/oauth2/models.py:155 -msgid "Enter each URI on a new line." -msgstr "每行输入一个URL。" - -#: authentik/providers/oauth2/models.py:160 msgid "Include claims in id_token" -msgstr "在 id_token 中包含声明" +msgstr "在 id_token 中包含聲明" -#: authentik/providers/oauth2/models.py:208 -msgid "RSA Key" -msgstr "RSA 密钥" +#: authentik/providers/oauth2/models.py:157 +msgid "" +"Include User claims from scopes in the id_token, for applications that don't" +" access the userinfo endpoint." +msgstr "在 id_token 中包含來自範疇的使用者聲明,適用於那些不存取 userinfo 端點的應用程式。" -#: authentik/providers/oauth2/models.py:212 +#: authentik/providers/oauth2/models.py:166 +msgid "" +"Access codes not valid on or after current time + this value (Format: " +"hours=1;minutes=2;seconds=3)." +msgstr "存取授權碼在當前時間加上此值後並非有效(格式:hours=1;minutes=2;seconds=3)。" + +#: authentik/providers/oauth2/models.py:174 +#: authentik/providers/oauth2/models.py:182 +msgid "" +"Tokens not valid on or after current time + this value (Format: " +"hours=1;minutes=2;seconds=3)." +msgstr "權杖在當前時間加上此值後並非有效(格式:hours=1;minutes=2;seconds=3)。" + +#: authentik/providers/oauth2/models.py:191 +msgid "" +"Configure what data should be used as unique User Identifier. For most " +"cases, the default should be fine." +msgstr "設定應該使用哪些資料作為唯一的使用者識別碼。在大多數情況下使用預設值即可。 " + +#: authentik/providers/oauth2/models.py:198 +msgid "Configure how the issuer field of the ID Token should be filled." +msgstr "設定該如何填寫 ID 權杖的發行者欄位。" + +#: authentik/providers/oauth2/models.py:203 +msgid "Signing Key" +msgstr "簽署金鑰" + +#: authentik/providers/oauth2/models.py:207 msgid "" "Key used to sign the tokens. Only required when JWT Algorithm is set to " "RS256." -msgstr "用于对令牌进行签名的密钥。仅当JWT算法设置为 RS256 时才需要。" +msgstr "用於對權杖進行簽署的金鑰。僅當 JWT 雜湊演算法設定為 RS256 時才需要。" -#: authentik/providers/oauth2/models.py:291 +#: authentik/providers/oauth2/models.py:214 +msgid "" +"Any JWT signed by the JWK of the selected source can be used to " +"authenticate." +msgstr "任何由選擇的 JWK 來源所簽署的 JWT 都可以用來進行身份驗證。" + +#: authentik/providers/oauth2/models.py:287 msgid "OAuth2/OpenID Provider" -msgstr "OAuth2/OpenID 提供商" +msgstr "OAuth2/OpenID 供應商" -#: authentik/providers/oauth2/models.py:292 +#: authentik/providers/oauth2/models.py:288 msgid "OAuth2/OpenID Providers" -msgstr "OAuth2/OpenID 提供商" +msgstr "OAuth2/OpenID 供應商" -#: authentik/providers/oauth2/models.py:300 +#: authentik/providers/oauth2/models.py:297 +#: authentik/providers/oauth2/models.py:429 msgid "Scopes" -msgstr "范围" +msgstr "範疇" -#: authentik/providers/oauth2/models.py:319 +#: authentik/providers/oauth2/models.py:316 msgid "Code" -msgstr "代码" +msgstr "授權碼" + +#: authentik/providers/oauth2/models.py:317 +msgid "Nonce" +msgstr "Nonce(單次使用隨機亂數)" + +#: authentik/providers/oauth2/models.py:318 +msgid "Code Challenge" +msgstr "授權碼驗證挑戰" #: authentik/providers/oauth2/models.py:320 -msgid "Nonce" -msgstr "Nonce" - -#: authentik/providers/oauth2/models.py:321 -msgid "Is Authentication?" -msgstr "是身份验证吗?" - -#: authentik/providers/oauth2/models.py:322 -msgid "Code Challenge" -msgstr "代码质询" - -#: authentik/providers/oauth2/models.py:324 msgid "Code Challenge Method" -msgstr "代码质询方法" +msgstr "授權碼驗證挑戰方法" -#: authentik/providers/oauth2/models.py:338 +#: authentik/providers/oauth2/models.py:340 msgid "Authorization Code" -msgstr "授权码" +msgstr "授權碼" -#: authentik/providers/oauth2/models.py:339 +#: authentik/providers/oauth2/models.py:341 msgid "Authorization Codes" -msgstr "授权码" - -#: authentik/providers/oauth2/models.py:382 -msgid "Access Token" -msgstr "访问令牌" +msgstr "授權碼" #: authentik/providers/oauth2/models.py:383 -msgid "Refresh Token" -msgstr "刷新令牌" +msgid "OAuth2 Access Token" +msgstr "OAuth2 存取權杖" #: authentik/providers/oauth2/models.py:384 +msgid "OAuth2 Access Tokens" +msgstr "OAuth2 存取權杖" + +#: authentik/providers/oauth2/models.py:394 msgid "ID Token" -msgstr "ID 令牌" +msgstr "ID 權杖" -#: authentik/providers/oauth2/models.py:387 -msgid "OAuth2 Token" -msgstr "OAuth2 令牌" +#: authentik/providers/oauth2/models.py:413 +msgid "OAuth2 Refresh Token" +msgstr "OAuth2 更新權杖" -#: authentik/providers/oauth2/models.py:388 -msgid "OAuth2 Tokens" -msgstr "OAuth2 令牌" +#: authentik/providers/oauth2/models.py:414 +msgid "OAuth2 Refresh Tokens" +msgstr "OAuth2 更新權杖" -#: authentik/providers/oauth2/views/authorize.py:458 -#: authentik/providers/saml/views/sso.py:69 +#: authentik/providers/oauth2/models.py:441 +msgid "Device Token" +msgstr "裝置權杖" + +#: authentik/providers/oauth2/models.py:442 +msgid "Device Tokens" +msgstr "裝置權杖" + +#: authentik/providers/oauth2/views/authorize.py:448 +#: authentik/providers/saml/views/flows.py:87 #, python-format -msgid "You're about to sign into %(application)s." -msgstr "你即将登入 %(application)s。" +msgid "Redirecting to %(app)s..." +msgstr "重新導向到%(app)s..." -#: authentik/providers/proxy/models.py:52 +#: authentik/providers/oauth2/views/device_init.py:151 +msgid "Invalid code" +msgstr "無效的授權碼" + +#: authentik/providers/oauth2/views/userinfo.py:55 +#: authentik/providers/oauth2/views/userinfo.py:56 +msgid "GitHub Compatibility: Access your User Information" +msgstr "GitHub 相容性:存取您的使用者資訊" + +#: authentik/providers/oauth2/views/userinfo.py:57 +msgid "GitHub Compatibility: Access you Email addresses" +msgstr "GitHub 相容性:存取您的電子郵件地址" + +#: authentik/providers/oauth2/views/userinfo.py:58 +msgid "GitHub Compatibility: Access your Groups" +msgstr "GitHub 相容性:存取您的群組" + +#: authentik/providers/oauth2/views/userinfo.py:59 +msgid "authentik API Access on behalf of your user" +msgstr "代表您的使用者存取 authentik API" + +#: authentik/providers/proxy/api.py:52 +msgid "User and password attributes must be set when basic auth is enabled." +msgstr "啟用基本認證時必須設定使用者和密碼屬性。" + +#: authentik/providers/proxy/api.py:63 +msgid "Internal host cannot be empty when forward auth is disabled." +msgstr "當禁用轉發認證時,內部主機不能為空。" + +#: authentik/providers/proxy/models.py:54 msgid "Validate SSL Certificates of upstream servers" -msgstr "验证上游服务器的 SSL 证书" +msgstr "驗證上游伺服器的 SSL 憑證" -#: authentik/providers/proxy/models.py:53 +#: authentik/providers/proxy/models.py:55 msgid "Internal host SSL Validation" -msgstr "内部主机 SSL 验证" +msgstr "內部主機 SSL 驗證" -#: authentik/providers/proxy/models.py:59 +#: authentik/providers/proxy/models.py:61 msgid "" "Enable support for forwardAuth in traefik and nginx auth_request. Exclusive " "with internal_host." -msgstr "在 traefik 和 nginx auth_request 中启用对 ForwardAuth 的支持。internal_host 独有。" +msgstr "" +"啟用在 traefik 和 nginx auth_request 中對 ForwardAuth 的支援。只在 internal_host 可使用。" -#: authentik/providers/proxy/models.py:77 +#: authentik/providers/proxy/models.py:70 +msgid "" +"Regular expressions for which authentication is not required. Each new line " +"is interpreted as a new Regular Expression." +msgstr "不需要身份驗證的正規表示式。每一行各都被解釋為一個正規表示式。" + +#: authentik/providers/proxy/models.py:78 +msgid "" +"When enabled, this provider will intercept the authorization header and " +"authenticate requests based on its value." +msgstr "當啟用時,此供應商將攔截授權標頭並根據其值對請求進行身份驗證。" + +#: authentik/providers/proxy/models.py:84 msgid "Set HTTP-Basic Authentication" -msgstr "设置 HTTP 基本身份验证" +msgstr "設定 HTTP 基本身份認證" -#: authentik/providers/proxy/models.py:79 +#: authentik/providers/proxy/models.py:86 msgid "" "Set a custom HTTP-Basic Authentication header based on values from " "authentik." -msgstr "根据来自 authentik 的值设置自定义 HTTP-Basic 身份验证标头。" +msgstr "透過 authentik 設定的值客製化 HTTP 基本身份驗證的標頭。" -#: authentik/providers/proxy/models.py:84 +#: authentik/providers/proxy/models.py:91 msgid "HTTP-Basic Username Key" -msgstr "HTTP-Basic 用户名密钥" +msgstr "HTTP 基本身份驗證的使用者名鍵值" -#: authentik/providers/proxy/models.py:94 +#: authentik/providers/proxy/models.py:93 +msgid "" +"User/Group Attribute used for the user part of the HTTP-Basic Header. If not" +" set, the user's Email address is used." +msgstr "用於 HTTP 基本身份驗證標頭中,使用者區塊中的使用者/群組屬性。如果未設定則套用使用者的電子郵件地址。" + +#: authentik/providers/proxy/models.py:99 msgid "HTTP-Basic Password Key" -msgstr "HTTP-Basic 密码密钥" +msgstr "HTTP 基本身份驗證的密碼鍵值" -#: authentik/providers/proxy/models.py:149 +#: authentik/providers/proxy/models.py:100 +msgid "" +"User/Group Attribute used for the password part of the HTTP-Basic Header." +msgstr "用於 HTTP 基本身份驗證標頭中,密碼區塊中的用戶/群組屬性。" + +#: authentik/providers/proxy/models.py:154 msgid "Proxy Provider" -msgstr "代理提供商" +msgstr "代理伺服器供應商" -#: authentik/providers/proxy/models.py:150 +#: authentik/providers/proxy/models.py:155 msgid "Proxy Providers" -msgstr "代理提供商" +msgstr "代理伺服器供應商" -#: authentik/providers/saml/api.py:176 +#: authentik/providers/radius/models.py:18 +msgid "Shared secret between clients and server to hash packets." +msgstr "用於用戶端與伺服器之間封包雜湊處理的共享金鑰。" + +#: authentik/providers/radius/models.py:24 +msgid "" +"List of CIDRs (comma-separated) that clients can connect from. A more " +"specific CIDR will match before a looser one. Clients connecting from a non-" +"specified CIDR will be dropped." +msgstr "用戶端可以從中連線的CIDR列表(以逗號分隔)。較窄的CIDR會在較寬的CIDR之前優先套用。來自未指定CIDR的用戶端連線將被拒絕。" + +#: authentik/providers/radius/models.py:49 +msgid "Radius Provider" +msgstr "Radius 供應商" + +#: authentik/providers/radius/models.py:50 +msgid "Radius Providers" +msgstr "Radius 供應商" + +#: authentik/providers/saml/api/providers.py:257 msgid "Invalid XML Syntax" -msgstr "XML 语法无效" +msgstr "無效的 XML 語法" -#: authentik/providers/saml/api.py:186 +#: authentik/providers/saml/api/providers.py:267 #, python-format msgid "Failed to import Metadata: %(message)s" -msgstr "导入元数据失败:%(message)s" +msgstr "匯入中繼資料失敗:%(message)s" #: authentik/providers/saml/models.py:38 msgid "ACS URL" -msgstr "ACS URL" +msgstr "ACS 網址" -#: authentik/providers/saml/models.py:49 +#: authentik/providers/saml/models.py:43 +msgid "" +"Value of the audience restriction field of the assertion. When left empty, " +"no audience restriction will be added." +msgstr "斷言的目標對象來源限制欄位的值。當未設定時將不會對任何目標對象進行限制。" + +#: authentik/providers/saml/models.py:47 msgid "Also known as EntityID" -msgstr "也称为 EntityID" +msgstr "也稱為 EntityID" + +#: authentik/providers/saml/models.py:51 +msgid "Service Provider Binding" +msgstr "服務供應商附加" #: authentik/providers/saml/models.py:53 -msgid "Service Provider Binding" -msgstr "服务提供商绑定" +msgid "" +"This determines how authentik sends the response back to the Service " +"Provider." +msgstr "這決定了 authentik 如何將回應發送回服務供應商。" + +#: authentik/providers/saml/models.py:63 +msgid "NameID Property Mapping" +msgstr "NameID 屬性對應" #: authentik/providers/saml/models.py:65 -msgid "NameID Property Mapping" -msgstr "nameID 属性映射" +msgid "" +"Configure how the NameID value will be created. When left empty, the " +"NameIDPolicy of the incoming request will be considered" +msgstr "設定 NameID 值建立的方式。如果未設定則會使用傳入請求中的 NameIDPolicy。" -#: authentik/providers/saml/models.py:109 authentik/sources/saml/models.py:128 +#: authentik/providers/saml/models.py:74 +msgid "" +"Assertion valid not before current time + this value (Format: " +"hours=-1;minutes=-2;seconds=-3)." +msgstr "斷言的有效起始時間是當前時間加上此值(格式:hours=-1;minutes=-2;seconds=-3)。" + +#: authentik/providers/saml/models.py:82 +msgid "" +"Assertion not valid on or after current time + this value (Format: " +"hours=1;minutes=2;seconds=3)." +msgstr "斷言的有效期限為當前時間加上此值(格式:hours=1;minutes=2;seconds=3)。" + +#: authentik/providers/saml/models.py:91 +msgid "" +"Session not valid on or after current time + this value (Format: " +"hours=1;minutes=2;seconds=3)." +msgstr "會談的有效期限是當前時間加上此值(格式:hours=1;minutes=2;seconds=3)。" + +#: authentik/providers/saml/models.py:99 authentik/sources/saml/models.py:150 msgid "SHA1" msgstr "SHA1" -#: authentik/providers/saml/models.py:110 authentik/sources/saml/models.py:129 +#: authentik/providers/saml/models.py:100 authentik/sources/saml/models.py:151 msgid "SHA256" msgstr "SHA256" -#: authentik/providers/saml/models.py:111 authentik/sources/saml/models.py:130 +#: authentik/providers/saml/models.py:101 authentik/sources/saml/models.py:152 msgid "SHA384" msgstr "SHA384" -#: authentik/providers/saml/models.py:112 authentik/sources/saml/models.py:131 +#: authentik/providers/saml/models.py:102 authentik/sources/saml/models.py:153 msgid "SHA512" msgstr "SHA512" -#: authentik/providers/saml/models.py:119 authentik/sources/saml/models.py:138 +#: authentik/providers/saml/models.py:109 authentik/sources/saml/models.py:160 msgid "RSA-SHA1" msgstr "RSA-SHA1" -#: authentik/providers/saml/models.py:120 authentik/sources/saml/models.py:139 +#: authentik/providers/saml/models.py:110 authentik/sources/saml/models.py:161 msgid "RSA-SHA256" msgstr "RSA-SHA256" -#: authentik/providers/saml/models.py:121 authentik/sources/saml/models.py:140 +#: authentik/providers/saml/models.py:111 authentik/sources/saml/models.py:162 msgid "RSA-SHA384" msgstr "RSA-SHA384" -#: authentik/providers/saml/models.py:122 authentik/sources/saml/models.py:141 +#: authentik/providers/saml/models.py:112 authentik/sources/saml/models.py:163 msgid "RSA-SHA512" msgstr "RSA-SHA512" -#: authentik/providers/saml/models.py:123 authentik/sources/saml/models.py:142 +#: authentik/providers/saml/models.py:113 authentik/sources/saml/models.py:164 msgid "DSA-SHA1" msgstr "DSA-SHA1" -#: authentik/providers/saml/models.py:140 +#: authentik/providers/saml/models.py:124 authentik/sources/saml/models.py:130 +msgid "" +"When selected, incoming assertion's Signatures will be validated against " +"this certificate. To allow unsigned Requests, leave on default." +msgstr "當選擇後,傳入的斷言簽章將會根據這個憑證進行驗證。若要允許未簽屬的請求,請保留為預設值。" + +#: authentik/providers/saml/models.py:128 authentik/sources/saml/models.py:134 msgid "Verification Certificate" -msgstr "验证证书" +msgstr "驗證憑證" -#: authentik/providers/saml/models.py:148 +#: authentik/providers/saml/models.py:136 msgid "Keypair used to sign outgoing Responses going to the Service Provider." -msgstr "密钥对用于签署发送给服务提供商的外发响应。" +msgstr "用於簽署傳給服務供應商回應的金鑰對。" -#: authentik/providers/saml/models.py:150 authentik/sources/saml/models.py:118 +#: authentik/providers/saml/models.py:138 authentik/sources/saml/models.py:144 msgid "Signing Keypair" -msgstr "签名密钥对" +msgstr "簽署金鑰對" -#: authentik/providers/saml/models.py:180 +#: authentik/providers/saml/models.py:167 msgid "SAML Provider" -msgstr "SAML 提供商" +msgstr "SAML 供應商" -#: authentik/providers/saml/models.py:181 +#: authentik/providers/saml/models.py:168 msgid "SAML Providers" -msgstr "SAML 提供商" +msgstr "SAML 供應商" -#: authentik/providers/saml/models.py:206 +#: authentik/providers/saml/models.py:192 msgid "SAML Property Mapping" -msgstr "SAML 属性映射" +msgstr "SAML 屬性對應" -#: authentik/providers/saml/models.py:207 +#: authentik/providers/saml/models.py:193 msgid "SAML Property Mappings" -msgstr "SAML 属性映射" +msgstr "SAML 屬性對應" + +#: authentik/providers/scim/models.py:20 +msgid "Base URL to SCIM requests, usually ends in /v2" +msgstr "SCIM 要求中的基礎網址,通常以 /v2 結尾。" + +#: authentik/providers/scim/models.py:21 +msgid "Authentication token" +msgstr "認證權杖" + +#: authentik/providers/scim/models.py:27 authentik/sources/ldap/models.py:94 +msgid "Property mappings used for group creation/updating." +msgstr "用於建立或更新群組的屬性對應。" + +#: authentik/providers/scim/models.py:60 +msgid "SCIM Provider" +msgstr "SCIM 供應商" + +#: authentik/providers/scim/models.py:61 +msgid "SCIM Providers" +msgstr "SCIM 供應商" + +#: authentik/providers/scim/models.py:81 +msgid "SCIM Mapping" +msgstr "SCIM 對應" + +#: authentik/providers/scim/models.py:82 +msgid "SCIM Mappings" +msgstr "SCIM 對應" + +#: authentik/providers/scim/tasks.py:52 +msgid "Starting full SCIM sync" +msgstr "開始完全同步 SCIM" + +#: authentik/providers/scim/tasks.py:59 +#, python-format +msgid "Syncing page %(page)d of users" +msgstr "同步第 %(page)d 頁中的使用者" + +#: authentik/providers/scim/tasks.py:63 +#, python-format +msgid "Syncing page %(page)d of groups" +msgstr "同步第 %(page)d 頁中的群組" + +#: authentik/providers/scim/tasks.py:92 +#, python-format +msgid "Failed to sync user %(user_name)s due to remote error: %(error)s" +msgstr "無法同步使用者 %(user_name)s因發生遠端錯誤:%(error)s" + +#: authentik/providers/scim/tasks.py:103 authentik/providers/scim/tasks.py:144 +#, python-format +msgid "Stopping sync due to error: %(error)s" +msgstr "停止同步因發生以下錯誤:%(error)s" + +#: authentik/providers/scim/tasks.py:133 +#, python-format +msgid "Failed to sync group %(group_name)s due to remote error: %(error)s" +msgstr "無法同步群組 %(group_name)s 因發生遠端錯誤:%(error)s" #: authentik/recovery/management/commands/create_admin_group.py:11 msgid "Create admin group if the default group gets deleted." -msgstr "如果默认群组被删除,则创建管理员群组。" +msgstr "如果預設群組被刪除,則建立管理員群組。" #: authentik/recovery/management/commands/create_recovery_key.py:17 msgid "Create a Key which can be used to restore access to authentik." -msgstr "创建一个密钥,该密钥可用于恢复对 authentik 的访问权限。" +msgstr "建立一個金鑰用於恢復對 authentik 的存取權限。" #: authentik/recovery/views.py:24 msgid "Used recovery-link to authenticate." -msgstr "已使用恢复链接进行身份验证。" +msgstr "已使用救援連結進行認證。" -#: authentik/sources/ldap/models.py:32 +#: authentik/sources/ldap/models.py:37 msgid "Server URI" -msgstr "服务器 URI" +msgstr "伺服器 URI" -#: authentik/sources/ldap/models.py:40 +#: authentik/sources/ldap/models.py:46 msgid "" "Optionally verify the LDAP Server's Certificate against the CA Chain in this" " keypair." -msgstr "可选,根据此密钥对中的 CA 链验证 LDAP 服务器的证书。" +msgstr "可選:根據此金鑰對中的 CA 鏈來驗證 LDAP 伺服器的憑證。" -#: authentik/sources/ldap/models.py:45 +#: authentik/sources/ldap/models.py:55 +msgid "" +"Client certificate to authenticate against the LDAP Server's Certificate." +msgstr "對 LDAP 伺服器的憑證進行認證的用戶端憑證。" + +#: authentik/sources/ldap/models.py:58 msgid "Bind CN" msgstr "Bind CN" -#: authentik/sources/ldap/models.py:47 +#: authentik/sources/ldap/models.py:60 msgid "Enable Start TLS" -msgstr "启用 “启动 TLS”" +msgstr "啟用 STARTTLS" -#: authentik/sources/ldap/models.py:49 +#: authentik/sources/ldap/models.py:61 +msgid "Use Server URI for SNI verification" +msgstr "使用伺服器 URI 進行 SNI 驗證" + +#: authentik/sources/ldap/models.py:63 msgid "Base DN" msgstr "Base DN" -#: authentik/sources/ldap/models.py:51 +#: authentik/sources/ldap/models.py:65 msgid "Prepended to Base DN for User-queries." -msgstr "预先添加到用户查询的Base DN 中。" - -#: authentik/sources/ldap/models.py:52 -msgid "Addition User DN" -msgstr "额外的用户 DN" - -#: authentik/sources/ldap/models.py:56 -msgid "Prepended to Base DN for Group-queries." -msgstr "在组查询的基本 Base DN 前面加上。" - -#: authentik/sources/ldap/models.py:57 -msgid "Addition Group DN" -msgstr "额外的 Group DN" - -#: authentik/sources/ldap/models.py:63 -msgid "Consider Objects matching this filter to be Users." -msgstr "将与此筛选器匹配的对象视为用户。" +msgstr "預先附加到使用者查詢的 Base DN 之前。" #: authentik/sources/ldap/models.py:66 -msgid "Field which contains members of a group." -msgstr "包含组成员的字段。" +msgid "Addition User DN" +msgstr "額外的使用者 DN" #: authentik/sources/ldap/models.py:70 -msgid "Consider Objects matching this filter to be Groups." -msgstr "将与此过滤器匹配的对象视为组。" +msgid "Prepended to Base DN for Group-queries." +msgstr "預先附加到群組查詢的 Base DN 之前。" -#: authentik/sources/ldap/models.py:73 -msgid "Field which contains a unique Identifier." -msgstr "包含唯一标识符的字段。" +#: authentik/sources/ldap/models.py:71 +msgid "Addition Group DN" +msgstr "額外的群組 DN" + +#: authentik/sources/ldap/models.py:77 +msgid "Consider Objects matching this filter to be Users." +msgstr "將符合此過濾條件的物件視為使用者。" #: authentik/sources/ldap/models.py:80 -msgid "Property mappings used for group creation/updating." -msgstr "用于创建/更新组的属性映射。" +msgid "Field which contains members of a group." +msgstr "包含群組成員的欄位。" -#: authentik/sources/ldap/models.py:145 +#: authentik/sources/ldap/models.py:84 +msgid "Consider Objects matching this filter to be Groups." +msgstr "將符合此過濾條件的物件視為群組。" + +#: authentik/sources/ldap/models.py:87 +msgid "Field which contains a unique Identifier." +msgstr "包含唯一識別碼的欄位。" + +#: authentik/sources/ldap/models.py:101 +msgid "" +"When a user changes their password, sync it back to LDAP. This can only be " +"enabled on a single LDAP source." +msgstr "當使用者更改密碼時,將其同步回 LDAP。這只能在只有單一 LDAP 來源上啟用。" + +#: authentik/sources/ldap/models.py:190 msgid "LDAP Source" -msgstr "LDAP 源" +msgstr "LDAP 來源" -#: authentik/sources/ldap/models.py:146 +#: authentik/sources/ldap/models.py:191 msgid "LDAP Sources" -msgstr "LDAP 源" +msgstr "LDAP 來源" -#: authentik/sources/ldap/models.py:169 +#: authentik/sources/ldap/models.py:213 msgid "LDAP Property Mapping" -msgstr "LDAP 属性映射" +msgstr "LDAP 屬性對應" -#: authentik/sources/ldap/models.py:170 +#: authentik/sources/ldap/models.py:214 msgid "LDAP Property Mappings" -msgstr "LDAP 属性映射" +msgstr "LDAP 屬性對應" -#: authentik/sources/ldap/signals.py:58 +#: authentik/sources/ldap/signals.py:50 msgid "Password does not match Active Directory Complexity." -msgstr "密码与活动目录复杂度不匹配。" +msgstr "密碼不符合 Active Directory 的複雜性要求。" + +#: authentik/sources/oauth/clients/oauth2.py:68 +msgid "No token received." +msgstr "未收到權杖。" #: authentik/sources/oauth/models.py:24 msgid "Request Token URL" -msgstr "请求令牌网址" +msgstr "請求權杖的網址" #: authentik/sources/oauth/models.py:26 msgid "" "URL used to request the initial token. This URL is only required for OAuth " "1." -msgstr "用于请求初始令牌的 URL。只有 OAuth 1 才需要此网址。" +msgstr "用於請求初始權杖的網址,僅用於 OAuth 1。" #: authentik/sources/oauth/models.py:32 msgid "Authorization URL" -msgstr "授权网址" +msgstr "授權網址" #: authentik/sources/oauth/models.py:33 msgid "URL the user is redirect to to conest the flow." -msgstr "用户被重定向到的URL,以控制流程。" +msgstr "使用者被重新導向到此網址以連接流程。" #: authentik/sources/oauth/models.py:38 msgid "Access Token URL" -msgstr "访问令牌 URL" +msgstr "存取權杖網址" #: authentik/sources/oauth/models.py:39 msgid "URL used by authentik to retrieve tokens." -msgstr "authentik 用来检索令牌的 URL。" +msgstr "authentik 用來擷取權杖的網址。" #: authentik/sources/oauth/models.py:44 msgid "Profile URL" -msgstr "个人资料网址" +msgstr "個人檔案網址" #: authentik/sources/oauth/models.py:45 msgid "URL used by authentik to get user information." -msgstr "authentik 用来获取用户信息的 URL。" +msgstr "authentik 用來擷取使用者資訊的網址。" -#: authentik/sources/oauth/models.py:97 -msgid "OAuth Source" -msgstr "OAuth 源" - -#: authentik/sources/oauth/models.py:98 -msgid "OAuth Sources" -msgstr "OAuth 源" - -#: authentik/sources/oauth/models.py:107 -msgid "GitHub OAuth Source" -msgstr "GitHub OAuth 来" +#: authentik/sources/oauth/models.py:48 +msgid "Additional Scopes" +msgstr "額外的範疇" #: authentik/sources/oauth/models.py:108 -msgid "GitHub OAuth Sources" -msgstr "GitHub OAuth 源" +msgid "OAuth Source" +msgstr "OAuth 來源" + +#: authentik/sources/oauth/models.py:109 +msgid "OAuth Sources" +msgstr "OAuth 來源" #: authentik/sources/oauth/models.py:117 -msgid "Twitter OAuth Source" -msgstr "Twitter OAuth 源" +msgid "GitHub OAuth Source" +msgstr "GitHub OAuth 來源" #: authentik/sources/oauth/models.py:118 -msgid "Twitter OAuth Sources" -msgstr "Twitter OAuth 源" +msgid "GitHub OAuth Sources" +msgstr "GitHub OAuth 來源" + +#: authentik/sources/oauth/models.py:126 +msgid "Twitch OAuth Source" +msgstr "Twitch OAuth 來源" #: authentik/sources/oauth/models.py:127 +msgid "Twitch OAuth Sources" +msgstr "Twitch OAuth 來源" + +#: authentik/sources/oauth/models.py:135 +msgid "Mailcow OAuth Source" +msgstr "Mailcow OAuth 來源" + +#: authentik/sources/oauth/models.py:136 +msgid "Mailcow OAuth Sources" +msgstr "Mailcow OAuth 來源" + +#: authentik/sources/oauth/models.py:144 +msgid "Twitter OAuth Source" +msgstr "X(Twitter) OAuth 來源" + +#: authentik/sources/oauth/models.py:145 +msgid "Twitter OAuth Sources" +msgstr "X(Twitter) OAuth 來源" + +#: authentik/sources/oauth/models.py:153 msgid "Facebook OAuth Source" -msgstr "Facebook OAuth 源" +msgstr "Facebook OAuth 來源" -#: authentik/sources/oauth/models.py:128 +#: authentik/sources/oauth/models.py:154 msgid "Facebook OAuth Sources" -msgstr "Facebook OAuth 源" +msgstr "Facebook OAuth 來源" -#: authentik/sources/oauth/models.py:137 +#: authentik/sources/oauth/models.py:162 msgid "Discord OAuth Source" -msgstr "Discord OAuth 源" +msgstr "Discord OAuth 來源" -#: authentik/sources/oauth/models.py:138 +#: authentik/sources/oauth/models.py:163 msgid "Discord OAuth Sources" -msgstr "Discord OAuth 源" +msgstr "Discord OAuth 來源" -#: authentik/sources/oauth/models.py:147 +#: authentik/sources/oauth/models.py:171 +msgid "Patreon OAuth Source" +msgstr "Patreon OAuth 來源" + +#: authentik/sources/oauth/models.py:172 +msgid "Patreon OAuth Sources" +msgstr "Patreon OAuth 來源" + +#: authentik/sources/oauth/models.py:180 msgid "Google OAuth Source" -msgstr "谷歌 OAuth 源" +msgstr "Google OAuth 來源" -#: authentik/sources/oauth/models.py:148 +#: authentik/sources/oauth/models.py:181 msgid "Google OAuth Sources" -msgstr "谷歌 OAuth 源" +msgstr "Google OAuth 來源" -#: authentik/sources/oauth/models.py:157 +#: authentik/sources/oauth/models.py:189 msgid "Azure AD OAuth Source" -msgstr "Azure AD OAuth 源" +msgstr "Azure AD OAuth 來源" -#: authentik/sources/oauth/models.py:158 +#: authentik/sources/oauth/models.py:190 msgid "Azure AD OAuth Sources" -msgstr "Azure AD OAuth 来源" +msgstr "Azure AD OAuth 來源" -#: authentik/sources/oauth/models.py:167 +#: authentik/sources/oauth/models.py:198 msgid "OpenID OAuth Source" -msgstr "OpenID OAuth 源" +msgstr "OpenID OAuth 來源" -#: authentik/sources/oauth/models.py:168 +#: authentik/sources/oauth/models.py:199 msgid "OpenID OAuth Sources" -msgstr "OpenID OAuth 源" +msgstr "OpenID OAuth 來源" -#: authentik/sources/oauth/models.py:177 +#: authentik/sources/oauth/models.py:207 msgid "Apple OAuth Source" -msgstr "Apple OAuth 源" +msgstr "Apple OAuth 來源" -#: authentik/sources/oauth/models.py:178 +#: authentik/sources/oauth/models.py:208 msgid "Apple OAuth Sources" -msgstr "Apple OAuth 源" +msgstr "Apple OAuth 來源" -#: authentik/sources/oauth/models.py:187 +#: authentik/sources/oauth/models.py:216 msgid "Okta OAuth Source" -msgstr "Okta OAuth 源" +msgstr "Okta OAuth 來源" -#: authentik/sources/oauth/models.py:188 +#: authentik/sources/oauth/models.py:217 msgid "Okta OAuth Sources" -msgstr "Okta OAuth 源" +msgstr "Okta OAuth 來源" -#: authentik/sources/oauth/models.py:203 +#: authentik/sources/oauth/models.py:225 +msgid "Reddit OAuth Source" +msgstr "Reddit OAuth 來源" + +#: authentik/sources/oauth/models.py:226 +msgid "Reddit OAuth Sources" +msgstr "Reddit OAuth 來源" + +#: authentik/sources/oauth/models.py:248 msgid "User OAuth Source Connection" -msgstr "用户 OAuth 源连接" +msgstr "使用者 OAuth 來源連線" -#: authentik/sources/oauth/models.py:204 +#: authentik/sources/oauth/models.py:249 msgid "User OAuth Source Connections" -msgstr "用户 OAuth 源连接" +msgstr "使用者 OAuth 來源連線" -#: authentik/sources/oauth/views/callback.py:98 -msgid "Authentication Failed." -msgstr "身份验证失败。" +#: authentik/sources/oauth/views/callback.py:100 +#, python-format +msgid "Authentication failed: %(reason)s" +msgstr "認證失敗:%(reason)s" #: authentik/sources/plex/models.py:37 msgid "Client identifier used to talk to Plex." -msgstr "用于与Plex通话的客户端标识符。" +msgstr "用於與 Plex 通話的用戶端識別碼。" + +#: authentik/sources/plex/models.py:44 +msgid "" +"Which servers a user has to be a member of to be granted access. Empty list " +"allows every server." +msgstr "使用者必須是其成員才能獲得存取權限的伺服器列表。留空則允許所有伺服器。" + +#: authentik/sources/plex/models.py:50 +msgid "Allow friends to authenticate, even if you don't share a server." +msgstr "允許朋友進行認證,即使你們沒有共用伺服器。" #: authentik/sources/plex/models.py:52 -msgid "Allow friends to authenticate, even if you don't share a server." -msgstr "允许朋友进行身份验证,即使您不共享服务器。" - -#: authentik/sources/plex/models.py:54 msgid "Plex token used to check friends" -msgstr "用于检查朋友的 Plex 令牌" +msgstr "用於檢查朋友的 Plex 權杖" -#: authentik/sources/plex/models.py:92 +#: authentik/sources/plex/models.py:95 msgid "Plex Source" -msgstr "Plex 源" +msgstr "Plex 來源" -#: authentik/sources/plex/models.py:93 +#: authentik/sources/plex/models.py:96 msgid "Plex Sources" -msgstr "Plex 源" +msgstr "Plex 來源" -#: authentik/sources/plex/models.py:104 +#: authentik/sources/plex/models.py:112 msgid "User Plex Source Connection" -msgstr "用户 Plex 源连接" +msgstr "使用者 Plex 來源連線" -#: authentik/sources/plex/models.py:105 +#: authentik/sources/plex/models.py:113 msgid "User Plex Source Connections" -msgstr "用户 Plex 源连接" +msgstr "使用者 Plex 來源連線" -#: authentik/sources/saml/models.py:36 +#: authentik/sources/saml/models.py:40 msgid "Redirect Binding" -msgstr "重定向绑定" +msgstr "重新導向附加" -#: authentik/sources/saml/models.py:37 +#: authentik/sources/saml/models.py:41 msgid "POST Binding" -msgstr "POST 绑定" +msgstr "POST 附加" -#: authentik/sources/saml/models.py:38 +#: authentik/sources/saml/models.py:42 msgid "POST Binding with auto-confirmation" -msgstr "带有自动确认功能的 POST 绑定" - -#: authentik/sources/saml/models.py:57 -msgid "Flow used before authentication." -msgstr "身份验证之前使用的流程。" - -#: authentik/sources/saml/models.py:64 -msgid "Issuer" -msgstr "Issuer" - -#: authentik/sources/saml/models.py:65 -msgid "Also known as Entity ID. Defaults the Metadata URL." -msgstr "也称为实体 ID。 默认为 Metadata URL。" - -#: authentik/sources/saml/models.py:69 -msgid "SSO URL" -msgstr "SSO 网址" +msgstr "使用自動確認的 POST 附加" #: authentik/sources/saml/models.py:70 -msgid "URL that the initial Login request is sent to." -msgstr "初始登录请求发送到的URL。" - -#: authentik/sources/saml/models.py:76 -msgid "SLO URL" -msgstr "SLO URL" +msgid "Flow used before authentication." +msgstr "在身分驗證之前所使用的流程。" #: authentik/sources/saml/models.py:77 -msgid "Optional URL if your IDP supports Single-Logout." -msgstr "如果您的 IDP 支持单点注销,则为可选 URL。" +msgid "Issuer" +msgstr "發行者" + +#: authentik/sources/saml/models.py:78 +msgid "Also known as Entity ID. Defaults the Metadata URL." +msgstr "亦稱為實體 ID。預設為中繼資料的網址。" + +#: authentik/sources/saml/models.py:82 +msgid "SSO URL" +msgstr "SSO 網址" #: authentik/sources/saml/models.py:83 +msgid "URL that the initial Login request is sent to." +msgstr "初始化登入要求時發送到的網址。" + +#: authentik/sources/saml/models.py:89 +msgid "SLO URL" +msgstr "SLO 網址" + +#: authentik/sources/saml/models.py:90 +msgid "Optional URL if your IDP supports Single-Logout." +msgstr "可選:如果您的身份提供者支援單一登出時使用的網址。" + +#: authentik/sources/saml/models.py:96 msgid "" "Allows authentication flows initiated by the IdP. This can be a security " "risk, as no validation of the request ID is done." -msgstr "允许由 IdP 启动的身份验证流。这可能存在安全风险,因为未对请求 ID 进行验证。" +msgstr "允許由身份提供者發起的認證流程。這可能是一個安全風險,因為不會驗證要求的 ID。" -#: authentik/sources/saml/models.py:91 +#: authentik/sources/saml/models.py:104 msgid "" "NameID Policy sent to the IdP. Can be unset, in which case no Policy is " "sent." -msgstr "已向 IdP 发送了 NameID 策略。可以取消设置,在这种情况下,不会发送任何策略。" +msgstr "發送給身份提供者的 NameID 政策。可以為空,在這種情況下則不會發送任何政策。" -#: authentik/sources/saml/models.py:102 +#: authentik/sources/saml/models.py:115 msgid "Delete temporary users after" -msgstr "之后删除临时用户" +msgstr "在以下狀況後刪除臨時使用者:" -#: authentik/sources/saml/models.py:120 +#: authentik/sources/saml/models.py:118 msgid "" -"Keypair which is used to sign outgoing requests. Leave empty to disable " -"signing." -msgstr "用于签署传出请求的密钥对。留空则禁用签名。" +"Time offset when temporary users should be deleted. This only applies if " +"your IDP uses the NameID Format 'transient', and the user doesn't log out " +"manually. (Format: hours=1;minutes=2;seconds=3)." +msgstr "" +"刪除臨時使用者的時間偏移量。這僅適用於您的身份提供者使用 NameID " +"格式「transient」,且用戶沒有手動登出的情況。(格式:hours=1;minutes=2;seconds=3)。" -#: authentik/sources/saml/models.py:188 +#: authentik/sources/saml/models.py:142 +msgid "" +"Keypair used to sign outgoing Responses going to the Identity Provider." +msgstr "用於簽署發往身份提供者的外送回應的金鑰對。" + +#: authentik/sources/saml/models.py:226 msgid "SAML Source" -msgstr "SAML 源" +msgstr "SAML 來源" -#: authentik/sources/saml/models.py:189 +#: authentik/sources/saml/models.py:227 msgid "SAML Sources" -msgstr "SAML 源" +msgstr "SAML 來源" -#: authentik/stages/authenticator_duo/models.py:64 +#: authentik/sources/saml/models.py:242 +msgid "User SAML Source Connection" +msgstr "使用者 SAML 來源連線" + +#: authentik/sources/saml/models.py:243 +msgid "User SAML Source Connections" +msgstr "使用者 SAML 來源連線" + +#: authentik/stages/authenticator_duo/models.py:79 msgid "Duo Authenticator Setup Stage" -msgstr "Duo 身份验证器设置阶段" +msgstr "Duo 身份驗證器設定階段" -#: authentik/stages/authenticator_duo/models.py:65 +#: authentik/stages/authenticator_duo/models.py:80 msgid "Duo Authenticator Setup Stages" -msgstr "Duo 身份验证器设置阶段" +msgstr "Duo 身份驗證器設定階段" -#: authentik/stages/authenticator_duo/models.py:82 +#: authentik/stages/authenticator_duo/models.py:103 msgid "Duo Device" -msgstr "Duo 设备" +msgstr "Duo 設備" -#: authentik/stages/authenticator_duo/models.py:83 +#: authentik/stages/authenticator_duo/models.py:104 msgid "Duo Devices" -msgstr "Duo 设备" +msgstr "Duo 設備" -#: authentik/stages/authenticator_sms/models.py:157 +#: authentik/stages/authenticator_sms/models.py:57 +msgid "" +"When enabled, the Phone number is only used during enrollment to verify the " +"users authenticity. Only a hash of the phone number is saved to ensure it is" +" not reused in the future." +msgstr "啟用時,手機號碼僅在註冊期間用於驗證使用者的真實性。只保存手機號碼的雜湊以確保未來不會重複使用。" + +#: authentik/stages/authenticator_sms/models.py:68 +msgid "Optionally modify the payload being sent to custom providers." +msgstr "可選:修改發送給自訂供應商的酬載。" + +#: authentik/stages/authenticator_sms/models.py:81 +#, python-format +msgid "Use this code to authenticate in authentik: %(token)s" +msgstr "使用此認證碼在 authentik 中進行認證:%(token)s" + +#: authentik/stages/authenticator_sms/models.py:180 msgid "SMS Authenticator Setup Stage" -msgstr "SMS 身份验证器设置阶段" +msgstr "簡訊身份驗證器設定階段" -#: authentik/stages/authenticator_sms/models.py:158 +#: authentik/stages/authenticator_sms/models.py:181 msgid "SMS Authenticator Setup Stages" -msgstr "SMS 身份验证器设置阶段" +msgstr "簡訊身份驗證器設定階段" -#: authentik/stages/authenticator_sms/models.py:175 +#: authentik/stages/authenticator_sms/models.py:226 msgid "SMS Device" -msgstr "短信设备" +msgstr "簡訊設備" -#: authentik/stages/authenticator_sms/models.py:176 +#: authentik/stages/authenticator_sms/models.py:227 msgid "SMS Devices" -msgstr "短信设备" +msgstr "簡訊設備" -#: authentik/stages/authenticator_sms/stage.py:54 -#: authentik/stages/authenticator_totp/stage.py:45 +#: authentik/stages/authenticator_sms/stage.py:55 +#: authentik/stages/authenticator_totp/stage.py:41 +#: authentik/stages/authenticator_totp/stage.py:44 msgid "Code does not match" -msgstr "代码不匹配" +msgstr "授權碼不符" -#: authentik/stages/authenticator_static/models.py:47 +#: authentik/stages/authenticator_sms/stage.py:71 +msgid "Invalid phone number" +msgstr "無效的電話號碼" + +#: authentik/stages/authenticator_static/models.py:52 msgid "Static Authenticator Stage" -msgstr "静态身份验证器阶段" +msgstr "靜態身份驗證器設定階段" -#: authentik/stages/authenticator_static/models.py:48 +#: authentik/stages/authenticator_static/models.py:53 msgid "Static Authenticator Stages" -msgstr "静态身份验证器阶段" +msgstr "靜態身份驗證器設定階段" -#: authentik/stages/authenticator_totp/models.py:16 +#: authentik/stages/authenticator_static/models.py:98 +msgid "Static device" +msgstr "靜態設備" + +#: authentik/stages/authenticator_static/models.py:99 +msgid "Static devices" +msgstr "靜態設備" + +#: authentik/stages/authenticator_totp/models.py:25 msgid "6 digits, widely compatible" -msgstr "6位数字,广泛兼容" +msgstr "6位數字,廣泛相容各類驗證器" -#: authentik/stages/authenticator_totp/models.py:17 +#: authentik/stages/authenticator_totp/models.py:26 msgid "8 digits, not compatible with apps like Google Authenticator" -msgstr "8位数字,与谷歌身份验证器等应用不兼容" +msgstr "8位數字,不相容於類似 Google Authenticator 等驗證器" -#: authentik/stages/authenticator_totp/models.py:54 +#: authentik/stages/authenticator_totp/models.py:62 msgid "TOTP Authenticator Setup Stage" -msgstr "TOTP 身份验证器设置阶段" +msgstr "TOTP 身份驗證器設定階段" -#: authentik/stages/authenticator_totp/models.py:55 +#: authentik/stages/authenticator_totp/models.py:63 msgid "TOTP Authenticator Setup Stages" -msgstr "TOTP 身份验证器设置阶段" +msgstr "TOTP 身份驗證器設定階段" -#: authentik/stages/authenticator_validate/challenge.py:99 +#: authentik/stages/authenticator_totp/models.py:244 +msgid "TOTP device" +msgstr "TOTP 裝置" + +#: authentik/stages/authenticator_totp/models.py:245 +msgid "TOTP devices" +msgstr "TOTP 裝置" + +#: authentik/stages/authenticator_validate/challenge.py:131 msgid "Invalid Token" -msgstr "无效令牌" +msgstr "無效權杖" -#: authentik/stages/authenticator_validate/models.py:17 +#: authentik/stages/authenticator_validate/models.py:18 +msgid "Static" +msgstr "靜態" + +#: authentik/stages/authenticator_validate/models.py:19 msgid "TOTP" msgstr "TOTP" -#: authentik/stages/authenticator_validate/models.py:18 +#: authentik/stages/authenticator_validate/models.py:20 msgid "WebAuthn" msgstr "WebAuthn" -#: authentik/stages/authenticator_validate/models.py:19 +#: authentik/stages/authenticator_validate/models.py:21 msgid "Duo" msgstr "Duo" -#: authentik/stages/authenticator_validate/models.py:20 +#: authentik/stages/authenticator_validate/models.py:22 msgid "SMS" -msgstr "短信" +msgstr "簡訊" -#: authentik/stages/authenticator_validate/models.py:58 +#: authentik/stages/authenticator_validate/models.py:49 +msgid "" +"Stages used to configure Authenticator when user doesn't have any compatible" +" devices. After this configuration Stage passes, the user is not prompted " +"again." +msgstr "在使用者沒有任何相容設備時用於設定身份驗證器的階段。在通過此設定階段後,使用者將不再收到提示。" + +#: authentik/stages/authenticator_validate/models.py:56 msgid "Device classes which can be used to authenticate" -msgstr "可用于进行身份验证的设备类别" +msgstr "可用於身分驗證的裝置類別" -#: authentik/stages/authenticator_validate/models.py:80 +#: authentik/stages/authenticator_validate/models.py:64 +msgid "" +"If any of the user's device has been used within this threshold, this stage " +"will be skipped" +msgstr "如果在此閾值內的使用者任何設備被使用過,則將跳過此階段。" + +#: authentik/stages/authenticator_validate/models.py:70 +msgid "Enforce user verification for WebAuthn devices." +msgstr "強制使用者使用 WebAuthn 裝置進行驗證。" + +#: authentik/stages/authenticator_validate/models.py:92 msgid "Authenticator Validation Stage" -msgstr "身份验证器验证阶段" +msgstr "身份驗證器驗證階段" -#: authentik/stages/authenticator_validate/models.py:81 +#: authentik/stages/authenticator_validate/models.py:93 msgid "Authenticator Validation Stages" -msgstr "身份验证器验证阶段" +msgstr "身份驗證器驗證階段" -#: authentik/stages/authenticator_webauthn/models.py:71 +#: authentik/stages/authenticator_webauthn/models.py:112 msgid "WebAuthn Authenticator Setup Stage" -msgstr "WebAuthn 身份验证器设置阶段" +msgstr "WebAuthn 身份驗證器設定階段" -#: authentik/stages/authenticator_webauthn/models.py:72 +#: authentik/stages/authenticator_webauthn/models.py:113 msgid "WebAuthn Authenticator Setup Stages" -msgstr "WebAuthn 身份验证器设置阶段" +msgstr "WebAuthn 身份驗證器設定階段" -#: authentik/stages/authenticator_webauthn/models.py:105 +#: authentik/stages/authenticator_webauthn/models.py:151 msgid "WebAuthn Device" -msgstr "WebAuthn 设备" +msgstr "WebAuthn 設備" -#: authentik/stages/authenticator_webauthn/models.py:106 +#: authentik/stages/authenticator_webauthn/models.py:152 msgid "WebAuthn Devices" -msgstr "WebAuthn 设备" +msgstr "WebAuthn 設備" + +#: authentik/stages/captcha/models.py:14 +msgid "Public key, acquired your captcha Provider." +msgstr "公鑰,由您的驗證碼供應商中取得" #: authentik/stages/captcha/models.py:15 -msgid "" -"Public key, acquired from https://www.google.com/recaptcha/intro/v3.html" -msgstr "公钥,从 https://www.google.com/recaptcha/intro/v3.html 获取" +msgid "Private key, acquired your captcha Provider." +msgstr "私鑰,由您的驗證碼供應商中取得" -#: authentik/stages/captcha/models.py:18 -msgid "" -"Private key, acquired from https://www.google.com/recaptcha/intro/v3.html" -msgstr "私钥,从 https://www.google.com/recaptcha/intro/v3.html 获取" - -#: authentik/stages/captcha/models.py:39 +#: authentik/stages/captcha/models.py:37 msgid "Captcha Stage" -msgstr "验证码阶段" +msgstr "驗證碼階段" -#: authentik/stages/captcha/models.py:40 +#: authentik/stages/captcha/models.py:38 msgid "Captcha Stages" -msgstr "验证码阶段" +msgstr "驗證碼階段" -#: authentik/stages/consent/models.py:52 +#: authentik/stages/consent/models.py:30 +msgid "" +"Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)." +msgstr "同意在此之後過期的時間偏移量。(格式:hours=1;minutes=2;seconds=3)。" + +#: authentik/stages/consent/models.py:50 msgid "Consent Stage" -msgstr "同意阶段" +msgstr "同意階段" -#: authentik/stages/consent/models.py:53 +#: authentik/stages/consent/models.py:51 msgid "Consent Stages" -msgstr "Consent Stages" +msgstr "同意階段" -#: authentik/stages/consent/models.py:68 +#: authentik/stages/consent/models.py:72 msgid "User Consent" -msgstr "用户同意" +msgstr "使用者同意" -#: authentik/stages/consent/models.py:69 +#: authentik/stages/consent/models.py:73 msgid "User Consents" -msgstr "用户同意" +msgstr "使用者同意" + +#: authentik/stages/deny/models.py:30 +msgid "Deny Stage" +msgstr "拒絕階段" #: authentik/stages/deny/models.py:31 -msgid "Deny Stage" -msgstr "拒绝舞台" - -#: authentik/stages/deny/models.py:32 msgid "Deny Stages" -msgstr "拒绝阶段" - -#: authentik/stages/dummy/models.py:33 -msgid "Dummy Stage" -msgstr "虚拟阶段" +msgstr "拒絕階段" #: authentik/stages/dummy/models.py:34 +msgid "Dummy Stage" +msgstr "假階段" + +#: authentik/stages/dummy/models.py:35 msgid "Dummy Stages" -msgstr "虚拟阶段" +msgstr "假階段" -#: authentik/stages/email/models.py:25 +#: authentik/stages/email/models.py:26 msgid "Password Reset" -msgstr "密码重置" +msgstr "重設密碼" -#: authentik/stages/email/models.py:29 +#: authentik/stages/email/models.py:30 msgid "Account Confirmation" -msgstr "账户确认" +msgstr "帳戶認證" -#: authentik/stages/email/models.py:75 +#: authentik/stages/email/models.py:59 +msgid "" +"When enabled, global Email connection settings will be used and connection " +"settings below will be ignored." +msgstr "啟用時,將使用全域電子郵件連線設定,在此之下的連線設定將被忽略。" + +#: authentik/stages/email/models.py:74 msgid "Activate users upon completion of stage." -msgstr "完成阶段后激活用户。" +msgstr "完成階段後啟用使用者。" -#: authentik/stages/email/models.py:79 +#: authentik/stages/email/models.py:78 msgid "Time in minutes the token sent is valid." -msgstr "发送的令牌的有效时间(以分钟为单位)。" +msgstr "發送權杖的有效時間(以分鐘為單位)。" -#: authentik/stages/email/models.py:125 +#: authentik/stages/email/models.py:132 msgid "Email Stage" -msgstr "电子邮件阶段" +msgstr "電子郵件階段" -#: authentik/stages/email/models.py:126 +#: authentik/stages/email/models.py:133 msgid "Email Stages" -msgstr "电子邮件阶段" +msgstr "電子郵件階段" -#: authentik/stages/email/stage.py:106 +#: authentik/stages/email/stage.py:117 msgid "Successfully verified Email." -msgstr "已成功验证电子邮件。" +msgstr "已成功認證電子郵件。" -#: authentik/stages/email/stage.py:113 authentik/stages/email/stage.py:135 +#: authentik/stages/email/stage.py:124 authentik/stages/email/stage.py:146 msgid "No pending user." -msgstr "没有待处理的用户。" +msgstr "沒有待處理的使用者。" -#: authentik/stages/email/stage.py:125 +#: authentik/stages/email/stage.py:136 msgid "Email sent." -msgstr "邮件已发出。" +msgstr "已發送電子郵件。" -#: authentik/stages/email/templates/email/account_confirmation.html:9 +#: authentik/stages/email/templates/email/account_confirmation.html:10 msgid "Welcome!" -msgstr "欢迎!" +msgstr "歡迎!" -#: authentik/stages/email/templates/email/account_confirmation.html:12 +#: authentik/stages/email/templates/email/account_confirmation.html:19 msgid "" "We're excited to have you get started. First, you need to confirm your " "account. Just press the button below." -msgstr "我们很高兴你能开始使用。首先,您需要确认您的帐户。只需按下下面的按钮即可。" +msgstr "我們很高興您能開始使用。首先,您需要驗證您的帳戶。只需按下方的按鈕即可。" -#: authentik/stages/email/templates/email/account_confirmation.html:21 +#: authentik/stages/email/templates/email/account_confirmation.html:24 msgid "Confirm Account" -msgstr "确认账户" +msgstr "驗證帳戶" -#: authentik/stages/email/templates/email/account_confirmation.html:30 +#: authentik/stages/email/templates/email/account_confirmation.html:36 #, python-format msgid "" "\n" @@ -1471,58 +2155,59 @@ msgid "" " " msgstr "" "\n" -" 如果这不起作用,请在浏览器中复制并粘贴以下链接:%(url)s\n" +" 如果這不起作用,請在瀏覽器中複製並貼上以下連結:%(url)s\n" " " -#: authentik/stages/email/templates/email/account_confirmation.html:35 -msgid "" -"If you have any questions, just reply to this email—we're always happy to " -"help out." -msgstr "如果您有任何疑问,请回复此电子邮件——我们很乐意为您提供帮助。" - -#: authentik/stages/email/templates/email/generic.html:24 -msgid "Additional Information" -msgstr "其他信息" - -#: authentik/stages/email/templates/email/password_reset.html:9 +#: authentik/stages/email/templates/email/event_notification.html:46 #, python-format msgid "" "\n" -" Hi %(username)s,\n" -" " +" This email was sent from the notification transport %(name)s.\n" +" " msgstr "" "\n" -" 你好 %(username)s,\n" -" " +" 這封信件來自通知通道:%(name)s。\n" +" " -#: authentik/stages/email/templates/email/password_reset.html:19 +#: authentik/stages/email/templates/email/password_reset.html:10 +#, python-format msgid "" "\n" -" You recently requested to change your password for you authentik account. Use the button below to set a new password.\n" -" " +" Hi %(username)s,\n" +" " msgstr "" "\n" -" 您最近请求更改您的 authentik 账户的密码。使用下面的按钮设置新密码。\n" -" " +" 嗨!%(username)s。\n" +" " -#: authentik/stages/email/templates/email/password_reset.html:33 +#: authentik/stages/email/templates/email/password_reset.html:21 +msgid "" +"\n" +" You recently requested to change your password for your authentik account. Use the button below to set a new password.\n" +" " +msgstr "" +"\n" +" 您最近要求重設您的 authentik 帳戶的密碼。請使用下方的按鈕來設定新密碼。\n" +" " + +#: authentik/stages/email/templates/email/password_reset.html:28 msgid "Reset Password" -msgstr "重置密码" +msgstr "重設密碼" -#: authentik/stages/email/templates/email/password_reset.html:45 +#: authentik/stages/email/templates/email/password_reset.html:39 #, python-format msgid "" "\n" -" If you did not request a password change, please ignore this Email. The link above is valid for %(expires)s.\n" -" " +" If you did not request a password change, please ignore this Email. The link above is valid for %(expires)s.\n" +" " msgstr "" "\n" -" 如果您没有请求更改密码,请忽略此电子邮件。上面的链接对 %(expires)s 有效。\n" -" " +" 如果您沒有要求重設密碼,請忽略本信件。以下連結將在%(expires)s 後過期。\n" +" " #: authentik/stages/email/templates/email/setup.html:9 msgid "authentik Test-Email" -msgstr "authentik 测试电子邮件" +msgstr "authentik 測試電子郵件" #: authentik/stages/email/templates/email/setup.html:17 msgid "" @@ -1531,219 +2216,339 @@ msgid "" " " msgstr "" "\n" -" 这是一封测试电子邮件,用于通知您,您已成功配置身份验证电子邮件。\n" +" 這是一封測試電子郵件用於通知您,您已成功設定了 authentik 電子郵件。\n" " " -#: authentik/stages/identification/models.py:42 +#: authentik/stages/identification/api.py:20 +msgid "When no user fields are selected, at least one source must be selected" +msgstr "當沒有使用者欄位被選擇時,至少要選擇一個來源" + +#: authentik/stages/identification/models.py:29 msgid "" -"When set, shows a password field, instead of showing the password field as " -"seaprate step." -msgstr "设置后,将显示密码字段,而不是将密码字段显示为单独的步骤。" +"Fields of the user object to match against. (Hold shift to select multiple " +"options)" +msgstr "用於配對使用者物件的欄位。(可按住 Shift 選擇多個選項)" -#: authentik/stages/identification/models.py:48 +#: authentik/stages/identification/models.py:47 msgid "When enabled, user fields are matched regardless of their casing." -msgstr "启用后,无论用户字段大小写如何,都将匹配用户字段。" +msgstr "啟用時,配對將無視使用者欄位的大小寫差異。" -#: authentik/stages/identification/models.py:68 +#: authentik/stages/identification/models.py:52 +msgid "" +"When a valid username/email has been entered, and this option is enabled, " +"the user's username and avatar will be shown. Otherwise, the text that the " +"user entered will be shown" +msgstr "當啟用了此選項,且輸入了有效的使用者名稱/電子郵件時,將顯示用戶的用戶名稱和頭像。否則,將顯示使用者輸入的文字。" + +#: authentik/stages/identification/models.py:65 msgid "Optional enrollment flow, which is linked at the bottom of the page." -msgstr "可选注册流程,链接在页面底部。" +msgstr "可選的註冊流程,連結在頁面的底部。" -#: authentik/stages/identification/models.py:77 +#: authentik/stages/identification/models.py:74 msgid "Optional recovery flow, which is linked at the bottom of the page." -msgstr "可选的恢复流程,链接在页面底部。" +msgstr "可選的救援流程,連結在頁面底部。" -#: authentik/stages/identification/models.py:86 +#: authentik/stages/identification/models.py:83 msgid "Optional passwordless flow, which is linked at the bottom of the page." -msgstr "可选的无密码流程,链接在页面底部。" +msgstr "可選的無密碼流程,連結在頁面底部。" -#: authentik/stages/identification/models.py:90 +#: authentik/stages/identification/models.py:87 msgid "Specify which sources should be shown." -msgstr "指定应显示哪些来源。" +msgstr "指定應顯示那些來源。" -#: authentik/stages/identification/models.py:112 +#: authentik/stages/identification/models.py:108 msgid "Identification Stage" -msgstr "识别阶段" +msgstr "識別階段" -#: authentik/stages/identification/models.py:113 +#: authentik/stages/identification/models.py:109 msgid "Identification Stages" -msgstr "识别阶段" +msgstr "識別階段" -#: authentik/stages/identification/stage.py:175 +#: authentik/stages/identification/stage.py:188 msgid "Log in" msgstr "登入" -#: authentik/stages/invitation/models.py:46 +#: authentik/stages/identification/stage.py:189 +msgid "Continue" +msgstr "繼續" + +#: authentik/stages/invitation/models.py:21 +msgid "" +"If this flag is set, this Stage will jump to the next Stage when no " +"Invitation is given. By default this Stage will cancel the Flow when no " +"invitation is given." +msgstr "如果設定了此旗標,在沒有給予邀請的情況下將跳轉到下一階段。預設情況下,當沒有給予邀請時,階段將取消流程。" + +#: authentik/stages/invitation/models.py:44 msgid "Invitation Stage" -msgstr "邀请阶段" +msgstr "邀請階段" -#: authentik/stages/invitation/models.py:47 +#: authentik/stages/invitation/models.py:45 msgid "Invitation Stages" -msgstr "邀请阶段" +msgstr "邀請階段" -#: authentik/stages/invitation/models.py:57 -msgid "When enabled, the invitation will be deleted after usage." -msgstr "启用后,邀请将在使用后被删除。" +#: authentik/stages/invitation/models.py:60 +msgid "When set, only the configured flow can use this invitation." +msgstr "如果設定此項,只有已設定的流程才能夠使用這個邀請。" #: authentik/stages/invitation/models.py:64 +msgid "When enabled, the invitation will be deleted after usage." +msgstr "啟用時,邀請將在使用後被刪除。" + +#: authentik/stages/invitation/models.py:71 msgid "Optional fixed data to enforce on user enrollment." -msgstr "在用户注册时强制执行的可选固定数据。" +msgstr "可選的固定資料,在使用者註冊中強制加入的固定資料。" -#: authentik/stages/invitation/models.py:72 +#: authentik/stages/invitation/models.py:84 msgid "Invitation" -msgstr "邀请" +msgstr "邀請" -#: authentik/stages/invitation/models.py:73 +#: authentik/stages/invitation/models.py:85 msgid "Invitations" -msgstr "邀请" +msgstr "邀請" + +#: authentik/stages/invitation/stage.py:62 +msgid "Invalid invite/invite not found" +msgstr "無效的邀請或找不到該邀請" #: authentik/stages/password/models.py:20 msgid "User database + standard password" -msgstr "用户数据库+标准密码" +msgstr "使用者資料庫 + 標準密碼" #: authentik/stages/password/models.py:24 msgid "User database + app passwords" -msgstr "用户数据库+应用程序密码" +msgstr "使用者資料庫 + 應用程式密碼" #: authentik/stages/password/models.py:28 msgid "User database + LDAP password" -msgstr "用户数据库 + LDAP 密码" +msgstr "使用者資料庫 + LDAP 密碼" #: authentik/stages/password/models.py:38 msgid "Selection of backends to test the password against." -msgstr "选择用于测试密码的后端。" +msgstr "選擇要對照測試密碼的後端。" -#: authentik/stages/password/models.py:78 +#: authentik/stages/password/models.py:43 +msgid "" +"How many attempts a user has before the flow is canceled. To lock the user " +"out, use a reputation policy and a user_write stage." +msgstr "在取消流程前使用者嘗試的次數。要鎖定使用者請使用融域政策和 user_write 階段。" + +#: authentik/stages/password/models.py:75 msgid "Password Stage" -msgstr "密码阶段" +msgstr "密碼階段" -#: authentik/stages/password/models.py:79 +#: authentik/stages/password/models.py:76 msgid "Password Stages" -msgstr "密码阶段" +msgstr "密碼階段" -#: authentik/stages/password/stage.py:152 +#: authentik/stages/password/stage.py:124 msgid "Invalid password" -msgstr "密码无效" +msgstr "無效的密碼" -#: authentik/stages/prompt/models.py:29 +#: authentik/stages/prompt/models.py:43 msgid "Text: Simple Text input" -msgstr "文本:简单文本输入" +msgstr "文字:簡單文字輸入" -#: authentik/stages/prompt/models.py:32 +#: authentik/stages/prompt/models.py:45 +msgid "Text area: Multiline Text Input." +msgstr "文字區塊:多行文字輸入。" + +#: authentik/stages/prompt/models.py:48 msgid "Text (read-only): Simple Text input, but cannot be edited." -msgstr "文本(只读):简单文本输入,但无法编辑。" +msgstr "文字(唯讀):簡單文字輸入,但無法編輯。" -#: authentik/stages/prompt/models.py:39 +#: authentik/stages/prompt/models.py:52 +msgid "Text area (read-only): Multiline Text input, but cannot be edited." +msgstr "文字區塊(唯讀):多行文字輸入。但無法編輯。" + +#: authentik/stages/prompt/models.py:58 +msgid "" +"Username: Same as Text input, but checks for and prevents duplicate " +"usernames." +msgstr "使用者名稱:與文字輸入相同,但檢查是否與現存有重複。" + +#: authentik/stages/prompt/models.py:60 msgid "Email: Text field with Email type." -msgstr "电子邮件:具有电子邮件类型的文本字段。" +msgstr "電子郵件:具有電子郵件類型的文字欄位。" -#: authentik/stages/prompt/models.py:55 +#: authentik/stages/prompt/models.py:64 +msgid "" +"Password: Masked input, multiple inputs of this type on the same prompt need" +" to be identical." +msgstr "密碼:遮罩輸入,同一提示上的多個此類輸入需要相同。" + +#: authentik/stages/prompt/models.py:71 +msgid "Fixed choice field rendered as a group of radio buttons." +msgstr "固定選項欄位將繪製成單選按紐群組。" + +#: authentik/stages/prompt/models.py:73 +msgid "Fixed choice field rendered as a dropdown." +msgstr "固定選項欄位將繪製成下拉式選單。" + +#: authentik/stages/prompt/models.py:80 +msgid "" +"File: File upload for arbitrary files. File content will be available in " +"flow context as data-URI" +msgstr "檔案:任意檔案類型上傳。檔案內容將會作為流程內文的資料URI" + +#: authentik/stages/prompt/models.py:85 msgid "Separator: Static Separator Line" -msgstr "分隔符:静态分隔线" +msgstr "分隔符號:靜態分隔線" -#: authentik/stages/prompt/models.py:56 +#: authentik/stages/prompt/models.py:86 msgid "Hidden: Hidden field, can be used to insert data into form." -msgstr "隐藏:隐藏字段,可用于将数据插入表单。" +msgstr "隱藏:隱藏欄位,可用於將資料插入表單。" -#: authentik/stages/prompt/models.py:57 +#: authentik/stages/prompt/models.py:87 msgid "Static: Static value, displayed as-is." -msgstr "静态:静态值,按原样显示。" +msgstr "靜態:靜態數值,按原狀顯示。" -#: authentik/stages/prompt/models.py:66 +#: authentik/stages/prompt/models.py:89 +msgid "authentik: Selection of locales authentik supports" +msgstr "authentik:選擇 authentik 支援的語系" + +#: authentik/stages/prompt/models.py:116 msgid "Name of the form field, also used to store the value" -msgstr "表单域的名称,也用于存储值" +msgstr "表單名稱,也用於儲存數值。" -#: authentik/stages/prompt/models.py:131 +#: authentik/stages/prompt/models.py:124 +msgid "" +"Optionally provide a short hint that describes the expected input value. " +"When creating a fixed choice field, enable interpreting as expression and " +"return a list to return multiple choices." +msgstr "可選:提供一個簡短提示,描述預期的輸入值。當建立一個固定選擇欄位時,啟用解釋為表達式,並回傳一個列表以提供多個選擇。" + +#: authentik/stages/prompt/models.py:132 +msgid "" +"Optionally pre-fill the input with an initial value. When creating a fixed " +"choice field, enable interpreting as expression and return a list to return " +"multiple default choices." +msgstr "可選:預先填入輸入框以一個初始值。當建立一個固定選擇欄位時,啟用解釋為表達式,並回傳一個列表以提供多個預設選擇。" + +#: authentik/stages/prompt/models.py:321 msgid "Prompt" msgstr "提示" -#: authentik/stages/prompt/models.py:132 +#: authentik/stages/prompt/models.py:322 msgid "Prompts" msgstr "提示" -#: authentik/stages/prompt/models.py:160 +#: authentik/stages/prompt/models.py:349 msgid "Prompt Stage" -msgstr "提示阶段" +msgstr "提示階段" -#: authentik/stages/prompt/models.py:161 +#: authentik/stages/prompt/models.py:350 msgid "Prompt Stages" -msgstr "提示阶段" +msgstr "提示階段" -#: authentik/stages/prompt/stage.py:94 +#: authentik/stages/prompt/stage.py:108 msgid "Passwords don't match." -msgstr "密码不匹配。" +msgstr "密碼不符。" + +#: authentik/stages/user_delete/models.py:31 +msgid "User Delete Stage" +msgstr "使用者刪除階段" #: authentik/stages/user_delete/models.py:32 -msgid "User Delete Stage" -msgstr "用户删除阶段" - -#: authentik/stages/user_delete/models.py:33 msgid "User Delete Stages" -msgstr "用户删除阶段" +msgstr "使用者刪除階段" -#: authentik/stages/user_delete/stage.py:24 +#: authentik/stages/user_delete/stage.py:18 msgid "No Pending User." -msgstr "没有待处理的用户。" +msgstr "無待處理的使用者。" #: authentik/stages/user_login/models.py:19 msgid "" "Determines how long a session lasts. Default of 0 means that the sessions " "lasts until the browser is closed. (Format: hours=-1;minutes=-2;seconds=-3)" -msgstr "确定会话持续多长时间。默认值为 0 表示会话持续到浏览器关闭为止。(格式:hours=-1;minutes=-2;seconds=-3)" +msgstr "確定會談持續多久。預設值為 0 表示會談會持續到關閉瀏覽器為止。(格式:hours=-1;minutes=-2;seconds=-3)" -#: authentik/stages/user_login/models.py:43 +#: authentik/stages/user_login/models.py:25 +msgid "Terminate all other sessions of the user logging in." +msgstr "終止所有已登入的使用者連線。" + +#: authentik/stages/user_login/models.py:31 +msgid "" +"Offset the session will be extended by when the user picks the remember me " +"option. Default of 0 means that the remember me option will not be shown. " +"(Format: hours=-1;minutes=-2;seconds=-3)" +msgstr "" +"當使用者選擇記住我選項時,會延長會談的偏移時間。預設值為 0 " +"意味著不會顯示記住我選項。(格式:hours=-1;minutes=-2;seconds=-3)" + +#: authentik/stages/user_login/models.py:54 msgid "User Login Stage" -msgstr "用户登录阶段" +msgstr "使用者登入階段" -#: authentik/stages/user_login/models.py:44 +#: authentik/stages/user_login/models.py:55 msgid "User Login Stages" -msgstr "用户登录阶段" - -#: authentik/stages/user_login/stage.py:29 -msgid "No Pending user to login." -msgstr "没有待定用户可以登录。" +msgstr "使用者登入階段" #: authentik/stages/user_login/stage.py:57 +msgid "No Pending user to login." +msgstr "無待登入的使用者。" + +#: authentik/stages/user_login/stage.py:90 msgid "Successfully logged in!" msgstr "已成功登入!" -#: authentik/stages/user_logout/models.py:31 +#: authentik/stages/user_logout/models.py:30 msgid "User Logout Stage" -msgstr "用户注销阶段" +msgstr "使用者登出階段" -#: authentik/stages/user_logout/models.py:32 +#: authentik/stages/user_logout/models.py:31 msgid "User Logout Stages" -msgstr "用户注销阶段" +msgstr "使用者登出階段" -#: authentik/stages/user_write/models.py:18 +#: authentik/stages/user_write/models.py:31 msgid "When set, newly created users are inactive and cannot login." -msgstr "设置后,新创建的用户将处于非活动状态,且无法登录。" +msgstr "設定時,新建立的使用者會是非活躍狀態,無法登入。" -#: authentik/stages/user_write/models.py:26 +#: authentik/stages/user_write/models.py:39 msgid "Optionally add newly created users to this group." -msgstr "(可选)将新创建的用户添加到此组。" +msgstr "可選:將新建立的使用者加入此群組。" -#: authentik/stages/user_write/models.py:47 +#: authentik/stages/user_write/models.py:64 msgid "User Write Stage" -msgstr "用户写入阶段" +msgstr "使用者寫入階段" -#: authentik/stages/user_write/models.py:48 +#: authentik/stages/user_write/models.py:65 msgid "User Write Stages" -msgstr "用户写入阶段" +msgstr "使用者寫入階段" -#: authentik/stages/user_write/stage.py:53 +#: authentik/stages/user_write/stage.py:130 msgid "No Pending data." -msgstr "没有待处理的数据。" +msgstr "無待處理的資料。" -#: authentik/tenants/models.py:18 +#: authentik/stages/user_write/stage.py:136 +msgid "No user found and can't create new user." +msgstr "找不到使用者以及無法建立新的使用者。" + +#: authentik/stages/user_write/stage.py:153 +#: authentik/stages/user_write/stage.py:167 +msgid "Failed to update user. Please try again later." +msgstr "無法更新使用者,請稍後重新嘗試。" + +#: authentik/tenants/models.py:23 msgid "" "Domain that activates this tenant. Can be a superset, i.e. `a.b` for `aa.b` " "and `ba.b`" -msgstr "激活此租户的域。可以是超集,例如 `aa.b` 表示 `aa.b` 和 `ba.b`" +msgstr "啟用此租戶的網域。可以是超集合,例如 `a.b` 對應於 `aa.b` 和 `ba.b`" -#: authentik/tenants/models.py:70 +#: authentik/tenants/models.py:58 +msgid "" +"Events will be deleted after this duration.(Format: " +"weeks=3;days=2;hours=3,seconds=2)." +msgstr "事件將在此期間後刪除。(格式:weeks=3;days=2;hours=3,seconds=2)" + +#: authentik/tenants/models.py:67 +msgid "Web Certificate used by the authentik Core webserver." +msgstr "用於 authentik Core 網頁伺服器的網頁憑證。" + +#: authentik/tenants/models.py:93 msgid "Tenant" -msgstr "租户" +msgstr "租戶" -#: authentik/tenants/models.py:71 +#: authentik/tenants/models.py:94 msgid "Tenants" -msgstr "租户" +msgstr "租戶" diff --git a/poetry.lock b/poetry.lock index 9886f5ac7..197b912fe 100644 --- a/poetry.lock +++ b/poetry.lock @@ -422,13 +422,13 @@ typecheck = ["mypy"] [[package]] name = "billiard" -version = "4.1.0" +version = "4.2.0" description = "Python multiprocessing fork with improvements and bugfixes" optional = false python-versions = ">=3.7" files = [ - {file = "billiard-4.1.0-py3-none-any.whl", hash = "sha256:0f50d6be051c6b2b75bfbc8bfd85af195c5739c281d3f5b86a5640c65563614a"}, - {file = "billiard-4.1.0.tar.gz", hash = "sha256:1ad2eeae8e28053d729ba3373d34d9d6e210f6e4d8bf0a9c64f92bd053f1edf5"}, + {file = "billiard-4.2.0-py3-none-any.whl", hash = "sha256:07aa978b308f334ff8282bd4a746e681b3513db5c9a514cbdd810cbbdc19714d"}, + {file = "billiard-4.2.0.tar.gz", hash = "sha256:9a3c3184cb275aa17a732f93f65b20c525d3d9f253722d26a82194803ade5a2c"}, ] [[package]] @@ -544,29 +544,29 @@ test = ["pytest", "pytest-cov"] [[package]] name = "celery" -version = "5.3.4" +version = "5.3.5" description = "Distributed Task Queue." optional = false python-versions = ">=3.8" files = [ - {file = "celery-5.3.4-py3-none-any.whl", hash = "sha256:1e6ed40af72695464ce98ca2c201ad0ef8fd192246f6c9eac8bba343b980ad34"}, - {file = "celery-5.3.4.tar.gz", hash = "sha256:9023df6a8962da79eb30c0c84d5f4863d9793a466354cc931d7f72423996de28"}, + {file = "celery-5.3.5-py3-none-any.whl", hash = "sha256:30b75ac60fb081c2d9f8881382c148ed7c9052031a75a1e8743ff4b4b071f184"}, + {file = "celery-5.3.5.tar.gz", hash = "sha256:6b65d8dd5db499dd6190c45aa6398e171b99592f2af62c312f7391587feb5458"}, ] [package.dependencies] -billiard = ">=4.1.0,<5.0" +billiard = ">=4.2.0,<5.0" click = ">=8.1.2,<9.0" click-didyoumean = ">=0.3.0" click-plugins = ">=1.1.1" click-repl = ">=0.2.0" -kombu = ">=5.3.2,<6.0" +kombu = ">=5.3.3,<6.0" python-dateutil = ">=2.8.2" tzdata = ">=2022.7" -vine = ">=5.0.0,<6.0" +vine = ">=5.1.0,<6.0" [package.extras] arangodb = ["pyArango (>=2.0.2)"] -auth = ["cryptography (==41.0.3)"] +auth = ["cryptography (==41.0.5)"] azureblockblob = ["azure-storage-blob (>=12.15.0)"] brotli = ["brotli (>=1.0.0)", "brotlipy (>=0.7.0)"] cassandra = ["cassandra-driver (>=3.25.0,<4)"] @@ -576,26 +576,26 @@ couchbase = ["couchbase (>=3.0.0)"] couchdb = ["pycouchdb (==1.14.2)"] django = ["Django (>=2.2.28)"] dynamodb = ["boto3 (>=1.26.143)"] -elasticsearch = ["elasticsearch (<8.0)"] +elasticsearch = ["elastic-transport (<=8.10.0)", "elasticsearch (<=8.10.1)"] eventlet = ["eventlet (>=0.32.0)"] gevent = ["gevent (>=1.5.0)"] librabbitmq = ["librabbitmq (>=2.0.0)"] memcache = ["pylibmc (==1.6.3)"] mongodb = ["pymongo[srv] (>=4.0.2)"] -msgpack = ["msgpack (==1.0.5)"] +msgpack = ["msgpack (==1.0.7)"] pymemcache = ["python-memcached (==1.59)"] pyro = ["pyro4 (==4.82)"] pytest = ["pytest-celery (==0.0.0)"] -redis = ["redis (>=4.5.2,!=4.5.5,<5.0.0)"] +redis = ["redis (>=4.5.2,!=4.5.5,<6.0.0)"] s3 = ["boto3 (>=1.26.143)"] slmq = ["softlayer-messaging (>=1.0.3)"] -solar = ["ephem (==4.1.4)"] +solar = ["ephem (==4.1.5)"] sqlalchemy = ["sqlalchemy (>=1.4.48,<2.1)"] sqs = ["boto3 (>=1.26.143)", "kombu[sqs] (>=5.3.0)", "pycurl (>=7.43.0.5)", "urllib3 (>=1.26.16)"] tblib = ["tblib (>=1.3.0)", "tblib (>=1.5.0)"] yaml = ["PyYAML (>=3.10)"] zookeeper = ["kazoo (>=1.3.1)"] -zstd = ["zstandard (==0.21.0)"] +zstd = ["zstandard (==0.22.0)"] [[package]] name = "certifi" @@ -1868,13 +1868,13 @@ referencing = ">=0.28.0" [[package]] name = "kombu" -version = "5.3.2" +version = "5.3.3" description = "Messaging library for Python." optional = false python-versions = ">=3.8" files = [ - {file = "kombu-5.3.2-py3-none-any.whl", hash = "sha256:b753c9cfc9b1e976e637a7cbc1a65d446a22e45546cd996ea28f932082b7dc9e"}, - {file = "kombu-5.3.2.tar.gz", hash = "sha256:0ba213f630a2cb2772728aef56ac6883dc3a2f13435e10048f6e97d48506dbbd"}, + {file = "kombu-5.3.3-py3-none-any.whl", hash = "sha256:6cd5c5d5ef77538434b8f81f3e265c414269418645dbb47dbf130a8a05c3e357"}, + {file = "kombu-5.3.3.tar.gz", hash = "sha256:1491df826cfc5178c80f3e89dd6dfba68e484ef334db81070eb5cb8094b31167"}, ] [package.dependencies] @@ -1884,14 +1884,14 @@ vine = "*" [package.extras] azureservicebus = ["azure-servicebus (>=7.10.0)"] azurestoragequeues = ["azure-identity (>=1.12.0)", "azure-storage-queue (>=12.6.0)"] -confluentkafka = ["confluent-kafka (==2.1.1)"] +confluentkafka = ["confluent-kafka (>=2.2.0)"] consul = ["python-consul2"] librabbitmq = ["librabbitmq (>=2.0.0)"] mongodb = ["pymongo (>=4.1.1)"] msgpack = ["msgpack"] pyro = ["pyro4"] qpid = ["qpid-python (>=0.26)", "qpid-tools (>=0.26)"] -redis = ["redis (>=4.5.2)"] +redis = ["redis (>=4.5.2,!=4.5.5,<6.0.0)"] slmq = ["softlayer-messaging (>=1.0.3)"] sqlalchemy = ["sqlalchemy (>=1.4.48,<2.1)"] sqs = ["boto3 (>=1.26.143)", "pycurl (>=7.43.0.5)", "urllib3 (>=1.26.16)"] @@ -3675,13 +3675,13 @@ wsproto = ">=0.14" [[package]] name = "twilio" -version = "8.10.0" +version = "8.10.1" description = "Twilio API client and TwiML generator" optional = false python-versions = ">=3.7.0" files = [ - {file = "twilio-8.10.0-py2.py3-none-any.whl", hash = "sha256:1eb04af92f3e70fcc87a2fd30617f53784e34045d054e4ae3dc9cfe7bdf1e692"}, - {file = "twilio-8.10.0.tar.gz", hash = "sha256:3bf2def228ceaa7519f4d6e58b2e3c9cb5d865af02b4618239e52c9d9e75e29d"}, + {file = "twilio-8.10.1-py2.py3-none-any.whl", hash = "sha256:eb08ac17c8eb4f6176907b4e7dea984102488fb86ad146ecd47e8a8dfcf3cfa3"}, + {file = "twilio-8.10.1.tar.gz", hash = "sha256:902267856d09cf1f59b7fa4af594edae0225fdd8b473a6ef8e5799e823e0a611"}, ] [package.dependencies] @@ -3924,13 +3924,13 @@ test = ["Cython (>=0.29.32,<0.30.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "my [[package]] name = "vine" -version = "5.0.0" -description = "Promises, promises, promises." +version = "5.1.0" +description = "Python promises." optional = false python-versions = ">=3.6" files = [ - {file = "vine-5.0.0-py2.py3-none-any.whl", hash = "sha256:4c9dceab6f76ed92105027c49c823800dd33cacce13bdedc5b914e3514b7fb30"}, - {file = "vine-5.0.0.tar.gz", hash = "sha256:7d3b1624a953da82ef63462013bbd271d3eb75751489f9807598e8f340bd637e"}, + {file = "vine-5.1.0-py3-none-any.whl", hash = "sha256:40fdf3c48b2cfe1c38a49e9ae2da6fda88e4794c810050a728bd7413811fb1dc"}, + {file = "vine-5.1.0.tar.gz", hash = "sha256:8b62e981d35c41049211cf62a0a1242d8c1ee9bd15bb196ce38aefd6799e61e0"}, ] [[package]] diff --git a/schema.yml b/schema.yml index d8c13331e..fd025466a 100644 --- a/schema.yml +++ b/schema.yml @@ -18942,7 +18942,7 @@ paths: description: '' /sources/ldap/{slug}/sync_status/: get: - operationId: sources_ldap_sync_status_list + operationId: sources_ldap_sync_status_retrieve description: Get source's sync status parameters: - in: path @@ -18960,9 +18960,7 @@ paths: content: application/json: schema: - type: array - items: - $ref: '#/components/schemas/Task' + $ref: '#/components/schemas/LDAPSyncStatus' description: '' '400': content: @@ -32812,9 +32810,19 @@ components: type: string format: uuid description: Property mappings used for group creation/updating. + connectivity: + type: object + additionalProperties: + type: object + additionalProperties: + type: string + nullable: true + description: Get cached source connectivity + readOnly: true required: - base_dn - component + - connectivity - icon - managed - meta_model_name @@ -32948,6 +32956,21 @@ components: - name - server_uri - slug + LDAPSyncStatus: + type: object + description: LDAP Source sync status + properties: + is_running: + type: boolean + readOnly: true + tasks: + type: array + items: + $ref: '#/components/schemas/Task' + readOnly: true + required: + - is_running + - tasks LayoutEnum: enum: - stacked diff --git a/tests/wdio/package-lock.json b/tests/wdio/package-lock.json index 26eb03402..081e13d33 100644 --- a/tests/wdio/package-lock.json +++ b/tests/wdio/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "@goauthentik/web-tests", "devDependencies": { - "@trivago/prettier-plugin-sort-imports": "^4.2.1", + "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@typescript-eslint/eslint-plugin": "^6.10.0", "@typescript-eslint/parser": "^6.10.0", "@wdio/cli": "^8.22.1", @@ -17,10 +17,10 @@ "eslint-config-google": "^0.14.0", "eslint-plugin-sonarjs": "^0.23.0", "npm-run-all": "^4.1.5", - "prettier": "^3.0.3", + "prettier": "^3.1.0", "ts-node": "^10.9.1", "typescript": "^5.2.2", - "wdio-wait-for": "^3.0.7" + "wdio-wait-for": "^3.0.8" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -787,9 +787,9 @@ "dev": true }, "node_modules/@trivago/prettier-plugin-sort-imports": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.2.1.tgz", - "integrity": "sha512-iuy2MPVURGdxILTchHr15VAioItuYBejKfcTmQFlxIuqA7jeaT6ngr5aUIG6S6U096d6a6lJCgaOwlRrPLlOPg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.3.0.tgz", + "integrity": "sha512-r3n0onD3BTOVUNPhR4lhVK4/pABGpbA7bW3eumZnYdKaHkf1qEC+Mag6DPbGNuuh0eG8AaYj+YqmVHSiGslaTQ==", "dev": true, "dependencies": { "@babel/generator": "7.17.7", @@ -6590,9 +6590,9 @@ } }, "node_modules/prettier": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", - "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz", + "integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -8571,9 +8571,9 @@ } }, "node_modules/wdio-wait-for": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/wdio-wait-for/-/wdio-wait-for-3.0.7.tgz", - "integrity": "sha512-NLxEg57+DAQvsEgsAcuF0zM2XDAQTfbKn2mN4nw9hDzz3RfgsZbCxvp93Nm/3609QuxpikC+MxgQ5ORLSoptvA==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/wdio-wait-for/-/wdio-wait-for-3.0.8.tgz", + "integrity": "sha512-Lptqzqso57sia7q6BRG2M+4S0YysXobcj9gchZxJBqYewgoH4e6Rime6i4WseIW85zmDMJu8pMSWNK4efong8A==", "dev": true, "engines": { "node": "^16.13 || >=18" diff --git a/tests/wdio/package.json b/tests/wdio/package.json index 0de4a622b..a5e1078fc 100644 --- a/tests/wdio/package.json +++ b/tests/wdio/package.json @@ -3,7 +3,7 @@ "private": true, "type": "module", "devDependencies": { - "@trivago/prettier-plugin-sort-imports": "^4.2.1", + "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@typescript-eslint/eslint-plugin": "^6.10.0", "@typescript-eslint/parser": "^6.10.0", "@wdio/cli": "^8.22.1", @@ -14,10 +14,10 @@ "eslint-config-google": "^0.14.0", "eslint-plugin-sonarjs": "^0.23.0", "npm-run-all": "^4.1.5", - "prettier": "^3.0.3", + "prettier": "^3.1.0", "ts-node": "^10.9.1", "typescript": "^5.2.2", - "wdio-wait-for": "^3.0.7" + "wdio-wait-for": "^3.0.8" }, "scripts": { "wdio": "wdio run ./wdio.conf.ts", diff --git a/web/package-lock.json b/web/package-lock.json index 08c618258..9f17f964e 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -15,17 +15,17 @@ "@codemirror/lang-xml": "^6.0.2", "@codemirror/legacy-modes": "^6.3.3", "@codemirror/theme-one-dark": "^6.1.2", - "@formatjs/intl-listformat": "^7.5.1", + "@formatjs/intl-listformat": "^7.5.2", "@fortawesome/fontawesome-free": "^6.4.2", - "@goauthentik/api": "^2023.10.3-1699554078", + "@goauthentik/api": "^2023.10.3-1699884123", "@lit-labs/context": "^0.4.0", "@lit-labs/task": "^3.1.0", "@lit/localize": "^0.11.4", "@open-wc/lit-helpers": "^0.6.0", "@patternfly/elements": "^2.4.0", "@patternfly/patternfly": "^4.224.2", - "@sentry/browser": "^7.79.0", - "@sentry/tracing": "^7.79.0", + "@sentry/browser": "^7.80.0", + "@sentry/tracing": "^7.80.0", "@webcomponents/webcomponentsjs": "^2.8.0", "base64-js": "^1.5.1", "chart.js": "^4.4.0", @@ -43,14 +43,14 @@ "yaml": "^2.3.4" }, "devDependencies": { - "@babel/core": "^7.23.2", + "@babel/core": "^7.23.3", "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-decorators": "^7.23.2", - "@babel/plugin-transform-private-methods": "^7.22.5", - "@babel/plugin-transform-private-property-in-object": "^7.22.11", - "@babel/plugin-transform-runtime": "^7.23.2", - "@babel/preset-env": "^7.23.2", - "@babel/preset-typescript": "^7.23.2", + "@babel/plugin-proposal-decorators": "^7.23.3", + "@babel/plugin-transform-private-methods": "^7.23.3", + "@babel/plugin-transform-private-property-in-object": "^7.23.3", + "@babel/plugin-transform-runtime": "^7.23.3", + "@babel/preset-env": "^7.23.3", + "@babel/preset-typescript": "^7.23.3", "@hcaptcha/types": "^1.0.3", "@jackfranklin/rollup-plugin-markdown": "^0.4.0", "@jeysal/storybook-addon-css-user-preferences": "^0.2.0", @@ -66,7 +66,7 @@ "@storybook/blocks": "^7.1.1", "@storybook/web-components": "^7.5.3", "@storybook/web-components-vite": "^7.5.3", - "@trivago/prettier-plugin-sort-imports": "^4.2.1", + "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/chart.js": "^2.9.40", "@types/codemirror": "5.60.13", "@types/grecaptcha": "^3.0.7", @@ -83,12 +83,12 @@ "eslint-plugin-storybook": "^0.6.15", "lit-analyzer": "^2.0.1", "npm-run-all": "^4.1.5", - "prettier": "^3.0.3", + "prettier": "^3.1.0", "pseudolocale": "^2.0.0", "pyright": "^1.1.335", "react": "^18.2.0", "react-dom": "^18.2.0", - "rollup": "^4.3.0", + "rollup": "^4.4.0", "rollup-plugin-copy": "^3.5.0", "rollup-plugin-cssimport": "^1.0.3", "rollup-plugin-postcss-lit": "^2.1.0", @@ -162,30 +162,30 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.2.tgz", - "integrity": "sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.3.tgz", + "integrity": "sha512-BmR4bWbDIoFJmJ9z2cZ8Gmm2MXgEDgjdWgpKmKWUt54UGFJdlj31ECtbaDvCG/qVdG3AQ1SfpZEs01lUFbzLOQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.2.tgz", - "integrity": "sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.3.tgz", + "integrity": "sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", + "@babel/generator": "^7.23.3", "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-module-transforms": "^7.23.0", + "@babel/helper-module-transforms": "^7.23.3", "@babel/helpers": "^7.23.2", - "@babel/parser": "^7.23.0", + "@babel/parser": "^7.23.3", "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0", + "@babel/traverse": "^7.23.3", + "@babel/types": "^7.23.3", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -200,6 +200,27 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/@babel/traverse": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.3.tgz", + "integrity": "sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.3", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.3", + "@babel/types": "^7.23.3", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/core/node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -207,12 +228,12 @@ "dev": true }, "node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.3.tgz", + "integrity": "sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg==", "dev": true, "dependencies": { - "@babel/types": "^7.23.0", + "@babel/types": "^7.23.3", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -376,9 +397,9 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", - "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", @@ -555,9 +576,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz", + "integrity": "sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -567,9 +588,9 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.15.tgz", - "integrity": "sha512-FB9iYlz7rURmRJyXRKEnalYPPdn87H5no108cyuQQyMwlpJ2SJtpIUBI27kdTin956pz+LPypkPVPUTlxOmrsg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz", + "integrity": "sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -582,14 +603,14 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.15.tgz", - "integrity": "sha512-Hyph9LseGvAeeXzikV88bczhsrLrIZqDPxO+sSmAunMPaGrBGhfMWzCPYTtiW9t+HzSE2wtV8e5cc5P6r1xMDQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz", + "integrity": "sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.22.15" + "@babel/plugin-transform-optional-chaining": "^7.23.3" }, "engines": { "node": ">=6.9.0" @@ -598,6 +619,22 @@ "@babel/core": "^7.13.0" } }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.3.tgz", + "integrity": "sha512-XaJak1qcityzrX0/IU5nKHb34VaibwP3saKqG6a/tppelgllOH13LUann4ZCIBcVOeE6H18K4Vx9QKkVww3z/w==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-proposal-class-properties": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", @@ -616,16 +653,16 @@ } }, "node_modules/@babel/plugin-proposal-decorators": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.23.2.tgz", - "integrity": "sha512-eR0gJQc830fJVGz37oKLvt9W9uUIQSAovUl0e9sJ3YeO09dlcoBVYD3CLrjCj4qHdXmfiyTyFt8yeQYSN5fxLg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.23.3.tgz", + "integrity": "sha512-u8SwzOcP0DYSsa++nHd/9exlHb0NAlHCb890qtZZbSwPX2bFv8LBEztxwN7Xg/dS8oAFFidhrI9PBcLBJSkGRQ==", "dev": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-replace-supers": "^7.22.20", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/plugin-syntax-decorators": "^7.22.10" + "@babel/plugin-syntax-decorators": "^7.23.3" }, "engines": { "node": ">=6.9.0" @@ -721,9 +758,9 @@ } }, "node_modules/@babel/plugin-syntax-decorators": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.22.10.tgz", - "integrity": "sha512-z1KTVemBjnz+kSEilAsI4lbkPOl5TvJH7YDSY1CTIzvLWJ+KHXp+mRe8VPmfnyvqOPqar1V2gid2PleKzRUstQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.23.3.tgz", + "integrity": "sha512-cf7Niq4/+/juY67E0PbgH0TDhLQ5J7zS8C/Q5FFx+DWyrRa9sUQdTXkjqKu8zGvuqr7vw1muKiukseihU+PJDA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -775,9 +812,9 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz", - "integrity": "sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz", + "integrity": "sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -790,9 +827,9 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz", - "integrity": "sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.23.3.tgz", + "integrity": "sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -829,9 +866,9 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", + "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -946,9 +983,9 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", + "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -977,9 +1014,9 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz", - "integrity": "sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz", + "integrity": "sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -992,9 +1029,9 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.2.tgz", - "integrity": "sha512-BBYVGxbDVHfoeXbOwcagAkOQAm9NxoTdMGfTqghu1GrvadSaw6iW3Je6IcL5PNOw8VwjxqBECXy50/iCQSY/lQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.3.tgz", + "integrity": "sha512-59GsVNavGxAXCDDbakWSMJhajASb4kBCqDjqJsv+p5nKdbz7istmZ3HrX3L2LuiI80+zsOADCvooqQH3qGCucQ==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", @@ -1010,14 +1047,14 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz", - "integrity": "sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz", + "integrity": "sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-module-imports": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.5" + "@babel/helper-remap-async-to-generator": "^7.22.20" }, "engines": { "node": ">=6.9.0" @@ -1027,9 +1064,9 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz", - "integrity": "sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz", + "integrity": "sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1042,9 +1079,9 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.0.tgz", - "integrity": "sha512-cOsrbmIOXmf+5YbL99/S49Y3j46k/T16b9ml8bm9lP6N9US5iQ2yBK7gpui1pg0V/WMcXdkfKbTb7HXq9u+v4g==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.3.tgz", + "integrity": "sha512-QPZxHrThbQia7UdvfpaRRlq/J9ciz1J4go0k+lPBXbgaNeY7IQrBj/9ceWjvMMI07/ZBzHl/F0R/2K0qH7jCVw==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1057,12 +1094,12 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz", - "integrity": "sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz", + "integrity": "sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { @@ -1073,12 +1110,12 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.11.tgz", - "integrity": "sha512-GMM8gGmqI7guS/llMFk1bJDkKfn3v3C4KHK9Yg1ey5qcHcOlKb0QvcMrgzvxo+T03/4szNh5lghY+fEC98Kq9g==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.3.tgz", + "integrity": "sha512-PENDVxdr7ZxKPyi5Ffc0LjXdnJyrJxyqF5T5YjlVg4a0VFfQHW0r8iAtRiDXkfHlu1wwcvdtnndGYIeJLSuRMQ==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.11", + "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-class-static-block": "^7.14.5" }, @@ -1090,18 +1127,18 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.15.tgz", - "integrity": "sha512-VbbC3PGjBdE0wAWDdHM9G8Gm977pnYI0XpqMd6LrKISj8/DJXEsWqgRuTYaNE9Bv0JGhTZUzHDlMk18IpOuoqw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.3.tgz", + "integrity": "sha512-FGEQmugvAEu2QtgtU0uTASXevfLMFfBeVCIIdcQhn/uBQsMTjBajdnAtanQlOcuihWh10PZ7+HWvc7NtBwP74w==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-optimise-call-expression": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-replace-supers": "^7.22.20", "@babel/helper-split-export-declaration": "^7.22.6", "globals": "^11.1.0" }, @@ -1113,13 +1150,13 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz", - "integrity": "sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz", + "integrity": "sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", - "@babel/template": "^7.22.5" + "@babel/template": "^7.22.15" }, "engines": { "node": ">=6.9.0" @@ -1129,9 +1166,9 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.0.tgz", - "integrity": "sha512-vaMdgNXFkYrB+8lbgniSYWHsgqK5gjaMNcc84bMIOMRLH0L9AqYq3hwMdvnyqj1OPqea8UtjPEuS/DCenah1wg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz", + "integrity": "sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1144,12 +1181,12 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz", - "integrity": "sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz", + "integrity": "sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-create-regexp-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { @@ -1160,9 +1197,9 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz", - "integrity": "sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz", + "integrity": "sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1175,9 +1212,9 @@ } }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.11.tgz", - "integrity": "sha512-g/21plo58sfteWjaO0ZNVb+uEOkJNjAaHhbejrnBmu011l/eNDScmkbjCC3l4FKb10ViaGU4aOkFznSu2zRHgA==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.3.tgz", + "integrity": "sha512-vTG+cTGxPFou12Rj7ll+eD5yWeNl5/8xvQvF08y5Gv3v4mZQoyFf8/n9zg4q5vvCWt5jmgymfzMAldO7orBn7A==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -1191,12 +1228,12 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz", - "integrity": "sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz", + "integrity": "sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==", "dev": true, "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.5", + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { @@ -1207,9 +1244,9 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.11.tgz", - "integrity": "sha512-xa7aad7q7OiT8oNZ1mU7NrISjlSkVdMbNxn9IuLZyL9AJEhs1Apba3I+u5riX1dIkdptP5EKDG5XDPByWxtehw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.3.tgz", + "integrity": "sha512-yCLhW34wpJWRdTxxWtFZASJisihrfyMOTOQexhVzA78jlU+dH7Dw+zQgcPepQ5F3C6bAIiblZZ+qBggJdHiBAg==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -1239,9 +1276,9 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.15.tgz", - "integrity": "sha512-me6VGeHsx30+xh9fbDLLPi0J1HzmeIIyenoOQHuw2D4m2SAU3NrspX5XxJLBpqn5yrLzrlw2Iy3RA//Bx27iOA==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.3.tgz", + "integrity": "sha512-X8jSm8X1CMwxmK878qsUGJRmbysKNbdpTv/O1/v0LuY/ZkZrng5WYiekYSdg9m09OTmDDUWeEDsTE+17WYbAZw==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1254,13 +1291,13 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz", - "integrity": "sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz", + "integrity": "sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==", "dev": true, "dependencies": { - "@babel/helper-compilation-targets": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { @@ -1271,9 +1308,9 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.11.tgz", - "integrity": "sha512-CxT5tCqpA9/jXFlme9xIBCc5RPtdDq3JpkkhgHQqtDdiTnTI0jtZ0QzXhr5DILeYifDPp2wvY2ad+7+hLMW5Pw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.23.3.tgz", + "integrity": "sha512-H9Ej2OiISIZowZHaBwF0tsJOih1PftXJtE8EWqlEIwpc7LMTGq0rPOrywKLQ4nefzx8/HMR0D3JGXoMHYvhi0A==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -1287,9 +1324,9 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz", - "integrity": "sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz", + "integrity": "sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1302,9 +1339,9 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.11.tgz", - "integrity": "sha512-qQwRTP4+6xFCDV5k7gZBF3C31K34ut0tbEcTKxlX/0KXxm9GLcO14p570aWxFvVzx6QAfPgq7gaeIHXJC8LswQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.3.tgz", + "integrity": "sha512-+pD5ZbxofyOygEp+zZAfujY2ShNCXRpDRIPOiBmTO693hhyOEteZgl876Xs9SAHPQpcV0vz8LvA/T+w8AzyX8A==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -1318,9 +1355,9 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz", - "integrity": "sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz", + "integrity": "sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1333,12 +1370,12 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.0.tgz", - "integrity": "sha512-xWT5gefv2HGSm4QHtgc1sYPbseOyf+FFDo2JbpE25GWl5BqTGO9IMwTYJRoIdjsF85GE+VegHxSCUt5EvoYTAw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz", + "integrity": "sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.23.0", + "@babel/helper-module-transforms": "^7.23.3", "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { @@ -1349,12 +1386,12 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.0.tgz", - "integrity": "sha512-32Xzss14/UVc7k9g775yMIvkVK8xwKE0DPdP5JTapr3+Z9w4tzeOuLNY6BXDQR6BdnzIlXnCGAzsk/ICHBLVWQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz", + "integrity": "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.23.0", + "@babel/helper-module-transforms": "^7.23.3", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-simple-access": "^7.22.5" }, @@ -1366,13 +1403,13 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.0.tgz", - "integrity": "sha512-qBej6ctXZD2f+DhlOC9yO47yEYgUh5CZNz/aBoH4j/3NOlRfJXJbY7xDQCqQVf9KbrqGzIWER1f23doHGrIHFg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.3.tgz", + "integrity": "sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ==", "dev": true, "dependencies": { "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-module-transforms": "^7.23.0", + "@babel/helper-module-transforms": "^7.23.3", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-validator-identifier": "^7.22.20" }, @@ -1384,12 +1421,12 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz", - "integrity": "sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz", + "integrity": "sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-module-transforms": "^7.23.3", "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { @@ -1416,9 +1453,9 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz", - "integrity": "sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz", + "integrity": "sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1431,9 +1468,9 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.11.tgz", - "integrity": "sha512-YZWOw4HxXrotb5xsjMJUDlLgcDXSfO9eCmdl1bgW4+/lAGdkjaEvOnQ4p5WKKdUgSzO39dgPl0pTnfxm0OAXcg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.3.tgz", + "integrity": "sha512-xzg24Lnld4DYIdysyf07zJ1P+iIfJpxtVFOzX4g+bsJ3Ng5Le7rXx9KwqKzuyaUeRnt+I1EICwQITqc0E2PmpA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -1447,9 +1484,9 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.11.tgz", - "integrity": "sha512-3dzU4QGPsILdJbASKhF/V2TVP+gJya1PsueQCxIPCEcerqF21oEcrob4mzjsp2Py/1nLfF5m+xYNMDpmA8vffg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.3.tgz", + "integrity": "sha512-s9GO7fIBi/BLsZ0v3Rftr6Oe4t0ctJ8h4CCXfPoEJwmvAPMyNrfkOOJzm6b9PX9YXcCJWWQd/sBF/N26eBiMVw==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -1463,16 +1500,16 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.15.tgz", - "integrity": "sha512-fEB+I1+gAmfAyxZcX1+ZUwLeAuuf8VIg67CTznZE0MqVFumWkh8xWtn58I4dxdVf080wn7gzWoF8vndOViJe9Q==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.3.tgz", + "integrity": "sha512-VxHt0ANkDmu8TANdE9Kc0rndo/ccsmfe2Cx2y5sI4hu3AukHQ5wAu4cM7j3ba8B9548ijVyclBU+nuDQftZsog==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.22.9", + "@babel/compat-data": "^7.23.3", "@babel/helper-compilation-targets": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.22.15" + "@babel/plugin-transform-parameters": "^7.23.3" }, "engines": { "node": ">=6.9.0" @@ -1482,13 +1519,13 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz", - "integrity": "sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz", + "integrity": "sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.5" + "@babel/helper-replace-supers": "^7.22.20" }, "engines": { "node": ">=6.9.0" @@ -1498,9 +1535,9 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.11.tgz", - "integrity": "sha512-rli0WxesXUeCJnMYhzAglEjLWVDF6ahb45HuprcmQuLidBJFWjNnOzssk2kuc6e33FlLaiZhG/kUIzUMWdBKaQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.23.3.tgz", + "integrity": "sha512-LxYSb0iLjUamfm7f1D7GpiS4j0UAC8AOiehnsGAP8BEsIX8EOi3qV6bbctw8M7ZvLtcoZfZX5Z7rN9PlWk0m5A==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -1514,9 +1551,9 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.0.tgz", - "integrity": "sha512-sBBGXbLJjxTzLBF5rFWaikMnOGOk/BmK6vVByIdEggZ7Vn6CvWXZyRkkLFK6WE0IF8jSliyOkUN6SScFgzCM0g==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.3.tgz", + "integrity": "sha512-zvL8vIfIUgMccIAK1lxjvNv572JHFJIKb4MWBz5OGdBQA0fB0Xluix5rmOby48exiJc987neOmP/m9Fnpkz3Tg==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -1531,9 +1568,9 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.15.tgz", - "integrity": "sha512-hjk7qKIqhyzhhUvRT683TYQOFa/4cQKwQy7ALvTpODswN40MljzNDa0YldevS6tGbxwaEKVn502JmY0dP7qEtQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz", + "integrity": "sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1546,12 +1583,12 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz", - "integrity": "sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz", + "integrity": "sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { @@ -1562,13 +1599,13 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.11.tgz", - "integrity": "sha512-sSCbqZDBKHetvjSwpyWzhuHkmW5RummxJBVbYLkGkaiTOWGxml7SXt0iWa03bzxFIx7wOj3g/ILRd0RcJKBeSQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.3.tgz", + "integrity": "sha512-a5m2oLNFyje2e/rGKjVfAELTVI5mbA0FeZpBnkOWWV7eSmKQ+T/XW0Vf+29ScLzSxX+rnsarvU0oie/4m6hkxA==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.11", + "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" }, @@ -1580,9 +1617,9 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz", - "integrity": "sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz", + "integrity": "sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1595,9 +1632,9 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.10.tgz", - "integrity": "sha512-F28b1mDt8KcT5bUyJc/U9nwzw6cV+UmTeRlXYIl2TNqMMJif0Jeey9/RQ3C4NOd2zp0/TRsDns9ttj2L523rsw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz", + "integrity": "sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -1611,9 +1648,9 @@ } }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz", - "integrity": "sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz", + "integrity": "sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1626,9 +1663,9 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.2.tgz", - "integrity": "sha512-XOntj6icgzMS58jPVtQpiuF6ZFWxQiJavISGx5KGjRj+3gqZr8+N6Kx+N9BApWzgS+DOjIZfXXj0ZesenOWDyA==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.3.tgz", + "integrity": "sha512-XcQ3X58CKBdBnnZpPaQjgVMePsXtSZzHoku70q9tUAQp02ggPQNM04BF3RvlW1GSM/McbSOQAzEK4MXbS7/JFg==", "dev": true, "dependencies": { "@babel/helper-module-imports": "^7.22.15", @@ -1646,9 +1683,9 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz", - "integrity": "sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz", + "integrity": "sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1661,9 +1698,9 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz", - "integrity": "sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz", + "integrity": "sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -1677,9 +1714,9 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz", - "integrity": "sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz", + "integrity": "sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1692,9 +1729,9 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz", - "integrity": "sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz", + "integrity": "sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1707,9 +1744,9 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz", - "integrity": "sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz", + "integrity": "sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1722,15 +1759,15 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.15.tgz", - "integrity": "sha512-1uirS0TnijxvQLnlv5wQBwOX3E1wCFX7ITv+9pBV2wKEk4K+M5tqDaoNXnTH8tjEIYHLO98MwiTWO04Ggz4XuA==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.23.3.tgz", + "integrity": "sha512-ogV0yWnq38CFwH20l2Afz0dfKuZBx9o/Y2Rmh5vuSS0YD1hswgEgTfyTzuSrT2q9btmHRSqYoSfwFUVaC1M1Jw==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-typescript": "^7.22.5" + "@babel/plugin-syntax-typescript": "^7.23.3" }, "engines": { "node": ">=6.9.0" @@ -1740,9 +1777,9 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.10.tgz", - "integrity": "sha512-lRfaRKGZCBqDlRU3UIFovdp9c9mEvlylmpod0/OatICsSfuQ9YFthRo1tpTkGsklEefZdqlEFdY4A2dwTb6ohg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz", + "integrity": "sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1755,12 +1792,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz", - "integrity": "sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.23.3.tgz", + "integrity": "sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-create-regexp-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { @@ -1771,12 +1808,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz", - "integrity": "sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz", + "integrity": "sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-create-regexp-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { @@ -1787,12 +1824,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz", - "integrity": "sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.23.3.tgz", + "integrity": "sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-create-regexp-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { @@ -1803,25 +1840,26 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.2.tgz", - "integrity": "sha512-BW3gsuDD+rvHL2VO2SjAUNTBe5YrjsTiDyqamPDWY723na3/yPQ65X5oQkFVJZ0o50/2d+svm1rkPoJeR1KxVQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.3.tgz", + "integrity": "sha512-ovzGc2uuyNfNAs/jyjIGxS8arOHS5FENZaNn4rtE7UdKMMkqHCvboHfcuhWLZNX5cB44QfcGNWjaevxMzzMf+Q==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.23.2", + "@babel/compat-data": "^7.23.3", "@babel/helper-compilation-targets": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-validator-option": "^7.22.15", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.15", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.15", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.3", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.22.5", - "@babel/plugin-syntax-import-attributes": "^7.22.5", + "@babel/plugin-syntax-import-assertions": "^7.23.3", + "@babel/plugin-syntax-import-attributes": "^7.23.3", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", @@ -1833,56 +1871,55 @@ "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.22.5", - "@babel/plugin-transform-async-generator-functions": "^7.23.2", - "@babel/plugin-transform-async-to-generator": "^7.22.5", - "@babel/plugin-transform-block-scoped-functions": "^7.22.5", - "@babel/plugin-transform-block-scoping": "^7.23.0", - "@babel/plugin-transform-class-properties": "^7.22.5", - "@babel/plugin-transform-class-static-block": "^7.22.11", - "@babel/plugin-transform-classes": "^7.22.15", - "@babel/plugin-transform-computed-properties": "^7.22.5", - "@babel/plugin-transform-destructuring": "^7.23.0", - "@babel/plugin-transform-dotall-regex": "^7.22.5", - "@babel/plugin-transform-duplicate-keys": "^7.22.5", - "@babel/plugin-transform-dynamic-import": "^7.22.11", - "@babel/plugin-transform-exponentiation-operator": "^7.22.5", - "@babel/plugin-transform-export-namespace-from": "^7.22.11", - "@babel/plugin-transform-for-of": "^7.22.15", - "@babel/plugin-transform-function-name": "^7.22.5", - "@babel/plugin-transform-json-strings": "^7.22.11", - "@babel/plugin-transform-literals": "^7.22.5", - "@babel/plugin-transform-logical-assignment-operators": "^7.22.11", - "@babel/plugin-transform-member-expression-literals": "^7.22.5", - "@babel/plugin-transform-modules-amd": "^7.23.0", - "@babel/plugin-transform-modules-commonjs": "^7.23.0", - "@babel/plugin-transform-modules-systemjs": "^7.23.0", - "@babel/plugin-transform-modules-umd": "^7.22.5", + "@babel/plugin-transform-arrow-functions": "^7.23.3", + "@babel/plugin-transform-async-generator-functions": "^7.23.3", + "@babel/plugin-transform-async-to-generator": "^7.23.3", + "@babel/plugin-transform-block-scoped-functions": "^7.23.3", + "@babel/plugin-transform-block-scoping": "^7.23.3", + "@babel/plugin-transform-class-properties": "^7.23.3", + "@babel/plugin-transform-class-static-block": "^7.23.3", + "@babel/plugin-transform-classes": "^7.23.3", + "@babel/plugin-transform-computed-properties": "^7.23.3", + "@babel/plugin-transform-destructuring": "^7.23.3", + "@babel/plugin-transform-dotall-regex": "^7.23.3", + "@babel/plugin-transform-duplicate-keys": "^7.23.3", + "@babel/plugin-transform-dynamic-import": "^7.23.3", + "@babel/plugin-transform-exponentiation-operator": "^7.23.3", + "@babel/plugin-transform-export-namespace-from": "^7.23.3", + "@babel/plugin-transform-for-of": "^7.23.3", + "@babel/plugin-transform-function-name": "^7.23.3", + "@babel/plugin-transform-json-strings": "^7.23.3", + "@babel/plugin-transform-literals": "^7.23.3", + "@babel/plugin-transform-logical-assignment-operators": "^7.23.3", + "@babel/plugin-transform-member-expression-literals": "^7.23.3", + "@babel/plugin-transform-modules-amd": "^7.23.3", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-modules-systemjs": "^7.23.3", + "@babel/plugin-transform-modules-umd": "^7.23.3", "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", - "@babel/plugin-transform-new-target": "^7.22.5", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.11", - "@babel/plugin-transform-numeric-separator": "^7.22.11", - "@babel/plugin-transform-object-rest-spread": "^7.22.15", - "@babel/plugin-transform-object-super": "^7.22.5", - "@babel/plugin-transform-optional-catch-binding": "^7.22.11", - "@babel/plugin-transform-optional-chaining": "^7.23.0", - "@babel/plugin-transform-parameters": "^7.22.15", - "@babel/plugin-transform-private-methods": "^7.22.5", - "@babel/plugin-transform-private-property-in-object": "^7.22.11", - "@babel/plugin-transform-property-literals": "^7.22.5", - "@babel/plugin-transform-regenerator": "^7.22.10", - "@babel/plugin-transform-reserved-words": "^7.22.5", - "@babel/plugin-transform-shorthand-properties": "^7.22.5", - "@babel/plugin-transform-spread": "^7.22.5", - "@babel/plugin-transform-sticky-regex": "^7.22.5", - "@babel/plugin-transform-template-literals": "^7.22.5", - "@babel/plugin-transform-typeof-symbol": "^7.22.5", - "@babel/plugin-transform-unicode-escapes": "^7.22.10", - "@babel/plugin-transform-unicode-property-regex": "^7.22.5", - "@babel/plugin-transform-unicode-regex": "^7.22.5", - "@babel/plugin-transform-unicode-sets-regex": "^7.22.5", + "@babel/plugin-transform-new-target": "^7.23.3", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.3", + "@babel/plugin-transform-numeric-separator": "^7.23.3", + "@babel/plugin-transform-object-rest-spread": "^7.23.3", + "@babel/plugin-transform-object-super": "^7.23.3", + "@babel/plugin-transform-optional-catch-binding": "^7.23.3", + "@babel/plugin-transform-optional-chaining": "^7.23.3", + "@babel/plugin-transform-parameters": "^7.23.3", + "@babel/plugin-transform-private-methods": "^7.23.3", + "@babel/plugin-transform-private-property-in-object": "^7.23.3", + "@babel/plugin-transform-property-literals": "^7.23.3", + "@babel/plugin-transform-regenerator": "^7.23.3", + "@babel/plugin-transform-reserved-words": "^7.23.3", + "@babel/plugin-transform-shorthand-properties": "^7.23.3", + "@babel/plugin-transform-spread": "^7.23.3", + "@babel/plugin-transform-sticky-regex": "^7.23.3", + "@babel/plugin-transform-template-literals": "^7.23.3", + "@babel/plugin-transform-typeof-symbol": "^7.23.3", + "@babel/plugin-transform-unicode-escapes": "^7.23.3", + "@babel/plugin-transform-unicode-property-regex": "^7.23.3", + "@babel/plugin-transform-unicode-regex": "^7.23.3", + "@babel/plugin-transform-unicode-sets-regex": "^7.23.3", "@babel/preset-modules": "0.1.6-no-external-plugins", - "@babel/types": "^7.23.0", "babel-plugin-polyfill-corejs2": "^0.4.6", "babel-plugin-polyfill-corejs3": "^0.8.5", "babel-plugin-polyfill-regenerator": "^0.5.3", @@ -1928,16 +1965,16 @@ } }, "node_modules/@babel/preset-typescript": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.23.2.tgz", - "integrity": "sha512-u4UJc1XsS1GhIGteM8rnGiIvf9rJpiVgMEeCnwlLA7WJPC+jcXWJAGxYmeqs5hOZD8BbAfnV5ezBOxQbb4OUxA==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.23.3.tgz", + "integrity": "sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-validator-option": "^7.22.15", - "@babel/plugin-syntax-jsx": "^7.22.5", - "@babel/plugin-transform-modules-commonjs": "^7.23.0", - "@babel/plugin-transform-typescript": "^7.22.15" + "@babel/plugin-syntax-jsx": "^7.23.3", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-typescript": "^7.23.3" }, "engines": { "node": ">=6.9.0" @@ -2149,9 +2186,9 @@ } }, "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.3.tgz", + "integrity": "sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.22.5", @@ -2847,28 +2884,28 @@ "integrity": "sha512-ou3elfqG/hZsbmF4bxeJhPHIf3G2pm0ujc39hYEZrfVqt7Vk/Zji6CXc3W0pmYM8BW1g40U+akTl9DKZhFhInQ==" }, "node_modules/@formatjs/ecma402-abstract": { - "version": "1.17.3", - "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.17.3.tgz", - "integrity": "sha512-2Q4hmKQ6CM30mRG/YMdSBW8LXf32BfuOb1FZgG+uVWPC/SQMoiVFz5JaeOukt96v6TZ4ddE+bHCmd611PW38QA==", + "version": "1.17.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.17.4.tgz", + "integrity": "sha512-mLxTq+UrMoOoafVxG5HdmrfPpjv0uINQw1d0ADKfQMqhDaR4SDXdw3olXkOP05DGSAsVvSxzfKEuVKcLB15b9A==", "dependencies": { - "@formatjs/intl-localematcher": "0.5.0", + "@formatjs/intl-localematcher": "0.5.1", "tslib": "^2.4.0" } }, "node_modules/@formatjs/intl-listformat": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-7.5.1.tgz", - "integrity": "sha512-J6heE28cikJ2cUZ7Mvmets418lE96k5OcbgDYQkXDFHVbRnUKCHQwRAXKRCPFEGDrEmcvtqUj7NmZEJYbsJdqQ==", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-7.5.2.tgz", + "integrity": "sha512-1jIZ+tnJ2nLNBZkDl9jUnB2sf87faU082ICc+MHfNXzalEacYEeOKzfFoJhfGCG2nzc+MhaSL1AMalDlc8MSvQ==", "dependencies": { - "@formatjs/ecma402-abstract": "1.17.3", - "@formatjs/intl-localematcher": "0.5.0", + "@formatjs/ecma402-abstract": "1.17.4", + "@formatjs/intl-localematcher": "0.5.1", "tslib": "^2.4.0" } }, "node_modules/@formatjs/intl-localematcher": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.0.tgz", - "integrity": "sha512-K1Xpg/8oyfCMxisJQa/fILoeoeyndcM0wcN8QiNG/uM5OAe1BcO1+2yd0gIboDI2tRJEsUi/sSBEYPbgkIdq4A==", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.1.tgz", + "integrity": "sha512-An6EtxT0txJI8dQwOmLqs4lC3/+Dl9YuTGDWdxxtAT/9sx0r9DupnC3/k6F87Tb5gqXdR/ACdB7bmR+Zo2eu+g==", "dependencies": { "tslib": "^2.4.0" } @@ -2883,9 +2920,9 @@ } }, "node_modules/@goauthentik/api": { - "version": "2023.10.3-1699554078", - "resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2023.10.3-1699554078.tgz", - "integrity": "sha512-tZIKpb+uLAPKH+FQw1sq/TCjB36dpeQa5SrB40gf44K9C//OfGP38LQCi7s8Q3efOAo01Q9SRsL+WmZfgqDZBQ==" + "version": "2023.10.3-1699884123", + "resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2023.10.3-1699884123.tgz", + "integrity": "sha512-Fp1ESaq6Og9u9xu/MtvkuaGURbrfmDpLcgGXgJn+PxzIhrzLJc+Z6sz+FJFUvUzxB1470Czz2RaEYHXc6goKaQ==" }, "node_modules/@hcaptcha/types": { "version": "1.0.3", @@ -4539,9 +4576,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.3.0.tgz", - "integrity": "sha512-/4pns6BYi8MXdwnXM44yoGAcFYVHL/BYlB2q1HXZ6AzH++LaiEVWFpBWQ/glXhbMbv3E3o09igrHFbP/snhAvA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.4.0.tgz", + "integrity": "sha512-AD30wtT58hZZsXIeiksytR6Gm2gofUxn5KqrDBdyzekgxXB9bXN9dqWIEcPfYo9lA9MVRm0lC42LuYGsscRxiA==", "cpu": [ "arm" ], @@ -4552,9 +4589,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.3.0.tgz", - "integrity": "sha512-nLO/JsL9idr416vzi3lHm3Xm+QZh4qHij8k3Er13kZr5YhL7/+kBAx84kDmPc7HMexLmwisjDCeDIKNFp8mDlQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.4.0.tgz", + "integrity": "sha512-PlqvhzFxy5FRTB3wLSsGgPhiakv9jrgfu8tjSojLJFP0CdhfZSRDOFvQ2emWLUEBOSCnjpL63XSuFVMwg59ZtA==", "cpu": [ "arm64" ], @@ -4565,9 +4602,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.3.0.tgz", - "integrity": "sha512-dGhVBlllt4iHwTGy21IEoMOTN5wZoid19zEIxsdY29xcEiOEHqzDa7Sqrkh5OE7LKCowL61eFJXxYe/+pYa7ZQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.4.0.tgz", + "integrity": "sha512-BYmhn1Hebmkmdyn5mBFy7HptowyjtMALyTpywNSNZYigWwyv4L8WQVr0XvOQE7eE6WoKrupSVxtIcGZW8MgZUA==", "cpu": [ "arm64" ], @@ -4578,9 +4615,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.3.0.tgz", - "integrity": "sha512-h8wRfHeLEbU3NzaP1Oku7BYXCJQiTRr+8U0lklyOQXxXiEpHLL8tk1hFl+tezoRKLcPJD7joKaK74ASsqt3Ekg==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.4.0.tgz", + "integrity": "sha512-7GXsMiX/giTDBMs/gL3rePLBRC6gV7DT7JQ0lNqoNDe5hm+Gm4NEWky9fwEmer64fIUbOsTiLUsyQ5fDXUbXPA==", "cpu": [ "x64" ], @@ -4591,9 +4628,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.3.0.tgz", - "integrity": "sha512-wP4VgR/gfV18sylTuym3sxRTkAgUR2vh6YLeX/GEznk5jCYcYSlx585XlcUcl0c8UffIZlRJ09raWSX3JDb4GA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.4.0.tgz", + "integrity": "sha512-kavnkaV50Gu6vESlOAwUad92wYY9mUrcaPmhzOQZKlNFnzWAUYyD/uhHmWvY7Z2chtwhWlng0LvCRBF5QiPO7w==", "cpu": [ "arm" ], @@ -4604,9 +4641,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.3.0.tgz", - "integrity": "sha512-v/14JCYVkqRSJeQbxFx4oUkwVQQw6lFMN7bd4vuARBc3X2lmomkxBsc+BFiIDL/BK+CTx5AOh/k9XmqDnKWRVg==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.4.0.tgz", + "integrity": "sha512-2hBHEtCjnBTeuLvDAlHRCqsuFQSyAhTQs9vbZEVBTV8ap35pDI1ukPbIVFFCWNvL/KE7xRor5YZFvfyGCfvLnA==", "cpu": [ "arm64" ], @@ -4617,9 +4654,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.3.0.tgz", - "integrity": "sha512-tNhfYqFH5OxtRzfkTOKdgFYlPSZnlDLNW4+leNEvQZhwTJxoTwsZAAhR97l3qVry/kkLyJPBK+Q8EAJLPinDIg==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.4.0.tgz", + "integrity": "sha512-u7zy0Ygzl7O5Gvr9TSNSQj+DBzvMJC7rXfyQNgZ13KwkhgJ8z0z+gt2AO4RPd01rZioMQ2/TA24XGGg4xqhd0Q==", "cpu": [ "arm64" ], @@ -4630,9 +4667,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.3.0.tgz", - "integrity": "sha512-pw77m8QywdsoFdFOgmc8roF1inBI0rciqzO8ffRUgLoq7+ee9o5eFqtEcS6hHOOplgifAUUisP8cAnwl9nUYPw==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.4.0.tgz", + "integrity": "sha512-VvpAdh5SgewmWo8sa5QPYG8aSKH9hU2Kr5+3of0GzBI/8n8PBqhLyvF0DbO+zDW8j5IM8NDebv82MpHrZaD0Cw==", "cpu": [ "x64" ], @@ -4643,9 +4680,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.3.0.tgz", - "integrity": "sha512-tJs7v2MnV2F8w6X1UpPHl/43OfxjUy9SuJ2ZPoxn79v9vYteChVYO/ueLHCpRMmyTUIVML3N9z4azl9ENH8Xxg==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.4.0.tgz", + "integrity": "sha512-3g6jaXxXVFaDnFoMn2+E3ludGcXFfEr6lDn+S1lh9Qe0JcL9sPt1wGh0g2cKIlb6OakNOFopZqJ5Yub9F7gQlA==", "cpu": [ "x64" ], @@ -4656,9 +4693,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.3.0.tgz", - "integrity": "sha512-OKGxp6kATQdTyI2DF+e9s+hB3/QZB45b6e+dzcfW1SUqiF6CviWyevhmT4USsMEdP3mlpC9zxLz3Oh+WaTMOSw==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.4.0.tgz", + "integrity": "sha512-jnoDRkg5Ve6Y1qx2m1+ehouOLQ4ddc15/iQSfFjcDUL6bqLdJJ5c4CKfUy/C6W1oCU4la+hMkveE9GG7ECN7dg==", "cpu": [ "arm64" ], @@ -4669,9 +4706,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.3.0.tgz", - "integrity": "sha512-DDZ5AH68JJ2ClQFEA1aNnfA7Ybqyeh0644rGbrLOdNehTmzfICHiWSn0OprzYi9HAshTPQvlwrM+bi2kuaIOjQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.4.0.tgz", + "integrity": "sha512-SoLQmJanozFow8o50ul2a3R+J7nk4pEhrp83PzTSXs5OzOmIZbPSp5kihtQ3f6ypo4MCbmh0V8Ev0bJIEp4Azw==", "cpu": [ "ia32" ], @@ -4682,9 +4719,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.3.0.tgz", - "integrity": "sha512-dMvGV8p92GQ8jhNlGIKpyhVZPzJlT258pPrM5q2F8lKcc9Iv9BbfdnhX1OfinYWnb9ms5zLw6MlaMnqLfUkKnQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.4.0.tgz", + "integrity": "sha512-Zaz6itfQ5sQF5Cia49YDW1ZTr+YfIKzTSb9npLyvQn346n7ulRDOv2J7GnL0zcOJ3cqW7HzG/ZisyO6fH43J9g==", "cpu": [ "x64" ], @@ -4695,84 +4732,84 @@ ] }, "node_modules/@sentry-internal/tracing": { - "version": "7.79.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.79.0.tgz", - "integrity": "sha512-Mf9Bd0OrZ24h1qZpvmz9IRnfORMGYNYC1xWBBFpIR1AauEDX89x+mJwIOrUc4KKAAAwt73shrJv1QA8QOm4E3g==", + "version": "7.80.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.80.0.tgz", + "integrity": "sha512-P1Ab9gamHLsbH9D82i1HY8xfq9dP8runvc4g50AAd6OXRKaJ45f2KGRZUmnMEVqBQ7YoPYp2LFMkrhNYbcZEoQ==", "dependencies": { - "@sentry/core": "7.79.0", - "@sentry/types": "7.79.0", - "@sentry/utils": "7.79.0" + "@sentry/core": "7.80.0", + "@sentry/types": "7.80.0", + "@sentry/utils": "7.80.0" }, "engines": { "node": ">=8" } }, "node_modules/@sentry/browser": { - "version": "7.79.0", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.79.0.tgz", - "integrity": "sha512-gWbWEElF61uZeTFLIZz3NMyCkAzBDOpMAogEbVu2GX91SHKB7GXlE//INnS/R5wfE5j/CFaZc53mzzoIuMy1sA==", + "version": "7.80.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.80.0.tgz", + "integrity": "sha512-Ngwjc+yyf/aH5q7iQM1LeDNlhM1Ilt4ZLUogTghZR/guwNWmCtk3OHcjOLz7fxBBj9wGFUc2pHPyeYM6bQhrEw==", "dependencies": { - "@sentry-internal/tracing": "7.79.0", - "@sentry/core": "7.79.0", - "@sentry/replay": "7.79.0", - "@sentry/types": "7.79.0", - "@sentry/utils": "7.79.0" + "@sentry-internal/tracing": "7.80.0", + "@sentry/core": "7.80.0", + "@sentry/replay": "7.80.0", + "@sentry/types": "7.80.0", + "@sentry/utils": "7.80.0" }, "engines": { "node": ">=8" } }, "node_modules/@sentry/core": { - "version": "7.79.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.79.0.tgz", - "integrity": "sha512-9vG7SfOcJNJNiqlqg4MuHDUCaSf2ZXpv3eZYRPbBkgPGr8X1ekrSABpOK+6kBNvbtKxfWVTWbLpAA6xU+cwnVw==", + "version": "7.80.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.80.0.tgz", + "integrity": "sha512-nJiiymdTSEyI035/rdD3VOq6FlOZ2wWLR5bit9LK8a3rzHU3UXkwScvEo6zYgs0Xp1sC0yu1S9+0BEiYkmi29A==", "dependencies": { - "@sentry/types": "7.79.0", - "@sentry/utils": "7.79.0" + "@sentry/types": "7.80.0", + "@sentry/utils": "7.80.0" }, "engines": { "node": ">=8" } }, "node_modules/@sentry/replay": { - "version": "7.79.0", - "resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.79.0.tgz", - "integrity": "sha512-vF79NxWGYfoD0hnIkdgUQqedoMcRHHp5UAfZlxhpQzJf4TnbOjollp63AvOrfd38osSG2d3E5kTUU9xs/zKhBQ==", + "version": "7.80.0", + "resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.80.0.tgz", + "integrity": "sha512-wWnpuJq3OaDLp1LutE4oxWXnau04fvwuzBjuaFvOXOV+pB/kn+pDPuVOC5+FH/RMRZ5ftwX5+dF6fojfcLVGCg==", "dependencies": { - "@sentry-internal/tracing": "7.79.0", - "@sentry/core": "7.79.0", - "@sentry/types": "7.79.0", - "@sentry/utils": "7.79.0" + "@sentry-internal/tracing": "7.80.0", + "@sentry/core": "7.80.0", + "@sentry/types": "7.80.0", + "@sentry/utils": "7.80.0" }, "engines": { "node": ">=12" } }, "node_modules/@sentry/tracing": { - "version": "7.79.0", - "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.79.0.tgz", - "integrity": "sha512-Y1awpIcMKCsTpC3wS+SaEJTcOMd6OugZZXCV2r9ozBIRKE7wG//ntDX3ApcRf4kbDzQMJY2Ylu0QKDIyNEAr0g==", + "version": "7.80.0", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.80.0.tgz", + "integrity": "sha512-y9zBVMpCgY5Y6dBZrnKKHf6K9YWjGo3S35tPwDV1mQLml64bi6bNr6Fc6OBzXyrl9OTJAO71A1Z7DlAu6BQY9w==", "dependencies": { - "@sentry-internal/tracing": "7.79.0" + "@sentry-internal/tracing": "7.80.0" }, "engines": { "node": ">=8" } }, "node_modules/@sentry/types": { - "version": "7.79.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.79.0.tgz", - "integrity": "sha512-3tV32+v/DF8w7kD0p3kLWtgVTVdFL39oGY02+vz//rjWg/vzeqSE95mCYKm5pUfd6cPETX/8dunCiuTBQIkTHQ==", + "version": "7.80.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.80.0.tgz", + "integrity": "sha512-4bpMO+2jWiWLDa8zbTASWWNLWe6yhjfPsa7/6VH5y9x1NGtL8oRbqUsTgsvjF3nmeHEMkHQsC8NHPaQ/ibFmZQ==", "engines": { "node": ">=8" } }, "node_modules/@sentry/utils": { - "version": "7.79.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.79.0.tgz", - "integrity": "sha512-tUTlb6PvfZawqBmBK9CPXflqrZDXHKWoX3fve2zLK6W0FSpIMOO4TH8PBqkHBFs0ZgF/bnv/bsM4z7uEAlAtzg==", + "version": "7.80.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.80.0.tgz", + "integrity": "sha512-XbBCEl6uLvE50ftKwrEo6XWdDaZXHXu+kkHXTPWQEcnbvfZKLuG9V0Hxtxxq3xQgyWmuF05OH1GcqYqiO+v5Yg==", "dependencies": { - "@sentry/types": "7.79.0" + "@sentry/types": "7.80.0" }, "engines": { "node": ">=8" @@ -9957,9 +9994,9 @@ } }, "node_modules/@trivago/prettier-plugin-sort-imports": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.2.1.tgz", - "integrity": "sha512-iuy2MPVURGdxILTchHr15VAioItuYBejKfcTmQFlxIuqA7jeaT6ngr5aUIG6S6U096d6a6lJCgaOwlRrPLlOPg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.3.0.tgz", + "integrity": "sha512-r3n0onD3BTOVUNPhR4lhVK4/pABGpbA7bW3eumZnYdKaHkf1qEC+Mag6DPbGNuuh0eG8AaYj+YqmVHSiGslaTQ==", "dev": true, "dependencies": { "@babel/generator": "7.17.7", @@ -11114,9 +11151,9 @@ } }, "node_modules/axios": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz", - "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.1.tgz", + "integrity": "sha512-vfBmhDpKafglh0EldBEbVuoe7DyAavGSLWhuSm5ZSEKQnHhBf0xAAwybbNH1IkrJNGnS/VG4I5yxig1pCEXE4g==", "dependencies": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -18158,9 +18195,9 @@ } }, "node_modules/prettier": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", - "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz", + "integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -19117,9 +19154,9 @@ "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" }, "node_modules/rollup": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.3.0.tgz", - "integrity": "sha512-scIi1NrKLDIYSPK66jjECtII7vIgdAMFmFo8h6qm++I6nN9qDSV35Ku6erzGVqYjx+lj+j5wkusRMr++8SyDZg==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.4.0.tgz", + "integrity": "sha512-3L67ubCc1Qm49wUodsQ72FM6JmJ9M37d63rGPjxbcKrzNJrwFipl+lDNHeWd6BId09S6Tb9KiBgYKbWhIuqVyg==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -19129,18 +19166,18 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.3.0", - "@rollup/rollup-android-arm64": "4.3.0", - "@rollup/rollup-darwin-arm64": "4.3.0", - "@rollup/rollup-darwin-x64": "4.3.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.3.0", - "@rollup/rollup-linux-arm64-gnu": "4.3.0", - "@rollup/rollup-linux-arm64-musl": "4.3.0", - "@rollup/rollup-linux-x64-gnu": "4.3.0", - "@rollup/rollup-linux-x64-musl": "4.3.0", - "@rollup/rollup-win32-arm64-msvc": "4.3.0", - "@rollup/rollup-win32-ia32-msvc": "4.3.0", - "@rollup/rollup-win32-x64-msvc": "4.3.0", + "@rollup/rollup-android-arm-eabi": "4.4.0", + "@rollup/rollup-android-arm64": "4.4.0", + "@rollup/rollup-darwin-arm64": "4.4.0", + "@rollup/rollup-darwin-x64": "4.4.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.4.0", + "@rollup/rollup-linux-arm64-gnu": "4.4.0", + "@rollup/rollup-linux-arm64-musl": "4.4.0", + "@rollup/rollup-linux-x64-gnu": "4.4.0", + "@rollup/rollup-linux-x64-musl": "4.4.0", + "@rollup/rollup-win32-arm64-msvc": "4.4.0", + "@rollup/rollup-win32-ia32-msvc": "4.4.0", + "@rollup/rollup-win32-x64-msvc": "4.4.0", "fsevents": "~2.3.2" } }, diff --git a/web/package.json b/web/package.json index 7a6e4cef5..0d056b9bd 100644 --- a/web/package.json +++ b/web/package.json @@ -36,17 +36,17 @@ "@codemirror/lang-xml": "^6.0.2", "@codemirror/legacy-modes": "^6.3.3", "@codemirror/theme-one-dark": "^6.1.2", - "@formatjs/intl-listformat": "^7.5.1", + "@formatjs/intl-listformat": "^7.5.2", "@fortawesome/fontawesome-free": "^6.4.2", - "@goauthentik/api": "^2023.10.3-1699554078", + "@goauthentik/api": "^2023.10.3-1699884123", "@lit-labs/context": "^0.4.0", "@lit-labs/task": "^3.1.0", "@lit/localize": "^0.11.4", "@open-wc/lit-helpers": "^0.6.0", "@patternfly/elements": "^2.4.0", "@patternfly/patternfly": "^4.224.2", - "@sentry/browser": "^7.79.0", - "@sentry/tracing": "^7.79.0", + "@sentry/browser": "^7.80.0", + "@sentry/tracing": "^7.80.0", "@webcomponents/webcomponentsjs": "^2.8.0", "base64-js": "^1.5.1", "chart.js": "^4.4.0", @@ -64,14 +64,14 @@ "yaml": "^2.3.4" }, "devDependencies": { - "@babel/core": "^7.23.2", + "@babel/core": "^7.23.3", "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-decorators": "^7.23.2", - "@babel/plugin-transform-private-methods": "^7.22.5", - "@babel/plugin-transform-private-property-in-object": "^7.22.11", - "@babel/plugin-transform-runtime": "^7.23.2", - "@babel/preset-env": "^7.23.2", - "@babel/preset-typescript": "^7.23.2", + "@babel/plugin-proposal-decorators": "^7.23.3", + "@babel/plugin-transform-private-methods": "^7.23.3", + "@babel/plugin-transform-private-property-in-object": "^7.23.3", + "@babel/plugin-transform-runtime": "^7.23.3", + "@babel/preset-env": "^7.23.3", + "@babel/preset-typescript": "^7.23.3", "@hcaptcha/types": "^1.0.3", "@jackfranklin/rollup-plugin-markdown": "^0.4.0", "@jeysal/storybook-addon-css-user-preferences": "^0.2.0", @@ -87,7 +87,7 @@ "@storybook/blocks": "^7.1.1", "@storybook/web-components": "^7.5.3", "@storybook/web-components-vite": "^7.5.3", - "@trivago/prettier-plugin-sort-imports": "^4.2.1", + "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/chart.js": "^2.9.40", "@types/codemirror": "5.60.13", "@types/grecaptcha": "^3.0.7", @@ -104,12 +104,12 @@ "eslint-plugin-storybook": "^0.6.15", "lit-analyzer": "^2.0.1", "npm-run-all": "^4.1.5", - "prettier": "^3.0.3", + "prettier": "^3.1.0", "pseudolocale": "^2.0.0", "pyright": "^1.1.335", "react": "^18.2.0", "react-dom": "^18.2.0", - "rollup": "^4.3.0", + "rollup": "^4.4.0", "rollup-plugin-copy": "^3.5.0", "rollup-plugin-cssimport": "^1.0.3", "rollup-plugin-postcss-lit": "^2.1.0", diff --git a/web/src/admin/admin-overview/charts/SyncStatusChart.ts b/web/src/admin/admin-overview/charts/SyncStatusChart.ts index f306a578d..28747d682 100644 --- a/web/src/admin/admin-overview/charts/SyncStatusChart.ts +++ b/web/src/admin/admin-overview/charts/SyncStatusChart.ts @@ -44,11 +44,11 @@ export class LDAPSyncStatusChart extends AKChart { await Promise.all( sources.results.map(async (element) => { try { - const health = await api.sourcesLdapSyncStatusList({ + const health = await api.sourcesLdapSyncStatusRetrieve({ slug: element.slug, }); - health.forEach((task) => { + health.tasks.forEach((task) => { if (task.status !== TaskStatusEnum.Successful) { metrics.failed += 1; } @@ -60,7 +60,7 @@ export class LDAPSyncStatusChart extends AKChart { metrics.healthy += 1; } }); - if (health.length < 1) { + if (health.tasks.length < 1) { metrics.unsynced += 1; } } catch { diff --git a/web/src/admin/sources/ldap/LDAPSourceConnectivity.ts b/web/src/admin/sources/ldap/LDAPSourceConnectivity.ts new file mode 100644 index 000000000..34cdc2ffe --- /dev/null +++ b/web/src/admin/sources/ldap/LDAPSourceConnectivity.ts @@ -0,0 +1,50 @@ +import { AKElement } from "@goauthentik/app/elements/Base"; +import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; + +import { msg } from "@lit/localize"; +import { CSSResult, TemplateResult, html } from "lit"; +import { customElement, property } from "lit/decorators.js"; + +import PFList from "@patternfly/patternfly/components/List/list.css"; +import PFBase from "@patternfly/patternfly/patternfly-base.css"; + +@customElement("ak-source-ldap-connectivity") +export class LDAPSourceConnectivity extends AKElement { + @property() + connectivity?: { + [key: string]: { + [key: string]: string; + }; + }; + + static get styles(): CSSResult[] { + return [PFBase, PFList]; + } + + render(): TemplateResult { + if (!this.connectivity) { + return html``; + } + return html`
    + ${Object.keys(this.connectivity).map((serverKey) => { + let serverLabel = html`${serverKey}`; + if (serverKey === "__all__") { + serverLabel = html`${msg("Global status")}`; + } + const server = this.connectivity![serverKey]; + const content = html`${serverLabel}: ${server.status}`; + let tooltip = html`${content}`; + if (server.status === "ok") { + tooltip = html` +
      +
    • ${msg("Vendor")}: ${server.vendor}
    • +
    • ${msg("Version")}: ${server.version}
    • +
    + ${content} +
    `; + } + return html`
  • ${tooltip}
  • `; + })} +
`; + } +} diff --git a/web/src/admin/sources/ldap/LDAPSourceViewPage.ts b/web/src/admin/sources/ldap/LDAPSourceViewPage.ts index 36129c3c4..6ac64c14b 100644 --- a/web/src/admin/sources/ldap/LDAPSourceViewPage.ts +++ b/web/src/admin/sources/ldap/LDAPSourceViewPage.ts @@ -1,3 +1,4 @@ +import "@goauthentik/admin/sources/ldap/LDAPSourceConnectivity"; import "@goauthentik/admin/sources/ldap/LDAPSourceForm"; import "@goauthentik/app/elements/rbac/ObjectPermissionsPage"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; @@ -25,9 +26,9 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css"; import { LDAPSource, + LDAPSyncStatus, RbacPermissionsAssignedByUsersListModelEnum, SourcesApi, - Task, TaskStatusEnum, } from "@goauthentik/api"; @@ -48,7 +49,7 @@ export class LDAPSourceViewPage extends AKElement { source!: LDAPSource; @state() - syncState: Task[] = []; + syncState?: LDAPSyncStatus; static get styles(): CSSResult[] { return [PFBase, PFPage, PFButton, PFGrid, PFContent, PFCard, PFDescriptionList, PFList]; @@ -62,6 +63,51 @@ export class LDAPSourceViewPage extends AKElement { }); } + renderSyncStatus(): TemplateResult { + if (!this.syncState) { + return html`${msg("No sync status.")}`; + } + if (this.syncState.isRunning) { + return html`${msg("Sync currently running.")}`; + } + if (this.syncState.tasks.length < 1) { + return html`${msg("Not synced yet.")}`; + } + return html` +
    + ${this.syncState.tasks.map((task) => { + let header = ""; + if (task.status === TaskStatusEnum.Warning) { + header = msg("Task finished with warnings"); + } else if (task.status === TaskStatusEnum.Error) { + header = msg("Task finished with errors"); + } else { + header = msg(str`Last sync: ${task.taskFinishTimestamp.toLocaleString()}`); + } + return html`
  • +

    ${task.taskName}

    +
      +
    • ${header}
    • + ${task.messages.map((m) => { + return html`
    • ${m}
    • `; + })} +
    +
  • `; + })} +
+ `; + } + + load(): void { + new SourcesApi(DEFAULT_CONFIG) + .sourcesLdapSyncStatusRetrieve({ + slug: this.source.slug, + }) + .then((state) => { + this.syncState = state; + }); + } + render(): TemplateResult { if (!this.source) { return html``; @@ -72,13 +118,7 @@ export class LDAPSourceViewPage extends AKElement { data-tab-title="${msg("Overview")}" class="pf-c-page__main-section pf-m-no-padding-mobile" @activate=${() => { - new SourcesApi(DEFAULT_CONFIG) - .sourcesLdapSyncStatusList({ - slug: this.source.slug, - }) - .then((state) => { - this.syncState = state; - }); + this.load(); }} >
@@ -137,42 +177,25 @@ export class LDAPSourceViewPage extends AKElement {
-
+
+
+

${msg("Connectivity")}

+
+
+ +
+
+

${msg("Sync status")}

-
- ${this.syncState.length < 1 - ? html`

${msg("Not synced yet.")}

` - : html` -
    - ${this.syncState.map((task) => { - let header = ""; - if (task.status === TaskStatusEnum.Warning) { - header = msg("Task finished with warnings"); - } else if (task.status === TaskStatusEnum.Error) { - header = msg("Task finished with errors"); - } else { - header = msg( - str`Last sync: ${task.taskFinishTimestamp.toLocaleString()}`, - ); - } - return html`
  • -

    ${task.taskName}

    -
      -
    • ${header}
    • - ${task.messages.map((m) => { - return html`
    • ${m}
    • `; - })} -
    -
  • `; - })} -
- `} -
+
${this.renderSyncStatus()}