Compare commits

..

4 Commits

145 changed files with 6921 additions and 13251 deletions

View File

@ -1,5 +1,5 @@
[bumpversion]
current_version = 2025.4.3
current_version = 2025.2.4
tag = True
commit = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(?:-(?P<rc_t>[a-zA-Z-]+)(?P<rc_n>[1-9]\\d*))?

View File

@ -36,7 +36,7 @@ runs:
with:
go-version-file: "go.mod"
- name: Setup docker cache
uses: AndreKurait/docker-cache@0fe76702a40db986d9663c24954fc14c6a6031b7
uses: ScribeMD/docker-cache@0.5.0
with:
key: docker-images-${{ runner.os }}-${{ hashFiles('.github/actions/setup/docker-compose.yml', 'Makefile') }}-${{ inputs.postgresql_version }}
- name: Setup dependencies

View File

@ -70,18 +70,22 @@ jobs:
- name: checkout stable
run: |
# Copy current, latest config to local
# Temporarly comment the .github backup while migrating to uv
cp authentik/lib/default.yml local.env.yml
cp -R .github ..
# cp -R .github ..
cp -R scripts ..
git checkout $(git tag --sort=version:refname | grep '^version/' | grep -vE -- '-rc[0-9]+$' | tail -n1)
rm -rf .github/ scripts/
mv ../.github ../scripts .
# rm -rf .github/ scripts/
# mv ../.github ../scripts .
rm -rf scripts/
mv ../scripts .
- name: Setup authentik env (stable)
uses: ./.github/actions/setup
with:
postgresql_version: ${{ matrix.psql }}
continue-on-error: true
- name: run migrations to stable
run: uv run python -m lifecycle.migrate
run: poetry run python -m lifecycle.migrate
- name: checkout current code
run: |
set -x

View File

