Compare commits

..

3 Commits

Author SHA1 Message Date
Teffen Ellis
12d8dfc42b Adjust color. 2026-05-07 15:19:36 +02:00
Teffen Ellis
062f03fd0c Adjust page padding, banners. 2026-05-07 15:19:29 +02:00
Teffen Ellis
ef2f316cda Flesh out sidebar invoker. 2026-05-07 15:09:00 +02:00
1643 changed files with 10768 additions and 40773 deletions

View File

@@ -25,7 +25,7 @@ runs:
if: ${{ contains(inputs.dependencies, 'system') || contains(inputs.dependencies, 'python') }}
uses: gerlero/apt-install@f4fa5265092af9e750549565d28c99aec7189639
with:
packages: libpq-dev openssl libxmlsec1-dev pkg-config gettext libclang-dev libkadm5clnt-mit12 libkadm5clnt7t64-heimdal libkrb5-dev krb5-kdc krb5-user krb5-admin-server
packages: libpq-dev openssl libxmlsec1-dev pkg-config gettext krb5-multidev libkrb5-dev heimdal-multidev libclang-dev krb5-kdc krb5-user krb5-admin-server
update: true
upgrade: false
install-recommends: false
@@ -64,7 +64,7 @@ runs:
rustflags: ""
- name: Setup rust dependencies
if: ${{ contains(inputs.dependencies, 'rust') }}
uses: taiki-e/install-action@711e1c3275189d76dcc4d34ddea63bf96ac49090 # v2
uses: taiki-e/install-action@db5fb34fa772531a3ece57ca434f579eb334e0fb # v2
with:
tool: cargo-deny cargo-machete cargo-llvm-cov nextest
- name: Setup node (web)

View File

@@ -68,8 +68,6 @@ jobs:
token: ${{ steps.generate_token.outputs.token }}
- name: Setup authentik env
uses: ./.github/actions/setup
with:
dependencies: "system,python,go,node,runtime,rust-nightly"
- name: Run migrations
run: make migrate
- name: Bump version

View File

@@ -82,14 +82,10 @@ jobs:
token: "${{ steps.app-token.outputs.token }}"
- name: Setup authentik env
uses: ./.github/actions/setup
with:
dependencies: "system,python,go,node,runtime,rust-nightly"
- name: Run migrations
run: make migrate
- name: Bump version
run: "make bump version=${{ inputs.version }}"
- name: Re-generate API Clients
run: make gen
- name: Commit and push
run: |
# ID from https://api.github.com/users/authentik-automation[bot]

12
Cargo.lock generated
View File

