Compare commits

..

4 Commits

73 changed files with 7588 additions and 8564 deletions

View File

@ -75,7 +75,7 @@ RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
/bin/sh -c "GEOIPUPDATE_LICENSE_KEY_FILE=/run/secrets/GEOIPUPDATE_LICENSE_KEY /usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0"
# Stage 4: Download uv
FROM ghcr.io/astral-sh/uv:0.7.13 AS uv
FROM ghcr.io/astral-sh/uv:0.7.12 AS uv
# Stage 5: Base python image
FROM ghcr.io/goauthentik/fips-python:3.13.4-slim-bookworm-fips AS python-base

View File

@ -94,7 +94,7 @@ gen-build: ## Extract the schema from the database
AUTHENTIK_DEBUG=true \
AUTHENTIK_TENANTS__ENABLED=true \
AUTHENTIK_OUTPOSTS__DISABLE_EMBEDDED_OUTPOST=true \
uv run ak make_blueprint_schema --file blueprints/schema.json
uv run ak make_blueprint_schema > blueprints/schema.json
AUTHENTIK_DEBUG=true \
AUTHENTIK_TENANTS__ENABLED=true \
AUTHENTIK_OUTPOSTS__DISABLE_EMBEDDED_OUTPOST=true \

View File

@ -72,33 +72,20 @@ class Command(BaseCommand):
"additionalProperties": True,
},
"entries": {
"anyOf": [
{
"type": "array",
"items": {"$ref": "#/$defs/blueprint_entry"},
},
{
"type": "object",
"additionalProperties": {
"type": "array",
"items": {"$ref": "#/$defs/blueprint_entry"},
},
},
],
"type": "array",
"items": {
"oneOf": [],
},
},
},
"$defs": {"blueprint_entry": {"oneOf": []}},
"$defs": {},
}
def add_arguments(self, parser):
parser.add_argument("--file", type=str)
@no_translations
def handle(self, *args, file: str, **options):
def handle(self, *args, **options):
"""Generate JSON Schema for blueprints"""
self.build()
with open(file, "w") as _schema:
_schema.write(dumps(self.schema, indent=4, default=Command.json_default))
self.stdout.write(dumps(self.schema, indent=4, default=Command.json_default))
@staticmethod
def json_default(value: Any) -> Any:
@ -125,7 +112,7 @@ class Command(BaseCommand):
}
)
model_path = f"{model._meta.app_label}.{model._meta.model_name}"
self.schema["$defs"]["blueprint_entry"]["oneOf"].append(
self.schema["properties"]["entries"]["items"]["oneOf"].append(
self.template_entry(model_path, model, serializer)
)
@ -147,7 +134,7 @@ class Command(BaseCommand):
"id": {"type": "string"},
"state": {
"type": "string",
"enum": sorted([s.value for s in BlueprintEntryDesiredState]),
"enum": [s.value for s in BlueprintEntryDesiredState],
"default": "present",
},
"conditions": {"type": "array", "items": {"type": "boolean"}},
@ -218,7 +205,7 @@ class Command(BaseCommand):
"type": "object",
"required": ["permission"],
"properties": {
"permission": {"type": "string", "enum": sorted(perms)},
"permission": {"type": "string", "enum": perms},
"user": {"type": "integer"},
"role": {"type": "string"},
},

View File

@ -1,11 +1,10 @@
version: 1
entries:
foo:
- identifiers:
name: "%(id)s"
slug: "%(id)s"
model: authentik_flows.flow
state: present
attrs:
designation: stage_configuration
title: foo
- identifiers:
name: "%(id)s"
slug: "%(id)s"
model: authentik_flows.flow
state: present
attrs:
designation: stage_configuration
title: foo

View File

@ -191,18 +191,11 @@ class Blueprint:
"""Dataclass used for a full export"""
version: int = field(default=1)
entries: list[BlueprintEntry] | dict[str, list[BlueprintEntry]] = field(default_factory=list)
entries: list[BlueprintEntry] = field(default_factory=list)
context: dict = field(default_factory=dict)
metadata: BlueprintMetadata | None = field(default=None)
def iter_entries(self) -> Iterable[BlueprintEntry]:
if isinstance(self.entries, dict):
for _section, entries in self.entries.items():
yield from entries
else:
yield from self.entries
class YAMLTag:
"""Base class for all YAML Tags"""
@ -233,7 +226,7 @@ class KeyOf(YAMLTag):
self.id_from = node.value
def resolve(self, entry: BlueprintEntry, blueprint: Blueprint) -> Any:
for _entry in blueprint.iter_entries():
for _entry in blueprint.entries:
if _entry.id == self.id_from and _entry._state.instance:
# Special handling for PolicyBindingModels, as they'll have a different PK
# which is used when creating policy bindings

View File

@ -384,7 +384,7 @@ class Importer:
def _apply_models(self, raise_errors=False) -> bool:
"""Apply (create/update) models yaml"""
self.__pk_map = {}
for entry in self._import.iter_entries():
for entry in self._import.entries:
model_app_label, model_name = entry.get_model(self._import).split(".")
try:
model: type[SerializerModel] = registry.get_model(model_app_label, model_name)

View File

@ -47,7 +47,7 @@ class MetaModelRegistry:
models = apps.get_models()
for _, value in self.models.items():
models.append(value)
return sorted(models, key=str)
return models
def get_model(self, app_label: str, model_id: str) -> type[Model]:
"""Get model checks if any virtual models are registered, and falls back

View File

@ -1,9 +1,11 @@
"""Websocket tests"""
from dataclasses import asdict
from unittest.mock import patch
from channels.routing import URLRouter
from channels.testing import WebsocketCommunicator
from django.contrib.contenttypes.models import ContentType
from django.test import TransactionTestCase
from authentik import __version__
@ -14,6 +16,12 @@ from authentik.providers.proxy.models import ProxyProvider
from authentik.root import websocket
def patched__get_ct_cached(app_label, codename):
"""Caches `ContentType` instances like its `QuerySet` does."""
return ContentType.objects.get(app_label=app_label, permission__codename=codename)
@patch("guardian.shortcuts._get_ct_cached", patched__get_ct_cached)
class TestOutpostWS(TransactionTestCase):
"""Websocket tests"""

View File

@ -15,7 +15,6 @@ class OAuth2Error(SentryIgnoredException):
error: str
description: str
cause: str | None = None
def create_dict(self):
"""Return error as dict for JSON Rendering"""
@ -35,10 +34,6 @@ class OAuth2Error(SentryIgnoredException):
**kwargs,
)
def with_cause(self, cause: str):
self.cause = cause
return self
class RedirectUriError(OAuth2Error):
"""The request fails due to a missing, invalid, or mismatching

View File

@ -12,7 +12,7 @@ from authentik.core.tests.utils import create_test_admin_user, create_test_flow
from authentik.events.models import Event, EventAction
from authentik.lib.generators import generate_id
from authentik.lib.utils.time import timedelta_from_string
from authentik.providers.oauth2.constants import SCOPE_OFFLINE_ACCESS, SCOPE_OPENID, TOKEN_TYPE
from authentik.providers.oauth2.constants import TOKEN_TYPE
from authentik.providers.oauth2.errors import AuthorizeError, ClientIdError, RedirectUriError
from authentik.providers.oauth2.models import (
AccessToken,
@ -43,7 +43,7 @@ class TestAuthorize(OAuthTestCase):
authorization_flow=create_test_flow(),
redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "http://local.invalid/Foo")],
)
with self.assertRaises(AuthorizeError) as cm:
with self.assertRaises(AuthorizeError):
request = self.factory.get(
"/",
data={
@ -53,7 +53,6 @@ class TestAuthorize(OAuthTestCase):
},
)
OAuthAuthorizationParams.from_request(request)
self.assertEqual(cm.exception.error, "unsupported_response_type")
def test_invalid_client_id(self):
"""Test invalid client ID"""
@ -69,7 +68,7 @@ class TestAuthorize(OAuthTestCase):
authorization_flow=create_test_flow(),
redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "http://local.invalid/Foo")],
)
with self.assertRaises(AuthorizeError) as cm:
with self.assertRaises(AuthorizeError):
request = self.factory.get(
"/",
data={
@ -80,30 +79,19 @@ class TestAuthorize(OAuthTestCase):
},
)
OAuthAuthorizationParams.from_request(request)
self.assertEqual(cm.exception.error, "request_not_supported")
def test_invalid_redirect_uri_missing(self):
"""test missing redirect URI"""
OAuth2Provider.objects.create(
name=generate_id(),
client_id="test",
authorization_flow=create_test_flow(),
redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "http://local.invalid")],
)
with self.assertRaises(RedirectUriError) as cm:
request = self.factory.get("/", data={"response_type": "code", "client_id": "test"})
OAuthAuthorizationParams.from_request(request)
self.assertEqual(cm.exception.cause, "redirect_uri_missing")
def test_invalid_redirect_uri(self):
"""test invalid redirect URI"""
"""test missing/invalid redirect URI"""
OAuth2Provider.objects.create(
name=generate_id(),
client_id="test",
authorization_flow=create_test_flow(),
redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "http://local.invalid")],
)
with self.assertRaises(RedirectUriError) as cm:
with self.assertRaises(RedirectUriError):
request = self.factory.get("/", data={"response_type": "code", "client_id": "test"})
OAuthAuthorizationParams.from_request(request)
with self.assertRaises(RedirectUriError):
request = self.factory.get(
"/",
data={
@ -113,7 +101,6 @@ class TestAuthorize(OAuthTestCase):
},
)
OAuthAuthorizationParams.from_request(request)
self.assertEqual(cm.exception.cause, "redirect_uri_no_match")
def test_blocked_redirect_uri(self):
"""test missing/invalid redirect URI"""
@ -121,9 +108,9 @@ class TestAuthorize(OAuthTestCase):
name=generate_id(),
client_id="test",
authorization_flow=create_test_flow(),
redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "data:localhost")],
redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "data:local.invalid")],
)
with self.assertRaises(RedirectUriError) as cm:
with self.assertRaises(RedirectUriError):
request = self.factory.get(
"/",
data={
@ -133,7 +120,6 @@ class TestAuthorize(OAuthTestCase):
},
)
OAuthAuthorizationParams.from_request(request)
self.assertEqual(cm.exception.cause, "redirect_uri_forbidden_scheme")
def test_invalid_redirect_uri_empty(self):
"""test missing/invalid redirect URI"""
@ -143,6 +129,9 @@ class TestAuthorize(OAuthTestCase):
authorization_flow=create_test_flow(),
redirect_uris=[],
)
with self.assertRaises(RedirectUriError):
request = self.factory.get("/", data={"response_type": "code", "client_id": "test"})
OAuthAuthorizationParams.from_request(request)
request = self.factory.get(
"/",
data={
@ -161,9 +150,12 @@ class TestAuthorize(OAuthTestCase):
name=generate_id(),
client_id="test",
authorization_flow=create_test_flow(),
redirect_uris=[RedirectURI(RedirectURIMatchingMode.REGEX, "http://local.invalid?")],
redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "http://local.invalid?")],
)
with self.assertRaises(RedirectUriError) as cm:
with self.assertRaises(RedirectUriError):
request = self.factory.get("/", data={"response_type": "code", "client_id": "test"})
OAuthAuthorizationParams.from_request(request)
with self.assertRaises(RedirectUriError):
request = self.factory.get(
"/",
data={
@ -173,7 +165,6 @@ class TestAuthorize(OAuthTestCase):
},
)
OAuthAuthorizationParams.from_request(request)
self.assertEqual(cm.exception.cause, "redirect_uri_no_match")
def test_redirect_uri_invalid_regex(self):
"""test missing/invalid redirect URI (invalid regex)"""
@ -181,9 +172,12 @@ class TestAuthorize(OAuthTestCase):
name=generate_id(),
client_id="test",
authorization_flow=create_test_flow(),
redirect_uris=[RedirectURI(RedirectURIMatchingMode.REGEX, "+")],
redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "+")],
)
with self.assertRaises(RedirectUriError) as cm:
with self.assertRaises(RedirectUriError):
request = self.factory.get("/", data={"response_type": "code", "client_id": "test"})
OAuthAuthorizationParams.from_request(request)
with self.assertRaises(RedirectUriError):
request = self.factory.get(
"/",
data={
@ -193,22 +187,23 @@ class TestAuthorize(OAuthTestCase):
},
)
OAuthAuthorizationParams.from_request(request)
self.assertEqual(cm.exception.cause, "redirect_uri_no_match")
def test_redirect_uri_regex(self):
"""test valid redirect URI (regex)"""
def test_empty_redirect_uri(self):
"""test empty redirect URI (configure in provider)"""
OAuth2Provider.objects.create(
name=generate_id(),
client_id="test",
authorization_flow=create_test_flow(),
redirect_uris=[RedirectURI(RedirectURIMatchingMode.REGEX, ".+")],
)
with self.assertRaises(RedirectUriError):
request = self.factory.get("/", data={"response_type": "code", "client_id": "test"})
OAuthAuthorizationParams.from_request(request)
request = self.factory.get(
"/",
data={
"response_type": "code",
"client_id": "test",
"redirect_uri": "http://foo.bar.baz",
"redirect_uri": "http://localhost",
},
)
OAuthAuthorizationParams.from_request(request)
@ -263,7 +258,7 @@ class TestAuthorize(OAuthTestCase):
GrantTypes.IMPLICIT,
)
# Implicit without openid scope
with self.assertRaises(AuthorizeError) as cm:
with self.assertRaises(AuthorizeError):
request = self.factory.get(
"/",
data={
@ -290,7 +285,7 @@ class TestAuthorize(OAuthTestCase):
self.assertEqual(
OAuthAuthorizationParams.from_request(request).grant_type, GrantTypes.HYBRID
)
with self.assertRaises(AuthorizeError) as cm:
with self.assertRaises(AuthorizeError):
request = self.factory.get(
"/",
data={
@ -300,7 +295,6 @@ class TestAuthorize(OAuthTestCase):
},
)
OAuthAuthorizationParams.from_request(request)
self.assertEqual(cm.exception.error, "unsupported_response_type")
def test_full_code(self):
"""Test full authorization"""
@ -621,54 +615,3 @@ class TestAuthorize(OAuthTestCase):
},
},
)
def test_openid_missing_invalid(self):
"""test request requiring an OpenID scope to be set"""
OAuth2Provider.objects.create(
name=generate_id(),
client_id="test",
authorization_flow=create_test_flow(),
redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "http://localhost")],
)
request = self.factory.get(
"/",
data={
"response_type": "id_token",
"client_id": "test",
"redirect_uri": "http://localhost",
"scope": "",
},
)
with self.assertRaises(AuthorizeError) as cm:
OAuthAuthorizationParams.from_request(request)
self.assertEqual(cm.exception.cause, "scope_openid_missing")
@apply_blueprint("system/providers-oauth2.yaml")
def test_offline_access_invalid(self):
"""test request for offline_access with invalid response type"""
provider = OAuth2Provider.objects.create(
name=generate_id(),
client_id="test",
authorization_flow=create_test_flow(),
redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "http://localhost")],
)
provider.property_mappings.set(
ScopeMapping.objects.filter(
managed__in=[
"goauthentik.io/providers/oauth2/scope-openid",
"goauthentik.io/providers/oauth2/scope-offline_access",
]
)
)
request = self.factory.get(
"/",
data={
"response_type": "id_token",
"client_id": "test",
"redirect_uri": "http://localhost",
"scope": f"{SCOPE_OPENID} {SCOPE_OFFLINE_ACCESS}",
"nonce": generate_id(),
},
)
parsed = OAuthAuthorizationParams.from_request(request)
self.assertNotIn(SCOPE_OFFLINE_ACCESS, parsed.scope)

View File

@ -190,7 +190,7 @@ class OAuthAuthorizationParams:
allowed_redirect_urls = self.provider.redirect_uris
if not self.redirect_uri:
LOGGER.warning("Missing redirect uri.")
raise RedirectUriError("", allowed_redirect_urls).with_cause("redirect_uri_missing")
raise RedirectUriError("", allowed_redirect_urls)
if len(allowed_redirect_urls) < 1:
LOGGER.info("Setting redirect for blank redirect_uris", redirect=self.redirect_uri)
@ -219,14 +219,10 @@ class OAuthAuthorizationParams:
provider=self.provider,
)
if not match_found:
raise RedirectUriError(self.redirect_uri, allowed_redirect_urls).with_cause(
"redirect_uri_no_match"
)
raise RedirectUriError(self.redirect_uri, allowed_redirect_urls)
# Check against forbidden schemes
if urlparse(self.redirect_uri).scheme in FORBIDDEN_URI_SCHEMES:
raise RedirectUriError(self.redirect_uri, allowed_redirect_urls).with_cause(
"redirect_uri_forbidden_scheme"
)
raise RedirectUriError(self.redirect_uri, allowed_redirect_urls)
def check_scope(self, github_compat=False):
"""Ensure openid scope is set in Hybrid flows, or when requesting an id_token"""
@ -255,9 +251,7 @@ class OAuthAuthorizationParams:
or self.response_type in [ResponseTypes.ID_TOKEN, ResponseTypes.ID_TOKEN_TOKEN]
):
LOGGER.warning("Missing 'openid' scope.")
raise AuthorizeError(
self.redirect_uri, "invalid_scope", self.grant_type, self.state
).with_cause("scope_openid_missing")
raise AuthorizeError(self.redirect_uri, "invalid_scope", self.grant_type, self.state)
if SCOPE_OFFLINE_ACCESS in self.scope:
# https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess
# Don't explicitly request consent with offline_access, as the spec allows for
@ -292,9 +286,7 @@ class OAuthAuthorizationParams:
return
if not self.nonce:
LOGGER.warning("Missing nonce for OpenID Request")
raise AuthorizeError(
self.redirect_uri, "invalid_request", self.grant_type, self.state
).with_cause("none_missing")
raise AuthorizeError(self.redirect_uri, "invalid_request", self.grant_type, self.state)
def check_code_challenge(self):
"""PKCE validation of the transformation method."""
@ -353,10 +345,10 @@ class AuthorizationFlowInitView(PolicyAccessView):
self.request, github_compat=self.github_compat
)
except AuthorizeError as error:
LOGGER.warning(error.description, redirect_uri=error.redirect_uri, cause=error.cause)
LOGGER.warning(error.description, redirect_uri=error.redirect_uri)
raise RequestValidationError(error.get_response(self.request)) from None
except OAuth2Error as error:
LOGGER.warning(error.description, cause=error.cause)
LOGGER.warning(error.description)
raise RequestValidationError(
bad_request_message(self.request, error.description, title=error.error)
) from None