@ -3,10 +3,10 @@ on:
push:
branches: [main]
paths:
- packages/docusaurus-config/**
- packages/eslint-config/**
- packages/prettier-config/**
- packages/tsconfig/**
- packages/docusaurus-config
- packages/eslint-config
- packages/prettier-config
- packages/tsconfig
workflow_dispatch:
jobs:
publish:

View File

@ -40,8 +40,7 @@ COPY ./web /work/web/
COPY ./website /work/website/
COPY ./gen-ts-api /work/web/node_modules/@goauthentik/api
RUN npm run build && \
npm run build:sfe
RUN npm run build
# Stage 3: Build go proxy
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.24-bookworm AS go-builder

View File

@ -20,8 +20,8 @@ Even if the issue is not a CVE, we still greatly appreciate your help in hardeni
| Version | Supported |
| --------- | --------- |
| 2024.12.x | ✅ |
| 2025.2.x | ✅ |
| 2025.4.x | ✅ |
## Reporting a Vulnerability

View File

@ -2,7 +2,7 @@
from os import environ
__version__ = "2025.4.3"
__version__ = "2025.2.4"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"

View File

@ -16,7 +16,7 @@ def migrate_custom_css(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
if not path.exists():
return
css = path.read_text()
Brand.objects.using(db_alias).all().update(branding_custom_css=css)
Brand.objects.using(db_alias).update(branding_custom_css=css)
class Migration(migrations.Migration):

View File

@ -99,17 +99,18 @@ class GroupSerializer(ModelSerializer):
if superuser
else "authentik_core.disable_group_superuser"
)
if self.instance or superuser:
has_perm = user.has_perm(perm) or user.has_perm(perm, self.instance)
if not has_perm:
raise ValidationError(
_(
(
"User does not have permission to set "
"superuser status to {superuser_status}."
).format_map({"superuser_status": superuser})
)
has_perm = user.has_perm(perm)
if self.instance and not has_perm:
has_perm = user.has_perm(perm, self.instance)
if not has_perm:
raise ValidationError(
_(
(
"User does not have permission to set "
"superuser status to {superuser_status}."
).format_map({"superuser_status": superuser})
)
)
return superuser
class Meta:

View File

@ -2,7 +2,6 @@
from django.apps import apps
from django.contrib.auth.management import create_permissions
from django.core.management import call_command
from django.core.management.base import BaseCommand, no_translations
from guardian.management import create_anonymous_user
@ -17,10 +16,6 @@ class Command(BaseCommand):
"""Check permissions for all apps"""
for tenant in Tenant.objects.filter(ready=True):
with tenant:
# See https://code.djangoproject.com/ticket/28417
# Remove potential lingering old permissions
call_command("remove_stale_contenttypes", "--no-input")
for app in apps.get_app_configs():
self.stdout.write(f"Checking app {app.name} ({app.label})\n")
create_permissions(app, verbosity=0)

View File

@ -31,10 +31,7 @@ class PickleSerializer:
def loads(self, data):
"""Unpickle data to be loaded from redis"""
try:
return pickle.loads(data) # nosec
except Exception:
return {}
return pickle.loads(data) # nosec
def _migrate_session(
@ -79,7 +76,6 @@ def _migrate_session(
AuthenticatedSession.objects.using(db_alias).create(
session=session,
user=old_auth_session.user,
uuid=old_auth_session.uuid,
)

View File

@ -1,103 +0,0 @@
# Generated by Django 5.1.9 on 2025-05-14 11:15
from django.apps.registry import Apps, apps as global_apps
from django.db import migrations
from django.contrib.contenttypes.management import create_contenttypes
from django.contrib.auth.management import create_permissions
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
def migrate_authenticated_session_permissions(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
"""Migrate permissions from OldAuthenticatedSession to AuthenticatedSession"""
db_alias = schema_editor.connection.alias
# `apps` here is just an instance of `django.db.migrations.state.AppConfigStub`, we need the
# real config for creating permissions and content types
authentik_core_config = global_apps.get_app_config("authentik_core")
# These are only ran by django after all migrations, but we need them right now.
# `global_apps` is needed,
create_permissions(authentik_core_config, using=db_alias, verbosity=1)
create_contenttypes(authentik_core_config, using=db_alias, verbosity=1)
# But from now on, this is just a regular migration, so use `apps`
Permission = apps.get_model("auth", "Permission")
ContentType = apps.get_model("contenttypes", "ContentType")
try:
old_ct = ContentType.objects.using(db_alias).get(
app_label="authentik_core", model="oldauthenticatedsession"
)
new_ct = ContentType.objects.using(db_alias).get(
app_label="authentik_core", model="authenticatedsession"
)
except ContentType.DoesNotExist:
# This should exist at this point, but if not, let's cut our losses
return
# Get all permissions for the old content type
old_perms = Permission.objects.using(db_alias).filter(content_type=old_ct)
# Create equivalent permissions for the new content type
for old_perm in old_perms:
new_perm = (
Permission.objects.using(db_alias)
.filter(
content_type=new_ct,
codename=old_perm.codename,
)
.first()
)
if not new_perm:
# This should exist at this point, but if not, let's cut our losses
continue
# Global user permissions
User = apps.get_model("authentik_core", "User")
User.user_permissions.through.objects.using(db_alias).filter(
permission=old_perm
).all().update(permission=new_perm)
# Global role permissions
DjangoGroup = apps.get_model("auth", "Group")
DjangoGroup.permissions.through.objects.using(db_alias).filter(
permission=old_perm
).all().update(permission=new_perm)
# Object user permissions
UserObjectPermission = apps.get_model("guardian", "UserObjectPermission")
UserObjectPermission.objects.using(db_alias).filter(permission=old_perm).all().update(
permission=new_perm, content_type=new_ct
)
# Object role permissions
GroupObjectPermission = apps.get_model("guardian", "GroupObjectPermission")
GroupObjectPermission.objects.using(db_alias).filter(permission=old_perm).all().update(
permission=new_perm, content_type=new_ct
)
def remove_old_authenticated_session_content_type(
apps: Apps, schema_editor: BaseDatabaseSchemaEditor
):
db_alias = schema_editor.connection.alias
ContentType = apps.get_model("contenttypes", "ContentType")
ContentType.objects.using(db_alias).filter(model="oldauthenticatedsession").delete()
class Migration(migrations.Migration):
dependencies = [
("authentik_core", "0047_delete_oldauthenticatedsession"),
]
operations = [
migrations.RunPython(
code=migrate_authenticated_session_permissions,
reverse_code=migrations.RunPython.noop,
),
migrations.RunPython(
code=remove_old_authenticated_session_content_type,
reverse_code=migrations.RunPython.noop,
),
]

View File

@ -124,16 +124,6 @@ class TestGroupsAPI(APITestCase):
{"is_superuser": ["User does not have permission to set superuser status to True."]},
)
def test_superuser_no_perm_no_superuser(self):
"""Test creating a group without permission and without superuser flag"""
assign_perm("authentik_core.add_group", self.login_user)
self.client.force_login(self.login_user)
res = self.client.post(
reverse("authentik_api:group-list"),
data={"name": generate_id(), "is_superuser": False},
)
self.assertEqual(res.status_code, 201)
def test_superuser_update_no_perm(self):
"""Test updating a superuser group without permission"""
group = Group.objects.create(name=generate_id(), is_superuser=True)

View File

@ -132,14 +132,13 @@ class LicenseKey:
"""Get a summarized version of all (not expired) licenses"""
total = LicenseKey(get_license_aud(), 0, "Summarized license", 0, 0)
for lic in License.objects.all():
if lic.is_valid:
total.internal_users += lic.internal_users
total.external_users += lic.external_users
total.license_flags.extend(lic.status.license_flags)
total.internal_users += lic.internal_users
total.external_users += lic.external_users
exp_ts = int(mktime(lic.expiry.timetuple()))
if total.exp == 0:
total.exp = exp_ts
total.exp = max(total.exp, exp_ts)
total.license_flags.extend(lic.status.license_flags)
return total
@staticmethod

View File

@ -39,10 +39,6 @@ class License(SerializerModel):
internal_users = models.BigIntegerField()
external_users = models.BigIntegerField()
@property
def is_valid(self) -> bool:
return self.expiry >= now()
@property
def serializer(self) -> type[BaseSerializer]:
from authentik.enterprise.api import LicenseSerializer

View File

@ -8,7 +8,6 @@ from django.test import TestCase
from django.utils.timezone import now
from rest_framework.exceptions import ValidationError
from authentik.core.models import User
from authentik.enterprise.license import LicenseKey
from authentik.enterprise.models import (
THRESHOLD_READ_ONLY_WEEKS,
@ -72,9 +71,9 @@ class TestEnterpriseLicense(TestCase):
)
def test_valid_multiple(self):
"""Check license verification"""
lic = License.objects.create(key=generate_id(), expiry=expiry_valid)
lic = License.objects.create(key=generate_id())
self.assertTrue(lic.status.status().is_valid)
lic2 = License.objects.create(key=generate_id(), expiry=expiry_valid)
lic2 = License.objects.create(key=generate_id())
self.assertTrue(lic2.status.status().is_valid)
total = LicenseKey.get_total()
self.assertEqual(total.internal_users, 200)
@ -233,9 +232,7 @@ class TestEnterpriseLicense(TestCase):
)
def test_expiry_expired(self):
"""Check license verification"""
User.objects.all().delete()
License.objects.all().delete()
License.objects.create(key=generate_id(), expiry=expiry_expired)
License.objects.create(key=generate_id())
self.assertEqual(LicenseKey.get_total().summary().status, LicenseUsageStatus.EXPIRED)
@patch(

View File

@ -15,7 +15,6 @@
{% endblock %}
<link rel="stylesheet" type="text/css" href="{% static 'dist/sfe/bootstrap.min.css' %}">
<meta name="sentry-trace" content="{{ sentry_trace }}" />
<link rel="prefetch" href="{{ flow_background_url }}" />
{% include "base/header_js.html" %}
<style>
html,
@ -23,7 +22,7 @@
height: 100%;
}
body {
background-image: url("{{ flow_background_url }}");
background-image: url("{{ flow.background_url }}");
background-repeat: no-repeat;
background-size: cover;
}

View File

@ -5,7 +5,7 @@
{% block head_before %}
{{ block.super }}
<link rel="prefetch" href="{{ flow_background_url }}" />
<link rel="prefetch" href="{{ flow.background_url }}" />
{% if flow.compatibility_mode and not inspector %}
<script>ShadyDOM = { force: !navigator.webdriver };</script>
{% endif %}
@ -21,7 +21,7 @@ window.authentik.flow = {
<script src="{% versioned_script 'dist/flow/FlowInterface-%v.js' %}" type="module"></script>
<style>
:root {
--ak-flow-background: url("{{ flow_background_url }}");
--ak-flow-background: url("{{ flow.background_url }}");
}
</style>
{% endblock %}

View File

@ -13,9 +13,7 @@ class FlowInterfaceView(InterfaceView):
"""Flow interface"""
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
flow = get_object_or_404(Flow, slug=self.kwargs.get("flow_slug"))
kwargs["flow"] = flow
kwargs["flow_background_url"] = flow.background_url(self.request)
kwargs["flow"] = get_object_or_404(Flow, slug=self.kwargs.get("flow_slug"))
kwargs["inspector"] = "inspector" in self.request.GET
return super().get_context_data(**kwargs)

View File

@ -363,9 +363,6 @@ def django_db_config(config: ConfigLoader | None = None) -> dict:
pool_options = config.get_dict_from_b64_json("postgresql.pool_options", True)
if not pool_options:
pool_options = True
# FIXME: Temporarily force pool to be deactivated.
# See https://github.com/goauthentik/authentik/issues/14320
pool_options = False
db = {
"default": {

View File

@ -494,88 +494,86 @@ class TestConfig(TestCase):
},
)
# FIXME: Temporarily force pool to be deactivated.
# See https://github.com/goauthentik/authentik/issues/14320
# def test_db_pool(self):
# """Test DB Config with pool"""
# config = ConfigLoader()
# config.set("postgresql.host", "foo")
# config.set("postgresql.name", "foo")
# config.set("postgresql.user", "foo")
# config.set("postgresql.password", "foo")
# config.set("postgresql.port", "foo")
# config.set("postgresql.test.name", "foo")
# config.set("postgresql.use_pool", True)
# conf = django_db_config(config)
# self.assertEqual(
# conf,
# {
# "default": {
# "ENGINE": "authentik.root.db",
# "HOST": "foo",
# "NAME": "foo",
# "OPTIONS": {
# "pool": True,
# "sslcert": None,
# "sslkey": None,
# "sslmode": None,
# "sslrootcert": None,
# },
# "PASSWORD": "foo",
# "PORT": "foo",
# "TEST": {"NAME": "foo"},
# "USER": "foo",
# "CONN_MAX_AGE": 0,
# "CONN_HEALTH_CHECKS": False,
# "DISABLE_SERVER_SIDE_CURSORS": False,
# }
# },
# )
def test_db_pool(self):
"""Test DB Config with pool"""
config = ConfigLoader()
config.set("postgresql.host", "foo")
config.set("postgresql.name", "foo")
config.set("postgresql.user", "foo")
config.set("postgresql.password", "foo")
config.set("postgresql.port", "foo")
config.set("postgresql.test.name", "foo")
config.set("postgresql.use_pool", True)
conf = django_db_config(config)
self.assertEqual(
conf,
{
"default": {
"ENGINE": "authentik.root.db",
"HOST": "foo",
"NAME": "foo",
"OPTIONS": {
"pool": True,
"sslcert": None,
"sslkey": None,
"sslmode": None,
"sslrootcert": None,
},
"PASSWORD": "foo",
"PORT": "foo",
"TEST": {"NAME": "foo"},
"USER": "foo",
"CONN_MAX_AGE": 0,
"CONN_HEALTH_CHECKS": False,
"DISABLE_SERVER_SIDE_CURSORS": False,
}
},
)
# def test_db_pool_options(self):
# """Test DB Config with pool"""
# config = ConfigLoader()
# config.set("postgresql.host", "foo")
# config.set("postgresql.name", "foo")
# config.set("postgresql.user", "foo")
# config.set("postgresql.password", "foo")
# config.set("postgresql.port", "foo")
# config.set("postgresql.test.name", "foo")
# config.set("postgresql.use_pool", True)
# config.set(
# "postgresql.pool_options",
# base64.b64encode(
# dumps(
# {
# "max_size": 15,
# }
# ).encode()
# ).decode(),
# )
# conf = django_db_config(config)
# self.assertEqual(
# conf,
# {
# "default": {
# "ENGINE": "authentik.root.db",
# "HOST": "foo",
# "NAME": "foo",
# "OPTIONS": {
# "pool": {
# "max_size": 15,
# },
# "sslcert": None,
# "sslkey": None,
# "sslmode": None,
# "sslrootcert": None,
# },
# "PASSWORD": "foo",
# "PORT": "foo",
# "TEST": {"NAME": "foo"},
# "USER": "foo",
# "CONN_MAX_AGE": 0,
# "CONN_HEALTH_CHECKS": False,
# "DISABLE_SERVER_SIDE_CURSORS": False,
# }
# },
# )
def test_db_pool_options(self):
"""Test DB Config with pool"""
config = ConfigLoader()
config.set("postgresql.host", "foo")
config.set("postgresql.name", "foo")
config.set("postgresql.user", "foo")
config.set("postgresql.password", "foo")
config.set("postgresql.port", "foo")
config.set("postgresql.test.name", "foo")
config.set("postgresql.use_pool", True)
config.set(
"postgresql.pool_options",
base64.b64encode(
dumps(
{
"max_size": 15,
}
).encode()
).decode(),
)
conf = django_db_config(config)
self.assertEqual(
conf,
{
"default": {
"ENGINE": "authentik.root.db",
"HOST": "foo",
"NAME": "foo",
"OPTIONS": {
"pool": {
"max_size": 15,
},
"sslcert": None,
"sslkey": None,
"sslmode": None,
"sslrootcert": None,
},
"PASSWORD": "foo",
"PORT": "foo",
"TEST": {"NAME": "foo"},
"USER": "foo",
"CONN_MAX_AGE": 0,
"CONN_HEALTH_CHECKS": False,
"DISABLE_SERVER_SIDE_CURSORS": False,
}
},
)

View File

@ -74,8 +74,6 @@ class OutpostConfig:
kubernetes_ingress_annotations: dict[str, str] = field(default_factory=dict)
kubernetes_ingress_secret_name: str = field(default="authentik-outpost-tls")
kubernetes_ingress_class_name: str | None = field(default=None)
kubernetes_httproute_annotations: dict[str, str] = field(default_factory=dict)
kubernetes_httproute_parent_refs: list[dict[str, str]] = field(default_factory=list)
kubernetes_service_type: str = field(default="ClusterIP")
kubernetes_disabled_components: list[str] = field(default_factory=list)
kubernetes_image_pull_secrets: list[str] = field(default_factory=list)

View File

@ -1,234 +0,0 @@
from dataclasses import asdict, dataclass, field
from typing import TYPE_CHECKING
from urllib.parse import urlparse
from dacite.core import from_dict
from kubernetes.client import ApiextensionsV1Api, CustomObjectsApi, V1ObjectMeta
from authentik.outposts.controllers.base import FIELD_MANAGER
from authentik.outposts.controllers.k8s.base import KubernetesObjectReconciler
from authentik.outposts.controllers.k8s.triggers import NeedsUpdate
from authentik.outposts.controllers.kubernetes import KubernetesController
from authentik.providers.proxy.models import ProxyMode, ProxyProvider
if TYPE_CHECKING:
from authentik.outposts.controllers.kubernetes import KubernetesController
@dataclass(slots=True)
class RouteBackendRef:
name: str
port: int
@dataclass(slots=True)
class RouteSpecParentRefs:
name: str
sectionName: str | None = None
port: int | None = None
namespace: str | None = None
kind: str = "Gateway"
group: str = "gateway.networking.k8s.io"
@dataclass(slots=True)
class HTTPRouteSpecRuleMatchPath:
type: str
value: str
@dataclass(slots=True)
class HTTPRouteSpecRuleMatchHeader:
name: str
value: str
type: str = "Exact"
@dataclass(slots=True)
class HTTPRouteSpecRuleMatch:
path: HTTPRouteSpecRuleMatchPath
headers: list[HTTPRouteSpecRuleMatchHeader]
@dataclass(slots=True)
class HTTPRouteSpecRule:
backendRefs: list[RouteBackendRef]
matches: list[HTTPRouteSpecRuleMatch]
@dataclass(slots=True)
class HTTPRouteSpec:
parentRefs: list[RouteSpecParentRefs]
hostnames: list[str]
rules: list[HTTPRouteSpecRule]
@dataclass(slots=True)
class HTTPRouteMetadata:
name: str
namespace: str
annotations: dict = field(default_factory=dict)
labels: dict = field(default_factory=dict)
@dataclass(slots=True)
class HTTPRoute:
apiVersion: str
kind: str
metadata: HTTPRouteMetadata
spec: HTTPRouteSpec
class HTTPRouteReconciler(KubernetesObjectReconciler):
"""Kubernetes Gateway API HTTPRoute Reconciler"""
def __init__(self, controller: "KubernetesController") -> None:
super().__init__(controller)
self.api_ex = ApiextensionsV1Api(controller.client)
self.api = CustomObjectsApi(controller.client)
self.crd_group = "gateway.networking.k8s.io"
self.crd_version = "v1"
self.crd_plural = "httproutes"
@staticmethod
def reconciler_name() -> str:
return "httproute"
@property
def noop(self) -> bool:
if not self.crd_exists():
self.logger.debug("CRD doesn't exist")
return True
if not self.controller.outpost.config.kubernetes_httproute_parent_refs:
self.logger.debug("HTTPRoute parentRefs not set.")
return True
return False
def crd_exists(self) -> bool:
"""Check if the Gateway API resources exists"""
return bool(
len(
self.api_ex.list_custom_resource_definition(
field_selector=f"metadata.name={self.crd_plural}.{self.crd_group}"
).items
)
)
def reconcile(self, current: HTTPRoute, reference: HTTPRoute):
super().reconcile(current, reference)
if current.metadata.annotations != reference.metadata.annotations:
raise NeedsUpdate()
if current.spec.parentRefs != reference.spec.parentRefs:
raise NeedsUpdate()
if current.spec.hostnames != reference.spec.hostnames:
raise NeedsUpdate()
if current.spec.rules != reference.spec.rules:
raise NeedsUpdate()
def get_object_meta(self, **kwargs) -> V1ObjectMeta:
return super().get_object_meta(
**kwargs,
)
def get_reference_object(self) -> HTTPRoute:
hostnames = []
rules = []
for proxy_provider in ProxyProvider.objects.filter(outpost__in=[self.controller.outpost]):
proxy_provider: ProxyProvider
external_host_name = urlparse(proxy_provider.external_host)
if proxy_provider.mode in [ProxyMode.FORWARD_SINGLE, ProxyMode.FORWARD_DOMAIN]:
rule = HTTPRouteSpecRule(
backendRefs=[RouteBackendRef(name=self.name, port=9000)],
matches=[
HTTPRouteSpecRuleMatch(
headers=[
HTTPRouteSpecRuleMatchHeader(
name="Host",
value=external_host_name.hostname,
)
],
path=HTTPRouteSpecRuleMatchPath(
type="PathPrefix", value="/outpost.goauthentik.io"
),
)
],
)
else:
rule = HTTPRouteSpecRule(
backendRefs=[RouteBackendRef(name=self.name, port=9000)],
matches=[
HTTPRouteSpecRuleMatch(
headers=[
HTTPRouteSpecRuleMatchHeader(
name="Host",
value=external_host_name.hostname,
)
],
path=HTTPRouteSpecRuleMatchPath(type="PathPrefix", value="/"),
)
],
)
hostnames.append(external_host_name.hostname)
rules.append(rule)
return HTTPRoute(
apiVersion=f"{self.crd_group}/{self.crd_version}",
kind="HTTPRoute",
metadata=HTTPRouteMetadata(
name=self.name,
namespace=self.namespace,
annotations=self.controller.outpost.config.kubernetes_httproute_annotations,
labels=self.get_object_meta().labels,
),
spec=HTTPRouteSpec(
parentRefs=[
from_dict(RouteSpecParentRefs, spec)
for spec in self.controller.outpost.config.kubernetes_httproute_parent_refs
],
hostnames=hostnames,
rules=rules,
),
)
def create(self, reference: HTTPRoute):
return self.api.create_namespaced_custom_object(
group=self.crd_group,
version=self.crd_version,
plural=self.crd_plural,
namespace=self.namespace,
body=asdict(reference),
field_manager=FIELD_MANAGER,
)
def delete(self, reference: HTTPRoute):
return self.api.delete_namespaced_custom_object(
group=self.crd_group,
version=self.crd_version,
plural=self.crd_plural,
namespace=self.namespace,
name=self.name,
)
def retrieve(self) -> HTTPRoute:
return from_dict(
HTTPRoute,
self.api.get_namespaced_custom_object(
group=self.crd_group,
version=self.crd_version,
plural=self.crd_plural,
namespace=self.namespace,
name=self.name,
),
)
def update(self, current: HTTPRoute, reference: HTTPRoute):
return self.api.patch_namespaced_custom_object(
group=self.crd_group,
version=self.crd_version,
plural=self.crd_plural,
namespace=self.namespace,
name=self.name,
body=asdict(reference),
field_manager=FIELD_MANAGER,
)

View File

@ -3,7 +3,6 @@
from authentik.outposts.controllers.base import DeploymentPort
from authentik.outposts.controllers.kubernetes import KubernetesController
from authentik.outposts.models import KubernetesServiceConnection, Outpost
from authentik.providers.proxy.controllers.k8s.httproute import HTTPRouteReconciler
from authentik.providers.proxy.controllers.k8s.ingress import IngressReconciler
from authentik.providers.proxy.controllers.k8s.traefik import TraefikMiddlewareReconciler
@ -19,10 +18,8 @@ class ProxyKubernetesController(KubernetesController):
DeploymentPort(9443, "https", "tcp"),
]
self.reconcilers[IngressReconciler.reconciler_name()] = IngressReconciler
self.reconcilers[HTTPRouteReconciler.reconciler_name()] = HTTPRouteReconciler
self.reconcilers[TraefikMiddlewareReconciler.reconciler_name()] = (
TraefikMiddlewareReconciler
)
self.reconcile_order.append(IngressReconciler.reconciler_name())
self.reconcile_order.append(HTTPRouteReconciler.reconciler_name())
self.reconcile_order.append(TraefikMiddlewareReconciler.reconciler_name())

View File

@ -66,10 +66,7 @@ class RACClientConsumer(AsyncWebsocketConsumer):
def init_outpost_connection(self):
"""Initialize guac connection settings"""
self.token = (
ConnectionToken.filter_not_expired(
token=self.scope["url_route"]["kwargs"]["token"],
session__session__session_key=self.scope["session"].session_key,
)
ConnectionToken.filter_not_expired(token=self.scope["url_route"]["kwargs"]["token"])
.select_related("endpoint", "provider", "session", "session__user")
.first()
)

View File

@ -87,22 +87,3 @@ class TestRACViews(APITestCase):
)
body = loads(flow_response.content)
self.assertEqual(body["component"], "ak-stage-access-denied")
def test_different_session(self):
"""Test request"""
self.client.force_login(self.user)
response = self.client.get(
reverse(
"authentik_providers_rac:start",
kwargs={"app": self.app.slug, "endpoint": str(self.endpoint.pk)},
)
)
self.assertEqual(response.status_code, 302)
flow_response = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
)
body = loads(flow_response.content)
next_url = body["to"]
self.client.logout()
final_response = self.client.get(next_url)
self.assertEqual(final_response.url, reverse("authentik_core:if-user"))

View File

@ -65,10 +65,7 @@ class RACInterface(InterfaceView):
def dispatch(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
# Early sanity check to ensure token still exists
token = ConnectionToken.filter_not_expired(
token=self.kwargs["token"],
session__session__session_key=request.session.session_key,
).first()
token = ConnectionToken.filter_not_expired(token=self.kwargs["token"]).first()
if not token:
return redirect("authentik_core:if-user")
self.token = token

View File

@ -99,7 +99,6 @@ class RBACPermissionViewSet(ReadOnlyModelViewSet):
filterset_class = PermissionFilter
permission_classes = [IsAuthenticated]
search_fields = [
"name",
"codename",
"content_type__model",
"content_type__app_label",

View File

@ -97,8 +97,7 @@ class GroupsView(SCIMObjectView):
self.logger.warning("Invalid group member", exc=exc)
continue
query |= Q(uuid=member.value)
if query:
group.users.set(User.objects.filter(query))
group.users.set(User.objects.filter(query))
if not connection:
connection, _ = SCIMSourceGroup.objects.get_or_create(
source=self.source,

File diff suppressed because one or more lines are too long

View File

@ -2,7 +2,7 @@
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "https://goauthentik.io/blueprints/schema.json",
"type": "object",
"title": "authentik 2025.4.3 Blueprint schema",
"title": "authentik 2025.2.4 Blueprint schema",
"required": [
"version",
"entries"

View File

@ -31,7 +31,7 @@ services:
volumes:
- redis:/data
server:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.4.3}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.2.4}
restart: unless-stopped
command: server
environment:
@ -55,7 +55,7 @@ services:
redis:
condition: service_healthy
worker:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.4.3}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.2.4}
restart: unless-stopped
command: worker
environment:

2
go.mod
View File

@ -27,7 +27,7 @@ require (
github.com/spf13/cobra v1.9.1
github.com/stretchr/testify v1.10.0
github.com/wwt/guac v1.3.2
goauthentik.io/api/v3 v3.2025024.9
goauthentik.io/api/v3 v3.2025024.8
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
golang.org/x/oauth2 v0.29.0
golang.org/x/sync v0.13.0

4
go.sum
View File

@ -290,8 +290,8 @@ go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
goauthentik.io/api/v3 v3.2025024.9 h1:i3tbkyotE32ZpJ729BsPWTuLQUdtZ54Li4aP1amZzsM=
goauthentik.io/api/v3 v3.2025024.9/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
goauthentik.io/api/v3 v3.2025024.8 h1:2mG4CqGSsmZq2CtRehxpDjsER43U/JQSoTOn5VC1ui4=
goauthentik.io/api/v3 v3.2025024.8/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=

View File

@ -29,4 +29,4 @@ func UserAgent() string {
return fmt.Sprintf("authentik@%s", FullVersion())
}
const VERSION = "2025.4.3"
const VERSION = "2025.2.4"

View File

@ -56,7 +56,6 @@ EXPOSE 3389 6636 9300
USER 1000
ENV TMPDIR=/dev/shm/ \
GOFIPS=1
ENV GOFIPS=1
ENTRYPOINT ["/ldap"]

View File

@ -62,8 +62,7 @@ function prepare_debug {
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y --no-install-recommends krb5-kdc krb5-user krb5-admin-server libkrb5-dev gcc
source "${VENV_PATH}/bin/activate"
uv sync --active --frozen
VIRTUAL_ENV=/ak-root/.venv uv sync --frozen
touch /unittest.xml
chown authentik:authentik /unittest.xml
}
@ -83,8 +82,7 @@ if [[ "$1" == "server" ]]; then
run_authentik
elif [[ "$1" == "worker" ]]; then
set_mode "worker"
shift
check_if_root "python -m manage worker $@"
check_if_root "python -m manage worker"
elif [[ "$1" == "worker-status" ]]; then
wait_for_db
celery -A authentik.root.celery flower \
@ -98,7 +96,6 @@ elif [[ "$1" == "test-all" ]]; then
elif [[ "$1" == "healthcheck" ]]; then
run_authentik healthcheck $(cat $MODE_FILE)
elif [[ "$1" == "dump_config" ]]; then
shift
exec python -m authentik.lib.config $@
elif [[ "$1" == "debug" ]]; then
exec sleep infinity

View File

@ -9,7 +9,7 @@
"version": "0.0.0",
"license": "MIT",
"devDependencies": {
"aws-cdk": "^2.1012.0",
"aws-cdk": "^2.1010.0",
"cross-env": "^7.0.3"
},
"engines": {
@ -17,9 +17,9 @@
}
},
"node_modules/aws-cdk": {
"version": "2.1012.0",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1012.0.tgz",
"integrity": "sha512-C6jSWkqP0hkY2Cs300VJHjspmTXDTMfB813kwZvRbd/OsKBfTBJBbYU16VoLAp1LVEOnQMf8otSlaSgzVF0X9A==",
"version": "2.1010.0",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1010.0.tgz",
"integrity": "sha512-kYNzBXVUZoRrTuYxRRA2Loz/Uvay0MqHobg8KPZaWylIbw/meUDgtoATRNt+stOdJ9PHODTjWmlDKI+2/KoF+w==",
"dev": true,
"license": "Apache-2.0",
"bin": {

View File

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

View File

@ -26,7 +26,7 @@ Parameters:
Description: authentik Docker image
AuthentikVersion:
Type: String
Default: 2025.4.3
Default: 2025.2.4
Description: authentik Docker image tag
AuthentikServerCPU:
Type: Number

View File

@ -3,7 +3,7 @@ from lifecycle.migrate import BaseMigration
SQL_STATEMENT = """
BEGIN TRANSACTION;
ALTER TABLE IF EXISTS authentik_tenants_tenant RENAME TO authentik_brands_brand;
ALTER TABLE authentik_tenants_tenant RENAME TO authentik_brands_brand;
UPDATE django_migrations SET app = replace(app, 'authentik_tenants', 'authentik_brands');
UPDATE django_content_type SET app_label = replace(app_label, 'authentik_tenants', 'authentik_brands');
COMMIT;

Binary file not shown.

View File

@ -8,6 +8,7 @@
# Jens L. <jens@goauthentik.io>, 2022
# Lars Lehmann <lars@lars-lehmann.net>, 2023
# Johannes —/—, 2023
# Dominic Wagner <mail@dominic-wagner.de>, 2023
# fde4f289d99ed356ff5cfdb762dc44aa_a8a971d, 2023
# Christian Foellmann <foellmann@foe-services.de>, 2023
# kidhab, 2023
@ -29,18 +30,17 @@
# Alexander Möbius, 2025
# Jonas, 2025
# Niklas Kroese, 2025
# datenschmutz, 2025
# 97cce0ae0cad2a2cc552d3165d04643e_de3d740, 2025
# Dominic Wagner <mail@dominic-wagner.de>, 2025
# datenschmutz, 2025
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
"POT-Creation-Date: 2025-04-11 00:10+0000\n"
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
"Last-Translator: Dominic Wagner <mail@dominic-wagner.de>, 2025\n"
"Last-Translator: datenschmutz, 2025\n"
"Language-Team: German (https://app.transifex.com/authentik/teams/119923/de/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@ -214,7 +214,6 @@ msgid "User's display name."
msgstr "Anzeigename"
#: authentik/core/models.py authentik/providers/oauth2/models.py
#: authentik/rbac/models.py
msgid "User"
msgstr "Benutzer"
@ -403,18 +402,6 @@ msgstr "Eigenschaft"
msgid "Property Mappings"
msgstr "Eigenschaften"
#: authentik/core/models.py
msgid "session data"
msgstr ""
#: authentik/core/models.py
msgid "Session"
msgstr "Sitzung"
#: authentik/core/models.py
msgid "Sessions"
msgstr "Sitzungen"
#: authentik/core/models.py
msgid "Authenticated Session"
msgstr "Authentifizierte Sitzung"
@ -524,38 +511,6 @@ msgstr "Lizenzverwendung"
msgid "License Usage Records"
msgstr "Lizenzverwendung Aufzeichnungen"
#: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py
msgid "Field key to check, field keys defined in Prompt stages are available."
msgstr ""
"Zu prüfender Feldschlüssel, die in den Aufforderungsstufen definierten "
"Feldschlüssel sind verfügbar."
#: authentik/enterprise/policies/unique_password/models.py
msgid "Number of passwords to check against."
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py
msgid "Password not set in context"
msgstr "Passwort nicht im Kontext festgelegt"
#: authentik/enterprise/policies/unique_password/models.py
msgid "This password has been used previously. Please choose a different one."
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policy"
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policies"
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "User Password History"
msgstr ""
#: authentik/enterprise/policy.py
msgid "Enterprise required to access this feature."
msgstr "Enterprise ist erforderlich, um auf diese Funktion zuzugreifen."
@ -1348,6 +1303,12 @@ msgstr "Richtlinien Cache Metriken anzeigen"
msgid "Clear Policy's cache metrics"
msgstr "Richtlinien Cache Metriken löschen"
#: authentik/policies/password/models.py
msgid "Field key to check, field keys defined in Prompt stages are available."
msgstr ""
"Zu prüfender Feldschlüssel, die in den Aufforderungsstufen definierten "
"Feldschlüssel sind verfügbar."
#: authentik/policies/password/models.py
msgid "How many times the password hash is allowed to be on haveibeenpwned"
msgstr "Wie häufig der Passwort-Hash auf haveibeenpwned vertreten sein darf"
@ -1359,6 +1320,10 @@ msgstr ""
"Die Richtlinie wird verweigert, wenn die zxcvbn-Bewertung gleich oder "
"kleiner diesem Wert ist."
#: authentik/policies/password/models.py
msgid "Password not set in context"
msgstr "Passwort nicht im Kontext festgelegt"
#: authentik/policies/password/models.py
msgid "Invalid password."
msgstr "Ungültiges Passwort."
@ -1400,6 +1365,20 @@ msgstr "Reputationswert"
msgid "Reputation Scores"
msgstr "Reputationswert"
#: authentik/policies/templates/policies/buffer.html
msgid "Waiting for authentication..."
msgstr ""
#: authentik/policies/templates/policies/buffer.html
msgid ""
"You're already authenticating in another tab. This page will refresh once "
"authentication is completed."
msgstr ""
#: authentik/policies/templates/policies/buffer.html
msgid "Authenticate in this tab"
msgstr ""
#: authentik/policies/templates/policies/denied.html
msgid "Permission denied"
msgstr "Erlaubnis verweigert"
@ -2229,10 +2208,6 @@ msgstr "Rolle"
msgid "Roles"
msgstr "Rollen"
#: authentik/rbac/models.py
msgid "Initial Permissions"
msgstr ""
#: authentik/rbac/models.py
msgid "System permission"
msgstr "Systemberechtigung"
@ -2503,22 +2478,6 @@ msgstr "LDAP Quelle Eigenschafts-Zuordnung"
msgid "LDAP Source Property Mappings"
msgstr "LDAP Quelle Eigenschafts-Zuordnungen"
#: authentik/sources/ldap/models.py
msgid "User LDAP Source Connection"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "User LDAP Source Connections"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "Group LDAP Source Connection"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "Group LDAP Source Connections"
msgstr ""
#: authentik/sources/ldap/signals.py
msgid "Password does not match Active Directory Complexity."
msgstr ""
@ -2528,14 +2487,6 @@ msgstr ""
msgid "No token received."
msgstr "Kein Token empfangen."
#: authentik/sources/oauth/models.py
msgid "HTTP Basic Authentication"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "Include the client ID and secret as request parameters"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "Request Token URL"
msgstr "Token-URL anfordern"
@ -2577,12 +2528,6 @@ msgstr ""
msgid "Additional Scopes"
msgstr "zusätzliche Scopes"
#: authentik/sources/oauth/models.py
msgid ""
"How to perform authentication during an authorization_code token request "
"flow"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "OAuth Source"
msgstr "Outh Quelle"
@ -3489,12 +3434,6 @@ msgstr ""
"Wenn aktiviert, wird die Phase auch dann erfolgreich abgeschlossen und "
"fortgesetzt, wenn falsche Benutzerdaten eingegeben wurden."
#: authentik/stages/identification/models.py
msgid ""
"Show the user the 'Remember me on this device' toggle, allowing repeat users"
" to skip straight to entering their password."
msgstr ""
#: authentik/stages/identification/models.py
msgid "Optional enrollment flow, which is linked at the bottom of the page."
msgstr "Optionaler Registrierungs-Flow, der unten auf der Seite verlinkt ist."
@ -3887,14 +3826,6 @@ msgstr ""
"Die Ereignisse werden nach dieser Dauer gelöscht (Format: "
"Wochen=3;Tage=2;Stunden=3,Sekunden=2)."
#: authentik/tenants/models.py
msgid "Reputation cannot decrease lower than this value. Zero or negative."
msgstr ""
#: authentik/tenants/models.py
msgid "Reputation cannot increase higher than this value. Zero or positive."
msgstr ""
#: authentik/tenants/models.py
msgid "The option configures the footer links on the flow executor pages."
msgstr ""

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
"POT-Creation-Date: 2025-04-22 13:40+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -1255,6 +1255,20 @@ msgstr ""
msgid "Reputation Scores"
msgstr ""
#: authentik/policies/templates/policies/buffer.html
msgid "Waiting for authentication..."
msgstr ""
#: authentik/policies/templates/policies/buffer.html
msgid ""
"You're already authenticating in another tab. This page will refresh once "
"authentication is completed."
msgstr ""
#: authentik/policies/templates/policies/buffer.html
msgid "Authenticate in this tab"
msgstr ""
#: authentik/policies/templates/policies/denied.html
msgid "Permission denied"
msgstr ""

Binary file not shown.

View File

@ -15,7 +15,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
"POT-Creation-Date: 2025-04-11 00:10+0000\n"
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
"Last-Translator: Jens L. <jens@goauthentik.io>, 2025\n"
"Language-Team: Spanish (https://app.transifex.com/authentik/teams/119923/es/)\n"
@ -190,7 +190,6 @@ msgid "User's display name."
msgstr "Nombre para mostrar del usuario."
#: authentik/core/models.py authentik/providers/oauth2/models.py
#: authentik/rbac/models.py
msgid "User"
msgstr "Usuario"
@ -379,18 +378,6 @@ msgstr "Asignación de Propiedades"
msgid "Property Mappings"
msgstr "Asignaciones de Propiedades"
#: authentik/core/models.py
msgid "session data"
msgstr ""
#: authentik/core/models.py
msgid "Session"
msgstr "Sesión"
#: authentik/core/models.py
msgid "Sessions"
msgstr "Sesiones"
#: authentik/core/models.py
msgid "Authenticated Session"
msgstr "Sesión autenticada"
@ -498,38 +485,6 @@ msgstr "Uso de Licencias"
msgid "License Usage Records"
msgstr "Registro de Uso de Licencias"
#: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py
msgid "Field key to check, field keys defined in Prompt stages are available."
msgstr ""
"Clave de campo a verificar, las claves de campo definidas en las etapas de "
"Solicitud están disponibles."
#: authentik/enterprise/policies/unique_password/models.py
msgid "Number of passwords to check against."
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py
msgid "Password not set in context"
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 ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policy"
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policies"
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "User Password History"
msgstr ""
#: authentik/enterprise/policy.py
msgid "Enterprise required to access this feature."
msgstr "Se requiere de Enterprise para acceder esta característica."
@ -1313,6 +1268,12 @@ msgstr "Ver las métricas de caché de la Política"
msgid "Clear Policy's cache metrics"
msgstr "Borrar las métricas de caché de la Política"
#: authentik/policies/password/models.py
msgid "Field key to check, field keys defined in Prompt stages are available."
msgstr ""
"Clave de campo a verificar, las claves de campo definidas en las etapas de "
"Solicitud están disponibles."
#: authentik/policies/password/models.py
msgid "How many times the password hash is allowed to be on haveibeenpwned"
msgstr ""
@ -1326,6 +1287,10 @@ msgstr ""
"Si la puntuación zxcvbn es igual o menor que este valor, la política "
"fallará."
#: authentik/policies/password/models.py
msgid "Password not set in context"
msgstr "La contraseña no se ha establecido en contexto"
#: authentik/policies/password/models.py
msgid "Invalid password."
msgstr "Contraseña inválida."
@ -1367,6 +1332,20 @@ msgstr "Puntuación de Reputacion"
msgid "Reputation Scores"
msgstr "Puntuaciones de Reputacion"
#: authentik/policies/templates/policies/buffer.html
msgid "Waiting for authentication..."
msgstr ""
#: authentik/policies/templates/policies/buffer.html
msgid ""
"You're already authenticating in another tab. This page will refresh once "
"authentication is completed."
msgstr ""
#: authentik/policies/templates/policies/buffer.html
msgid "Authenticate in this tab"
msgstr ""
#: authentik/policies/templates/policies/denied.html
msgid "Permission denied"
msgstr "Permiso denegado"
@ -2196,10 +2175,6 @@ msgstr "Rol"
msgid "Roles"
msgstr "Roles"
#: authentik/rbac/models.py
msgid "Initial Permissions"
msgstr ""
#: authentik/rbac/models.py
msgid "System permission"
msgstr "Permiso de sistema"
@ -2468,22 +2443,6 @@ msgstr "Asignación de Propiedades de Fuente de LDAP"
msgid "LDAP Source Property Mappings"
msgstr "Asignaciones de Propiedades de Fuente de LDAP"
#: authentik/sources/ldap/models.py
msgid "User LDAP Source Connection"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "User LDAP Source Connections"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "Group LDAP Source Connection"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "Group LDAP Source Connections"
msgstr ""
#: authentik/sources/ldap/signals.py
msgid "Password does not match Active Directory Complexity."
msgstr "La contraseña no coincide con la complejidad de Active Directory."
@ -2492,14 +2451,6 @@ msgstr "La contraseña no coincide con la complejidad de Active Directory."
msgid "No token received."
msgstr "No se recibió ningún token."
#: authentik/sources/oauth/models.py
msgid "HTTP Basic Authentication"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "Include the client ID and secret as request parameters"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "Request Token URL"
msgstr "Solicitar URL de token"
@ -2540,12 +2491,6 @@ msgstr "URL utilizada por authentik para obtener información del usuario."
msgid "Additional Scopes"
msgstr "Alcances Adicionales"
#: authentik/sources/oauth/models.py
msgid ""
"How to perform authentication during an authorization_code token request "
"flow"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "OAuth Source"
msgstr "Fuente de OAuth"
@ -3462,12 +3407,6 @@ msgstr ""
"Cuando está habilitado, la etapa tendrá éxito y continuará incluso cuando se"
" ingrese información de usuario incorrecta."
#: authentik/stages/identification/models.py
msgid ""
"Show the user the 'Remember me on this device' toggle, allowing repeat users"
" to skip straight to entering their password."
msgstr ""
#: authentik/stages/identification/models.py
msgid "Optional enrollment flow, which is linked at the bottom of the page."
msgstr ""
@ -3855,14 +3794,6 @@ msgstr ""
"Los Eventos serán eliminados después de este periodo. (Formato: "
"weeks=3;days=2;hours=3,seconds=2)."
#: authentik/tenants/models.py
msgid "Reputation cannot decrease lower than this value. Zero or negative."
msgstr ""
#: authentik/tenants/models.py
msgid "Reputation cannot increase higher than this value. Zero or positive."
msgstr ""
#: authentik/tenants/models.py
msgid "The option configures the footer links on the flow executor pages."
msgstr ""

Binary file not shown.

View File

@ -15,7 +15,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
"POT-Creation-Date: 2025-04-11 00:10+0000\n"
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
"Last-Translator: Ville Ranki, 2025\n"
"Language-Team: Finnish (https://app.transifex.com/authentik/teams/119923/fi/)\n"
@ -186,7 +186,6 @@ msgid "User's display name."
msgstr "Käyttäjän näytettävä nimi"
#: authentik/core/models.py authentik/providers/oauth2/models.py
#: authentik/rbac/models.py
msgid "User"
msgstr "Käyttäjä"
@ -372,18 +371,6 @@ msgstr "Ominaisuuskytkentä"
msgid "Property Mappings"
msgstr "Ominaisuuskytkennät"
#: authentik/core/models.py
msgid "session data"
msgstr ""
#: authentik/core/models.py
msgid "Session"
msgstr "Istunto"
#: authentik/core/models.py
msgid "Sessions"
msgstr ""
#: authentik/core/models.py
msgid "Authenticated Session"
msgstr "Autentikoitu istunto"
@ -491,38 +478,6 @@ msgstr "Lisenssin käyttö"
msgid "License Usage Records"
msgstr "Lisenssin käyttötiedot"
#: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py
msgid "Field key to check, field keys defined in Prompt stages are available."
msgstr ""
"Kentän avain, joka tarkistetaan. Kysymysvaiheissa määritellyt kenttien "
"avaimet ovat käytettävissä."
#: authentik/enterprise/policies/unique_password/models.py
msgid "Number of passwords to check against."
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py
msgid "Password not set in context"
msgstr "Salasanaa ei ole asetettu kontekstissa"
#: authentik/enterprise/policies/unique_password/models.py
msgid "This password has been used previously. Please choose a different one."
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policy"
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policies"
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "User Password History"
msgstr ""
#: authentik/enterprise/policy.py
msgid "Enterprise required to access this feature."
msgstr "Tämän ominaisuuden käyttöön tarvitaan Enterprise-versiota."
@ -1296,6 +1251,12 @@ msgstr "Näytä käytäntövälimuistitilastot"
msgid "Clear Policy's cache metrics"
msgstr "Tyhjennä käytäntövälimuistitilastot"
#: authentik/policies/password/models.py
msgid "Field key to check, field keys defined in Prompt stages are available."
msgstr ""
"Kentän avain, joka tarkistetaan. Kysymysvaiheissa määritellyt kenttien "
"avaimet ovat käytettävissä."
#: authentik/policies/password/models.py
msgid "How many times the password hash is allowed to be on haveibeenpwned"
msgstr ""
@ -1308,6 +1269,10 @@ msgstr ""
"Jos zxcvbn-pistemäärä on tämä arvo tai pienempi, käytännön suorittaminen "
"epäonnistuu."
#: authentik/policies/password/models.py
msgid "Password not set in context"
msgstr "Salasanaa ei ole asetettu kontekstissa"
#: authentik/policies/password/models.py
msgid "Invalid password."
msgstr "Virheellinen salasana."
@ -1349,6 +1314,20 @@ msgstr "Mainepistemäärä"
msgid "Reputation Scores"
msgstr "Mainepistemäärät"
#: authentik/policies/templates/policies/buffer.html
msgid "Waiting for authentication..."
msgstr ""
#: authentik/policies/templates/policies/buffer.html
msgid ""
"You're already authenticating in another tab. This page will refresh once "
"authentication is completed."
msgstr ""
#: authentik/policies/templates/policies/buffer.html
msgid "Authenticate in this tab"
msgstr ""
#: authentik/policies/templates/policies/denied.html
msgid "Permission denied"
msgstr "Käyttö evätty"
@ -2176,10 +2155,6 @@ msgstr "Rooli"
msgid "Roles"
msgstr "Roolit"
#: authentik/rbac/models.py
msgid "Initial Permissions"
msgstr ""
#: authentik/rbac/models.py
msgid "System permission"
msgstr "Järjestelmän käyttöoikeus"
@ -2445,22 +2420,6 @@ msgstr "LDAP-lähteen ominaisuuskytkentä"
msgid "LDAP Source Property Mappings"
msgstr "LDAP-lähteen ominaisuuskytkennät"
#: authentik/sources/ldap/models.py
msgid "User LDAP Source Connection"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "User LDAP Source Connections"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "Group LDAP Source Connection"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "Group LDAP Source Connections"
msgstr ""
#: authentik/sources/ldap/signals.py
msgid "Password does not match Active Directory Complexity."
msgstr "Salasana ei vastaa Active Directoryn monimutkaisuusmääritystä."
@ -2469,14 +2428,6 @@ msgstr "Salasana ei vastaa Active Directoryn monimutkaisuusmääritystä."
msgid "No token received."
msgstr "Tunnistetta ei saatu."
#: authentik/sources/oauth/models.py
msgid "HTTP Basic Authentication"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "Include the client ID and secret as request parameters"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "Request Token URL"
msgstr "Pyyntötunnisteen URL"
@ -2517,12 +2468,6 @@ msgstr "URL, jota authentik käyttää käyttäjätiedon hakemiseksi."
msgid "Additional Scopes"
msgstr "Lisäkäyttöalueet"
#: authentik/sources/oauth/models.py
msgid ""
"How to perform authentication during an authorization_code token request "
"flow"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "OAuth Source"
msgstr "OAuth-lähde"
@ -3432,12 +3377,6 @@ msgstr ""
"Kun tämä on käytössä, vaihe onnistuu ja suoritus jatkuu, vaikka olisi "
"syötetty virheelliset käyttäjätiedot."
#: authentik/stages/identification/models.py
msgid ""
"Show the user the 'Remember me on this device' toggle, allowing repeat users"
" to skip straight to entering their password."
msgstr ""
#: authentik/stages/identification/models.py
msgid "Optional enrollment flow, which is linked at the bottom of the page."
msgstr ""
@ -3815,14 +3754,6 @@ msgstr ""
"Tapahtumat poistetaan tämän ajan jälkeen. (Muoto: "
"weeks=3;days=2;hours=3;seconds=2)."
#: authentik/tenants/models.py
msgid "Reputation cannot decrease lower than this value. Zero or negative."
msgstr ""
#: authentik/tenants/models.py
msgid "Reputation cannot increase higher than this value. Zero or positive."
msgstr ""
#: authentik/tenants/models.py
msgid "The option configures the footer links on the flow executor pages."
msgstr ""

Binary file not shown.

View File

@ -9,8 +9,8 @@
# Kyllian Delaye-Maillot, 2023
# Manuel Viens, 2023
# Mordecai, 2023
# Tina, 2024
# Charles Leclerc, 2025
# Tina, 2025
# nerdinator <florian.dupret@gmail.com>, 2025
# Marc Schmitt, 2025
#
@ -19,7 +19,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
"POT-Creation-Date: 2025-04-17 00:09+0000\n"
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
"Last-Translator: Marc Schmitt, 2025\n"
"Language-Team: French (https://app.transifex.com/authentik/teams/119923/fr/)\n"
@ -502,38 +502,6 @@ msgstr "Utilisation de la licence"
msgid "License Usage Records"
msgstr "Registre d'utilisation de la licence"
#: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py
msgid "Field key to check, field keys defined in Prompt stages are available."
msgstr ""
"Clé de champ à vérifier ; les clés de champ définies dans les étapes de "
"d'invite sont disponibles."
#: authentik/enterprise/policies/unique_password/models.py
msgid "Number of passwords to check against."
msgstr "Nombre de mots de passe à vérifier."
#: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py
msgid "Password not set in context"
msgstr "Mot de passe non défini dans le contexte"
#: authentik/enterprise/policies/unique_password/models.py
msgid "This password has been used previously. Please choose a different one."
msgstr "Ce mot de passe a déjà été utilisé. Veuillez en choisir un autre."
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policy"
msgstr "Politique d'unicité des mots de passe"
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policies"
msgstr "Politiques d'unicité des mots de passe"
#: authentik/enterprise/policies/unique_password/models.py
msgid "User Password History"
msgstr "Historique des mots de passe utilisateur"
#: authentik/enterprise/policy.py
msgid "Enterprise required to access this feature."
msgstr "Entreprise est requis pour accéder à cette fonctionnalité."
@ -1328,6 +1296,12 @@ msgstr "Voir les métriques de cache de la politique"
msgid "Clear Policy's cache metrics"
msgstr "Nettoyer les métriques de cache de la politique"
#: authentik/policies/password/models.py
msgid "Field key to check, field keys defined in Prompt stages are available."
msgstr ""
"Clé de champ à vérifier ; les clés de champ définies dans les étapes de "
"d'invite sont disponibles."
#: authentik/policies/password/models.py
msgid "How many times the password hash is allowed to be on haveibeenpwned"
msgstr ""
@ -1341,6 +1315,10 @@ msgstr ""
"Si le score zxcvbn est égal ou inférieur à cette valeur, la politique "
"échouera."
#: authentik/policies/password/models.py
msgid "Password not set in context"
msgstr "Mot de passe non défini dans le contexte"
#: authentik/policies/password/models.py
msgid "Invalid password."
msgstr "Mot de passe invalide."
@ -1382,6 +1360,22 @@ msgstr "Score de Réputation"
msgid "Reputation Scores"
msgstr "Scores de Réputation"
#: authentik/policies/templates/policies/buffer.html
msgid "Waiting for authentication..."
msgstr "En attente de l'authentification..."
#: authentik/policies/templates/policies/buffer.html
msgid ""
"You're already authenticating in another tab. This page will refresh once "
"authentication is completed."
msgstr ""
"Vous êtes déjà en cours d'authentification dans un autre onglet. Cette page "
"se rafraîchira lorsque l'authentification sera terminée."
#: authentik/policies/templates/policies/buffer.html
msgid "Authenticate in this tab"
msgstr "S'authentifier dans cet onglet"
#: authentik/policies/templates/policies/denied.html
msgid "Permission denied"
msgstr "Permission refusée"
@ -3491,15 +3485,6 @@ msgstr ""
"Lorsqu'activé, l'étape réussira et continuera même lorsque les informations "
"utilisateurs entrées sont invalides."
#: authentik/stages/identification/models.py
msgid ""
"Show the user the 'Remember me on this device' toggle, allowing repeat users"
" to skip straight to entering their password."
msgstr ""
"Afficher à l'utilisateur l'option \"Se souvenir de moi sur cet appareil\", "
"afin de permettre aux utilisateurs réguliers de passer directement à la "
"saisie de leur mot de passe."
#: authentik/stages/identification/models.py
msgid "Optional enrollment flow, which is linked at the bottom of the page."
msgstr "Flux d'inscription facultatif, qui sera accessible en bas de page."

View File

@ -12,17 +12,17 @@
# tmassimi, 2024
# Marc Schmitt, 2024
# albanobattistella <albanobattistella@gmail.com>, 2024
# Kowalski Dragon (kowalski7cc) <kowalski.7cc@gmail.com>, 2025
# Matteo Piccina <altermatte@gmail.com>, 2025
# Kowalski Dragon (kowalski7cc) <kowalski.7cc@gmail.com>, 2025
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
"POT-Creation-Date: 2025-04-11 00:10+0000\n"
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
"Last-Translator: Matteo Piccina <altermatte@gmail.com>, 2025\n"
"Last-Translator: Kowalski Dragon (kowalski7cc) <kowalski.7cc@gmail.com>, 2025\n"
"Language-Team: Italian (https://app.transifex.com/authentik/teams/119923/it/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@ -194,7 +194,6 @@ msgid "User's display name."
msgstr "Nome visualizzato dell'utente."
#: authentik/core/models.py authentik/providers/oauth2/models.py
#: authentik/rbac/models.py
msgid "User"
msgstr "Utente"
@ -381,18 +380,6 @@ msgstr "Mappatura della proprietà"
msgid "Property Mappings"
msgstr "Mappatura delle proprietà"
#: authentik/core/models.py
msgid "session data"
msgstr ""
#: authentik/core/models.py
msgid "Session"
msgstr "Sessione"
#: authentik/core/models.py
msgid "Sessions"
msgstr "Sessioni"
#: authentik/core/models.py
msgid "Authenticated Session"
msgstr "Sessione Autenticata"
@ -500,38 +487,6 @@ msgstr "Utilizzo della licenza"
msgid "License Usage Records"
msgstr "Registri sull'utilizzo della licenza"
#: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py
msgid "Field key to check, field keys defined in Prompt stages are available."
msgstr ""
"Chiave di campo da verificare, sono disponibili le chiavi di campo definite "
"nelle fasi Richiesta."
#: authentik/enterprise/policies/unique_password/models.py
msgid "Number of passwords to check against."
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py
msgid "Password not set in context"
msgstr "Password non impostata nel contesto"
#: authentik/enterprise/policies/unique_password/models.py
msgid "This password has been used previously. Please choose a different one."
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policy"
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policies"
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "User Password History"
msgstr ""
#: authentik/enterprise/policy.py
msgid "Enterprise required to access this feature."
msgstr "Versione Enterprise richiesta per accedere a questa funzione"
@ -1319,6 +1274,12 @@ msgstr "Visualizza le metriche della cache della Policy"
msgid "Clear Policy's cache metrics"
msgstr "Cancellare le metriche della cache della Policy"
#: authentik/policies/password/models.py
msgid "Field key to check, field keys defined in Prompt stages are available."
msgstr ""
"Chiave di campo da verificare, sono disponibili le chiavi di campo definite "
"nelle fasi Richiesta."
#: authentik/policies/password/models.py
msgid "How many times the password hash is allowed to be on haveibeenpwned"
msgstr ""
@ -1331,6 +1292,10 @@ msgstr ""
"Se il punteggio zxcvbn è inferiore o uguale a questo valore, il criterio non"
" verrà soddisfatto."
#: authentik/policies/password/models.py
msgid "Password not set in context"
msgstr "Password non impostata nel contesto"
#: authentik/policies/password/models.py
msgid "Invalid password."
msgstr "Password invalida."
@ -1372,6 +1337,22 @@ msgstr "Punteggio di reputazione"
msgid "Reputation Scores"
msgstr "Punteggi di reputazione"
#: authentik/policies/templates/policies/buffer.html
msgid "Waiting for authentication..."
msgstr "In attesa di autenticazione..."
#: authentik/policies/templates/policies/buffer.html
msgid ""
"You're already authenticating in another tab. This page will refresh once "
"authentication is completed."
msgstr ""
"Ti stai già autenticando in un'altra scheda. Questa pagina si aggiornerà una"
" volta completata l'autenticazione."
#: authentik/policies/templates/policies/buffer.html
msgid "Authenticate in this tab"
msgstr "Autenticati in questa scheda"
#: authentik/policies/templates/policies/denied.html
msgid "Permission denied"
msgstr "Permesso negato"
@ -2201,10 +2182,6 @@ msgstr "Ruolo"
msgid "Roles"
msgstr "Ruoli"
#: authentik/rbac/models.py
msgid "Initial Permissions"
msgstr ""
#: authentik/rbac/models.py
msgid "System permission"
msgstr "Autorizzazione di sistema"
@ -2475,22 +2452,6 @@ msgstr "Mappatura delle proprietà sorgente LDAP"
msgid "LDAP Source Property Mappings"
msgstr "Mappature delle proprietà della sorgente LDAP"
#: authentik/sources/ldap/models.py
msgid "User LDAP Source Connection"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "User LDAP Source Connections"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "Group LDAP Source Connection"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "Group LDAP Source Connections"
msgstr ""
#: authentik/sources/ldap/signals.py
msgid "Password does not match Active Directory Complexity."
msgstr "La password non soddisfa la complessità Active Directory."
@ -2499,14 +2460,6 @@ msgstr "La password non soddisfa la complessità Active Directory."
msgid "No token received."
msgstr "Nessun token ricevuto."
#: authentik/sources/oauth/models.py
msgid "HTTP Basic Authentication"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "Include the client ID and secret as request parameters"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "Request Token URL"
msgstr "URL di Richiesta Token"
@ -2547,12 +2500,6 @@ msgstr "URL utilizzato da authentik per ottenere le informazioni dell'utente."
msgid "Additional Scopes"
msgstr "Ambiti aggiuntivi"
#: authentik/sources/oauth/models.py
msgid ""
"How to perform authentication during an authorization_code token request "
"flow"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "OAuth Source"
msgstr "Sorgente OAuth"
@ -3479,12 +3426,6 @@ msgstr ""
"Quando abilitato, la fase avrà successo e continuerà anche quando vengono "
"inserite informazioni utente errate."
#: authentik/stages/identification/models.py
msgid ""
"Show the user the 'Remember me on this device' toggle, allowing repeat users"
" to skip straight to entering their password."
msgstr ""
#: authentik/stages/identification/models.py
msgid "Optional enrollment flow, which is linked at the bottom of the page."
msgstr "Flusso di iscrizione opzionale, che è collegato in fondo alla pagina."
@ -3871,14 +3812,6 @@ msgstr ""
"Gli eventi saranno cancellati dopo questa durata. (Formato: "
"weeks=3;days=2;hours=3,seconds=2)."
#: authentik/tenants/models.py
msgid "Reputation cannot decrease lower than this value. Zero or negative."
msgstr ""
#: authentik/tenants/models.py
msgid "Reputation cannot increase higher than this value. Zero or positive."
msgstr ""
#: authentik/tenants/models.py
msgid "The option configures the footer links on the flow executor pages."
msgstr ""

View File

@ -12,7 +12,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
"POT-Creation-Date: 2025-03-31 00:10+0000\n"
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
"Last-Translator: NavyStack, 2023\n"
"Language-Team: Korean (https://app.transifex.com/authentik/teams/119923/ko/)\n"
@ -176,7 +176,6 @@ msgid "User's display name."
msgstr "사용자의 표시 이름"
#: authentik/core/models.py authentik/providers/oauth2/models.py
#: authentik/rbac/models.py
msgid "User"
msgstr "사용자"
@ -345,18 +344,6 @@ msgstr "속성 매핑"
msgid "Property Mappings"
msgstr "속성 매핑"
#: authentik/core/models.py
msgid "session data"
msgstr ""
#: authentik/core/models.py
msgid "Session"
msgstr ""
#: authentik/core/models.py
msgid "Sessions"
msgstr ""
#: authentik/core/models.py
msgid "Authenticated Session"
msgstr "인증된 세션"
@ -460,36 +447,6 @@ msgstr "라이선스 사용"
msgid "License Usage Records"
msgstr "라이선스 사용 기록"
#: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py
msgid "Field key to check, field keys defined in Prompt stages are available."
msgstr "확인하려는 필드 키, 프롬프트 스테이지에서 정의된 필드 키를 사용할 수 있습니다."
#: authentik/enterprise/policies/unique_password/models.py
msgid "Number of passwords to check against."
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py
msgid "Password not set in context"
msgstr "비밀번호가 컨텍스트에 설정되지 않음"
#: authentik/enterprise/policies/unique_password/models.py
msgid "This password has been used previously. Please choose a different one."
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policy"
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policies"
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "User Password History"
msgstr ""
#: authentik/enterprise/policy.py
msgid "Enterprise required to access this feature."
msgstr ""
@ -1225,6 +1182,10 @@ msgstr "정책의 캐시 메트릭 보기"
msgid "Clear Policy's cache metrics"
msgstr "정책의 캐시 메트릭 삭제"
#: authentik/policies/password/models.py
msgid "Field key to check, field keys defined in Prompt stages are available."
msgstr "확인하려는 필드 키, 프롬프트 스테이지에서 정의된 필드 키를 사용할 수 있습니다."
#: authentik/policies/password/models.py
msgid "How many times the password hash is allowed to be on haveibeenpwned"
msgstr "비밀번호 해시가 허용되는 해시 횟수"
@ -1234,6 +1195,10 @@ msgid ""
"If the zxcvbn score is equal or less than this value, the policy will fail."
msgstr "만약 zxcvbn 점수가 이 값과 같거나 이 값보다 작다면, 정책이 실패합니다."
#: authentik/policies/password/models.py
msgid "Password not set in context"
msgstr "비밀번호가 컨텍스트에 설정되지 않음"
#: authentik/policies/password/models.py
msgid "Invalid password."
msgstr ""
@ -1275,6 +1240,20 @@ msgstr "평판 점수"
msgid "Reputation Scores"
msgstr "평판 점수"
#: authentik/policies/templates/policies/buffer.html
msgid "Waiting for authentication..."
msgstr ""
#: authentik/policies/templates/policies/buffer.html
msgid ""
"You're already authenticating in another tab. This page will refresh once "
"authentication is completed."
msgstr ""
#: authentik/policies/templates/policies/buffer.html
msgid "Authenticate in this tab"
msgstr ""
#: authentik/policies/templates/policies/denied.html
msgid "Permission denied"
msgstr "권한 거부됨"
@ -2034,10 +2013,6 @@ msgstr "역할"
msgid "Roles"
msgstr "역할"
#: authentik/rbac/models.py
msgid "Initial Permissions"
msgstr ""
#: authentik/rbac/models.py
msgid "System permission"
msgstr "시스템 권한"
@ -2256,13 +2231,6 @@ msgid ""
"enabled on a single LDAP source."
msgstr "사용자가 비밀번호를 변경하면 LDAP로 다시 동기화합니다. 이 기능은 단일의 LDAP 소스에서만 활성화할 수 있습니다."
#: authentik/sources/ldap/models.py
msgid ""
"Lookup group membership based on a user attribute instead of a group "
"attribute. This allows nested group resolution on systems like FreeIPA and "
"Active Directory"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "LDAP Source"
msgstr "LDAP 소스"
@ -2279,22 +2247,6 @@ msgstr ""
msgid "LDAP Source Property Mappings"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "User LDAP Source Connection"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "User LDAP Source Connections"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "Group LDAP Source Connection"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "Group LDAP Source Connections"
msgstr ""
#: authentik/sources/ldap/signals.py
msgid "Password does not match Active Directory Complexity."
msgstr "비밀번호가 Active Directory 복잡도와 일치하지 않습니다."
@ -2303,14 +2255,6 @@ msgstr "비밀번호가 Active Directory 복잡도와 일치하지 않습니다.
msgid "No token received."
msgstr "수신된 토큰이 없습니다."
#: authentik/sources/oauth/models.py
msgid "HTTP Basic Authentication"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "Include the client ID and secret as request parameters"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "Request Token URL"
msgstr "토큰 요청 URL"
@ -2349,12 +2293,6 @@ msgstr "사용자 정보를 가져오기 위해 authentik에서 사용하는 URL
msgid "Additional Scopes"
msgstr "추가 스코프"
#: authentik/sources/oauth/models.py
msgid ""
"How to perform authentication during an authorization_code token request "
"flow"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "OAuth Source"
msgstr "OAuth 소스"
@ -3211,12 +3149,6 @@ msgid ""
"info is entered."
msgstr "활성화되면 잘못된 사용자 정보가 입력되더라도 단계가 성공하고 계속됩니다."
#: authentik/stages/identification/models.py
msgid ""
"Show the user the 'Remember me on this device' toggle, allowing repeat users"
" to skip straight to entering their password."
msgstr ""
#: authentik/stages/identification/models.py
msgid "Optional enrollment flow, which is linked at the bottom of the page."
msgstr "페이지 하단에 링크된, 선택적 등록 플로우를 참조하세요."
@ -3568,14 +3500,6 @@ msgid ""
"weeks=3;days=2;hours=3,seconds=2)."
msgstr "이 기간이 지나면 이벤트가 삭제됩니다. (서식: hours=-1;minutes=-2;seconds=-3)"
#: authentik/tenants/models.py
msgid "Reputation cannot decrease lower than this value. Zero or negative."
msgstr ""
#: authentik/tenants/models.py
msgid "Reputation cannot increase higher than this value. Zero or positive."
msgstr ""
#: authentik/tenants/models.py
msgid "The option configures the footer links on the flow executor pages."
msgstr ""

View File

@ -7,18 +7,18 @@
# Bartosz Karpiński, 2023
# Michał Jastrzębski, 2024
# Tomci 12 <drizztes@gmail.com>, 2024
# Darek “NeroPcStation” NeroPcStation <dareknowacki2001@gmail.com>, 2024
# Marc Schmitt, 2025
# Jens L. <jens@goauthentik.io>, 2025
# Darek “NeroPcStation” NeroPcStation <dareknowacki2001@gmail.com>, 2025
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
"POT-Creation-Date: 2025-04-11 00:10+0000\n"
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
"Last-Translator: Jens L. <jens@goauthentik.io>, 2025\n"
"Last-Translator: Darek “NeroPcStation” NeroPcStation <dareknowacki2001@gmail.com>, 2025\n"
"Language-Team: Polish (https://app.transifex.com/authentik/teams/119923/pl/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@ -189,7 +189,6 @@ msgid "User's display name."
msgstr "Wyświetlana nazwa użytkownika."
#: authentik/core/models.py authentik/providers/oauth2/models.py
#: authentik/rbac/models.py
msgid "User"
msgstr "Użytkownik"
@ -372,18 +371,6 @@ msgstr "Mapowanie właściwości"
msgid "Property Mappings"
msgstr "Mapowanie właściwości"
#: authentik/core/models.py
msgid "session data"
msgstr ""
#: authentik/core/models.py
msgid "Session"
msgstr "Sesja"
#: authentik/core/models.py
msgid "Sessions"
msgstr "Sesje"
#: authentik/core/models.py
msgid "Authenticated Session"
msgstr "Sesja uwierzytelniona"
@ -492,38 +479,6 @@ msgstr "Wykorzystanie licencji"
msgid "License Usage Records"
msgstr "Rejestr wykorzystania licencji"
#: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py
msgid "Field key to check, field keys defined in Prompt stages are available."
msgstr ""
"Klucz pola do sprawdzenia, dostępne są klucze pola zdefiniowane w etapach "
"monitu."
#: authentik/enterprise/policies/unique_password/models.py
msgid "Number of passwords to check against."
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py
msgid "Password not set in context"
msgstr "Hasło nie jest ustawione w kontekście"
#: authentik/enterprise/policies/unique_password/models.py
msgid "This password has been used previously. Please choose a different one."
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policy"
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policies"
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "User Password History"
msgstr ""
#: authentik/enterprise/policy.py
msgid "Enterprise required to access this feature."
msgstr "Wymagane jest konto Enterprise, aby uzyskać dostęp do tej funkcji."
@ -1302,6 +1257,12 @@ msgstr "Wyświetl metryki pamięci podręcznej Zasady"
msgid "Clear Policy's cache metrics"
msgstr "Wyczyść metryki pamięci podręcznej Zasady"
#: authentik/policies/password/models.py
msgid "Field key to check, field keys defined in Prompt stages are available."
msgstr ""
"Klucz pola do sprawdzenia, dostępne są klucze pola zdefiniowane w etapach "
"monitu."
#: authentik/policies/password/models.py
msgid "How many times the password hash is allowed to be on haveibeenpwned"
msgstr "Ile razy skrót hasła może być na haveibeenpwned"
@ -1313,6 +1274,10 @@ msgstr ""
"Jeśli wynik zxcvbn jest równy lub mniejszy od tej wartości, zasada zakończy "
"się niepowodzeniem."
#: authentik/policies/password/models.py
msgid "Password not set in context"
msgstr "Hasło nie jest ustawione w kontekście"
#: authentik/policies/password/models.py
msgid "Invalid password."
msgstr ""
@ -1354,6 +1319,20 @@ msgstr "Punkty reputacji"
msgid "Reputation Scores"
msgstr "Punkty reputacji"
#: authentik/policies/templates/policies/buffer.html
msgid "Waiting for authentication..."
msgstr "Oczekiwanie na uwierzytelnienie..."
#: authentik/policies/templates/policies/buffer.html
msgid ""
"You're already authenticating in another tab. This page will refresh once "
"authentication is completed."
msgstr ""
#: authentik/policies/templates/policies/buffer.html
msgid "Authenticate in this tab"
msgstr ""
#: authentik/policies/templates/policies/denied.html
msgid "Permission denied"
msgstr "Odmowa uprawnień"
@ -2162,10 +2141,6 @@ msgstr "Rola"
msgid "Roles"
msgstr "Role"
#: authentik/rbac/models.py
msgid "Initial Permissions"
msgstr ""
#: authentik/rbac/models.py
msgid "System permission"
msgstr "Uprawnienie systemowe"
@ -2415,22 +2390,6 @@ msgstr ""
msgid "LDAP Source Property Mappings"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "User LDAP Source Connection"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "User LDAP Source Connections"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "Group LDAP Source Connection"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "Group LDAP Source Connections"
msgstr ""
#: authentik/sources/ldap/signals.py
msgid "Password does not match Active Directory Complexity."
msgstr "Hasło nie pasuje do złożoności usługi Active Directory."
@ -2439,14 +2398,6 @@ msgstr "Hasło nie pasuje do złożoności usługi Active Directory."
msgid "No token received."
msgstr "Nie otrzymano tokena."
#: authentik/sources/oauth/models.py
msgid "HTTP Basic Authentication"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "Include the client ID and secret as request parameters"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "Request Token URL"
msgstr "URL żądania tokena"
@ -2489,12 +2440,6 @@ msgstr "URL używany przez authentik do uzyskania informacji o użytkowniku."
msgid "Additional Scopes"
msgstr "Dodatkowe zakresy"
#: authentik/sources/oauth/models.py
msgid ""
"How to perform authentication during an authorization_code token request "
"flow"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "OAuth Source"
msgstr "Źródło OAuth"
@ -3399,12 +3344,6 @@ msgstr ""
"Po włączeniu tej opcji etap zakończy się powodzeniem i będzie kontynuowany "
"nawet po wprowadzeniu nieprawidłowych danych użytkownika."
#: authentik/stages/identification/models.py
msgid ""
"Show the user the 'Remember me on this device' toggle, allowing repeat users"
" to skip straight to entering their password."
msgstr ""
#: authentik/stages/identification/models.py
msgid "Optional enrollment flow, which is linked at the bottom of the page."
msgstr ""
@ -3788,14 +3727,6 @@ msgstr ""
"Zdarzenia zostaną usunięte po upływie tego czasu. (Format: "
"weeks=3;days=2;hours=3,seconds=2)."
#: authentik/tenants/models.py
msgid "Reputation cannot decrease lower than this value. Zero or negative."
msgstr ""
#: authentik/tenants/models.py
msgid "Reputation cannot increase higher than this value. Zero or positive."
msgstr ""
#: authentik/tenants/models.py
msgid "The option configures the footer links on the flow executor pages."
msgstr "Opcja ta konfiguruje łącza stopki na stronach wykonawców przepływu."

View File

@ -18,7 +18,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
"POT-Creation-Date: 2025-04-11 00:10+0000\n"
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
"Last-Translator: Gil Poiares-Oliveira, 2025\n"
"Language-Team: Portuguese (Brazil) (https://app.transifex.com/authentik/teams/119923/pt_BR/)\n"
@ -192,7 +192,6 @@ msgid "User's display name."
msgstr "Nome de exibição do usuário."
#: authentik/core/models.py authentik/providers/oauth2/models.py
#: authentik/rbac/models.py
msgid "User"
msgstr "Usuário"
@ -377,18 +376,6 @@ msgstr "Mapeamento de propriedades"
msgid "Property Mappings"
msgstr "Mapeamentos de propriedades"
#: authentik/core/models.py
msgid "session data"
msgstr ""
#: authentik/core/models.py
msgid "Session"
msgstr ""
#: authentik/core/models.py
msgid "Sessions"
msgstr ""
#: authentik/core/models.py
msgid "Authenticated Session"
msgstr "Sessão Autenticada"
@ -496,38 +483,6 @@ msgstr "Uso de licença"
msgid "License Usage Records"
msgstr "Registros de uso de licença"
#: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py
msgid "Field key to check, field keys defined in Prompt stages are available."
msgstr ""
"Chave de campo para verificar, as chaves de campo definidas nos estágios de "
"prompt estão disponíveis."
#: authentik/enterprise/policies/unique_password/models.py
msgid "Number of passwords to check against."
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py
msgid "Password not set in context"
msgstr "Senha não definida no contexto"
#: authentik/enterprise/policies/unique_password/models.py
msgid "This password has been used previously. Please choose a different one."
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policy"
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policies"
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "User Password History"
msgstr ""
#: authentik/enterprise/policy.py
msgid "Enterprise required to access this feature."
msgstr "Entrerprise é necessário para acessar essa funcionalidade"
@ -1297,6 +1252,12 @@ msgstr ""
msgid "Clear Policy's cache metrics"
msgstr ""
#: authentik/policies/password/models.py
msgid "Field key to check, field keys defined in Prompt stages are available."
msgstr ""
"Chave de campo para verificar, as chaves de campo definidas nos estágios de "
"prompt estão disponíveis."
#: authentik/policies/password/models.py
msgid "How many times the password hash is allowed to be on haveibeenpwned"
msgstr "Quantas vezes o hash da senha pode estar em haveibeenpwned"
@ -1307,6 +1268,10 @@ msgid ""
msgstr ""
"Se a pontuação zxcvbn for igual ou menor que esse valor, a política falhará."
#: authentik/policies/password/models.py
msgid "Password not set in context"
msgstr "Senha não definida no contexto"
#: authentik/policies/password/models.py
msgid "Invalid password."
msgstr ""
@ -1348,6 +1313,20 @@ msgstr "Pontuação de reputação"
msgid "Reputation Scores"
msgstr "Pontuações de reputação"
#: authentik/policies/templates/policies/buffer.html
msgid "Waiting for authentication..."
msgstr ""
#: authentik/policies/templates/policies/buffer.html
msgid ""
"You're already authenticating in another tab. This page will refresh once "
"authentication is completed."
msgstr ""
#: authentik/policies/templates/policies/buffer.html
msgid "Authenticate in this tab"
msgstr ""
#: authentik/policies/templates/policies/denied.html
msgid "Permission denied"
msgstr "Permissão negada"
@ -2162,10 +2141,6 @@ msgstr ""
msgid "Roles"
msgstr ""
#: authentik/rbac/models.py
msgid "Initial Permissions"
msgstr ""
#: authentik/rbac/models.py
msgid "System permission"
msgstr "Permissão do sistema"
@ -2412,22 +2387,6 @@ msgstr ""
msgid "LDAP Source Property Mappings"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "User LDAP Source Connection"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "User LDAP Source Connections"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "Group LDAP Source Connection"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "Group LDAP Source Connections"
msgstr ""
#: authentik/sources/ldap/signals.py
msgid "Password does not match Active Directory Complexity."
msgstr "A senha não corresponde à complexidade do Active Directory."
@ -2436,14 +2395,6 @@ msgstr "A senha não corresponde à complexidade do Active Directory."
msgid "No token received."
msgstr "Nenhum token recebido."
#: authentik/sources/oauth/models.py
msgid "HTTP Basic Authentication"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "Include the client ID and secret as request parameters"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "Request Token URL"
msgstr "URL do token de solicitação"
@ -2484,12 +2435,6 @@ msgstr "URL usado pelo authentik para obter informações do usuário."
msgid "Additional Scopes"
msgstr "Escopos Adicionais"
#: authentik/sources/oauth/models.py
msgid ""
"How to perform authentication during an authorization_code token request "
"flow"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "OAuth Source"
msgstr "Fonte OAuth"
@ -3373,12 +3318,6 @@ msgid ""
"info is entered."
msgstr ""
#: authentik/stages/identification/models.py
msgid ""
"Show the user the 'Remember me on this device' toggle, allowing repeat users"
" to skip straight to entering their password."
msgstr ""
#: authentik/stages/identification/models.py
msgid "Optional enrollment flow, which is linked at the bottom of the page."
msgstr "Optional enrollment flow, which is linked at the bottom of the page."
@ -3739,14 +3678,6 @@ msgstr ""
"Os eventos serão excluídos após esta duração.(Formato: "
"semanas=3;dias=2;horas=3,segundos=2)."
#: authentik/tenants/models.py
msgid "Reputation cannot decrease lower than this value. Zero or negative."
msgstr ""
#: authentik/tenants/models.py
msgid "Reputation cannot increase higher than this value. Zero or positive."
msgstr ""
#: authentik/tenants/models.py
msgid "The option configures the footer links on the flow executor pages."
msgstr ""

View File

@ -18,7 +18,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
"POT-Creation-Date: 2025-04-11 00:10+0000\n"
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
"Last-Translator: Marc Schmitt, 2025\n"
"Language-Team: Russian (https://app.transifex.com/authentik/teams/119923/ru/)\n"
@ -191,7 +191,6 @@ msgid "User's display name."
msgstr "Отображаемое имя пользователя."
#: authentik/core/models.py authentik/providers/oauth2/models.py
#: authentik/rbac/models.py
msgid "User"
msgstr "Пользователь"
@ -380,18 +379,6 @@ msgstr "Сопоставление свойств"
msgid "Property Mappings"
msgstr "Сопоставление свойств"
#: authentik/core/models.py
msgid "session data"
msgstr ""
#: authentik/core/models.py
msgid "Session"
msgstr ""
#: authentik/core/models.py
msgid "Sessions"
msgstr ""
#: authentik/core/models.py
msgid "Authenticated Session"
msgstr "Аутентифицированная Сессия"
@ -500,37 +487,6 @@ msgstr "Использование лицензии"
msgid "License Usage Records"
msgstr "Записи использования лицензии"
#: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py
msgid "Field key to check, field keys defined in Prompt stages are available."
msgstr ""
"Ключ поля для проверки, доступны ключи поля, определенные в этапах запроса."
#: authentik/enterprise/policies/unique_password/models.py
msgid "Number of passwords to check against."
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py
msgid "Password not set in context"
msgstr "Пароль не задан в контексте"
#: authentik/enterprise/policies/unique_password/models.py
msgid "This password has been used previously. Please choose a different one."
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policy"
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policies"
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "User Password History"
msgstr ""
#: authentik/enterprise/policy.py
msgid "Enterprise required to access this feature."
msgstr "Для доступа к этой функции требуется Enterprise."
@ -1311,6 +1267,11 @@ msgstr "Просмотр показателей кэша политики"
msgid "Clear Policy's cache metrics"
msgstr "Очистка показателей кэша политики"
#: authentik/policies/password/models.py
msgid "Field key to check, field keys defined in Prompt stages are available."
msgstr ""
"Ключ поля для проверки, доступны ключи поля, определенные в этапах запроса."
#: authentik/policies/password/models.py
msgid "How many times the password hash is allowed to be on haveibeenpwned"
msgstr "Как часто хэш пароля может быть представлен на haveibeenpwned"
@ -1322,6 +1283,10 @@ msgstr ""
"Если показатель zxcvbn равен или меньше этого значения, политика будет "
"провалена."
#: authentik/policies/password/models.py
msgid "Password not set in context"
msgstr "Пароль не задан в контексте"
#: authentik/policies/password/models.py
msgid "Invalid password."
msgstr "Неправильный пароль"
@ -1363,6 +1328,20 @@ msgstr "Оценка репутации"
msgid "Reputation Scores"
msgstr "Оценка репутации"
#: authentik/policies/templates/policies/buffer.html
msgid "Waiting for authentication..."
msgstr ""
#: authentik/policies/templates/policies/buffer.html
msgid ""
"You're already authenticating in another tab. This page will refresh once "
"authentication is completed."
msgstr ""
#: authentik/policies/templates/policies/buffer.html
msgid "Authenticate in this tab"
msgstr ""
#: authentik/policies/templates/policies/denied.html
msgid "Permission denied"
msgstr "Доступ запрещен"
@ -2185,10 +2164,6 @@ msgstr "Роль"
msgid "Roles"
msgstr "Роли"
#: authentik/rbac/models.py
msgid "Initial Permissions"
msgstr ""
#: authentik/rbac/models.py
msgid "System permission"
msgstr "Системное разрешение"
@ -2446,22 +2421,6 @@ msgstr "Сопоставление свойства LDAP источника"
msgid "LDAP Source Property Mappings"
msgstr "Сопоставление свойств LDAP источника"
#: authentik/sources/ldap/models.py
msgid "User LDAP Source Connection"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "User LDAP Source Connections"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "Group LDAP Source Connection"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "Group LDAP Source Connections"
msgstr ""
#: authentik/sources/ldap/signals.py
msgid "Password does not match Active Directory Complexity."
msgstr "Пароль не соответствует сложности Active Directory."
@ -2470,14 +2429,6 @@ msgstr "Пароль не соответствует сложности Active D
msgid "No token received."
msgstr "Токен не был получен."
#: authentik/sources/oauth/models.py
msgid "HTTP Basic Authentication"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "Include the client ID and secret as request parameters"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "Request Token URL"
msgstr "URL-адрес запроса токена"
@ -2520,12 +2471,6 @@ msgstr ""
msgid "Additional Scopes"
msgstr "Дополнительные области"
#: authentik/sources/oauth/models.py
msgid ""
"How to perform authentication during an authorization_code token request "
"flow"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "OAuth Source"
msgstr "Источник OAuth"
@ -3431,12 +3376,6 @@ msgstr ""
"При включении этап будет завершаться успешно и продолжаться даже в случае "
"ввода неправильной информации о пользователе."
#: authentik/stages/identification/models.py
msgid ""
"Show the user the 'Remember me on this device' toggle, allowing repeat users"
" to skip straight to entering their password."
msgstr ""
#: authentik/stages/identification/models.py
msgid "Optional enrollment flow, which is linked at the bottom of the page."
msgstr ""
@ -3828,14 +3767,6 @@ msgstr ""
"По истечении этого времени события будут удалены. (Формат: недели=3; дни=2; "
"часы=3, секунды=2)."
#: authentik/tenants/models.py
msgid "Reputation cannot decrease lower than this value. Zero or negative."
msgstr ""
#: authentik/tenants/models.py
msgid "Reputation cannot increase higher than this value. Zero or positive."
msgstr ""
#: authentik/tenants/models.py
msgid "The option configures the footer links on the flow executor pages."
msgstr ""

Binary file not shown.

View File

@ -13,7 +13,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
"POT-Creation-Date: 2025-03-31 00:10+0000\n"
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
"Last-Translator: Jens L. <jens@goauthentik.io>, 2025\n"
"Language-Team: Turkish (https://app.transifex.com/authentik/teams/119923/tr/)\n"
@ -187,7 +187,6 @@ msgid "User's display name."
msgstr "Kullanıcının görünen adı."
#: authentik/core/models.py authentik/providers/oauth2/models.py
#: authentik/rbac/models.py
msgid "User"
msgstr "Kullanıcı"
@ -373,18 +372,6 @@ msgstr "Özellik Eşleme"
msgid "Property Mappings"
msgstr "Özellik Eşlemeleri"
#: authentik/core/models.py
msgid "session data"
msgstr ""
#: authentik/core/models.py
msgid "Session"
msgstr "Oturum"
#: authentik/core/models.py
msgid "Sessions"
msgstr "Oturumlar"
#: authentik/core/models.py
msgid "Authenticated Session"
msgstr "Kimliği Doğrulanmış Oturum"
@ -492,38 +479,6 @@ msgstr "Lisans Kullanımı"
msgid "License Usage Records"
msgstr "Lisans Kullanım Kayıtları"
#: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py
msgid "Field key to check, field keys defined in Prompt stages are available."
msgstr ""
"Alan tuşu kontrol etmek için, İstem aşamalarında tanımlanan alan tuşları "
"mevcuttur."
#: authentik/enterprise/policies/unique_password/models.py
msgid "Number of passwords to check against."
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py
msgid "Password not set in context"
msgstr "Parola bağlam içinde ayarlanmamış"
#: authentik/enterprise/policies/unique_password/models.py
msgid "This password has been used previously. Please choose a different one."
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policy"
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policies"
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "User Password History"
msgstr ""
#: authentik/enterprise/policy.py
msgid "Enterprise required to access this feature."
msgstr "Bu özelliğe erişmek için Kurumsal Paket gereklidir."
@ -1298,6 +1253,12 @@ msgstr "İlke'nin önbellek ölçümlerini görüntüleme"
msgid "Clear Policy's cache metrics"
msgstr "İlke'nin önbellek ölçümlerini temizleyin"
#: authentik/policies/password/models.py
msgid "Field key to check, field keys defined in Prompt stages are available."
msgstr ""
"Alan tuşu kontrol etmek için, İstem aşamalarında tanımlanan alan tuşları "
"mevcuttur."
#: authentik/policies/password/models.py
msgid "How many times the password hash is allowed to be on haveibeenpwned"
msgstr ""
@ -1310,6 +1271,10 @@ msgstr ""
"Eğer zxcvbn puanı bu değere eşit veya daha az ise, politika başarısız "
"olacaktır."
#: authentik/policies/password/models.py
msgid "Password not set in context"
msgstr "Parola bağlam içinde ayarlanmamış"
#: authentik/policies/password/models.py
msgid "Invalid password."
msgstr ""
@ -1351,6 +1316,20 @@ msgstr "İtibar Puanı"
msgid "Reputation Scores"
msgstr "İtibar Puanları"
#: authentik/policies/templates/policies/buffer.html
msgid "Waiting for authentication..."
msgstr ""
#: authentik/policies/templates/policies/buffer.html
msgid ""
"You're already authenticating in another tab. This page will refresh once "
"authentication is completed."
msgstr ""
#: authentik/policies/templates/policies/buffer.html
msgid "Authenticate in this tab"
msgstr ""
#: authentik/policies/templates/policies/denied.html
msgid "Permission denied"
msgstr "İzin reddedildi"
@ -2176,10 +2155,6 @@ msgstr "Rol"
msgid "Roles"
msgstr "Roller"
#: authentik/rbac/models.py
msgid "Initial Permissions"
msgstr ""
#: authentik/rbac/models.py
msgid "System permission"
msgstr "Sistem yetkisi"
@ -2423,13 +2398,6 @@ msgstr ""
"Bir kullanıcı parolasını değiştirdiğinde, parolayı LDAP ile geri eşitleyin. "
"Bu yalnızca tek bir LDAP kaynağında etkinleştirilebilir."
#: authentik/sources/ldap/models.py
msgid ""
"Lookup group membership based on a user attribute instead of a group "
"attribute. This allows nested group resolution on systems like FreeIPA and "
"Active Directory"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "LDAP Source"
msgstr "LDAP Kaynağı"
@ -2446,22 +2414,6 @@ msgstr "LDAP Kaynak Özellik Eşlemesi"
msgid "LDAP Source Property Mappings"
msgstr "LDAP Kaynak Özellik Eşlemeleri"
#: authentik/sources/ldap/models.py
msgid "User LDAP Source Connection"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "User LDAP Source Connections"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "Group LDAP Source Connection"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "Group LDAP Source Connections"
msgstr ""
#: authentik/sources/ldap/signals.py
msgid "Password does not match Active Directory Complexity."
msgstr "Parola Active Directory Karmaşıklığıyla eşleşmiyor."
@ -2470,14 +2422,6 @@ msgstr "Parola Active Directory Karmaşıklığıyla eşleşmiyor."
msgid "No token received."
msgstr "Jeton alınmadı."
#: authentik/sources/oauth/models.py
msgid "HTTP Basic Authentication"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "Include the client ID and secret as request parameters"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "Request Token URL"
msgstr "Jeton URL'si İste"
@ -2518,12 +2462,6 @@ msgstr "Kullanıcı bilgilerini almak için authentik tarafından kullanılan UR
msgid "Additional Scopes"
msgstr "Ek Kapsamlar"
#: authentik/sources/oauth/models.py
msgid ""
"How to perform authentication during an authorization_code token request "
"flow"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "OAuth Source"
msgstr "OAuth Kaynağı"
@ -3422,12 +3360,6 @@ msgstr ""
"Etkinleştirildiğinde, yanlış kullanıcı bilgisi girilse bile aşama başarılı "
"olur ve devam eder."
#: authentik/stages/identification/models.py
msgid ""
"Show the user the 'Remember me on this device' toggle, allowing repeat users"
" to skip straight to entering their password."
msgstr ""
#: authentik/stages/identification/models.py
msgid "Optional enrollment flow, which is linked at the bottom of the page."
msgstr "Sayfanın alt kısmında bağlanan isteğe bağlı kayıt akışı."
@ -3802,14 +3734,6 @@ msgstr ""
"Olaylar bu süreden sonra silinecektir (Format: "
"weeks=3;days=2;hours=3,seconds=2)."
#: authentik/tenants/models.py
msgid "Reputation cannot decrease lower than this value. Zero or negative."
msgstr ""
#: authentik/tenants/models.py
msgid "Reputation cannot increase higher than this value. Zero or positive."
msgstr ""
#: authentik/tenants/models.py
msgid "The option configures the footer links on the flow executor pages."
msgstr ""

Binary file not shown.

View File

@ -15,7 +15,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
"POT-Creation-Date: 2025-04-18 00:09+0000\n"
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
"Last-Translator: deluxghost, 2025\n"
"Language-Team: Chinese Simplified (https://app.transifex.com/authentik/teams/119923/zh-Hans/)\n"
@ -461,36 +461,6 @@ msgstr "许可证使用情况"
msgid "License Usage Records"
msgstr "许可证使用情况记录"
#: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py
msgid "Field key to check, field keys defined in Prompt stages are available."
msgstr "要检查的字段键,可以使用输入阶段中定义的字段键。"
#: authentik/enterprise/policies/unique_password/models.py
msgid "Number of passwords to check against."
msgstr "检查指定数量的密码。"
#: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py
msgid "Password not set in context"
msgstr "未在上下文中设置密码"
#: authentik/enterprise/policies/unique_password/models.py
msgid "This password has been used previously. Please choose a different one."
msgstr "此密码被使用过。请选择其他密码。"
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policy"
msgstr "密码唯一性策略"
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policies"
msgstr "密码唯一性策略"
#: authentik/enterprise/policies/unique_password/models.py
msgid "User Password History"
msgstr "用户密码历史记录"
#: authentik/enterprise/policy.py
msgid "Enterprise required to access this feature."
msgstr "访问此功能需要企业版。"
@ -1220,6 +1190,10 @@ msgstr "查看策略缓存指标"
msgid "Clear Policy's cache metrics"
msgstr "清除策略缓存指标"
#: authentik/policies/password/models.py
msgid "Field key to check, field keys defined in Prompt stages are available."
msgstr "要检查的字段键,可以使用输入阶段中定义的字段键。"
#: authentik/policies/password/models.py
msgid "How many times the password hash is allowed to be on haveibeenpwned"
msgstr "密码哈希允许出现在 HaveIBeenPwned 中多少次"
@ -1229,6 +1203,10 @@ msgid ""
"If the zxcvbn score is equal or less than this value, the policy will fail."
msgstr "如果 zxcvbn 分数小于等于此值,则策略失败。"
#: authentik/policies/password/models.py
msgid "Password not set in context"
msgstr "未在上下文中设置密码"
#: authentik/policies/password/models.py
msgid "Invalid password."
msgstr "无效密码。"
@ -1270,6 +1248,20 @@ msgstr "信誉分数"
msgid "Reputation Scores"
msgstr "信誉分数"
#: authentik/policies/templates/policies/buffer.html
msgid "Waiting for authentication..."
msgstr "正在等待身份验证…"
#: authentik/policies/templates/policies/buffer.html
msgid ""
"You're already authenticating in another tab. This page will refresh once "
"authentication is completed."
msgstr "您正在另一个标签页中验证身份。身份验证完成后,此页面会刷新。"
#: authentik/policies/templates/policies/buffer.html
msgid "Authenticate in this tab"
msgstr "在此标签页中验证身份"
#: authentik/policies/templates/policies/denied.html
msgid "Permission denied"
msgstr "权限被拒绝"

View File

@ -14,7 +14,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
"POT-Creation-Date: 2025-04-18 00:09+0000\n"
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
"Last-Translator: deluxghost, 2025\n"
"Language-Team: Chinese (China) (https://app.transifex.com/authentik/teams/119923/zh_CN/)\n"
@ -460,36 +460,6 @@ msgstr "许可证使用情况"
msgid "License Usage Records"
msgstr "许可证使用情况记录"
#: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py
msgid "Field key to check, field keys defined in Prompt stages are available."
msgstr "要检查的字段键,可以使用输入阶段中定义的字段键。"
#: authentik/enterprise/policies/unique_password/models.py
msgid "Number of passwords to check against."
msgstr "检查指定数量的密码。"
#: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py
msgid "Password not set in context"
msgstr "未在上下文中设置密码"
#: authentik/enterprise/policies/unique_password/models.py
msgid "This password has been used previously. Please choose a different one."
msgstr "此密码被使用过。请选择其他密码。"
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policy"
msgstr "密码唯一性策略"
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policies"
msgstr "密码唯一性策略"
#: authentik/enterprise/policies/unique_password/models.py
msgid "User Password History"
msgstr "用户密码历史记录"
#: authentik/enterprise/policy.py
msgid "Enterprise required to access this feature."
msgstr "访问此功能需要企业版。"
@ -1219,6 +1189,10 @@ msgstr "查看策略缓存指标"
msgid "Clear Policy's cache metrics"
msgstr "清除策略缓存指标"
#: authentik/policies/password/models.py
msgid "Field key to check, field keys defined in Prompt stages are available."
msgstr "要检查的字段键,可以使用输入阶段中定义的字段键。"
#: authentik/policies/password/models.py
msgid "How many times the password hash is allowed to be on haveibeenpwned"
msgstr "密码哈希允许出现在 HaveIBeenPwned 中多少次"
@ -1228,6 +1202,10 @@ msgid ""
"If the zxcvbn score is equal or less than this value, the policy will fail."
msgstr "如果 zxcvbn 分数小于等于此值,则策略失败。"
#: authentik/policies/password/models.py
msgid "Password not set in context"
msgstr "未在上下文中设置密码"
#: authentik/policies/password/models.py
msgid "Invalid password."
msgstr "无效密码。"
@ -1269,6 +1247,20 @@ msgstr "信誉分数"
msgid "Reputation Scores"
msgstr "信誉分数"
#: authentik/policies/templates/policies/buffer.html
msgid "Waiting for authentication..."
msgstr "正在等待身份验证…"
#: authentik/policies/templates/policies/buffer.html
msgid ""
"You're already authenticating in another tab. This page will refresh once "
"authentication is completed."
msgstr "您正在另一个标签页中验证身份。身份验证完成后,此页面会刷新。"
#: authentik/policies/templates/policies/buffer.html
msgid "Authenticate in this tab"
msgstr "在此标签页中验证身份"
#: authentik/policies/templates/policies/denied.html
msgid "Permission denied"
msgstr "权限被拒绝"

View File

@ -14,7 +14,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
"POT-Creation-Date: 2025-04-11 00:10+0000\n"
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
"Last-Translator: 刘松, 2025\n"
"Language-Team: Chinese (Taiwan) (https://app.transifex.com/authentik/teams/119923/zh_TW/)\n"
@ -178,7 +178,6 @@ msgid "User's display name."
msgstr "使用者的顯示名稱。"
#: authentik/core/models.py authentik/providers/oauth2/models.py
#: authentik/rbac/models.py
msgid "User"
msgstr "使用者"
@ -345,18 +344,6 @@ msgstr "屬性對應"
msgid "Property Mappings"
msgstr "屬性對應"
#: authentik/core/models.py
msgid "session data"
msgstr ""
#: authentik/core/models.py
msgid "Session"
msgstr "会话"
#: authentik/core/models.py
msgid "Sessions"
msgstr "会话"
#: authentik/core/models.py
msgid "Authenticated Session"
msgstr "已認證會談"
@ -460,36 +447,6 @@ msgstr "授權使用情況"
msgid "License Usage Records"
msgstr "授權使用紀錄"
#: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py
msgid "Field key to check, field keys defined in Prompt stages are available."
msgstr "要檢查的欄位鍵,在提示階段中有可用的已定義欄位鍵。"
#: authentik/enterprise/policies/unique_password/models.py
msgid "Number of passwords to check against."
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py
msgid "Password not set in context"
msgstr "未在上下文中設定密碼"
#: authentik/enterprise/policies/unique_password/models.py
msgid "This password has been used previously. Please choose a different one."
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policy"
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policies"
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "User Password History"
msgstr ""
#: authentik/enterprise/policy.py
msgid "Enterprise required to access this feature."
msgstr "企業版才能存取此功能。"
@ -1219,6 +1176,10 @@ msgstr "檢視原則的快取指標"
msgid "Clear Policy's cache metrics"
msgstr "清除原則的快取指標"
#: authentik/policies/password/models.py
msgid "Field key to check, field keys defined in Prompt stages are available."
msgstr "要檢查的欄位鍵,在提示階段中有可用的已定義欄位鍵。"
#: authentik/policies/password/models.py
msgid "How many times the password hash is allowed to be on haveibeenpwned"
msgstr "密碼雜湊在 haveibeenpwned 上允許出現的次數"
@ -1228,6 +1189,10 @@ msgid ""
"If the zxcvbn score is equal or less than this value, the policy will fail."
msgstr "如果 zxcvbn 分數等於或小於此值,則該政策將失敗。"
#: authentik/policies/password/models.py
msgid "Password not set in context"
msgstr "未在上下文中設定密碼"
#: authentik/policies/password/models.py
msgid "Invalid password."
msgstr ""
@ -1269,6 +1234,20 @@ msgstr "信譽分數"
msgid "Reputation Scores"
msgstr "信譽分數"
#: authentik/policies/templates/policies/buffer.html
msgid "Waiting for authentication..."
msgstr ""
#: authentik/policies/templates/policies/buffer.html
msgid ""
"You're already authenticating in another tab. This page will refresh once "
"authentication is completed."
msgstr ""
#: authentik/policies/templates/policies/buffer.html
msgid "Authenticate in this tab"
msgstr ""
#: authentik/policies/templates/policies/denied.html
msgid "Permission denied"
msgstr "權限不足。"
@ -2020,10 +1999,6 @@ msgstr "角色"
msgid "Roles"
msgstr "角色"
#: authentik/rbac/models.py
msgid "Initial Permissions"
msgstr ""
#: authentik/rbac/models.py
msgid "System permission"
msgstr "系統權限"
@ -2265,22 +2240,6 @@ msgstr ""
msgid "LDAP Source Property Mappings"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "User LDAP Source Connection"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "User LDAP Source Connections"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "Group LDAP Source Connection"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "Group LDAP Source Connections"
msgstr ""
#: authentik/sources/ldap/signals.py
msgid "Password does not match Active Directory Complexity."
msgstr "密碼不符合 Active Directory 的複雜性要求。"
@ -2289,14 +2248,6 @@ msgstr "密碼不符合 Active Directory 的複雜性要求。"
msgid "No token received."
msgstr "未收到權杖。"
#: authentik/sources/oauth/models.py
msgid "HTTP Basic Authentication"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "Include the client ID and secret as request parameters"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "Request Token URL"
msgstr "請求權杖的網址"
@ -2335,12 +2286,6 @@ msgstr "authentik 用來擷取使用者資訊的網址。"
msgid "Additional Scopes"
msgstr "附加範圍"
#: authentik/sources/oauth/models.py
msgid ""
"How to perform authentication during an authorization_code token request "
"flow"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "OAuth Source"
msgstr "OAuth 來源"
@ -3192,12 +3137,6 @@ msgid ""
"info is entered."
msgstr ""
#: authentik/stages/identification/models.py
msgid ""
"Show the user the 'Remember me on this device' toggle, allowing repeat users"
" to skip straight to entering their password."
msgstr ""
#: authentik/stages/identification/models.py
msgid "Optional enrollment flow, which is linked at the bottom of the page."
msgstr "可選的註冊流程,連結在頁面的底部。"
@ -3542,14 +3481,6 @@ msgid ""
"weeks=3;days=2;hours=3,seconds=2)."
msgstr "事件將在此期間後刪除。格式weeks=3;days=2;hours=3,seconds=2"
#: authentik/tenants/models.py
msgid "Reputation cannot decrease lower than this value. Zero or negative."
msgstr ""
#: authentik/tenants/models.py
msgid "Reputation cannot increase higher than this value. Zero or positive."
msgstr ""
#: authentik/tenants/models.py
msgid "The option configures the footer links on the flow executor pages."
msgstr ""

View File

@ -1,5 +1,5 @@
{
"name": "@goauthentik/authentik",
"version": "2025.4.3",
"version": "2025.2.4",
"private": true
}

View File

@ -18,7 +18,9 @@
}
.badge--support-community {
--ifm-badge-background-color: var(--ifm-color-secondary-contrast-foreground);
--ifm-badge-background-color: var(
--ifm-color-secondary-contrast-foreground
);
--ifm-badge-border-color: var(--ifm-color-secondary-dark);
--ifm-badge-color: var(--ifm-color-secondary-contrast-background);
}

View File

@ -1,12 +1,12 @@
:root {
--ifm-font-family-base:
RedHatVF, system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans,
sans-serif, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
RedHatVF, system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell,
Noto Sans, sans-serif, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial,
sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
--ifm-font-family-monospace:
RedHatMonoVF, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
monospace;
RedHatMonoVF, SFMono-Regular, Menlo, Monaco, Consolas,
"Liberation Mono", "Courier New", monospace;
--ifm-heading-font-family: RedHatDisplayVF, var(--ifm-font-family-base);

View File

@ -7,7 +7,11 @@
}
.homepage_hero__subtitle p {
font-size: clamp(1.125rem, 0.9946rem + 0.6522vi, 1.5rem); /* Adjust font as page scales */
font-size: clamp(
1.125rem,
0.9946rem + 0.6522vi,
1.5rem
); /* Adjust font as page scales */
max-width: 28ch; /* Apply a maximum to keep everything in the box */
text-wrap: balance; /* Prevent widows, orphans, and runts. Doesn't work in Safari */
}