@@ -171,7 +171,7 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "authentik"
version = "2026.5.0-rc2"
version = "2026.5.0-rc1"
dependencies = [
"arc-swap",
"argh",
@@ -196,7 +196,7 @@ dependencies = [
[[package]]
name = "authentik-axum"
version = "2026.5.0-rc2"
version = "2026.5.0-rc1"
dependencies = [
"authentik-common",
"axum",
@@ -216,7 +216,7 @@ dependencies = [
[[package]]
name = "authentik-client"
version = "2026.5.0-rc2"
version = "2026.5.0-rc1"
dependencies = [
"aws-lc-rs",
"reqwest",
@@ -232,7 +232,7 @@ dependencies = [
[[package]]
name = "authentik-common"
version = "2026.5.0-rc2"
version = "2026.5.0-rc1"
dependencies = [
"arc-swap",
"authentik-client",
@@ -3934,9 +3934,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.52.3"
version = "1.52.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe"
checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6"
dependencies = [
"bytes",
"libc",

View File

@@ -8,7 +8,7 @@ members = [
resolver = "3"
[workspace.package]
version = "2026.5.0-rc2"
version = "2026.5.0-rc1"
authors = ["authentik Team <hello@goauthentik.io>"]
description = "Making authentication simple."
edition = "2024"
@@ -97,7 +97,7 @@ sqlx = { version = "= 0.8.6", default-features = false, features = [
tempfile = "= 3.27.0"
thiserror = "= 2.0.18"
time = { version = "= 0.3.47", features = ["macros"] }
tokio = { version = "= 1.52.3", features = ["full", "tracing"] }
tokio = { version = "= 1.52.1", features = ["full", "tracing"] }
tokio-retry2 = "= 0.9.1"
tokio-rustls = "= 0.26.4"
tokio-util = { version = "= 0.7.18", features = ["full"] }
@@ -115,9 +115,9 @@ url = "= 2.5.8"
uuid = { version = "= 1.23.1", features = ["serde", "v4"] }
which = "= 8.0.2"
ak-axum = { package = "authentik-axum", version = "2026.5.0-rc2", path = "./packages/ak-axum" }
ak-client = { package = "authentik-client", version = "2026.5.0-rc2", path = "./packages/client-rust" }
ak-common = { package = "authentik-common", version = "2026.5.0-rc2", path = "./packages/ak-common", default-features = false }
ak-axum = { package = "authentik-axum", version = "2026.5.0-rc1", path = "./packages/ak-axum" }
ak-client = { package = "authentik-client", version = "2026.5.0-rc1", path = "./packages/client-rust" }
ak-common = { package = "authentik-common", version = "2026.5.0-rc1", path = "./packages/ak-common", default-features = false }
[workspace.lints.rust]
ambiguous_negative_literals = "warn"

View File

@@ -160,7 +160,7 @@ endif
$(eval current_version := $(shell cat ${PWD}/internal/constants/VERSION))
$(SED_INPLACE) 's/^version = ".*"/version = "$(version)"/' ${PWD}/pyproject.toml
$(SED_INPLACE) 's/^VERSION = ".*"/VERSION = "$(version)"/' ${PWD}/authentik/__init__.py
$(SED_INPLACE) "s/version = \"${current_version}\"/version = \"$(version)\"/" ${PWD}/Cargo.toml ${PWD}/Cargo.lock
$(SED_INPLACE) "s/version = \"${current_version}\"/version = \"$(version)\"" ${PWD}/Cargo.toml ${PWD}/Cargo.lock
$(MAKE) gen-build gen-compose aws-cfn
$(SED_INPLACE) "s/\"${current_version}\"/\"$(version)\"/" ${PWD}/package.json ${PWD}/package-lock.json ${PWD}/web/package.json ${PWD}/web/package-lock.json
echo -n $(version) > ${PWD}/internal/constants/VERSION

View File

@@ -3,7 +3,7 @@
from functools import lru_cache
from os import environ
VERSION = "2026.5.0-rc2"
VERSION = "2026.5.0-rc1"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"

View File

@@ -42,29 +42,11 @@ def validate_auth(header: bytes, format="bearer") -> str | None:
return auth_credentials
class VirtualUser(AnonymousUser):
is_active = True
@property
def type(self):
return UserTypes.INTERNAL_SERVICE_ACCOUNT
@property
def is_anonymous(self):
return False
@property
def is_authenticated(self):
return True
def all_roles(self):
return []
class IPCUser(VirtualUser):
class IPCUser(AnonymousUser):
"""'Virtual' user for IPC communication between authentik core and the authentik router"""
username = "authentik:system"
is_active = True
is_superuser = True
@property
@@ -80,6 +62,17 @@ class IPCUser(VirtualUser):
def has_module_perms(self, module):
return True
@property
def is_anonymous(self):
return False
@property
def is_authenticated(self):
return True
def all_roles(self):
return []
class TokenAuthentication(BaseAuthentication):
"""Token-based authentication using HTTP Bearer authentication"""

View File

@@ -217,7 +217,10 @@ class BlueprintInstanceViewSet(UsedByMixin, ModelViewSet):
@extend_schema(
request={"multipart/form-data": BlueprintUploadSerializer},
responses={200: BlueprintImportResultSerializer},
responses={
204: BlueprintImportResultSerializer,
400: BlueprintImportResultSerializer,
},
)
@action(url_path="import", detail=False, methods=["POST"], parser_classes=(MultiPartParser,))
@validate(
@@ -244,13 +247,21 @@ class BlueprintInstanceViewSet(UsedByMixin, ModelViewSet):
import_response = self.BlueprintImportResultSerializer(
data={
"logs": [LogEventSerializer(log).data for log in logs],
"success": valid,
"logs": [],
"success": False,
}
)
import_response.is_valid(raise_exception=True)
if valid:
import_response.initial_data["success"] = importer.apply()
import_response.is_valid()
import_response.initial_data["logs"] = [LogEventSerializer(log).data for log in logs]
import_response.initial_data["success"] = valid
import_response.is_valid()
if not valid:
return Response(data=import_response.initial_data, status=200)
successful = importer.apply()
import_response.initial_data["success"] = successful
import_response.is_valid()
if not successful:
return Response(data=import_response.initial_data, status=200)
return Response(data=import_response.initial_data, status=200)

View File

@@ -3,7 +3,6 @@
from json import dumps, loads
from tempfile import NamedTemporaryFile, mkdtemp
from django.core.files.uploadedfile import SimpleUploadedFile
from django.urls import reverse
from rest_framework.test import APITestCase
from yaml import dump
@@ -142,20 +141,6 @@ class TestBlueprintsV1API(APITestCase):
)
self.assertEqual(res.status_code, 200)
def test_api_import_invalid_blueprint_returns_result_payload(self):
"""Invalid blueprint content returns a result payload instead of a 400 response."""
file = SimpleUploadedFile("invalid-blueprint.yaml", b'{"version": 3}')
res = self.client.post(
reverse("authentik_api:blueprintinstance-import-"),
data={"file": file},
format="multipart",
)
self.assertEqual(res.status_code, 200)
self.assertFalse(res.json()["success"])
self.assertGreater(len(res.json()["logs"]), 0)
def test_api_import_unknown_path(self):
"""Path not in available blueprints is rejected (covers api.py:56)."""
res = self.client.post(

View File

@@ -246,25 +246,6 @@ class GroupSerializer(ModelSerializer):
)
return superuser
def validate_users(self, users: list) -> list:
"""Require add_user_to_group permission when adding new members via group PATCH."""
request: Request = self.context.get("request", None)
if not request:
return users
if not self.instance:
return users
# BulkManyRelatedField returns raw PKs, not model instances
current_user_pks = set(self.instance.users.values_list("pk", flat=True))
new_users = [u for u in users if u not in current_user_pks]
if not new_users:
return users
has_perm = request.user.has_perm(
"authentik_core.add_user_to_group"
) or request.user.has_perm("authentik_core.add_user_to_group", self.instance)
if not has_perm:
raise ValidationError(_("User does not have permission to add members to this group."))
return users
class Meta:
model = Group
fields = [

View File

@@ -297,36 +297,6 @@ class UserSerializer(ModelSerializer):
raise ValidationError(_("Setting a user to internal service account is not allowed."))
return user_type
def validate_groups(self, groups: list) -> list:
"""Require enable_group_superuser permission when adding a user to a superuser group."""
request: Request = self.context.get("request", None)
if not request:
return groups
current_groups = set(self.instance.groups.all()) if self.instance else set()
for group in groups:
if not group.is_superuser:
continue
if group in current_groups:
continue
if not request.user.has_perm("authentik_core.enable_group_superuser"):
raise ValidationError(
_("User does not have permission to add members to a superuser group.")
)
return groups
def validate_roles(self, roles: list) -> list:
"""Require change_role permission when assigning new roles to a user."""
request: Request = self.context.get("request", None)
if not request:
return roles
current_roles = set(self.instance.roles.all()) if self.instance else set()
new_roles = [r for r in roles if r not in current_roles]
if not new_roles:
return roles
if not request.user.has_perm("authentik_rbac.change_role"):
raise ValidationError(_("User does not have permission to assign roles."))
return roles
def validate(self, attrs: dict) -> dict:
if self.instance and self.instance.type == UserTypes.INTERNAL_SERVICE_ACCOUNT:
raise ValidationError(_("Can't modify internal service account users"))

View File

@@ -158,58 +158,3 @@ class TestGroupsAPI(APITestCase):
data={"name": generate_id(), "is_superuser": True},
)
self.assertEqual(res.status_code, 201)
def test_patch_users_no_perm(self):
"""PATCH group with new users without add_user_to_group must be rejected."""
group = Group.objects.create(name=generate_id())
self.login_user.assign_perms_to_managed_role("authentik_core.view_group", group)
self.login_user.assign_perms_to_managed_role("authentik_core.change_group", group)
self.client.force_login(self.login_user)
res = self.client.patch(
reverse("authentik_api:group-detail", kwargs={"pk": group.pk}),
data={"users": [self.user.pk]},
content_type="application/json",
)
self.assertEqual(res.status_code, 400)
def test_patch_users_with_global_perm(self):
"""PATCH group with new users with global add_user_to_group must succeed."""
group = Group.objects.create(name=generate_id())
self.login_user.assign_perms_to_managed_role("authentik_core.view_group", group)
self.login_user.assign_perms_to_managed_role("authentik_core.change_group", group)
self.login_user.assign_perms_to_managed_role("authentik_core.add_user_to_group")
self.client.force_login(self.login_user)
res = self.client.patch(
reverse("authentik_api:group-detail", kwargs={"pk": group.pk}),
data={"users": [self.user.pk]},
content_type="application/json",
)
self.assertEqual(res.status_code, 200)
def test_patch_users_with_obj_perm(self):
"""PATCH group with new users with object-level add_user_to_group must succeed."""
group = Group.objects.create(name=generate_id())
self.login_user.assign_perms_to_managed_role("authentik_core.view_group", group)
self.login_user.assign_perms_to_managed_role("authentik_core.change_group", group)
self.login_user.assign_perms_to_managed_role("authentik_core.add_user_to_group", group)
self.client.force_login(self.login_user)
res = self.client.patch(
reverse("authentik_api:group-detail", kwargs={"pk": group.pk}),
data={"users": [self.user.pk]},
content_type="application/json",
)
self.assertEqual(res.status_code, 200)
def test_patch_existing_users_no_perm(self):
"""PATCH group keeping existing membership without add_user_to_group must succeed."""
group = Group.objects.create(name=generate_id())
group.users.add(self.user)
self.login_user.assign_perms_to_managed_role("authentik_core.view_group", group)
self.login_user.assign_perms_to_managed_role("authentik_core.change_group", group)
self.client.force_login(self.login_user)
res = self.client.patch(
reverse("authentik_api:group-detail", kwargs={"pk": group.pk}),
data={"users": [self.user.pk]},
content_type="application/json",
)
self.assertEqual(res.status_code, 200)

View File

@@ -12,7 +12,6 @@ from authentik.brands.models import Brand
from authentik.core.models import (
USER_ATTRIBUTE_TOKEN_EXPIRING,
AuthenticatedSession,
Group,
Session,
Token,
User,
@@ -26,7 +25,6 @@ from authentik.core.tests.utils import (
)
from authentik.flows.models import FlowAuthenticationRequirement, FlowDesignation
from authentik.lib.generators import generate_id, generate_key
from authentik.rbac.models import Role
from authentik.stages.email.models import EmailStage
INVALID_PASSWORD_HASH = "not-a-valid-hash"
@@ -941,79 +939,3 @@ class TestUsersAPI(APITestCase):
self.assertIn(user2.pk, pks)
# Verify user2 comes before user1 in descending order
self.assertLess(pks.index(user2.pk), pks.index(user1.pk))
class TestUsersAPIGroupRoleValidation(APITestCase):
"""Test that PATCH /api/v3/core/users/{pk}/ enforces group and role permission checks."""
def setUp(self) -> None:
self.actor = create_test_user()
self.target = create_test_user()
def _patch(self, data: dict):
self.client.force_login(self.actor)
return self.client.patch(
reverse("authentik_api:user-detail", kwargs={"pk": self.target.pk}),
data=data,
content_type="application/json",
)
def test_patch_superuser_group_no_perm(self):
"""Assigning a superuser group without enable_group_superuser must be rejected."""
self.actor.assign_perms_to_managed_role("authentik_core.view_user")
self.actor.assign_perms_to_managed_role("authentik_core.change_user", self.target)
group = Group.objects.create(name=generate_id(), is_superuser=True)
res = self._patch({"groups": [str(group.pk)]})
self.assertEqual(res.status_code, 400)
def test_patch_superuser_group_with_perm(self):
"""Assigning a superuser group with enable_group_superuser must succeed."""
self.actor.assign_perms_to_managed_role("authentik_core.view_user")
self.actor.assign_perms_to_managed_role("authentik_core.change_user", self.target)
self.actor.assign_perms_to_managed_role("authentik_core.enable_group_superuser")
group = Group.objects.create(name=generate_id(), is_superuser=True)
res = self._patch({"groups": [str(group.pk)]})
self.assertEqual(res.status_code, 200)
def test_patch_non_superuser_group_no_perm(self):
"""Assigning a non-superuser group without special permission must succeed."""
self.actor.assign_perms_to_managed_role("authentik_core.view_user")
self.actor.assign_perms_to_managed_role("authentik_core.change_user", self.target)
group = Group.objects.create(name=generate_id(), is_superuser=False)
res = self._patch({"groups": [str(group.pk)]})
self.assertEqual(res.status_code, 200)
def test_patch_existing_superuser_group_no_perm(self):
"""Keeping an existing superuser group membership without the permission must succeed."""
self.actor.assign_perms_to_managed_role("authentik_core.view_user")
self.actor.assign_perms_to_managed_role("authentik_core.change_user", self.target)
group = Group.objects.create(name=generate_id(), is_superuser=True)
self.target.groups.add(group)
res = self._patch({"groups": [str(group.pk)]})
self.assertEqual(res.status_code, 200)
def test_patch_role_no_perm(self):
"""Assigning a new role without change_role must be rejected."""
self.actor.assign_perms_to_managed_role("authentik_core.view_user")
self.actor.assign_perms_to_managed_role("authentik_core.change_user", self.target)
role = Role.objects.create(name=generate_id())
res = self._patch({"roles": [str(role.pk)]})
self.assertEqual(res.status_code, 400)
def test_patch_role_with_perm(self):
"""Assigning a new role with change_role must succeed."""
self.actor.assign_perms_to_managed_role("authentik_core.view_user")
self.actor.assign_perms_to_managed_role("authentik_core.change_user", self.target)
self.actor.assign_perms_to_managed_role("authentik_rbac.change_role")
role = Role.objects.create(name=generate_id())
res = self._patch({"roles": [str(role.pk)]})
self.assertEqual(res.status_code, 200)
def test_patch_existing_role_no_perm(self):
"""Keeping an existing role without change_role must succeed."""
self.actor.assign_perms_to_managed_role("authentik_core.view_user")
self.actor.assign_perms_to_managed_role("authentik_core.change_user", self.target)
role = Role.objects.create(name=generate_id())
self.target.roles.add(role)
res = self._patch({"roles": [str(role.pk)]})
self.assertEqual(res.status_code, 200)

View File

@@ -7,7 +7,7 @@ from drf_spectacular.utils import OpenApiParameter, OpenApiResponse, extend_sche
from rest_framework.decorators import action
from rest_framework.exceptions import PermissionDenied, ValidationError
from rest_framework.fields import ChoiceField
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.permissions import IsAuthenticated
from rest_framework.relations import PrimaryKeyRelatedField
from rest_framework.request import Request
from rest_framework.response import Response
@@ -44,6 +44,7 @@ from authentik.stages.password.stage import PLAN_CONTEXT_METHOD, PLAN_CONTEXT_ME
class AgentConnectorSerializer(ConnectorSerializer):
class Meta(ConnectorSerializer.Meta):
model = AgentConnector
fields = ConnectorSerializer.Meta.fields + [
@@ -62,6 +63,7 @@ class AgentConnectorSerializer(ConnectorSerializer):
class MDMConfigSerializer(PassiveSerializer):
platform = ChoiceField(choices=OSFamily.choices)
enrollment_token = PrimaryKeyRelatedField(
queryset=EnrollmentToken.objects.including_expired().all()
@@ -87,6 +89,7 @@ class AgentConnectorViewSet(
UsedByMixin,
ModelViewSet,
):
queryset = AgentConnector.objects.all()
serializer_class = AgentConnectorSerializer
search_fields = ["name"]
@@ -118,8 +121,6 @@ class AgentConnectorViewSet(
methods=["POST"],
detail=False,
authentication_classes=[AgentEnrollmentAuth],
# Permissions are handled via AgentEnrollmentAuth
permission_classes=[AllowAny],
)
def enroll(self, request: Request):
token: EnrollmentToken = request.auth
@@ -150,13 +151,7 @@ class AgentConnectorViewSet(
request=OpenApiTypes.NONE,
responses=AgentConfigSerializer(),
)
@action(
methods=["GET"],
detail=False,
authentication_classes=[AgentAuth],
# Permissions are handled via AgentAuth
permission_classes=[AllowAny],
)
@action(methods=["GET"], detail=False, authentication_classes=[AgentAuth])
def agent_config(self, request: Request):
token: DeviceToken = request.auth
connector: AgentConnector = token.device.connector.agentconnector
@@ -170,13 +165,7 @@ class AgentConnectorViewSet(
request=DeviceFacts(),
responses={204: OpenApiResponse(description="Successfully checked in")},
)
@action(
methods=["POST"],
detail=False,
authentication_classes=[AgentAuth],
# Permissions are handled via AgentAuth
permission_classes=[AllowAny],
)
@action(methods=["POST"], detail=False, authentication_classes=[AgentAuth])
def check_in(self, request: Request):
token: DeviceToken = request.auth
data = DeviceFacts(data=request.data)

View File

@@ -1,6 +1,5 @@
from typing import Any
from django.db.models import Model
from django.http import HttpRequest
from django.utils.timezone import now
from drf_spectacular.extensions import OpenApiAuthenticationExtension
@@ -10,7 +9,7 @@ from rest_framework.exceptions import PermissionDenied
from rest_framework.request import Request
from structlog.stdlib import get_logger
from authentik.api.authentication import VirtualUser, validate_auth
from authentik.api.authentication import IPCUser, validate_auth
from authentik.core.middleware import CTX_AUTH_VIA
from authentik.core.models import User
from authentik.crypto.apps import MANAGED_KEY
@@ -26,18 +25,9 @@ LOGGER = get_logger()
PLATFORM_ISSUER = "goauthentik.io/platform"
class DeviceUser(VirtualUser):
class DeviceUser(IPCUser):
username = "authentik:endpoints:device"
def has_perm(self, perm: str, obj: Model | None = None) -> bool:
if perm in [
"authentik_core.view_user",
"authentik_core.view_group",
]:
return True
return False
class AgentEnrollmentAuth(BaseAuthentication):

View File

@@ -223,17 +223,3 @@ class TestAgentAPI(APITestCase):
data={"platform": OSFamily.macOS, "enrollment_token": self.token.pk},
)
self.assertEqual(res.status_code, 200)
def test_users_list(self):
response = self.client.get(
reverse("authentik_api:user-list"),
HTTP_AUTHORIZATION=f"Bearer+agent {self.device_token.key}",
)
self.assertEqual(response.status_code, 200)
def test_other_api_forbidden(self):
response = self.client.get(
reverse("authentik_api:application-list"),
HTTP_AUTHORIZATION=f"Bearer+agent {self.device_token.key}",
)
self.assertEqual(response.status_code, 403)

View File

@@ -2,7 +2,6 @@ from django.urls import reverse
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema
from rest_framework.decorators import action
from rest_framework.permissions import AllowAny
from rest_framework.request import Request
from rest_framework.response import Response
from structlog.stdlib import get_logger
@@ -26,13 +25,7 @@ class AgentConnectorViewSetMixin:
request=OpenApiTypes.NONE,
responses=AgentAuthenticationResponse(),
)
@action(
methods=["POST"],
detail=False,
authentication_classes=[AgentAuth],
# Permissions are handled via AgentAuth
permission_classes=[AllowAny],
)
@action(methods=["POST"], detail=False, authentication_classes=[AgentAuth])
@enterprise_action
def auth_ia(self, request: Request) -> Response:
token: DeviceToken = request.auth

View File

@@ -1,72 +1,14 @@
from datetime import datetime
from django.urls import reverse
from django.utils.translation import gettext as _
from rest_framework.exceptions import ValidationError
from authentik.enterprise.license import LicenseKey
from authentik.providers.scim.models import SCIMAuthenticationMode, SCIMProvider
from authentik.sources.oauth.models import UserOAuthSourceConnection
from authentik.providers.scim.models import SCIMAuthenticationMode
class SCIMProviderSerializerMixin:
def _get_token(self, instance: SCIMProvider) -> UserOAuthSourceConnection | None:
user = instance.auth_oauth_user
conn = UserOAuthSourceConnection.objects.filter(
user=user, source=instance.auth_oauth
).first()
return conn
def get_auth_oauth_token_last_updated(self, instance: SCIMProvider) -> datetime | None:
conn = self._get_token(instance)
return conn.last_updated if conn else None
def get_auth_oauth_token_expires(self, instance: SCIMProvider) -> datetime | None:
conn = self._get_token(instance)
return conn.expires if conn else None
def get_auth_oauth_url_callback(self, instance: SCIMProvider) -> str | None:
if (
instance.auth_mode
in [
SCIMAuthenticationMode.TOKEN,
SCIMAuthenticationMode.OAUTH_SILENT,
]
or not instance.backchannel_application
):
return None
relative_url = reverse(
"authentik_enterprise_providers_scim:callback",
kwargs={"application_slug": instance.backchannel_application.slug},
)
if "request" not in self.context:
return relative_url
return self.context["request"].build_absolute_uri(relative_url)
def get_auth_oauth_url_start(self, instance: SCIMProvider) -> str | None:
if (
instance.auth_mode
in [
SCIMAuthenticationMode.TOKEN,
SCIMAuthenticationMode.OAUTH_SILENT,
]
or not instance.backchannel_application
):
return None
relative_url = reverse(
"authentik_enterprise_providers_scim:start",
kwargs={"application_slug": instance.backchannel_application.slug},
)
if "request" not in self.context:
return relative_url
return self.context["request"].build_absolute_uri(relative_url)
def validate_auth_mode(self, auth_mode: SCIMAuthenticationMode) -> SCIMAuthenticationMode:
if auth_mode in [
SCIMAuthenticationMode.OAUTH_SILENT,
SCIMAuthenticationMode.OAUTH_INTERACTIVE,
]:
if auth_mode == SCIMAuthenticationMode.OAUTH:
if not LicenseKey.cached_summary().status.is_valid:
raise ValidationError(_("Enterprise is required to use the OAuth mode."))
return auth_mode

View File

@@ -7,4 +7,3 @@ class AuthentikEnterpriseProviderSCIMConfig(EnterpriseConfig):
label = "authentik_enterprise_providers_scim"
verbose_name = "authentik Enterprise.Providers.SCIM"
default = True
mountpoint = "application/scim/"

View File

@@ -1,14 +1,12 @@
from datetime import timedelta
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING
from django.utils.timezone import now
from requests import Request, RequestException
from structlog.stdlib import get_logger
from authentik.common.oauth.constants import GRANT_TYPE_PASSWORD, GRANT_TYPE_REFRESH_TOKEN
from authentik.providers.scim.clients.exceptions import SCIMRequestException
from authentik.providers.scim.models import SCIMAuthenticationMode
from authentik.sources.oauth.clients.base import BaseOAuthClient
from authentik.sources.oauth.clients.oauth2 import OAuth2Client
from authentik.sources.oauth.models import OAuthSource, UserOAuthSourceConnection
if TYPE_CHECKING:
@@ -20,26 +18,23 @@ class SCIMOAuthException(SCIMRequestException):
class SCIMOAuthAuth:
def __init__(self, provider: SCIMProvider):
self.provider = provider
self.user = provider.auth_oauth_user
self.logger = get_logger().bind()
self.connection = self.get_connection()
def retrieve_token(self, conn: UserOAuthSourceConnection | None) -> dict[str, Any]:
def retrieve_token(self):
if not self.provider.auth_oauth:
return None
source: OAuthSource = self.provider.auth_oauth
client: BaseOAuthClient = source.source_type.callback_view(request=None).get_client(source)
client = OAuth2Client(source, None)
access_token_url = source.source_type.access_token_url or ""
if source.source_type.urls_customizable and source.access_token_url:
access_token_url = source.access_token_url
data = client.get_access_token_args(None, None)
if self.provider.auth_mode == SCIMAuthenticationMode.OAUTH_SILENT:
data["grant_type"] = GRANT_TYPE_PASSWORD
elif self.provider.auth_mode == SCIMAuthenticationMode.OAUTH_INTERACTIVE:
data["grant_type"] = GRANT_TYPE_REFRESH_TOKEN
if not conn:
raise SCIMOAuthException(None, "Could not refresh SCIM OAuth token")
data["refresh_token"] = conn.refresh_token
data["grant_type"] = "password"
data.update(self.provider.auth_oauth_params)
try:
response = client.do_request(
@@ -59,14 +54,12 @@ class SCIMOAuthAuth:
raise SCIMOAuthException(exc.response, message="Failed to get OAuth token") from exc
def get_connection(self):
if not self.provider.auth_oauth:
return None
conn = UserOAuthSourceConnection.objects.filter(
source=self.provider.auth_oauth, user=self.user
token = UserOAuthSourceConnection.objects.filter(
source=self.provider.auth_oauth, user=self.user, expires__gt=now()
).first()
if conn and conn.access_token and conn.expires > now():
return conn
token = self.retrieve_token(conn)
if token and token.access_token:
return token
token = self.retrieve_token()
access_token = token["access_token"]
expires_in = int(token.get("expires_in", 0))
token, _ = UserOAuthSourceConnection.objects.update_or_create(
@@ -74,10 +67,7 @@ class SCIMOAuthAuth:
user=self.user,
defaults={
"access_token": access_token,
"refresh_token": token.get("refresh_token"),
"expires": now() + timedelta(seconds=expires_in),
# When using `update_or_create`, `last_updated` is not updated
"last_updated": now(),
},
)
return token

View File

@@ -14,10 +14,7 @@ def scim_provider_post_save(sender: type[Model], instance: SCIMProvider, created
"""Create service account before provider is saved"""
identifier = f"ak-providers-scim-{instance.pk}"
with audit_ignore():
if instance.auth_mode in [
SCIMAuthenticationMode.OAUTH_SILENT,
SCIMAuthenticationMode.OAUTH_INTERACTIVE,
]:
if instance.auth_mode == SCIMAuthenticationMode.OAUTH:
user, user_created = User.objects.update_or_create(
username=identifier,
defaults={

View File

@@ -2,7 +2,7 @@
from base64 import b64encode
from datetime import timedelta
from urllib.parse import parse_qs, urlencode, urlparse
from unittest.mock import MagicMock, PropertyMock, patch
from django.urls import reverse
from django.utils.timezone import now
@@ -11,14 +11,17 @@ from rest_framework.test import APITestCase
from authentik.blueprints.tests import apply_blueprint
from authentik.core.models import Application, Group, User
from authentik.core.tests.utils import create_test_admin_user
from authentik.enterprise.license import LicenseKey
from authentik.enterprise.models import License
from authentik.enterprise.tests.test_license import expiry_valid
from authentik.lib.generators import generate_id
from authentik.providers.scim.models import SCIMAuthenticationMode, SCIMMapping, SCIMProvider
from authentik.sources.oauth.models import OAuthSource, UserOAuthSourceConnection
from authentik.tenants.models import Tenant
from tests.live import create_test_admin_user
class TestSCIMOAuthToken(APITestCase):
class SCIMOAuthTests(APITestCase):
"""SCIM User tests"""
@apply_blueprint("system/providers-scim.yaml")
@@ -39,7 +42,7 @@ class TestSCIMOAuthToken(APITestCase):
self.provider = SCIMProvider.objects.create(
name=generate_id(),
url="https://localhost",
auth_mode=SCIMAuthenticationMode.OAUTH_SILENT,
auth_mode=SCIMAuthenticationMode.OAUTH,
auth_oauth=self.source,
auth_oauth_params={
"foo": "bar",
@@ -57,9 +60,8 @@ class TestSCIMOAuthToken(APITestCase):
self.provider.property_mappings_group.add(
SCIMMapping.objects.get(managed="goauthentik.io/providers/scim/group")
)
self.admin = create_test_admin_user()
def test_retrieve_token_silent(self):
def test_retrieve_token(self):
"""Test token retrieval"""
with Mocker() as mocker:
token = generate_id()
@@ -84,44 +86,6 @@ class TestSCIMOAuthToken(APITestCase):
)
self.assertEqual(mocker.request_history[0].body, "grant_type=password&foo=bar")
def test_retrieve_token_interactive(self):
"""Test token retrieval"""
self.provider.auth_mode = SCIMAuthenticationMode.OAUTH_INTERACTIVE
self.provider.save()
refresh_token = generate_id()
access_token = generate_id()
UserOAuthSourceConnection.objects.create(
user=self.provider.auth_oauth_user,
source=self.source,
refresh_token=refresh_token,
access_token=access_token,
)
with Mocker() as mocker:
token = generate_id()
mocker.post("http://localhost/token", json={"access_token": token, "expires_in": 3600})
self.provider.scim_auth()
conn = UserOAuthSourceConnection.objects.filter(
source=self.source,
user=self.provider.auth_oauth_user,
).first()
self.assertIsNotNone(conn)
self.assertTrue(conn.is_valid)
auth = (
b64encode(
b":".join((self.source.consumer_key.encode(), self.source.consumer_secret.encode()))
)
.strip()
.decode()
)
self.assertEqual(
mocker.request_history[0].headers["Authorization"],
f"Basic {auth}",
)
self.assertEqual(
mocker.request_history[0].body,
f"grant_type=refresh_token&refresh_token={refresh_token}&foo=bar",
)
def test_existing_token(self):
"""Test existing token"""
UserOAuthSourceConnection.objects.create(
@@ -134,54 +98,96 @@ class TestSCIMOAuthToken(APITestCase):
self.provider.scim_auth()
self.assertEqual(len(mocker.request_history), 0)
def test_interactive_start(self):
self.client.force_login(self.admin)
res = self.client.get(
reverse(
"authentik_enterprise_providers_scim:start",
kwargs={
"application_slug": self.app.slug,
@Mocker()
def test_user_create(self, mock: Mocker):
"""Test user creation"""
scim_id = generate_id()
token = generate_id()
mock.post("http://localhost/token", json={"access_token": token, "expires_in": 3600})
mock.get(
"https://localhost/ServiceProviderConfig",
json={},
)
mock.post(
"https://localhost/Users",
json={
"id": scim_id,
},
)
uid = generate_id()
user = User.objects.create(
username=uid,
name=f"{uid} {uid}",
email=f"{uid}@goauthentik.io",
)
self.assertEqual(mock.call_count, 3)
self.assertEqual(mock.request_history[1].method, "GET")
self.assertEqual(mock.request_history[2].method, "POST")
self.assertJSONEqual(
mock.request_history[2].body,
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"active": True,
"emails": [
{
"primary": True,
"type": "other",
"value": f"{uid}@goauthentik.io",
}
],
"externalId": user.uid,
"name": {
"familyName": uid,
"formatted": f"{uid} {uid}",
"givenName": uid,
},
)
"displayName": f"{uid} {uid}",
"userName": uid,
},
)
self.assertEqual(res.status_code, 302)
query = parse_qs(urlparse(res.url).query)
self.assertEqual(query["client_id"], [self.source.consumer_key])
self.assertEqual(
query["redirect_uri"],
[f"http://testserver/application/scim/{self.app.slug}/oauth2/callback/"],
)
self.assertEqual(query["response_type"], ["code"])
def test_interactive_callback(self):
self.client.force_login(self.admin)
res = self.client.get(
reverse(
"authentik_enterprise_providers_scim:start",
kwargs={
"application_slug": self.app.slug,
},
@patch(
"authentik.enterprise.license.LicenseKey.validate",
MagicMock(
return_value=LicenseKey(
aud="",
exp=expiry_valid,
name=generate_id(),
internal_users=100,
external_users=100,
)
),
)
def test_api_create(self):
License.objects.create(key=generate_id())
self.client.force_login(create_test_admin_user())
res = self.client.post(
reverse("authentik_api:scimprovider-list"),
{
"name": generate_id(),
"url": "http://localhost",
"auth_mode": "oauth",
"auth_oauth": str(self.source.pk),
},
)
self.assertEqual(res.status_code, 302)
query = parse_qs(urlparse(res.url).query)
self.assertEqual(res.status_code, 201)
with Mocker() as mock:
token = generate_id()
mock.post("http://localhost/token", json={"access_token": token, "expires_in": 3600})
res = self.client.get(
reverse(
"authentik_enterprise_providers_scim:callback",
kwargs={
"application_slug": self.app.slug,
},
)
+ "?"
+ urlencode({"state": query["state"][0], "code": generate_id()})
)
self.assertEqual(res.status_code, 302)
conn = UserOAuthSourceConnection.objects.filter(source=self.source).first()
self.assertIsNotNone(conn)
self.assertTrue(conn.is_valid)
@patch(
"authentik.enterprise.models.LicenseUsageStatus.is_valid",
PropertyMock(return_value=False),
)
def test_api_create_no_license(self):
self.client.force_login(create_test_admin_user())
res = self.client.post(
reverse("authentik_api:scimprovider-list"),
{
"name": generate_id(),
"url": "http://localhost",
"auth_mode": "oauth",
"auth_oauth": str(self.source.pk),
},
)
self.assertEqual(res.status_code, 400)
self.assertJSONEqual(
res.content, {"auth_mode": ["Enterprise is required to use the OAuth mode."]}
)

View File

@@ -1,73 +0,0 @@
"""SCIM OAuth tests"""
from unittest.mock import MagicMock, PropertyMock, patch
from django.urls import reverse
from rest_framework.test import APITestCase
from authentik.core.tests.utils import create_test_admin_user
from authentik.enterprise.license import LicenseKey
from authentik.enterprise.models import License
from authentik.enterprise.tests.test_license import expiry_valid
from authentik.lib.generators import generate_id
from authentik.sources.oauth.models import OAuthSource
class TestSCIMOAuthAPI(APITestCase):
"""SCIM User tests"""
def setUp(self):
self.source = OAuthSource.objects.create(
name=generate_id(),
slug=generate_id(),
access_token_url="http://localhost/token", # nosec
consumer_key=generate_id(),
consumer_secret=generate_id(),
provider_type="openidconnect",
)
@patch(
"authentik.enterprise.license.LicenseKey.validate",
MagicMock(
return_value=LicenseKey(
aud="",
exp=expiry_valid,
name=generate_id(),
internal_users=100,
external_users=100,
)
),
)
def test_api_create(self):
License.objects.create(key=generate_id())
self.client.force_login(create_test_admin_user())
res = self.client.post(
reverse("authentik_api:scimprovider-list"),
{
"name": generate_id(),
"url": "http://localhost",
"auth_mode": "oauth",
"auth_oauth": str(self.source.pk),
},
)
self.assertEqual(res.status_code, 201)
@patch(
"authentik.enterprise.models.LicenseUsageStatus.is_valid",
PropertyMock(return_value=False),
)
def test_api_create_no_license(self):
self.client.force_login(create_test_admin_user())
res = self.client.post(
reverse("authentik_api:scimprovider-list"),
{
"name": generate_id(),
"url": "http://localhost",
"auth_mode": "oauth",
"auth_oauth": str(self.source.pk),
},
)
self.assertEqual(res.status_code, 400)
self.assertJSONEqual(
res.content, {"auth_mode": ["Enterprise is required to use the OAuth mode."]}
)

View File

@@ -1,100 +0,0 @@
"""SCIM OAuth tests"""
from requests_mock import Mocker
from rest_framework.test import APITestCase
from authentik.blueprints.tests import apply_blueprint
from authentik.core.models import Application, Group, User
from authentik.lib.generators import generate_id
from authentik.providers.scim.models import SCIMAuthenticationMode, SCIMMapping, SCIMProvider
from authentik.sources.oauth.models import OAuthSource
from authentik.tenants.models import Tenant
class TestSCIMOAuthAuth(APITestCase):
"""SCIM User tests"""
@apply_blueprint("system/providers-scim.yaml")
def setUp(self) -> None:
# Delete all users and groups as the mocked HTTP responses only return one ID
# which will cause errors with multiple users
Tenant.objects.update(avatars="none")
User.objects.all().exclude_anonymous().delete()
Group.objects.all().delete()
self.source = OAuthSource.objects.create(
name=generate_id(),
slug=generate_id(),
access_token_url="http://localhost/token", # nosec
consumer_key=generate_id(),
consumer_secret=generate_id(),
provider_type="openidconnect",
)
self.provider = SCIMProvider.objects.create(
name=generate_id(),
url="https://localhost",
auth_mode=SCIMAuthenticationMode.OAUTH_SILENT,
auth_oauth=self.source,
auth_oauth_params={
"foo": "bar",
},
exclude_users_service_account=True,
)
self.app: Application = Application.objects.create(
name=generate_id(),
slug=generate_id(),
)
self.app.backchannel_providers.add(self.provider)
self.provider.property_mappings.add(
SCIMMapping.objects.get(managed="goauthentik.io/providers/scim/user")
)
self.provider.property_mappings_group.add(
SCIMMapping.objects.get(managed="goauthentik.io/providers/scim/group")
)
@Mocker()
def test_user_create(self, mock: Mocker):
"""Test user creation"""
scim_id = generate_id()
token = generate_id()
mock.post("http://localhost/token", json={"access_token": token, "expires_in": 3600})
mock.get(
"https://localhost/ServiceProviderConfig",
json={},
)
mock.post(
"https://localhost/Users",
json={
"id": scim_id,
},
)
uid = generate_id()
user = User.objects.create(
username=uid,
name=f"{uid} {uid}",
email=f"{uid}@goauthentik.io",
)
self.assertEqual(mock.call_count, 3)
self.assertEqual(mock.request_history[1].method, "GET")
self.assertEqual(mock.request_history[2].method, "POST")
self.assertJSONEqual(
mock.request_history[2].body,
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"active": True,
"emails": [
{
"primary": True,
"type": "other",
"value": f"{uid}@goauthentik.io",
}
],
"externalId": user.uid,
"name": {
"familyName": uid,
"formatted": f"{uid} {uid}",
"givenName": uid,
},
"displayName": f"{uid} {uid}",
"userName": uid,
},
)

View File

@@ -1,10 +0,0 @@
from django.urls import path
from authentik.enterprise.providers.scim.views import SCIMOAuthStart, SCIMRedirectCallback
urlpatterns = [
path("<slug:application_slug>/oauth2/start/", SCIMOAuthStart.as_view(), name="start"),
path(
"<slug:application_slug>/oauth2/callback/", SCIMRedirectCallback.as_view(), name="callback"
),
]

View File

@@ -1,70 +0,0 @@
from datetime import timedelta
from django.core.exceptions import PermissionDenied
from django.http import HttpRequest
from django.shortcuts import redirect
from django.urls import reverse
from django.utils.timezone import now
from authentik.core.models import Application
from authentik.providers.scim.models import SCIMProvider
from authentik.sources.oauth.clients.base import BaseOAuthClient
from authentik.sources.oauth.models import OAuthSource, UserOAuthSourceConnection
from authentik.sources.oauth.types.registry import RequestKind, registry
from authentik.sources.oauth.views.callback import OAuthCallback
from authentik.sources.oauth.views.redirect import OAuthRedirect
class SCIMOAuthViewMixin:
provider: SCIMProvider
def get_client(self, source: OAuthSource, **kwargs) -> BaseOAuthClient:
source: OAuthSource = self.provider.auth_oauth
source_cls = registry.find(source.provider_type, kind=RequestKind.CALLBACK)
if not source_cls.client_class:
return super().get_client(source, **kwargs)
return source_cls.client_class(source, self.request, **kwargs)
def _get_scim_provider(self, app_slug: str):
app = Application.objects.filter(slug=app_slug).first()
if not app:
return None
provider = SCIMProvider.objects.filter(backchannel_application=app)
return provider.first()
def dispatch(self, request: HttpRequest, application_slug: str):
if not request.user.is_authenticated:
raise PermissionDenied()
provider = self._get_scim_provider(application_slug)
if not provider or not provider.auth_oauth:
raise PermissionDenied()
if not request.user.has_perm(
"authentik_providers_scim.change_scimprovider",
provider,
):
raise PermissionDenied()
self.provider = provider
return super().dispatch(request, source_slug=provider.auth_oauth.slug)
class SCIMOAuthStart(SCIMOAuthViewMixin, OAuthRedirect):
def get_callback_url(self, source: OAuthSource):
return reverse("authentik_enterprise_providers_scim:callback", kwargs=self.kwargs)
class SCIMRedirectCallback(SCIMOAuthViewMixin, OAuthCallback):
def redirect_flow_manager(self, client: BaseOAuthClient):
expires_in = int(self.token.get("expires_in", 0))
UserOAuthSourceConnection.objects.update_or_create(
source=self.provider.auth_oauth,
user=self.provider.auth_oauth_user,
defaults={
"access_token": self.token.get("access_token"),
"refresh_token": self.token.get("refresh_token"),
"expires": now() + timedelta(seconds=expires_in),
},
)
return redirect("authentik_core:if-admin")

View File

@@ -1,5 +1,4 @@
from dataclasses import dataclass
from urllib.parse import urlparse
from django.http import HttpRequest
from django.shortcuts import get_object_or_404
@@ -56,9 +55,7 @@ class SignInRequest:
_, provider = req.get_app_provider()
if not req.wreply:
req.wreply = provider.acs_url
reply = urlparse(req.wreply)
configured = urlparse(provider.acs_url)
if not (reply[:2] == configured[:2] and reply.path.startswith(configured.path)):
if not req.wreply.startswith(provider.acs_url):
raise ValueError("Invalid wreply")
return req

View File

@@ -1,5 +1,4 @@
from dataclasses import dataclass
from urllib.parse import urlparse
from django.http import HttpRequest
from django.shortcuts import get_object_or_404
@@ -33,9 +32,7 @@ class SignOutRequest:
_, provider = req.get_app_provider()
if not req.wreply:
req.wreply = provider.acs_url
reply = urlparse(req.wreply)
configured = urlparse(provider.acs_url)
if not (reply[:2] == configured[:2] and reply.path.startswith(configured.path)):
if not req.wreply.startswith(provider.acs_url):
raise ValueError("Invalid wreply")
return req

View File

@@ -27,27 +27,12 @@ class TestWSFedSignIn(TestCase):
name=generate_id(),
authorization_flow=self.flow,
signing_kp=self.cert,
acs_url="https://t.goauthentik.io",
audience="foo",
)
self.app = Application.objects.create(
name=generate_id(), slug=generate_id(), provider=self.provider
)
self.factory = RequestFactory()
def test_wreply(self):
request = self.factory.get(
"/?wreply=https://t.goauthentik.io/foo&wa=wsignin1.0&wtrealm=foo",
user=get_anonymous_user(),
)
SignInRequest.parse(request)
with self.assertRaises(ValueError):
request = self.factory.get(
"/?wreply=https://t.goauthentik.io.invalid.com&wa=wsignin1.0&wtrealm=foo",
user=get_anonymous_user(),
)
SignInRequest.parse(request)
def test_token_gen(self):
request = self.factory.get("/", user=get_anonymous_user())
proc = SignInProcessor(

View File

@@ -11,9 +11,7 @@ from authentik.events.models import NotificationRule
class NotificationRuleSerializer(ModelSerializer):
"""NotificationRule Serializer"""
destination_group_obj = GroupSerializer(
read_only=True, source="destination_group", required=False, allow_null=True
)
destination_group_obj = GroupSerializer(read_only=True, source="destination_group")
class Meta:
model = NotificationRule

View File

@@ -9,10 +9,10 @@ from rest_framework.fields import CharField, ListField, SerializerMethodField
from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework.viewsets import GenericViewSet
from authentik.core.api.providers import ProviderSerializer
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.users import UserSerializer
from authentik.core.api.utils import MetaNameSerializer, ModelSerializer
from authentik.providers.oauth2.api.providers import OAuth2ProviderSerializer
from authentik.providers.oauth2.models import AccessToken, AuthorizationCode, RefreshToken
@@ -20,7 +20,7 @@ class ExpiringBaseGrantModelSerializer(ModelSerializer, MetaNameSerializer):
"""Serializer for BaseGrantModel and ExpiringBaseGrant"""
user = UserSerializer()
provider = ProviderSerializer()
provider = OAuth2ProviderSerializer()
scope = ListField(child=CharField())
class Meta:

View File

@@ -61,11 +61,6 @@ class SAMLProviderSerializer(ProviderSerializer):
url_download_metadata = SerializerMethodField()
url_issuer = SerializerMethodField()
# Unified SAML endpoint (primary)
url_unified = SerializerMethodField()
url_unified_init = SerializerMethodField()
# Legacy endpoints (for backward compatibility)
url_sso_post = SerializerMethodField()
url_sso_redirect = SerializerMethodField()
url_sso_init = SerializerMethodField()
@@ -102,21 +97,6 @@ class SAMLProviderSerializer(ProviderSerializer):
if "request" not in self._context:
return DEFAULT_ISSUER
request: HttpRequest = self._context["request"]._request
try:
return request.build_absolute_uri(
reverse(
"authentik_providers_saml:metadata-download",
kwargs={"application_slug": instance.application.slug},
)
)
except Provider.application.RelatedObjectDoesNotExist:
return DEFAULT_ISSUER
def get_url_unified(self, instance: SAMLProvider) -> str:
"""Get unified SAML endpoint URL (handles SSO and SLO)"""
if "request" not in self._context:
return ""
request: HttpRequest = self._context["request"]._request
try:
return request.build_absolute_uri(
reverse(
@@ -125,22 +105,7 @@ class SAMLProviderSerializer(ProviderSerializer):
)
)
except Provider.application.RelatedObjectDoesNotExist:
return "-"
def get_url_unified_init(self, instance: SAMLProvider) -> str:
"""Get IdP-initiated SAML URL"""
if "request" not in self._context:
return ""
request: HttpRequest = self._context["request"]._request
try:
return request.build_absolute_uri(
reverse(
"authentik_providers_saml:init",
kwargs={"application_slug": instance.application.slug},
)
)
except Provider.application.RelatedObjectDoesNotExist:
return "-"
return DEFAULT_ISSUER
def get_url_sso_post(self, instance: SAMLProvider) -> str:
"""Get SSO Post URL"""
@@ -278,8 +243,6 @@ class SAMLProviderSerializer(ProviderSerializer):
"default_name_id_policy",
"url_download_metadata",
"url_issuer",
"url_unified",
"url_unified_init",
"url_sso_post",
"url_sso_redirect",
"url_sso_init",

View File

@@ -241,7 +241,7 @@ class SAMLProvider(Provider):
"""Use IDP-Initiated SAML flow as launch URL"""
try:
return reverse(
"authentik_providers_saml:init",
"authentik_providers_saml:sso-init",
kwargs={"application_slug": self.application.slug},
)
except Provider.application.RelatedObjectDoesNotExist:

View File

@@ -147,7 +147,7 @@ class AssertionProcessor:
return self.http_request.build_absolute_uri(
reverse(
"authentik_providers_saml:metadata-download",
"authentik_providers_saml:base",
kwargs={"application_slug": self.provider.application.slug},
)
)

View File

@@ -48,7 +48,7 @@ class MetadataProcessor:
return self.http_request.build_absolute_uri(
reverse(
"authentik_providers_saml:metadata-download",
"authentik_providers_saml:base",
kwargs={"application_slug": self.provider.application.slug},
)
)
@@ -81,35 +81,54 @@ class MetadataProcessor:
element.text = name_id_format
yield element
def _get_unified_url(self) -> str:
"""Get the unified SAML endpoint URL"""
return self.http_request.build_absolute_uri(
reverse(
"authentik_providers_saml:base",
kwargs={"application_slug": self.provider.application.slug},
)
)
def get_sso_bindings(self) -> Iterator[Element]:
"""Get all SSO Bindings - both point to unified endpoint"""
unified_url = self._get_unified_url()
for binding in [SAML_BINDING_REDIRECT, SAML_BINDING_POST]:
"""Get all Bindings supported"""
binding_url_map = {
(SAML_BINDING_REDIRECT, "SingleSignOnService"): self.http_request.build_absolute_uri(
reverse(
"authentik_providers_saml:sso-redirect",
kwargs={"application_slug": self.provider.application.slug},
)
),
(SAML_BINDING_POST, "SingleSignOnService"): self.http_request.build_absolute_uri(
reverse(
"authentik_providers_saml:sso-post",
kwargs={"application_slug": self.provider.application.slug},
)
),
}
for binding_svc, url in binding_url_map.items():
binding, svc = binding_svc
if self.force_binding and self.force_binding != binding:
continue
element = Element(f"{{{NS_SAML_METADATA}}}SingleSignOnService")
element = Element(f"{{{NS_SAML_METADATA}}}{svc}")
element.attrib["Binding"] = binding
element.attrib["Location"] = unified_url
element.attrib["Location"] = url
yield element
def get_slo_bindings(self) -> Iterator[Element]:
"""Get all SLO Bindings - both point to unified endpoint"""
unified_url = self._get_unified_url()
for binding in [SAML_BINDING_REDIRECT, SAML_BINDING_POST]:
"""Get all Bindings supported"""
binding_url_map = {
(SAML_BINDING_REDIRECT, "SingleLogoutService"): self.http_request.build_absolute_uri(
reverse(
"authentik_providers_saml:slo-redirect",
kwargs={"application_slug": self.provider.application.slug},
)
),
(SAML_BINDING_POST, "SingleLogoutService"): self.http_request.build_absolute_uri(
reverse(
"authentik_providers_saml:slo-post",
kwargs={"application_slug": self.provider.application.slug},
)
),
}
for binding_svc, url in binding_url_map.items():
binding, svc = binding_svc
if self.force_binding and self.force_binding != binding:
continue
element = Element(f"{{{NS_SAML_METADATA}}}SingleLogoutService")
element = Element(f"{{{NS_SAML_METADATA}}}{svc}")
element.attrib["Binding"] = binding
element.attrib["Location"] = unified_url
element.attrib["Location"] = url
yield element
def _prepare_signature(self, entity_descriptor: _Element):

View File

@@ -4,26 +4,19 @@ from django.urls import path
from authentik.providers.saml.api.property_mappings import SAMLPropertyMappingViewSet
from authentik.providers.saml.api.providers import SAMLProviderViewSet
from authentik.providers.saml.views import metadata, sso, unified
from authentik.providers.saml.views import metadata, sso
from authentik.providers.saml.views.sp_slo import (
SPInitiatedSLOBindingPOSTView,
SPInitiatedSLOBindingRedirectView,
)
urlpatterns = [
# Unified Endpoint - handles SSO and SLO based on message type
# Base path for Issuer/Entity ID
path(
"<slug:application_slug>/",
unified.SAMLUnifiedView.as_view(),
sso.SAMLSSOBindingRedirectView.as_view(),
name="base",
),
# IdP-initiated
path(
"<slug:application_slug>/init/",
sso.SAMLSSOBindingInitView.as_view(),
name="init",
),
# LEGACY Endpoints (backward compatibility)
# SSO Bindings
path(
"<slug:application_slug>/sso/binding/redirect/",

View File

@@ -1,118 +0,0 @@
"""Unified SAML endpoint - handles SSO and SLO based on message type"""
from base64 import b64decode
from defusedxml.lxml import fromstring
from django.http import HttpRequest, HttpResponse
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.clickjacking import xframe_options_sameorigin
from django.views.decorators.csrf import csrf_exempt
from structlog.stdlib import get_logger
from authentik.common.saml.constants import NS_MAP
from authentik.flows.views.executor import SESSION_KEY_POST
from authentik.lib.views import bad_request_message
from authentik.providers.saml.utils.encoding import decode_base64_and_inflate
from authentik.providers.saml.views.flows import (
REQUEST_KEY_SAML_REQUEST,
REQUEST_KEY_SAML_RESPONSE,
)
from authentik.providers.saml.views.sp_slo import (
SPInitiatedSLOBindingPOSTView,
SPInitiatedSLOBindingRedirectView,
)
from authentik.providers.saml.views.sso import (
SAMLSSOBindingPOSTView,
SAMLSSOBindingRedirectView,
)
LOGGER = get_logger()
# SAML message type constants
SAML_MESSAGE_TYPE_AUTHN_REQUEST = "AuthnRequest"
SAML_MESSAGE_TYPE_LOGOUT_REQUEST = "LogoutRequest"
def detect_saml_message_type(saml_request: str, is_post_binding: bool) -> str | None:
"""Parse SAML request to determine if AuthnRequest or LogoutRequest."""
try:
if is_post_binding:
decoded_xml = b64decode(saml_request.encode())
else:
decoded_xml = decode_base64_and_inflate(saml_request)
root = fromstring(decoded_xml)
if len(root.xpath("//samlp:AuthnRequest", namespaces=NS_MAP)):
return SAML_MESSAGE_TYPE_AUTHN_REQUEST
if len(root.xpath("//samlp:LogoutRequest", namespaces=NS_MAP)):
return SAML_MESSAGE_TYPE_LOGOUT_REQUEST
return None
except Exception: # noqa: BLE001
return None
@method_decorator(xframe_options_sameorigin, name="dispatch")
@method_decorator(csrf_exempt, name="dispatch")
class SAMLUnifiedView(View):
"""Unified SAML endpoint - handles SSO and SLO based on message type.
The operation type is determined by parsing
the incoming SAML message:
- AuthnRequest -> SSO flow (delegates to SAMLSSOBindingRedirectView/POSTView)
- LogoutRequest -> SLO flow (delegates to SPInitiatedSLOBindingRedirectView/POSTView)
- LogoutResponse -> SLO completion (delegates to SPInitiatedSLOBindingRedirectView/POSTView)
"""
def dispatch(self, request: HttpRequest, application_slug: str) -> HttpResponse:
"""Route the request based on SAML message type."""
# ak user was not logged in, redirected to login, and is back w POST payload in session
if SESSION_KEY_POST in request.session:
return self._delegate_to_sso(request, application_slug, is_post_binding=True)
# Determine binding from HTTP method
is_post_binding = request.method == "POST"
data = request.POST if is_post_binding else request.GET
# LogoutResponse - delegate to SLO view (handles it in dispatch)
if REQUEST_KEY_SAML_RESPONSE in data:
return self._delegate_to_slo(request, application_slug, is_post_binding)
# Check for SAML request
if REQUEST_KEY_SAML_REQUEST not in data:
LOGGER.info("SAML payload missing")
return bad_request_message(request, "The SAML request payload is missing.")
# Detect message type and delegate
saml_request = data[REQUEST_KEY_SAML_REQUEST]
message_type = detect_saml_message_type(saml_request, is_post_binding)
if message_type == SAML_MESSAGE_TYPE_AUTHN_REQUEST:
return self._delegate_to_sso(request, application_slug, is_post_binding)
elif message_type == SAML_MESSAGE_TYPE_LOGOUT_REQUEST:
return self._delegate_to_slo(request, application_slug, is_post_binding)
else:
LOGGER.warning("Unknown SAML message type", message_type=message_type)
return bad_request_message(
request, f"Unsupported SAML message type: {message_type or 'unknown'}"
)
def _delegate_to_sso(
self, request: HttpRequest, application_slug: str, is_post_binding: bool
) -> HttpResponse:
"""Delegate to the appropriate SSO view."""
if is_post_binding:
view = SAMLSSOBindingPOSTView.as_view()
else:
view = SAMLSSOBindingRedirectView.as_view()
return view(request, application_slug=application_slug)
def _delegate_to_slo(
self, request: HttpRequest, application_slug: str, is_post_binding: bool
) -> HttpResponse:
"""Delegate to the appropriate SLO view."""
if is_post_binding:
view = SPInitiatedSLOBindingPOSTView.as_view()
else:
view = SPInitiatedSLOBindingRedirectView.as_view()
return view(request, application_slug=application_slug)

View File

@@ -1,6 +1,5 @@
"""SCIM Provider API Views"""
from rest_framework.fields import SerializerMethodField
from rest_framework.viewsets import ModelViewSet
from authentik.core.api.providers import ProviderSerializer
@@ -17,11 +16,6 @@ class SCIMProviderSerializer(
):
"""SCIMProvider Serializer"""
auth_oauth_token_last_updated = SerializerMethodField()
auth_oauth_token_expires = SerializerMethodField()
auth_oauth_url_callback = SerializerMethodField()
auth_oauth_url_start = SerializerMethodField()
class Meta:
model = SCIMProvider
fields = [
@@ -41,10 +35,6 @@ class SCIMProviderSerializer(
"auth_mode",
"auth_oauth",
"auth_oauth_params",
"auth_oauth_token_last_updated",
"auth_oauth_token_expires",
"auth_oauth_url_callback",
"auth_oauth_url_start",
"compatibility_mode",
"service_provider_config_cache_timeout",
"exclude_users_service_account",

View File

@@ -102,16 +102,4 @@ class Migration(migrations.Migration):
verbose_name="SCIM Compatibility Mode",
),
),
migrations.AlterField(
model_name="scimprovider",
name="auth_mode",
field=models.TextField(
choices=[
("token", "Token"),
("oauth", "OAuth (Silent)"),
("oauth_interactive", "OAuth (interactive)"),
],
default="token",
),
),
]

View File

@@ -72,8 +72,7 @@ class SCIMAuthenticationMode(models.TextChoices):
"""SCIM authentication modes"""
TOKEN = "token", _("Token")
OAUTH_SILENT = "oauth", _("OAuth (Silent)")
OAUTH_INTERACTIVE = "oauth_interactive", _("OAuth (interactive)")
OAUTH = "oauth", _("OAuth")
class SCIMCompatibilityMode(models.TextChoices):
@@ -145,10 +144,7 @@ class SCIMProvider(OutgoingSyncProvider, BackchannelProvider):
)
def scim_auth(self) -> AuthBase:
if self.auth_mode in [
SCIMAuthenticationMode.OAUTH_SILENT,
SCIMAuthenticationMode.OAUTH_INTERACTIVE,
]:
if self.auth_mode == SCIMAuthenticationMode.OAUTH:
try:
from authentik.enterprise.providers.scim.auth_oauth2 import SCIMOAuthAuth

View File

@@ -1,5 +1,6 @@
"""Source type manager"""
from collections.abc import Callable
from enum import Enum
from typing import Any
@@ -113,7 +114,7 @@ class SourceTypeRegistry:
)
return found_type
def find(self, type_name: str, kind: RequestKind) -> type[OAuthCallback | OAuthRedirect]:
def find(self, type_name: str, kind: RequestKind) -> Callable:
"""Find fitting Source Type"""
found_type = self.find_type(type_name)
if kind == RequestKind.CALLBACK:

View File

@@ -15,7 +15,6 @@ from structlog.stdlib import get_logger
from authentik.core.sources.flow_manager import SourceFlowManager
from authentik.events.models import Event, EventAction
from authentik.sources.oauth.clients.base import BaseOAuthClient
from authentik.sources.oauth.models import (
GroupOAuthSourceConnection,
OAuthSource,
@@ -30,7 +29,7 @@ class OAuthCallback(OAuthClientMixin, View):
"Base OAuth callback view."
source: OAuthSource
token: dict[str, Any] | None = None
token: dict | None = None
def dispatch(self, request: HttpRequest, *_, **kwargs) -> HttpResponse:
"""View Get handler"""
@@ -50,31 +49,20 @@ class OAuthCallback(OAuthClientMixin, View):
if "error" in self.token:
return self.handle_login_failure(self.token["error"])
# Fetch profile info
try:
res = self.redirect_flow_manager(client)
except ValueError as exc:
# if we're authenticated and not in a source stage and this new flag is enabled,
# just continue
if self.request.user.is_authenticated:
pass
return self.handle_login_failure(exc.args[0])
return res
def redirect_flow_manager(self, client: BaseOAuthClient) -> HttpResponse:
try:
raw_info = client.get_profile_info(self.token)
if raw_info is None:
raise ValueError("Could not retrieve profile.")
return self.handle_login_failure("Could not retrieve profile.")
except JSONDecodeError as exc:
Event.new(
EventAction.CONFIGURATION_ERROR,
message="Failed to JSON-decode profile.",
raw_profile=exc.doc,
).from_http(self.request)
raise ValueError("Could not retrieve profile.") from None
return self.handle_login_failure("Could not retrieve profile.")
identifier = self.get_user_id(info=raw_info)
if identifier is None:
raise ValueError("Could not determine id.")
return self.handle_login_failure("Could not determine id.")
sfm = OAuthSourceFlowManager(
source=self.source,
request=self.request,

View File

@@ -1,7 +1,6 @@
"""authentik saml source processor"""
from base64 import b64decode
from datetime import UTC, datetime
from time import mktime
from typing import TYPE_CHECKING
@@ -41,7 +40,6 @@ from authentik.sources.saml.exceptions import (
InvalidSignature,
MismatchedRequestID,
MissingSAMLResponse,
SAMLException,
UnsupportedNameIDFormat,
)
from authentik.sources.saml.models import (
@@ -97,7 +95,6 @@ class ResponseProcessor:
self._verify_request_id()
self._verify_status()
self._verify_conditions()
def _decrypt_response(self):
"""Decrypt SAMLResponse EncryptedAssertion Element"""
@@ -129,20 +126,6 @@ class ResponseProcessor:
)
self._assertion = decrypted_assertion
def _verify_conditions(self):
conditions = self.get_assertion().find(f"{{{NS_SAML_ASSERTION}}}Conditions")
if conditions is None:
return
_now = now()
before = conditions.attrib.get("NotBefore")
if before:
if datetime.fromisoformat(before).replace(tzinfo=UTC) > _now:
raise SAMLException("Assertion is not valid yet or expired.")
on_or_after = conditions.attrib.get("NotOnOrAfter")
if on_or_after:
if datetime.fromisoformat(on_or_after).replace(tzinfo=UTC) < _now:
raise SAMLException("Assertion is not valid yet or expired.")
def _verify_signature(self, signature_node: _Element):
"""Verify a single signature node"""
xmlsec.tree.add_ids(self._root, ["ID"])
@@ -232,9 +215,10 @@ class ResponseProcessor:
user has an attribute that refers to our Source for cleanup. The user is also deleted
on logout and periodically."""
# Create a temporary User
name_id_el, name_id = self._get_name_id()
name_id = self._get_name_id()
username = name_id.text
# trim username to ensure it is max 150 chars
username = f"ak-{name_id[: USERNAME_MAX_LENGTH - 14]}-transient"
username = f"ak-{username[: USERNAME_MAX_LENGTH - 14]}-transient"
expiry = mktime(
(now() + timedelta_from_string(self._source.temporary_user_delete_after)).timetuple()
)
@@ -250,18 +234,20 @@ class ResponseProcessor:
},
path=self._source.get_user_path(),
)
LOGGER.debug("Created temporary user for NameID Transient", username=name_id)
LOGGER.debug("Created temporary user for NameID Transient", username=name_id.text)
user.set_unusable_password()
user.save()
UserSAMLSourceConnection.objects.create(source=self._source, user=user, identifier=name_id)
UserSAMLSourceConnection.objects.create(
source=self._source, user=user, identifier=name_id.text
)
return SAMLSourceFlowManager(
source=self._source,
request=self._http_request,
identifier=str(name_id),
identifier=str(name_id.text),
user_info={
"root": self._root,
"assertion": self.get_assertion(),
"name_id": name_id_el,
"name_id": name_id,
},
policy_context={},
)
@@ -272,7 +258,7 @@ class ResponseProcessor:
return self._assertion
return self._root.find(f"{{{NS_SAML_ASSERTION}}}Assertion")
def _get_name_id(self) -> tuple[Element, str]:
def _get_name_id(self) -> Element:
"""Get NameID Element"""
assertion = self.get_assertion()
if assertion is None:
@@ -283,11 +269,12 @@ class ResponseProcessor:
name_id = subject.find(f"{{{NS_SAML_ASSERTION}}}NameID")
if name_id is None:
raise ValueError("NameID element not found")
return name_id, "".join(name_id.itertext())
return name_id
def _get_name_id_filter(self) -> dict[str, str]:
"""Returns the subject's NameID as a Filter for the `User`"""
name_id_el, name_id = self._get_name_id()
name_id_el = self._get_name_id()
name_id = name_id_el.text
if not name_id:
raise UnsupportedNameIDFormat("Subject's NameID is empty.")
_format = name_id_el.attrib["Format"]
@@ -308,26 +295,26 @@ class ResponseProcessor:
def prepare_flow_manager(self) -> SourceFlowManager:
"""Prepare flow plan depending on whether or not the user exists"""
name_id_el, name_id = self._get_name_id()
name_id = self._get_name_id()
# Sanity check, show a warning if NameIDPolicy doesn't match what we go
if self._source.name_id_policy != name_id_el.attrib["Format"]:
if self._source.name_id_policy != name_id.attrib["Format"]:
LOGGER.warning(
"NameID from IdP doesn't match our policy",
expected=self._source.name_id_policy,
got=name_id_el.attrib["Format"],
got=name_id.attrib["Format"],
)
# transient NameIDs are handled separately as they don't have to go through flows.
if name_id_el.attrib["Format"] == SAML_NAME_ID_FORMAT_TRANSIENT:
if name_id.attrib["Format"] == SAML_NAME_ID_FORMAT_TRANSIENT:
return self._handle_name_id_transient()
return SAMLSourceFlowManager(
source=self._source,
request=self._http_request,
identifier=str(name_id),
identifier=str(name_id.text),
user_info={
"root": self._root,
"assertion": self.get_assertion(),
"name_id": name_id_el,
"name_id": name_id,
},
policy_context={
"saml_response": etree.tostring(self._root),

View File

@@ -4,7 +4,6 @@ from base64 import b64encode
from defusedxml.lxml import fromstring
from django.test import TestCase
from freezegun import freeze_time
from authentik.common.saml.constants import NS_SAML_ASSERTION
from authentik.core.tests.utils import RequestFactory, create_test_flow
@@ -35,7 +34,6 @@ class TestPropertyMappings(TestCase):
pre_authentication_flow=create_test_flow(),
)
@freeze_time("2022-10-14T14:15:00")
def test_user_base_properties(self):
"""Test user base properties"""
properties = self.source.get_base_user_properties(
@@ -63,7 +61,6 @@ class TestPropertyMappings(TestCase):
properties = self.source.get_base_group_properties(root=ROOT, group_id=group_id)
self.assertEqual(properties, {"name": group_id})
@freeze_time("2022-10-14T14:15:00")
def test_user_property_mappings(self):
"""Test user property mappings"""
self.source.user_property_mappings.add(
@@ -97,7 +94,6 @@ class TestPropertyMappings(TestCase):
},
)
@freeze_time("2022-10-14T14:15:00")
def test_group_property_mappings(self):
"""Test group property mappings"""
self.source.group_property_mappings.add(

View File

@@ -3,7 +3,6 @@
from base64 import b64encode
from django.test import TestCase
from freezegun import freeze_time
from authentik.core.tests.utils import RequestFactory, create_test_cert, create_test_flow
from authentik.crypto.models import CertificateKeyPair
@@ -47,7 +46,6 @@ class TestResponseProcessor(TestCase):
):
ResponseProcessor(self.source, request).parse()
@freeze_time("2022-10-14T14:15:00")
def test_success(self):
"""Test success"""
request = self.factory.post(
@@ -74,7 +72,6 @@ class TestResponseProcessor(TestCase):
},
)
@freeze_time("2022-10-14T14:16:40Z")
def test_success_with_status_message_and_detail(self):
"""Test success with StatusMessage and StatusDetail present (should not raise error)"""
request = self.factory.post(
@@ -91,7 +88,6 @@ class TestResponseProcessor(TestCase):
sfm = parser.prepare_flow_manager()
self.assertEqual(sfm.user_properties["username"], "jens@goauthentik.io")
@freeze_time("2022-10-14T14:16:40Z")
def test_error_with_message_and_detail(self):
"""Test error status with StatusMessage and StatusDetail includes both in error"""
request = self.factory.post(
@@ -109,7 +105,6 @@ class TestResponseProcessor(TestCase):
self.assertIn("User account is disabled", str(ctx.exception))
self.assertIn("Authentication failed", str(ctx.exception))
@freeze_time("2024-08-07T15:48:09.325Z")
def test_encrypted_correct(self):
"""Test encrypted"""
key = load_fixture("fixtures/encrypted-key.pem")
@@ -147,7 +142,6 @@ class TestResponseProcessor(TestCase):
with self.assertRaises(InvalidEncryption):
parser.parse()
@freeze_time("2022-10-14T14:16:40Z")
def test_verification_assertion(self):
"""Test verifying signature inside assertion"""
key = load_fixture("fixtures/signature_cert.pem")
@@ -170,7 +164,6 @@ class TestResponseProcessor(TestCase):
parser = ResponseProcessor(self.source, request)
parser.parse()
@freeze_time("2014-07-17T01:02:18Z")
def test_verification_assertion_duplicate(self):
"""Test verifying signature inside assertion, where the response has another assertion
before our signed assertion"""
@@ -193,35 +186,9 @@ class TestResponseProcessor(TestCase):
parser = ResponseProcessor(self.source, request)
parser.parse()
self.assertNotEqual(parser._get_name_id()[1], "bad")
self.assertEqual(parser._get_name_id()[1], "_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7")
self.assertNotEqual(parser._get_name_id().text, "bad")
self.assertEqual(parser._get_name_id().text, "_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7")
@freeze_time("2022-10-14T14:15:00")
def test_name_id_comment(self):
"""Test comment in name ID"""
fixture = load_fixture("fixtures/response_signed_assertion_dup.xml")
fixture = fixture.replace(
"_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7",
"_ce3d2948b4cf20146dee0a0b3dd6f<!--x-->69b6cf86f62d7",
)
key = load_fixture("fixtures/signature_cert.pem")
kp = CertificateKeyPair.objects.create(
name=generate_id(),
certificate_data=key,
)
self.source.verification_kp = kp
self.source.signed_assertion = True
self.source.signed_response = False
request = self.factory.post(
"/",
data={"SAMLResponse": b64encode(fixture.encode()).decode()},
)
parser = ResponseProcessor(self.source, request)
parser.parse()
self.assertEqual(parser._get_name_id()[1], "_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7")
@freeze_time("2014-07-17T01:02:18Z")
def test_verification_response(self):
"""Test verifying signature inside response"""
key = load_fixture("fixtures/signature_cert.pem")
@@ -244,7 +211,6 @@ class TestResponseProcessor(TestCase):
parser = ResponseProcessor(self.source, request)
parser.parse()
@freeze_time("2024-01-18T06:20:48Z")
def test_verification_response_and_assertion(self):
"""Test verifying signature inside response and assertion"""
key = load_fixture("fixtures/signature_cert.pem")
@@ -291,7 +257,6 @@ class TestResponseProcessor(TestCase):
with self.assertRaisesMessage(InvalidSignature, ""):
parser.parse()
@freeze_time("2022-10-14T14:15:00")
def test_verification_no_signature(self):
"""Test rejecting response without signature when signed_assertion is True"""
key = load_fixture("fixtures/signature_cert.pem")
@@ -338,7 +303,6 @@ class TestResponseProcessor(TestCase):
with self.assertRaisesMessage(InvalidSignature, ""):
parser.parse()
@freeze_time("2025-10-30T05:45:47.619Z")
def test_signed_encrypted_response(self):
"""Test signed & encrypted response"""
verification_key = load_fixture("fixtures/signature_cert2.pem")
@@ -366,7 +330,6 @@ class TestResponseProcessor(TestCase):
parser = ResponseProcessor(self.source, request)
parser.parse()
@freeze_time("2026-01-21T14:23")
def test_transient(self):
"""Test SAML transient NameID"""
verification_key = load_fixture("fixtures/signature_cert2.pem")

View File

@@ -4,7 +4,6 @@ from base64 import b64encode
from django.test import RequestFactory, TestCase
from django.urls import reverse
from freezegun import freeze_time
from authentik.core.tests.utils import create_test_flow
from authentik.flows.planner import PLAN_CONTEXT_REDIRECT, FlowPlan
@@ -27,7 +26,6 @@ class TestViews(TestCase):
pre_authentication_flow=create_test_flow(),
)
@freeze_time("2022-10-14T14:15:00")
def test_enroll(self):
"""Enroll"""
flow = create_test_flow()
@@ -54,7 +52,6 @@ class TestViews(TestCase):
plan: FlowPlan = self.client.session.get(SESSION_KEY_PLAN)
self.assertIsNotNone(plan)
@freeze_time("2022-10-14T14:15:00")
def test_enroll_redirect(self):
"""Enroll when attempting to access a provider"""
initial_redirect = f"http://{generate_id()}"

View File

@@ -389,19 +389,17 @@ class ThrottlingMixin(models.Model):
"""Check if throttling is enabled"""
return self.get_throttle_factor() > 0
def get_throttle_factor(self) -> float: # pragma: no cover
def get_throttle_factor(self): # pragma: no cover
"""
Returns the throttling factor.
"""
return getattr(self, "_throttle_factor", 1.0)
def set_throttle_factor(self, throttle_factor: float) -> None:
"""
Sets the throttle factor to use. Call this to override the default value of 1.
This must be implemented to return the throttle factor.
The number of seconds required between verification attempts will be
:math:`c2^{n-1}` where `c` is this factor and `n` is the number of
previous failures. A factor of 1 translates to delays of 1, 2, 4, 8,
etc. seconds. A factor of 0 disables the throttling.
Normally this is just a wrapper for a plugin-specific setting like
:setting:`OTP_EMAIL_THROTTLE_FACTOR`.
"""
self._throttle_factor = throttle_factor
raise NotImplementedError()

View File

@@ -6,6 +6,7 @@ from threading import Thread
from django.contrib.auth.models import AnonymousUser
from django.db import connection
from django.test import TestCase, TransactionTestCase
from django.test.utils import override_settings
from django.utils import timezone
from freezegun import freeze_time
@@ -109,24 +110,8 @@ class ThrottlingTestMixin:
self.assertEqual(verify_is_allowed3, True)
self.assertEqual(data3, None)
def test_set_throttle_factor_is_reflected(self):
"""`set_throttle_factor` must drive `get_throttle_factor`."""
self.device.set_throttle_factor(5.5)
self.assertEqual(self.device.get_throttle_factor(), 5.5)
self.device.set_throttle_factor(0)
self.assertEqual(self.device.get_throttle_factor(), 0)
def test_throttling_disabled_by_factor_zero(self):
"""Setting the throttle factor to 0 must actually disable throttling.
A failed attempt followed by a successful one must succeed. The lockout
path must not kick in when the factor is 0.
"""
self.device.set_throttle_factor(0)
self.assertFalse(self.device.verify_token(self.invalid_token()))
self.assertTrue(self.device.verify_token(self.valid_token()))
@override_settings(OTP_STATIC_THROTTLE_FACTOR=0)
class APITestCase(TestCase):
"""Test API"""
@@ -134,7 +119,6 @@ class APITestCase(TestCase):
self.alice = create_test_admin_user("alice")
self.bob = create_test_admin_user("bob")
device = self.alice.staticdevice_set.create()
device.set_throttle_factor(0)
self.valid = generate_id(length=16)
device.token_set.create(token=self.valid)
@@ -154,8 +138,6 @@ class APITestCase(TestCase):
verified = verify_token(self.alice, device.persistent_id, "bogus")
self.assertIsNone(verified)
self.alice.staticdevice_set.get().throttle_reset()
verified = verify_token(self.alice, device.persistent_id, self.valid)
self.assertIsNotNone(verified)
@@ -164,12 +146,11 @@ class APITestCase(TestCase):
verified = match_token(self.alice, "bogus")
self.assertIsNone(verified)
self.alice.staticdevice_set.get().throttle_reset()
verified = match_token(self.alice, self.valid)
self.assertEqual(verified, self.alice.staticdevice_set.first())
@override_settings(OTP_STATIC_THROTTLE_FACTOR=0)
class ConcurrencyTestCase(TransactionTestCase):
"""Test concurrent verifications"""

View File

@@ -1,33 +0,0 @@
# Generated by Django 5.2.12 on 2026-04-02 15:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
(
"authentik_stages_authenticator_email",
"0002_alter_authenticatoremailstage_friendly_name",
),
]
operations = [
migrations.AddField(
model_name="emaildevice",
name="throttling_failure_count",
field=models.PositiveIntegerField(
default=0, help_text="Number of successive failed attempts."
),
),
migrations.AddField(
model_name="emaildevice",
name="throttling_failure_timestamp",
field=models.DateTimeField(
blank=True,
default=None,
help_text="A timestamp of the last failed verification attempt. Null if last attempt succeeded.",
null=True,
),
),
]

View File

@@ -14,7 +14,7 @@ from authentik.flows.models import ConfigurableStage, FriendlyNamedStage, Stage
from authentik.lib.config import CONFIG
from authentik.lib.models import SerializerModel
from authentik.lib.utils.time import timedelta_string_validator
from authentik.stages.authenticator.models import SideChannelDevice, ThrottlingMixin
from authentik.stages.authenticator.models import SideChannelDevice
from authentik.stages.email.models import EmailTemplates
from authentik.stages.email.utils import TemplateEmailMessage
@@ -116,7 +116,7 @@ class AuthenticatorEmailStage(ConfigurableStage, FriendlyNamedStage, Stage):
verbose_name_plural = _("Email Authenticator Setup Stages")
class EmailDevice(SerializerModel, ThrottlingMixin, SideChannelDevice):
class EmailDevice(SerializerModel, SideChannelDevice):
"""Email Device"""
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
@@ -130,20 +130,6 @@ class EmailDevice(SerializerModel, ThrottlingMixin, SideChannelDevice):
return EmailDeviceSerializer
def verify_token(self, token: str) -> bool:
verify_allowed, _ = self.verify_is_allowed()
if verify_allowed:
verified = super().verify_token(token)
if verified:
self.throttle_reset()
else:
self.throttle_increment()
else:
verified = False
return verified
def _compose_email(self) -> TemplateEmailMessage:
try:
pending_user = self.user

View File

@@ -8,7 +8,6 @@ from django.core.mail.backends.locmem import EmailBackend
from django.core.mail.backends.smtp import EmailBackend as SMTPEmailBackend
from django.db.utils import IntegrityError
from django.template.exceptions import TemplateDoesNotExist
from django.test import TestCase
from django.urls import reverse
from django.utils.timezone import now
@@ -17,7 +16,6 @@ from authentik.flows.models import FlowStageBinding
from authentik.flows.tests import FlowTestCase
from authentik.lib.config import CONFIG
from authentik.lib.utils.email import mask_email
from authentik.stages.authenticator.tests import ThrottlingTestMixin
from authentik.stages.authenticator_email.api import (
AuthenticatorEmailStageSerializer,
EmailDeviceSerializer,
@@ -81,7 +79,6 @@ class TestAuthenticatorEmailStage(FlowTestCase):
self.assertFalse(self.device.verify_token("000000"))
# Verify correct token (should clear token after verification)
self.device.throttle_reset(commit=False)
self.assertTrue(self.device.verify_token(token))
self.assertIsNone(self.device.token)
@@ -332,27 +329,3 @@ class TestAuthenticatorEmailStage(FlowTestCase):
# Test AuthenticatorEmailStage send method
self.stage.send(self.device)
self.assertEqual(len(mail.outbox), 1)
class TestEmailDeviceThrottling(ThrottlingTestMixin, TestCase):
def setUp(self):
super().setUp()
flow = create_test_flow()
user = create_test_user()
stage = AuthenticatorEmailStage.objects.create(
name="email-authenticator-throttle",
use_global_settings=True,
from_address="test@authentik.local",
configure_flow=flow,
token_expiry="minutes=30",
) # nosec
self.device = EmailDevice.objects.create(
user=user, stage=stage, email="throttle@authentik.local"
)
self.device.generate_token()
def valid_token(self):
return self.device.token
def invalid_token(self):
return "000000" if self.device.token != "000000" else "111111"

View File

@@ -1,30 +0,0 @@
# Generated by Django 5.2.12 on 2026-04-16 17:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_stages_authenticator_sms", "0008_alter_authenticatorsmsstage_friendly_name"),
]
operations = [
migrations.AddField(
model_name="smsdevice",
name="throttling_failure_count",
field=models.PositiveIntegerField(
default=0, help_text="Number of successive failed attempts."
),
),
migrations.AddField(
model_name="smsdevice",
name="throttling_failure_timestamp",
field=models.DateTimeField(
blank=True,
default=None,
help_text="A timestamp of the last failed verification attempt. Null if last attempt succeeded.",
null=True,
),
),
]

View File

@@ -20,7 +20,7 @@ from authentik.events.utils import sanitize_item
from authentik.flows.models import ConfigurableStage, FriendlyNamedStage, Stage
from authentik.lib.models import SerializerModel
from authentik.lib.utils.http import get_http_session
from authentik.stages.authenticator.models import SideChannelDevice, ThrottlingMixin
from authentik.stages.authenticator.models import SideChannelDevice
LOGGER = get_logger()
@@ -197,7 +197,7 @@ def hash_phone_number(phone_number: str) -> str:
return "hash:" + sha256(phone_number.encode()).hexdigest()
class SMSDevice(SerializerModel, ThrottlingMixin, SideChannelDevice):
class SMSDevice(SerializerModel, SideChannelDevice):
"""SMS Device"""
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
@@ -224,19 +224,11 @@ class SMSDevice(SerializerModel, ThrottlingMixin, SideChannelDevice):
return SMSDeviceSerializer
def verify_token(self, token: str) -> bool:
verify_allowed, _ = self.verify_is_allowed()
if verify_allowed:
verified = super().verify_token(token)
if verified:
self.throttle_reset()
else:
self.throttle_increment()
else:
verified = False
return verified
def verify_token(self, token):
valid = super().verify_token(token)
if valid:
self.save()
return valid
def __str__(self):
return str(self.name) or str(self.user_id)

View File

@@ -3,7 +3,6 @@
from unittest.mock import MagicMock, patch
from urllib.parse import parse_qsl
from django.test import TestCase
from django.urls import reverse
from requests_mock import Mocker
@@ -13,7 +12,6 @@ from authentik.flows.planner import FlowPlan
from authentik.flows.tests import FlowTestCase
from authentik.flows.views.executor import SESSION_KEY_PLAN
from authentik.lib.generators import generate_id
from authentik.stages.authenticator.tests import ThrottlingTestMixin
from authentik.stages.authenticator_sms.models import (
AuthenticatorSMSStage,
SMSDevice,
@@ -359,30 +357,3 @@ class AuthenticatorSMSStageTests(FlowTestCase):
},
phone_number_required=False,
)
class TestSMSDeviceThrottling(ThrottlingTestMixin, TestCase):
"""Test ThrottlingMixin behaviour on SMSDevice.verify_token"""
def setUp(self):
super().setUp()
flow = create_test_flow()
user = create_test_admin_user()
stage = AuthenticatorSMSStage.objects.create(
flow=flow,
name="sms-throttle",
provider=SMSProviders.GENERIC,
from_number="1234",
)
self.device = SMSDevice.objects.create(
user=user,
stage=stage,
phone_number="+15551230001",
)
self.device.generate_token()
def valid_token(self):
return self.device.token
def invalid_token(self):
return "000000" if self.device.token != "000000" else "111111"

View File

@@ -3,6 +3,7 @@
from base64 import b32encode
from os import urandom
from django.conf import settings
from django.core.validators import MaxValueValidator
from django.db import models
from django.utils.translation import gettext_lazy as _
@@ -77,6 +78,9 @@ class StaticDevice(SerializerModel, ThrottlingMixin, Device):
return StaticDeviceSerializer
def get_throttle_factor(self):
return getattr(settings, "OTP_STATIC_THROTTLE_FACTOR", 1)
def verify_token(self, token):
verify_allowed, _ = self.verify_is_allowed()
if verify_allowed:

View File

@@ -1,5 +1,6 @@
"""Test Static API"""
from django.test.utils import override_settings
from django.urls import reverse
from rest_framework.test import APITestCase
@@ -43,6 +44,9 @@ class DeviceTest(TestCase):
str(device)
@override_settings(
OTP_STATIC_THROTTLE_FACTOR=1,
)
class ThrottlingTestCase(ThrottlingTestMixin, TestCase):
"""Test static device throttling"""

View File

@@ -194,6 +194,9 @@ class TOTPDevice(SerializerModel, ThrottlingMixin, Device):
return verified
def get_throttle_factor(self):
return getattr(settings, "OTP_TOTP_THROTTLE_FACTOR", 1)
@property
def config_url(self):
"""

View File

@@ -63,14 +63,11 @@ class TOTPDeviceMixin:
@override_settings(
OTP_TOTP_SYNC=False,
OTP_TOTP_THROTTLE_FACTOR=0,
)
class TOTPTest(TOTPDeviceMixin, TestCase):
"""TOTP tests"""
def setUp(self):
super().setUp()
self.device.set_throttle_factor(0)
def test_default_key(self):
"""Ensure default_key is valid"""
device = self.alice.totpdevice_set.create()
@@ -193,6 +190,9 @@ class TOTPTest(TOTPDeviceMixin, TestCase):
self.assertEqual(params["image"][0], image_url)
@override_settings(
OTP_TOTP_THROTTLE_FACTOR=1,
)
class ThrottlingTestCase(TOTPDeviceMixin, ThrottlingTestMixin, TestCase):
"""Test TOTP Throttling"""

View File

@@ -39,10 +39,6 @@ class AuthenticatorValidateStageSerializer(StageSerializer):
"webauthn_hints",
"webauthn_allowed_device_types",
"webauthn_allowed_device_types_obj",
"email_otp_throttling_factor",
"sms_otp_throttling_factor",
"totp_otp_throttling_factor",
"static_otp_throttling_factor",
]

View File

@@ -3,7 +3,6 @@
from typing import TYPE_CHECKING
from urllib.parse import urlencode
from django.db import transaction
from django.http import HttpRequest
from django.http.response import Http404
from django.shortcuts import get_object_or_404
@@ -30,8 +29,8 @@ from authentik.flows.stage import StageView
from authentik.lib.utils.email import mask_email
from authentik.lib.utils.time import timedelta_from_string
from authentik.root.middleware import ClientIPMiddleware
from authentik.stages.authenticator import devices_for_user
from authentik.stages.authenticator.models import Device, ThrottlingMixin
from authentik.stages.authenticator import match_token
from authentik.stages.authenticator.models import Device
from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice
from authentik.stages.authenticator_email.models import EmailDevice
from authentik.stages.authenticator_sms.models import SMSDevice
@@ -144,20 +143,7 @@ def select_challenge_email(request: HttpRequest, device: EmailDevice):
def validate_challenge_code(code: str, stage_view: StageView, user: User) -> Device:
"""Validate code-based challenges. We test against every device, on purpose, as
the user mustn't choose between totp and static devices."""
with transaction.atomic():
for device in devices_for_user(user, for_verify=True):
if isinstance(device, ThrottlingMixin):
throttling_factor = stage_view.executor.current_stage.get_throttling_factor(
DeviceClasses.from_model_label(device.model_label())
)
if throttling_factor is not None:
device.set_throttle_factor(throttling_factor)
if device.verify_token(code):
break
else:
device = None
device = match_token(user, code)
if not device:
login_failed.send(
sender=__name__,

View File

@@ -1,36 +0,0 @@
# Generated by Django 5.2.12 on 2026-04-16 16:33
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
(
"authentik_stages_authenticator_validate",
"0015_authenticatorvalidatestage_webauthn_hints",
),
]
operations = [
migrations.AddField(
model_name="authenticatorvalidatestage",
name="email_otp_throttling_factor",
field=models.FloatField(default=1),
),
migrations.AddField(
model_name="authenticatorvalidatestage",
name="sms_otp_throttling_factor",
field=models.FloatField(default=1),
),
migrations.AddField(
model_name="authenticatorvalidatestage",
name="static_otp_throttling_factor",
field=models.FloatField(default=1),
),
migrations.AddField(
model_name="authenticatorvalidatestage",
name="totp_otp_throttling_factor",
field=models.FloatField(default=1),
),
]

View File

@@ -22,12 +22,6 @@ class DeviceClasses(models.TextChoices):
SMS = "sms", _("SMS")
EMAIL = "email", _("Email")
@staticmethod
def from_model_label(model_label: str) -> DeviceClasses:
return getattr(
DeviceClasses, model_label.rsplit(".", maxsplit=1)[-1][: -len("device")].upper()
)
def default_device_classes() -> list:
"""By default, accept all device classes"""
@@ -88,11 +82,6 @@ class AuthenticatorValidateStage(Stage):
"authentik_stages_authenticator_webauthn.WebAuthnDeviceType", blank=True
)
email_otp_throttling_factor = models.FloatField(default=1)
sms_otp_throttling_factor = models.FloatField(default=1)
totp_otp_throttling_factor = models.FloatField(default=1)
static_otp_throttling_factor = models.FloatField(default=1)
@property
def serializer(self) -> type[BaseSerializer]:
from authentik.stages.authenticator_validate.api import AuthenticatorValidateStageSerializer
@@ -109,17 +98,6 @@ class AuthenticatorValidateStage(Stage):
def component(self) -> str:
return "ak-stage-authenticator-validate-form"
def get_throttling_factor(self, device_class: DeviceClasses) -> float | None:
if device_class == DeviceClasses.EMAIL:
return self.email_otp_throttling_factor
elif device_class == DeviceClasses.SMS:
return self.sms_otp_throttling_factor
elif device_class == DeviceClasses.TOTP:
return self.totp_otp_throttling_factor
elif device_class == DeviceClasses.STATIC:
return self.static_otp_throttling_factor
return None
class Meta:
verbose_name = _("Authenticator Validation Stage")
verbose_name_plural = _("Authenticator Validation Stages")

View File

@@ -1,247 +0,0 @@
from django.test import TestCase
from django.test.client import RequestFactory
from django.urls.base import reverse
from rest_framework.exceptions import ValidationError
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
from authentik.flows.models import FlowStageBinding
from authentik.flows.stage import StageView
from authentik.flows.tests import FlowTestCase
from authentik.flows.views.executor import FlowExecutorView
from authentik.lib.generators import generate_id
from authentik.stages.authenticator_email.models import AuthenticatorEmailStage, EmailDevice
from authentik.stages.authenticator_sms.models import (
AuthenticatorSMSStage,
SMSDevice,
SMSProviders,
)
from authentik.stages.authenticator_validate.challenge import validate_challenge_code
from authentik.stages.authenticator_validate.models import (
AuthenticatorValidateStage,
DeviceClasses,
)
from authentik.stages.identification.models import IdentificationStage, UserFields
class DeviceClassesHelperTests(TestCase):
"""Tests for the DeviceClasses.from_model_label helper."""
def test_from_model_label_all_classes(self):
cases = {
"authentik_stages_authenticator_email.emaildevice": DeviceClasses.EMAIL,
"authentik_stages_authenticator_sms.smsdevice": DeviceClasses.SMS,
"authentik_stages_authenticator_totp.totpdevice": DeviceClasses.TOTP,
"authentik_stages_authenticator_static.staticdevice": DeviceClasses.STATIC,
"authentik_stages_authenticator_duo.duodevice": DeviceClasses.DUO,
"authentik_stages_authenticator_webauthn.webauthndevice": DeviceClasses.WEBAUTHN,
}
for label, expected in cases.items():
with self.subTest(label=label):
self.assertEqual(DeviceClasses.from_model_label(label), expected)
class AuthenticatorValidateStageFactorTests(TestCase):
"""Tests for AuthenticatorValidateStage.get_throttling_factor."""
def test_per_class_factors_returned(self):
stage = AuthenticatorValidateStage.objects.create(
name=generate_id(),
email_otp_throttling_factor=5,
sms_otp_throttling_factor=6,
totp_otp_throttling_factor=7,
static_otp_throttling_factor=8,
)
self.assertEqual(stage.get_throttling_factor(DeviceClasses.EMAIL), 5)
self.assertEqual(stage.get_throttling_factor(DeviceClasses.SMS), 6)
self.assertEqual(stage.get_throttling_factor(DeviceClasses.TOTP), 7)
self.assertEqual(stage.get_throttling_factor(DeviceClasses.STATIC), 8)
def test_no_factor_for_webauthn_or_duo(self):
stage = AuthenticatorValidateStage.objects.create(name=generate_id())
self.assertIsNone(stage.get_throttling_factor(DeviceClasses.WEBAUTHN))
self.assertIsNone(stage.get_throttling_factor(DeviceClasses.DUO))
class ValidateChallengeCodeThrottlingTests(FlowTestCase):
"""Tests for validate_challenge_code throttling behavior."""
def setUp(self) -> None:
super().setUp()
self.user = create_test_admin_user()
self.request_factory = RequestFactory()
self.email_stage = AuthenticatorEmailStage.objects.create(
name="email-stage-validate-throttle",
use_global_settings=True,
from_address="test@authentik.local",
token_expiry="minutes=30",
) # nosec
self.sms_stage = AuthenticatorSMSStage.objects.create(
name="sms-stage-validate-throttle",
provider=SMSProviders.GENERIC,
from_number="1234",
)
def _validate_stage(self, **factors) -> AuthenticatorValidateStage:
return AuthenticatorValidateStage.objects.create(
name=generate_id(),
device_classes=[
DeviceClasses.EMAIL,
DeviceClasses.SMS,
DeviceClasses.TOTP,
DeviceClasses.STATIC,
],
**factors,
)
def _stage_view(self, validate_stage: AuthenticatorValidateStage) -> StageView:
request = self.request_factory.get("/")
return StageView(FlowExecutorView(current_stage=validate_stage), request=request)
def _email_device(self, email: str = "throttle@authentik.local") -> EmailDevice:
return EmailDevice.objects.create(
user=self.user,
stage=self.email_stage,
confirmed=True,
email=email,
)
def _sms_device(self, phone_number: str = "+15551230101") -> SMSDevice:
return SMSDevice.objects.create(
user=self.user,
stage=self.sms_stage,
confirmed=True,
phone_number=phone_number,
)
def test_stage_factor_applied_to_email_device(self):
"""The stage's email_otp_throttling_factor is pushed onto the device before verify."""
stage = self._validate_stage(email_otp_throttling_factor=3)
device = self._email_device()
device.generate_token()
with self.assertRaises(ValidationError):
validate_challenge_code("000000", self._stage_view(stage), self.user)
device.refresh_from_db()
self.assertEqual(device.throttling_failure_count, 1)
# verify_is_allowed must compute the delay using factor=3 (3 * 2^0 = 3s).
device.set_throttle_factor(3)
allowed, data = device.verify_is_allowed()
self.assertFalse(allowed)
required = data["locked_until"] - device.throttling_failure_timestamp
self.assertAlmostEqual(required.total_seconds(), 3, places=3)
def test_factor_zero_disables_throttling_end_to_end(self):
"""With email_otp_throttling_factor=0, repeated failures do not lock the device."""
stage = self._validate_stage(email_otp_throttling_factor=0)
device = self._email_device()
device.generate_token()
token = device.token
for _ in range(10):
with self.assertRaises(ValidationError):
validate_challenge_code("000000", self._stage_view(stage), self.user)
matched = validate_challenge_code(token, self._stage_view(stage), self.user)
self.assertEqual(matched.pk, device.pk)
def test_lockout_persists_across_calls(self):
"""
A correct token on the second call is still blocked and does not increment the counter.
"""
stage = self._validate_stage(email_otp_throttling_factor=1)
device = self._email_device()
device.generate_token()
token = device.token
invalid_token = "000000" if token != "000000" else "111111" # nosec
with self.assertRaises(ValidationError):
validate_challenge_code(invalid_token, self._stage_view(stage), self.user)
# Immediately try with the correct token: lockout still active, attempt must be rejected.
with self.assertRaises(ValidationError):
validate_challenge_code(token, self._stage_view(stage), self.user)
device.refresh_from_db()
# Token wasn't consumed (verification never ran), and counter didn't get incremented.
self.assertEqual(device.token, token)
self.assertEqual(device.throttling_failure_count, 1)
class ValidateStageThrottlingFlowTests(FlowTestCase):
"""End-to-end lockout behavior through the flow executor HTTP API."""
def setUp(self) -> None:
super().setUp()
self.user = create_test_admin_user()
self.email_stage = AuthenticatorEmailStage.objects.create(
name="email-stage-flow-throttle",
use_global_settings=True,
from_address="test@authentik.local",
token_expiry="minutes=30",
) # nosec
self.ident_stage = IdentificationStage.objects.create(
name=generate_id(),
user_fields=[UserFields.USERNAME],
)
self.validate_stage = AuthenticatorValidateStage.objects.create(
name=generate_id(),
device_classes=[DeviceClasses.EMAIL],
email_otp_throttling_factor=1,
)
self.flow = create_test_flow()
FlowStageBinding.objects.create(target=self.flow, stage=self.ident_stage, order=0)
FlowStageBinding.objects.create(target=self.flow, stage=self.validate_stage, order=1)
def _identify(self):
response = self.client.post(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
{"uid_field": self.user.username},
follow=True,
)
self.assertEqual(response.status_code, 200)
def _select_email(self, device: EmailDevice):
self.client.post(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
{
"component": "ak-stage-authenticator-validate",
"selected_challenge": {
"device_class": "email",
"device_uid": str(device.pk),
"challenge": {},
"last_used": None,
},
},
)
def test_bad_code_then_correct_code_is_still_blocked(self):
"""After a bad code over HTTP, a subsequent correct code is still rejected
because the lockout persists in the database."""
device = EmailDevice.objects.create(
user=self.user,
confirmed=True,
stage=self.email_stage,
email="throttle-flow@authentik.local",
)
self._identify()
self._select_email(device)
# Server generated and stored the token - grab it from DB.
device.refresh_from_db()
token = device.token
# First attempt: bad code - must increment the DB counter.
self.client.post(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
{"component": "ak-stage-authenticator-validate", "code": "000000"},
)
device.refresh_from_db()
self.assertEqual(device.throttling_failure_count, 1)
self.assertEqual(device.token, token)
# Second attempt with the correct token - still blocked.
response = self.client.post(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
{"component": "ak-stage-authenticator-validate", "code": token},
)
self.assertStageResponse(
response,
flow=self.flow,
component="ak-stage-authenticator-validate",
)
device.refresh_from_db()
# Counter wasn't incremented on a blocked attempt
self.assertEqual(device.throttling_failure_count, 1)
# Token wasn't consumed.
self.assertEqual(device.token, token)

File diff suppressed because one or more lines are too long

View File

@@ -19,30 +19,24 @@ from authentik.tenants.models import Tenant
class FlagJSONField(JSONDictField):
def to_internal_value(self, data: str):
flags = super().to_internal_value(data)
for flag in Flag.available(visibility="system", exclude_system=False):
flags[flag().key] = flag.get()
return flags
def to_representation(self, value: dict) -> dict:
"""Exclude any system flags that aren't modifiable"""
new_value = value.copy()
for flag in Flag.available(exclude_system=False):
_flag = flag()
# Exclude any system flags that aren't modifiable
if _flag.visibility == "system":
new_value.pop(_flag.key, None)
# Explicitly present unset flags as if they were set to default
if _flag.key not in value:
value[_flag.key] = _flag.default
return super().to_representation(new_value)
def run_validators(self, value: dict):
super().run_validators(value)
for flag in Flag.available():
for flag in Flag.available(exclude_system=False):
_flag = flag()
if _flag.key not in value:
continue
if _flag.visibility == "system":
value.pop(_flag.key, None)
continue
flag_value = value.get(_flag.key)
flag_type = get_args(_flag.__orig_bases__[0])[0]
if flag_value and not isinstance(flag_value, flag_type):

View File

@@ -85,30 +85,10 @@ class TestLocalSettingsAPI(APITestCase):
"flags": {"tenants_test_flag_sys": 123},
},
)
print(response.content)
self.assertEqual(response.status_code, 200)
self.tenant.refresh_from_db()
self.assertEqual(self.tenant.flags, {"setup": False, "tenants_test_flag_sys": False})
def test_settings_flags_system_empty_put(self):
"""Test settings API"""
self.tenant.flags = {}
self.tenant.save()
class _TestFlag(Flag[bool], key="tenants_test_flag_sys"):
default = False
visibility = "system"
self.client.force_login(self.local_admin)
response = self.client.patch(
reverse("authentik_api:tenant_settings"),
data={
"flags": {},
},
)
self.assertEqual(response.status_code, 200)
self.tenant.refresh_from_db()
self.assertEqual(self.tenant.flags, {"setup": False, "tenants_test_flag_sys": False})
self.assertEqual(self.tenant.flags, {})
def test_command(self):
self.tenant.flags = {}

View File

@@ -36,10 +36,14 @@ entries:
attrs:
order: 50
initial_value: |
actor_uuid = str(getattr(http_request.user, "pk", ""))
pending_user = user if getattr(user, "is_authenticated", False) else None
target_uuid = str(getattr(pending_user, "pk", ""))
is_self_service = not target_uuid or target_uuid == actor_uuid
target_uuid = (http_request.session.get("authentik/flows/get", {}) or {}).get("user_uuid")
current_user_uuid = str(getattr(user, "pk", "") or getattr(http_request.user, "pk", ""))
is_self_service = not target_uuid or target_uuid == current_user_uuid
pending_user = None
if target_uuid and not is_self_service:
from authentik.core.models import User
pending_user = User.objects.filter(pk=target_uuid).first()
if is_self_service:
return (
"<p><strong>You are about to lock down your own account.</strong></p>"
@@ -59,15 +63,14 @@ entries:
from django.utils.html import escape
if pending_user:
detail = pending_user.email or pending_user.name
user_html = f"<code>{escape(pending_user.username)}</code>"
if detail and detail != pending_user.username:
user_html = f"{user_html} ({escape(detail)})"
email = escape(pending_user.email or pending_user.name or "No email")
user_html = f"<p><code>{escape(pending_user.username)}</code> ({email})</p>"
else:
user_html = "the account selected when this one-time lockdown link was created"
user_html = "<p>the account selected when this one-time lockdown link was created</p>"
return (
f"<p><strong>You are about to lock down the following account:</strong> {user_html}</p>"
"<p><strong>You are about to lock down the following account:</strong></p>"
f"{user_html}"
"<p>This is an emergency action for cutting off access to the account right away. "
"It does not lock the administrator who opened this page.</p>"
"<p><strong>This will immediately:</strong></p>"
@@ -96,9 +99,9 @@ entries:
attrs:
order: 100
initial_value: |
actor_uuid = str(getattr(http_request.user, "pk", ""))
target_uuid = str(getattr(user, "pk", ""))
is_self_service = not target_uuid or target_uuid == actor_uuid
target_uuid = (http_request.session.get("authentik/flows/get", {}) or {}).get("user_uuid")
current_user_uuid = str(getattr(user, "pk", "") or getattr(http_request.user, "pk", ""))
is_self_service = not target_uuid or target_uuid == current_user_uuid
if is_self_service:
info = (
"Use this if you no longer trust your current password or sessions. "
@@ -131,9 +134,9 @@ entries:
attrs:
order: 200
placeholder: |
actor_uuid = str(getattr(http_request.user, "pk", ""))
target_uuid = str(getattr(user, "pk", ""))
is_self_service = not target_uuid or target_uuid == actor_uuid
target_uuid = (http_request.session.get("authentik/flows/get", {}) or {}).get("user_uuid")
current_user_uuid = str(getattr(user, "pk", "") or getattr(http_request.user, "pk", ""))
is_self_service = not target_uuid or target_uuid == current_user_uuid
if is_self_service:
return "Describe why you are locking your account..."
return "Describe why this account is being locked down..."
@@ -181,10 +184,14 @@ entries:
attrs:
order: 300
initial_value: |
target_uuid = (http_request.session.get("authentik/flows/get", {}) or {}).get("user_uuid")
from django.utils.html import escape
from authentik.core.models import User
if getattr(user, "is_authenticated", False):
return f"<p><code>{escape(user.username)}</code> has been locked down.</p>"
if target_uuid:
target = User.objects.filter(pk=target_uuid).first()
if target:
return f"<p><code>{escape(target.username)}</code> has been locked down.</p>"
return "<p>The selected account has been locked down.</p>"
initial_value_expression: true
@@ -214,9 +221,9 @@ entries:
attrs:
name: default-account-lockdown-admin-policy
expression: |
actor_uuid = str(getattr(request.http_request.user, "pk", ""))
target_uuid = str(getattr(request.user, "pk", ""))
return bool(target_uuid) and target_uuid != actor_uuid
target_uuid = (request.http_request.session.get("authentik/flows/get", {}) or {}).get("user_uuid")
current_user_uuid = str(getattr(request.user, "pk", "") or getattr(request.http_request.user, "pk", ""))
return bool(target_uuid) and target_uuid != current_user_uuid
identifiers:
name: default-account-lockdown-admin-policy
id: admin-policy

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 2026.5.0-rc2 Blueprint schema",
"title": "authentik 2026.5.0-rc1 Blueprint schema",
"required": [
"version",
"entries"
@@ -11203,8 +11203,7 @@
"type": "string",
"enum": [
"token",
"oauth",
"oauth_interactive"
"oauth"
],
"title": "Auth mode"
},
@@ -14937,22 +14936,6 @@
"format": "uuid"
},
"title": "Webauthn allowed device types"
},
"email_otp_throttling_factor": {
"type": "number",
"title": "Email otp throttling factor"
},
"sms_otp_throttling_factor": {
"type": "number",
"title": "Sms otp throttling factor"
},
"totp_otp_throttling_factor": {
"type": "number",
"title": "Totp otp throttling factor"
},
"static_otp_throttling_factor": {
"type": "number",
"title": "Static otp throttling factor"
}
},
"required": []

View File

@@ -1 +1 @@
2026.5.0-rc2
2026.5.0-rc1

View File

@@ -110,6 +110,17 @@ func (a *Application) getTraefikForwardUrl(r *http.Request) (*url.URL, error) {
// getNginxForwardUrl See https://github.com/kubernetes/ingress-nginx/blob/main/rootfs/etc/nginx/template/nginx.tmpl
func (a *Application) getNginxForwardUrl(r *http.Request) (*url.URL, error) {
ou := r.Header.Get("X-Original-URI")
if ou != "" {
// Turn this full URL into a relative URL
u := &url.URL{
Host: "",
Scheme: "",
Path: ou,
}
a.log.WithField("url", u.String()).Info("building forward URL from X-Original-URI")
return u, nil
}
h := r.Header.Get("X-Original-URL")
if len(h) < 1 {
return nil, errors.New("no forward URL found")

View File

@@ -5,8 +5,10 @@ import (
"net/http/httptest"
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"goauthentik.io/internal/outpost/proxyv2/constants"
"goauthentik.io/internal/outpost/proxyv2/types"
api "goauthentik.io/packages/client-go"
)
@@ -45,6 +47,67 @@ func TestForwardHandleNginx_Single_Headers(t *testing.T) {
assert.Equal(t, "http://test.goauthentik.io/app", s.Values[constants.SessionRedirect])
}
func TestForwardHandleNginx_Single_URI(t *testing.T) {
a := newTestApplication()
req, _ := http.NewRequest("GET", "https://foo.bar/outpost.goauthentik.io/auth/nginx", nil)
req.Header.Set("X-Original-URI", "/app")
rr := httptest.NewRecorder()
a.forwardHandleNginx(rr, req)
assert.Equal(t, http.StatusUnauthorized, rr.Code)
s, _ := a.sessions.Get(req, a.SessionName())
assert.Equal(t, "/app", s.Values[constants.SessionRedirect])
}
func TestForwardHandleNginx_Single_Claims(t *testing.T) {
a := newTestApplication()
req, _ := http.NewRequest("GET", "/outpost.goauthentik.io/auth/nginx", nil)
req.Header.Set("X-Original-URI", "/")
rr := httptest.NewRecorder()
a.forwardHandleNginx(rr, req)
s, _ := a.sessions.Get(req, a.SessionName())
s.ID = uuid.New().String()
s.Options.MaxAge = 86400
s.Values[constants.SessionClaims] = types.Claims{
Sub: "foo",
Proxy: &types.ProxyClaims{
UserAttributes: map[string]any{
"username": "foo",
"password": "bar",
"additionalHeaders": map[string]any{
"foo": "bar",
},
},
},
}
err := a.sessions.Save(req, rr, s)
if err != nil {
panic(err)
}
rr = httptest.NewRecorder()
a.forwardHandleNginx(rr, req)
h := rr.Result().Header
assert.Equal(t, []string{"Basic Zm9vOmJhcg=="}, h["Authorization"])
assert.Equal(t, []string{"bar"}, h["Foo"])
assert.Equal(t, []string{""}, h["User-Agent"])
assert.Equal(t, []string{""}, h["X-Authentik-Email"])
assert.Equal(t, []string{""}, h["X-Authentik-Groups"])
assert.Equal(t, []string{""}, h["X-Authentik-Jwt"])
assert.Equal(t, []string{""}, h["X-Authentik-Meta-App"])
assert.Equal(t, []string{""}, h["X-Authentik-Meta-Jwks"])
assert.Equal(t, []string{""}, h["X-Authentik-Meta-Outpost"])
assert.Equal(t, []string{""}, h["X-Authentik-Name"])
assert.Equal(t, []string{"foo"}, h["X-Authentik-Uid"])
assert.Equal(t, []string{""}, h["X-Authentik-Username"])
}
func TestForwardHandleNginx_Domain_Blank(t *testing.T) {
a := newTestApplication()
a.proxyConfig.Mode = api.PROXYMODE_FORWARD_DOMAIN.Ptr()

View File

@@ -38,10 +38,6 @@ function run_authentik {
echo cargo run -- "$@"
fi
;;
manage)
shift 1
echo python -m manage "$@"
;;
*)
echo "$@"
;;

View File

@@ -9,7 +9,7 @@
"version": "0.0.0",
"license": "MIT",
"devDependencies": {
"aws-cdk": "^2.1120.0",
"aws-cdk": "^2.1119.0",
"cross-env": "^10.1.0"
},
"engines": {
@@ -25,9 +25,9 @@
"license": "MIT"
},
"node_modules/aws-cdk": {
"version": "2.1120.0",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1120.0.tgz",
"integrity": "sha512-vDVa0IX0FhizARdY/GLSParFglKbdHCIhM8IDmynrAv9w8uLLljzWMeLUOhC1XpMErDZ/npYEihAOjfKxTaMIw==",
"version": "2.1119.0",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1119.0.tgz",
"integrity": "sha512-XBxZEKH3BY4M1EX6x0qBkmOAj8viErjpww14iH6Z3z6nI0YzjZeJ05eEl7eJwzUgv7NTGagWBS9m/eDJW5+dAg==",
"dev": true,
"license": "Apache-2.0",
"bin": {

View File

@@ -7,7 +7,7 @@
"aws-cfn": "cross-env CI=false cdk synth --version-reporting=false > template.yaml"
},
"devDependencies": {
"aws-cdk": "^2.1120.0",
"aws-cdk": "^2.1119.0",
"cross-env": "^10.1.0"
},
"engines": {

View File

@@ -18,7 +18,7 @@ Parameters:
Description: authentik Docker image
AuthentikVersion:
Type: String
Default: 2026.5.0-rc2
Default: 2026.5.0-rc1
Description: authentik Docker image tag
AuthentikServerCPU:
Type: Number

View File

@@ -228,7 +228,8 @@ RUN apt-get update && \
# Required for runtime
apt-get install -y --no-install-recommends \
libpq5 libmaxminddb0 ca-certificates \
libkadm5clnt-mit12 libkadm5clnt7t64-heimdal \
krb5-multidev libkrb5-3 libkdb5-10 libkadm5clnt-mit12 \
heimdal-multidev libkadm5clnt7t64-heimdal \
libltdl7 libxslt1.1 && \
# Required for bootstrap & healtcheck
apt-get install -y --no-install-recommends runit && \

View File

@@ -31,7 +31,7 @@ services:
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?secret key required}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2026.5.0-rc2}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2026.5.0-rc1}
ports:
- ${COMPOSE_PORT_HTTP:-9000}:9000
- ${COMPOSE_PORT_HTTPS:-9443}:9443
@@ -53,7 +53,7 @@ services:
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?secret key required}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2026.5.0-rc2}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2026.5.0-rc1}
restart: unless-stopped
shm_size: 512mb
user: root

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -12,7 +12,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-05-06 00:27+0000\n"
"POT-Creation-Date: 2026-02-10 19:27+0000\n"
"PO-Revision-Date: 2025-12-01 19:09+0000\n"
"Last-Translator: Václav Nováček <waclaw661@gmail.com>, 2026\n"
"Language-Team: Czech (Czech Republic) (https://app.transifex.com/authentik/teams/119923/cs_CZ/)\n"
@@ -106,14 +106,6 @@ msgstr "Chyba validace"
msgid "Blueprint file does not exist"
msgstr "Soubor s konfigurační šablonou neexistuje"
#: authentik/blueprints/api.py
msgid "Context must be valid JSON"
msgstr ""
#: authentik/blueprints/api.py
msgid "Context must be a JSON object"
msgstr ""
#: authentik/blueprints/api.py
msgid "Failed to validate blueprint"
msgstr "Ověřování konfigurační šablony selhalo"
@@ -122,11 +114,6 @@ msgstr "Ověřování konfigurační šablony selhalo"
msgid "Either path or content must be set."
msgstr "Musí být nastavena buď cesta, nebo obsah."
#: authentik/blueprints/api.py
#, python-brace-format
msgid "User lacks permission to create {model}"
msgstr "Uživatel nemá oprávnění vytvořit {model}"
#: authentik/blueprints/models.py
msgid "Managed by authentik"
msgstr "Spravuje authentik"
@@ -257,13 +244,10 @@ msgstr ""
"pouze poskytovatele backchannel. Pokud je vypnuto, backchannel poskytovatelé"
" nejsou zahrnuti."
#: authentik/core/api/users.py
msgid "Invalid password hash format. Must be a valid Django password hash."
msgstr ""
#: authentik/core/api/users.py
msgid "Cannot set both password and password_hash. Use only one."
msgstr ""
#: authentik/core/api/transactional_applications.py
#, python-brace-format
msgid "User lacks permission to create {model}"
msgstr "Uživatel nemá oprávnění vytvořit {model}"
#: authentik/core/api/users.py
msgid "No leading or trailing slashes allowed."
@@ -325,12 +309,6 @@ msgstr ""
msgid "This field is required."
msgstr "Toto pole je povinné."
#: authentik/core/apps.py
msgid ""
"Configure if applications without any policy/group/user bindings should be "
"accessible to any user."
msgstr ""
#: authentik/core/models.py
msgid "name"
msgstr "Jméno"
@@ -437,10 +415,6 @@ msgstr "Interní název aplikace, používaný v URI."
msgid "Open launch URL in a new browser tab or window."
msgstr "Otevřít úvodní URL v novém okně nebo kartě prohlížeče."
#: authentik/core/models.py
msgid "Hide this application from the user's My applications page."
msgstr ""
#: authentik/core/models.py
msgid "Application"
msgstr "Aplikace"
@@ -632,14 +606,6 @@ msgstr "Odstranit dočasné uživatele vytvořené zdroji SAML."
msgid "Go home"
msgstr "Přejít domů"
#: authentik/core/templates/login/base_full.html
msgid "Site footer"
msgstr ""
#: authentik/core/templates/login/base_full.html
msgid "Flow links"
msgstr ""
#: authentik/core/templates/login/base_full.html
#: authentik/flows/templates/if/flow-sfe.html
msgid "Powered by authentik"
@@ -746,10 +712,6 @@ msgstr ""
msgid "Discover, import and update certificates from the filesystem."
msgstr "Objevit, importovat a aktualizovat certifikáty na souborovém systému."
#: authentik/endpoints/api/stages.py
msgid "Selected connector is not compatible with this stage."
msgstr ""
#: authentik/endpoints/connectors/agent/api/connectors.py
msgid "Selected platform not supported"
msgstr ""
@@ -804,14 +766,6 @@ msgstr ""
msgid "Apple Nonces"
msgstr ""
#: authentik/endpoints/connectors/agent/models.py
msgid "Apple Independent Secure Enclave"
msgstr ""
#: authentik/endpoints/connectors/agent/models.py
msgid "Apple Independent Secure Enclaves"
msgstr ""
#: authentik/endpoints/facts.py
msgid "Operating System name, such as 'Server 2022' or 'Ubuntu'"
msgstr ""
@@ -883,12 +837,6 @@ msgstr ""
msgid "Enterprise is required to use this endpoint."
msgstr ""
#: authentik/enterprise/audit/apps.py
msgid ""
"Include additional information in audit logs, may incur a performance "
"penalty."
msgstr ""
#: authentik/enterprise/endpoints/connectors/fleet/models.py
#: authentik/events/models.py
msgid ""
@@ -906,19 +854,6 @@ msgstr ""
msgid "Fleet Connectors"
msgstr ""
#: authentik/enterprise/endpoints/connectors/google_chrome/models.py
msgid "Google Device Trust Connector"
msgstr ""
#: authentik/enterprise/endpoints/connectors/google_chrome/models.py
msgid "Google Device Trust Connectors"
msgstr ""
#: authentik/enterprise/endpoints/connectors/google_chrome/stage.py
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/stage.py
msgid "Verifying your browser..."
msgstr "Ověřuji Váš prohlížeč..."
#: authentik/enterprise/lifecycle/api/reviews.py
msgid "You are not allowed to submit a review for this object."
msgstr ""
@@ -935,6 +870,10 @@ msgstr ""
msgid "Grace period must be shorter than the interval."
msgstr ""
#: authentik/enterprise/lifecycle/api/rules.py
msgid "Only one type-wide rule for each object type is allowed."
msgstr ""
#: authentik/enterprise/lifecycle/models.py
msgid ""
"Select which transports should be used to notify the reviewers. If none are "
@@ -962,8 +901,7 @@ msgid "Go to {self._get_model_name()}"
msgstr ""
#: authentik/enterprise/lifecycle/models.py
msgid ""
"Access review is due for {self.content_type.name.lower()} {object_label}"
msgid "Access review is due for {self.content_type.name} {str(self.object)}"
msgstr ""
#: authentik/enterprise/lifecycle/models.py
@@ -977,7 +915,7 @@ msgid ""
msgstr ""
#: authentik/enterprise/lifecycle/tasks.py
msgid "Dispatch tasks to apply lifecycle rules."
msgid "Dispatch tasks to validate lifecycle rules."
msgstr ""
#: authentik/enterprise/lifecycle/tasks.py
@@ -1220,14 +1158,6 @@ msgstr "Pro použití EAP-TLS je nutná Enterprise licence."
msgid "Enterprise is required to use the OAuth mode."
msgstr "Pro použití OAuth režimu je vyžadována Enterprise licence."
#: authentik/enterprise/providers/ssf/models.py
msgid "SSF RFC Push"
msgstr ""
#: authentik/enterprise/providers/ssf/models.py
msgid "SSF RFC Pull"
msgstr ""
#: authentik/enterprise/providers/ssf/models.py
#: authentik/providers/oauth2/models.py
msgid "Signing Key"
@@ -1309,78 +1239,6 @@ msgstr ""
msgid "Generate data export."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "User to lock. If omitted, locks the current user (self-service)."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "No lockdown flow configured."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Lockdown flow is not applicable."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Choose the target account, then return a flow link."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "No lockdown flow configured or the flow is not applicable"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Permission denied (when targeting another user)"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Deactivate the user account (set is_active to False)"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Set an unusable password for the user"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Delete all active sessions for the user"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid ""
"Revoke all tokens for the user (API, app password, recovery, verification, "
"OAuth)"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid ""
"Flow to redirect users to after self-service lockdown. This flow should not "
"require authentication since the user's session is deleted."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Account Lockdown Stage"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Account Lockdown Stages"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "No target user specified for account lockdown"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "You do not have permission to lock down this account."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "Account lockdown failed for this account."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "Self-service account lockdown requires a completion flow."
msgstr ""
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/models.py
msgid "Endpoint Authenticator Google Device Trust Connector Stage"
msgstr "Fáze konektoru Endpoint Authenticator Google Device Trust"
@@ -1397,6 +1255,10 @@ msgstr "Koncové zařízení"
msgid "Endpoint Devices"
msgstr "Koncová zařízení"
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/stage.py
msgid "Verifying your browser..."
msgstr "Ověřuji Váš prohlížeč..."
#: authentik/enterprise/stages/mtls/models.py
msgid ""
"Configure certificate authorities to validate the certificate against. This "
@@ -1479,12 +1341,6 @@ msgstr ""
"Odeslat oznámení pouze jednou, například při posílání webhooku do kanálu "
"chatu."
#: authentik/events/models.py
msgid ""
"When set, the selected ceritifcate is used to validate the certificate of "
"the webhook server."
msgstr ""
#: authentik/events/models.py
msgid ""
"Customize the body of the request. Mapping should return data that is JSON-"
@@ -1655,15 +1511,6 @@ msgstr "Zásady před tokem"
msgid "Flow"
msgstr "Tok"
#: authentik/flows/apps.py
msgid "Refresh other tabs after successful authentication."
msgstr ""
#: authentik/flows/apps.py
msgid ""
"Upon successful authentication, re-start authentication in other open tabs."
msgstr ""
#: authentik/flows/exceptions.py
msgid "Flow does not apply to current user."
msgstr "Tok se nevztahuje na aktuálního uživatele."
@@ -1773,8 +1620,8 @@ msgstr "Token Toku"
msgid "Flow Tokens"
msgstr "Tokeny Toků"
#: authentik/flows/planner.py
msgid "This link is invalid or has expired. Please request a new one."
#: authentik/flows/templates/if/flow.html
msgid "Site footer"
msgstr ""
#: authentik/flows/views/executor.py
@@ -2159,6 +2006,22 @@ msgstr "Reputační skóre"
msgid "Reputation Scores"
msgstr "Reputační skóre"
#: authentik/policies/templates/policies/buffer.html
msgid "Waiting for authentication..."
msgstr "Čeká se na ověření..."
#: authentik/policies/templates/policies/buffer.html
msgid ""
"You're already authenticating in another tab. This page will refresh once "
"authentication is completed."
msgstr ""
"Už se přihlašujete na jiné záložce. Stránka se obnoví, jakmile bude ověření "
"dokončeno."
#: authentik/policies/templates/policies/buffer.html
msgid "Authenticate in this tab"
msgstr "Ověřit na této záložce"
#: authentik/policies/templates/policies/denied.html
msgid "Permission denied"
msgstr "Nedostatečná oprávnění"
@@ -2284,14 +2147,6 @@ msgstr "Striktní porovnání URL"
msgid "Regular Expression URL matching"
msgstr "Porovnání URL regulárním výrazem"
#: authentik/providers/oauth2/models.py
msgid "Authorization"
msgstr ""
#: authentik/providers/oauth2/models.py
msgid "Logout"
msgstr ""
#: authentik/providers/oauth2/models.py
msgid "Back-channel"
msgstr "Back-channel"
@@ -2649,6 +2504,10 @@ msgstr "Poskytovatel proxy"
msgid "Proxy Providers"
msgstr "Poskytovatelé proxy"
#: authentik/providers/proxy/tasks.py
msgid "Terminate session on Proxy outpost."
msgstr "Ukončit relaci na outpostu proxy."
#: authentik/providers/rac/models.py authentik/stages/user_login/models.py
msgid ""
"Determines how long a session lasts. Default of 0 means that the sessions "
@@ -2776,10 +2635,8 @@ msgstr ""
"omezení publika nebude přidáno."
#: authentik/providers/saml/models.py
msgid ""
"Also known as EntityID. Providing a value overrides the default issuer "
"generated by authentik."
msgstr ""
msgid "Also known as EntityID"
msgstr "Také známé jako EntityID."
#: authentik/providers/saml/models.py
msgid "SLS URL"
@@ -2997,10 +2854,6 @@ msgstr "Hodnota SAML NameID pro tuto relaci"
msgid "SAML NameID format"
msgstr "Formát SAML NameID"
#: authentik/providers/saml/models.py
msgid "SAML Issuer used for this session"
msgstr ""
#: authentik/providers/saml/models.py
msgid "SAML Session"
msgstr "Relace SAML"
@@ -3029,14 +2882,6 @@ msgstr "Slack"
msgid "Salesforce"
msgstr ""
#: authentik/providers/scim/models.py
msgid "Webex"
msgstr ""
#: authentik/providers/scim/models.py
msgid "vCenter"
msgstr ""
#: authentik/providers/scim/models.py
msgid "Group filters used to define sync-scope for groups."
msgstr ""
@@ -3313,7 +3158,7 @@ msgstr ""
" Prosím, kontaktujte správce.\n"
" "
#: authentik/sources/ldap/api/sources.py
#: authentik/sources/ldap/api.py
msgid "Only a single LDAP Source with password synchronization is allowed"
msgstr "Je dovolen pouze jeden zdroj LDAP se synchronizací hesel"
@@ -3843,12 +3688,6 @@ msgstr ""
"Povolit autentikační tok iniciovaný Identity Providerem. Může představovat "
"bezpečnostní riziko, protože se nekontroluje request ID."
#: authentik/sources/saml/models.py
msgid ""
"When enabled, the IdP will re-authenticate the user even if a session "
"exists."
msgstr ""
#: authentik/sources/saml/models.py
msgid ""
"NameID Policy sent to the IdP. Can be unset, in which case no Policy is "
@@ -4269,10 +4108,6 @@ msgstr "Kroky validace autentikátoru"
msgid "No (allowed) MFA authenticator configured."
msgstr "Žádný (povolený) MFA autentikátor nebyl nastaven."
#: authentik/stages/authenticator_webauthn/models.py
msgid "When enabled, a given device can only be registered once."
msgstr ""
#: authentik/stages/authenticator_webauthn/models.py
msgid "WebAuthn Authenticator Setup Stage"
msgstr "Krok nastavení autentikátoru WebAuthn"
@@ -4408,10 +4243,6 @@ msgstr "Email OTP"
msgid "Event Notification"
msgstr "Oznámení o události"
#: authentik/stages/email/models.py authentik/stages/invitation/models.py
msgid "Invitation"
msgstr "Pozvánka"
#: authentik/stages/email/models.py
msgid ""
"The time window used to count recent account recovery attempts. If the "
@@ -4530,62 +4361,6 @@ msgstr ""
"\n"
"Tento email byl odeslán z transportu oznámení %(name)s.\n"
#: authentik/stages/email/templates/email/invitation.html
msgid ""
"\n"
" You're Invited!\n"
" "
msgstr ""
#: authentik/stages/email/templates/email/invitation.html
#, python-format
msgid ""
"\n"
" You have been invited to join %(host)s. Click the button below to get started.\n"
" "
msgstr ""
#: authentik/stages/email/templates/email/invitation.html
#, python-format
msgid ""
"\n"
" This invitation expires %(expires)s.\n"
" "
msgstr ""
#: authentik/stages/email/templates/email/invitation.html
#: authentik/stages/email/templates/email/invitation.txt
msgid "Accept Invitation"
msgstr ""
#: authentik/stages/email/templates/email/invitation.html
msgid ""
"\n"
" If you cannot click the button above, please copy and paste the following URL into your browser:\n"
" "
msgstr ""
#: authentik/stages/email/templates/email/invitation.txt
msgid "You're Invited!"
msgstr ""
#: authentik/stages/email/templates/email/invitation.txt
#, python-format
msgid ""
"You have been invited to join %(host)s. Use the link below to get started."
msgstr ""
#: authentik/stages/email/templates/email/invitation.txt
#, python-format
msgid "This invitation expires %(expires)s."
msgstr ""
#: authentik/stages/email/templates/email/invitation.txt
msgid ""
"If you cannot click the link above, please copy and paste the following URL "
"into your browser:"
msgstr ""
#: authentik/stages/email/templates/email/password_reset.html
msgid ""
"\n"
@@ -4763,6 +4538,10 @@ msgstr "Pokud je povoleno, pozvánka bude po použití smazána."
msgid "Optional fixed data to enforce on user enrollment."
msgstr ""
#: authentik/stages/invitation/models.py
msgid "Invitation"
msgstr "Pozvánka"
#: authentik/stages/invitation/models.py
msgid "Invitations"
msgstr "Pozvánky"
@@ -4875,18 +4654,6 @@ msgstr ""
msgid "Static: Static value, displayed as-is."
msgstr ""
#: authentik/stages/prompt/models.py
msgid "Alert (Info): Static alert box with info styling"
msgstr ""
#: authentik/stages/prompt/models.py
msgid "Alert (Warning): Static alert box with warning styling"
msgstr ""
#: authentik/stages/prompt/models.py
msgid "Alert (Danger): Static alert box with danger styling"
msgstr ""
#: authentik/stages/prompt/models.py
msgid "authentik: Selection of locales authentik supports"
msgstr "authentik: Výběr jazyků, které authentik podporuje"

View File

@@ -14,7 +14,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-05-06 00:27+0000\n"
"POT-Creation-Date: 2026-04-23 00:25+0000\n"
"PO-Revision-Date: 2025-12-01 19:09+0000\n"
"Last-Translator: Lukas Nielsen, 2026\n"
"Language-Team: German (Germany) (https://app.transifex.com/authentik/teams/119923/de_DE/)\n"
@@ -111,14 +111,6 @@ msgstr "Validierungsfehler"
msgid "Blueprint file does not exist"
msgstr "Vorlagendatei existiert nicht"
#: authentik/blueprints/api.py
msgid "Context must be valid JSON"
msgstr ""
#: authentik/blueprints/api.py
msgid "Context must be a JSON object"
msgstr ""
#: authentik/blueprints/api.py
msgid "Failed to validate blueprint"
msgstr "Fehler bei der Validierung der Vorlage"
@@ -265,14 +257,6 @@ msgstr ""
"werden nur die backchannel Provider zurück gegeben. Zudem werden bei "
"Deaktivierung die backchannel Provider ausgeschlossen."
#: authentik/core/api/users.py
msgid "Invalid password hash format. Must be a valid Django password hash."
msgstr ""
#: authentik/core/api/users.py
msgid "Cannot set both password and password_hash. Use only one."
msgstr ""
#: authentik/core/api/users.py
msgid "No leading or trailing slashes allowed."
msgstr "Es sind keine führenden oder abschließenden Schrägstriche erlaubt."
@@ -451,10 +435,6 @@ msgstr "Interner Anwendungsname, wird in URLs verwendet."
msgid "Open launch URL in a new browser tab or window."
msgstr "Start-URL in einem neuen Browser-Fenster öffnen."
#: authentik/core/models.py
msgid "Hide this application from the user's My applications page."
msgstr ""
#: authentik/core/models.py
msgid "Application"
msgstr "Anwendung"
@@ -954,6 +934,10 @@ msgstr "Es muss entweder eine Prüfergruppe oder ein Prüfer festgelegt werden."
msgid "Grace period must be shorter than the interval."
msgstr "Die Nachfrist muss kürzer sein als das Intervall."
#: authentik/enterprise/lifecycle/api/rules.py
msgid "Only one type-wide rule for each object type is allowed."
msgstr "Für jeden Objekttyp ist nur eine typweite Regel zulässig."
#: authentik/enterprise/lifecycle/models.py
msgid ""
"Select which transports should be used to notify the reviewers. If none are "
@@ -984,9 +968,10 @@ msgid "Go to {self._get_model_name()}"
msgstr "Gehe zu {self._get_model_name()}"
#: authentik/enterprise/lifecycle/models.py
msgid ""
"Access review is due for {self.content_type.name.lower()} {object_label}"
msgid "Access review is due for {self.content_type.name} {str(self.object)}"
msgstr ""
"Die Zugriffsüberprüfung für {self.content_type.name} {str(self.object)} "
"steht an"
#: authentik/enterprise/lifecycle/models.py
msgid ""
@@ -1003,8 +988,8 @@ msgstr ""
"erledigt"
#: authentik/enterprise/lifecycle/tasks.py
msgid "Dispatch tasks to apply lifecycle rules."
msgstr ""
msgid "Dispatch tasks to validate lifecycle rules."
msgstr "Aufgaben zur Überprüfung von Lebenszyklusregeln zuweisen."
#: authentik/enterprise/lifecycle/tasks.py
msgid "Apply lifecycle rule."
@@ -1347,78 +1332,6 @@ msgstr "Download"
msgid "Generate data export."
msgstr "Datenexport generieren."
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "User to lock. If omitted, locks the current user (self-service)."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "No lockdown flow configured."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Lockdown flow is not applicable."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Choose the target account, then return a flow link."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "No lockdown flow configured or the flow is not applicable"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Permission denied (when targeting another user)"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Deactivate the user account (set is_active to False)"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Set an unusable password for the user"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Delete all active sessions for the user"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid ""
"Revoke all tokens for the user (API, app password, recovery, verification, "
"OAuth)"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid ""
"Flow to redirect users to after self-service lockdown. This flow should not "
"require authentication since the user's session is deleted."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Account Lockdown Stage"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Account Lockdown Stages"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "No target user specified for account lockdown"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "You do not have permission to lock down this account."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "Account lockdown failed for this account."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "Self-service account lockdown requires a completion flow."
msgstr ""
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/models.py
msgid "Endpoint Authenticator Google Device Trust Connector Stage"
msgstr "Endpunkt-Authenticator für Google Gerätevertrauen Verbindungs Stage"
@@ -2864,10 +2777,8 @@ msgstr ""
"Feld leer, wird keine Zielgruppenbeschränkung hinzugefügt."
#: authentik/providers/saml/models.py
msgid ""
"Also known as EntityID. Providing a value overrides the default issuer "
"generated by authentik."
msgstr ""
msgid "Also known as EntityID"
msgstr "Auch bekannt als EntityID"
#: authentik/providers/saml/models.py
msgid "SLS URL"
@@ -3089,10 +3000,6 @@ msgstr "SAML-NameID-Wert für diese Sitzung"
msgid "SAML NameID format"
msgstr "SAML-NameID-Format"
#: authentik/providers/saml/models.py
msgid "SAML Issuer used for this session"
msgstr ""
#: authentik/providers/saml/models.py
msgid "SAML Session"
msgstr "SAML Sitzung"
@@ -3125,10 +3032,6 @@ msgstr "Salesforce"
msgid "Webex"
msgstr "Webex"
#: authentik/providers/scim/models.py
msgid "vCenter"
msgstr ""
#: authentik/providers/scim/models.py
msgid "Group filters used to define sync-scope for groups."
msgstr ""
@@ -5043,18 +4946,6 @@ msgstr ""
msgid "Static: Static value, displayed as-is."
msgstr "Statisch: Statischer Wert, wird so angezeigt, wie er ist."
#: authentik/stages/prompt/models.py
msgid "Alert (Info): Static alert box with info styling"
msgstr ""
#: authentik/stages/prompt/models.py
msgid "Alert (Warning): Static alert box with warning styling"
msgstr ""
#: authentik/stages/prompt/models.py
msgid "Alert (Danger): Static alert box with danger styling"
msgstr ""
#: authentik/stages/prompt/models.py
msgid "authentik: Selection of locales authentik supports"
msgstr "Authentik: Auswahl der von Authentik unterstützten Gebietsschemata"

View File

@@ -22,12 +22,9 @@ Gestionnaire
ghec
Gitea
Gravitee
HACS
Homarr
Informatique
Jellyseerr
Kimai
Kiota
Knoc
Knocknoc
Komodo
@@ -46,16 +43,13 @@ Organizr
Packagify
Palo
Papra
PhotoPrism
pfSense
phpipam
Planka
Plesk
PostHog
proftpd
Qube
Relatedly
Seerr
Sidero
snipeit
sonarqube
@@ -67,6 +61,7 @@ Vikunja
Wazuh
Wdio
Weixin
Kiota
Wekan
Xcreds
Zammad

View File

@@ -11,4 +11,3 @@ Naur
Wärting
Aadit
Kilby
Kahmen

View File

@@ -164,4 +164,3 @@ yamltags
zxcvbn
~uuid
~uuids
wreply

Binary file not shown.

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-05-06 00:27+0000\n"
"POT-Creation-Date: 2026-02-10 19:27+0000\n"
"PO-Revision-Date: 2025-12-01 19:09+0000\n"
"Last-Translator: Marc Schmitt, 2025\n"
"Language-Team: Spanish (Spain) (https://app.transifex.com/authentik/teams/119923/es_ES/)\n"
@@ -105,14 +105,6 @@ msgstr "Error de validación"
msgid "Blueprint file does not exist"
msgstr "El archivo de plantilla(blueprint) no existe"
#: authentik/blueprints/api.py
msgid "Context must be valid JSON"
msgstr ""
#: authentik/blueprints/api.py
msgid "Context must be a JSON object"
msgstr ""
#: authentik/blueprints/api.py
msgid "Failed to validate blueprint"
msgstr "No se pudo validar la plantilla(blueprint)"
@@ -121,11 +113,6 @@ msgstr "No se pudo validar la plantilla(blueprint)"
msgid "Either path or content must be set."
msgstr "Se debe establecer una ruta o contenido."
#: authentik/blueprints/api.py
#, python-brace-format
msgid "User lacks permission to create {model}"
msgstr "El usuario carece de permisos para crear {model}"
#: authentik/blueprints/models.py
msgid "Managed by authentik"
msgstr "Administrado por authentik"
@@ -261,13 +248,10 @@ msgstr ""
"secundario. Cuando se configura como falso, se excluyen los proveedores de "
"canal secundario."
#: authentik/core/api/users.py
msgid "Invalid password hash format. Must be a valid Django password hash."
msgstr ""
#: authentik/core/api/users.py
msgid "Cannot set both password and password_hash. Use only one."
msgstr ""
#: authentik/core/api/transactional_applications.py
#, python-brace-format
msgid "User lacks permission to create {model}"
msgstr "El usuario carece de permisos para crear {model}"
#: authentik/core/api/users.py
msgid "No leading or trailing slashes allowed."
@@ -329,12 +313,6 @@ msgstr ""
msgid "This field is required."
msgstr ""
#: authentik/core/apps.py
msgid ""
"Configure if applications without any policy/group/user bindings should be "
"accessible to any user."
msgstr ""
#: authentik/core/models.py
msgid "name"
msgstr "nombre"
@@ -441,10 +419,6 @@ msgstr "Nombre de la aplicación interna, utilizado en las URL."
msgid "Open launch URL in a new browser tab or window."
msgstr "Abrir la URL de inicio en una nueva pestaña o ventana del navegador."
#: authentik/core/models.py
msgid "Hide this application from the user's My applications page."
msgstr ""
#: authentik/core/models.py
msgid "Application"
msgstr "Aplicación"
@@ -635,14 +609,6 @@ msgstr "Eliminar usuarios temporales creados por SAML Sources."
msgid "Go home"
msgstr "Ir al inicio"
#: authentik/core/templates/login/base_full.html
msgid "Site footer"
msgstr ""
#: authentik/core/templates/login/base_full.html
msgid "Flow links"
msgstr ""
#: authentik/core/templates/login/base_full.html
#: authentik/flows/templates/if/flow-sfe.html
msgid "Powered by authentik"
@@ -750,10 +716,6 @@ msgid "Discover, import and update certificates from the filesystem."
msgstr ""
"Descubra, importe y actualice certificados desde el sistema de archivos."
#: authentik/endpoints/api/stages.py
msgid "Selected connector is not compatible with this stage."
msgstr ""
#: authentik/endpoints/connectors/agent/api/connectors.py
msgid "Selected platform not supported"
msgstr ""
@@ -808,14 +770,6 @@ msgstr ""
msgid "Apple Nonces"
msgstr ""
#: authentik/endpoints/connectors/agent/models.py
msgid "Apple Independent Secure Enclave"
msgstr ""
#: authentik/endpoints/connectors/agent/models.py
msgid "Apple Independent Secure Enclaves"
msgstr ""
#: authentik/endpoints/facts.py
msgid "Operating System name, such as 'Server 2022' or 'Ubuntu'"
msgstr ""
@@ -886,12 +840,6 @@ msgstr "Se requiere de Enterprise para crear/actualizar este objeto."
msgid "Enterprise is required to use this endpoint."
msgstr ""
#: authentik/enterprise/audit/apps.py
msgid ""
"Include additional information in audit logs, may incur a performance "
"penalty."
msgstr ""
#: authentik/enterprise/endpoints/connectors/fleet/models.py
#: authentik/events/models.py
msgid ""
@@ -909,19 +857,6 @@ msgstr ""
msgid "Fleet Connectors"
msgstr ""
#: authentik/enterprise/endpoints/connectors/google_chrome/models.py
msgid "Google Device Trust Connector"
msgstr ""
#: authentik/enterprise/endpoints/connectors/google_chrome/models.py
msgid "Google Device Trust Connectors"
msgstr ""
#: authentik/enterprise/endpoints/connectors/google_chrome/stage.py
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/stage.py
msgid "Verifying your browser..."
msgstr "Verificando tu navegador..."
#: authentik/enterprise/lifecycle/api/reviews.py
msgid "You are not allowed to submit a review for this object."
msgstr ""
@@ -938,6 +873,10 @@ msgstr ""
msgid "Grace period must be shorter than the interval."
msgstr ""
#: authentik/enterprise/lifecycle/api/rules.py
msgid "Only one type-wide rule for each object type is allowed."
msgstr ""
#: authentik/enterprise/lifecycle/models.py
msgid ""
"Select which transports should be used to notify the reviewers. If none are "
@@ -965,8 +904,7 @@ msgid "Go to {self._get_model_name()}"
msgstr ""
#: authentik/enterprise/lifecycle/models.py
msgid ""
"Access review is due for {self.content_type.name.lower()} {object_label}"
msgid "Access review is due for {self.content_type.name} {str(self.object)}"
msgstr ""
#: authentik/enterprise/lifecycle/models.py
@@ -980,7 +918,7 @@ msgid ""
msgstr ""
#: authentik/enterprise/lifecycle/tasks.py
msgid "Dispatch tasks to apply lifecycle rules."
msgid "Dispatch tasks to validate lifecycle rules."
msgstr ""
#: authentik/enterprise/lifecycle/tasks.py
@@ -1221,14 +1159,6 @@ msgstr ""
msgid "Enterprise is required to use the OAuth mode."
msgstr ""
#: authentik/enterprise/providers/ssf/models.py
msgid "SSF RFC Push"
msgstr ""
#: authentik/enterprise/providers/ssf/models.py
msgid "SSF RFC Pull"
msgstr ""
#: authentik/enterprise/providers/ssf/models.py
#: authentik/providers/oauth2/models.py
msgid "Signing Key"
@@ -1310,78 +1240,6 @@ msgstr ""
msgid "Generate data export."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "User to lock. If omitted, locks the current user (self-service)."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "No lockdown flow configured."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Lockdown flow is not applicable."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Choose the target account, then return a flow link."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "No lockdown flow configured or the flow is not applicable"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Permission denied (when targeting another user)"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Deactivate the user account (set is_active to False)"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Set an unusable password for the user"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Delete all active sessions for the user"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid ""
"Revoke all tokens for the user (API, app password, recovery, verification, "
"OAuth)"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid ""
"Flow to redirect users to after self-service lockdown. This flow should not "
"require authentication since the user's session is deleted."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Account Lockdown Stage"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Account Lockdown Stages"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "No target user specified for account lockdown"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "You do not have permission to lock down this account."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "Account lockdown failed for this account."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "Self-service account lockdown requires a completion flow."
msgstr ""
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/models.py
msgid "Endpoint Authenticator Google Device Trust Connector Stage"
msgstr ""
@@ -1402,6 +1260,10 @@ msgstr "Dispositivo de Punto de Conexión"
msgid "Endpoint Devices"
msgstr "Dispositivos de Punto de Conexión"
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/stage.py
msgid "Verifying your browser..."
msgstr "Verificando tu navegador..."
#: authentik/enterprise/stages/mtls/models.py
msgid ""
"Configure certificate authorities to validate the certificate against. This "
@@ -1485,12 +1347,6 @@ msgstr ""
"Envía notificaciones solo una vez, por ejemplo, al enviar un webhook a un "
"canal de chat."
#: authentik/events/models.py
msgid ""
"When set, the selected ceritifcate is used to validate the certificate of "
"the webhook server."
msgstr ""
#: authentik/events/models.py
msgid ""
"Customize the body of the request. Mapping should return data that is JSON-"
@@ -1662,15 +1518,6 @@ msgstr "Políticas pre-flujo"
msgid "Flow"
msgstr "Flujo"
#: authentik/flows/apps.py
msgid "Refresh other tabs after successful authentication."
msgstr ""
#: authentik/flows/apps.py
msgid ""
"Upon successful authentication, re-start authentication in other open tabs."
msgstr ""
#: authentik/flows/exceptions.py
msgid "Flow does not apply to current user."
msgstr "El flujo no aplica al usuario actual."
@@ -1783,8 +1630,8 @@ msgstr "Token de flujo"
msgid "Flow Tokens"
msgstr "Tokens de flujo"
#: authentik/flows/planner.py
msgid "This link is invalid or has expired. Please request a new one."
#: authentik/flows/templates/if/flow.html
msgid "Site footer"
msgstr ""
#: authentik/flows/views/executor.py
@@ -2175,6 +2022,22 @@ msgstr "Puntuación de Reputacion"
msgid "Reputation Scores"
msgstr "Puntuaciones de Reputacion"
#: authentik/policies/templates/policies/buffer.html
msgid "Waiting for authentication..."
msgstr "Esperando autenticación"
#: authentik/policies/templates/policies/buffer.html
msgid ""
"You're already authenticating in another tab. This page will refresh once "
"authentication is completed."
msgstr ""
"Ya estás autenticándote en otra pestaña. Esta página se actualizará una vez "
"que la autenticación se haya completado."
#: authentik/policies/templates/policies/buffer.html
msgid "Authenticate in this tab"
msgstr "Autenticar en esta pestaña"
#: authentik/policies/templates/policies/denied.html
msgid "Permission denied"
msgstr "Permiso denegado"
@@ -2303,14 +2166,6 @@ msgstr "Comparación de URL estricta"
msgid "Regular Expression URL matching"
msgstr "Coincidencia de URL con Expresiones Regulares"
#: authentik/providers/oauth2/models.py
msgid "Authorization"
msgstr ""
#: authentik/providers/oauth2/models.py
msgid "Logout"
msgstr ""
#: authentik/providers/oauth2/models.py
msgid "Back-channel"
msgstr ""
@@ -2673,6 +2528,10 @@ msgstr "Proveedor de Proxy"
msgid "Proxy Providers"
msgstr "Proveedores de Proxy"
#: authentik/providers/proxy/tasks.py
msgid "Terminate session on Proxy outpost."
msgstr "Terminar sesión en Proxy outpost."
#: authentik/providers/rac/models.py authentik/stages/user_login/models.py
msgid ""
"Determines how long a session lasts. Default of 0 means that the sessions "
@@ -2804,10 +2663,8 @@ msgstr ""
"vacío, no se agregará ninguna restricción de audiencia."
#: authentik/providers/saml/models.py
msgid ""
"Also known as EntityID. Providing a value overrides the default issuer "
"generated by authentik."
msgstr ""
msgid "Also known as EntityID"
msgstr "También conocido como EntityID"
#: authentik/providers/saml/models.py
msgid "SLS URL"
@@ -3020,10 +2877,6 @@ msgstr ""
msgid "SAML NameID format"
msgstr ""
#: authentik/providers/saml/models.py
msgid "SAML Issuer used for this session"
msgstr ""
#: authentik/providers/saml/models.py
msgid "SAML Session"
msgstr ""
@@ -3052,14 +2905,6 @@ msgstr "Slack"
msgid "Salesforce"
msgstr ""
#: authentik/providers/scim/models.py
msgid "Webex"
msgstr ""
#: authentik/providers/scim/models.py
msgid "vCenter"
msgstr ""
#: authentik/providers/scim/models.py
msgid "Group filters used to define sync-scope for groups."
msgstr ""
@@ -3343,7 +3188,7 @@ msgstr ""
" Por favor, contacta a tu administrador.\n"
" "
#: authentik/sources/ldap/api/sources.py
#: authentik/sources/ldap/api.py
msgid "Only a single LDAP Source with password synchronization is allowed"
msgstr ""
"Solo está permitida una Fuente de LDAP con sincronización de contraseña"
@@ -3877,12 +3722,6 @@ msgstr ""
" un riesgo para la seguridad, ya que no se valida el identificador de la "
"solicitud."
#: authentik/sources/saml/models.py
msgid ""
"When enabled, the IdP will re-authenticate the user even if a session "
"exists."
msgstr ""
#: authentik/sources/saml/models.py
msgid ""
"NameID Policy sent to the IdP. Can be unset, in which case no Policy is "
@@ -4311,10 +4150,6 @@ msgstr "Etapas de Validación del Autenticador"
msgid "No (allowed) MFA authenticator configured."
msgstr "No hay un autenticador MFA (permitido) configurado."
#: authentik/stages/authenticator_webauthn/models.py
msgid "When enabled, a given device can only be registered once."
msgstr ""
#: authentik/stages/authenticator_webauthn/models.py
msgid "WebAuthn Authenticator Setup Stage"
msgstr "Etapa de Configuración del Autenticador WebAuthn"
@@ -4453,10 +4288,6 @@ msgstr "OTP por Correo Electrónico"
msgid "Event Notification"
msgstr ""
#: authentik/stages/email/models.py authentik/stages/invitation/models.py
msgid "Invitation"
msgstr "Invitación"
#: authentik/stages/email/models.py
msgid ""
"The time window used to count recent account recovery attempts. If the "
@@ -4577,62 +4408,6 @@ msgstr ""
"\n"
"Este correo electrónico fue enviado desde el transporte de notificaciones %(name)s.\n"
#: authentik/stages/email/templates/email/invitation.html
msgid ""
"\n"
" You're Invited!\n"
" "
msgstr ""
#: authentik/stages/email/templates/email/invitation.html
#, python-format
msgid ""
"\n"
" You have been invited to join %(host)s. Click the button below to get started.\n"
" "
msgstr ""
#: authentik/stages/email/templates/email/invitation.html
#, python-format
msgid ""
"\n"
" This invitation expires %(expires)s.\n"
" "
msgstr ""
#: authentik/stages/email/templates/email/invitation.html
#: authentik/stages/email/templates/email/invitation.txt
msgid "Accept Invitation"
msgstr ""
#: authentik/stages/email/templates/email/invitation.html
msgid ""
"\n"
" If you cannot click the button above, please copy and paste the following URL into your browser:\n"
" "
msgstr ""
#: authentik/stages/email/templates/email/invitation.txt
msgid "You're Invited!"
msgstr ""
#: authentik/stages/email/templates/email/invitation.txt
#, python-format
msgid ""
"You have been invited to join %(host)s. Use the link below to get started."
msgstr ""
#: authentik/stages/email/templates/email/invitation.txt
#, python-format
msgid "This invitation expires %(expires)s."
msgstr ""
#: authentik/stages/email/templates/email/invitation.txt
msgid ""
"If you cannot click the link above, please copy and paste the following URL "
"into your browser:"
msgstr ""
#: authentik/stages/email/templates/email/password_reset.html
msgid ""
"\n"
@@ -4816,6 +4591,10 @@ msgstr "Cuando se habilita, la invitación se eliminará después de su uso."
msgid "Optional fixed data to enforce on user enrollment."
msgstr "Datos fijos opcionales para aplicar en la inscripción de usuarios."
#: authentik/stages/invitation/models.py
msgid "Invitation"
msgstr "Invitación"
#: authentik/stages/invitation/models.py
msgid "Invitations"
msgstr "Invitaciones"
@@ -4940,18 +4719,6 @@ msgstr ""
msgid "Static: Static value, displayed as-is."
msgstr "Estático: valor estático, que se muestra tal cual."
#: authentik/stages/prompt/models.py
msgid "Alert (Info): Static alert box with info styling"
msgstr ""
#: authentik/stages/prompt/models.py
msgid "Alert (Warning): Static alert box with warning styling"
msgstr ""
#: authentik/stages/prompt/models.py
msgid "Alert (Danger): Static alert box with danger styling"
msgstr ""
#: authentik/stages/prompt/models.py
msgid "authentik: Selection of locales authentik supports"
msgstr ""

Binary file not shown.

View File

@@ -5,18 +5,18 @@
#
# Translators:
# Marc Schmitt, 2025
# Skyler Mäntysaari, 2025
# Jiri Grönroos <jiri.gronroos@iki.fi>, 2025
# Uumas, 2026
# Skyler Mäntysaari, 2026
# Viima Veteläinen, 2026
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-05-06 00:27+0000\n"
"POT-Creation-Date: 2026-02-10 19:27+0000\n"
"PO-Revision-Date: 2025-12-01 19:09+0000\n"
"Last-Translator: Skyler Mäntysaari, 2026\n"
"Last-Translator: Viima Veteläinen, 2026\n"
"Language-Team: Finnish (Finland) (https://app.transifex.com/authentik/teams/119923/fi_FI/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -113,14 +113,6 @@ msgstr "Vahvistusvirhe"
msgid "Blueprint file does not exist"
msgstr "Suunnitelman tiedostoa ei löydetty"
#: authentik/blueprints/api.py
msgid "Context must be valid JSON"
msgstr ""
#: authentik/blueprints/api.py
msgid "Context must be a JSON object"
msgstr ""
#: authentik/blueprints/api.py
msgid "Failed to validate blueprint"
msgstr "Suunnitelman validointi ei onnistunut"
@@ -129,11 +121,6 @@ msgstr "Suunnitelman validointi ei onnistunut"
msgid "Either path or content must be set."
msgstr "Joko polku tai sisältö on määritettävä."
#: authentik/blueprints/api.py
#, python-brace-format
msgid "User lacks permission to create {model}"
msgstr "Käyttäjältä puuttuu oikeus luoda {model}"
#: authentik/blueprints/models.py
msgid "Managed by authentik"
msgstr "Authentikin hallinnoima"
@@ -261,13 +248,10 @@ msgstr ""
"true, vain taustakanava-tarjoajat palautetaan. Kun asetus on false, "
"takakanava-tarjoajat suljetaan pois."
#: authentik/core/api/users.py
msgid "Invalid password hash format. Must be a valid Django password hash."
msgstr ""
#: authentik/core/api/users.py
msgid "Cannot set both password and password_hash. Use only one."
msgstr ""
#: authentik/core/api/transactional_applications.py
#, python-brace-format
msgid "User lacks permission to create {model}"
msgstr "Käyttäjältä puuttuu oikeus luoda {model}"
#: authentik/core/api/users.py
msgid "No leading or trailing slashes allowed."
@@ -329,12 +313,6 @@ msgstr "Sähköpostivaihetta ei löydetty."
msgid "This field is required."
msgstr "Tämä kenttä on pakollinen."
#: authentik/core/apps.py
msgid ""
"Configure if applications without any policy/group/user bindings should be "
"accessible to any user."
msgstr ""
#: authentik/core/models.py
msgid "name"
msgstr "nimi"
@@ -441,10 +419,6 @@ msgstr "Sovelluksen sisäinen nimi, jota käytetään URLeissa."
msgid "Open launch URL in a new browser tab or window."
msgstr "Avaa käynnistys-URL uuteen selainvälilehteen tai -ikkunaan."
#: authentik/core/models.py
msgid "Hide this application from the user's My applications page."
msgstr ""
#: authentik/core/models.py
msgid "Application"
msgstr "Sovellus"
@@ -634,14 +608,6 @@ msgstr "Poista SAML-lähteiden luomat tilapäiset käyttäjät."
msgid "Go home"
msgstr "Siirry etusivulle"
#: authentik/core/templates/login/base_full.html
msgid "Site footer"
msgstr "Sivuston alatunniste"
#: authentik/core/templates/login/base_full.html
msgid "Flow links"
msgstr ""
#: authentik/core/templates/login/base_full.html
#: authentik/flows/templates/if/flow-sfe.html
msgid "Powered by authentik"
@@ -748,10 +714,6 @@ msgstr ""
msgid "Discover, import and update certificates from the filesystem."
msgstr "Havaitse, tuo ja päivitä sertifikaatteja levyjärjestelmästä."
#: authentik/endpoints/api/stages.py
msgid "Selected connector is not compatible with this stage."
msgstr ""
#: authentik/endpoints/connectors/agent/api/connectors.py
msgid "Selected platform not supported"
msgstr "Valittu alusta ei ole tuettu"
@@ -806,14 +768,6 @@ msgstr ""
msgid "Apple Nonces"
msgstr ""
#: authentik/endpoints/connectors/agent/models.py
msgid "Apple Independent Secure Enclave"
msgstr ""
#: authentik/endpoints/connectors/agent/models.py
msgid "Apple Independent Secure Enclaves"
msgstr ""
#: authentik/endpoints/facts.py
msgid "Operating System name, such as 'Server 2022' or 'Ubuntu'"
msgstr ""
@@ -884,12 +838,6 @@ msgstr "Tämän objektin luontiin/päivittämiseen tarvitaan Enterprise-versiota
msgid "Enterprise is required to use this endpoint."
msgstr ""
#: authentik/enterprise/audit/apps.py
msgid ""
"Include additional information in audit logs, may incur a performance "
"penalty."
msgstr ""
#: authentik/enterprise/endpoints/connectors/fleet/models.py
#: authentik/events/models.py
msgid ""
@@ -907,19 +855,6 @@ msgstr ""
msgid "Fleet Connectors"
msgstr ""
#: authentik/enterprise/endpoints/connectors/google_chrome/models.py
msgid "Google Device Trust Connector"
msgstr ""
#: authentik/enterprise/endpoints/connectors/google_chrome/models.py
msgid "Google Device Trust Connectors"
msgstr ""
#: authentik/enterprise/endpoints/connectors/google_chrome/stage.py
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/stage.py
msgid "Verifying your browser..."
msgstr "Selaintasi varmennetaan..."
#: authentik/enterprise/lifecycle/api/reviews.py
msgid "You are not allowed to submit a review for this object."
msgstr ""
@@ -936,6 +871,10 @@ msgstr ""
msgid "Grace period must be shorter than the interval."
msgstr ""
#: authentik/enterprise/lifecycle/api/rules.py
msgid "Only one type-wide rule for each object type is allowed."
msgstr ""
#: authentik/enterprise/lifecycle/models.py
msgid ""
"Select which transports should be used to notify the reviewers. If none are "
@@ -963,8 +902,7 @@ msgid "Go to {self._get_model_name()}"
msgstr ""
#: authentik/enterprise/lifecycle/models.py
msgid ""
"Access review is due for {self.content_type.name.lower()} {object_label}"
msgid "Access review is due for {self.content_type.name} {str(self.object)}"
msgstr ""
#: authentik/enterprise/lifecycle/models.py
@@ -978,7 +916,7 @@ msgid ""
msgstr ""
#: authentik/enterprise/lifecycle/tasks.py
msgid "Dispatch tasks to apply lifecycle rules."
msgid "Dispatch tasks to validate lifecycle rules."
msgstr ""
#: authentik/enterprise/lifecycle/tasks.py
@@ -1222,14 +1160,6 @@ msgstr "EAP-TLS:n käyttöön tarvitaan Enterprise-versiota."
msgid "Enterprise is required to use the OAuth mode."
msgstr "OAuth-tilan käyttöön tarvitaan Enterprise-versiota."
#: authentik/enterprise/providers/ssf/models.py
msgid "SSF RFC Push"
msgstr ""
#: authentik/enterprise/providers/ssf/models.py
msgid "SSF RFC Pull"
msgstr ""
#: authentik/enterprise/providers/ssf/models.py
#: authentik/providers/oauth2/models.py
msgid "Signing Key"
@@ -1311,78 +1241,6 @@ msgstr "Lataa"
msgid "Generate data export."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "User to lock. If omitted, locks the current user (self-service)."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "No lockdown flow configured."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Lockdown flow is not applicable."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Choose the target account, then return a flow link."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "No lockdown flow configured or the flow is not applicable"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Permission denied (when targeting another user)"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Deactivate the user account (set is_active to False)"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Set an unusable password for the user"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Delete all active sessions for the user"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid ""
"Revoke all tokens for the user (API, app password, recovery, verification, "
"OAuth)"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid ""
"Flow to redirect users to after self-service lockdown. This flow should not "
"require authentication since the user's session is deleted."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Account Lockdown Stage"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Account Lockdown Stages"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "No target user specified for account lockdown"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "You do not have permission to lock down this account."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "Account lockdown failed for this account."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "Self-service account lockdown requires a completion flow."
msgstr ""
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/models.py
msgid "Endpoint Authenticator Google Device Trust Connector Stage"
msgstr "Päätepisteen todentaja Google Device Trust Connector -vaihe"
@@ -1399,6 +1257,10 @@ msgstr "Päätelaite"
msgid "Endpoint Devices"
msgstr "Päätelaitteet"
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/stage.py
msgid "Verifying your browser..."
msgstr "Selaintasi varmennetaan..."
#: authentik/enterprise/stages/mtls/models.py
msgid ""
"Configure certificate authorities to validate the certificate against. This "
@@ -1482,12 +1344,6 @@ msgstr ""
"Lähetä notifikaatio vain kerran, esimerkiksi kun lähetetään webhook-"
"tapahtuma pikaviestinkanavalle."
#: authentik/events/models.py
msgid ""
"When set, the selected ceritifcate is used to validate the certificate of "
"the webhook server."
msgstr ""
#: authentik/events/models.py
msgid ""
"Customize the body of the request. Mapping should return data that is JSON-"
@@ -1659,15 +1515,6 @@ msgstr "Prosessia edeltävät käytännöt"
msgid "Flow"
msgstr "Prosessi"
#: authentik/flows/apps.py
msgid "Refresh other tabs after successful authentication."
msgstr ""
#: authentik/flows/apps.py
msgid ""
"Upon successful authentication, re-start authentication in other open tabs."
msgstr ""
#: authentik/flows/exceptions.py
msgid "Flow does not apply to current user."
msgstr "Prosessi ei koske nykyistä käyttäjää."
@@ -1777,9 +1624,9 @@ msgstr "Prosessin tunniste"
msgid "Flow Tokens"
msgstr "Prosessin tunnisteet"
#: authentik/flows/planner.py
msgid "This link is invalid or has expired. Please request a new one."
msgstr ""
#: authentik/flows/templates/if/flow.html
msgid "Site footer"
msgstr "Sivuston alatunniste"
#: authentik/flows/views/executor.py
msgid "Invalid next URL"
@@ -2165,6 +2012,22 @@ msgstr "Mainepistemäärä"
msgid "Reputation Scores"
msgstr "Mainepistemäärät"
#: authentik/policies/templates/policies/buffer.html
msgid "Waiting for authentication..."
msgstr "Odotetaan todennusta..."
#: authentik/policies/templates/policies/buffer.html
msgid ""
"You're already authenticating in another tab. This page will refresh once "
"authentication is completed."
msgstr ""
"Kirjaudut jo toisella välilehdellä. Tämä sivu päivittyy kun todennus on "
"valmis."
#: authentik/policies/templates/policies/buffer.html
msgid "Authenticate in this tab"
msgstr "Kirjaudu tällä välilehdellä"
#: authentik/policies/templates/policies/denied.html
msgid "Permission denied"
msgstr "Käyttö evätty"
@@ -2293,14 +2156,6 @@ msgstr "Tiukka URL-vertailu"
msgid "Regular Expression URL matching"
msgstr "Regular Expression -pohjainen URL-vertailu"
#: authentik/providers/oauth2/models.py
msgid "Authorization"
msgstr "Valtuutus"
#: authentik/providers/oauth2/models.py
msgid "Logout"
msgstr "Kirjaudu ulos"
#: authentik/providers/oauth2/models.py
msgid "Back-channel"
msgstr "Taustakanava"
@@ -2665,6 +2520,10 @@ msgstr "Välityspalveluntarjoaja"
msgid "Proxy Providers"
msgstr "Välityspalveluntarjoajat"
#: authentik/providers/proxy/tasks.py
msgid "Terminate session on Proxy outpost."
msgstr "Katkaise istunto välityspalvelutukikohdasta."
#: authentik/providers/rac/models.py authentik/stages/user_login/models.py
msgid ""
"Determines how long a session lasts. Default of 0 means that the sessions "
@@ -2797,10 +2656,8 @@ msgstr ""
"yleisörajoitusta ei lisätä."
#: authentik/providers/saml/models.py
msgid ""
"Also known as EntityID. Providing a value overrides the default issuer "
"generated by authentik."
msgstr ""
msgid "Also known as EntityID"
msgstr "Tunnetaan myös nimellä EntityID"
#: authentik/providers/saml/models.py
msgid "SLS URL"
@@ -3023,10 +2880,6 @@ msgstr "SAML NameID:n arvo tälle istunnolle"
msgid "SAML NameID format"
msgstr "SAML NameID:n muoto"
#: authentik/providers/saml/models.py
msgid "SAML Issuer used for this session"
msgstr ""
#: authentik/providers/saml/models.py
msgid "SAML Session"
msgstr "SAML-istunto"
@@ -3055,14 +2908,6 @@ msgstr "Slack"
msgid "Salesforce"
msgstr "Salesforce"
#: authentik/providers/scim/models.py
msgid "Webex"
msgstr ""
#: authentik/providers/scim/models.py
msgid "vCenter"
msgstr ""
#: authentik/providers/scim/models.py
msgid "Group filters used to define sync-scope for groups."
msgstr ""
@@ -3348,7 +3193,7 @@ msgstr ""
" Ota yhteyttä ylläpitäjään.\n"
" "
#: authentik/sources/ldap/api/sources.py
#: authentik/sources/ldap/api.py
msgid "Only a single LDAP Source with password synchronization is allowed"
msgstr "Vain yksi LDAP-lähde salasanojen synkronoinnilla on sallittu"
@@ -3885,12 +3730,6 @@ msgstr ""
"Sallii IdP-lähtöiset todentamisprosessit. Tämä voi olla tietoturvariski, "
"koska pyynnön ID:tä ei validoida."
#: authentik/sources/saml/models.py
msgid ""
"When enabled, the IdP will re-authenticate the user even if a session "
"exists."
msgstr ""
#: authentik/sources/saml/models.py
msgid ""
"NameID Policy sent to the IdP. Can be unset, in which case no Policy is "
@@ -4316,10 +4155,6 @@ msgstr "Todentajan validaatiovaiheet"
msgid "No (allowed) MFA authenticator configured."
msgstr "Yhtään (sallittua) MFA-todentajaa ei ole määritelty."
#: authentik/stages/authenticator_webauthn/models.py
msgid "When enabled, a given device can only be registered once."
msgstr ""
#: authentik/stages/authenticator_webauthn/models.py
msgid "WebAuthn Authenticator Setup Stage"
msgstr "WebAuthn-todentajan asetusvaihe"
@@ -4458,10 +4293,6 @@ msgstr "Sähköposti-OTP"
msgid "Event Notification"
msgstr "Tapahtumanotifikaatio"
#: authentik/stages/email/models.py authentik/stages/invitation/models.py
msgid "Invitation"
msgstr "Kutsu"
#: authentik/stages/email/models.py
msgid ""
"The time window used to count recent account recovery attempts. If the "
@@ -4580,62 +4411,6 @@ msgstr ""
"\n"
"Tämä viesti on lähetetty notifikaatiokanavasta %(name)s.\n"
#: authentik/stages/email/templates/email/invitation.html
msgid ""
"\n"
" You're Invited!\n"
" "
msgstr ""
#: authentik/stages/email/templates/email/invitation.html
#, python-format
msgid ""
"\n"
" You have been invited to join %(host)s. Click the button below to get started.\n"
" "
msgstr ""
#: authentik/stages/email/templates/email/invitation.html
#, python-format
msgid ""
"\n"
" This invitation expires %(expires)s.\n"
" "
msgstr ""
#: authentik/stages/email/templates/email/invitation.html
#: authentik/stages/email/templates/email/invitation.txt
msgid "Accept Invitation"
msgstr ""
#: authentik/stages/email/templates/email/invitation.html
msgid ""
"\n"
" If you cannot click the button above, please copy and paste the following URL into your browser:\n"
" "
msgstr ""
#: authentik/stages/email/templates/email/invitation.txt
msgid "You're Invited!"
msgstr ""
#: authentik/stages/email/templates/email/invitation.txt
#, python-format
msgid ""
"You have been invited to join %(host)s. Use the link below to get started."
msgstr ""
#: authentik/stages/email/templates/email/invitation.txt
#, python-format
msgid "This invitation expires %(expires)s."
msgstr ""
#: authentik/stages/email/templates/email/invitation.txt
msgid ""
"If you cannot click the link above, please copy and paste the following URL "
"into your browser:"
msgstr ""
#: authentik/stages/email/templates/email/password_reset.html
msgid ""
"\n"
@@ -4816,6 +4591,10 @@ msgid "Optional fixed data to enforce on user enrollment."
msgstr ""
"Valinnainen kiinteä data joka pakotetaan käyttäjän rekisteröitymisessä."
#: authentik/stages/invitation/models.py
msgid "Invitation"
msgstr "Kutsu"
#: authentik/stages/invitation/models.py
msgid "Invitations"
msgstr "Kutsut"
@@ -4940,18 +4719,6 @@ msgstr ""
msgid "Static: Static value, displayed as-is."
msgstr "Staattinen: Staattinen arvo, näytetään sellaisenaan."
#: authentik/stages/prompt/models.py
msgid "Alert (Info): Static alert box with info styling"
msgstr ""
#: authentik/stages/prompt/models.py
msgid "Alert (Warning): Static alert box with warning styling"
msgstr ""
#: authentik/stages/prompt/models.py
msgid "Alert (Danger): Static alert box with danger styling"
msgstr ""
#: authentik/stages/prompt/models.py
msgid "authentik: Selection of locales authentik supports"
msgstr "authentik: Valittavat kielialueet, joita authentik tukee"

View File

@@ -15,7 +15,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-05-06 00:27+0000\n"
"POT-Creation-Date: 2026-05-01 03:47+0000\n"
"PO-Revision-Date: 2025-12-01 19:09+0000\n"
"Last-Translator: Sp P, 2026\n"
"Language-Team: French (France) (https://app.transifex.com/authentik/teams/119923/fr_FR/)\n"
@@ -116,14 +116,6 @@ msgstr "Erreur de Validation"
msgid "Blueprint file does not exist"
msgstr "Le fichier de plan n'existe pas"
#: authentik/blueprints/api.py
msgid "Context must be valid JSON"
msgstr ""
#: authentik/blueprints/api.py
msgid "Context must be a JSON object"
msgstr ""
#: authentik/blueprints/api.py
msgid "Failed to validate blueprint"
msgstr "Échec de validation du plan"

Binary file not shown.

View File

@@ -5,16 +5,15 @@
#
# Translators:
# Marc Schmitt, 2025
# Pao P, 2026
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-05-06 00:27+0000\n"
"POT-Creation-Date: 2026-02-10 19:27+0000\n"
"PO-Revision-Date: 2025-12-01 19:09+0000\n"
"Last-Translator: Pao P, 2026\n"
"Last-Translator: Marc Schmitt, 2025\n"
"Language-Team: Italian (Italy) (https://app.transifex.com/authentik/teams/119923/it_IT/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -58,14 +57,12 @@ msgstr ""
#: authentik/admin/files/validation.py
#, python-brace-format
msgid "File name too long (max {MAX_FILE_NAME_LENGTH} characters)"
msgstr "Nome del file troppo lungo (max {MAX_FILE_NAME_LENGTH} caratteri)"
msgstr ""
#: authentik/admin/files/validation.py
#, python-brace-format
msgid "Path component too long (max {MAX_PATH_COMPONENT_LENGTH} characters)"
msgstr ""
"Componente del percorso troppo lungo (max {MAX_PATH_COMPONENT_LENGTH} "
"caratteri)"
#: authentik/admin/models.py
msgid "Version history"
@@ -108,14 +105,6 @@ msgstr "Errore di validazione"
msgid "Blueprint file does not exist"
msgstr "File del progetto inesistente"
#: authentik/blueprints/api.py
msgid "Context must be valid JSON"
msgstr ""
#: authentik/blueprints/api.py
msgid "Context must be a JSON object"
msgstr ""
#: authentik/blueprints/api.py
msgid "Failed to validate blueprint"
msgstr "Impossibile convalidare il progetto"
@@ -124,11 +113,6 @@ msgstr "Impossibile convalidare il progetto"
msgid "Either path or content must be set."
msgstr "È necessario impostare il percorso o il contenuto."
#: authentik/blueprints/api.py
#, python-brace-format
msgid "User lacks permission to create {model}"
msgstr "L'utente non ha i diritti per creare {model}"
#: authentik/blueprints/models.py
msgid "Managed by authentik"
msgstr "Gestito da authentik"
@@ -254,13 +238,10 @@ msgstr ""
" vengono restituiti solo i provider di backchannel. Se impostato su falso, i"
" provider di backchannel vengono esclusi"
#: authentik/core/api/users.py
msgid "Invalid password hash format. Must be a valid Django password hash."
msgstr ""
#: authentik/core/api/users.py
msgid "Cannot set both password and password_hash. Use only one."
msgstr ""
#: authentik/core/api/transactional_applications.py
#, python-brace-format
msgid "User lacks permission to create {model}"
msgstr "L'utente non ha i diritti per creare {model}"
#: authentik/core/api/users.py
msgid "No leading or trailing slashes allowed."
@@ -322,12 +303,6 @@ msgstr ""
msgid "This field is required."
msgstr "Questo campo è obbligatorio."
#: authentik/core/apps.py
msgid ""
"Configure if applications without any policy/group/user bindings should be "
"accessible to any user."
msgstr ""
#: authentik/core/models.py
msgid "name"
msgstr "nome"
@@ -434,10 +409,6 @@ msgstr "Nome interno dell'applicazione, utilizzato negli URL."
msgid "Open launch URL in a new browser tab or window."
msgstr "Apri l'URL di avvio in una nuova scheda o finestra del browser."
#: authentik/core/models.py
msgid "Hide this application from the user's My applications page."
msgstr ""
#: authentik/core/models.py
msgid "Application"
msgstr "Applicazione"
@@ -626,14 +597,6 @@ msgstr "Rimuovi gli utenti temporanei creati da SAML Sources."
msgid "Go home"
msgstr "Vai alla pagina iniziale"
#: authentik/core/templates/login/base_full.html
msgid "Site footer"
msgstr ""
#: authentik/core/templates/login/base_full.html
msgid "Flow links"
msgstr ""
#: authentik/core/templates/login/base_full.html
#: authentik/flows/templates/if/flow-sfe.html
msgid "Powered by authentik"
@@ -740,10 +703,6 @@ msgstr ""
msgid "Discover, import and update certificates from the filesystem."
msgstr "Scopri, importa e aggiorna i certificati dal file system."
#: authentik/endpoints/api/stages.py
msgid "Selected connector is not compatible with this stage."
msgstr ""
#: authentik/endpoints/connectors/agent/api/connectors.py
msgid "Selected platform not supported"
msgstr ""
@@ -798,14 +757,6 @@ msgstr ""
msgid "Apple Nonces"
msgstr ""
#: authentik/endpoints/connectors/agent/models.py
msgid "Apple Independent Secure Enclave"
msgstr ""
#: authentik/endpoints/connectors/agent/models.py
msgid "Apple Independent Secure Enclaves"
msgstr ""
#: authentik/endpoints/facts.py
msgid "Operating System name, such as 'Server 2022' or 'Ubuntu'"
msgstr ""
@@ -876,12 +827,6 @@ msgstr "Versione Enterprise richiesta per creare/aggiornare questo oggetto"
msgid "Enterprise is required to use this endpoint."
msgstr ""
#: authentik/enterprise/audit/apps.py
msgid ""
"Include additional information in audit logs, may incur a performance "
"penalty."
msgstr ""
#: authentik/enterprise/endpoints/connectors/fleet/models.py
#: authentik/events/models.py
msgid ""
@@ -899,19 +844,6 @@ msgstr ""
msgid "Fleet Connectors"
msgstr ""
#: authentik/enterprise/endpoints/connectors/google_chrome/models.py
msgid "Google Device Trust Connector"
msgstr ""
#: authentik/enterprise/endpoints/connectors/google_chrome/models.py
msgid "Google Device Trust Connectors"
msgstr ""
#: authentik/enterprise/endpoints/connectors/google_chrome/stage.py
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/stage.py
msgid "Verifying your browser..."
msgstr "Verifica del tuo browser..."
#: authentik/enterprise/lifecycle/api/reviews.py
msgid "You are not allowed to submit a review for this object."
msgstr ""
@@ -928,6 +860,10 @@ msgstr ""
msgid "Grace period must be shorter than the interval."
msgstr ""
#: authentik/enterprise/lifecycle/api/rules.py
msgid "Only one type-wide rule for each object type is allowed."
msgstr ""
#: authentik/enterprise/lifecycle/models.py
msgid ""
"Select which transports should be used to notify the reviewers. If none are "
@@ -955,8 +891,7 @@ msgid "Go to {self._get_model_name()}"
msgstr ""
#: authentik/enterprise/lifecycle/models.py
msgid ""
"Access review is due for {self.content_type.name.lower()} {object_label}"
msgid "Access review is due for {self.content_type.name} {str(self.object)}"
msgstr ""
#: authentik/enterprise/lifecycle/models.py
@@ -970,7 +905,7 @@ msgid ""
msgstr ""
#: authentik/enterprise/lifecycle/tasks.py
msgid "Dispatch tasks to apply lifecycle rules."
msgid "Dispatch tasks to validate lifecycle rules."
msgstr ""
#: authentik/enterprise/lifecycle/tasks.py
@@ -1218,14 +1153,6 @@ msgstr "Per Enterprise è tenuta a utilizzare EAP-TLS."
msgid "Enterprise is required to use the OAuth mode."
msgstr "Per Enterprise è obbligatorio utilizzare la modalità OAuth."
#: authentik/enterprise/providers/ssf/models.py
msgid "SSF RFC Push"
msgstr ""
#: authentik/enterprise/providers/ssf/models.py
msgid "SSF RFC Pull"
msgstr ""
#: authentik/enterprise/providers/ssf/models.py
#: authentik/providers/oauth2/models.py
msgid "Signing Key"
@@ -1307,78 +1234,6 @@ msgstr ""
msgid "Generate data export."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "User to lock. If omitted, locks the current user (self-service)."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "No lockdown flow configured."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Lockdown flow is not applicable."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Choose the target account, then return a flow link."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "No lockdown flow configured or the flow is not applicable"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Permission denied (when targeting another user)"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Deactivate the user account (set is_active to False)"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Set an unusable password for the user"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Delete all active sessions for the user"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid ""
"Revoke all tokens for the user (API, app password, recovery, verification, "
"OAuth)"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid ""
"Flow to redirect users to after self-service lockdown. This flow should not "
"require authentication since the user's session is deleted."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Account Lockdown Stage"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Account Lockdown Stages"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "No target user specified for account lockdown"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "You do not have permission to lock down this account."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "Account lockdown failed for this account."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "Self-service account lockdown requires a completion flow."
msgstr ""
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/models.py
msgid "Endpoint Authenticator Google Device Trust Connector Stage"
msgstr ""
@@ -1397,6 +1252,10 @@ msgstr "Dispositivo di Accesso"
msgid "Endpoint Devices"
msgstr "Dispositivi di Accesso"
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/stage.py
msgid "Verifying your browser..."
msgstr "Verifica del tuo browser..."
#: authentik/enterprise/stages/mtls/models.py
msgid ""
"Configure certificate authorities to validate the certificate against. This "
@@ -1480,12 +1339,6 @@ msgstr ""
"Invia una notifica solo una volta, ad esempio quando invii un webhook in un "
"canale di chat."
#: authentik/events/models.py
msgid ""
"When set, the selected ceritifcate is used to validate the certificate of "
"the webhook server."
msgstr ""
#: authentik/events/models.py
msgid ""
"Customize the body of the request. Mapping should return data that is JSON-"
@@ -1656,15 +1509,6 @@ msgstr "Politiche pre-flusso"
msgid "Flow"
msgstr "Flusso"
#: authentik/flows/apps.py
msgid "Refresh other tabs after successful authentication."
msgstr ""
#: authentik/flows/apps.py
msgid ""
"Upon successful authentication, re-start authentication in other open tabs."
msgstr ""
#: authentik/flows/exceptions.py
msgid "Flow does not apply to current user."
msgstr "Il flusso non si applica all'utente corrente."
@@ -1779,8 +1623,8 @@ msgstr "Token del flusso"
msgid "Flow Tokens"
msgstr "Tokens del flusso"
#: authentik/flows/planner.py
msgid "This link is invalid or has expired. Please request a new one."
#: authentik/flows/templates/if/flow.html
msgid "Site footer"
msgstr ""
#: authentik/flows/views/executor.py
@@ -2169,6 +2013,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"
@@ -2296,14 +2156,6 @@ msgstr "Confronto URL rigoroso"
msgid "Regular Expression URL matching"
msgstr "Corrispondenza URL espressione regolare"
#: authentik/providers/oauth2/models.py
msgid "Authorization"
msgstr ""
#: authentik/providers/oauth2/models.py
msgid "Logout"
msgstr ""
#: authentik/providers/oauth2/models.py
msgid "Back-channel"
msgstr ""
@@ -2666,6 +2518,10 @@ msgstr "Provider Proxy"
msgid "Proxy Providers"
msgstr "Providers Proxy"
#: authentik/providers/proxy/tasks.py
msgid "Terminate session on Proxy outpost."
msgstr ""
#: authentik/providers/rac/models.py authentik/stages/user_login/models.py
msgid ""
"Determines how long a session lasts. Default of 0 means that the sessions "
@@ -2797,10 +2653,8 @@ msgstr ""
"vuoto, non verrà aggiunta alcuna restrizione sul pubblico."
#: authentik/providers/saml/models.py
msgid ""
"Also known as EntityID. Providing a value overrides the default issuer "
"generated by authentik."
msgstr ""
msgid "Also known as EntityID"
msgstr "Conosciuto anche come EntityID"
#: authentik/providers/saml/models.py
msgid "SLS URL"
@@ -3014,10 +2868,6 @@ msgstr ""
msgid "SAML NameID format"
msgstr ""
#: authentik/providers/saml/models.py
msgid "SAML Issuer used for this session"
msgstr ""
#: authentik/providers/saml/models.py
msgid "SAML Session"
msgstr "Sessione SAML "
@@ -3046,14 +2896,6 @@ msgstr "Slack"
msgid "Salesforce"
msgstr ""
#: authentik/providers/scim/models.py
msgid "Webex"
msgstr ""
#: authentik/providers/scim/models.py
msgid "vCenter"
msgstr ""
#: authentik/providers/scim/models.py
msgid "Group filters used to define sync-scope for groups."
msgstr ""
@@ -3335,7 +3177,7 @@ msgstr ""
" e di aver configurato correttamente il browser. \n"
"Contatta il tuo amministratore."
#: authentik/sources/ldap/api/sources.py
#: authentik/sources/ldap/api.py
msgid "Only a single LDAP Source with password synchronization is allowed"
msgstr ""
"È consentita solo una singola sorgente LDAP con sincronizzazione della "
@@ -3869,12 +3711,6 @@ msgstr ""
"rappresentare un rischio per la sicurezza, poiché non viene eseguita alcuna "
"convalida dell'ID richiesta."
#: authentik/sources/saml/models.py
msgid ""
"When enabled, the IdP will re-authenticate the user even if a session "
"exists."
msgstr ""
#: authentik/sources/saml/models.py
msgid ""
"NameID Policy sent to the IdP. Can be unset, in which case no Policy is "
@@ -4306,10 +4142,6 @@ msgstr "Fasi di convalida dell'autenticatore"
msgid "No (allowed) MFA authenticator configured."
msgstr "Nessun autenticatore MFA (consentito) configurato."
#: authentik/stages/authenticator_webauthn/models.py
msgid "When enabled, a given device can only be registered once."
msgstr ""
#: authentik/stages/authenticator_webauthn/models.py
msgid "WebAuthn Authenticator Setup Stage"
msgstr "Fase di configurazione dell'autenticatore WebAuthn"
@@ -4448,10 +4280,6 @@ msgstr "Email OTP"
msgid "Event Notification"
msgstr "Notifica evento"
#: authentik/stages/email/models.py authentik/stages/invitation/models.py
msgid "Invitation"
msgstr "Invito"
#: authentik/stages/email/models.py
msgid ""
"The time window used to count recent account recovery attempts. If the "
@@ -4570,62 +4398,6 @@ msgstr ""
"\n"
"Questa email è stata inviata dal trasporto delle notifiche %(name)s.\n"
#: authentik/stages/email/templates/email/invitation.html
msgid ""
"\n"
" You're Invited!\n"
" "
msgstr ""
#: authentik/stages/email/templates/email/invitation.html
#, python-format
msgid ""
"\n"
" You have been invited to join %(host)s. Click the button below to get started.\n"
" "
msgstr ""
#: authentik/stages/email/templates/email/invitation.html
#, python-format
msgid ""
"\n"
" This invitation expires %(expires)s.\n"
" "
msgstr ""
#: authentik/stages/email/templates/email/invitation.html
#: authentik/stages/email/templates/email/invitation.txt
msgid "Accept Invitation"
msgstr ""
#: authentik/stages/email/templates/email/invitation.html
msgid ""
"\n"
" If you cannot click the button above, please copy and paste the following URL into your browser:\n"
" "
msgstr ""
#: authentik/stages/email/templates/email/invitation.txt
msgid "You're Invited!"
msgstr ""
#: authentik/stages/email/templates/email/invitation.txt
#, python-format
msgid ""
"You have been invited to join %(host)s. Use the link below to get started."
msgstr ""
#: authentik/stages/email/templates/email/invitation.txt
#, python-format
msgid "This invitation expires %(expires)s."
msgstr ""
#: authentik/stages/email/templates/email/invitation.txt
msgid ""
"If you cannot click the link above, please copy and paste the following URL "
"into your browser:"
msgstr ""
#: authentik/stages/email/templates/email/password_reset.html
msgid ""
"\n"
@@ -4805,6 +4577,10 @@ msgstr "Se abilitato, l'invito verrà eliminato dopo l'utilizzo."
msgid "Optional fixed data to enforce on user enrollment."
msgstr "Dati fissi facoltativi da applicare alla registrazione dell'utente."
#: authentik/stages/invitation/models.py
msgid "Invitation"
msgstr "Invito"
#: authentik/stages/invitation/models.py
msgid "Invitations"
msgstr "Inviti"
@@ -4930,18 +4706,6 @@ msgstr ""
msgid "Static: Static value, displayed as-is."
msgstr "Statico: Valore statico, visualizzato così com'è."
#: authentik/stages/prompt/models.py
msgid "Alert (Info): Static alert box with info styling"
msgstr ""
#: authentik/stages/prompt/models.py
msgid "Alert (Warning): Static alert box with warning styling"
msgstr ""
#: authentik/stages/prompt/models.py
msgid "Alert (Danger): Static alert box with danger styling"
msgstr ""
#: authentik/stages/prompt/models.py
msgid "authentik: Selection of locales authentik supports"
msgstr ""

Binary file not shown.

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-05-06 00:27+0000\n"
"POT-Creation-Date: 2026-02-10 19:27+0000\n"
"PO-Revision-Date: 2025-12-01 19:09+0000\n"
"Last-Translator: Marc Schmitt, 2025\n"
"Language-Team: Japanese (Japan) (https://app.transifex.com/authentik/teams/119923/ja_JP/)\n"
@@ -105,14 +105,6 @@ msgstr "検証エラー"
msgid "Blueprint file does not exist"
msgstr "ブループリントファイルがありません"
#: authentik/blueprints/api.py
msgid "Context must be valid JSON"
msgstr ""
#: authentik/blueprints/api.py
msgid "Context must be a JSON object"
msgstr ""
#: authentik/blueprints/api.py
msgid "Failed to validate blueprint"
msgstr "ブループリントの検証に失敗しました"
@@ -121,11 +113,6 @@ msgstr "ブループリントの検証に失敗しました"
msgid "Either path or content must be set."
msgstr "パスかコンテンツの設定は必須です。"
#: authentik/blueprints/api.py
#, python-brace-format
msgid "User lacks permission to create {model}"
msgstr "ユーザーは {model} を作成するための権限がありません"
#: authentik/blueprints/models.py
msgid "Managed by authentik"
msgstr "Managed by authentik"
@@ -241,13 +228,10 @@ msgid ""
msgstr ""
"設定されていない場合、すべてのプロバイダーが返されます。trueに設定すると、バックチャネルプロバイダーのみが返されます。falseに設定すると、バックチャネルプロバイダーは除外されます"
#: authentik/core/api/users.py
msgid "Invalid password hash format. Must be a valid Django password hash."
msgstr ""
#: authentik/core/api/users.py
msgid "Cannot set both password and password_hash. Use only one."
msgstr ""
#: authentik/core/api/transactional_applications.py
#, python-brace-format
msgid "User lacks permission to create {model}"
msgstr "ユーザーは {model} を作成するための権限がありません"
#: authentik/core/api/users.py
msgid "No leading or trailing slashes allowed."
@@ -309,12 +293,6 @@ msgstr ""
msgid "This field is required."
msgstr "このフィールドは必須です。"
#: authentik/core/apps.py
msgid ""
"Configure if applications without any policy/group/user bindings should be "
"accessible to any user."
msgstr ""
#: authentik/core/models.py
msgid "name"
msgstr "名前"
@@ -417,10 +395,6 @@ msgstr "URLで使用される内部アプリ名。"
msgid "Open launch URL in a new browser tab or window."
msgstr "ブラウザーの新しいタブまたはウィンドウで起動URLを開きます。"
#: authentik/core/models.py
msgid "Hide this application from the user's My applications page."
msgstr ""
#: authentik/core/models.py
msgid "Application"
msgstr "アプリ"
@@ -592,14 +566,6 @@ msgstr "SAMLで作成された一時ユーザを削除。"
msgid "Go home"
msgstr "ホームに戻る"
#: authentik/core/templates/login/base_full.html
msgid "Site footer"
msgstr ""
#: authentik/core/templates/login/base_full.html
msgid "Flow links"
msgstr ""
#: authentik/core/templates/login/base_full.html
#: authentik/flows/templates/if/flow-sfe.html
msgid "Powered by authentik"
@@ -704,10 +670,6 @@ msgstr ""
msgid "Discover, import and update certificates from the filesystem."
msgstr "証明書をファイルシステムから検出、インポート、更新する。"
#: authentik/endpoints/api/stages.py
msgid "Selected connector is not compatible with this stage."
msgstr ""
#: authentik/endpoints/connectors/agent/api/connectors.py
msgid "Selected platform not supported"
msgstr ""
@@ -762,14 +724,6 @@ msgstr ""
msgid "Apple Nonces"
msgstr ""
#: authentik/endpoints/connectors/agent/models.py
msgid "Apple Independent Secure Enclave"
msgstr ""
#: authentik/endpoints/connectors/agent/models.py
msgid "Apple Independent Secure Enclaves"
msgstr ""
#: authentik/endpoints/facts.py
msgid "Operating System name, such as 'Server 2022' or 'Ubuntu'"
msgstr ""
@@ -840,12 +794,6 @@ msgstr "このオブジェクトの作成/更新にはエンタープライズ
msgid "Enterprise is required to use this endpoint."
msgstr ""
#: authentik/enterprise/audit/apps.py
msgid ""
"Include additional information in audit logs, may incur a performance "
"penalty."
msgstr ""
#: authentik/enterprise/endpoints/connectors/fleet/models.py
#: authentik/events/models.py
msgid ""
@@ -861,19 +809,6 @@ msgstr ""
msgid "Fleet Connectors"
msgstr ""
#: authentik/enterprise/endpoints/connectors/google_chrome/models.py
msgid "Google Device Trust Connector"
msgstr ""
#: authentik/enterprise/endpoints/connectors/google_chrome/models.py
msgid "Google Device Trust Connectors"
msgstr ""
#: authentik/enterprise/endpoints/connectors/google_chrome/stage.py
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/stage.py
msgid "Verifying your browser..."
msgstr "ブラウザの確認中...。"
#: authentik/enterprise/lifecycle/api/reviews.py
msgid "You are not allowed to submit a review for this object."
msgstr ""
@@ -890,6 +825,10 @@ msgstr ""
msgid "Grace period must be shorter than the interval."
msgstr ""
#: authentik/enterprise/lifecycle/api/rules.py
msgid "Only one type-wide rule for each object type is allowed."
msgstr ""
#: authentik/enterprise/lifecycle/models.py
msgid ""
"Select which transports should be used to notify the reviewers. If none are "
@@ -917,8 +856,7 @@ msgid "Go to {self._get_model_name()}"
msgstr ""
#: authentik/enterprise/lifecycle/models.py
msgid ""
"Access review is due for {self.content_type.name.lower()} {object_label}"
msgid "Access review is due for {self.content_type.name} {str(self.object)}"
msgstr ""
#: authentik/enterprise/lifecycle/models.py
@@ -932,7 +870,7 @@ msgid ""
msgstr ""
#: authentik/enterprise/lifecycle/tasks.py
msgid "Dispatch tasks to apply lifecycle rules."
msgid "Dispatch tasks to validate lifecycle rules."
msgstr ""
#: authentik/enterprise/lifecycle/tasks.py
@@ -1157,14 +1095,6 @@ msgstr "EAP-TLSを使用するにはエンタープライズが必要です。"
msgid "Enterprise is required to use the OAuth mode."
msgstr "OAuthモードを使用するにはエンタープライズが必要です。"
#: authentik/enterprise/providers/ssf/models.py
msgid "SSF RFC Push"
msgstr ""
#: authentik/enterprise/providers/ssf/models.py
msgid "SSF RFC Pull"
msgstr ""
#: authentik/enterprise/providers/ssf/models.py
#: authentik/providers/oauth2/models.py
msgid "Signing Key"
@@ -1246,78 +1176,6 @@ msgstr ""
msgid "Generate data export."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "User to lock. If omitted, locks the current user (self-service)."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "No lockdown flow configured."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Lockdown flow is not applicable."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Choose the target account, then return a flow link."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "No lockdown flow configured or the flow is not applicable"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Permission denied (when targeting another user)"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Deactivate the user account (set is_active to False)"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Set an unusable password for the user"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Delete all active sessions for the user"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid ""
"Revoke all tokens for the user (API, app password, recovery, verification, "
"OAuth)"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid ""
"Flow to redirect users to after self-service lockdown. This flow should not "
"require authentication since the user's session is deleted."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Account Lockdown Stage"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Account Lockdown Stages"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "No target user specified for account lockdown"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "You do not have permission to lock down this account."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "Account lockdown failed for this account."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "Self-service account lockdown requires a completion flow."
msgstr ""
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/models.py
msgid "Endpoint Authenticator Google Device Trust Connector Stage"
msgstr "エンドポイント認証器Google Device Trust Connectorステージ"
@@ -1334,6 +1192,10 @@ msgstr "エンドポイントデバイス"
msgid "Endpoint Devices"
msgstr "エンドポイントデバイス"
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/stage.py
msgid "Verifying your browser..."
msgstr "ブラウザの確認中...。"
#: authentik/enterprise/stages/mtls/models.py
msgid ""
"Configure certificate authorities to validate the certificate against. This "
@@ -1411,12 +1273,6 @@ msgid ""
"channel."
msgstr "チャットチャンネルにWebhookを送るときのような場合に、一度だけ通知を送信します。"
#: authentik/events/models.py
msgid ""
"When set, the selected ceritifcate is used to validate the certificate of "
"the webhook server."
msgstr ""
#: authentik/events/models.py
msgid ""
"Customize the body of the request. Mapping should return data that is JSON-"
@@ -1576,15 +1432,6 @@ msgstr "事前フローのポリシー"
msgid "Flow"
msgstr "フロー"
#: authentik/flows/apps.py
msgid "Refresh other tabs after successful authentication."
msgstr ""
#: authentik/flows/apps.py
msgid ""
"Upon successful authentication, re-start authentication in other open tabs."
msgstr ""
#: authentik/flows/exceptions.py
msgid "Flow does not apply to current user."
msgstr "フローは現在のユーザーに適用されません。"
@@ -1689,8 +1536,8 @@ msgstr "フロートークン"
msgid "Flow Tokens"
msgstr "フロートークン"
#: authentik/flows/planner.py
msgid "This link is invalid or has expired. Please request a new one."
#: authentik/flows/templates/if/flow.html
msgid "Site footer"
msgstr ""
#: authentik/flows/views/executor.py
@@ -2050,6 +1897,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 "権限がありません"
@@ -2170,14 +2031,6 @@ msgstr "厳密な URL 比較"
msgid "Regular Expression URL matching"
msgstr "正規表現 URL マッチング"
#: authentik/providers/oauth2/models.py
msgid "Authorization"
msgstr ""
#: authentik/providers/oauth2/models.py
msgid "Logout"
msgstr ""
#: authentik/providers/oauth2/models.py
msgid "Back-channel"
msgstr "バックチャネル"
@@ -2505,6 +2358,10 @@ msgstr "プロキシプロバイダー"
msgid "Proxy Providers"
msgstr "プロキシプロバイダー"
#: authentik/providers/proxy/tasks.py
msgid "Terminate session on Proxy outpost."
msgstr "Proxy Outpost でセッションを終了。"
#: authentik/providers/rac/models.py authentik/stages/user_login/models.py
msgid ""
"Determines how long a session lasts. Default of 0 means that the sessions "
@@ -2628,10 +2485,8 @@ msgid ""
msgstr "アサーションのオーディエンス制限フィールドの値。空の場合、オーディエンス制限は追加されません。"
#: authentik/providers/saml/models.py
msgid ""
"Also known as EntityID. Providing a value overrides the default issuer "
"generated by authentik."
msgstr ""
msgid "Also known as EntityID"
msgstr "EntityID とも呼ばれる"
#: authentik/providers/saml/models.py
msgid "SLS URL"
@@ -2831,10 +2686,6 @@ msgstr "このセッションの SAML NameID 値"
msgid "SAML NameID format"
msgstr "SAML NameID フォーマット"
#: authentik/providers/saml/models.py
msgid "SAML Issuer used for this session"
msgstr ""
#: authentik/providers/saml/models.py
msgid "SAML Session"
msgstr "SAML セッション"
@@ -2863,14 +2714,6 @@ msgstr "Slack"
msgid "Salesforce"
msgstr "Salesforce"
#: authentik/providers/scim/models.py
msgid "Webex"
msgstr ""
#: authentik/providers/scim/models.py
msgid "vCenter"
msgstr ""
#: authentik/providers/scim/models.py
msgid "Group filters used to define sync-scope for groups."
msgstr ""
@@ -3135,7 +2978,7 @@ msgstr ""
" 管理者に連絡してください。\n"
" "
#: authentik/sources/ldap/api/sources.py
#: authentik/sources/ldap/api.py
msgid "Only a single LDAP Source with password synchronization is allowed"
msgstr "パスワード同期を備えた単一の LDAP ソースのみが許可されます"
@@ -3648,12 +3491,6 @@ msgid ""
"risk, as no validation of the request ID is done."
msgstr "IdP によって開始される認証フローを許可します。リクエスト ID の検証が行われないため、セキュリティリスクになる可能性があります。"
#: authentik/sources/saml/models.py
msgid ""
"When enabled, the IdP will re-authenticate the user even if a session "
"exists."
msgstr ""
#: authentik/sources/saml/models.py
msgid ""
"NameID Policy sent to the IdP. Can be unset, in which case no Policy is "
@@ -4057,10 +3894,6 @@ msgstr "認証器検証ステージ"
msgid "No (allowed) MFA authenticator configured."
msgstr "許可されたMFA 認証器が設定されていません。"
#: authentik/stages/authenticator_webauthn/models.py
msgid "When enabled, a given device can only be registered once."
msgstr ""
#: authentik/stages/authenticator_webauthn/models.py
msgid "WebAuthn Authenticator Setup Stage"
msgstr "WebAuthn 認証器セットアップステージ"
@@ -4193,10 +4026,6 @@ msgstr "メール OTP"
msgid "Event Notification"
msgstr "イベント通知"
#: authentik/stages/email/models.py authentik/stages/invitation/models.py
msgid "Invitation"
msgstr "招待"
#: authentik/stages/email/models.py
msgid ""
"The time window used to count recent account recovery attempts. If the "
@@ -4309,62 +4138,6 @@ msgstr ""
"\n"
"このメールは通知トランスポート %(name)s から送信されました。\n"
#: authentik/stages/email/templates/email/invitation.html
msgid ""
"\n"
" You're Invited!\n"
" "
msgstr ""
#: authentik/stages/email/templates/email/invitation.html
#, python-format
msgid ""
"\n"
" You have been invited to join %(host)s. Click the button below to get started.\n"
" "
msgstr ""
#: authentik/stages/email/templates/email/invitation.html
#, python-format
msgid ""
"\n"
" This invitation expires %(expires)s.\n"
" "
msgstr ""
#: authentik/stages/email/templates/email/invitation.html
#: authentik/stages/email/templates/email/invitation.txt
msgid "Accept Invitation"
msgstr ""
#: authentik/stages/email/templates/email/invitation.html
msgid ""
"\n"
" If you cannot click the button above, please copy and paste the following URL into your browser:\n"
" "
msgstr ""
#: authentik/stages/email/templates/email/invitation.txt
msgid "You're Invited!"
msgstr ""
#: authentik/stages/email/templates/email/invitation.txt
#, python-format
msgid ""
"You have been invited to join %(host)s. Use the link below to get started."
msgstr ""
#: authentik/stages/email/templates/email/invitation.txt
#, python-format
msgid "This invitation expires %(expires)s."
msgstr ""
#: authentik/stages/email/templates/email/invitation.txt
msgid ""
"If you cannot click the link above, please copy and paste the following URL "
"into your browser:"
msgstr ""
#: authentik/stages/email/templates/email/password_reset.html
msgid ""
"\n"
@@ -4528,6 +4301,10 @@ msgstr "有効にすると、招待は使用後に削除されます。"
msgid "Optional fixed data to enforce on user enrollment."
msgstr "ユーザー登録に強制するオプショナル固定データ。"
#: authentik/stages/invitation/models.py
msgid "Invitation"
msgstr "招待"
#: authentik/stages/invitation/models.py
msgid "Invitations"
msgstr "招待"
@@ -4638,18 +4415,6 @@ msgstr "非表示: 非表示フィールド、フォームにデータを挿入
msgid "Static: Static value, displayed as-is."
msgstr "静的: 静的値、そのまま表示。"
#: authentik/stages/prompt/models.py
msgid "Alert (Info): Static alert box with info styling"
msgstr ""
#: authentik/stages/prompt/models.py
msgid "Alert (Warning): Static alert box with warning styling"
msgstr ""
#: authentik/stages/prompt/models.py
msgid "Alert (Danger): Static alert box with danger styling"
msgstr ""
#: authentik/stages/prompt/models.py
msgid "authentik: Selection of locales authentik supports"
msgstr "authentik: authentik がサポートするロケールの選択"

View File

@@ -12,7 +12,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-05-06 00:27+0000\n"
"POT-Creation-Date: 2026-04-23 00:25+0000\n"
"PO-Revision-Date: 2025-12-01 19:09+0000\n"
"Last-Translator: Raphael Cancelliere, 2026\n"
"Language-Team: Norwegian (Norway) (https://app.transifex.com/authentik/teams/119923/no_NO/)\n"
@@ -109,14 +109,6 @@ msgstr "Valideringsfeil"
msgid "Blueprint file does not exist"
msgstr "Blueprint-filen eksisterer ikke"
#: authentik/blueprints/api.py
msgid "Context must be valid JSON"
msgstr ""
#: authentik/blueprints/api.py
msgid "Context must be a JSON object"
msgstr ""
#: authentik/blueprints/api.py
msgid "Failed to validate blueprint"
msgstr "Klarte ikke å validere blueprint"
@@ -255,14 +247,6 @@ msgstr ""
" kun backchannel-leverandører. Når satt til false, ekskluderes backchannel-"
"leverandører."
#: authentik/core/api/users.py
msgid "Invalid password hash format. Must be a valid Django password hash."
msgstr ""
#: authentik/core/api/users.py
msgid "Cannot set both password and password_hash. Use only one."
msgstr ""
#: authentik/core/api/users.py
msgid "No leading or trailing slashes allowed."
msgstr "Ingen skråstreker i starten eller slutten er tillatt."
@@ -437,10 +421,6 @@ msgstr "Internt applikasjonsnavn, brukt i URL-er."
msgid "Open launch URL in a new browser tab or window."
msgstr "Åpne start-URL i en ny nettleserfane eller -vindu."
#: authentik/core/models.py
msgid "Hide this application from the user's My applications page."
msgstr ""
#: authentik/core/models.py
msgid "Application"
msgstr "Applikasjon"
@@ -937,6 +917,10 @@ msgstr "Enten en vurderingsgruppe eller en vurderer må være angitt."
msgid "Grace period must be shorter than the interval."
msgstr "Respittiden må være kortere enn intervallet."
#: authentik/enterprise/lifecycle/api/rules.py
msgid "Only one type-wide rule for each object type is allowed."
msgstr "Kun én type-omfattende regel for hver objekttype er tillatt."
#: authentik/enterprise/lifecycle/models.py
msgid ""
"Select which transports should be used to notify the reviewers. If none are "
@@ -966,9 +950,9 @@ msgid "Go to {self._get_model_name()}"
msgstr "Gå til {self._get_model_name()}"
#: authentik/enterprise/lifecycle/models.py
msgid ""
"Access review is due for {self.content_type.name.lower()} {object_label}"
msgid "Access review is due for {self.content_type.name} {str(self.object)}"
msgstr ""
"Tilgangsvurdering forfaller for {self.content_type.name} {str(self.object)}"
#: authentik/enterprise/lifecycle/models.py
msgid ""
@@ -984,8 +968,8 @@ msgstr ""
"Tilgangsvurdering fullført for {self.content_type.name} {str(self.object)}"
#: authentik/enterprise/lifecycle/tasks.py
msgid "Dispatch tasks to apply lifecycle rules."
msgstr ""
msgid "Dispatch tasks to validate lifecycle rules."
msgstr "Send ut oppgaver for å validere livssyklusregler."
#: authentik/enterprise/lifecycle/tasks.py
msgid "Apply lifecycle rule."
@@ -1321,78 +1305,6 @@ msgstr "Last ned"
msgid "Generate data export."
msgstr "Generer eksport av data."
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "User to lock. If omitted, locks the current user (self-service)."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "No lockdown flow configured."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Lockdown flow is not applicable."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Choose the target account, then return a flow link."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "No lockdown flow configured or the flow is not applicable"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Permission denied (when targeting another user)"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Deactivate the user account (set is_active to False)"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Set an unusable password for the user"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Delete all active sessions for the user"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid ""
"Revoke all tokens for the user (API, app password, recovery, verification, "
"OAuth)"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid ""
"Flow to redirect users to after self-service lockdown. This flow should not "
"require authentication since the user's session is deleted."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Account Lockdown Stage"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Account Lockdown Stages"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "No target user specified for account lockdown"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "You do not have permission to lock down this account."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "Account lockdown failed for this account."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "Self-service account lockdown requires a completion flow."
msgstr ""
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/models.py
msgid "Endpoint Authenticator Google Device Trust Connector Stage"
msgstr "Trinn for endepunktautentisering via Google Device Trust-kobling"
@@ -2793,10 +2705,8 @@ msgstr ""
" vil ingen målgrupperestriksjon bli lagt til."
#: authentik/providers/saml/models.py
msgid ""
"Also known as EntityID. Providing a value overrides the default issuer "
"generated by authentik."
msgstr ""
msgid "Also known as EntityID"
msgstr "Også kjent som EntityID"
#: authentik/providers/saml/models.py
msgid "SLS URL"
@@ -3014,10 +2924,6 @@ msgstr "SAML NameID-verdi for denne økten"
msgid "SAML NameID format"
msgstr "SAML NameID-format"
#: authentik/providers/saml/models.py
msgid "SAML Issuer used for this session"
msgstr ""
#: authentik/providers/saml/models.py
msgid "SAML Session"
msgstr "SAML-økt"
@@ -3050,10 +2956,6 @@ msgstr "Salesforce"
msgid "Webex"
msgstr "Webex"
#: authentik/providers/scim/models.py
msgid "vCenter"
msgstr ""
#: authentik/providers/scim/models.py
msgid "Group filters used to define sync-scope for groups."
msgstr "Gruppefiltre brukt for å definere synkroniseringsomfang for grupper."
@@ -4928,18 +4830,6 @@ msgstr "Skjult: Skjult felt, kan brukes til å sette inn data i skjemaet."
msgid "Static: Static value, displayed as-is."
msgstr "Statisk: Statisk verdi, vises som den er."
#: authentik/stages/prompt/models.py
msgid "Alert (Info): Static alert box with info styling"
msgstr ""
#: authentik/stages/prompt/models.py
msgid "Alert (Warning): Static alert box with warning styling"
msgstr ""
#: authentik/stages/prompt/models.py
msgid "Alert (Danger): Static alert box with danger styling"
msgstr ""
#: authentik/stages/prompt/models.py
msgid "authentik: Selection of locales authentik supports"
msgstr "authentik: Utvalg av språk som authentik støtter"

Binary file not shown.

View File

@@ -6,16 +6,16 @@
# Translators:
# Marc Schmitt, 2025
# Darek “NeroPcStation” NeroPcStation <dareknowacki2001@gmail.com>, 2025
# Jens L. <jens@goauthentik.io>, 2026
# Jens L. <jens@goauthentik.io>, 2025
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-05-06 00:27+0000\n"
"POT-Creation-Date: 2026-02-10 19:27+0000\n"
"PO-Revision-Date: 2025-12-01 19:09+0000\n"
"Last-Translator: Jens L. <jens@goauthentik.io>, 2026\n"
"Last-Translator: Jens L. <jens@goauthentik.io>, 2025\n"
"Language-Team: Polish (Poland) (https://app.transifex.com/authentik/teams/119923/pl_PL/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -107,14 +107,6 @@ msgstr "Błąd walidacji"
msgid "Blueprint file does not exist"
msgstr "Plik szablonu nie istnieje"
#: authentik/blueprints/api.py
msgid "Context must be valid JSON"
msgstr ""
#: authentik/blueprints/api.py
msgid "Context must be a JSON object"
msgstr ""
#: authentik/blueprints/api.py
msgid "Failed to validate blueprint"
msgstr "Nie udało się zweryfikować szablonu"
@@ -123,11 +115,6 @@ msgstr "Nie udało się zweryfikować szablonu"
msgid "Either path or content must be set."
msgstr "Ścieżka albo treść muszą być ustawione."
#: authentik/blueprints/api.py
#, python-brace-format
msgid "User lacks permission to create {model}"
msgstr ""
#: authentik/blueprints/models.py
msgid "Managed by authentik"
msgstr "Zarządzane przez authentik"
@@ -254,12 +241,9 @@ msgstr ""
"zwracani są tylko dostawcy kanału zwrotnego. Gdy ustawiono na fałsz, "
"dostawcy kanału zwrotnego są wykluczeni."
#: authentik/core/api/users.py
msgid "Invalid password hash format. Must be a valid Django password hash."
msgstr ""
#: authentik/core/api/users.py
msgid "Cannot set both password and password_hash. Use only one."
#: authentik/core/api/transactional_applications.py
#, python-brace-format
msgid "User lacks permission to create {model}"
msgstr ""
#: authentik/core/api/users.py
@@ -322,12 +306,6 @@ msgstr ""
msgid "This field is required."
msgstr ""
#: authentik/core/apps.py
msgid ""
"Configure if applications without any policy/group/user bindings should be "
"accessible to any user."
msgstr ""
#: authentik/core/models.py
msgid "name"
msgstr "nazwa"
@@ -434,10 +412,6 @@ msgstr "Wewnętrzna nazwa aplikacji, używana w adresach URL."
msgid "Open launch URL in a new browser tab or window."
msgstr "Otwórz adres URL uruchamiania w nowej karcie lub oknie przeglądarki."
#: authentik/core/models.py
msgid "Hide this application from the user's My applications page."
msgstr ""
#: authentik/core/models.py
msgid "Application"
msgstr "Aplikacja"
@@ -622,14 +596,6 @@ msgstr ""
msgid "Go home"
msgstr "Przejdź do domu"
#: authentik/core/templates/login/base_full.html
msgid "Site footer"
msgstr ""
#: authentik/core/templates/login/base_full.html
msgid "Flow links"
msgstr ""
#: authentik/core/templates/login/base_full.html
#: authentik/flows/templates/if/flow-sfe.html
msgid "Powered by authentik"
@@ -736,10 +702,6 @@ msgstr ""
msgid "Discover, import and update certificates from the filesystem."
msgstr ""
#: authentik/endpoints/api/stages.py
msgid "Selected connector is not compatible with this stage."
msgstr ""
#: authentik/endpoints/connectors/agent/api/connectors.py
msgid "Selected platform not supported"
msgstr ""
@@ -794,14 +756,6 @@ msgstr ""
msgid "Apple Nonces"
msgstr ""
#: authentik/endpoints/connectors/agent/models.py
msgid "Apple Independent Secure Enclave"
msgstr ""
#: authentik/endpoints/connectors/agent/models.py
msgid "Apple Independent Secure Enclaves"
msgstr ""
#: authentik/endpoints/facts.py
msgid "Operating System name, such as 'Server 2022' or 'Ubuntu'"
msgstr ""
@@ -873,12 +827,6 @@ msgstr ""
msgid "Enterprise is required to use this endpoint."
msgstr ""
#: authentik/enterprise/audit/apps.py
msgid ""
"Include additional information in audit logs, may incur a performance "
"penalty."
msgstr ""
#: authentik/enterprise/endpoints/connectors/fleet/models.py
#: authentik/events/models.py
msgid ""
@@ -894,19 +842,6 @@ msgstr ""
msgid "Fleet Connectors"
msgstr ""
#: authentik/enterprise/endpoints/connectors/google_chrome/models.py
msgid "Google Device Trust Connector"
msgstr ""
#: authentik/enterprise/endpoints/connectors/google_chrome/models.py
msgid "Google Device Trust Connectors"
msgstr ""
#: authentik/enterprise/endpoints/connectors/google_chrome/stage.py
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/stage.py
msgid "Verifying your browser..."
msgstr "Weryfikowanie Twojej przeglądarki..."
#: authentik/enterprise/lifecycle/api/reviews.py
msgid "You are not allowed to submit a review for this object."
msgstr ""
@@ -923,6 +858,10 @@ msgstr ""
msgid "Grace period must be shorter than the interval."
msgstr ""
#: authentik/enterprise/lifecycle/api/rules.py
msgid "Only one type-wide rule for each object type is allowed."
msgstr ""
#: authentik/enterprise/lifecycle/models.py
msgid ""
"Select which transports should be used to notify the reviewers. If none are "
@@ -950,8 +889,7 @@ msgid "Go to {self._get_model_name()}"
msgstr ""
#: authentik/enterprise/lifecycle/models.py
msgid ""
"Access review is due for {self.content_type.name.lower()} {object_label}"
msgid "Access review is due for {self.content_type.name} {str(self.object)}"
msgstr ""
#: authentik/enterprise/lifecycle/models.py
@@ -965,7 +903,7 @@ msgid ""
msgstr ""
#: authentik/enterprise/lifecycle/tasks.py
msgid "Dispatch tasks to apply lifecycle rules."
msgid "Dispatch tasks to validate lifecycle rules."
msgstr ""
#: authentik/enterprise/lifecycle/tasks.py
@@ -1192,14 +1130,6 @@ msgstr ""
msgid "Enterprise is required to use the OAuth mode."
msgstr ""
#: authentik/enterprise/providers/ssf/models.py
msgid "SSF RFC Push"
msgstr ""
#: authentik/enterprise/providers/ssf/models.py
msgid "SSF RFC Pull"
msgstr ""
#: authentik/enterprise/providers/ssf/models.py
#: authentik/providers/oauth2/models.py
msgid "Signing Key"
@@ -1281,78 +1211,6 @@ msgstr "Pobierz"
msgid "Generate data export."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "User to lock. If omitted, locks the current user (self-service)."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "No lockdown flow configured."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Lockdown flow is not applicable."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Choose the target account, then return a flow link."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "No lockdown flow configured or the flow is not applicable"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Permission denied (when targeting another user)"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Deactivate the user account (set is_active to False)"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Set an unusable password for the user"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Delete all active sessions for the user"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid ""
"Revoke all tokens for the user (API, app password, recovery, verification, "
"OAuth)"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid ""
"Flow to redirect users to after self-service lockdown. This flow should not "
"require authentication since the user's session is deleted."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Account Lockdown Stage"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Account Lockdown Stages"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "No target user specified for account lockdown"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "You do not have permission to lock down this account."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "Account lockdown failed for this account."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "Self-service account lockdown requires a completion flow."
msgstr ""
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/models.py
msgid "Endpoint Authenticator Google Device Trust Connector Stage"
msgstr ""
@@ -1369,6 +1227,10 @@ msgstr ""
msgid "Endpoint Devices"
msgstr ""
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/stage.py
msgid "Verifying your browser..."
msgstr "Weryfikowanie Twojej przeglądarki..."
#: authentik/enterprise/stages/mtls/models.py
msgid ""
"Configure certificate authorities to validate the certificate against. This "
@@ -1449,12 +1311,6 @@ msgstr ""
"Wyślij powiadomienie tylko raz, na przykład podczas wysyłania webhooka na "
"kanał czatu."
#: authentik/events/models.py
msgid ""
"When set, the selected ceritifcate is used to validate the certificate of "
"the webhook server."
msgstr ""
#: authentik/events/models.py
msgid ""
"Customize the body of the request. Mapping should return data that is JSON-"
@@ -1619,15 +1475,6 @@ msgstr "Przed-przepływowe zasady"
msgid "Flow"
msgstr "Przepływ"
#: authentik/flows/apps.py
msgid "Refresh other tabs after successful authentication."
msgstr ""
#: authentik/flows/apps.py
msgid ""
"Upon successful authentication, re-start authentication in other open tabs."
msgstr ""
#: authentik/flows/exceptions.py
msgid "Flow does not apply to current user."
msgstr "Przepływ nie dotyczy bieżącego użytkownika."
@@ -1743,8 +1590,8 @@ msgstr "Token przepływu"
msgid "Flow Tokens"
msgstr "Tokeny przepływu"
#: authentik/flows/planner.py
msgid "This link is invalid or has expired. Please request a new one."
#: authentik/flows/templates/if/flow.html
msgid "Site footer"
msgstr ""
#: authentik/flows/views/executor.py
@@ -2128,6 +1975,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ń"
@@ -2254,14 +2115,6 @@ msgstr ""
msgid "Regular Expression URL matching"
msgstr ""
#: authentik/providers/oauth2/models.py
msgid "Authorization"
msgstr "Autoryzacja"
#: authentik/providers/oauth2/models.py
msgid "Logout"
msgstr "Wyloguj"
#: authentik/providers/oauth2/models.py
msgid "Back-channel"
msgstr ""
@@ -2612,6 +2465,10 @@ msgstr "Dostawca proxy"
msgid "Proxy Providers"
msgstr "Dostawcy proxy"
#: authentik/providers/proxy/tasks.py
msgid "Terminate session on Proxy outpost."
msgstr ""
#: authentik/providers/rac/models.py authentik/stages/user_login/models.py
msgid ""
"Determines how long a session lasts. Default of 0 means that the sessions "
@@ -2739,10 +2596,8 @@ msgstr ""
" ograniczenie odbiorców nie zostanie dodane."
#: authentik/providers/saml/models.py
msgid ""
"Also known as EntityID. Providing a value overrides the default issuer "
"generated by authentik."
msgstr ""
msgid "Also known as EntityID"
msgstr "Znany również jako EntityID"
#: authentik/providers/saml/models.py
msgid "SLS URL"
@@ -2946,10 +2801,6 @@ msgstr ""
msgid "SAML NameID format"
msgstr ""
#: authentik/providers/saml/models.py
msgid "SAML Issuer used for this session"
msgstr ""
#: authentik/providers/saml/models.py
msgid "SAML Session"
msgstr ""
@@ -2978,14 +2829,6 @@ msgstr "Slack"
msgid "Salesforce"
msgstr ""
#: authentik/providers/scim/models.py
msgid "Webex"
msgstr ""
#: authentik/providers/scim/models.py
msgid "vCenter"
msgstr ""
#: authentik/providers/scim/models.py
msgid "Group filters used to define sync-scope for groups."
msgstr ""
@@ -3246,7 +3089,7 @@ msgid ""
" "
msgstr ""
#: authentik/sources/ldap/api/sources.py
#: authentik/sources/ldap/api.py
msgid "Only a single LDAP Source with password synchronization is allowed"
msgstr ""
@@ -3773,12 +3616,6 @@ msgstr ""
" Może to stanowić zagrożenie bezpieczeństwa, ponieważ nie przeprowadza się "
"weryfikacji identyfikatora żądania."
#: authentik/sources/saml/models.py
msgid ""
"When enabled, the IdP will re-authenticate the user even if a session "
"exists."
msgstr ""
#: authentik/sources/saml/models.py
msgid ""
"NameID Policy sent to the IdP. Can be unset, in which case no Policy is "
@@ -4192,10 +4029,6 @@ msgstr "Etapy weryfikacji uwierzytelniacza"
msgid "No (allowed) MFA authenticator configured."
msgstr "Nie skonfigurowano (dozwolonego) uwierzytelniania MFA."
#: authentik/stages/authenticator_webauthn/models.py
msgid "When enabled, a given device can only be registered once."
msgstr ""
#: authentik/stages/authenticator_webauthn/models.py
msgid "WebAuthn Authenticator Setup Stage"
msgstr "Etap konfiguracji uwierzytelniacza WebAuthn"
@@ -4332,10 +4165,6 @@ msgstr ""
msgid "Event Notification"
msgstr ""
#: authentik/stages/email/models.py authentik/stages/invitation/models.py
msgid "Invitation"
msgstr "Zaproszenie"
#: authentik/stages/email/models.py
msgid ""
"The time window used to count recent account recovery attempts. If the "
@@ -4451,62 +4280,6 @@ msgstr ""
"\n"
"Ta wiadomość e-mail została wysłana z transportu powiadomień %(name)s.\n"
#: authentik/stages/email/templates/email/invitation.html
msgid ""
"\n"
" You're Invited!\n"
" "
msgstr ""
#: authentik/stages/email/templates/email/invitation.html
#, python-format
msgid ""
"\n"
" You have been invited to join %(host)s. Click the button below to get started.\n"
" "
msgstr ""
#: authentik/stages/email/templates/email/invitation.html
#, python-format
msgid ""
"\n"
" This invitation expires %(expires)s.\n"
" "
msgstr ""
#: authentik/stages/email/templates/email/invitation.html
#: authentik/stages/email/templates/email/invitation.txt
msgid "Accept Invitation"
msgstr ""
#: authentik/stages/email/templates/email/invitation.html
msgid ""
"\n"
" If you cannot click the button above, please copy and paste the following URL into your browser:\n"
" "
msgstr ""
#: authentik/stages/email/templates/email/invitation.txt
msgid "You're Invited!"
msgstr ""
#: authentik/stages/email/templates/email/invitation.txt
#, python-format
msgid ""
"You have been invited to join %(host)s. Use the link below to get started."
msgstr ""
#: authentik/stages/email/templates/email/invitation.txt
#, python-format
msgid "This invitation expires %(expires)s."
msgstr ""
#: authentik/stages/email/templates/email/invitation.txt
msgid ""
"If you cannot click the link above, please copy and paste the following URL "
"into your browser:"
msgstr ""
#: authentik/stages/email/templates/email/password_reset.html
msgid ""
"\n"
@@ -4683,6 +4456,10 @@ msgstr "Gdy ta opcja jest włączona, zaproszenie zostanie usunięte po użyciu.
msgid "Optional fixed data to enforce on user enrollment."
msgstr "Opcjonalne stałe dane do wymuszenia przy rejestracji użytkownika."
#: authentik/stages/invitation/models.py
msgid "Invitation"
msgstr "Zaproszenie"
#: authentik/stages/invitation/models.py
msgid "Invitations"
msgstr "Zaproszenia"
@@ -4806,18 +4583,6 @@ msgstr "Ukryte: Ukryte pole, może służyć do wstawiania danych do formularza.
msgid "Static: Static value, displayed as-is."
msgstr "Statyczny: wartość statyczna, wyświetlana w stanie, w jakim jest."
#: authentik/stages/prompt/models.py
msgid "Alert (Info): Static alert box with info styling"
msgstr ""
#: authentik/stages/prompt/models.py
msgid "Alert (Warning): Static alert box with warning styling"
msgstr ""
#: authentik/stages/prompt/models.py
msgid "Alert (Danger): Static alert box with danger styling"
msgstr ""
#: authentik/stages/prompt/models.py
msgid "authentik: Selection of locales authentik supports"
msgstr "authentik: Wybór ustawień regionalnych obsługiwanych przez authentik"

View File

@@ -6,18 +6,17 @@
# Translators:
# Marc Schmitt, 2025
# André Cristian Neidert, 2025
# Rafael Mundel, 2025
# Ariel Amaral, 2025
# Rafael Mundel, 2026
# Gil Poiares-Oliveira, 2026
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-05-06 00:27+0000\n"
"POT-Creation-Date: 2026-02-10 19:27+0000\n"
"PO-Revision-Date: 2025-12-01 19:09+0000\n"
"Last-Translator: Gil Poiares-Oliveira, 2026\n"
"Last-Translator: Ariel Amaral, 2025\n"
"Language-Team: Portuguese (Brazil) (https://app.transifex.com/authentik/teams/119923/pt_BR/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -112,14 +111,6 @@ msgstr "Erro de Validação"
msgid "Blueprint file does not exist"
msgstr "Arquivo de Blueprint não existe"
#: authentik/blueprints/api.py
msgid "Context must be valid JSON"
msgstr ""
#: authentik/blueprints/api.py
msgid "Context must be a JSON object"
msgstr ""
#: authentik/blueprints/api.py
msgid "Failed to validate blueprint"
msgstr "Falha ao validar o projeto"
@@ -128,11 +119,6 @@ msgstr "Falha ao validar o projeto"
msgid "Either path or content must be set."
msgstr "O caminho ou o conteúdo devem ser definidos."
#: authentik/blueprints/api.py
#, python-brace-format
msgid "User lacks permission to create {model}"
msgstr "O usuário não tem permissão para criar {model}"
#: authentik/blueprints/models.py
msgid "Managed by authentik"
msgstr "Gerenciado pelo authentik"
@@ -262,13 +248,10 @@ msgstr ""
"true, somente os provedores de backchannel são retornados. Quando definido "
"para false, provedores de backchannel são excluídos"
#: authentik/core/api/users.py
msgid "Invalid password hash format. Must be a valid Django password hash."
msgstr ""
#: authentik/core/api/users.py
msgid "Cannot set both password and password_hash. Use only one."
msgstr ""
#: authentik/core/api/transactional_applications.py
#, python-brace-format
msgid "User lacks permission to create {model}"
msgstr "O usuário não tem permissão para criar {model}"
#: authentik/core/api/users.py
msgid "No leading or trailing slashes allowed."
@@ -330,12 +313,6 @@ msgstr ""
msgid "This field is required."
msgstr "Este campo é obrigatório."
#: authentik/core/apps.py
msgid ""
"Configure if applications without any policy/group/user bindings should be "
"accessible to any user."
msgstr ""
#: authentik/core/models.py
msgid "name"
msgstr "nome"
@@ -442,10 +419,6 @@ msgstr "Nome do aplicativo interno, usado em URLs."
msgid "Open launch URL in a new browser tab or window."
msgstr "Abra o URL de inicialização em uma nova guia ou janela do navegador."
#: authentik/core/models.py
msgid "Hide this application from the user's My applications page."
msgstr ""
#: authentik/core/models.py
msgid "Application"
msgstr "Aplicativo"
@@ -634,14 +607,6 @@ msgstr "Remover usuários temporários criados por Fontes SAML."
msgid "Go home"
msgstr "Ir para casa"
#: authentik/core/templates/login/base_full.html
msgid "Site footer"
msgstr "Rodapé do site"
#: authentik/core/templates/login/base_full.html
msgid "Flow links"
msgstr ""
#: authentik/core/templates/login/base_full.html
#: authentik/flows/templates/if/flow-sfe.html
msgid "Powered by authentik"
@@ -750,10 +715,6 @@ msgstr "Visualizar chave privada do par de chaves"
msgid "Discover, import and update certificates from the filesystem."
msgstr "Descobrir, importar e atualizar certificados do sistema de arquivos."
#: authentik/endpoints/api/stages.py
msgid "Selected connector is not compatible with this stage."
msgstr ""
#: authentik/endpoints/connectors/agent/api/connectors.py
msgid "Selected platform not supported"
msgstr "A plataforma selecionada não é compatível."
@@ -808,14 +769,6 @@ msgstr "Nonce Apple"
msgid "Apple Nonces"
msgstr "Nonces Apple"
#: authentik/endpoints/connectors/agent/models.py
msgid "Apple Independent Secure Enclave"
msgstr ""
#: authentik/endpoints/connectors/agent/models.py
msgid "Apple Independent Secure Enclaves"
msgstr ""
#: authentik/endpoints/facts.py
msgid "Operating System name, such as 'Server 2022' or 'Ubuntu'"
msgstr ""
@@ -886,12 +839,6 @@ msgstr "Enterprise é necessário para criar/atualizar esse objeto."
msgid "Enterprise is required to use this endpoint."
msgstr "Enterprise é necessário para usar este endpoint."
#: authentik/enterprise/audit/apps.py
msgid ""
"Include additional information in audit logs, may incur a performance "
"penalty."
msgstr ""
#: authentik/enterprise/endpoints/connectors/fleet/models.py
#: authentik/events/models.py
msgid ""
@@ -909,19 +856,6 @@ msgstr ""
msgid "Fleet Connectors"
msgstr ""
#: authentik/enterprise/endpoints/connectors/google_chrome/models.py
msgid "Google Device Trust Connector"
msgstr ""
#: authentik/enterprise/endpoints/connectors/google_chrome/models.py
msgid "Google Device Trust Connectors"
msgstr ""
#: authentik/enterprise/endpoints/connectors/google_chrome/stage.py
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/stage.py
msgid "Verifying your browser..."
msgstr "Verificando seu navegador…"
#: authentik/enterprise/lifecycle/api/reviews.py
msgid "You are not allowed to submit a review for this object."
msgstr ""
@@ -938,6 +872,10 @@ msgstr ""
msgid "Grace period must be shorter than the interval."
msgstr ""
#: authentik/enterprise/lifecycle/api/rules.py
msgid "Only one type-wide rule for each object type is allowed."
msgstr ""
#: authentik/enterprise/lifecycle/models.py
msgid ""
"Select which transports should be used to notify the reviewers. If none are "
@@ -965,8 +903,7 @@ msgid "Go to {self._get_model_name()}"
msgstr ""
#: authentik/enterprise/lifecycle/models.py
msgid ""
"Access review is due for {self.content_type.name.lower()} {object_label}"
msgid "Access review is due for {self.content_type.name} {str(self.object)}"
msgstr ""
#: authentik/enterprise/lifecycle/models.py
@@ -980,7 +917,7 @@ msgid ""
msgstr ""
#: authentik/enterprise/lifecycle/tasks.py
msgid "Dispatch tasks to apply lifecycle rules."
msgid "Dispatch tasks to validate lifecycle rules."
msgstr ""
#: authentik/enterprise/lifecycle/tasks.py
@@ -1224,14 +1161,6 @@ msgstr "Enterprise é necessário para usar EAP-TLS."
msgid "Enterprise is required to use the OAuth mode."
msgstr "Enterprise é necessário para usar o modo OAuth."
#: authentik/enterprise/providers/ssf/models.py
msgid "SSF RFC Push"
msgstr ""
#: authentik/enterprise/providers/ssf/models.py
msgid "SSF RFC Pull"
msgstr ""
#: authentik/enterprise/providers/ssf/models.py
#: authentik/providers/oauth2/models.py
msgid "Signing Key"
@@ -1313,78 +1242,6 @@ msgstr "Download"
msgid "Generate data export."
msgstr "Gerar exportação de dados."
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "User to lock. If omitted, locks the current user (self-service)."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "No lockdown flow configured."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Lockdown flow is not applicable."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Choose the target account, then return a flow link."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "No lockdown flow configured or the flow is not applicable"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/api.py
msgid "Permission denied (when targeting another user)"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Deactivate the user account (set is_active to False)"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Set an unusable password for the user"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Delete all active sessions for the user"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid ""
"Revoke all tokens for the user (API, app password, recovery, verification, "
"OAuth)"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid ""
"Flow to redirect users to after self-service lockdown. This flow should not "
"require authentication since the user's session is deleted."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Account Lockdown Stage"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/models.py
msgid "Account Lockdown Stages"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "No target user specified for account lockdown"
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "You do not have permission to lock down this account."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "Account lockdown failed for this account."
msgstr ""
#: authentik/enterprise/stages/account_lockdown/stage.py
msgid "Self-service account lockdown requires a completion flow."
msgstr ""
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/models.py
msgid "Endpoint Authenticator Google Device Trust Connector Stage"
msgstr "Etapa do Conector Google Device Trust do autenticador de endpoint."
@@ -1401,6 +1258,10 @@ msgstr "Dispositivo de endpoint."
msgid "Endpoint Devices"
msgstr "Dispositivos de endpoint."
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/stage.py
msgid "Verifying your browser..."
msgstr "Verificando seu navegador…"
#: authentik/enterprise/stages/mtls/models.py
msgid ""
"Configure certificate authorities to validate the certificate against. This "
@@ -1483,12 +1344,6 @@ msgstr ""
"Envie uma notificação apenas uma vez, por exemplo, ao enviar um webhook para"
" um canal de bate-papo."
#: authentik/events/models.py
msgid ""
"When set, the selected ceritifcate is used to validate the certificate of "
"the webhook server."
msgstr ""
#: authentik/events/models.py
msgid ""
"Customize the body of the request. Mapping should return data that is JSON-"
@@ -1659,15 +1514,6 @@ msgstr "Políticas de pré-fluxo"
msgid "Flow"
msgstr "Fluxo"
#: authentik/flows/apps.py
msgid "Refresh other tabs after successful authentication."
msgstr ""
#: authentik/flows/apps.py
msgid ""
"Upon successful authentication, re-start authentication in other open tabs."
msgstr ""
#: authentik/flows/exceptions.py
msgid "Flow does not apply to current user."
msgstr "O fluxo não se aplica ao usuário atual."
@@ -1778,9 +1624,9 @@ msgstr "Token de Fluxo"
msgid "Flow Tokens"
msgstr "Tokens de Fluxo"
#: authentik/flows/planner.py
msgid "This link is invalid or has expired. Please request a new one."
msgstr ""
#: authentik/flows/templates/if/flow.html
msgid "Site footer"
msgstr "Rodapé do site"
#: authentik/flows/views/executor.py
msgid "Invalid next URL"
@@ -2164,6 +2010,22 @@ 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 "Aguardando autenticação…"
#: authentik/policies/templates/policies/buffer.html
msgid ""
"You're already authenticating in another tab. This page will refresh once "
"authentication is completed."
msgstr ""
"Você já está autenticando em outra aba. Esta página será atualizada quando a"
" autenticação for concluída."
#: authentik/policies/templates/policies/buffer.html
msgid "Authenticate in this tab"
msgstr "Autenticar nesta aba"
#: authentik/policies/templates/policies/denied.html
msgid "Permission denied"
msgstr "Permissão negada"
@@ -2288,14 +2150,6 @@ msgstr "Comparação estrita de URL"
msgid "Regular Expression URL matching"
msgstr "Correspondência de URL por expressão regular"
#: authentik/providers/oauth2/models.py
msgid "Authorization"
msgstr "Autorização"
#: authentik/providers/oauth2/models.py
msgid "Logout"
msgstr "Sair"
#: authentik/providers/oauth2/models.py
msgid "Back-channel"
msgstr "Back-channel"
@@ -2658,6 +2512,10 @@ msgstr "Provedor de proxy"
msgid "Proxy Providers"
msgstr "Provedores de proxy"
#: authentik/providers/proxy/tasks.py
msgid "Terminate session on Proxy outpost."
msgstr "Encerrar sessão no outpost Proxy"
#: authentik/providers/rac/models.py authentik/stages/user_login/models.py
msgid ""
"Determines how long a session lasts. Default of 0 means that the sessions "
@@ -2790,10 +2648,8 @@ msgstr ""
"branco, nenhuma restrição de público será adicionada."
#: authentik/providers/saml/models.py
msgid ""
"Also known as EntityID. Providing a value overrides the default issuer "
"generated by authentik."
msgstr ""
msgid "Also known as EntityID"
msgstr "Também conhecido como EntityID"
#: authentik/providers/saml/models.py
msgid "SLS URL"
@@ -3014,10 +2870,6 @@ msgstr "Valor do SAML NameID para essa sessão"
msgid "SAML NameID format"
msgstr "Formato do SAML NameID"
#: authentik/providers/saml/models.py
msgid "SAML Issuer used for this session"
msgstr ""
#: authentik/providers/saml/models.py
msgid "SAML Session"
msgstr "Sessão SAML"
@@ -3046,14 +2898,6 @@ msgstr "Slack"
msgid "Salesforce"
msgstr "Salesforce"
#: authentik/providers/scim/models.py
msgid "Webex"
msgstr ""
#: authentik/providers/scim/models.py
msgid "vCenter"
msgstr ""
#: authentik/providers/scim/models.py
msgid "Group filters used to define sync-scope for groups."
msgstr ""
@@ -3337,7 +3181,7 @@ msgstr ""
"e que o navegador esteja configurado corretamente. \n"
"Contate seu administrador."
#: authentik/sources/ldap/api/sources.py
#: authentik/sources/ldap/api.py
msgid "Only a single LDAP Source with password synchronization is allowed"
msgstr "Somente uma Origem LDAP com sincronização de senha é aceita"
@@ -3872,12 +3716,6 @@ msgstr ""
"Permite fluxos de autenticação iniciados pelo IdP. Isso pode ser um risco de"
" segurança, pois nenhuma validação do ID da solicitação é feita."
#: authentik/sources/saml/models.py
msgid ""
"When enabled, the IdP will re-authenticate the user even if a session "
"exists."
msgstr ""
#: authentik/sources/saml/models.py
msgid ""
"NameID Policy sent to the IdP. Can be unset, in which case no Policy is "
@@ -4303,10 +4141,6 @@ msgstr "Etapas de validação do autenticador"
msgid "No (allowed) MFA authenticator configured."
msgstr "Nenhum autenticador MFA (permitido) configurado."
#: authentik/stages/authenticator_webauthn/models.py
msgid "When enabled, a given device can only be registered once."
msgstr ""
#: authentik/stages/authenticator_webauthn/models.py
msgid "WebAuthn Authenticator Setup Stage"
msgstr "Estágio de configuração do autenticador WebAuthn"
@@ -4446,10 +4280,6 @@ msgstr "OTP por Email"
msgid "Event Notification"
msgstr "Notificação de Evento"
#: authentik/stages/email/models.py authentik/stages/invitation/models.py
msgid "Invitation"
msgstr "Convite"
#: authentik/stages/email/models.py
msgid ""
"The time window used to count recent account recovery attempts. If the "
@@ -4569,62 +4399,6 @@ msgstr ""
"\n"
"Este email foi enviado pelo transporte de notificações %(name)s.\n"
#: authentik/stages/email/templates/email/invitation.html
msgid ""
"\n"
" You're Invited!\n"
" "
msgstr ""
#: authentik/stages/email/templates/email/invitation.html
#, python-format
msgid ""
"\n"
" You have been invited to join %(host)s. Click the button below to get started.\n"
" "
msgstr ""
#: authentik/stages/email/templates/email/invitation.html
#, python-format
msgid ""
"\n"
" This invitation expires %(expires)s.\n"
" "
msgstr ""
#: authentik/stages/email/templates/email/invitation.html
#: authentik/stages/email/templates/email/invitation.txt
msgid "Accept Invitation"
msgstr ""
#: authentik/stages/email/templates/email/invitation.html
msgid ""
"\n"
" If you cannot click the button above, please copy and paste the following URL into your browser:\n"
" "
msgstr ""
#: authentik/stages/email/templates/email/invitation.txt
msgid "You're Invited!"
msgstr ""
#: authentik/stages/email/templates/email/invitation.txt
#, python-format
msgid ""
"You have been invited to join %(host)s. Use the link below to get started."
msgstr ""
#: authentik/stages/email/templates/email/invitation.txt
#, python-format
msgid "This invitation expires %(expires)s."
msgstr ""
#: authentik/stages/email/templates/email/invitation.txt
msgid ""
"If you cannot click the link above, please copy and paste the following URL "
"into your browser:"
msgstr ""
#: authentik/stages/email/templates/email/password_reset.html
msgid ""
"\n"
@@ -4803,6 +4577,10 @@ msgstr "Quando ativado, o convite será excluído após o uso."
msgid "Optional fixed data to enforce on user enrollment."
msgstr "Dados fixos opcionais para aplicar na inscrição do usuário."
#: authentik/stages/invitation/models.py
msgid "Invitation"
msgstr "Convite"
#: authentik/stages/invitation/models.py
msgid "Invitations"
msgstr "Convites"
@@ -4926,18 +4704,6 @@ msgstr ""
msgid "Static: Static value, displayed as-is."
msgstr "Estático: valor estático, exibido como está."
#: authentik/stages/prompt/models.py
msgid "Alert (Info): Static alert box with info styling"
msgstr ""
#: authentik/stages/prompt/models.py
msgid "Alert (Warning): Static alert box with warning styling"
msgstr ""
#: authentik/stages/prompt/models.py
msgid "Alert (Danger): Static alert box with danger styling"
msgstr ""
#: authentik/stages/prompt/models.py
msgid "authentik: Selection of locales authentik supports"
msgstr "authentik: Seleção de locais suportados pelo authentik"

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