View File

@ -20,9 +20,6 @@ from authentik.lib.utils.time import timedelta_from_string
from authentik.policies.engine import PolicyEngine
from authentik.policies.views import PolicyAccessView
from authentik.providers.rac.models import ConnectionToken, Endpoint, RACProvider
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
PLAN_CONNECTION_SETTINGS = "connection_settings"
class RACStartView(PolicyAccessView):
@ -112,15 +109,10 @@ class RACFinalStage(RedirectStage):
return super().dispatch(request, *args, **kwargs)
def get_challenge(self, *args, **kwargs) -> RedirectChallenge:
settings = self.executor.plan.context.get(PLAN_CONNECTION_SETTINGS)
if not settings:
settings = self.executor.plan.context.get(PLAN_CONTEXT_PROMPT, {}).get(
PLAN_CONNECTION_SETTINGS
)
token = ConnectionToken.objects.create(
provider=self.provider,
endpoint=self.endpoint,
settings=settings or {},
settings=self.executor.plan.context.get("connection_settings", {}),
session=self.request.session["authenticatedsession"],
expires=now() + timedelta_from_string(self.provider.connection_expiry),
expiring=True,

View File

@ -3,38 +3,21 @@
import os
from argparse import ArgumentParser
from unittest import TestCase
from unittest.mock import patch
import pytest
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.test.runner import DiscoverRunner
from structlog.stdlib import get_logger
from authentik.lib.config import CONFIG
from authentik.lib.sentry import sentry_init
from authentik.root.signals import post_startup, pre_startup, startup
from tests.e2e.utils import get_docker_tag
# globally set maxDiff to none to show full assert error
TestCase.maxDiff = None
def get_docker_tag() -> str:
"""Get docker-tag based off of CI variables"""
env_pr_branch = "GITHUB_HEAD_REF"
default_branch = "GITHUB_REF"
branch_name = os.environ.get(default_branch, "main")
if os.environ.get(env_pr_branch, "") != "":
branch_name = os.environ[env_pr_branch]
branch_name = branch_name.replace("refs/heads/", "").replace("/", "-")
return f"gh-{branch_name}"
def patched__get_ct_cached(app_label, codename):
"""Caches `ContentType` instances like its `QuerySet` does."""
return ContentType.objects.get(app_label=app_label, permission__codename=codename)
class PytestTestRunner(DiscoverRunner): # pragma: no cover
"""Runs pytest to discover and run tests."""
@ -166,9 +149,8 @@ class PytestTestRunner(DiscoverRunner): # pragma: no cover
return 1
self.logger.info("Running tests", test_files=self.args)
with patch("guardian.shortcuts._get_ct_cached", patched__get_ct_cached):
try:
return pytest.main(self.args)
except Exception as e:
self.logger.error("Error running tests", error=str(e), test_files=self.args)
return 1
try:
return pytest.main(self.args)
except Exception as e:
self.logger.error("Error running tests", error=str(e), test_files=self.args)
return 1

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,7 @@
"version": "0.0.0",
"license": "MIT",
"devDependencies": {
"aws-cdk": "^2.1018.1",
"aws-cdk": "^2.1018.0",
"cross-env": "^7.0.3"
},
"engines": {
@ -17,9 +17,9 @@
}
},
"node_modules/aws-cdk": {
"version": "2.1018.1",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1018.1.tgz",
"integrity": "sha512-kFPRox5kSm+ktJ451o0ng9rD+60p5Kt1CZIWw8kXnvqbsxN2xv6qbmyWSXw7sGVXVwqrRKVj+71/JeDr+LMAZw==",
"version": "2.1018.0",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1018.0.tgz",
"integrity": "sha512-sppVsNtFJTW4wawS/PBudHCSNHb8xwaZ2WX1mpsfwaPNyTWm0eSUVJsRbRiRBu9O/Us8pgrd4woUjfM1lgD7Kw==",
"dev": true,
"license": "Apache-2.0",
"bin": {

View File

@ -10,7 +10,7 @@
"node": ">=20"
},
"devDependencies": {
"aws-cdk": "^2.1018.1",
"aws-cdk": "^2.1018.0",
"cross-env": "^7.0.3"
}
}

View File