View File

@ -1,5 +1,5 @@
:root {
--ifm-menu-link-padding-vertical: 0.5em;
--ifm-menu-link-padding-vertical: 1em;
}
.menu__list-item {

View File

@ -75,14 +75,17 @@
--ifm-navbar-item-padding-horizontal: 1rem;
}
.navbar {
.docs-wrapper .navbar {
margin: 0;
padding-inline-start: 0;
}
.navbar__brand {
justify-content: center;
width: var(--doc-sidebar-width, 300px);
}
.docs-wrapper .navbar__brand {
width: var(--doc-sidebar-width);
margin: 0;
}
@ -119,8 +122,12 @@
@media (min-width: 999px) {
border-inline-start: 1px solid var(--ifm-hover-overlay);
margin-inline-start: calc(var(--ifm-navbar-item-padding-horizontal) / 2);
padding-inline-start: calc(var(--ifm-navbar-item-padding-horizontal) / 2);
margin-inline-start: calc(
var(--ifm-navbar-item-padding-horizontal) / 2
);
padding-inline-start: calc(
var(--ifm-navbar-item-padding-horizontal) / 2
);
}
}
@ -144,14 +151,19 @@
hsl(236.84deg 34.55% 10.78%)
);
--docsearch-key-shadow:
inset 0 -2px 0 0 hsl(233.33deg 36% 24.51%), inset 0 0 1px 1px hsl(232.11deg 34.86% 57.25%),
inset 0 -2px 0 0 hsl(233.33deg 36% 24.51%),
inset 0 0 1px 1px hsl(232.11deg 34.86% 57.25%),
0 2px 2px 0 rgba(3, 4, 9, 0.3);
--docsearch-key-pressed-shadow:
inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px hsl(231.82deg 21.36% 40.39%),
inset 0 -2px 0 0 #282d55,
inset 0 0 1px 1px hsl(231.82deg 21.36% 40.39%),
0 1px 1px 0 hsl(230deg 50% 2.35% / 30.2%);
padding: var(--ifm-navbar-item-padding-vertical) var(--ifm-navbar-item-padding-horizontal) !important;
padding-inline-end: calc(var(--ifm-navbar-item-padding-horizontal) * 1.25) !important;
padding: var(--ifm-navbar-item-padding-vertical)
var(--ifm-navbar-item-padding-horizontal) !important;
padding-inline-end: calc(
var(--ifm-navbar-item-padding-horizontal) * 1.25
) !important;
.DocSearch-Button-Placeholder {
font-family: var(--ifm-heading-font-family);

View File

@ -13,3 +13,7 @@
--ifm-color-content: hsl(216 35% 3%);
}
body {
overscroll-behavior-x: none;
}

