Compare commits
66 Commits
web/flow/t
...
version-20
Author | SHA1 | Date | |
---|---|---|---|
a36f788e60 | |||
50ad69bdad | |||
0edd7531a1 | |||
5a2c914d19 | |||
f21062581a | |||
676e7885e8 | |||
80441d2277 | |||
e760f73518 | |||
948f80d7ae | |||
0e4b153e7f | |||
efac5ce7bd | |||
d9fbe1d467 | |||
527e584699 | |||
80dfe371e6 | |||
a3d1491aee | |||
1b98792637 | |||
111e120220 | |||
20642d49c3 | |||
a9776a83d3 | |||
b9faae83b4 | |||
afc2998697 | |||
fabacc56c4 | |||
11b013d3b8 | |||
e10c47d8b8 | |||
d2b194f6b7 | |||
780a59c908 | |||
f8015fccd8 | |||
05f4e738a1 | |||
f535a23c03 | |||
91905530c7 | |||
40a970e321 | |||
b51d8d0ba3 | |||
7e8891338f | |||
3ae0001bb5 | |||
66a4970014 | |||
7ab9300761 | |||
a2eccd5022 | |||
31aeaa247f | |||
f49008bbb6 | |||
feb13c8ee5 | |||
d5ef831718 | |||
64676819ec | |||
7ed268fef4 | |||
f6526d1be9 | |||
12f8b4566b | |||
665de8ef22 | |||
9eaa723bf8 | |||
b2ca9c8cbc | |||
7927392100 | |||
d8d07e32cb | |||
f7c5d329eb | |||
92dec32547 | |||
510feccd31 | |||
364a9a1f02 | |||
40cbb7567b | |||
8ad0f63994 | |||
6ce33ab912 | |||
d96b577abd | |||
8c547589f6 | |||
3775e5b84f | |||
fa30339f65 | |||
e825eda106 | |||
246cae3dfa | |||
6cfd2bd1af | |||
f0e4f93fe6 | |||
434aa57ba7 |
@ -1,5 +1,5 @@
|
||||
[bumpversion]
|
||||
current_version = 2024.10.2
|
||||
current_version = 2024.10.5
|
||||
tag = True
|
||||
commit = True
|
||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(?:-(?P<rc_t>[a-zA-Z-]+)(?P<rc_n>[1-9]\\d*))?
|
||||
|
2
.github/actions/setup/action.yml
vendored
2
.github/actions/setup/action.yml
vendored
@ -35,7 +35,7 @@ runs:
|
||||
run: |
|
||||
export PSQL_TAG=${{ inputs.postgresql_version }}
|
||||
docker compose -f .github/actions/setup/docker-compose.yml up -d
|
||||
poetry install
|
||||
poetry install --sync
|
||||
cd web && npm ci
|
||||
- name: Generate config
|
||||
shell: poetry run python {0}
|
||||
|
6
.github/workflows/ci-main.yml
vendored
6
.github/workflows/ci-main.yml
vendored
@ -116,7 +116,7 @@ jobs:
|
||||
poetry run make test
|
||||
poetry run coverage xml
|
||||
- if: ${{ always() }}
|
||||
uses: codecov/codecov-action@v5
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
flags: unit
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
@ -140,7 +140,7 @@ jobs:
|
||||
poetry run coverage run manage.py test tests/integration
|
||||
poetry run coverage xml
|
||||
- if: ${{ always() }}
|
||||
uses: codecov/codecov-action@v5
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
flags: integration
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
@ -198,7 +198,7 @@ jobs:
|
||||
poetry run coverage run manage.py test ${{ matrix.job.glob }}
|
||||
poetry run coverage xml
|
||||
- if: ${{ always() }}
|
||||
uses: codecov/codecov-action@v5
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
flags: e2e
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
2
.github/workflows/release-tag.yml
vendored
2
.github/workflows/release-tag.yml
vendored
@ -18,7 +18,7 @@ jobs:
|
||||
echo "AUTHENTIK_SECRET_KEY=$(openssl rand 32 | base64 -w 0)" >> .env
|
||||
docker buildx install
|
||||
mkdir -p ./gen-ts-api
|
||||
docker build -t testing:latest .
|
||||
docker build --no-cache -t testing:latest .
|
||||
echo "AUTHENTIK_IMAGE=testing" >> .env
|
||||
echo "AUTHENTIK_TAG=latest" >> .env
|
||||
docker compose up --no-start
|
||||
|
@ -80,7 +80,7 @@ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
|
||||
go build -o /go/authentik ./cmd/server
|
||||
|
||||
# Stage 4: MaxMind GeoIP
|
||||
FROM --platform=${BUILDPLATFORM} ghcr.io/maxmind/geoipupdate:v7.1.0 AS geoip
|
||||
FROM --platform=${BUILDPLATFORM} ghcr.io/maxmind/geoipupdate:v7.0.1 AS geoip
|
||||
|
||||
ENV GEOIPUPDATE_EDITION_IDS="GeoLite2-City GeoLite2-ASN"
|
||||
ENV GEOIPUPDATE_VERBOSE="1"
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
from os import environ
|
||||
|
||||
__version__ = "2024.10.2"
|
||||
__version__ = "2024.10.5"
|
||||
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"
|
||||
|
||||
|
||||
|
@ -293,11 +293,7 @@ class Importer:
|
||||
|
||||
serializer_kwargs = {}
|
||||
model_instance = existing_models.first()
|
||||
if (
|
||||
not isinstance(model(), BaseMetaModel)
|
||||
and model_instance
|
||||
and entry.state != BlueprintEntryDesiredState.MUST_CREATED
|
||||
):
|
||||
if not isinstance(model(), BaseMetaModel) and model_instance:
|
||||
self.logger.debug(
|
||||
"Initialise serializer with instance",
|
||||
model=model,
|
||||
@ -307,12 +303,11 @@ class Importer:
|
||||
serializer_kwargs["instance"] = model_instance
|
||||
serializer_kwargs["partial"] = True
|
||||
elif model_instance and entry.state == BlueprintEntryDesiredState.MUST_CREATED:
|
||||
msg = (
|
||||
f"State is set to {BlueprintEntryDesiredState.MUST_CREATED.value} "
|
||||
"and object exists already",
|
||||
)
|
||||
raise EntryInvalidError.from_entry(
|
||||
ValidationError({k: msg for k in entry.identifiers.keys()}, "unique"),
|
||||
(
|
||||
f"State is set to {BlueprintEntryDesiredState.MUST_CREATED} "
|
||||
"and object exists already",
|
||||
),
|
||||
entry,
|
||||
)
|
||||
else:
|
||||
|
@ -1,12 +1,10 @@
|
||||
"""transactional application and provider creation"""
|
||||
|
||||
from django.apps import apps
|
||||
from django.db.models import Model
|
||||
from django.utils.translation import gettext as _
|
||||
from drf_spectacular.utils import PolymorphicProxySerializer, extend_schema, extend_schema_field
|
||||
from rest_framework.exceptions import PermissionDenied, ValidationError
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.fields import BooleanField, CharField, ChoiceField, DictField, ListField
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.permissions import IsAdminUser
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
@ -24,7 +22,6 @@ from authentik.core.api.applications import ApplicationSerializer
|
||||
from authentik.core.api.utils import PassiveSerializer
|
||||
from authentik.core.models import Provider
|
||||
from authentik.lib.utils.reflection import all_subclasses
|
||||
from authentik.policies.api.bindings import PolicyBindingSerializer
|
||||
|
||||
|
||||
def get_provider_serializer_mapping():
|
||||
@ -48,13 +45,6 @@ class TransactionProviderField(DictField):
|
||||
"""Dictionary field which can hold provider creation data"""
|
||||
|
||||
|
||||
class TransactionPolicyBindingSerializer(PolicyBindingSerializer):
|
||||
"""PolicyBindingSerializer which does not require target as target is set implicitly"""
|
||||
|
||||
class Meta(PolicyBindingSerializer.Meta):
|
||||
fields = [x for x in PolicyBindingSerializer.Meta.fields if x != "target"]
|
||||
|
||||
|
||||
class TransactionApplicationSerializer(PassiveSerializer):
|
||||
"""Serializer for creating a provider and an application in one transaction"""
|
||||
|
||||
@ -62,8 +52,6 @@ class TransactionApplicationSerializer(PassiveSerializer):
|
||||
provider_model = ChoiceField(choices=list(get_provider_serializer_mapping().keys()))
|
||||
provider = TransactionProviderField()
|
||||
|
||||
policy_bindings = TransactionPolicyBindingSerializer(many=True, required=False)
|
||||
|
||||
_provider_model: type[Provider] = None
|
||||
|
||||
def validate_provider_model(self, fq_model_name: str) -> str:
|
||||
@ -108,19 +96,6 @@ class TransactionApplicationSerializer(PassiveSerializer):
|
||||
id="app",
|
||||
)
|
||||
)
|
||||
for binding in attrs.get("policy_bindings", []):
|
||||
binding["target"] = KeyOf(None, ScalarNode(tag="", value="app"))
|
||||
for key, value in binding.items():
|
||||
if not isinstance(value, Model):
|
||||
continue
|
||||
binding[key] = value.pk
|
||||
blueprint.entries.append(
|
||||
BlueprintEntry(
|
||||
model="authentik_policies.policybinding",
|
||||
state=BlueprintEntryDesiredState.MUST_CREATED,
|
||||
identifiers=binding,
|
||||
)
|
||||
)
|
||||
importer = Importer(blueprint, {})
|
||||
try:
|
||||
valid, _ = importer.validate(raise_validation_errors=True)
|
||||
@ -145,7 +120,8 @@ class TransactionApplicationResponseSerializer(PassiveSerializer):
|
||||
class TransactionalApplicationView(APIView):
|
||||
"""Create provider and application and attach them in a single transaction"""
|
||||
|
||||
permission_classes = [IsAuthenticated]
|
||||
# TODO: Migrate to a more specific permission
|
||||
permission_classes = [IsAdminUser]
|
||||
|
||||
@extend_schema(
|
||||
request=TransactionApplicationSerializer(),
|
||||
@ -157,23 +133,8 @@ class TransactionalApplicationView(APIView):
|
||||
"""Convert data into a blueprint, validate it and apply it"""
|
||||
data = TransactionApplicationSerializer(data=request.data)
|
||||
data.is_valid(raise_exception=True)
|
||||
blueprint: Blueprint = data.validated_data
|
||||
for entry in blueprint.entries:
|
||||
full_model = entry.get_model(blueprint)
|
||||
app, __, model = full_model.partition(".")
|
||||
if not request.user.has_perm(f"{app}.add_{model}"):
|
||||
raise PermissionDenied(
|
||||
{
|
||||
entry.id: _(
|
||||
"User lacks permission to create {model}".format_map(
|
||||
{
|
||||
"model": full_model,
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
importer = Importer(blueprint, {})
|
||||
|
||||
importer = Importer(data.validated_data, {})
|
||||
applied = importer.apply()
|
||||
response = {"applied": False, "logs": []}
|
||||
response["applied"] = applied
|
||||
|
@ -666,12 +666,7 @@ class UserViewSet(UsedByMixin, ModelViewSet):
|
||||
|
||||
@permission_required("authentik_core.impersonate")
|
||||
@extend_schema(
|
||||
request=inline_serializer(
|
||||
"ImpersonationSerializer",
|
||||
{
|
||||
"reason": CharField(required=True),
|
||||
},
|
||||
),
|
||||
request=OpenApiTypes.NONE,
|
||||
responses={
|
||||
"204": OpenApiResponse(description="Successfully started impersonation"),
|
||||
"401": OpenApiResponse(description="Access denied"),
|
||||
@ -684,7 +679,6 @@ class UserViewSet(UsedByMixin, ModelViewSet):
|
||||
LOGGER.debug("User attempted to impersonate", user=request.user)
|
||||
return Response(status=401)
|
||||
user_to_be = self.get_object()
|
||||
reason = request.data.get("reason", "")
|
||||
# Check both object-level perms and global perms
|
||||
if not request.user.has_perm(
|
||||
"authentik_core.impersonate", user_to_be
|
||||
@ -694,16 +688,11 @@ class UserViewSet(UsedByMixin, ModelViewSet):
|
||||
if user_to_be.pk == self.request.user.pk:
|
||||
LOGGER.debug("User attempted to impersonate themselves", user=request.user)
|
||||
return Response(status=401)
|
||||
if not reason and request.tenant.impersonation_require_reason:
|
||||
LOGGER.debug(
|
||||
"User attempted to impersonate without providing a reason", user=request.user
|
||||
)
|
||||
return Response(status=401)
|
||||
|
||||
request.session[SESSION_KEY_IMPERSONATE_ORIGINAL_USER] = request.user
|
||||
request.session[SESSION_KEY_IMPERSONATE_USER] = user_to_be
|
||||
|
||||
Event.new(EventAction.IMPERSONATION_STARTED, reason=reason).from_http(request, user_to_be)
|
||||
Event.new(EventAction.IMPERSONATION_STARTED).from_http(request, user_to_be)
|
||||
|
||||
return Response(status=201)
|
||||
|
||||
|
@ -29,8 +29,7 @@ class TestImpersonation(APITestCase):
|
||||
reverse(
|
||||
"authentik_api:user-impersonate",
|
||||
kwargs={"pk": self.other_user.pk},
|
||||
),
|
||||
data={"reason": "some reason"},
|
||||
)
|
||||
)
|
||||
|
||||
response = self.client.get(reverse("authentik_api:user-me"))
|
||||
@ -56,8 +55,7 @@ class TestImpersonation(APITestCase):
|
||||
reverse(
|
||||
"authentik_api:user-impersonate",
|
||||
kwargs={"pk": self.other_user.pk},
|
||||
),
|
||||
data={"reason": "some reason"},
|
||||
)
|
||||
)
|
||||
self.assertEqual(response.status_code, 201)
|
||||
|
||||
@ -77,8 +75,7 @@ class TestImpersonation(APITestCase):
|
||||
reverse(
|
||||
"authentik_api:user-impersonate",
|
||||
kwargs={"pk": self.other_user.pk},
|
||||
),
|
||||
data={"reason": "some reason"},
|
||||
)
|
||||
)
|
||||
self.assertEqual(response.status_code, 201)
|
||||
|
||||
@ -92,8 +89,7 @@ class TestImpersonation(APITestCase):
|
||||
self.client.force_login(self.other_user)
|
||||
|
||||
response = self.client.post(
|
||||
reverse("authentik_api:user-impersonate", kwargs={"pk": self.user.pk}),
|
||||
data={"reason": "some reason"},
|
||||
reverse("authentik_api:user-impersonate", kwargs={"pk": self.user.pk})
|
||||
)
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
@ -109,8 +105,7 @@ class TestImpersonation(APITestCase):
|
||||
self.client.force_login(self.user)
|
||||
|
||||
response = self.client.post(
|
||||
reverse("authentik_api:user-impersonate", kwargs={"pk": self.other_user.pk}),
|
||||
data={"reason": "some reason"},
|
||||
reverse("authentik_api:user-impersonate", kwargs={"pk": self.other_user.pk})
|
||||
)
|
||||
self.assertEqual(response.status_code, 401)
|
||||
|
||||
@ -123,22 +118,7 @@ class TestImpersonation(APITestCase):
|
||||
self.client.force_login(self.user)
|
||||
|
||||
response = self.client.post(
|
||||
reverse("authentik_api:user-impersonate", kwargs={"pk": self.user.pk}),
|
||||
data={"reason": "some reason"},
|
||||
)
|
||||
self.assertEqual(response.status_code, 401)
|
||||
|
||||
response = self.client.get(reverse("authentik_api:user-me"))
|
||||
response_body = loads(response.content.decode())
|
||||
self.assertEqual(response_body["user"]["username"], self.user.username)
|
||||
|
||||
def test_impersonate_reason_required(self):
|
||||
"""test impersonation that user must provide reason"""
|
||||
self.client.force_login(self.user)
|
||||
|
||||
response = self.client.post(
|
||||
reverse("authentik_api:user-impersonate", kwargs={"pk": self.user.pk}),
|
||||
data={"reason": ""},
|
||||
reverse("authentik_api:user-impersonate", kwargs={"pk": self.user.pk})
|
||||
)
|
||||
self.assertEqual(response.status_code, 401)
|
||||
|
||||
|
@ -1,13 +1,11 @@
|
||||
"""Test Transactional API"""
|
||||
|
||||
from django.urls import reverse
|
||||
from guardian.shortcuts import assign_perm
|
||||
from rest_framework.test import APITestCase
|
||||
|
||||
from authentik.core.models import Application, Group
|
||||
from authentik.core.tests.utils import create_test_flow, create_test_user
|
||||
from authentik.core.models import Application
|
||||
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.policies.models import PolicyBinding
|
||||
from authentik.providers.oauth2.models import OAuth2Provider
|
||||
|
||||
|
||||
@ -15,9 +13,7 @@ class TestTransactionalApplicationsAPI(APITestCase):
|
||||
"""Test Transactional API"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
self.user = create_test_user()
|
||||
assign_perm("authentik_core.add_application", self.user)
|
||||
assign_perm("authentik_providers_oauth2.add_oauth2provider", self.user)
|
||||
self.user = create_test_admin_user()
|
||||
|
||||
def test_create_transactional(self):
|
||||
"""Test transactional Application + provider creation"""
|
||||
@ -46,66 +42,6 @@ class TestTransactionalApplicationsAPI(APITestCase):
|
||||
self.assertIsNotNone(app)
|
||||
self.assertEqual(app.provider.pk, provider.pk)
|
||||
|
||||
def test_create_transactional_permission_denied(self):
|
||||
"""Test transactional Application + provider creation (missing permissions)"""
|
||||
self.client.force_login(self.user)
|
||||
uid = generate_id()
|
||||
response = self.client.put(
|
||||
reverse("authentik_api:core-transactional-application"),
|
||||
data={
|
||||
"app": {
|
||||
"name": uid,
|
||||
"slug": uid,
|
||||
},
|
||||
"provider_model": "authentik_providers_saml.samlprovider",
|
||||
"provider": {
|
||||
"name": uid,
|
||||
"authorization_flow": str(create_test_flow().pk),
|
||||
"invalidation_flow": str(create_test_flow().pk),
|
||||
"acs_url": "https://goauthentik.io",
|
||||
},
|
||||
},
|
||||
)
|
||||
self.assertJSONEqual(
|
||||
response.content.decode(),
|
||||
{"provider": "User lacks permission to create authentik_providers_saml.samlprovider"},
|
||||
)
|
||||
|
||||
def test_create_transactional_bindings(self):
|
||||
"""Test transactional Application + provider creation"""
|
||||
assign_perm("authentik_policies.add_policybinding", self.user)
|
||||
self.client.force_login(self.user)
|
||||
uid = generate_id()
|
||||
group = Group.objects.create(name=generate_id())
|
||||
authorization_flow = create_test_flow()
|
||||
response = self.client.put(
|
||||
reverse("authentik_api:core-transactional-application"),
|
||||
data={
|
||||
"app": {
|
||||
"name": uid,
|
||||
"slug": uid,
|
||||
},
|
||||
"provider_model": "authentik_providers_oauth2.oauth2provider",
|
||||
"provider": {
|
||||
"name": uid,
|
||||
"authorization_flow": str(authorization_flow.pk),
|
||||
"invalidation_flow": str(authorization_flow.pk),
|
||||
"redirect_uris": [],
|
||||
},
|
||||
"policy_bindings": [{"group": group.pk, "order": 0}],
|
||||
},
|
||||
)
|
||||
self.assertJSONEqual(response.content.decode(), {"applied": True, "logs": []})
|
||||
provider = OAuth2Provider.objects.filter(name=uid).first()
|
||||
self.assertIsNotNone(provider)
|
||||
app = Application.objects.filter(slug=uid).first()
|
||||
self.assertIsNotNone(app)
|
||||
self.assertEqual(app.provider.pk, provider.pk)
|
||||
binding = PolicyBinding.objects.filter(target=app).first()
|
||||
self.assertIsNotNone(binding)
|
||||
self.assertEqual(binding.target, app)
|
||||
self.assertEqual(binding.group, group)
|
||||
|
||||
def test_create_transactional_invalid(self):
|
||||
"""Test transactional Application + provider creation"""
|
||||
self.client.force_login(self.user)
|
||||
@ -135,32 +71,3 @@ class TestTransactionalApplicationsAPI(APITestCase):
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
def test_create_transactional_duplicate_name_provider(self):
|
||||
"""Test transactional Application + provider creation"""
|
||||
self.client.force_login(self.user)
|
||||
uid = generate_id()
|
||||
OAuth2Provider.objects.create(
|
||||
name=uid,
|
||||
authorization_flow=create_test_flow(),
|
||||
invalidation_flow=create_test_flow(),
|
||||
)
|
||||
response = self.client.put(
|
||||
reverse("authentik_api:core-transactional-application"),
|
||||
data={
|
||||
"app": {
|
||||
"name": uid,
|
||||
"slug": uid,
|
||||
},
|
||||
"provider_model": "authentik_providers_oauth2.oauth2provider",
|
||||
"provider": {
|
||||
"name": uid,
|
||||
"authorization_flow": str(create_test_flow().pk),
|
||||
"invalidation_flow": str(create_test_flow().pk),
|
||||
},
|
||||
},
|
||||
)
|
||||
self.assertJSONEqual(
|
||||
response.content.decode(),
|
||||
{"provider": {"name": ["State is set to must_created and object exists already"]}},
|
||||
)
|
||||
|
@ -6,6 +6,7 @@ from django.http import HttpRequest, HttpResponse, JsonResponse
|
||||
from django.urls import resolve
|
||||
from structlog.stdlib import BoundLogger, get_logger
|
||||
|
||||
from authentik.core.api.users import UserViewSet
|
||||
from authentik.enterprise.api import LicenseViewSet
|
||||
from authentik.enterprise.license import LicenseKey
|
||||
from authentik.enterprise.models import LicenseUsageStatus
|
||||
@ -59,6 +60,9 @@ class EnterpriseMiddleware:
|
||||
# Flow executor is mounted as an API path but explicitly allowed
|
||||
if request.resolver_match._func_path == class_to_path(FlowExecutorView):
|
||||
return True
|
||||
# Always allow making changes to users, even in case the license has ben exceeded
|
||||
if request.resolver_match._func_path == class_to_path(UserViewSet):
|
||||
return True
|
||||
# Only apply these restrictions to the API
|
||||
if "authentik_api" not in request.resolver_match.app_names:
|
||||
return True
|
||||
|
@ -4,7 +4,9 @@ from typing import Any
|
||||
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
|
||||
from django.template.response import TemplateResponse
|
||||
from django.urls import reverse
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views import View
|
||||
from django.views.decorators.clickjacking import xframe_options_sameorigin
|
||||
from googleapiclient.discovery import build
|
||||
|
||||
from authentik.enterprise.stages.authenticator_endpoint_gdtc.models import (
|
||||
@ -26,6 +28,7 @@ HEADER_ACCESS_CHALLENGE_RESPONSE = "X-Verified-Access-Challenge-Response"
|
||||
DEVICE_TRUST_VERIFIED_ACCESS = "VerifiedAccess"
|
||||
|
||||
|
||||
@method_decorator(xframe_options_sameorigin, name="dispatch")
|
||||
class GoogleChromeDeviceTrustConnector(View):
|
||||
"""Google Chrome Device-trust connector based endpoint authenticator"""
|
||||
|
||||
|
@ -215,3 +215,49 @@ class TestReadOnly(FlowTestCase):
|
||||
{"detail": "Request denied due to expired/invalid license.", "code": "denied_license"},
|
||||
)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
@patch(
|
||||
"authentik.enterprise.license.LicenseKey.validate",
|
||||
MagicMock(
|
||||
return_value=LicenseKey(
|
||||
aud="",
|
||||
exp=expiry_valid,
|
||||
name=generate_id(),
|
||||
internal_users=100,
|
||||
external_users=100,
|
||||
)
|
||||
),
|
||||
)
|
||||
@patch(
|
||||
"authentik.enterprise.license.LicenseKey.get_internal_user_count",
|
||||
MagicMock(return_value=1000),
|
||||
)
|
||||
@patch(
|
||||
"authentik.enterprise.license.LicenseKey.get_external_user_count",
|
||||
MagicMock(return_value=1000),
|
||||
)
|
||||
@patch(
|
||||
"authentik.enterprise.license.LicenseKey.record_usage",
|
||||
MagicMock(),
|
||||
)
|
||||
def test_manage_users(self):
|
||||
"""Test that managing users is still possible"""
|
||||
License.objects.create(key=generate_id())
|
||||
usage = LicenseUsage.objects.create(
|
||||
internal_user_count=100,
|
||||
external_user_count=100,
|
||||
status=LicenseUsageStatus.VALID,
|
||||
)
|
||||
usage.record_date = now() - timedelta(weeks=THRESHOLD_READ_ONLY_WEEKS + 1)
|
||||
usage.save(update_fields=["record_date"])
|
||||
|
||||
admin = create_test_admin_user()
|
||||
self.client.force_login(admin)
|
||||
|
||||
# Reading is always allowed
|
||||
response = self.client.get(reverse("authentik_api:user-list"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Writing should also be allowed
|
||||
response = self.client.patch(reverse("authentik_api:user-detail", kwargs={"pk": admin.pk}))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
@ -60,7 +60,7 @@ def default_event_duration():
|
||||
"""Default duration an Event is saved.
|
||||
This is used as a fallback when no brand is available"""
|
||||
try:
|
||||
tenant = get_current_tenant(only=["event_retention"])
|
||||
tenant = get_current_tenant()
|
||||
return now() + timedelta_from_string(tenant.event_retention)
|
||||
except Tenant.DoesNotExist:
|
||||
return now() + timedelta(days=365)
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.http import HttpRequest
|
||||
from django.http.request import QueryDict
|
||||
@ -224,6 +225,14 @@ class ChallengeStageView(StageView):
|
||||
full_errors[field].append(field_error)
|
||||
challenge_response.initial_data["response_errors"] = full_errors
|
||||
if not challenge_response.is_valid():
|
||||
if settings.TEST:
|
||||
raise StageInvalidException(
|
||||
(
|
||||
f"Invalid challenge response: \n\t{challenge_response.errors}"
|
||||
f"\n\nValidated data:\n\t {challenge_response.data}"
|
||||
f"\n\nInitial data:\n\t {challenge_response.initial_data}"
|
||||
),
|
||||
)
|
||||
self.logger.error(
|
||||
"f(ch): invalid challenge response",
|
||||
errors=challenge_response.errors,
|
||||
|
@ -5,6 +5,7 @@ import json
|
||||
import os
|
||||
from collections.abc import Mapping
|
||||
from contextlib import contextmanager
|
||||
from copy import deepcopy
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
from glob import glob
|
||||
@ -336,6 +337,58 @@ def redis_url(db: int) -> str:
|
||||
return _redis_url
|
||||
|
||||
|
||||
def django_db_config(config: ConfigLoader | None = None) -> dict:
|
||||
if not config:
|
||||
config = CONFIG
|
||||
db = {
|
||||
"default": {
|
||||
"ENGINE": "authentik.root.db",
|
||||
"HOST": config.get("postgresql.host"),
|
||||
"NAME": config.get("postgresql.name"),
|
||||
"USER": config.get("postgresql.user"),
|
||||
"PASSWORD": config.get("postgresql.password"),
|
||||
"PORT": config.get("postgresql.port"),
|
||||
"OPTIONS": {
|
||||
"sslmode": config.get("postgresql.sslmode"),
|
||||
"sslrootcert": config.get("postgresql.sslrootcert"),
|
||||
"sslcert": config.get("postgresql.sslcert"),
|
||||
"sslkey": config.get("postgresql.sslkey"),
|
||||
},
|
||||
"TEST": {
|
||||
"NAME": config.get("postgresql.test.name"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if config.get_bool("postgresql.use_pgpool", False):
|
||||
db["default"]["DISABLE_SERVER_SIDE_CURSORS"] = True
|
||||
|
||||
if config.get_bool("postgresql.use_pgbouncer", False):
|
||||
# https://docs.djangoproject.com/en/4.0/ref/databases/#transaction-pooling-server-side-cursors
|
||||
db["default"]["DISABLE_SERVER_SIDE_CURSORS"] = True
|
||||
# https://docs.djangoproject.com/en/4.0/ref/databases/#persistent-connections
|
||||
db["default"]["CONN_MAX_AGE"] = None # persistent
|
||||
|
||||
for replica in config.get_keys("postgresql.read_replicas"):
|
||||
_database = deepcopy(db["default"])
|
||||
for setting, current_value in db["default"].items():
|
||||
if isinstance(current_value, dict):
|
||||
continue
|
||||
override = config.get(
|
||||
f"postgresql.read_replicas.{replica}.{setting.lower()}", default=UNSET
|
||||
)
|
||||
if override is not UNSET:
|
||||
_database[setting] = override
|
||||
for setting in db["default"]["OPTIONS"].keys():
|
||||
override = config.get(
|
||||
f"postgresql.read_replicas.{replica}.{setting.lower()}", default=UNSET
|
||||
)
|
||||
if override is not UNSET:
|
||||
_database["OPTIONS"][setting] = override
|
||||
db[f"replica_{replica}"] = _database
|
||||
return db
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(argv) < 2: # noqa: PLR2004
|
||||
print(dumps(CONFIG.raw, indent=4, cls=AttrEncoder))
|
||||
|
@ -9,7 +9,14 @@ from unittest import mock
|
||||
from django.conf import ImproperlyConfigured
|
||||
from django.test import TestCase
|
||||
|
||||
from authentik.lib.config import ENV_PREFIX, UNSET, Attr, AttrEncoder, ConfigLoader
|
||||
from authentik.lib.config import (
|
||||
ENV_PREFIX,
|
||||
UNSET,
|
||||
Attr,
|
||||
AttrEncoder,
|
||||
ConfigLoader,
|
||||
django_db_config,
|
||||
)
|
||||
|
||||
|
||||
class TestConfig(TestCase):
|
||||
@ -175,3 +182,201 @@ class TestConfig(TestCase):
|
||||
config = ConfigLoader()
|
||||
config.set("foo.bar", "baz")
|
||||
self.assertEqual(list(config.get_keys("foo")), ["bar"])
|
||||
|
||||
def test_db_default(self):
|
||||
"""Test default DB Config"""
|
||||
config = ConfigLoader()
|
||||
config.set("postgresql.host", "foo")
|
||||
config.set("postgresql.name", "foo")
|
||||
config.set("postgresql.user", "foo")
|
||||
config.set("postgresql.password", "foo")
|
||||
config.set("postgresql.port", "foo")
|
||||
config.set("postgresql.sslmode", "foo")
|
||||
config.set("postgresql.sslrootcert", "foo")
|
||||
config.set("postgresql.sslcert", "foo")
|
||||
config.set("postgresql.sslkey", "foo")
|
||||
config.set("postgresql.test.name", "foo")
|
||||
conf = django_db_config(config)
|
||||
self.assertEqual(
|
||||
conf,
|
||||
{
|
||||
"default": {
|
||||
"ENGINE": "authentik.root.db",
|
||||
"HOST": "foo",
|
||||
"NAME": "foo",
|
||||
"OPTIONS": {
|
||||
"sslcert": "foo",
|
||||
"sslkey": "foo",
|
||||
"sslmode": "foo",
|
||||
"sslrootcert": "foo",
|
||||
},
|
||||
"PASSWORD": "foo",
|
||||
"PORT": "foo",
|
||||
"TEST": {"NAME": "foo"},
|
||||
"USER": "foo",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
def test_db_read_replicas(self):
|
||||
"""Test read replicas"""
|
||||
config = ConfigLoader()
|
||||
config.set("postgresql.host", "foo")
|
||||
config.set("postgresql.name", "foo")
|
||||
config.set("postgresql.user", "foo")
|
||||
config.set("postgresql.password", "foo")
|
||||
config.set("postgresql.port", "foo")
|
||||
config.set("postgresql.sslmode", "foo")
|
||||
config.set("postgresql.sslrootcert", "foo")
|
||||
config.set("postgresql.sslcert", "foo")
|
||||
config.set("postgresql.sslkey", "foo")
|
||||
config.set("postgresql.test.name", "foo")
|
||||
# Read replica
|
||||
config.set("postgresql.read_replicas.0.host", "bar")
|
||||
conf = django_db_config(config)
|
||||
self.assertEqual(
|
||||
conf,
|
||||
{
|
||||
"default": {
|
||||
"ENGINE": "authentik.root.db",
|
||||
"HOST": "foo",
|
||||
"NAME": "foo",
|
||||
"OPTIONS": {
|
||||
"sslcert": "foo",
|
||||
"sslkey": "foo",
|
||||
"sslmode": "foo",
|
||||
"sslrootcert": "foo",
|
||||
},
|
||||
"PASSWORD": "foo",
|
||||
"PORT": "foo",
|
||||
"TEST": {"NAME": "foo"},
|
||||
"USER": "foo",
|
||||
},
|
||||
"replica_0": {
|
||||
"ENGINE": "authentik.root.db",
|
||||
"HOST": "bar",
|
||||
"NAME": "foo",
|
||||
"OPTIONS": {
|
||||
"sslcert": "foo",
|
||||
"sslkey": "foo",
|
||||
"sslmode": "foo",
|
||||
"sslrootcert": "foo",
|
||||
},
|
||||
"PASSWORD": "foo",
|
||||
"PORT": "foo",
|
||||
"TEST": {"NAME": "foo"},
|
||||
"USER": "foo",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
def test_db_read_replicas_pgpool(self):
|
||||
"""Test read replicas"""
|
||||
config = ConfigLoader()
|
||||
config.set("postgresql.host", "foo")
|
||||
config.set("postgresql.name", "foo")
|
||||
config.set("postgresql.user", "foo")
|
||||
config.set("postgresql.password", "foo")
|
||||
config.set("postgresql.port", "foo")
|
||||
config.set("postgresql.sslmode", "foo")
|
||||
config.set("postgresql.sslrootcert", "foo")
|
||||
config.set("postgresql.sslcert", "foo")
|
||||
config.set("postgresql.sslkey", "foo")
|
||||
config.set("postgresql.test.name", "foo")
|
||||
config.set("postgresql.use_pgpool", True)
|
||||
# Read replica
|
||||
config.set("postgresql.read_replicas.0.host", "bar")
|
||||
# This isn't supported
|
||||
config.set("postgresql.read_replicas.0.use_pgpool", False)
|
||||
conf = django_db_config(config)
|
||||
self.assertEqual(
|
||||
conf,
|
||||
{
|
||||
"default": {
|
||||
"DISABLE_SERVER_SIDE_CURSORS": True,
|
||||
"ENGINE": "authentik.root.db",
|
||||
"HOST": "foo",
|
||||
"NAME": "foo",
|
||||
"OPTIONS": {
|
||||
"sslcert": "foo",
|
||||
"sslkey": "foo",
|
||||
"sslmode": "foo",
|
||||
"sslrootcert": "foo",
|
||||
},
|
||||
"PASSWORD": "foo",
|
||||
"PORT": "foo",
|
||||
"TEST": {"NAME": "foo"},
|
||||
"USER": "foo",
|
||||
},
|
||||
"replica_0": {
|
||||
"DISABLE_SERVER_SIDE_CURSORS": True,
|
||||
"ENGINE": "authentik.root.db",
|
||||
"HOST": "bar",
|
||||
"NAME": "foo",
|
||||
"OPTIONS": {
|
||||
"sslcert": "foo",
|
||||
"sslkey": "foo",
|
||||
"sslmode": "foo",
|
||||
"sslrootcert": "foo",
|
||||
},
|
||||
"PASSWORD": "foo",
|
||||
"PORT": "foo",
|
||||
"TEST": {"NAME": "foo"},
|
||||
"USER": "foo",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
def test_db_read_replicas_diff_ssl(self):
|
||||
"""Test read replicas (with different SSL Settings)"""
|
||||
"""Test read replicas"""
|
||||
config = ConfigLoader()
|
||||
config.set("postgresql.host", "foo")
|
||||
config.set("postgresql.name", "foo")
|
||||
config.set("postgresql.user", "foo")
|
||||
config.set("postgresql.password", "foo")
|
||||
config.set("postgresql.port", "foo")
|
||||
config.set("postgresql.sslmode", "foo")
|
||||
config.set("postgresql.sslrootcert", "foo")
|
||||
config.set("postgresql.sslcert", "foo")
|
||||
config.set("postgresql.sslkey", "foo")
|
||||
config.set("postgresql.test.name", "foo")
|
||||
# Read replica
|
||||
config.set("postgresql.read_replicas.0.host", "bar")
|
||||
config.set("postgresql.read_replicas.0.sslcert", "bar")
|
||||
conf = django_db_config(config)
|
||||
self.assertEqual(
|
||||
conf,
|
||||
{
|
||||
"default": {
|
||||
"ENGINE": "authentik.root.db",
|
||||
"HOST": "foo",
|
||||
"NAME": "foo",
|
||||
"OPTIONS": {
|
||||
"sslcert": "foo",
|
||||
"sslkey": "foo",
|
||||
"sslmode": "foo",
|
||||
"sslrootcert": "foo",
|
||||
},
|
||||
"PASSWORD": "foo",
|
||||
"PORT": "foo",
|
||||
"TEST": {"NAME": "foo"},
|
||||
"USER": "foo",
|
||||
},
|
||||
"replica_0": {
|
||||
"ENGINE": "authentik.root.db",
|
||||
"HOST": "bar",
|
||||
"NAME": "foo",
|
||||
"OPTIONS": {
|
||||
"sslcert": "bar",
|
||||
"sslkey": "foo",
|
||||
"sslmode": "foo",
|
||||
"sslrootcert": "foo",
|
||||
},
|
||||
"PASSWORD": "foo",
|
||||
"PORT": "foo",
|
||||
"TEST": {"NAME": "foo"},
|
||||
"USER": "foo",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
@ -12,7 +12,7 @@ from sentry_sdk import set_tag
|
||||
from xmlsec import enable_debug_trace
|
||||
|
||||
from authentik import __version__
|
||||
from authentik.lib.config import CONFIG, redis_url
|
||||
from authentik.lib.config import CONFIG, django_db_config, redis_url
|
||||
from authentik.lib.logging import get_logger_config, structlog_configure
|
||||
from authentik.lib.sentry import sentry_init
|
||||
from authentik.lib.utils.reflection import get_env
|
||||
@ -38,7 +38,6 @@ LANGUAGE_COOKIE_NAME = "authentik_language"
|
||||
SESSION_COOKIE_NAME = "authentik_session"
|
||||
SESSION_COOKIE_DOMAIN = CONFIG.get("cookie_domain", None)
|
||||
APPEND_SLASH = False
|
||||
X_FRAME_OPTIONS = "SAMEORIGIN"
|
||||
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
"django.contrib.auth.backends.ModelBackend",
|
||||
@ -296,45 +295,7 @@ CHANNEL_LAYERS = {
|
||||
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases
|
||||
|
||||
ORIGINAL_BACKEND = "django_prometheus.db.backends.postgresql"
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "authentik.root.db",
|
||||
"HOST": CONFIG.get("postgresql.host"),
|
||||
"NAME": CONFIG.get("postgresql.name"),
|
||||
"USER": CONFIG.get("postgresql.user"),
|
||||
"PASSWORD": CONFIG.get("postgresql.password"),
|
||||
"PORT": CONFIG.get("postgresql.port"),
|
||||
"SSLMODE": CONFIG.get("postgresql.sslmode"),
|
||||
"SSLROOTCERT": CONFIG.get("postgresql.sslrootcert"),
|
||||
"SSLCERT": CONFIG.get("postgresql.sslcert"),
|
||||
"SSLKEY": CONFIG.get("postgresql.sslkey"),
|
||||
"TEST": {
|
||||
"NAME": CONFIG.get("postgresql.test.name"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if CONFIG.get_bool("postgresql.use_pgpool", False):
|
||||
DATABASES["default"]["DISABLE_SERVER_SIDE_CURSORS"] = True
|
||||
|
||||
if CONFIG.get_bool("postgresql.use_pgbouncer", False):
|
||||
# https://docs.djangoproject.com/en/4.0/ref/databases/#transaction-pooling-server-side-cursors
|
||||
DATABASES["default"]["DISABLE_SERVER_SIDE_CURSORS"] = True
|
||||
# https://docs.djangoproject.com/en/4.0/ref/databases/#persistent-connections
|
||||
DATABASES["default"]["CONN_MAX_AGE"] = None # persistent
|
||||
|
||||
for replica in CONFIG.get_keys("postgresql.read_replicas"):
|
||||
_database = DATABASES["default"].copy()
|
||||
for setting in DATABASES["default"].keys():
|
||||
default = object()
|
||||
if setting in ("TEST",):
|
||||
continue
|
||||
override = CONFIG.get(
|
||||
f"postgresql.read_replicas.{replica}.{setting.lower()}", default=default
|
||||
)
|
||||
if override is not default:
|
||||
_database[setting] = override
|
||||
DATABASES[f"replica_{replica}"] = _database
|
||||
DATABASES = django_db_config()
|
||||
|
||||
DATABASE_ROUTERS = (
|
||||
"authentik.tenants.db.FailoverRouter",
|
||||
|
@ -6,6 +6,7 @@ from tempfile import gettempdir
|
||||
from typing import Any
|
||||
|
||||
import gssapi
|
||||
import kadmin
|
||||
import pglock
|
||||
from django.db import connection, models
|
||||
from django.db.models.fields import b64decode
|
||||
@ -13,8 +14,6 @@ from django.http import HttpRequest
|
||||
from django.shortcuts import reverse
|
||||
from django.templatetags.static import static
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from kadmin import KAdmin
|
||||
from kadmin.exceptions import PyKAdminException
|
||||
from rest_framework.serializers import Serializer
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
@ -31,8 +30,9 @@ from authentik.flows.challenge import RedirectChallenge
|
||||
LOGGER = get_logger()
|
||||
|
||||
|
||||
# Creating kadmin connections is expensive. As such, this global is used to reuse
|
||||
# existing kadmin connections instead of creating new ones
|
||||
# python-kadmin leaks file descriptors. As such, this global is used to reuse
|
||||
# existing kadmin connections instead of creating new ones, which results in less to no file
|
||||
# descriptors leaks
|
||||
_kadmin_connections: dict[str, Any] = {}
|
||||
|
||||
|
||||
@ -198,13 +198,13 @@ class KerberosSource(Source):
|
||||
conf_path.write_text(self.krb5_conf)
|
||||
return str(conf_path)
|
||||
|
||||
def _kadmin_init(self) -> KAdmin | None:
|
||||
def _kadmin_init(self) -> "kadmin.KAdmin | None":
|
||||
# kadmin doesn't use a ccache for its connection
|
||||
# as such, we don't need to create a separate ccache for each source
|
||||
if not self.sync_principal:
|
||||
return None
|
||||
if self.sync_password:
|
||||
return KAdmin.with_password(
|
||||
return kadmin.init_with_password(
|
||||
self.sync_principal,
|
||||
self.sync_password,
|
||||
)
|
||||
@ -215,18 +215,18 @@ class KerberosSource(Source):
|
||||
keytab_path.touch(mode=0o600)
|
||||
keytab_path.write_bytes(b64decode(self.sync_keytab))
|
||||
keytab = f"FILE:{keytab_path}"
|
||||
return KAdmin.with_keytab(
|
||||
return kadmin.init_with_keytab(
|
||||
self.sync_principal,
|
||||
keytab,
|
||||
)
|
||||
if self.sync_ccache:
|
||||
return KAdmin.with_ccache(
|
||||
return kadmin.init_with_ccache(
|
||||
self.sync_principal,
|
||||
self.sync_ccache,
|
||||
)
|
||||
return None
|
||||
|
||||
def connection(self) -> KAdmin | None:
|
||||
def connection(self) -> "kadmin.KAdmin | None":
|
||||
"""Get kadmin connection"""
|
||||
if str(self.pk) not in _kadmin_connections:
|
||||
kadm = self._kadmin_init()
|
||||
@ -246,7 +246,7 @@ class KerberosSource(Source):
|
||||
status["status"] = "no connection"
|
||||
return status
|
||||
status["principal_exists"] = kadm.principal_exists(self.sync_principal)
|
||||
except PyKAdminException as exc:
|
||||
except kadmin.KAdminError as exc:
|
||||
status["status"] = str(exc)
|
||||
return status
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
"""authentik kerberos source signals"""
|
||||
|
||||
import kadmin
|
||||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
from kadmin.exceptions import PyKAdminException
|
||||
from rest_framework.serializers import ValidationError
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
@ -48,7 +48,7 @@ def kerberos_sync_password(sender, user: User, password: str, **_):
|
||||
source.connection().getprinc(user_source_connection.identifier).change_password(
|
||||
password
|
||||
)
|
||||
except PyKAdminException as exc:
|
||||
except kadmin.KAdminError as exc:
|
||||
LOGGER.warning("failed to set Kerberos password", exc=exc, source=source)
|
||||
Event.new(
|
||||
EventAction.CONFIGURATION_ERROR,
|
||||
|
@ -2,9 +2,9 @@
|
||||
|
||||
from typing import Any
|
||||
|
||||
import kadmin
|
||||
from django.core.exceptions import FieldError
|
||||
from django.db import IntegrityError, transaction
|
||||
from kadmin import KAdmin
|
||||
from structlog.stdlib import BoundLogger, get_logger
|
||||
|
||||
from authentik.core.expression.exceptions import (
|
||||
@ -30,7 +30,7 @@ class KerberosSync:
|
||||
|
||||
_source: KerberosSource
|
||||
_logger: BoundLogger
|
||||
_connection: KAdmin
|
||||
_connection: "kadmin.KAdmin"
|
||||
mapper: SourceMapper
|
||||
user_manager: PropertyMappingManager
|
||||
group_manager: PropertyMappingManager
|
||||
@ -161,7 +161,7 @@ class KerberosSync:
|
||||
|
||||
user_count = 0
|
||||
with Krb5ConfContext(self._source):
|
||||
for principal in self._connection.list_principals(None):
|
||||
for principal in self._connection.principals():
|
||||
if self._handle_principal(principal):
|
||||
user_count += 1
|
||||
return user_count
|
||||
|
@ -23,7 +23,6 @@ class TestKerberosAuth(KerberosTestCase):
|
||||
)
|
||||
self.user = User.objects.create(username=generate_id())
|
||||
self.user.set_unusable_password()
|
||||
self.user.save()
|
||||
UserKerberosSourceConnection.objects.create(
|
||||
source=self.source, user=self.user, identifier=self.realm.user_princ
|
||||
)
|
||||
|
@ -2,8 +2,6 @@
|
||||
|
||||
from base64 import b64decode, b64encode
|
||||
from pathlib import Path
|
||||
from sys import platform
|
||||
from unittest import skipUnless
|
||||
|
||||
import gssapi
|
||||
from django.urls import reverse
|
||||
@ -38,7 +36,6 @@ class TestSPNEGOSource(KerberosTestCase):
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
@skipUnless(platform.startswith("linux"), "Requires compatible GSSAPI implementation")
|
||||
def test_source_login(self):
|
||||
"""test login view"""
|
||||
response = self.client.get(
|
||||
|
@ -332,7 +332,7 @@ class AuthenticatorValidateStageView(ChallengeStageView):
|
||||
serializer = SelectableStageSerializer(
|
||||
data={
|
||||
"pk": stage.pk,
|
||||
"name": getattr(stage, "friendly_name", stage.name),
|
||||
"name": getattr(stage, "friendly_name", stage.name) or stage.name,
|
||||
"verbose_name": str(stage._meta.verbose_name)
|
||||
.replace("Setup Stage", "")
|
||||
.strip(),
|
||||
|
@ -4,6 +4,7 @@ from unittest.mock import MagicMock, patch
|
||||
|
||||
from django.test.client import RequestFactory
|
||||
from django.urls.base import reverse
|
||||
from django.utils.timezone import now
|
||||
|
||||
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
|
||||
from authentik.flows.models import FlowDesignation, FlowStageBinding, NotConfiguredAction
|
||||
@ -13,6 +14,7 @@ from authentik.flows.views.executor import SESSION_KEY_PLAN
|
||||
from authentik.lib.generators import generate_id, generate_key
|
||||
from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice
|
||||
from authentik.stages.authenticator_static.models import AuthenticatorStaticStage
|
||||
from authentik.stages.authenticator_totp.models import AuthenticatorTOTPStage, TOTPDigits
|
||||
from authentik.stages.authenticator_validate.api import AuthenticatorValidateStageSerializer
|
||||
from authentik.stages.authenticator_validate.models import AuthenticatorValidateStage, DeviceClasses
|
||||
from authentik.stages.authenticator_validate.stage import PLAN_CONTEXT_DEVICE_CHALLENGES
|
||||
@ -76,8 +78,8 @@ class AuthenticatorValidateStageTests(FlowTestCase):
|
||||
conf_stage = AuthenticatorStaticStage.objects.create(
|
||||
name=generate_id(),
|
||||
)
|
||||
conf_stage2 = AuthenticatorStaticStage.objects.create(
|
||||
name=generate_id(),
|
||||
conf_stage2 = AuthenticatorTOTPStage.objects.create(
|
||||
name=generate_id(), digits=TOTPDigits.SIX
|
||||
)
|
||||
stage = AuthenticatorValidateStage.objects.create(
|
||||
name=generate_id(),
|
||||
@ -153,10 +155,14 @@ class AuthenticatorValidateStageTests(FlowTestCase):
|
||||
{
|
||||
"device_class": "static",
|
||||
"device_uid": "1",
|
||||
"challenge": {},
|
||||
"last_used": now(),
|
||||
},
|
||||
{
|
||||
"device_class": "totp",
|
||||
"device_uid": "2",
|
||||
"challenge": {},
|
||||
"last_used": now(),
|
||||
},
|
||||
]
|
||||
session[SESSION_KEY_PLAN] = plan
|
||||
|
File diff suppressed because one or more lines are too long
@ -26,6 +26,7 @@ from authentik.flows.models import FlowDesignation
|
||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
|
||||
from authentik.flows.stage import PLAN_CONTEXT_PENDING_USER_IDENTIFIER, ChallengeStageView
|
||||
from authentik.flows.views.executor import SESSION_KEY_APPLICATION_PRE, SESSION_KEY_GET
|
||||
from authentik.lib.avatars import DEFAULT_AVATAR
|
||||
from authentik.lib.utils.reflection import all_subclasses
|
||||
from authentik.lib.utils.urls import reverse_with_qs
|
||||
from authentik.root.middleware import ClientIPMiddleware
|
||||
@ -76,7 +77,7 @@ class IdentificationChallenge(Challenge):
|
||||
allow_show_password = BooleanField(default=False)
|
||||
application_pre = CharField(required=False)
|
||||
flow_designation = ChoiceField(FlowDesignation.choices)
|
||||
captcha_stage = CaptchaChallenge(required=False)
|
||||
captcha_stage = CaptchaChallenge(required=False, allow_null=True)
|
||||
|
||||
enroll_url = CharField(required=False)
|
||||
recovery_url = CharField(required=False)
|
||||
@ -224,6 +225,8 @@ class IdentificationStageView(ChallengeStageView):
|
||||
"js_url": current_stage.captcha_stage.js_url,
|
||||
"site_key": current_stage.captcha_stage.public_key,
|
||||
"interactive": current_stage.captcha_stage.interactive,
|
||||
"pending_user": "",
|
||||
"pending_user_avatar": DEFAULT_AVATAR,
|
||||
}
|
||||
if current_stage.captcha_stage
|
||||
else None
|
||||
|
@ -23,7 +23,6 @@ class SettingsSerializer(ModelSerializer):
|
||||
"footer_links",
|
||||
"gdpr_compliance",
|
||||
"impersonation",
|
||||
"impersonation_require_reason",
|
||||
"default_token_duration",
|
||||
"default_token_length",
|
||||
]
|
||||
|
@ -1,21 +0,0 @@
|
||||
# Generated by Django 5.0.9 on 2024-11-07 15:08
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_tenants", "0003_alter_tenant_default_token_duration"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="tenant",
|
||||
name="impersonation_require_reason",
|
||||
field=models.BooleanField(
|
||||
default=True,
|
||||
help_text="Require administrators to provide a reason for impersonating a user.",
|
||||
),
|
||||
),
|
||||
]
|
@ -85,10 +85,6 @@ class Tenant(TenantMixin, SerializerModel):
|
||||
impersonation = models.BooleanField(
|
||||
help_text=_("Globally enable/disable impersonation."), default=True
|
||||
)
|
||||
impersonation_require_reason = models.BooleanField(
|
||||
help_text=_("Require administrators to provide a reason for impersonating a user."),
|
||||
default=True,
|
||||
)
|
||||
default_token_duration = models.TextField(
|
||||
help_text=_("Default token duration"),
|
||||
default=DEFAULT_TOKEN_DURATION,
|
||||
|
@ -8,11 +8,9 @@ from authentik.root.install_id import get_install_id
|
||||
from authentik.tenants.models import Tenant
|
||||
|
||||
|
||||
def get_current_tenant(only: list[str] | None = None) -> Tenant:
|
||||
def get_current_tenant() -> Tenant:
|
||||
"""Get tenant for current request"""
|
||||
if only is None:
|
||||
only = []
|
||||
return Tenant.objects.only(*only).get(schema_name=connection.schema_name)
|
||||
return Tenant.objects.get(schema_name=connection.schema_name)
|
||||
|
||||
|
||||
def get_unique_identifier() -> str:
|
||||
|
@ -2,7 +2,7 @@
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://goauthentik.io/blueprints/schema.json",
|
||||
"type": "object",
|
||||
"title": "authentik 2024.10.2 Blueprint schema",
|
||||
"title": "authentik 2024.10.5 Blueprint schema",
|
||||
"required": [
|
||||
"version",
|
||||
"entries"
|
||||
|
@ -31,7 +31,7 @@ services:
|
||||
volumes:
|
||||
- redis:/data
|
||||
server:
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.10.2}
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.10.5}
|
||||
restart: unless-stopped
|
||||
command: server
|
||||
environment:
|
||||
@ -52,7 +52,7 @@ services:
|
||||
- postgresql
|
||||
- redis
|
||||
worker:
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.10.2}
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.10.5}
|
||||
restart: unless-stopped
|
||||
command: worker
|
||||
environment:
|
||||
|
6
go.mod
6
go.mod
@ -29,10 +29,10 @@ require (
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/wwt/guac v1.3.2
|
||||
goauthentik.io/api/v3 v3.2024102.2
|
||||
goauthentik.io/api/v3 v3.2024083.13
|
||||
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
|
||||
golang.org/x/oauth2 v0.24.0
|
||||
golang.org/x/sync v0.9.0
|
||||
golang.org/x/oauth2 v0.23.0
|
||||
golang.org/x/sync v0.8.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
layeh.com/radius v0.0.0-20210819152912-ad72663a72ab
|
||||
)
|
||||
|
12
go.sum
12
go.sum
@ -299,8 +299,8 @@ go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y
|
||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
goauthentik.io/api/v3 v3.2024102.2 h1:k2sIU7TkT2fOomBYo5KEc/mz5ipzaZUp5TuEOJLPX4g=
|
||||
goauthentik.io/api/v3 v3.2024102.2/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
||||
goauthentik.io/api/v3 v3.2024083.13 h1:xKh3feJYUeLw583zZ5ifgV0qjD37ZCOzgXPfbHQSbHM=
|
||||
goauthentik.io/api/v3 v3.2024083.13/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
@ -388,8 +388,8 @@ golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4Iltr
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
|
||||
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
|
||||
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@ -400,8 +400,8 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -29,4 +29,4 @@ func UserAgent() string {
|
||||
return fmt.Sprintf("authentik@%s", FullVersion())
|
||||
}
|
||||
|
||||
const VERSION = "2024.10.2"
|
||||
const VERSION = "2024.10.5"
|
||||
|
@ -33,7 +33,6 @@ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
|
||||
# Stage 2: Run
|
||||
FROM ghcr.io/goauthentik/fips-debian:bookworm-slim-fips
|
||||
|
||||
ARG VERSION
|
||||
ARG GIT_BUILD_HASH
|
||||
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
|
||||
|
||||
|
@ -1,17 +1,15 @@
|
||||
"""Wrapper for lifecycle/ak, to be installed by poetry"""
|
||||
|
||||
from os import system, waitstatus_to_exitcode
|
||||
from os import system
|
||||
from pathlib import Path
|
||||
from sys import argv, exit
|
||||
from sys import argv
|
||||
|
||||
|
||||
def main():
|
||||
"""Wrapper around ak bash script"""
|
||||
current_path = Path(__file__)
|
||||
args = " ".join(argv[1:])
|
||||
res = system(f"{current_path.parent}/ak {args}") # nosec
|
||||
exit_code = waitstatus_to_exitcode(res)
|
||||
exit(exit_code)
|
||||
system(f"{current_path.parent}/ak {args}") # nosec
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-11-18 00:09+0000\n"
|
||||
"POT-Creation-Date: 2024-10-23 16:39+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -101,21 +101,12 @@ msgstr ""
|
||||
msgid "Brands"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/core/api/devices.py
|
||||
msgid "Extra description not available"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/core/api/providers.py
|
||||
msgid ""
|
||||
"When not set all providers are returned. When set to true, only backchannel "
|
||||
"providers are returned. When set to false, backchannel providers are excluded"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/core/api/transactional_applications.py
|
||||
#, python-brace-format
|
||||
msgid "User lacks permission to create {model}"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/core/api/users.py
|
||||
msgid "No leading or trailing slashes allowed."
|
||||
msgstr ""
|
||||
@ -1135,10 +1126,6 @@ msgstr ""
|
||||
msgid "Password not set in context"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/policies/password/models.py
|
||||
msgid "Invalid password."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/policies/password/models.py
|
||||
#, python-format
|
||||
msgid "Password exists on %(count)d online lists."
|
||||
@ -2627,7 +2614,12 @@ msgid "Captcha Stages"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
msgid "Invalid captcha response. Retrying may solve this issue."
|
||||
msgid "Unknown error"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
#, python-brace-format
|
||||
msgid "Failed to validate token: {error}"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
@ -3214,10 +3206,6 @@ msgstr ""
|
||||
msgid "Globally enable/disable impersonation."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/tenants/models.py
|
||||
msgid "Require administrators to provide a reason for impersonating a user."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/tenants/models.py
|
||||
msgid "Default token duration"
|
||||
msgstr ""
|
||||
|
Binary file not shown.
Binary file not shown.
@ -11,17 +11,15 @@
|
||||
# Marco Vitale, 2024
|
||||
# Kowalski Dragon (kowalski7cc) <kowalski.7cc@gmail.com>, 2024
|
||||
# albanobattistella <albanobattistella@gmail.com>, 2024
|
||||
# Nicola Mersi, 2024
|
||||
# tom max, 2024
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-11-18 00:09+0000\n"
|
||||
"POT-Creation-Date: 2024-10-18 00:09+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: tom max, 2024\n"
|
||||
"Last-Translator: albanobattistella <albanobattistella@gmail.com>, 2024\n"
|
||||
"Language-Team: Italian (https://app.transifex.com/authentik/teams/119923/it/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -121,10 +119,6 @@ msgstr "Brand"
|
||||
msgid "Brands"
|
||||
msgstr "Brands"
|
||||
|
||||
#: authentik/core/api/devices.py
|
||||
msgid "Extra description not available"
|
||||
msgstr "Descrizione extra non disponibile"
|
||||
|
||||
#: authentik/core/api/providers.py
|
||||
msgid ""
|
||||
"When not set all providers are returned. When set to true, only backchannel "
|
||||
@ -135,11 +129,6 @@ msgstr ""
|
||||
" vengono restituiti solo i provider di backchannel. Se impostato su falso, i"
|
||||
" provider di backchannel vengono esclusi"
|
||||
|
||||
#: 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."
|
||||
msgstr "Non sono consentite barre oblique iniziali o finali."
|
||||
@ -594,28 +583,6 @@ msgstr "Limite massimo di connessioni raggiunto."
|
||||
msgid "(You are already connected in another tab/window)"
|
||||
msgstr "(Sei già connesso in un'altra scheda/finestra)"
|
||||
|
||||
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/models.py
|
||||
msgid "Endpoint Authenticator Google Device Trust Connector Stage"
|
||||
msgstr ""
|
||||
"Fase di autenticazione per la verifica dispositivo Google tramite endpoint"
|
||||
|
||||
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/models.py
|
||||
msgid "Endpoint Authenticator Google Device Trust Connector Stages"
|
||||
msgstr ""
|
||||
"Fasi di autenticazione per la verifica dispositivo Google tramite endpoint"
|
||||
|
||||
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/models.py
|
||||
msgid "Endpoint Device"
|
||||
msgstr "Dispositivo di Accesso"
|
||||
|
||||
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/models.py
|
||||
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/source/models.py
|
||||
msgid ""
|
||||
"Amount of time a user can take to return from the source to continue the "
|
||||
@ -1249,10 +1216,6 @@ msgstr ""
|
||||
msgid "Password not set in context"
|
||||
msgstr "Password non impostata nel contesto"
|
||||
|
||||
#: authentik/policies/password/models.py
|
||||
msgid "Invalid password."
|
||||
msgstr "Password invalida."
|
||||
|
||||
#: authentik/policies/password/models.py
|
||||
#, python-format
|
||||
msgid "Password exists on %(count)d online lists."
|
||||
@ -2054,124 +2017,6 @@ msgstr ""
|
||||
msgid "Used recovery-link to authenticate."
|
||||
msgstr "Utilizzato il link di recupero per autenticarsi."
|
||||
|
||||
#: authentik/sources/kerberos/models.py
|
||||
msgid "Kerberos realm"
|
||||
msgstr "Dominio Kerberos"
|
||||
|
||||
#: authentik/sources/kerberos/models.py
|
||||
msgid "Custom krb5.conf to use. Uses the system one by default"
|
||||
msgstr ""
|
||||
"krb5.conf personalizzato da usare. Usa la configurazione di sistema per "
|
||||
"default"
|
||||
|
||||
#: authentik/sources/kerberos/models.py
|
||||
msgid "Sync users from Kerberos into authentik"
|
||||
msgstr "Sincronizza utenti da Kerberos a authentik"
|
||||
|
||||
#: authentik/sources/kerberos/models.py
|
||||
msgid "When a user changes their password, sync it back to Kerberos"
|
||||
msgstr "Quando un utente cambia la sua password, sincronizzala in Kerberos"
|
||||
|
||||
#: authentik/sources/kerberos/models.py
|
||||
msgid "Principal to authenticate to kadmin for sync."
|
||||
msgstr "Entità da autenticare su kadmin per la sincronizzazione."
|
||||
|
||||
#: authentik/sources/kerberos/models.py
|
||||
msgid "Password to authenticate to kadmin for sync"
|
||||
msgstr "Password per autenticarsi in kadmin per sincronizzare"
|
||||
|
||||
#: authentik/sources/kerberos/models.py
|
||||
msgid ""
|
||||
"Keytab to authenticate to kadmin for sync. Must be base64-encoded or in the "
|
||||
"form TYPE:residual"
|
||||
msgstr ""
|
||||
"Keytab per autenticarsi su kadmin per la sincronizzazione. Deve essere con "
|
||||
"codifica base64 o nel formato TYPE:residual"
|
||||
|
||||
#: authentik/sources/kerberos/models.py
|
||||
msgid ""
|
||||
"Credentials cache to authenticate to kadmin for sync. Must be in the form "
|
||||
"TYPE:residual"
|
||||
msgstr ""
|
||||
"Credenziali memorizzate nella cache per autenticarsi su kadmin per la "
|
||||
"sincronizzazione. Devono essere nel formato TYPE:residual"
|
||||
|
||||
#: authentik/sources/kerberos/models.py
|
||||
msgid ""
|
||||
"Force the use of a specific server name for SPNEGO. Must be in the form "
|
||||
"HTTP@hostname"
|
||||
msgstr ""
|
||||
"Forza l'uso di un nome server specifico per SPNEGO. Deve essere nel formato "
|
||||
"HTTP@nomehost"
|
||||
|
||||
#: authentik/sources/kerberos/models.py
|
||||
msgid "SPNEGO keytab base64-encoded or path to keytab in the form FILE:path"
|
||||
msgstr ""
|
||||
"keytab SPNEGO con codifica base64 o percorso del keytab nel formato "
|
||||
"FILE:percorso"
|
||||
|
||||
#: authentik/sources/kerberos/models.py
|
||||
msgid "Credential cache to use for SPNEGO in form type:residual"
|
||||
msgstr ""
|
||||
"Cache delle credenziali da utilizzare per SPNEGO nella forma type:residual"
|
||||
|
||||
#: authentik/sources/kerberos/models.py
|
||||
msgid ""
|
||||
"If enabled, the authentik-stored password will be updated upon login with "
|
||||
"the Kerberos password backend"
|
||||
msgstr ""
|
||||
"Se abilitato, la password memorizzata in authentik verrà aggiornata al login"
|
||||
" nel backend Kerberos"
|
||||
|
||||
#: authentik/sources/kerberos/models.py
|
||||
msgid "Kerberos Source"
|
||||
msgstr "Sorgente Kerberos"
|
||||
|
||||
#: authentik/sources/kerberos/models.py
|
||||
msgid "Kerberos Sources"
|
||||
msgstr "Sorgenti Kerberos"
|
||||
|
||||
#: authentik/sources/kerberos/models.py
|
||||
msgid "Kerberos Source Property Mapping"
|
||||
msgstr "Mappa delle proprietà della sorgente kerberos"
|
||||
|
||||
#: authentik/sources/kerberos/models.py
|
||||
msgid "Kerberos Source Property Mappings"
|
||||
msgstr "Mappe delle proprietà della sorgente kerberos"
|
||||
|
||||
#: authentik/sources/kerberos/models.py
|
||||
msgid "User Kerberos Source Connection"
|
||||
msgstr "Connessione sorgente dell'utente kerberos"
|
||||
|
||||
#: authentik/sources/kerberos/models.py
|
||||
msgid "User Kerberos Source Connections"
|
||||
msgstr " Connessioni alle sorgente dell'utente kerberos"
|
||||
|
||||
#: authentik/sources/kerberos/models.py
|
||||
msgid "Group Kerberos Source Connection"
|
||||
msgstr " Connessione sorgente del gruppo kerberos"
|
||||
|
||||
#: authentik/sources/kerberos/models.py
|
||||
msgid "Group Kerberos Source Connections"
|
||||
msgstr "Connessioni alle sorgenti del gruppo kerberos"
|
||||
|
||||
#: authentik/sources/kerberos/views.py
|
||||
msgid "SPNEGO authentication required"
|
||||
msgstr "autenticazione SPNEGO necessaria"
|
||||
|
||||
#: authentik/sources/kerberos/views.py
|
||||
msgid ""
|
||||
"\n"
|
||||
" Make sure you have valid tickets (obtainable via kinit)\n"
|
||||
" and configured the browser correctly.\n"
|
||||
" Please contact your administrator.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Assicurati di avere un ticket valido (ottenibile tramite kinit)\n"
|
||||
" e di aver configurato correttamente il browser. \n"
|
||||
"Contatta il tuo amministratore."
|
||||
|
||||
#: authentik/sources/ldap/api.py
|
||||
msgid "Only a single LDAP Source with password synchronization is allowed"
|
||||
msgstr ""
|
||||
@ -2890,10 +2735,13 @@ msgid "Captcha Stages"
|
||||
msgstr "Fasi Captcha"
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
msgid "Invalid captcha response. Retrying may solve this issue."
|
||||
msgstr ""
|
||||
"Risposta captcha non valida. Un nuovo tentativo potrebbe risolvere il "
|
||||
"problema."
|
||||
msgid "Unknown error"
|
||||
msgstr "Errore sconosciuto"
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
#, python-brace-format
|
||||
msgid "Failed to validate token: {error}"
|
||||
msgstr "Impossibile convalidare il token: {error}"
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
msgid "Invalid captcha response"
|
||||
@ -3266,10 +3114,6 @@ msgstr "Database utente + password app"
|
||||
msgid "User database + LDAP password"
|
||||
msgstr "Database utenti + password LDAP"
|
||||
|
||||
#: authentik/stages/password/models.py
|
||||
msgid "User database + Kerberos password"
|
||||
msgstr "Database utenti + password Kerberos"
|
||||
|
||||
#: authentik/stages/password/models.py
|
||||
msgid "Selection of backends to test the password against."
|
||||
msgstr "Selezione di backend su cui testare la password."
|
||||
@ -3563,12 +3407,6 @@ msgstr ""
|
||||
msgid "Globally enable/disable impersonation."
|
||||
msgstr "Abilita/disabilita globalmente la l'impersonazione."
|
||||
|
||||
#: authentik/tenants/models.py
|
||||
msgid "Require administrators to provide a reason for impersonating a user."
|
||||
msgstr ""
|
||||
"Richiedi agli amministratori di fornire una ragione per impersonare un "
|
||||
"utente."
|
||||
|
||||
#: authentik/tenants/models.py
|
||||
msgid "Default token duration"
|
||||
msgstr "Durata token predefinita"
|
||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -15,7 +15,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-11-18 00:09+0000\n"
|
||||
"POT-Creation-Date: 2024-10-23 16:39+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: deluxghost, 2024\n"
|
||||
"Language-Team: Chinese Simplified (https://app.transifex.com/authentik/teams/119923/zh-Hans/)\n"
|
||||
@ -110,10 +110,6 @@ msgstr "品牌"
|
||||
msgid "Brands"
|
||||
msgstr "品牌"
|
||||
|
||||
#: authentik/core/api/devices.py
|
||||
msgid "Extra description not available"
|
||||
msgstr "额外描述不可用"
|
||||
|
||||
#: authentik/core/api/providers.py
|
||||
msgid ""
|
||||
"When not set all providers are returned. When set to true, only backchannel "
|
||||
@ -121,11 +117,6 @@ msgid ""
|
||||
"excluded"
|
||||
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."
|
||||
msgstr "不允许以斜线开始或结尾。"
|
||||
@ -1149,10 +1140,6 @@ msgstr "如果 zxcvbn 分数小于等于此值,则策略失败。"
|
||||
msgid "Password not set in context"
|
||||
msgstr "未在上下文中设置密码"
|
||||
|
||||
#: authentik/policies/password/models.py
|
||||
msgid "Invalid password."
|
||||
msgstr "无效密码。"
|
||||
|
||||
#: authentik/policies/password/models.py
|
||||
#, python-format
|
||||
msgid "Password exists on %(count)d online lists."
|
||||
@ -2662,8 +2649,13 @@ msgid "Captcha Stages"
|
||||
msgstr "验证码阶段"
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
msgid "Invalid captcha response. Retrying may solve this issue."
|
||||
msgstr "无效的验证码响应。重试可能会解决此问题。"
|
||||
msgid "Unknown error"
|
||||
msgstr "未知错误"
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
#, python-brace-format
|
||||
msgid "Failed to validate token: {error}"
|
||||
msgstr "验证令牌失败:{error}"
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
msgid "Invalid captcha response"
|
||||
@ -3269,10 +3261,6 @@ msgstr "启用时,所有由用户造成的事件会在相应用户被删除时
|
||||
msgid "Globally enable/disable impersonation."
|
||||
msgstr "全局启用/禁用模拟身份。"
|
||||
|
||||
#: authentik/tenants/models.py
|
||||
msgid "Require administrators to provide a reason for impersonating a user."
|
||||
msgstr "需要管理员提供模拟用户的原因。"
|
||||
|
||||
#: authentik/tenants/models.py
|
||||
msgid "Default token duration"
|
||||
msgstr "默认令牌持续时间"
|
||||
|
Binary file not shown.
@ -14,7 +14,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-11-18 00:09+0000\n"
|
||||
"POT-Creation-Date: 2024-10-23 16:39+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: deluxghost, 2024\n"
|
||||
"Language-Team: Chinese (China) (https://app.transifex.com/authentik/teams/119923/zh_CN/)\n"
|
||||
@ -109,10 +109,6 @@ msgstr "品牌"
|
||||
msgid "Brands"
|
||||
msgstr "品牌"
|
||||
|
||||
#: authentik/core/api/devices.py
|
||||
msgid "Extra description not available"
|
||||
msgstr "额外描述不可用"
|
||||
|
||||
#: authentik/core/api/providers.py
|
||||
msgid ""
|
||||
"When not set all providers are returned. When set to true, only backchannel "
|
||||
@ -120,11 +116,6 @@ msgid ""
|
||||
"excluded"
|
||||
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."
|
||||
msgstr "不允许前缀或后缀斜线。"
|
||||
@ -1148,10 +1139,6 @@ msgstr "如果 zxcvbn 分数小于等于此值,则策略失败。"
|
||||
msgid "Password not set in context"
|
||||
msgstr "未在上下文中设置密码"
|
||||
|
||||
#: authentik/policies/password/models.py
|
||||
msgid "Invalid password."
|
||||
msgstr "无效密码。"
|
||||
|
||||
#: authentik/policies/password/models.py
|
||||
#, python-format
|
||||
msgid "Password exists on %(count)d online lists."
|
||||
@ -2661,8 +2648,13 @@ msgid "Captcha Stages"
|
||||
msgstr "验证码阶段"
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
msgid "Invalid captcha response. Retrying may solve this issue."
|
||||
msgstr "无效的验证码响应。重试可能会解决此问题。"
|
||||
msgid "Unknown error"
|
||||
msgstr "未知错误"
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
#, python-brace-format
|
||||
msgid "Failed to validate token: {error}"
|
||||
msgstr "验证令牌失败:{error}"
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
msgid "Invalid captcha response"
|
||||
@ -3268,10 +3260,6 @@ msgstr "启用时,所有由用户造成的事件会在相应用户被删除时
|
||||
msgid "Globally enable/disable impersonation."
|
||||
msgstr "全局启用/禁用模拟身份。"
|
||||
|
||||
#: authentik/tenants/models.py
|
||||
msgid "Require administrators to provide a reason for impersonating a user."
|
||||
msgstr "需要管理员提供模拟用户的原因。"
|
||||
|
||||
#: authentik/tenants/models.py
|
||||
msgid "Default token duration"
|
||||
msgstr "默认令牌持续时间"
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@goauthentik/authentik",
|
||||
"version": "2024.10.2",
|
||||
"version": "2024.10.5",
|
||||
"private": true
|
||||
}
|
||||
|
1422
poetry.lock
generated
1422
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "authentik"
|
||||
version = "2024.10.2"
|
||||
version = "2024.10.5"
|
||||
description = ""
|
||||
authors = ["authentik Team <hello@goauthentik.io>"]
|
||||
|
||||
@ -131,13 +131,15 @@ pydantic-scim = "*"
|
||||
pyjwt = "*"
|
||||
pyrad = "*"
|
||||
python = "~3.12"
|
||||
python-kadmin-rs = "0.2.0"
|
||||
# Fork of python-kadmin with compilation fixes as it's unmaintained
|
||||
python-kadmin = { git = "https://github.com/authentik-community/python-kadmin.git", tag = "v0.2.0" }
|
||||
pyyaml = "*"
|
||||
requests-oauthlib = "*"
|
||||
scim2-filter-parser = "*"
|
||||
sentry-sdk = "*"
|
||||
service_identity = "*"
|
||||
setproctitle = "*"
|
||||
setuptools = "~69.1"
|
||||
structlog = "*"
|
||||
swagger-spec-validator = "*"
|
||||
tenant-schemas-celery = "*"
|
||||
|
67
schema.yml
67
schema.yml
@ -1,7 +1,7 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: authentik
|
||||
version: 2024.10.2
|
||||
version: 2024.10.5
|
||||
description: Making authentication simple.
|
||||
contact:
|
||||
email: hello@goauthentik.io
|
||||
@ -5295,12 +5295,6 @@ paths:
|
||||
required: true
|
||||
tags:
|
||||
- core
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ImpersonationRequest'
|
||||
required: true
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
@ -42718,14 +42712,6 @@ components:
|
||||
incorrect user info is entered.
|
||||
required:
|
||||
- name
|
||||
ImpersonationRequest:
|
||||
type: object
|
||||
properties:
|
||||
reason:
|
||||
type: string
|
||||
minLength: 1
|
||||
required:
|
||||
- reason
|
||||
InstallID:
|
||||
type: object
|
||||
properties:
|
||||
@ -50046,10 +50032,6 @@ components:
|
||||
impersonation:
|
||||
type: boolean
|
||||
description: Globally enable/disable impersonation.
|
||||
impersonation_require_reason:
|
||||
type: boolean
|
||||
description: Require administrators to provide a reason for impersonating
|
||||
a user.
|
||||
default_token_duration:
|
||||
type: string
|
||||
minLength: 1
|
||||
@ -53804,10 +53786,6 @@ components:
|
||||
impersonation:
|
||||
type: boolean
|
||||
description: Globally enable/disable impersonation.
|
||||
impersonation_require_reason:
|
||||
type: boolean
|
||||
description: Require administrators to provide a reason for impersonating
|
||||
a user.
|
||||
default_token_duration:
|
||||
type: string
|
||||
description: Default token duration
|
||||
@ -53847,10 +53825,6 @@ components:
|
||||
impersonation:
|
||||
type: boolean
|
||||
description: Globally enable/disable impersonation.
|
||||
impersonation_require_reason:
|
||||
type: boolean
|
||||
description: Require administrators to provide a reason for impersonating
|
||||
a user.
|
||||
default_token_duration:
|
||||
type: string
|
||||
minLength: 1
|
||||
@ -54707,10 +54681,6 @@ components:
|
||||
$ref: '#/components/schemas/ProviderModelEnum'
|
||||
provider:
|
||||
$ref: '#/components/schemas/modelRequest'
|
||||
policy_bindings:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/TransactionPolicyBindingRequest'
|
||||
required:
|
||||
- app
|
||||
- provider
|
||||
@ -54728,41 +54698,6 @@ components:
|
||||
required:
|
||||
- applied
|
||||
- logs
|
||||
TransactionPolicyBindingRequest:
|
||||
type: object
|
||||
description: PolicyBindingSerializer which does not require target as target
|
||||
is set implicitly
|
||||
properties:
|
||||
policy:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
group:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
user:
|
||||
type: integer
|
||||
nullable: true
|
||||
negate:
|
||||
type: boolean
|
||||
description: Negates the outcome of the policy. Messages are unaffected.
|
||||
enabled:
|
||||
type: boolean
|
||||
order:
|
||||
type: integer
|
||||
maximum: 2147483647
|
||||
minimum: -2147483648
|
||||
timeout:
|
||||
type: integer
|
||||
maximum: 2147483647
|
||||
minimum: 0
|
||||
description: Timeout after which Policy execution is terminated.
|
||||
failure_result:
|
||||
type: boolean
|
||||
description: Result if the Policy execution fails.
|
||||
required:
|
||||
- order
|
||||
TypeCreate:
|
||||
type: object
|
||||
description: Types of an object that can be created
|
||||
|
@ -12,6 +12,9 @@ with open("local.env.yml", "w", encoding="utf-8") as _config:
|
||||
"secret_key": generate_id(),
|
||||
"postgresql": {
|
||||
"user": "postgres",
|
||||
"read_replicas": {
|
||||
"0": {},
|
||||
},
|
||||
},
|
||||
"outposts": {
|
||||
"container_image_base": "ghcr.io/goauthentik/dev-%(type)s:gh-%(build_hash)s",
|
||||
|
@ -6,7 +6,6 @@
|
||||
"de",
|
||||
"es",
|
||||
"fr",
|
||||
"it",
|
||||
"ko",
|
||||
"nl",
|
||||
"pl",
|
||||
|
@ -208,13 +208,6 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
|
||||
help=${msg("Globally enable/disable impersonation.")}
|
||||
>
|
||||
</ak-switch-input>
|
||||
<ak-switch-input
|
||||
name="impersonationRequireReason"
|
||||
label=${msg("Require reason for impersonation")}
|
||||
?checked="${this._settings?.impersonationRequireReason}"
|
||||
help=${msg("Require administrators to provide a reason for impersonating a user.")}
|
||||
>
|
||||
</ak-switch-input>
|
||||
<ak-text-input
|
||||
name="defaultTokenDuration"
|
||||
label=${msg("Default token duration")}
|
||||
|
@ -2,7 +2,6 @@ import "@goauthentik/admin/applications/ApplicationForm";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import MDApplication from "@goauthentik/docs/add-secure-apps/applications/index.md";
|
||||
import "@goauthentik/elements/AppIcon.js";
|
||||
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
|
||||
import "@goauthentik/elements/Markdown";
|
||||
import "@goauthentik/elements/buttons/SpinnerButton";
|
||||
import "@goauthentik/elements/forms/DeleteBulkForm";
|
||||
@ -13,7 +12,7 @@ import { TableColumn } from "@goauthentik/elements/table/Table";
|
||||
import { TablePage } from "@goauthentik/elements/table/TablePage";
|
||||
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
|
||||
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
@ -41,7 +40,7 @@ export const applicationListStyle = css`
|
||||
`;
|
||||
|
||||
@customElement("ak-application-list")
|
||||
export class ApplicationListPage extends WithBrandConfig(TablePage<Application>) {
|
||||
export class ApplicationListPage extends TablePage<Application> {
|
||||
searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
@ -50,7 +49,7 @@ export class ApplicationListPage extends WithBrandConfig(TablePage<Application>)
|
||||
}
|
||||
pageDescription(): string {
|
||||
return msg(
|
||||
str`External applications that use ${this.brand.brandingTitle || "authentik"} as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.`,
|
||||
"External applications that use authentik as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.",
|
||||
);
|
||||
}
|
||||
pageIcon(): string {
|
||||
|
@ -25,7 +25,6 @@ import {
|
||||
type TransactionApplicationRequest,
|
||||
type TransactionApplicationResponse,
|
||||
ValidationError,
|
||||
instanceOfValidationError,
|
||||
} from "@goauthentik/api";
|
||||
|
||||
import BasePanel from "../BasePanel";
|
||||
@ -70,9 +69,6 @@ const successState: State = {
|
||||
icon: ["fa-check-circle", "pf-m-success"],
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const isValidationError = (v: any): v is ValidationError => instanceOfValidationError(v);
|
||||
|
||||
@customElement("ak-application-wizard-commit-application")
|
||||
export class ApplicationWizardCommitApplication extends BasePanel {
|
||||
static get styles() {
|
||||
@ -138,25 +134,10 @@ export class ApplicationWizardCommitApplication extends BasePanel {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
.catch(async (resolution: any) => {
|
||||
const errors = await parseAPIError(resolution);
|
||||
|
||||
// THIS is a really gross special case; if the user is duplicating the name of an
|
||||
// existing provider, the error appears on the `app` (!) error object. We have to
|
||||
// move that to the `provider.name` error field so it shows up in the right place.
|
||||
if (isValidationError(errors) && Array.isArray(errors?.app?.provider)) {
|
||||
const providerError = errors.app.provider;
|
||||
errors.provider = errors.provider ?? {};
|
||||
errors.provider.name = providerError;
|
||||
delete errors.app.provider;
|
||||
if (Object.keys(errors.app).length === 0) {
|
||||
delete errors.app;
|
||||
}
|
||||
}
|
||||
|
||||
this.errors = errors;
|
||||
this.dispatchWizardUpdate({
|
||||
update: {
|
||||
...this.wizard,
|
||||
errors: this.errors,
|
||||
errors,
|
||||
},
|
||||
status: "failed",
|
||||
});
|
||||
|
@ -71,7 +71,6 @@ export class ApplicationWizardApplicationDetails extends WithBrandConfig(BasePro
|
||||
flowType=${FlowsInstancesListDesignationEnum.Invalidation}
|
||||
.currentFlow=${provider?.invalidationFlow}
|
||||
.brandFlow=${this.brand.flowInvalidation}
|
||||
defaultFlowSlug="default-invalidation-flow"
|
||||
required
|
||||
></ak-branded-flow-search>
|
||||
<p class="pf-c-form__helper-text">${msg("Flow used for unbinding users.")}</p>
|
||||
|
@ -86,7 +86,7 @@ export class ApplicationWizardAuthenticationByRadius extends WithBrandConfig(Bas
|
||||
<ak-flow-search
|
||||
flowType=${FlowsInstancesListDesignationEnum.Invalidation}
|
||||
.currentFlow=${provider?.invalidationFlow}
|
||||
defaultFlowSlug="default-invalidation-flow"
|
||||
defaultFlowSlug="default-provider-invalidation-flow"
|
||||
required
|
||||
></ak-flow-search>
|
||||
<p class="pf-c-form__helper-text">
|
||||
|
@ -1,11 +1,9 @@
|
||||
import "@goauthentik/admin/users/ServiceAccountForm";
|
||||
import "@goauthentik/admin/users/UserActiveForm";
|
||||
import "@goauthentik/admin/users/UserForm";
|
||||
import "@goauthentik/admin/users/UserImpersonateForm";
|
||||
import "@goauthentik/admin/users/UserPasswordForm";
|
||||
import "@goauthentik/admin/users/UserResetEmailForm";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { PFSize } from "@goauthentik/common/enums.js";
|
||||
import { MessageLevel } from "@goauthentik/common/messages";
|
||||
import { me } from "@goauthentik/common/users";
|
||||
import { getRelativeTime } from "@goauthentik/common/utils";
|
||||
@ -215,22 +213,21 @@ export class RelatedUserList extends WithBrandConfig(WithCapabilitiesConfig(Tabl
|
||||
</ak-forms-modal>
|
||||
${canImpersonate
|
||||
? html`
|
||||
<ak-forms-modal size=${PFSize.Medium} id="impersonate-request">
|
||||
<span slot="submit">${msg("Impersonate")}</span>
|
||||
<span slot="header">${msg("Impersonate")} ${item.username}</span>
|
||||
<ak-user-impersonate-form
|
||||
slot="form"
|
||||
.instancePk=${item.pk}
|
||||
></ak-user-impersonate-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-tertiary">
|
||||
<pf-tooltip
|
||||
position="top"
|
||||
content=${msg("Temporarily assume the identity of this user")}
|
||||
>
|
||||
<span>${msg("Impersonate")}</span>
|
||||
</pf-tooltip>
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-action-button
|
||||
class="pf-m-tertiary"
|
||||
.apiRequest=${() => {
|
||||
return new CoreApi(DEFAULT_CONFIG)
|
||||
.coreUsersImpersonateCreate({
|
||||
id: item.pk,
|
||||
impersonationRequest: { reason: "" },
|
||||
})
|
||||
.then(() => {
|
||||
window.location.href = "/";
|
||||
});
|
||||
}}
|
||||
>
|
||||
${msg("Impersonate")}
|
||||
</ak-action-button>
|
||||
`
|
||||
: html``}`,
|
||||
];
|
||||
|
@ -157,7 +157,6 @@ export class LDAPProviderFormPage extends WithBrandConfig(BaseProviderForm<LDAPP
|
||||
flowType=${FlowsInstancesListDesignationEnum.Invalidation}
|
||||
.currentFlow=${this.instance?.invalidationFlow}
|
||||
.brandFlow=${this.brand.flowInvalidation}
|
||||
defaultFlowSlug="default-invalidation-flow"
|
||||
required
|
||||
></ak-branded-flow-search>
|
||||
<p class="pf-c-form__helper-text">
|
||||
|
@ -176,7 +176,7 @@ export class RadiusProviderFormPage extends WithBrandConfig(BaseProviderForm<Rad
|
||||
<ak-flow-search
|
||||
flowType=${FlowsInstancesListDesignationEnum.Invalidation}
|
||||
.currentFlow=${this.instance?.invalidationFlow}
|
||||
defaultFlowSlug="default-invalidation-flow"
|
||||
defaultFlowSlug="default-provider-invalidation-flow"
|
||||
required
|
||||
></ak-flow-search>
|
||||
<p class="pf-c-form__helper-text">
|
||||
|
@ -20,9 +20,6 @@ export class UserForm extends ModelForm<User, number> {
|
||||
@property({ attribute: false })
|
||||
group?: Group;
|
||||
|
||||
@property()
|
||||
defaultPath: string = "users";
|
||||
|
||||
static get defaultUserAttributes(): { [key: string]: unknown } {
|
||||
return {};
|
||||
}
|
||||
@ -175,7 +172,7 @@ export class UserForm extends ModelForm<User, number> {
|
||||
<ak-form-element-horizontal label=${msg("Path")} ?required=${true} name="path">
|
||||
<input
|
||||
type="text"
|
||||
value="${first(this.instance?.path, this.defaultPath)}"
|
||||
value="${first(this.instance?.path, "users")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
|
@ -1,40 +0,0 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import "@goauthentik/components/ak-text-input";
|
||||
import { Form } from "@goauthentik/elements/forms/Form";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import { CoreApi, ImpersonationRequest } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-user-impersonate-form")
|
||||
export class UserImpersonateForm extends Form<ImpersonationRequest> {
|
||||
@property({ type: Number })
|
||||
instancePk?: number;
|
||||
|
||||
async send(data: ImpersonationRequest): Promise<void> {
|
||||
return new CoreApi(DEFAULT_CONFIG)
|
||||
.coreUsersImpersonateCreate({
|
||||
id: this.instancePk || 0,
|
||||
impersonationRequest: data,
|
||||
})
|
||||
.then(() => {
|
||||
window.location.href = "/";
|
||||
});
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<ak-text-input
|
||||
name="reason"
|
||||
label=${msg("Reason")}
|
||||
help=${msg("Reason for impersonating the user")}
|
||||
></ak-text-input>`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ak-user-impersonate-form": UserImpersonateForm;
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@ import { AdminInterface } from "@goauthentik/admin/AdminInterface";
|
||||
import "@goauthentik/admin/users/ServiceAccountForm";
|
||||
import "@goauthentik/admin/users/UserActiveForm";
|
||||
import "@goauthentik/admin/users/UserForm";
|
||||
import "@goauthentik/admin/users/UserImpersonateForm";
|
||||
import "@goauthentik/admin/users/UserPasswordForm";
|
||||
import "@goauthentik/admin/users/UserResetEmailForm";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
@ -267,22 +266,21 @@ export class UserListPage extends WithBrandConfig(WithCapabilitiesConfig(TablePa
|
||||
</ak-forms-modal>
|
||||
${canImpersonate
|
||||
? html`
|
||||
<ak-forms-modal size=${PFSize.Medium} id="impersonate-request">
|
||||
<span slot="submit">${msg("Impersonate")}</span>
|
||||
<span slot="header">${msg("Impersonate")} ${item.username}</span>
|
||||
<ak-user-impersonate-form
|
||||
slot="form"
|
||||
.instancePk=${item.pk}
|
||||
></ak-user-impersonate-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-tertiary">
|
||||
<pf-tooltip
|
||||
position="top"
|
||||
content=${msg("Temporarily assume the identity of this user")}
|
||||
>
|
||||
<span>${msg("Impersonate")}</span>
|
||||
</pf-tooltip>
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-action-button
|
||||
class="pf-m-tertiary"
|
||||
.apiRequest=${() => {
|
||||
return new CoreApi(DEFAULT_CONFIG)
|
||||
.coreUsersImpersonateCreate({
|
||||
id: item.pk,
|
||||
impersonationRequest: { reason: "" },
|
||||
})
|
||||
.then(() => {
|
||||
window.location.href = "/";
|
||||
});
|
||||
}}
|
||||
>
|
||||
${msg("Impersonate")}
|
||||
</ak-action-button>
|
||||
`
|
||||
: html``}`,
|
||||
];
|
||||
@ -395,7 +393,7 @@ export class UserListPage extends WithBrandConfig(WithCapabilitiesConfig(TablePa
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${msg("Create")} </span>
|
||||
<span slot="header"> ${msg("Create User")} </span>
|
||||
<ak-user-form defaultPath=${this.activePath} slot="form"> </ak-user-form>
|
||||
<ak-user-form slot="form"> </ak-user-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">${msg("Create")}</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-modal .closeAfterSuccessfulSubmit=${false} .cancelText=${msg("Close")}>
|
||||
@ -417,9 +415,6 @@ export class UserListPage extends WithBrandConfig(WithCapabilitiesConfig(TablePa
|
||||
<ak-treeview
|
||||
.items=${this.userPaths?.paths || []}
|
||||
activePath=${this.activePath}
|
||||
@ak-refresh=${(ev: CustomEvent<{ path: string }>) => {
|
||||
this.activePath = ev.detail.path;
|
||||
}}
|
||||
></ak-treeview>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -5,7 +5,6 @@ import "@goauthentik/admin/users/UserActiveForm";
|
||||
import "@goauthentik/admin/users/UserApplicationTable";
|
||||
import "@goauthentik/admin/users/UserChart";
|
||||
import "@goauthentik/admin/users/UserForm";
|
||||
import "@goauthentik/admin/users/UserImpersonateForm";
|
||||
import {
|
||||
renderRecoveryEmailRequest,
|
||||
requestRecoveryLink,
|
||||
@ -209,22 +208,27 @@ export class UserViewPage extends WithCapabilitiesConfig(AKElement) {
|
||||
</ak-user-active-form>
|
||||
${canImpersonate
|
||||
? html`
|
||||
<ak-forms-modal size=${PFSize.Medium} id="impersonate-request">
|
||||
<span slot="submit">${msg("Impersonate")}</span>
|
||||
<span slot="header">${msg("Impersonate")} ${user.username}</span>
|
||||
<ak-user-impersonate-form
|
||||
slot="form"
|
||||
.instancePk=${user.pk}
|
||||
></ak-user-impersonate-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary pf-m-block">
|
||||
<pf-tooltip
|
||||
position="top"
|
||||
content=${msg("Temporarily assume the identity of this user")}
|
||||
>
|
||||
<span>${msg("Impersonate")}</span>
|
||||
</pf-tooltip>
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-action-button
|
||||
class="pf-m-secondary pf-m-block"
|
||||
id="impersonate-user-button"
|
||||
.apiRequest=${() => {
|
||||
return new CoreApi(DEFAULT_CONFIG)
|
||||
.coreUsersImpersonateCreate({
|
||||
id: user.pk,
|
||||
impersonationRequest: { reason: "" },
|
||||
})
|
||||
.then(() => {
|
||||
window.location.href = "/";
|
||||
});
|
||||
}}
|
||||
>
|
||||
<pf-tooltip
|
||||
position="top"
|
||||
content=${msg("Temporarily assume the identity of this user")}
|
||||
>
|
||||
${msg("Impersonate")}
|
||||
</pf-tooltip>
|
||||
</ak-action-button>
|
||||
`
|
||||
: nothing}
|
||||
</div> `;
|
||||
|
@ -3,7 +3,7 @@ export const SUCCESS_CLASS = "pf-m-success";
|
||||
export const ERROR_CLASS = "pf-m-danger";
|
||||
export const PROGRESS_CLASS = "pf-m-in-progress";
|
||||
export const CURRENT_CLASS = "pf-m-current";
|
||||
export const VERSION = "2024.10.2";
|
||||
export const VERSION = "2024.10.5";
|
||||
export const TITLE_DEFAULT = "authentik";
|
||||
export const ROUTE_SEPARATOR = ";";
|
||||
|
||||
|
@ -89,9 +89,6 @@ export class TreeViewNode extends AKElement {
|
||||
new CustomEvent(EVENT_REFRESH, {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: {
|
||||
path: this.fullPath,
|
||||
},
|
||||
}),
|
||||
);
|
||||
}}
|
||||
|
@ -42,19 +42,18 @@ const debug: LocaleRow = [
|
||||
|
||||
// prettier-ignore
|
||||
const LOCALE_TABLE: LocaleRow[] = [
|
||||
["de", /^de([_-]|$)/i, () => msg("German"), async () => await import("@goauthentik/locales/de")],
|
||||
["en", /^en([_-]|$)/i, () => msg("English"), async () => await import("@goauthentik/locales/en")],
|
||||
["es", /^es([_-]|$)/i, () => msg("Spanish"), async () => await import("@goauthentik/locales/es")],
|
||||
["de", /^de([_-]|$)/i, () => msg("German"), async () => await import("@goauthentik/locales/de")],
|
||||
["fr", /^fr([_-]|$)/i, () => msg("French"), async () => await import("@goauthentik/locales/fr")],
|
||||
["it", /^it([_-]|$)/i, () => msg("Italian"), async () => await import("@goauthentik/locales/it")],
|
||||
["ko", /^ko([_-]|$)/i, () => msg("Korean"), async () => await import("@goauthentik/locales/ko")],
|
||||
["nl", /^nl([_-]|$)/i, () => msg("Dutch"), async () => await import("@goauthentik/locales/nl")],
|
||||
["pl", /^pl([_-]|$)/i, () => msg("Polish"), async () => await import("@goauthentik/locales/pl")],
|
||||
["ru", /^ru([_-]|$)/i, () => msg("Russian"), async () => await import("@goauthentik/locales/ru")],
|
||||
["tr", /^tr([_-]|$)/i, () => msg("Turkish"), async () => await import("@goauthentik/locales/tr")],
|
||||
["zh-Hant", /^zh[_-](HK|Hant)/i, () => msg("Chinese (traditional)"), async () => await import("@goauthentik/locales/zh-Hant")],
|
||||
["zh_TW", /^zh[_-]TW$/i, () => msg("Taiwanese Mandarin"), async () => await import("@goauthentik/locales/zh_TW")],
|
||||
["zh-Hans", /^zh(\b|_)/i, () => msg("Chinese (simplified)"), async () => await import("@goauthentik/locales/zh-Hans")],
|
||||
["zh-Hant", /^zh[_-](HK|Hant)/i, () => msg("Chinese (traditional)"), async () => await import("@goauthentik/locales/zh-Hant")],
|
||||
debug
|
||||
];
|
||||
|
||||
|
41
web/src/elements/utils/listenerController.ts
Normal file
41
web/src/elements/utils/listenerController.ts
Normal file
@ -0,0 +1,41 @@
|
||||
// This is a more modern way to handle disconnecting listeners on demand.
|
||||
|
||||
// example usage:
|
||||
|
||||
/*
|
||||
export class MyElement extends LitElement {
|
||||
|
||||
this.listenerController = new ListenerController();
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
window.addEventListener("event-1", handler1, { signal: this.listenerController.signal });
|
||||
window.addEventListener("event-2", handler2, { signal: this.listenerController.signal });
|
||||
window.addEventListener("event-3", handler3, { signal: this.listenerController.signal });
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
// This will disconnect *all* the event listeners at once, and resets the listenerController,
|
||||
// releasing the memory used for the signal as well. No more trying to map all the
|
||||
// `addEventListener` to `removeEventListener` tediousness!
|
||||
this.listenerController.abort();
|
||||
super.disconnectedCallback();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
export class ListenerController {
|
||||
listenerController?: AbortController;
|
||||
|
||||
get signal() {
|
||||
if (!this.listenerController) {
|
||||
this.listenerController = new AbortController();
|
||||
}
|
||||
return this.listenerController.signal;
|
||||
}
|
||||
|
||||
abort() {
|
||||
this.listenerController?.abort();
|
||||
this.listenerController = undefined;
|
||||
}
|
||||
}
|
@ -5,14 +5,15 @@ import {
|
||||
TITLE_DEFAULT,
|
||||
} from "@goauthentik/common/constants";
|
||||
import { globalAK } from "@goauthentik/common/global";
|
||||
import { purify } from "@goauthentik/common/purify";
|
||||
import { configureSentry } from "@goauthentik/common/sentry";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import { WebsocketClient } from "@goauthentik/common/ws";
|
||||
import { Interface } from "@goauthentik/elements/Interface";
|
||||
import "@goauthentik/elements/LoadingOverlay";
|
||||
import "@goauthentik/elements/ak-locale-context";
|
||||
import { DefaultBrand } from "@goauthentik/elements/sidebar/SidebarBrand";
|
||||
import { themeImage } from "@goauthentik/elements/utils/images";
|
||||
import "@goauthentik/flow/components/ak-brand-footer";
|
||||
import "@goauthentik/flow/sources/apple/AppleLoginInit";
|
||||
import "@goauthentik/flow/sources/plex/PlexLoginInit";
|
||||
import "@goauthentik/flow/stages/FlowErrorStage";
|
||||
@ -25,7 +26,6 @@ import { CSSResult, PropertyValues, TemplateResult, css, html, nothing } from "l
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
||||
import { until } from "lit/directives/until.js";
|
||||
import { html as staticHtml, unsafeStatic } from "lit/static-html.js";
|
||||
|
||||
import PFBackgroundImage from "@patternfly/patternfly/components/BackgroundImage/background-image.css";
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
@ -49,52 +49,6 @@ import {
|
||||
UiThemeEnum,
|
||||
} from "@goauthentik/api";
|
||||
|
||||
type StageRenderer = {
|
||||
// Provide the lit-element tag if it's different from the challenge.component name
|
||||
tag?: string;
|
||||
// Provide a dynamic import whenever possible; otherwise, make sure you include it in the
|
||||
// build-time imports above.
|
||||
import?: () => Promise<unknown>;
|
||||
};
|
||||
type StageRenderers = { [key: string]: StageRenderer };
|
||||
|
||||
// authentik's standard stages and the Lit components that handle them. A "standard stage" conforms
|
||||
// to an API that takes two properties:
|
||||
// `.host=${host: StageHost} .challenge=${challenge: ChallengeTypes}`
|
||||
// Exceptions are handled in a switch/case statement below the renderer for these.
|
||||
|
||||
// All of that `async () => await import("@goauthentik/flow/...")` boilerplate cannot be abstracted
|
||||
// away because [import is not a function](https://v8.dev/features/dynamic-import), it is a
|
||||
// _statement_, and its contents are statically analyzed by bundlers, compilers, and the V8
|
||||
// interpreter.
|
||||
|
||||
// Prettier ignore to keep the table looking like a table:
|
||||
// prettier-ignore
|
||||
const allStages: StageRenderers = {
|
||||
"ak-stage-access-denied": { import: async () => await import("@goauthentik/flow/stages/access_denied/AccessDeniedStage") },
|
||||
"ak-stage-identification": { import: async () => await import("@goauthentik/flow/stages/identification/IdentificationStage") },
|
||||
"ak-stage-password": { import: async () => await import("@goauthentik/flow/stages/password/PasswordStage") },
|
||||
"ak-stage-captcha": { import: async () => await import("@goauthentik/flow/stages/captcha/CaptchaStage") },
|
||||
"ak-stage-consent": { import: async () => await import("@goauthentik/flow/stages/consent/ConsentStage") },
|
||||
"ak-stage-dummy": { import: async () => await import("@goauthentik/flow/stages/dummy/DummyStage") },
|
||||
"ak-stage-email": { import: async () => await import("@goauthentik/flow/stages/email/EmailStage") },
|
||||
"ak-stage-autosubmit": { import: async () => await import("@goauthentik/flow/stages/autosubmit/AutosubmitStage") },
|
||||
"ak-stage-prompt": { import: async () => await import("@goauthentik/flow/stages/prompt/PromptStage") },
|
||||
"ak-stage-authenticator-totp": { import: async () => await import("@goauthentik/flow/stages/authenticator_totp/AuthenticatorTOTPStage") },
|
||||
"ak-stage-authenticator-duo": { import: async () => await import("@goauthentik/flow/stages/authenticator_duo/AuthenticatorDuoStage") },
|
||||
"ak-stage-authenticator-static": { import: async () => await import("@goauthentik/flow/stages/authenticator_static/AuthenticatorStaticStage") },
|
||||
"ak-stage-authenticator-webauthn": { },
|
||||
"ak-stage-authenticator-sms": { import: async () => await import("@goauthentik/flow/stages/authenticator_sms/AuthenticatorSMSStage") },
|
||||
"ak-stage-authenticator-validate": { import: async () => await import("@goauthentik/flow/stages/authenticator_validate/AuthenticatorValidateStage") },
|
||||
"ak-stage-user-login": { import: async () => await import("@goauthentik/flow/stages/user_login/UserLoginStage") },
|
||||
"ak-source-plex": { tag: "ak-flow-source-plex" },
|
||||
"ak-source-oauth-apple": { tag: "ak-flow-source-oauth-apple" },
|
||||
"ak-provider-oauth2-device-code": { tag: "ak-flow-provider-oauth2-code", import: async () => await import("@goauthentik/flow/providers/oauth2/DeviceCode") },
|
||||
"ak-provider-oauth2-device-code-finish": { tag: "ak-flow-provider-oauth2-code-finish", import: async () => await import("@goauthentik/flow/providers/oauth2/DeviceCodeFinish") },
|
||||
"ak-stage-session-end": { import: async () => await import("@goauthentik/flow/providers/SessionEnd") },
|
||||
"ak-stage-flow-error": { },
|
||||
} as const;
|
||||
|
||||
@customElement("ak-flow-executor")
|
||||
export class FlowExecutor extends Interface implements StageHost {
|
||||
@property()
|
||||
@ -345,21 +299,142 @@ export class FlowExecutor extends Interface implements StageHost {
|
||||
if (!this.challenge) {
|
||||
return html`<ak-empty-state loading> </ak-empty-state>`;
|
||||
}
|
||||
const stage = allStages[this.challenge.component];
|
||||
if (stage) {
|
||||
if (stage.import) {
|
||||
await stage.import();
|
||||
}
|
||||
const tag = stage.tag ?? this.challenge.component;
|
||||
// Prettier doesn't know what `staticHTML` is, will try to format it by
|
||||
// prettier-ignore
|
||||
return staticHtml`<${unsafeStatic(tag)}
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></${unsafeStatic(tag)}>`;
|
||||
}
|
||||
|
||||
switch (this.challenge?.component) {
|
||||
case "ak-stage-access-denied":
|
||||
await import("@goauthentik/flow/stages/access_denied/AccessDeniedStage");
|
||||
return html`<ak-stage-access-denied
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-access-denied>`;
|
||||
case "ak-stage-identification":
|
||||
await import("@goauthentik/flow/stages/identification/IdentificationStage");
|
||||
return html`<ak-stage-identification
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-identification>`;
|
||||
case "ak-stage-password":
|
||||
await import("@goauthentik/flow/stages/password/PasswordStage");
|
||||
return html`<ak-stage-password
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-password>`;
|
||||
case "ak-stage-captcha":
|
||||
await import("@goauthentik/flow/stages/captcha/CaptchaStage");
|
||||
return html`<ak-stage-captcha
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-captcha>`;
|
||||
case "ak-stage-consent":
|
||||
await import("@goauthentik/flow/stages/consent/ConsentStage");
|
||||
return html`<ak-stage-consent
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-consent>`;
|
||||
case "ak-stage-dummy":
|
||||
await import("@goauthentik/flow/stages/dummy/DummyStage");
|
||||
return html`<ak-stage-dummy
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-dummy>`;
|
||||
case "ak-stage-email":
|
||||
await import("@goauthentik/flow/stages/email/EmailStage");
|
||||
return html`<ak-stage-email
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-email>`;
|
||||
case "ak-stage-autosubmit":
|
||||
await import("@goauthentik/flow/stages/autosubmit/AutosubmitStage");
|
||||
return html`<ak-stage-autosubmit
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-autosubmit>`;
|
||||
case "ak-stage-prompt":
|
||||
await import("@goauthentik/flow/stages/prompt/PromptStage");
|
||||
return html`<ak-stage-prompt
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-prompt>`;
|
||||
case "ak-stage-authenticator-totp":
|
||||
await import("@goauthentik/flow/stages/authenticator_totp/AuthenticatorTOTPStage");
|
||||
return html`<ak-stage-authenticator-totp
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-authenticator-totp>`;
|
||||
case "ak-stage-authenticator-duo":
|
||||
await import("@goauthentik/flow/stages/authenticator_duo/AuthenticatorDuoStage");
|
||||
return html`<ak-stage-authenticator-duo
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-authenticator-duo>`;
|
||||
case "ak-stage-authenticator-static":
|
||||
await import(
|
||||
"@goauthentik/flow/stages/authenticator_static/AuthenticatorStaticStage"
|
||||
);
|
||||
return html`<ak-stage-authenticator-static
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-authenticator-static>`;
|
||||
case "ak-stage-authenticator-webauthn":
|
||||
return html`<ak-stage-authenticator-webauthn
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-authenticator-webauthn>`;
|
||||
case "ak-stage-authenticator-sms":
|
||||
await import("@goauthentik/flow/stages/authenticator_sms/AuthenticatorSMSStage");
|
||||
return html`<ak-stage-authenticator-sms
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-authenticator-sms>`;
|
||||
case "ak-stage-authenticator-validate":
|
||||
await import(
|
||||
"@goauthentik/flow/stages/authenticator_validate/AuthenticatorValidateStage"
|
||||
);
|
||||
return html`<ak-stage-authenticator-validate
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-authenticator-validate>`;
|
||||
case "ak-stage-user-login":
|
||||
await import("@goauthentik/flow/stages/user_login/UserLoginStage");
|
||||
return html`<ak-stage-user-login
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-user-login>`;
|
||||
// Sources
|
||||
case "ak-source-plex":
|
||||
return html`<ak-flow-source-plex
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-flow-source-plex>`;
|
||||
case "ak-source-oauth-apple":
|
||||
return html`<ak-flow-source-oauth-apple
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-flow-source-oauth-apple>`;
|
||||
// Providers
|
||||
case "ak-provider-oauth2-device-code":
|
||||
await import("@goauthentik/flow/providers/oauth2/DeviceCode");
|
||||
return html`<ak-flow-provider-oauth2-code
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-flow-provider-oauth2-code>`;
|
||||
case "ak-provider-oauth2-device-code-finish":
|
||||
await import("@goauthentik/flow/providers/oauth2/DeviceCodeFinish");
|
||||
return html`<ak-flow-provider-oauth2-code-finish
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-flow-provider-oauth2-code-finish>`;
|
||||
case "ak-stage-session-end":
|
||||
await import("@goauthentik/flow/providers/SessionEnd");
|
||||
return html`<ak-stage-session-end
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-session-end>`;
|
||||
// Internal stages
|
||||
case "ak-stage-flow-error":
|
||||
return html`<ak-stage-flow-error
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-flow-error>`;
|
||||
case "xak-flow-redirect":
|
||||
return html`<ak-stage-redirect
|
||||
.host=${this as StageHost}
|
||||
@ -429,9 +504,11 @@ export class FlowExecutor extends Interface implements StageHost {
|
||||
>
|
||||
<img
|
||||
src="${themeImage(
|
||||
this.brand?.brandingLogo ??
|
||||
globalAK()?.brand.brandingLogo ??
|
||||
first(
|
||||
this.brand?.brandingLogo,
|
||||
globalAK()?.brand.brandingLogo,
|
||||
DefaultBrand.brandingLogo,
|
||||
),
|
||||
)}"
|
||||
alt="authentik Logo"
|
||||
/>
|
||||
@ -439,9 +516,25 @@ export class FlowExecutor extends Interface implements StageHost {
|
||||
${until(this.renderChallenge())}
|
||||
</div>
|
||||
<footer class="pf-c-login__footer">
|
||||
<ak-brand-links
|
||||
.links=${this.brand?.uiFooterLinks ?? []}
|
||||
></ak-brand-links>
|
||||
<ul class="pf-c-list pf-m-inline">
|
||||
${this.brand?.uiFooterLinks?.map((link) => {
|
||||
if (link.href) {
|
||||
return html`${purify(
|
||||
html`<li>
|
||||
<a href="${link.href}"
|
||||
>${link.name}</a
|
||||
>
|
||||
</li>`,
|
||||
)}`;
|
||||
}
|
||||
return html`<li>
|
||||
<span>${link.name}</span>
|
||||
</li>`;
|
||||
})}
|
||||
<li>
|
||||
<span>${msg("Powered by authentik")}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,51 +0,0 @@
|
||||
import { purify } from "@goauthentik/common/purify";
|
||||
import { AKElement } from "@goauthentik/elements/Base.js";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { map } from "lit/directives/map.js";
|
||||
|
||||
import PFList from "@patternfly/patternfly/components/List/list.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
import { FooterLink } from "@goauthentik/api";
|
||||
|
||||
const styles = css`
|
||||
.pf-c-list a {
|
||||
color: unset;
|
||||
}
|
||||
ul.pf-c-list.pf-m-inline {
|
||||
justify-content: center;
|
||||
padding: calc(var(--pf-global--spacer--xs) / 2) 0px;
|
||||
}
|
||||
`;
|
||||
|
||||
const salesMark: FooterLink = { name: msg("Powered by authentik"), href: "" };
|
||||
|
||||
@customElement("ak-brand-links")
|
||||
export class BrandLinks extends AKElement {
|
||||
static get styles() {
|
||||
return [PFBase, PFList, styles];
|
||||
}
|
||||
|
||||
@property({ type: Array, attribute: false })
|
||||
links: FooterLink[] = [];
|
||||
|
||||
render() {
|
||||
const links = [...(this.links ?? []), salesMark];
|
||||
return html` <ul class="pf-c-list pf-m-inline">
|
||||
${map(links, (link) =>
|
||||
link.href
|
||||
? purify(html`<li><a href="${link.href}">${link.name}</a></li>`)
|
||||
: html`<li><span>${link.name}</span></li>`,
|
||||
)}
|
||||
</ul>`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ak-brand-links": BrandLinks;
|
||||
}
|
||||
}
|
@ -1,14 +1,18 @@
|
||||
///<reference types="@hcaptcha/types"/>
|
||||
import { renderStatic } from "@goauthentik/common/purify";
|
||||
import "@goauthentik/elements/EmptyState";
|
||||
import { akEmptyState } from "@goauthentik/elements/EmptyState";
|
||||
import { bound } from "@goauthentik/elements/decorators/bound";
|
||||
import "@goauthentik/elements/forms/FormElement";
|
||||
import { ListenerController } from "@goauthentik/elements/utils/listenerController.js";
|
||||
import { randomId } from "@goauthentik/elements/utils/randomId";
|
||||
import "@goauthentik/flow/FormStatic";
|
||||
import { BaseStage } from "@goauthentik/flow/stages/base";
|
||||
import { P, match } from "ts-pattern";
|
||||
import type { TurnstileObject } from "turnstile-types";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, PropertyValues, TemplateResult, css, html } from "lit";
|
||||
import { CSSResult, PropertyValues, TemplateResult, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
@ -23,8 +27,72 @@ import { CaptchaChallenge, CaptchaChallengeResponseRequest } from "@goauthentik/
|
||||
interface TurnstileWindow extends Window {
|
||||
turnstile: TurnstileObject;
|
||||
}
|
||||
|
||||
type TokenHandler = (token: string) => void;
|
||||
|
||||
type Dims = { height: number };
|
||||
|
||||
type IframeCaptchaMessage = {
|
||||
source?: string;
|
||||
context?: string;
|
||||
message: "captcha";
|
||||
token: string;
|
||||
};
|
||||
|
||||
type IframeResizeMessage = {
|
||||
source?: string;
|
||||
context?: string;
|
||||
message: "resize";
|
||||
size: Dims;
|
||||
};
|
||||
|
||||
type IframeMessageEvent = MessageEvent<IframeCaptchaMessage | IframeResizeMessage>;
|
||||
|
||||
type CaptchaHandler = {
|
||||
name: string;
|
||||
interactive: () => Promise<unknown>;
|
||||
execute: () => Promise<unknown>;
|
||||
};
|
||||
|
||||
// A container iframe for a hosted Captcha, with an event emitter to monitor when the Captcha forces
|
||||
// a resize. Because the Captcha is itself in an iframe, the reported height is often off by some
|
||||
// margin, so adding 2rem of height to our container adds padding and prevents scroll bars or hidden
|
||||
// rendering.
|
||||
|
||||
const iframeTemplate = (captchaElement: TemplateResult, challengeUrl: string) =>
|
||||
html`<!doctype html>
|
||||
<head>
|
||||
<html>
|
||||
<body style="display:flex;flex-direction:row;justify-content:center;">
|
||||
${captchaElement}
|
||||
<script>
|
||||
new ResizeObserver((entries) => {
|
||||
const height =
|
||||
document.body.offsetHeight +
|
||||
parseFloat(getComputedStyle(document.body).fontSize) * 2;
|
||||
window.parent.postMessage({
|
||||
message: "resize",
|
||||
source: "goauthentik.io",
|
||||
context: "flow-executor",
|
||||
size: { height },
|
||||
});
|
||||
}).observe(document.querySelector(".ak-captcha-container"));
|
||||
</script>
|
||||
<script src=${challengeUrl}></script>
|
||||
<script>
|
||||
function callback(token) {
|
||||
window.parent.postMessage({
|
||||
message: "captcha",
|
||||
source: "goauthentik.io",
|
||||
context: "flow-executor",
|
||||
token: token,
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</head>`;
|
||||
|
||||
@customElement("ak-stage-captcha")
|
||||
export class CaptchaStage extends BaseStage<CaptchaChallenge, CaptchaChallengeResponseRequest> {
|
||||
static get styles(): CSSResult[] {
|
||||
@ -37,26 +105,12 @@ export class CaptchaStage extends BaseStage<CaptchaChallenge, CaptchaChallengeRe
|
||||
css`
|
||||
iframe {
|
||||
width: 100%;
|
||||
height: 73px; /* tmp */
|
||||
height: 0;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
handlers = [this.handleGReCaptcha, this.handleHCaptcha, this.handleTurnstile];
|
||||
|
||||
@state()
|
||||
error?: string;
|
||||
|
||||
@state()
|
||||
captchaFrame: HTMLIFrameElement;
|
||||
|
||||
@state()
|
||||
captchaDocumentContainer: HTMLDivElement;
|
||||
|
||||
@state()
|
||||
scriptElement?: HTMLScriptElement;
|
||||
|
||||
@property({ type: Boolean })
|
||||
embedded = false;
|
||||
|
||||
@ -65,209 +119,177 @@ export class CaptchaStage extends BaseStage<CaptchaChallenge, CaptchaChallengeRe
|
||||
this.host.submit({ component: "ak-stage-captcha", token });
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.captchaFrame = document.createElement("iframe");
|
||||
this.captchaFrame.src = "about:blank";
|
||||
this.captchaFrame.id = `ak-captcha-${randomId()}`;
|
||||
@state()
|
||||
error?: string;
|
||||
|
||||
this.captchaDocumentContainer = document.createElement("div");
|
||||
this.captchaDocumentContainer.id = `ak-captcha-${randomId()}`;
|
||||
this.messageCallback = this.messageCallback.bind(this);
|
||||
}
|
||||
handlers: CaptchaHandler[] = [
|
||||
{
|
||||
name: "grecaptcha",
|
||||
interactive: this.renderGReCaptchaFrame,
|
||||
execute: this.executeGReCaptcha,
|
||||
},
|
||||
{
|
||||
name: "hcaptcha",
|
||||
interactive: this.renderHCaptchaFrame,
|
||||
execute: this.executeHCaptcha,
|
||||
},
|
||||
{
|
||||
name: "turnstile",
|
||||
interactive: this.renderTurnstileFrame,
|
||||
execute: this.executeTurnstile,
|
||||
},
|
||||
];
|
||||
|
||||
_captchaFrame?: HTMLIFrameElement;
|
||||
_captchaDocumentContainer?: HTMLDivElement;
|
||||
_listenController = new ListenerController();
|
||||
|
||||
connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
window.addEventListener("message", this.messageCallback);
|
||||
window.addEventListener("message", this.onIframeMessage, {
|
||||
signal: this._listenController.signal,
|
||||
});
|
||||
}
|
||||
|
||||
disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
window.removeEventListener("message", this.messageCallback);
|
||||
if (!this.challenge.interactive) {
|
||||
document.body.removeChild(this.captchaDocumentContainer);
|
||||
this._listenController.abort();
|
||||
if (!this.challenge?.interactive) {
|
||||
if (document.body.contains(this.captchaDocumentContainer)) {
|
||||
document.body.removeChild(this.captchaDocumentContainer);
|
||||
}
|
||||
}
|
||||
super.disconnectedCallback();
|
||||
}
|
||||
|
||||
messageCallback(
|
||||
ev: MessageEvent<{
|
||||
source?: string;
|
||||
context?: string;
|
||||
message: string;
|
||||
token: string;
|
||||
}>,
|
||||
) {
|
||||
const msg = ev.data;
|
||||
if (msg.source !== "goauthentik.io" || msg.context !== "flow-executor") {
|
||||
return;
|
||||
get captchaDocumentContainer() {
|
||||
if (this._captchaDocumentContainer) {
|
||||
return this._captchaDocumentContainer;
|
||||
}
|
||||
if (msg.message !== "captcha") {
|
||||
return;
|
||||
this._captchaDocumentContainer = document.createElement("div");
|
||||
this._captchaDocumentContainer.id = `ak-captcha-${randomId()}`;
|
||||
return this._captchaDocumentContainer;
|
||||
}
|
||||
|
||||
get captchaFrame() {
|
||||
if (this._captchaFrame) {
|
||||
return this._captchaFrame;
|
||||
}
|
||||
this.onTokenChange(msg.token);
|
||||
this._captchaFrame = document.createElement("iframe");
|
||||
this._captchaFrame.src = "about:blank";
|
||||
this._captchaFrame.id = `ak-captcha-${randomId()}`;
|
||||
return this._captchaFrame;
|
||||
}
|
||||
|
||||
onFrameResize({ height }: Dims) {
|
||||
this.captchaFrame.style.height = `${height}px`;
|
||||
}
|
||||
|
||||
// ADR: Did not to put anything into `otherwise` or `exhaustive` here because iframe messages
|
||||
// that were not of interest to us also weren't necessarily corrupt or suspicious. For example,
|
||||
// during testing Storybook throws a lot of cross-iframe messages that we don't care about.
|
||||
|
||||
@bound
|
||||
onIframeMessage({ data }: IframeMessageEvent) {
|
||||
match(data)
|
||||
.with(
|
||||
{ source: "goauthentik.io", context: "flow-executor", message: "captcha" },
|
||||
({ token }) => this.onTokenChange(token),
|
||||
)
|
||||
.with(
|
||||
{ source: "goauthentik.io", context: "flow-executor", message: "resize" },
|
||||
({ size }) => this.onFrameResize(size),
|
||||
)
|
||||
.with(
|
||||
{ source: "goauthentik.io", context: "flow-executor", message: P.any },
|
||||
({ message }) => {
|
||||
console.debug(`authentik/stages/captcha: Unknown message: ${message}`);
|
||||
},
|
||||
)
|
||||
.otherwise(() => {});
|
||||
}
|
||||
|
||||
async renderGReCaptchaFrame() {
|
||||
this.renderFrame(
|
||||
html`<div
|
||||
class="g-recaptcha ak-captcha-container"
|
||||
data-sitekey="${this.challenge.siteKey}"
|
||||
data-callback="callback"
|
||||
></div>`,
|
||||
);
|
||||
}
|
||||
|
||||
async executeGReCaptcha() {
|
||||
return grecaptcha.ready(() => {
|
||||
grecaptcha.execute(
|
||||
grecaptcha.render(this.captchaDocumentContainer, {
|
||||
sitekey: this.challenge.siteKey,
|
||||
callback: this.onTokenChange,
|
||||
size: "invisible",
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
async renderHCaptchaFrame() {
|
||||
this.renderFrame(
|
||||
html`<div
|
||||
class="h-captcha ak-captcha-container"
|
||||
data-sitekey="${this.challenge.siteKey}"
|
||||
data-theme="${this.activeTheme ? this.activeTheme : "light"}"
|
||||
data-callback="callback"
|
||||
></div> `,
|
||||
);
|
||||
}
|
||||
|
||||
async executeHCaptcha() {
|
||||
return hcaptcha.execute(
|
||||
hcaptcha.render(this.captchaDocumentContainer, {
|
||||
sitekey: this.challenge.siteKey,
|
||||
callback: this.onTokenChange,
|
||||
size: "invisible",
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async renderTurnstileFrame() {
|
||||
this.renderFrame(
|
||||
html`<div
|
||||
class="cf-turnstile ak-captcha-container"
|
||||
data-sitekey="${this.challenge.siteKey}"
|
||||
data-callback="callback"
|
||||
></div>`,
|
||||
);
|
||||
}
|
||||
|
||||
async executeTurnstile() {
|
||||
return (window as unknown as TurnstileWindow).turnstile.render(
|
||||
this.captchaDocumentContainer,
|
||||
{
|
||||
sitekey: this.challenge.siteKey,
|
||||
callback: this.onTokenChange,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async renderFrame(captchaElement: TemplateResult) {
|
||||
this.captchaFrame.contentWindow?.document.open();
|
||||
this.captchaFrame.contentWindow?.document.write(
|
||||
await renderStatic(
|
||||
html`<!doctype html>
|
||||
<html>
|
||||
<body style="display:flex;flex-direction:row;justify-content:center;">
|
||||
${captchaElement}
|
||||
<script src=${this.challenge.jsUrl}></script>
|
||||
<script>
|
||||
function callback(token) {
|
||||
window.parent.postMessage({
|
||||
message: "captcha",
|
||||
source: "goauthentik.io",
|
||||
context: "flow-executor",
|
||||
token: token,
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>`,
|
||||
),
|
||||
await renderStatic(iframeTemplate(captchaElement, this.challenge.jsUrl)),
|
||||
);
|
||||
this.captchaFrame.contentWindow?.document.close();
|
||||
}
|
||||
|
||||
updated(changedProperties: PropertyValues<this>) {
|
||||
if (changedProperties.has("challenge") && this.challenge !== undefined) {
|
||||
this.scriptElement = document.createElement("script");
|
||||
this.scriptElement.src = this.challenge.jsUrl;
|
||||
this.scriptElement.async = true;
|
||||
this.scriptElement.defer = true;
|
||||
this.scriptElement.dataset.akCaptchaScript = "true";
|
||||
this.scriptElement.onload = async () => {
|
||||
console.debug("authentik/stages/captcha: script loaded");
|
||||
let found = false;
|
||||
let lastError = undefined;
|
||||
this.handlers.forEach(async (handler) => {
|
||||
let handlerFound = false;
|
||||
try {
|
||||
console.debug(`authentik/stages/captcha[${handler.name}]: trying handler`);
|
||||
handlerFound = await handler.apply(this);
|
||||
if (handlerFound) {
|
||||
console.debug(
|
||||
`authentik/stages/captcha[${handler.name}]: handler succeeded`,
|
||||
);
|
||||
found = true;
|
||||
}
|
||||
} catch (exc) {
|
||||
console.debug(
|
||||
`authentik/stages/captcha[${handler.name}]: handler failed: ${exc}`,
|
||||
);
|
||||
if (handlerFound) {
|
||||
lastError = exc;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!found && lastError) {
|
||||
this.error = (lastError as Error).toString();
|
||||
}
|
||||
};
|
||||
document.head
|
||||
.querySelectorAll("[data-ak-captcha-script=true]")
|
||||
.forEach((el) => el.remove());
|
||||
document.head.appendChild(this.scriptElement);
|
||||
if (!this.challenge.interactive) {
|
||||
document.body.appendChild(this.captchaDocumentContainer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async handleGReCaptcha(): Promise<boolean> {
|
||||
if (!Object.hasOwn(window, "grecaptcha")) {
|
||||
return false;
|
||||
}
|
||||
if (this.challenge.interactive) {
|
||||
this.renderFrame(
|
||||
html`<div
|
||||
class="g-recaptcha"
|
||||
data-sitekey="${this.challenge.siteKey}"
|
||||
data-callback="callback"
|
||||
></div>`,
|
||||
);
|
||||
} else {
|
||||
grecaptcha.ready(() => {
|
||||
const captchaId = grecaptcha.render(this.captchaDocumentContainer, {
|
||||
sitekey: this.challenge.siteKey,
|
||||
callback: this.onTokenChange,
|
||||
size: "invisible",
|
||||
});
|
||||
grecaptcha.execute(captchaId);
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async handleHCaptcha(): Promise<boolean> {
|
||||
if (!Object.hasOwn(window, "hcaptcha")) {
|
||||
return false;
|
||||
}
|
||||
if (this.challenge.interactive) {
|
||||
this.renderFrame(
|
||||
html`<div
|
||||
class="h-captcha"
|
||||
data-sitekey="${this.challenge.siteKey}"
|
||||
data-theme="${this.activeTheme ? this.activeTheme : "light"}"
|
||||
data-callback="callback"
|
||||
></div> `,
|
||||
);
|
||||
} else {
|
||||
const captchaId = hcaptcha.render(this.captchaDocumentContainer, {
|
||||
sitekey: this.challenge.siteKey,
|
||||
callback: this.onTokenChange,
|
||||
size: "invisible",
|
||||
});
|
||||
hcaptcha.execute(captchaId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async handleTurnstile(): Promise<boolean> {
|
||||
if (!Object.hasOwn(window, "turnstile")) {
|
||||
return false;
|
||||
}
|
||||
if (this.challenge.interactive) {
|
||||
this.renderFrame(
|
||||
html`<div
|
||||
class="cf-turnstile"
|
||||
data-sitekey="${this.challenge.siteKey}"
|
||||
data-callback="callback"
|
||||
></div>`,
|
||||
);
|
||||
} else {
|
||||
(window as unknown as TurnstileWindow).turnstile.render(this.captchaDocumentContainer, {
|
||||
sitekey: this.challenge.siteKey,
|
||||
callback: this.onTokenChange,
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
renderBody() {
|
||||
if (this.error) {
|
||||
return html`<ak-empty-state icon="fa-times" header=${this.error}> </ak-empty-state>`;
|
||||
}
|
||||
if (this.challenge.interactive) {
|
||||
return html`${this.captchaFrame}`;
|
||||
}
|
||||
return html`<ak-empty-state loading header=${msg("Verifying...")}></ak-empty-state>`;
|
||||
// [hasError, isInteractive]
|
||||
// prettier-ignore
|
||||
return match([Boolean(this.error), Boolean(this.challenge?.interactive)])
|
||||
.with([true, P.any], () => akEmptyState({ icon: "fa-times", header: this.error }))
|
||||
.with([false, true], () => html`${this.captchaFrame}`)
|
||||
.with([false, false], () => akEmptyState({ loading: true, header: msg("Verifying...") }))
|
||||
.exhaustive();
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.embedded) {
|
||||
if (!this.challenge.interactive) {
|
||||
return html``;
|
||||
}
|
||||
return this.renderBody();
|
||||
}
|
||||
if (!this.challenge) {
|
||||
return html`<ak-empty-state loading> </ak-empty-state>`;
|
||||
}
|
||||
renderMain() {
|
||||
return html`<header class="pf-c-login__main-header">
|
||||
<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo?.title}</h1>
|
||||
</header>
|
||||
@ -291,6 +313,63 @@ export class CaptchaStage extends BaseStage<CaptchaChallenge, CaptchaChallengeRe
|
||||
<ul class="pf-c-login__main-footer-links"></ul>
|
||||
</footer>`;
|
||||
}
|
||||
|
||||
render() {
|
||||
// [isEmbedded, hasChallenge, isInteractive]
|
||||
// prettier-ignore
|
||||
return match([this.embedded, Boolean(this.challenge), Boolean(this.challenge?.interactive)])
|
||||
.with([true, false, P.any], () => nothing)
|
||||
.with([true, true, false], () => nothing)
|
||||
.with([true, true, true], () => this.renderBody())
|
||||
.with([false, false, P.any], () => akEmptyState({ loading: true }))
|
||||
.with([false, true, P.any], () => this.renderMain())
|
||||
.exhaustive();
|
||||
}
|
||||
|
||||
updated(changedProperties: PropertyValues<this>) {
|
||||
if (!(changedProperties.has("challenge") && this.challenge !== undefined)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const attachCaptcha = async () => {
|
||||
console.debug("authentik/stages/captcha: script loaded");
|
||||
const handlers = this.handlers.filter(({ name }) => Object.hasOwn(window, name));
|
||||
let lastError = undefined;
|
||||
let found = false;
|
||||
for (const { name, interactive, execute } of handlers) {
|
||||
console.debug(`authentik/stages/captcha: trying handler ${name}`);
|
||||
try {
|
||||
const runner = this.challenge.interactive ? interactive : execute;
|
||||
await runner.apply(this);
|
||||
console.debug(`authentik/stages/captcha[${name}]: handler succeeded`);
|
||||
found = true;
|
||||
break;
|
||||
} catch (exc) {
|
||||
console.debug(`authentik/stages/captcha[${name}]: handler failed`);
|
||||
console.debug(exc);
|
||||
lastError = exc;
|
||||
}
|
||||
}
|
||||
this.error = found ? undefined : (lastError ?? "Unspecified error").toString();
|
||||
};
|
||||
|
||||
const scriptElement = document.createElement("script");
|
||||
scriptElement.src = this.challenge.jsUrl;
|
||||
scriptElement.async = true;
|
||||
scriptElement.defer = true;
|
||||
scriptElement.dataset.akCaptchaScript = "true";
|
||||
scriptElement.onload = attachCaptcha;
|
||||
|
||||
document.head
|
||||
.querySelectorAll("[data-ak-captcha-script=true]")
|
||||
.forEach((el) => el.remove());
|
||||
|
||||
document.head.appendChild(scriptElement);
|
||||
|
||||
if (!this.challenge.interactive) {
|
||||
document.body.appendChild(this.captchaDocumentContainer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@ -15,7 +15,6 @@ export const targetLocales = [
|
||||
`en`,
|
||||
`es`,
|
||||
`fr`,
|
||||
`it`,
|
||||
`ko`,
|
||||
`nl`,
|
||||
`pl`,
|
||||
@ -37,7 +36,6 @@ export const allLocales = [
|
||||
`en`,
|
||||
`es`,
|
||||
`fr`,
|
||||
`it`,
|
||||
`ko`,
|
||||
`nl`,
|
||||
`pl`,
|
||||
|
@ -5741,6 +5741,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s070fdfb03034ca9b">
|
||||
<source>One hint, 'New Application Wizard', is currently hidden</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s61bd841e66966325">
|
||||
<source>External applications that use authentik as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1cc306d8e28c4464">
|
||||
<source>Deny message</source>
|
||||
</trans-unit>
|
||||
@ -5816,6 +5819,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="sfc31264ef7ff86ef">
|
||||
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc615309d10a9228c">
|
||||
<source>RBAC is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s32babfed740fd3c1">
|
||||
<source>User type used for newly created users.</source>
|
||||
</trans-unit>
|
||||
@ -5960,6 +5966,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s38e7cd1a24e70faa">
|
||||
<source>Create Endpoint</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4770c10e5b1c028c">
|
||||
<source>RAC is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s168565f5ac74a89f">
|
||||
<source>Update RAC Provider</source>
|
||||
</trans-unit>
|
||||
@ -6214,6 +6223,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="sd176021da2ea0fe3">
|
||||
<source>Selected Applications</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s862505f29064fc72">
|
||||
<source>This option configures the footer links on the flow executor pages. It must be a valid YAML or JSON list and can be used as follows:</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6d3f81dc4bcacbda">
|
||||
<source>Last used</source>
|
||||
</trans-unit>
|
||||
@ -7008,51 +7020,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2e1d5a7d320c25ef">
|
||||
<source>Enter the code from your authenticator device.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="scc1a17d28912e974">
|
||||
<source>Kerberos Source is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s54154a8d64a3597b">
|
||||
<source>Captcha stage</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0c250af62ddbf801">
|
||||
<source>When set, adds functionality exactly like a Captcha stage, but baked into the Identification stage.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sabf8a430d504f8c8">
|
||||
<source>Endpoint Google Chrome Device Trust is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6f4f35a5a4b9b3cb">
|
||||
<source>Interactive</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd020240e41e4c207">
|
||||
<source>Enable this flag if the configured captcha requires User-interaction. Required for reCAPTCHA v2, hCaptcha and Cloudflare Turnstile.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="saa29a2ac03cd9d19">
|
||||
<source>Reason</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se9fd92a824994eba">
|
||||
<source>Reason for impersonating the user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s29fab8887734192f">
|
||||
<source>Require reason for impersonation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc24af6de78468cfa">
|
||||
<source>Require administrators to provide a reason for impersonating a user.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd48cc52117f4d68b">
|
||||
<source>Italian</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6cee92a3b310e650">
|
||||
<source>Add entry</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8f36fb59c31d33eb">
|
||||
<source>Link Title</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s92205c10ba1f0f4c">
|
||||
<source>This option configures the footer links on the flow executor pages. The URL is limited to web and mail addresses. If the name is left blank, the URL will be shown.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s66f572bec2bde9c4">
|
||||
<source>External applications that use <x id="0" equiv-text="${this.brand.brandingTitle || "authentik"}"/> as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -6006,6 +6006,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s070fdfb03034ca9b">
|
||||
<source>One hint, 'New Application Wizard', is currently hidden</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s61bd841e66966325">
|
||||
<source>External applications that use authentik as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1cc306d8e28c4464">
|
||||
<source>Deny message</source>
|
||||
</trans-unit>
|
||||
@ -6081,6 +6084,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="sfc31264ef7ff86ef">
|
||||
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc615309d10a9228c">
|
||||
<source>RBAC is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s32babfed740fd3c1">
|
||||
<source>User type used for newly created users.</source>
|
||||
</trans-unit>
|
||||
@ -6225,6 +6231,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s38e7cd1a24e70faa">
|
||||
<source>Create Endpoint</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4770c10e5b1c028c">
|
||||
<source>RAC is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s168565f5ac74a89f">
|
||||
<source>Update RAC Provider</source>
|
||||
</trans-unit>
|
||||
@ -6479,6 +6488,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="sd176021da2ea0fe3">
|
||||
<source>Selected Applications</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s862505f29064fc72">
|
||||
<source>This option configures the footer links on the flow executor pages. It must be a valid YAML or JSON list and can be used as follows:</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6d3f81dc4bcacbda">
|
||||
<source>Last used</source>
|
||||
</trans-unit>
|
||||
@ -7273,51 +7285,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2e1d5a7d320c25ef">
|
||||
<source>Enter the code from your authenticator device.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="scc1a17d28912e974">
|
||||
<source>Kerberos Source is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s54154a8d64a3597b">
|
||||
<source>Captcha stage</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0c250af62ddbf801">
|
||||
<source>When set, adds functionality exactly like a Captcha stage, but baked into the Identification stage.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sabf8a430d504f8c8">
|
||||
<source>Endpoint Google Chrome Device Trust is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6f4f35a5a4b9b3cb">
|
||||
<source>Interactive</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd020240e41e4c207">
|
||||
<source>Enable this flag if the configured captcha requires User-interaction. Required for reCAPTCHA v2, hCaptcha and Cloudflare Turnstile.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="saa29a2ac03cd9d19">
|
||||
<source>Reason</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se9fd92a824994eba">
|
||||
<source>Reason for impersonating the user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s29fab8887734192f">
|
||||
<source>Require reason for impersonation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc24af6de78468cfa">
|
||||
<source>Require administrators to provide a reason for impersonating a user.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd48cc52117f4d68b">
|
||||
<source>Italian</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6cee92a3b310e650">
|
||||
<source>Add entry</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8f36fb59c31d33eb">
|
||||
<source>Link Title</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s92205c10ba1f0f4c">
|
||||
<source>This option configures the footer links on the flow executor pages. The URL is limited to web and mail addresses. If the name is left blank, the URL will be shown.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s66f572bec2bde9c4">
|
||||
<source>External applications that use <x id="0" equiv-text="${this.brand.brandingTitle || "authentik"}"/> as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -5658,6 +5658,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s070fdfb03034ca9b">
|
||||
<source>One hint, 'New Application Wizard', is currently hidden</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s61bd841e66966325">
|
||||
<source>External applications that use authentik as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1cc306d8e28c4464">
|
||||
<source>Deny message</source>
|
||||
</trans-unit>
|
||||
@ -5733,6 +5736,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="sfc31264ef7ff86ef">
|
||||
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc615309d10a9228c">
|
||||
<source>RBAC is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s32babfed740fd3c1">
|
||||
<source>User type used for newly created users.</source>
|
||||
</trans-unit>
|
||||
@ -5877,6 +5883,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s38e7cd1a24e70faa">
|
||||
<source>Create Endpoint</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4770c10e5b1c028c">
|
||||
<source>RAC is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s168565f5ac74a89f">
|
||||
<source>Update RAC Provider</source>
|
||||
</trans-unit>
|
||||
@ -6131,6 +6140,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="sd176021da2ea0fe3">
|
||||
<source>Selected Applications</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s862505f29064fc72">
|
||||
<source>This option configures the footer links on the flow executor pages. It must be a valid YAML or JSON list and can be used as follows:</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6d3f81dc4bcacbda">
|
||||
<source>Last used</source>
|
||||
</trans-unit>
|
||||
@ -6925,51 +6937,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2e1d5a7d320c25ef">
|
||||
<source>Enter the code from your authenticator device.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="scc1a17d28912e974">
|
||||
<source>Kerberos Source is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s54154a8d64a3597b">
|
||||
<source>Captcha stage</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0c250af62ddbf801">
|
||||
<source>When set, adds functionality exactly like a Captcha stage, but baked into the Identification stage.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sabf8a430d504f8c8">
|
||||
<source>Endpoint Google Chrome Device Trust is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6f4f35a5a4b9b3cb">
|
||||
<source>Interactive</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd020240e41e4c207">
|
||||
<source>Enable this flag if the configured captcha requires User-interaction. Required for reCAPTCHA v2, hCaptcha and Cloudflare Turnstile.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="saa29a2ac03cd9d19">
|
||||
<source>Reason</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se9fd92a824994eba">
|
||||
<source>Reason for impersonating the user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s29fab8887734192f">
|
||||
<source>Require reason for impersonation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc24af6de78468cfa">
|
||||
<source>Require administrators to provide a reason for impersonating a user.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd48cc52117f4d68b">
|
||||
<source>Italian</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6cee92a3b310e650">
|
||||
<source>Add entry</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8f36fb59c31d33eb">
|
||||
<source>Link Title</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s92205c10ba1f0f4c">
|
||||
<source>This option configures the footer links on the flow executor pages. The URL is limited to web and mail addresses. If the name is left blank, the URL will be shown.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s66f572bec2bde9c4">
|
||||
<source>External applications that use <x id="0" equiv-text="${this.brand.brandingTitle || "authentik"}"/> as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -7542,6 +7542,10 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
|
||||
<source>One hint, 'New Application Wizard', is currently hidden</source>
|
||||
<target>Un indice, l'assistant nouvelle application est actuellement caché</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s61bd841e66966325">
|
||||
<source>External applications that use authentik as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
|
||||
<target>Applications externes qui utilisent authentik comme fournisseur d'identité, en utilisant des protocoles comme OAuth2 et SAML. Toutes les applications sont affichées ici, même celles auxquelles vous n'avez pas accès.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1cc306d8e28c4464">
|
||||
<source>Deny message</source>
|
||||
<target>Message de refus</target>
|
||||
@ -7642,6 +7646,10 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
|
||||
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
||||
<target>Pour nginx auth_request ou traefik forwardAuth par domaine racine</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc615309d10a9228c">
|
||||
<source>RBAC is in preview.</source>
|
||||
<target>RBAC est en aperçu.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s32babfed740fd3c1">
|
||||
<source>User type used for newly created users.</source>
|
||||
<target>Type d'utilisateur pour les utilisateurs nouvellement créés.</target>
|
||||
@ -7834,6 +7842,10 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
|
||||
<source>Create Endpoint</source>
|
||||
<target>Créer un point de terminaison</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4770c10e5b1c028c">
|
||||
<source>RAC is in preview.</source>
|
||||
<target>RAC est en aperçu.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s168565f5ac74a89f">
|
||||
<source>Update RAC Provider</source>
|
||||
<target>Mettre à jour le fournisseur RAC</target>
|
||||
@ -8176,6 +8188,10 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
|
||||
<source>Selected Applications</source>
|
||||
<target>Applications sélectionnées</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s862505f29064fc72">
|
||||
<source>This option configures the footer links on the flow executor pages. It must be a valid YAML or JSON list and can be used as follows:</source>
|
||||
<target>Cette option configure les liens de pied de page sur les pages de l'exécuteur de flux. Doit être une liste YAML ou JSON valide et peut être utilisée comme suit :</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6d3f81dc4bcacbda">
|
||||
<source>Last used</source>
|
||||
<target>Utilisé pour la dernière fois</target>
|
||||
@ -9232,51 +9248,6 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
|
||||
</trans-unit>
|
||||
<trans-unit id="s2e1d5a7d320c25ef">
|
||||
<source>Enter the code from your authenticator device.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="scc1a17d28912e974">
|
||||
<source>Kerberos Source is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s54154a8d64a3597b">
|
||||
<source>Captcha stage</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0c250af62ddbf801">
|
||||
<source>When set, adds functionality exactly like a Captcha stage, but baked into the Identification stage.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sabf8a430d504f8c8">
|
||||
<source>Endpoint Google Chrome Device Trust is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6f4f35a5a4b9b3cb">
|
||||
<source>Interactive</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd020240e41e4c207">
|
||||
<source>Enable this flag if the configured captcha requires User-interaction. Required for reCAPTCHA v2, hCaptcha and Cloudflare Turnstile.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="saa29a2ac03cd9d19">
|
||||
<source>Reason</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se9fd92a824994eba">
|
||||
<source>Reason for impersonating the user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s29fab8887734192f">
|
||||
<source>Require reason for impersonation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc24af6de78468cfa">
|
||||
<source>Require administrators to provide a reason for impersonating a user.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd48cc52117f4d68b">
|
||||
<source>Italian</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6cee92a3b310e650">
|
||||
<source>Add entry</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8f36fb59c31d33eb">
|
||||
<source>Link Title</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s92205c10ba1f0f4c">
|
||||
<source>This option configures the footer links on the flow executor pages. The URL is limited to web and mail addresses. If the name is left blank, the URL will be shown.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s66f572bec2bde9c4">
|
||||
<source>External applications that use <x id="0" equiv-text="${this.brand.brandingTitle || "authentik"}"/> as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
9256
web/xliff/it.xlf
9256
web/xliff/it.xlf
File diff suppressed because it is too large
Load Diff
@ -7512,6 +7512,10 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>One hint, 'New Application Wizard', is currently hidden</source>
|
||||
<target>힌트, '새 애플리케이션 마법사'는 현재, 숨겨져 있습니다.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s61bd841e66966325">
|
||||
<source>External applications that use authentik as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
|
||||
<target>OAuth2 및 SAML과 같은 프로토콜을 통해 인증서를 ID 공급자로 사용하는 외부 애플리케이션. 액세스할 수 없는 애플리케이션을 포함한 모든 애플리케이션이 여기에 표시됩니다.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1cc306d8e28c4464">
|
||||
<source>Deny message</source>
|
||||
<target>거부 메시지</target>
|
||||
@ -7612,6 +7616,10 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
||||
<target>루트 도메인 당 Nginx의 auth_request 또는 Traefik의 forwardAuth 경우</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc615309d10a9228c">
|
||||
<source>RBAC is in preview.</source>
|
||||
<target>RBAC 는 현재 프리뷰입니다.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s32babfed740fd3c1">
|
||||
<source>User type used for newly created users.</source>
|
||||
<target>새로 생성된 사용자에 사용되는 사용자 유형입니다.</target>
|
||||
@ -7806,6 +7814,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s38e7cd1a24e70faa">
|
||||
<source>Create Endpoint</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4770c10e5b1c028c">
|
||||
<source>RAC is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s168565f5ac74a89f">
|
||||
<source>Update RAC Provider</source>
|
||||
</trans-unit>
|
||||
@ -8045,6 +8056,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="sd176021da2ea0fe3">
|
||||
<source>Selected Applications</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s862505f29064fc72">
|
||||
<source>This option configures the footer links on the flow executor pages. It must be a valid YAML or JSON list and can be used as follows:</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6d3f81dc4bcacbda">
|
||||
<source>Last used</source>
|
||||
</trans-unit>
|
||||
@ -8839,51 +8853,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2e1d5a7d320c25ef">
|
||||
<source>Enter the code from your authenticator device.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="scc1a17d28912e974">
|
||||
<source>Kerberos Source is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s54154a8d64a3597b">
|
||||
<source>Captcha stage</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0c250af62ddbf801">
|
||||
<source>When set, adds functionality exactly like a Captcha stage, but baked into the Identification stage.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sabf8a430d504f8c8">
|
||||
<source>Endpoint Google Chrome Device Trust is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6f4f35a5a4b9b3cb">
|
||||
<source>Interactive</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd020240e41e4c207">
|
||||
<source>Enable this flag if the configured captcha requires User-interaction. Required for reCAPTCHA v2, hCaptcha and Cloudflare Turnstile.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="saa29a2ac03cd9d19">
|
||||
<source>Reason</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se9fd92a824994eba">
|
||||
<source>Reason for impersonating the user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s29fab8887734192f">
|
||||
<source>Require reason for impersonation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc24af6de78468cfa">
|
||||
<source>Require administrators to provide a reason for impersonating a user.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd48cc52117f4d68b">
|
||||
<source>Italian</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6cee92a3b310e650">
|
||||
<source>Add entry</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8f36fb59c31d33eb">
|
||||
<source>Link Title</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s92205c10ba1f0f4c">
|
||||
<source>This option configures the footer links on the flow executor pages. The URL is limited to web and mail addresses. If the name is left blank, the URL will be shown.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s66f572bec2bde9c4">
|
||||
<source>External applications that use <x id="0" equiv-text="${this.brand.brandingTitle || "authentik"}"/> as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -7396,6 +7396,9 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
|
||||
<trans-unit id="sf79f8681e5ffaee2">
|
||||
<source>Assign to new user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc615309d10a9228c">
|
||||
<source>RBAC is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="saabeb4cab074b0b9">
|
||||
<source>User Object Permissions</source>
|
||||
</trans-unit>
|
||||
@ -7483,6 +7486,9 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
|
||||
<trans-unit id="s38e7cd1a24e70faa">
|
||||
<source>Create Endpoint</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4770c10e5b1c028c">
|
||||
<source>RAC is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s168565f5ac74a89f">
|
||||
<source>Update RAC Provider</source>
|
||||
</trans-unit>
|
||||
@ -7495,6 +7501,9 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
|
||||
<trans-unit id="s2b1c81130a65a55b">
|
||||
<source>Sync currently running.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s61bd841e66966325">
|
||||
<source>External applications that use authentik as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb35c08e3a541188f">
|
||||
<source>Also known as Client ID.</source>
|
||||
</trans-unit>
|
||||
@ -7893,6 +7902,9 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
|
||||
<trans-unit id="sd176021da2ea0fe3">
|
||||
<source>Selected Applications</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s862505f29064fc72">
|
||||
<source>This option configures the footer links on the flow executor pages. It must be a valid YAML or JSON list and can be used as follows:</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6d3f81dc4bcacbda">
|
||||
<source>Last used</source>
|
||||
</trans-unit>
|
||||
@ -8687,51 +8699,6 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
|
||||
</trans-unit>
|
||||
<trans-unit id="s2e1d5a7d320c25ef">
|
||||
<source>Enter the code from your authenticator device.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="scc1a17d28912e974">
|
||||
<source>Kerberos Source is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s54154a8d64a3597b">
|
||||
<source>Captcha stage</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0c250af62ddbf801">
|
||||
<source>When set, adds functionality exactly like a Captcha stage, but baked into the Identification stage.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sabf8a430d504f8c8">
|
||||
<source>Endpoint Google Chrome Device Trust is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6f4f35a5a4b9b3cb">
|
||||
<source>Interactive</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd020240e41e4c207">
|
||||
<source>Enable this flag if the configured captcha requires User-interaction. Required for reCAPTCHA v2, hCaptcha and Cloudflare Turnstile.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="saa29a2ac03cd9d19">
|
||||
<source>Reason</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se9fd92a824994eba">
|
||||
<source>Reason for impersonating the user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s29fab8887734192f">
|
||||
<source>Require reason for impersonation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc24af6de78468cfa">
|
||||
<source>Require administrators to provide a reason for impersonating a user.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd48cc52117f4d68b">
|
||||
<source>Italian</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6cee92a3b310e650">
|
||||
<source>Add entry</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8f36fb59c31d33eb">
|
||||
<source>Link Title</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s92205c10ba1f0f4c">
|
||||
<source>This option configures the footer links on the flow executor pages. The URL is limited to web and mail addresses. If the name is left blank, the URL will be shown.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s66f572bec2bde9c4">
|
||||
<source>External applications that use <x id="0" equiv-text="${this.brand.brandingTitle || "authentik"}"/> as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -7546,6 +7546,10 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
|
||||
<source>One hint, 'New Application Wizard', is currently hidden</source>
|
||||
<target>Jedna podpowiedź, „Kreator nowej aplikacji”, jest obecnie ukryty</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s61bd841e66966325">
|
||||
<source>External applications that use authentik as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
|
||||
<target>Aplikacje zewnętrzne, które używają authentik jako dostawcy tożsamości za pośrednictwem protokołów takich jak OAuth2 i SAML. Tutaj wyświetlane są wszystkie aplikacje, nawet te, do których nie masz dostępu.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1cc306d8e28c4464">
|
||||
<source>Deny message</source>
|
||||
<target>Komunikat odmowy</target>
|
||||
@ -7646,6 +7650,10 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
|
||||
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
||||
<target>Dla nginx's auth_request lub traefik's forwardAuth dla domeny głównej</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc615309d10a9228c">
|
||||
<source>RBAC is in preview.</source>
|
||||
<target>RBAC jest w fazie zapoznawczej.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s32babfed740fd3c1">
|
||||
<source>User type used for newly created users.</source>
|
||||
<target>Typ użytkownika używany dla nowo utworzonych użytkowników.</target>
|
||||
@ -7838,6 +7846,10 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
|
||||
<source>Create Endpoint</source>
|
||||
<target>Utwórz punkt końcowy</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4770c10e5b1c028c">
|
||||
<source>RAC is in preview.</source>
|
||||
<target>RAC jest w fazie zapoznawczej.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s168565f5ac74a89f">
|
||||
<source>Update RAC Provider</source>
|
||||
<target>Aktualizuj dostawcę RAC</target>
|
||||
@ -8180,6 +8192,10 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
|
||||
<source>Selected Applications</source>
|
||||
<target>Wybrane aplikacje</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s862505f29064fc72">
|
||||
<source>This option configures the footer links on the flow executor pages. It must be a valid YAML or JSON list and can be used as follows:</source>
|
||||
<target>Ta opcja konfiguruje linki stopki na stronach wykonawców przepływu. Musi to być prawidłowa lista YAML lub JSON i może być używana w następujący sposób:</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6d3f81dc4bcacbda">
|
||||
<source>Last used</source>
|
||||
<target>Ostatnio używany</target>
|
||||
@ -9102,51 +9118,6 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
|
||||
</trans-unit>
|
||||
<trans-unit id="s2e1d5a7d320c25ef">
|
||||
<source>Enter the code from your authenticator device.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="scc1a17d28912e974">
|
||||
<source>Kerberos Source is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s54154a8d64a3597b">
|
||||
<source>Captcha stage</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0c250af62ddbf801">
|
||||
<source>When set, adds functionality exactly like a Captcha stage, but baked into the Identification stage.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sabf8a430d504f8c8">
|
||||
<source>Endpoint Google Chrome Device Trust is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6f4f35a5a4b9b3cb">
|
||||
<source>Interactive</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd020240e41e4c207">
|
||||
<source>Enable this flag if the configured captcha requires User-interaction. Required for reCAPTCHA v2, hCaptcha and Cloudflare Turnstile.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="saa29a2ac03cd9d19">
|
||||
<source>Reason</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se9fd92a824994eba">
|
||||
<source>Reason for impersonating the user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s29fab8887734192f">
|
||||
<source>Require reason for impersonation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc24af6de78468cfa">
|
||||
<source>Require administrators to provide a reason for impersonating a user.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd48cc52117f4d68b">
|
||||
<source>Italian</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6cee92a3b310e650">
|
||||
<source>Add entry</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8f36fb59c31d33eb">
|
||||
<source>Link Title</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s92205c10ba1f0f4c">
|
||||
<source>This option configures the footer links on the flow executor pages. The URL is limited to web and mail addresses. If the name is left blank, the URL will be shown.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s66f572bec2bde9c4">
|
||||
<source>External applications that use <x id="0" equiv-text="${this.brand.brandingTitle || "authentik"}"/> as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -7490,6 +7490,10 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>One hint, 'New Application Wizard', is currently hidden</source>
|
||||
<target>Ōńē ĥĩńţ, 'Ńēŵ Àƥƥĺĩćàţĩōń Ŵĩźàŕď', ĩś ćũŕŕēńţĺŷ ĥĩďďēń</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s61bd841e66966325">
|
||||
<source>External applications that use authentik as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
|
||||
<target>Ēxţēŕńàĺ àƥƥĺĩćàţĩōńś ţĥàţ ũśē àũţĥēńţĩķ àś àń ĩďēńţĩţŷ ƥŕōvĩďēŕ vĩà ƥŕōţōćōĺś ĺĩķē ŌÀũţĥ2 àńď ŚÀḾĹ. Àĺĺ àƥƥĺĩćàţĩōńś àŕē śĥōŵń ĥēŕē, ēvēń ōńēś ŷōũ ćàńńōţ àććēśś.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1cc306d8e28c4464">
|
||||
<source>Deny message</source>
|
||||
<target>Ďēńŷ ḿēśśàĝē</target>
|
||||
@ -7590,6 +7594,10 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
||||
<target>Ƒōŕ ńĝĩńx'ś àũţĥ_ŕēǫũēśţ ōŕ ţŕàēƒĩķ'ś ƒōŕŵàŕďÀũţĥ ƥēŕ ŕōōţ ďōḿàĩń</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc615309d10a9228c">
|
||||
<source>RBAC is in preview.</source>
|
||||
<target>ŔßÀĆ ĩś ĩń ƥŕēvĩēŵ.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s32babfed740fd3c1">
|
||||
<source>User type used for newly created users.</source>
|
||||
<target>Ũśēŕ ţŷƥē ũśēď ƒōŕ ńēŵĺŷ ćŕēàţēď ũśēŕś.</target>
|
||||
@ -7782,6 +7790,10 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>Create Endpoint</source>
|
||||
<target>Ćŕēàţē Ēńďƥōĩńţ</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4770c10e5b1c028c">
|
||||
<source>RAC is in preview.</source>
|
||||
<target>ŔÀĆ ĩś ĩń ƥŕēvĩēŵ.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s168565f5ac74a89f">
|
||||
<source>Update RAC Provider</source>
|
||||
<target>Ũƥďàţē ŔÀĆ Ƥŕōvĩďēŕ</target>
|
||||
@ -8124,6 +8136,10 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>Selected Applications</source>
|
||||
<target>Śēĺēćţēď Àƥƥĺĩćàţĩōńś</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s862505f29064fc72">
|
||||
<source>This option configures the footer links on the flow executor pages. It must be a valid YAML or JSON list and can be used as follows:</source>
|
||||
<target>Ţĥĩś ōƥţĩōń ćōńƒĩĝũŕēś ţĥē ƒōōţēŕ ĺĩńķś ōń ţĥē ƒĺōŵ ēxēćũţōŕ ƥàĝēś. Ĩţ ḿũśţ ƀē à vàĺĩď ŶÀḾĹ ōŕ ĵŚŌŃ ĺĩśţ àńď ćàń ƀē ũśēď àś ƒōĺĺōŵś:</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6d3f81dc4bcacbda">
|
||||
<source>Last used</source>
|
||||
<target>Ĺàśţ ũśēď</target>
|
||||
@ -9142,49 +9158,4 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s2e1d5a7d320c25ef">
|
||||
<source>Enter the code from your authenticator device.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="scc1a17d28912e974">
|
||||
<source>Kerberos Source is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s54154a8d64a3597b">
|
||||
<source>Captcha stage</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0c250af62ddbf801">
|
||||
<source>When set, adds functionality exactly like a Captcha stage, but baked into the Identification stage.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sabf8a430d504f8c8">
|
||||
<source>Endpoint Google Chrome Device Trust is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6f4f35a5a4b9b3cb">
|
||||
<source>Interactive</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd020240e41e4c207">
|
||||
<source>Enable this flag if the configured captcha requires User-interaction. Required for reCAPTCHA v2, hCaptcha and Cloudflare Turnstile.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="saa29a2ac03cd9d19">
|
||||
<source>Reason</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se9fd92a824994eba">
|
||||
<source>Reason for impersonating the user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s29fab8887734192f">
|
||||
<source>Require reason for impersonation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc24af6de78468cfa">
|
||||
<source>Require administrators to provide a reason for impersonating a user.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd48cc52117f4d68b">
|
||||
<source>Italian</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6cee92a3b310e650">
|
||||
<source>Add entry</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8f36fb59c31d33eb">
|
||||
<source>Link Title</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s92205c10ba1f0f4c">
|
||||
<source>This option configures the footer links on the flow executor pages. The URL is limited to web and mail addresses. If the name is left blank, the URL will be shown.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s66f572bec2bde9c4">
|
||||
<source>External applications that use <x id="0" equiv-text="${this.brand.brandingTitle || "authentik"}"/> as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
|
||||
</trans-unit>
|
||||
</body></file></xliff>
|
||||
|
@ -7545,6 +7545,10 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>One hint, 'New Application Wizard', is currently hidden</source>
|
||||
<target>Одна подсказка, "Мастер создания нового приложения", в настоящее время скрыта</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s61bd841e66966325">
|
||||
<source>External applications that use authentik as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
|
||||
<target>Внешние приложения, использующие authentik в качестве поставщика идентификационных данных по таким протоколам, как OAuth2 и SAML. Здесь показаны все приложения, даже те, к которым вы не можете получить доступ.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1cc306d8e28c4464">
|
||||
<source>Deny message</source>
|
||||
<target>Запретить сообщение</target>
|
||||
@ -7645,6 +7649,10 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
||||
<target>Для nginx's auth_request или traefik's forwardAuth для корневого домена</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc615309d10a9228c">
|
||||
<source>RBAC is in preview.</source>
|
||||
<target>RBAC находится в предварительной версии.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s32babfed740fd3c1">
|
||||
<source>User type used for newly created users.</source>
|
||||
<target>Тип пользователя, используемый для вновь созданных пользователей.</target>
|
||||
@ -7837,6 +7845,10 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>Create Endpoint</source>
|
||||
<target>Создать конечную точку</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4770c10e5b1c028c">
|
||||
<source>RAC is in preview.</source>
|
||||
<target>RAC находится в предварительной версии.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s168565f5ac74a89f">
|
||||
<source>Update RAC Provider</source>
|
||||
<target>Обновить RAC провайдера</target>
|
||||
@ -8179,6 +8191,10 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>Selected Applications</source>
|
||||
<target>Выбранные приложения</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s862505f29064fc72">
|
||||
<source>This option configures the footer links on the flow executor pages. It must be a valid YAML or JSON list and can be used as follows:</source>
|
||||
<target>Этот параметр настраивает ссылки нижнего колонтитула на страницах исполнителей потока. Это должен быть корректный YAML или JSON, который можно использовать следующим образом:</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6d3f81dc4bcacbda">
|
||||
<source>Last used</source>
|
||||
<target>Послед. использование</target>
|
||||
@ -9165,51 +9181,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2e1d5a7d320c25ef">
|
||||
<source>Enter the code from your authenticator device.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="scc1a17d28912e974">
|
||||
<source>Kerberos Source is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s54154a8d64a3597b">
|
||||
<source>Captcha stage</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0c250af62ddbf801">
|
||||
<source>When set, adds functionality exactly like a Captcha stage, but baked into the Identification stage.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sabf8a430d504f8c8">
|
||||
<source>Endpoint Google Chrome Device Trust is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6f4f35a5a4b9b3cb">
|
||||
<source>Interactive</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd020240e41e4c207">
|
||||
<source>Enable this flag if the configured captcha requires User-interaction. Required for reCAPTCHA v2, hCaptcha and Cloudflare Turnstile.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="saa29a2ac03cd9d19">
|
||||
<source>Reason</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se9fd92a824994eba">
|
||||
<source>Reason for impersonating the user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s29fab8887734192f">
|
||||
<source>Require reason for impersonation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc24af6de78468cfa">
|
||||
<source>Require administrators to provide a reason for impersonating a user.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd48cc52117f4d68b">
|
||||
<source>Italian</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6cee92a3b310e650">
|
||||
<source>Add entry</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8f36fb59c31d33eb">
|
||||
<source>Link Title</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s92205c10ba1f0f4c">
|
||||
<source>This option configures the footer links on the flow executor pages. The URL is limited to web and mail addresses. If the name is left blank, the URL will be shown.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s66f572bec2bde9c4">
|
||||
<source>External applications that use <x id="0" equiv-text="${this.brand.brandingTitle || "authentik"}"/> as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
2502
web/xliff/tr.xlf
2502
web/xliff/tr.xlf
File diff suppressed because it is too large
Load Diff
@ -1375,6 +1375,9 @@
|
||||
<trans-unit id="s9fb28be12e2c6317">
|
||||
<source>Superuser</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc615309d10a9228c">
|
||||
<source>RBAC is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa1db89262360550b">
|
||||
<source>Send us feedback!</source>
|
||||
</trans-unit>
|
||||
@ -1911,6 +1914,9 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
<trans-unit id="s38e7cd1a24e70faa">
|
||||
<source>Create Endpoint</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4770c10e5b1c028c">
|
||||
<source>RAC is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s168565f5ac74a89f">
|
||||
<source>Update RAC Provider</source>
|
||||
</trans-unit>
|
||||
@ -1989,6 +1995,9 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
<trans-unit id="s6ba50bb0842ba1e2">
|
||||
<source>Applications</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s61bd841e66966325">
|
||||
<source>External applications that use authentik as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s96b2fefc550e4b1c">
|
||||
<source>Provider Type</source>
|
||||
</trans-unit>
|
||||
@ -5062,6 +5071,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="sd176021da2ea0fe3">
|
||||
<source>Selected Applications</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s862505f29064fc72">
|
||||
<source>This option configures the footer links on the flow executor pages. It must be a valid YAML or JSON list and can be used as follows:</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6d3f81dc4bcacbda">
|
||||
<source>Last used</source>
|
||||
</trans-unit>
|
||||
@ -5857,51 +5869,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s2e1d5a7d320c25ef">
|
||||
<source>Enter the code from your authenticator device.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="scc1a17d28912e974">
|
||||
<source>Kerberos Source is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s54154a8d64a3597b">
|
||||
<source>Captcha stage</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0c250af62ddbf801">
|
||||
<source>When set, adds functionality exactly like a Captcha stage, but baked into the Identification stage.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sabf8a430d504f8c8">
|
||||
<source>Endpoint Google Chrome Device Trust is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6f4f35a5a4b9b3cb">
|
||||
<source>Interactive</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd020240e41e4c207">
|
||||
<source>Enable this flag if the configured captcha requires User-interaction. Required for reCAPTCHA v2, hCaptcha and Cloudflare Turnstile.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="saa29a2ac03cd9d19">
|
||||
<source>Reason</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se9fd92a824994eba">
|
||||
<source>Reason for impersonating the user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s29fab8887734192f">
|
||||
<source>Require reason for impersonation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc24af6de78468cfa">
|
||||
<source>Require administrators to provide a reason for impersonating a user.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd48cc52117f4d68b">
|
||||
<source>Italian</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6cee92a3b310e650">
|
||||
<source>Add entry</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8f36fb59c31d33eb">
|
||||
<source>Link Title</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s92205c10ba1f0f4c">
|
||||
<source>This option configures the footer links on the flow executor pages. The URL is limited to web and mail addresses. If the name is left blank, the URL will be shown.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s66f572bec2bde9c4">
|
||||
<source>External applications that use <x id="0" equiv-text="${this.brand.brandingTitle || "authentik"}"/> as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
@ -7544,6 +7544,10 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>One hint, 'New Application Wizard', is currently hidden</source>
|
||||
<target>“新应用程序向导”提示目前已隐藏</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s61bd841e66966325">
|
||||
<source>External applications that use authentik as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
|
||||
<target>通过 OAuth2 和 SAML 等协议,使用 authentik 作为身份提供程序的外部应用程序。此处显示了所有应用程序,即使您无法访问的也包括在内。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1cc306d8e28c4464">
|
||||
<source>Deny message</source>
|
||||
<target>拒绝消息</target>
|
||||
@ -7644,6 +7648,10 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
||||
<target>适用于按根域名配置的 nginx 的 auth_request 或 traefik 的 forwardAuth</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc615309d10a9228c">
|
||||
<source>RBAC is in preview.</source>
|
||||
<target>RBAC 目前处于预览状态。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s32babfed740fd3c1">
|
||||
<source>User type used for newly created users.</source>
|
||||
<target>新创建用户使用的用户类型。</target>
|
||||
@ -7836,6 +7844,10 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>Create Endpoint</source>
|
||||
<target>创建端点</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4770c10e5b1c028c">
|
||||
<source>RAC is in preview.</source>
|
||||
<target>RAC 目前处于预览状态。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s168565f5ac74a89f">
|
||||
<source>Update RAC Provider</source>
|
||||
<target>更新 RAC 提供程序</target>
|
||||
@ -8178,6 +8190,10 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>Selected Applications</source>
|
||||
<target>已选应用</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s862505f29064fc72">
|
||||
<source>This option configures the footer links on the flow executor pages. It must be a valid YAML or JSON list and can be used as follows:</source>
|
||||
<target>此选项配置流程执行器页面上的页脚链接。必须为有效的 YAML 或 JSON 列表,可以使用以下值:</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6d3f81dc4bcacbda">
|
||||
<source>Last used</source>
|
||||
<target>上次使用</target>
|
||||
@ -9112,190 +9128,99 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sbfee780fa0a2c83e">
|
||||
<source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be deleted</source>
|
||||
<target>设备类型 <x id="0" equiv-text="${device.verboseName}"/> 无法被删除</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s336936629cdeb3e5">
|
||||
<source>Stage used to verify users' browsers using Google Chrome Device Trust. This stage can be used in authentication/authorization flows.</source>
|
||||
<target>通过 Google Chrome 设备信任来验证用户浏览器的阶段。此阶段可在身份验证/授权流程中使用。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s85fe794c71b4ace8">
|
||||
<source>Google Verified Access API</source>
|
||||
<target>Google Verified Access API</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s013620384af7c8b4">
|
||||
<source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be edited</source>
|
||||
<target>设备类型 <x id="0" equiv-text="${device.verboseName}"/> 无法被编辑</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4347135696fc7cde">
|
||||
<source>Advanced flow settings</source>
|
||||
<target>高级流程设置</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf52ff57fd136cc2f">
|
||||
<source>Enable this option to write password changes made in authentik back to Kerberos. Ignored if sync is disabled.</source>
|
||||
<target>启用此选项会将 authentik 作出的密码修改写入回 Kerberos。如果未启用同步则忽略。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s14a16542f956e11d">
|
||||
<source>Realm settings</source>
|
||||
<target>领域设置</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9c2eae548d3c1c30">
|
||||
<source>Realm</source>
|
||||
<target>领域</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6b032212997e2491">
|
||||
<source>Kerberos 5 configuration</source>
|
||||
<target>Kerberos 5 配置</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sbf50181022f47de3">
|
||||
<source>Kerberos 5 configuration. See man krb5.conf(5) for configuration format. If left empty, a default krb5.conf will be used.</source>
|
||||
<target>Kerberos 5 配置。请阅读 man krb5.conf(5) 了解配置格式。如果留空,则使用默认的 krb5.conf。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2386539a0bd62fab">
|
||||
<source>Sync connection settings</source>
|
||||
<target>同步连接设置</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0d1a6f3fe81351f8">
|
||||
<source>Sync principal</source>
|
||||
<target>同步主体</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa691d6e1974295fa">
|
||||
<source>Principal used to authenticate to the KDC for syncing.</source>
|
||||
<target>向 KDC 进行身份验证以进行同步的主体。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s977b9c629eed3d33">
|
||||
<source>Sync password</source>
|
||||
<target>同步密码</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s77772860385de948">
|
||||
<source>Password used to authenticate to the KDC for syncing. Optional if Sync keytab or Sync credentials cache is provided.</source>
|
||||
<target>向 KDC 进行身份验证以进行同步的密码。如果提供了同步 Keytab 或同步凭据缓存,则此选项是可选的。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc59ec59c3d5e74dc">
|
||||
<source>Sync keytab</source>
|
||||
<target>同步 Keytab</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="scd42997958453f05">
|
||||
<source>Keytab used to authenticate to the KDC for syncing. Optional if Sync password or Sync credentials cache is provided. Must be base64 encoded or in the form TYPE:residual.</source>
|
||||
<target>向 KDC 进行身份验证以进行同步的 Keytab。如果提供了同步密码或同步凭据缓存,则此选项是可选的。必须以 Base64 编码,或者形式为 TYPE:residual。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s60eaf439ccdca1f2">
|
||||
<source>Sync credentials cache</source>
|
||||
<target>同步凭据缓存</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s95722900b0c9026f">
|
||||
<source>Credentials cache used to authenticate to the KDC for syncing. Optional if Sync password or Sync keytab is provided. Must be in the form TYPE:residual.</source>
|
||||
<target>向 KDC 进行身份验证以进行同步的凭据缓存。如果提供了同步密码或同步 Keytab,则此选项是可选的。形式必须为 TYPE:residual。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf9c055db98d7994a">
|
||||
<source>SPNEGO settings</source>
|
||||
<target>SPNEGO 设置</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sab580a45dc46937f">
|
||||
<source>SPNEGO server name</source>
|
||||
<target>SPNEGO 服务器名称</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7a79d6174d17ab2d">
|
||||
<source>Force the use of a specific server name for SPNEGO. Must be in the form HTTP@domain</source>
|
||||
<target>强制为 SPNEGO 使用特定服务器名称。形式必须为 HTTP@域名</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa4ba2b2081472ccd">
|
||||
<source>SPNEGO keytab</source>
|
||||
<target>SPNEGO Keytab</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s64adda975c1106c0">
|
||||
<source>Keytab used for SPNEGO. Optional if SPNEGO credentials cache is provided. Must be base64 encoded or in the form TYPE:residual.</source>
|
||||
<target>SPNEGO 使用的 Keytab。如果提供了 SPNEGO 凭据缓存,则此选项是可选的。必须以 Base64 编码,或者形式为 TYPE:residual。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s92247825b92587b5">
|
||||
<source>SPNEGO credentials cache</source>
|
||||
<target>SPNEGO 凭据缓存</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd9757c345e4062f8">
|
||||
<source>Credentials cache used for SPNEGO. Optional if SPNEGO keytab is provided. Must be in the form TYPE:residual.</source>
|
||||
<target>SPNEGO 使用的凭据缓存。如果提供了 SPNEGO Keytab,则此选项是可选的。形式必须为 TYPE:residual。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s734ab8fbcae0b69e">
|
||||
<source>Kerberos Attribute mapping</source>
|
||||
<target>Kerberos 属性映射</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2c378e86e025fdb2">
|
||||
<source>Update Kerberos Source</source>
|
||||
<target>更新 Kerberos 源</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s03e4044abe0b556c">
|
||||
<source>User database + Kerberos password</source>
|
||||
<target>用户数据库 + Kerberos 密码</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s98bb2ae796f1ceef">
|
||||
<source>Select another authentication method</source>
|
||||
<target>选择另一种身份验证方法</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s21d95b4651ad7a1e">
|
||||
<source>Enter a one-time recovery code for this user.</source>
|
||||
<target>为此用户输入一次性恢复代码。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2e1d5a7d320c25ef">
|
||||
<source>Enter the code from your authenticator device.</source>
|
||||
<target>请输入来自您身份验证设备的代码。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="scc1a17d28912e974">
|
||||
<source>Kerberos Source is in preview.</source>
|
||||
<target>Kerberos 源目前处于预览状态。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s54154a8d64a3597b">
|
||||
<source>Captcha stage</source>
|
||||
<target>验证码阶段</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0c250af62ddbf801">
|
||||
<source>When set, adds functionality exactly like a Captcha stage, but baked into the Identification stage.</source>
|
||||
<target>设置后,添加与验证码阶段完全相同的功能,但融入识别阶段。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sabf8a430d504f8c8">
|
||||
<source>Endpoint Google Chrome Device Trust is in preview.</source>
|
||||
<target>端点 Google Chrome 设备信任处于预览状态。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6f4f35a5a4b9b3cb">
|
||||
<source>Interactive</source>
|
||||
<target>交互式</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd020240e41e4c207">
|
||||
<source>Enable this flag if the configured captcha requires User-interaction. Required for reCAPTCHA v2, hCaptcha and Cloudflare Turnstile.</source>
|
||||
<target>如果配置的验证码需要用户交互则启用此选项。reCAPTCHA v2、hCaptcha 和 Cloudflare Turnstile 需要启用。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="saa29a2ac03cd9d19">
|
||||
<source>Reason</source>
|
||||
<target>原因</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="se9fd92a824994eba">
|
||||
<source>Reason for impersonating the user</source>
|
||||
<target>模拟此用户的原因</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s29fab8887734192f">
|
||||
<source>Require reason for impersonation</source>
|
||||
<target>需要模拟原因</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc24af6de78468cfa">
|
||||
<source>Require administrators to provide a reason for impersonating a user.</source>
|
||||
<target>需要管理员提供模拟用户的原因。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd48cc52117f4d68b">
|
||||
<source>Italian</source>
|
||||
<target>意大利语</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6cee92a3b310e650">
|
||||
<source>Add entry</source>
|
||||
<target>添加条目</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8f36fb59c31d33eb">
|
||||
<source>Link Title</source>
|
||||
<target>链接标题</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s92205c10ba1f0f4c">
|
||||
<source>This option configures the footer links on the flow executor pages. The URL is limited to web and mail addresses. If the name is left blank, the URL will be shown.</source>
|
||||
<target>此选项配置流程执行器页面上的页脚链接。URL 限为 Web 和电子邮件地址。如果名称留空,则显示 URL 自身。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s66f572bec2bde9c4">
|
||||
<source>External applications that use <x id="0" equiv-text="${this.brand.brandingTitle || "authentik"}"/> as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -5699,6 +5699,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s070fdfb03034ca9b">
|
||||
<source>One hint, 'New Application Wizard', is currently hidden</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s61bd841e66966325">
|
||||
<source>External applications that use authentik as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1cc306d8e28c4464">
|
||||
<source>Deny message</source>
|
||||
</trans-unit>
|
||||
@ -5774,6 +5777,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="sfc31264ef7ff86ef">
|
||||
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc615309d10a9228c">
|
||||
<source>RBAC is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s32babfed740fd3c1">
|
||||
<source>User type used for newly created users.</source>
|
||||
</trans-unit>
|
||||
@ -5918,6 +5924,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s38e7cd1a24e70faa">
|
||||
<source>Create Endpoint</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4770c10e5b1c028c">
|
||||
<source>RAC is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s168565f5ac74a89f">
|
||||
<source>Update RAC Provider</source>
|
||||
</trans-unit>
|
||||
@ -6172,6 +6181,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="sd176021da2ea0fe3">
|
||||
<source>Selected Applications</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s862505f29064fc72">
|
||||
<source>This option configures the footer links on the flow executor pages. It must be a valid YAML or JSON list and can be used as follows:</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6d3f81dc4bcacbda">
|
||||
<source>Last used</source>
|
||||
</trans-unit>
|
||||
@ -6966,51 +6978,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2e1d5a7d320c25ef">
|
||||
<source>Enter the code from your authenticator device.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="scc1a17d28912e974">
|
||||
<source>Kerberos Source is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s54154a8d64a3597b">
|
||||
<source>Captcha stage</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0c250af62ddbf801">
|
||||
<source>When set, adds functionality exactly like a Captcha stage, but baked into the Identification stage.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sabf8a430d504f8c8">
|
||||
<source>Endpoint Google Chrome Device Trust is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6f4f35a5a4b9b3cb">
|
||||
<source>Interactive</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd020240e41e4c207">
|
||||
<source>Enable this flag if the configured captcha requires User-interaction. Required for reCAPTCHA v2, hCaptcha and Cloudflare Turnstile.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="saa29a2ac03cd9d19">
|
||||
<source>Reason</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se9fd92a824994eba">
|
||||
<source>Reason for impersonating the user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s29fab8887734192f">
|
||||
<source>Require reason for impersonation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc24af6de78468cfa">
|
||||
<source>Require administrators to provide a reason for impersonating a user.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd48cc52117f4d68b">
|
||||
<source>Italian</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6cee92a3b310e650">
|
||||
<source>Add entry</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8f36fb59c31d33eb">
|
||||
<source>Link Title</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s92205c10ba1f0f4c">
|
||||
<source>This option configures the footer links on the flow executor pages. The URL is limited to web and mail addresses. If the name is left blank, the URL will be shown.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s66f572bec2bde9c4">
|
||||
<source>External applications that use <x id="0" equiv-text="${this.brand.brandingTitle || "authentik"}"/> as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -6618,6 +6618,11 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>Please enter your code</source>
|
||||
<target>请输入您的代码</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s18b910437b73e8e8">
|
||||
<source>Return to device picker</source>
|
||||
<target>返回设备选择器</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="se409d01b52c4e12f">
|
||||
<source>Retry authentication</source>
|
||||
@ -7648,6 +7653,10 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
||||
<target>适用于按根域名配置的 nginx 的 auth_request 或 traefik 的 forwardAuth</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc615309d10a9228c">
|
||||
<source>RBAC is in preview.</source>
|
||||
<target>RBAC 目前处于预览状态。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s32babfed740fd3c1">
|
||||
<source>User type used for newly created users.</source>
|
||||
<target>新创建用户使用的用户类型。</target>
|
||||
@ -7840,6 +7849,10 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>Create Endpoint</source>
|
||||
<target>创建端点</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4770c10e5b1c028c">
|
||||
<source>RAC is in preview.</source>
|
||||
<target>RAC 目前处于预览状态。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s168565f5ac74a89f">
|
||||
<source>Update RAC Provider</source>
|
||||
<target>更新 RAC 提供程序</target>
|
||||
@ -8182,6 +8195,10 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>Selected Applications</source>
|
||||
<target>已选应用</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s862505f29064fc72">
|
||||
<source>This option configures the footer links on the flow executor pages. It must be a valid YAML or JSON list and can be used as follows:</source>
|
||||
<target>此选项配置流程执行器页面上的页脚链接。必须为有效的 YAML 或 JSON 列表,可以使用以下值:</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6d3f81dc4bcacbda">
|
||||
<source>Last used</source>
|
||||
<target>上次使用</target>
|
||||
@ -9113,190 +9130,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s8a598f7aef81c3bc">
|
||||
<source>Key used to encrypt the tokens.</source>
|
||||
<target>用于加密令牌的密钥。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sbfee780fa0a2c83e">
|
||||
<source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be deleted</source>
|
||||
<target>设备类型 <x id="0" equiv-text="${device.verboseName}"/> 无法被删除</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s336936629cdeb3e5">
|
||||
<source>Stage used to verify users' browsers using Google Chrome Device Trust. This stage can be used in authentication/authorization flows.</source>
|
||||
<target>通过 Google Chrome 设备信任来验证用户浏览器的阶段。此阶段可在身份验证/授权流程中使用。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s85fe794c71b4ace8">
|
||||
<source>Google Verified Access API</source>
|
||||
<target>Google Verified Access API</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s013620384af7c8b4">
|
||||
<source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be edited</source>
|
||||
<target>设备类型 <x id="0" equiv-text="${device.verboseName}"/> 无法被编辑</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4347135696fc7cde">
|
||||
<source>Advanced flow settings</source>
|
||||
<target>高级流程设置</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf52ff57fd136cc2f">
|
||||
<source>Enable this option to write password changes made in authentik back to Kerberos. Ignored if sync is disabled.</source>
|
||||
<target>启用此选项会将 authentik 作出的密码修改写入回 Kerberos。如果未启用同步则忽略。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s14a16542f956e11d">
|
||||
<source>Realm settings</source>
|
||||
<target>领域设置</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9c2eae548d3c1c30">
|
||||
<source>Realm</source>
|
||||
<target>领域</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6b032212997e2491">
|
||||
<source>Kerberos 5 configuration</source>
|
||||
<target>Kerberos 5 配置</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sbf50181022f47de3">
|
||||
<source>Kerberos 5 configuration. See man krb5.conf(5) for configuration format. If left empty, a default krb5.conf will be used.</source>
|
||||
<target>Kerberos 5 配置。请阅读 man krb5.conf(5) 了解配置格式。如果留空,则使用默认的 krb5.conf。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2386539a0bd62fab">
|
||||
<source>Sync connection settings</source>
|
||||
<target>同步连接设置</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0d1a6f3fe81351f8">
|
||||
<source>Sync principal</source>
|
||||
<target>同步主体</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa691d6e1974295fa">
|
||||
<source>Principal used to authenticate to the KDC for syncing.</source>
|
||||
<target>向 KDC 进行身份验证以进行同步的主体。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s977b9c629eed3d33">
|
||||
<source>Sync password</source>
|
||||
<target>同步密码</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s77772860385de948">
|
||||
<source>Password used to authenticate to the KDC for syncing. Optional if Sync keytab or Sync credentials cache is provided.</source>
|
||||
<target>向 KDC 进行身份验证以进行同步的密码。如果提供了同步 Keytab 或同步凭据缓存,则此选项是可选的。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc59ec59c3d5e74dc">
|
||||
<source>Sync keytab</source>
|
||||
<target>同步 Keytab</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="scd42997958453f05">
|
||||
<source>Keytab used to authenticate to the KDC for syncing. Optional if Sync password or Sync credentials cache is provided. Must be base64 encoded or in the form TYPE:residual.</source>
|
||||
<target>向 KDC 进行身份验证以进行同步的 Keytab。如果提供了同步密码或同步凭据缓存,则此选项是可选的。必须以 Base64 编码,或者形式为 TYPE:residual。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s60eaf439ccdca1f2">
|
||||
<source>Sync credentials cache</source>
|
||||
<target>同步凭据缓存</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s95722900b0c9026f">
|
||||
<source>Credentials cache used to authenticate to the KDC for syncing. Optional if Sync password or Sync keytab is provided. Must be in the form TYPE:residual.</source>
|
||||
<target>向 KDC 进行身份验证以进行同步的凭据缓存。如果提供了同步密码或同步 Keytab,则此选项是可选的。形式必须为 TYPE:residual。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf9c055db98d7994a">
|
||||
<source>SPNEGO settings</source>
|
||||
<target>SPNEGO 设置</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sab580a45dc46937f">
|
||||
<source>SPNEGO server name</source>
|
||||
<target>SPNEGO 服务器名称</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7a79d6174d17ab2d">
|
||||
<source>Force the use of a specific server name for SPNEGO. Must be in the form HTTP@domain</source>
|
||||
<target>强制为 SPNEGO 使用特定服务器名称。形式必须为 HTTP@域名</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa4ba2b2081472ccd">
|
||||
<source>SPNEGO keytab</source>
|
||||
<target>SPNEGO Keytab</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s64adda975c1106c0">
|
||||
<source>Keytab used for SPNEGO. Optional if SPNEGO credentials cache is provided. Must be base64 encoded or in the form TYPE:residual.</source>
|
||||
<target>SPNEGO 使用的 Keytab。如果提供了 SPNEGO 凭据缓存,则此选项是可选的。必须以 Base64 编码,或者形式为 TYPE:residual。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s92247825b92587b5">
|
||||
<source>SPNEGO credentials cache</source>
|
||||
<target>SPNEGO 凭据缓存</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd9757c345e4062f8">
|
||||
<source>Credentials cache used for SPNEGO. Optional if SPNEGO keytab is provided. Must be in the form TYPE:residual.</source>
|
||||
<target>SPNEGO 使用的凭据缓存。如果提供了 SPNEGO Keytab,则此选项是可选的。形式必须为 TYPE:residual。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s734ab8fbcae0b69e">
|
||||
<source>Kerberos Attribute mapping</source>
|
||||
<target>Kerberos 属性映射</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2c378e86e025fdb2">
|
||||
<source>Update Kerberos Source</source>
|
||||
<target>更新 Kerberos 源</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s03e4044abe0b556c">
|
||||
<source>User database + Kerberos password</source>
|
||||
<target>用户数据库 + Kerberos 密码</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s98bb2ae796f1ceef">
|
||||
<source>Select another authentication method</source>
|
||||
<target>选择另一种身份验证方法</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s21d95b4651ad7a1e">
|
||||
<source>Enter a one-time recovery code for this user.</source>
|
||||
<target>为此用户输入一次性恢复代码。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2e1d5a7d320c25ef">
|
||||
<source>Enter the code from your authenticator device.</source>
|
||||
<target>请输入来自您身份验证设备的代码。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="scc1a17d28912e974">
|
||||
<source>Kerberos Source is in preview.</source>
|
||||
<target>Kerberos 源目前处于预览状态。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s54154a8d64a3597b">
|
||||
<source>Captcha stage</source>
|
||||
<target>验证码阶段</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0c250af62ddbf801">
|
||||
<source>When set, adds functionality exactly like a Captcha stage, but baked into the Identification stage.</source>
|
||||
<target>设置后,添加与验证码阶段完全相同的功能,但融入识别阶段。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sabf8a430d504f8c8">
|
||||
<source>Endpoint Google Chrome Device Trust is in preview.</source>
|
||||
<target>端点 Google Chrome 设备信任处于预览状态。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6f4f35a5a4b9b3cb">
|
||||
<source>Interactive</source>
|
||||
<target>交互式</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd020240e41e4c207">
|
||||
<source>Enable this flag if the configured captcha requires User-interaction. Required for reCAPTCHA v2, hCaptcha and Cloudflare Turnstile.</source>
|
||||
<target>如果配置的验证码需要用户交互则启用此选项。reCAPTCHA v2、hCaptcha 和 Cloudflare Turnstile 需要启用。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="saa29a2ac03cd9d19">
|
||||
<source>Reason</source>
|
||||
<target>原因</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="se9fd92a824994eba">
|
||||
<source>Reason for impersonating the user</source>
|
||||
<target>模拟此用户的原因</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s29fab8887734192f">
|
||||
<source>Require reason for impersonation</source>
|
||||
<target>需要模拟原因</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc24af6de78468cfa">
|
||||
<source>Require administrators to provide a reason for impersonating a user.</source>
|
||||
<target>需要管理员提供模拟用户的原因。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd48cc52117f4d68b">
|
||||
<source>Italian</source>
|
||||
<target>意大利语</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6cee92a3b310e650">
|
||||
<source>Add entry</source>
|
||||
<target>添加条目</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8f36fb59c31d33eb">
|
||||
<source>Link Title</source>
|
||||
<target>链接标题</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s92205c10ba1f0f4c">
|
||||
<source>This option configures the footer links on the flow executor pages. The URL is limited to web and mail addresses. If the name is left blank, the URL will be shown.</source>
|
||||
<target>此选项配置流程执行器页面上的页脚链接。URL 限为 Web 和电子邮件地址。如果名称留空,则显示 URL 自身。</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -7486,6 +7486,10 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>One hint, 'New Application Wizard', is currently hidden</source>
|
||||
<target>提示:「新增應用程式設定精靈」目前處於隱藏中</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s61bd841e66966325">
|
||||
<source>External applications that use authentik as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
|
||||
<target>使用 authentik 作為身份供應商的外部應用程式,透過像 OAuth2 和 SAML 這樣的協議。此處顯示所有應用程式,即使是您無法存取的應用程式也包括在內。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1cc306d8e28c4464">
|
||||
<source>Deny message</source>
|
||||
<target>拒絕的訊息</target>
|
||||
@ -7586,6 +7590,10 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
||||
<target>適用於每個主網域的 nginx 的「auth_request」或 traefik 的「forwardAuth」</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc615309d10a9228c">
|
||||
<source>RBAC is in preview.</source>
|
||||
<target>RBAC 正處於預覽版本。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s32babfed740fd3c1">
|
||||
<source>User type used for newly created users.</source>
|
||||
<target>用於建立使用者的使用者類型。</target>
|
||||
@ -7752,6 +7760,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s38e7cd1a24e70faa">
|
||||
<source>Create Endpoint</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4770c10e5b1c028c">
|
||||
<source>RAC is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s168565f5ac74a89f">
|
||||
<source>Update RAC Provider</source>
|
||||
</trans-unit>
|
||||
@ -8006,6 +8017,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="sd176021da2ea0fe3">
|
||||
<source>Selected Applications</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s862505f29064fc72">
|
||||
<source>This option configures the footer links on the flow executor pages. It must be a valid YAML or JSON list and can be used as follows:</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6d3f81dc4bcacbda">
|
||||
<source>Last used</source>
|
||||
</trans-unit>
|
||||
@ -8800,51 +8814,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2e1d5a7d320c25ef">
|
||||
<source>Enter the code from your authenticator device.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="scc1a17d28912e974">
|
||||
<source>Kerberos Source is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s54154a8d64a3597b">
|
||||
<source>Captcha stage</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0c250af62ddbf801">
|
||||
<source>When set, adds functionality exactly like a Captcha stage, but baked into the Identification stage.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sabf8a430d504f8c8">
|
||||
<source>Endpoint Google Chrome Device Trust is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6f4f35a5a4b9b3cb">
|
||||
<source>Interactive</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd020240e41e4c207">
|
||||
<source>Enable this flag if the configured captcha requires User-interaction. Required for reCAPTCHA v2, hCaptcha and Cloudflare Turnstile.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="saa29a2ac03cd9d19">
|
||||
<source>Reason</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se9fd92a824994eba">
|
||||
<source>Reason for impersonating the user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s29fab8887734192f">
|
||||
<source>Require reason for impersonation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc24af6de78468cfa">
|
||||
<source>Require administrators to provide a reason for impersonating a user.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd48cc52117f4d68b">
|
||||
<source>Italian</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6cee92a3b310e650">
|
||||
<source>Add entry</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8f36fb59c31d33eb">
|
||||
<source>Link Title</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s92205c10ba1f0f4c">
|
||||
<source>This option configures the footer links on the flow executor pages. The URL is limited to web and mail addresses. If the name is left blank, the URL will be shown.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s66f572bec2bde9c4">
|
||||
<source>External applications that use <x id="0" equiv-text="${this.brand.brandingTitle || "authentik"}"/> as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -34,7 +34,7 @@ The following options can be configured:
|
||||
|
||||
If the authentik server does not have a volume mounted under `/media`, you'll get a text input. This accepts absolute URLs. If you've mounted single files into the container, you can reference them using `https://authentik.company/media/my-file.png`.
|
||||
|
||||
If there is a mount under `/media` or if [S3 storage](../../sys-mgmt/ops/storage-s3.md) is configured, you'll instead see a field to upload a file.
|
||||
If there is a mount under `/media` or if [S3 storage](../../install-config/storage-s3.md) is configured, you'll instead see a field to upload a file.
|
||||
|
||||
- _Publisher_: Text shown below the application
|
||||
- _Description_: Subtext shown on the application card below the publisher
|
||||
|
@ -7,7 +7,6 @@ metadata:
|
||||
name: authentik
|
||||
spec:
|
||||
forwardAuth:
|
||||
# This address should point to the cluster endpoint provided by the kubernetes service, not the Ingress.
|
||||
address: http://outpost.company:9000/outpost.goauthentik.io/auth/traefik
|
||||
trustForwardHeader: true
|
||||
authResponseHeaders:
|
||||
|
@ -101,7 +101,15 @@ The following events occur when a license expires or the internal/external user
|
||||
|
||||
- After another 2 weeks, users get a warning banner
|
||||
|
||||
- After another 2 weeks, the authentik Enterprise instance becomes “read-only”
|
||||
- After another 2 weeks, the authentik Enterprise instance becomes "read-only"
|
||||
|
||||
When an authentik instance is in read-only mode, the following actions are still possible:
|
||||
|
||||
- Users can authenticate and authorize applications
|
||||
- Licenses can be modified
|
||||
- Users can be modified/deleted <span class="badge badge--version">authentik 2024.10.5+</span>
|
||||
|
||||
After the violation is corrected (either the user count returns to be within the limits of the license or the license is renewed), authentik will return to the standard read-write mode and the notification will disappear.
|
||||
|
||||
### About users and licenses
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
# GeoIP
|
||||
|
||||
authentik supports GeoIP to add additional information to login/authorization/enrollment requests. Additionally, a [GeoIP policy](../../customize/policies/index.md#geoip-policy) can be used to make policy decisions based on the lookup result.
|
||||
authentik supports GeoIP to add additional information to login/authorization/enrollment requests. Additionally, a [GeoIP policy](../customize/policies/index.md#geoip-policy) can be used to make policy decisions based on the lookup result.
|
||||
|
||||
### Configuration
|
||||
|
@ -1,13 +1,13 @@
|
||||
---
|
||||
title: Installation and Configuration
|
||||
title: Installation
|
||||
---
|
||||
|
||||
Everything you need to get authentik up and running!
|
||||
|
||||
The installation process for our free open source version and our [Enterprise](../enterprise/index.md) version are exactly the same. For information about obtaining an Enterprise license, refer to [License management](../enterprise/manage-enterprise.md#license-management) documentation.
|
||||
|
||||
For information about upgrading to a new version, refer to the <b>Upgrade</b> section in the relevant [Release Notes](../releases) and to our [Upgrade authentik](./upgrade.mdx) documentation.
|
||||
|
||||
The installation process for our free open source version and our [Enterprise](../enterprise/index.md) version are exactly the same. For information about obtaining an Enterprise license, refer to [License management](../enterprise/manage-enterprise.md#license-management) documentation.
|
||||
|
||||
import DocCardList from "@theme/DocCardList";
|
||||
|
||||
<DocCardList />
|
||||
|
@ -83,7 +83,7 @@ The `ENDPOINT` setting specifies how authentik talks to the S3 provider.
|
||||
|
||||
The `CUSTOM_DOMAIN` setting specifies how URLs are constructed to be shown on the web interface. For example, an object stored at `application-icons/application.png` with a `CUSTOM__DOMAIN` setting of `s3.provider/authentik-media` will result in a URL of `https://s3.provider/authentik-media/application-icons/application.png`. You can also use subdomains for your buckets depending on what your S3 provider offers: `authentik-media.s3.provider`. Whether HTTPS is used is controlled by `AUTHENTIK_STORAGE__MEDIA__S3__SECURE_URLS`, which defaults to true.
|
||||
|
||||
For more control over settings, refer to the [configuration reference](../../install-config/configuration/configuration.mdx#media-storage-settings)
|
||||
For more control over settings, refer to the [configuration reference](./configuration/configuration.mdx#media-storage-settings)
|
||||
|
||||
### Migrating between storage backends
|
||||
|
@ -13,7 +13,7 @@ slug: "/releases/2022.12"
|
||||
|
||||
- Bundled GeoIP City database
|
||||
|
||||
authentik now comes with a bundled MaxMind GeoLite2 City database. This allows everyone to take advantage of the extra data provided by GeoIP. The default docker-compose file removes the GeoIP update container as it is no longer needed. See more [here](../../sys-mgmt/ops/geoip.mdx).
|
||||
authentik now comes with a bundled MaxMind GeoLite2 City database. This allows everyone to take advantage of the extra data provided by GeoIP. The default docker-compose file removes the GeoIP update container as it is no longer needed. See more [here](../../install-config/geoip.mdx).
|
||||
|
||||
- Improved UX for user & group management and stage/policy binding
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user