@ -6,18 +6,18 @@
# Translators:
# jcamat, 2022
# Angel, 2024
# Iamanaws, 2024
# Marcelo Elizeche Landó, 2025
# Jens L. <jens@goauthentik.io>, 2025
# Iamanaws, 2025
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-06-04 00:12+0000\n"
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
"Last-Translator: Iamanaws, 2025\n"
"Last-Translator: Jens L. <jens@goauthentik.io>, 2025\n"
"Language-Team: Spanish (https://app.transifex.com/authentik/teams/119923/es/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@ -111,7 +111,7 @@ msgstr "Certificado Web usado por el servidor web Core de authentik"
#: authentik/brands/models.py
msgid "Certificates used for client authentication."
msgstr "Certificados utilizados para la autenticación del cliente."
msgstr ""
#: authentik/brands/models.py
msgid "Brand"
@ -131,7 +131,7 @@ msgstr "Descripción adicional no disponible."
#: authentik/core/api/groups.py
msgid "Cannot set group as parent of itself."
msgstr "No se puede establecer un grupo como su propio padre."
msgstr "No se puede establecer el grupo como padre de sí mismo."
#: authentik/core/api/providers.py
msgid ""
@ -183,11 +183,11 @@ msgstr "Remueve usuario del grupo"
#: authentik/core/models.py
msgid "Enable superuser status"
msgstr "Habilitar el estado de superusuario"
msgstr "Habiliar estado de \"superusuario\""
#: authentik/core/models.py
msgid "Disable superuser status"
msgstr "Deshabilitar el estado de superusuario"
msgstr "Deshabiliar estado de \"superusuario\""
#: authentik/core/models.py
msgid "User's display name."
@ -241,7 +241,7 @@ msgstr "Flujo utilizado al autorizar a este proveedor."
#: authentik/core/models.py
msgid "Flow used ending the session from a provider."
msgstr "Flujo utilizado para finalizar la sesión desde un proveedor."
msgstr "Flujo usado para terminar la sesión de un proveedor."
#: authentik/core/models.py
msgid ""
@ -273,11 +273,11 @@ msgstr "Aplicaciones"
#: authentik/core/models.py
msgid "Application Entitlement"
msgstr "Derecho de Aplicación"
msgstr ""
#: authentik/core/models.py
msgid "Application Entitlements"
msgstr "Derechos de Aplicación"
msgstr ""
#: authentik/core/models.py
msgid "Use the source-specific identifier"
@ -288,9 +288,9 @@ msgid ""
"Link to a user with identical email address. Can have security implications "
"when a source doesn't validate email addresses."
msgstr ""
"Enlace a un usuario con la misma dirección de correo electrónico. Puede "
"tener implicaciones de seguridad cuando una fuente no valida las direcciones"
" de correo electrónico."
"Apunta a un usuario con una dirección de correo electrónico idéntica. Puede "
"tener implicaciones de seguridad cuando una fuente no valida la dirección de"
" correo electrónico."
#: authentik/core/models.py
msgid ""
@ -305,8 +305,8 @@ msgid ""
"Link to a user with identical username. Can have security implications when "
"a username is used with another source."
msgstr ""
"Enlace a un usuario con el mismo nombre de usuario. Puede tener "
"implicaciones de seguridad cuando un nombre de usuario se utiliza con otra "
"Enlace a un usuario con un nombre de usuario idéntico. Puede tener "
"implicaciones de seguridad cuando se usa un nombre de usuario con otra "
"fuente."
#: authentik/core/models.py
@ -322,8 +322,8 @@ msgid ""
"Link to a group with identical name. Can have security implications when a "
"group name is used with another source."
msgstr ""
"Enlace a un grupo con el mismo nombre. Puede tener implicaciones de "
"seguridad cuando un nombre de grupo se utiliza con otra fuente."
"Enlace a un grupo con un nombre idéntico. Puede tener implicaciones de "
"seguridad cuando se utiliza un nombre de grupo con otra fuente."
#: authentik/core/models.py
msgid "Use the group name, but deny enrollment when the name already exists."
@ -385,7 +385,7 @@ msgstr "Asignaciones de Propiedades"
#: authentik/core/models.py
msgid "session data"
msgstr "datos de sesión"
msgstr ""
#: authentik/core/models.py
msgid "Session"
@ -424,7 +424,7 @@ msgstr "¡Autenticado exitosamente con {source}!"
#: authentik/core/sources/flow_manager.py
#, python-brace-format
msgid "Successfully linked {source}!"
msgstr "¡{source} enlazado correctamente!"
msgstr "¡{source} vinculado exitosamente!"
#: authentik/core/sources/flow_manager.py
msgid "Source is not configured for enrollment."
@ -476,11 +476,11 @@ msgstr ""
#: authentik/crypto/models.py
msgid "Certificate-Key Pair"
msgstr "Par Certificado-Clave"
msgstr "Par de claves de certificado"
#: authentik/crypto/models.py
msgid "Certificate-Key Pairs"
msgstr "Pares Certificado-Clave"
msgstr "Pares de claves de certificado"
#: authentik/enterprise/api.py
msgid "Enterprise is required to create/update this object."
@ -511,7 +511,7 @@ msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "Number of passwords to check against."
msgstr "Número de contraseñas contra las que verificar."
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py
@ -521,20 +521,18 @@ msgstr "La contraseña no se ha establecido en contexto"
#: authentik/enterprise/policies/unique_password/models.py
msgid "This password has been used previously. Please choose a different one."
msgstr ""
"Esta contraseña se ha utilizado anteriormente. Por favor, elija una "
"diferente."
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policy"
msgstr "Política de Unicidad de Contraseñas"
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policies"
msgstr "Políticas de Unicidad de Contraseñas"
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "User Password History"
msgstr "Historial de Contraseñas del Usuario"
msgstr ""
#: authentik/enterprise/policy.py
msgid "Enterprise required to access this feature."
@ -619,39 +617,39 @@ msgstr "Clave de firma"
#: authentik/enterprise/providers/ssf/models.py
msgid "Key used to sign the SSF Events."
msgstr "Clave utilizada para firmar los eventos SSF."
msgstr ""
#: authentik/enterprise/providers/ssf/models.py
msgid "Shared Signals Framework Provider"
msgstr "Proveedor del Marco de Señales Compartidas"
msgstr ""
#: authentik/enterprise/providers/ssf/models.py
msgid "Shared Signals Framework Providers"
msgstr "Proveedores del Marco de Señales Compartidas"
msgstr ""
#: authentik/enterprise/providers/ssf/models.py
msgid "Add stream to SSF provider"
msgstr "Agregar flujo de datos al proveedor SSF"
msgstr ""
#: authentik/enterprise/providers/ssf/models.py
msgid "SSF Stream"
msgstr "Flujo de Datos SSF"
msgstr ""
#: authentik/enterprise/providers/ssf/models.py
msgid "SSF Streams"
msgstr "Flujos de Datos SSF"
msgstr ""
#: authentik/enterprise/providers/ssf/models.py
msgid "SSF Stream Event"
msgstr "Evento de Flujo de Datos SSF"
msgstr ""
#: authentik/enterprise/providers/ssf/models.py
msgid "SSF Stream Events"
msgstr "Eventos de Flujos de Datos SSF"
msgstr ""
#: authentik/enterprise/providers/ssf/tasks.py
msgid "Failed to send request"
msgstr "Error al enviar la solicitud"
msgstr "Falló envio de petición"
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/models.py
msgid "Endpoint Authenticator Google Device Trust Connector Stage"
@ -683,29 +681,26 @@ msgid ""
"option has a higher priority than the `client_certificate` option on "
"`Brand`."
msgstr ""
"Configura las autoridades certificadoras para validar el certificado. Esta "
"opción tiene una prioridad mayor que la opción `client_certificate` en "
"`Brand`."
#: authentik/enterprise/stages/mtls/models.py
msgid "Mutual TLS Stage"
msgstr "Etapa de TLS mutuo"
msgstr ""
#: authentik/enterprise/stages/mtls/models.py
msgid "Mutual TLS Stages"
msgstr "Etapas de TLS mutuo"
msgstr ""
#: authentik/enterprise/stages/mtls/models.py
msgid "Permissions to pass Certificates for outposts."
msgstr "Permisos para pasar Certificados a los puestos avanzados."
msgstr ""
#: authentik/enterprise/stages/mtls/stage.py
msgid "Certificate required but no certificate was given."
msgstr "Se requiere certificado, pero no se proporcionó ninguno."
msgstr ""
#: authentik/enterprise/stages/mtls/stage.py
msgid "No user found for certificate."
msgstr "No se encontró usuario para el certificado."
msgstr ""
#: authentik/enterprise/stages/source/models.py
msgid ""
@ -758,16 +753,12 @@ msgid ""
"Customize the body of the request. Mapping should return data that is JSON-"
"serializable."
msgstr ""
"Personaliza el cuerpo de la solicitud. El mapeo debe devolver datos que sean"
" serializables en JSON."
#: authentik/events/models.py
msgid ""
"Configure additional headers to be sent. Mapping should return a dictionary "
"of key-value pairs"
msgstr ""
"Configura encabezados adicionales para enviar. El mapeo debe devolver un "
"diccionario de pares clave-valor"
#: authentik/events/models.py
msgid ""
@ -795,7 +786,7 @@ msgstr "Transporte de notificaciones"
#: authentik/events/models.py
msgid "Notification Transports"
msgstr "Medios de Notificación"
msgstr "Transportes de notificación"
#: authentik/events/models.py
msgid "Notice"
@ -822,9 +813,9 @@ 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 ""
"Selecciona qué medios se deben usar para notificar al usuario. Si no se "
"selecciona ninguno, la notificación solo se mostrará en la interfaz de "
"authentik."
"Seleccione qué transportes se deben usar para notificar al usuario. Si no se"
" selecciona ninguno, la notificación solo se mostrará en la interfaz de "
"usuario de authentik."
#: authentik/events/models.py
msgid "Controls which severity level the created notifications will have."
@ -996,7 +987,7 @@ msgstr "Evalúa políticas durante el proceso de planeación del Flujo."
#: authentik/flows/models.py
msgid "Evaluate policies when the Stage is presented to the user."
msgstr "Evaluar las políticas cuando la Etapa se presenta al usuario."
msgstr ""
#: authentik/flows/models.py
msgid ""
@ -1043,8 +1034,6 @@ msgid ""
"When enabled, provider will not modify or create objects in the remote "
"system."
msgstr ""
"Cuando está habilitado, el proveedor no modificará ni creará objetos en el "
"sistema remoto."
#: authentik/lib/sync/outgoing/tasks.py
msgid "Starting full provider sync"
@ -1052,21 +1041,20 @@ msgstr "Iniciando sincronización completa de proveedor"
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing users"
msgstr "Sincronizando usuarios"
msgstr ""
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing groups"
msgstr "Sincronizando grupos"
msgstr ""
#: authentik/lib/sync/outgoing/tasks.py
#, python-brace-format
msgid "Syncing page {page} of {object_type}"
msgstr "Sincronizando página {page} de {object_type}"
msgid "Syncing page {page} of groups"
msgstr "Sincronizando página {page} de grupos"
#: authentik/lib/sync/outgoing/tasks.py
msgid "Dropping mutating request due to dry run"
msgstr ""
"Descartando solicitud de mutación debido a ejecución en modo de simulación"
#: authentik/lib/sync/outgoing/tasks.py
#, python-brace-format
@ -1245,7 +1233,7 @@ msgstr ""
#: authentik/policies/expiry/models.py
msgid "Password has expired."
msgstr "La contraseña ha expirado."
msgstr "La contraseña ha caducado."
#: authentik/policies/expiry/models.py
msgid "Password Expiry Policy"
@ -1283,7 +1271,7 @@ msgstr "La IP del cliente no está en un país permitido."
#: authentik/policies/geoip/models.py
msgid "Distance from previous authentication is larger than threshold."
msgstr "La distancia desde la autenticación anterior es mayor que el umbral."
msgstr "La distancia desde la autenticación previa es mayor que el límite."
#: authentik/policies/geoip/models.py
msgid "Distance is further than possible."
@ -1332,7 +1320,7 @@ msgstr "Vinculación de Políticas"
#: authentik/policies/models.py
msgid "Policy Bindings"
msgstr "Vinculaciones de Políticas"
msgstr "Vinculaciones de políticas"
#: authentik/policies/models.py
msgid ""
@ -1606,11 +1594,11 @@ msgstr "ES256 (Encriptación Asimétrica)"
#: authentik/providers/oauth2/models.py
msgid "ES384 (Asymmetric Encryption)"
msgstr "ES384 (Encriptación Asimétrica)"
msgstr ""
#: authentik/providers/oauth2/models.py
msgid "ES512 (Asymmetric Encryption)"
msgstr "ES512 (Encriptación Asimétrica)"
msgstr ""
#: authentik/providers/oauth2/models.py
msgid "Scope used by the client"
@ -1825,7 +1813,7 @@ msgstr "Valida Certificados SSL de servidores de origen"
#: authentik/providers/proxy/models.py
msgid "Internal host SSL Validation"
msgstr "Validación SSL del host interno"
msgstr "Validación SSL de host interno"
#: authentik/providers/proxy/models.py
msgid ""
@ -2039,7 +2027,7 @@ msgstr ""
#: authentik/providers/saml/models.py
msgid "AuthnContextClassRef Property Mapping"
msgstr "Asignación de Propiedades de AuthnContextClassRef"
msgstr ""
#: authentik/providers/saml/models.py
msgid ""
@ -2047,9 +2035,6 @@ msgid ""
"empty, the AuthnContextClassRef will be set based on which authentication "
"methods the user used to authenticate."
msgstr ""
"Configura cómo se creará el valor de AuthnContextClassRef. Si se deja vacío,"
" el AuthnContextClassRef se establecerá según los métodos de autenticación "
"que el usuario haya utilizado para autenticarse."
#: authentik/providers/saml/models.py
msgid ""
@ -2199,11 +2184,11 @@ msgstr "Predeterminado"
#: authentik/providers/scim/models.py
msgid "AWS"
msgstr "AWS"
msgstr ""
#: authentik/providers/scim/models.py
msgid "Slack"
msgstr "Slack"
msgstr ""
#: authentik/providers/scim/models.py
msgid "Base URL to SCIM requests, usually ends in /v2"
@ -2215,13 +2200,11 @@ msgstr "Token de Autenticación"
#: authentik/providers/scim/models.py
msgid "SCIM Compatibility Mode"
msgstr "Modo de Compatibilidad SCIM"
msgstr ""
#: authentik/providers/scim/models.py
msgid "Alter authentik behavior for vendor-specific SCIM implementations."
msgstr ""
"Modificar el comportamiento de authentik para implementaciones SCIM "
"específicas de proveedores."
#: authentik/providers/scim/models.py
msgid "SCIM Provider"
@ -2249,7 +2232,7 @@ msgstr "Roles"
#: authentik/rbac/models.py
msgid "Initial Permissions"
msgstr "Permisos Iniciales"
msgstr ""
#: authentik/rbac/models.py
msgid "System permission"
@ -2287,7 +2270,7 @@ msgstr ""
#: authentik/recovery/views.py
msgid "Used recovery-link to authenticate."
msgstr "Se utilizó un enlace de recuperación para autenticarse."
msgstr "Se usó el enlace de recuperación para autenticarse."
#: authentik/sources/kerberos/models.py
msgid "Kerberos realm"
@ -2299,7 +2282,7 @@ msgstr "krb5.conf personalizado a usar. Usa el del sistema por defecto."
#: authentik/sources/kerberos/models.py
msgid "KAdmin server type"
msgstr "Tipo de servidor KAdmin"
msgstr ""
#: authentik/sources/kerberos/models.py
msgid "Sync users from Kerberos into authentik"
@ -2307,24 +2290,23 @@ msgstr "Sincronizar usuarios desde Kerberos hacia Authentik"
#: authentik/sources/kerberos/models.py
msgid "When a user changes their password, sync it back to Kerberos"
msgstr ""
"Cuando un usuario cambie su contraseña, sincronizarla de vuelta a Kerberos."
msgstr "Cuando un usuario cambia su contraseña, sincronizarlo hacia Kerberos"
#: authentik/sources/kerberos/models.py
msgid "Principal to authenticate to kadmin for sync."
msgstr "Principal para autenticarse en kadmin para la sincronización."
msgstr "Principal para autenticarse como kadmin para la sincronización."
#: authentik/sources/kerberos/models.py
msgid "Password to authenticate to kadmin for sync"
msgstr "Contraseña para autenticarse en kadmin para la sincronización"
msgstr "Contraseña para autenticarse como kadmin para la sincronización"
#: authentik/sources/kerberos/models.py
msgid ""
"Keytab to authenticate to kadmin for sync. Must be base64-encoded or in the "
"form TYPE:residual"
msgstr ""
"Keytab para autenticarse en kadmin para la sincronización. Debe estar "
"codificado en base64 o en el formato TIPO:residuo"
"Keytab para autenticarse como kadmin para la sincronización. Debe estar "
"codificado en base64 o en el formato TIPO:residual"
#: authentik/sources/kerberos/models.py
msgid ""
@ -2340,7 +2322,7 @@ msgid ""
"HTTP@hostname"
msgstr ""
"Forzar el uso de un nombre de servidor específico para SPNEGO. Debe estar en"
" el formato HTTP@nombre_de_host"
" el formato HTTP@nombredelservidor"
#: authentik/sources/kerberos/models.py
msgid "SPNEGO keytab base64-encoded or path to keytab in the form FILE:path"
@ -2357,8 +2339,8 @@ msgid ""
"If enabled, the authentik-stored password will be updated upon login with "
"the Kerberos password backend"
msgstr ""
"Si está habilitado, la contraseña almacenada en authentik se actualizará al "
"iniciar sesión con el backend de contraseñas de Kerberos."
"Si está habilitado, la contraseña almacenada por authentik se actualizada "
"al iniciar sesión con el backend de contraseñas Kerberos"
#: authentik/sources/kerberos/models.py
msgid "Kerberos Source"
@ -2406,7 +2388,7 @@ msgid ""
msgstr ""
"\n"
" Asegúrate de que tienes entradas válidas\n"
" (obtenibles mediante kinit) \n"
" (se obtienen a través de kinit) \n"
" y de haber configurado correctamente el navegador.\n"
" Por favor, contacta a tu administrador.\n"
" "
@ -2471,10 +2453,6 @@ msgstr "DN de grupo de adición"
msgid "Consider Objects matching this filter to be Users."
msgstr "Considere que los objetos que coinciden con este filtro son usuarios."
#: authentik/sources/ldap/models.py
msgid "Attribute which matches the value of `group_membership_field`."
msgstr "Atributo que coincide con el valor de `group_membership_field`."
#: authentik/sources/ldap/models.py
msgid "Field which contains members of a group."
msgstr "Campo que contiene los miembros de un grupo."
@ -2507,17 +2485,12 @@ msgid ""
"attribute. This allows nested group resolution on systems like FreeIPA and "
"Active Directory"
msgstr ""
"Buscar la pertenencia a grupos basándose en un atributo del usuario en lugar"
" de un atributo del grupo. Esto permite la resolución de grupos anidados en "
"sistemas como FreeIPA y Active Directory"
#: authentik/sources/ldap/models.py
msgid ""
"Delete authentik users and groups which were previously supplied by this "
"source, but are now missing from it."
msgstr ""
"Eliminar usuarios y grupos de authentik que fueron proporcionados "
"previamente por esta fuente, pero que ahora están ausentes."
#: authentik/sources/ldap/models.py
msgid "LDAP Source"
@ -2539,24 +2512,22 @@ msgstr "Asignaciones de Propiedades de Fuente de LDAP"
msgid ""
"Unique ID used while checking if this object still exists in the directory."
msgstr ""
"ID único utilizado para verificar si este objeto aún existe en el "
"directorio."
#: authentik/sources/ldap/models.py
msgid "User LDAP Source Connection"
msgstr "Conexión de Fuente LDAP de Usuario"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "User LDAP Source Connections"
msgstr "Conexiones de Fuente LDAP de Usuario"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "Group LDAP Source Connection"
msgstr "Conexión de Fuente LDAP de Grupo"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "Group LDAP Source Connections"
msgstr "Conexiones de Fuente LDAP de Grupo"
msgstr ""
#: authentik/sources/ldap/signals.py
msgid "Password does not match Active Directory Complexity."
@ -2568,11 +2539,11 @@ msgstr "No se recibió ningún token."
#: authentik/sources/oauth/models.py
msgid "HTTP Basic Authentication"
msgstr "Autenticación Básica HTTP"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "Include the client ID and secret as request parameters"
msgstr "Incluir el ID de cliente y el secreto como parámetros de la solicitud"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "Request Token URL"
@ -2619,8 +2590,6 @@ msgid ""
"How to perform authentication during an authorization_code token request "
"flow"
msgstr ""
"Cómo realizar la autenticación durante un flujo de solicitud de token con "
"authorization_code"
#: authentik/sources/oauth/models.py
msgid "OAuth Source"
@ -2938,7 +2907,7 @@ msgstr "Conexiones de Fuente de SAML de Grupo"
#: authentik/sources/saml/views.py
#, python-brace-format
msgid "Continue to {source_name}"
msgstr "Continuar a {source_name}"
msgstr ""
#: authentik/sources/scim/models.py
msgid "SCIM Source"
@ -2974,7 +2943,7 @@ msgstr "Dispositivos Duo"
#: authentik/stages/authenticator_email/models.py
msgid "Email OTP"
msgstr "OTP por Correo Electrónico"
msgstr ""
#: authentik/stages/authenticator_email/models.py
#: authentik/stages/email/models.py
@ -2995,11 +2964,11 @@ msgstr ""
#: authentik/stages/authenticator_email/models.py
msgid "Email Authenticator Setup Stage"
msgstr "Etapa de Configuración del Autenticador de Correo Electrónico"
msgstr ""
#: authentik/stages/authenticator_email/models.py
msgid "Email Authenticator Setup Stages"
msgstr "Etapas de Configuración del Autenticador de Correo Electrónico"
msgstr ""
#: authentik/stages/authenticator_email/models.py
#: authentik/stages/authenticator_email/stage.py
@ -3010,11 +2979,11 @@ msgstr ""
#: authentik/stages/authenticator_email/models.py
msgid "Email Device"
msgstr "Dispositivo de correo electrónico"
msgstr "Dispositivo de Email"
#: authentik/stages/authenticator_email/models.py
msgid "Email Devices"
msgstr "Dispositivos de correo electrónico"
msgstr "Dispositivos de Email"
#: authentik/stages/authenticator_email/stage.py
#: authentik/stages/authenticator_sms/stage.py
@ -3024,7 +2993,7 @@ msgstr "El código no coincide"
#: authentik/stages/authenticator_email/stage.py
msgid "Invalid email"
msgstr "Correo electrónico inválido"
msgstr "Email Inválido"
#: authentik/stages/authenticator_email/templates/email/email_otp.html
#: authentik/stages/email/templates/email/password_reset.html
@ -3044,9 +3013,6 @@ msgid ""
" Email MFA code.\n"
" "
msgstr ""
"\n"
" Código MFA por correo electrónico.\n"
" "
#: authentik/stages/authenticator_email/templates/email/email_otp.html
#, python-format
@ -3056,8 +3022,7 @@ msgid ""
" "
msgstr ""
"\n"
" Si no solicitaste este código, por favor ignora este correo. El código anterior es válido por %(expires)s.\n"
" "
"Si no solicitaste este código, por favor ignora este correo. El código anterior es válido por %(expires)s."
#: authentik/stages/authenticator_email/templates/email/email_otp.txt
#: authentik/stages/email/templates/email/password_reset.txt
@ -3070,8 +3035,6 @@ msgid ""
"\n"
"Email MFA code\n"
msgstr ""
"\n"
"Código MFA por correo electrónico\n"
#: authentik/stages/authenticator_email/templates/email/email_otp.txt
#, python-format
@ -3313,8 +3276,8 @@ msgstr "No se pudo validar el token"
msgid ""
"Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
msgstr ""
"Desfase después del cual expira el consentimiento. (Formato: "
"hours=1;minutes=2;seconds=3)."
"Compensación después de la cual caduca el consentimiento. (Formato: horas = "
"1; minutos = 2; segundos = 3)."
#: authentik/stages/consent/models.py
msgid "Consent Stage"
@ -3334,7 +3297,7 @@ msgstr "Consentimientos del usuario"
#: authentik/stages/consent/stage.py
msgid "Invalid consent token, re-showing prompt"
msgstr "Token de consentimiento inválido, mostrando el aviso nuevamente"
msgstr ""
#: authentik/stages/deny/models.py
msgid "Deny Stage"
@ -3354,11 +3317,11 @@ msgstr "Etapas ficticias"
#: authentik/stages/email/flow.py
msgid "Continue to confirm this email address."
msgstr "Continúa para confirmar esta dirección de correo electrónico."
msgstr ""
#: authentik/stages/email/flow.py
msgid "Link was already used, please request a new link."
msgstr "El enlace ya fue utilizado, por favor, solícita uno nuevo."
msgstr ""
#: authentik/stages/email/models.py
msgid "Password Reset"
@ -3482,8 +3445,7 @@ msgid ""
" "
msgstr ""
"\n"
" Si no solicitaste un cambio de contraseña, por favor ignora este correo. El enlace anterior es válido por %(expires)s.\n"
" "
"Si no solicitaste un cambio de contraseña, por favor ignora este correo. El enlace anterior es válido por %(expires)s."
#: authentik/stages/email/templates/email/password_reset.txt
msgid ""
@ -3567,26 +3529,24 @@ msgid ""
"Show the user the 'Remember me on this device' toggle, allowing repeat users"
" to skip straight to entering their password."
msgstr ""
"Mostrar al usuario la opción \"Recordarme en este dispositivo\", permitiendo"
" que los usuarios recurrentes pasen directamente a ingresar su contraseña."
#: authentik/stages/identification/models.py
msgid "Optional enrollment flow, which is linked at the bottom of the page."
msgstr ""
"Flujo de inscripción opcional, que se enlaza en la parte inferior de la "
"página."
"Flujo de inscripción opcional, que está vinculado en la parte inferior de la"
" página."
#: authentik/stages/identification/models.py
msgid "Optional recovery flow, which is linked at the bottom of the page."
msgstr ""
"Flujo de recuperación opcional, que se enlaza en la parte inferior de la "
"página."
"Flujo de recuperación opcional, que está vinculado en la parte inferior de "
"la página."
#: authentik/stages/identification/models.py
msgid "Optional passwordless flow, which is linked at the bottom of the page."
msgstr ""
"Flujo opcional sin contraseña, que se enlaza en la parte inferior de la "
"página."
"Flujo sin contraseña opcional, el cual está vinculado en la parte inferior "
"de la página."
#: authentik/stages/identification/models.py
msgid "Specify which sources should be shown."
@ -3820,11 +3780,11 @@ msgstr "Las contraseñas no coinciden."
#: authentik/stages/redirect/api.py
msgid "Target URL should be present when mode is Static."
msgstr "La URL de destino debe estar presente cuando el modo es Estático."
msgstr ""
#: authentik/stages/redirect/api.py
msgid "Target Flow should be present when mode is Flow."
msgstr "El Flujo de Destino debe estar presente cuando el modo es Flujo."
msgstr ""
#: authentik/stages/redirect/models.py
msgid "Redirect Stage"
@ -3881,6 +3841,10 @@ msgstr "Etapas de inicio de"
msgid "No Pending user to login."
msgstr "Ningún usuario pendiente para iniciar sesión."
#: authentik/stages/user_login/stage.py
msgid "Successfully logged in!"
msgstr "¡Se ha iniciado sesión correctamente!"
#: authentik/stages/user_logout/models.py
msgid "User Logout Stage"
msgstr "Etapa de cierre de sesión del usuario"
@ -3956,12 +3920,10 @@ msgstr ""
#: authentik/tenants/models.py
msgid "Reputation cannot decrease lower than this value. Zero or negative."
msgstr ""
"La reputación no puede disminuir por debajo de este valor. Cero o negativo."
#: authentik/tenants/models.py
msgid "Reputation cannot increase higher than this value. Zero or positive."
msgstr ""
"La reputación no puede aumentar por encima de este valor. Cero o positivo."
#: authentik/tenants/models.py
msgid "The option configures the footer links on the flow executor pages."
@ -3984,8 +3946,8 @@ msgstr "Personificación habilitada/deshabilitada globalmente."
#: authentik/tenants/models.py
msgid "Require administrators to provide a reason for impersonating a user."
msgstr ""
"Requerir que los administradores proporcionen una razón para personificar a "
"un usuario."
"Requerir a los administradores proporcionar una razón para suplantar un "
"usuario."
#: authentik/tenants/models.py
msgid "Default token duration"
@ -3997,7 +3959,7 @@ msgstr "Longitud predeterminada del token"
#: authentik/tenants/models.py
msgid "Tenant"
msgstr "Inquilino"
msgstr "inquilino"
#: authentik/tenants/models.py
msgid "Tenants"

View File