View File

@ -4,8 +4,8 @@
* @import { Config as DocusaurusConfig } from "@docusaurus/types"
* @import { UserThemeConfig } from "./theme.js"
*/
import { deepmerge } from "deepmerge-ts";
import { deepmerge } from "deepmerge-ts";
import { createThemeConfig } from "./theme.js";
//#region Types

View File

@ -4,6 +4,7 @@
* @import { UserThemeConfig as UserThemeConfigCommon } from "@docusaurus/theme-common";
* @import { UserThemeConfig as UserThemeConfigAlgolia } from "@docusaurus/theme-search-algolia";
*/
import { deepmerge } from "deepmerge-ts";
import { themes as prismThemes } from "prism-react-renderer";

View File

@ -1,12 +1,12 @@
{
"name": "@goauthentik/docusaurus-config",
"version": "1.0.5",
"version": "1.0.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@goauthentik/docusaurus-config",
"version": "1.0.5",
"version": "1.0.2",
"license": "MIT",
"dependencies": {
"deepmerge-ts": "^7.1.5",

View File

@ -1,6 +1,6 @@
{
"name": "@goauthentik/docusaurus-config",
"version": "1.0.5",
"version": "1.0.4",
"description": "authentik's Docusaurus config",
"license": "MIT",
"scripts": {

View File

@ -76,7 +76,6 @@ EXPOSE 9000 9300 9443
USER 1000
ENV TMPDIR=/dev/shm/ \
GOFIPS=1
ENV GOFIPS=1
ENTRYPOINT ["/proxy"]

View File

@ -1,6 +1,6 @@
[project]
name = "authentik"
version = "2025.4.3"
version = "2025.2.4"
description = ""
authors = [{ name = "authentik Team", email = "hello@goauthentik.io" }]
requires-python = "==3.12.*"

View File

@ -56,7 +56,6 @@ HEALTHCHECK --interval=5s --retries=20 --start-period=3s CMD [ "/rac", "healthch
USER 1000
ENV TMPDIR=/dev/shm/ \
GOFIPS=1
ENV GOFIPS=1
ENTRYPOINT ["/rac"]

View File

@ -56,7 +56,6 @@ EXPOSE 1812/udp 9300
USER 1000
ENV TMPDIR=/dev/shm/ \
GOFIPS=1
ENV GOFIPS=1
ENTRYPOINT ["/radius"]

View File

@ -1,7 +1,7 @@
openapi: 3.0.3
info:
title: authentik
version: 2025.4.3
version: 2025.2.4
description: Making authentication simple.
contact:
email: hello@goauthentik.io

14
uv.lock generated
View File

@ -165,7 +165,7 @@ wheels = [
[[package]]
name = "authentik"
version = "2025.4.3"
version = "2025.2.4"
source = { editable = "." }
dependencies = [
{ name = "argon2-cffi" },
@ -1436,11 +1436,11 @@ wheels = [
[[package]]
name = "h11"
version = "0.16.0"
version = "0.14.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250 }
sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 },
{ url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 },
]
[[package]]
@ -1467,15 +1467,15 @@ wheels = [
[[package]]
name = "httpcore"
version = "1.0.9"
version = "1.0.8"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
{ name = "h11" },
]
sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484 }
sdist = { url = "https://files.pythonhosted.org/packages/9f/45/ad3e1b4d448f22c0cff4f5692f5ed0666658578e358b8d58a19846048059/httpcore-1.0.8.tar.gz", hash = "sha256:86e94505ed24ea06514883fd44d2bc02d90e77e7979c8eb71b90f41d364a1bad", size = 85385 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 },
{ url = "https://files.pythonhosted.org/packages/18/8d/f052b1e336bb2c1fc7ed1aaed898aa570c0b61a09707b108979d9fc6e308/httpcore-1.0.8-py3-none-any.whl", hash = "sha256:5254cf149bcb5f75e9d1b2b9f729ea4a4b883d1ad7379fc632b727cec23674be", size = 78732 },
]
[[package]]

12196
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -12,8 +12,7 @@
"@floating-ui/dom": "^1.6.11",
"@formatjs/intl-listformat": "^7.5.7",
"@fortawesome/fontawesome-free": "^6.6.0",
"@goauthentik/api": "^2025.2.4-1745519715",
"@lit-labs/ssr": "3.2.2",
"@goauthentik/api": "^2025.2.4-1745325566",
"@lit/context": "^1.1.2",
"@lit/localize": "^0.12.2",
"@lit/reactive-element": "^2.0.4",
@ -54,6 +53,7 @@
"remark-gfm": "^4.0.1",
"remark-mdx-frontmatter": "^5.0.0",
"style-mod": "^4.1.2",
"trusted-types": "^2.0.0",
"ts-pattern": "^5.4.0",
"unist-util-visit": "^5.0.0",
"webcomponent-qr-code": "^1.2.0",

View File

@ -47,16 +47,7 @@ class SimpleFlowExecutor {
return `${ak().api.base}api/v3/flows/executor/${this.flowSlug}/?query=${encodeURIComponent(window.location.search.substring(1))}`;
}
loading() {
this.container.innerHTML = `<div class="d-flex justify-content-center">
<div class="spinner-border spinner-border-md" role="status">
<span class="sr-only">Loading...</span>
</div>
</div>`;
}
start() {
this.loading();
$.ajax({
type: "GET",
url: this.apiURL,

View File

@ -4,13 +4,17 @@ import { ROUTES } from "@goauthentik/admin/Routes";
import {
EVENT_API_DRAWER_TOGGLE,
EVENT_NOTIFICATION_DRAWER_TOGGLE,
EVENT_SIDEBAR_TOGGLE,
} from "@goauthentik/common/constants";
import { configureSentry } from "@goauthentik/common/sentry";
import { me } from "@goauthentik/common/users";
import { WebsocketClient } from "@goauthentik/common/ws";
import { AuthenticatedInterface } from "@goauthentik/elements/Interface";
import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider.js";
import "@goauthentik/elements/ak-locale-context";
import "@goauthentik/elements/banner/EnterpriseStatusBanner";
import "@goauthentik/elements/banner/EnterpriseStatusBanner";
import "@goauthentik/elements/banner/VersionBanner";
import "@goauthentik/elements/banner/VersionBanner";
import "@goauthentik/elements/messages/MessageContainer";
import "@goauthentik/elements/messages/MessageContainer";
@ -21,25 +25,32 @@ import "@goauthentik/elements/router/RouterOutlet";
import "@goauthentik/elements/sidebar/Sidebar";
import "@goauthentik/elements/sidebar/SidebarItem";
import { CSSResult, TemplateResult, css, html } from "lit";
import { CSSResult, TemplateResult, css, html, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators.js";
import { classMap } from "lit/directives/class-map.js";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFDrawer from "@patternfly/patternfly/components/Drawer/drawer.css";
import PFNav from "@patternfly/patternfly/components/Nav/nav.css";
import PFPage from "@patternfly/patternfly/components/Page/page.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
import { SessionUser, UiThemeEnum } from "@goauthentik/api";
import { LicenseSummaryStatusEnum, SessionUser, UiThemeEnum } from "@goauthentik/api";
import "./AdminSidebar";
import {
AdminSidebarEnterpriseEntries,
AdminSidebarEntries,
renderSidebarItems,
} from "./AdminSidebar.js";
if (process.env.NODE_ENV === "development") {
await import("@goauthentik/esbuild-plugin-live-reload/client");
}
@customElement("ak-interface-admin")
export class AdminInterface extends AuthenticatedInterface {
export class AdminInterface extends WithLicenseSummary(AuthenticatedInterface) {
//#region Properties
@property({ type: Boolean })
notificationDrawerOpen = getURLParam("notificationDrawerOpen", false);
@ -54,12 +65,24 @@ export class AdminInterface extends AuthenticatedInterface {
@query("ak-about-modal")
aboutModal?: AboutModal;
@property({ type: Boolean, reflect: true })
public sidebarOpen = true;
#toggleSidebar = () => {
this.sidebarOpen = !this.sidebarOpen;
};
//#endregion
//#region Styles
static get styles(): CSSResult[] {
return [
PFBase,
PFPage,
PFButton,
PFDrawer,
PFNav,
css`
.pf-c-page__main,
.pf-c-drawer__content,
@ -67,23 +90,30 @@ export class AdminInterface extends AuthenticatedInterface {
z-index: auto !important;
background-color: transparent;
}
.display-none {
display: none;
}
.pf-c-page {
background-color: var(--pf-c-page--BackgroundColor) !important;
}
/* Global page background colour */
:host([theme="dark"]) .pf-c-page {
--pf-c-page--BackgroundColor: var(--ak-dark-background);
:host([theme="dark"]) {
/* Global page background colour */
.pf-c-page {
--pf-c-page--BackgroundColor: var(--ak-dark-background);
}
}
ak-enterprise-status,
ak-version-banner {
ak-page-navbar {
grid-area: header;
}
ak-admin-sidebar {
.ak-sidebar {
grid-area: nav;
}
.pf-c-drawer__panel {
z-index: var(--pf-global--ZIndex--xl);
}
@ -91,9 +121,19 @@ export class AdminInterface extends AuthenticatedInterface {
];
}
//#endregion
//#region Lifecycle
constructor() {
super();
this.ws = new WebsocketClient();
}
public connectedCallback(): void {
super.connectedCallback();
window.addEventListener(EVENT_SIDEBAR_TOGGLE, this.#toggleSidebar);
window.addEventListener(EVENT_NOTIFICATION_DRAWER_TOGGLE, () => {
this.notificationDrawerOpen = !this.notificationDrawerOpen;
@ -110,6 +150,11 @@ export class AdminInterface extends AuthenticatedInterface {
});
}
public disconnectedCallback(): void {
super.disconnectedCallback();
window.removeEventListener(EVENT_SIDEBAR_TOGGLE, this.#toggleSidebar);
}
async firstUpdated(): Promise<void> {
configureSentry(true);
this.user = await me();
@ -118,6 +163,7 @@ export class AdminInterface extends AuthenticatedInterface {
this.user.user.isSuperuser ||
// TODO: somehow add `access_admin_interface` to the API schema
this.user.user.systemPermissions.includes("access_admin_interface");
if (!canAccessAdmin && this.user.user.pk > 0) {
window.location.assign("/if/user/");
}
@ -125,10 +171,14 @@ export class AdminInterface extends AuthenticatedInterface {
render(): TemplateResult {
const sidebarClasses = {
"pf-c-page__sidebar": true,
"pf-m-light": this.activeTheme === UiThemeEnum.Light,
"pf-m-expanded": this.sidebarOpen,
"pf-m-collapsed": !this.sidebarOpen,
};
const drawerOpen = this.notificationDrawerOpen || this.apiDrawerOpen;
const drawerClasses = {
"pf-m-expanded": drawerOpen,
"pf-m-collapsed": !drawerOpen,
@ -136,11 +186,18 @@ export class AdminInterface extends AuthenticatedInterface {
return html` <ak-locale-context>
<div class="pf-c-page">
<ak-enterprise-status interface="admin"></ak-enterprise-status>
<ak-version-banner></ak-version-banner>
<ak-admin-sidebar
class="pf-c-page__sidebar ${classMap(sidebarClasses)}"
></ak-admin-sidebar>
<ak-page-navbar>
<ak-version-banner></ak-version-banner>
<ak-enterprise-status interface="admin"></ak-enterprise-status>
</ak-page-navbar>
<ak-sidebar class="${classMap(sidebarClasses)}">
${renderSidebarItems(AdminSidebarEntries)}
${this.licenseSummary?.status !== LicenseSummaryStatusEnum.Unlicensed
? renderSidebarItems(AdminSidebarEnterpriseEntries)
: nothing}
</ak-sidebar>
<div class="pf-c-page__drawer">
<div class="pf-c-drawer ${classMap(drawerClasses)}">
<div class="pf-c-drawer__main">

View File

@ -1,186 +1,98 @@
import { EVENT_SIDEBAR_TOGGLE } from "@goauthentik/common/constants";
import { me } from "@goauthentik/common/users";
import { AKElement } from "@goauthentik/elements/Base";
import {
CapabilitiesEnum,
WithCapabilitiesConfig,
} from "@goauthentik/elements/Interface/capabilitiesProvider";
import { WithVersion } from "@goauthentik/elements/Interface/versionProvider";
import { ID_REGEX, SLUG_REGEX, UUID_REGEX } from "@goauthentik/elements/router/Route";
import { getRootStyle } from "@goauthentik/elements/utils/getRootStyle";
import { spread } from "@open-wc/lit-helpers";
import { msg } from "@lit/localize";
import { TemplateResult, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import { map } from "lit/directives/map.js";
import { repeat } from "lit/directives/repeat.js";
import { UiThemeEnum } from "@goauthentik/api";
import type { SessionUser, UserSelf } from "@goauthentik/api";
// The second attribute type is of string[] to help with the 'activeWhen' control, which was
// commonplace and singular enough to merit its own handler.
type SidebarEntry = [
path: string | null,
label: string,
attributes?: Record<string, any> | string[] | null, // eslint-disable-line
children?: SidebarEntry[],
];
@customElement("ak-admin-sidebar")
export class AkAdminSidebar extends WithCapabilitiesConfig(WithVersion(AKElement)) {
@property({ type: Boolean, reflect: true })
open = true;
/**
* Recursively renders a sidebar entry.
*/
export function renderSidebarItem([
path,
label,
attributes,
children,
]: SidebarEntry): TemplateResult {
const properties = Array.isArray(attributes)
? { ".activeWhen": attributes }
: (attributes ?? {});
@state()
impersonation: UserSelf["username"] | null = null;
constructor() {
super();
me().then((user: SessionUser) => {
this.impersonation = user.original ? user.user.username : null;
});
this.toggleOpen = this.toggleOpen.bind(this);
this.checkWidth = this.checkWidth.bind(this);
if (path) {
properties.path = path;
}
// This has to be a bound method so the event listener can be removed on disconnection as
// needed.
toggleOpen() {
this.open = !this.open;
}
checkWidth() {
// This works just fine, but it assumes that the `--ak-sidebar--minimum-auto-width` is in
// REMs. If that changes, this code will have to be adjusted as well.
const minWidth =
parseFloat(getRootStyle("--ak-sidebar--minimum-auto-width")) *
parseFloat(getRootStyle("font-size"));
this.open = window.innerWidth >= minWidth;
}
connectedCallback() {
super.connectedCallback();
window.addEventListener(EVENT_SIDEBAR_TOGGLE, this.toggleOpen);
window.addEventListener("resize", this.checkWidth);
// After connecting to the DOM, we can now perform this check to see if the sidebar should
// be open by default.
this.checkWidth();
}
// The symmetry (☟, ☝) here is critical in that you want to start adding these handlers after
// connection, and removing them before disconnection.
disconnectedCallback() {
window.removeEventListener(EVENT_SIDEBAR_TOGGLE, this.toggleOpen);
window.removeEventListener("resize", this.checkWidth);
super.disconnectedCallback();
}
render() {
return html`
<ak-sidebar
class="pf-c-page__sidebar ${this.open ? "pf-m-expanded" : "pf-m-collapsed"} ${this
.activeTheme === UiThemeEnum.Light
? "pf-m-light"
: ""}"
>
${this.renderSidebarItems()}
</ak-sidebar>
`;
}
updated() {
// This is permissible as`:host.classList` is not one of the properties Lit uses as a
// scheduling trigger. This sort of shenanigans can trigger an loop, in that it will trigger
// a browser reflow, which may trigger some other styling the application is monitoring,
// triggering a re-render which triggers a browser reflow, ad infinitum. But we've been
// living with that since jQuery, and it's both well-known and fortunately rare.
// eslint-disable-next-line wc/no-self-class
this.classList.remove("pf-m-expanded", "pf-m-collapsed");
// eslint-disable-next-line wc/no-self-class
this.classList.add(this.open ? "pf-m-expanded" : "pf-m-collapsed");
}
renderSidebarItems(): TemplateResult {
// The second attribute type is of string[] to help with the 'activeWhen' control, which was
// commonplace and singular enough to merit its own handler.
type SidebarEntry = [
path: string | null,
label: string,
attributes?: Record<string, any> | string[] | null, // eslint-disable-line
children?: SidebarEntry[],
];
// prettier-ignore
const sidebarContent: SidebarEntry[] = [
[null, msg("Dashboards"), { "?expanded": true }, [
["/administration/overview", msg("Overview")],
["/administration/dashboard/users", msg("User Statistics")],
["/administration/system-tasks", msg("System Tasks")]]],
[null, msg("Applications"), null, [
["/core/applications", msg("Applications"), [`^/core/applications/(?<slug>${SLUG_REGEX})$`]],
["/core/providers", msg("Providers"), [`^/core/providers/(?<id>${ID_REGEX})$`]],
["/outpost/outposts", msg("Outposts")]]],
[null, msg("Events"), null, [
["/events/log", msg("Logs"), [`^/events/log/(?<id>${UUID_REGEX})$`]],
["/events/rules", msg("Notification Rules")],
["/events/transports", msg("Notification Transports")]]],
[null, msg("Customization"), null, [
["/policy/policies", msg("Policies")],
["/core/property-mappings", msg("Property Mappings")],
["/blueprints/instances", msg("Blueprints")],
["/policy/reputation", msg("Reputation scores")]]],
[null, msg("Flows and Stages"), null, [
["/flow/flows", msg("Flows"), [`^/flow/flows/(?<slug>${SLUG_REGEX})$`]],
["/flow/stages", msg("Stages")],
["/flow/stages/prompts", msg("Prompts")]]],
[null, msg("Directory"), null, [
["/identity/users", msg("Users"), [`^/identity/users/(?<id>${ID_REGEX})$`]],
["/identity/groups", msg("Groups"), [`^/identity/groups/(?<id>${UUID_REGEX})$`]],
["/identity/roles", msg("Roles"), [`^/identity/roles/(?<id>${UUID_REGEX})$`]],
["/identity/initial-permissions", msg("Initial Permissions"), [`^/identity/initial-permissions/(?<id>${ID_REGEX})$`]],
["/core/sources", msg("Federation and Social login"), [`^/core/sources/(?<slug>${SLUG_REGEX})$`]],
["/core/tokens", msg("Tokens and App passwords")],
["/flow/stages/invitations", msg("Invitations")]]],
[null, msg("System"), null, [
["/core/brands", msg("Brands")],
["/crypto/certificates", msg("Certificates")],
["/outpost/integrations", msg("Outpost Integrations")],
["/admin/settings", msg("Settings")]]],
];
// Typescript requires the type here to correctly type the recursive path
type SidebarRenderer = (_: SidebarEntry) => TemplateResult;
const renderOneSidebarItem: SidebarRenderer = ([path, label, attributes, children]) => {
const properties = Array.isArray(attributes)
? { ".activeWhen": attributes }
: (attributes ?? {});
if (path) {
properties.path = path;
}
return html`<ak-sidebar-item ${spread(properties)}>
${label ? html`<span slot="label">${label}</span>` : nothing}
${map(children, renderOneSidebarItem)}
</ak-sidebar-item>`;
};
// prettier-ignore
return html`
${map(sidebarContent, renderOneSidebarItem)}
${this.renderEnterpriseMenu()}
`;
}
renderEnterpriseMenu() {
return this.can(CapabilitiesEnum.IsEnterprise)
? html`
<ak-sidebar-item>
<span slot="label">${msg("Enterprise")}</span>
<ak-sidebar-item path="/enterprise/licenses">
<span slot="label">${msg("Licenses")}</span>
</ak-sidebar-item>
</ak-sidebar-item>
`
: nothing;
}
return html`<ak-sidebar-item ${spread(properties)}>
${label ? html`<span slot="label">${label}</span>` : nothing}
${children ? renderSidebarItems(children) : nothing}
</ak-sidebar-item>`;
}
declare global {
interface HTMLElementTagNameMap {
"ak-admin-sidebar": AkAdminSidebar;
}
/**
* Recursively renders a collection of sidebar entries.
*/
export function renderSidebarItems(entries: readonly SidebarEntry[]) {
console.debug("authentik/sidebar: Rendering sidebar items", entries);
return repeat(entries, ([path, label]) => path || label, renderSidebarItem);
}
// prettier-ignore
export const AdminSidebarEntries: readonly SidebarEntry[] = [
[null, msg("Dashboards"), { "?expanded": true }, [
["/administration/overview", msg("Overview")],
["/administration/dashboard/users", msg("User Statistics")],
["/administration/system-tasks", msg("System Tasks")]]
],
[null, msg("Applications"), null, [
["/core/applications", msg("Applications"), [`^/core/applications/(?<slug>${SLUG_REGEX})$`]],
["/core/providers", msg("Providers"), [`^/core/providers/(?<id>${ID_REGEX})$`]],
["/outpost/outposts", msg("Outposts")]]
],
[null, msg("Events"), null, [
["/events/log", msg("Logs"), [`^/events/log/(?<id>${UUID_REGEX})$`]],
["/events/rules", msg("Notification Rules")],
["/events/transports", msg("Notification Transports")]]
],
[null, msg("Customization"), null, [
["/policy/policies", msg("Policies")],
["/core/property-mappings", msg("Property Mappings")],
["/blueprints/instances", msg("Blueprints")],
["/policy/reputation", msg("Reputation scores")]]
],
[null, msg("Flows and Stages"), null, [
["/flow/flows", msg("Flows"), [`^/flow/flows/(?<slug>${SLUG_REGEX})$`]],
["/flow/stages", msg("Stages")],
["/flow/stages/prompts", msg("Prompts")]]
],
[null, msg("Directory"), null, [
["/identity/users", msg("Users"), [`^/identity/users/(?<id>${ID_REGEX})$`]],
["/identity/groups", msg("Groups"), [`^/identity/groups/(?<id>${UUID_REGEX})$`]],
["/identity/roles", msg("Roles"), [`^/identity/roles/(?<id>${UUID_REGEX})$`]],
["/identity/initial-permissions", msg("Initial Permissions"), [`^/identity/initial-permissions/(?<id>${ID_REGEX})$`]],
["/core/sources", msg("Federation and Social login"), [`^/core/sources/(?<slug>${SLUG_REGEX})$`]],
["/core/tokens", msg("Tokens and App passwords")],
["/flow/stages/invitations", msg("Invitations")]]
],
[null, msg("System"), null, [
["/core/brands", msg("Brands")],
["/crypto/certificates", msg("Certificates")],
["/outpost/integrations", msg("Outpost Integrations")],
["/admin/settings", msg("Settings")]]
],
];
// prettier-ignore
export const AdminSidebarEnterpriseEntries: readonly SidebarEntry[] = [
[null, msg("Enterprise"), null, [
["/enterprise/licenses", msg("Licenses"), null]
],
]]

View File

@ -58,9 +58,6 @@ export class AdminOverviewPage extends AdminOverviewBase {
PFContent,
PFDivider,
css`
.pf-l-grid__item {
height: 100%;
}
.pf-l-grid__item.big-graph-container {
height: 35em;
}
@ -74,6 +71,10 @@ export class AdminOverviewPage extends AdminOverviewBase {
line-height: normal;
font-size: var(--pf-global--icon--FontSize--sm);
}
.chart-item {
aspect-ratio: 2 / 1;
}
`,
];
}
@ -94,22 +95,31 @@ export class AdminOverviewPage extends AdminOverviewBase {
}
render(): TemplateResult {
const name = this.user?.user.name ?? this.user?.user.username;
const username = this.user?.user.name || this.user?.user.username;
return html`<ak-page-header description=${msg("General system status")} ?hasIcon=${false}>
<span slot="header"> ${msg(str`Welcome, ${name || ""}.`)} </span>
return html` <ak-page-header
header=${msg(str`Welcome, ${username || ""}.`)}
description=${msg("General system status")}
?hasIcon=${false}
>
</ak-page-header>
<section class="pf-c-page__main-section">
<div class="pf-l-grid pf-m-gutter">
<!-- row 1 -->
<div
class="pf-l-grid__item pf-m-12-col pf-m-6-col-on-xl pf-m-6-col-on-2xl pf-l-grid pf-m-gutter"
>
<div class="pf-l-grid__item pf-m-12-col pf-m-6-col-on-xl pf-m-4-col-on-2xl">
<ak-quick-actions-card .actions=${this.quickActions}>
</ak-quick-actions-card>
</div>
<div class="pf-l-grid__item pf-m-12-col pf-m-6-col-on-xl pf-m-4-col-on-2xl">
<div class="pf-l-grid__item pf-m-12-col pf-m-2-row pf-m-9-col-on-xl">
<ak-recent-events pageSize="6"></ak-recent-events>
</div>
<div class="pf-l-grid__item pf-m-12-col pf-m-6-col-on-sm pf-m-3-col-on-xl">
<ak-quick-actions-card .actions=${this.quickActions}>
</ak-quick-actions-card>
</div>
<div class="pf-l-grid__item pf-m-12-col pf-m-6-col-on-sm pf-m-3-col-on-xl">
<ak-admin-status-version> </ak-admin-status-version>
</div>
<div class="pf-l-grid pf-l-grid__item pf-m-12-col pf-m-gutter">
${this.renderSecondaryRow()}
<div class="pf-l-grid__item pf-m-12-col pf-m-6-col-on-md chart-item">
<ak-aggregate-card
icon="pf-icon pf-icon-zone"
header=${msg("Outpost status")}
@ -118,24 +128,13 @@ export class AdminOverviewPage extends AdminOverviewBase {
<ak-admin-status-chart-outpost></ak-admin-status-chart-outpost>
</ak-aggregate-card>
</div>
<div
class="pf-l-grid__item pf-m-12-col pf-m-12-col-on-xl pf-m-4-col-on-2xl"
>
<div class="pf-l-grid__item pf-m-12-col pf-m-6-col-on-md chart-item">
<ak-aggregate-card icon="fa fa-sync-alt" header=${msg("Sync status")}>
<ak-admin-status-chart-sync></ak-admin-status-chart-sync>
</ak-aggregate-card>
</div>
<div class="pf-l-grid__item pf-m-12-col">
<hr class="pf-c-divider" />
</div>
${this.renderCards()}
</div>
<div class="pf-l-grid__item pf-m-12-col pf-m-6-col-on-xl">
<ak-recent-events pageSize="6"></ak-recent-events>
</div>
<div class="pf-l-grid__item pf-m-12-col">
<hr class="pf-c-divider" />
</div>
<!-- row 3 -->
<div
class="pf-l-grid__item pf-m-12-col pf-m-6-col-on-xl pf-m-8-col-on-2xl big-graph-container"
@ -163,32 +162,34 @@ export class AdminOverviewPage extends AdminOverviewBase {
</section>`;
}
renderCards() {
renderSecondaryRow() {
const isEnterprise = this.hasEnterpriseLicense;
const colSpan = isEnterprise ? 4 : 6;
const classes = {
"card-container": true,
"pf-l-grid__item": true,
"pf-m-6-col": true,
"pf-m-4-col-on-md": !isEnterprise,
"pf-m-4-col-on-xl": !isEnterprise,
"pf-m-3-col-on-md": isEnterprise,
"pf-m-3-col-on-xl": isEnterprise,
[`pf-m-12-col`]: true,
[`pf-m-${colSpan}-col-on-md`]: true,
};
return html`<div class=${classMap(classes)}>
return html`
<div class=${classMap(classes)}>
<ak-admin-status-system> </ak-admin-status-system>
</div>
<div class=${classMap(classes)}>
<ak-admin-status-version> </ak-admin-status-version>
</div>
<div class=${classMap(classes)}>
<ak-admin-status-card-workers> </ak-admin-status-card-workers>
</div>
${isEnterprise
? html` <div class=${classMap(classes)}>
<ak-admin-fips-status-system> </ak-admin-fips-status-system>
</div>`
: nothing} `;
? html`
<div class=${classMap(classes)}>
<ak-admin-fips-status-system> </ak-admin-fips-status-system>
</div>
`
: nothing}
`;
}
renderActions() {

View File

@ -83,13 +83,10 @@ export class AdminSettingsPage extends AKElement {
}
render() {
if (!this.settings) {
return nothing;
}
if (!this.settings) return nothing;
return html`
<ak-page-header icon="fa fa-cog" header="" description="">
<span slot="header"> ${msg("System settings")} </span>
</ak-page-header>
<ak-page-header icon="fa fa-cog" header="${msg("System settings")}"> </ak-page-header>
<section class="pf-c-page__main-section pf-m-no-padding-mobile pf-l-grid pf-m-gutter">
<div class="pf-c-card">
<div class="pf-c-card__body">

View File

@ -89,24 +89,19 @@ export class RoleObjectPermissionForm extends ModelForm<RoleAssignData, number>
>
</ak-search-select>
</ak-form-element-horizontal>
${this.modelPermissions?.results
.filter((perm) => {
const [_app, model] = this.model?.split(".") || "";
return perm.codename !== `add_${model}`;
})
.map((perm) => {
return html` <ak-form-element-horizontal name="permissions.${perm.codename}">
<label class="pf-c-switch">
<input class="pf-c-switch__input" type="checkbox" />
<span class="pf-c-switch__toggle">
<span class="pf-c-switch__toggle-icon">
<i class="fas fa-check" aria-hidden="true"></i>
</span>
${this.modelPermissions?.results.map((perm) => {
return html` <ak-form-element-horizontal name="permissions.${perm.codename}">
<label class="pf-c-switch">
<input class="pf-c-switch__input" type="checkbox" />
<span class="pf-c-switch__toggle">
<span class="pf-c-switch__toggle-icon">
<i class="fas fa-check" aria-hidden="true"></i>
</span>
<span class="pf-c-switch__label">${perm.name}</span>
</label>
</ak-form-element-horizontal>`;
})}
</span>
<span class="pf-c-switch__label">${perm.name}</span>
</label>
</ak-form-element-horizontal>`;
})}
</form>`;
}
}

View File

@ -45,7 +45,7 @@ export class RoleAssignedObjectPermissionTable extends Table<RoleAssignedObjectP
ordering: "codename",
});
modelPermissions.results = modelPermissions.results.filter((value) => {
return value.codename !== `add_${this.model?.split(".")[1]}`;
return !value.codename.startsWith("add_");
});
this.modelPermissions = modelPermissions;
return perms;

View File

@ -3,7 +3,7 @@ export const SUCCESS_CLASS = "pf-m-success";
export const ERROR_CLASS = "pf-m-danger";
export const PROGRESS_CLASS = "pf-m-in-progress";
export const CURRENT_CLASS = "pf-m-current";
export const VERSION = "2025.4.3";
export const VERSION = "2025.2.4";
export const TITLE_DEFAULT = "authentik";
export const ROUTE_SEPARATOR = ";";

View File

@ -1,26 +1,110 @@
import type { Config as DOMPurifyConfig } from "dompurify";
import DOMPurify from "dompurify";
import { trustedTypes } from "trusted-types";
import { render } from "@lit-labs/ssr";
import { collectResult } from "@lit-labs/ssr/lib/render-result.js";
import { TemplateResult, html } from "lit";
import { render } from "lit";
import { unsafeHTML } from "lit/directives/unsafe-html.js";
import { until } from "lit/directives/until.js";
/**
* Trusted types policy that escapes HTML content in place.
*
* @see {@linkcode SanitizedTrustPolicy} to strip HTML content.
*
* @returns {TrustedHTML} All HTML content, escaped.
*/
export const EscapeTrustPolicy = trustedTypes.createPolicy("authentik-escape", {
createHTML: (untrustedHTML: string) => {
return DOMPurify.sanitize(untrustedHTML, {
RETURN_TRUSTED_TYPE: false,
});
},
});
/**
* Trusted types policy, stripping all HTML content.
*
* @returns {TrustedHTML} Text content only, all HTML tags stripped.
*/
export const SanitizedTrustPolicy = trustedTypes.createPolicy("authentik-sanitize", {
createHTML: (untrustedHTML: string) => {
return DOMPurify.sanitize(untrustedHTML, {
RETURN_TRUSTED_TYPE: false,
ALLOWED_TAGS: ["#text"],
});
},
});
/**
* Trusted types policy, allowing a minimal set of _safe_ HTML tags supplied by
* a trusted source, such as the brand API.
*/
export const BrandedHTMLPolicy = trustedTypes.createPolicy("authentik-restrict", {
createHTML: (untrustedHTML: string) => {
return DOMPurify.sanitize(untrustedHTML, {
RETURN_TRUSTED_TYPE: false,
FORBID_TAGS: [
"script",
"style",
"iframe",
"link",
"object",
"embed",
"applet",
"meta",
"base",
"form",
"input",
"textarea",
"select",
"button",
],
FORBID_ATTR: [
"onerror",
"onclick",
"onload",
"onmouseover",
"onmouseout",
"onmouseup",
"onmousedown",
"onfocus",
"onblur",
"onsubmit",
],
});
},
});
export type AuthentikTrustPolicy =
| typeof EscapeTrustPolicy
| typeof SanitizedTrustPolicy
| typeof BrandedHTMLPolicy;
/**
* Sanitize an untrusted HTML string using a trusted types policy.
*/
export function sanitizeHTML(trustPolicy: AuthentikTrustPolicy, untrustedHTML: string) {
return unsafeHTML(trustPolicy.createHTML(untrustedHTML).toString());
}
/**
* DOMPurify configuration for strict sanitization.
*
* This configuration only allows text nodes and disallows all HTML tags.
*/
export const DOM_PURIFY_STRICT = {
ALLOWED_TAGS: ["#text"],
} as const satisfies DOMPurifyConfig;
export async function renderStatic(input: TemplateResult): Promise<string> {
return await collectResult(render(input));
}
/**
* Render untrusted HTML to a string without escaping it.
*
* @returns {string} The rendered HTML string.
*/
export function renderStaticHTMLUnsafe(untrustedHTML: unknown): string {
const container = document.createElement("html");
render(untrustedHTML, container);
export function purify(input: TemplateResult): TemplateResult {
return html`${until(
(async () => {
const rendered = await renderStatic(input);
const purified = DOMPurify.sanitize(rendered);
return html`${unsafeHTML(purified)}`;
})(),
)}`;
const result = container.innerHTML;
return result;
}

View File

@ -17,6 +17,13 @@
/* Minimum width after which the sidebar becomes automatic */
--ak-sidebar--minimum-auto-width: 80rem;
/**
* The height of the navbar and branded sidebar.
* @todo This shouldn't be necessary. The sidebar can instead use a grid layout
* ensuring they share the same height.
*/
--ak-navbar--height: 7rem;
}
@supports selector(::-webkit-scrollbar) {

View File

@ -67,6 +67,12 @@ export class NavigationButtons extends AKElement {
:host([theme="light"]) .pf-c-page__header-tools-group .pf-c-button {
color: var(--ak-global--Color--100) !important;
}
@media (max-width: 768px) {
.pf-c-avatar {
display: none;
}
}
`,
];
}
@ -156,9 +162,7 @@ export class NavigationButtons extends AKElement {
}
renderImpersonation() {
if (!this.me?.original) {
return nothing;
}
if (!this.me?.original) return nothing;
const onClick = async () => {
await new CoreApi(DEFAULT_CONFIG).coreUsersImpersonateEndRetrieve();
@ -175,6 +179,14 @@ export class NavigationButtons extends AKElement {
</div>`;
}
renderAvatar() {
return html`<img
class="pf-c-avatar"
src=${ifDefined(this.me?.user.avatar)}
alt="${msg("Avatar image")}"
/>`;
}
get userDisplayName() {
return match<UserDisplay | undefined, string | undefined>(this.uiConfig?.navbar.userDisplay)
.with(UserDisplay.username, () => this.me?.user.username)
@ -212,11 +224,7 @@ export class NavigationButtons extends AKElement {
</div>
</div>`
: nothing}
<img
class="pf-c-avatar"
src=${ifDefined(this.me?.user.avatar)}
alt="${msg("Avatar image")}"
/>
${this.renderAvatar()}
</div>`;
}
}

View File

@ -5,20 +5,23 @@ import {
} from "@goauthentik/common/constants";
import { globalAK } from "@goauthentik/common/global";
import { currentInterface } from "@goauthentik/common/sentry";
import { UIConfig, UserDisplay, uiConfig } from "@goauthentik/common/ui/config";
import { UIConfig, UserDisplay, getConfigForUser } from "@goauthentik/common/ui/config";
import { me } from "@goauthentik/common/users";
import "@goauthentik/components/ak-nav-buttons";
import { AKElement } from "@goauthentik/elements/Base";
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
import { DefaultBrand } from "@goauthentik/elements/sidebar/SidebarBrand";
import { themeImage } from "@goauthentik/elements/utils/images";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, css, html, nothing } from "lit";
import { CSSResult, LitElement, TemplateResult, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFContent from "@patternfly/patternfly/components/Content/content.css";
import PFDrawer from "@patternfly/patternfly/components/Drawer/drawer.css";
import PFDropdown from "@patternfly/patternfly/components/Dropdown/dropdown.css";
import PFNotificationBadge from "@patternfly/patternfly/components/NotificationBadge/notification-badge.css";
import PFPage from "@patternfly/patternfly/components/Page/page.css";
@ -26,34 +29,52 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
import { SessionUser } from "@goauthentik/api";
@customElement("ak-page-header")
export class PageHeader extends WithBrandConfig(AKElement) {
@property()
icon?: string;
//#region Page Navbar
@property({ type: Boolean })
iconImage = false;
@property()
header = "";
@property()
export interface PageNavbarDetails {
header?: string;
description?: string;
icon?: string;
iconImage?: boolean;
}
@property({ type: Boolean })
hasIcon = true;
/**
* A global navbar component at the top of the page.
*
* Internally, this component listens for the `ak-page-header` event, which is
* dispatched by the `ak-page-header` component.
*/
@customElement("ak-page-navbar")
export class AKPageNavbar extends WithBrandConfig(AKElement) implements PageNavbarDetails {
//#region Static Properties
@state()
me?: SessionUser;
private static elementRef: AKPageNavbar | null = null;
@state()
uiConfig!: UIConfig;
static readonly setNavbarDetails = (detail: Partial<PageNavbarDetails>): void => {
const { elementRef } = AKPageNavbar;
if (!elementRef) {
console.debug(
`ak-page-header: Could not find ak-page-navbar, skipping event dispatch.`,
);
return;
}
const { header, description, icon, iconImage } = detail;
elementRef.header = header;
elementRef.description = description;
elementRef.icon = icon;
elementRef.iconImage = iconImage || false;
elementRef.hasIcon = !!icon;
};
static get styles(): CSSResult[] {
return [
PFBase,
PFButton,
PFPage,
PFDrawer,
PFNotificationBadge,
PFContent,
PFAvatar,
@ -63,143 +84,392 @@ export class PageHeader extends WithBrandConfig(AKElement) {
position: sticky;
top: 0;
z-index: var(--pf-global--ZIndex--lg);
--pf-c-page__header-tools--MarginRight: 0;
--ak-brand-logo-height: var(--pf-global--FontSize--4xl, 2.25rem);
--ak-brand-background-color: var(
--pf-c-page__sidebar--m-light--BackgroundColor
);
}
.bar {
:host([theme="dark"]) {
--ak-brand-background-color: var(--pf-c-page__sidebar--BackgroundColor);
--pf-c-page__sidebar--BackgroundColor: var(--ak-dark-background-light);
color: var(--ak-dark-foreground);
}
navbar {
border-bottom: var(--pf-global--BorderWidth--sm);
border-bottom-style: solid;
border-bottom-color: var(--pf-global--BorderColor--100);
background-color: var(--pf-c-page--BackgroundColor);
display: flex;
flex-direction: row;
min-height: 114px;
max-height: 114px;
background-color: var(--pf-c-page--BackgroundColor);
min-height: 6rem;
display: grid;
row-gap: var(--pf-global--spacer--sm);
column-gap: var(--pf-global--spacer--sm);
grid-template-columns: [brand] auto [toggle] auto [primary] 1fr [secondary] auto;
grid-template-rows: auto auto;
grid-template-areas:
"brand toggle primary secondary"
"brand toggle description secondary";
@media (max-width: 768px) {
row-gap: var(--pf-global--spacer--xs);
align-items: center;
grid-template-areas:
"toggle primary secondary"
"toggle description description";
justify-content: space-between;
width: 100%;
}
}
.pf-c-page__main-section.pf-m-light {
background-color: transparent;
.items {
display: block;
&.primary {
grid-column: primary;
grid-row: primary / description;
align-content: center;
padding-block: var(--pf-global--spacer--md);
@media (min-width: 426px) {
&.block-sibling {
padding-block-end: 0;
grid-row: primary;
}
}
@media (max-width: 768px) {
padding-block: var(--pf-global--spacer--sm);
}
.accent-icon {
height: 1em;
width: 1em;
@media (max-width: 768px) {
display: none;
}
}
}
&.page-description {
grid-area: description;
padding-block-end: var(--pf-global--spacer--md);
@media (max-width: 425px) {
display: none;
}
@media (min-width: 769px) {
text-wrap: balance;
}
}
&.secondary {
grid-area: secondary;
flex: 0 0 auto;
justify-self: end;
padding-block: var(--pf-global--spacer--sm);
padding-inline-end: var(--pf-global--spacer--sm);
@media (min-width: 769px) {
align-content: center;
padding-block: var(--pf-global--spacer--md);
padding-inline-end: var(--pf-global--spacer--xl);
}
}
}
.pf-c-page__main-section {
flex-grow: 1;
flex-shrink: 1;
.brand {
grid-area: brand;
background-color: var(--ak-brand-background-color);
height: 100%;
width: var(--pf-c-page__sidebar--Width);
align-items: center;
padding-inline: var(--pf-global--spacer--sm);
display: flex;
flex-direction: column;
justify-content: center;
&.pf-m-collapsed {
display: none;
}
@media (max-width: 1279px) {
display: none;
}
}
img.pf-icon {
max-height: 24px;
.sidebar-trigger {
grid-area: toggle;
height: 100%;
}
.logo {
flex: 0 0 auto;
height: var(--ak-brand-logo-height);
& img {
height: 100%;
}
}
.sidebar-trigger,
.notification-trigger {
font-size: 24px;
font-size: 1.5rem;
}
.notification-trigger.has-notifications {
color: var(--pf-global--active-color--100);
}
.page-title {
display: flex;
gap: var(--pf-global--spacer--xs);
}
h1 {
display: flex;
flex-direction: row;
align-items: center !important;
}
.pf-c-page__header-tools {
flex-shrink: 0;
}
.pf-c-page__header-tools-group {
height: 100%;
}
:host([theme="dark"]) .pf-c-page__header-tools {
color: var(--ak-dark-foreground) !important;
}
`,
];
}
constructor() {
super();
window.addEventListener(EVENT_WS_MESSAGE, () => {
this.firstUpdated();
});
}
//#endregion
async firstUpdated() {
this.me = await me();
this.uiConfig = await uiConfig();
this.uiConfig.navbar.userDisplay = UserDisplay.none;
}
//#region Properties
setTitle(header?: string) {
@property({ type: String })
icon?: string;
@property({ type: Boolean })
iconImage = false;
@property({ type: String })
header?: string;
@property({ type: String })
description?: string;
@property({ type: Boolean })
hasIcon = true;
@property({ type: Boolean })
open = true;
@state()
session?: SessionUser;
@state()
uiConfig!: UIConfig;
//#endregion
//#region Private Methods
#setTitle(header?: string) {
const currentIf = currentInterface();
let title = this.brand?.brandingTitle || TITLE_DEFAULT;
if (currentIf === "admin") {
title = `${msg("Admin")} - ${title}`;
}
// Prepend the header to the title
if (header !== undefined && header !== "") {
if (header) {
title = `${header} - ${title}`;
}
document.title = title;
}
#toggleSidebar() {
this.open = !this.open;
this.dispatchEvent(
new CustomEvent(EVENT_SIDEBAR_TOGGLE, {
bubbles: true,
composed: true,
}),
);
}
//#endregion
//#region Lifecycle
public connectedCallback(): void {
super.connectedCallback();
AKPageNavbar.elementRef = this;
window.addEventListener(EVENT_WS_MESSAGE, () => {
this.firstUpdated();
});
}
public disconnectedCallback(): void {
super.disconnectedCallback();
AKPageNavbar.elementRef = null;
}
public async firstUpdated() {
this.session = await me();
this.uiConfig = getConfigForUser(this.session.user);
this.uiConfig.navbar.userDisplay = UserDisplay.none;
}
willUpdate() {
// Always update title, even if there's no header value set,
// as in that case we still need to return to the generic title
this.setTitle(this.header);
this.#setTitle(this.header);
}
//#endregion
//#region Render
renderIcon() {
if (this.icon) {
if (this.iconImage && !this.icon.startsWith("fa://")) {
return html`<img class="pf-icon" src="${this.icon}" alt="page icon" />`;
return html`<img class="accent-icon pf-icon" src="${this.icon}" alt="page icon" />`;
}
const icon = this.icon.replaceAll("fa://", "fa ");
return html`<i class=${icon}></i>`;
return html`<i class="accent-icon ${icon}"></i>`;
}
return nothing;
}
render(): TemplateResult {
return html`<div class="bar">
<button
class="sidebar-trigger pf-c-button pf-m-plain"
@click=${() => {
this.dispatchEvent(
new CustomEvent(EVENT_SIDEBAR_TOGGLE, {
bubbles: true,
composed: true,
}),
);
}}
>
<i class="fas fa-bars"></i>
</button>
<section class="pf-c-page__main-section pf-m-light">
<div class="pf-c-content">
<h1>
return html`<navbar aria-label="Main" class="navbar">
<aside class="brand ${this.open ? "" : "pf-m-collapsed"}">
<a href="#/">
<div class="logo">
<img
src=${themeImage(
this.brand?.brandingLogo ?? DefaultBrand.brandingLogo,
)}
alt="${msg("authentik Logo")}"
loading="lazy"
/>
</div>
</a>
</aside>
<button
class="sidebar-trigger pf-c-button pf-m-plain"
@click=${this.#toggleSidebar}
aria-label=${msg("Toggle sidebar")}
aria-expanded=${this.open ? "true" : "false"}
>
<i class="fas fa-bars"></i>
</button>
<section
class="items primary pf-c-content ${this.description ? "block-sibling" : ""}"
>
<h1 class="page-title">
${this.hasIcon
? html`<slot name="icon">${this.renderIcon()}</slot>&nbsp;`
? html`<slot name="icon">${this.renderIcon()}</slot>`
: nothing}
<slot name="header">${this.header}</slot>
${this.header}
</h1>
${this.description ? html`<p>${this.description}</p>` : html``}
</div>
</section>
<div class="pf-c-page__header-tools">
<div class="pf-c-page__header-tools-group">
<ak-nav-buttons .uiConfig=${this.uiConfig} .me=${this.me}>
<a
class="pf-c-button pf-m-secondary pf-m-small pf-u-display-none pf-u-display-block-on-md"
href="${globalAK().api.base}if/user/"
slot="extra"
>
${msg("User interface")}
</a>
</ak-nav-buttons>
</div>
</div>
</div>`;
</section>
${this.description
? html`<section class="items page-description pf-c-content">
<p>${this.description}</p>
</section>`
: nothing}
<section class="items secondary">
<div class="pf-c-page__header-tools-group">
<ak-nav-buttons .uiConfig=${this.uiConfig} .me=${this.session}>
<a
class="pf-c-button pf-m-secondary pf-m-small pf-u-display-none pf-u-display-block-on-md"
href="${globalAK().api.base}if/user/"
slot="extra"
>
${msg("User interface")}
</a>
</ak-nav-buttons>
</div>
</section>
</navbar>
<slot></slot>`;
}
//#endregion
}
//#endregion
//#region Page Header
/**
* A page header component, used to display the page title and description.
*
* Internally, this component dispatches the `ak-page-header` event, which is
* listened to by the `ak-page-navbar` component.
*
* @singleton
*/
@customElement("ak-page-header")
export class AKPageHeader extends LitElement implements PageNavbarDetails {
@property({ type: String })
header?: string;
@property({ type: String })
description?: string;
@property({ type: String })
icon?: string;
@property({ type: Boolean })
iconImage = false;
static get styles(): CSSResult[] {
return [
css`
:host {
display: none;
}
`,
];
}
connectedCallback(): void {
super.connectedCallback();
AKPageNavbar.setNavbarDetails({
header: this.header,
description: this.description,
icon: this.icon,
iconImage: this.iconImage,
});
}
updated(): void {
AKPageNavbar.setNavbarDetails({
header: this.header,
description: this.description,
icon: this.icon,
iconImage: this.iconImage,
});
}
}
//#endregion
declare global {
interface HTMLElementTagNameMap {
"ak-page-header": PageHeader;
"ak-page-header": AKPageHeader;
"ak-page-navbar": AKPageNavbar;
}
}

View File

@ -80,6 +80,7 @@ export class AggregateCard extends AKElement implements IAggregateCard {
.center-value {
font-size: var(--pf-global--icon--FontSize--lg);
text-align: center;
place-content: center;
}
.subtext {
margin-top: var(--pf-global--spacer--sm);

View File

@ -85,6 +85,7 @@ export abstract class AKChart<T> extends AKElement {
.container {
height: 100%;
width: 100%;
aspect-ratio: 1 / 1;
display: flex;
justify-content: center;

View File

@ -35,10 +35,7 @@ export class Sidebar extends AKElement {
.pf-c-nav__section + .pf-c-nav__section {
--pf-c-nav__section--section--MarginTop: var(--pf-global--spacer--sm);
}
.pf-c-nav__list .sidebar-brand {
max-height: 82px;
margin-bottom: -0.5rem;
}
nav {
display: flex;
flex-direction: column;
@ -70,7 +67,6 @@ export class Sidebar extends AKElement {
class="pf-c-nav ${this.activeTheme === UiThemeEnum.Light ? "pf-m-light" : ""}"
aria-label=${msg("Global")}
>
<ak-sidebar-brand></ak-sidebar-brand>
<ul class="pf-c-nav__list">
<slot></slot>
</ul>

View File

@ -1,17 +1,3 @@
import { EVENT_SIDEBAR_TOGGLE } from "@goauthentik/common/constants";
import { AKElement } from "@goauthentik/elements/Base";
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
import { themeImage } from "@goauthentik/elements/utils/images";
import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, css, html } from "lit";
import { customElement } from "lit/decorators.js";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFPage from "@patternfly/patternfly/components/Page/page.css";
import PFGlobal from "@patternfly/patternfly/patternfly-base.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
import { CurrentBrand, UiThemeEnum } from "@goauthentik/api";
// If the viewport is wider than MIN_WIDTH, the sidebar
@ -28,79 +14,3 @@ export const DefaultBrand: CurrentBrand = {
matchedDomain: "",
defaultLocale: "",
};
@customElement("ak-sidebar-brand")
export class SidebarBrand extends WithBrandConfig(AKElement) {
static get styles(): CSSResult[] {
return [
PFBase,
PFGlobal,
PFPage,
PFButton,
css`
:host {
display: flex;
flex-direction: row;
align-items: center;
height: 114px;
min-height: 114px;
border-bottom: var(--pf-global--BorderWidth--sm);
border-bottom-style: solid;
border-bottom-color: var(--pf-global--BorderColor--100);
}
.pf-c-brand img {
padding: 0 0.5rem;
height: 42px;
}
button.pf-c-button.sidebar-trigger {
background-color: transparent;
border-radius: 0px;
height: 100%;
color: var(--ak-dark-foreground);
}
`,
];
}
constructor() {
super();
window.addEventListener("resize", () => {
this.requestUpdate();
});
}
render(): TemplateResult {
return html` ${window.innerWidth <= MIN_WIDTH
? html`
<button
class="sidebar-trigger pf-c-button"
@click=${() => {
this.dispatchEvent(
new CustomEvent(EVENT_SIDEBAR_TOGGLE, {
bubbles: true,
composed: true,
}),
);
}}
>
<i class="fas fa-bars"></i>
</button>
`
: html``}
<a href="#/" class="pf-c-page__header-brand-link">
<div class="pf-c-brand ak-brand">
<img
src=${themeImage(this.brand?.brandingLogo ?? DefaultBrand.brandingLogo)}
alt="${msg("authentik Logo")}"
loading="lazy"
/>
</div>
</a>`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ak-sidebar-brand": SidebarBrand;
}
}

View File

@ -0,0 +1,55 @@
/**
* @file IFrame Utilities
*/
interface IFrameLoadResult {
contentWindow: Window;
contentDocument: Document;
}
export function pluckIFrameContent(iframe: HTMLIFrameElement) {
const contentWindow = iframe.contentWindow;
const contentDocument = iframe.contentDocument;
if (!contentWindow) {
throw new Error("Iframe contentWindow is not accessible");
}
if (!contentDocument) {
throw new Error("Iframe contentDocument is not accessible");
}
return {
contentWindow,
contentDocument,
};
}
export function resolveIFrameContent(iframe: HTMLIFrameElement): Promise<IFrameLoadResult> {
if (iframe.contentDocument?.readyState === "complete") {
return Promise.resolve(pluckIFrameContent(iframe));
}
return new Promise((resolve) => {
iframe.addEventListener("load", () => resolve(pluckIFrameContent(iframe)), { once: true });
});
}
/**
* Creates a minimal HTML wrapper for an iframe.
*
* @deprecated Use the `contentDocument.body` directly instead.
*/
export function createIFrameHTMLWrapper(bodyContent: string): string {
const html = String.raw;
return html`<!doctype html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body style="display:flex;flex-direction:row;justify-content:center;">
${bodyContent}
</body>
</html>`;
}

View File

@ -1,4 +1,4 @@
import { purify } from "@goauthentik/common/purify";
import { BrandedHTMLPolicy, sanitizeHTML } from "@goauthentik/common/purify";
import { AKElement } from "@goauthentik/elements/Base.js";
import { msg } from "@lit/localize";
@ -21,8 +21,6 @@ const styles = css`
}
`;
const poweredBy: FooterLink = { name: msg("Powered by authentik"), href: null };
@customElement("ak-brand-links")
export class BrandLinks extends AKElement {
static get styles() {
@ -33,13 +31,21 @@ export class BrandLinks extends AKElement {
links: FooterLink[] = [];
render() {
const links = [...(this.links ?? []), poweredBy];
const links = [...(this.links ?? [])];
return html` <ul class="pf-c-list pf-m-inline">
${map(links, (link) =>
link.href
? purify(html`<li><a href="${link.href}">${link.name}</a></li>`)
: html`<li><span>${link.name}</span></li>`,
)}
${map(links, (link) => {
const children = sanitizeHTML(BrandedHTMLPolicy, link.name);
if (link.href) {
return html`<li><a href="${link.href}">${children}</a></li>`;
}
return html`<li>
<span> ${children} </span>
</li>`;
})}
<li><span>${msg("Powered by authentik")}</span></li>
</ul>`;
}
}

View File

@ -1,15 +1,16 @@
///<reference types="@hcaptcha/types"/>
import { renderStatic } from "@goauthentik/common/purify";
/// <reference types="@hcaptcha/types"/>
/// <reference types="turnstile-types"/>
import { renderStaticHTMLUnsafe } from "@goauthentik/common/purify";
import "@goauthentik/elements/EmptyState";
import { akEmptyState } from "@goauthentik/elements/EmptyState";
import { bound } from "@goauthentik/elements/decorators/bound";
import "@goauthentik/elements/forms/FormElement";
import { createIFrameHTMLWrapper } from "@goauthentik/elements/utils/iframe";
import { ListenerController } from "@goauthentik/elements/utils/listenerController.js";
import { randomId } from "@goauthentik/elements/utils/randomId";
import "@goauthentik/flow/FormStatic";
import { BaseStage } from "@goauthentik/flow/stages/base";
import { P, match } from "ts-pattern";
import type * as _ from "turnstile-types";
import { msg } from "@lit/localize";
import { CSSResult, PropertyValues, TemplateResult, css, html, nothing } from "lit";
@ -56,40 +57,36 @@ type CaptchaHandler = {
// a resize. Because the Captcha is itself in an iframe, the reported height is often off by some
// margin, so adding 2rem of height to our container adds padding and prevents scroll bars or hidden
// rendering.
function iframeTemplate(children: TemplateResult, challengeURL: string): TemplateResult {
return html` ${children}
<script>
new ResizeObserver((entries) => {
const height =
document.body.offsetHeight +
parseFloat(getComputedStyle(document.body).fontSize) * 2;
const iframeTemplate = (captchaElement: TemplateResult, challengeUrl: string) =>
html`<!doctype html>
<head>
<html>
<body style="display:flex;flex-direction:row;justify-content:center;">
${captchaElement}
<script>
new ResizeObserver((entries) => {
const height =
document.body.offsetHeight +
parseFloat(getComputedStyle(document.body).fontSize) * 2;
window.parent.postMessage({
message: "resize",
source: "goauthentik.io",
context: "flow-executor",
size: { height },
});
}).observe(document.querySelector(".ak-captcha-container"));
</script>
<script src=${challengeUrl}></script>
<script>
function callback(token) {
window.parent.postMessage({
message: "captcha",
source: "goauthentik.io",
context: "flow-executor",
token: token,
});
}
</script>
</body>
</html>
</head>`;
window.parent.postMessage({
message: "resize",
source: "goauthentik.io",
context: "flow-executor",
size: { height },
});
}).observe(document.querySelector(".ak-captcha-container"));
</script>
<script src=${challengeURL}></script>
<script>
function callback(token) {
window.parent.postMessage({
message: "captcha",
source: "goauthentik.io",
context: "flow-executor",
token,
});
}
</script>`;
}
@customElement("ak-stage-captcha")
export class CaptchaStage extends BaseStage<CaptchaChallenge, CaptchaChallengeResponseRequest> {
@ -305,11 +302,25 @@ export class CaptchaStage extends BaseStage<CaptchaChallenge, CaptchaChallengeRe
}
async renderFrame(captchaElement: TemplateResult) {
this.captchaFrame.contentWindow?.document.open();
this.captchaFrame.contentWindow?.document.write(
await renderStatic(iframeTemplate(captchaElement, this.challenge.jsUrl)),
const { contentDocument } = this.captchaFrame || {};
if (!contentDocument) {
console.debug(
"authentik/stages/captcha: unable to render captcha frame, no contentDocument",
);
return;
}
contentDocument.open();
contentDocument.write(
createIFrameHTMLWrapper(
renderStaticHTMLUnsafe(iframeTemplate(captchaElement, this.challenge.jsUrl)),
),
);
this.captchaFrame.contentWindow?.document.close();
contentDocument.close();
}
renderBody() {

View File

@ -1091,7 +1091,7 @@
</trans-unit>
<trans-unit id="sd62cfc27ad4aa33b">
<source>Based on the User's Email</source>
<target>Basierend auf der E-Mail-Adresse des Benutzers</target>
<target>Basierend auf der E-Mail Adresse des Benutzers</target>
</trans-unit>
<trans-unit id="s55eb75bedf96be0f">
@ -2361,7 +2361,6 @@
</trans-unit>
<trans-unit id="s9307f3dbb07a73b5">
<source>Only fail the policy, don't invalidate user's password</source>
<target>Nur die Richtlinie fehlschlagen lassen, das Passwort des Benutzers nicht ungültig machen.</target>
</trans-unit>
<trans-unit id="scea1f16238093e35">
@ -2810,7 +2809,6 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="sfbc59ff17a73503d">
<source>User path</source>
<target>Nutzerpfad</target>
</trans-unit>
<trans-unit id="sd18170637295bace">
@ -2880,7 +2878,6 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s995535e7af30d754">
<source>Use the user's email address, but deny enrollment when the email address already exists</source>
<target>Verwende die E-Mail-Adresse des Benutzers, aber verweigere die Registrierung, wenn die E-Mail-Adresse bereits existiert.</target>
</trans-unit>
<trans-unit id="s542ecb4130f6cea5">
@ -2889,7 +2886,6 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s2a1debf34e5aeba4">
<source>Use the user's username, but deny enrollment when the username already exists</source>
<target>Verwende den Anmeldenamen des Benutzers, aber verweigere die Registrierung von der Anmeldename bereits existiert.</target>
</trans-unit>
<trans-unit id="s81ce0d54727f42d2">
@ -3744,9 +3740,6 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s14401ff4a0cba208">
<source>Failed to update <x id="0" equiv-text="${this.objectLabel}"/>: <x id="1" equiv-text="${pluckErrorDetail(parsedError)}"/></source>
<target>Aktualisieren von
<x id="0" equiv-text="${this.objectLabel}"/>fehlgeschlagen:
<x id="1" equiv-text="${e.toString()}"/></target>
</trans-unit>
<trans-unit id="sa95a538bfbb86111">
@ -4958,7 +4951,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="se50a08ab71bb96ed">
<source>When a valid username/email has been entered, and this option is enabled, the user's username and avatar will be shown. Otherwise, the text that the user entered will be shown.</source>
<target>Sofern eine gültige E-Mail-Adresse oder Benutzername angegeben wurde und diese Option aktiviert ist, wird das Profilbild und der Benutzername des Benutzers angezeigt. Ansonsten wird der vom Benutzer eingegebene Text angezeigt.</target>
<target>Sofern eine gültige E-Mailadresse oder Benutzername angegeben wurde und diese Option aktiviert ist, wird das Profilbild und der Benutzername des Benutzers angezeigt. Ansonsten wird der vom Benutzer eingegebene Text angezeigt.</target>
</trans-unit>
<trans-unit id="s0295ce5d6f635d75">
@ -6252,7 +6245,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s670ad066cc0e50a3">
<source>Login to continue to <x id="0" equiv-text="${this.challenge.applicationPre}"/>.</source>
<target>Anmelden, um mit <x id="0" equiv-text="${this.challenge.applicationPre}"/> fortzufahren.</target>
<target>Anmelden um mit <x id="0" equiv-text="${this.challenge.applicationPre}"/> fortzufahren.</target>
</trans-unit>
<trans-unit id="scf5ce91bfba10a61">
@ -7458,7 +7451,6 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s28b99b59541f54ca">
<source>Connection failed after <x id="0" equiv-text="${this.connectionAttempt}"/> attempts.</source>
<target>Verbindung nach <x id="0" equiv-text="${this.connectionAttempt}"/> Versuch(en) fehlgeschlagen.</target>
</trans-unit>
<trans-unit id="s7c7d956418e1c8c8">
<source>Re-connecting in <x id="0" equiv-text="${Math.max(1, delay / 1000)}"/> second(s).</source>
@ -7580,11 +7572,11 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s456d88f3679190fd">
<source>Allow users to change email</source>
<target>Benutzer können E-Mail-Adresse ändern</target>
<target>Benutzer können E-Mail ändern</target>
</trans-unit>
<trans-unit id="s5fc6c14d106f40d3">
<source>Enable the ability for users to change their email.</source>
<target>Benutzer haben die Möglichkeit, ihre E-Mail-Adresse zu ändern.</target>
<target>Benutzer haben die Möglichkeit, ihre E-Mail zu ändern.</target>
</trans-unit>
<trans-unit id="s628e414bb2367057">
<source>Allow users to change username</source>
@ -7809,7 +7801,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s8cc0075913c67566">
<source>Enter the email associated with your account, and we'll send you a link to reset your password.</source>
<target>Gib die E-Mail-Adresse deines Accounts ein und du erhältst einen Link zum zurücksetzen des Passworts.</target>
<target>Gib die Email deines Accounts ein und du erhältst einen Link zum zurücksetzen des Passworts.</target>
</trans-unit>
<trans-unit id="s06bfe45ffef2cf60">
<source>Stage name: <x id="0" equiv-text="${this.challenge.name}"/></source>
@ -7893,7 +7885,6 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sc1673c93148583ba">
<source>Request failed. Please try again later.</source>
<target>Anfrage fehlgeschlagen. Bitte versuchen Sie es später erneut.</target>
</trans-unit>
<trans-unit id="s85be1f5e7a0fa3b1">
<source>Available Roles</source>
@ -8286,6 +8277,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s354405ae02cb262d">
<source>Last seen: <x id="0" equiv-text="${formatElapsedTime(lastSeen)}"/> (<x id="1" equiv-text="${lastSeen.toLocaleTimeString()}"/>)</source>
<target>Zuletzt gesehen: <x id="0" equiv-text="${getRelativeTime(lastSeen)}"/> (<x id="1" equiv-text="${lastSeen.toLocaleTimeString()}"/>)</target>
</trans-unit>
<trans-unit id="s5aebe06e3ddf6ca9">
<source>Sign assertions</source>
@ -8857,11 +8849,9 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s61ffea061fae0af4">
<source>No notifications found.</source>
<target>Keine Benachrichtigungen gefunden.</target>
</trans-unit>
<trans-unit id="scf8d5cdc8b434982">
<source>You don't have any notifications currently.</source>
<target>Sie haben zur Zeit keine Benachrichtigungen.</target>
</trans-unit>
<trans-unit id="s358e08de4fbebf51">
<source>Version <x id="0" equiv-text="${this.version?.versionCurrent || &quot;&quot;}"/></source>

Some files were not shown because too many files have changed in this diff Show More