diff --git a/.bumpversion.cfg b/.bumpversion.cfg index f78e67f49..d679517c7 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 2023.10.4 +current_version = 2023.10.5 tag = True commit = True parse = (?P\d+)\.(?P\d+)\.(?P\d+) diff --git a/Dockerfile b/Dockerfile index 629d3258b..114be6253 100644 --- a/Dockerfile +++ b/Dockerfile @@ -71,7 +71,7 @@ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \ # Stage 4: MaxMind GeoIP FROM --platform=${BUILDPLATFORM} ghcr.io/maxmind/geoipupdate:v6.0 as geoip -ENV GEOIPUPDATE_EDITION_IDS="GeoLite2-City" +ENV GEOIPUPDATE_EDITION_IDS="GeoLite2-City GeoLite2-ASN" ENV GEOIPUPDATE_VERBOSE="true" ENV GEOIPUPDATE_ACCOUNT_ID_FILE="/run/secrets/GEOIPUPDATE_ACCOUNT_ID" ENV GEOIPUPDATE_LICENSE_KEY_FILE="/run/secrets/GEOIPUPDATE_LICENSE_KEY" diff --git a/authentik/__init__.py b/authentik/__init__.py index fc368ceeb..0c651c834 100644 --- a/authentik/__init__.py +++ b/authentik/__init__.py @@ -2,7 +2,7 @@ from os import environ from typing import Optional -__version__ = "2023.10.4" +__version__ = "2023.10.5" ENV_GIT_HASH_KEY = "GIT_BUILD_HASH" diff --git a/authentik/api/v3/config.py b/authentik/api/v3/config.py index 3137a1922..49493234b 100644 --- a/authentik/api/v3/config.py +++ b/authentik/api/v3/config.py @@ -19,7 +19,7 @@ from rest_framework.response import Response from rest_framework.views import APIView from authentik.core.api.utils import PassiveSerializer -from authentik.events.geo import GEOIP_READER +from authentik.events.context_processors.base import get_context_processors from authentik.lib.config import CONFIG capabilities = Signal() @@ -30,6 +30,7 @@ class Capabilities(models.TextChoices): CAN_SAVE_MEDIA = "can_save_media" CAN_GEO_IP = "can_geo_ip" + CAN_ASN = "can_asn" CAN_IMPERSONATE = "can_impersonate" CAN_DEBUG = "can_debug" IS_ENTERPRISE = "is_enterprise" @@ -68,8 +69,9 @@ class ConfigView(APIView): deb_test = settings.DEBUG or settings.TEST if Path(settings.MEDIA_ROOT).is_mount() or deb_test: caps.append(Capabilities.CAN_SAVE_MEDIA) - if GEOIP_READER.enabled: - caps.append(Capabilities.CAN_GEO_IP) + for processor in get_context_processors(): + if cap := processor.capability(): + caps.append(cap) if self.request.tenant.impersonation: caps.append(Capabilities.CAN_IMPERSONATE) if settings.DEBUG: # pragma: no cover diff --git a/authentik/core/api/authenticated_sessions.py b/authentik/core/api/authenticated_sessions.py index 03c1aeaf3..2d77937be 100644 --- a/authentik/core/api/authenticated_sessions.py +++ b/authentik/core/api/authenticated_sessions.py @@ -14,7 +14,8 @@ from ua_parser import user_agent_parser from authentik.api.authorization import OwnerSuperuserPermissions from authentik.core.api.used_by import UsedByMixin from authentik.core.models import AuthenticatedSession -from authentik.events.geo import GEOIP_READER, GeoIPDict +from authentik.events.context_processors.asn import ASN_CONTEXT_PROCESSOR, ASNDict +from authentik.events.context_processors.geoip import GEOIP_CONTEXT_PROCESSOR, GeoIPDict class UserAgentDeviceDict(TypedDict): @@ -59,6 +60,7 @@ class AuthenticatedSessionSerializer(ModelSerializer): current = SerializerMethodField() user_agent = SerializerMethodField() geo_ip = SerializerMethodField() + asn = SerializerMethodField() def get_current(self, instance: AuthenticatedSession) -> bool: """Check if session is currently active session""" @@ -70,8 +72,12 @@ class AuthenticatedSessionSerializer(ModelSerializer): return user_agent_parser.Parse(instance.last_user_agent) def get_geo_ip(self, instance: AuthenticatedSession) -> Optional[GeoIPDict]: # pragma: no cover - """Get parsed user agent""" - return GEOIP_READER.city_dict(instance.last_ip) + """Get GeoIP Data""" + return GEOIP_CONTEXT_PROCESSOR.city_dict(instance.last_ip) + + def get_asn(self, instance: AuthenticatedSession) -> Optional[ASNDict]: # pragma: no cover + """Get ASN Data""" + return ASN_CONTEXT_PROCESSOR.asn_dict(instance.last_ip) class Meta: model = AuthenticatedSession @@ -80,6 +86,7 @@ class AuthenticatedSessionSerializer(ModelSerializer): "current", "user_agent", "geo_ip", + "asn", "user", "last_ip", "last_user_agent", diff --git a/authentik/core/expression/evaluator.py b/authentik/core/expression/evaluator.py index 85e6ccbc4..480caea21 100644 --- a/authentik/core/expression/evaluator.py +++ b/authentik/core/expression/evaluator.py @@ -44,6 +44,7 @@ class PropertyMappingEvaluator(BaseEvaluator): if request: req.http_request = request self._context["request"] = req + req.context.update(**kwargs) self._context.update(**kwargs) self.dry_run = dry_run diff --git a/authentik/events/apps.py b/authentik/events/apps.py index 34368856d..3d939d568 100644 --- a/authentik/events/apps.py +++ b/authentik/events/apps.py @@ -2,6 +2,7 @@ from prometheus_client import Gauge from authentik.blueprints.apps import ManagedAppConfig +from authentik.lib.config import CONFIG, ENV_PREFIX GAUGE_TASKS = Gauge( "authentik_system_tasks", @@ -21,3 +22,24 @@ class AuthentikEventsConfig(ManagedAppConfig): def reconcile_load_events_signals(self): """Load events signals""" self.import_module("authentik.events.signals") + + def reconcile_check_deprecations(self): + """Check for config deprecations""" + from authentik.events.models import Event, EventAction + + for key_replace, msg in CONFIG.deprecations.items(): + key, replace = key_replace + key_env = f"{ENV_PREFIX}_{key.replace('.', '__')}".upper() + replace_env = f"{ENV_PREFIX}_{replace.replace('.', '__')}".upper() + if Event.objects.filter( + action=EventAction.CONFIGURATION_ERROR, context__deprecated_option=key + ).exists(): + continue + Event.new( + EventAction.CONFIGURATION_ERROR, + deprecated_option=key, + deprecated_env=key_env, + replacement_option=replace, + replacement_env=replace_env, + message=msg, + ).save() diff --git a/authentik/events/context_processors/__init__.py b/authentik/events/context_processors/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/authentik/events/context_processors/asn.py b/authentik/events/context_processors/asn.py new file mode 100644 index 000000000..4478c7fb0 --- /dev/null +++ b/authentik/events/context_processors/asn.py @@ -0,0 +1,81 @@ +"""ASN Enricher""" +from typing import TYPE_CHECKING, Optional, TypedDict + +from django.http import HttpRequest +from geoip2.errors import GeoIP2Error +from geoip2.models import ASN +from sentry_sdk import Hub + +from authentik.events.context_processors.mmdb import MMDBContextProcessor +from authentik.lib.config import CONFIG +from authentik.root.middleware import ClientIPMiddleware + +if TYPE_CHECKING: + from authentik.api.v3.config import Capabilities + from authentik.events.models import Event + + +class ASNDict(TypedDict): + """ASN Details""" + + asn: int + as_org: str | None + network: str | None + + +class ASNContextProcessor(MMDBContextProcessor): + """ASN Database reader wrapper""" + + def capability(self) -> Optional["Capabilities"]: + from authentik.api.v3.config import Capabilities + + return Capabilities.CAN_ASN + + def path(self) -> str | None: + return CONFIG.get("events.context_processors.asn") + + def enrich_event(self, event: "Event"): + asn = self.asn_dict(event.client_ip) + if not asn: + return + event.context["asn"] = asn + + def enrich_context(self, request: HttpRequest) -> dict: + return { + "asn": self.asn_dict(ClientIPMiddleware.get_client_ip(request)), + } + + def asn(self, ip_address: str) -> Optional[ASN]: + """Wrapper for Reader.asn""" + with Hub.current.start_span( + op="authentik.events.asn.asn", + description=ip_address, + ): + if not self.configured(): + return None + self.check_expired() + try: + return self.reader.asn(ip_address) + except (GeoIP2Error, ValueError): + return None + + def asn_to_dict(self, asn: ASN | None) -> ASNDict: + """Convert ASN to dict""" + if not asn: + return {} + asn_dict: ASNDict = { + "asn": asn.autonomous_system_number, + "as_org": asn.autonomous_system_organization, + "network": str(asn.network) if asn.network else None, + } + return asn_dict + + def asn_dict(self, ip_address: str) -> Optional[ASNDict]: + """Wrapper for self.asn that returns a dict""" + asn = self.asn(ip_address) + if not asn: + return None + return self.asn_to_dict(asn) + + +ASN_CONTEXT_PROCESSOR = ASNContextProcessor() diff --git a/authentik/events/context_processors/base.py b/authentik/events/context_processors/base.py new file mode 100644 index 000000000..96a46a65a --- /dev/null +++ b/authentik/events/context_processors/base.py @@ -0,0 +1,43 @@ +"""Base event enricher""" +from functools import cache +from typing import TYPE_CHECKING, Optional + +from django.http import HttpRequest + +if TYPE_CHECKING: + from authentik.api.v3.config import Capabilities + from authentik.events.models import Event + + +class EventContextProcessor: + """Base event enricher""" + + def capability(self) -> Optional["Capabilities"]: + """Return the capability this context processor provides""" + return None + + def configured(self) -> bool: + """Return true if this context processor is configured""" + return False + + def enrich_event(self, event: "Event"): + """Modify event""" + raise NotImplementedError + + def enrich_context(self, request: HttpRequest) -> dict: + """Modify context""" + raise NotImplementedError + + +@cache +def get_context_processors() -> list[EventContextProcessor]: + """Get a list of all configured context processors""" + from authentik.events.context_processors.asn import ASN_CONTEXT_PROCESSOR + from authentik.events.context_processors.geoip import GEOIP_CONTEXT_PROCESSOR + + processors_types = [ASN_CONTEXT_PROCESSOR, GEOIP_CONTEXT_PROCESSOR] + processors = [] + for _type in processors_types: + if _type.configured(): + processors.append(_type) + return processors diff --git a/authentik/events/context_processors/geoip.py b/authentik/events/context_processors/geoip.py new file mode 100644 index 000000000..40ea0b012 --- /dev/null +++ b/authentik/events/context_processors/geoip.py @@ -0,0 +1,86 @@ +"""events GeoIP Reader""" +from typing import TYPE_CHECKING, Optional, TypedDict + +from django.http import HttpRequest +from geoip2.errors import GeoIP2Error +from geoip2.models import City +from sentry_sdk.hub import Hub + +from authentik.events.context_processors.mmdb import MMDBContextProcessor +from authentik.lib.config import CONFIG +from authentik.root.middleware import ClientIPMiddleware + +if TYPE_CHECKING: + from authentik.api.v3.config import Capabilities + from authentik.events.models import Event + + +class GeoIPDict(TypedDict): + """GeoIP Details""" + + continent: str + country: str + lat: float + long: float + city: str + + +class GeoIPContextProcessor(MMDBContextProcessor): + """Slim wrapper around GeoIP API""" + + def capability(self) -> Optional["Capabilities"]: + from authentik.api.v3.config import Capabilities + + return Capabilities.CAN_GEO_IP + + def path(self) -> str | None: + return CONFIG.get("events.context_processors.geoip") + + def enrich_event(self, event: "Event"): + city = self.city_dict(event.client_ip) + if not city: + return + event.context["geo"] = city + + def enrich_context(self, request: HttpRequest) -> dict: + # Different key `geoip` vs `geo` for legacy reasons + return {"geoip": self.city(ClientIPMiddleware.get_client_ip(request))} + + def city(self, ip_address: str) -> Optional[City]: + """Wrapper for Reader.city""" + with Hub.current.start_span( + op="authentik.events.geo.city", + description=ip_address, + ): + if not self.configured(): + return None + self.check_expired() + try: + return self.reader.city(ip_address) + except (GeoIP2Error, ValueError): + return None + + def city_to_dict(self, city: City | None) -> GeoIPDict: + """Convert City to dict""" + if not city: + return {} + city_dict: GeoIPDict = { + "continent": city.continent.code, + "country": city.country.iso_code, + "lat": city.location.latitude, + "long": city.location.longitude, + "city": "", + } + if city.city.name: + city_dict["city"] = city.city.name + return city_dict + + def city_dict(self, ip_address: str) -> Optional[GeoIPDict]: + """Wrapper for self.city that returns a dict""" + city = self.city(ip_address) + if not city: + return None + return self.city_to_dict(city) + + +GEOIP_CONTEXT_PROCESSOR = GeoIPContextProcessor() diff --git a/authentik/events/context_processors/mmdb.py b/authentik/events/context_processors/mmdb.py new file mode 100644 index 000000000..45bd85411 --- /dev/null +++ b/authentik/events/context_processors/mmdb.py @@ -0,0 +1,53 @@ +"""Common logic for reading MMDB files""" +from pathlib import Path +from typing import Optional + +from geoip2.database import Reader +from structlog.stdlib import get_logger + +from authentik.events.context_processors.base import EventContextProcessor + + +class MMDBContextProcessor(EventContextProcessor): + """Common logic for reading MaxMind DB files, including re-loading if the file has changed""" + + def __init__(self): + self.reader: Optional[Reader] = None + self._last_mtime: float = 0.0 + self.logger = get_logger() + self.open() + + def path(self) -> str | None: + """Get the path to the MMDB file to load""" + raise NotImplementedError + + def open(self): + """Get GeoIP Reader, if configured, otherwise none""" + path = self.path() + if path == "" or not path: + return + try: + self.reader = Reader(path) + self._last_mtime = Path(path).stat().st_mtime + self.logger.info("Loaded MMDB database", last_write=self._last_mtime, file=path) + except OSError as exc: + self.logger.warning("Failed to load MMDB database", path=path, exc=exc) + + def check_expired(self): + """Check if the modification date of the MMDB database has + changed, and reload it if so""" + path = self.path() + if path == "" or not path: + return + try: + mtime = Path(path).stat().st_mtime + diff = self._last_mtime < mtime + if diff > 0: + self.logger.info("Found new MMDB Database, reopening", diff=diff, path=path) + self.open() + except OSError as exc: + self.logger.warning("Failed to check MMDB age", exc=exc) + + def configured(self) -> bool: + """Return true if this context processor is configured""" + return bool(self.reader) diff --git a/authentik/events/geo.py b/authentik/events/geo.py deleted file mode 100644 index 95a28539c..000000000 --- a/authentik/events/geo.py +++ /dev/null @@ -1,100 +0,0 @@ -"""events GeoIP Reader""" -from os import stat -from typing import Optional, TypedDict - -from geoip2.database import Reader -from geoip2.errors import GeoIP2Error -from geoip2.models import City -from sentry_sdk.hub import Hub -from structlog.stdlib import get_logger - -from authentik.lib.config import CONFIG - -LOGGER = get_logger() - - -class GeoIPDict(TypedDict): - """GeoIP Details""" - - continent: str - country: str - lat: float - long: float - city: str - - -class GeoIPReader: - """Slim wrapper around GeoIP API""" - - def __init__(self): - self.__reader: Optional[Reader] = None - self.__last_mtime: float = 0.0 - self.__open() - - def __open(self): - """Get GeoIP Reader, if configured, otherwise none""" - path = CONFIG.get("geoip") - if path == "" or not path: - return - try: - self.__reader = Reader(path) - self.__last_mtime = stat(path).st_mtime - LOGGER.info("Loaded GeoIP database", last_write=self.__last_mtime) - except OSError as exc: - LOGGER.warning("Failed to load GeoIP database", exc=exc) - - def __check_expired(self): - """Check if the modification date of the GeoIP database has - changed, and reload it if so""" - path = CONFIG.get("geoip") - try: - mtime = stat(path).st_mtime - diff = self.__last_mtime < mtime - if diff > 0: - LOGGER.info("Found new GeoIP Database, reopening", diff=diff) - self.__open() - except OSError as exc: - LOGGER.warning("Failed to check GeoIP age", exc=exc) - return - - @property - def enabled(self) -> bool: - """Check if GeoIP is enabled""" - return bool(self.__reader) - - def city(self, ip_address: str) -> Optional[City]: - """Wrapper for Reader.city""" - with Hub.current.start_span( - op="authentik.events.geo.city", - description=ip_address, - ): - if not self.enabled: - return None - self.__check_expired() - try: - return self.__reader.city(ip_address) - except (GeoIP2Error, ValueError): - return None - - def city_to_dict(self, city: City) -> GeoIPDict: - """Convert City to dict""" - city_dict: GeoIPDict = { - "continent": city.continent.code, - "country": city.country.iso_code, - "lat": city.location.latitude, - "long": city.location.longitude, - "city": "", - } - if city.city.name: - city_dict["city"] = city.city.name - return city_dict - - def city_dict(self, ip_address: str) -> Optional[GeoIPDict]: - """Wrapper for self.city that returns a dict""" - city = self.city(ip_address) - if not city: - return None - return self.city_to_dict(city) - - -GEOIP_READER = GeoIPReader() diff --git a/authentik/events/models.py b/authentik/events/models.py index a6717c24a..fbeca416f 100644 --- a/authentik/events/models.py +++ b/authentik/events/models.py @@ -28,7 +28,7 @@ from authentik.core.middleware import ( SESSION_KEY_IMPERSONATE_USER, ) from authentik.core.models import ExpiringModel, Group, PropertyMapping, User -from authentik.events.geo import GEOIP_READER +from authentik.events.context_processors.base import get_context_processors from authentik.events.utils import ( cleanse_dict, get_user, @@ -249,21 +249,15 @@ class Event(SerializerModel, ExpiringModel): self.user["on_behalf_of"] = get_user(request.session[SESSION_KEY_IMPERSONATE_USER]) # User 255.255.255.255 as fallback if IP cannot be determined self.client_ip = ClientIPMiddleware.get_client_ip(request) - # Apply GeoIP Data, when enabled - self.with_geoip() + # Enrich event data + for processor in get_context_processors(): + processor.enrich_event(self) # If there's no app set, we get it from the requests too if not self.app: self.app = Event._get_app_from_request(request) self.save() return self - def with_geoip(self): # pragma: no cover - """Apply GeoIP Data, when enabled""" - city = GEOIP_READER.city_dict(self.client_ip) - if not city: - return - self.context["geo"] = city - def save(self, *args, **kwargs): if self._state.adding: LOGGER.info( @@ -470,7 +464,7 @@ class NotificationTransport(SerializerModel): } mail = TemplateEmailMessage( subject=subject_prefix + context["title"], - to=[notification.user.email], + to=[f"{notification.user.name} <{notification.user.email}>"], language=notification.user.locale(), template_name="email/event_notification.html", template_context=context, diff --git a/authentik/events/signals.py b/authentik/events/signals.py index 5d15d1dc1..8d999e20a 100644 --- a/authentik/events/signals.py +++ b/authentik/events/signals.py @@ -45,9 +45,14 @@ def get_login_event(request: HttpRequest) -> Optional[Event]: @receiver(user_logged_out) -def on_user_logged_out(sender, request: HttpRequest, user: User, **_): +def on_user_logged_out(sender, request: HttpRequest, user: User, **kwargs): """Log successfully logout""" - Event.new(EventAction.LOGOUT).from_http(request, user=user) + # Check if this even comes from the user_login stage's middleware, which will set an extra + # argument + event = Event.new(EventAction.LOGOUT) + if "event_extra" in kwargs: + event.context.update(kwargs["event_extra"]) + event.from_http(request, user=user) @receiver(user_write) diff --git a/authentik/events/tests/test_enrich_asn.py b/authentik/events/tests/test_enrich_asn.py new file mode 100644 index 000000000..2844c4591 --- /dev/null +++ b/authentik/events/tests/test_enrich_asn.py @@ -0,0 +1,24 @@ +"""Test ASN Wrapper""" +from django.test import TestCase + +from authentik.events.context_processors.asn import ASNContextProcessor + + +class TestASN(TestCase): + """Test ASN Wrapper""" + + def setUp(self) -> None: + self.reader = ASNContextProcessor() + + def test_simple(self): + """Test simple asn wrapper""" + # IPs from + # https://github.com/maxmind/MaxMind-DB/blob/main/source-data/GeoLite2-ASN-Test.json + self.assertEqual( + self.reader.asn_dict("1.0.0.1"), + { + "asn": 15169, + "as_org": "Google Inc.", + "network": "1.0.0.0/24", + }, + ) diff --git a/authentik/events/tests/test_geoip.py b/authentik/events/tests/test_enrich_geoip.py similarity index 83% rename from authentik/events/tests/test_geoip.py rename to authentik/events/tests/test_enrich_geoip.py index 3120dacae..5317901a4 100644 --- a/authentik/events/tests/test_geoip.py +++ b/authentik/events/tests/test_enrich_geoip.py @@ -1,14 +1,14 @@ """Test GeoIP Wrapper""" from django.test import TestCase -from authentik.events.geo import GeoIPReader +from authentik.events.context_processors.geoip import GeoIPContextProcessor class TestGeoIP(TestCase): """Test GeoIP Wrapper""" def setUp(self) -> None: - self.reader = GeoIPReader() + self.reader = GeoIPContextProcessor() def test_simple(self): """Test simple city wrapper""" diff --git a/authentik/events/utils.py b/authentik/events/utils.py index a7c3bdf3e..cf1b1b78b 100644 --- a/authentik/events/utils.py +++ b/authentik/events/utils.py @@ -17,12 +17,13 @@ from django.db.models.base import Model from django.http.request import HttpRequest from django.utils import timezone from django.views.debug import SafeExceptionReporterFilter -from geoip2.models import City +from geoip2.models import ASN, City from guardian.utils import get_anonymous_user from authentik.blueprints.v1.common import YAMLTag from authentik.core.models import User -from authentik.events.geo import GEOIP_READER +from authentik.events.context_processors.asn import ASN_CONTEXT_PROCESSOR +from authentik.events.context_processors.geoip import GEOIP_CONTEXT_PROCESSOR from authentik.policies.types import PolicyRequest # Special keys which are *not* cleaned, even when the default filter @@ -123,7 +124,9 @@ def sanitize_item(value: Any) -> Any: if isinstance(value, (HttpRequest, WSGIRequest)): return ... if isinstance(value, City): - return GEOIP_READER.city_to_dict(value) + return GEOIP_CONTEXT_PROCESSOR.city_to_dict(value) + if isinstance(value, ASN): + return ASN_CONTEXT_PROCESSOR.asn_to_dict(value) if isinstance(value, Path): return str(value) if isinstance(value, Exception): diff --git a/authentik/lib/config.py b/authentik/lib/config.py index ec96b0e9b..319be4412 100644 --- a/authentik/lib/config.py +++ b/authentik/lib/config.py @@ -36,6 +36,7 @@ REDIS_ENV_KEYS = [ # Old key -> new key DEPRECATIONS = { + "geoip": "events.context_processors.geoip", "redis.broker_url": "broker.url", "redis.broker_transport_options": "broker.transport_options", "redis.cache_timeout": "cache.timeout", @@ -113,6 +114,8 @@ class ConfigLoader: A variable like AUTHENTIK_POSTGRESQL__HOST would translate to postgresql.host""" + deprecations: dict[tuple[str, str], str] = {} + def __init__(self, **kwargs): super().__init__() self.__config = {} @@ -139,9 +142,9 @@ class ConfigLoader: self.update_from_file(env_file) self.update_from_env() self.update(self.__config, kwargs) - self.check_deprecations() + self.deprecations = self.check_deprecations() - def check_deprecations(self): + def check_deprecations(self) -> dict[str, str]: """Warn if any deprecated configuration options are used""" def _pop_deprecated_key(current_obj, dot_parts, index): @@ -154,25 +157,23 @@ class ConfigLoader: current_obj.pop(dot_part) return value + deprecation_replacements = {} 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 + if self.get(deprecation, default=UNSET) is UNSET: + continue + message = ( + f"'{deprecation}' has been deprecated in favor of '{replacement}'! " + + "Please update your configuration." + ) + self.log( + "warning", + message, + ) + deprecation_replacements[(deprecation, replacement)] = message - 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) + deprecated_attr = _pop_deprecated_key(self.__config, deprecation.split("."), 0) + self.set(replacement, deprecated_attr) + return deprecation_replacements def log(self, level: str, message: str, **kwargs): """Custom Log method, we want to ensure ConfigLoader always logs JSON even when @@ -319,7 +320,9 @@ class ConfigLoader: 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) + if not isinstance(value, Attr): + value = Attr(value) + set_path_in_dict(self.raw, path, value, sep=sep) CONFIG = ConfigLoader() diff --git a/authentik/lib/default.yml b/authentik/lib/default.yml index 4b86bd37e..22eda58ae 100644 --- a/authentik/lib/default.yml +++ b/authentik/lib/default.yml @@ -37,8 +37,8 @@ redis: tls_reqs: "none" # broker: -# url: "" -# transport_options: "" +# url: "" +# transport_options: "" cache: # url: "" @@ -48,10 +48,10 @@ cache: timeout_reputation: 300 # channel: -# url: "" +# url: "" # result_backend: -# url: "" +# url: "" debug: false remote_debug: false @@ -104,7 +104,10 @@ reputation: cookie_domain: null disable_update_check: false disable_startup_analytics: false -geoip: "/geoip/GeoLite2-City.mmdb" +events: + context_processors: + geoip: "/geoip/GeoLite2-City.mmdb" + asn: "/geoip/GeoLite2-ASN.mmdb" cert_discovery_dir: /certs default_token_length: 60 diff --git a/authentik/lib/utils/http.py b/authentik/lib/utils/http.py index 3d6638104..f8d33db98 100644 --- a/authentik/lib/utils/http.py +++ b/authentik/lib/utils/http.py @@ -1,5 +1,8 @@ """http helpers""" -from requests.sessions import Session +from uuid import uuid4 + +from django.conf import settings +from requests.sessions import PreparedRequest, Session from structlog.stdlib import get_logger from authentik import get_full_version @@ -12,8 +15,25 @@ def authentik_user_agent() -> str: return f"authentik@{get_full_version()}" +class DebugSession(Session): + """requests session which logs http requests and responses""" + + def send(self, req: PreparedRequest, *args, **kwargs): + request_id = str(uuid4()) + LOGGER.debug("HTTP request sent", uid=request_id, path=req.path_url, headers=req.headers) + resp = super().send(req, *args, **kwargs) + LOGGER.debug( + "HTTP response received", + uid=request_id, + status=resp.status_code, + body=resp.text, + headers=resp.headers, + ) + return resp + + def get_http_session() -> Session: """Get a requests session with common headers""" - session = Session() + session = DebugSession() if settings.DEBUG else Session() session.headers["User-Agent"] = authentik_user_agent() return session diff --git a/authentik/policies/reputation/api.py b/authentik/policies/reputation/api.py index 9e9d95e13..885f6c162 100644 --- a/authentik/policies/reputation/api.py +++ b/authentik/policies/reputation/api.py @@ -47,6 +47,7 @@ class ReputationSerializer(ModelSerializer): "identifier", "ip", "ip_geo_data", + "ip_asn_data", "score", "updated", ] diff --git a/authentik/policies/reputation/migrations/0006_reputation_ip_asn_data.py b/authentik/policies/reputation/migrations/0006_reputation_ip_asn_data.py new file mode 100644 index 000000000..05557392e --- /dev/null +++ b/authentik/policies/reputation/migrations/0006_reputation_ip_asn_data.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.7 on 2023-12-05 22:20 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("authentik_policies_reputation", "0005_reputation_expires_reputation_expiring"), + ] + + operations = [ + migrations.AddField( + model_name="reputation", + name="ip_asn_data", + field=models.JSONField(default=dict), + ), + ] diff --git a/authentik/policies/reputation/models.py b/authentik/policies/reputation/models.py index 7fccfa11a..ea8ac2bd6 100644 --- a/authentik/policies/reputation/models.py +++ b/authentik/policies/reputation/models.py @@ -76,6 +76,7 @@ class Reputation(ExpiringModel, SerializerModel): identifier = models.TextField() ip = models.GenericIPAddressField() ip_geo_data = models.JSONField(default=dict) + ip_asn_data = models.JSONField(default=dict) score = models.BigIntegerField(default=0) expires = models.DateTimeField(default=reputation_expiry) diff --git a/authentik/policies/reputation/tasks.py b/authentik/policies/reputation/tasks.py index 7fd7b775f..ac65d1748 100644 --- a/authentik/policies/reputation/tasks.py +++ b/authentik/policies/reputation/tasks.py @@ -2,7 +2,8 @@ from django.core.cache import cache from structlog.stdlib import get_logger -from authentik.events.geo import GEOIP_READER +from authentik.events.context_processors.asn import ASN_CONTEXT_PROCESSOR +from authentik.events.context_processors.geoip import GEOIP_CONTEXT_PROCESSOR from authentik.events.monitored_tasks import ( MonitoredTask, TaskResult, @@ -26,7 +27,8 @@ def save_reputation(self: MonitoredTask): ip=score["ip"], identifier=score["identifier"], ) - rep.ip_geo_data = GEOIP_READER.city_dict(score["ip"]) or {} + rep.ip_geo_data = GEOIP_CONTEXT_PROCESSOR.city_dict(score["ip"]) or {} + rep.ip_asn_data = ASN_CONTEXT_PROCESSOR.asn_dict(score["ip"]) or {} rep.score = score["score"] objects_to_update.append(rep) Reputation.objects.bulk_update(objects_to_update, ["score", "ip_geo_data"]) diff --git a/authentik/policies/types.py b/authentik/policies/types.py index 5e59dbbf0..c9e2e8d86 100644 --- a/authentik/policies/types.py +++ b/authentik/policies/types.py @@ -8,7 +8,7 @@ from django.db.models import Model from django.http import HttpRequest from structlog.stdlib import get_logger -from authentik.events.geo import GEOIP_READER +from authentik.events.context_processors.base import get_context_processors if TYPE_CHECKING: from authentik.core.models import User @@ -37,15 +37,9 @@ class PolicyRequest: def set_http_request(self, request: HttpRequest): # pragma: no cover """Load data from HTTP request, including geoip when enabled""" - from authentik.root.middleware import ClientIPMiddleware - self.http_request = request - if not GEOIP_READER.enabled: - return - client_ip = ClientIPMiddleware.get_client_ip(request) - if not client_ip: - return - self.context["geoip"] = GEOIP_READER.city(client_ip) + for processor in get_context_processors(): + self.context.update(processor.enrich_context(request)) @property def should_cache(self) -> bool: diff --git a/authentik/providers/oauth2/migrations/0017_accesstoken_session_id_authorizationcode_session_id_and_more.py b/authentik/providers/oauth2/migrations/0017_accesstoken_session_id_authorizationcode_session_id_and_more.py new file mode 100644 index 000000000..9a8e8d210 --- /dev/null +++ b/authentik/providers/oauth2/migrations/0017_accesstoken_session_id_authorizationcode_session_id_and_more.py @@ -0,0 +1,27 @@ +# Generated by Django 5.0 on 2023-12-22 23:20 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("authentik_providers_oauth2", "0016_alter_refreshtoken_token"), + ] + + operations = [ + migrations.AddField( + model_name="accesstoken", + name="session_id", + field=models.CharField(blank=True, default=""), + ), + migrations.AddField( + model_name="authorizationcode", + name="session_id", + field=models.CharField(blank=True, default=""), + ), + migrations.AddField( + model_name="refreshtoken", + name="session_id", + field=models.CharField(blank=True, default=""), + ), + ] diff --git a/authentik/providers/oauth2/models.py b/authentik/providers/oauth2/models.py index e9a2f731a..2917f607f 100644 --- a/authentik/providers/oauth2/models.py +++ b/authentik/providers/oauth2/models.py @@ -296,6 +296,7 @@ class BaseGrantModel(models.Model): revoked = models.BooleanField(default=False) _scope = models.TextField(default="", verbose_name=_("Scopes")) auth_time = models.DateTimeField(verbose_name="Authentication time") + session_id = models.CharField(default="", blank=True) @property def scope(self) -> list[str]: diff --git a/authentik/providers/oauth2/views/authorize.py b/authentik/providers/oauth2/views/authorize.py index 8c31192bf..520f02d65 100644 --- a/authentik/providers/oauth2/views/authorize.py +++ b/authentik/providers/oauth2/views/authorize.py @@ -1,6 +1,7 @@ """authentik OAuth2 Authorization views""" from dataclasses import dataclass, field from datetime import timedelta +from hashlib import sha256 from json import dumps from re import error as RegexError from re import fullmatch @@ -282,6 +283,7 @@ class OAuthAuthorizationParams: expires=now + timedelta_from_string(self.provider.access_code_validity), scope=self.scope, nonce=self.nonce, + session_id=sha256(request.session.session_key.encode("ascii")).hexdigest(), ) if self.code_challenge and self.code_challenge_method: @@ -569,6 +571,7 @@ class OAuthFulfillmentStage(StageView): expires=access_token_expiry, provider=self.provider, auth_time=auth_event.created if auth_event else now, + session_id=sha256(self.request.session.session_key.encode("ascii")).hexdigest(), ) id_token = IDToken.new(self.provider, token, self.request) diff --git a/authentik/providers/oauth2/views/token.py b/authentik/providers/oauth2/views/token.py index 0c0d7f4a8..93578d6ce 100644 --- a/authentik/providers/oauth2/views/token.py +++ b/authentik/providers/oauth2/views/token.py @@ -487,6 +487,7 @@ class TokenView(View): # Keep same scopes as previous token scope=self.params.authorization_code.scope, auth_time=self.params.authorization_code.auth_time, + session_id=self.params.authorization_code.session_id, ) access_token.id_token = IDToken.new( self.provider, @@ -502,6 +503,7 @@ class TokenView(View): expires=refresh_token_expiry, provider=self.provider, auth_time=self.params.authorization_code.auth_time, + session_id=self.params.authorization_code.session_id, ) id_token = IDToken.new( self.provider, @@ -539,6 +541,7 @@ class TokenView(View): # Keep same scopes as previous token scope=self.params.refresh_token.scope, auth_time=self.params.refresh_token.auth_time, + session_id=self.params.refresh_token.session_id, ) access_token.id_token = IDToken.new( self.provider, @@ -554,6 +557,7 @@ class TokenView(View): expires=refresh_token_expiry, provider=self.provider, auth_time=self.params.refresh_token.auth_time, + session_id=self.params.refresh_token.session_id, ) id_token = IDToken.new( self.provider, diff --git a/authentik/providers/proxy/tasks.py b/authentik/providers/proxy/tasks.py index aec8e669a..1496ff6ec 100644 --- a/authentik/providers/proxy/tasks.py +++ b/authentik/providers/proxy/tasks.py @@ -1,4 +1,6 @@ """proxy provider tasks""" +from hashlib import sha256 + from asgiref.sync import async_to_sync from channels.layers import get_channel_layer from django.db import DatabaseError, InternalError, ProgrammingError @@ -23,6 +25,7 @@ def proxy_set_defaults(): def proxy_on_logout(session_id: str): """Update outpost instances connected to a single outpost""" layer = get_channel_layer() + hashed_session_id = sha256(session_id.encode("ascii")).hexdigest() for outpost in Outpost.objects.filter(type=OutpostType.PROXY): group = OUTPOST_GROUP % {"outpost_pk": str(outpost.pk)} async_to_sync(layer.group_send)( @@ -30,6 +33,6 @@ def proxy_on_logout(session_id: str): { "type": "event.provider.specific", "sub_type": "logout", - "session_id": session_id, + "session_id": hashed_session_id, }, ) diff --git a/authentik/providers/scim/api/providers.py b/authentik/providers/scim/api/providers.py index 546e62e65..ddca8cfef 100644 --- a/authentik/providers/scim/api/providers.py +++ b/authentik/providers/scim/api/providers.py @@ -2,6 +2,7 @@ from django.utils.text import slugify from drf_spectacular.utils import OpenApiResponse, extend_schema from rest_framework.decorators import action +from rest_framework.fields import BooleanField from rest_framework.request import Request from rest_framework.response import Response from rest_framework.viewsets import ModelViewSet @@ -9,6 +10,7 @@ from rest_framework.viewsets import ModelViewSet from authentik.admin.api.tasks import TaskSerializer from authentik.core.api.providers import ProviderSerializer from authentik.core.api.used_by import UsedByMixin +from authentik.core.api.utils import PassiveSerializer from authentik.events.monitored_tasks import TaskInfo from authentik.providers.scim.models import SCIMProvider @@ -37,6 +39,13 @@ class SCIMProviderSerializer(ProviderSerializer): extra_kwargs = {} +class SCIMSyncStatusSerializer(PassiveSerializer): + """SCIM Provider sync status""" + + is_running = BooleanField(read_only=True) + tasks = TaskSerializer(many=True, read_only=True) + + class SCIMProviderViewSet(UsedByMixin, ModelViewSet): """SCIMProvider Viewset""" @@ -48,15 +57,18 @@ class SCIMProviderViewSet(UsedByMixin, ModelViewSet): @extend_schema( responses={ - 200: TaskSerializer(), + 200: SCIMSyncStatusSerializer(), 404: OpenApiResponse(description="Task not found"), } ) @action(methods=["GET"], detail=True, pagination_class=None, filter_backends=[]) def sync_status(self, request: Request, pk: int) -> Response: """Get provider's sync status""" - provider = self.get_object() + provider: SCIMProvider = self.get_object() task = TaskInfo.by_name(f"scim_sync:{slugify(provider.name)}") - if not task: - return Response(status=404) - return Response(TaskSerializer(task).data) + tasks = [task] if task else [] + status = { + "tasks": tasks, + "is_running": provider.sync_lock.locked(), + } + return Response(SCIMSyncStatusSerializer(status).data) diff --git a/authentik/providers/scim/models.py b/authentik/providers/scim/models.py index ac27288f3..b43a59375 100644 --- a/authentik/providers/scim/models.py +++ b/authentik/providers/scim/models.py @@ -1,11 +1,14 @@ """SCIM Provider models""" +from django.core.cache import cache from django.db import models from django.db.models import QuerySet from django.utils.translation import gettext_lazy as _ from guardian.shortcuts import get_anonymous_user +from redis.lock import Lock from rest_framework.serializers import Serializer from authentik.core.models import BackchannelProvider, Group, PropertyMapping, User, UserTypes +from authentik.providers.scim.clients import PAGE_TIMEOUT class SCIMProvider(BackchannelProvider): @@ -27,6 +30,15 @@ class SCIMProvider(BackchannelProvider): help_text=_("Property mappings used for group creation/updating."), ) + @property + def sync_lock(self) -> Lock: + """Redis lock for syncing SCIM to prevent multiple parallel syncs happening""" + return Lock( + cache.client.get_client(), + name=f"goauthentik.io/providers/scim/sync-{str(self.pk)}", + timeout=(60 * 60 * PAGE_TIMEOUT) * 3, + ) + def get_user_qs(self) -> QuerySet[User]: """Get queryset of all users with consistent ordering according to the provider's settings""" diff --git a/authentik/providers/scim/tasks.py b/authentik/providers/scim/tasks.py index c0c074ea4..7b16b293e 100644 --- a/authentik/providers/scim/tasks.py +++ b/authentik/providers/scim/tasks.py @@ -47,6 +47,10 @@ def scim_sync(self: MonitoredTask, provider_pk: int) -> None: ).first() if not provider: return + lock = provider.sync_lock + if lock.locked(): + LOGGER.debug("SCIM sync locked, skipping task", source=provider.name) + return self.set_uid(slugify(provider.name)) result = TaskResult(TaskResultStatus.SUCCESSFUL, []) result.messages.append(_("Starting full SCIM sync")) diff --git a/authentik/root/middleware.py b/authentik/root/middleware.py index 5eca4a6e5..ba5465d4f 100644 --- a/authentik/root/middleware.py +++ b/authentik/root/middleware.py @@ -56,7 +56,7 @@ class SessionMiddleware(UpstreamSessionMiddleware): pass return session_key - def process_request(self, request): + def process_request(self, request: HttpRequest): raw_session = request.COOKIES.get(settings.SESSION_COOKIE_NAME) session_key = SessionMiddleware.decode_session_key(raw_session) request.session = self.SessionStore(session_key) @@ -297,7 +297,7 @@ class LoggingMiddleware: response = self.get_response(request) status_code = response.status_code kwargs = { - "request_id": request.request_id, + "request_id": getattr(request, "request_id", None), } kwargs.update(getattr(response, "ak_context", {})) self.log(request, status_code, int((default_timer() - start) * 1000), **kwargs) diff --git a/authentik/root/settings.py b/authentik/root/settings.py index 60f08eacb..c71ae37b6 100644 --- a/authentik/root/settings.py +++ b/authentik/root/settings.py @@ -230,7 +230,7 @@ MIDDLEWARE = [ "authentik.root.middleware.LoggingMiddleware", "django_prometheus.middleware.PrometheusBeforeMiddleware", "authentik.root.middleware.ClientIPMiddleware", - "authentik.root.middleware.SessionMiddleware", + "authentik.stages.user_login.middleware.BoundSessionMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "authentik.core.middleware.RequestIDMiddleware", "authentik.brands.middleware.BrandMiddleware", diff --git a/authentik/root/test_runner.py b/authentik/root/test_runner.py index 6e99fde3e..5c5c058e2 100644 --- a/authentik/root/test_runner.py +++ b/authentik/root/test_runner.py @@ -31,7 +31,8 @@ class PytestTestRunner(DiscoverRunner): # pragma: no cover settings.TEST = True settings.CELERY["task_always_eager"] = True - CONFIG.set("geoip", "tests/GeoLite2-City-Test.mmdb") + CONFIG.set("events.context_processors.geoip", "tests/GeoLite2-City-Test.mmdb") + CONFIG.set("events.context_processors.asn", "tests/GeoLite2-ASN-Test.mmdb") CONFIG.set("blueprints_dir", "./blueprints") CONFIG.set( "outposts.container_image_base", diff --git a/authentik/sources/ldap/password.py b/authentik/sources/ldap/password.py index d4e0dff4e..0593b2078 100644 --- a/authentik/sources/ldap/password.py +++ b/authentik/sources/ldap/password.py @@ -4,7 +4,11 @@ from re import split from typing import Optional from ldap3 import BASE -from ldap3.core.exceptions import LDAPAttributeError, LDAPUnwillingToPerformResult +from ldap3.core.exceptions import ( + LDAPAttributeError, + LDAPNoSuchAttributeResult, + LDAPUnwillingToPerformResult, +) from structlog.stdlib import get_logger from authentik.core.models import User @@ -97,7 +101,7 @@ class LDAPPasswordChanger: return try: self._connection.extend.microsoft.modify_password(user_dn, password) - except (LDAPAttributeError, LDAPUnwillingToPerformResult): + except (LDAPAttributeError, LDAPUnwillingToPerformResult, LDAPNoSuchAttributeResult): self._connection.extend.standard.modify_password(user_dn, new_password=password) def _ad_check_password_existing(self, password: str, user_dn: str) -> bool: diff --git a/authentik/sources/oauth/types/azure_ad.py b/authentik/sources/oauth/types/azure_ad.py index 39c744843..a247cea5d 100644 --- a/authentik/sources/oauth/types/azure_ad.py +++ b/authentik/sources/oauth/types/azure_ad.py @@ -4,8 +4,8 @@ from typing import Any from structlog.stdlib import get_logger from authentik.sources.oauth.clients.oauth2 import UserprofileHeaderAuthClient +from authentik.sources.oauth.types.oidc import OpenIDConnectOAuth2Callback from authentik.sources.oauth.types.registry import SourceType, registry -from authentik.sources.oauth.views.callback import OAuthCallback from authentik.sources.oauth.views.redirect import OAuthRedirect LOGGER = get_logger() @@ -20,7 +20,7 @@ class AzureADOAuthRedirect(OAuthRedirect): } -class AzureADOAuthCallback(OAuthCallback): +class AzureADOAuthCallback(OpenIDConnectOAuth2Callback): """AzureAD OAuth2 Callback""" client_class = UserprofileHeaderAuthClient @@ -50,7 +50,7 @@ class AzureADType(SourceType): authorization_url = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize" access_token_url = "https://login.microsoftonline.com/common/oauth2/v2.0/token" # nosec - profile_url = "https://graph.microsoft.com/v1.0/me" + profile_url = "https://login.microsoftonline.com/common/openid/userinfo" oidc_well_known_url = ( "https://login.microsoftonline.com/common/.well-known/openid-configuration" ) diff --git a/authentik/sources/oauth/types/oidc.py b/authentik/sources/oauth/types/oidc.py index 7ebd24579..bd6853117 100644 --- a/authentik/sources/oauth/types/oidc.py +++ b/authentik/sources/oauth/types/oidc.py @@ -23,7 +23,7 @@ class OpenIDConnectOAuth2Callback(OAuthCallback): client_class = UserprofileHeaderAuthClient def get_user_id(self, info: dict[str, str]) -> str: - return info.get("sub", "") + return info.get("sub", None) def get_user_enroll_context( self, diff --git a/authentik/sources/oauth/types/okta.py b/authentik/sources/oauth/types/okta.py index 8a305bce7..2de02edde 100644 --- a/authentik/sources/oauth/types/okta.py +++ b/authentik/sources/oauth/types/okta.py @@ -3,8 +3,8 @@ from typing import Any from authentik.sources.oauth.clients.oauth2 import UserprofileHeaderAuthClient from authentik.sources.oauth.models import OAuthSource +from authentik.sources.oauth.types.oidc import OpenIDConnectOAuth2Callback from authentik.sources.oauth.types.registry import SourceType, registry -from authentik.sources.oauth.views.callback import OAuthCallback from authentik.sources.oauth.views.redirect import OAuthRedirect @@ -17,7 +17,7 @@ class OktaOAuthRedirect(OAuthRedirect): } -class OktaOAuth2Callback(OAuthCallback): +class OktaOAuth2Callback(OpenIDConnectOAuth2Callback): """Okta OAuth2 Callback""" # Okta has the same quirk as azure and throws an error if the access token @@ -25,9 +25,6 @@ class OktaOAuth2Callback(OAuthCallback): # see https://github.com/goauthentik/authentik/issues/1910 client_class = UserprofileHeaderAuthClient - def get_user_id(self, info: dict[str, str]) -> str: - return info.get("sub", "") - def get_user_enroll_context( self, info: dict[str, Any], diff --git a/authentik/sources/oauth/types/twitch.py b/authentik/sources/oauth/types/twitch.py index 5fa9fad74..62e7b94d4 100644 --- a/authentik/sources/oauth/types/twitch.py +++ b/authentik/sources/oauth/types/twitch.py @@ -3,8 +3,8 @@ from json import dumps from typing import Any, Optional from authentik.sources.oauth.clients.oauth2 import UserprofileHeaderAuthClient +from authentik.sources.oauth.types.oidc import OpenIDConnectOAuth2Callback from authentik.sources.oauth.types.registry import SourceType, registry -from authentik.sources.oauth.views.callback import OAuthCallback from authentik.sources.oauth.views.redirect import OAuthRedirect @@ -27,14 +27,11 @@ class TwitchOAuthRedirect(OAuthRedirect): } -class TwitchOAuth2Callback(OAuthCallback): +class TwitchOAuth2Callback(OpenIDConnectOAuth2Callback): """Twitch OAuth2 Callback""" client_class = TwitchClient - def get_user_id(self, info: dict[str, str]) -> str: - return info.get("sub", "") - def get_user_enroll_context( self, info: dict[str, Any], diff --git a/authentik/stages/email/stage.py b/authentik/stages/email/stage.py index 0fa36bfbe..160a68e92 100644 --- a/authentik/stages/email/stage.py +++ b/authentik/stages/email/stage.py @@ -110,7 +110,7 @@ class EmailStageView(ChallengeStageView): try: message = TemplateEmailMessage( subject=_(current_stage.subject), - to=[email], + to=[f"{pending_user.name} <{email}>"], language=pending_user.locale(self.request), template_name=current_stage.template, template_context={ diff --git a/authentik/stages/email/templates/email/account_confirmation.txt b/authentik/stages/email/templates/email/account_confirmation.txt new file mode 100644 index 000000000..0a1fc70d1 --- /dev/null +++ b/authentik/stages/email/templates/email/account_confirmation.txt @@ -0,0 +1,8 @@ +{% load i18n %}{% translate "Welcome!" %} + +{% translate "We're excited to have you get started. First, you need to confirm your account. Just open the link below." %} + +{{ url }} + +-- +Powered by goauthentik.io. diff --git a/authentik/stages/email/templates/email/event_notification.html b/authentik/stages/email/templates/email/event_notification.html index e34563404..7ca78fc64 100644 --- a/authentik/stages/email/templates/email/event_notification.html +++ b/authentik/stages/email/templates/email/event_notification.html @@ -44,7 +44,7 @@ {% blocktranslate with name=source.from %} - This email was sent from the notification transport {{name}}. + This email was sent from the notification transport {{ name }}. {% endblocktranslate %} diff --git a/authentik/stages/email/templates/email/event_notification.txt b/authentik/stages/email/templates/email/event_notification.txt new file mode 100644 index 000000000..bd7d92896 --- /dev/null +++ b/authentik/stages/email/templates/email/event_notification.txt @@ -0,0 +1,18 @@ +{% load authentik_stages_email %}{% load i18n %}{% translate "Dear authentik user," %} + +{% translate "The following notification was created:" %} + + {{ body|indent }} + +{% if key_value %} +{% translate "Additional attributes:" %} +{% for key, value in key_value.items %} + {{ key }}: {{ value|indent }}{% endfor %} +{% endif %} + +{% if source %}{% blocktranslate with name=source.from %} +This email was sent from the notification transport {{ name }}. +{% endblocktranslate %}{% endif %} + +-- +Powered by goauthentik.io. diff --git a/authentik/stages/email/templates/email/password_reset.txt b/authentik/stages/email/templates/email/password_reset.txt new file mode 100644 index 000000000..0c13ad2f8 --- /dev/null +++ b/authentik/stages/email/templates/email/password_reset.txt @@ -0,0 +1,12 @@ +{% load i18n %}{% load humanize %}{% blocktrans with username=user.username %}Hi {{ username }},{% endblocktrans %} + +{% blocktrans %} +You recently requested to change your password for your authentik account. Use the link below to set a new password. +{% endblocktrans %} +{{ url }} +{% blocktrans with expires=expires|naturaltime %} +If you did not request a password change, please ignore this Email. The link above is valid for {{ expires }}. +{% endblocktrans %} + +-- +Powered by goauthentik.io. diff --git a/authentik/stages/email/templates/email/setup.txt b/authentik/stages/email/templates/email/setup.txt new file mode 100644 index 000000000..6d0eb0ce0 --- /dev/null +++ b/authentik/stages/email/templates/email/setup.txt @@ -0,0 +1,7 @@ +{% load i18n %}authentik Test-Email +{% blocktrans %} +This is a test email to inform you, that you've successfully configured authentik emails. +{% endblocktrans %} + +-- +Powered by goauthentik.io. diff --git a/authentik/stages/email/templatetags/authentik_stages_email.py b/authentik/stages/email/templatetags/authentik_stages_email.py index 7623c6c71..9b3dfb194 100644 --- a/authentik/stages/email/templatetags/authentik_stages_email.py +++ b/authentik/stages/email/templatetags/authentik_stages_email.py @@ -29,3 +29,9 @@ def inline_static_binary(path: str) -> str: b64content = b64encode(_file.read().encode()) return f"data:image/{result.suffix};base64,{b64content.decode('utf-8')}" return path + + +@register.filter(name="indent") +def indent_string(val, num_spaces=4): + """Intent text by a given amount of spaces""" + return val.replace("\n", "\n" + " " * num_spaces) diff --git a/authentik/stages/email/tests/test_sending.py b/authentik/stages/email/tests/test_sending.py index 424d474ce..5c67c8842 100644 --- a/authentik/stages/email/tests/test_sending.py +++ b/authentik/stages/email/tests/test_sending.py @@ -58,9 +58,11 @@ class TestEmailStageSending(FlowTestCase): events = Event.objects.filter(action=EventAction.EMAIL_SENT) self.assertEqual(len(events), 1) event = events.first() - self.assertEqual(event.context["message"], f"Email to {self.user.email} sent") + self.assertEqual( + event.context["message"], f"Email to {self.user.name} <{self.user.email}> sent" + ) self.assertEqual(event.context["subject"], "authentik") - self.assertEqual(event.context["to_email"], [self.user.email]) + self.assertEqual(event.context["to_email"], [f"{self.user.name} <{self.user.email}>"]) self.assertEqual(event.context["from_email"], "system@authentik.local") def test_pending_fake_user(self): diff --git a/authentik/stages/email/tests/test_stage.py b/authentik/stages/email/tests/test_stage.py index 853bc2a77..277549937 100644 --- a/authentik/stages/email/tests/test_stage.py +++ b/authentik/stages/email/tests/test_stage.py @@ -94,7 +94,7 @@ class TestEmailStage(FlowTestCase): self.assertEqual(response.status_code, 200) self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].subject, "authentik") - self.assertEqual(mail.outbox[0].to, [self.user.email]) + self.assertEqual(mail.outbox[0].to, [f"{self.user.name} <{self.user.email}>"]) @patch( "authentik.stages.email.models.EmailStage.backend_class", @@ -114,7 +114,7 @@ class TestEmailStage(FlowTestCase): self.assertEqual(response.status_code, 200) self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].subject, "authentik") - self.assertEqual(mail.outbox[0].to, ["foo@bar.baz"]) + self.assertEqual(mail.outbox[0].to, [f"{self.user.name} "]) @patch( "authentik.stages.email.models.EmailStage.backend_class", diff --git a/authentik/stages/email/utils.py b/authentik/stages/email/utils.py index a6edd4609..8f7f702ce 100644 --- a/authentik/stages/email/utils.py +++ b/authentik/stages/email/utils.py @@ -4,6 +4,7 @@ from functools import lru_cache from pathlib import Path from django.core.mail import EmailMultiAlternatives +from django.template.exceptions import TemplateDoesNotExist from django.template.loader import render_to_string from django.utils import translation @@ -24,9 +25,15 @@ class TemplateEmailMessage(EmailMultiAlternatives): """Wrapper around EmailMultiAlternatives with integrated template rendering""" def __init__(self, template_name=None, template_context=None, language="", **kwargs): + super().__init__(**kwargs) with translation.override(language): html_content = render_to_string(template_name, template_context) - super().__init__(**kwargs) - self.content_subtype = "html" + try: + text_content = render_to_string( + template_name.replace("html", "txt"), template_context + ) + self.body = text_content + except TemplateDoesNotExist: + pass self.mixed_subtype = "related" self.attach_alternative(html_content, "text/html") diff --git a/authentik/stages/user_login/api.py b/authentik/stages/user_login/api.py index c0acc8d5c..dad5f777c 100644 --- a/authentik/stages/user_login/api.py +++ b/authentik/stages/user_login/api.py @@ -15,6 +15,8 @@ class UserLoginStageSerializer(StageSerializer): "session_duration", "terminate_other_sessions", "remember_me_offset", + "network_binding", + "geoip_binding", ] diff --git a/authentik/stages/user_login/middleware.py b/authentik/stages/user_login/middleware.py new file mode 100644 index 000000000..8fea4c408 --- /dev/null +++ b/authentik/stages/user_login/middleware.py @@ -0,0 +1,212 @@ +"""Sessions bound to ASN/Network and GeoIP/Continent/etc""" +from django.conf import settings +from django.contrib.auth.middleware import AuthenticationMiddleware +from django.contrib.auth.signals import user_logged_out +from django.http.request import HttpRequest +from django.shortcuts import redirect +from structlog.stdlib import get_logger + +from authentik.core.models import AuthenticatedSession +from authentik.events.context_processors.asn import ASN_CONTEXT_PROCESSOR +from authentik.events.context_processors.geoip import GEOIP_CONTEXT_PROCESSOR +from authentik.lib.sentry import SentryIgnoredException +from authentik.root.middleware import ClientIPMiddleware, SessionMiddleware +from authentik.stages.user_login.models import GeoIPBinding, NetworkBinding + +SESSION_KEY_LAST_IP = "authentik/stages/user_login/last_ip" +SESSION_KEY_BINDING_NET = "authentik/stages/user_login/binding/net" +SESSION_KEY_BINDING_GEO = "authentik/stages/user_login/binding/geo" +LOGGER = get_logger() + + +class SessionBindingBroken(SentryIgnoredException): + """Session binding was broken due to specified `reason`""" + + # pylint: disable=too-many-arguments + def __init__( + self, reason: str, old_value: str, new_value: str, old_ip: str, new_ip: str + ) -> None: + self.reason = reason + self.old_value = old_value + self.new_value = new_value + self.old_ip = old_ip + self.new_ip = new_ip + + def __repr__(self) -> str: + return ( + f"Session binding broken due to {self.reason}; " + f"old value: {self.old_value}, new value: {self.new_value}" + ) + + def to_event(self) -> dict: + """Convert to dict for usage with event""" + return { + "logout_reason": "Session binding broken", + "binding": { + "reason": self.reason, + "previous_value": self.old_value, + "new_value": self.new_value, + }, + "ip": { + "previous": self.old_ip, + "new": self.new_ip, + }, + } + + +def logout_extra(request: HttpRequest, exc: SessionBindingBroken): + """Similar to django's logout method, but able to carry more info to the signal""" + # Dispatch the signal before the user is logged out so the receivers have a + # chance to find out *who* logged out. + user = getattr(request, "user", None) + if not getattr(user, "is_authenticated", True): + user = None + user_logged_out.send( + sender=user.__class__, request=request, user=user, event_extra=exc.to_event() + ) + request.session.flush() + if hasattr(request, "user"): + from django.contrib.auth.models import AnonymousUser + + request.user = AnonymousUser() + + +class BoundSessionMiddleware(SessionMiddleware): + """Sessions bound to ASN/Network and GeoIP/Continent/etc""" + + def process_request(self, request: HttpRequest): + super().process_request(request) + try: + self.recheck_session(request) + except SessionBindingBroken as exc: + LOGGER.warning("Session binding broken", exc=exc) + # At this point, we need to logout the current user + # however since this middleware has to run before the `AuthenticationMiddleware` + # we don't have access to the user yet + # Logout will still work, however event logs won't display the user being logged out + AuthenticationMiddleware(lambda request: request).process_request(request) + logout_extra(request, exc) + request.session.clear() + return redirect(settings.LOGIN_URL) + return None + + def recheck_session(self, request: HttpRequest): + """Check if a session is still valid with a changed IP""" + last_ip = request.session.get(SESSION_KEY_LAST_IP) + new_ip = ClientIPMiddleware.get_client_ip(request) + # Check changed IP + if new_ip == last_ip: + return + configured_binding_net = request.session.get( + SESSION_KEY_BINDING_NET, NetworkBinding.NO_BINDING + ) + configured_binding_geo = request.session.get( + SESSION_KEY_BINDING_GEO, GeoIPBinding.NO_BINDING + ) + if configured_binding_net != NetworkBinding.NO_BINDING: + self.recheck_session_net(configured_binding_net, last_ip, new_ip) + if configured_binding_geo != GeoIPBinding.NO_BINDING: + self.recheck_session_geo(configured_binding_geo, last_ip, new_ip) + # If we got to this point without any error being raised, we need to + # update the last saved IP to the current one + request.session[SESSION_KEY_LAST_IP] = new_ip + AuthenticatedSession.objects.filter(session_key=request.session.session_key).update( + last_ip=new_ip, last_user_agent=request.META.get("HTTP_USER_AGENT", "") + ) + + def recheck_session_net(self, binding: NetworkBinding, last_ip: str, new_ip: str): + """Check network/ASN binding""" + last_asn = ASN_CONTEXT_PROCESSOR.asn(last_ip) + new_asn = ASN_CONTEXT_PROCESSOR.asn(new_ip) + if not last_asn or not new_asn: + raise SessionBindingBroken( + "network.missing", + ASN_CONTEXT_PROCESSOR.asn_to_dict(last_asn), + ASN_CONTEXT_PROCESSOR.asn_to_dict(new_asn), + last_ip, + new_ip, + ) + if binding in [ + NetworkBinding.BIND_ASN, + NetworkBinding.BIND_ASN_NETWORK, + NetworkBinding.BIND_ASN_NETWORK_IP, + ]: + # Check ASN which is required for all 3 modes + if last_asn.autonomous_system_number != new_asn.autonomous_system_number: + raise SessionBindingBroken( + "network.asn", + last_asn.autonomous_system_number, + new_asn.autonomous_system_number, + last_ip, + new_ip, + ) + if binding in [NetworkBinding.BIND_ASN_NETWORK, NetworkBinding.BIND_ASN_NETWORK_IP]: + # Check Network afterwards + if last_asn.network != new_asn.network: + raise SessionBindingBroken( + "network.asn_network", + last_asn.network, + new_asn.network, + last_ip, + new_ip, + ) + if binding in [NetworkBinding.BIND_ASN_NETWORK_IP]: + # Only require strict IP checking + if last_ip != new_ip: + raise SessionBindingBroken( + "network.ip", + last_ip, + new_ip, + last_ip, + new_ip, + ) + + def recheck_session_geo(self, binding: GeoIPBinding, last_ip: str, new_ip: str): + """Check GeoIP binding""" + last_geo = GEOIP_CONTEXT_PROCESSOR.city(last_ip) + new_geo = GEOIP_CONTEXT_PROCESSOR.city(new_ip) + if not last_geo or not new_geo: + raise SessionBindingBroken( + "geoip.missing", + GEOIP_CONTEXT_PROCESSOR.city_to_dict(last_geo), + GEOIP_CONTEXT_PROCESSOR.city_to_dict(new_geo), + last_ip, + new_ip, + ) + if binding in [ + GeoIPBinding.BIND_CONTINENT, + GeoIPBinding.BIND_CONTINENT_COUNTRY, + GeoIPBinding.BIND_CONTINENT_COUNTRY_CITY, + ]: + # Check Continent which is required for all 3 modes + if last_geo.continent != new_geo.continent: + raise SessionBindingBroken( + "geoip.continent", + last_geo.continent, + new_geo.continent, + last_ip, + new_ip, + ) + if binding in [ + GeoIPBinding.BIND_CONTINENT_COUNTRY, + GeoIPBinding.BIND_CONTINENT_COUNTRY_CITY, + ]: + # Check Country afterwards + if last_geo.country != new_geo.country: + raise SessionBindingBroken( + "geoip.country", + last_geo.country, + new_geo.country, + last_ip, + new_ip, + ) + if binding in [GeoIPBinding.BIND_CONTINENT_COUNTRY_CITY]: + # Check city afterwards + if last_geo.city != new_geo.city: + raise SessionBindingBroken( + "geoip.city", + last_geo.city, + new_geo.city, + last_ip, + new_ip, + ) diff --git a/authentik/stages/user_login/migrations/0006_userloginstage_geoip_binding_and_more.py b/authentik/stages/user_login/migrations/0006_userloginstage_geoip_binding_and_more.py new file mode 100644 index 000000000..155c42b81 --- /dev/null +++ b/authentik/stages/user_login/migrations/0006_userloginstage_geoip_binding_and_more.py @@ -0,0 +1,40 @@ +# Generated by Django 4.2.7 on 2023-12-14 10:46 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("authentik_stages_user_login", "0005_userloginstage_remember_me_offset"), + ] + + operations = [ + migrations.AddField( + model_name="userloginstage", + name="geoip_binding", + field=models.TextField( + choices=[ + ("no_binding", "No Binding"), + ("bind_continent", "Bind Continent"), + ("bind_continent_country", "Bind Continent Country"), + ("bind_continent_country_city", "Bind Continent Country City"), + ], + default="no_binding", + help_text="Bind sessions created by this stage to the configured GeoIP location", + ), + ), + migrations.AddField( + model_name="userloginstage", + name="network_binding", + field=models.TextField( + choices=[ + ("no_binding", "No Binding"), + ("bind_asn", "Bind Asn"), + ("bind_asn_network", "Bind Asn Network"), + ("bind_asn_network_ip", "Bind Asn Network Ip"), + ], + default="no_binding", + help_text="Bind sessions created by this stage to the configured network", + ), + ), + ] diff --git a/authentik/stages/user_login/models.py b/authentik/stages/user_login/models.py index 01c85b4fa..27bb6f3bf 100644 --- a/authentik/stages/user_login/models.py +++ b/authentik/stages/user_login/models.py @@ -9,6 +9,26 @@ from authentik.flows.models import Stage from authentik.lib.utils.time import timedelta_string_validator +class NetworkBinding(models.TextChoices): + """Network session binding modes""" + + NO_BINDING = "no_binding" + BIND_ASN = "bind_asn" # Bind to ASN only + BIND_ASN_NETWORK = "bind_asn_network" # Bind to ASN and Network + BIND_ASN_NETWORK_IP = "bind_asn_network_ip" # Bind to ASN, Network and IP + + +class GeoIPBinding(models.TextChoices): + """Geo session binding modes""" + + NO_BINDING = "no_binding" + BIND_CONTINENT = "bind_continent" # Bind to continent only + BIND_CONTINENT_COUNTRY = "bind_continent_country" # Bind to continent and country + BIND_CONTINENT_COUNTRY_CITY = ( + "bind_continent_country_city" # Bind to continent, country and city + ) + + class UserLoginStage(Stage): """Attaches the currently pending user to the current session.""" @@ -21,6 +41,16 @@ class UserLoginStage(Stage): "(Format: hours=-1;minutes=-2;seconds=-3)" ), ) + network_binding = models.TextField( + choices=NetworkBinding.choices, + default=NetworkBinding.NO_BINDING, + help_text=_("Bind sessions created by this stage to the configured network"), + ) + geoip_binding = models.TextField( + choices=GeoIPBinding.choices, + default=GeoIPBinding.NO_BINDING, + help_text=_("Bind sessions created by this stage to the configured GeoIP location"), + ) terminate_other_sessions = models.BooleanField( default=False, help_text=_("Terminate all other sessions of the user logging in.") ) diff --git a/authentik/stages/user_login/stage.py b/authentik/stages/user_login/stage.py index 0475c1b84..f771f8faa 100644 --- a/authentik/stages/user_login/stage.py +++ b/authentik/stages/user_login/stage.py @@ -1,4 +1,6 @@ """Login stage logic""" +from datetime import timedelta + from django.contrib import messages from django.contrib.auth import login from django.http import HttpRequest, HttpResponse @@ -10,8 +12,14 @@ from authentik.flows.challenge import ChallengeResponse, ChallengeTypes, WithUse from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, PLAN_CONTEXT_SOURCE from authentik.flows.stage import ChallengeStageView from authentik.lib.utils.time import timedelta_from_string +from authentik.root.middleware import ClientIPMiddleware from authentik.stages.password import BACKEND_INBUILT from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND +from authentik.stages.user_login.middleware import ( + SESSION_KEY_BINDING_GEO, + SESSION_KEY_BINDING_NET, + SESSION_KEY_LAST_IP, +) from authentik.stages.user_login.models import UserLoginStage @@ -51,6 +59,26 @@ class UserLoginStageView(ChallengeStageView): def challenge_valid(self, response: UserLoginChallengeResponse) -> HttpResponse: return self.do_login(self.request, response.validated_data["remember_me"]) + def set_session_duration(self, remember: bool) -> timedelta: + """Update the sessions' expiry""" + delta = timedelta_from_string(self.executor.current_stage.session_duration) + if remember: + offset = timedelta_from_string(self.executor.current_stage.remember_me_offset) + delta = delta + offset + if delta.total_seconds() == 0: + self.request.session.set_expiry(0) + else: + self.request.session.set_expiry(delta) + return delta + + def set_session_ip(self): + """Set the sessions' last IP and session bindings""" + stage: UserLoginStage = self.executor.current_stage + + self.request.session[SESSION_KEY_LAST_IP] = ClientIPMiddleware.get_client_ip(self.request) + self.request.session[SESSION_KEY_BINDING_NET] = stage.network_binding + self.request.session[SESSION_KEY_BINDING_GEO] = stage.geoip_binding + def do_login(self, request: HttpRequest, remember: bool = False) -> HttpResponse: """Attach the currently pending user to the current session""" if PLAN_CONTEXT_PENDING_USER not in self.executor.plan.context: @@ -64,14 +92,8 @@ class UserLoginStageView(ChallengeStageView): user: User = self.executor.plan.context[PLAN_CONTEXT_PENDING_USER] if not user.is_active: self.logger.warning("User is not active, login will not work.") - delta = timedelta_from_string(self.executor.current_stage.session_duration) - if remember: - offset = timedelta_from_string(self.executor.current_stage.remember_me_offset) - delta = delta + offset - if delta.total_seconds() == 0: - self.request.session.set_expiry(0) - else: - self.request.session.set_expiry(delta) + delta = self.set_session_duration(remember) + self.set_session_ip() login( self.request, user, diff --git a/blueprints/schema.json b/blueprints/schema.json index 47656f070..15ecdab6a 100644 --- a/blueprints/schema.json +++ b/blueprints/schema.json @@ -3874,6 +3874,11 @@ "additionalProperties": true, "title": "Ip geo data" }, + "ip_asn_data": { + "type": "object", + "additionalProperties": true, + "title": "Ip asn data" + }, "score": { "type": "integer", "minimum": -9223372036854775808, @@ -5419,13 +5424,13 @@ "type": "string", "enum": [ "apple", + "openidconnect", "azuread", "discord", "facebook", "github", "google", "mailcow", - "openidconnect", "okta", "patreon", "reddit", @@ -8286,6 +8291,28 @@ "minLength": 1, "title": "Remember me offset", "description": "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)" + }, + "network_binding": { + "type": "string", + "enum": [ + "no_binding", + "bind_asn", + "bind_asn_network", + "bind_asn_network_ip" + ], + "title": "Network binding", + "description": "Bind sessions created by this stage to the configured network" + }, + "geoip_binding": { + "type": "string", + "enum": [ + "no_binding", + "bind_continent", + "bind_continent_country", + "bind_continent_country_city" + ], + "title": "Geoip binding", + "description": "Bind sessions created by this stage to the configured GeoIP location" } }, "required": [] diff --git a/blueprints/system/providers-proxy.yaml b/blueprints/system/providers-proxy.yaml index 0086645a8..2fd4db560 100644 --- a/blueprints/system/providers-proxy.yaml +++ b/blueprints/system/providers-proxy.yaml @@ -14,8 +14,11 @@ entries: expression: | # This mapping is used by the authentik proxy. It passes extra user attributes, # which are used for example for the HTTP-Basic Authentication mapping. + session_id = None + if "token" in request.context: + session_id = request.context.get("token").session_id return { - "sid": request.http_request.session.session_key, + "sid": session_id, "ak_proxy": { "user_attributes": request.user.group_attributes(request), "is_superuser": request.user.is_superuser, diff --git a/docker-compose.yml b/docker-compose.yml index 68e7530e7..a7491656d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -32,7 +32,7 @@ services: volumes: - redis:/data server: - image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.10.4} + image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.10.5} restart: unless-stopped command: server environment: @@ -53,7 +53,7 @@ services: - postgresql - redis worker: - image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.10.4} + image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.10.5} restart: unless-stopped command: worker environment: diff --git a/go.mod b/go.mod index 8823d467a..b3e4c8e8e 100644 --- a/go.mod +++ b/go.mod @@ -23,11 +23,11 @@ require ( github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484 github.com/pires/go-proxyproto v0.7.0 github.com/prometheus/client_golang v1.17.0 - github.com/redis/go-redis/v9 v9.3.0 + github.com/redis/go-redis/v9 v9.3.1 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.2023104.3 + goauthentik.io/api/v3 v3.2023105.2 golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab golang.org/x/oauth2 v0.15.0 golang.org/x/sync v0.5.0 diff --git a/go.sum b/go.sum index a94e23a58..34c933ba3 100644 --- a/go.sum +++ b/go.sum @@ -256,8 +256,8 @@ github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdO github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= -github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0u0= -github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= +github.com/redis/go-redis/v9 v9.3.1 h1:KqdY8U+3X6z+iACvumCNxnoluToB+9Me+TvyFa21Mds= +github.com/redis/go-redis/v9 v9.3.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= @@ -309,8 +309,8 @@ go.opentelemetry.io/otel/trace v1.17.0 h1:/SWhSRHmDPOImIAetP1QAeMnZYiQXrTy4fMMYO go.opentelemetry.io/otel/trace v1.17.0/go.mod h1:I/4vKTgFclIsXRVucpH25X0mpFSczM7aHeaz0ZBLWjY= 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.2023104.3 h1:MzwdB21Q+G+wACEZiX0T1iVV4l7PjopjaVv6muqJE1M= -goauthentik.io/api/v3 v3.2023104.3/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw= +goauthentik.io/api/v3 v3.2023105.2 h1:ZUblqN5LidnCSlEZ/L19h7OnwppnAA3m5AGC7wUN0Ew= +goauthentik.io/api/v3 v3.2023105.2/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= diff --git a/internal/constants/constants.go b/internal/constants/constants.go index fb12db690..806dc131c 100644 --- a/internal/constants/constants.go +++ b/internal/constants/constants.go @@ -29,4 +29,4 @@ func UserAgent() string { return fmt.Sprintf("authentik@%s", FullVersion()) } -const VERSION = "2023.10.4" +const VERSION = "2023.10.5" diff --git a/internal/outpost/proxyv2/ws.go b/internal/outpost/proxyv2/ws.go index b75ba50fd..4632a67cb 100644 --- a/internal/outpost/proxyv2/ws.go +++ b/internal/outpost/proxyv2/ws.go @@ -36,6 +36,7 @@ func (ps *ProxyServer) handleWSMessage(ctx context.Context, args map[string]inte switch msg.SubType { case WSProviderSubTypeLogout: for _, p := range ps.apps { + ps.log.WithField("provider", p.Host).Debug("Logging out") err := p.Logout(ctx, func(c application.Claims) bool { return c.Sid == msg.SessionID }) diff --git a/locale/en/LC_MESSAGES/django.po b/locale/en/LC_MESSAGES/django.po index 86d63c8d0..b4f8c3a56 100644 --- a/locale/en/LC_MESSAGES/django.po +++ b/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-12-20 08:41+0000\n" +"POT-Creation-Date: 2023-12-27 10:56+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -343,7 +343,7 @@ msgid "Powered by authentik" msgstr "" #: authentik/core/views/apps.py:53 -#: authentik/providers/oauth2/views/authorize.py:393 +#: authentik/providers/oauth2/views/authorize.py:395 #: authentik/providers/oauth2/views/device_init.py:70 #: authentik/providers/saml/views/sso.py:70 #, python-format @@ -388,105 +388,105 @@ msgstr "" msgid "License Usage Records" msgstr "" -#: authentik/events/models.py:295 +#: authentik/events/models.py:289 msgid "Event" msgstr "" -#: authentik/events/models.py:296 +#: authentik/events/models.py:290 msgid "Events" msgstr "" -#: authentik/events/models.py:302 +#: authentik/events/models.py:296 msgid "authentik inbuilt notifications" msgstr "" -#: authentik/events/models.py:303 +#: authentik/events/models.py:297 msgid "Generic Webhook" msgstr "" -#: authentik/events/models.py:304 +#: authentik/events/models.py:298 msgid "Slack Webhook (Slack/Discord)" msgstr "" -#: authentik/events/models.py:305 +#: authentik/events/models.py:299 msgid "Email" msgstr "" -#: authentik/events/models.py:323 +#: authentik/events/models.py:317 msgid "" "Only send notification once, for example when sending a webhook into a chat " "channel." msgstr "" -#: authentik/events/models.py:388 +#: authentik/events/models.py:382 msgid "Severity" msgstr "" -#: authentik/events/models.py:393 +#: authentik/events/models.py:387 msgid "Dispatched for user" msgstr "" -#: authentik/events/models.py:402 +#: authentik/events/models.py:396 msgid "Event user" msgstr "" -#: authentik/events/models.py:496 +#: authentik/events/models.py:490 msgid "Notification Transport" msgstr "" -#: authentik/events/models.py:497 +#: authentik/events/models.py:491 msgid "Notification Transports" msgstr "" -#: authentik/events/models.py:503 +#: authentik/events/models.py:497 msgid "Notice" msgstr "" -#: authentik/events/models.py:504 +#: authentik/events/models.py:498 msgid "Warning" msgstr "" -#: authentik/events/models.py:505 +#: authentik/events/models.py:499 msgid "Alert" msgstr "" -#: authentik/events/models.py:530 +#: authentik/events/models.py:524 msgid "Notification" msgstr "" -#: authentik/events/models.py:531 +#: authentik/events/models.py:525 msgid "Notifications" msgstr "" -#: authentik/events/models.py:541 +#: authentik/events/models.py:535 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/events/models.py:549 +#: authentik/events/models.py:543 msgid "Controls which severity level the created notifications will have." msgstr "" -#: authentik/events/models.py:554 +#: authentik/events/models.py:548 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:572 +#: authentik/events/models.py:566 msgid "Notification Rule" msgstr "" -#: authentik/events/models.py:573 +#: authentik/events/models.py:567 msgid "Notification Rules" msgstr "" -#: authentik/events/models.py:593 +#: authentik/events/models.py:587 msgid "Webhook Mapping" msgstr "" -#: authentik/events/models.py:594 +#: authentik/events/models.py:588 msgid "Webhook Mappings" msgstr "" @@ -920,11 +920,11 @@ msgstr "" msgid "Reputation Policies" msgstr "" -#: authentik/policies/reputation/models.py:95 +#: authentik/policies/reputation/models.py:96 msgid "Reputation Score" msgstr "" -#: authentik/policies/reputation/models.py:96 +#: authentik/policies/reputation/models.py:97 msgid "Reputation Scores" msgstr "" @@ -1187,63 +1187,63 @@ msgid "OAuth2/OpenID Providers" msgstr "" #: authentik/providers/oauth2/models.py:297 -#: authentik/providers/oauth2/models.py:429 +#: authentik/providers/oauth2/models.py:430 msgid "Scopes" msgstr "" -#: authentik/providers/oauth2/models.py:316 +#: authentik/providers/oauth2/models.py:317 msgid "Code" msgstr "" -#: authentik/providers/oauth2/models.py:317 +#: authentik/providers/oauth2/models.py:318 msgid "Nonce" msgstr "" -#: authentik/providers/oauth2/models.py:318 +#: authentik/providers/oauth2/models.py:319 msgid "Code Challenge" msgstr "" -#: authentik/providers/oauth2/models.py:320 +#: authentik/providers/oauth2/models.py:321 msgid "Code Challenge Method" msgstr "" -#: authentik/providers/oauth2/models.py:340 +#: authentik/providers/oauth2/models.py:341 msgid "Authorization Code" msgstr "" -#: authentik/providers/oauth2/models.py:341 +#: authentik/providers/oauth2/models.py:342 msgid "Authorization Codes" msgstr "" -#: authentik/providers/oauth2/models.py:383 +#: authentik/providers/oauth2/models.py:384 msgid "OAuth2 Access Token" msgstr "" -#: authentik/providers/oauth2/models.py:384 +#: authentik/providers/oauth2/models.py:385 msgid "OAuth2 Access Tokens" msgstr "" -#: authentik/providers/oauth2/models.py:394 +#: authentik/providers/oauth2/models.py:395 msgid "ID Token" msgstr "" -#: authentik/providers/oauth2/models.py:413 +#: authentik/providers/oauth2/models.py:414 msgid "OAuth2 Refresh Token" msgstr "" -#: authentik/providers/oauth2/models.py:414 +#: authentik/providers/oauth2/models.py:415 msgid "OAuth2 Refresh Tokens" msgstr "" -#: authentik/providers/oauth2/models.py:441 +#: authentik/providers/oauth2/models.py:442 msgid "Device Token" msgstr "" -#: authentik/providers/oauth2/models.py:442 +#: authentik/providers/oauth2/models.py:443 msgid "Device Tokens" msgstr "" -#: authentik/providers/oauth2/views/authorize.py:448 +#: authentik/providers/oauth2/views/authorize.py:450 #: authentik/providers/saml/views/flows.py:87 #, python-format msgid "Redirecting to %(app)s..." @@ -1494,59 +1494,59 @@ msgstr "" msgid "SAML Property Mappings" msgstr "" -#: authentik/providers/scim/models.py:20 +#: authentik/providers/scim/models.py:23 msgid "Base URL to SCIM requests, usually ends in /v2" msgstr "" -#: authentik/providers/scim/models.py:21 +#: authentik/providers/scim/models.py:24 msgid "Authentication token" msgstr "" -#: authentik/providers/scim/models.py:27 authentik/sources/ldap/models.py:98 +#: authentik/providers/scim/models.py:30 authentik/sources/ldap/models.py:98 msgid "Property mappings used for group creation/updating." msgstr "" -#: authentik/providers/scim/models.py:60 +#: authentik/providers/scim/models.py:72 msgid "SCIM Provider" msgstr "" -#: authentik/providers/scim/models.py:61 +#: authentik/providers/scim/models.py:73 msgid "SCIM Providers" msgstr "" -#: authentik/providers/scim/models.py:81 +#: authentik/providers/scim/models.py:93 msgid "SCIM Mapping" msgstr "" -#: authentik/providers/scim/models.py:82 +#: authentik/providers/scim/models.py:94 msgid "SCIM Mappings" msgstr "" -#: authentik/providers/scim/tasks.py:52 +#: authentik/providers/scim/tasks.py:56 msgid "Starting full SCIM sync" msgstr "" -#: authentik/providers/scim/tasks.py:62 -#, python-format -msgid "Syncing page %(page)d of users" -msgstr "" - #: authentik/providers/scim/tasks.py:66 #, python-format +msgid "Syncing page %(page)d of users" +msgstr "" + +#: authentik/providers/scim/tasks.py:70 +#, python-format msgid "Syncing page %(page)d of groups" msgstr "" -#: authentik/providers/scim/tasks.py:98 +#: authentik/providers/scim/tasks.py:102 #, python-format msgid "Failed to sync user %(user_name)s due to remote error: %(error)s" msgstr "" -#: authentik/providers/scim/tasks.py:109 authentik/providers/scim/tasks.py:150 +#: authentik/providers/scim/tasks.py:113 authentik/providers/scim/tasks.py:154 #, python-format msgid "Stopping sync due to error: %(error)s" msgstr "" -#: authentik/providers/scim/tasks.py:139 +#: authentik/providers/scim/tasks.py:143 #, python-format msgid "Failed to sync group %(group_name)s due to remote error: %(error)s" msgstr "" @@ -1583,6 +1583,14 @@ msgstr "" msgid "Can access admin interface" msgstr "" +#: authentik/rbac/models.py:73 +msgid "Can view system settings" +msgstr "" + +#: authentik/rbac/models.py:74 +msgid "Can edit system settings" +msgstr "" + #: authentik/recovery/management/commands/create_admin_group.py:11 msgid "Create admin group if the default group gets deleted." msgstr "" @@ -2245,6 +2253,7 @@ msgid "Email Successfully sent." msgstr "" #: authentik/stages/email/templates/email/account_confirmation.html:10 +#: authentik/stages/email/templates/email/account_confirmation.txt:1 msgid "Welcome!" msgstr "" @@ -2267,6 +2276,12 @@ msgid "" " " msgstr "" +#: authentik/stages/email/templates/email/account_confirmation.txt:3 +msgid "" +"We're excited to have you get started. First, you need to confirm your " +"account. Just open the link below." +msgstr "" + #: authentik/stages/email/templates/email/event_notification.html:46 #, python-format msgid "" @@ -2276,6 +2291,25 @@ msgid "" " " msgstr "" +#: authentik/stages/email/templates/email/event_notification.txt:1 +msgid "Dear authentik user," +msgstr "" + +#: authentik/stages/email/templates/email/event_notification.txt:3 +msgid "The following notification was created:" +msgstr "" + +#: authentik/stages/email/templates/email/event_notification.txt:8 +msgid "Additional attributes:" +msgstr "" + +#: authentik/stages/email/templates/email/event_notification.txt:13 +#, python-format +msgid "" +"\n" +"This email was sent from the notification transport %(name)s.\n" +msgstr "" + #: authentik/stages/email/templates/email/password_reset.html:10 #, python-format msgid "" @@ -2301,6 +2335,26 @@ msgid "" " " msgstr "" +#: authentik/stages/email/templates/email/password_reset.txt:1 +#, python-format +msgid "Hi %(username)s," +msgstr "" + +#: authentik/stages/email/templates/email/password_reset.txt:3 +msgid "" +"\n" +"You recently requested to change your password for your authentik account. " +"Use the link below to set a new password.\n" +msgstr "" + +#: authentik/stages/email/templates/email/password_reset.txt:7 +#, 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" +msgstr "" + #: authentik/stages/email/templates/email/setup.html:9 msgid "authentik Test-Email" msgstr "" @@ -2313,6 +2367,13 @@ msgid "" " " msgstr "" +#: authentik/stages/email/templates/email/setup.txt:2 +msgid "" +"\n" +"This is a test email to inform you, that you've successfully configured " +"authentik emails.\n" +msgstr "" + #: authentik/stages/identification/api.py:20 msgid "When no user fields are selected, at least one source must be selected" msgstr "" @@ -2557,36 +2618,44 @@ msgstr "" msgid "No Pending User." msgstr "" -#: authentik/stages/user_login/models.py:19 +#: authentik/stages/user_login/models.py:39 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 "" -#: authentik/stages/user_login/models.py:25 +#: authentik/stages/user_login/models.py:47 +msgid "Bind sessions created by this stage to the configured network" +msgstr "" + +#: authentik/stages/user_login/models.py:52 +msgid "Bind sessions created by this stage to the configured GeoIP location" +msgstr "" + +#: authentik/stages/user_login/models.py:55 msgid "Terminate all other sessions of the user logging in." msgstr "" -#: authentik/stages/user_login/models.py:31 +#: authentik/stages/user_login/models.py:61 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 "" -#: authentik/stages/user_login/models.py:54 +#: authentik/stages/user_login/models.py:84 msgid "User Login Stage" msgstr "" -#: authentik/stages/user_login/models.py:55 +#: authentik/stages/user_login/models.py:85 msgid "User Login Stages" msgstr "" -#: authentik/stages/user_login/stage.py:57 +#: authentik/stages/user_login/stage.py:85 msgid "No Pending user to login." msgstr "" -#: authentik/stages/user_login/stage.py:90 +#: authentik/stages/user_login/stage.py:112 msgid "Successfully logged in!" msgstr "" diff --git a/locale/ko/LC_MESSAGES/django.mo b/locale/ko/LC_MESSAGES/django.mo new file mode 100644 index 000000000..971aa815b Binary files /dev/null and b/locale/ko/LC_MESSAGES/django.mo differ diff --git a/locale/ko/LC_MESSAGES/django.po b/locale/ko/LC_MESSAGES/django.po new file mode 100644 index 000000000..345c0916d --- /dev/null +++ b/locale/ko/LC_MESSAGES/django.po @@ -0,0 +1,2717 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# Translators: +# Eunsung Kim, 2023 +# NavyStack, 2023 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-12-06 16:55+0000\n" +"PO-Revision-Date: 2022-09-26 16:47+0000\n" +"Last-Translator: NavyStack, 2023\n" +"Language-Team: Korean (https://app.transifex.com/authentik/teams/119923/ko/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ko\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: authentik/admin/api/tasks.py:127 +#, python-format +msgid "Successfully re-scheduled Task %(name)s!" +msgstr "%(name)s 작업을 성공적으로 다시 예약했습니다." + +#: authentik/api/schema.py:25 +msgid "Generic API Error" +msgstr "일반 API 오류" + +#: authentik/api/schema.py:33 +msgid "Validation Error" +msgstr "유효성 검사 오류" + +#: authentik/blueprints/api.py:43 +msgid "Blueprint file does not exist" +msgstr "Blueprint 파일이 존재하지 않습니다" + +#: authentik/blueprints/api.py:54 +#, python-format +msgid "Failed to validate blueprint: %(logs)s" +msgstr "blueprint 유효성을 검사하지 못했습니다: %(logs)s" + +#: authentik/blueprints/api.py:59 +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 "SAML 공급자 메타데이터" + +#: authentik/core/api/providers.py:121 +msgid "Create a SAML Provider by importing its Metadata." +msgstr "메타데이터를 가져와 SAML 공급자를 생성합니다." + +#: authentik/core/api/users.py:156 +msgid "No leading or trailing slashes allowed." +msgstr "앞 또는 뒤에 슬래시가 허용되지 않습니다." + +#: authentik/core/api/users.py:159 +msgid "No empty segments in user path allowed." +msgstr "사용자 경로에 빈 세그먼트가 허용되지 않습니다." + +#: authentik/core/models.py:86 +msgid "name" +msgstr "이름" + +#: authentik/core/models.py:88 +msgid "Users added to this group will be superusers." +msgstr "이 그룹에 추가된 사용자는 슈퍼유저가 됩니다." + +#: authentik/core/models.py:162 +msgid "Group" +msgstr "그룹" + +#: authentik/core/models.py:163 +msgid "Groups" +msgstr "그룹" + +#: authentik/core/models.py:178 +msgid "User's display name." +msgstr "사용자의 표시 이름" + +#: authentik/core/models.py:274 authentik/providers/oauth2/models.py:295 +msgid "User" +msgstr "사용자" + +#: authentik/core/models.py:275 +msgid "Users" +msgstr "사용자" + +#: authentik/core/models.py:277 +#: authentik/stages/email/templates/email/password_reset.html:28 +msgid "Reset Password" +msgstr "비밀번호 초기화" + +#: authentik/core/models.py:278 +msgid "Can impersonate other users" +msgstr "다른 사람으로 위장할 수 있음" + +#: authentik/core/models.py:279 authentik/rbac/models.py:54 +msgid "Can assign permissions to users" +msgstr "다른 사용자의 권한을 할당할 수 있음" + +#: authentik/core/models.py:280 authentik/rbac/models.py:55 +msgid "Can unassign permissions from users" +msgstr "다른 사용자의 권한을 취소 할당할 수 있음" + +#: authentik/core/models.py:294 +msgid "" +"Flow used for authentication when the associated application is accessed by " +"an un-authenticated user." +msgstr "플로우는 연결된 응용 프로그램이 인증되지 않은 사용자에게 액세스될 때 사용됩니다." + +#: authentik/core/models.py:304 +msgid "Flow used when authorizing this provider." +msgstr "이 공급자를 인증할 때 사용되는 플로우입니다." + +#: authentik/core/models.py:316 +msgid "" +"Accessed from applications; optional backchannel providers for protocols " +"like LDAP and SCIM." +msgstr "애플리케이션에서 액세스, LDAP 및 SCIM과 같은 프로토콜을 위한 선택적 백채널 공급자입니다." + +#: authentik/core/models.py:371 +msgid "Application's display Name." +msgstr "애플리케이션의 표시 이름입니다." + +#: authentik/core/models.py:372 +msgid "Internal application name, used in URLs." +msgstr "URL에 사용되는, 내부 애플리케이션 이름입니다." + +#: authentik/core/models.py:384 +msgid "Open launch URL in a new browser tab or window." +msgstr "새 브라우저 탭 또는 창에서 실행 URL을 엽니다." + +#: authentik/core/models.py:448 +msgid "Application" +msgstr "애플리케이션" + +#: authentik/core/models.py:449 +msgid "Applications" +msgstr "애플리케이션" + +#: authentik/core/models.py:455 +msgid "Use the source-specific identifier" +msgstr "소스별 식별자를 사용합니다." + +#: authentik/core/models.py:457 +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:461 +msgid "" +"Use the user's email address, but deny enrollment when the email address " +"already exists." +msgstr "사용자의 이메일 주소를 사용하되, 이메일 주소가 이미 존재하는 경우 등록을 거부합니다." + +#: authentik/core/models.py:464 +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:468 +msgid "" +"Use the user's username, but deny enrollment when the username already " +"exists." +msgstr "사용자의 사용자명을 사용하지만, 사용자명이 이미 존재하는 경우 등록을 거부합니다." + +#: authentik/core/models.py:475 +msgid "Source's display Name." +msgstr "소스의 표시 이름입니다." + +#: authentik/core/models.py:476 +msgid "Internal source name, used in URLs." +msgstr "URL에 사용되는, 내부 소스 이름입니다." + +#: authentik/core/models.py:495 +msgid "Flow to use when authenticating existing users." +msgstr "기존 사용자를 인증할 때 사용되는 플로우입니다." + +#: authentik/core/models.py:504 +msgid "Flow to use when enrolling new users." +msgstr "새 사용자를 등록할 때 사용되는 플로우입니다." + +#: authentik/core/models.py:512 +msgid "" +"How the source determines if an existing user should be authenticated or a " +"new user enrolled." +msgstr "소스가 기존 사용자를 인증할지 또는 새 사용자를 등록할지를 결정하는 방법입니다." + +#: authentik/core/models.py:684 +msgid "Token" +msgstr "토큰" + +#: authentik/core/models.py:685 +msgid "Tokens" +msgstr "토큰" + +#: authentik/core/models.py:690 +msgid "View token's key" +msgstr "토큰 키 보기" + +#: authentik/core/models.py:726 +msgid "Property Mapping" +msgstr "속성 매핑" + +#: authentik/core/models.py:727 +msgid "Property Mappings" +msgstr "속성 매핑" + +#: authentik/core/models.py:762 +msgid "Authenticated Session" +msgstr "인증된 세션" + +#: authentik/core/models.py:763 +msgid "Authenticated Sessions" +msgstr "인증된 세션" + +#: authentik/core/sources/flow_manager.py:190 +#, 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:242 +msgid "Configured flow does not exist." +msgstr "구성된 플로우가 존재하지 않습니다." + +#: authentik/core/sources/flow_manager.py:272 +#: authentik/core/sources/flow_manager.py:324 +#, python-format +msgid "Successfully authenticated with %(source)s!" +msgstr "성공적으로 %(source)s을(를) 통해 인증되었습니다!" + +#: authentik/core/sources/flow_manager.py:296 +#, python-format +msgid "Successfully linked %(source)s!" +msgstr "%(source)s을(를) 성공적으로 연결했습니다!" + +#: authentik/core/sources/flow_manager.py:315 +msgid "Source is not configured for enrollment." +msgstr "소스가 등록을 위해 구성되지 않았습니다." + +#: authentik/core/templates/if/end_session.html:7 +msgid "End session" +msgstr "세션 종료" + +#: authentik/core/templates/if/end_session.html:11 +#, python-format +msgid "" +"\n" +"You've logged out of %(application)s.\n" +msgstr "" +"\n" +"%(application)s에서 로그아웃 했습니다.\n" + +#: authentik/core/templates/if/end_session.html:19 +#, python-format +msgid "" +"\n" +" You've logged out of %(application)s. You can go back to the overview to launch another application, or log out of your %(branding_title)s account.\n" +" " +msgstr "" +"\n" +" %(application)s에서 로그아웃했습니다. 다른 응용 프로그램을 시작하거나 %(branding_title)s 계정에서 로그아웃할 수 있습니다.\n" +" " + +#: authentik/core/templates/if/end_session.html:25 +msgid "Go back to overview" +msgstr "개요로 돌아가기" + +#: authentik/core/templates/if/end_session.html:29 +#, python-format +msgid "" +"\n" +" Log out of %(branding_title)s\n" +" " +msgstr "" +"\n" +" %(branding_title)s에서 로그아웃합니다.\n" +" " + +#: authentik/core/templates/if/end_session.html:36 +#, python-format +msgid "" +"\n" +" Log back into %(application)s\n" +" " +msgstr "" +"\n" +" %(application)s에 다시 로그인\n" +" " + +#: authentik/core/templates/if/error.html:18 +msgid "Go home" +msgstr "홈으로 가기" + +#: authentik/core/templates/login/base_full.html:89 +msgid "Powered by authentik" +msgstr "Powered by authentik" + +#: 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 "주체 대체 이름" + +#: authentik/crypto/models.py:30 +msgid "PEM-encoded Certificate data" +msgstr "PEM 인코딩된 인증서 데이터" + +#: 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:101 +msgid "Certificate-Key Pair" +msgstr "인증서-키 쌍" + +#: authentik/crypto/models.py:102 +msgid "Certificate-Key Pairs" +msgstr "인증서-키 쌍" + +#: authentik/enterprise/models.py:183 +msgid "License" +msgstr "라이선스" + +#: authentik/enterprise/models.py:184 +msgid "Licenses" +msgstr "라이선스" + +#: authentik/enterprise/models.py:206 +msgid "License Usage" +msgstr "라이선스 사용" + +#: authentik/enterprise/models.py:207 +msgid "License Usage Records" +msgstr "라이선스 사용 기록" + +#: authentik/events/models.py:291 +msgid "Event" +msgstr "이력" + +#: authentik/events/models.py:292 +msgid "Events" +msgstr "이력" + +#: authentik/events/models.py:298 +msgid "authentik inbuilt notifications" +msgstr "authentik 내장 통지" + +#: authentik/events/models.py:299 +msgid "Generic Webhook" +msgstr "일반 웹훅" + +#: authentik/events/models.py:300 +msgid "Slack Webhook (Slack/Discord)" +msgstr "Slack 웹훅 (Slack/Discord)" + +#: authentik/events/models.py:301 +msgid "Email" +msgstr "이메일" + +#: authentik/events/models.py:319 +msgid "" +"Only send notification once, for example when sending a webhook into a chat " +"channel." +msgstr "예를 들어 채팅 채널로 웹훅을 보낼 때 알림을 한 번만 보내세요." + +#: authentik/events/models.py:384 +msgid "Severity" +msgstr "심각도" + +#: authentik/events/models.py:389 +msgid "Dispatched for user" +msgstr "사용자를 위해 발송됨" + +#: authentik/events/models.py:398 +msgid "Event user" +msgstr "사용자 이력" + +#: authentik/events/models.py:492 +msgid "Notification Transport" +msgstr "통지 전송" + +#: authentik/events/models.py:493 +msgid "Notification Transports" +msgstr "통지 전송" + +#: authentik/events/models.py:499 +msgid "Notice" +msgstr "통지" + +#: authentik/events/models.py:500 +msgid "Warning" +msgstr "주의" + +#: authentik/events/models.py:501 +msgid "Alert" +msgstr "경고" + +#: authentik/events/models.py:526 +msgid "Notification" +msgstr "통지" + +#: authentik/events/models.py:527 +msgid "Notifications" +msgstr "통지" + +#: authentik/events/models.py:537 +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 UI에만 표시됩니다." + +#: authentik/events/models.py:545 +msgid "Controls which severity level the created notifications will have." +msgstr "생성된 통지의 심각도 수준을 제어합니다." + +#: authentik/events/models.py:550 +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:568 +msgid "Notification Rule" +msgstr "통지 규칙" + +#: authentik/events/models.py:569 +msgid "Notification Rules" +msgstr "통지 규칙" + +#: authentik/events/models.py:589 +msgid "Webhook Mapping" +msgstr "웹훅 매핑" + +#: authentik/events/models.py:590 +msgid "Webhook Mappings" +msgstr "웹훅 매핑" + +#: authentik/events/monitored_tasks.py:205 +msgid "Task has not been run yet." +msgstr "작업이 아직 실행되지 않았습니다." + +#: authentik/flows/api/flows.py:295 +#, python-format +msgid "Flow not applicable to current user/request: %(messages)s" +msgstr "현재 사용자/요청에는 플로우를 적용할 수 없습니다: %(messages)s" + +#: 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/api/flows_diagram.py:71 +#, python-format +msgid "Binding %(order)d" +msgstr "바인딩 %(order)d" + +#: authentik/flows/api/flows_diagram.py:118 +msgid "Policy passed" +msgstr "정책 통과됨" + +#: authentik/flows/api/flows_diagram.py:122 +#, python-format +msgid "Stage (%(type)s)" +msgstr "스테이지 (%(type)s)" + +#: 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/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 "URL에 표시됩니다." + +#: 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:197 +msgid "Can export a Flow" +msgstr "플로우를 내보낼 수 있습니다." + +#: authentik/flows/models.py:198 +msgid "Can inspect a Flow's execution" +msgstr "플로우 실행을 검사할 수 있습니다." + +#: authentik/flows/models.py:199 +msgid "View Flow's cache metrics" +msgstr "플로우의 캐시 메트릭 보기" + +#: authentik/flows/models.py:200 +msgid "Clear Flow's cache metrics" +msgstr "플로우의 캐시 메트릭 삭제" + +#: authentik/flows/models.py:216 +msgid "Evaluate policies during the Flow planning process." +msgstr "플로우 계획 프로세스 중에 정책을 평가할 수 있습니다." + +#: authentik/flows/models.py:220 +msgid "Evaluate policies when the Stage is present to the user." +msgstr "스테이지가 사용자에게 표시될 때 정책을 심사합니다." + +#: authentik/flows/models.py:227 +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는 현재 컨텍스트를 유지하면서 플로우를" +" 다시 시작합니다." + +#: authentik/flows/models.py:250 +msgid "Flow Stage Binding" +msgstr "플로우 스테이지 바인딩" + +#: authentik/flows/models.py:251 +msgid "Flow Stage Bindings" +msgstr "플로우 스테이지 바인딩" + +#: authentik/flows/models.py:266 +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:306 +msgid "Flow Token" +msgstr "플로우 토큰" + +#: authentik/flows/models.py:307 +msgid "Flow Tokens" +msgstr "플로우 토큰" + +#: 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'인 올바른 형식이 아닙니다." + +#: authentik/lib/validators.py:16 +#, python-brace-format +msgid "The fields {field_names} must be used together." +msgstr "{field_names} 필드는 함께 사용되어야 합니다." + +#: authentik/outposts/api/service_connections.py:127 +msgid "" +"You can only use an empty kubeconfig when connecting to a local cluster." +msgstr "로컬 클러스터에 연결할 때만 빈 kubeconfig을 사용할 수 있습니다." + +#: authentik/outposts/api/service_connections.py:135 +msgid "Invalid kubeconfig" +msgstr "kubeconfig이 올바르지 않음" + +#: authentik/outposts/models.py:122 +msgid "" +"If enabled, use the local connection. Required Docker socket/Kubernetes " +"Integration" +msgstr "활성화되면 로컬 연결을 사용합니다. 필요한 Docker 소켓/쿠버네티스 통합에 필요합니다" + +#: authentik/outposts/models.py:152 +msgid "Outpost Service-Connection" +msgstr "Outpost 서비스 연결" + +#: 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 "" +"로컬 도커 데몬에 연결할 때는 '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 "인증에 사용되는 인증서/키입니다. 인증이 필요하지 않은 경우 비워 둘 수 있습니다." + +#: authentik/outposts/models.py:203 +msgid "Docker Service-Connection" +msgstr "도커 서비스 연결" + +#: authentik/outposts/models.py:204 +msgid "Docker Service-Connections" +msgstr "도커 서비스 연결" + +#: 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 "쿠버네티스 서비스 연결" + +#: authentik/outposts/models.py:236 +msgid "Kubernetes Service-Connections" +msgstr "쿠버네티스 서비스 연결" + +#: 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 "" +"이 Outpost를 관리하는 데 사용할 서비스 연결을 선택하십시오. authentik이 배포를 처리하지 않아야 하는 경우 비워 두십시오." + +#: authentik/outposts/models.py:419 +msgid "Outpost" +msgstr "Outpost" + +#: authentik/outposts/models.py:420 +msgid "Outposts" +msgstr "Outposts" + +#: authentik/policies/denied.py:24 +msgid "Access denied" +msgstr "액세스 거부됨" + +#: authentik/policies/dummy/models.py:44 +msgid "Dummy Policy" +msgstr "더미 정책" + +#: authentik/policies/dummy/models.py:45 +msgid "Dummy Policies" +msgstr "더미 정책" + +#: 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 "이벤트 일치 정책" + +#: authentik/policies/event_matcher/models.py:144 +msgid "Event Matcher Policies" +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 "비밀번호가 만료되었습니다." + +#: authentik/policies/expiry/models.py:53 +msgid "Password Expiry Policy" +msgstr "비밀번호 만료 정책" + +#: authentik/policies/expiry/models.py:54 +msgid "Password Expiry Policies" +msgstr "비밀번호 만료 정책" + +#: authentik/policies/expression/models.py:40 +msgid "Expression Policy" +msgstr "정규표현식 정책" + +#: authentik/policies/expression/models.py:41 +msgid "Expression Policies" +msgstr "정규표현식 정책" + +#: authentik/policies/models.py:22 +msgid "all, all policies must pass" +msgstr "all, 모든 정책이 통과해야 함" + +#: authentik/policies/models.py:23 +msgid "any, any policy must pass" +msgstr "any, 어떤 정책이라도 통과하면 됨" + +#: authentik/policies/models.py:46 +msgid "Policy Binding Model" +msgstr "정책 바인딩 모델" + +#: authentik/policies/models.py:47 +msgid "Policy Binding Models" +msgstr "정책 바인딩 모델" + +#: authentik/policies/models.py:86 +msgid "Negates the outcome of the policy. Messages are unaffected." +msgstr "정책의 결과를 무효화합니다. 메시지는 영향을 받지 않습니다." + +#: authentik/policies/models.py:89 +msgid "Timeout after which Policy execution is terminated." +msgstr "시간을 초과하면 정책 실행을 종료합니다." + +#: authentik/policies/models.py:92 +msgid "Result if the Policy execution fails." +msgstr "정책 실행이 실패한 경우의 결과입니다." + +#: authentik/policies/models.py:145 +msgid "Policy Binding" +msgstr "정책 바인딩" + +#: authentik/policies/models.py:146 +msgid "Policy Bindings" +msgstr "정책 바인딩" + +#: 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 "정책" + +#: authentik/policies/models.py:190 +msgid "Policies" +msgstr "정책" + +#: authentik/policies/models.py:193 +msgid "View Policy's cache metrics" +msgstr "정책의 캐시 메트릭 보기" + +#: authentik/policies/models.py:194 +msgid "Clear Policy's cache metrics" +msgstr "정책의 캐시 메트릭 삭제" + +#: 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 "비밀번호 해시가 허용되는 해시 횟수" + +#: 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 "비밀번호 정책" + +#: authentik/policies/password/models.py:163 +msgid "Password Policies" +msgstr "비밀번호 정책" + +#: 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 "평판 정책" + +#: authentik/policies/reputation/models.py:68 +msgid "Reputation Policies" +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 "권한 거부됨" + +#: 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 "요청이 거부되었습니다." + +#: authentik/policies/templates/policies/denied.html:44 +msgid "Messages:" +msgstr "메세지:" + +#: authentik/policies/templates/policies/denied.html:54 +msgid "Explanation:" +msgstr "설명:" + +#: authentik/policies/templates/policies/denied.html:58 +#, python-format +msgid "" +"\n" +" Policy binding '%(name)s' returned result '%(result)s'\n" +" " +msgstr "" +"\n" +" 정책 바인딩 '%(name)s'이(가) 결과 '%(result)s'을(를) 반환했습니다\n" +" " + +#: authentik/policies/views.py:68 +msgid "Failed to resolve application" +msgstr "애플리케이션 확인 실패" + +#: authentik/providers/ldap/models.py:25 +msgid "DN under which objects are accessible." +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 "이 그룹의 사용자는 검색 쿼리를 실행할 수 있습니다. 설정되지 않은 경우 모든 사용자가 검색 쿼리를 실행할 수 있습니다." + +#: authentik/providers/ldap/models.py:53 +msgid "" +"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 "" +"UID의 시작값입니다. 이 숫자는 user.Pk에 추가되어 POSIX 사용자의 숫자가 너무 낮지 않도록 합니다. 기본값은 2000으로 " +"설정되어 로컬 사용자 UID와 충돌하지 않도록 합니다." + +#: 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. " +"Default is 4000 to ensure that we don't collide with local groups or users " +"primary groups gidNumber" +msgstr "" +"GID의 시작값입니다. 이 숫자는 group.Pk에서 생성된 숫자에 추가되어 POSIX 그룹의 숫자가 너무 낮지 않도록 합니다. 기본값은" +" 4000으로 설정되어 로컬 그룹 또는 사용자의 기본 그룹 gidNumber와 충돌하지 않도록 합니다." + +#: authentik/providers/ldap/models.py:76 +#: authentik/providers/radius/models.py:34 +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 코드를 추가하여 코드 기반 멀티팩터 인증을 사용할 수 있습니다. 비밀번호에 " +"세미콜론이 포함되어 있으면 비밀번호가 거부될 수 있으므로, 이 공급자에 바인딩할 모든 사용자가 TOTP 디바이스를 구성한 경우에만 이 " +"기능을 사용하도록 설정해야 합니다." + +#: authentik/providers/ldap/models.py:108 +msgid "LDAP Provider" +msgstr "LDAP 공급자" + +#: authentik/providers/ldap/models.py:109 +msgid "LDAP Providers" +msgstr "LDAP 공급자" + +#: authentik/providers/oauth2/id_token.py:27 +msgid "Based on the Hashed User ID" +msgstr "해시된 사용자 ID 기반" + +#: 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 "사용자명 기반" + +#: authentik/providers/oauth2/id_token.py:33 +msgid "Based on the User's Email. This is recommended over the UPN method." +msgstr "사용자 이메일 기반. 이 방법은 UPN 방법보다 권장됩니다." + +#: 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과 메일 도메인이 다른" +" 경우에만 사용하세요." + +#: 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 "모든 공급자에 대해 동일한 식별자가 사용됨" + +#: authentik/providers/oauth2/models.py:68 +msgid "Each provider has a different issuer, based on the application slug." +msgstr "각 공급자는 애플리케이션 슬러그를 기반으로 서로 다른 발급자를 가짐" + +#: 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 token (암시적 플로우)" + +#: authentik/providers/oauth2/models.py:78 +msgid "code token (Hybrid Flow)" +msgstr "code token (하이브리드 플로우)" + +#: authentik/providers/oauth2/models.py:79 +msgid "code id_token (Hybrid Flow)" +msgstr "code id_token (하이브리드 플로우)" + +#: authentik/providers/oauth2/models.py:80 +msgid "code id_token token (Hybrid Flow)" +msgstr "code id_token token (하이브리드 플로우)" + +#: authentik/providers/oauth2/models.py:86 +msgid "HS256 (Symmetric Encryption)" +msgstr "HS256 (대칭 암호화)" + +#: authentik/providers/oauth2/models.py:87 +msgid "RS256 (Asymmetric Encryption)" +msgstr "RS256 (비대칭 암호화)" + +#: authentik/providers/oauth2/models.py:88 +msgid "ES256 (Asymmetric Encryption)" +msgstr "ES256 (비대칭 암호화)" + +#: authentik/providers/oauth2/models.py:94 +msgid "Scope used by the client" +msgstr "클라이언트에서 사용할 스코프" + +#: 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 "스코프 매핑" + +#: authentik/providers/oauth2/models.py:118 +msgid "Scope Mappings" +msgstr "스코프 매핑" + +#: authentik/providers/oauth2/models.py:128 +msgid "Client Type" +msgstr "클라이언트 유형" + +#: 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" + +#: authentik/providers/oauth2/models.py:143 +msgid "Client Secret" +msgstr "클라이언트 Secret" + +#: authentik/providers/oauth2/models.py:149 +msgid "Redirect URIs" +msgstr "리다이렉트 URI" + +#: authentik/providers/oauth2/models.py:150 +msgid "Enter each URI on a new line." +msgstr "각 행마다 URI를 입력하세요." + +#: authentik/providers/oauth2/models.py:155 +msgid "Include claims in id_token" +msgstr "id_token에 클레임 포함" + +#: 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에 포함하세요." + +#: 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으로 설정된 경우에만 필요합니다." + +#: 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 공급자" + +#: authentik/providers/oauth2/models.py:288 +msgid "OAuth2/OpenID Providers" +msgstr "OAuth2/OpenID 공급자" + +#: authentik/providers/oauth2/models.py:297 +#: authentik/providers/oauth2/models.py:429 +msgid "Scopes" +msgstr "스코프" + +#: authentik/providers/oauth2/models.py:316 +msgid "Code" +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 "Code Challenge Method" +msgstr "코드 챌린지 메서드" + +#: authentik/providers/oauth2/models.py:340 +msgid "Authorization Code" +msgstr "인증 코드" + +#: authentik/providers/oauth2/models.py:341 +msgid "Authorization Codes" +msgstr "인증 코드" + +#: authentik/providers/oauth2/models.py:383 +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 토큰" + +#: authentik/providers/oauth2/models.py:413 +msgid "OAuth2 Refresh Token" +msgstr "OAuth2 토큰 새로고침" + +#: authentik/providers/oauth2/models.py:414 +msgid "OAuth2 Refresh Tokens" +msgstr "OAuth2 토큰 새로고침" + +#: 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 "Redirecting to %(app)s..." +msgstr "%(app)s 으로 리디렉션 중..." + +#: 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 Compatibility: 사용자 정보에 액세스" + +#: authentik/providers/oauth2/views/userinfo.py:57 +msgid "GitHub Compatibility: Access you Email addresses" +msgstr "GitHub Compatibility: 이메일 주소에 액세스" + +#: authentik/providers/oauth2/views/userinfo.py:58 +msgid "GitHub Compatibility: Access your Groups" +msgstr "GitHub Compatibility 그룹 정보에 액세스" + +#: 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 인증서 유효성 검사" + +#: authentik/providers/proxy/models.py:55 +msgid "Internal host SSL Validation" +msgstr "내부 호스트 SSL 유효성 검사" + +#: 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는 제외입니다." + +#: 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-Basic 인증 설정" + +#: authentik/providers/proxy/models.py:86 +msgid "" +"Set a custom HTTP-Basic Authentication header based on values from " +"authentik." +msgstr "authentik의 값을 기반으로 커스텀 HTTP-Basic 인증 헤더를 설정합니다." + +#: authentik/providers/proxy/models.py:91 +msgid "HTTP-Basic Username Key" +msgstr "HTTP-Basic 사용자명 키" + +#: 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-Basic 헤더의 사용자 부분에 사용되는 사용자/그룹 속성입니다. 설정하지 않으면 사용자의 이메일 주소가 사용됩니다." + +#: authentik/providers/proxy/models.py:99 +msgid "HTTP-Basic Password Key" +msgstr "HTTP-Basic 비밀번호 키" + +#: authentik/providers/proxy/models.py:100 +msgid "" +"User/Group Attribute used for the password part of the HTTP-Basic Header." +msgstr "HTTP-Basic 헤더의 비밀번호 부분에 사용되는 사용자/그룹 속성입니다." + +#: authentik/providers/proxy/models.py:154 +msgid "Proxy Provider" +msgstr "프록시 공급자" + +#: authentik/providers/proxy/models.py:155 +msgid "Proxy Providers" +msgstr "프록시 공급자" + +#: 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:60 +msgid "Radius Provider" +msgstr "Radius 공급자" + +#: authentik/providers/radius/models.py:61 +msgid "Radius Providers" +msgstr "Radius 공급자" + +#: authentik/providers/saml/api/providers.py:258 +msgid "Invalid XML Syntax" +msgstr "올바르지 않은 XML 구문" + +#: authentik/providers/saml/api/providers.py:268 +#, python-format +msgid "Failed to import Metadata: %(message)s" +msgstr "메타데이터를 가져오지 못했습니다: %(message)s" + +#: authentik/providers/saml/models.py:38 +msgid "ACS URL" +msgstr "ACS URL" + +#: 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라고도 합니다." + +#: authentik/providers/saml/models.py:51 +msgid "Service Provider Binding" +msgstr "서비스 공급자 바인딩" + +#: authentik/providers/saml/models.py:53 +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 "" +"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: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:100 authentik/sources/saml/models.py:151 +msgid "SHA256" +msgstr "SHA256" + +#: authentik/providers/saml/models.py:101 authentik/sources/saml/models.py:152 +msgid "SHA384" +msgstr "SHA384" + +#: authentik/providers/saml/models.py:102 authentik/sources/saml/models.py:153 +msgid "SHA512" +msgstr "SHA512" + +#: authentik/providers/saml/models.py:109 authentik/sources/saml/models.py:160 +msgid "RSA-SHA1" +msgstr "RSA-SHA1" + +#: authentik/providers/saml/models.py:110 authentik/sources/saml/models.py:161 +msgid "RSA-SHA256" +msgstr "RSA-SHA256" + +#: authentik/providers/saml/models.py:111 authentik/sources/saml/models.py:162 +msgid "RSA-SHA384" +msgstr "RSA-SHA384" + +#: authentik/providers/saml/models.py:112 authentik/sources/saml/models.py:163 +msgid "RSA-SHA512" +msgstr "RSA-SHA512" + +#: authentik/providers/saml/models.py:113 authentik/sources/saml/models.py:164 +msgid "DSA-SHA1" +msgstr "DSA-SHA1" + +#: 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 "검증 인증서" + +#: authentik/providers/saml/models.py:136 +msgid "Keypair used to sign outgoing Responses going to the Service Provider." +msgstr "서비스 공급자에게 보내는 응답에 서명하는 데 사용되는 키 쌍입니다." + +#: authentik/providers/saml/models.py:138 authentik/sources/saml/models.py:144 +msgid "Signing Keypair" +msgstr "서명 키 쌍" + +#: authentik/providers/saml/models.py:142 +msgid "Default relay_state value for IDP-initiated logins" +msgstr "IDP-initiated 로그인에 대한 기본 relay_state 값" + +#: authentik/providers/saml/models.py:171 +msgid "SAML Provider" +msgstr "SAML 공급자" + +#: authentik/providers/saml/models.py:172 +msgid "SAML Providers" +msgstr "SAML 공급자" + +#: authentik/providers/saml/models.py:196 +msgid "SAML Property Mapping" +msgstr "SAML 속성 매핑" + +#: authentik/providers/saml/models.py:197 +msgid "SAML Property Mappings" +msgstr "SAML 속성 매핑" + +#: authentik/providers/scim/models.py:20 +msgid "Base URL to SCIM requests, usually ends in /v2" +msgstr "SCIM 요청에 대한 기본 URL은, 일반적으로 /v2로 끝납니다" + +#: authentik/providers/scim/models.py:21 +msgid "Authentication token" +msgstr "인증 토큰" + +#: authentik/providers/scim/models.py:27 authentik/sources/ldap/models.py:98 +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/rbac/models.py:51 +msgid "Role" +msgstr "역할" + +#: authentik/rbac/models.py:52 +msgid "Roles" +msgstr "역할" + +#: authentik/rbac/models.py:66 +msgid "System permission" +msgstr "시스템 권한" + +#: authentik/rbac/models.py:67 +msgid "System permissions" +msgstr "시스템 권한" + +#: authentik/rbac/models.py:69 +msgid "Can view system info" +msgstr "시스템 정보를 볼 수 있음" + +#: authentik/rbac/models.py:70 +msgid "Can view system tasks" +msgstr "시스템 작업을 볼 수 있음" + +#: authentik/rbac/models.py:71 +msgid "Can run system tasks" +msgstr "시스템 작업을 실행할 수 있음" + +#: authentik/rbac/models.py:72 +msgid "Can access admin interface" +msgstr "관리자 인터페이스 액세스 가능" + +#: authentik/recovery/management/commands/create_admin_group.py:11 +msgid "Create admin group if the default group gets deleted." +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에 대한 액세스를 복원하는 데 사용할 수 있는 키를 만듭니다." + +#: authentik/recovery/views.py:24 +msgid "Used recovery-link to authenticate." +msgstr "인증하는 데 복구 링크를 사용합니다." + +#: authentik/sources/ldap/models.py:41 +msgid "Server URI" +msgstr "서버 URI" + +#: authentik/sources/ldap/models.py:50 +msgid "" +"Optionally verify the LDAP Server's Certificate against the CA Chain in this" +" keypair." +msgstr "선택적으로 이 키 쌍의 CA 체인에 대해 LDAP 서버의 인증서를 인증합니다." + +#: authentik/sources/ldap/models.py:59 +msgid "" +"Client certificate to authenticate against the LDAP Server's Certificate." +msgstr "클라이언트 인증서를 사용하여 LDAP 서버의 인증서에 대해 인증합니다." + +#: authentik/sources/ldap/models.py:62 +msgid "Bind CN" +msgstr "Bind CN" + +#: authentik/sources/ldap/models.py:64 +msgid "Enable Start TLS" +msgstr "TLS 시작 활성화" + +#: authentik/sources/ldap/models.py:65 +msgid "Use Server URI for SNI verification" +msgstr "SNI 확인에 서버 URI 사용" + +#: authentik/sources/ldap/models.py:67 +msgid "Base DN" +msgstr "Base DN" + +#: authentik/sources/ldap/models.py:69 +msgid "Prepended to Base DN for User-queries." +msgstr "사용자 쿼리의 Base DN 앞에 추가됩니다." + +#: authentik/sources/ldap/models.py:70 +msgid "Addition User DN" +msgstr "추가 사용자 DN" + +#: authentik/sources/ldap/models.py:74 +msgid "Prepended to Base DN for Group-queries." +msgstr "그룹 쿼리의 Base DN 앞에 추가됩니다." + +#: authentik/sources/ldap/models.py:75 +msgid "Addition Group DN" +msgstr "추가 그룹 DN" + +#: authentik/sources/ldap/models.py:81 +msgid "Consider Objects matching this filter to be Users." +msgstr "이 필터와 일치하는 개체를 사용자로 간주합니다." + +#: authentik/sources/ldap/models.py:84 +msgid "Field which contains members of a group." +msgstr "그룹의 구성원을 포함하는 필드입니다." + +#: authentik/sources/ldap/models.py:88 +msgid "Consider Objects matching this filter to be Groups." +msgstr "이 필터와 일치하는 개체를 그룹으로 간주합니다." + +#: authentik/sources/ldap/models.py:91 +msgid "Field which contains a unique Identifier." +msgstr "고유 식별자를 포함하는 필드입니다." + +#: authentik/sources/ldap/models.py:105 +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:248 +msgid "LDAP Source" +msgstr "LDAP 소스" + +#: authentik/sources/ldap/models.py:249 +msgid "LDAP Sources" +msgstr "LDAP 소스" + +#: authentik/sources/ldap/models.py:271 +msgid "LDAP Property Mapping" +msgstr "LDAP 속성 매핑" + +#: authentik/sources/ldap/models.py:272 +msgid "LDAP Property Mappings" +msgstr "LDAP 속성 매핑" + +#: authentik/sources/ldap/signals.py:52 +msgid "Password does not match Active Directory Complexity." +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 "토큰 요청 URL" + +#: authentik/sources/oauth/models.py:26 +msgid "" +"URL used to request the initial token. This URL is only required for OAuth " +"1." +msgstr "초기 토큰을 요청하는 데 사용되는 URL입니다. 이 URL은 OAuth 1에만 필요합니다." + +#: authentik/sources/oauth/models.py:32 +msgid "Authorization URL" +msgstr "인증 URL" + +#: authentik/sources/oauth/models.py:33 +msgid "URL the user is redirect to to conest the flow." +msgstr "사용자가 플로우에 이의를 제기하기 위해 리디렉션되는 URL입니다." + +#: authentik/sources/oauth/models.py:38 +msgid "Access Token URL" +msgstr "액세스 토큰 URL" + +#: authentik/sources/oauth/models.py:39 +msgid "URL used by authentik to retrieve tokens." +msgstr "토큰을 검색하기 위해 authentik에서 사용하는 URL입니다." + +#: authentik/sources/oauth/models.py:44 +msgid "Profile URL" +msgstr "프로필 URL" + +#: authentik/sources/oauth/models.py:45 +msgid "URL used by authentik to get user information." +msgstr "사용자 정보를 가져오기 위해 authentik에서 사용하는 URL입니다." + +#: authentik/sources/oauth/models.py:48 +msgid "Additional Scopes" +msgstr "추가 스코프" + +#: authentik/sources/oauth/models.py:107 +msgid "OAuth Source" +msgstr "OAuth 소스" + +#: authentik/sources/oauth/models.py:108 +msgid "OAuth Sources" +msgstr "OAuth 소스" + +#: authentik/sources/oauth/models.py:116 +msgid "GitHub OAuth Source" +msgstr "GitHub OAuth 소스" + +#: authentik/sources/oauth/models.py:117 +msgid "GitHub OAuth Sources" +msgstr "GitHub OAuth 소스" + +#: authentik/sources/oauth/models.py:125 +msgid "Twitch OAuth Source" +msgstr "Twitch OAuth 소스" + +#: authentik/sources/oauth/models.py:126 +msgid "Twitch OAuth Sources" +msgstr "Twitch OAuth 소스" + +#: authentik/sources/oauth/models.py:134 +msgid "Mailcow OAuth Source" +msgstr "Mailcow OAuth 소스" + +#: authentik/sources/oauth/models.py:135 +msgid "Mailcow OAuth Sources" +msgstr "Mailcow OAuth 소스" + +#: authentik/sources/oauth/models.py:143 +msgid "Twitter OAuth Source" +msgstr "Twitter OAuth 소스" + +#: authentik/sources/oauth/models.py:144 +msgid "Twitter OAuth Sources" +msgstr "Twitter OAuth 소스" + +#: authentik/sources/oauth/models.py:152 +msgid "Facebook OAuth Source" +msgstr "Facebook OAuth 소스" + +#: authentik/sources/oauth/models.py:153 +msgid "Facebook OAuth Sources" +msgstr "Facebook OAuth 소스" + +#: authentik/sources/oauth/models.py:161 +msgid "Discord OAuth Source" +msgstr "Discord OAuth 소스" + +#: authentik/sources/oauth/models.py:162 +msgid "Discord OAuth Sources" +msgstr "Discord OAuth 소스" + +#: authentik/sources/oauth/models.py:170 +msgid "Patreon OAuth Source" +msgstr "Patreon OAuth 소스" + +#: authentik/sources/oauth/models.py:171 +msgid "Patreon OAuth Sources" +msgstr "Patreon OAuth 소스" + +#: authentik/sources/oauth/models.py:179 +msgid "Google OAuth Source" +msgstr "Google OAuth 소스" + +#: authentik/sources/oauth/models.py:180 +msgid "Google OAuth Sources" +msgstr "Google OAuth 소스" + +#: authentik/sources/oauth/models.py:188 +msgid "Azure AD OAuth Source" +msgstr "Azure AD OAuth 소스" + +#: authentik/sources/oauth/models.py:189 +msgid "Azure AD OAuth Sources" +msgstr "Azure AD OAuth 소스" + +#: authentik/sources/oauth/models.py:197 +msgid "OpenID OAuth Source" +msgstr "OpenID OAuth 소스" + +#: authentik/sources/oauth/models.py:198 +msgid "OpenID OAuth Sources" +msgstr "OpenID OAuth 소스" + +#: authentik/sources/oauth/models.py:206 +msgid "Apple OAuth Source" +msgstr "Apple OAuth 소스" + +#: authentik/sources/oauth/models.py:207 +msgid "Apple OAuth Sources" +msgstr "Apple OAuth 소스" + +#: authentik/sources/oauth/models.py:215 +msgid "Okta OAuth Source" +msgstr "Okta OAuth 소스" + +#: authentik/sources/oauth/models.py:216 +msgid "Okta OAuth Sources" +msgstr "Okta OAuth 소스" + +#: authentik/sources/oauth/models.py:224 +msgid "Reddit OAuth Source" +msgstr "Reddit OAuth 소스" + +#: authentik/sources/oauth/models.py:225 +msgid "Reddit OAuth Sources" +msgstr "Reddit OAuth 소스" + +#: authentik/sources/oauth/models.py:247 +msgid "User OAuth Source Connection" +msgstr "사용자 OAuth 소스 연결" + +#: authentik/sources/oauth/models.py:248 +msgid "User OAuth Source Connections" +msgstr "사용자 OAuth 소스 연결" + +#: 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와 통신하는 데 사용되는 클라이언트 식별자입니다." + +#: 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 "Plex token used to check friends" +msgstr "친구를 확인하는 데 사용되는 Plex 토큰" + +#: authentik/sources/plex/models.py:95 +msgid "Plex Source" +msgstr "Plex 소스" + +#: authentik/sources/plex/models.py:96 +msgid "Plex Sources" +msgstr "Plex 소스" + +#: authentik/sources/plex/models.py:112 +msgid "User Plex Source Connection" +msgstr "사용자 Plex 소스 연결" + +#: authentik/sources/plex/models.py:113 +msgid "User Plex Source Connections" +msgstr "사용자 Plex 소스 연결" + +#: authentik/sources/saml/models.py:40 +msgid "Redirect Binding" +msgstr "리다이렉트 바인딩" + +#: authentik/sources/saml/models.py:41 +msgid "POST Binding" +msgstr "POST 바인딩" + +#: authentik/sources/saml/models.py:42 +msgid "POST Binding with auto-confirmation" +msgstr "자동 확인 기능이 있는 POST 바인딩" + +#: authentik/sources/saml/models.py:70 +msgid "Flow used before authentication." +msgstr "인증 전에 사용될 플로우" + +#: authentik/sources/saml/models.py:77 +msgid "Issuer" +msgstr "발행자" + +#: authentik/sources/saml/models.py:78 +msgid "Also known as Entity ID. Defaults the Metadata URL." +msgstr "Entity ID라고도 합니다. 기본값은 Metadata URL입니다." + +#: authentik/sources/saml/models.py:82 +msgid "SSO URL" +msgstr "SSO URL" + +#: authentik/sources/saml/models.py:83 +msgid "URL that the initial Login request is sent to." +msgstr "초기 로그인 요청을 전송할 URL입니다." + +#: authentik/sources/saml/models.py:89 +msgid "SLO URL" +msgstr "SLO URL" + +#: authentik/sources/saml/models.py:90 +msgid "Optional URL if your IDP supports Single-Logout." +msgstr "IDP가 단일 로그아웃을 지원하는 경우 사용하는 선택적 URL입니다." + +#: 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에 대한 유효성 검사가 수행되지 않으므로 보안 위험이 있을 수 있습니다." + +#: 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 정책입니다. 비워둘 수 있으며, 비워둘 경우 정책이 전송되지 않습니다." + +#: authentik/sources/saml/models.py:115 +msgid "Delete temporary users after" +msgstr "다음 이후 임시 사용자를 삭제" + +#: authentik/sources/saml/models.py:118 +msgid "" +"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 "" +"임시 사용자를 삭제해야 하는 시간 오프셋입니다. 이 설정은 IDP가 'Transient' NameID 형식을 사용하고 사용자가 수동으로 " +"로그아웃하지 않는 경우에만 적용됩니다. (형식: hours=1;minutes=2;seconds=3)" + +#: 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 소스" + +#: authentik/sources/saml/models.py:227 +msgid "SAML Sources" +msgstr "SAML 소스" + +#: 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 인증기 설정 스테이지" + +#: authentik/stages/authenticator_duo/models.py:80 +msgid "Duo Authenticator Setup Stages" +msgstr "Duo 인증기 설정 스테이지" + +#: authentik/stages/authenticator_duo/models.py:103 +msgid "Duo Device" +msgstr "Duo 디바이스" + +#: authentik/stages/authenticator_duo/models.py:104 +msgid "Duo Devices" +msgstr "Duo 디바이스" + +#: 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 인증기 설정 스테이지" + +#: authentik/stages/authenticator_sms/models.py:181 +msgid "SMS Authenticator Setup Stages" +msgstr "SMS 인증기 설정 스테이지" + +#: authentik/stages/authenticator_sms/models.py:226 +msgid "SMS Device" +msgstr "SMS 디바이스" + +#: authentik/stages/authenticator_sms/models.py:227 +msgid "SMS Devices" +msgstr "SMS 디바이스" + +#: authentik/stages/authenticator_sms/stage.py:57 +#: authentik/stages/authenticator_totp/stage.py:41 +#: authentik/stages/authenticator_totp/stage.py:44 +msgid "Code does not match" +msgstr "코드가 일치하지 않음" + +#: authentik/stages/authenticator_sms/stage.py:73 +msgid "Invalid phone number" +msgstr "유효하지 않은 전화번호" + +#: authentik/stages/authenticator_static/models.py:52 +msgid "Static Authenticator Stage" +msgstr "정적 인증 스테이지" + +#: authentik/stages/authenticator_static/models.py:53 +msgid "Static Authenticator Stages" +msgstr "정적 인증 스테이지" + +#: 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_static/models.py:129 +msgid "Static Token" +msgstr "정적 토큰" + +#: authentik/stages/authenticator_static/models.py:130 +msgid "Static Tokens" +msgstr "정적 토큰" + +#: authentik/stages/authenticator_totp/models.py:25 +msgid "6 digits, widely compatible" +msgstr "6자리, 널리 호환됩니다." + +#: authentik/stages/authenticator_totp/models.py:26 +msgid "8 digits, not compatible with apps like Google Authenticator" +msgstr "8자리, Google Authenticator와 같은 앱과 호환되지 않습니다." + +#: authentik/stages/authenticator_totp/models.py:62 +msgid "TOTP Authenticator Setup Stage" +msgstr "TOTP 인증 설정 스테이지" + +#: authentik/stages/authenticator_totp/models.py:63 +msgid "TOTP Authenticator Setup Stages" +msgstr "TOTP 인증 설정 스테이지" + +#: 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 "올바르지 않은 토큰" + +#: 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:20 +msgid "WebAuthn" +msgstr "WebAuthn" + +#: authentik/stages/authenticator_validate/models.py:21 +msgid "Duo" +msgstr "Duo" + +#: authentik/stages/authenticator_validate/models.py:22 +msgid "SMS" +msgstr "SMS" + +#: 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 "인증에 사용할 수 있는 디바이스 클래스" + +#: 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 "인증기 유효성 검사 스테이지" + +#: authentik/stages/authenticator_validate/models.py:93 +msgid "Authenticator Validation Stages" +msgstr "인증기 유효성 검사 스테이지" + +#: authentik/stages/authenticator_webauthn/models.py:112 +msgid "WebAuthn Authenticator Setup Stage" +msgstr "WebAuthn 인증 설정 스테이지" + +#: authentik/stages/authenticator_webauthn/models.py:113 +msgid "WebAuthn Authenticator Setup Stages" +msgstr "WebAuthn 인증 설정 스테이지" + +#: authentik/stages/authenticator_webauthn/models.py:151 +msgid "WebAuthn Device" +msgstr "WebAuthn 디바이스" + +#: authentik/stages/authenticator_webauthn/models.py:152 +msgid "WebAuthn Devices" +msgstr "WebAuthn 디바이스" + +#: authentik/stages/captcha/models.py:14 +msgid "Public key, acquired your captcha Provider." +msgstr "공개 키, 캡챠 공급자에서 얻을 수 있습니다." + +#: authentik/stages/captcha/models.py:15 +msgid "Private key, acquired your captcha Provider." +msgstr "개인 키, 캡챠 공급자에서 얻을 수 있습니다." + +#: authentik/stages/captcha/models.py:37 +msgid "Captcha Stage" +msgstr "캡챠 스테이지" + +#: authentik/stages/captcha/models.py:38 +msgid "Captcha Stages" +msgstr "캡챠 스테이지" + +#: 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 "동의 스테이지" + +#: authentik/stages/consent/models.py:51 +msgid "Consent Stages" +msgstr "동의 스테이지" + +#: authentik/stages/consent/models.py:72 +msgid "User Consent" +msgstr "사용자 동의" + +#: authentik/stages/consent/models.py:73 +msgid "User Consents" +msgstr "사용자 동의" + +#: authentik/stages/deny/models.py:32 +msgid "Deny Stage" +msgstr "거부 스테이지" + +#: authentik/stages/deny/models.py:33 +msgid "Deny Stages" +msgstr "거부 스테이지" + +#: authentik/stages/dummy/models.py:34 +msgid "Dummy Stage" +msgstr "더미 스테이지" + +#: authentik/stages/dummy/models.py:35 +msgid "Dummy Stages" +msgstr "더미 스테이지" + +#: authentik/stages/email/models.py:26 +msgid "Password Reset" +msgstr "비밀번호 초기화" + +#: authentik/stages/email/models.py:30 +msgid "Account Confirmation" +msgstr "계정 확인" + +#: 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 "스테이지가 완료되면 사용자를 활성화합니다." + +#: authentik/stages/email/models.py:78 +msgid "Time in minutes the token sent is valid." +msgstr "전송된 토큰이 유효한 시간(분)입니다." + +#: authentik/stages/email/models.py:132 +msgid "Email Stage" +msgstr "이메일 스테이지" + +#: authentik/stages/email/models.py:133 +msgid "Email Stages" +msgstr "이메일 스테이지" + +#: authentik/stages/email/stage.py:126 +msgid "Exception occurred while rendering E-mail template" +msgstr "이메일 템플릿 렌더링 중 예외가 발생" + +#: authentik/stages/email/stage.py:140 +msgid "Successfully verified Email." +msgstr "이메일을 성공적으로 확인했습니다." + +#: authentik/stages/email/stage.py:147 authentik/stages/email/stage.py:173 +msgid "No pending user." +msgstr "보류 중인 사용자가 없습니다." + +#: authentik/stages/email/stage.py:163 +msgid "Email sent." +msgstr "이메일을 보냈습니다." + +#: authentik/stages/email/stage.py:176 +msgid "Email Successfully sent." +msgstr "성공적으로 이메일을 보냈습니다." + +#: authentik/stages/email/templates/email/account_confirmation.html:10 +msgid "Welcome!" +msgstr "반갑습니다!" + +#: 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 "시작하게 되어 기쁩니다. 먼저 계정을 확인해야 합니다. 아래 버튼을 누르기만 하면 됩니다." + +#: authentik/stages/email/templates/email/account_confirmation.html:24 +msgid "Confirm Account" +msgstr "계정 확인" + +#: authentik/stages/email/templates/email/account_confirmation.html:36 +#, python-format +msgid "" +"\n" +" If that doesn't work, copy and paste the following link in your browser: %(url)s\n" +" " +msgstr "" +"\n" +" 그래도 문제가 해결되지 않으면 다음 링크를 복사하여 브라우저에 붙여넣으세요: %(url)s\n" +" " + +#: authentik/stages/email/templates/email/event_notification.html:46 +#, python-format +msgid "" +"\n" +" This email was sent from the notification transport %(name)s.\n" +" " +msgstr "" +"\n" +" 이 이메일은 통지 전송 %(name)s에서 전송되었습니다.\n" +" " + +#: authentik/stages/email/templates/email/password_reset.html:10 +#, python-format +msgid "" +"\n" +" Hi %(username)s,\n" +" " +msgstr "" +"\n" +" %(username)s 님께,\n" +" " + +#: 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" +" 최근에 인증 계정의 비밀번호 변경을 요청하셨습니다. 아래 버튼을 사용하여 새 비밀번호를 설정하세요.\n" +" " + +#: 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" +" " +msgstr "" +"\n" +" 비밀번호 변경을 요청하지 않은 경우 이 이메일을 무시하세요. 위의 링크는 %(expires)s후에 유효합니다.\n" +" " + +#: authentik/stages/email/templates/email/setup.html:9 +msgid "authentik Test-Email" +msgstr "authentik 테스트 이메일" + +#: authentik/stages/email/templates/email/setup.html:17 +msgid "" +"\n" +" This is a test email to inform you, that you've successfully configured authentik emails.\n" +" " +msgstr "" +"\n" +" 이 이메일은 인증 이메일을 성공적으로 구성했음을 알려주는 테스트 이메일입니다.\n" +" " + +#: 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 "" +"Fields of the user object to match against. (Hold shift to select multiple " +"options)" +msgstr "사용자 오브젝트와 일치시키기 위한 사용자 필드입니다. (여러 옵션을 선택하려면 Shift 키를 누르세요)" + +#: authentik/stages/identification/models.py:47 +msgid "When enabled, user fields are matched regardless of their casing." +msgstr "활성화하면, 사용자 필드를 대소문자에 관계없이 일치시킵니다." + +#: 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:60 +msgid "" +"When enabled, the stage will succeed and continue even when incorrect user " +"info is entered." +msgstr "활성화되면 잘못된 사용자 정보가 입력되더라도 단계가 성공하고 계속됩니다." + +#: authentik/stages/identification/models.py:72 +msgid "Optional enrollment flow, which is linked at the bottom of the page." +msgstr "페이지 하단에 링크된, 선택적 등록 플로우를 참조하세요." + +#: authentik/stages/identification/models.py:81 +msgid "Optional recovery flow, which is linked at the bottom of the page." +msgstr "페이지 하단에 링크된, 선택적 복구 플로우를 참조하세요." + +#: authentik/stages/identification/models.py:90 +msgid "Optional passwordless flow, which is linked at the bottom of the page." +msgstr "페이지 하단에 링크된, 선택적 비밀번호 없는 플로우를 참조하세요." + +#: authentik/stages/identification/models.py:94 +msgid "Specify which sources should be shown." +msgstr "표시할 소스를 지정합니다." + +#: authentik/stages/identification/models.py:115 +msgid "Identification Stage" +msgstr "식별 스테이지" + +#: authentik/stages/identification/models.py:116 +msgid "Identification Stages" +msgstr "식별 스테이지" + +#: authentik/stages/identification/stage.py:188 +msgid "Log in" +msgstr "로그인" + +#: 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 "초대 스테이지" + +#: authentik/stages/invitation/models.py:45 +msgid "Invitation Stages" +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 "사용자 등록에 강제로 적용할 선택적인 고정 데이터입니다." + +#: authentik/stages/invitation/models.py:84 +msgid "Invitation" +msgstr "초대" + +#: authentik/stages/invitation/models.py:85 +msgid "Invitations" +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 "사용자 데이터베이스 + 표준 비밀번호" + +#: authentik/stages/password/models.py:24 +msgid "User database + app passwords" +msgstr "사용자 데이터베이스 + 앱 비밀번호" + +#: authentik/stages/password/models.py:28 +msgid "User database + LDAP password" +msgstr "사용자 데이터베이스 + LDAP 비밀번호" + +#: authentik/stages/password/models.py:38 +msgid "Selection of backends to test the password against." +msgstr "비밀번호를 테스트할 백엔드를 선택합니다." + +#: 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 "비밀번호 스테이지" + +#: authentik/stages/password/models.py:76 +msgid "Password Stages" +msgstr "비밀번호 스테이지" + +#: authentik/stages/password/stage.py:124 +msgid "Invalid password" +msgstr "올바르지 않은 비밀번호" + +#: authentik/stages/prompt/models.py:43 +msgid "Text: Simple Text input" +msgstr "텍스트: 간단한 텍스트 입력" + +#: 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 "텍스트(읽기 전용): 간단한 텍스트 입력이 가능하지만 편집은 불가능합니다." + +#: 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 "이메일: 이메일 유형이 있는 텍스트 필드입니다." + +#: 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 "구분자: 정적 구분선" + +#: authentik/stages/prompt/models.py:86 +msgid "Hidden: Hidden field, can be used to insert data into form." +msgstr "숨김: 숨겨진 필드로, 양식에 데이터를 삽입하는 데 사용할 수 있습니다." + +#: authentik/stages/prompt/models.py:87 +msgid "Static: Static value, displayed as-is." +msgstr "정적: 정적 값, 있는 그대로 표시됩니다." + +#: 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 "값을 저장하는 데 사용되는, 양식 필드의 이름입니다." + +#: 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:322 +msgid "Prompts" +msgstr "프롬프트" + +#: authentik/stages/prompt/models.py:349 +msgid "Prompt Stage" +msgstr "프롬프트 스테이지" + +#: authentik/stages/prompt/models.py:350 +msgid "Prompt Stages" +msgstr "프롬프트 스테이지" + +#: authentik/stages/prompt/stage.py:108 +msgid "Passwords don't match." +msgstr "비밀번호가 일치하지 않습니다." + +#: authentik/stages/user_delete/models.py:31 +msgid "User Delete Stage" +msgstr "사용자 삭제 스테이지" + +#: authentik/stages/user_delete/models.py:32 +msgid "User Delete Stages" +msgstr "사용자 삭제 스테이지" + +#: authentik/stages/user_delete/stage.py:18 +msgid "No Pending User." +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)" + +#: 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 "사용자 로그인 스테이지" + +#: authentik/stages/user_login/models.py:55 +msgid "User Login Stages" +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:30 +msgid "User Logout Stage" +msgstr "사용자 로그아웃 스테이지" + +#: authentik/stages/user_logout/models.py:31 +msgid "User Logout Stages" +msgstr "사용자 로그아웃 스테이지" + +#: authentik/stages/user_write/models.py:31 +msgid "When set, newly created users are inactive and cannot login." +msgstr "이 옵션을 설정하면 새로 만든 사용자가 비활성화되어 로그인할 수 없습니다." + +#: authentik/stages/user_write/models.py:39 +msgid "Optionally add newly created users to this group." +msgstr "선택적으로 새로 만든 사용자를 이 그룹에 추가합니다." + +#: authentik/stages/user_write/models.py:68 +msgid "User Write Stage" +msgstr "사용자 쓰기 스테이지" + +#: authentik/stages/user_write/models.py:69 +msgid "User Write Stages" +msgstr "사용자 쓰기 스테이지" + +#: authentik/stages/user_write/stage.py:141 +msgid "No Pending data." +msgstr "보류 중인 데이터가 없습니다." + +#: authentik/stages/user_write/stage.py:147 +msgid "No user found and can't create new user." +msgstr "사용자를 찾을 수 없으며 새 사용자를 만들 수 없습니다." + +#: authentik/stages/user_write/stage.py:164 +#: authentik/stages/user_write/stage.py:178 +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 "" +"이 테넌트를 활성화하는 도메인입니다. 상위 집합도 가능합니다. 예를 들어, `a.b`는 `aa.b`와 `ba.b`가 될 수 있습니다." + +#: authentik/tenants/models.py:58 +msgid "" +"Events will be deleted after this duration.(Format: " +"weeks=3;days=2;hours=3,seconds=2)." +msgstr "이 기간이 지나면 이벤트가 삭제됩니다. (서식: hours=-1;minutes=-2;seconds=-3)" + +#: 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 "테넌트" + +#: authentik/tenants/models.py:94 +msgid "Tenants" +msgstr "테넌트" diff --git a/locale/zh-Hans/LC_MESSAGES/django.mo b/locale/zh-Hans/LC_MESSAGES/django.mo index 7e17e6c28..d9e5e2ac3 100644 Binary files a/locale/zh-Hans/LC_MESSAGES/django.mo and b/locale/zh-Hans/LC_MESSAGES/django.mo differ diff --git a/pyproject.toml b/pyproject.toml index feac7e438..8f120dec3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,12 +17,12 @@ pythonPlatform = "All" [tool.black] line-length = 100 -target-version = ['py311'] +target-version = ['py312'] exclude = 'node_modules' [tool.ruff] line-length = 100 -target-version = "py311" +target-version = "py312" exclude = ["**/migrations/**", "**/node_modules/**"] [tool.isort] @@ -113,7 +113,7 @@ filterwarnings = [ [tool.poetry] name = "authentik" -version = "2023.10.4" +version = "2023.10.5" description = "" authors = ["authentik Team "] diff --git a/schema.yml b/schema.yml index 8541e5317..480e5ebf3 100644 --- a/schema.yml +++ b/schema.yml @@ -1,7 +1,7 @@ openapi: 3.0.3 info: title: authentik - version: 2023.10.4 + version: 2023.10.5 description: Making authentication simple. contact: email: hello@goauthentik.io @@ -17164,7 +17164,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Task' + $ref: '#/components/schemas/SCIMSyncStatus' description: '' '404': description: Task not found @@ -27047,10 +27047,42 @@ paths: operationId: stages_user_login_list description: UserLoginStage Viewset parameters: + - in: query + name: geoip_binding + schema: + type: string + enum: + - bind_continent + - bind_continent_country + - bind_continent_country_city + - no_binding + description: |- + Bind sessions created by this stage to the configured GeoIP location + + * `no_binding` - No Binding + * `bind_continent` - Bind Continent + * `bind_continent_country` - Bind Continent Country + * `bind_continent_country_city` - Bind Continent Country City - in: query name: name schema: type: string + - in: query + name: network_binding + schema: + type: string + enum: + - bind_asn + - bind_asn_network + - bind_asn_network_ip + - no_binding + description: |- + Bind sessions created by this stage to the configured network + + * `no_binding` - No Binding + * `bind_asn` - Bind Asn + * `bind_asn_network` - Bind Asn Network + * `bind_asn_network_ip` - Bind Asn Network Ip - name: ordering required: false in: query @@ -28802,7 +28834,7 @@ components: readOnly: true geo_ip: type: object - description: Get parsed user agent + description: Get GeoIP Data properties: continent: type: string @@ -28824,6 +28856,24 @@ components: - long nullable: true readOnly: true + asn: + type: object + description: Get ASN Data + properties: + asn: + type: integer + as_org: + type: string + nullable: true + network: + type: string + nullable: true + required: + - as_org + - asn + - network + nullable: true + readOnly: true user: type: integer last_ip: @@ -28838,6 +28888,7 @@ components: type: string format: date-time required: + - asn - current - geo_ip - last_ip @@ -29910,6 +29961,7 @@ components: enum: - can_save_media - can_geo_ip + - can_asn - can_impersonate - can_debug - is_enterprise @@ -29917,6 +29969,7 @@ components: description: |- * `can_save_media` - Can Save Media * `can_geo_ip` - Can Geo Ip + * `can_asn` - Can Asn * `can_impersonate` - Can Impersonate * `can_debug` - Can Debug * `is_enterprise` - Is Enterprise @@ -32475,6 +32528,18 @@ components: type: string required: - detail + GeoipBindingEnum: + enum: + - no_binding + - bind_continent + - bind_continent_country + - bind_continent_country_city + type: string + description: |- + * `no_binding` - No Binding + * `bind_continent` - Bind Continent + * `bind_continent_country` - Bind Continent Country + * `bind_continent_country_city` - Bind Continent Country City Group: type: object description: Group Serializer @@ -34055,6 +34120,18 @@ components: * `urn:oasis:names:tc:SAML:2.0:nameid-format:X509SubjectName` - X509 * `urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName` - Windows * `urn:oasis:names:tc:SAML:2.0:nameid-format:transient` - Transient + NetworkBindingEnum: + enum: + - no_binding + - bind_asn + - bind_asn_network + - bind_asn_network_ip + type: string + description: |- + * `no_binding` - No Binding + * `bind_asn` - Bind Asn + * `bind_asn_network` - Bind Asn Network + * `bind_asn_network_ip` - Bind Asn Network Ip NotConfiguredActionEnum: enum: - skip @@ -38810,6 +38887,26 @@ components: description: '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)' + network_binding: + allOf: + - $ref: '#/components/schemas/NetworkBindingEnum' + description: |- + Bind sessions created by this stage to the configured network + + * `no_binding` - No Binding + * `bind_asn` - Bind Asn + * `bind_asn_network` - Bind Asn Network + * `bind_asn_network_ip` - Bind Asn Network Ip + geoip_binding: + allOf: + - $ref: '#/components/schemas/GeoipBindingEnum' + description: |- + Bind sessions created by this stage to the configured GeoIP location + + * `no_binding` - No Binding + * `bind_continent` - Bind Continent + * `bind_continent_country` - Bind Continent Country + * `bind_continent_country_city` - Bind Continent Country City PatchedUserLogoutStageRequest: type: object description: UserLogoutStage Serializer @@ -39841,13 +39938,13 @@ components: ProviderTypeEnum: enum: - apple + - openidconnect - azuread - discord - facebook - github - google - mailcow - - openidconnect - okta - patreon - reddit @@ -39856,13 +39953,13 @@ components: type: string description: |- * `apple` - Apple + * `openidconnect` - OpenID Connect * `azuread` - Azure AD * `discord` - Discord * `facebook` - Facebook * `github` - GitHub * `google` - Google * `mailcow` - Mailcow - * `openidconnect` - OpenID Connect * `okta` - Okta * `patreon` - Patreon * `reddit` - Reddit @@ -40418,6 +40515,7 @@ components: ip: type: string ip_geo_data: {} + ip_asn_data: {} score: type: integer maximum: 9223372036854775807 @@ -41374,6 +41472,21 @@ components: - name - token - url + SCIMSyncStatus: + type: object + description: SCIM Provider sync status + properties: + is_running: + type: boolean + readOnly: true + tasks: + type: array + items: + $ref: '#/components/schemas/Task' + readOnly: true + required: + - is_running + - tasks SMSDevice: type: object description: Serializer for sms authenticator devices @@ -42749,6 +42862,26 @@ components: description: '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)' + network_binding: + allOf: + - $ref: '#/components/schemas/NetworkBindingEnum' + description: |- + Bind sessions created by this stage to the configured network + + * `no_binding` - No Binding + * `bind_asn` - Bind Asn + * `bind_asn_network` - Bind Asn Network + * `bind_asn_network_ip` - Bind Asn Network Ip + geoip_binding: + allOf: + - $ref: '#/components/schemas/GeoipBindingEnum' + description: |- + Bind sessions created by this stage to the configured GeoIP location + + * `no_binding` - No Binding + * `bind_continent` - Bind Continent + * `bind_continent_country` - Bind Continent Country + * `bind_continent_country_city` - Bind Continent Country City required: - component - meta_model_name @@ -42781,6 +42914,26 @@ components: description: '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)' + network_binding: + allOf: + - $ref: '#/components/schemas/NetworkBindingEnum' + description: |- + Bind sessions created by this stage to the configured network + + * `no_binding` - No Binding + * `bind_asn` - Bind Asn + * `bind_asn_network` - Bind Asn Network + * `bind_asn_network_ip` - Bind Asn Network Ip + geoip_binding: + allOf: + - $ref: '#/components/schemas/GeoipBindingEnum' + description: |- + Bind sessions created by this stage to the configured GeoIP location + + * `no_binding` - No Binding + * `bind_continent` - Bind Continent + * `bind_continent_country` - Bind Continent Country + * `bind_continent_country_city` - Bind Continent Country City required: - name UserLogoutStage: diff --git a/scripts/generate_config.py b/scripts/generate_config.py index 2351cab09..379ec1873 100644 --- a/scripts/generate_config.py +++ b/scripts/generate_config.py @@ -18,7 +18,12 @@ with open("local.env.yml", "w", encoding="utf-8") as _config: }, "blueprints_dir": "./blueprints", "cert_discovery_dir": "./certs", - "geoip": "tests/GeoLite2-City-Test.mmdb", + "events": { + "processors": { + "geoip": "tests/GeoLite2-City-Test.mmdb", + "asn": "tests/GeoLite2-ASN-Test.mmdb", + } + }, "storage": { "media": { "backend": "file", diff --git a/tests/GeoLite2-ASN-Test.mmdb b/tests/GeoLite2-ASN-Test.mmdb new file mode 100644 index 000000000..3e5033144 Binary files /dev/null and b/tests/GeoLite2-ASN-Test.mmdb differ diff --git a/tests/wdio/package-lock.json b/tests/wdio/package-lock.json index 11905de2f..c9ce79620 100644 --- a/tests/wdio/package-lock.json +++ b/tests/wdio/package-lock.json @@ -7,12 +7,12 @@ "name": "@goauthentik/web-tests", "devDependencies": { "@trivago/prettier-plugin-sort-imports": "^4.3.0", - "@typescript-eslint/eslint-plugin": "^6.15.0", - "@typescript-eslint/parser": "^6.15.0", - "@wdio/cli": "^8.26.3", - "@wdio/local-runner": "^8.26.3", - "@wdio/mocha-framework": "^8.26.3", - "@wdio/spec-reporter": "^8.26.3", + "@typescript-eslint/eslint-plugin": "^6.16.0", + "@typescript-eslint/parser": "^6.16.0", + "@wdio/cli": "^8.27.0", + "@wdio/local-runner": "^8.27.0", + "@wdio/mocha-framework": "^8.27.0", + "@wdio/spec-reporter": "^8.27.0", "eslint": "^8.56.0", "eslint-config-google": "^0.14.0", "eslint-plugin-sonarjs": "^0.23.0", @@ -946,16 +946,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.15.0.tgz", - "integrity": "sha512-j5qoikQqPccq9QoBAupOP+CBu8BaJ8BLjaXSioDISeTZkVO3ig7oSIKh3H+rEpee7xCXtWwSB4KIL5l6hWZzpg==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.16.0.tgz", + "integrity": "sha512-O5f7Kv5o4dLWQtPX4ywPPa+v9G+1q1x8mz0Kr0pXUtKsevo+gIJHLkGc8RxaZWtP8RrhwhSNIWThnW42K9/0rQ==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.15.0", - "@typescript-eslint/type-utils": "6.15.0", - "@typescript-eslint/utils": "6.15.0", - "@typescript-eslint/visitor-keys": "6.15.0", + "@typescript-eslint/scope-manager": "6.16.0", + "@typescript-eslint/type-utils": "6.16.0", + "@typescript-eslint/utils": "6.16.0", + "@typescript-eslint/visitor-keys": "6.16.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -981,15 +981,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.15.0.tgz", - "integrity": "sha512-MkgKNnsjC6QwcMdlNAel24jjkEO/0hQaMDLqP4S9zq5HBAUJNQB6y+3DwLjX7b3l2b37eNAxMPLwb3/kh8VKdA==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.16.0.tgz", + "integrity": "sha512-H2GM3eUo12HpKZU9njig3DF5zJ58ja6ahj1GoHEHOgQvYxzoFJJEvC1MQ7T2l9Ha+69ZSOn7RTxOdpC/y3ikMw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.15.0", - "@typescript-eslint/types": "6.15.0", - "@typescript-eslint/typescript-estree": "6.15.0", - "@typescript-eslint/visitor-keys": "6.15.0", + "@typescript-eslint/scope-manager": "6.16.0", + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/typescript-estree": "6.16.0", + "@typescript-eslint/visitor-keys": "6.16.0", "debug": "^4.3.4" }, "engines": { @@ -1009,13 +1009,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.15.0.tgz", - "integrity": "sha512-+BdvxYBltqrmgCNu4Li+fGDIkW9n//NrruzG9X1vBzaNK+ExVXPoGB71kneaVw/Jp+4rH/vaMAGC6JfMbHstVg==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.16.0.tgz", + "integrity": "sha512-0N7Y9DSPdaBQ3sqSCwlrm9zJwkpOuc6HYm7LpzLAPqBL7dmzAUimr4M29dMkOP/tEwvOCC/Cxo//yOfJD3HUiw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.15.0", - "@typescript-eslint/visitor-keys": "6.15.0" + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/visitor-keys": "6.16.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1026,13 +1026,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.15.0.tgz", - "integrity": "sha512-CnmHKTfX6450Bo49hPg2OkIm/D/TVYV7jO1MCfPYGwf6x3GO0VU8YMO5AYMn+u3X05lRRxA4fWCz87GFQV6yVQ==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.16.0.tgz", + "integrity": "sha512-ThmrEOcARmOnoyQfYkHw/DX2SEYBalVECmoldVuH6qagKROp/jMnfXpAU/pAIWub9c4YTxga+XwgAkoA0pxfmg==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.15.0", - "@typescript-eslint/utils": "6.15.0", + "@typescript-eslint/typescript-estree": "6.16.0", + "@typescript-eslint/utils": "6.16.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -1053,9 +1053,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.15.0.tgz", - "integrity": "sha512-yXjbt//E4T/ee8Ia1b5mGlbNj9fB9lJP4jqLbZualwpP2BCQ5is6BcWwxpIsY4XKAhmdv3hrW92GdtJbatC6dQ==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.16.0.tgz", + "integrity": "sha512-hvDFpLEvTJoHutVl87+MG/c5C8I6LOgEx05zExTSJDEVU7hhR3jhV8M5zuggbdFCw98+HhZWPHZeKS97kS3JoQ==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1066,16 +1066,17 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.15.0.tgz", - "integrity": "sha512-7mVZJN7Hd15OmGuWrp2T9UvqR2Ecg+1j/Bp1jXUEY2GZKV6FXlOIoqVDmLpBiEiq3katvj/2n2mR0SDwtloCew==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.16.0.tgz", + "integrity": "sha512-VTWZuixh/vr7nih6CfrdpmFNLEnoVBF1skfjdyGnNwXOH1SLeHItGdZDHhhAIzd3ACazyY2Fg76zuzOVTaknGA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.15.0", - "@typescript-eslint/visitor-keys": "6.15.0", + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/visitor-keys": "6.16.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", + "minimatch": "9.0.3", "semver": "^7.5.4", "ts-api-utils": "^1.0.1" }, @@ -1092,18 +1093,42 @@ } } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/utils": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.15.0.tgz", - "integrity": "sha512-eF82p0Wrrlt8fQSRL0bGXzK5nWPRV2dYQZdajcfzOD9+cQz9O7ugifrJxclB+xVOvWvagXfqS4Es7vpLP4augw==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.16.0.tgz", + "integrity": "sha512-T83QPKrBm6n//q9mv7oiSvy/Xq/7Hyw9SzSEhMHJwznEmQayfBM87+oAlkNAMEO7/MjIwKyOHgBJbxB0s7gx2A==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.15.0", - "@typescript-eslint/types": "6.15.0", - "@typescript-eslint/typescript-estree": "6.15.0", + "@typescript-eslint/scope-manager": "6.16.0", + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/typescript-estree": "6.16.0", "semver": "^7.5.4" }, "engines": { @@ -1118,12 +1143,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.15.0.tgz", - "integrity": "sha512-1zvtdC1a9h5Tb5jU9x3ADNXO9yjP8rXlaoChu0DQX40vf5ACVpYIVIZhIMZ6d5sDXH7vq4dsZBT1fEGj8D2n2w==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.16.0.tgz", + "integrity": "sha512-QSFQLruk7fhs91a/Ep/LqRdbJCZ1Rq03rqBdKT5Ky17Sz8zRLUksqIe9DW0pKtg/Z35/ztbLQ6qpOCN6rOC11A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.15.0", + "@typescript-eslint/types": "6.16.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -1141,18 +1166,18 @@ "dev": true }, "node_modules/@wdio/cli": { - "version": "8.26.3", - "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-8.26.3.tgz", - "integrity": "sha512-wrq145sNBw4DrsF5GEK8TBxqVWln7GZpNpM5QeDqCcZzVHIqDud4f7nADgZGbR8dJ96NVfC3rby6wbeRQUA+eg==", + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-8.27.0.tgz", + "integrity": "sha512-wdNYNvu52XxOqNHqDMGAtexBz+MM0RE2Z5U5ljyllbP3ed5vcvvK9vswURtI4cFGoqobVeoC7wif3VeD3aN+aQ==", "dev": true, "dependencies": { "@types/node": "^20.1.1", - "@wdio/config": "8.26.3", - "@wdio/globals": "8.26.3", + "@wdio/config": "8.27.0", + "@wdio/globals": "8.27.0", "@wdio/logger": "8.24.12", "@wdio/protocols": "8.24.12", - "@wdio/types": "8.26.3", - "@wdio/utils": "8.26.3", + "@wdio/types": "8.27.0", + "@wdio/utils": "8.27.0", "async-exit-hook": "^2.0.1", "chalk": "^5.2.0", "chokidar": "^3.5.3", @@ -1167,7 +1192,7 @@ "lodash.union": "^4.6.0", "read-pkg-up": "^10.0.0", "recursive-readdir": "^2.2.3", - "webdriverio": "8.26.3", + "webdriverio": "8.27.0", "yargs": "^17.7.2" }, "bin": { @@ -1190,14 +1215,14 @@ } }, "node_modules/@wdio/config": { - "version": "8.26.3", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.26.3.tgz", - "integrity": "sha512-NWh2JXRSyP4gY+jeC79u0L3hSXW/s3rOWez4M6qAglT91fZTXFbIl1GM8lnZlCq03ye2qFPqYrZ+4tGNQj7YxQ==", + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.27.0.tgz", + "integrity": "sha512-zYM5daeiBVVAbQj0ASymAt0RUsocLVIwKiUHNa8gg/1GsZnztGjetXExSp1gXlxtMVM5xWUSKjh6ceFK79gWDQ==", "dev": true, "dependencies": { "@wdio/logger": "8.24.12", - "@wdio/types": "8.26.3", - "@wdio/utils": "8.26.3", + "@wdio/types": "8.27.0", + "@wdio/utils": "8.27.0", "decamelize": "^6.0.0", "deepmerge-ts": "^5.0.0", "glob": "^10.2.2", @@ -1208,29 +1233,29 @@ } }, "node_modules/@wdio/globals": { - "version": "8.26.3", - "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.26.3.tgz", - "integrity": "sha512-RW3UsvnUb4DjxVOqIngXQMcDJlbH+QL/LeChznUF0FW+Mqg/mZWukBld5/dDwgQHk9F2TOzc8ctk5FM3s1AoWQ==", + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.27.0.tgz", + "integrity": "sha512-HUPOIsrmxfF0LhU68lVsNGQGZkW/bWOvcCd8WxeaggTAH9JyxasxxfwzeCceAuhAvwtlwoMXITOpjAXO2mj38Q==", "dev": true, "engines": { "node": "^16.13 || >=18" }, "optionalDependencies": { "expect-webdriverio": "^4.6.1", - "webdriverio": "8.26.3" + "webdriverio": "8.27.0" } }, "node_modules/@wdio/local-runner": { - "version": "8.26.3", - "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.26.3.tgz", - "integrity": "sha512-YWxTBp6tc8Dlz09rnRhV2GXV4b3w5G0WyYEf81D+kXDI3QxDvYn6QujByr6Q7fQ9yLwJU4ptnT2uL5IbAwVo2A==", + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.27.0.tgz", + "integrity": "sha512-nxS17mhoLkXP20eoPMkz7tbMFMOQejSw0hZfkEvuDCNhJokr8ugp6IjYXL9f7yV9IB9UDGHox8WGY4ArSrOeBA==", "dev": true, "dependencies": { "@types/node": "^20.1.0", "@wdio/logger": "8.24.12", "@wdio/repl": "8.24.12", - "@wdio/runner": "8.26.3", - "@wdio/types": "8.26.3", + "@wdio/runner": "8.27.0", + "@wdio/types": "8.27.0", "async-exit-hook": "^2.0.1", "split2": "^4.1.0", "stream-buffers": "^3.0.2" @@ -1267,16 +1292,16 @@ } }, "node_modules/@wdio/mocha-framework": { - "version": "8.26.3", - "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-8.26.3.tgz", - "integrity": "sha512-r9uHUcXhh6TKFqTBCacnbtQx0nZjFsnV9Pony8CY9qcNCn2q666ucQbrtMfZdjuevhn5N0E710+El4eAvK3jyw==", + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-8.27.0.tgz", + "integrity": "sha512-NaFUPv90ks1XlZy0qdUaJ5/ilBtiCCgTIxaPexshJiaVDT5cV+Igjag/O80HIcvqknOZpdKAR0I1ArQzhJrmcA==", "dev": true, "dependencies": { "@types/mocha": "^10.0.0", "@types/node": "^20.1.0", "@wdio/logger": "8.24.12", - "@wdio/types": "8.26.3", - "@wdio/utils": "8.26.3", + "@wdio/types": "8.27.0", + "@wdio/utils": "8.27.0", "mocha": "^10.0.0" }, "engines": { @@ -1302,14 +1327,14 @@ } }, "node_modules/@wdio/reporter": { - "version": "8.26.3", - "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-8.26.3.tgz", - "integrity": "sha512-F/sF1Hwxp1osM2wto4JydONQGxqkbOhwbLM0o4Y8eHPgK7/m+Kn9uygBDqPVpgQnpf0kUfhpICe9gaZzG4Jt+g==", + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-8.27.0.tgz", + "integrity": "sha512-kBwsrHbsblmXfHSWlaOKXjPRPeT29WSKTUoCmzuTcCkhvbjY4TrEB0p04cpaM7uNqdIZTxHng54gZVaG/nZPiw==", "dev": true, "dependencies": { "@types/node": "^20.1.0", "@wdio/logger": "8.24.12", - "@wdio/types": "8.26.3", + "@wdio/types": "8.27.0", "diff": "^5.0.0", "object-inspect": "^1.12.0" }, @@ -1318,35 +1343,35 @@ } }, "node_modules/@wdio/runner": { - "version": "8.26.3", - "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.26.3.tgz", - "integrity": "sha512-mbZGkBbXTRtj1hL5QUbNxpJvhE4rkXvYlUuea1uOVk3e2/+k2dZeGeKPgh1Q7Dt07118dfujCB7pQCYldE/dGg==", + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.27.0.tgz", + "integrity": "sha512-da332r2d1QXdRhMhsDxMObcqLZS0l/u14pHICNTvEHp+72gOttbjUDvdMHPQY6Ae5ul7AVVQ05qpmz9CX7TzOg==", "dev": true, "dependencies": { "@types/node": "^20.1.0", - "@wdio/config": "8.26.3", - "@wdio/globals": "8.26.3", + "@wdio/config": "8.27.0", + "@wdio/globals": "8.27.0", "@wdio/logger": "8.24.12", - "@wdio/types": "8.26.3", - "@wdio/utils": "8.26.3", + "@wdio/types": "8.27.0", + "@wdio/utils": "8.27.0", "deepmerge-ts": "^5.0.0", "expect-webdriverio": "^4.6.1", "gaze": "^1.1.2", - "webdriver": "8.26.3", - "webdriverio": "8.26.3" + "webdriver": "8.27.0", + "webdriverio": "8.27.0" }, "engines": { "node": "^16.13 || >=18" } }, "node_modules/@wdio/spec-reporter": { - "version": "8.26.3", - "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-8.26.3.tgz", - "integrity": "sha512-YfKlBOmxGyJk08BpDnsjPsp85XyhG7Cu2qoAVxtJ8kkJOZaGfUg9TBV9DXDqvdZcxCMnPfDfQIda6LzfkZf58Q==", + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-8.27.0.tgz", + "integrity": "sha512-EOXLBIr4oLzSDp/BQ86IqCulSF0jwEAj2EiMeY6dh9WXzBBtoR8WnoX/27xFoZ8GU2zetWC3EVnLJ0Ex8Up1mA==", "dev": true, "dependencies": { - "@wdio/reporter": "8.26.3", - "@wdio/types": "8.26.3", + "@wdio/reporter": "8.27.0", + "@wdio/types": "8.27.0", "chalk": "^5.1.2", "easy-table": "^1.2.0", "pretty-ms": "^7.0.0" @@ -1368,9 +1393,9 @@ } }, "node_modules/@wdio/types": { - "version": "8.26.3", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.26.3.tgz", - "integrity": "sha512-WOxvSV4sKJ5QCRNTJHeKCzgO2TZmcK1eDlJ+FObt9Pnt+4pCRy/881eVY/Aj2bozn2hhzq0AK/h6oPAUV/gjCg==", + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.27.0.tgz", + "integrity": "sha512-LbP9FKh8r0uW9/dKhTIUCC1Su8PsP9TmzGKXkWt6/IMacgJiB/zW3u1CgyaLw9lG0UiQORHGoeJX9zB2HZAh4w==", "dev": true, "dependencies": { "@types/node": "^20.1.0" @@ -1380,14 +1405,14 @@ } }, "node_modules/@wdio/utils": { - "version": "8.26.3", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.26.3.tgz", - "integrity": "sha512-LA/iCKgJQemAAXoN6vHyKBtngdkFUWEnjB8Yd1Xm3gUQTvY4GVlvcqOxC2RF5Th7/L2LNxc6TWuErYv/mm5H+w==", + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.27.0.tgz", + "integrity": "sha512-4BY+JBQssVn003P5lA289uDMie3LtGinHze5btkcW9timB6VaU+EeZS4eKTPC0pziizLhteVvXYxv3YTpeeRfA==", "dev": true, "dependencies": { "@puppeteer/browsers": "^1.6.0", "@wdio/logger": "8.24.12", - "@wdio/types": "8.26.3", + "@wdio/types": "8.27.0", "decamelize": "^6.0.0", "deepmerge-ts": "^5.1.0", "edgedriver": "^5.3.5", @@ -8495,18 +8520,18 @@ } }, "node_modules/webdriver": { - "version": "8.26.3", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.26.3.tgz", - "integrity": "sha512-vHbMj0BFXPMtKVmJsVIkFVbdOT8eXkjFeJ7LmJL8cMMe1S5Lt44DqRjSBBoGsqYoYgIBmKpqAQcDrHrv9m7smQ==", + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.27.0.tgz", + "integrity": "sha512-n1IA+rR3u84XxU9swiKUM06BkEC0GDimfZkBML57cny+utQOUbdM/mBpqCUnkWX/RBz/p2EfHdKNyOs3/REaog==", "dev": true, "dependencies": { "@types/node": "^20.1.0", "@types/ws": "^8.5.3", - "@wdio/config": "8.26.3", + "@wdio/config": "8.27.0", "@wdio/logger": "8.24.12", "@wdio/protocols": "8.24.12", - "@wdio/types": "8.26.3", - "@wdio/utils": "8.26.3", + "@wdio/types": "8.27.0", + "@wdio/utils": "8.27.0", "deepmerge-ts": "^5.1.0", "got": "^12.6.1", "ky": "^0.33.0", @@ -8517,18 +8542,18 @@ } }, "node_modules/webdriverio": { - "version": "8.26.3", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.26.3.tgz", - "integrity": "sha512-5Ka8MOQoK866EI3whiCvzD1IiKFBq9niWF3lh92uMt6ZjbUZZoe5esWIHhFsHFxT6dOOU8uXR/Gr6qsBJFZReA==", + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.27.0.tgz", + "integrity": "sha512-Qh5VCiBjEmxnmXcL1QEFoDzFqTtaWKrXriuU5G0yHKCModGAt2G7IHTkAok3CpmkVJfZpEvY630aP1MvgDtFhw==", "dev": true, "dependencies": { "@types/node": "^20.1.0", - "@wdio/config": "8.26.3", + "@wdio/config": "8.27.0", "@wdio/logger": "8.24.12", "@wdio/protocols": "8.24.12", "@wdio/repl": "8.24.12", - "@wdio/types": "8.26.3", - "@wdio/utils": "8.26.3", + "@wdio/types": "8.27.0", + "@wdio/utils": "8.27.0", "archiver": "^6.0.0", "aria-query": "^5.0.0", "css-shorthand-properties": "^1.1.1", @@ -8545,7 +8570,7 @@ "resq": "^1.9.1", "rgb2hex": "0.2.5", "serialize-error": "^11.0.1", - "webdriver": "8.26.3" + "webdriver": "8.27.0" }, "engines": { "node": "^16.13 || >=18" diff --git a/tests/wdio/package.json b/tests/wdio/package.json index 78b0928fc..eeb0c124e 100644 --- a/tests/wdio/package.json +++ b/tests/wdio/package.json @@ -4,12 +4,12 @@ "type": "module", "devDependencies": { "@trivago/prettier-plugin-sort-imports": "^4.3.0", - "@typescript-eslint/eslint-plugin": "^6.15.0", - "@typescript-eslint/parser": "^6.15.0", - "@wdio/cli": "^8.26.3", - "@wdio/local-runner": "^8.26.3", - "@wdio/mocha-framework": "^8.26.3", - "@wdio/spec-reporter": "^8.26.3", + "@typescript-eslint/eslint-plugin": "^6.16.0", + "@typescript-eslint/parser": "^6.16.0", + "@wdio/cli": "^8.27.0", + "@wdio/local-runner": "^8.27.0", + "@wdio/mocha-framework": "^8.27.0", + "@wdio/spec-reporter": "^8.27.0", "eslint": "^8.56.0", "eslint-config-google": "^0.14.0", "eslint-plugin-sonarjs": "^0.23.0", diff --git a/web/package-lock.json b/web/package-lock.json index 5e7f5a503..8caa84294 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -17,15 +17,15 @@ "@codemirror/theme-one-dark": "^6.1.2", "@formatjs/intl-listformat": "^7.5.3", "@fortawesome/fontawesome-free": "^6.5.1", - "@goauthentik/api": "^2023.10.4-1702989148", + "@goauthentik/api": "^2023.10.5-1703290840", "@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.88.0", - "@sentry/tracing": "^7.88.0", + "@sentry/browser": "^7.91.0", + "@sentry/tracing": "^7.91.0", "@webcomponents/webcomponentsjs": "^2.8.0", "base64-js": "^1.5.1", "chart.js": "^4.4.1", @@ -61,19 +61,19 @@ "@rollup/plugin-replace": "^5.0.5", "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^11.1.5", - "@storybook/addon-essentials": "^7.6.5", - "@storybook/addon-links": "^7.6.5", - "@storybook/api": "^7.6.5", + "@storybook/addon-essentials": "^7.6.6", + "@storybook/addon-links": "^7.6.6", + "@storybook/api": "^7.6.6", "@storybook/blocks": "^7.6.4", - "@storybook/manager-api": "^7.6.5", - "@storybook/web-components": "^7.6.5", - "@storybook/web-components-vite": "^7.6.5", + "@storybook/manager-api": "^7.6.6", + "@storybook/web-components": "^7.6.6", + "@storybook/web-components-vite": "^7.6.6", "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/chart.js": "^2.9.41", "@types/codemirror": "5.60.15", "@types/grecaptcha": "^3.0.7", - "@typescript-eslint/eslint-plugin": "^6.15.0", - "@typescript-eslint/parser": "^6.15.0", + "@typescript-eslint/eslint-plugin": "^6.16.0", + "@typescript-eslint/parser": "^6.16.0", "babel-plugin-macros": "^3.1.0", "babel-plugin-tsconfig-paths": "^1.0.3", "cross-env": "^7.0.3", @@ -95,7 +95,7 @@ "rollup-plugin-cssimport": "^1.0.3", "rollup-plugin-modify": "^3.0.0", "rollup-plugin-postcss-lit": "^2.1.0", - "storybook": "^7.6.5", + "storybook": "^7.6.6", "storybook-addon-mock": "^4.3.0", "ts-lit-plugin": "^2.0.1", "tslib": "^2.6.2", @@ -2914,9 +2914,9 @@ } }, "node_modules/@goauthentik/api": { - "version": "2023.10.4-1702989148", - "resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2023.10.4-1702989148.tgz", - "integrity": "sha512-BiCZgS/sw9arpQjjXboqjUrAOhn/m5vKH4jg9gioSD5xFQLBBOvfwP0eLpmZTL60ohKZ16/XikowvRPx0K154A==" + "version": "2023.10.5-1703290840", + "resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2023.10.5-1703290840.tgz", + "integrity": "sha512-xYZ2TnUskucxc6sjM7WGl6eiHyXI+ioB3xeXMBd+v07Bvx1xFuQIcv3PXwauKvFzAQD8GtSUbL8tgZ08WpePLQ==" }, "node_modules/@hcaptcha/types": { "version": "1.0.3", @@ -4752,98 +4752,98 @@ ] }, "node_modules/@sentry-internal/feedback": { - "version": "7.88.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-7.88.0.tgz", - "integrity": "sha512-lbK6jgO1I0M96nZQ99mcLSZ55ebwPAP6LhEWhkmc+eAfy97VpiY+qsbmgsmOzCEPqMmEUCEcI0rEZ7fiye2v2Q==", + "version": "7.91.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-7.91.0.tgz", + "integrity": "sha512-SJKTSaz68F5YIwF79EttBm915M2LnacgZMYRnRumyTmMKnebGhYQLwWbZdpaDvOa1U18dgRajDX8Qed/8A3tXw==", "dependencies": { - "@sentry/core": "7.88.0", - "@sentry/types": "7.88.0", - "@sentry/utils": "7.88.0" + "@sentry/core": "7.91.0", + "@sentry/types": "7.91.0", + "@sentry/utils": "7.91.0" }, "engines": { "node": ">=12" } }, "node_modules/@sentry-internal/tracing": { - "version": "7.88.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.88.0.tgz", - "integrity": "sha512-xXQdcYhsS+ourzJHjXNjZC9zakuc97udmpgaXRjEP7FjPYclIx+YXwgFBdHM2kzAwZLFOsEce5dr46GVXUDfZw==", + "version": "7.91.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.91.0.tgz", + "integrity": "sha512-JH5y6gs6BS0its7WF2DhySu7nkhPDfZcdpAXldxzIlJpqFkuwQKLU5nkYJpiIyZz1NHYYtW5aum2bV2oCOdDRA==", "dependencies": { - "@sentry/core": "7.88.0", - "@sentry/types": "7.88.0", - "@sentry/utils": "7.88.0" + "@sentry/core": "7.91.0", + "@sentry/types": "7.91.0", + "@sentry/utils": "7.91.0" }, "engines": { "node": ">=8" } }, "node_modules/@sentry/browser": { - "version": "7.88.0", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.88.0.tgz", - "integrity": "sha512-il4x3PB99nuU/OJQw2RltgYYbo8vtnYoIgneOeEiw4m0ppK1nKkMkd3vDRipGL6E/0i7IUmQfYYy6U10J5Rx+g==", + "version": "7.91.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.91.0.tgz", + "integrity": "sha512-lJv3x/xekzC/biiyAsVCioq2XnKNOZhI6jY3ZzLJZClYV8eKRi7D3KCsHRvMiCdGak1d/6sVp8F4NYY+YiWy1Q==", "dependencies": { - "@sentry-internal/feedback": "7.88.0", - "@sentry-internal/tracing": "7.88.0", - "@sentry/core": "7.88.0", - "@sentry/replay": "7.88.0", - "@sentry/types": "7.88.0", - "@sentry/utils": "7.88.0" + "@sentry-internal/feedback": "7.91.0", + "@sentry-internal/tracing": "7.91.0", + "@sentry/core": "7.91.0", + "@sentry/replay": "7.91.0", + "@sentry/types": "7.91.0", + "@sentry/utils": "7.91.0" }, "engines": { "node": ">=8" } }, "node_modules/@sentry/core": { - "version": "7.88.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.88.0.tgz", - "integrity": "sha512-Jzbb7dcwiCO7kI0a1w+32UzWxbEn2OcZWzp55QMEeAh6nZ/5CXhXwpuHi0tW7doPj+cJdmxMTMu9LqMVfdGkzQ==", + "version": "7.91.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.91.0.tgz", + "integrity": "sha512-tu+gYq4JrTdrR+YSh5IVHF0fJi/Pi9y0HZ5H9HnYy+UMcXIotxf6hIEaC6ZKGeLWkGXffz2gKpQLe/g6vy/lPA==", "dependencies": { - "@sentry/types": "7.88.0", - "@sentry/utils": "7.88.0" + "@sentry/types": "7.91.0", + "@sentry/utils": "7.91.0" }, "engines": { "node": ">=8" } }, "node_modules/@sentry/replay": { - "version": "7.88.0", - "resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.88.0.tgz", - "integrity": "sha512-em5dPKLPG7c/HGDbpIj3aHrWbA4iMwqjevqTzn+++KNO1YslkOosCaGsb1whU3AL1T9c3aIFIhZ4u3rNo+DxcA==", + "version": "7.91.0", + "resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.91.0.tgz", + "integrity": "sha512-XwbesnLLNtaVXKtDoyBB96GxJuhGi9zy3a662Ba/McmumCnkXrMQYpQPh08U7MgkTyDRgjDwm7PXDhiKpcb03g==", "dependencies": { - "@sentry-internal/tracing": "7.88.0", - "@sentry/core": "7.88.0", - "@sentry/types": "7.88.0", - "@sentry/utils": "7.88.0" + "@sentry-internal/tracing": "7.91.0", + "@sentry/core": "7.91.0", + "@sentry/types": "7.91.0", + "@sentry/utils": "7.91.0" }, "engines": { "node": ">=12" } }, "node_modules/@sentry/tracing": { - "version": "7.88.0", - "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.88.0.tgz", - "integrity": "sha512-Lj4hhLraalN4w3swXkP2do1hcaQVOuLvO6eJJbcwf10b+P4CcdlwAnqvxOOCNxA5VzG4/K2BqQ8LRU4S0Ulj9A==", + "version": "7.91.0", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.91.0.tgz", + "integrity": "sha512-IlSAMvqfCL/2TwwN4Tmk6bGMgilGruv5oIJ1GMenVZk53bHwjpjzMbd0ms8+S5zJwAgTQXoCbRhaFFrNmptteQ==", "dependencies": { - "@sentry-internal/tracing": "7.88.0" + "@sentry-internal/tracing": "7.91.0" }, "engines": { "node": ">=8" } }, "node_modules/@sentry/types": { - "version": "7.88.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.88.0.tgz", - "integrity": "sha512-FvwvmX1pWAZKicPj4EpKyho8Wm+C4+r5LiepbbBF8oKwSPJdD2QV1fo/LWxsrzNxWOllFIVIXF5Ed3nPYQWpTw==", + "version": "7.91.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.91.0.tgz", + "integrity": "sha512-bcQnb7J3P3equbCUc+sPuHog2Y47yGD2sCkzmnZBjvBT0Z1B4f36fI/5WjyZhTjLSiOdg3F2otwvikbMjmBDew==", "engines": { "node": ">=8" } }, "node_modules/@sentry/utils": { - "version": "7.88.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.88.0.tgz", - "integrity": "sha512-ukminfRmdBXTzk49orwJf3Lu3hR60ZRHjE2a4IXwYhyDT6JJgJqgsq1hzGXx0AyFfyS4WhfZ6QUBy7fu3BScZQ==", + "version": "7.91.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.91.0.tgz", + "integrity": "sha512-fvxjrEbk6T6Otu++Ax9ntlQ0sGRiwSC179w68aC3u26Wr30FAIRKqHTCCdc2jyWk7Gd9uWRT/cq+g8NG/8BfSg==", "dependencies": { - "@sentry/types": "7.88.0" + "@sentry/types": "7.91.0" }, "engines": { "node": ">=8" @@ -4856,12 +4856,12 @@ "dev": true }, "node_modules/@storybook/addon-actions": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-7.6.5.tgz", - "integrity": "sha512-lW/m9YcaNfBZk+TZLxyzHdd563mBWpsUIveOKYjcPdl/q0FblWWZrRsFHqwLK1ldZ4AZXs8J/47G8CBr6Ew2uQ==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-7.6.6.tgz", + "integrity": "sha512-mLJip9Evb2Chj7ymKbpaybe5NgDy3Du7oSWeURPy/0qXJ2cBqHWnhZ8CTK2DasrstsUhQSJaZVXHhaENT+fn+g==", "dev": true, "dependencies": { - "@storybook/core-events": "7.6.5", + "@storybook/core-events": "7.6.6", "@storybook/global": "^5.0.0", "@types/uuid": "^9.0.1", "dequal": "^2.0.2", @@ -4874,9 +4874,9 @@ } }, "node_modules/@storybook/addon-backgrounds": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-7.6.5.tgz", - "integrity": "sha512-wZZOL19vg4TTRtOTl71XKqPe5hQx3XUh9Fle0wOi91FiFrBdqusrppnyS89wPS8RQG5lXEOFEUvYcMmdCcdZfw==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-7.6.6.tgz", + "integrity": "sha512-w5dZ/0cOe55M2G/Lx9f+Ptk4txUPb+Ng+KqEvTaTNqHoh0Xw4QxEn/ciJwmh1u1g3aMZsOgOvwPwug7ykmLgsA==", "dev": true, "dependencies": { "@storybook/global": "^5.0.0", @@ -4889,12 +4889,12 @@ } }, "node_modules/@storybook/addon-controls": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-7.6.5.tgz", - "integrity": "sha512-EdSZ2pYf74mOXZGGJ22lrDvdvL0YKc95iWv9FFEhUFOloMy/0OZPB2ybYmd2KVCy3SeIE4Zfeiw8pDXdCUniOQ==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-7.6.6.tgz", + "integrity": "sha512-VAXXfPLi1M3RXhBf3uIBZ2hrD9UPDe7yvXHIlCzgj1HIJELODCFyUc+RtvN0mPc/nnlEfzhGfJtenZou5LYwIw==", "dev": true, "dependencies": { - "@storybook/blocks": "7.6.5", + "@storybook/blocks": "7.6.6", "lodash": "^4.17.21", "ts-dedent": "^2.0.0" }, @@ -4904,26 +4904,26 @@ } }, "node_modules/@storybook/addon-docs": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-7.6.5.tgz", - "integrity": "sha512-D9tZyD41IujCHiPYdfS2bKtZRJPNwO4EydzyqODXppomluhFbY3uTEaf0H1UFnJLQxWNXZ7rr3aS0V3O6yu8pA==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-7.6.6.tgz", + "integrity": "sha512-l4gtoNTn1wHE11x44te1cDkqfm+/w+eNonHe56bwgSqETclS5z18wvM9bQZF32G6C9fpSefaJW3cxVvcuJL1fg==", "dev": true, "dependencies": { "@jest/transform": "^29.3.1", "@mdx-js/react": "^2.1.5", - "@storybook/blocks": "7.6.5", - "@storybook/client-logger": "7.6.5", - "@storybook/components": "7.6.5", - "@storybook/csf-plugin": "7.6.5", - "@storybook/csf-tools": "7.6.5", + "@storybook/blocks": "7.6.6", + "@storybook/client-logger": "7.6.6", + "@storybook/components": "7.6.6", + "@storybook/csf-plugin": "7.6.6", + "@storybook/csf-tools": "7.6.6", "@storybook/global": "^5.0.0", "@storybook/mdx2-csf": "^1.0.0", - "@storybook/node-logger": "7.6.5", - "@storybook/postinstall": "7.6.5", - "@storybook/preview-api": "7.6.5", - "@storybook/react-dom-shim": "7.6.5", - "@storybook/theming": "7.6.5", - "@storybook/types": "7.6.5", + "@storybook/node-logger": "7.6.6", + "@storybook/postinstall": "7.6.6", + "@storybook/preview-api": "7.6.6", + "@storybook/react-dom-shim": "7.6.6", + "@storybook/theming": "7.6.6", + "@storybook/types": "7.6.6", "fs-extra": "^11.1.0", "remark-external-links": "^8.0.0", "remark-slug": "^6.0.0", @@ -4939,17 +4939,17 @@ } }, "node_modules/@storybook/addon-docs/node_modules/@storybook/preview-api": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.6.5.tgz", - "integrity": "sha512-9XzuDXXgNuA6dDZ3DXsUwEG6ElxeTbzLuYuzcjtS1FusSICZ2iYmxfS0GfSud9MjPPYOJYoSOvMdIHjorjgByA==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.6.6.tgz", + "integrity": "sha512-Bt6xIAR5yZ/JWc90X4BbLOA97iL65glZ1SOBgFFv2mHrdZ1lcdKhAlQr2aeJAf1mLvBtalPjvKzi9EuVY3FZ4w==", "dev": true, "dependencies": { - "@storybook/channels": "7.6.5", - "@storybook/client-logger": "7.6.5", - "@storybook/core-events": "7.6.5", + "@storybook/channels": "7.6.6", + "@storybook/client-logger": "7.6.6", + "@storybook/core-events": "7.6.6", "@storybook/csf": "^0.1.2", "@storybook/global": "^5.0.0", - "@storybook/types": "7.6.5", + "@storybook/types": "7.6.6", "@types/qs": "^6.9.5", "dequal": "^2.0.2", "lodash": "^4.17.21", @@ -4979,24 +4979,24 @@ } }, "node_modules/@storybook/addon-essentials": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-7.6.5.tgz", - "integrity": "sha512-VCLj1JAEpGoqF5iFJOo1CZFFck/tg4m/98DLdQuNuXvxT6jqaF0NI9UUQuJLIGteDCR7NKRbTFc1hV3/Ev+Ziw==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-7.6.6.tgz", + "integrity": "sha512-OQ8A6r06mg/HvyIk/j2Gt9DK5Qtqgtwq2Ydm5IgVW6gZsuRnv1FAeUG6okf8oXowAzpYoHdsDmCVwNOAGWGO7w==", "dev": true, "dependencies": { - "@storybook/addon-actions": "7.6.5", - "@storybook/addon-backgrounds": "7.6.5", - "@storybook/addon-controls": "7.6.5", - "@storybook/addon-docs": "7.6.5", - "@storybook/addon-highlight": "7.6.5", - "@storybook/addon-measure": "7.6.5", - "@storybook/addon-outline": "7.6.5", - "@storybook/addon-toolbars": "7.6.5", - "@storybook/addon-viewport": "7.6.5", - "@storybook/core-common": "7.6.5", - "@storybook/manager-api": "7.6.5", - "@storybook/node-logger": "7.6.5", - "@storybook/preview-api": "7.6.5", + "@storybook/addon-actions": "7.6.6", + "@storybook/addon-backgrounds": "7.6.6", + "@storybook/addon-controls": "7.6.6", + "@storybook/addon-docs": "7.6.6", + "@storybook/addon-highlight": "7.6.6", + "@storybook/addon-measure": "7.6.6", + "@storybook/addon-outline": "7.6.6", + "@storybook/addon-toolbars": "7.6.6", + "@storybook/addon-viewport": "7.6.6", + "@storybook/core-common": "7.6.6", + "@storybook/manager-api": "7.6.6", + "@storybook/node-logger": "7.6.6", + "@storybook/preview-api": "7.6.6", "ts-dedent": "^2.0.0" }, "funding": { @@ -5009,17 +5009,17 @@ } }, "node_modules/@storybook/addon-essentials/node_modules/@storybook/preview-api": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.6.5.tgz", - "integrity": "sha512-9XzuDXXgNuA6dDZ3DXsUwEG6ElxeTbzLuYuzcjtS1FusSICZ2iYmxfS0GfSud9MjPPYOJYoSOvMdIHjorjgByA==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.6.6.tgz", + "integrity": "sha512-Bt6xIAR5yZ/JWc90X4BbLOA97iL65glZ1SOBgFFv2mHrdZ1lcdKhAlQr2aeJAf1mLvBtalPjvKzi9EuVY3FZ4w==", "dev": true, "dependencies": { - "@storybook/channels": "7.6.5", - "@storybook/client-logger": "7.6.5", - "@storybook/core-events": "7.6.5", + "@storybook/channels": "7.6.6", + "@storybook/client-logger": "7.6.6", + "@storybook/core-events": "7.6.6", "@storybook/csf": "^0.1.2", "@storybook/global": "^5.0.0", - "@storybook/types": "7.6.5", + "@storybook/types": "7.6.6", "@types/qs": "^6.9.5", "dequal": "^2.0.2", "lodash": "^4.17.21", @@ -5035,9 +5035,9 @@ } }, "node_modules/@storybook/addon-highlight": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-7.6.5.tgz", - "integrity": "sha512-CxzmIb30F9nLPQwT0lCPYhOAwGlGF4IkgkO8hYA7VfGCGUkJZEyyN/YkP/ZCUSdCIRChDBouR3KiFFd4mDFKzg==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-7.6.6.tgz", + "integrity": "sha512-B85UaCts2uMpa0yHBSnupzy2WCdW4vfB+lfaBug9beyOyZQdel07BumblE0KwSJftYgdCNPUZ5MRlqEDzMLTWQ==", "dev": true, "dependencies": { "@storybook/global": "^5.0.0" @@ -5048,9 +5048,9 @@ } }, "node_modules/@storybook/addon-links": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-7.6.5.tgz", - "integrity": "sha512-Lx4Ng+iXt0YpIrKGr+nOZlpN9ypOoEDoP/7bZ6m7GXuVAkDm3JrRCBp7e2ZKSKcTxPdjPuO9HVKkIjtqjINlpw==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-7.6.6.tgz", + "integrity": "sha512-NEcqOz6zZ1dJnCcVmYdaQTAMAGIb8NFAZGnr9DU0q+t4B1fTaWUgqLtBM5V6YqIrXGSC/oKLpjWUkS5UpswlHA==", "dev": true, "dependencies": { "@storybook/csf": "^0.1.2", @@ -5071,9 +5071,9 @@ } }, "node_modules/@storybook/addon-measure": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-7.6.5.tgz", - "integrity": "sha512-tlUudVQSrA+bwI4dhO8J7nYHtYdylcBZ86ybnqMmdTthsnyc7jnaFVQwbb6bbQJpPxvEvoNds5bVGUFocuvymQ==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-7.6.6.tgz", + "integrity": "sha512-b4hyCudlhsbYN1We8pfZHZJ0i0sfC8+GJvrqZQqdSqGicUmA00mggY1GE+gEoHziQ5/4auxFRS3HfUgaQWUNjg==", "dev": true, "dependencies": { "@storybook/global": "^5.0.0", @@ -5085,9 +5085,9 @@ } }, "node_modules/@storybook/addon-outline": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-7.6.5.tgz", - "integrity": "sha512-P7X4+Z9L/l/RZW9UvvM+iuK2SUHD22KPc+dbYOifRXDovUqhfmcKVh1CUqTDMyZrg2ZAbropehMz1eI9BlQfxg==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-7.6.6.tgz", + "integrity": "sha512-BMjpjzNEnN8LC7JK92WCXyWgmJwAaEQjRDinr7eD4cBt4Uas5kbciw1g8PtTnh0GbYUsImKao0nzakSVObAdzg==", "dev": true, "dependencies": { "@storybook/global": "^5.0.0", @@ -5099,9 +5099,9 @@ } }, "node_modules/@storybook/addon-toolbars": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-7.6.5.tgz", - "integrity": "sha512-/zqWbVNE/SHc8I5Prnd2Q8U57RGEIYvHfeXjfkuLcE2Quc4Iss4x/9eU7SKu4jm+IOO2s0wlN6HcqI3XEf2XxA==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-7.6.6.tgz", + "integrity": "sha512-sQm5+FcoSMSGn1ioXHoukO6OhUlcNZil0/fonAY50uvp6Z4DyI0FTU7BKIm/NoMqAExQk3sZRfAC/nZZ9Epb0Q==", "dev": true, "funding": { "type": "opencollective", @@ -5109,9 +5109,9 @@ } }, "node_modules/@storybook/addon-viewport": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-7.6.5.tgz", - "integrity": "sha512-9ghKTaduIUvQ6oShmWLuwMeTjtMR4RgKeKHrTJ7THMqvE/ydDPCYeL7ugF65ocXZSEz/QmxdK7uL686ZMKsqNA==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-7.6.6.tgz", + "integrity": "sha512-/ijbzDf1Iq30LvZW2NE8cO4TeHusw0N+jTDUK1+vhxGNMFo9DUIgRkAi6VpFEfS0aQ5d82523WSWzVso7b/Hmg==", "dev": true, "dependencies": { "memoizerific": "^1.11.3" @@ -5304,13 +5304,13 @@ "dev": true }, "node_modules/@storybook/api": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/api/-/api-7.6.5.tgz", - "integrity": "sha512-o1RH47iIgG4ie4hjJP1HgsCiuTKlGW0egaAy6E6Np3bDmujy5udWEf8vnXbcaBerc5ZSrQs45kfSWugHy2a4FA==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/api/-/api-7.6.6.tgz", + "integrity": "sha512-e3k45k7twP3z5ZJ+rCCaHI+jmYm5yoFo2eLjYmnYFUv2V3vvYPgqD2CiT0crne7uWmhpRxP49aU9DEvQaEZtdA==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.6.5", - "@storybook/manager-api": "7.6.5" + "@storybook/client-logger": "7.6.6", + "@storybook/manager-api": "7.6.6" }, "funding": { "type": "opencollective", @@ -5318,22 +5318,22 @@ } }, "node_modules/@storybook/blocks": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-7.6.5.tgz", - "integrity": "sha512-/NjuYkPks5w9lKn47KLgVC5cBkwfc+ERAp0CY0Xe//BQJkP+bcI8lE8d9Qc9IXFbOTvYEULeQrFgCkesk5BmLg==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-7.6.6.tgz", + "integrity": "sha512-QLqkiSNrtGnh8RK9ipD63jVAUenkRu+72xR31DViZWRV9V8G2hzky5E/RoZWPEx+DfmBIUJ7Tcef6cCRcxEj9A==", "dev": true, "dependencies": { - "@storybook/channels": "7.6.5", - "@storybook/client-logger": "7.6.5", - "@storybook/components": "7.6.5", - "@storybook/core-events": "7.6.5", + "@storybook/channels": "7.6.6", + "@storybook/client-logger": "7.6.6", + "@storybook/components": "7.6.6", + "@storybook/core-events": "7.6.6", "@storybook/csf": "^0.1.2", - "@storybook/docs-tools": "7.6.5", + "@storybook/docs-tools": "7.6.6", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "7.6.5", - "@storybook/preview-api": "7.6.5", - "@storybook/theming": "7.6.5", - "@storybook/types": "7.6.5", + "@storybook/manager-api": "7.6.6", + "@storybook/preview-api": "7.6.6", + "@storybook/theming": "7.6.6", + "@storybook/types": "7.6.6", "@types/lodash": "^4.14.167", "color-convert": "^2.0.1", "dequal": "^2.0.2", @@ -5357,17 +5357,17 @@ } }, "node_modules/@storybook/blocks/node_modules/@storybook/preview-api": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.6.5.tgz", - "integrity": "sha512-9XzuDXXgNuA6dDZ3DXsUwEG6ElxeTbzLuYuzcjtS1FusSICZ2iYmxfS0GfSud9MjPPYOJYoSOvMdIHjorjgByA==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.6.6.tgz", + "integrity": "sha512-Bt6xIAR5yZ/JWc90X4BbLOA97iL65glZ1SOBgFFv2mHrdZ1lcdKhAlQr2aeJAf1mLvBtalPjvKzi9EuVY3FZ4w==", "dev": true, "dependencies": { - "@storybook/channels": "7.6.5", - "@storybook/client-logger": "7.6.5", - "@storybook/core-events": "7.6.5", + "@storybook/channels": "7.6.6", + "@storybook/client-logger": "7.6.6", + "@storybook/core-events": "7.6.6", "@storybook/csf": "^0.1.2", "@storybook/global": "^5.0.0", - "@storybook/types": "7.6.5", + "@storybook/types": "7.6.6", "@types/qs": "^6.9.5", "dequal": "^2.0.2", "lodash": "^4.17.21", @@ -5383,15 +5383,15 @@ } }, "node_modules/@storybook/builder-manager": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/builder-manager/-/builder-manager-7.6.5.tgz", - "integrity": "sha512-FQyI+tfzMam2XKXq7k921YVafIJs9Vqvos5qx8vyRnRffo55UU8tgunwjGn0PswtbMm6sThVqE0C0ZzVr7RG8A==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/builder-manager/-/builder-manager-7.6.6.tgz", + "integrity": "sha512-96vmtUqh016H2n80xhvBZU2w5flTOzY7S0nW9nfxbY4UY4b39WajgwJ5wpg8l0YmCwQTrxCwY9/VE2Pd6CCqPA==", "dev": true, "dependencies": { "@fal-works/esbuild-plugin-global-externals": "^2.1.2", - "@storybook/core-common": "7.6.5", - "@storybook/manager": "7.6.5", - "@storybook/node-logger": "7.6.5", + "@storybook/core-common": "7.6.6", + "@storybook/manager": "7.6.6", + "@storybook/node-logger": "7.6.6", "@types/ejs": "^3.1.1", "@types/find-cache-dir": "^3.2.1", "@yarnpkg/esbuild-plugin-pnp": "^3.0.0-rc.10", @@ -5425,19 +5425,19 @@ } }, "node_modules/@storybook/builder-vite": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-7.6.5.tgz", - "integrity": "sha512-VbAYTGr92lgCWTwO2Z7NgSW3f5/K4Vr0Qxa2IlTgMCymWdDbWdIQiREcmCP0vjAGM2ftq1+vxngohVgx/r7pUw==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-7.6.6.tgz", + "integrity": "sha512-vDBHjsswnVScVgGHeIZ22R/LoRt5T1F62p5czusydBSxKGzma5Va4JHQJp4/IKXwiCZbXcua/Cs7VKtBLO+50A==", "dev": true, "dependencies": { - "@storybook/channels": "7.6.5", - "@storybook/client-logger": "7.6.5", - "@storybook/core-common": "7.6.5", - "@storybook/csf-plugin": "7.6.5", - "@storybook/node-logger": "7.6.5", - "@storybook/preview": "7.6.5", - "@storybook/preview-api": "7.6.5", - "@storybook/types": "7.6.5", + "@storybook/channels": "7.6.6", + "@storybook/client-logger": "7.6.6", + "@storybook/core-common": "7.6.6", + "@storybook/csf-plugin": "7.6.6", + "@storybook/node-logger": "7.6.6", + "@storybook/preview": "7.6.6", + "@storybook/preview-api": "7.6.6", + "@storybook/types": "7.6.6", "@types/find-cache-dir": "^3.2.1", "browser-assert": "^1.2.1", "es-module-lexer": "^0.9.3", @@ -5470,17 +5470,17 @@ } }, "node_modules/@storybook/builder-vite/node_modules/@storybook/preview-api": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.6.5.tgz", - "integrity": "sha512-9XzuDXXgNuA6dDZ3DXsUwEG6ElxeTbzLuYuzcjtS1FusSICZ2iYmxfS0GfSud9MjPPYOJYoSOvMdIHjorjgByA==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.6.6.tgz", + "integrity": "sha512-Bt6xIAR5yZ/JWc90X4BbLOA97iL65glZ1SOBgFFv2mHrdZ1lcdKhAlQr2aeJAf1mLvBtalPjvKzi9EuVY3FZ4w==", "dev": true, "dependencies": { - "@storybook/channels": "7.6.5", - "@storybook/client-logger": "7.6.5", - "@storybook/core-events": "7.6.5", + "@storybook/channels": "7.6.6", + "@storybook/client-logger": "7.6.6", + "@storybook/core-events": "7.6.6", "@storybook/csf": "^0.1.2", "@storybook/global": "^5.0.0", - "@storybook/types": "7.6.5", + "@storybook/types": "7.6.6", "@types/qs": "^6.9.5", "dequal": "^2.0.2", "lodash": "^4.17.21", @@ -5526,13 +5526,13 @@ } }, "node_modules/@storybook/channels": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.5.tgz", - "integrity": "sha512-FIlNkyfQy9uHoJfAFL2/wO3ASGJELFvBzURBE2rcEF/TS7GcUiqWnBfiDxAbwSEjSOm2F0eEq3UXhaZEjpJHDw==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.6.tgz", + "integrity": "sha512-vvo7fBe2WffPonNNOA7Xx7jcHAto8qJYlq+VMysfheXrsRRbhHl3WQOA18Vm8hV9txtqdqk0hwQiXOWvhYVpeQ==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.6.5", - "@storybook/core-events": "7.6.5", + "@storybook/client-logger": "7.6.6", + "@storybook/core-events": "7.6.6", "@storybook/global": "^5.0.0", "qs": "^6.10.0", "telejson": "^7.2.0", @@ -5544,23 +5544,23 @@ } }, "node_modules/@storybook/cli": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/cli/-/cli-7.6.5.tgz", - "integrity": "sha512-w+Y8dx5oCLQVESOVmpsQuFksr/ewARKrnSKl9kwnVMN4sMgjOgoZ3zmV66J7SKexvwyuwlOjf840pmEglGdPPg==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/cli/-/cli-7.6.6.tgz", + "integrity": "sha512-FLmWrbmGOqe1VYwqyIWxU2lJcYPssORmSbSVVPw6OqQIXx3NrNBrmZDLncMwbVCDQ8eU54J1zb+MyDmSqMbVFg==", "dev": true, "dependencies": { "@babel/core": "^7.23.2", "@babel/preset-env": "^7.23.2", "@babel/types": "^7.23.0", "@ndelangen/get-tarball": "^3.0.7", - "@storybook/codemod": "7.6.5", - "@storybook/core-common": "7.6.5", - "@storybook/core-events": "7.6.5", - "@storybook/core-server": "7.6.5", - "@storybook/csf-tools": "7.6.5", - "@storybook/node-logger": "7.6.5", - "@storybook/telemetry": "7.6.5", - "@storybook/types": "7.6.5", + "@storybook/codemod": "7.6.6", + "@storybook/core-common": "7.6.6", + "@storybook/core-events": "7.6.6", + "@storybook/core-server": "7.6.6", + "@storybook/csf-tools": "7.6.6", + "@storybook/node-logger": "7.6.6", + "@storybook/telemetry": "7.6.6", + "@storybook/types": "7.6.6", "@types/semver": "^7.3.4", "@yarnpkg/fslib": "2.10.3", "@yarnpkg/libzip": "2.3.0", @@ -5724,9 +5724,9 @@ "dev": true }, "node_modules/@storybook/client-logger": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.5.tgz", - "integrity": "sha512-S5aROWgssqg7tcs9lgW5wmCAz4SxMAtioiyVj5oFecmPCbQtFVIAREYzeoxE4GfJL+plrfRkum4BzziANn8EhQ==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.6.tgz", + "integrity": "sha512-WEvVyuQR5oNF8jcMmGA13zDjxP/l46kOBBvB6JSc8toUdtLZ/kZWSnU0ioNM8+ECpFqXHjBcF2K6uSJOEb6YEg==", "dev": true, "dependencies": { "@storybook/global": "^5.0.0" @@ -5737,18 +5737,18 @@ } }, "node_modules/@storybook/codemod": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/codemod/-/codemod-7.6.5.tgz", - "integrity": "sha512-K5C9ltBClZ0aSyujGt3RJFtRicrUZy8nzhHrcADUj27rrQD26jH/p+Y05jWKj9JcI8SyMg978GN5X/1aw2Y31A==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/codemod/-/codemod-7.6.6.tgz", + "integrity": "sha512-6QwW6T6ZgwwbTkEoZ7CAoX7lUUob7Sy7bRkMHhSjJe2++wEVFOYLvzHcLUJCupK59+WhmsJU9PpUMlXEKi40TQ==", "dev": true, "dependencies": { "@babel/core": "^7.23.2", "@babel/preset-env": "^7.23.2", "@babel/types": "^7.23.0", "@storybook/csf": "^0.1.2", - "@storybook/csf-tools": "7.6.5", - "@storybook/node-logger": "7.6.5", - "@storybook/types": "7.6.5", + "@storybook/csf-tools": "7.6.6", + "@storybook/node-logger": "7.6.6", + "@storybook/types": "7.6.6", "@types/cross-spawn": "^6.0.2", "cross-spawn": "^7.0.3", "globby": "^11.0.2", @@ -5778,18 +5778,18 @@ } }, "node_modules/@storybook/components": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/components/-/components-7.6.5.tgz", - "integrity": "sha512-w4ZucbBBZ+NKMWlJKVj2I/bMBBq7gzDp9lzc4+8QaQ3vUPXKqc1ilIPYo/7UR5oxwDVMZocmMSgl9L8lvf7+Mw==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-7.6.6.tgz", + "integrity": "sha512-FSfcRxdmV4+LJHjMk0eodGVnZdb2qrKKmbtsn0O/434z586zPA287/wJJsm4JS/Xr1WS9oTvU6mYMDChkcxgeQ==", "dev": true, "dependencies": { "@radix-ui/react-select": "^1.2.2", "@radix-ui/react-toolbar": "^1.0.4", - "@storybook/client-logger": "7.6.5", + "@storybook/client-logger": "7.6.6", "@storybook/csf": "^0.1.2", "@storybook/global": "^5.0.0", - "@storybook/theming": "7.6.5", - "@storybook/types": "7.6.5", + "@storybook/theming": "7.6.6", + "@storybook/types": "7.6.6", "memoizerific": "^1.11.3", "use-resize-observer": "^9.1.0", "util-deprecate": "^1.0.2" @@ -5804,13 +5804,13 @@ } }, "node_modules/@storybook/core-client": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/core-client/-/core-client-7.6.5.tgz", - "integrity": "sha512-6FtyJcz8MSl+JYwNJZ53FM6rkT27pFHWcJPdtw/9229Ec8as9RpkNeZ/NBZjRTeDkn9Ki0VOiVAefNie9tZ/8Q==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/core-client/-/core-client-7.6.6.tgz", + "integrity": "sha512-P100aNf+WpvzlfULZp1NPd60/nxsppLmft2DdIyAx1j4QPMZvUJyJB+hdBMzTFiPEhIUncIMoIVf2R3UXC5DfA==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.6.5", - "@storybook/preview-api": "7.6.5" + "@storybook/client-logger": "7.6.6", + "@storybook/preview-api": "7.6.6" }, "funding": { "type": "opencollective", @@ -5818,17 +5818,17 @@ } }, "node_modules/@storybook/core-client/node_modules/@storybook/preview-api": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.6.5.tgz", - "integrity": "sha512-9XzuDXXgNuA6dDZ3DXsUwEG6ElxeTbzLuYuzcjtS1FusSICZ2iYmxfS0GfSud9MjPPYOJYoSOvMdIHjorjgByA==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.6.6.tgz", + "integrity": "sha512-Bt6xIAR5yZ/JWc90X4BbLOA97iL65glZ1SOBgFFv2mHrdZ1lcdKhAlQr2aeJAf1mLvBtalPjvKzi9EuVY3FZ4w==", "dev": true, "dependencies": { - "@storybook/channels": "7.6.5", - "@storybook/client-logger": "7.6.5", - "@storybook/core-events": "7.6.5", + "@storybook/channels": "7.6.6", + "@storybook/client-logger": "7.6.6", + "@storybook/core-events": "7.6.6", "@storybook/csf": "^0.1.2", "@storybook/global": "^5.0.0", - "@storybook/types": "7.6.5", + "@storybook/types": "7.6.6", "@types/qs": "^6.9.5", "dequal": "^2.0.2", "lodash": "^4.17.21", @@ -5844,14 +5844,14 @@ } }, "node_modules/@storybook/core-common": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-7.6.5.tgz", - "integrity": "sha512-z4EgzZSIVbID6Ib0jhh3jimKeaDWU8OOhoZYfn3galFmgQWowWOv1oMgipWiXfRLWw9DaLFQiCHIdLANH+VO2g==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-7.6.6.tgz", + "integrity": "sha512-DpbFSYw8LHuwpeU2ec5uWryxrSqslFJnWTfNA7AvpzCviWXkz4kq+YYrDee9XExF6OozNwILmG6m52SnraysBA==", "dev": true, "dependencies": { - "@storybook/core-events": "7.6.5", - "@storybook/node-logger": "7.6.5", - "@storybook/types": "7.6.5", + "@storybook/core-events": "7.6.6", + "@storybook/node-logger": "7.6.6", + "@storybook/types": "7.6.6", "@types/find-cache-dir": "^3.2.1", "@types/node": "^18.0.0", "@types/node-fetch": "^2.6.4", @@ -6000,9 +6000,9 @@ } }, "node_modules/@storybook/core-events": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.5.tgz", - "integrity": "sha512-zk2q/qicYXAzHA4oV3GDbIql+Kd4TOHUgDE8e4jPCOPp856z2ScqEKUAbiJizs6eEJOH4nW9Db1kuzgrBVEykQ==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.6.tgz", + "integrity": "sha512-7+q9HiZiLxaQcwpaSLQrLdjHNHBoOoUY9ZcZXI9iNFSopOgb/ItDnzzlpv08NC7CbKae1hVKJM/t5aSTl7tCMw==", "dev": true, "dependencies": { "ts-dedent": "^2.0.0" @@ -6013,26 +6013,26 @@ } }, "node_modules/@storybook/core-server": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/core-server/-/core-server-7.6.5.tgz", - "integrity": "sha512-BfKzK/ObTjUcPvE5/r1pogCifM/4nLRhOUYJl7XekwHkOQwn19e6H3/ku1W3jDoYXBu642Dc9X7l/ERjKTqxFg==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/core-server/-/core-server-7.6.6.tgz", + "integrity": "sha512-QFVahaExgGtq9swBXgQAMUiCqpCcyVXOiKTIy1j+1uAhPVqhpCxBkkFoXruih5hbIMZyohE4mLPCAr/ivicoDg==", "dev": true, "dependencies": { "@aw-web-design/x-default-browser": "1.4.126", "@discoveryjs/json-ext": "^0.5.3", - "@storybook/builder-manager": "7.6.5", - "@storybook/channels": "7.6.5", - "@storybook/core-common": "7.6.5", - "@storybook/core-events": "7.6.5", + "@storybook/builder-manager": "7.6.6", + "@storybook/channels": "7.6.6", + "@storybook/core-common": "7.6.6", + "@storybook/core-events": "7.6.6", "@storybook/csf": "^0.1.2", - "@storybook/csf-tools": "7.6.5", + "@storybook/csf-tools": "7.6.6", "@storybook/docs-mdx": "^0.1.0", "@storybook/global": "^5.0.0", - "@storybook/manager": "7.6.5", - "@storybook/node-logger": "7.6.5", - "@storybook/preview-api": "7.6.5", - "@storybook/telemetry": "7.6.5", - "@storybook/types": "7.6.5", + "@storybook/manager": "7.6.6", + "@storybook/node-logger": "7.6.6", + "@storybook/preview-api": "7.6.6", + "@storybook/telemetry": "7.6.6", + "@storybook/types": "7.6.6", "@types/detect-port": "^1.3.0", "@types/node": "^18.0.0", "@types/pretty-hrtime": "^1.0.0", @@ -6066,17 +6066,17 @@ } }, "node_modules/@storybook/core-server/node_modules/@storybook/preview-api": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.6.5.tgz", - "integrity": "sha512-9XzuDXXgNuA6dDZ3DXsUwEG6ElxeTbzLuYuzcjtS1FusSICZ2iYmxfS0GfSud9MjPPYOJYoSOvMdIHjorjgByA==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.6.6.tgz", + "integrity": "sha512-Bt6xIAR5yZ/JWc90X4BbLOA97iL65glZ1SOBgFFv2mHrdZ1lcdKhAlQr2aeJAf1mLvBtalPjvKzi9EuVY3FZ4w==", "dev": true, "dependencies": { - "@storybook/channels": "7.6.5", - "@storybook/client-logger": "7.6.5", - "@storybook/core-events": "7.6.5", + "@storybook/channels": "7.6.6", + "@storybook/client-logger": "7.6.6", + "@storybook/core-events": "7.6.6", "@storybook/csf": "^0.1.2", "@storybook/global": "^5.0.0", - "@storybook/types": "7.6.5", + "@storybook/types": "7.6.6", "@types/qs": "^6.9.5", "dequal": "^2.0.2", "lodash": "^4.17.21", @@ -6209,12 +6209,12 @@ } }, "node_modules/@storybook/csf-plugin": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-7.6.5.tgz", - "integrity": "sha512-iQ8Y/Qq1IUhHRddjDVicWJA2sM7OZA1FR97OvWUT2240WjCuQSCfy32JD8TQlYjqXgEolJeLPv3zW4qH5om4LQ==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-7.6.6.tgz", + "integrity": "sha512-SqdffT14+XNpf+7vA29Elur28VArXtFv4cXMlsCbswbRuY+a0A8vYNwVIfCUy9u4WHTcQX1/tUkDAMh80lrVRQ==", "dev": true, "dependencies": { - "@storybook/csf-tools": "7.6.5", + "@storybook/csf-tools": "7.6.6", "unplugin": "^1.3.1" }, "funding": { @@ -6223,9 +6223,9 @@ } }, "node_modules/@storybook/csf-tools": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/csf-tools/-/csf-tools-7.6.5.tgz", - "integrity": "sha512-1iaCh7nt+WE7Q5UwRhLLc5flMNoAV/vBr0tvDSCKiHaO+D3dZzlZOe/U+S6wegdyN2QNcvT2xs179CcrX6Qp6w==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/csf-tools/-/csf-tools-7.6.6.tgz", + "integrity": "sha512-VXOZCzfSVJL832u17pPhFu1x3PPaAN9d8VXNFX+t/2raga7tK3T7Qhe7lWfP7EZcrVvSCEEp0aMRz2EzzDGVtw==", "dev": true, "dependencies": { "@babel/generator": "^7.23.0", @@ -6233,7 +6233,7 @@ "@babel/traverse": "^7.23.2", "@babel/types": "^7.23.0", "@storybook/csf": "^0.1.2", - "@storybook/types": "7.6.5", + "@storybook/types": "7.6.6", "fs-extra": "^11.1.0", "recast": "^0.23.1", "ts-dedent": "^2.0.0" @@ -6264,14 +6264,14 @@ "dev": true }, "node_modules/@storybook/docs-tools": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/docs-tools/-/docs-tools-7.6.5.tgz", - "integrity": "sha512-UyHkHu5Af6jMpYsR4lZ69D32GQGeA0pLAn7jaBbQndgAjBdK1ykZcifiUC7Wz1hG7+YpuYspEGuDEddOh+X8FQ==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/docs-tools/-/docs-tools-7.6.6.tgz", + "integrity": "sha512-nc5ZjN2s8SC2PtsZoFf9Wm6gD8TcSlkYbF/mjtyLCGN+Fi+k5B5iudqoa65H19hwiLlzBdcnpQ8C89AiK33J9Q==", "dev": true, "dependencies": { - "@storybook/core-common": "7.6.5", - "@storybook/preview-api": "7.6.5", - "@storybook/types": "7.6.5", + "@storybook/core-common": "7.6.6", + "@storybook/preview-api": "7.6.6", + "@storybook/types": "7.6.6", "@types/doctrine": "^0.0.3", "assert": "^2.1.0", "doctrine": "^3.0.0", @@ -6283,17 +6283,17 @@ } }, "node_modules/@storybook/docs-tools/node_modules/@storybook/preview-api": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.6.5.tgz", - "integrity": "sha512-9XzuDXXgNuA6dDZ3DXsUwEG6ElxeTbzLuYuzcjtS1FusSICZ2iYmxfS0GfSud9MjPPYOJYoSOvMdIHjorjgByA==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.6.6.tgz", + "integrity": "sha512-Bt6xIAR5yZ/JWc90X4BbLOA97iL65glZ1SOBgFFv2mHrdZ1lcdKhAlQr2aeJAf1mLvBtalPjvKzi9EuVY3FZ4w==", "dev": true, "dependencies": { - "@storybook/channels": "7.6.5", - "@storybook/client-logger": "7.6.5", - "@storybook/core-events": "7.6.5", + "@storybook/channels": "7.6.6", + "@storybook/client-logger": "7.6.6", + "@storybook/core-events": "7.6.6", "@storybook/csf": "^0.1.2", "@storybook/global": "^5.0.0", - "@storybook/types": "7.6.5", + "@storybook/types": "7.6.6", "@types/qs": "^6.9.5", "dequal": "^2.0.2", "lodash": "^4.17.21", @@ -6315,9 +6315,9 @@ "dev": true }, "node_modules/@storybook/manager": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/manager/-/manager-7.6.5.tgz", - "integrity": "sha512-y1KLH0O1PGPyMxGMvOhppzFSO7r4ibjTve5iqsI0JZwxUjNuBKRLYbrhXdAyC2iacvxYNrHgevae1k9XdD+FQw==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/manager/-/manager-7.6.6.tgz", + "integrity": "sha512-Ga3LcSu/xxSyg+cLlO9AS8QjW+D667V+c9qQPmsFyU6qfFc6m6mVqcRLSmFVD5e7P/o0FL7STOf9jAKkDcW8xw==", "dev": true, "funding": { "type": "opencollective", @@ -6325,19 +6325,19 @@ } }, "node_modules/@storybook/manager-api": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-7.6.5.tgz", - "integrity": "sha512-tE3OShOcs6A3XtI3NJd6hYQOZLaP++Fn0dCtowBwYh/vS1EN/AyroVmL97tsxn1DZTyoRt0GidwbB6dvLMBOwA==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-7.6.6.tgz", + "integrity": "sha512-euRAbSZAUzHDt6z1Pq/g45N/RNqta9RaQAym18zt/oLWiYOIrkLmdf7kCuFYsmuA5XQBytiJqwkAD7uF1aLe0g==", "dev": true, "dependencies": { - "@storybook/channels": "7.6.5", - "@storybook/client-logger": "7.6.5", - "@storybook/core-events": "7.6.5", + "@storybook/channels": "7.6.6", + "@storybook/client-logger": "7.6.6", + "@storybook/core-events": "7.6.6", "@storybook/csf": "^0.1.2", "@storybook/global": "^5.0.0", - "@storybook/router": "7.6.5", - "@storybook/theming": "7.6.5", - "@storybook/types": "7.6.5", + "@storybook/router": "7.6.6", + "@storybook/theming": "7.6.6", + "@storybook/types": "7.6.6", "dequal": "^2.0.2", "lodash": "^4.17.21", "memoizerific": "^1.11.3", @@ -6391,9 +6391,9 @@ "dev": true }, "node_modules/@storybook/node-logger": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-7.6.5.tgz", - "integrity": "sha512-xKw6IH1wLkIssekdBv3bd13xYKUF1t8EwqDR8BYcN8AVjZlqJMTifssqG4bYV+G/B7J3tz4ugJ5nmtWg6RQ0Qw==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-7.6.6.tgz", + "integrity": "sha512-b2OF9GRNI01MlBlnDGS8S6/yOpBNl8eH/0ONafuMPzFEZs5PouHGsFflJvQwwcdVTknMjF5uVS2eSmnLZ8spvA==", "dev": true, "funding": { "type": "opencollective", @@ -6401,9 +6401,9 @@ } }, "node_modules/@storybook/postinstall": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/postinstall/-/postinstall-7.6.5.tgz", - "integrity": "sha512-12WxfpqGKsk7GQ3KWiZSbamsYK8vtRmhOTkavZ9IQkcJ/zuVfmqK80/Mds+njJMudUPzuREuSFGWACczo17EDA==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/postinstall/-/postinstall-7.6.6.tgz", + "integrity": "sha512-jamn7QNTJPZiu22nu25LqfSTJohugFhCu4b48yqP+pdMdkQ3qVd3NdDYhBlgkH/Btar+kppiJP1gRtoiJF761w==", "dev": true, "funding": { "type": "opencollective", @@ -6411,9 +6411,9 @@ } }, "node_modules/@storybook/preview": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/preview/-/preview-7.6.5.tgz", - "integrity": "sha512-zmLa7C7yFGTYhgGZXoecdww9rx0Z5HpNi/GDBRWoNSK+FEdE8Jj2jF5NJ2ncldtYIyegz9ku29JFMKbhMj9K5Q==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/preview/-/preview-7.6.6.tgz", + "integrity": "sha512-Rl+Pom2bNwzc0MdlbFALmvxsbCkbIwlpTZlRZZTh5Ah8JViV9htQgP9e8uwo3NZA2BhjbDLkmnZeloWxQnI5Ig==", "dev": true, "funding": { "type": "opencollective", @@ -6507,9 +6507,9 @@ } }, "node_modules/@storybook/react-dom-shim": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-7.6.5.tgz", - "integrity": "sha512-Qp3N3zENdvx20ikHmz5yI03z+mAWF8bUAwUofqXarVtZUkBNtvfTfUwgAezOAF0eClClH+ktIziIKd976tLSPw==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-7.6.6.tgz", + "integrity": "sha512-WWNlXtCVoBWXX/kLNulUeMgzmlAEHi2aBrdIv2jz0DScPf0YxeWAkWmgK7F0zMot9mdwYncr+pk1AILbTBJSyg==", "dev": true, "funding": { "type": "opencollective", @@ -6521,12 +6521,12 @@ } }, "node_modules/@storybook/router": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/router/-/router-7.6.5.tgz", - "integrity": "sha512-QiTC86gRuoepzzmS6HNJZTwfz/n27NcqtaVEIxJi1Yvsx2/kLa9NkRhylNkfTuZ1gEry9stAlKWanMsB2aKyjQ==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/router/-/router-7.6.6.tgz", + "integrity": "sha512-dkn81MtxrG7JMDbOHEcVZkTDVKsneg72CyqJ8ELZfC81iKQcDMQkV9mdmnMl45aKn6UrscudI4K23OxQmsevkw==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.6.5", + "@storybook/client-logger": "7.6.6", "memoizerific": "^1.11.3", "qs": "^6.10.0" }, @@ -6536,14 +6536,14 @@ } }, "node_modules/@storybook/telemetry": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/telemetry/-/telemetry-7.6.5.tgz", - "integrity": "sha512-FiLRh9k9LoGphqgBqPYySWdGqplihiZyDwqdo+Qs19RcQ/eiKg0W7fdA09nStcdcsHmDl/1cMfRhz9KUiMtwOw==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/telemetry/-/telemetry-7.6.6.tgz", + "integrity": "sha512-2WdDcrMrt1bPVgdMVO0tFmVxT6YIjiPRfKbH/7wwYMOGmV75m4mJ9Ha2gzZc/oXTSK1M4/fiK12IgW+S3ErcMg==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.6.5", - "@storybook/core-common": "7.6.5", - "@storybook/csf-tools": "7.6.5", + "@storybook/client-logger": "7.6.6", + "@storybook/core-common": "7.6.6", + "@storybook/csf-tools": "7.6.6", "chalk": "^4.1.0", "detect-package-manager": "^2.0.1", "fetch-retry": "^5.0.2", @@ -6622,13 +6622,13 @@ } }, "node_modules/@storybook/theming": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.6.5.tgz", - "integrity": "sha512-RpcWT0YEgiobO41McVPDfQQHHFnjyr1sJnNTPJIvOUgSfURdgSj17mQVxtD5xcXcPWUdle5UhIOrCixHbL/NNw==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.6.6.tgz", + "integrity": "sha512-hNZOOxaF55iAGUEM0dvAIP6LfGMgPKCJQIk/qyotFk+SKkg3PBqzph89XfFl9yCD3KiX5cryqarULgVuNawLJg==", "dev": true, "dependencies": { "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@storybook/client-logger": "7.6.5", + "@storybook/client-logger": "7.6.6", "@storybook/global": "^5.0.0", "memoizerific": "^1.11.3" }, @@ -6642,12 +6642,12 @@ } }, "node_modules/@storybook/types": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.5.tgz", - "integrity": "sha512-Q757v+fYZZSaEpks/zDL5YgXRozxkgKakXFc+BoQHK5q5sVhJ+0jvpLJiAQAniIIaMIkqY/G24Kd6Uo6UdKBCg==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.6.tgz", + "integrity": "sha512-77vbQp3GX93OD8UzFkY4a0fAmkZrqLe61XVo6yABrwbVDY0EcAwaCF5gcXRhOHldlH7KYbLfEQkDkkKTBjX7ow==", "dev": true, "dependencies": { - "@storybook/channels": "7.6.5", + "@storybook/channels": "7.6.6", "@types/babel__core": "^7.0.0", "@types/express": "^4.7.0", "file-system-cache": "2.3.0" @@ -6658,18 +6658,18 @@ } }, "node_modules/@storybook/web-components": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/web-components/-/web-components-7.6.5.tgz", - "integrity": "sha512-uBL1d/qSwvbmkV/JGBdLySdmyOnZKBK+hn7+lgfQfTPV7JjOmTzuifnnSrhKlI/wupdBM/LYjlax5kGYt3oBXQ==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/web-components/-/web-components-7.6.6.tgz", + "integrity": "sha512-oBfZBM1Vkzs/rZySk/HXCIRZ10FSYx6wgyMbiT5EmGm7Jz9y5qaqQhG/sPYKsYL0TlPRRKjf1iukHkxD6DWmpA==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.6.5", - "@storybook/core-client": "7.6.5", - "@storybook/docs-tools": "7.6.5", + "@storybook/client-logger": "7.6.6", + "@storybook/core-client": "7.6.6", + "@storybook/docs-tools": "7.6.6", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "7.6.5", - "@storybook/preview-api": "7.6.5", - "@storybook/types": "7.6.5", + "@storybook/manager-api": "7.6.6", + "@storybook/preview-api": "7.6.6", + "@storybook/types": "7.6.6", "tiny-invariant": "^1.3.1", "ts-dedent": "^2.0.0" }, @@ -6685,15 +6685,15 @@ } }, "node_modules/@storybook/web-components-vite": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/web-components-vite/-/web-components-vite-7.6.5.tgz", - "integrity": "sha512-j4lfrgPjlqAeoUdfXWMESUAxrOn+YUJekGh3saxsHxXIiYnzJBdkiJju4Hzqav4j8645bJzlZBw51e6wJJsuFw==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/web-components-vite/-/web-components-vite-7.6.6.tgz", + "integrity": "sha512-AOxvnA7eIRnKqZ21QgUHJ/ENX1MMhJCKPiytvkX8U02gs+0HE+NnZPbhZLPh/Ruax7KfLPUGItv3BcfrIxV5lw==", "dev": true, "dependencies": { - "@storybook/builder-vite": "7.6.5", - "@storybook/core-server": "7.6.5", - "@storybook/node-logger": "7.6.5", - "@storybook/web-components": "7.6.5", + "@storybook/builder-vite": "7.6.6", + "@storybook/core-server": "7.6.6", + "@storybook/node-logger": "7.6.6", + "@storybook/web-components": "7.6.6", "magic-string": "^0.30.0" }, "engines": { @@ -6705,17 +6705,17 @@ } }, "node_modules/@storybook/web-components/node_modules/@storybook/preview-api": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.6.5.tgz", - "integrity": "sha512-9XzuDXXgNuA6dDZ3DXsUwEG6ElxeTbzLuYuzcjtS1FusSICZ2iYmxfS0GfSud9MjPPYOJYoSOvMdIHjorjgByA==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.6.6.tgz", + "integrity": "sha512-Bt6xIAR5yZ/JWc90X4BbLOA97iL65glZ1SOBgFFv2mHrdZ1lcdKhAlQr2aeJAf1mLvBtalPjvKzi9EuVY3FZ4w==", "dev": true, "dependencies": { - "@storybook/channels": "7.6.5", - "@storybook/client-logger": "7.6.5", - "@storybook/core-events": "7.6.5", + "@storybook/channels": "7.6.6", + "@storybook/client-logger": "7.6.6", + "@storybook/core-events": "7.6.6", "@storybook/csf": "^0.1.2", "@storybook/global": "^5.0.0", - "@storybook/types": "7.6.5", + "@storybook/types": "7.6.6", "@types/qs": "^6.9.5", "dequal": "^2.0.2", "lodash": "^4.17.21", @@ -7605,16 +7605,16 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.15.0.tgz", - "integrity": "sha512-j5qoikQqPccq9QoBAupOP+CBu8BaJ8BLjaXSioDISeTZkVO3ig7oSIKh3H+rEpee7xCXtWwSB4KIL5l6hWZzpg==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.16.0.tgz", + "integrity": "sha512-O5f7Kv5o4dLWQtPX4ywPPa+v9G+1q1x8mz0Kr0pXUtKsevo+gIJHLkGc8RxaZWtP8RrhwhSNIWThnW42K9/0rQ==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.15.0", - "@typescript-eslint/type-utils": "6.15.0", - "@typescript-eslint/utils": "6.15.0", - "@typescript-eslint/visitor-keys": "6.15.0", + "@typescript-eslint/scope-manager": "6.16.0", + "@typescript-eslint/type-utils": "6.16.0", + "@typescript-eslint/utils": "6.16.0", + "@typescript-eslint/visitor-keys": "6.16.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -7673,15 +7673,15 @@ "dev": true }, "node_modules/@typescript-eslint/parser": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.15.0.tgz", - "integrity": "sha512-MkgKNnsjC6QwcMdlNAel24jjkEO/0hQaMDLqP4S9zq5HBAUJNQB6y+3DwLjX7b3l2b37eNAxMPLwb3/kh8VKdA==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.16.0.tgz", + "integrity": "sha512-H2GM3eUo12HpKZU9njig3DF5zJ58ja6ahj1GoHEHOgQvYxzoFJJEvC1MQ7T2l9Ha+69ZSOn7RTxOdpC/y3ikMw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.15.0", - "@typescript-eslint/types": "6.15.0", - "@typescript-eslint/typescript-estree": "6.15.0", - "@typescript-eslint/visitor-keys": "6.15.0", + "@typescript-eslint/scope-manager": "6.16.0", + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/typescript-estree": "6.16.0", + "@typescript-eslint/visitor-keys": "6.16.0", "debug": "^4.3.4" }, "engines": { @@ -7701,13 +7701,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.15.0.tgz", - "integrity": "sha512-+BdvxYBltqrmgCNu4Li+fGDIkW9n//NrruzG9X1vBzaNK+ExVXPoGB71kneaVw/Jp+4rH/vaMAGC6JfMbHstVg==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.16.0.tgz", + "integrity": "sha512-0N7Y9DSPdaBQ3sqSCwlrm9zJwkpOuc6HYm7LpzLAPqBL7dmzAUimr4M29dMkOP/tEwvOCC/Cxo//yOfJD3HUiw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.15.0", - "@typescript-eslint/visitor-keys": "6.15.0" + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/visitor-keys": "6.16.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -7718,13 +7718,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.15.0.tgz", - "integrity": "sha512-CnmHKTfX6450Bo49hPg2OkIm/D/TVYV7jO1MCfPYGwf6x3GO0VU8YMO5AYMn+u3X05lRRxA4fWCz87GFQV6yVQ==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.16.0.tgz", + "integrity": "sha512-ThmrEOcARmOnoyQfYkHw/DX2SEYBalVECmoldVuH6qagKROp/jMnfXpAU/pAIWub9c4YTxga+XwgAkoA0pxfmg==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.15.0", - "@typescript-eslint/utils": "6.15.0", + "@typescript-eslint/typescript-estree": "6.16.0", + "@typescript-eslint/utils": "6.16.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -7745,9 +7745,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.15.0.tgz", - "integrity": "sha512-yXjbt//E4T/ee8Ia1b5mGlbNj9fB9lJP4jqLbZualwpP2BCQ5is6BcWwxpIsY4XKAhmdv3hrW92GdtJbatC6dQ==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.16.0.tgz", + "integrity": "sha512-hvDFpLEvTJoHutVl87+MG/c5C8I6LOgEx05zExTSJDEVU7hhR3jhV8M5zuggbdFCw98+HhZWPHZeKS97kS3JoQ==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -7758,16 +7758,17 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.15.0.tgz", - "integrity": "sha512-7mVZJN7Hd15OmGuWrp2T9UvqR2Ecg+1j/Bp1jXUEY2GZKV6FXlOIoqVDmLpBiEiq3katvj/2n2mR0SDwtloCew==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.16.0.tgz", + "integrity": "sha512-VTWZuixh/vr7nih6CfrdpmFNLEnoVBF1skfjdyGnNwXOH1SLeHItGdZDHhhAIzd3ACazyY2Fg76zuzOVTaknGA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.15.0", - "@typescript-eslint/visitor-keys": "6.15.0", + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/visitor-keys": "6.16.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", + "minimatch": "9.0.3", "semver": "^7.5.4", "ts-api-utils": "^1.0.1" }, @@ -7784,6 +7785,15 @@ } } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -7796,6 +7806,21 @@ "node": ">=10" } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -7818,17 +7843,17 @@ "dev": true }, "node_modules/@typescript-eslint/utils": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.15.0.tgz", - "integrity": "sha512-eF82p0Wrrlt8fQSRL0bGXzK5nWPRV2dYQZdajcfzOD9+cQz9O7ugifrJxclB+xVOvWvagXfqS4Es7vpLP4augw==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.16.0.tgz", + "integrity": "sha512-T83QPKrBm6n//q9mv7oiSvy/Xq/7Hyw9SzSEhMHJwznEmQayfBM87+oAlkNAMEO7/MjIwKyOHgBJbxB0s7gx2A==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.15.0", - "@typescript-eslint/types": "6.15.0", - "@typescript-eslint/typescript-estree": "6.15.0", + "@typescript-eslint/scope-manager": "6.16.0", + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/typescript-estree": "6.16.0", "semver": "^7.5.4" }, "engines": { @@ -7876,12 +7901,12 @@ "dev": true }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.15.0.tgz", - "integrity": "sha512-1zvtdC1a9h5Tb5jU9x3ADNXO9yjP8rXlaoChu0DQX40vf5ACVpYIVIZhIMZ6d5sDXH7vq4dsZBT1fEGj8D2n2w==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.16.0.tgz", + "integrity": "sha512-QSFQLruk7fhs91a/Ep/LqRdbJCZ1Rq03rqBdKT5Ky17Sz8zRLUksqIe9DW0pKtg/Z35/ztbLQ6qpOCN6rOC11A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.15.0", + "@typescript-eslint/types": "6.16.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -11436,9 +11461,9 @@ "dev": true }, "node_modules/flow-parser": { - "version": "0.224.0", - "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.224.0.tgz", - "integrity": "sha512-S1P78o0VLB1FZvkoGSIpaRiiTUQ3xDhm9I4Z1qc3lglmkjehfR2sjM0vhwKS7UC1G12VT4Leb/GGV/KlactqjA==", + "version": "0.225.0", + "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.225.0.tgz", + "integrity": "sha512-wTr10/1z9wBuaNf+RGdGwD5FOI6TsNcWrMDhE+JBc2vEKe1e4SZuO5zVZCBq9SrFqPyWy0wFO9+hTH4zuPDbMA==", "dev": true, "engines": { "node": ">=0.4.0" @@ -16952,12 +16977,12 @@ "dev": true }, "node_modules/storybook": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/storybook/-/storybook-7.6.5.tgz", - "integrity": "sha512-uHPrL+g/0v6iIVtDA8J0uWd3jDZcdr51lCR/vPXTkrCY1uVaFjswzl8EMy5PR05I7jMpKUzkJWZtFbgbh9e1Bw==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/storybook/-/storybook-7.6.6.tgz", + "integrity": "sha512-PmJxpjGdLvDOHaRzqLOvcJ3ALQPaNeW6D5Lv7rPPVbuO24wdDzd/75dPRP7gJKYcGE0NnDZ6cLQq3UlCfbkIBA==", "dev": true, "dependencies": { - "@storybook/cli": "7.6.5" + "@storybook/cli": "7.6.6" }, "bin": { "sb": "index.js", diff --git a/web/package.json b/web/package.json index 786580462..4d7002840 100644 --- a/web/package.json +++ b/web/package.json @@ -42,15 +42,15 @@ "@codemirror/theme-one-dark": "^6.1.2", "@formatjs/intl-listformat": "^7.5.3", "@fortawesome/fontawesome-free": "^6.5.1", - "@goauthentik/api": "^2023.10.4-1702989148", + "@goauthentik/api": "^2023.10.5-1703290840", "@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.88.0", - "@sentry/tracing": "^7.88.0", + "@sentry/browser": "^7.91.0", + "@sentry/tracing": "^7.91.0", "@webcomponents/webcomponentsjs": "^2.8.0", "base64-js": "^1.5.1", "chart.js": "^4.4.1", @@ -86,19 +86,19 @@ "@rollup/plugin-replace": "^5.0.5", "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^11.1.5", - "@storybook/addon-essentials": "^7.6.5", - "@storybook/addon-links": "^7.6.5", - "@storybook/api": "^7.6.5", + "@storybook/addon-essentials": "^7.6.6", + "@storybook/addon-links": "^7.6.6", + "@storybook/api": "^7.6.6", "@storybook/blocks": "^7.6.4", - "@storybook/manager-api": "^7.6.5", - "@storybook/web-components": "^7.6.5", - "@storybook/web-components-vite": "^7.6.5", + "@storybook/manager-api": "^7.6.6", + "@storybook/web-components": "^7.6.6", + "@storybook/web-components-vite": "^7.6.6", "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/chart.js": "^2.9.41", "@types/codemirror": "5.60.15", "@types/grecaptcha": "^3.0.7", - "@typescript-eslint/eslint-plugin": "^6.15.0", - "@typescript-eslint/parser": "^6.15.0", + "@typescript-eslint/eslint-plugin": "^6.16.0", + "@typescript-eslint/parser": "^6.16.0", "babel-plugin-macros": "^3.1.0", "babel-plugin-tsconfig-paths": "^1.0.3", "cross-env": "^7.0.3", @@ -120,7 +120,7 @@ "rollup-plugin-cssimport": "^1.0.3", "rollup-plugin-modify": "^3.0.0", "rollup-plugin-postcss-lit": "^2.1.0", - "storybook": "^7.6.5", + "storybook": "^7.6.6", "storybook-addon-mock": "^4.3.0", "ts-lit-plugin": "^2.0.1", "tslib": "^2.6.2", diff --git a/web/src/admin/admin-overview/charts/SyncStatusChart.ts b/web/src/admin/admin-overview/charts/SyncStatusChart.ts index 28747d682..d9a3cce6c 100644 --- a/web/src/admin/admin-overview/charts/SyncStatusChart.ts +++ b/web/src/admin/admin-overview/charts/SyncStatusChart.ts @@ -93,15 +93,16 @@ export class LDAPSyncStatusChart extends AKChart { const health = await api.providersScimSyncStatusRetrieve({ id: element.pk, }); - - if (health.status !== TaskStatusEnum.Successful) { - sourceKey = "failed"; - } - const now = new Date().getTime(); - const maxDelta = 3600000; // 1 hour - if (!health || now - health.taskFinishTimestamp.getTime() > maxDelta) { - sourceKey = "unsynced"; - } + health.tasks.forEach((task) => { + if (task.status !== TaskStatusEnum.Successful) { + sourceKey = "failed"; + } + const now = new Date().getTime(); + const maxDelta = 3600000; // 1 hour + if (!health || now - task.taskFinishTimestamp.getTime() > maxDelta) { + sourceKey = "unsynced"; + } + }); } catch { sourceKey = "unsynced"; } diff --git a/web/src/admin/providers/scim/SCIMProviderViewPage.ts b/web/src/admin/providers/scim/SCIMProviderViewPage.ts index 3998c7c81..d745ed55e 100644 --- a/web/src/admin/providers/scim/SCIMProviderViewPage.ts +++ b/web/src/admin/providers/scim/SCIMProviderViewPage.ts @@ -10,7 +10,7 @@ import "@goauthentik/elements/Tabs"; import "@goauthentik/elements/buttons/ActionButton"; import "@goauthentik/elements/buttons/ModalButton"; -import { msg } from "@lit/localize"; +import { msg, str } from "@lit/localize"; import { CSSResult, TemplateResult, html } from "lit"; import { customElement, property, state } from "lit/decorators.js"; @@ -31,7 +31,8 @@ import { ProvidersApi, RbacPermissionsAssignedByUsersListModelEnum, SCIMProvider, - Task, + SCIMSyncStatus, + TaskStatusEnum, } from "@goauthentik/api"; @customElement("ak-provider-scim-view") @@ -54,7 +55,7 @@ export class SCIMProviderViewPage extends AKElement { provider?: SCIMProvider; @state() - syncState?: Task; + syncState?: SCIMSyncStatus; static get styles(): CSSResult[] { return [ @@ -128,6 +129,41 @@ export class SCIMProviderViewPage 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}
    • `; + })} +
    +
  • `; + })} +
+ `; + } + renderTabOverview(): TemplateResult { if (!this.provider) { return html``; @@ -186,16 +222,7 @@ export class SCIMProviderViewPage extends AKElement {

${msg("Sync status")}

-
- ${this.syncState - ? html`
    - ${this.syncState.messages.map((m) => { - return html`
  • ${m}
  • `; - })} -
` - : html` ${msg("Sync not run yet.")} `} -
- +
${this.renderSyncStatus()}
${this.getEmailInfo(this.event.context)}
- + `; } diff --git a/web/xliff/de.xlf b/web/xliff/de.xlf index 6bbeca533..5e8869ffd 100644 --- a/web/xliff/de.xlf +++ b/web/xliff/de.xlf @@ -1585,15 +1585,30 @@ NameID attribute + + No sync status. + + + Sync currently running. + + + Not synced yet. + + + Task finished with warnings + + + Task finished with errors + + + Last sync: + Warning: Provider is not assigned to an application as backchannel provider. Update SCIM Provider - - Sync not run yet. - Run sync again @@ -2325,24 +2340,6 @@ doesn't pass when either or both of the selected options are equal or above the Vendor - - No sync status. - - - Sync currently running. - - - Not synced yet. - - - Task finished with warnings - - - Task finished with errors - - - Last sync: - Update LDAP Source @@ -3848,6 +3845,39 @@ doesn't pass when either or both of the selected options are equal or above the If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here. + + Network binding + + + No binding + + + Bind ASN + + + Bind ASN and Network + + + Bind ASN, Network and IP + + + Configure if sessions created by this stage should be bound to the Networks they were created in. + + + GeoIP binding + + + Bind Continent + + + Bind Continent and Country + + + Bind Continent, Country and City + + + Configure if sessions created by this stage should be bound to their GeoIP-based location + Terminate other sessions diff --git a/web/xliff/en.xlf b/web/xliff/en.xlf index c83cf1467..429448042 100644 --- a/web/xliff/en.xlf +++ b/web/xliff/en.xlf @@ -1585,15 +1585,30 @@ NameID attribute + + No sync status. + + + Sync currently running. + + + Not synced yet. + + + Task finished with warnings + + + Task finished with errors + + + Last sync: + Warning: Provider is not assigned to an application as backchannel provider. Update SCIM Provider - - Sync not run yet. - Run sync again @@ -2325,24 +2340,6 @@ doesn't pass when either or both of the selected options are equal or above the Vendor - - No sync status. - - - Sync currently running. - - - Not synced yet. - - - Task finished with warnings - - - Task finished with errors - - - Last sync: - Update LDAP Source @@ -3848,6 +3845,39 @@ doesn't pass when either or both of the selected options are equal or above the If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here. + + Network binding + + + No binding + + + Bind ASN + + + Bind ASN and Network + + + Bind ASN, Network and IP + + + Configure if sessions created by this stage should be bound to the Networks they were created in. + + + GeoIP binding + + + Bind Continent + + + Bind Continent and Country + + + Bind Continent, Country and City + + + Configure if sessions created by this stage should be bound to their GeoIP-based location + Terminate other sessions diff --git a/web/xliff/es.xlf b/web/xliff/es.xlf index aaa3d1517..c457c535c 100644 --- a/web/xliff/es.xlf +++ b/web/xliff/es.xlf @@ -1585,15 +1585,30 @@ NameID attribute + + No sync status. + + + Sync currently running. + + + Not synced yet. + + + Task finished with warnings + + + Task finished with errors + + + Last sync: + Warning: Provider is not assigned to an application as backchannel provider. Update SCIM Provider - - Sync not run yet. - Run sync again @@ -2325,24 +2340,6 @@ doesn't pass when either or both of the selected options are equal or above the Vendor - - No sync status. - - - Sync currently running. - - - Not synced yet. - - - Task finished with warnings - - - Task finished with errors - - - Last sync: - Update LDAP Source @@ -3848,6 +3845,39 @@ doesn't pass when either or both of the selected options are equal or above the If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here. + + Network binding + + + No binding + + + Bind ASN + + + Bind ASN and Network + + + Bind ASN, Network and IP + + + Configure if sessions created by this stage should be bound to the Networks they were created in. + + + GeoIP binding + + + Bind Continent + + + Bind Continent and Country + + + Bind Continent, Country and City + + + Configure if sessions created by this stage should be bound to their GeoIP-based location + Terminate other sessions diff --git a/web/xliff/fr.xlf b/web/xliff/fr.xlf index 39863966d..fec56a943 100644 --- a/web/xliff/fr.xlf +++ b/web/xliff/fr.xlf @@ -1585,15 +1585,30 @@ NameID attribute + + No sync status. + + + Sync currently running. + + + Not synced yet. + + + Task finished with warnings + + + Task finished with errors + + + Last sync: + Warning: Provider is not assigned to an application as backchannel provider. Update SCIM Provider - - Sync not run yet. - Run sync again @@ -2325,24 +2340,6 @@ doesn't pass when either or both of the selected options are equal or above the Vendor - - No sync status. - - - Sync currently running. - - - Not synced yet. - - - Task finished with warnings - - - Task finished with errors - - - Last sync: - Update LDAP Source @@ -3848,6 +3845,39 @@ doesn't pass when either or both of the selected options are equal or above the If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here. + + Network binding + + + No binding + + + Bind ASN + + + Bind ASN and Network + + + Bind ASN, Network and IP + + + Configure if sessions created by this stage should be bound to the Networks they were created in. + + + GeoIP binding + + + Bind Continent + + + Bind Continent and Country + + + Bind Continent, Country and City + + + Configure if sessions created by this stage should be bound to their GeoIP-based location + Terminate other sessions diff --git a/web/xliff/ko.xlf b/web/xliff/ko.xlf new file mode 100644 index 000000000..ed1d838d9 --- /dev/null +++ b/web/xliff/ko.xlf @@ -0,0 +1,8010 @@ + + + + + English + 영어 + + + + French + 프랑스어 + + + + Turkish + 터키어 + + + + Spanish + 스페인어 + + + + Polish + 폴란드어 + + + + Taiwanese Mandarin + 대만어 + + + + Chinese (simplified) + 중국어 (간체) + + + + Chinese (traditional) + 중국어 (번체) + + + + German + 독일어 + + + + Loading... + 로드 중... + + + + Application + 애플리케이션 + + + + Logins + 로그인 + + + + Show less + 적게 표시 + + + + Show more + 자세히 보기 + + + + UID + UID + + + + Name + 이름 + + + + App + + + + + Model Name + 모델 이름 + + + + Message + 메시지 + + + + Subject + Subject + + + + From + From + + + + To + To + + + + Context + 맥락 + + + + User + 사용자 + + + + Affected model: + 영향 받는 모델: + + + + Authorized application: + 인가된 애플리케이션: + + + + Using flow + 플로우 사용 + + + + Email info: + 이메일 정보: + + + + Secret: + 비밀: + + + + Open issue on GitHub... + GitHub에서 이슈 열기... + + + + Exception + 예외 + + + + Expression + 표현식 + + + + Binding + 바인딩 + + + + Request + 요청 + + + + Object + 오브젝트 + + + + Result + 결과 + + + + Passing + 통과 + + + + Messages + 메시지 + + + + Using source + 소스 사용 + + + + Attempted to log in as + 로 로그인을 시도함 + + + + No additional data available. + 추가 데이터를 사용할 수 없습니다. + + + + Click to change value + 클릭하여 값 변경 + + + + Select an object. + 오브젝트를 선택합니다. + + + + Loading options... + 옵션 로드 중... + + + + Connection error, reconnecting... + 연결 오류, 다시 연결 중... + + + + Login + 로그인 + + + + Failed login + 로그인 실패 + + + + Logout + 로그아웃 + + + + User was written to + 사용자가 다음에 기록됨 + + + + Suspicious request + 의심스러운 요청 + + + + Password set + 비밀번호 설정 + + + + Secret was viewed + 비밀이 표시됨 + + + + Secret was rotated + 비밀이 회전됨 + + + + Invitation used + 사용된 초대 + + + + Application authorized + 승인된 애플리케이션 + + + + Source linked + 링크된 소스 + + + + Impersonation started + 사용자 위장 시작 + + + + Impersonation ended + 사용자 위장 종료 + + + + Flow execution + 플로우 실행 + + + + Policy execution + 정책 실행 + + + + Policy exception + 정책 예외 + + + + Property Mapping exception + 속성 매핑 예외 + + + + System task execution + 시스템 작업 실행 + + + + System task exception + 시스템 작업 예외 + + + + General system exception + 일반 시스템 예외 + + + + Configuration error + 구성 오류 + + + + Model created + 모델 생성함 + + + + Model updated + 모델 업데이트함 + + + + Model deleted + 모델 삭제함 + + + + Email sent + 이메일 보냄 + + + + Update available + 업데이트 가능 + + + + Unknown severity + 심각도 불명 + + + + Alert + 경고 + + + + Notice + 공지 + + + + Warning + 주의 + + + + no tabs defined + 탭이 정의되지 않음 + + + + - of + + - + of + + + + + Go to previous page + 이전 페이지 가기 + + + + Go to next page + 다음 페이지 가기 + + + + Search... + 검색... + + + + Loading + 로드 중 + + + + No objects found. + 오브젝트를 찾을 수 없습니다. + + + + Failed to fetch objects. + 오브젝트를 가져오는데 실패했습니다. + + + + Refresh + 새로고침 + + + + Select all rows + 모든 행 선택 + + + + Action + 액션 + + + + Creation Date + 생성일 + + + + Client IP + 클라이언트 IP + + + + Tenant + 테넌트 + + + + Recent events + 최근 이력 + + + + On behalf of + +을 대신하여 + + + + - + - + + + + No Events found. + 이력을 찾을 수 없습니다. + + + + No matching events could be found. + 일치하는 이력을 찾을 수 없습니다. + + + + Embedded outpost is not configured correctly. + 내장된 Outpost가 올바르게 구성되지 않았습니다. + + + + Check outposts. + Outpost를 확인합니다. + + + + HTTPS is not detected correctly + HTTPS가 올바르게 감지되지 않음 + + + + Server and client are further than 5 seconds apart. + 서버와 클라이언트의 간격이 5초 이상입니다. + + + + OK + OK + + + + Everything is ok. + 모든 것이 양호합니다. + + + + System status + 시스템 상태 + + + + Based on + +기반 + + + + is available! + + 버전 사용가능! + + + + Up-to-date! + 최신-버전! + + + + Version + 버전 + + + + Workers + 워커 + + + + No workers connected. Background tasks will not run. + 연결된 워커가 없습니다. 백그라운드 작업이 실행되지 않습니다. + + + + hour(s) ago + + 시간 전 + + + + day(s) ago + + 일 전 + + + + Authorizations + 인가됨 + + + + Failed Logins + 실패한 로그인 + + + + Successful Logins + 성공한 로그인 + + + + : + + : + + + + + Cancel + 취소 + + + + LDAP Source + LDAP 소스 + + + + SCIM Provider + SCIM 공급자 + + + + Healthy + 양호 + + + + Healthy outposts + 양호한 outpost + + + + Admin + 관리자 + + + + Not found + 찾을 수 없음 + + + + The URL "" was not found. + URL "" 을 찾을 수 없습니다. + + + + Return home + 홈으로 가기 + + + + General system status + 일반 시스템 상태 + + + + Welcome, . + 님 반갑습니다. + + + + Quick actions + 빠른 액션 + + + + Create a new application + 새로운 애플리케이션 생성 + + + + Check the logs + 로그 확인 + + + + Explore integrations + 통합 살펴보기 + + + + Manage users + 사용자 관리 + + + + Outpost status + Outpost 상태 + + + + Sync status + 동기화 상태 + + + + Logins and authorizations over the last week (per 8 hours) + 최근 한 주 동안의 로그인 및 승인(8시간당) + + + + Apps with most usage + 사용량이 가장 많은 앱 + + + + days ago + + 일 전 + + + + Objects created + 오브젝트 생성됨 + + + + Users created per day in the last month + 최근 한 달 동안 하루에 생성된 사용자 수 + + + + Logins per day in the last month + 최근 한 달 일일 로그인 수 + + + + Failed Logins per day in the last month + 최근 한 달 일일 로그인 실패 횟수 + + + + Clear search + 검색 삭제 + + + + System Tasks + 시스템 작업 + + + + Long-running operations which authentik executes in the background. + authentik이 백그라운드에서 실행하는 장기 실행 작업입니다. + + + + Identifier + 식별자 + + + + Description + 기술 + + + + Last run + 마지막 실행 + + + + Status + 상태 + + + + Actions + 액션 + + + + Successful + 성공 + + + + Error + 오류 + + + + Unknown + 알 수 없음 + + + + Duration + 지속시간 + + + + seconds + + + + + + Authentication + 인증 + + + + Authorization + 인가 + + + + Enrollment + 등록 + + + + Invalidation + 무효 + + + + Recovery + Recovery + + + + Stage Configuration + 스테이지 설정 + + + + Unenrollment + 등록 취소 + + + + Unknown designation + 지정할 수 없는 지정 + + + + Stacked + Stacked + + + + Content left + 왼쪽 콘텐츠 + + + + Content right + 오른쪽 콘텐츠 + + + + Sidebar left + 왼쪽 사이드바 + + + + Sidebar right + 오른쪽 사이드바 + + + + Unknown layout + 지정할 수 없는 배치 + + + + Successfully updated provider. + 성공적으로 공급자 업데이트 했습니다. + + + + Successfully created provider. + 성공적으로 공급자를 만들었습니다. + + + + Bind flow + 플로우 바인드 + + + + Flow used for users to authenticate. + 사용자가 인증하는 데 사용되는 플로우입니다. + + + + Search group + 그룹 검색 + + + + Users in the selected group can do search queries. If no group is selected, no LDAP Searches are allowed. + 선택한 그룹의 사용자는 검색 쿼리를 수행할 수 있습니다. 그룹이 선택되지 않은 경우, LDAP 검색을 할 수 없습니다. + + + + Bind mode + 모드 바인드 + + + + Cached binding + 캐시된 바인딩 + + + + Flow is executed and session is cached in memory. Flow is executed when session expires + 플로우가 실행되고 세션이 메모리에 캐시됩니다. 세션이 만료되면 플로우를 실행 + + + + Direct binding + 직접 바인딩 + + + + Always execute the configured bind flow to authenticate the user + 항상 구성된 바인드 플로우를 실행하여 사용자를 인증 + + + + Configure how the outpost authenticates requests. + Outpost가 요청을 인증하는 방법을 구성합니다. + + + + Search mode + 모드 검색 + + + + Cached querying + 캐시된 쿼리 + + + + The outpost holds all users and groups in-memory and will refresh every 5 Minutes + Outpost는 모든 사용자와 그룹을 인메모리에 보관하며 5분마다 새로 고칩니다. + + + + Direct querying + 직접 쿼리 + + + + Always returns the latest data, but slower than cached querying + 항상 최신 데이터를 반환하지만 캐시된 쿼리보다는 느립니다. + + + + Configure how the outpost queries the core authentik server's users. + Outpost가 코어 authentik 서버의 사용자를 조회하는 방식을 구성합니다. + + + + Protocol settings + 프로토콜 설정 + + + + Base DN + Base DN + + + + LDAP DN under which bind requests and search requests can be made. + 바인드 요청 및 검색 요청을 수행할 LDAP DN입니다. + + + + Certificate + 인증서 + + + + UID start number + UID 시작 번호 + + + + 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 + UID의 시작값입니다. 이 숫자는 user.Pk에 추가되어 POSIX 사용자의 숫자가 너무 낮지 않도록 합니다. 기본값은 2000으로 설정되어 로컬 사용자 UID와 충돌하지 않도록 합니다. + + + + GID start number + GID 시작 번호 + + + + 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. Default is 4000 to ensure that we don't collide with local groups or users primary groups gidNumber + GID의 시작값입니다. 이 숫자는 group.Pk에서 생성된 숫자에 추가되어 POSIX 그룹의 숫자가 너무 낮지 않도록 합니다. 기본값은 4000으로 설정되어 로컬 그룹 또는 사용자의 기본 그룹 gidNumber와 충돌하지 않도록 합니다. + + + + (Format: hours=-1;minutes=-2;seconds=-3). + (서식: hours=-1;minutes=-2;seconds=-3). + + + + (Format: hours=1;minutes=2;seconds=3). + (서식: hours=1;minutes=2;seconds=3). + + + + The following keywords are supported: + 지원하는 키워드: + + + + Authentication flow + 인증 플로우 + + + + Flow used when a user access this provider and is not authenticated. + 사용자가 이 공급자에 액세스하고 인증되지 않은 경우에 사용하는 플로우입니다. + + + + Authorization flow + 인가 플로우 + + + + Flow used when authorizing this provider. + 이 공급자를 승인할 때 사용하는 플로우입니다. + + + + Client type + 클라이언트 유형 + + + + Confidential + 기밀 + + + + Confidential clients are capable of maintaining the confidentiality of their credentials such as client secrets + 기밀 클라이언트는 클라이언트 비밀과 같은 자격 증명의 기밀성을 유지할 수 있는 클라이언트입니다. + + + + Public + 공개 + + + + Public clients are incapable of maintaining the confidentiality and should use methods like PKCE. + 공개 클라이언트는 기밀을 유지할 수 없으며 PKCE와 같은 방법을 사용해야 합니다. + + + + Client ID + 클라이언트 ID + + + + Client Secret + 클라이언트 비밀 + + + + Redirect URIs/Origins (RegEx) + 리디렉션 URI/Origin (정규표현식) + + + + Valid redirect URLs after a successful authorization flow. Also specify any origins here for Implicit flows. + 성공적인 인증 플로우 이후의 유효한 리디렉션 URL을 지정하세요. 또한 암시적 플로우에 대한 경우 여기에 origin을 지정하세요. + + + + If no explicit redirect URIs are specified, the first successfully used redirect URI will be saved. + 명시적인 리디렉션 URI를 지정하지 않으면 가장 먼저 성공한 리디렉션 URI가 저장됩니다. + + + + To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have. + 리디렉션 URI를 허용하려면 이 값을 ".*"로 설정합니다. 이로 인해 발생할 수 있는 보안상의 영향에 유의하세요. + + + + Signing Key + 서명 키 + + + + Key used to sign the tokens. + 토큰을 서명하는 데 사용되는 키입니다. + + + + Advanced protocol settings + 고급 프로토콜 설정 + + + + Access code validity + 액세스 코드의 유효성 + + + + Configure how long access codes are valid for. + 액세스 코드가 유효한 기간을 구성합니다. + + + + Access Token validity + 액세스 토큰 유효기간 + + + + Configure how long access tokens are valid for. + 액세스 토큰이 유효한 기간을 구성합니다. + + + + Refresh Token validity + 토큰 유효 기간 갱신 + + + + Configure how long refresh tokens are valid for. + 갱신한 토큰의 만료 날짜를 구성합니다. + + + + Scopes + 스코프 + + + + Select which scopes can be used by the client. The client still has to specify the scope to access the data. + 클라이언트가 사용할 수 있는 범위를 선택하세요. 클라이언트는 여전히 데이터에 액세스하려면 범위를 명시적으로 지정해야 합니다. + + + + Hold control/command to select multiple items. + 여러 항목을 선택하려면 Ctrl/Command를 누르세요. + + + + Subject mode + Subject 모드 + + + + Based on the User's hashed ID + 사용자의 해시된 ID 기반 + + + + Based on the User's ID + 사용자의 ID 기반 + + + + Based on the User's UUID + 사용자의 UUID 기반 + + + + Based on the User's username + 사용자의 사용자이름 기반 + + + + Based on the User's Email + 사용자의 이메일 기반 + + + + This is recommended over the UPN mode. + 이 모드는 UPN보다 권장합니다. + + + + Based on the User's UPN + 사용자의 UPN 기반 + + + + Requires the user to have a 'upn' attribute set, and falls back to hashed user ID. Use this mode only if you have different UPN and Mail domains. + 사용자에게 'upn' 속성을 설정해야 하며, 해시된 사용자 ID로 대체됩니다. UPN과 메일 도메인이 다른 경우에만 이 모드를 사용하십시오. + + + + Configure what data should be used as unique User Identifier. For most cases, the default should be fine. + 고유한 사용자 식별자로 사용할 데이터를 구성합니다. 대부분의 경우 기본값을 사용하는 것이 적절합니다. + + + + Include claims in id_token + Id_token에 요청 포함 + + + + Include User claims from scopes in the id_token, for applications that don't access the userinfo endpoint. + 사용자 정보 엔드포인트에 액세스하지 않는 애플리케이션의 경우, 범위의 사용자 선언을 Id_token에 포함하세요. + + + + Issuer mode + Issuer 모드 + + + + Each provider has a different issuer, based on the application slug + 각 공급자는 애플리케이션 슬러그를 기반으로 한 다른 발급자를 가짐 + + + + Same identifier is used for all providers + 모든 공급자에 동일한 식별자를 사용 + + + + Configure how the issuer field of the ID Token should be filled. + ID 토큰의 발급자 필드를 어떻게 채울지 구성합니다. + + + + Machine-to-Machine authentication settings + 머신간 M2M 인증 설정 + + + + Trusted OIDC Sources + 신뢰할 수 있는 OIDC 소스 + + + + JWTs signed by certificates configured in the selected sources can be used to authenticate to this provider. + 선택한 소스에 구성된 인증서로 서명된 JWT를 사용하여 이 공급자를 인증할 수 있습니다. + + + + HTTP-Basic Username Key + HTTP-Basic 사용자명 키 + + + + User/Group Attribute used for the user part of the HTTP-Basic Header. If not set, the user's Email address is used. + HTTP-Basic 헤더의 사용자 부분에 사용되는 사용자/그룹 속성입니다. 설정하지 않으면 사용자의 이메일 주소가 사용됩니다. + + + + HTTP-Basic Password Key + HTTP-Basic 비밀번호 키 + + + + User/Group Attribute used for the password part of the HTTP-Basic Header. + HTTP-Basic 헤더의 비밀번호 부분에 사용되는 사용자/그룹 속성입니다. + + + + Proxy + 프록시 + + + + Forward auth (single application) + Forward auth (단일 애플리케이션) + + + + Forward auth (domain level) + Forward auth (도메인 레벨) + + + + This provider will behave like a transparent reverse-proxy, except requests must be authenticated. If your upstream application uses HTTPS, make sure to connect to the outpost using HTTPS as well. + 이 공급자는 투명한 리버스 프록시처럼 동작하지만 요청은 반드시 인증되어야 합니다. 업스트림 애플리케이션이 HTTPS를 사용하는 경우 아웃포스트에 연결할 때도 HTTPS를 사용해야합니다. + + + + External host + 외부 호스트 + + + + The external URL you'll access the application at. Include any non-standard port. + 애플리케이션에서 엑세스할 외부 URL입니다. 비표준 포트를 포함해야합니다. + + + + Internal host + 내부 호스트 + + + + Upstream host that the requests are forwarded to. + 요청을 전달해야하는 업스트림 호스트입니다. + + + + Internal host SSL Validation + 내부 호스트 SSL 유효성 검사 + + + + Validate SSL Certificates of upstream servers. + 업스트림 서버의 SSL 인증서를 유효성 검사합니다. + + + + Use this provider with nginx's auth_request or traefik's forwardAuth. Only a single provider is required per root domain. You can't do per-application authorization, but you don't have to create a provider for each application. + 이 공급자는 Nginx의 auth_request 또는 Traefik의 forwardAuth와 함께 사용됩니다. 루트 도메인 당 하나의 공급자만 필요합니다. 응용 프로그램 당 인증은 수행할 수 없지만 각 응용 프로그램마다 별도의 공급자를 생성할 필요는 없습니다. + + + + An example setup can look like this: + 예제 설정은 다음과 같습니다: + + + + authentik running on auth.example.com + authentik이 auth.example.com에서 실행 중인 경우 + + + + app1 running on app1.example.com + app1은 app1.example.com에서 실행 중인 경우 + + + + In this case, you'd set the Authentication URL to auth.example.com and Cookie domain to example.com. + 위의 상황에서는, 인증 URL을 auth.example.com으로 설정하고 쿠키 도메인을 example.com으로 설정합니다. + + + + Authentication URL + 인증 URL + + + + The external URL you'll authenticate at. The authentik core server should be reachable under this URL. + 외부 호스트는 인증을 수행할 외부 URL입니다. authentik 코어 서버는 이 URL 아래에 도달 가능해야 합니다. + + + + Cookie domain + 쿠키 도메인 + + + + Set this to the domain you wish the authentication to be valid for. Must be a parent domain of the URL above. If you're running applications as app1.domain.tld, app2.domain.tld, set this to 'domain.tld'. + 이를 원하는 인증이 유효한 도메인으로 설정하세요. 위의 URL의 상위 도메인이어야 합니다. 예를 들어 app1.domain.tld, app2.domain.tld와 같은 응용 프로그램을 실행 중이라면 'domain.tld'로 설정하세요. + + + + Unknown proxy mode + 알려지지 않은 프록시 모드 + + + + Token validity + 토큰 수명 + + + + Configure how long tokens are valid for. + 토큰이 유효한 기간을 구성합니다. + + + + Additional scopes + 스코프 추가 + + + + Additional scope mappings, which are passed to the proxy. + 프록시에 전달되는 추가 스코프를 매핑하세요. + + + + Unauthenticated URLs + Unauthenticated URLs + + + + Unauthenticated Paths + Unauthenticated Paths + + + + Regular expressions for which authentication is not required. Each new line is interpreted as a new expression. + 인증이 필요하지 않은 정규 표현식. 각 새 줄은 새 표현식으로 해석됩니다. + + + + When using proxy or forward auth (single application) mode, the requested URL Path is checked against the regular expressions. When using forward auth (domain mode), the full requested URL including scheme and host is matched against the regular expressions. + 프록시 또는 forward auth(단일 응용 프로그램) 모드를 사용할 때 요청된 URL 경로는 정규 표현식과 일치하는지 확인합니다. forward auth(도메인 모드)을 사용할 때는 스킴 및 호스트를 포함한 전체 요청된 URL이 정규 표현식과 일치해야합니다. + + + + Authentication settings + 인증 설정 + + + + Intercept header authentication + Authorization header 가로채기 + + + + When enabled, authentik will intercept the Authorization header to authenticate the request. + 활성화 하면, authentik은 요청을 인증하기 위해 Authorization 헤더를 가로챕니다. + + + + Send HTTP-Basic Authentication + HTTP-Basic 인증 전송 + + + + Send a custom HTTP-Basic Authentication header based on values from authentik. + authentik의 값에 기반하여 사용자 정의 HTTP-Basic 인증 헤더를 전송합니다. + + + + ACS URL + ACS URL + + + + Issuer + 발급자 + + + + Also known as EntityID. + EntityID로도 표현하기도 합니다. + + + + Service Provider Binding + 서비스 공급자 바인딩 + + + + Redirect + 리디렉트 + + + + Post + 포스트 + + + + Determines how authentik sends the response back to the Service Provider. + authentik이 응답을 서비스 공급자에 다시 전송하는 방식을 결정합니다. + + + + Audience + 수신처 + + + + Signing Certificate + 서명 인증서 + + + + Certificate used to sign outgoing Responses going to the Service Provider. + 서비스 공급자에게 전송되는 응답을 서명하는 데 사용되는 인증서입니다. + + + + Verification Certificate + 검증 인증서 + + + + When selected, incoming assertion's Signatures will be validated against this certificate. To allow unsigned Requests, leave on default. + 선택한 경우, 들어오는 요청의 서명은 이 인증서를 기반으로 유효성을 검사합니다. 서명되지 않은 요청을 허용하려면, 기본값으로 유지하세요. + + + + Property mappings + 속성 매핑 + + + + NameID Property Mapping + NameID 속성 매핑 + + + + Configure how the NameID value will be created. When left empty, the NameIDPolicy of the incoming request will be respected. + NameID 값이 어떻게 생성될지 구성합니다. 비워 둘 경우, 들어오는 요청의 NameIDPolicy를 존중합니다. + + + + Assertion valid not before + 다음 시간 이전의 어설션은 무효 + + + + Configure the maximum allowed time drift for an assertion. + 어설션에 대한 최대 허용 시간 드리프트를 구성합니다. + + + + Assertion valid not on or after + 다음 시간의 어설션 혹은 그 이후는 무효 + + + + Assertion not valid on or after current time + this value. + 어설션은 현재 시간 이후에 무효가 되거나, 현재 시간 + 값에 무효가 됩니다. + + + + Session valid not on or after + 다음 시간의 세션 혹은 그 이후는 무효 + + + + Session not valid on or after current time + this value. + 세션은 현재 시간 이후에 무효가 되거나, 현재 시간 + 값에 무효가 됩니다. + + + + Digest algorithm + 해시 알고리즘 + + + + Signature algorithm + 서명 알고리즘 + + + + Successfully imported provider. + 성공적으로 공급자를 불러왔습니다. + + + + Metadata + 메타데이터 + + + + Apply changes + 변경사항 저장 + + + + Close + 닫기 + + + + Finish + 마침 + + + + Back + 뒤로 + + + + No form found + 찾을 수 없음 + + + + Form didn't return a promise for submitting + 양식에서 제출하기로 한 것을 반환하지 않음 + + + + Select type + 유형 선택 + + + + Try the new application wizard + 새로운 애플리케이션 마법사 사용 + + + + The new application wizard greatly simplifies the steps required to create applications and providers. + 새로운 애플리케이션 마법사는 애플리케이션 및 공급자 생성에 필요한 스테이지를 크게 간소화합니다. + + + + Try it now + 지금 사용 + + + + Create + 생성 + + + + New provider + 새 공급자 + + + + Create a new provider. + 새 공급자를 만듭니다. + + + + Create + 생성 + + + + Shared secret + 공유 비밀 키 + + + + Client Networks + 클라이언트 네트워크 + + + + List of CIDRs (comma-seperated) 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. + 클라이언트가 연결할 수 있는 CIDR 목록(쉼표로 구분)입니다. 더 구체적인 + CIDR이 더 넓은 것보다 먼저 일치합니다. 지정되지 않은 CIDR에서 연결하는 클라이언트는 + 거부됩니다. + + + URL + URL + + + + SCIM base url, usually ends in /v2. + 일반적으로 /v2로 끝나는 SCIM 기본 URL 입니다. + + + + Token + 토큰 + + + + Token to authenticate with. Currently only bearer authentication is supported. + 인증에 사용되는 토큰입니다. 현재는 bearer authentication만 지원됩니다. + + + + User filtering + 사용자 필터링 + + + + Exclude service accounts + 서비스 계정 제외 + + + + Group + 그룹 + + + + Only sync users within the selected group. + 선택한 그룹 내의 사용자만 동기화합니다. + + + + Attribute mapping + 속성 매핑 + + + + User Property Mappings + 사용자 속성 매핑 + + + + Property mappings used to user mapping. + 사용자 매핑에 사용되는 속성 매핑입니다. + + + + Group Property Mappings + 그룹 속성 매핑 + + + + Property mappings used to group creation. + 그룹 생성에 사용되는 속성 매핑입니다. + + + + Not used by any other object. + 다른 오브젝트에서 사용하지 않습니다. + + + + object will be DELETED + 오브젝트가 삭제됩니다 + + + + connection will be deleted + 연결이 삭제됩니다 + + + + reference will be reset to default value + 참조가 기본값으로 재설정됩니다 + + + + reference will be set to an empty value + 참조가 빈 값으로 설정됩니다 + + + + () + + ( + ) + + + + ID + ID + + + + Successfully deleted + 성공적으로 가 제거됨 + + + Failed to delete : + 제거에 실패함 : + + + + Delete + 제거 + + + + Are you sure you want to delete ? + 정말 삭제하시겠습니까? + + + Delete + 제거 + + + + Providers + 공급자 + + + + Provide support for protocols like SAML and OAuth to assigned applications. + 할당된 애플리케이션에 SAML 및 OAuth와 같은 프로토콜을 지원합니다. + + + + Type + 유형 + + + + Provider(s) + 제공자(목록) + + + + Assigned to application + 애플리케이션에 할당 + + + + Assigned to application (backchannel) + 애플리케이션에 할당 (백채널) + + + + Warning: Provider not assigned to any application. + 경고: 공급자가 애플리케이션에 할당되지 않았습니다. + + + + Update + 업데이트 + + + + Update + 업데이트 + + + + Select providers to add to application + 애플리케이션에 추가할 공급자 선택 + + + + Add + + + + + Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test". + 전체 URL, 상대 경로를 입력하거나, 또는 'fa://fa-test'를 사용하여 Font Awesome 아이콘 "fa-test"를 사용합니다. + + + + Path template for users created. Use placeholders like `%(slug)s` to insert the source slug. + 사용자용 경로 템플릿이 생성되었습니다. 소스 슬러그를 삽입하려면 `%(slug)s`와 같은 자리 표시자를 사용하세요. + + + + Successfully updated application. + 애플리케이션을 성공적으로 업데이트했습니다. + + + + Successfully created application. + 애플리케이션을 성공적으로 만들었습니다. + + + + Application's display Name. + 애플리케이션의 표시 이름입니다. + + + + Slug + 슬러그 + + + + Optionally enter a group name. Applications with identical groups are shown grouped together. + 선택 사항으로 그룹 이름을 입력합니다. 동일한 그룹을 가진 애플리케이션이 함께 그룹으로 표시됩니다. + + + + Provider + 공급자 + + + + Select a provider that this application should use. + 이 애플리케이션이 사용할 공급자를 선택합니다. + + + + Select backchannel providers which augment the functionality of the main provider. + 기본 공급자의 기능을 보강하는 백채널 공급자를 선택합니다. + + + + Policy engine mode + 정책 엔진 모드 + + + + Any policy must match to grant access + 여러 정책 중 어느 하나라도 충족하면 액세스를 허용 + + + + All policies must match to grant access + 여러 정책 중 모든 정책이 충족하면 액세스를 허용 + + + + UI settings + UI 설정 + + + + Launch URL + 시작 URL + + + + If left empty, authentik will try to extract the launch URL based on the selected provider. + 비워두면 authentik이 선택한 공급업체를 기반으로 시작 URL을 추출하려고 시도합니다. + + + + Open in new tab + 새 브라우저 탭에서 열기 + + + + If checked, the launch URL will open in a new browser tab or window from the user's application library. + 이 옵션을 선택하면 사용자의 애플리케이션 라이브러리에서 새 브라우저 탭이나 창에서 실행 URL이 열립니다. + + + + Icon + 아이콘 + + + + Currently set to: + 현재 다음으로 설정됨: + + + + Clear icon + 아이콘 삭제 + + + + Publisher + 발행인 + + + + Create Application + 애플리케이션 생성 + + + + Overview + 개요 + + + + Changelog + 변경 로그 + + + + Warning: Provider is not used by any Outpost. + 경고: 공급자는 Outpost에서 사용하고 있지 않습니다. + + + + Assigned to application + 애플리케이션에 할당 + + + + Update LDAP Provider + LDAP 공급자 업데이트 + + + + Edit + 수정 + + + + How to connect + 연결 방법 + + + + Connect to the LDAP Server on port 389: + 포트 389에서 LDAP 서버에 연결합니다: + + + + Check the IP of the Kubernetes service, or + 쿠버네티스 서비스의 IP를 확인하거나, 또는 + + + + The Host IP of the docker host + 도커 호스트의 호스트 IP + + + + Bind DN + DN 바인드 + + + + Bind Password + 비밀번호 바인드 + + + + Search base + 검색 베이스 + + + + Preview + 미리보기 + + + + Warning: Provider is not used by an Application. + 경고: 공급자는 애플리케이션에서 사용하지 않습니다. + + + + Redirect URIs + 리디렉션 URI + + + + Update OAuth2 Provider + OAuth2 공급자 업데이트 + + + + OpenID Configuration URL + OpenID 구성 URL + + + + OpenID Configuration Issuer + OpenID 구성 발급자 + + + + Authorize URL + 인가 URL + + + + Token URL + 토큰 URL + + + + Userinfo URL + 사용자정보 URL + + + + Logout URL + 로그아웃 URL + + + + JWKS URL + JWKS URL + + + + Example JWT payload (for currently authenticated user) + JWT 페이로드 예시(현재 인가된 사용자의 경우) + + + + Forward auth (domain-level) + Forward auth (domain-level) + + + + Nginx (Ingress) + Nginx (Ingress) + + + + Nginx (Proxy Manager) + Nginx (Proxy Manager) + + + + Nginx (standalone) + Nginx (standalone) + + + + Traefik (Ingress) + Traefik (Ingress) + + + + Traefik (Compose) + Traefik (Compose) + + + + Traefik (Standalone) + Traefik (Standalone) + + + + Caddy (Standalone) + Caddy (Standalone) + + + + Internal Host + 내부 호스트 + + + + External Host + 외부 호스트 + + + + Basic-Auth + Basic-Auth + + + + Yes + + + + + Mode + 모드 + + + + Update Proxy Provider + 프록시 공급자 업데이트 + + + + Protocol Settings + 프로토콜 설정 + + + + Allowed Redirect URIs + 허가된 리디렉션 URI + + + + Setup + 설정 + + + + No additional setup is required. + 추가 설정이 필요하지 않습니다. + + + + Update Radius Provider + Radius 공급자 업데이트 + + + + Download + 다운로드 + + + + Copy download URL + 다운로드 URL 복사 + + + + Download signing certificate + 서명 인증서 다운로드 + + + + Related objects + 관련 오브젝트 + + + + Update SAML Provider + SAML 공급자 업데이트 + + + + SAML Configuration + SAML 구성 + + + + EntityID/Issuer + EntityID/Issuer + + + + SSO URL (Post) + SSO URL (Post) + + + + SSO URL (Redirect) + SSO URL (Redirect) + + + + SSO URL (IdP-initiated Login) + SSO URL (IdP-initiated Login) + + + + SLO URL (Post) + SLO URL (Post) + + + + SLO URL (Redirect) + SLO URL (Redirect) + + + + SAML Metadata + SAML 메타데이터 + + + + Example SAML attributes + SAML 특성 예시 + + + + NameID attribute + NameID 특성 + + + + Warning: Provider is not assigned to an application as backchannel provider. + 경고: 공급자가 애플리케이션에 백채널 공급자로 할당되지 않았습니다. + + + + Update SCIM Provider + SCIM 공급자 업데이트 + + + + Run sync again + 다시 동기화 진행 + + + + Modern applications, APIs and Single-page applications. + 현대적인 애플리케이션, API 및 단일 페이지 애플리케이션. + + + + LDAP + LDAP + + + + Provide an LDAP interface for applications and users to authenticate against. + 응용 프로그램 및 사용자가 인증하기 위한 LDAP 인터페이스를 제공하세요. + + + + New application + 새 애플리케이션 + + + + Applications + 애플리케이션 + + + + Provider Type + 공급자 유형 + + + + Application(s) + 애플리케이션(목록) + + + + Application Icon + 애플리케이션 아이콘 + + + + Update Application + 애플리케이션 업데이트 + + + + Successfully sent test-request. + 테스트-요청을 성공적으로 보냈습니다. + + + + Log messages + 로그 메시지 + + + + No log messages. + 로그 메시지가 없습니다. + + + + Active + 활성 + + + + Last login + 마지막 로그인 + + + + Select users to add + 추가할 사용자 선택 + + + + Successfully updated group. + 그룹을 성공적으로 업데이트했습니다. + + + + Successfully created group. + 그룹을 성공적으로 생성했습니다. + + + + Is superuser + 슈퍼유저 + + + + Users added to this group will be superusers. + 이 그룹에 추가된 사용자는 슈퍼유저가 됩니다. + + + + Parent + 상위 + + + + Attributes + 특성(Attributes) + + + + Set custom attributes using YAML or JSON. + YAML 또는 JSON을 사용하여 사용자 정의 특성(Attributes)을 설정하세요. + + + + Successfully updated binding. + 바인딩을 성공적으로 업데이트했습니다. + + + + Successfully created binding. + 바인딩을 성공적으로 만들었습니다. + + + + Policy + 정책 + + + + Group mappings can only be checked if a user is already logged in when trying to access this source. + 그룹 매핑은 사용자가 이 소스에 액세스하려고 할 때 이미 로그인한 경우에만 확인할 수 있습니다. + + + + User mappings can only be checked if a user is already logged in when trying to access this source. + 사용자 매핑은 사용자가 이 소스에 액세스하려고 할 때 이미 로그인한 경우에만 확인할 수 있습니다. + + + + Enabled + 활성화됨 + + + + Negate result + 결과 무효화 + + + + Negates the outcome of the binding. Messages are unaffected. + 바인딩 결과를 무효화합니다. 메시지는 영향을 받지 않습니다. + + + + Order + 순서 + + + + Timeout + 타임아웃 + + + + Successfully updated policy. + 정책을 성공적으로 업데이트했습니다. + + + + Successfully created policy. + 정책을 성공적으로 만들었습니다. + + + + A policy used for testing. Always returns the same result as specified below after waiting a random duration. + 테스트에 사용되는 정책입니다. 임의의 시간을 기다린 후 항상 아래에 지정된 것과 동일한 결과를 반환합니다. + + + + Execution logging + 실행 로깅 + + + + When this option is enabled, all executions of this policy will be logged. By default, only execution errors are logged. + 이 옵션을 활성화하면 이 정책의 모든 실행이 기록됩니다. 기본적으로 실행 오류만 기록됩니다. + + + + Policy-specific settings + 정책별 설정 + + + + Pass policy? + 정책을 통과함? + + + + Wait (min) + 대기 (최소) + + + + The policy takes a random time to execute. This controls the minimum time it will take. + 정책은 실행하는 데 임의의 시간이 걸립니다. 이 값은 실행에 걸리는 최소 시간을 제어합니다. + + + + Wait (max) + 대기 (최대) + + + + Matches an event against a set of criteria. If any of the configured values match, the policy passes. + 기준 집합에 대해 이벤트를 일치시킵니다. 구성된 값 중 하나라도 일치하면 정책이 통과됩니다. + + + + Match created events with this action type. When left empty, all action types will be matched. + 생성된 이벤트를 이 작업 유형과 일치시킵니다. 비워두면 모든 액션 유형이 일치합니다. + + + + Matches Event's Client IP (strict matching, for network matching use an Expression Policy. + 이벤트의 클라이언트 IP를 일치시킵니다 (엄격 일치, 네트워크 일치의 경우 표현식 정책 사용. + + + + Match events created by selected application. When left empty, all applications are matched. + 선택한 애플리케이션에서 생성한 이벤트와 일치시킵니다. 비워두면 모든 애플리케이션이 일치합니다. + + + + Checks if the request's user's password has been changed in the last x days, and denys based on settings. + 요청 사용자의 비밀번호가 지난 x일 동안 변경되었는지 확인하고 설정에 따라 거부합니다. + + + + Maximum age (in days) + Maximum age (일) + + + + Only fail the policy, don't invalidate user's password + 정책만 실패하고 사용자의 비밀번호는 무효화하지 않습니다. + + + + Executes the python snippet to determine whether to allow or deny a request. + 파이썬 스니펫을 실행하여 요청을 허용할지 거부할지 결정합니다. + + + + Expression using Python. + 파이썬을 사용하는 정규표현식. + + + + See documentation for a list of all variables. + 모든 변수의 목록은 문서를 참조하세요. + + + + Static rules + 정적 규칙 + + + + Minimum length + 최소 길이 + + + + Minimum amount of Uppercase Characters + 대문자의 최소 개수 + + + + Minimum amount of Lowercase Characters + 소문자의 최소 개수 + + + + Minimum amount of Digits + 숫자의 최소 수 + + + + Minimum amount of Symbols Characters + 특수 문자의 최소 수 + + + + Error message + 오류 메시지 + + + + Symbol charset + 기호 문자셋 + + + + Characters which are considered as symbols. + 심볼로 간주되는 문자입니다. + + + + HaveIBeenPwned settings + HaveIBeenPwned 설정 + + + + Allowed count + 허용된 횟수 + + + + Allow up to N occurrences in the HIBP database. + HIBP 데이터베이스에서 최대 N개의 항목을 허용합니다. + + + + zxcvbn settings + zxcvbn 설정 + + + + Score threshold + 점수 기준점 + + + + If the password's score is less than or equal this value, the policy will fail. + 비밀번호의 점수가 이 값보다 작거나 같으면 정책이 실패합니다. + + + + Checks the value from the policy request against several rules, mostly used to ensure password strength. + 정책 요청의 값을 여러 규칙과 비교하여 확인하며, 주로 비밀번호 강도를 보장하는 데 사용됩니다. + + + + Password field + 비밀번호 입력란 + + + + Field key to check, field keys defined in Prompt stages are available. + 확인하려는 필드 키, 프롬프트 스테이지에서 정의된 필드 키를 사용할 수 있습니다. + + + + Check static rules + 정적 규칙 확인 + + + + Check haveibeenpwned.com + haveibeenpwned.com에서 확인 + + + + For more info see: + 자세한 내용은 다음을 참조하세요: + + + + Check zxcvbn + zxcvbn 확인 + + + + Password strength estimator created by Dropbox, see: + Dropbox에서 만든 비밀번호 강도 추정기를 참조하세요: + + + + Allows/denys requests based on the users and/or the IPs reputation. + 사용자 및/또는 IP 평판에 따라 요청을 허용/거부합니다. + + + + Invalid login attempts will decrease the score for the client's IP, and the +username they are attempting to login as, by one. + 잘못된 로그인 시도는 클라이언트의 IP와 로그인을 시도하는 사용자 아이디의 사용자명에 대한 점수가 하나씩 감소합니다. + + + The policy passes when the reputation score is below the threshold, and +doesn't pass when either or both of the selected options are equal or above the threshold. + 평판 점수가 임계값보다 낮으면 정책이 통과되며, 선택한 옵션이 모두 임계값보다 높으면 는 선택한 옵션 중 하나 또는 둘 다 임계값 이상이면 통과하지 않습니다. + + + Check IP + IP 확인 + + + + Check Username + 사용자명 확인 + + + + Threshold + 임계값 + + + + New policy + 새 정책 + + + + Create a new policy. + 새 정책을 만듭니다. + + + + Create Binding + 바인딩 생성 + + + + Superuser + 슈퍼유저 + + + + Members + 멤버 + + + + Select groups to add user to + 사용자를 추가할 그룹 선택 + + + + Warning: Adding the user to the selected group(s) will give them superuser permissions. + 경고: 선택한 그룹에 사용자를 추가하면 해당 사용자에게 슈퍼유저 권한이 부여됩니다. + + + + Successfully updated user. + 사용자 업데이트에 성공했습니다. + + + + Successfully created user. + 사용자를 성공적으로 만들었습니다. + + + + Username + 사용자명 + + + + User's primary identifier. 150 characters or fewer. + 사용자의 기본 식별자입니다. 150자 이하. + + + + User's display name. + 사용자의 표시 이름입니다. + + + + Email + 이메일 + + + + Is active + 활성화 + + + + Designates whether this user should be treated as active. Unselect this instead of deleting accounts. + 이 사용자를 활성 상태로 처리할지 여부를 지정합니다. 계정을 삭제하는 대신 이 옵션을 선택 취소합니다. + + + + Path + 경로 + + + + Policy / User / Group + 정책 / 사용자 / 그룹 + + + + Policy + 정책 + + + + Group + 그룹 + + + + User + 사용자 + + + + Edit Policy + 정책 편집 + + + + Update Group + 그룹 업데이트 + + + + Edit Group + 그룹 편집 + + + + Update User + 사용자 업데이트 + + + + Edit User + 사용자 편집 + + + + Policy binding(s) + 정책 바인딩 + + + + Update Binding + 바인딩 업데이트 + + + + Edit Binding + 바인딩 편집 + + + + No Policies bound. + 정책 바인딩 없습니다. + + + + No policies are currently bound to this object. + 현재 이 오브젝트에 바인딩된 정책이 없습니다. + + + + Bind existing policy + 기존 정책 바인딩 + + + + Warning: Application is not used by any Outpost. + 경고: 애플리케이션이 어떤 Outpost에서도 사용하지 않습니다. + + + + Related + 관련 + + + + Backchannel Providers + 백채널 공급자 + + + + Check access + 액세스 확인 + + + + Check + 확인 + + + + Check Application access + 애플리케이션 액세스 확인 + + + + Test + 테스트 + + + + Launch + 실행 + + + + Logins over the last week (per 8 hours) + 지난 한 주 동안의 로그인 횟수(8시간당) + + + + Policy / Group / User Bindings + 정책 / 그룹 / 사용자 바인딩 + + + + These policies control which users can access this application. + 이러한 정책은 이 애플리케이션에 액세스할 수 있는 사용자를 제어합니다. + + + + Successfully updated source. + 소스를 성공적으로 업데이트했습니다. + + + + Successfully created source. + 소스를 성공적으로 만들었습니다. + + + + Sync users + 사용자 동기화 + + + + User password writeback + 사용자 비밀번호 재설정 + + + + Login password is synced from LDAP into authentik automatically. Enable this option only to write password changes in authentik back to LDAP. + 로그인 비밀번호가 LDAP에서 인증으로 자동 동기화됩니다. 이 옵션은 인증에서 변경된 비밀번호를 LDAP에 다시 쓰려는 경우에만 사용하도록 설정합니다. + + + + Sync groups + 그룹 동기화 + + + + Connection settings + 연결 설정 + + + + Server URI + 서버 URI + + + + Specify multiple server URIs by separating them with a comma. + 여러 서버 URI를 쉼표로 구분하여 지정합니다. + + + + Enable StartTLS + StartTLS 사용 + + + + To use SSL instead, use 'ldaps://' and disable this option. + 대신 SSL을 사용하려면 'ldaps://'를 사용하고 이 옵션을 비활성화하세요. + + + + TLS Verification Certificate + TLS 인증 인증서 + + + + When connecting to an LDAP Server with TLS, certificates are not checked by default. Specify a keypair to validate the remote certificate. + TLS를 사용하여 LDAP 서버에 연결할 때 인증서는 기본적으로 확인되지 않습니다. 원격 인증서의 유효성을 검사할 키쌍을 지정합니다. + + + + Bind CN + CN 바인딩 + + + + LDAP Attribute mapping + LDAP 특성 매핑 + + + + Property mappings used to user creation. + 사용자 생성에 사용되는 특성 매핑입니다. + + + + Additional settings + 추가 설정 + + + + Parent group for all the groups imported from LDAP. + LDAP에서 가져온 모든 그룹의 상위 그룹입니다. + + + + User path + 사용자 경로 + + + + Addition User DN + 사용자 DN 추가 + + + + Additional user DN, prepended to the Base DN. + 기본 DN 앞에 추가 사용자 DN을 추가합니다. + + + + Addition Group DN + 추가 그룹 DN + + + + Additional group DN, prepended to the Base DN. + 기본 DN 앞에 추가 그룹 DN을 붙입니다. + + + + User object filter + 사용자 오브젝트 필터 + + + + Consider Objects matching this filter to be Users. + 이 필터와 일치하는 오브젝트를 사용자로 간주합니다. + + + + Group object filter + 그룹 오브젝트 필터 + + + + Consider Objects matching this filter to be Groups. + 이 필터와 일치하는 오브젝트를 그룹으로 간주합니다. + + + + Group membership field + 그룹 구성원 필드 + + + + Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...' + 그룹 구성원이 포함된 필드입니다. 'memberUid' 필드를 사용하는 경우 값에 상대적인 고유 이름이 포함된 것으로 가정합니다 (예:'memberUid=some-user' 대신 'memberUid=cn=some-user,ou=groups,...'). + + + + Object uniqueness field + 오브젝트 고유 필드 + + + + Field which contains a unique Identifier. + 고유 식별자가 포함된 필드입니다. + + + + Link users on unique identifier + 고유 식별자에 사용자 연결 + + + + Link to a user with identical email address. Can have security implications when a source doesn't validate email addresses + 이메일 주소가 동일한 사용자에게 링크합니다. 소스에서 이메일 주소의 유효성을 검사하지 않을 경우 보안에 영향을 미칠 수 있습니다 + + + + Use the user's email address, but deny enrollment when the email address already exists + 사용자의 이메일 주소를 사용하되, 이메일 주소가 이미 존재하는 경우 등록을 거부합니다. + + + + Link to a user with identical username. Can have security implications when a username is used with another source + 동일한 사용자 아이디를 가진 사용자와 연결합니다. 사용자 아이디가 다른 소스와 함께 사용될 경우 보안에 영향을 미칠 수 있습니다 + + + + Use the user's username, but deny enrollment when the username already exists + 사용자의 사용자 아이디를 사용하되, 사용자 아이디가 이미 존재하는 경우 등록을 거부합니다 + + + + Unknown user matching mode + 알 수 없는 사용자 매칭 모드 + + + + URL settings + URL 설정 + + + + Authorization URL + 인가(Authorization) URL + + + + URL the user is redirect to to consent the authorization. + 사용자가 승인을 위해 리디렉션되는 URL입니다. + + + + Access token URL + 액세스 토큰 URL + + + + URL used by authentik to retrieve tokens. + 토큰을 검색하기 위해 authentik에서 사용하는 URL입니다. + + + + Profile URL + 프로필 URL + + + + URL used by authentik to get user information. + 사용자 정보를 가져오기 위해 authentik에서 사용하는 URL입니다. + + + + Request token URL + 토큰 요청 URL + + + + URL used to request the initial token. This URL is only required for OAuth 1. + 초기 토큰을 요청하는 데 사용되는 URL입니다. 이 URL은 OAuth 1에서만 필요합니다. + + + + OIDC Well-known URL + OIDC Well-known URL + + + + OIDC well-known configuration URL. Can be used to automatically configure the URLs above. + OIDC의 well-known 구성 URL입니다. 위의 URL을 자동으로 구성하는 데 사용할 수 있습니다. + + + + OIDC JWKS URL + OIDC JWKS URL + + + + JSON Web Key URL. Keys from the URL will be used to validate JWTs from this source. + JSON 웹 키 URL입니다. URL의 키는 이 소스의 JWT를 검증하는 데 사용됩니다. + + + + OIDC JWKS + OIDC JWKS + + + + Raw JWKS data. + Raw JWKS 데이터. + + + + User matching mode + 사용자 매칭 모드 + + + + Delete currently set icon. + 현재 설정한 아이콘을 삭제합니다. + + + + Consumer key + 고객 키 + + + + Consumer secret + 고객 비밀 + + + + Additional scopes to be passed to the OAuth Provider, separated by space. To replace existing scopes, prefix with *. + OAuth 공급자에게 전달할 추가 범위를 공백으로 구분하여 입력합니다. 기존 범위를 바꾸려면 접두사 앞에 *를 붙입니다. + + + + Flow settings + 플로우 설정 + + + + Flow to use when authenticating existing users. + 기존 사용자를 인증할 때 사용할 플로우입니다. + + + + Enrollment flow + 등록 플로우 + + + + Flow to use when enrolling new users. + 새 사용자를 등록할 때 사용할 플로우입니다. + + + + Load servers + 서버 로드 + + + + Re-authenticate with plex + Plex로 다시 인증하기 + + + + Allow friends to authenticate via Plex, even if you don't share any servers + 친구들이 Plex를 통해 인증할 수 있도록 허용하십시오. 심지어 서버를 공유하지 않는 경우에도 해당됩니다. + + + + Allowed servers + Allowed servers + + + + Select which server a user has to be a member of to be allowed to authenticate. + 사용자가 인증을 허용받기 위해 구성원이어야 하는 서버를 선택합니다. + + + + SSO URL + SSO URL + + + + URL that the initial Login request is sent to. + 초기 로그인 요청을 전송할 URL입니다. + + + + SLO URL + SLO URL + + + + Optional URL if the IDP supports Single-Logout. + IDP가 단일 로그아웃을 지원하는 경우 선택적 URL입니다. + + + + Also known as Entity ID. Defaults the Metadata URL. + Entity ID라고도 합니다. 기본값은 Metadata URL입니다. + + + + Binding Type + 바인딩 유형 + + + + Redirect binding + 리디렉션 바인딩 + + + + Post-auto binding + Post-auto 바인딩 + + + + Post binding but the request is automatically sent and the user doesn't have to confirm. + 바인딩을 게시하지만 요청이 자동으로 전송되므로 사용자가 확인할 필요가 없습니다. + + + + Post binding + Post 바인딩 + + + + Signing keypair + 서명 키쌍 + + + + Keypair which is used to sign outgoing requests. Leave empty to disable signing. + 보내는 요청에 서명하는 데 사용되는 키쌍입니다. 서명을 사용하지 않으려면 비워 둡니다. + + + + Allow IDP-initiated logins + IDP-initiated login 허용 + + + + Allows authentication flows initiated by the IdP. This can be a security risk, as no validation of the request ID is done. + IdP에 의해 시작된 인증 플로우를 허용합니다. 요청 ID에 대한 유효성 검사가 수행되지 않으므로 보안 위험이 있을 수 있습니다. + + + + NameID Policy + NameID 정책 + + + + Persistent + Persistent(영구) + + + + Email address + 이메일 주소 + + + + Windows + Windows + + + + X509 Subject + X509 Subject + + + + Transient + Transient(임시) + + + + Delete temporary users after + 다음 이후 임시 사용자를 삭제합니다. + + + + 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. + 임시 사용자를 삭제해야 하는 시간 오프셋입니다. 이 설정은 IDP가 'Transient' NameID 형식을 사용하고 사용자가 수동으로 로그아웃하지 않는 경우에만 적용됩니다. + + + + Pre-authentication flow + 사전 인증 플로우 + + + + Flow used before authentication. + 사전 인증에 사용되는 플로우입니다. + + + + New source + 새 소스 + + + + Create a new source. + 새 소스를 만듭니다. + + + + Sources of identities, which can either be synced into authentik's database, or can be used by users to authenticate and enroll themselves. + 신원 소스는 인증 데이터베이스에 동기화하거나 사용자가 자신을 인증하고 등록하는 데 사용할 수 있습니다. + + + + Source(s) + 소스 + + + + Disabled + 비활성화됨 + + + + Built-in + Built-in + + + + Update LDAP Source + LDAP 소스 업데이트 + + + + Not synced yet. + 아직 동기화되지 않았습니다. + + + + Task finished with warnings + 작업 완료(경고) + + + + Task finished with errors + 작업 완료(오류) + + + + Last sync: + 에 마지막으로 동기화 됨 + + + + OAuth Source + OAuth 소스 + + + + Generic OpenID Connect + 일반 OpenID Connect + + + + Unknown provider type + Unknown provider type + + + + Details + 상세 + + + + Callback URL + 콜백 URL + + + + Access Key + 액세스 Key + + + + Update OAuth Source + OAuth 소스 업데이트 + + + + Diagram + Diagram + + + + Policy Bindings + 정책 바인딩 + + + + These bindings control which users can access this source. + You can only use policies here as access is checked before the user is authenticated. + 이러한 바인딩은 이 소스에 액세스할 수 있는 사용자를 제어합니다. + 사용자가 인증되기 전에 액세스가 확인되므로 여기에서만 정책을 사용할 수 있습니다. + + + + Update Plex Source + Plex 소스 업데이트 + + + + Update SAML Source + SAML 소스 업데이트 + + + + Successfully updated mapping. + 매핑을 성공적으로 업데이트했습니다. + + + + Successfully created mapping. + 매핑을 성공적으로 생성했습니다. + + + + Object field + 오브젝트 필드 + + + + Field of the user object this value is written to. + 이 값을 기록하는 사용자 객체의 필드입니다. + + + + SAML Attribute Name + SAML 특성 이름 + + + + Attribute name used for SAML Assertions. Can be a URN OID, a schema reference, or a any other string. If this property mapping is used for NameID Property, this field is discarded. + SAML 어설션에 사용되는 특성(Attribute) 이름입니다. URN OID, 스키마 참조 또는 기타 문자열일 수 있습니다. 이 속성(Property) 매핑이 NameID 속성(Property)에 사용되는 경우 이 필드는 삭제됩니다. + + + + Friendly Name + Friendly Name + + + + Optionally set the 'FriendlyName' value of the Assertion attribute. + 선택적으로 어설션 특성(Attribute)의 'FriendlyName' 값을 설정합니다. + + + + Scope name + 범위 이름 + + + + Scope which the client can specify to access these properties. + 클라이언트가 이러한 속성(Property)에 액세스하도록 지정할 수 있는 범위입니다. + + + + Description shown to the user when consenting. If left empty, the user won't be informed. + 동의 시 사용자에게 표시되는 설명입니다. 비워두면 사용자에게 알림이 표시되지 않습니다. + + + + Example context data + 컨텍스트 데이터 예시 + + + + Active Directory User + Active Directory 사용자 + + + + Active Directory Group + Active Directory 그룹 + + + + New property mapping + 새로운 속성(Property) 매핑 + + + + Create a new property mapping. + 새 속성(Property) 매핑을 만듭니다. + + + + Property Mappings + 속성 매핑 + + + + Control how authentik exposes and interprets information. + Authentik이 정보를 표시하고 해석하는 방법을 제어합니다. + + + + Property Mapping(s) + 속성(Property) 매핑 + + + + Test Property Mapping + 테스트 속성(Property) 매핑 + + + + Hide managed mappings + 관리되는 매핑 숨김 + + + + Successfully updated token. + 토큰을 성공적으로 업데이트했습니다. + + + + Successfully created token. + 토큰을 성공적으로 생성했습니다. + + + + Unique identifier the token is referenced by. + 토큰이 참조하는 고유 식별자입니다. + + + + Intent + 인텐트 + + + + API Token + API 토큰 + + + + Used to access the API programmatically + 프로그래밍 방식으로 API에 액세스하는 데 사용됩니다. + + + + App password. + 앱 비밀번호. + + + + Used to login using a flow executor + 플로우 실행기를 사용하여 로그인하는 데 사용 + + + + Expiring + 만료 예정 + + + + If this is selected, the token will expire. Upon expiration, the token will be rotated. + 이 옵션을 선택하면 토큰이 만료됩니다. 토큰이 만료되면 토큰이 회전됩니다. + + + + Expires on + 만료일 + + + + API Access + API 액세스 + + + + App password + 앱 비밀번호 + + + + Verification + Verification + + + + Unknown intent + intent를 알 수 없음 (Unknown intent) + + + + Tokens + 토큰 + + + + Tokens are used throughout authentik for Email validation stages, Recovery keys and API access. + 토큰은 이메일 유효성 검사 스테이지, 복구 키 및 API 액세스를 위해 인증 전반에 걸쳐 사용됩니다.. + + + + Expires? + 만료? + + + + Expiry date + 만료일 + + + + Token(s) + 토큰 + + + + Create Token + 토큰 생성 + + + + Token is managed by authentik. + 토큰은 Authentik에서 관리합니다. + + + + Update Token + 토큰 업데이트 + + + + Successfully updated tenant. + 테넌트를 성공적으로 업데이트했습니다. + + + + Successfully created tenant. + 테넌트를 성공적으로 만들었습니다. + + + + Domain + 도메인 + + + + Matching is done based on domain suffix, so if you enter domain.tld, foo.domain.tld will still match. + 도메인 접미사를 기준으로 매칭이 이루어지므로 도메인.tld를 입력해도 foo.domain.tld가 매칭됩니다. + + + + Default + 기본 + + + + Use this tenant for each domain that doesn't have a dedicated tenant. + 전용 테넌트가 없는 각 도메인에 이 테넌트를 사용하세요. + + + + Branding settings + 브랜딩 설정 + + + + Title + Title + + + + Branding shown in page title and several other places. + 페이지 제목 및 기타 여러 위치에 브랜딩이 표시됩니다. + + + + Logo + 로고 + + + + Icon shown in sidebar/header and flow executor. + 사이드바/헤더 및 플로우 실행기에 표시되는 아이콘입니다. + + + + Favicon + 파비콘 + + + + Icon shown in the browser tab. + 브라우저 탭에 표시되는 아이콘입니다. + + + + Default flows + 기본 플로우 + + + + Flow used to authenticate users. If left empty, the first applicable flow sorted by the slug is used. + 사용자 인증에 사용되는 플로우입니다. 비워두면 슬러그에 의해 정렬된 첫 번째 적용 가능한 플로우가 사용됩니다. + + + + Invalidation flow + 무효 처리 플로우 + + + + Flow used to logout. If left empty, the first applicable flow sorted by the slug is used. + 로그아웃에 사용되는 플로우입니다. 비워 두면 슬러그를 기준으로 정렬된 첫 번째 적용 가능한 플로우가 사용됩니다. + + + + Recovery flow + 복구 플로우 + + + + Recovery flow. If left empty, the first applicable flow sorted by the slug is used. + 복구 플로우. 비워두면 슬러그별로 정렬된 첫 번째 적용 가능한 플로우가 사용됩니다. + + + + Unenrollment flow + 등록 취소 플로우 + + + + If set, users are able to unenroll themselves using this flow. If no flow is set, option is not shown. + 설정된 경우 사용자는 이 플로우를 사용하여 등록을 취소할 수 있습니다. 플로우가 설정되어 있지 않으면 옵션이 표시되지 않습니다. + + + + User settings flow + 사용자 설정 플로우 + + + + If set, users are able to configure details of their profile. + 이 옵션을 설정하면 사용자가 프로필의 세부 정보를 구성할 수 있습니다. + + + + Device code flow + 디바이스 코드 플로우 + + + + If set, the OAuth Device Code profile can be used, and the selected flow will be used to enter the code. + 설정하면 OAuth 디바이스 코드 프로필을 사용할 수 있으며, 선택한 플로우를 사용하여 코드를 입력할 수 있습니다. + + + + Other global settings + 기타 전역 설정 + + + + Web Certificate + 웹 인증서 + + + + Event retention + 이력 보존 + + + + Duration after which events will be deleted from the database. + 이력이 데이터베이스에서 삭제되는 기간입니다. + + + + When using an external logging solution for archiving, this can be set to "minutes=5". + 아카이브에 외부 로깅 솔루션을 사용하는 경우, 이 값을 "minutes=5"로 설정할 수 있습니다. + + + + This setting only affects new Events, as the expiration is saved per-event. + T만료는 이벤트별로 저장되므로 설정은 새 이벤트에만 영향을 줍니다. + + + + Format: "weeks=3;days=2;hours=3,seconds=2". + 서식: "weeks=3;days=2;hours=3,seconds=2". + + + + Set custom attributes using YAML or JSON. Any attributes set here will be inherited by users, if the request is handled by this tenant. + YAML 또는 JSON을 사용하여 사용자 지정 속성을 설정합니다. 이 테넌트가 요청을 처리하는 경우 여기에서 설정한 모든 속성은 사용자가 상속받게 됩니다. + + + + Tenants + 테넌트 + + + + Configure visual settings and defaults for different domains. + 다양한 도메인에 대한 시각적 설정 및 기본값을 구성합니다. + + + + Default? + 기본값? + + + + Tenant(s) + 테넌트 + + + + Update Tenant + 테넌트 업데이트 + + + + Create Tenant + 테넌트 생성 + + + + Policies + 정책 + + + + Allow users to use Applications based on properties, enforce Password Criteria and selectively apply Stages. + 사용자가 속성(Property)을 기반으로 애플리케이션을 사용하고, 비밀번호 기준을 적용하고, 스테이지를 선택적으로 적용할 수 있도록 허용합니다. + + + + Assigned to object(s). + 에 오브젝트 할당. + + + + Warning: Policy is not assigned. + 경고: 정책이 할당되지 않았습니다. + + + + Test Policy + 정책 테스트 + + + + Policy / Policies + 정책 + + + + Successfully cleared policy cache + 정책 캐시를 성공적으로 지웠습니다. + + + + Failed to delete policy cache + 정책 캐시 삭제에 실패했습니다. + + + + Clear cache + 캐시 삭제 + + + + Clear Policy cache + 정책 캐시 삭제 + + + + Are you sure you want to clear the policy cache? This will cause all policies to be re-evaluated on their next usage. + 정책 캐시를 지우시겠습니까? 이렇게 하면 다음 사용 시 모든 정책이 재평가됩니다. + + + Reputation scores + 평판 점수 + + + + Reputation for IP and user identifiers. Scores are decreased for each failed login and increased for each successful login. + IP 및 사용자 식별자에 대한 평판. 로그인에 실패할 때마다 점수가 감소하고 로그인에 성공할 때마다 점수가 증가합니다. + + + + IP + IP + + + + Score + 점수 + + + + Updated + 업데이트됨 + + + + Reputation + 평판 + + + + Groups + 그룹 + + + + Group users together and give them permissions based on the membership. + 사용자를 그룹화하고 멤버십에 따라 권한을 부여합니다. + + + + Superuser privileges? + 슈퍼유저 권한? + + + + Group(s) + 그룹 + + + + Create Group + 그룹 생성 + + + + Create group + 그룹 생성 + + + + Enabling this toggle will create a group named after the user, with the user as member. + 이 토글을 활성화하면 사용자의 이름을 딴 그룹이 생성되며, 이 그룹에는 해당 사용자가 멤버로 포함됩니다. + + + + Use the username and password below to authenticate. The password can be retrieved later on the Tokens page. + 아래의 사용자 아이디와 비밀번호를 사용하여 인증합니다. 비밀번호는 나중에 토큰 페이지에서 검색할 수 있습니다. + + + + Password + 비밀번호 + + + + Valid for 360 days, after which the password will automatically rotate. You can copy the password from the Token List. + 360일 동안 유효하며, 그 이후에는 비밀번호가 자동으로 회전합니다. 토큰 목록에서 비밀번호를 복사할 수 있습니다. + + + + The following objects use + 다음 오브젝트는 을(를) 사용합니다. + + + + connecting object will be deleted + 연결 오브젝트가 삭제됩니다. + + + + Successfully updated + 을(를) 성공적으로 업데이트했습니다 + + + Failed to update : + : 을(를) 업데이트하는데 실패했습니다 + + + + Are you sure you want to update ""? + 정말 "" 을(를) 업데이트 하시겠습니까? + + + + Successfully updated password. + 비밀번호를 성공적으로 업데이트했습니다. + + + + Successfully sent email. + 이메일을 성공적으로 전송했습니다. + + + + Email stage + 이메일 스테이지 + + + + Successfully added user(s). + 사용자를 성공적으로 추가했습니다. + + + + Users to add + 추가할 사용자를 선택 + + + + User(s) + 사용자 + + + + Remove Users(s) + 사용자 제거 + + + + Are you sure you want to remove the selected users from the group ? + 그룹 에서 +선택한 사용자를 제거하시겠습니까? + + + + Remove + 제거 + + + + Impersonate + 사용자 위장 + + + + User status + 사용자 상태 + + + + Change status + 상태 변경 + + + + Deactivate + 비활성화 + + + + Update password + 비밀번호 업데이트 + + + + Set password + 비밀번호 설정 + + + + Successfully generated recovery link + 복구 링크가 성공적으로 생성되었습니다 + + + + No recovery flow is configured. + 복구 플로우가 구성되지 않았습니다. + + + + Copy recovery link + 복구 링크 복사 + + + + Send link + 링크 전송 + + + + Send recovery link to user + 사용자에게 복구 링크 보내기 + + + + Email recovery link + 이메일 복구 링크 + + + + Recovery link cannot be emailed, user has no email address saved. + 복구 링크를 이메일로 보낼 수 없습니다. 사용자가 저장한 이메일 주소가 없습니다. + + + + To let a user directly reset a their password, configure a recovery flow on the currently active tenant. + 사용자가 직접 비밀번호를 재설정할 수 있도록 하려면 현재 활성 상태인 테넌트에서 복구 플로우를 구성하세요. + + + + Add User + 사용자 추가 + + + + Warning: This group is configured with superuser access. Added users will have superuser access. + 경고: 이 그룹은 수퍼유저 액세스 권한으로 구성됩니다. 추가된 사용자는 수퍼유저 액세스 권한을 갖게 됩니다. + + + + Add existing user + 기존 사용자 추가 + + + + Create user + 사용자 생성 + + + + Create User + 사용자 생성 + + + + Create Service account + 서비스 계정 생성 + + + + Hide service-accounts + 서비스 계정 숨김 + + + + Group Info + 그룹 정보 + + + + Notes + 참고 + + + + Edit the notes attribute of this group to add notes here. + 이 그룹의 노트 속성(Attribute)을 수정하여 여기에 노트를 추가합니다. + + + + Users + 사용자 + + + + Root + Root + + + + Warning: You're about to delete the user you're logged in as (). Proceed at your own risk. + 경고: 현재 로그인한() 사용자를 삭제하려고 합니다. 본인 책임하에 진행하세요. + + + + Hide deactivated user + 비활성화된 사용자 숨기기 + + + + User folders + 사용자 폴더 + + + + Successfully added user to group(s). + 그룹에 사용자를 성공적으로 추가했습니다. + + + + Groups to add + 추가할 그룹 + + + + Remove from Group(s) + 그룹에서 삭제 + + + + Are you sure you want to remove user from the following groups? + 정말 이 사용자를 다음의 그룹에서 삭제하시겠습니까? + + + + Add Group + 그룹 추가 + + + + Add to existing group + 기존 그룹에 추가 + + + + Add new group + 새 그룹 추가 + + + + Application authorizations + 애플리케이션 인가 + + + + Revoked? + 취소되었나요? + + + + Expires + 만료 + + + + ID Token + ID 토큰 + + + + Refresh Tokens(s) + 토큰 갱신 + + + + Last IP + 마지막 IP + + + + Session(s) + 세션 + + + + Expiry + 만료 + + + + (Current session) + (현재 세션) + + + + Permissions + 권한 + + + + Consent(s) + 동의 사항 + + + + Successfully updated device. + 디바이스를 성공적으로 업데이트했습니다. + + + + Static tokens + 정적 토큰 + + + + TOTP Device + TOTP 디바이스 + + + + Enroll + 등록 + + + + Device(s) + 디바이스 + + + + Update Device + 디바이스 업데이트 + + + + Confirmed + 확인됨 + + + + User Info + 사용자 정보 + + + + Actions over the last week (per 8 hours) + 지난 한 주 동안의 액션 (8시간당) + + + + Edit the notes attribute of this user to add notes here. + 이 사용자의 참고 특성(Attribute)을 편집하여 여기에 참고를 추가합니다. + + + + Sessions + 세션 + + + + User events + 사용자 이력 + + + + Explicit Consent + 명시적 동의 + + + + OAuth Refresh Tokens + OAuth 토큰 갱신 + + + + MFA Authenticators + MFA 인증기 + + + + Successfully updated invitation. + 초대를 성공적으로 업데이트했습니다. + + + + Successfully created invitation. + 초대를 성공적으로 생성했습니다. + + + + Flow + 플로우 + + + + When selected, the invite will only be usable with the flow. By default the invite is accepted on all flows with invitation stages. + 이 옵션을 선택하면 해당 플로우에서만 초대를 사용할 수 있습니다. 기본적으로 초대는 초대 스테이지가 있는 모든 플로우에서 수락됩니다. + + + + Optional data which is loaded into the flow's 'prompt_data' context variable. YAML or JSON. + 플로우의 'prompt_data' 컨텍스트 변수에 로드되는 선택적 데이터입니다. YAML 또는 JSON입니다. + + + + Single use + 일회용 + + + + When enabled, the invitation will be deleted after usage. + 활성화하면, 한 번만 사용할 수 있습니다. + + + + Select an enrollment flow + 등록 플로우 선택 + + + + Link to use the invitation. + 초대를 사용할 링크를 클릭합니다. + + + + Invitations + 초대 + + + + Create Invitation Links to enroll Users, and optionally force specific attributes of their account. + 초대 링크를 생성하여 사용자를 등록하고 선택적으로 계정의 특정 특성(Attribute)을 강제로 적용합니다. + + + + Created by + 작성자 + + + + Invitation(s) + 초대 + + + + Invitation not limited to any flow, and can be used with any enrollment flow. + 초대는 특정 플로우에 국한되지 않으며 모든 등록 플로우에서 사용할 수 있습니다. + + + + Update Invitation + 초대 업데이트 + + + + Create Invitation + 초대 생성 + + + + Warning: No invitation stage is bound to any flow. Invitations will not work as expected. + 경고: 초대 스테이지는 어떤 플로우에도 바인딩 되어있지 않습니다. 초대가 예상대로 작동하지 않을 수 있습니다. + + + + Auto-detect (based on your browser) + 자동-감지 (브라우저 기반) + + + + Required. + 필수. + + + + Continue + 계속 + + + + Successfully updated prompt. + 프롬프트가 성공적으로 업데이트되었습니다. + + + + Successfully created prompt. + 프롬프트가 성공적으로 생성되었습니다. + + + + Text: Simple Text input + 텍스트: 간단한 텍스트 입력 + + + + Text Area: Multiline text input + 텍스트 영역: 여러 줄 텍스트 입력 + + + + Text (read-only): Simple Text input, but cannot be edited. + 텍스트(읽기 전용): 간단한 텍스트 입력이 가능하지만 편집은 불가능합니다. + + + + Text Area (read-only): Multiline text input, but cannot be edited. + 텍스트 영역(읽기 전용): 여러 줄 텍스트 입력이 가능하지만 편집은 불가능합니다. + + + + Username: Same as Text input, but checks for and prevents duplicate usernames. + 사용자이름: 텍스트 입력과 동일하지만 중복된 사용자이름을 확인하고 방지합니다. + + + + Email: Text field with Email type. + 이메일: 이메일 유형이 있는 텍스트 필드입니다. + + + + Password: Masked input, multiple inputs of this type on the same prompt need to be identical. + 비밀번호: 마스킹 입력, 동일한 프롬프트에서 이 유형의 입력을 여러 번 입력할 경우 동일해야 합니다. + + + + Number + 번호 + + + + Checkbox + 체크박스 + + + + Radio Button Group (fixed choice) + 라디오 버튼 그룹(고정 선택 항목) + + + + Dropdown (fixed choice) + 드롭다운(고정 선택 항목) + + + + Date + 날짜 + + + + Date Time + 날짜 및 시간 + + + + File + 파일 + + + + Separator: Static Separator Line + 구분자: 정적 구분선 + + + + Hidden: Hidden field, can be used to insert data into form. + 숨김: 숨겨진 필드로, 양식에 데이터를 삽입하는 데 사용할 수 있습니다. + + + + Static: Static value, displayed as-is. + Static: 정적 값, 있는 그대로 표시됩니다. + + + + authentik: Locale: Displays a list of locales authentik supports. + authentik: 로캘: authentik이 지원하는 로캘 목록을 표시합니다. + + + + Preview errors + 미리보기 오류 + + + + Data preview + 데이터 미리보기 + + + + Unique name of this field, used for selecting fields in prompt stages. + 프롬프트 스테이지에서 필드를 선택하는 데 사용되는 스테이지의 고유 이름입니다. + + + + Field Key + 필드 키 + + + + Name of the form field, also used to store the value. + 값을 저장하는 데 사용되는 양식 필드의 이름입니다. + + + + When used in conjunction with a User Write stage, use attributes.foo to write attributes. + 사용자 작성 스테이지와 함께 사용하는 경우 attributes.foo를 사용하여 속성을 작성합니다. + + + + Label + 라벨 + + + + Label shown next to/above the prompt. + 프롬프트 옆/위에 표시되는 라벨입니다. + + + + Required + 필수 + + + + Interpret placeholder as expression + 자리 표시자를 정규표현식으로 해석 + + + + When checked, the placeholder will be evaluated in the same way a property mapping is. + If the evaluation fails, the placeholder itself is returned. + 이 옵션을 선택하면 특성(Attribute) 매핑과 동일한 방식으로 플레이스홀더가 평가됩니다. + 평가에 실패하면 플레이스홀더 자체가 반환됩니다. + + + Placeholder + Placeholder + + + + 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. + 옵션으로 예상되는 입력값을 설명하는 짧은 힌트를 제공할 수 있습니다. + 고정 선택 필드를 만들 때, 식으로 해석을 활성화하고 여러 선택지를 반환하려면 + 목록을 반환하세요. + + + Interpret initial value as expression + 초기값을 정규표현식으로 해석 + + + + When checked, the initial value will be evaluated in the same way a property mapping is. + If the evaluation fails, the initial value itself is returned. + 선택한 경우, 초기 값은 속성 매핑과 동일한 방식으로 평가됩니다. + 평가가 실패하면 초기 값 자체가 반환됩니다. + + + Initial value + Initial value + + + + 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. + 옵션으로 입력을 초기 값으로 채울 수 있습니다. + 고정 선택 필드를 만들 때 정규표현식으로 해석을 활성화하고 + 목록을 반환하여 여러 개의 기본 선택지를 반환합니다. + + + Help text + 도움말 텍스트 + + + + Any HTML can be used. + 어떤 HTML이든 사용할 수 있습니다. + + + + Prompts + 프롬프트 + + + + Single Prompts that can be used for Prompt Stages. + 프롬프트 스테이지에 사용할 수 있는 단일 프롬프트입니다. + + + + Field + 필드 + + + + Stages + 스테이지 + + + + Prompt(s) + 프롬프트 + + + + Update Prompt + 프롬프트 업데이트 + + + + Create Prompt + 프롬프트 생성 + + + + Target + 타겟 + + + + Stage + 스테이지 + + + + Evaluate when flow is planned + 플로우가 계획될 때 평가 + + + + Evaluate policies during the Flow planning process. + 플로우 계획 프로세스 중에 정책을 평가합니다. + + + + Evaluate when stage is run + 스테이지가 실행될 때 평가 + + + + Evaluate policies before the Stage is present to the user. + 스테이지가 사용자에게 표시되기 전에 정책을 평가합니다. + + + + Invalid response behavior + 유효하지 않은 응답 동작 + + + + Returns the error message and a similar challenge to the executor + 실행자에게 오류 메시지와 유사한 챌린지를 반환합니다 + + + + Restarts the flow from the beginning + 플로우를 처음부터 다시 시작 + + + + Restarts the flow from the beginning, while keeping the flow context + 플로우 컨텍스트를 유지하면서 플로우를 처음부터 다시 시작합니다 + + + + Configure how the flow executor should handle an invalid response to a challenge given by this bound stage. + 이 바운드 스테이지에서 제공된 챌린지에 대한 유효하지 않은 응답을 플로우 실행기가 어떻게 처리해야 하는지 구성합니다. + + + + Successfully updated stage. + 스테이지를 성공적으로 업데이트했습니다. + + + + Successfully created stage. + 스테이지를 성공적으로 생성했습니다. + + + + Stage used to configure a duo-based authenticator. This stage should be used for configuration flows. + duo-based 인증기를 구성하는 데 사용되는 스테이지입니다. 이 스테이지는 구성 플로우에 사용해야 합니다. + + + + Authenticator type name + 인증기 유형 이름 + + + + Display name of this authenticator, used by users when they enroll an authenticator. + 사용자가 인증기를 등록할 때 사용하는 이 인증가의 표시 이름입니다. + + + + API Hostname + API 호스트명 + + + + Duo Auth API + Duo Auth API + + + + Integration key + 연동 키 + + + + Secret key + 비밀 키 + + + + Duo Admin API (optional) + Duo 관리자 API (선택사항) + + + + When using a Duo MFA, Access or Beyond plan, an Admin API application can be created. + This will allow authentik to import devices automatically. + Duo MFA, Access 또는 Beyond 요금제를 사용하는 경우 관리자 API 애플리케이션을 만들 수 있습니다. + 인증을 통해 디바이스를 자동으로 가져올 수 있습니다. + + + Stage-specific settings + 스테이지별 설정 + + + + Configuration flow + 플로우 구성 + + + + Flow used by an authenticated user to configure this Stage. If empty, user will not be able to configure this stage. + 인증된 사용자가 이 스테이지를 구성하는 데 사용하는 플로우입니다. 비어 있으면 사용자가 이 스테이지를 구성할 수 없습니다. + + + + Twilio Account SID + Twilio 계정 SID + + + + Get this value from https://console.twilio.com + 이 값은 다음에서 가져오세요. https://console.twilio.com + + + + Twilio Auth Token + Twilio Auth 토큰 + + + + Authentication Type + 인증 유형 + + + + Basic Auth + Basic Auth + + + + Bearer Token + Bearer 토큰 + + + + External API URL + 외부 API URL + + + + This is the full endpoint to send POST requests to. + POST 요청을 전송할 전체 엔드포인트입니다. + + + + API Auth Username + API 인증 사용자명 + + + + This is the username to be used with basic auth or the token when used with bearer token + Basic 인증에 사용할 사용자명 또는 무기명 토큰과 함께 사용할 경우 토큰입니다. + + + + API Auth password + API Auth 비밀번호 + + + + This is the password to be used with basic auth + Basic auth에 사용할 비밀번호입니다. + + + + Mapping + 매핑 + + + + Modify the payload sent to the custom provider. + 사용자 지정 공급자로 전송되는 페이로드를 수정합니다. + + + + Stage used to configure an SMS-based TOTP authenticator. + SMS 기반 TOTP 인증기를 구성하는 데 사용되는 스테이지입니다. + + + + Twilio + Twilio + + + + Generic + 일반 + + + + From number + 발신 번호 + + + + Number the SMS will be sent from. + SMS를 발송할 번호입니다. + + + + Hash phone number + 해시한 전화 번호 + + + + If enabled, only a hash of the phone number will be saved. This can be done for data-protection reasons. Devices created from a stage with this enabled cannot be used with the authenticator validation stage. + 활성화하면 휴대폰 번호의 해시만 저장됩니다. 이는 데이터 보호를 위해 설정할 수 있습니다. 이 옵션을 활성화한 단계에서 만든 디바이스는 인증기 유효성 검사 스테이지에서 사용할 수 없습니다. + + + + Stage used to configure a static authenticator (i.e. static tokens). This stage should be used for configuration flows. + 정적 인증자(즉, 정적 토큰)를 구성하는 데 사용되는 스테이지입니다. 이 단계는 구성 플로우에 사용해야 합니다. + + + + Token count + 토큰 수 + + + + Stage used to configure a TOTP authenticator (i.e. Authy/Google Authenticator). + TOTP 인증자를 구성하는 데 사용되는 단계(예: Authy/Google 인증자)입니다. + + + + Digits + PIN(Digits) + + + + 6 digits, widely compatible + 6자리 숫자, 폭넓은 호환성 + + + + 8 digits, not compatible with apps like Google Authenticator + 8자리 숫자, Google 인증기와 같은 앱과 호환되지 않음 + + + + Stage used to validate any authenticator. This stage should be used during authentication or authorization flows. + 인증기의 유효성을 검사하는 데 사용되는 스테이지입니다. 이 스테이지는 인증 또는 인가 플로우 중에 사용해야 합니다. + + + + Device classes + 디바이스 클래스 + + + + Static Tokens + 정적 토큰 + + + + TOTP Authenticators + TOTP 인증기 + + + + WebAuthn Authenticators + WebAuthn 인증기 + + + + Duo Authenticators + Duo 인증기 + + + + SMS-based Authenticators + SMS-기반 인증기 + + + + Device classes which can be used to authenticate. + 인증에 사용할 수 있는 디바이스 클래스입니다. + + + + Last validation threshold + 최종 유효성 검사 임계치 + + + + If any of the devices user of the types selected above have been used within this duration, this stage will be skipped. + 위에서 선택한 유형의 사용자가 이 기간 내에 사용한 적이 있는 디바이스가 있으면 이 단계는 건너뛰게 됩니다. + + + + Not configured action + 구성되지 않은 액션 + + + + Force the user to configure an authenticator + 사용자가 인증기를 구성하도록 강제 + + + + Deny the user access + 사용자 액세스 거부 + + + + WebAuthn User verification + WebAuthn 사용자 확인 + + + + User verification must occur. + 사용자 확인이 이루어져야 합니다. + + + + User verification is preferred if available, but not required. + 가능한 경우 사용자 확인이 선호되지만 필수는 아닙니다. + + + + User verification should not occur. + 사용자 확인이 이루어지지 않아야 합니다. + + + + Configuration stages + 스테이지 구성 + + + + Stages used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again. + 사용자에게 호환되는 디바이스가 없는 경우 인증기를 구성하는 데 사용되는 단계입니다. 이 구성 스테이지를 통과한 후에는 사용자에게 다시 메시지가 표시되지 않습니다. + + + + When multiple stages are selected, the user can choose which one they want to enroll. + 여러 스테이지를 선택한 경우 사용자는 등록할 스테이지를 선택할 수 있습니다. + + + + User verification + 사용자 확인 + + + + Resident key requirement + Resident Key 요구 사항 + + + + Authenticator Attachment + 인증기 첨부 + + + + No preference is sent + 기본 설정이 전송되지 않음 + + + + A non-removable authenticator, like TouchID or Windows Hello + TouchID 또는 Windows Hello와 같은 제거할 수 없는 인증기 + + + + A "roaming" authenticator, like a YubiKey + YubiKey 같은 "로밍" 인증기 + + + + This stage checks the user's current session against the Google reCaptcha (or compatible) service. + 이 스테이지에서는 사용자의 현재 세션을 Google 리캡차(또는 호환) 서비스와 비교하여 확인합니다. + + + + Public Key + 공개 키 + + + + Public key, acquired from https://www.google.com/recaptcha/intro/v3.html. + 공개 키는, https://www.google.com/recaptcha/intro/v3.html 에서 얻을 수 있습니다. + + + + Private Key + 개인 키 + + + + Private key, acquired from https://www.google.com/recaptcha/intro/v3.html. + 개인 키는, https://www.google.com/recaptcha/intro/v3.html 에서 얻을 수 있습니다. + + + + Advanced settings + 고급 설정 + + + + JS URL + JS URL + + + + URL to fetch JavaScript from, defaults to recaptcha. Can be replaced with any compatible alternative. + JavaScript를 가져올 URL입니다. 기본값은 recaptcha입니다. 호환되는 대체 사이트로 교체할 수 있습니다. + + + + API URL + API URL + + + + URL used to validate captcha response, defaults to recaptcha. Can be replaced with any compatible alternative. + 보안 문자 응답의 유효성을 검사하는 데 사용되는 URL로, 기본값은 리캡차입니다. 호환 가능한 다른 것으로 대체할 수 있습니다. + + + + Prompt for the user's consent. The consent can either be permanent or expire in a defined amount of time. + 사용자의 동의를 요청하는 프롬포트를 표시합니다. 동의는 영구적이거나 정해진 시간 후에 만료할 수 있습니다. + + + + Always require consent + 항상 동의 요청 + + + + Consent given last indefinitely + 비한정으로 동의 지속 + + + + Consent expires. + 동의가 만료됩니다. + + + + Consent expires in + 동의가 만료되는 시점 + + + + Offset after which consent expires. + 동의가 만료되는 오프셋입니다. + + + + Dummy stage used for testing. Shows a simple continue button and always passes. + 테스트에 사용되는 더미 스테이지입니다. 간단한 계속 버튼을 표시하고 항상 통과합니다. + + + + Throw error? + 오류 발생? + + + + SMTP Host + SMTP 호스트 + + + + SMTP Port + SMTP 포트 + + + + SMTP Username + SMTP 사용자명 + + + + SMTP Password + SMTP 비밀번호 + + + + Use TLS + TLS 사용 + + + + Use SSL + SSL 사용 + + + + From address + 발신자 주소 + + + + Verify the user's email address by sending them a one-time-link. Can also be used for recovery to verify the user's authenticity. + 일회성 링크를 전송하여 사용자의 이메일 주소를 확인합니다. 사용자의 진위 여부를 확인하기 위한 복구용으로도 사용할 수 있습니다. + + + + Activate pending user on success + 성공 시 보류 중인 사용자 활성화 + + + + When a user returns from the email successfully, their account will be activated. + 사용자가 이메일에서 성공적으로 돌아오면 계정이 활성화됩니다. + + + + Use global settings + 전역 설정 사용 + + + + When enabled, global Email connection settings will be used and connection settings below will be ignored. + 활성화하면, 전역 이메일 연결 설정이 사용되며 아래의 연결 설정은 무시됩니다. + + + + Token expiry + 토큰 유효기간 + + + + Time in minutes the token sent is valid. + 전송한 토큰이 유효한 시간입니다. + + + + Template + 템플릿 + + + + Let the user identify themselves with their username or Email address. + 사용자가 사용자 아이디 또는 이메일 주소로 신원을 확인할 수 있도록 합니다. + + + + User fields + 사용자 필드 + + + + UPN + UPN + + + + Fields a user can identify themselves with. If no fields are selected, the user will only be able to use sources. + 사용자가 자신을 식별할 수 있는 필드입니다. 필드를 선택하지 않으면 사용자는 소스만 사용할 수 있습니다. + + + + Password stage + 비밀번호 스테이지 + + + + When selected, a password field is shown on the same page instead of a separate page. This prevents username enumeration attacks. + 이 옵션을 선택하면 비밀번호 필드가 별도의 페이지가 아닌 같은 페이지에 표시됩니다. 이렇게 하면 사용자명 무차별 입력 공격을 방지할 수 있습니다. + + + + Case insensitive matching + 대소문자를 구분하지 않는 매칭 + + + + When enabled, user fields are matched regardless of their casing. + 활성화하면 사용자 필드를 대소문자에 관계없이 매치합니다. + + + + Show matched user + 일치하는 사용자 표시 + + + + 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. + 유효한 사용자 아이디/이메일을 입력하고 이 옵션을 활성화하면 사용자의 사용자 아이디와 아바타가 표시됩니다. 그렇지 않으면 사용자가 입력한 텍스트가 표시됩니다. + + + + Source settings + 소스 설정 + + + + Sources + 소스 + + + + Select sources should be shown for users to authenticate with. This only affects web-based sources, not LDAP. + 사용자가 인증할 소스를 선택할 수 있도록 표시해야 합니다. 이는 웹 기반 소스에만 영향을 미치며 LDAP에는 영향을 미치지 않습니다. + + + + Show sources' labels + 소스의 라벨 표시 + + + + By default, only icons are shown for sources. Enable this to show their full names. + 기본적으로 소스에는, 아이콘만 표시됩니다. 전체 이름을 표시하려면 이 옵션을 활성화합니다. + + + + Passwordless flow + 비밀번호 없는 플로우 + + + + Optional passwordless flow, which is linked at the bottom of the page. When configured, users can use this flow to authenticate with a WebAuthn authenticator, without entering any details. + 페이지 하단에 링크된 선택적 비밀번호 없는 플로우입니다. 이 플로우를 구성하면 사용자는 세부 정보를 입력하지 않고도 이 플로우를 사용하여 WebAuthn 인증기로 인증할 수 있습니다. + + + + Optional enrollment flow, which is linked at the bottom of the page. + 페이지 하단에 링크된, 선택적 등록 플로우를 참조하세요. + + + + Optional recovery flow, which is linked at the bottom of the page. + 페이지 하단에 링크된, 선택적 복구 플로우를 참조하세요. + + + + This stage can be included in enrollment flows to accept invitations. + 이 스테이지는 등록 플로우에 포함시켜 초대를 수락할 수 있습니다. + + + + Continue flow without invitation + 초대 없이 플로우 계속 + + + + 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. + 이 플래그를 설정하면 초대를 받지 못했을 때 이 스테이지가 다음 스테이지로 이동합니다. 기본적으로 이 스테이지는 초대를 받지 못하면 플로우를 취소합니다. + + + + Validate the user's password against the selected backend(s). + 선택한 백엔드에 대해 사용자의 비밀번호를 검증합니다. + + + + Backends + 백엔드 + + + + User database + standard password + 사용자 데이터베이스 + 표준 비밀번호 + + + + User database + app passwords + 사용자 데이터베이스 + 앱 비밀번호 + + + + User database + LDAP password + 사용자 데이터베이스 + LDAP 비밀번호 + + + + Selection of backends to test the password against. + 비밀번호를 테스트할 백엔드를 선택합니다. + + + + Flow used by an authenticated user to configure their password. If empty, user will not be able to configure change their password. + 인증된 사용자가 비밀번호를 구성하는 데 사용하는 플로우입니다. 비어 있으면 사용자가 비밀번호 변경을 구성할 수 없습니다. + + + + Failed attempts before cancel + 취소 전 실패 시도 횟수 + + + + 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. + 플로우가 취소되기 전에 사용자가 시도할 수 있는 횟수입니다. 사용자를 잠그려면 평판 정책과 user_write 단계를 사용하세요. + + + + Show arbitrary input fields to the user, for example during enrollment. Data is saved in the flow context under the 'prompt_data' variable. + 예를 들어, 등록 시 사용자에게 임의의 입력 필드를 표시합니다. 데이터는 플로우 컨텍스트의 'prompt_data' 변수 아래에 저장됩니다. + + + + Fields + 필드 + + + + ("", of type ) + ("", of type ) + + + + Validation Policies + 유효성 검사 정책 + + + + Selected policies are executed when the stage is submitted to validate the data. + 데이터 유효성 검사를 위해 스테이지가 제출될 때 선택한 정책이 실행됩니다. + + + + Delete the currently pending user. CAUTION, this stage does not ask for confirmation. Use a consent stage to ensure the user is aware of their actions. + 현재 대기 중인 사용자를 삭제합니다. 주의: 이 단계에서는 확인을 요청하지 않습니다. 동의 단계를 사용하여 사용자가 자신의 행동에 대해 인지하도록 하세요. + + + Log the currently pending user in. + 현재 대기 중인 사용자를 로그인합니다. + + + + Session duration + 세션 지속시간 + + + + Determines how long a session lasts. Default of 0 seconds means that the sessions lasts until the browser is closed. + 세션이 지속되는 시간을 결정합니다. 기본값인 0초는 브라우저가 닫힐 때까지 세션이 지속된다는 의미입니다. + + + + Different browsers handle session cookies differently, and might not remove them even when the browser is closed. + 브라우저마다 세션 쿠키를 처리하는 방식이 다르기 때문에 브라우저를 닫아도 세션 쿠키가 제거되지 않을 수 있습니다. + + + + See here. + 여기를 참조하세요. + + + + Stay signed in offset + 로그인 상태 유지 오프셋 + + + + If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here. + 기간을 0 이상으로 설정하면, 사용자에게 '로그인 상태 유지'를 선택할 수 있는 옵션이 제공되며, 이 경우 세션이 여기에 지정된 시간만큼 연장됩니다. + + + + Terminate other sessions + 다른 세션 종료 + + + + When enabled, all previous sessions of the user will be terminated. + 활성화하면 사용자의 이전 세션이 모두 종료됩니다. + + + + Remove the user from the current session. + 현재 세션에서 사용자를 제거합니다. + + + + Write any data from the flow's context's 'prompt_data' to the currently pending user. If no user + is pending, a new user is created, and data is written to them. + 플로우 컨텍스트의 'prompt_data'에 있는 데이터를 현재 대기 중인 사용자에게 씁니다. 대기 중인 사용자가 + 없는 경우 새 사용자가 생성되고 해당 사용자에게 데이터가 기록됩니다. + + + Never create users + 사용자 생성 안 함 + + + + When no user is present in the flow context, the stage will fail. + 플로우 컨텍스트에 사용자가 없으면 스테이지가 실패합니다. + + + + Create users when required + 필요할 때 사용자 생성 + + + + When no user is present in the the flow context, a new user is created. + 플로우 컨텍스트에 사용자가 없는 경우 새 사용자를 생성합니다. + + + + Always create new users + 항상 새 사용자 생성 + + + + Create a new user even if a user is in the flow context. + 사용자가 플로우 컨텍스트에 있는 경우에도 새 사용자를 만듭니다. + + + + Create users as inactive + 사용자를 비활성 상태로 생성 + + + + Mark newly created users as inactive. + 새로 생성된 사용자를 비활성으로 표시합니다. + + + + User path template + 사용자 경로 템플릿 + + + + Path new users will be created under. If left blank, the default path will be used. + 새 사용자가 생성될 경로입니다. 비워 두면 기본 경로가 사용됩니다. + + + + Newly created users are added to this group, if a group is selected. + 그룹이 선택되어 있으면 새로 생성된 사용자가 이 그룹에 추가됩니다. + + + + New stage + 새 스테이지 + + + + Create a new stage. + 새 스테이지를 만듭니다. + + + + Successfully imported device. + 디바이스를 성공적으로 가져왔습니다. + + + + The user in authentik this device will be assigned to. + 이 디바이스를 할당할 Authentik의 사용자입니다. + + + + Duo User ID + Duo 사용자 ID + + + + The user ID in Duo, can be found in the URL after clicking on a user. + Duo의 사용자 ID는, 사용자를 클릭한 후 URL에서 찾을 수 있습니다. + + + + Automatic import + 자동으로 가져오기 + + + + Successfully imported devices. + 성공적으로 디바이스를 가져왔습니다. + + + + Start automatic import + 자동으로 가져오기 시작 + + + + Or manually import + 혹은 수동으로 가져오기 + + + + Stages are single steps of a Flow that a user is guided through. A stage can only be executed from within a flow. + 스테이지는 사용자에게 안내되는 플로우의 단일 단계입니다. 스테이지는 플로우 내에서만 실행할 수 있습니다. + + + + Flows + 플로우 + + + + Stage(s) + 스테이지 + + + + Import + 가져오기 + + + + Import Duo device + Duo 디바이스 가져오기 + + + + Successfully updated flow. + 플로우를 성공적으로 업데이트했습니다. + + + + Successfully created flow. + 플로우를 성공적으로 생성했습니다. + + + + Shown as the Title in Flow pages. + 플로우 페이지에서 제목으로 표시됩니다. + + + + Visible in the URL. + URL에 표시됩니다. + + + + Designation + 지정 + + + + Decides what this Flow is used for. For example, the Authentication flow is redirect to when an un-authenticated user visits authentik. + 이 플로우가 사용되는 용도를 결정합니다. 예를 들어, 인증되지 않은 사용자가 authentik을 방문할 때 인증 플로우가 리디렉션됩니다. + + + + No requirement + 필수 조건 없음 + + + + Require authentication + 인증 필요 + + + + Require no authentication. + 인증 필요 없음. + + + + Require superuser. + 슈퍼유저권한이 필요합니다. + + + + Required authentication level for this flow. + 플로우에 필요한 인증 레벨을 요구합니다. + + + + Behavior settings + 동작 설정 + + + + Compatibility mode + 호환성 모드 + + + + Increases compatibility with password managers and mobile devices. + 비밀번호 관리자 및 모바일 디바이스와의 호환성을 높입니다. + + + + Denied action + 거부 조치 + + + + Will follow the ?next parameter if set, otherwise show a message + ?next 매개변수가 설정되어 있으면 이를 따르고, 그렇지 않으면 메시지를 표시 + + + + Will either follow the ?next parameter or redirect to the default interface + ?next 매개변수를 따르거나 기본 인터페이스로 리디렉션 + + + + Will notify the user the flow isn't applicable + 사용자에게 해당 플로우가 적용되지 않음을 알림 + + + + Decides the response when a policy denies access to this flow for a user. + 정책에서 사용자의 이 플로우에 대한 액세스를 거부할 때 응답을 결정합니다. + + + + Appearance settings + 외형 설정 + + + + Layout + 레이아웃 + + + + Background + 배경 + + + + Background shown during execution. + 실행 중 배경이 표시됩니다. + + + + Clear background + 배경 지우기 + + + + Delete currently set background image. + 현재 설정된 배경 이미지를 삭제합니다. + + + + Successfully imported flow. + 플로우를 성공적으로 가져왔습니다. + + + + .yaml files, which can be found on goauthentik.io and can be exported by authentik. + .yaml 파일로 내보낼 수 있으며, 이 파일은 goauthentik.io에서 찾을 수 있고 authentik을 통해 내보낼 수 있습니다. + + + + Flows describe a chain of Stages to authenticate, enroll or recover a user. Stages are chosen based on policies applied to them. + 플로우는 사용자를 인증, 등록 또는 복구하기 위한 일련의 단계를 설명합니다. 스테이지에 적용되는 정책에 따라 스테이지가 선택됩니다. + + + + Flow(s) + 플로우 + + + + Update Flow + 플로우 업데이트 + + + + Create Flow + 플로우 생성 + + + + Import Flow + 플로우 가져오기 + + + + Successfully cleared flow cache + 플로우 캐시를 성공적으로 지웠습니다. + + + + Failed to delete flow cache + 플로우 캐시를 삭제하지 못했습니다. + + + + Clear Flow cache + 플로우 캐시 지우기 + + + + Are you sure you want to clear the flow cache? + This will cause all flows to be re-evaluated on their next usage. + 플로우 캐시를 지우시겠습니까? + 이렇게 하면 다음 사용 시 모든 플로우가 재평가됩니다. + + + Stage binding(s) + 스테이지 바인딩 + + + + Stage type + 스테이지 유형 + + + + Edit Stage + 스테이지 편집 + + + + Update Stage binding + 스테이지 바인딩 업데이트 + + + + These bindings control if this stage will be applied to the flow. + 이러한 바인딩은 이 단계가 플로우에 적용될지 여부를 제어합니다. + + + + No Stages bound + 스테이지 바인딩 없음 + + + + No stages are currently bound to this flow. + 현재 이 흐름에 바인딩된 스테이지가 없습니다. + + + + Create Stage binding + 스테이지 바인딩 생성 + + + + Bind stage + 스테이지 바인드 + + + + Bind existing stage + 기존 스테이지 바인드 + + + + Flow Overview + 플로우 개요 + + + + Related actions + 관련 액션 + + + + Execute flow + 플로우 실행 + + + + Normal + Normal + + + + with current user + 현재 사용자로 + + + + with inspector + 감사관과 함께 + + + + Export flow + 플로우 내보내기 + + + + Export + 내보내기 + + + + Stage Bindings + 스테이지 바인딩 + + + + These bindings control which users can access this flow. + 이러한 바인딩은 이 플로우에 액세스할 수 있는 사용자를 제어합니다. + + + + Event Log + 이력 로그 + + + + Event + 이력 + + + + Event info + 이력 정보 + + + + Created + 생성됨 + + + + Successfully updated transport. + 전송을 성공적으로 업데이트했습니다. + + + + Successfully created transport. + 전송을 성공적으로 생성했습니다. + + + + Local (notifications will be created within authentik) + 로컬 (통지는 Authentik 내에 생성됩니다.) + + + + Webhook (generic) + 웹훅 (일반) + + + + Webhook (Slack/Discord) + 웹훅 (Slack/Discord) + + + + Webhook URL + 웹훅 URL + + + + Webhook Mapping + 웹훅 맵핑 + + + + Send once + 한 번만 전송 + + + + Only send notification once, for example when sending a webhook into a chat channel. + 예를 들어 채팅 채널로 웹훅을 보낼 때 알림을 한 번만 보내세요. + + + + Notification Transports + 통지 전송 + + + + Define how notifications are sent to users, like Email or Webhook. + 이메일 또는 웹훅과 같이 사용자에게 알림을 보내는 방법을 정의합니다. + + + + Notification transport(s) + 통지 전송 + + + + Update Notification Transport + 통지 전송 업데이트 + + + + Create Notification Transport + 통지 전송 생성 + + + + Successfully updated rule. + 규칙을 성공적으로 업데이트했습니다. + + + + Successfully created rule. + 규칙을 성공적으로 생성했습니다. + + + + Select the group of users which the alerts are sent to. If no group is selected the rule is disabled. + 알림을 전송할 사용자 그룹을 선택합니다. 그룹을 선택하지 않으면 규칙이 비활성화됩니다. + + + + Transports + 전송 + + + + Select which transports should be used to notify the user. If none are selected, the notification will only be shown in the authentik UI. + 사용자에게 통지를 보내는 데 사용할 전송을 선택합니다. 아무것도 선택하지 않으면 통지는 authentik UI에만 표시됩니다. + + + + Severity + 심각도 + + + + Notification Rules + 통지 규칙 + + + + Send notifications whenever a specific Event is created and matched by policies. + 특정 이력이 생성되고 정책과 일치할 때마다 통지를 보냅니다. + + + + Sent to group + 그룹에게 전송 + + + + Notification rule(s) + 통지 규칙 + + + + None (rule disabled) + 없음 (규칙 비활성화) + + + + Update Notification Rule + 통지 규칙 업데이트 + + + + Create Notification Rule + 통지 규칙 생성 + + + + These bindings control upon which events this rule triggers. +Bindings to groups/users are checked against the user of the event. + 이러한 바인딩은 이 규칙이 격발되는 이력을 제어합니다. +그룹/사용자에 대한 바인딩은 이벤트의 사용자에 대해 확인됩니다. + + + Outpost Deployment Info + Outpost 배포 정보 + + + + View deployment documentation + 배포 보기 + + + + Click to copy token + 토큰을 복사하려면 클릭 + + + + If your authentik Instance is using a self-signed certificate, set this value. + Authentik 인스턴스에서 자체 서명 인증서를 사용하는 경우, 이 값을 설정합니다. + + + + If your authentik_host setting does not match the URL you want to login with, add this setting. + authentik_host 설정이 로그인하려는 URL과 일치하지 않는 경우, 이 설정을 추가하세요. + + + + Successfully updated outpost. + Outpost를 성공적으로 업데이트했습니다. + + + + Successfully created outpost. + Outpost를 성공적으로 생성했습니다. + + + + Radius + Radius + + + + Integration + 통합 + + + + Selecting an integration enables the management of the outpost by authentik. + 통합을 선택하면 authentik을 통해 outpost를 관리할 수 있습니다. + + + + You can only select providers that match the type of the outpost. + Outpost 유형과 일치하는 공급자만 선택할 수 있습니다. + + + + Configuration + 구성 + + + + See more here: + 자세한 내용은 여기를 참조하세요: + + + + Documentation + 문서 + + + + Last seen + 마지막 확인 + + + + , should be + 은, 이어야 합니다 + + + + Hostname + 호스트명 + + + + Not available + 사용 불가 + + + + Last seen: + 마지막 확인: + + + + Unknown type + 알 수 없는 유형 + + + + Outposts + Outposts + + + + Outposts are deployments of authentik components to support different environments and protocols, like reverse proxies. + Outposts는 리버스 프록시와 같이 다양한 환경과 프로토콜을 지원하기 위한 인증 구성 요소의 배포입니다. + + + + Health and Version + 상태 및 버전 + + + + Warning: authentik Domain is not configured, authentication will not work. + 경고: authentik 도메인이 구성되지 않아, 인증이 불가능할 수 있습니다. + + + + Logging in via . + 를 통해 로그인 함. + + + + No integration active + 활성화된 통합 없음 + + + + Update Outpost + Outpost 업데이트 + + + + View Deployment Info + 배포 정보 보기 + + + + Detailed health (one instance per column, data is cached so may be out of date) + 상세 상태 (열당 하나의 인스턴스, 데이터는 캐시되므로 최신 정보가 아닐 수 있음) + + + + Outpost(s) + Outpost(s) + + + + Create Outpost + Outpost 생성 + + + + Successfully updated integration. + 통합을 성공적으로 업데이트했습니다. + + + + Successfully created integration. + 통합을 성공적으로 생성했습니다. + + + + Local + 로컬 + + + + If enabled, use the local connection. Required Docker socket/Kubernetes Integration. + 활성화된 경우, 로컬 연결을 사용합니다. 도커/쿠버네티스 통합에 필수적입니다. + + + + Docker URL + 도커 URL + + + + Can be in the format of 'unix://' when connecting to a local docker daemon, using 'ssh://' to connect via SSH, or 'https://:2376' when connecting to a remote system. + 로컬 도커 데몬에 연결할 때는 'unix://', SSH를 통해 연결할 때는 'ssh://', 원격 시스템에 연결할 때는 'https://:2376'의 형식을 사용할 수 있습니다. + + + + CA which the endpoint's Certificate is verified against. Can be left empty for no validation. + 엔드포인트의 인증서를 검증하는 CA입니다. 검증을 수행하지 않으려면 비워둘 수 있습니다. + + + + TLS Authentication Certificate/SSH Keypair + TLS 인증 인증서/SSH 키쌍 + + + + Certificate/Key used for authentication. Can be left empty for no authentication. + 인증에 사용되는 인증서/키입니다. 인증이 필요하지 않은 경우 비워 둘 수 있습니다. + + + + When connecting via SSH, this keypair is used for authentication. + SSH를 통해 연결할 때 이 키쌍은 인증에 사용됩니다. + + + + Kubeconfig + 쿠브설정 + + + + Verify Kubernetes API SSL Certificate + 쿠버네티스 API SSL 인증서 확인 + + + + New outpost integration + 새 outpost 통합 + + + + Create a new outpost integration. + 새 outpost 통합을 생성합니다. + + + + State + 상태 + + + + Unhealthy + 불량함 + + + + Outpost integration(s) + Outpost 통합 + + + + Successfully generated certificate-key pair. + 인증서-키 쌍을 성공적으로 생성했습니다. + + + + Common Name + 일반 이름 + + + + Subject-alt name + 주체 대체 이름 + + + + Optional, comma-separated SubjectAlt Names. + 추가적으로, 주체 대체 이름을 콤마로 구분할 수 있습니다. + + + + Validity days + 유효 기간 + + + + Successfully updated certificate-key pair. + 인증서-키 쌍을 성공적으로 업데이트했습니다. + + + + Successfully created certificate-key pair. + 인증서-키 쌍을 성공적으로 만들었습니다. + + + + PEM-encoded Certificate data. + PEM 인코딩된 인증서 데이터. + + + + Optional Private Key. If this is set, you can use this keypair for encryption. + 선택적 개인 키. 이 옵션을 설정하면 이 키쌍을 암호화에 사용할 수 있습니다. + + + + Certificate-Key Pairs + 인증서-키 쌍 + + + + Import certificates of external providers or create certificates to sign requests with. + 외부 공급자의 인증서를 가져오거나 요청에 서명할 인증서를 만들 수 있습니다. + + + + Private key available? + 개인 키를 사용할 수 있나요? + + + + Certificate-Key Pair(s) + 인증서 키 쌍 + + + + Managed by authentik + Authentik에서 관리 + + + + Managed by authentik (Discovered) + Authentik에서 관리(발견함) + + + + Yes () + () 예 + + + + No + 아니오 + + + + Update Certificate-Key Pair + 인증서-키 쌍 업데이트 + + + + Certificate Fingerprint (SHA1) + 인증서 지문(SHA1) + + + + Certificate Fingerprint (SHA256) + 인증서 지문(SHA256) + + + + Certificate Subject + 인증서 주제 + + + + Download Certificate + 인증서 다운로드 + + + + Download Private key + 개인 키 다운로드 + + + + Create Certificate-Key Pair + 인증서-키 쌍 만들기 + + + + Generate + 발행 + + + + Generate Certificate-Key Pair + 인증서-키 쌍 발행 + + + + Successfully updated instance. + 인스턴스 업데이트에 성공했습니다. + + + + Successfully created instance. + 인스턴스를 성공적으로 생성했습니다. + + + + Disabled blueprints are never applied. + 비활성화된 블루프린트는 적용되지 않습니다. + + + + Local path + 로컬 경로 + + + + OCI Registry + OCI 레지스트리 + + + + Internal + 내부 + + + + OCI URL, in the format of oci://registry.domain.tld/path/to/manifest. + OCI URL은, oci://registry.domain.tld/path/to/manifest 형식입니다. + + + + See more about OCI support here: + OCI 지원에 대한 자세한 내용은 여기를 참조하세요: + + + + Blueprint + 블루프린트트 + + + + Configure the blueprint context, used for templating. + 템플릿에 사용되는 Blueprint 컨텍스트를 구성합니다. + + + + Orphaned + 독립적 + + + + Blueprints + 블루프린트 + + + + Automate and template configuration within authentik. + Authentik 내에서 구성을 자동화하고 템플릿을 작성하세요. + + + + Last applied + 마지막 적용 + + + + Blueprint(s) + 블루프린트 + + + + Update Blueprint + 블루프린트 업데이트 + + + + Create Blueprint Instance + 블루프린트 인스턴스 생성 + + + + API Requests + API 요청 + + + + Open API Browser + API 브라우저 열기 + + + + Notifications + 알림 + + + + unread + 읽지 않음 + + + + Successfully cleared notifications + 알림을 성공적으로 지웠습니다. + + + + Clear all + 모두 삭제 + + + + A newer version of the frontend is available. + 새로운 버전의 프론트엔드를 사용할 수 있습니다. + + + + You're currently impersonating . Click to stop. + 현재 로 가장(impersonating)하고 있습니다. 클릭해서 정지. + + + + User interface + 사용자 인터페이스 + + + + Dashboards + 대시보드 + + + + Events + 이력 + + + + Logs + 로그 + + + + Customisation + 사용자 정의 + + + + Directory + 디렉토리 + + + + System + 시스템 + + + + Certificates + 인증서 + + + + Outpost Integrations + Outpost 통합 + + + + API request failed + API 요청 실패 + + + + User's avatar + 사용자 아바타 + + + + Something went wrong! Please try again later. + 문제가 발생했습니다! 나중에 다시 시도해 주세요. + + + + Request ID + 요청 ID + + + + You may close this page now. + 이제 이 페이지를 닫아도 됩니다. + + + + You're about to be redirect to the following URL. + 다음 URL로 리디렉션됩니다. + + + + Follow redirect + 리디렉션 따라가기 + + + + Request has been denied. + 요청이 거부되었습니다. + + + + Not you? + 본인이 아닌가요? + + + + Need an account? + 계정이 필요하신가요? + + + + Sign up. + 가입. + + + + Forgot username or password? + 사용자명이나 비밀번호를 잊으셨나요? + + + + Select one of the sources below to login. + 아래 중 하나를 선택하여 로그인합니다. + + + + Or + 혹은 + + + + Use a security key + 보안 키 사용 + + + + Login to continue to . + 로 계속해서 로그인하세요. + + + + Please enter your password + 비밀번호를 입력하세요 + + + + Forgot password? + 비밀번호를 잊으셨나요? + + + + Application requires following permissions: + 애플리케이션에는 다음 권한이 필요합니다: + + + + Application already has access to the following permissions: + 애플리케이션에 이미 다음 권한에 대한 액세스 권한이 있습니다: + + + + Application requires following new permissions: + 애플리케이션에는 다음과 같은 새로운 권한이 필요합니다: + + + + Check your Inbox for a verification email. + 받은 편지함에서 인증 이메일을 확인합니다. + + + + Send Email again. + 이메일을 다시 보냅니다. + + + + Successfully copied TOTP Config. + TOTP 구성을 성공적으로 복사했습니다. + + + + Copy + 복사 + + + + Code + 코드 + + + + Please enter your TOTP Code + TOTP 코드를 입력하세요. + + + + Duo activation QR code + Duo 활성화 QR 코드 + + + + Alternatively, if your current device has Duo installed, click on this link: + 대안으로, 현재 디바이스에 Duo가 설치되어 있는 경우 이 링크를 클릭합니다: + + + + Duo activation + Duo 활성화 + + + + Check status + 상태 확인 + + + + Make sure to keep these tokens in a safe place. + 이 토큰은 반드시 안전한 곳에 보관하세요. + + + + Phone number + 전화 번호 + + + + Please enter your Phone number. + 전화번호를 입력하세요. + + + + Please enter the code you received via SMS + SMS로 받은 코드를 입력하세요. + + + + A code has been sent to you via SMS. + SMS로 코드가 전송되었습니다. + + + + Open your two-factor authenticator app to view your authentication code. + 2단계 인증 앱을 열어 인증 코드를 확인합니다. + + + + Static token + 정적 토큰 + + + + Authentication code + 인증 코드 + + + + Please enter your code + 코드를 입력하세요. + + + + Return to device picker + 디바이스 선택기로 돌아가기 + + + + Sending Duo push notification + Duo 푸시 알림 보내기 + + + + Assertions is empty + 어설션이 비어 있음 + + + + Error when creating credential: + 자격 증명 생성 시 오류 발생: + + + + Error when validating assertion on server: + 서버에서 어설션 유효성 검사 시 오류 발생: + + + + Retry authentication + 인증 다시 시도 + + + + Duo push-notifications + Duo push-알림 + + + + Receive a push notification on your device. + 디바이스에서 푸시 알림을 받습니다. + + + + Authenticator + 인증기 + + + + Use a security key to prove your identity. + 보안 키를 사용하여 신원을 증명하세요. + + + + Traditional authenticator + 전통적 인증기 + + + + Use a code-based authenticator. + 코드 기반 인증기를 사용합니다. + + + + Recovery keys + 복구 키 + + + + In case you can't access any other method. + 다른 방법으로 액세스할 수 없는 경우. + + + + SMS + SMS + + + + Tokens sent via SMS. + SMS를 통해 토큰을 전송했습니다. + + + + Select an authentication method. + 인증 방법을 선택합니다. + + + + Stay signed in? + 로그인을 유지하시겠습니까? + + + + Select Yes to reduce the number of times you're asked to sign in. + 로그인 요청 횟수를 줄이려면 예를 선택합니다. + + + + Authenticating with Plex... + Plex로 인증... + + + + Waiting for authentication... + 인증을 기다리는 중... + + + + If no Plex popup opens, click the button below. + Plex 팝업이 열리지 않으면 아래 버튼을 클릭하세요. + + + + Open login + 로그인 열기 + + + + Authenticating with Apple... + Apple로 인증... + + + + Retry + 재시도 + + + + Enter the code shown on your device. + 디바이스에 표시된 코드를 입력합니다. + + + + Please enter your Code + 코드를 입력하세요. + + + + You've successfully authenticated your device. + 디바이스 인증에 성공했습니다. + + + + Flow inspector + 플로우 검사기 + + + + Next stage + 다음 스테이지 + + + + Stage name + 스테이지 이름 + + + + Stage kind + 스테이지 종류 + + + + Stage object + 스테이지 오브젝트 + + + + This flow is completed. + 이 플로우는 완료되었습니다. + + + + Plan history + 플랜 내역 + + + + Current plan context + 현재 플랜 컨텍스트 + + + + Session ID + 세션 ID + + + + Powered by authentik + Powered by authentik + + + + Background image + 배경 이미지 + + + + Error creating credential: + 자격 증명 생성 오류: + + + + Server validation of credential failed: + 자격 증명의 서버 유효성 검사 실패: + + + + Register device + 디바이스 등록 + + + + Refer to documentation + 문서 참조 + + + No Applications available. + 사용 가능한 애플리케이션이 없습니다. + + + + Either no applications are defined, or you don’t have access to any. + 정의된 애플리케이션이 없거나 애플리케이션에 대한 액세스 권한이 없는 경우입니다. + + + My Applications + 내 애플리케이션 + + + + My applications + 내 애플리케이션 + + + + Change your password + 비밀번호 변경 + + + + Change password + 비밀번호 변경 + + + + + + + + + + + Save + 저장 + + + + Delete account + 계정 삭제 + + + + Successfully updated details + 세부 정보를 성공적으로 업데이트했습니다 + + + + Open settings + 설정 열기 + + + + No settings flow configured. + 설정 플로우가 구성되지 않았습니다. + + + + Update details + 업데이트 세부 정보 + + + + Successfully disconnected source + 소스 연결을 성공적으로 끊었습니다. + + + + Failed to disconnected source: + 소스 연결 끊기 실패: + + + + Disconnect + 연결 해제 + + + + Connect + 연결 + + + + Error: unsupported source settings: + 오류: 지원되지 않는 소스 설정입니다: + + + + Connect your user account to the services listed below, to allow you to login using the service instead of traditional credentials. + 사용자 계정을 아래 나열된 서비스에 연결하여 기존 자격증명 대신 서비스를 사용하여 로그인할 수 있도록 합니다. + + + + No services available. + 사용 가능한 서비스가 없습니다. + + + + Create App password + 앱 비밀번호 생성 + + + + User details + 사용자 세부 정보 + + + + Consent + 동의 + + + + MFA Devices + MFA 디바이스 + + + + Connected services + 연결된 서비스 + + + + Tokens and App passwords + 토큰 및 앱 비밀번호 + + + + Unread notifications + 읽지 않은 통지 + + + + Admin interface + 관리자 인터페이스 + + + + Stop impersonation + 가장(impersonation) 중지 + + + + Avatar image + 아바타 이미지 + + + + Failed + 실패 + + + + Unsynced / N/A + 비동기화 / N/A + + + + Outdated outposts + 구식 outpost + + + + Unhealthy outposts + 불량한 outposts + + + + Next + 다음 + + + + Inactive + 비활성 + + + + Regular user + 일반 사용자 + + + + Activate + 활성화 + + + + Use Server URI for SNI verification + SNI 확인에 서버 URI 사용 + + + Required for servers using TLS 1.3+ + TLS 1.3 이상을 사용하는 서버에 필요 + + + Client certificate keypair to authenticate against the LDAP Server's Certificate. + 클라이언트 인증서 키쌍으로 LDAP 서버의 인증서에 대해 인증합니다. + + + The certificate for the above configured Base DN. As a fallback, the provider uses a self-signed certificate. + 위에 구성된 기본 DN에 대한 인증서입니다. 공급자는 대체 수단으로 자체 서명된 인증서를 사용합니다. + + + TLS Server name + TLS 서버 이름 + + + DNS name for which the above configured certificate should be used. The certificate cannot be detected based on the base DN, as the SSL/TLS negotiation happens before such data is exchanged. + 위에 구성된 인증서를 사용해야 하는 DNS 이름입니다. SSL/TLS 협상이 발생하기 전에 이러한 데이터가 교환되기 때문에 기본 Distinguished Name(DN)을 기준으로 인증서를 감지할 수 없습니다. + + + TLS Client authentication certificate + TLS 클라이언트 인증 인증서 + + + Model + 모델 + + + Match events created by selected model. When left empty, all models are matched. + 선택한 모델에 의해 생성된 이벤트와 일치시킵니다. 비워두면 모든 모델이 일치합니다. + + + Code-based MFA Support + 코드 기반 다중 인증 (MFA) 지원 + + + 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. + 이 옵션을 활성화하면, 비밀번호에 세미콜론과 TOTP 코드를 추가하여 코드 기반 멀티팩터 인증을 사용할 수 있습니다. 비밀번호에 세미콜론이 포함되어 있으면 비밀번호가 거부될 수 있으므로, 이 공급자에 바인딩할 모든 사용자가 TOTP 디바이스를 구성한 경우에만 이 기능을 사용하도록 설정해야 합니다. + + + User type + 사용자 유형 + + + Successfully updated license. + 라이선스 업데이트에 성공했습니다. + + + Successfully created license. + 라이선스를 성공적으로 생성했습니다. + + + Install ID + 설치 ID + + + License key + 라이선스 키 + + + Licenses + 라이선스 + + + License(s) + 라이선스 + + + Enterprise is in preview. + Enterprise는 현재 미리보기 입니다. + + + Cumulative license expiry + 누적 라이선스 만료 기간 + + + Update License + 라이선스 업데이트 + + + Warning: The current user count has exceeded the configured licenses. + 경고: 현재 사용자 수가 구성된 라이선스를 초과했습니다. + + + Click here for more info. + 자세한 내용을 보려면 여기를 클릭하세요. + + + Enterprise + Enterprise + + + Manage enterprise licenses + Enterprise 라이선스 관리 + + + No licenses found. + 라이선스를 찾을 수 없습니다. + + + Send us feedback! + feedback 부탁드립니다! + + + Get a license + 라이선스 얻기 + + + Go to Customer Portal + 고객 포털로 가기 + + + Forecast internal users + 내부 사용자 예측 + + + Estimated user count one year from now based on current internal users and forecasted internal users. + 명의 내부 사용자를 기준으로 예상하는 일 년후 예상 사용자수는 입니다. + + + Forecast external users + 외부 사용자 예측 + + + Estimated user count one year from now based on current external users and forecasted external users. + 명의 외부 사용자를 기준으로 예상하는 일 년후 예상 사용자수는 입니다. + + + Install + 설치 + + + Install License + 라이선스 설치 + + + Internal users might be users such as company employees, which will get access to the full Enterprise feature set. + 내부 사용자는 회사 직원과 같은 사용자로, 전체 엔터프라이즈 기능 세트에 액세스할 수 있습니다. + + + External users might be external consultants or B2C customers. These users don't get access to enterprise features. + 외부 사용자는 외부 컨설턴트나 B2C 고객일 수 있습니다. 이러한 사용자는 엔터프라이즈 기능에 액세스할 수 없습니다. + + + Service accounts should be used for machine-to-machine authentication or other automations. + 서비스 계정은 M2M(기계 간 인증) 또는 기타 자동화에 사용되어야 합니다. + + + Less details + 적게 보기 + + + More details + 자세히 보기 + + + Remove item + 항목 제거 + + + Open API drawer + API 서랍 열기 + + + Open Notification drawer + 통지 서랍 열기 + + + Restart task + 작업 다시 시작 + + + Add provider + 공급자 추가 + + + Open + 열기 + + + Copy token + 토큰 복사 + + + Add users + 사용자 추가 + + + Add group + 그룹 추가 + + + Import devices + 디바이스 가져오기 + + + Execute + 실행 + + + Show details + 세부 정보 표시 + + + Apply + 적용 + + + Settings + 설정 + + + Sign out + 로그아웃 + + + The number of tokens generated whenever this stage is used. Every token generated per stage execution will be attached to a single static device. + 이 스테이지가 사용될 때마다 생성되는 토큰의 수입니다. 스테이지가 실행될 때마다 생성되는 모든 토큰은 하나의 정적 디바이스에 연결됩니다. + + + Token length + 토큰 길이 + + + The length of the individual generated tokens. Can be increased to improve security. + 개별적으로 생성된 토큰의 길이입니다. 보안을 강화하기 위해 늘릴 수 있습니다. + + + Internal: + 내부: + + + External: + 외부: + + + Statically deny the flow. To use this stage effectively, disable *Evaluate when flow is planned* on the respective binding. + 플로우를 정적으로 거부합니다. 이 스테이지를 효과적으로 사용하려면 각 바인딩에서 *플로우가 계획될 때 평가*를 비활성화하세요. + + + Create and bind Policy + 정책 생성 및 바인드 + + + Federation and Social login + 연합 및 소셜 로그인 + + + Create and bind Stage + 바인드 스테이지 생성 + + + Flows and Stages + 플로우 및 스테이지 + + + New version available + 새 버전 사용가능 + + + Failure result + 실패한 결과 + + + Pass + 통과 + + + Don't pass + 미통과 + + + Result used when policy execution fails. + 정책 실행이 실패할 때 사용되는 결과입니다. + + + Required: User verification must occur. + 필수: 사용자 확인이 이루어져야 합니다. + + + Preferred: User verification is preferred if available, but not required. + 선호: 가능한 경우 사용자 확인이 선호되지만 필수는 아닙니다. + + + Discouraged: User verification should not occur. + 비권장: 사용자 확인이 이루어지지 않아야 합니다. + + + Required: The authenticator MUST create a dedicated credential. If it cannot, the RP is prepared for an error to occur + 필수: 인증기는 반드시 전용 자격 증명을 만들어야 합니다. 생성할 수 없는 경우, RP는 오류 발생에 대비해야 합니다 + + + Preferred: The authenticator can create and store a dedicated credential, but if it doesn't that's alright too + 선호: 인증기는 특별한 자격 증명을 생성하고 저장할 수 있지만, 그렇지 않아도 괜찮습니다 + + + Discouraged: The authenticator should not create a dedicated credential + 비권장: 인증기는 특별한 자격 증명을 생성하지 않아야 합니다 + + + Lock the user out of this system + 이 시스템에서 사용자 잠금 + + + Allow the user to log in and use this system + 사용자가 로그인하여 이 시스템을 사용할 수 있도록 허용 + + + Temporarily assume the identity of this user + 일시적으로 이 사용자의 신원을 가정 + + + Enter a new password for this user + 이 사용자에 대한 새 비밀번호 입력 + + + Create a link for this user to reset their password + 이 사용자가 비밀번호를 재설정할 수 있는 링크 생성 + + + WebAuthn requires this page to be accessed via HTTPS. + WebAuthn을 사용하려면 이 페이지에 HTTPS를 통해 액세스해야 합니다. + + + WebAuthn not supported by browser. + 브라우저에서 WebAuthn을 지원하지 않습니다. + + + Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a managed outpost, this is done for you). + Nginx의 auth_request 또는 Traefik의 forwardAuth와 함께 이 공급자를 사용합니다. 각 응용 프로그램/도메인은 자체 공급자가 필요하며, 각 도메인에서는 /outpost.goauthentik.io가 아웃포스트로 라우팅되어야 합니다 (관리되는 아웃포스트를 사용하는 경우 이 작업은 자동으로 수행됩니다). + + + Default relay state + 기본 릴레이 상태 + + + When using IDP-initiated logins, the relay state will be set to this value. + IDP-initiated 로그인을 사용하는 경우, 릴레이 상태가 이 값으로 설정됩니다. + + + Flow Info + 플로우 정보 + + + Stage used to configure a WebAuthn authenticator (i.e. Yubikey, FaceID/Windows Hello). + WebAuthn 인증기를 구성하는 데 사용되는 스테이지(예: Yubikey, FaceID/Windows Hello). + +<<<<<<< HEAD + + Internal application name used in URLs. + URL에 사용되는 내부 애플리케이션 이름입니다. + + + Submit + 제출 + + + UI Settings + UI 설정 + + + Transparent Reverse Proxy + 투명 리버스 프록시 + + + For transparent reverse proxies with required authentication + 인증이 필요한 투명한 리버스 프록시의 경우 + + + Configure SAML provider manually + SAML 공급자를 수동으로 구성 + + + Configure RADIUS provider manually + RADIUS 공급자를 수동으로 구성 + + + Configure SCIM provider manually + SCIM 공급자를 수동으로 구성 + + + Saving Application... + Saving Application... + + + Authentik was unable to save this application: + authentik이 이 애플리케이션을 저장하지 못했습니다: + + + Your application has been saved + 애플리케이션 저장됨 + + + Method's display Name. + Method의 표시 이름. + + + Use this provider with nginx's auth_request or traefik's + forwardAuth. Each application/domain needs its own provider. + Additionally, on each domain, /outpost.goauthentik.io must be + routed to the outpost (when using a managed outpost, this is done for you). + Use this provider with nginx's auth_request or traefik's + forwardAuth. Each application/domain needs its own provider. + Additionally, on each domain, /outpost.goauthentik.io must be + routed to the outpost (when using a managed outpost, this is done for you). + + + Custom attributes + 사용자 정의 속성(Attribute) + + + Don't show this message again. + 이 메시지를 다시 표시하지 마세요. + + + Failed to fetch + 가져오지 못함 + + + Failed to fetch data. + 데이터를 가져오지 못했습니다. + + + Successfully assigned permission. + 권한을 성공적으로 할당했습니다. + + + Role + 역할 + + + Assign + 할당 + + + Assign permission to role + 역할에 권한 할당 + + + Assign to new role + 새 역할에 할당 + + + Directly assigned + 직접 할당됨 + + + Assign permission to user + 사용자에 권한 부여 + + + Assign to new user + 새 사용자에 할당 + + + User Object Permissions + 사용자 오브젝트 권한 + + + Role Object Permissions + 역할 오브젝트 권한 + + + Roles + 역할 + + + Select roles to grant this groups' users' permissions from the selected roles. + 선택한 역할에서 이 그룹의 사용자에게 권한을 부여할 역할을 선택합니다. + + + Update Permissions + 권한 업데이트 + + + Editing is disabled for managed tokens + 관리되는 토큰의 경우 편집이 비활성화됩니다. + + + Select permissions to grant + 부여할 권한 선택 + + + Permissions to add + 추가할 권한 + + + Select permissions + 권한 선택 + + + Assign permission + 권한 할당 + + + Permission(s) + 권한 + + + Permission + 권한 + + + User doesn't have view permission so description cannot be retrieved. + 사용자에게 보기 권한이 없으므로 설명을 검색할 수 없습니다. + + + Assigned permissions + 할당된 권한 + + + Assigned global permissions + 전역 권한 할당 + + + Assigned object permissions + 할당된 오브젝트 권한 + + + Successfully updated role. + 역할을 성공적으로 업데이트했습니다. + + + Successfully created role. + 역할을 성공적으로 만들었습니다. + + + Manage roles which grant permissions to objects within authentik. + Authentik 내에서 개체에 권한을 부여하는 역할을 관리합니다. + + + Role(s) + 역할 + + + Update Role + 역할 업데이트 + + + Create Role + 역할 생성 + + + Role doesn't have view permission so description cannot be retrieved. + 역할에 보기 권한이 없으므로 설명을 검색할 수 없습니다. + + + Role + 역할 + + + Role Info + 역할 정보 + + + Pseudolocale (for testing) + 가짜-로캘 (테스트용) + + + Create With Wizard + authentik 마법사로 생성 + + + One hint, 'New Application Wizard', is currently hidden + 힌트, '새 애플리케이션 마법사'는 현재, 숨겨져 있습니다. + + + External applications that use authentik as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access. + OAuth2 및 SAML과 같은 프로토콜을 통해 인증서를 ID 공급자로 사용하는 외부 애플리케이션. 액세스할 수 없는 애플리케이션을 포함한 모든 애플리케이션이 여기에 표시됩니다. + + + Deny message + 거부 메시지 + + + Message shown when this stage is run. + 이 스테이지가 실행될 때 표시되는 메시지입니다. + + + Open Wizard + 마법사 열기 + + + Demo Wizard + 데모 마법사 + + + Run the demo wizard + 데모 마법사 실행 + + + OAuth2/OIDC (Open Authorization/OpenID Connect) + OAuth2/OIDC (Open Authorization/OpenID Connect) + + + LDAP (Lightweight Directory Access Protocol) + LDAP (Lightweight Directory Access Protocol) + + + Forward Auth (Single Application) + Forward Auth (단일 애플리케이션) + + + Forward Auth (Domain Level) + Forward Auth (도메인 레벨) + + + SAML (Security Assertion Markup Language) + SAML (Security Assertion Markup Language) + + + RADIUS (Remote Authentication Dial-In User Service) + RADIUS (Remote Authentication Dial-In User Service) + + + SCIM (System for Cross-domain Identity Management) + SCIM (System for Cross-domain Identity Management) + + + The token has been copied to your clipboard + 토큰이 클립보드에 복사되었습니다. + + + The token was displayed because authentik does not have permission to write to the clipboard + authentik이 클립보드에 쓸 수 있는 권한이 없기 때문에 토큰이 표시되었습니다. + + + A copy of this recovery link has been placed in your clipboard + 이 복구 링크의 사본이 클립보드에 저장되었습니다. + + + The current tenant must have a recovery flow configured to use a recovery link + 현재 테넌트에 복구 링크를 사용하도록 구성된 복구 플로우가 있어야 합니다. + + + Create recovery link + 복구 링크 생성 + + + Create Recovery Link + 이 사용자가 비밀번호를 재설정할 수 있는 링크 생성 + + + External + 외부 + + + Service account + 서비스 계정 + + + Service account (internal) + 서비스 계정 (내부) + + + Check the release notes + 릴리스 정보 확인 + + + User Statistics + 사용자 통계 + + + <No name set> + <설정된 이름 없음> + + + For nginx's auth_request or traefik's forwardAuth + Nginx의 auth_request 또는 Traefik의 forwardAuth의 경우 + + + For nginx's auth_request or traefik's forwardAuth per root domain + 루트 도메인 당 Nginx의 auth_request 또는 Traefik의 forwardAuth 경우 + + + RBAC is in preview. + RBAC 는 현재 프리뷰입니다. + + + User type used for newly created users. + 새로 생성된 사용자에 사용되는 사용자 유형입니다. + + + Users created + 유서 생성됨 + + + Failed logins + 실패한 로그인 + + + Also known as Client ID. + 클라이언트 ID로도 알려져 있습니다. + + + Also known as Client Secret. + 클라이언트 비밀이라고도 합니다. + + + Global status + 전역 상태 + + + Vendor + Vendor + + + No sync status. + 동기화 상태가 없습니다. + + + Sync currently running. + 현재 동기화가 실행 중입니다. + + + Connectivity + Connectivity + + + 0: Too guessable: risky password. (guesses &lt; 10^3) + 0: 너무 추측하기 쉬움: 위험한 비밀번호. (guesses &lt; 10^3) + + + 1: Very guessable: protection from throttled online attacks. (guesses &lt; 10^6) + 1: 매우 예측 가능함: 제한된 온라인 공격으로부터의 보호. (guesses &lt; 10^6) + + + 2: Somewhat guessable: protection from unthrottled online attacks. (guesses &lt; 10^8) + 2: 다소 예측 가능함: 제한되지 않은 온라인 공격으로부터의 보호. (guesses &lt; 10^8) + + + 3: Safely unguessable: moderate protection from offline slow-hash scenario. (guesses &lt; 10^10) + 3: 예측하기 어려움: 오프라인 느린 해시 시나리오로부터의 중간 수준의 보호. (guesses &lt; 10^10) + + + 4: Very unguessable: strong protection from offline slow-hash scenario. (guesses &gt;= 10^10) + 4: 매우 예측하기 어려움: 오프라인 느린 해시 시나리오로부터의 강력한 보호. (guesses &gt;= 10^10) + + + Successfully created user and added to group + 사용자 생성과 그룹 추가에 성공했습니다. + + + This user will be added to the group "". + 이 사용자는 "" 그룹에 추가됩니다. + + + Pretend user exists + 사용자가 존재하는 것처럼 가장 + + + When enabled, the stage will always accept the given user identifier and continue. + 활성화되면 해당 스테이지는 항상 제공된 사용자 식별자를 수락하고 계속 진행합니다. + + + There was an error in the application. + 애플리케이션에 오류가 발생했습니다. + + + Review the application. + 애플리케이션 검토하기. + + + There was an error in the provider. + 공급자에 오류가 발생했습니다. + + + Review the provider. + 공급자 검토하기. + + + There was an error + 오류가 발생함 + + + There was an error creating the application, but no error message was sent. Please review the server logs. + 애플리케이션을 만드는 동안 오류가 발생했지만, 오류 메시지는 전송되지 않았습니다. 서버 로그를 검토해 보세요. + + + Configure LDAP Provider + LDAP 공급자 구성 + + + Configure OAuth2/OpenId Provider + OAuth2/OpenId 공급자 구성 + + + Configure Proxy Provider + 프록시 공급자 구성 + + + AdditionalScopes + 추가 사용성 + + + Configure Radius Provider + Radius 제공자 구성 + + + Configure SAML Provider + SAML 공급자 구성 + + + Property mappings used for user mapping. + 사용자 매핑에 사용되는 속성 매핑입니다. + + + Configure SCIM Provider + SCIM 공급자 구성 + + + Property mappings used for group creation. + 그룹을 만드는데 사용할 속성 매핑입니다. + + + Event volume + 이력 규모 + + + Require Outpost (flow can only be executed from an outpost). + Outpost필요 (플로우는 Outpost에서만 실행할 수 있음). + + + + \ No newline at end of file diff --git a/web/xliff/pl.xlf b/web/xliff/pl.xlf index 46ec08b71..aa9983509 100644 --- a/web/xliff/pl.xlf +++ b/web/xliff/pl.xlf @@ -1585,15 +1585,30 @@ NameID attribute + + No sync status. + + + Sync currently running. + + + Not synced yet. + + + Task finished with warnings + + + Task finished with errors + + + Last sync: + Warning: Provider is not assigned to an application as backchannel provider. Update SCIM Provider - - Sync not run yet. - Run sync again @@ -2325,24 +2340,6 @@ doesn't pass when either or both of the selected options are equal or above the Vendor - - No sync status. - - - Sync currently running. - - - Not synced yet. - - - Task finished with warnings - - - Task finished with errors - - - Last sync: - Update LDAP Source @@ -3848,6 +3845,39 @@ doesn't pass when either or both of the selected options are equal or above the If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here. + + Network binding + + + No binding + + + Bind ASN + + + Bind ASN and Network + + + Bind ASN, Network and IP + + + Configure if sessions created by this stage should be bound to the Networks they were created in. + + + GeoIP binding + + + Bind Continent + + + Bind Continent and Country + + + Bind Continent, Country and City + + + Configure if sessions created by this stage should be bound to their GeoIP-based location + Terminate other sessions diff --git a/web/xliff/pseudo-LOCALE.xlf b/web/xliff/pseudo-LOCALE.xlf index 57488f36a..0b0e8a01e 100644 --- a/web/xliff/pseudo-LOCALE.xlf +++ b/web/xliff/pseudo-LOCALE.xlf @@ -1585,15 +1585,30 @@ NameID attribute + + No sync status. + + + Sync currently running. + + + Not synced yet. + + + Task finished with warnings + + + Task finished with errors + + + Last sync: + Warning: Provider is not assigned to an application as backchannel provider. Update SCIM Provider - - Sync not run yet. - Run sync again @@ -2325,24 +2340,6 @@ doesn't pass when either or both of the selected options are equal or above the Vendor - - No sync status. - - - Sync currently running. - - - Not synced yet. - - - Task finished with warnings - - - Task finished with errors - - - Last sync: - Update LDAP Source @@ -3848,6 +3845,39 @@ doesn't pass when either or both of the selected options are equal or above the If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here. + + Network binding + + + No binding + + + Bind ASN + + + Bind ASN and Network + + + Bind ASN, Network and IP + + + Configure if sessions created by this stage should be bound to the Networks they were created in. + + + GeoIP binding + + + Bind Continent + + + Bind Continent and Country + + + Bind Continent, Country and City + + + Configure if sessions created by this stage should be bound to their GeoIP-based location + Terminate other sessions diff --git a/web/xliff/tr.xlf b/web/xliff/tr.xlf index 5f06c9ffd..e54d1fd0e 100644 --- a/web/xliff/tr.xlf +++ b/web/xliff/tr.xlf @@ -1585,15 +1585,30 @@ NameID attribute + + No sync status. + + + Sync currently running. + + + Not synced yet. + + + Task finished with warnings + + + Task finished with errors + + + Last sync: + Warning: Provider is not assigned to an application as backchannel provider. Update SCIM Provider - - Sync not run yet. - Run sync again @@ -2325,24 +2340,6 @@ doesn't pass when either or both of the selected options are equal or above the Vendor - - No sync status. - - - Sync currently running. - - - Not synced yet. - - - Task finished with warnings - - - Task finished with errors - - - Last sync: - Update LDAP Source @@ -3848,6 +3845,39 @@ doesn't pass when either or both of the selected options are equal or above the If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here. + + Network binding + + + No binding + + + Bind ASN + + + Bind ASN and Network + + + Bind ASN, Network and IP + + + Configure if sessions created by this stage should be bound to the Networks they were created in. + + + GeoIP binding + + + Bind Continent + + + Bind Continent and Country + + + Bind Continent, Country and City + + + Configure if sessions created by this stage should be bound to their GeoIP-based location + Terminate other sessions diff --git a/web/xliff/zh-Hans.xlf b/web/xliff/zh-Hans.xlf index 16a4701c2..75295d3b7 100644 --- a/web/xliff/zh-Hans.xlf +++ b/web/xliff/zh-Hans.xlf @@ -1585,15 +1585,30 @@ NameID attribute + + No sync status. + + + Sync currently running. + + + Not synced yet. + + + Task finished with warnings + + + Task finished with errors + + + Last sync: + Warning: Provider is not assigned to an application as backchannel provider. Update SCIM Provider - - Sync not run yet. - Run sync again @@ -2325,24 +2340,6 @@ doesn't pass when either or both of the selected options are equal or above the Vendor - - No sync status. - - - Sync currently running. - - - Not synced yet. - - - Task finished with warnings - - - Task finished with errors - - - Last sync: - Update LDAP Source @@ -3848,6 +3845,39 @@ doesn't pass when either or both of the selected options are equal or above the If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here. + + Network binding + + + No binding + + + Bind ASN + + + Bind ASN and Network + + + Bind ASN, Network and IP + + + Configure if sessions created by this stage should be bound to the Networks they were created in. + + + GeoIP binding + + + Bind Continent + + + Bind Continent and Country + + + Bind Continent, Country and City + + + Configure if sessions created by this stage should be bound to their GeoIP-based location + Terminate other sessions diff --git a/web/xliff/zh-Hant.xlf b/web/xliff/zh-Hant.xlf index 1eac37616..d92acd4f9 100644 --- a/web/xliff/zh-Hant.xlf +++ b/web/xliff/zh-Hant.xlf @@ -1585,15 +1585,30 @@ NameID attribute + + No sync status. + + + Sync currently running. + + + Not synced yet. + + + Task finished with warnings + + + Task finished with errors + + + Last sync: + Warning: Provider is not assigned to an application as backchannel provider. Update SCIM Provider - - Sync not run yet. - Run sync again @@ -2325,24 +2340,6 @@ doesn't pass when either or both of the selected options are equal or above the Vendor - - No sync status. - - - Sync currently running. - - - Not synced yet. - - - Task finished with warnings - - - Task finished with errors - - - Last sync: - Update LDAP Source @@ -3848,6 +3845,39 @@ doesn't pass when either or both of the selected options are equal or above the If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here. + + Network binding + + + No binding + + + Bind ASN + + + Bind ASN and Network + + + Bind ASN, Network and IP + + + Configure if sessions created by this stage should be bound to the Networks they were created in. + + + GeoIP binding + + + Bind Continent + + + Bind Continent and Country + + + Bind Continent, Country and City + + + Configure if sessions created by this stage should be bound to their GeoIP-based location + Terminate other sessions diff --git a/web/xliff/zh_TW.xlf b/web/xliff/zh_TW.xlf index a5db0c5c4..5404ad5e3 100644 --- a/web/xliff/zh_TW.xlf +++ b/web/xliff/zh_TW.xlf @@ -1585,15 +1585,30 @@ NameID attribute + + No sync status. + + + Sync currently running. + + + Not synced yet. + + + Task finished with warnings + + + Task finished with errors + + + Last sync: + Warning: Provider is not assigned to an application as backchannel provider. Update SCIM Provider - - Sync not run yet. - Run sync again @@ -2325,24 +2340,6 @@ doesn't pass when either or both of the selected options are equal or above the Vendor - - No sync status. - - - Sync currently running. - - - Not synced yet. - - - Task finished with warnings - - - Task finished with errors - - - Last sync: - Update LDAP Source @@ -3848,6 +3845,39 @@ doesn't pass when either or both of the selected options are equal or above the If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here. + + Network binding + + + No binding + + + Bind ASN + + + Bind ASN and Network + + + Bind ASN, Network and IP + + + Configure if sessions created by this stage should be bound to the Networks they were created in. + + + GeoIP binding + + + Bind Continent + + + Bind Continent and Country + + + Bind Continent, Country and City + + + Configure if sessions created by this stage should be bound to their GeoIP-based location + Terminate other sessions diff --git a/website/blog/2023-12-21-five-lessons-from-choosing-infrastructure-tooling/item.md b/website/blog/2023-12-21-five-lessons-from-choosing-infrastructure-tooling/item.md new file mode 100644 index 000000000..4f18882e4 --- /dev/null +++ b/website/blog/2023-12-21-five-lessons-from-choosing-infrastructure-tooling/item.md @@ -0,0 +1,164 @@ +--- +title: "Building the dream infrastructure stack for a security startup: preparing for human and technical scaling" +description: "What's in our stack: the tools we use to build authentik (and why we chose them)." +slug: 2023-12-21-five-lessons-from-choosing-infrastructure-tooling +authors: + - name: Marc Schmitt + title: Infrastructure Engineer at Authentik Security Inc + url: https://github.com/rissson + image_url: https://github.com/rissson.png + - name: Rebecca Dodd + title: Contributing Writer + url: https://www.thebasementoffice.co.uk + image_url: https://github.com/rebeccadee.png +tags: + - authentik + - startups + - infrastructure tooling + - tools + - technology stack + - Loki + - Argo CD + - Prometheus + - Thanos + - Transifex + - Lit + - Redis + - Grafana + - authentication + - Authentik Security +hide_table_of_contents: false +image: ./tech-stack1.png +--- + +> **_authentik is an open source Identity Provider that unifies your identity needs into a single platform, replacing Okta, Active Directory, and auth0. Authentik Security is a [public benefit company](https://github.com/OpenCoreVentures/ocv-public-benefit-company/blob/main/ocv-public-benefit-company-charter.md) building on top of the open source project._** + +--- + +With great power (to choose your own tools) comes great responsibility. Not inheriting a legacy toolchain is an infrastructure engineer’s dream, but it can be hard to know where to start. + +As the first infrastructure engineer hired to work on authentik, I saw the greenfield opportunities, but also the responsibility and long-term importance of choosing the best stack of tools and build processes. From my past roles, I already knew many of the considerations we would need to factor in. + +For example, we know that ease of maintenance is a primary consideration, as is the stability and probable longevity of the tool, how well the tools integrate, and of course the level of support we were likely to get for each tool. + +In this post we share some of what we are using to build authentik, and the lessons behind those choices. +![technology stack for startups](./tech-stack1.png) + + + +## #1 Choices are often human, not technical + +If there isn’t much difference between two tools, the choice isn’t a technical decision. It’s going to come down to human factors like ease of use or the team’s familiarity with the tool. This is why we use [GitHub Actions](https://docs.github.com/en/actions) for our CI—[we’re already on GitHub](https://github.com/goauthentik) so it just makes sense. + +> Familiarity with a tool means that you and your team can move faster, leading to higher business efficiency and a happier team. + +### We use Argo CD for GitOps + +When I joined Authentik Security, we were using [Flux CD](https://fluxcd.io/). Jens, our founder and CTO, had set up a small Kubernetes cluster to run an authentik instance for us to log into different services (some monitoring tools), and he was deploying all of this using Flux CD. + +If you’re not familiar, Flux and [Argo CD](https://argo-cd.readthedocs.io/en/stable/) enable you to do GitOps: whatever you want to deploy, you push that to a Git repository and then synchronize whatever is in production from that Git repository. Everything is committed and tracked in the Git history (helping you to understand what has changed and why). + +You also don’t need to do anything manually on the production servers or clusters—it’s all done in Git. This helps with auditing, as history is tracked, and you can easily find who made a change. You don’t need to give access to your production servers and cluster to whoever is conducting the audit, since they can see how everything is configured in the Git repo. + +#### Flux and Argo CD essentially do the same thing + +Despite Flux and Argo CD both being good at what they do, I advocated for switching to Argo CD because I have always worked with it and that familiarity with the tool meant I’d be able to work with much greater efficiency and velocity. + +Since switching to Argo CD, we’ve automated deployment of new pull requests with the `deploy me` label. A developer can add that label to one of their open PRs, and the changes get deployed to a production-like environment so they can test those changes with a real domain and real certificates—it’s exactly the same as how a client would interact with those changes. It’s especially useful for mobile app development because instead of launching an authentik instance locally, you can test the mobile app against a production-like environment. This ability to access this “test deployment” is great for QA, tech writers, technical marketing teams, and anyone else who needs early access to a feature before it even gets merged. + +#### Setting us up to scale + +Argo CD also comes with a built-in UI, which Flux does not. This is useful because as we grow as a company, we will have more developers and we want to enable self-service and a culture of “you build it, you run it.” + +With the Argo CD UI, a developer can make changes in Git, view the changes in the UI, and validate if the application started correctly and if everything is running. There’s no need to build another tool or set up Grafana dashboards or some other solution for developers to check if the application is running correctly. + +“You build it, you run it” in this case isn’t about operations or infrastructure leaving developers to figure things out on their own. What we actually want is to empower devs to run things themselves so that: + +1. Everyone shares the burden of production. +2. Developers have a shorter feedback loop to see how their app behaves in production. + +This type of choice is about setting things up for scalability down the road, which leads me to our next lesson. + +## #2 Build with scale in mind + +Our founder, Jens, has written before about [building apps with scale in mind](https://goauthentik.io/blog/2023-06-13-building-apps-with-scale-in-mind) and [doing things the ‘right’ way first time](https://goauthentik.io/blog/2023/10/26/you-might-be-doing-containers-wrong/). + +As an infrastructure engineer especially, it can be so hard to deal with legacy tools and solutions (sometimes you just want to burn it all down and start over). It’s just so much easier to maintain things if you do it properly from the beginning. Part of why I wanted to join Authentik Security was because there wasn’t any legacy to deal with! + +Yes, premature optimization is the root of all evil, but that doesn’t mean you can’t think about scalability when designing something. Having a design that can scale up if we need it to, but that can also run with few resources (human or machine)—even if a few compromises are necessary to allow it to do so—is oftentimes better that having a design that wasn’t built with scale in mind. This can spare you having to redesign it later (and then on top of that, migrate the old one). + +### We use Transifex for translation + +Internationalization isn’t often high on the list for open source projects or developer tool companies, but we’ve been doing it with [Transifex](https://www.transifex.com/). + +If your users are developers they are probably used to working with tools in English. Whoever administers authentik for a company in France, for example, probably knows enough English to get by. But that company’s users may not need to speak English at all in their role because they’re on the legal or finance side. Those users still need to log in using authentik, so it’s great to be able to provide it in their language. + +We use [Lit](https://lit.dev/) for our frontend (Jens has written about [choosing Lit over React](https://goauthentik.io/blog/2023-05-04-i-gambled-against-react-and-lost)), which supports translation by default: + +- With Lit, we’re able to extract strings of text that we want to translate. +- Those strings are sent to Transifex, where we can crowdsource translations. +- We do this by marking strings as “source strings” with just three extra characters per string, which is not that much of an effort if you’re doing it from the outset vs implementing afterwards. + +Native speakers of a given language can help us polish our translations; this is a great way to enable people to contribute to the project (not everyone can or wants to contribute code, for example). + +## #3 Think about product-specific requirements + +As a security company, some of our choices are influenced by product- and industry-specific needs. + +As you’re building your own stack, you may need to think about the requirements of your own product space or industry. You might have customer expectations to meet or compliance requirements, for example. + +### We use Redis for reputation data + +Most of our storage is done in [PostgreSQL](https://www.postgresql.org/), but for some types of storage we use [Redis](https://redis.io/) for latency reasons, as it’s much faster to fetch data from. + +We have two use cases for Redis: + +#### Reputation data + +If someone tries to log in and fails, we temporarily store ‘bad reputation’ weights associated with their IP address in Redis. This enables authentik admins to manage logins more securely; if someone has a reputation of less than a certain threshold (because they tried bad login details a few too many times), the authentik admin can block them. + +That data is stored in Redis temporarily; we have a subsequent task that fetches it from Redis and stores it in the database. That way, if you want to keep updating the reputation data for a user (because they keep trying to log in with bad inputs), we’re just updating Redis and not PostgreSQL every time. Then when that data is moved from Redis to PostgreSQL, it’s compacted. + +#### Session data + +This use case is more common: with every request, we check that the user is still logged in or if they need to log in. Again, we store this data in Redis for latency reasons. + +# #4 Your choices will cost you, one way or another + +Of course, budget is going to play a role in the tools you choose. You have to balance your investments of money OR time spent on your tooling. + +### We use Loki for logging + +We talked about this in our recent [post about building a security stack with mostly free and open source software](https://goauthentik.io/blog/2023-11-22-how-we-saved-over-100k). As we wrote, [Loki](https://grafana.com/oss/loki/) is free, open source, and cheap to run. We could have gone with something like Elasticsearch (and the whole Elastic Stack) but it’s so expensive to run in terms of processing power and memory resources. Loki isn’t as easy to run, but we save on costs. + +> It comes back to the idea of “you either pay in time or money” for software, and for most of authentik’s tooling I’ve already paid in time for it. + +## #5 Optimize for stability and support + +Infrastructure people just want to be able to sleep at night and not get paged at all hours (for that we use [Alertmanager](https://prometheus.io/docs/alerting/latest/alertmanager/), just in case). I just wanted to set up things that work and are reliable—pragmatic reasons similar to [why we chose Python and Django](https://goauthentik.io/blog/2023-03-16-authentik-on-django-500-slower-to-run-but-200-faster-to-build). + +These next tools (all open source) were easy choices because I’ve already used them and know how to configure them properly and what pitfalls to watch out for. + +### We use Grafana and Prometheus for monitoring and metrics + +[Grafana](https://grafana.com/grafana/) is widely used and comes with a lot of existing resources, so we don’t have to build everything ourselves. We use it to display our logs and metrics in dashboards so we can view and correlate data from Loki and [Prometheus](https://grafana.com/oss/prometheus/) together. Prometheus scrapes metrics from our infrastructure tools, authentik instances, and other applications. + +#### We use Thanos to manage metrics data + +I used Thanos for a large-scale deployment in a previous role—storing something like 20TB of metrics per six months—so I knew it would work for us now and later as we scale. + +Prometheus stores metrics for ~1 day before [Thanos](https://thanos.io/) fetches them and pushes to S3 storage for longer-term retention. We do this because Prometheus doesn’t do well storing large amounts of data. We’re also running Prometheus in highly available fashion, which would mean storing the data twice, which would be expensive. + +Thanos compresses the metrics data and also does downsampling: + +- For 30 days we keep everything that’s scraped (every 30/60 seconds) +- Beyond that, for 90 days we keep only a metric point every five minutes +- For a year, we keep just one metric point per hour + +By retaining less data as time passes, queries are faster and storage is cheaper. Why keep metrics for such a long time? It gives us a view of the seasonality of traffic so we can do better capacity planning. + +## Final thoughts + +This isn’t a groundbreaking stack. We aren’t optimizing for the latest edge technology. (The only somewhat controversial choice we’ve made has been moving to an [IPv6 only](https://goauthentik.io/blog/2023-11-09-IPv6-addresses) network.) For the most part we’ve gone with options that are tried and tested, well known to the team, and play nicely with the other parts of our stack. + +As always, we would be interested in hearing your thoughts about the stack we use, and about the tools that you and your team have chosen and the reasons behind those choices. Send us an email to hello@goauthentik.io or chime in on [Discord](https://discord.com/channels/809154715984199690/809154716507963434). diff --git a/website/blog/2023-12-21-five-lessons-from-choosing-infrastructure-tooling/tech-stack1.png b/website/blog/2023-12-21-five-lessons-from-choosing-infrastructure-tooling/tech-stack1.png new file mode 100644 index 000000000..5010a16f0 Binary files /dev/null and b/website/blog/2023-12-21-five-lessons-from-choosing-infrastructure-tooling/tech-stack1.png differ diff --git a/website/developer-docs/api/browser.mdx b/website/developer-docs/api/browser.mdx index 7aa35b209..4ea697cfe 100644 --- a/website/developer-docs/api/browser.mdx +++ b/website/developer-docs/api/browser.mdx @@ -1,38 +1,9 @@ +--- +hide_table_of_contents: true +--- + # API Browser -import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; -import useBaseUrl from "@docusaurus/useBaseUrl"; -import BrowserOnly from "@docusaurus/core/lib/client/exports/BrowserOnly"; -import { useColorMode } from "@docusaurus/theme-common"; - -export function APIBrowser() { - const context = useDocusaurusContext(); - const { siteConfig = {} } = context; - const { colorMode, setColorMode } = useColorMode(); - let bg = "#1b1b1d"; - if (colorMode === "light") { - bg = "#fff"; - } - return ( - - {() => { - import("rapidoc"); - return ( - - ); - }} - - ); -} +import APIBrowser from "../../src/components/APIBrowser"; diff --git a/website/docs/core/geoip.mdx b/website/docs/core/geoip.mdx index b3cedbe47..ff73e7a39 100644 --- a/website/docs/core/geoip.mdx +++ b/website/docs/core/geoip.mdx @@ -27,7 +27,8 @@ import TabItem from "@theme/TabItem"; Add the following block to your `.env` file: ```shell -AUTHENTIK_GEOIP=/tmp/non-existent-file +AUTHENTIK_EVENTS__CONTEXT_PROCESSORS__GEOIP=/tmp/non-existent-file +AUTHENTIK_EVENTS__CONTEXT_PROCESSORS__ASN=/tmp/non-existent-file ``` Afterwards, run the upgrade commands from the latest release notes. @@ -38,7 +39,10 @@ Add the following block to your `values.yml` file: ```yaml authentik: - geoip: /tmp/non-existent-file + events: + context_processors: + geoip: "/tmp/non-existent-file" + asn: "/tmp/non-existent-file" ``` Afterwards, run the upgrade commands from the latest release notes. @@ -74,7 +78,7 @@ services: volumes: - "geoip:/usr/share/GeoIP" environment: - GEOIPUPDATE_EDITION_IDS: "GeoLite2-City" + GEOIPUPDATE_EDITION_IDS: "GeoLite2-City GeoLite2-ASN" GEOIPUPDATE_FREQUENCY: "8" GEOIPUPDATE_ACCOUNT_ID: "*your account ID*" GEOIPUPDATE_LICENSE_KEY: "*your license key*" @@ -94,7 +98,7 @@ geoip: enabled: true accountId: "*your account ID*" licenseKey: "*your license key*" - editionIds: "GeoLite2-City" + editionIds: "GeoLite2-City GeoLite2-ASN" image: maxmindinc/geoipupdate:v4.8 updateInterval: 8 ``` diff --git a/website/docs/events/index.md b/website/docs/events/index.md index 612cc638e..856c3d907 100644 --- a/website/docs/events/index.md +++ b/website/docs/events/index.md @@ -231,6 +231,11 @@ A user authorizes an application. "action": "authorize_application", "app": "authentik.providers.oauth2.views.authorize", "context": { + "asn": { + "asn": 6805, + "as_org": "Telefonica Germany", + "network": "5.4.0.0/14" + }, "geo": { "lat": 42.0, "city": "placeholder", diff --git a/website/docs/flow/stages/email/email_recovery.png b/website/docs/flow/stages/email/email_recovery.png index 1dc5dbbc4..0bc14bafd 100644 Binary files a/website/docs/flow/stages/email/email_recovery.png and b/website/docs/flow/stages/email/email_recovery.png differ diff --git a/website/docs/flow/stages/email/index.mdx b/website/docs/flow/stages/email/index.mdx index 1064d8f13..a7751e295 100644 --- a/website/docs/flow/stages/email/index.mdx +++ b/website/docs/flow/stages/email/index.mdx @@ -25,6 +25,10 @@ return True You can also use custom email templates, to use your own design or layout. +:::info +Starting with authentik 2024.1, it is possible to create `.txt` files with the same name as the `.html` template. If a matching `.txt` file exists, the email sent will be a multipart email with both the text and HTML template. +::: + import Tabs from "@theme/Tabs"; import TabItem from "@theme/TabItem"; @@ -81,13 +85,17 @@ Templates are rendered using Django's templating engine. The following variables - `user`: The pending user object. - `expires`: The timestamp when the token expires. + + ```html -{# This is how you can write comments which aren't rendered. #} {# Extend this -template from the base email template, which includes base layout and CSS. #} {% -extends "email/base.html" %} {# Load the internationalization module to -translate strings, and humanize to show date-time #} {% load i18n %} {% load -humanize %} {# The email/base.html template uses a single "content" block #} {% -block content %} +{# This is how you can write comments which aren't rendered. #} +{# Extend this template from the base email template, which includes base layout and CSS. #} +{% extends "email/base.html" %} +{# Load the internationalization module to translate strings, and humanize to show date-time #} +{% load i18n %} +{% load humanize %} +{# The email/base.html template uses a single "content" block #} +{% block content %} {% blocktrans with username=user.username %} Hi {{ username }}, {% @@ -99,9 +107,9 @@ block content %} @@ -130,8 +138,7 @@ block content %} href="{{ url }}" rel="noopener noreferrer" target="_blank" - >{% trans 'Reset - Password' %}{% trans 'Reset Password' %} @@ -145,9 +152,9 @@ block content %}
- {% blocktrans %} You recently requested to change your - password for you authentik account. Use the button below to - set a new password. {% endblocktrans %} + {% blocktrans %} + You recently requested to change your password for you authentik account. Use the button below to set a new password. + {% endblocktrans %}
- {% blocktrans with expires=expires|naturaltime %} If you did - not request a password change, please ignore this Email. The - link above is valid for {{ expires }}. {% endblocktrans %} + {% blocktrans with expires=expires|naturaltime %} + If you did not request a password change, please ignore this Email. The link above is valid for {{ expires }}. + {% endblocktrans %}
@@ -155,3 +162,5 @@ block content %} {% endblock %} ``` + + diff --git a/website/docs/flow/stages/user_login/index.md b/website/docs/flow/stages/user_login/index.md index 324aff1a4..92d655093 100644 --- a/website/docs/flow/stages/user_login/index.md +++ b/website/docs/flow/stages/user_login/index.md @@ -32,6 +32,51 @@ When this is set to a higher value than the default _seconds=0_, a prompt is sho ![](./stay_signed_in.png) +## Network binding/GeoIP binding + +When configured, all sessions authenticated by this stage will be bound to the selected network/GeoIP criteria. + +Sessions which break this binding will be terminated on use. The created [`logout`](../../../events/index.md#logout) event will contain additional data related to what caused the binding to be broken: + +```json + +Context +{ + "asn": { + "asn": 6805, + "as_org": "Telefonica Germany", + "network": "5.4.0.0/14" + }, + "geo": { + "lat": 51.2993, + "city": "", + "long": 9.491, + "country": "DE", + "continent": "EU" + }, + "binding": { + "reason": "network.missing", + "new_value": { + "asn": 6805, + "as_org": "Telefonica Germany", + "network": "5.4.0.0/14" + }, + "previous_value": {} + }, + "ip": { + "previous": "1.2.3.4", + "new": "5.6.7.8", + }, + "http_request": { + "args": {}, + "path": "/if/admin/", + "method": "GET", + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" + }, + "logout_reason": "Session binding broken" +} +``` + ## Terminate other sessions When enabled, previous sessions of the user logging in will be revoked. This has no affect on OAuth refresh tokens. diff --git a/website/docs/installation/configuration.mdx b/website/docs/installation/configuration.mdx index e2d15eb7c..034776ec5 100644 --- a/website/docs/installation/configuration.mdx +++ b/website/docs/installation/configuration.mdx @@ -170,9 +170,13 @@ Defaults to `info`. Which domain the session cookie should be set to. By default, the cookie is set to the domain authentik is accessed under. -### `AUTHENTIK_GEOIP` +### `AUTHENTIK_EVENTS__CONTEXT_PROCESSORS__GEOIP` -Path to the GeoIP database. Defaults to `/geoip/GeoLite2-City.mmdb`. If the file is not found, authentik will skip GeoIP support. +Path to the GeoIP City database. Defaults to `/geoip/GeoLite2-City.mmdb`. If the file is not found, authentik will skip GeoIP support. + +### `AUTHENTIK_EVENTS__CONTEXT_PROCESSORS__ASN` + +Path to the GeoIP ASN database. Defaults to `/geoip/GeoLite2-ASN.mmdb`. If the file is not found, authentik will skip GeoIP support. ### `AUTHENTIK_DISABLE_UPDATE_CHECK` diff --git a/website/docs/policies/expression.mdx b/website/docs/policies/expression.mdx index 2200cc96f..aaf850608 100644 --- a/website/docs/policies/expression.mdx +++ b/website/docs/policies/expression.mdx @@ -54,6 +54,11 @@ import Objects from "../expressions/_objects.md"; - `request.context`: A dictionary with dynamic data. This depends on the origin of the execution. - `geoip`: GeoIP object, see [GeoIP](https://geoip2.readthedocs.io/en/latest/#geoip2.models.City) + + ```python + return context["geoip"].country.iso_code == "US" + ``` + - `ak_is_sso_flow`: Boolean which is true if request was initiated by authenticating through an external provider. - `ak_client_ip`: Client's IP Address or 255.255.255.255 if no IP Address could be extracted. Can be [compared](#comparing-ip-addresses), for example diff --git a/website/docs/releases/2023/v2023.10.md b/website/docs/releases/2023/v2023.10.md index a1f74470c..1034d0115 100644 --- a/website/docs/releases/2023/v2023.10.md +++ b/website/docs/releases/2023/v2023.10.md @@ -165,6 +165,22 @@ helm upgrade authentik authentik/authentik -f values.yaml --version ^2023.10 - stages/email: use uuid for email confirmation token instead of username (cherry-pick #7581) (#7584) - web/admin: fix admins not able to delete MFA devices (#7660) +## Fixed in 2023.10.5 + +- blueprints: improve file change handler (cherry-pick #7813) (#7934) +- events: add better fallback for sanitize_item to ensure everything can be saved as JSON (cherry-pick #7694) (#7937) +- events: fix lint (#7700) +- events: include user agent in events (cherry-pick #7693) (#7938) +- providers/scim: change familyName default (cherry-pick #7904) (#7930) +- root: don't show warning when app has no URLs to import (cherry-pick #7765) (#7935) +- root: Fix cache related image build issues (cherry-pick #7831) (#7932) +- stages/email: improve error handling for incorrect template syntax (cherry-pick #7758) (#7936) +- tests: fix flaky tests (cherry-pick #7676) (#7939) +- web: dark/light theme fixes (#7872) +- web: fix overflow glitch on ak-page-header (cherry-pick #7883) (#7931) +- web/admin: always show oidc well-known URL fields when they're set (#7560) +- web/user: fix search not updating app (cherry-pick #7825) (#7933) + ## API Changes #### What's New diff --git a/website/docs/user-group-role/user/invitations.md b/website/docs/user-group-role/user/invitations.md index 7387543f5..e3e58f42e 100644 --- a/website/docs/user-group-role/user/invitations.md +++ b/website/docs/user-group-role/user/invitations.md @@ -18,7 +18,7 @@ The fastest way to create an invitation is to use our pre-defined `default-enrol To download the `default-enrollment-flow` file, run this command: ``` -wget https://raw.githubusercontent.com/goauthentik/authentik/main/website/developer-docs/blueprints/example/flows-enrollment-2-stage.yaml +wget https://goauthentik.io/blueprints/example/flows-enrollment-2-stage.yaml ``` Alternatively, use this [link](/blueprints/example/flows-enrollment-2-stage.yaml) to view and save the file. For more details, refer to the [documentation](https://goauthentik.io/docs/flow/examples/flows#enrollment-2-stage). diff --git a/website/src/components/APIBrowser/index.tsx b/website/src/components/APIBrowser/index.tsx new file mode 100644 index 000000000..25c43b743 --- /dev/null +++ b/website/src/components/APIBrowser/index.tsx @@ -0,0 +1,40 @@ +import React from "react"; +import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; +import useBaseUrl from "@docusaurus/useBaseUrl"; +import BrowserOnly from "@docusaurus/BrowserOnly"; +import { useColorMode } from "@docusaurus/theme-common"; + +export function APIBrowser() { + const context = useDocusaurusContext(); + const { siteConfig = {} } = context; + const { colorMode, setColorMode } = useColorMode(); + let bg = "#1b1b1d"; + if (colorMode === "light") { + bg = "#fff"; + } + return ( + + {() => { + require("rapidoc"); + return ( + + ); + }} + + ); +} + +export default APIBrowser; diff --git a/website/src/pages/api/index.jsx b/website/src/pages/api/index.jsx index 8e6647dc0..5ba633bdb 100644 --- a/website/src/pages/api/index.jsx +++ b/website/src/pages/api/index.jsx @@ -1,7 +1,7 @@ import React from "react"; import Layout from "@theme/Layout"; import Head from "@docusaurus/Head"; -import BrowserOnly from "@docusaurus/core/lib/client/exports/BrowserOnly"; +import BrowserOnly from "@docusaurus/BrowserOnly"; function APIPage() { return ( diff --git a/website/src/pages/api/v3.jsx b/website/src/pages/api/v3.jsx index 7cdd45289..8ea704650 100644 --- a/website/src/pages/api/v3.jsx +++ b/website/src/pages/api/v3.jsx @@ -1,7 +1,7 @@ import React from "react"; import Layout from "@theme/Layout"; import Head from "@docusaurus/Head"; -import BrowserOnly from "@docusaurus/core/lib/client/exports/BrowserOnly"; +import BrowserOnly from "@docusaurus/BrowserOnly"; function APIPage() { return ( diff --git a/website/src/pages/index.jsx b/website/src/pages/index.jsx index feea2b292..fcbf951fd 100644 --- a/website/src/pages/index.jsx +++ b/website/src/pages/index.jsx @@ -3,7 +3,7 @@ import clsx from "clsx"; import Layout from "@theme/Layout"; import Link from "@docusaurus/Link"; import Head from "@docusaurus/Head"; -import BrowserOnly from "@docusaurus/core/lib/client/exports/BrowserOnly"; +import BrowserOnly from "@docusaurus/BrowserOnly"; import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; import useBaseUrl from "@docusaurus/useBaseUrl"; import styles from "./styles.module.css"; diff --git a/website/src/pages/terraform-provider.jsx b/website/src/pages/terraform-provider.jsx index 47bfa52f1..e930a8a56 100644 --- a/website/src/pages/terraform-provider.jsx +++ b/website/src/pages/terraform-provider.jsx @@ -1,7 +1,7 @@ import React from "react"; import Layout from "@theme/Layout"; import Head from "@docusaurus/Head"; -import BrowserOnly from "@docusaurus/core/lib/client/exports/BrowserOnly"; +import BrowserOnly from "@docusaurus/BrowserOnly"; function TerraformProviderPage() { return (