@ -40,7 +40,7 @@ dependencies = [
"gunicorn==23.0.0",
"jsonpatch==1.33",
"jwcrypto==1.5.6",
"kubernetes==33.1.0",
"kubernetes==32.0.1",
"ldap3==2.9.1",
"lxml==5.4.0",
"msgraph-sdk==1.33.0",
@ -56,13 +56,13 @@ dependencies = [
"pyyaml==6.0.2",
"requests-oauthlib==2.0.0",
"scim2-filter-parser==0.7.0",
"sentry-sdk==2.30.0",
"sentry-sdk==2.29.1",
"service-identity==24.2.0",
"setproctitle==1.3.6",
"structlog==25.4.0",
"swagger-spec-validator==3.0.4",
"tenant-schemas-celery==3.0.0",
"twilio==9.6.3",
"twilio==9.6.2",
"ua-parser==1.0.1",
"unidecode==1.4.0",
"urllib3<3",

View File

@ -2,6 +2,7 @@
from dataclasses import asdict
from time import sleep
from unittest.mock import patch
from guardian.shortcuts import assign_perm
from ldap3 import ALL, ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES, SUBTREE, Connection, Server
@ -15,10 +16,12 @@ from authentik.flows.models import Flow
from authentik.lib.generators import generate_id
from authentik.outposts.apps import MANAGED_OUTPOST
from authentik.outposts.models import Outpost, OutpostConfig, OutpostType
from authentik.outposts.tests.test_ws import patched__get_ct_cached
from authentik.providers.ldap.models import APIAccessMode, LDAPProvider
from tests.e2e.utils import SeleniumTestCase, retry
@patch("guardian.shortcuts._get_ct_cached", patched__get_ct_cached)
class TestProviderLDAP(SeleniumTestCase):
"""LDAP and Outpost e2e tests"""

View File

@ -6,6 +6,7 @@ from json import loads
from sys import platform
from time import sleep
from unittest.case import skip, skipUnless
from unittest.mock import patch
from channels.testing import ChannelsLiveServerTestCase
from jwt import decode
@ -17,10 +18,12 @@ from authentik.flows.models import Flow
from authentik.lib.generators import generate_id
from authentik.outposts.models import DockerServiceConnection, Outpost, OutpostConfig, OutpostType
from authentik.outposts.tasks import outpost_connection_discovery
from authentik.outposts.tests.test_ws import patched__get_ct_cached
from authentik.providers.proxy.models import ProxyProvider
from tests.e2e.utils import SeleniumTestCase, retry
@patch("guardian.shortcuts._get_ct_cached", patched__get_ct_cached)
class TestProviderProxy(SeleniumTestCase):
"""Proxy and Outpost e2e tests"""

View File

@ -4,6 +4,7 @@ from json import loads
from pathlib import Path
from time import sleep
from unittest import skip
from unittest.mock import patch
from selenium.webdriver.common.by import By
@ -12,10 +13,12 @@ from authentik.core.models import Application
from authentik.flows.models import Flow
from authentik.lib.generators import generate_id
from authentik.outposts.models import Outpost, OutpostType
from authentik.outposts.tests.test_ws import patched__get_ct_cached
from authentik.providers.proxy.models import ProxyMode, ProxyProvider
from tests.e2e.utils import SeleniumTestCase, retry
@patch("guardian.shortcuts._get_ct_cached", patched__get_ct_cached)
class TestProviderProxyForward(SeleniumTestCase):
"""Proxy and Outpost e2e tests"""

View File

@ -2,6 +2,7 @@
from dataclasses import asdict
from time import sleep
from unittest.mock import patch
from pyrad.client import Client
from pyrad.dictionary import Dictionary
@ -12,10 +13,12 @@ from authentik.core.models import Application, User
from authentik.flows.models import Flow
from authentik.lib.generators import generate_id, generate_key
from authentik.outposts.models import Outpost, OutpostConfig, OutpostType
from authentik.outposts.tests.test_ws import patched__get_ct_cached
from authentik.providers.radius.models import RadiusProvider
from tests.e2e.utils import SeleniumTestCase, retry
@patch("guardian.shortcuts._get_ct_cached", patched__get_ct_cached)
class TestProviderRadius(SeleniumTestCase):
"""Radius Outpost e2e tests"""

View File

@ -1,6 +1,7 @@
"""authentik e2e testing utilities"""
import json
import os
import socket
from collections.abc import Callable
from functools import lru_cache, wraps
@ -36,12 +37,22 @@ from authentik.core.api.users import UserSerializer
from authentik.core.models import User
from authentik.core.tests.utils import create_test_admin_user
from authentik.lib.generators import generate_id
from authentik.root.test_runner import get_docker_tag
IS_CI = "CI" in environ
RETRIES = int(environ.get("RETRIES", "3")) if IS_CI else 1
def get_docker_tag() -> str:
"""Get docker-tag based off of CI variables"""
env_pr_branch = "GITHUB_HEAD_REF"
default_branch = "GITHUB_REF"
branch_name = os.environ.get(default_branch, "main")
if os.environ.get(env_pr_branch, "") != "":
branch_name = os.environ[env_pr_branch]
branch_name = branch_name.replace("refs/heads/", "").replace("/", "-")
return f"gh-{branch_name}"
def get_local_ip() -> str:
"""Get the local machine's IP"""
hostname = socket.gethostname()

24
uv.lock generated
View File

@ -301,7 +301,7 @@ requires-dist = [
{ name = "gunicorn", specifier = "==23.0.0" },
{ name = "jsonpatch", specifier = "==1.33" },
{ name = "jwcrypto", specifier = "==1.5.6" },
{ name = "kubernetes", specifier = "==33.1.0" },
{ name = "kubernetes", specifier = "==32.0.1" },
{ name = "ldap3", specifier = "==2.9.1" },
{ name = "lxml", specifier = "==5.4.0" },
{ name = "msgraph-sdk", specifier = "==1.33.0" },
@ -317,13 +317,13 @@ requires-dist = [
{ name = "pyyaml", specifier = "==6.0.2" },
{ name = "requests-oauthlib", specifier = "==2.0.0" },
{ name = "scim2-filter-parser", specifier = "==0.7.0" },
{ name = "sentry-sdk", specifier = "==2.30.0" },
{ name = "sentry-sdk", specifier = "==2.29.1" },
{ name = "service-identity", specifier = "==24.2.0" },
{ name = "setproctitle", specifier = "==1.3.6" },
{ name = "structlog", specifier = "==25.4.0" },
{ name = "swagger-spec-validator", specifier = "==3.0.4" },
{ name = "tenant-schemas-celery", specifier = "==3.0.0" },
{ name = "twilio", specifier = "==9.6.3" },
{ name = "twilio", specifier = "==9.6.2" },
{ name = "ua-parser", specifier = "==1.0.1" },
{ name = "unidecode", specifier = "==1.4.0" },
{ name = "urllib3", specifier = "<3" },
@ -1772,7 +1772,7 @@ wheels = [
[[package]]
name = "kubernetes"
version = "33.1.0"
version = "32.0.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
@ -1787,9 +1787,9 @@ dependencies = [
{ name = "urllib3" },
{ name = "websocket-client" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ae/52/19ebe8004c243fdfa78268a96727c71e08f00ff6fe69a301d0b7fcbce3c2/kubernetes-33.1.0.tar.gz", hash = "sha256:f64d829843a54c251061a8e7a14523b521f2dc5c896cf6d65ccf348648a88993", size = 1036779, upload-time = "2025-06-09T21:57:58.521Z" }
sdist = { url = "https://files.pythonhosted.org/packages/b7/e8/0598f0e8b4af37cd9b10d8b87386cf3173cb8045d834ab5f6ec347a758b3/kubernetes-32.0.1.tar.gz", hash = "sha256:42f43d49abd437ada79a79a16bd48a604d3471a117a8347e87db693f2ba0ba28", size = 946691, upload-time = "2025-02-18T21:06:34.148Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/89/43/d9bebfc3db7dea6ec80df5cb2aad8d274dd18ec2edd6c4f21f32c237cbbb/kubernetes-33.1.0-py2.py3-none-any.whl", hash = "sha256:544de42b24b64287f7e0aa9513c93cb503f7f40eea39b20f66810011a86eabc5", size = 1941335, upload-time = "2025-06-09T21:57:56.327Z" },
{ url = "https://files.pythonhosted.org/packages/08/10/9f8af3e6f569685ce3af7faab51c8dd9d93b9c38eba339ca31c746119447/kubernetes-32.0.1-py2.py3-none-any.whl", hash = "sha256:35282ab8493b938b08ab5526c7ce66588232df00ef5e1dbe88a419107dc10998", size = 1988070, upload-time = "2025-02-18T21:06:31.391Z" },
]
[[package]]
@ -2931,15 +2931,15 @@ wheels = [
[[package]]
name = "sentry-sdk"
version = "2.30.0"
version = "2.29.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
{ name = "urllib3" },
]
sdist = { url = "https://files.pythonhosted.org/packages/04/4c/af31e0201b48469786ddeb1bf6fd3dfa3a291cc613a0fe6a60163a7535f9/sentry_sdk-2.30.0.tar.gz", hash = "sha256:436369b02afef7430efb10300a344fb61a11fe6db41c2b11f41ee037d2dd7f45", size = 326767, upload-time = "2025-06-12T10:34:34.733Z" }
sdist = { url = "https://files.pythonhosted.org/packages/22/67/d552a5f8e5a6a56b2feea6529e2d8ccd54349084c84176d5a1f7295044bc/sentry_sdk-2.29.1.tar.gz", hash = "sha256:8d4a0206b95fa5fe85e5e7517ed662e3888374bdc342c00e435e10e6d831aa6d", size = 325518, upload-time = "2025-05-19T14:27:38.512Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5a/99/31ac6faaae33ea698086692638f58d14f121162a8db0039e68e94135e7f1/sentry_sdk-2.30.0-py2.py3-none-any.whl", hash = "sha256:59391db1550662f746ea09b483806a631c3ae38d6340804a1a4c0605044f6877", size = 343149, upload-time = "2025-06-12T10:34:32.896Z" },
{ url = "https://files.pythonhosted.org/packages/f0/e5/da07b0bd832cefd52d16f2b9bbbe31624d57552602c06631686b93ccb1bd/sentry_sdk-2.29.1-py2.py3-none-any.whl", hash = "sha256:90862fe0616ded4572da6c9dadb363121a1ae49a49e21c418f0634e9d10b4c19", size = 341553, upload-time = "2025-05-19T14:27:36.882Z" },
]
[[package]]
@ -3151,7 +3151,7 @@ wheels = [
[[package]]
name = "twilio"
version = "9.6.3"
version = "9.6.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "aiohttp" },
@ -3159,9 +3159,9 @@ dependencies = [
{ name = "pyjwt" },
{ name = "requests" },
]
sdist = { url = "https://files.pythonhosted.org/packages/fb/af/1b401bc4cfd3eb41c7e2a98d0040d2bcfd2ad3217f3163401121179b3fb3/twilio-9.6.3.tar.gz", hash = "sha256:16a8c2ab9550343c25c8a195f31db9e230d9b341eca31ebdd301109910fd9730", size = 1041494, upload-time = "2025-06-12T10:40:55.63Z" }
sdist = { url = "https://files.pythonhosted.org/packages/fa/c9/441a07f6552f2b504812501d56c41bd85b02afeef6c23ab8baf41ed6c70e/twilio-9.6.2.tar.gz", hash = "sha256:5da13bb497e39ece34cb9f2b3bc911f3288928612748f7688b3bda262c2767a1", size = 1041300, upload-time = "2025-05-29T12:25:04.59Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c9/35/d61a3581eb223e5e1fc0add1c397d7bb60014b22790e8f89aa5eb4e41e04/twilio-9.6.3-py2.py3-none-any.whl", hash = "sha256:a9b2cf11b0718394f12c43585ca25b9094f12b82ff975f1561fcec7f0f6f49b2", size = 1909549, upload-time = "2025-06-12T10:40:53.67Z" },
{ url = "https://files.pythonhosted.org/packages/67/91/382e83e5d205a7ae4325b66d40cd2fa6ce85526f2ed8fc553265e19abbe4/twilio-9.6.2-py2.py3-none-any.whl", hash = "sha256:8d4af6f42850734a921857df42940f7fed84e3e4a508d0d6bef5b9fb7dc08357", size = 1909253, upload-time = "2025-05-29T12:25:02.521Z" },
]
[[package]]

92
web/package-lock.json generated
View File

@ -31,8 +31,8 @@
"@open-wc/lit-helpers": "^0.7.0",
"@patternfly/elements": "^4.1.0",
"@patternfly/patternfly": "^4.224.2",
"@sentry/browser": "^9.28.1",
"@spotlightjs/spotlight": "^3.0.0",
"@sentry/browser": "^9.28.0",
"@spotlightjs/spotlight": "^2.13.3",
"@webcomponents/webcomponentsjs": "^2.8.0",
"base64-js": "^1.5.1",
"change-case": "^5.4.4",
@ -4478,75 +4478,75 @@
"dev": true
},
"node_modules/@sentry-internal/browser-utils": {
"version": "9.28.1",
"resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-9.28.1.tgz",
"integrity": "sha512-P/FEZkT7UqTw9P/2n/Y4Aa1OtGP6dnCvyqzPPkjiRdVa7Ep7S5ElBJloGv7077TLLBtAfCsEUVRlM1F6/jQoaA==",
"version": "9.28.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-9.28.0.tgz",
"integrity": "sha512-SqntPnIXudP3FoKj4mQ1BVPC1RNzo4CGtAxJnLpbIUpdT/khJVM6Q59zrGl2MgZ7URZCI986L5jXihQeferf6g==",
"license": "MIT",
"dependencies": {
"@sentry/core": "9.28.1"
"@sentry/core": "9.28.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@sentry-internal/feedback": {
"version": "9.28.1",
"resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-9.28.1.tgz",
"integrity": "sha512-HOk/c26D3nlClO/xEefev8fIJzRA621PFQvNFPu/y0Z5HujEqSmIsrff0cXszPPYD95h4Mwk63E0ZYdspdeXcw==",
"version": "9.28.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-9.28.0.tgz",
"integrity": "sha512-z2jShmVENsesmDnShEOv841Saw0zXe1tX6GHNgkK9f6NrUMbL970JvGKByBFTffhQH6uQ0WeNPnXJ5L/YKnfDg==",
"license": "MIT",
"dependencies": {
"@sentry/core": "9.28.1"
"@sentry/core": "9.28.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@sentry-internal/replay": {
"version": "9.28.1",
"resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-9.28.1.tgz",
"integrity": "sha512-Tv9pkfAX+1bmhxF42TL0c4uTiK2+rp5LMYEPdz6JBfpfvG/Z1unPGsuB7fQmHYKyfHBQJmi92DZV+smljm7w/g==",
"version": "9.28.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-9.28.0.tgz",
"integrity": "sha512-BVGVBlmcpJdT55d/vywjfK1u6zMC5ycjJBxU1wUCNgCU3cSKRDBnvmYgk/+Ay23bFryT28Q4hM1p5qBBAOfxjQ==",
"license": "MIT",
"dependencies": {
"@sentry-internal/browser-utils": "9.28.1",
"@sentry/core": "9.28.1"
"@sentry-internal/browser-utils": "9.28.0",
"@sentry/core": "9.28.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@sentry-internal/replay-canvas": {
"version": "9.28.1",
"resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-9.28.1.tgz",
"integrity": "sha512-RtkogfcIpXLFCyV8CTnXmVTH2QauT/KwmUAXBbeOz3rRWsM19yjN1moHrsjxn7OdjTv+D4qWSCA8Ka1aKSpr7g==",
"version": "9.28.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-9.28.0.tgz",
"integrity": "sha512-Bv4mbtUrRV3p6PpFQPseLv3+Uaen+3AlfX02Z6QHY1sMa4lpt+U8OHfRGLprnzb6Rarw6fK2LNVL5rnV9LNMwA==",
"license": "MIT",
"dependencies": {
"@sentry-internal/replay": "9.28.1",
"@sentry/core": "9.28.1"
"@sentry-internal/replay": "9.28.0",
"@sentry/core": "9.28.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@sentry/browser": {
"version": "9.28.1",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-9.28.1.tgz",
"integrity": "sha512-XAS46iQSq8lXTnv9udQP025JTf3PwSVRE9ePJVQhx25QBWxedqGhEOv5qqX9b1Ijf8KiZYXXhBWMQxBBXVzUaw==",
"version": "9.28.0",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-9.28.0.tgz",
"integrity": "sha512-ttqiv3D9sIB43nZnJTTln1nXw1p4C5BDSh+sHmGUOiqdCH6ND3HByDITYMYIOz1lACSISTT4V+MEpqx0V25Tlw==",
"license": "MIT",
"dependencies": {
"@sentry-internal/browser-utils": "9.28.1",
"@sentry-internal/feedback": "9.28.1",
"@sentry-internal/replay": "9.28.1",
"@sentry-internal/replay-canvas": "9.28.1",
"@sentry/core": "9.28.1"
"@sentry-internal/browser-utils": "9.28.0",
"@sentry-internal/feedback": "9.28.0",
"@sentry-internal/replay": "9.28.0",
"@sentry-internal/replay-canvas": "9.28.0",
"@sentry/core": "9.28.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@sentry/core": {
"version": "9.28.1",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-9.28.1.tgz",
"integrity": "sha512-6q59r/71MeE+4StkvwdKAAyhBBNpWcii0HeiWBZ3l1gaFYQlb6bChjZJRZmxSzF5dnvkdF4duQbAC3JmjeIbPA==",
"version": "9.28.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-9.28.0.tgz",
"integrity": "sha512-vzD9xhg9S864jxfCpq77feCE4y7iP2cZYsNMoTupl1vTUlmXlhp7XgF832fEMjEZq4vrPhaqCNsde7Sc3PAbaQ==",
"license": "MIT",
"engines": {
"node": ">=18"
@ -4717,15 +4717,15 @@
}
},
"node_modules/@spotlightjs/overlay": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@spotlightjs/overlay/-/overlay-3.0.0.tgz",
"integrity": "sha512-0b03WtsykqpcOKmjDRnRZf0GGfaEB6ZHGctLZZxFK4NHTDBNJ6BaQZjunr4XU35kKR5BT2OFp5E/DPKluih0Hg==",
"version": "2.15.1",
"resolved": "https://registry.npmjs.org/@spotlightjs/overlay/-/overlay-2.15.1.tgz",
"integrity": "sha512-5TpHWFRiTm8rrNINOQs9iFsqVnguFGHU1cK/bmhrysNzts4tYQT9d+kWvl++GlItKezIPbu5xPD9VoapO30cyw==",
"license": "Apache-2.0"
},
"node_modules/@spotlightjs/sidecar": {
"version": "1.11.4",
"resolved": "https://registry.npmjs.org/@spotlightjs/sidecar/-/sidecar-1.11.4.tgz",
"integrity": "sha512-8uDJNhvt6uVNvIoBltjRBqb0a//SxkKoyPACtNjq9k9qMYSfFhE0RVtgqnJNBineXeJfxzK5uvzeG/X7pEhYeQ==",
"version": "1.11.3",
"resolved": "https://registry.npmjs.org/@spotlightjs/sidecar/-/sidecar-1.11.3.tgz",
"integrity": "sha512-2FNZjnvJH71pAsYlJA/LIaEZ0jdtjqrlD58F/xJ5ZhI7z6US5zIqE7DMrqaK/tvObFam71CyCncKHRG6M0l6Cg==",
"license": "Apache-2.0",
"dependencies": {
"@jridgewell/trace-mapping": "^0.3.25",
@ -4741,21 +4741,21 @@
}
},
"node_modules/@spotlightjs/spotlight": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@spotlightjs/spotlight/-/spotlight-3.0.0.tgz",
"integrity": "sha512-dkMineYpONLUmkHh7gvBhjf34ES8a08KDQXNem9/0JzAMy/bXSDlC95sqkX9wDfKWjq2rJKYjJulNtCuGHDaeA==",
"version": "2.13.3",
"resolved": "https://registry.npmjs.org/@spotlightjs/spotlight/-/spotlight-2.13.3.tgz",
"integrity": "sha512-wDnXJaSVexPC/+blgXXx2AYCk7S+5lT4TCJmu0HZAVtYd2sDgNub/wAOitsKYxvpRtIQnPe55IlvL4r1X7goSg==",
"license": "Apache-2.0",
"dependencies": {
"@sentry/node": "^8.49.0",
"@spotlightjs/overlay": "3.0.0",
"@spotlightjs/sidecar": "1.11.4",
"@spotlightjs/overlay": "2.15.1",
"@spotlightjs/sidecar": "1.11.3",
"import-meta-resolve": "^4.1.0"
},
"bin": {
"spotlight": "bin/run.js"
},
"engines": {
"node": ">=20"
"node": ">=18"
}
},
"node_modules/@stencil/core": {
@ -17143,9 +17143,9 @@
}
},
"node_modules/import-in-the-middle": {
"version": "1.14.0",
"resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.14.0.tgz",
"integrity": "sha512-g5zLT0HaztRJWysayWYiUq/7E5H825QIiecMD2pI5QO7Wzr847l6GDvPvmZaDIdrDtS2w7qRczywxiK6SL5vRw==",
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.13.2.tgz",
"integrity": "sha512-Yjp9X7s2eHSXvZYQ0aye6UvwYPrVB5C2k47fuXjFKnYinAByaDZjh4t9MT2wEga9775n6WaIqyHnQhBxYtX2mg==",
"license": "Apache-2.0",
"dependencies": {
"acorn": "^8.14.0",

View File

@ -102,8 +102,8 @@
"@open-wc/lit-helpers": "^0.7.0",
"@patternfly/elements": "^4.1.0",
"@patternfly/patternfly": "^4.224.2",
"@sentry/browser": "^9.28.1",
"@spotlightjs/spotlight": "^3.0.0",
"@sentry/browser": "^9.28.0",
"@spotlightjs/spotlight": "^2.13.3",
"@webcomponents/webcomponentsjs": "^2.8.0",
"base64-js": "^1.5.1",
"change-case": "^5.4.4",

View File

@ -1,5 +1,5 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/components/ak-secret-textarea-input.js";
import "@goauthentik/components/ak-private-textarea-input.js";
import "@goauthentik/elements/CodeMirror";
import "@goauthentik/elements/forms/HorizontalFormElement";
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
@ -46,7 +46,7 @@ export class CertificateKeyPairForm extends ModelForm<CertificateKeyPair, string
required
/>
</ak-form-element-horizontal>
<ak-secret-textarea-input
<ak-private-textarea-input
label=${msg("Certificate")}
name="certificateData"
input-hint="code"
@ -54,8 +54,8 @@ export class CertificateKeyPairForm extends ModelForm<CertificateKeyPair, string
required
?revealed=${this.instance === undefined}
help=${msg("PEM-encoded Certificate data.")}
></ak-secret-textarea-input>
<ak-secret-textarea-input
></ak-private-textarea-input>
<ak-private-textarea-input
label=${msg("Private Key")}
name="keyData"
input-hint="code"
@ -63,7 +63,7 @@ export class CertificateKeyPairForm extends ModelForm<CertificateKeyPair, string
help=${msg(
"Optional Private Key. If this is set, you can use this keypair for encryption.",
)}
></ak-secret-textarea-input>`;
></ak-private-textarea-input>`;
}
}

View File

@ -1,6 +1,6 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EVENT_REFRESH_ENTERPRISE } from "@goauthentik/common/constants";
import "@goauthentik/components/ak-secret-textarea-input.js";
import "@goauthentik/components/ak-private-textarea-input.js";
import "@goauthentik/elements/CodeMirror";
import "@goauthentik/elements/forms/HorizontalFormElement";
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
@ -62,13 +62,13 @@ export class EnterpriseLicenseForm extends ModelForm<License, string> {
value="${ifDefined(this.installID)}"
/>
</ak-form-element-horizontal>
<ak-secret-textarea-input
<ak-private-textarea-input
name="key"
?revealed=${this.instance === undefined}
label=${msg("License key")}
input-hint="code"
>
</ak-secret-textarea-input>`;
</ak-private-textarea-input>`;
}
}

View File

@ -4,7 +4,6 @@ import {
propertyMappingsSelector,
} from "@goauthentik/admin/providers/microsoft_entra/MicrosoftEntraProviderFormHelpers.js";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/components/ak-hidden-text-input";
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
import "@goauthentik/elements/ak-dual-select/ak-dual-select-provider.js";
import "@goauthentik/elements/forms/FormGroup";
@ -69,15 +68,21 @@ export class MicrosoftEntraProviderFormPage extends BaseProviderForm<MicrosoftEn
${msg("Client ID for the app registration.")}
</p>
</ak-form-element-horizontal>
<ak-hidden-text-input
name="clientSecret"
<ak-form-element-horizontal
label=${msg("Client Secret")}
value="${this.instance?.clientSecret ?? ""}"
input-hint="code"
required
.help=${msg("Client secret for the app registration.")}
name="clientSecret"
>
</ak-hidden-text-input>
<input
type="text"
value="${this.instance?.clientSecret ?? ""}"
class="pf-c-form-control pf-m-monospace"
required
/>
<p class="pf-c-form__helper-text">
${msg("Client secret for the app registration.")}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal label=${msg("Tenant ID")} required name="tenantId">
<input
type="text"

View File

@ -5,7 +5,6 @@ import {
akOAuthRedirectURIInput,
} from "@goauthentik/admin/providers/oauth2/OAuth2ProviderRedirectURI";
import { ascii_letters, digits, randomString } from "@goauthentik/common/utils";
import "@goauthentik/components/ak-hidden-text-input";
import "@goauthentik/components/ak-radio-input";
import "@goauthentik/components/ak-text-input";
import "@goauthentik/components/ak-textarea-input";
@ -167,16 +166,17 @@ export function renderForm(
input-hint="code"
>
</ak-text-input>
<ak-hidden-text-input
<ak-text-input
name="clientSecret"
label=${msg("Client Secret")}
value="${provider?.clientSecret ?? randomString(128, ascii_letters + digits)}"
input-hint="code"
?hidden=${!showClientSecret}
>
</ak-hidden-text-input>
</ak-text-input>
<ak-form-element-horizontal
label=${msg("Redirect URIs/Origins (RegEx)")}
required
name="redirectUris"
>
<ak-array-input

View File

@ -1,8 +1,6 @@
import "@goauthentik/admin/common/ak-flow-search/ak-branded-flow-search";
import "@goauthentik/admin/common/ak-flow-search/ak-flow-search";
import { ascii_letters, digits, randomString } from "@goauthentik/common/utils";
import "@goauthentik/components/ak-hidden-text-input";
import "@goauthentik/components/ak-text-input";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
import "@goauthentik/elements/forms/SearchSelect";
@ -76,14 +74,14 @@ export function renderForm(
<ak-form-group expanded>
<span slot="header"> ${msg("Protocol settings")} </span>
<div slot="body" class="pf-c-form">
<ak-hidden-text-input
<ak-text-input
name="sharedSecret"
label=${msg("Shared secret")}
.errorMessages=${errors?.sharedSecret ?? []}
value=${provider?.sharedSecret ?? randomString(128, ascii_letters + digits)}
required
input-hint="code"
></ak-hidden-text-input>
></ak-text-input>
<ak-text-input
name="clientNetworks"
label=${msg("Client Networks")}

View File

@ -1,5 +1,4 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/components/ak-hidden-text-input";
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
@ -51,7 +50,7 @@ export function renderForm(provider?: Partial<SCIMProvider>, errors: ValidationE
>
</ak-switch-input>
<ak-hidden-text-input
<ak-text-input
name="token"
label=${msg("Token")}
value="${provider?.token ?? ""}"
@ -61,7 +60,7 @@ export function renderForm(provider?: Partial<SCIMProvider>, errors: ValidationE
"Token to authenticate with. Currently only bearer authentication is supported.",
)}
input-hint="code"
></ak-hidden-text-input>
></ak-text-input>
<ak-radio-input
name="compatibilityMode"
label=${msg("Compatibility Mode")}

View File

@ -7,8 +7,8 @@ import {
UserMatchingModeToLabel,
} from "@goauthentik/admin/sources/oauth/utils";
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
import "@goauthentik/components/ak-secret-text-input.js";
import "@goauthentik/components/ak-secret-textarea-input.js";
import "@goauthentik/components/ak-private-text-input.js";
import "@goauthentik/components/ak-private-textarea-input.js";
import "@goauthentik/components/ak-switch-input";
import "@goauthentik/components/ak-text-input";
import "@goauthentik/components/ak-textarea-input";
@ -248,22 +248,22 @@ export class KerberosSourceForm extends WithCapabilitiesConfig(BaseSourceForm<Ke
value=${ifDefined(this.instance?.syncPrincipal)}
help=${msg("Principal used to authenticate to the KDC for syncing.")}
></ak-text-input>
<ak-secret-text-input
<ak-private-text-input
name="syncPassword"
label=${msg("Sync password")}
?revealed=${this.instance === undefined}
help=${msg(
"Password used to authenticate to the KDC for syncing. Optional if Sync keytab or Sync credentials cache is provided.",
)}
></ak-secret-text-input>
<ak-secret-textarea-input
></ak-private-text-input>
<ak-private-textarea-input
name="syncKeytab"
label=${msg("Sync keytab")}
?revealed=${this.instance === undefined}
help=${msg(
"Keytab used to authenticate to the KDC for syncing. Optional if Sync password or Sync credentials cache is provided. Must be base64 encoded or in the form TYPE:residual.",
)}
></ak-secret-textarea-input>
></ak-private-textarea-input>
<ak-text-input
name="syncCcache"
label=${msg("Sync credentials cache")}
@ -285,14 +285,14 @@ export class KerberosSourceForm extends WithCapabilitiesConfig(BaseSourceForm<Ke
"Force the use of a specific server name for SPNEGO. Must be in the form HTTP@domain",
)}
></ak-text-input>
<ak-secret-textarea-input
<ak-private-textarea-input
name="spnegoKeytab"
label=${msg("SPNEGO keytab")}
?revealed=${this.instance === undefined}
help=${msg(
"Keytab used for SPNEGO. Optional if SPNEGO credentials cache is provided. Must be base64 encoded or in the form TYPE:residual.",
)}
></ak-secret-textarea-input>
></ak-private-textarea-input>
<ak-text-input
name="spnegoCcache"
label=${msg("SPNEGO credentials cache")}

View File

@ -2,7 +2,7 @@ import "@goauthentik/admin/common/ak-crypto-certificate-search";
import { placeholderHelperText } from "@goauthentik/admin/helperText";
import { BaseSourceForm } from "@goauthentik/admin/sources/BaseSourceForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/components/ak-secret-text-input.js";
import "@goauthentik/components/ak-private-text-input.js";
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
@ -260,11 +260,11 @@ export class LDAPSourceForm extends BaseSourceForm<LDAPSource> {
class="pf-c-form-control"
/>
</ak-form-element-horizontal>
<ak-secret-text-input
<ak-private-text-input
label=${msg("Bind Password")}
name="bindPassword"
?revealed=${this.instance === undefined}
></ak-secret-text-input>
></ak-private-text-input>
<ak-form-element-horizontal label=${msg("Base DN")} required name="baseDn">
<input
type="text"

View File

@ -8,8 +8,8 @@ import {
UserMatchingModeToLabel,
} from "@goauthentik/admin/sources/oauth/utils";
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
import "@goauthentik/components/ak-private-textarea-input.js";
import "@goauthentik/components/ak-radio-input";
import "@goauthentik/components/ak-secret-textarea-input.js";
import "@goauthentik/elements/CodeMirror";
import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
@ -441,14 +441,14 @@ export class OAuthSourceForm extends WithCapabilitiesConfig(BaseSourceForm<OAuth
/>
<p class="pf-c-form__helper-text">${msg("Also known as Client ID.")}</p>
</ak-form-element-horizontal>
<ak-secret-textarea-input
<ak-private-textarea-input
label=${msg("Consumer secret")}
name="consumerSecret"
input-hint="code"
help=${msg("Also known as Client Secret.")}
required
?revealed=${this.instance === undefined}
></ak-secret-textarea-input>
></ak-private-textarea-input>
<ak-form-element-horizontal label=${msg("Scopes")} name="additionalScopes">
<input
type="text"

View File

@ -128,7 +128,7 @@ export class PlexSourceForm extends WithCapabilitiesConfig(BaseSourceForm<PlexSo
this.doAuth();
}}
>
${msg("Re-authenticate with Plex")}
${msg("Re-authenticate with plex")}
</button>
<ak-form-element-horizontal name="allowFriends">
<label class="pf-c-switch">

View File

@ -1,7 +1,7 @@
import { RenderFlowOption } from "@goauthentik/admin/flows/utils";
import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/components/ak-secret-text-input.js";
import "@goauthentik/components/ak-private-text-input.js";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
import "@goauthentik/elements/forms/SearchSelect";
@ -95,13 +95,13 @@ export class AuthenticatorDuoStageForm extends BaseStageForm<AuthenticatorDuoSta
required
/>
</ak-form-element-horizontal>
<ak-secret-text-input
<ak-private-text-input
name="clientSecret"
label=${msg("Secret key")}
input-hint="code"
required
?revealed=${this.instance === undefined}
></ak-secret-text-input>
></ak-private-text-input>
</div>
</ak-form-group>
<ak-form-group>
@ -125,12 +125,12 @@ export class AuthenticatorDuoStageForm extends BaseStageForm<AuthenticatorDuoSta
spellcheck="false"
/>
</ak-form-element-horizontal>
<ak-secret-text-input
<ak-private-text-input
name="adminSecretKey"
label=${msg("Secret key")}
input-hint="code"
?revealed=${this.instance === undefined}
></ak-secret-text-input>
></ak-private-text-input>
</div>
</ak-form-group>
<ak-form-group expanded>

View File

@ -1,7 +1,7 @@
import { RenderFlowOption } from "@goauthentik/admin/flows/utils";
import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/components/ak-secret-text-input.js";
import "@goauthentik/components/ak-private-text-input.js";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
import "@goauthentik/elements/forms/Radio";
@ -77,11 +77,11 @@ export class AuthenticatorEmailStageForm extends BaseStageForm<AuthenticatorEmai
/>
</ak-form-element-horizontal>
<ak-secret-text-input
<ak-private-text-input
name="password"
label=${msg("SMTP Password")}
?revealed=${this.instance === undefined}
></ak-secret-text-input>
></ak-private-text-input>
<ak-form-element-horizontal name="useTls">
<label class="pf-c-switch">

View File

@ -1,7 +1,7 @@
import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/components/ak-number-input";
import "@goauthentik/components/ak-secret-text-input.js";
import "@goauthentik/components/ak-private-text-input.js";
import "@goauthentik/components/ak-switch-input";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
@ -70,7 +70,7 @@ export class CaptchaStageForm extends BaseStageForm<CaptchaStage> {
</p>
</ak-form-element-horizontal>
<ak-secret-text-input
<ak-private-text-input
name="privateKey"
label=${msg("Private Key")}
input-hint="code"
@ -79,7 +79,7 @@ export class CaptchaStageForm extends BaseStageForm<CaptchaStage> {
help=${msg(
"Private key, acquired from https://www.google.com/recaptcha/intro/v3.html.",
)}
></ak-secret-text-input>
></ak-private-text-input>
<ak-switch-input
name="interactive"

View File

@ -1,6 +1,6 @@
import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/components/ak-secret-text-input.js";
import "@goauthentik/components/ak-private-text-input.js";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
import "@goauthentik/elements/utils/TimeDeltaHelp";
@ -73,11 +73,11 @@ export class EmailStageForm extends BaseStageForm<EmailStage> {
class="pf-c-form-control"
/>
</ak-form-element-horizontal>
<ak-secret-text-input
<ak-private-text-input
label=${msg("SMTP Password")}
name="password"
?revealed=${this.instance === undefined}
></ak-secret-text-input>
></ak-private-text-input>
<ak-form-element-horizontal name="useTls">
<label class="pf-c-switch">
<input

View File

@ -1,6 +1,5 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { dateTimeLocal } from "@goauthentik/common/temporal";
import "@goauthentik/components/ak-hidden-text-input";
import { Form } from "@goauthentik/elements/forms/Form";
import "@goauthentik/elements/forms/HorizontalFormElement";
import { ModalForm } from "@goauthentik/elements/forms/ModalForm";
@ -125,14 +124,19 @@ export class ServiceAccountForm extends Form<UserServiceAccountRequest> {
class="pf-c-form-control"
/>
</ak-form-element-horizontal>
<ak-hidden-text-input
label=${msg("Password")}
value="${this.result?.token ?? ""}"
.help=${msg(
"Valid for 360 days, after which the password will automatically rotate. You can copy the password from the Token List.",
)}
>
</ak-hidden-text-input>
<ak-form-element-horizontal label=${msg("Password")}>
<input
type="text"
readonly
value=${ifDefined(this.result?.token)}
class="pf-c-form-control"
/>
<p class="pf-c-form__helper-text">
${msg(
"Valid for 360 days, after which the password will automatically rotate. You can copy the password from the Token List.",
)}
</p>
</ak-form-element-horizontal>
</form>`;
}

View File

@ -1,4 +1,4 @@
import { AKElement, type AKElementProps } from "@goauthentik/elements/Base";
import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/forms/HorizontalFormElement.js";
import { TemplateResult, html, nothing } from "lit";
@ -6,19 +6,6 @@ import { property } from "lit/decorators.js";
type HelpType = TemplateResult | typeof nothing;
export interface HorizontalLightComponentProps<T> extends AKElementProps {
name: string;
label?: string;
required?: boolean;
help?: string;
bighelp?: TemplateResult | TemplateResult[];
hidden?: boolean;
invalid?: boolean;
errorMessages?: string[];
value?: T;
inputHint?: string;
}
export class HorizontalLightComponent<T> extends AKElement {
// Render into the lightDOM. This effectively erases the shadowDOM nature of this component, but
// we're not actually using that and, for the meantime, we need the form handlers to be able to
@ -31,81 +18,37 @@ export class HorizontalLightComponent<T> extends AKElement {
return this;
}
/**
* The name attribute for the form element
* @property
* @attribute
*/
@property({ type: String, reflect: true })
name!: string;
/**
* The label for the input control
* @property
* @attribute
*/
@property({ type: String, reflect: true })
label = "";
/**
* @property
* @attribute
*/
@property({ type: Boolean, reflect: true })
required = false;
/**
* Help text to display below the form element. Optional
* @property
* @attribute
*/
@property({ type: String, reflect: true })
help = "";
/**
* Extended help content. Optional. Expects to be a TemplateResult
* @property
*/
@property({ type: Object })
bighelp?: TemplateResult | TemplateResult[];
/**
* @property
* @attribute
*/
@property({ type: Boolean, reflect: true })
hidden = false;
/**
* @property
* @attribute
*/
@property({ type: Boolean, reflect: true })
invalid = false;
/**
* @property
*/
@property({ attribute: false })
errorMessages: string[] = [];
/**
* @attribute
* @property
*/
@property({ attribute: false })
value?: T;
/**
* Input hint.
* - `code`: uses a monospace font and disables spellcheck & autocomplete
* @property
* @attribute
*/
@property({ type: String, attribute: "input-hint" })
inputHint = "";
protected renderControl() {
renderControl() {
throw new Error("Must be implemented in a subclass");
}

View File

@ -1,159 +0,0 @@
import { bound } from "#elements/decorators/bound";
import { msg } from "@lit/localize";
import { css, html } from "lit";
import { customElement, property, query } from "lit/decorators.js";
import { classMap } from "lit/directives/class-map.js";
import { ifDefined } from "lit/directives/if-defined.js";
import {
HorizontalLightComponent,
HorizontalLightComponentProps,
} from "./HorizontalLightComponent";
import "./ak-visibility-toggle.js";
import type { VisibilityToggleProps } from "./ak-visibility-toggle.js";
type BaseProps = HorizontalLightComponentProps<string> &
Pick<VisibilityToggleProps, "showMessage" | "hideMessage">;
export interface AkHiddenTextInputProps extends BaseProps {
revealed: boolean;
placeholder?: string;
}
export type InputLike = HTMLTextAreaElement | HTMLInputElement;
/**
* @element ak-hidden-text-input
* @class AkHiddenTextInput
*
* A text-input field with a visibility control, so you can show/hide sensitive fields.
*
* ## CSS Parts
* @csspart container - The main container div
* @csspart input - The input element
* @csspart toggle - The visibility toggle button
*
*/
@customElement("ak-hidden-text-input")
export class AkHiddenTextInput<T extends InputLike = HTMLInputElement>
extends HorizontalLightComponent<string>
implements AkHiddenTextInputProps
{
public static get styles() {
return [
css`
main {
display: flex;
}
`,
];
}
/**
* @property
* @attribute
*/
@property({ type: String, reflect: true })
public value = "";
/**
* @property
* @attribute
*/
@property({ type: Boolean, reflect: true })
public revealed = false;
/**
* Text for when the input has no set value
*
* @property
* @attribute
*/
@property({ type: String })
public placeholder?: string;
/**
* Specify kind of help the browser should try to provide
*
* @property
* @attribute
*/
@property({ type: String })
public autocomplete?: "none" | AutoFill;
/**
* @property
* @attribute
*/
@property({ type: String, attribute: "show-message" })
public showMessage = msg("Show field content");
/**
* @property
* @attribute
*/
@property({ type: String, attribute: "hide-message" })
public hideMessage = msg("Hide field content");
@query("#main > input")
protected inputField!: T;
@bound
private handleToggleVisibility() {
this.revealed = !this.revealed;
// Maintain focus on input after toggle
this.updateComplete.then(() => {
if (this.inputField && document.activeElement === this) {
this.inputField.focus();
}
});
}
// TODO: Because of the peculiarities of how HorizontalLightComponent works, keeping its content
// in the LightDom so the inner components actually inherit styling, the normal `css` options
// aren't available. Embedding styles is bad styling, and we'll fix it in the next style
// refresh.
protected renderInputField(setValue: (ev: InputEvent) => void, code: boolean) {
return html` <input
style="flex: 1 1 auto; min-width: 0;"
part="input"
type=${this.revealed ? "text" : "password"}
@input=${setValue}
value=${ifDefined(this.value)}
placeholder=${ifDefined(this.placeholder)}
class="${classMap({
"pf-c-form-control": true,
"pf-m-monospace": code,
})}"
spellcheck=${code ? "false" : "true"}
?required=${this.required}
/>`;
}
protected override renderControl() {
const code = this.inputHint === "code";
const setValue = (ev: InputEvent) => {
this.value = (ev.target as T).value;
};
return html` <div style="display: flex; gap: 0.25rem">
${this.renderInputField(setValue, code)}
<!-- -->
<ak-visibility-toggle
part="toggle"
style="flex: 0 0 auto; align-self: flex-start"
?open=${this.revealed}
show-message=${this.showMessage}
hide-message=${this.hideMessage}
@click=${() => (this.revealed = !this.revealed)}
></ak-visibility-toggle>
</div>`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ak-hidden-text-input": AkHiddenTextInput;
}
}

View File

@ -1,128 +0,0 @@
import { css, html } from "lit";
import { customElement, property, query } from "lit/decorators.js";
import { classMap } from "lit/directives/class-map.js";
import { ifDefined } from "lit/directives/if-defined.js";
import { AkHiddenTextInput, type AkHiddenTextInputProps } from "./ak-hidden-text-input.js";
export interface AkHiddenTextAreaInputProps extends AkHiddenTextInputProps {
/**
* Number of visible text lines (rows)
*/
rows?: number;
/**
* Number of visible character width (cols)
*/
cols?: number;
/**
* How the textarea can be resized
*/
resize?: "none" | "both" | "horizontal" | "vertical";
/**
* Whether text should wrap
*/
wrap?: "soft" | "hard" | "off";
}
/**
* @element ak-hidden-text-input
* @class AkHiddenTextInput
*
* A text-input field with a visibility control, so you can show/hide sensitive fields.
*
* ## CSS Parts
* @csspart container - The main container div
* @csspart input - The input element
* @csspart toggle - The visibility toggle button
*
*/
@customElement("ak-hidden-textarea-input")
export class AkHiddenTextAreaInput
extends AkHiddenTextInput<HTMLTextAreaElement>
implements AkHiddenTextAreaInputProps
{
/* These are mostly just forwarded to the textarea component. */
/**
* @property
* @attribute
*/
@property({ type: Number })
rows?: number = 4;
/**
* @property
* @attribute
*/
@property({ type: Number })
cols?: number;
/**
* @property
* @attribute
*
* You want `resize=true` so that the resize value is visible in the component tag, activating
* the CSS associated with these values.
*/
@property({ type: String, reflect: true })
resize?: "none" | "both" | "horizontal" | "vertical" = "vertical";
/**
* @property
* @attribute
*/
@property({ type: String })
wrap?: "soft" | "hard" | "off" = "soft";
@query("#main > textarea")
protected inputField!: HTMLTextAreaElement;
get displayValue() {
const value = this.value ?? "";
if (this.revealed) {
return value;
}
return value
.split("\n")
.reduce((acc: string[], line: string) => [...acc, "*".repeat(line.length)], [])
.join("\n");
}
// TODO: Because of the peculiarities of how HorizontalLightComponent works, keeping its content
// in the LightDom so the inner components actually inherit styling, the normal `css` options
// aren't available. Embedding styles is bad styling, and we'll fix it in the next style
// refresh.
protected override renderInputField(setValue: (ev: InputEvent) => void, code: boolean) {
const wrap = this.revealed ? this.wrap : "soft";
return html`
<textarea
style="flex: 1 1 auto; min-width: 0;"
part="textarea"
@input=${setValue}
placeholder=${ifDefined(this.placeholder)}
rows=${ifDefined(this.rows)}
cols=${ifDefined(this.cols)}
wrap=${ifDefined(wrap)}
class=${classMap({
"pf-c-form-control": true,
"pf-m-monospace": code,
})}
spellcheck=${code ? "false" : "true"}
?required=${this.required}
>
${this.displayValue}</textarea
>
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ak-hidden-textarea-input": AkHiddenTextAreaInput;
}
}

View File

@ -8,8 +8,8 @@ import { ifDefined } from "lit/directives/if-defined.js";
import { HorizontalLightComponent } from "./HorizontalLightComponent";
@customElement("ak-secret-text-input")
export class AkSecretTextInput extends HorizontalLightComponent<string> {
@customElement("ak-private-text-input")
export class AkPrivateTextInput extends HorizontalLightComponent<string> {
@property({ type: String, reflect: true })
public value = "";
@ -23,7 +23,7 @@ export class AkSecretTextInput extends HorizontalLightComponent<string> {
this.revealed = true;
}
#renderSecretInput() {
#renderPrivateInput() {
return html`<div class="pf-c-form__horizontal-group" @click=${() => this.#onReveal()}>
<input
class="pf-c-form-control"
@ -60,14 +60,14 @@ export class AkSecretTextInput extends HorizontalLightComponent<string> {
}
public override renderControl() {
return this.revealed ? this.renderVisibleInput() : this.#renderSecretInput();
return this.revealed ? this.renderVisibleInput() : this.#renderPrivateInput();
}
}
export default AkSecretTextInput;
export default AkPrivateTextInput;
declare global {
interface HTMLElementTagNameMap {
"ak-secret-text-input": AkSecretTextInput;
"ak-private-text-input": AkPrivateTextInput;
}
}

View File

@ -5,10 +5,10 @@ import { customElement, property } from "lit/decorators.js";
import { classMap } from "lit/directives/class-map.js";
import { ifDefined } from "lit/directives/if-defined.js";
import { AkSecretTextInput } from "./ak-secret-text-input.js";
import { AkPrivateTextInput } from "./ak-private-text-input.js";
@customElement("ak-secret-textarea-input")
export class AkSecretTextAreaInput extends AkSecretTextInput {
@customElement("ak-private-textarea-input")
export class AkPrivateTextAreaInput extends AkPrivateTextInput {
protected override renderVisibleInput() {
const code = this.inputHint === "code";
const setValue = (ev: InputEvent) => {
@ -34,10 +34,10 @@ export class AkSecretTextAreaInput extends AkSecretTextInput {
}
}
export default AkSecretTextAreaInput;
export default AkPrivateTextAreaInput;
declare global {
interface HTMLElementTagNameMap {
"ak-secret-textarea-input": AkSecretTextAreaInput;
"ak-private-textarea-input": AkPrivateTextAreaInput;
}
}

View File

@ -1,89 +0,0 @@
import { AKElement } from "@goauthentik/elements/Base.js";
import { msg } from "@lit/localize";
import { html } from "lit";
import { customElement, property } from "lit/decorators.js";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
export interface VisibilityToggleProps {
open: boolean;
disabled: boolean;
showMessage: string;
hideMessage: string;
}
/**
* @component ak-visibility-toggle
* @class VisibilityToggle
*
* A straightforward two-state iconic button we use in a few places as way of telling users to hide
* or show something secret, such as a password or private key. Expects the client to manage its
* state.
*
* @events
* - click: when the toggle is clicked.
*/
@customElement("ak-visibility-toggle")
export class VisibilityToggle extends AKElement implements VisibilityToggleProps {
static get styles() {
return [PFBase, PFButton];
}
/**
* @property
* @attribute
*/
@property({ type: Boolean, reflect: true })
open = false;
/**
* @property
* @attribute
*/
@property({ type: Boolean, reflect: true })
disabled = false;
/**
* @property
* @attribute
*/
@property({ type: String, attribute: "show-message" })
showMessage = msg("Show field content");
/**
* @property
* @attribute
*/
@property({ type: String, attribute: "hide-message" })
hideMessage = msg("Hide field content");
render() {
const [label, icon] = this.open
? [this.hideMessage, "fa-eye"]
: [this.showMessage, "fa-eye-slash"];
const onClick = (ev: PointerEvent) => {
ev.stopPropagation();
this.dispatchEvent(new PointerEvent(ev.type, ev));
};
return html`<button
aria-label=${label}
title=${label}
@click=${onClick}
?disabled=${this.disabled}
class="pf-c-button pf-m-control"
type="button"
>
<i class="fas ${icon}" aria-hidden="true"></i>
</button>`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ak-visibility-toggle": VisibilityToggle;
}
}

View File

@ -1,93 +0,0 @@
import type { Meta, StoryObj } from "@storybook/web-components";
import { html, nothing } from "lit";
import { ifDefined } from "lit/directives/if-defined.js";
import "../ak-hidden-text-input";
import { type AkHiddenTextInput, type AkHiddenTextInputProps } from "../ak-hidden-text-input.js";
const metadata: Meta<AkHiddenTextInputProps> = {
title: "Components / <ak-hidden-text-input>",
component: "ak-hidden-text-input",
tags: ["autodocs"],
parameters: {
docs: {
description: {
component: `
# Hidden Text Input Component
A text-input field with a visibility control, so you can show/hide sensitive fields.
`,
},
},
layout: "padded",
},
argTypes: {
label: {
control: "text",
description: "Label text for the input field",
},
value: {
control: "text",
description: "Current value of the input",
},
revealed: {
control: "boolean",
description: "Whether the text is currently visible",
},
placeholder: {
control: "text",
description: "Placeholder text for the input",
},
required: {
control: "boolean",
description: "Whether the input is required",
},
inputHint: {
control: "select",
options: ["text", "code"],
description: "Input type hint for styling and behavior",
},
showMessage: {
control: "text",
description: "Custom message for show action",
},
hideMessage: {
control: "text",
description: "Custom message for hide action",
},
},
};
export default metadata;
type Story = StoryObj<AkHiddenTextInput>;
const Template: Story = {
args: {
label: "Hidden Text Input",
value: "",
revealed: false,
},
render: (args) => html`
<ak-hidden-text-input
label=${ifDefined(args.label)}
value=${ifDefined(args.value)}
?revealed=${args.revealed}
placeholder=${ifDefined(args.placeholder)}
?required=${args.required}
input-hint=${ifDefined(args.inputHint)}
show-message=${ifDefined(args.showMessage)}
hide-message=${ifDefined(args.hideMessage)}
></ak-hidden-text-input>
`,
};
export const Password: Story = {
...Template,
args: {
label: "Password",
placeholder: "Enter your password",
required: true,
},
};

View File

@ -1,140 +0,0 @@
import type { Meta, StoryObj } from "@storybook/web-components";
import { html, nothing } from "lit";
import { ifDefined } from "lit/directives/if-defined.js";
import "../ak-hidden-textarea-input";
import {
type AkHiddenTextAreaInput,
type AkHiddenTextAreaInputProps,
} from "../ak-hidden-textarea-input.js";
const metadata: Meta<AkHiddenTextAreaInputProps> = {
title: "Components / <ak-hidden-textarea-input>",
component: "ak-hidden-textarea-input",
tags: ["autodocs"],
parameters: {
docs: {
description: {
component: `
# Hidden Textarea Input Component
A textarea input field with a visibility control, so you can show/hide sensitive fields.
`,
},
},
layout: "padded",
},
argTypes: {
label: {
control: "text",
description: "Label text for the input field",
},
value: {
control: "text",
description: "Current value of the input",
},
revealed: {
control: "boolean",
description: "Whether the text is currently visible",
},
placeholder: {
control: "text",
description: "Placeholder text for the input",
},
required: {
control: "boolean",
description: "Whether the input is required",
},
inputHint: {
control: "select",
options: ["text", "code"],
description: "Input type hint for styling and behavior",
},
showMessage: {
control: "text",
description: "Custom message for show action",
},
hideMessage: {
control: "text",
description: "Custom message for hide action",
},
rows: {
control: { type: "number", min: 1, max: 50 },
description: "Number of visible text lines",
},
cols: {
control: { type: "number", min: 10, max: 200 },
description: "Number of visible character width",
},
resize: {
control: "select",
options: ["none", "both", "horizontal", "vertical"],
description: "How the textarea can be resized",
},
wrap: {
control: "select",
options: ["soft", "hard", "off"],
description: "Text wrapping behavior",
},
},
};
export default metadata;
type Story = StoryObj<AkHiddenTextAreaInput>;
const Template: Story = {
args: {
label: "Hidden Textarea Input",
value: "",
revealed: false,
rows: 4,
},
render: (args) => html`
<ak-hidden-textarea-input
label=${ifDefined(args.label)}
value=${ifDefined(args.value)}
?revealed=${args.revealed}
placeholder=${ifDefined(args.placeholder)}
rows=${ifDefined(args.rows)}
cols=${ifDefined(args.cols)}
resize=${ifDefined(args.resize)}
wrap=${ifDefined(args.wrap)}
?required=${args.required}
input-hint=${ifDefined(args.inputHint)}
show-message=${ifDefined(args.showMessage)}
hide-message=${ifDefined(args.hideMessage)}
></ak-hidden-textarea-input>
`,
};
export const SslCertificate: Story = {
...Template,
args: {
label: "SSL Certificate",
value: `-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJAKoK/heBjcOuMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTcwNTEwMTk0MDA2WhcNMTgwNTEwMTk0MDA2WjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMB4XDTE3MDUxMDE5NDAwNloXDTE4MDUxMDE5
NDAwNlowRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNV
BAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBALdUlNS31SzxwoFShahGfjHj6GgpcVbzL1Siq0Pqnf82T6M2
EDuneMLzAgMBAAECggEBAJkPFn6jeMHyiq0Pqnf82T6M2EDuneMLzAgMBAAECggE
BAJkPFn6jeMHyiq0Pqnf82T6M2EDuneMLzAgMBAAECggEBAJkPFn6jeMHyiq0Pqn
f82T6M2EDuneMLzAgMBAAECggEBAJkPFn6jeMHyiq0Pqnf82T6M2EDuneMLzAgM
BAAECggEBAJkPFn6jeMHyiq0Pqnf82T6M2EDuneMLzAgMBAAECggEBAJkPFn6jeM
Hyiq0Pqnf82T6M2EDuneMLzAgMBAAECggEBAJkPFn6jeMHyiq0Pqnf82T6M2EDu
neMLzAgMBAAECggEBAJkPFn6jeMHyiq0Pqnf82T6M2EDuneMLzAgMBAAECggEBAJ
kPFn6jeMHyiq0Pqnf82T6M2EDuneMLzAgMBAAE=
-----END CERTIFICATE-----`,
inputHint: "code",
rows: 15,
resize: "vertical",
showMessage: "Show certificate content",
hideMessage: "Hide certificate content",
autocomplete: "off",
},
};

View File

@ -1,121 +0,0 @@
import type { Meta, StoryObj } from "@storybook/web-components";
import { html, nothing } from "lit";
import { ifDefined } from "lit/directives/if-defined.js";
import "../ak-visibility-toggle";
import { type VisibilityToggle, type VisibilityToggleProps } from "../ak-visibility-toggle.js";
const metadata: Meta<VisibilityToggleProps> = {
title: "Elements/<ak-visibility-toggle>",
component: "ak-visibility-toggle",
tags: ["autodocs"],
parameters: {
docs: {
description: {
component: `
# Visibility Toggle Component
A straightforward two-state iconic button for toggling the visibility of sensitive content such as passwords, private keys, or other secret information.
- Use for sensitive content that users might want to temporarily reveal
- There are default hide/show messages for screen readers, but they can be overridden
- Clients always handle the state
- The \`open\` state is false by default; we assume you want sensitive content hidden at start
`,
},
},
layout: "padded",
},
argTypes: {
open: {
control: "boolean",
description: "Whether the toggle is in the 'show' state (true) or 'hide' state (false)",
},
showMessage: {
control: "text",
description:
'Message for screen readers when in hide state (default: "Show field content")',
},
hideMessage: {
control: "text",
description:
'Message for screen readers when in show state (default: "Hide field content")',
},
disabled: {
control: "boolean",
description: "Whether the button should be disabled (for demo purposes)",
},
},
};
export default metadata;
type Story = StoryObj<VisibilityToggle>;
const Template: Story = {
args: {
open: false,
showMessage: "Show field content",
hideMessage: "Hide field content",
},
render: (args) => html`
<ak-visibility-toggle
?open=${args.open}
show-message=${ifDefined(args.showMessage)}
hide-message=${ifDefined(args.hideMessage)}
@click=${(e: Event) => {
const target = e.target as VisibilityToggle;
target.open = !target.open;
// In a real application, you would also toggle the visibility
// of the associated content here
}}
></ak-visibility-toggle>
`,
};
// Password field integration example
export const PasswordFieldExample: Story = {
args: {
showMessage: "Reveal password",
hideMessage: "Conceal password",
},
render: () => {
let isVisible = false;
const toggleVisibility = (e: Event) => {
isVisible = !isVisible;
const toggle = e.target as VisibilityToggle;
const passwordField = document.querySelector("#demo-password") as HTMLInputElement;
toggle.open = isVisible;
if (passwordField) {
passwordField.type = isVisible ? "text" : "password";
}
};
return html`
<div style="display: flex; flex-direction: column; gap: 1rem; max-width: 300px;">
<label for="demo-password" style="font-weight: bold;">Password:</label>
<div style="display: flex; align-items: center; gap: 0.5rem;">
<input
id="demo-password"
type="password"
value="supersecretpassword123"
style="flex: 1; padding: 0.5rem; border: 1px solid #ccc; border-radius: 4px;"
readonly
/>
<ak-visibility-toggle
?open=${isVisible}
show-message="Show password"
hide-message="Hide password"
@click=${toggleVisibility}
></ak-visibility-toggle>
</div>
<p style="font-size: 0.875rem; color: #666;">
Click the eye icon to toggle password visibility
</p>
</div>
`;
},
};

View File

@ -16,12 +16,8 @@ import { property } from "lit/decorators.js";
import { UiThemeEnum } from "@goauthentik/api";
export interface AKElementProps {
activeTheme: ResolvedUITheme;
}
@localized()
export class AKElement extends LitElement implements AKElementProps {
export class AKElement extends LitElement {
//#region Static Properties
public static styles?: Array<CSSResult | CSSModule>;

View File

@ -33,7 +33,7 @@ import {
function localeComparator(a: DualSelectPair, b: DualSelectPair) {
const aSortBy = a[2] || a[0];
const bSortBy = b[2] || b[0];
const bSortBy = b[2] || a[0];
return aSortBy.localeCompare(bSortBy);
}

View File

@ -3001,6 +3001,11 @@ doesn't pass when either or both of the selected options are equal or above the
<source>Load servers</source>
<target>Server laden</target>
</trans-unit>
<trans-unit id="s24f405197ede5ebb">
<source>Re-authenticate with plex</source>
<target>Mit Plex erneut authentifizieren</target>
</trans-unit>
<trans-unit id="sc297b2e13c28ecf9">
<source>Allow friends to authenticate via Plex, even if you don't share any servers</source>
@ -9236,15 +9241,6 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sb3d5c0a0501669df">
<source>Generate New Certificate-Key Pair</source>
</trans-unit>
<trans-unit id="sf9686d31d28fcf7d">
<source>Show field content</source>
</trans-unit>
<trans-unit id="sb1b05a7573ab618c">
<source>Hide field content</source>
</trans-unit>
<trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source>
</trans-unit>
</body>
</file>

View File

@ -2414,6 +2414,10 @@ doesn't pass when either or both of the selected options are equal or above the
<source>Load servers</source>
<target>Load servers</target>
</trans-unit>
<trans-unit id="s24f405197ede5ebb">
<source>Re-authenticate with plex</source>
<target>Re-authenticate with plex</target>
</trans-unit>
<trans-unit id="sc297b2e13c28ecf9">
<source>Allow friends to authenticate via Plex, even if you don't share any servers</source>
<target>Allow friends to authenticate via Plex, even if you don't share any servers</target>
@ -7744,15 +7748,6 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sb3d5c0a0501669df">
<source>Generate New Certificate-Key Pair</source>
</trans-unit>
<trans-unit id="sf9686d31d28fcf7d">
<source>Show field content</source>
</trans-unit>
<trans-unit id="sb1b05a7573ab618c">
<source>Hide field content</source>
</trans-unit>
<trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source>
</trans-unit>
</body>
</file>

View File

@ -2982,6 +2982,11 @@ no se aprueba cuando una o ambas de las opciones seleccionadas son iguales o sup
<source>Load servers</source>
<target>Servidores de carga</target>
</trans-unit>
<trans-unit id="s24f405197ede5ebb">
<source>Re-authenticate with plex</source>
<target>Vuelva a autenticarse con plex</target>
</trans-unit>
<trans-unit id="sc297b2e13c28ecf9">
<source>Allow friends to authenticate via Plex, even if you don't share any servers</source>
@ -9296,15 +9301,6 @@ Las vinculaciones a grupos o usuarios se comparan con el usuario del evento.</ta
</trans-unit>
<trans-unit id="sb3d5c0a0501669df">
<source>Generate New Certificate-Key Pair</source>
</trans-unit>
<trans-unit id="sf9686d31d28fcf7d">
<source>Show field content</source>
</trans-unit>
<trans-unit id="sb1b05a7573ab618c">
<source>Hide field content</source>
</trans-unit>
<trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source>
</trans-unit>
</body>
</file>

View File

@ -3010,6 +3010,11 @@ doesn't pass when either or both of the selected options are equal or above the
<source>Load servers</source>
<target>Charger les serveurs</target>
</trans-unit>
<trans-unit id="s24f405197ede5ebb">
<source>Re-authenticate with plex</source>
<target>Se ré-authentifier avec Plex</target>
</trans-unit>
<trans-unit id="sc297b2e13c28ecf9">
<source>Allow friends to authenticate via Plex, even if you don't share any servers</source>
@ -9865,15 +9870,6 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
</trans-unit>
<trans-unit id="sb3d5c0a0501669df">
<source>Generate New Certificate-Key Pair</source>
</trans-unit>
<trans-unit id="sf9686d31d28fcf7d">
<source>Show field content</source>
</trans-unit>
<trans-unit id="sb1b05a7573ab618c">
<source>Hide field content</source>
</trans-unit>
<trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source>
</trans-unit>
</body>
</file>

View File

@ -3011,6 +3011,11 @@ doesn't pass when either or both of the selected options are equal or above the
<source>Load servers</source>
<target>Carico server</target>
</trans-unit>
<trans-unit id="s24f405197ede5ebb">
<source>Re-authenticate with plex</source>
<target>Riautenticarsi con plex</target>
</trans-unit>
<trans-unit id="sc297b2e13c28ecf9">
<source>Allow friends to authenticate via Plex, even if you don't share any servers</source>
@ -9848,15 +9853,6 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sb3d5c0a0501669df">
<source>Generate New Certificate-Key Pair</source>
</trans-unit>
<trans-unit id="sf9686d31d28fcf7d">
<source>Show field content</source>
</trans-unit>
<trans-unit id="sb1b05a7573ab618c">
<source>Hide field content</source>
</trans-unit>
<trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source>
</trans-unit>
</body>
</file>

View File

@ -2973,6 +2973,11 @@ doesn't pass when either or both of the selected options are equal or above the
<source>Load servers</source>
<target>서버 로드</target>
</trans-unit>
<trans-unit id="s24f405197ede5ebb">
<source>Re-authenticate with plex</source>
<target>Plex로 다시 인증하기</target>
</trans-unit>
<trans-unit id="sc297b2e13c28ecf9">
<source>Allow friends to authenticate via Plex, even if you don't share any servers</source>
@ -9204,15 +9209,6 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sb3d5c0a0501669df">
<source>Generate New Certificate-Key Pair</source>
</trans-unit>
<trans-unit id="sf9686d31d28fcf7d">
<source>Show field content</source>
</trans-unit>
<trans-unit id="sb1b05a7573ab618c">
<source>Hide field content</source>
</trans-unit>
<trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source>
</trans-unit>
</body>
</file>

View File

@ -2987,6 +2987,11 @@ slaagt niet wanneer een of beide geselecteerde opties gelijk zijn aan of boven d
<source>Load servers</source>
<target>Servers laden</target>
</trans-unit>
<trans-unit id="s24f405197ede5ebb">
<source>Re-authenticate with plex</source>
<target>Opnieuw authenticeren met Plex</target>
</trans-unit>
<trans-unit id="sc297b2e13c28ecf9">
<source>Allow friends to authenticate via Plex, even if you don't share any servers</source>
@ -9108,15 +9113,6 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
</trans-unit>
<trans-unit id="sb3d5c0a0501669df">
<source>Generate New Certificate-Key Pair</source>
</trans-unit>
<trans-unit id="sf9686d31d28fcf7d">
<source>Show field content</source>
</trans-unit>
<trans-unit id="sb1b05a7573ab618c">
<source>Hide field content</source>
</trans-unit>
<trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source>
</trans-unit>
</body>
</file>

View File

@ -3012,6 +3012,11 @@ nie przechodzi, gdy jedna lub obie wybrane opcje są równe lub wyższe od progu
<source>Load servers</source>
<target>Załaduj serwery</target>
</trans-unit>
<trans-unit id="s24f405197ede5ebb">
<source>Re-authenticate with plex</source>
<target>Ponowne uwierzytelnienie za pomocą plex</target>
</trans-unit>
<trans-unit id="sc297b2e13c28ecf9">
<source>Allow friends to authenticate via Plex, even if you don't share any servers</source>
@ -9531,15 +9536,6 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
</trans-unit>
<trans-unit id="sb3d5c0a0501669df">
<source>Generate New Certificate-Key Pair</source>
</trans-unit>
<trans-unit id="sf9686d31d28fcf7d">
<source>Show field content</source>
</trans-unit>
<trans-unit id="sb1b05a7573ab618c">
<source>Hide field content</source>
</trans-unit>
<trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source>
</trans-unit>
</body>
</file>

View File

@ -2990,6 +2990,11 @@ doesn't pass when either or both of the selected options are equal or above the
<source>Load servers</source>
<target>Ĺōàď śēŕvēŕś</target>
</trans-unit>
<trans-unit id="s24f405197ede5ebb">
<source>Re-authenticate with plex</source>
<target>Ŕē-àũţĥēńţĩćàţē ŵĩţĥ ƥĺēx</target>
</trans-unit>
<trans-unit id="sc297b2e13c28ecf9">
<source>Allow friends to authenticate via Plex, even if you don't share any servers</source>
@ -9540,13 +9545,4 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="sb3d5c0a0501669df">
<source>Generate New Certificate-Key Pair</source>
</trans-unit>
<trans-unit id="sf9686d31d28fcf7d">
<source>Show field content</source>
</trans-unit>
<trans-unit id="sb1b05a7573ab618c">
<source>Hide field content</source>
</trans-unit>
<trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source>
</trans-unit>
</body></file></xliff>

View File

@ -3011,6 +3011,11 @@ doesn't pass when either or both of the selected options are equal or above the
<source>Load servers</source>
<target>Загрузить серверы</target>
</trans-unit>
<trans-unit id="s24f405197ede5ebb">
<source>Re-authenticate with plex</source>
<target>Повторная аутентификация с помощью plex</target>
</trans-unit>
<trans-unit id="sc297b2e13c28ecf9">
<source>Allow friends to authenticate via Plex, even if you don't share any servers</source>
@ -9623,15 +9628,6 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sb3d5c0a0501669df">
<source>Generate New Certificate-Key Pair</source>
</trans-unit>
<trans-unit id="sf9686d31d28fcf7d">
<source>Show field content</source>
</trans-unit>
<trans-unit id="sb1b05a7573ab618c">
<source>Hide field content</source>
</trans-unit>
<trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source>
</trans-unit>
</body>
</file>

View File

@ -2990,6 +2990,11 @@ Belirlenen seçeneklerden biri veya her ikisi de eşiğe eşit veya eşiğin üz
<source>Load servers</source>
<target>Sunucuları yükle</target>
</trans-unit>
<trans-unit id="s24f405197ede5ebb">
<source>Re-authenticate with plex</source>
<target>Plex ile yeniden kimlik doğrulama</target>
</trans-unit>
<trans-unit id="sc297b2e13c28ecf9">
<source>Allow friends to authenticate via Plex, even if you don't share any servers</source>
@ -9595,15 +9600,6 @@ Gruplara/kullanıcılara yapılan bağlamalar, etkinliğin kullanıcısına kar
</trans-unit>
<trans-unit id="sb3d5c0a0501669df">
<source>Generate New Certificate-Key Pair</source>
</trans-unit>
<trans-unit id="sf9686d31d28fcf7d">
<source>Show field content</source>
</trans-unit>
<trans-unit id="sb1b05a7573ab618c">
<source>Hide field content</source>
</trans-unit>
<trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source>
</trans-unit>
</body>
</file>

View File

@ -2128,6 +2128,9 @@ doesn't pass when either or both of the selected options are equal or above the
<trans-unit id="s91f389c796720a81">
<source>Load servers</source>
</trans-unit>
<trans-unit id="s24f405197ede5ebb">
<source>Re-authenticate with plex</source>
</trans-unit>
<trans-unit id="sc297b2e13c28ecf9">
<source>Allow friends to authenticate via Plex, even if you don't share any servers</source>
</trans-unit>
@ -6359,15 +6362,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="sb3d5c0a0501669df">
<source>Generate New Certificate-Key Pair</source>
</trans-unit>
<trans-unit id="sf9686d31d28fcf7d">
<source>Show field content</source>
</trans-unit>
<trans-unit id="sb1b05a7573ab618c">
<source>Hide field content</source>
</trans-unit>
<trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -1,4 +1,4 @@
<?xml version="1.0"?><xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<?xml version="1.0" ?><xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file target-language="zh-Hans" source-language="en" original="lit-localize-inputs" datatype="plaintext">
<body>
<trans-unit id="s4caed5b7a7e5d89b">
@ -596,9 +596,9 @@
</trans-unit>
<trans-unit id="saa0e2675da69651b">
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
<target>未找到 URL "
<x id="0" equiv-text="${this.url}"/>"。</target>
<source>The URL &quot;<x id="0" equiv-text="${this.url}"/>&quot; was not found.</source>
<target>未找到 URL &quot;
<x id="0" equiv-text="${this.url}"/>&quot;。</target>
</trans-unit>
<trans-unit id="s58cd9c2fe836d9c6">
@ -1709,8 +1709,8 @@
</trans-unit>
<trans-unit id="sa90b7809586c35ce">
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
<target>输入完整 URL、相对路径或者使用 'fa://fa-test' 来使用 Font Awesome 图标 "fa-test"。</target>
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon &quot;fa-test&quot;.</source>
<target>输入完整 URL、相对路径或者使用 'fa://fa-test' 来使用 Font Awesome 图标 &quot;fa-test&quot;。</target>
</trans-unit>
<trans-unit id="s0410779cb47de312">
@ -3011,6 +3011,11 @@ doesn't pass when either or both of the selected options are equal or above the
<source>Load servers</source>
<target>加载服务器</target>
</trans-unit>
<trans-unit id="s24f405197ede5ebb">
<source>Re-authenticate with plex</source>
<target>使用 Plex 重新验证身份</target>
</trans-unit>
<trans-unit id="sc297b2e13c28ecf9">
<source>Allow friends to authenticate via Plex, even if you don't share any servers</source>
@ -3757,10 +3762,10 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="sa95a538bfbb86111">
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> &quot;<x id="1" equiv-text="${this.obj?.name}"/>&quot;?</source>
<target>您确定要更新
<x id="0" equiv-text="${this.objectLabel}"/>"
<x id="1" equiv-text="${this.obj?.name}"/>" 吗?</target>
<x id="0" equiv-text="${this.objectLabel}"/>&quot;
<x id="1" equiv-text="${this.obj?.name}"/>&quot; 吗?</target>
</trans-unit>
<trans-unit id="sc92d7cfb6ee1fec6">
@ -4826,7 +4831,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="sdf1d8edef27236f0">
<source>A "roaming" authenticator, like a YubiKey</source>
<source>A &quot;roaming&quot; authenticator, like a YubiKey</source>
<target>像 YubiKey 这样的“漫游”身份验证器</target>
</trans-unit>
@ -5185,7 +5190,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s1608b2f94fa0dbd4">
<source>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.</source>
<source>If set to a duration above 0, the user will have the option to choose to &quot;stay signed in&quot;, which will extend their session by the time specified here.</source>
<target>如果设置时长大于 0用户可以选择“保持登录”选项这将使用户的会话延长此处设置的时间。</target>
</trans-unit>
@ -7461,7 +7466,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<target>成功创建用户并添加到组 <x id="0" equiv-text="${this.group.name}"/></target>
</trans-unit>
<trans-unit id="s824e0943a7104668">
<source>This user will be added to the group "<x id="0" equiv-text="${this.targetGroup.name}"/>".</source>
<source>This user will be added to the group &quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&quot;.</source>
<target>此用户将会被添加到组 &amp;quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&amp;quot;。</target>
</trans-unit>
<trans-unit id="s62e7f6ed7d9cb3ca">
@ -8743,7 +8748,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<target>同步组</target>
</trans-unit>
<trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
<source><x id="0" equiv-text="${p.name}"/> (&quot;<x id="1" equiv-text="${p.fieldKey}"/>&quot;, of type <x id="2" equiv-text="${p.type}"/>)</source>
<target><x id="0" equiv-text="${p.name}"/>&amp;quot;<x id="1" equiv-text="${p.fieldKey}"/>&amp;quot;,类型为 <x id="2" equiv-text="${p.type}"/></target>
</trans-unit>
<trans-unit id="s25bacc19d98b444e">
@ -8991,8 +8996,8 @@ Bindings to groups/users are checked against the user of the event.</source>
<target>授权流程成功后有效的重定向 URI。还可以在此处为隐式流程指定任何来源。</target>
</trans-unit>
<trans-unit id="s4c49d27de60a532b">
<source>To allow any redirect URI, set the mode to Regex and the value to ".*". Be aware of the possible security implications this can have.</source>
<target>要允许任何重定向 URI请设置模式为正则表达式并将此值设置为 ".*"。请注意这可能带来的安全影响。</target>
<source>To allow any redirect URI, set the mode to Regex and the value to &quot;.*&quot;. Be aware of the possible security implications this can have.</source>
<target>要允许任何重定向 URI请设置模式为正则表达式并将此值设置为 &quot;.*&quot;。请注意这可能带来的安全影响。</target>
</trans-unit>
<trans-unit id="sa52bf79fe1ccb13e">
<source>Federated OIDC Sources</source>
@ -9745,7 +9750,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<target>在 authorization_code 令牌请求流程期间,如何执行身份验证</target>
</trans-unit>
<trans-unit id="s844baf19a6c4a9b4">
<source>Enable "Remember me on this device"</source>
<source>Enable &quot;Remember me on this device&quot;</source>
<target>启用“在此设备上记住我”</target>
</trans-unit>
<trans-unit id="sfa72bca733f40692">
@ -9875,16 +9880,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="sb3d5c0a0501669df">
<source>Generate New Certificate-Key Pair</source>
<target>生成新的证书密钥对</target>
</trans-unit>
<trans-unit id="sf9686d31d28fcf7d">
<source>Show field content</source>
</trans-unit>
<trans-unit id="sb1b05a7573ab618c">
<source>Hide field content</source>
</trans-unit>
<trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source>
</trans-unit>
</body>
</file>
</xliff>
</xliff>

View File

@ -2290,6 +2290,10 @@ doesn't pass when either or both of the selected options are equal or above the
<source>Load servers</source>
<target>加载服务器</target>
</trans-unit>
<trans-unit id="s24f405197ede5ebb">
<source>Re-authenticate with plex</source>
<target>使用 plex 重新进行身份验证</target>
</trans-unit>
<trans-unit id="sc297b2e13c28ecf9">
<source>Allow friends to authenticate via Plex, even if you don't share any servers</source>
<target>允许好友通过Plex进行身份验证即使您不共享任何服务器</target>
@ -7444,15 +7448,6 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sb3d5c0a0501669df">
<source>Generate New Certificate-Key Pair</source>
</trans-unit>
<trans-unit id="sf9686d31d28fcf7d">
<source>Show field content</source>
</trans-unit>
<trans-unit id="sb1b05a7573ab618c">
<source>Hide field content</source>
</trans-unit>
<trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source>
</trans-unit>
</body>
</file>

View File

@ -661,6 +661,12 @@
<source>Apps with most usage</source>
<target>使用率最高的应用</target>
</trans-unit>
<trans-unit id="sda5e1499f93146ad">
<source><x id="0" equiv-text="${ago}"/> days ago</source>
<target>
<x id="0" equiv-text="${ago}"/>天前</target>
</trans-unit>
<trans-unit id="s51ea3a244c781b1f">
<source>Objects created</source>
@ -6112,11 +6118,21 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Download Private key</source>
<target>下载私钥</target>
</trans-unit>
<trans-unit id="s3a5fec3d73ac9edc">
<source>Create Certificate-Key Pair</source>
<target>创建证书密钥对</target>
</trans-unit>
<trans-unit id="s45cb501abd43ba52">
<source>Generate</source>
<target>生成</target>
</trans-unit>
<trans-unit id="sf9bddaf910f4eea5">
<source>Generate Certificate-Key Pair</source>
<target>生成证书密钥对</target>
</trans-unit>
<trans-unit id="see2bcbc11bb91960">
<source>Successfully updated instance.</source>
@ -7517,6 +7533,10 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Configure SCIM Provider</source>
<target>配置 SCIM 提供程序</target>
</trans-unit>
<trans-unit id="s7513372fe60f6387">
<source>Event volume</source>
<target>事件容量</target>
</trans-unit>
<trans-unit id="s3271da6c18c25b18">
<source>Connection settings.</source>
<target>连接设置。</target>
@ -9868,18 +9888,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s04bb32ec9f359507">
<source>Additional Group DN</source>
<target>额外的组 DN</target>
</trans-unit>
<trans-unit id="sb7af25ce6e30d61a">
<source>The currently selected policy engine mode is <x id="0" equiv-text="${policyEngineMode.label}"/>:</source>
<target>当前所选策略引擎模式为 <x id="0" equiv-text="${policyEngineMode.label}"/></target>
</trans-unit>
<trans-unit id="se1d2545eda4b1600">
<source>Import Existing Certificate-Key Pair</source>
<target>导入已有的证书密钥对</target>
</trans-unit>
<trans-unit id="sb3d5c0a0501669df">
<source>Generate New Certificate-Key Pair</source>
<target>生成新的证书密钥对</target>
</trans-unit>
</body>
</file>

View File

@ -2972,6 +2972,11 @@ doesn't pass when either or both of the selected options are equal or above the
<source>Load servers</source>
<target>載入伺服器</target>
</trans-unit>
<trans-unit id="s24f405197ede5ebb">
<source>Re-authenticate with plex</source>
<target>使用 plex 重新身分認證</target>
</trans-unit>
<trans-unit id="sc297b2e13c28ecf9">
<source>Allow friends to authenticate via Plex, even if you don't share any servers</source>
@ -9183,15 +9188,6 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sb3d5c0a0501669df">
<source>Generate New Certificate-Key Pair</source>
</trans-unit>
<trans-unit id="sf9686d31d28fcf7d">
<source>Show field content</source>
</trans-unit>
<trans-unit id="sb1b05a7573ab618c">
<source>Hide field content</source>
</trans-unit>
<trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source>
</trans-unit>
</body>
</file>

View File

@ -1,10 +1,15 @@
```nginx
# Upgrade WebSocket if requested, otherwise use keepalive
map $http_upgrade $connection_upgrade_keepalive {
default upgrade;
'' '';
}
Create a `http_top.conf` file in your nginx `conf.d` directory (`/nginx/conf.d/`) with the following content:
```nginx
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
```
Use the following nginx template:
```nginx
server {
# SSL and VHost configuration
listen 443 ssl http2;
@ -20,13 +25,13 @@ server {
proxy_buffer_size 32k;
location / {
# Put your proxy_pass to your application here, and all the other statements you'll need
# Put your proxy_pass to your application here, and all the other statements you'll need.
# proxy_pass http://localhost:5000;
# proxy_set_header Host $host;
# proxy_set_header ...
# Support for websocket
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade_keepalive;
proxy_set_header Connection $connection_upgrade;
##############################
# authentik-specific config

View File

@ -72,7 +72,7 @@ To check if your config has been applied correctly, you can run the following co
- `AUTHENTIK_POSTGRESQL__PASSWORD`: Database password, defaults to the environment variable `POSTGRES_PASSWORD`
{/* TODO: Temporarily deactivated feature, see https://github.com/goauthentik/authentik/issues/14320 */}
{/* - `AUTHENTIK_POSTGRESQL__USE_POOL`: Use a [connection pool](https://docs.djangoproject.com/en/stable/ref/databases/#connection-pool) for PostgreSQL connections. Defaults to `false`. :ak-version[2025.4] */}
{/* - `AUTHENTIK_POSTGRESQL__POOL_OPTIONS`: Extra configuration to pass to the [ConnectionPool object](https://www.psycopg.org/psycopg3/docs/api/pool.html#psycopg_pool.ConnectionPool) when it is created. Must be a base64-encoded JSON dictionary. Ignored when `USE_POOL` is set to `false`. :ak-version[2025.4] */}
- `AUTHENTIK_POSTGRESQL__POOL_OPTIONS`: Extra configuration to pass to the [ConnectionPool object](https://www.psycopg.org/psycopg3/docs/api/pool.html#psycopg_pool.ConnectionPool) when it is created. Must be a base64-encoded JSON dictionary. Ignored when `USE_POOL` is set to `false`. :ak-version[2025.4]
- `AUTHENTIK_POSTGRESQL__USE_PGBOUNCER`: Adjust configuration to support connection to PgBouncer. Deprecated, see below
- `AUTHENTIK_POSTGRESQL__USE_PGPOOL`: Adjust configuration to support connection to Pgpool. Deprecated, see below
- `AUTHENTIK_POSTGRESQL__SSLMODE`: Strictness of ssl verification. Defaults to `"verify-ca"`

View File

@ -19,11 +19,11 @@
"@goauthentik/docusaurus-config": "^1.1.0",
"@goauthentik/tsconfig": "^1.0.4",
"@mdx-js/react": "^3.1.0",
"@swc/html-linux-x64-gnu": "1.12.1",
"@swc/html-linux-x64-gnu": "1.12.0",
"clsx": "^2.1.1",
"docusaurus-plugin-openapi-docs": "^4.4.0",
"docusaurus-theme-openapi-docs": "^4.4.0",
"postcss": "^8.5.5",
"postcss": "^8.5.4",
"prism-react-renderer": "^2.4.1",
"react": "^18.3.1",
"react-before-after-slider-component": "^1.1.8",
@ -42,7 +42,7 @@
"@goauthentik/tsconfig": "^1.0.4",
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
"@types/lodash": "^4.17.17",
"@types/node": "^24.0.1",
"@types/node": "^24.0.0",
"@types/postman-collection": "^3.5.11",
"@types/react": "^18.3.22",
"@types/semver": "^7.7.0",
@ -64,12 +64,12 @@
"@rspack/binding-darwin-arm64": "1.3.15",
"@rspack/binding-linux-arm64-gnu": "1.3.15",
"@rspack/binding-linux-x64-gnu": "1.3.15",
"@swc/core-darwin-arm64": "1.12.1",
"@swc/core-linux-arm64-gnu": "1.12.1",
"@swc/core-linux-x64-gnu": "1.12.1",
"@swc/html-darwin-arm64": "1.12.1",
"@swc/html-linux-arm64-gnu": "1.12.1",
"@swc/html-linux-x64-gnu": "1.12.1",
"@swc/core-darwin-arm64": "1.12.0",
"@swc/core-linux-arm64-gnu": "1.12.0",
"@swc/core-linux-x64-gnu": "1.12.0",
"@swc/html-darwin-arm64": "1.12.0",
"@swc/html-linux-arm64-gnu": "1.12.0",
"@swc/html-linux-x64-gnu": "1.12.0",
"lightningcss-darwin-arm64": "1.30.1",
"lightningcss-linux-arm64-gnu": "1.30.1",
"lightningcss-linux-x64-gnu": "1.30.1"
@ -5592,9 +5592,9 @@
}
},
"node_modules/@swc/core-darwin-arm64": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.12.1.tgz",
"integrity": "sha512-nUjWVcJ3YS2N40ZbKwYO2RJ4+o2tWYRzNOcIQp05FqW0+aoUCVMdAUUzQinPDynfgwVshDAXCKemY8X7nN5MaA==",
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.12.0.tgz",
"integrity": "sha512-usLr8kC80GDv3pwH2zoEaS279kxtWY0MY3blbMFw7zA8fAjqxa8IDxm3WcgyNLNWckWn4asFfguEwz/Weem3nA==",
"cpu": [
"arm64"
],
@ -5640,9 +5640,9 @@
}
},
"node_modules/@swc/core-linux-arm64-gnu": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.12.1.tgz",
"integrity": "sha512-BxJDIJPq1+aCh9UsaSAN6wo3tuln8UhNXruOrzTI8/ElIig/3sAueDM6Eq7GvZSGGSA7ljhNATMJ0elD7lFatQ==",
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.12.0.tgz",
"integrity": "sha512-Al0x33gUVxNY5tutEYpSyv7mze6qQS1ONa0HEwoRxcK9WXsX0NHLTiOSGZoCUS1SsXM37ONlbA6/Bsp1MQyP+g==",
"cpu": [
"arm64"
],
@ -5672,9 +5672,9 @@
}
},
"node_modules/@swc/core-linux-x64-gnu": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.12.1.tgz",
"integrity": "sha512-CrYnV8SZIgArQ9LKH0xEF95PKXzX9WkRSc5j55arOSBeDCeDUQk1Bg/iKdnDiuj5HC1hZpvzwMzSBJjv+Z70jA==",
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.12.0.tgz",
"integrity": "sha512-ltIvqNi7H0c5pRawyqjeYSKEIfZP4vv/datT3mwT6BW7muJtd1+KIDCPFLMIQ4wm/h76YQwPocsin3fzmnFdNA==",
"cpu": [
"x64"
],
@ -5830,9 +5830,9 @@
}
},
"node_modules/@swc/html-darwin-arm64": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@swc/html-darwin-arm64/-/html-darwin-arm64-1.12.1.tgz",
"integrity": "sha512-vbCqYgBBdoxlsnUe/G6irBJ69LUOrlLVXgdxWxDSZ3YcbnpVmwi5YEeaRvqf4vNzZ/nzBMd4DYl6KK2Qsi0prw==",
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/@swc/html-darwin-arm64/-/html-darwin-arm64-1.12.0.tgz",
"integrity": "sha512-okpx8G7xGSPiSekxS4FQu3aR8k+q8nZJCfVKzanQxdZUaCm7YDVUci2Unqp9TvpgZJRA0GOWs1U3QMu2vdr0sQ==",
"cpu": [
"arm64"
],
@ -5878,9 +5878,9 @@
}
},
"node_modules/@swc/html-linux-arm64-gnu": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@swc/html-linux-arm64-gnu/-/html-linux-arm64-gnu-1.12.1.tgz",
"integrity": "sha512-KbqPLtsPVt0/kjp7sUT1APfEtNQUqMam3S0RzJkvuMz9jB2F9DREvj5EG+DPnx2s/kxnDm4sh9vM2sG2xNHErQ==",
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/@swc/html-linux-arm64-gnu/-/html-linux-arm64-gnu-1.12.0.tgz",
"integrity": "sha512-ImZLbghifCPqQhwbEprv2zojieD0j/RGJ+tkNpJ6DyGqcf5qVFfPgGDe/WDPEHCMbJlAodvp1iKTdLSAdTfaLg==",
"cpu": [
"arm64"
],
@ -5910,9 +5910,9 @@
}
},
"node_modules/@swc/html-linux-x64-gnu": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@swc/html-linux-x64-gnu/-/html-linux-x64-gnu-1.12.1.tgz",
"integrity": "sha512-9QNCTgCZtyQVifLXqDTW7v4lgaC11v0/iL9OhsSZ19ycJrBmnxBmZtDIbuQrXAIzE1GD8mMOK/GLey2IeceoDQ==",
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/@swc/html-linux-x64-gnu/-/html-linux-x64-gnu-1.12.0.tgz",
"integrity": "sha512-IVFXgsyn0/8e9nfVrQXAdGDFboom0nls7KSOJ/+oXMmdK917wrnYLDt7M4DyRT2c+xJmMxgR6tyaTW8KLAl02w==",
"cpu": [
"x64"
],
@ -6615,9 +6615,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "24.0.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.1.tgz",
"integrity": "sha512-MX4Zioh39chHlDJbKmEgydJDS3tspMP/lnQC67G3SWsTnb9NeYVWOjkxpOSy4oMfPs4StcWHwBrvUb4ybfnuaw==",
"version": "24.0.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.0.tgz",
"integrity": "sha512-yZQa2zm87aRVcqDyH5+4Hv9KYgSdgwX1rFnGvpbzMaC7YAljmhBET93TPiTd3ObwTL+gSpIzPKg5BqVxdCvxKg==",
"license": "MIT",
"dependencies": {
"undici-types": "~7.8.0"
@ -20672,9 +20672,9 @@
}
},
"node_modules/postcss": {
"version": "8.5.5",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.5.tgz",
"integrity": "sha512-d/jtm+rdNT8tpXuHY5MMtcbJFBkhXE6593XVR9UoGCH8jSFGci7jGvMGH5RYd5PBJW+00NZQt6gf7CbagJCrhg==",
"version": "8.5.4",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.4.tgz",
"integrity": "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==",
"funding": [
{
"type": "opencollective",

View File

@ -37,7 +37,7 @@
"clsx": "^2.1.1",
"docusaurus-plugin-openapi-docs": "^4.4.0",
"docusaurus-theme-openapi-docs": "^4.4.0",
"postcss": "^8.5.5",
"postcss": "^8.5.4",
"prism-react-renderer": "^2.4.1",
"react": "^18.3.1",
"react-before-after-slider-component": "^1.1.8",
@ -56,7 +56,7 @@
"@goauthentik/tsconfig": "^1.0.4",
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
"@types/lodash": "^4.17.17",
"@types/node": "^24.0.1",
"@types/node": "^24.0.0",
"@types/postman-collection": "^3.5.11",
"@types/react": "^18.3.22",
"@types/semver": "^7.7.0",
@ -75,12 +75,12 @@
"@rspack/binding-darwin-arm64": "1.3.15",
"@rspack/binding-linux-arm64-gnu": "1.3.15",
"@rspack/binding-linux-x64-gnu": "1.3.15",
"@swc/core-darwin-arm64": "1.12.1",
"@swc/core-linux-arm64-gnu": "1.12.1",
"@swc/core-linux-x64-gnu": "1.12.1",
"@swc/html-darwin-arm64": "1.12.1",
"@swc/html-linux-arm64-gnu": "1.12.1",
"@swc/html-linux-x64-gnu": "1.12.1",
"@swc/core-darwin-arm64": "1.12.0",
"@swc/core-linux-arm64-gnu": "1.12.0",
"@swc/core-linux-x64-gnu": "1.12.0",
"@swc/html-darwin-arm64": "1.12.0",
"@swc/html-linux-arm64-gnu": "1.12.0",
"@swc/html-linux-x64-gnu": "1.12.0",
"lightningcss-darwin-arm64": "1.30.1",
"lightningcss-linux-arm64-gnu": "1.30.1",
"lightningcss-linux-x64-gnu": "1.30.1"