Compare commits
124 Commits
workspace-
...
version/20
Author | SHA1 | Date | |
---|---|---|---|
644705e6fe | |||
ff8ef523db | |||
1051dd19ea | |||
04cb4fd267 | |||
da9508f839 | |||
841a286a25 | |||
63c48d7b99 | |||
5994fd2c61 | |||
5f745e682e | |||
6f1b16e7f9 | |||
57bce19e7a | |||
850c5d5a45 | |||
8b7d11f94c | |||
45737909f6 | |||
4c5fe84f92 | |||
5faa224c81 | |||
736da3abef | |||
52d90f8d3b | |||
7b812de977 | |||
a4bd2cc263 | |||
14038ba8d2 | |||
eaff59b6b0 | |||
cb702ca07a | |||
cb0bfb0dad | |||
bf46d5c916 | |||
59e686c8b9 | |||
9e736f2838 | |||
c2dd3d9c1b | |||
42302d3187 | |||
20ccabf3ec | |||
8f939fa577 | |||
2519bcef89 | |||
3e3615a859 | |||
79e82c8dc9 | |||
ccd4432e1f | |||
b3137f5307 | |||
2591ed9840 | |||
b3e89ef570 | |||
45b48c5cd6 | |||
1eefd834fc | |||
4cc6ed97c5 | |||
bb55d9b3de | |||
3972afb865 | |||
04a013cc1b | |||
fb396f7737 | |||
cf120ff3ff | |||
3e4923d52e | |||
01793088f0 | |||
e2bf2ec2cc | |||
4dfbe28709 | |||
b2021a7191 | |||
81e5fb0c18 | |||
a2a2d940a8 | |||
c034930219 | |||
da3dc51d87 | |||
d217a39513 | |||
7729a9317c | |||
be5f5dd3f0 | |||
bed8d5da4b | |||
4f70f84e80 | |||
97b8551866 | |||
9a0b67e700 | |||
97e4c89cec | |||
65aedde8f7 | |||
17450f23bf | |||
ab3ad6b7fd | |||
45bc3cbd41 | |||
9c1bcac6af | |||
0a133265c5 | |||
57f25a97c9 | |||
8f32242787 | |||
c4bb19051d | |||
10f4fae711 | |||
2d9eab3f60 | |||
fa66195619 | |||
134eb126b6 | |||
f5a6136a58 | |||
1a82dfcd61 | |||
61fc1dc1fb | |||
1f921cc18e | |||
2f94ee3f1f | |||
154fba12e0 | |||
0d18c1d797 | |||
e905dd52d8 | |||
245126a1c3 | |||
15d84d30ba | |||
c6333f9e19 | |||
56565b0895 | |||
cbbc7c1825 | |||
908aaa5afa | |||
937342eab1 | |||
82823a7449 | |||
ad50f14a3e | |||
e0cf6128df | |||
bfbe8b8038 | |||
36ba8bc4e7 | |||
dd5edf7fd9 | |||
da1b252f3b | |||
a8e543972a | |||
6e03045d1f | |||
f4b39e7465 | |||
e7cd5880b5 | |||
d8c6a2417d | |||
a1fe471a59 | |||
054dfda73f | |||
2e5e8f5c58 | |||
c28b65a3f2 | |||
afc9847e36 | |||
620c95dfa1 | |||
15c7a0a9be | |||
5fed8ca575 | |||
f471ddfb29 | |||
1b1f06c9f7 | |||
67c31a8ac3 | |||
638180d246 | |||
a3be1bbb57 | |||
fbd0ba2865 | |||
182ad912cb | |||
e850b2ba1a | |||
b4ce7f9ab0 | |||
5f0bd6f5ea | |||
f5944ccb95 | |||
9bd3bad605 | |||
dff60ee9fb |
@ -1,16 +1,16 @@
|
||||
[bumpversion]
|
||||
current_version = 2025.4.1
|
||||
current_version = 2025.6.0
|
||||
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*))?
|
||||
serialize =
|
||||
serialize =
|
||||
{major}.{minor}.{patch}-{rc_t}{rc_n}
|
||||
{major}.{minor}.{patch}
|
||||
message = release: {new_version}
|
||||
tag_name = version/{new_version}
|
||||
|
||||
[bumpversion:part:rc_t]
|
||||
values =
|
||||
values =
|
||||
rc
|
||||
final
|
||||
optional_value = final
|
||||
|
2
.github/actions/setup/action.yml
vendored
2
.github/actions/setup/action.yml
vendored
@ -36,7 +36,7 @@ runs:
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
- name: Setup docker cache
|
||||
uses: ScribeMD/docker-cache@0.5.0
|
||||
uses: AndreKurait/docker-cache@0fe76702a40db986d9663c24954fc14c6a6031b7
|
||||
with:
|
||||
key: docker-images-${{ runner.os }}-${{ hashFiles('.github/actions/setup/docker-compose.yml', 'Makefile') }}-${{ inputs.postgresql_version }}
|
||||
- name: Setup dependencies
|
||||
|
2
.github/workflows/ci-main.yml
vendored
2
.github/workflows/ci-main.yml
vendored
@ -62,6 +62,7 @@ jobs:
|
||||
psql:
|
||||
- 15-alpine
|
||||
- 16-alpine
|
||||
- 17-alpine
|
||||
run_id: [1, 2, 3, 4, 5]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@ -116,6 +117,7 @@ jobs:
|
||||
psql:
|
||||
- 15-alpine
|
||||
- 16-alpine
|
||||
- 17-alpine
|
||||
run_id: [1, 2, 3, 4, 5]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
17
.github/workflows/packages-npm-publish.yml
vendored
17
.github/workflows/packages-npm-publish.yml
vendored
@ -7,7 +7,7 @@ on:
|
||||
- packages/eslint-config/**
|
||||
- packages/prettier-config/**
|
||||
- packages/tsconfig/**
|
||||
- packages/web/esbuild-plugin-live-reload/**
|
||||
- web/packages/esbuild-plugin-live-reload/**
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
publish:
|
||||
@ -17,27 +17,28 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
package:
|
||||
- docusaurus-config
|
||||
- eslint-config
|
||||
- prettier-config
|
||||
- tsconfig
|
||||
- packages/docusaurus-config
|
||||
- packages/eslint-config
|
||||
- packages/prettier-config
|
||||
- packages/tsconfig
|
||||
- web/packages/esbuild-plugin-live-reload
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 2
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: packages/${{ matrix.package }}/package.json
|
||||
node-version-file: ${{ matrix.package }}/package.json
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
- name: Get changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@ed68ef82c095e0d48ec87eccea555d944a631a4c
|
||||
with:
|
||||
files: |
|
||||
packages/${{ matrix.package }}/package.json
|
||||
${{ matrix.package }}/package.json
|
||||
- name: Publish package
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
working-directory: packages/${{ matrix.package}}
|
||||
working-directory: ${{ matrix.package }}
|
||||
run: |
|
||||
npm ci
|
||||
npm run build
|
||||
|
@ -94,7 +94,7 @@ RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
|
||||
/bin/sh -c "GEOIPUPDATE_LICENSE_KEY_FILE=/run/secrets/GEOIPUPDATE_LICENSE_KEY /usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0"
|
||||
|
||||
# Stage 5: Download uv
|
||||
FROM ghcr.io/astral-sh/uv:0.7.6 AS uv
|
||||
FROM ghcr.io/astral-sh/uv:0.7.8 AS uv
|
||||
# Stage 6: Base python image
|
||||
FROM ghcr.io/goauthentik/fips-python:3.13.3-slim-bookworm-fips AS python-base
|
||||
|
||||
|
2
Makefile
2
Makefile
@ -1,6 +1,6 @@
|
||||
.PHONY: gen dev-reset all clean test web website
|
||||
|
||||
SHELL := /bin/bash
|
||||
SHELL := /usr/bin/env bash
|
||||
.SHELLFLAGS += ${SHELLFLAGS} -e -o pipefail
|
||||
PWD = $(shell pwd)
|
||||
UID = $(shell id -u)
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
from os import environ
|
||||
|
||||
__version__ = "2025.4.1"
|
||||
__version__ = "2025.6.0"
|
||||
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"
|
||||
|
||||
|
||||
|
@ -84,6 +84,7 @@ from authentik.flows.views.executor import QS_KEY_TOKEN
|
||||
from authentik.lib.avatars import get_avatar
|
||||
from authentik.rbac.decorators import permission_required
|
||||
from authentik.rbac.models import get_permission_choices
|
||||
from authentik.stages.email.flow import pickle_flow_token_for_email
|
||||
from authentik.stages.email.models import EmailStage
|
||||
from authentik.stages.email.tasks import send_mails
|
||||
from authentik.stages.email.utils import TemplateEmailMessage
|
||||
@ -451,7 +452,7 @@ class UserViewSet(UsedByMixin, ModelViewSet):
|
||||
def list(self, request, *args, **kwargs):
|
||||
return super().list(request, *args, **kwargs)
|
||||
|
||||
def _create_recovery_link(self) -> tuple[str, Token]:
|
||||
def _create_recovery_link(self, for_email=False) -> tuple[str, Token]:
|
||||
"""Create a recovery link (when the current brand has a recovery flow set),
|
||||
that can either be shown to an admin or sent to the user directly"""
|
||||
brand: Brand = self.request._request.brand
|
||||
@ -473,12 +474,16 @@ class UserViewSet(UsedByMixin, ModelViewSet):
|
||||
raise ValidationError(
|
||||
{"non_field_errors": "Recovery flow not applicable to user"}
|
||||
) from None
|
||||
_plan = FlowToken.pickle(plan)
|
||||
if for_email:
|
||||
_plan = pickle_flow_token_for_email(plan)
|
||||
token, __ = FlowToken.objects.update_or_create(
|
||||
identifier=f"{user.uid}-password-reset",
|
||||
defaults={
|
||||
"user": user,
|
||||
"flow": flow,
|
||||
"_plan": FlowToken.pickle(plan),
|
||||
"_plan": _plan,
|
||||
"revoke_on_execution": not for_email,
|
||||
},
|
||||
)
|
||||
querystring = urlencode({QS_KEY_TOKEN: token.key})
|
||||
@ -648,7 +653,7 @@ class UserViewSet(UsedByMixin, ModelViewSet):
|
||||
if for_user.email == "":
|
||||
LOGGER.debug("User doesn't have an email address")
|
||||
raise ValidationError({"non_field_errors": "User does not have an email address set."})
|
||||
link, token = self._create_recovery_link()
|
||||
link, token = self._create_recovery_link(for_email=True)
|
||||
# Lookup the email stage to assure the current user can access it
|
||||
stages = get_objects_for_user(
|
||||
request.user, "authentik_stages_email.view_emailstage"
|
||||
|
@ -79,6 +79,7 @@ def _migrate_session(
|
||||
AuthenticatedSession.objects.using(db_alias).create(
|
||||
session=session,
|
||||
user=old_auth_session.user,
|
||||
uuid=old_auth_session.uuid,
|
||||
)
|
||||
|
||||
|
||||
|
@ -1,10 +1,81 @@
|
||||
# Generated by Django 5.1.9 on 2025-05-14 11:15
|
||||
|
||||
from django.apps.registry import Apps
|
||||
from django.apps.registry import Apps, apps as global_apps
|
||||
from django.db import migrations
|
||||
from django.contrib.contenttypes.management import create_contenttypes
|
||||
from django.contrib.auth.management import create_permissions
|
||||
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
||||
|
||||
|
||||
def migrate_authenticated_session_permissions(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
|
||||
"""Migrate permissions from OldAuthenticatedSession to AuthenticatedSession"""
|
||||
db_alias = schema_editor.connection.alias
|
||||
|
||||
# `apps` here is just an instance of `django.db.migrations.state.AppConfigStub`, we need the
|
||||
# real config for creating permissions and content types
|
||||
authentik_core_config = global_apps.get_app_config("authentik_core")
|
||||
# These are only ran by django after all migrations, but we need them right now.
|
||||
# `global_apps` is needed,
|
||||
create_permissions(authentik_core_config, using=db_alias, verbosity=1)
|
||||
create_contenttypes(authentik_core_config, using=db_alias, verbosity=1)
|
||||
|
||||
# But from now on, this is just a regular migration, so use `apps`
|
||||
Permission = apps.get_model("auth", "Permission")
|
||||
ContentType = apps.get_model("contenttypes", "ContentType")
|
||||
|
||||
try:
|
||||
old_ct = ContentType.objects.using(db_alias).get(
|
||||
app_label="authentik_core", model="oldauthenticatedsession"
|
||||
)
|
||||
new_ct = ContentType.objects.using(db_alias).get(
|
||||
app_label="authentik_core", model="authenticatedsession"
|
||||
)
|
||||
except ContentType.DoesNotExist:
|
||||
# This should exist at this point, but if not, let's cut our losses
|
||||
return
|
||||
|
||||
# Get all permissions for the old content type
|
||||
old_perms = Permission.objects.using(db_alias).filter(content_type=old_ct)
|
||||
|
||||
# Create equivalent permissions for the new content type
|
||||
for old_perm in old_perms:
|
||||
new_perm = (
|
||||
Permission.objects.using(db_alias)
|
||||
.filter(
|
||||
content_type=new_ct,
|
||||
codename=old_perm.codename,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if not new_perm:
|
||||
# This should exist at this point, but if not, let's cut our losses
|
||||
continue
|
||||
|
||||
# Global user permissions
|
||||
User = apps.get_model("authentik_core", "User")
|
||||
User.user_permissions.through.objects.using(db_alias).filter(
|
||||
permission=old_perm
|
||||
).all().update(permission=new_perm)
|
||||
|
||||
# Global role permissions
|
||||
DjangoGroup = apps.get_model("auth", "Group")
|
||||
DjangoGroup.permissions.through.objects.using(db_alias).filter(
|
||||
permission=old_perm
|
||||
).all().update(permission=new_perm)
|
||||
|
||||
# Object user permissions
|
||||
UserObjectPermission = apps.get_model("guardian", "UserObjectPermission")
|
||||
UserObjectPermission.objects.using(db_alias).filter(permission=old_perm).all().update(
|
||||
permission=new_perm, content_type=new_ct
|
||||
)
|
||||
|
||||
# Object role permissions
|
||||
GroupObjectPermission = apps.get_model("guardian", "GroupObjectPermission")
|
||||
GroupObjectPermission.objects.using(db_alias).filter(permission=old_perm).all().update(
|
||||
permission=new_perm, content_type=new_ct
|
||||
)
|
||||
|
||||
|
||||
def remove_old_authenticated_session_content_type(
|
||||
apps: Apps, schema_editor: BaseDatabaseSchemaEditor
|
||||
):
|
||||
@ -21,7 +92,12 @@ class Migration(migrations.Migration):
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
code=migrate_authenticated_session_permissions,
|
||||
reverse_code=migrations.RunPython.noop,
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=remove_old_authenticated_session_content_type,
|
||||
reverse_code=migrations.RunPython.noop,
|
||||
),
|
||||
]
|
||||
|
@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.1.9 on 2025-05-27 12:52
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_flows", "0027_auto_20231028_1424"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="flowtoken",
|
||||
name="revoke_on_execution",
|
||||
field=models.BooleanField(default=True),
|
||||
),
|
||||
]
|
@ -303,9 +303,10 @@ class FlowToken(Token):
|
||||
|
||||
flow = models.ForeignKey(Flow, on_delete=models.CASCADE)
|
||||
_plan = models.TextField()
|
||||
revoke_on_execution = models.BooleanField(default=True)
|
||||
|
||||
@staticmethod
|
||||
def pickle(plan) -> str:
|
||||
def pickle(plan: "FlowPlan") -> str:
|
||||
"""Pickle into string"""
|
||||
data = dumps(plan)
|
||||
return b64encode(data).decode()
|
||||
|
@ -99,9 +99,10 @@ class ChallengeStageView(StageView):
|
||||
self.logger.debug("Got StageInvalidException", exc=exc)
|
||||
return self.executor.stage_invalid()
|
||||
if not challenge.is_valid():
|
||||
self.logger.warning(
|
||||
self.logger.error(
|
||||
"f(ch): Invalid challenge",
|
||||
errors=challenge.errors,
|
||||
challenge=challenge.data,
|
||||
)
|
||||
return HttpChallengeResponse(challenge)
|
||||
|
||||
|
@ -146,7 +146,8 @@ class FlowExecutorView(APIView):
|
||||
except (AttributeError, EOFError, ImportError, IndexError) as exc:
|
||||
LOGGER.warning("f(exec): Failed to restore token plan", exc=exc)
|
||||
finally:
|
||||
token.delete()
|
||||
if token.revoke_on_execution:
|
||||
token.delete()
|
||||
if not isinstance(plan, FlowPlan):
|
||||
return None
|
||||
plan.context[PLAN_CONTEXT_IS_RESTORED] = token
|
||||
|
@ -81,7 +81,6 @@ debugger: false
|
||||
|
||||
log_level: info
|
||||
|
||||
session_storage: cache
|
||||
sessions:
|
||||
unauthenticated_age: days=1
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
from collections.abc import Callable
|
||||
from dataclasses import asdict
|
||||
|
||||
from celery import group
|
||||
from celery.exceptions import Retry
|
||||
from celery.result import allow_join_result
|
||||
from django.core.paginator import Paginator
|
||||
@ -82,21 +83,41 @@ class SyncTasks:
|
||||
self.logger.debug("Failed to acquire sync lock, skipping", provider=provider.name)
|
||||
return
|
||||
try:
|
||||
for page in users_paginator.page_range:
|
||||
messages.append(_("Syncing page {page} of users".format(page=page)))
|
||||
for msg in sync_objects.apply_async(
|
||||
args=(class_to_path(User), page, provider_pk),
|
||||
time_limit=PAGE_TIMEOUT,
|
||||
soft_time_limit=PAGE_TIMEOUT,
|
||||
).get():
|
||||
messages.append(_("Syncing users"))
|
||||
user_results = (
|
||||
group(
|
||||
[
|
||||
sync_objects.signature(
|
||||
args=(class_to_path(User), page, provider_pk),
|
||||
time_limit=PAGE_TIMEOUT,
|
||||
soft_time_limit=PAGE_TIMEOUT,
|
||||
)
|
||||
for page in users_paginator.page_range
|
||||
]
|
||||
)
|
||||
.apply_async()
|
||||
.get()
|
||||
)
|
||||
for result in user_results:
|
||||
for msg in result:
|
||||
messages.append(LogEvent(**msg))
|
||||
for page in groups_paginator.page_range:
|
||||
messages.append(_("Syncing page {page} of groups".format(page=page)))
|
||||
for msg in sync_objects.apply_async(
|
||||
args=(class_to_path(Group), page, provider_pk),
|
||||
time_limit=PAGE_TIMEOUT,
|
||||
soft_time_limit=PAGE_TIMEOUT,
|
||||
).get():
|
||||
messages.append(_("Syncing groups"))
|
||||
group_results = (
|
||||
group(
|
||||
[
|
||||
sync_objects.signature(
|
||||
args=(class_to_path(Group), page, provider_pk),
|
||||
time_limit=PAGE_TIMEOUT,
|
||||
soft_time_limit=PAGE_TIMEOUT,
|
||||
)
|
||||
for page in groups_paginator.page_range
|
||||
]
|
||||
)
|
||||
.apply_async()
|
||||
.get()
|
||||
)
|
||||
for result in group_results:
|
||||
for msg in result:
|
||||
messages.append(LogEvent(**msg))
|
||||
except TransientSyncException as exc:
|
||||
self.logger.warning("transient sync exception", exc=exc)
|
||||
@ -109,7 +130,7 @@ class SyncTasks:
|
||||
def sync_objects(
|
||||
self, object_type: str, page: int, provider_pk: int, override_dry_run=False, **filter
|
||||
):
|
||||
_object_type = path_to_class(object_type)
|
||||
_object_type: type[Model] = path_to_class(object_type)
|
||||
self.logger = get_logger().bind(
|
||||
provider_type=class_to_path(self._provider_model),
|
||||
provider_pk=provider_pk,
|
||||
@ -132,6 +153,19 @@ class SyncTasks:
|
||||
self.logger.debug("starting discover")
|
||||
client.discover()
|
||||
self.logger.debug("starting sync for page", page=page)
|
||||
messages.append(
|
||||
asdict(
|
||||
LogEvent(
|
||||
_(
|
||||
"Syncing page {page} of {object_type}".format(
|
||||
page=page, object_type=_object_type._meta.verbose_name_plural
|
||||
)
|
||||
),
|
||||
log_level="info",
|
||||
logger=f"{provider._meta.verbose_name}@{object_type}",
|
||||
)
|
||||
)
|
||||
)
|
||||
for obj in paginator.page(page).object_list:
|
||||
obj: Model
|
||||
try:
|
||||
|
@ -166,7 +166,6 @@ class ConnectionToken(ExpiringModel):
|
||||
always_merger.merge(settings, default_settings)
|
||||
always_merger.merge(settings, self.endpoint.provider.settings)
|
||||
always_merger.merge(settings, self.endpoint.settings)
|
||||
always_merger.merge(settings, self.settings)
|
||||
|
||||
def mapping_evaluator(mappings: QuerySet):
|
||||
for mapping in mappings:
|
||||
@ -191,6 +190,7 @@ class ConnectionToken(ExpiringModel):
|
||||
mapping_evaluator(
|
||||
RACPropertyMapping.objects.filter(endpoint__in=[self.endpoint]).order_by("name")
|
||||
)
|
||||
always_merger.merge(settings, self.settings)
|
||||
|
||||
settings["drive-path"] = f"/tmp/connection/{self.token}" # nosec
|
||||
settings["create-drive-path"] = "true"
|
||||
|
@ -90,23 +90,6 @@ class TestModels(TransactionTestCase):
|
||||
"resize-method": "display-update",
|
||||
},
|
||||
)
|
||||
# Set settings in token
|
||||
token.settings = {
|
||||
"level": "token",
|
||||
}
|
||||
token.save()
|
||||
self.assertEqual(
|
||||
token.get_settings(),
|
||||
{
|
||||
"hostname": self.endpoint.host.split(":")[0],
|
||||
"port": "1324",
|
||||
"client-name": f"authentik - {self.user}",
|
||||
"drive-path": path,
|
||||
"create-drive-path": "true",
|
||||
"level": "token",
|
||||
"resize-method": "display-update",
|
||||
},
|
||||
)
|
||||
# Set settings in property mapping (provider)
|
||||
mapping = RACPropertyMapping.objects.create(
|
||||
name=generate_id(),
|
||||
@ -151,3 +134,22 @@ class TestModels(TransactionTestCase):
|
||||
"resize-method": "display-update",
|
||||
},
|
||||
)
|
||||
# Set settings in token
|
||||
token.settings = {
|
||||
"level": "token",
|
||||
}
|
||||
token.save()
|
||||
self.assertEqual(
|
||||
token.get_settings(),
|
||||
{
|
||||
"hostname": self.endpoint.host.split(":")[0],
|
||||
"port": "1324",
|
||||
"client-name": f"authentik - {self.user}",
|
||||
"drive-path": path,
|
||||
"create-drive-path": "true",
|
||||
"foo": "true",
|
||||
"bar": "6",
|
||||
"resize-method": "display-update",
|
||||
"level": "token",
|
||||
},
|
||||
)
|
||||
|
@ -47,15 +47,16 @@ class SCIMGroupClient(SCIMClient[Group, SCIMProviderGroup, SCIMGroupSchema]):
|
||||
|
||||
def to_schema(self, obj: Group, connection: SCIMProviderGroup) -> SCIMGroupSchema:
|
||||
"""Convert authentik user into SCIM"""
|
||||
raw_scim_group = super().to_schema(
|
||||
obj,
|
||||
connection,
|
||||
schemas=(SCIM_GROUP_SCHEMA,),
|
||||
)
|
||||
raw_scim_group = super().to_schema(obj, connection)
|
||||
try:
|
||||
scim_group = SCIMGroupSchema.model_validate(delete_none_values(raw_scim_group))
|
||||
except ValidationError as exc:
|
||||
raise StopSync(exc, obj) from exc
|
||||
if SCIM_GROUP_SCHEMA not in scim_group.schemas:
|
||||
scim_group.schemas.insert(0, SCIM_GROUP_SCHEMA)
|
||||
# As this might be unset, we need to tell pydantic it's set so ensure the schemas
|
||||
# are included, even if its just the defaults
|
||||
scim_group.schemas = list(scim_group.schemas)
|
||||
if not scim_group.externalId:
|
||||
scim_group.externalId = str(obj.pk)
|
||||
|
||||
|
@ -31,15 +31,16 @@ class SCIMUserClient(SCIMClient[User, SCIMProviderUser, SCIMUserSchema]):
|
||||
|
||||
def to_schema(self, obj: User, connection: SCIMProviderUser) -> SCIMUserSchema:
|
||||
"""Convert authentik user into SCIM"""
|
||||
raw_scim_user = super().to_schema(
|
||||
obj,
|
||||
connection,
|
||||
schemas=(SCIM_USER_SCHEMA,),
|
||||
)
|
||||
raw_scim_user = super().to_schema(obj, connection)
|
||||
try:
|
||||
scim_user = SCIMUserSchema.model_validate(delete_none_values(raw_scim_user))
|
||||
except ValidationError as exc:
|
||||
raise StopSync(exc, obj) from exc
|
||||
if SCIM_USER_SCHEMA not in scim_user.schemas:
|
||||
scim_user.schemas.insert(0, SCIM_USER_SCHEMA)
|
||||
# As this might be unset, we need to tell pydantic it's set so ensure the schemas
|
||||
# are included, even if its just the defaults
|
||||
scim_user.schemas = list(scim_user.schemas)
|
||||
if not scim_user.externalId:
|
||||
scim_user.externalId = str(obj.uid)
|
||||
return scim_user
|
||||
|
@ -91,6 +91,57 @@ class SCIMUserTests(TestCase):
|
||||
},
|
||||
)
|
||||
|
||||
@Mocker()
|
||||
def test_user_create_custom_schema(self, mock: Mocker):
|
||||
"""Test user creation with custom schema"""
|
||||
schema = SCIMMapping.objects.create(
|
||||
name="custom_schema",
|
||||
expression="""return {"schemas": ["foo"]}""",
|
||||
)
|
||||
self.provider.property_mappings.add(schema)
|
||||
scim_id = generate_id()
|
||||
mock.get(
|
||||
"https://localhost/ServiceProviderConfig",
|
||||
json={},
|
||||
)
|
||||
mock.post(
|
||||
"https://localhost/Users",
|
||||
json={
|
||||
"id": scim_id,
|
||||
},
|
||||
)
|
||||
uid = generate_id()
|
||||
user = User.objects.create(
|
||||
username=uid,
|
||||
name=f"{uid} {uid}",
|
||||
email=f"{uid}@goauthentik.io",
|
||||
)
|
||||
self.assertEqual(mock.call_count, 2)
|
||||
self.assertEqual(mock.request_history[0].method, "GET")
|
||||
self.assertEqual(mock.request_history[1].method, "POST")
|
||||
self.assertJSONEqual(
|
||||
mock.request_history[1].body,
|
||||
{
|
||||
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User", "foo"],
|
||||
"active": True,
|
||||
"emails": [
|
||||
{
|
||||
"primary": True,
|
||||
"type": "other",
|
||||
"value": f"{uid}@goauthentik.io",
|
||||
}
|
||||
],
|
||||
"externalId": user.uid,
|
||||
"name": {
|
||||
"familyName": uid,
|
||||
"formatted": f"{uid} {uid}",
|
||||
"givenName": uid,
|
||||
},
|
||||
"displayName": f"{uid} {uid}",
|
||||
"userName": uid,
|
||||
},
|
||||
)
|
||||
|
||||
@Mocker()
|
||||
def test_user_create_different_provider_same_id(self, mock: Mocker):
|
||||
"""Test user creation with multiple providers that happen
|
||||
@ -384,7 +435,7 @@ class SCIMUserTests(TestCase):
|
||||
self.assertIn(request.method, SAFE_METHODS)
|
||||
task = SystemTask.objects.filter(uid=slugify(self.provider.name)).first()
|
||||
self.assertIsNotNone(task)
|
||||
drop_msg = task.messages[2]
|
||||
drop_msg = task.messages[3]
|
||||
self.assertEqual(drop_msg["event"], "Dropping mutating request due to dry run")
|
||||
self.assertIsNotNone(drop_msg["attributes"]["url"])
|
||||
self.assertIsNotNone(drop_msg["attributes"]["body"])
|
||||
|
@ -424,7 +424,7 @@ else:
|
||||
"BACKEND": "authentik.root.storages.FileStorage",
|
||||
"OPTIONS": {
|
||||
"location": Path(CONFIG.get("storage.media.file.path")),
|
||||
"base_url": "/media/",
|
||||
"base_url": CONFIG.get("web.path", "/") + "media/",
|
||||
},
|
||||
}
|
||||
# Compatibility for apps not supporting top-level STORAGES
|
||||
|
@ -31,6 +31,8 @@ class PytestTestRunner(DiscoverRunner): # pragma: no cover
|
||||
|
||||
if kwargs.get("randomly_seed", None):
|
||||
self.args.append(f"--randomly-seed={kwargs['randomly_seed']}")
|
||||
if kwargs.get("no_capture", False):
|
||||
self.args.append("--capture=no")
|
||||
|
||||
settings.TEST = True
|
||||
settings.CELERY["task_always_eager"] = True
|
||||
@ -64,6 +66,11 @@ class PytestTestRunner(DiscoverRunner): # pragma: no cover
|
||||
"Default behaviour: use random.Random().getrandbits(32), so the seed is"
|
||||
"different on each run.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-capture",
|
||||
action="store_true",
|
||||
help="Disable any capturing of stdout/stderr during tests.",
|
||||
)
|
||||
|
||||
def run_tests(self, test_labels, extra_tests=None, **kwargs):
|
||||
"""Run pytest and return the exitcode.
|
||||
|
@ -103,6 +103,7 @@ class LDAPSourceSerializer(SourceSerializer):
|
||||
"user_object_filter",
|
||||
"group_object_filter",
|
||||
"group_membership_field",
|
||||
"user_membership_attribute",
|
||||
"object_uniqueness_field",
|
||||
"password_login_update_internal_password",
|
||||
"sync_users",
|
||||
@ -111,6 +112,7 @@ class LDAPSourceSerializer(SourceSerializer):
|
||||
"sync_parent_group",
|
||||
"connectivity",
|
||||
"lookup_groups_from_user",
|
||||
"delete_not_found_objects",
|
||||
]
|
||||
extra_kwargs = {"bind_password": {"write_only": True}}
|
||||
|
||||
@ -138,6 +140,7 @@ class LDAPSourceViewSet(UsedByMixin, ModelViewSet):
|
||||
"user_object_filter",
|
||||
"group_object_filter",
|
||||
"group_membership_field",
|
||||
"user_membership_attribute",
|
||||
"object_uniqueness_field",
|
||||
"password_login_update_internal_password",
|
||||
"sync_users",
|
||||
@ -147,6 +150,7 @@ class LDAPSourceViewSet(UsedByMixin, ModelViewSet):
|
||||
"user_property_mappings",
|
||||
"group_property_mappings",
|
||||
"lookup_groups_from_user",
|
||||
"delete_not_found_objects",
|
||||
]
|
||||
search_fields = ["name", "slug"]
|
||||
ordering = ["name"]
|
||||
|
@ -0,0 +1,48 @@
|
||||
# Generated by Django 5.1.9 on 2025-05-28 08:15
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_core", "0048_delete_oldauthenticatedsession_content_type"),
|
||||
("authentik_sources_ldap", "0008_groupldapsourceconnection_userldapsourceconnection"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="groupldapsourceconnection",
|
||||
name="validated_by",
|
||||
field=models.UUIDField(
|
||||
blank=True,
|
||||
help_text="Unique ID used while checking if this object still exists in the directory.",
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="ldapsource",
|
||||
name="delete_not_found_objects",
|
||||
field=models.BooleanField(
|
||||
default=False,
|
||||
help_text="Delete authentik users and groups which were previously supplied by this source, but are now missing from it.",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="userldapsourceconnection",
|
||||
name="validated_by",
|
||||
field=models.UUIDField(
|
||||
blank=True,
|
||||
help_text="Unique ID used while checking if this object still exists in the directory.",
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="groupldapsourceconnection",
|
||||
index=models.Index(fields=["validated_by"], name="authentik_s_validat_b70447_idx"),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="userldapsourceconnection",
|
||||
index=models.Index(fields=["validated_by"], name="authentik_s_validat_ff2ebc_idx"),
|
||||
),
|
||||
]
|
@ -0,0 +1,32 @@
|
||||
# Generated by Django 5.1.9 on 2025-05-29 11:22
|
||||
|
||||
from django.apps.registry import Apps
|
||||
from django.db import migrations, models
|
||||
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
||||
|
||||
|
||||
def set_user_membership_attribute(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
|
||||
LDAPSource = apps.get_model("authentik_sources_ldap", "LDAPSource")
|
||||
db_alias = schema_editor.connection.alias
|
||||
|
||||
LDAPSource.objects.using(db_alias).filter(group_membership_field="memberUid").all().update(
|
||||
user_membership_attribute="ldap_uniq"
|
||||
)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("authentik_sources_ldap", "0009_groupldapsourceconnection_validated_by_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="ldapsource",
|
||||
name="user_membership_attribute",
|
||||
field=models.TextField(
|
||||
default="distinguishedName",
|
||||
help_text="Attribute which matches the value of `group_membership_field`.",
|
||||
),
|
||||
),
|
||||
migrations.RunPython(set_user_membership_attribute, migrations.RunPython.noop),
|
||||
]
|
@ -100,6 +100,10 @@ class LDAPSource(Source):
|
||||
default="(objectClass=person)",
|
||||
help_text=_("Consider Objects matching this filter to be Users."),
|
||||
)
|
||||
user_membership_attribute = models.TextField(
|
||||
default=LDAP_DISTINGUISHED_NAME,
|
||||
help_text=_("Attribute which matches the value of `group_membership_field`."),
|
||||
)
|
||||
group_membership_field = models.TextField(
|
||||
default="member", help_text=_("Field which contains members of a group.")
|
||||
)
|
||||
@ -137,6 +141,14 @@ class LDAPSource(Source):
|
||||
),
|
||||
)
|
||||
|
||||
delete_not_found_objects = models.BooleanField(
|
||||
default=False,
|
||||
help_text=_(
|
||||
"Delete authentik users and groups which were previously supplied by this source, "
|
||||
"but are now missing from it."
|
||||
),
|
||||
)
|
||||
|
||||
@property
|
||||
def component(self) -> str:
|
||||
return "ak-source-ldap-form"
|
||||
@ -321,6 +333,12 @@ class LDAPSourcePropertyMapping(PropertyMapping):
|
||||
|
||||
|
||||
class UserLDAPSourceConnection(UserSourceConnection):
|
||||
validated_by = models.UUIDField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text=_("Unique ID used while checking if this object still exists in the directory."),
|
||||
)
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[Serializer]:
|
||||
from authentik.sources.ldap.api import (
|
||||
@ -332,9 +350,18 @@ class UserLDAPSourceConnection(UserSourceConnection):
|
||||
class Meta:
|
||||
verbose_name = _("User LDAP Source Connection")
|
||||
verbose_name_plural = _("User LDAP Source Connections")
|
||||
indexes = [
|
||||
models.Index(fields=["validated_by"]),
|
||||
]
|
||||
|
||||
|
||||
class GroupLDAPSourceConnection(GroupSourceConnection):
|
||||
validated_by = models.UUIDField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text=_("Unique ID used while checking if this object still exists in the directory."),
|
||||
)
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[Serializer]:
|
||||
from authentik.sources.ldap.api import (
|
||||
@ -346,3 +373,6 @@ class GroupLDAPSourceConnection(GroupSourceConnection):
|
||||
class Meta:
|
||||
verbose_name = _("Group LDAP Source Connection")
|
||||
verbose_name_plural = _("Group LDAP Source Connections")
|
||||
indexes = [
|
||||
models.Index(fields=["validated_by"]),
|
||||
]
|
||||
|
@ -9,7 +9,7 @@ from structlog.stdlib import BoundLogger, get_logger
|
||||
from authentik.core.sources.mapper import SourceMapper
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.lib.sync.mapper import PropertyMappingManager
|
||||
from authentik.sources.ldap.models import LDAPSource
|
||||
from authentik.sources.ldap.models import LDAPSource, flatten
|
||||
|
||||
|
||||
class BaseLDAPSynchronizer:
|
||||
@ -77,6 +77,16 @@ class BaseLDAPSynchronizer:
|
||||
"""Get objects from LDAP, implemented in subclass"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_attributes(self, object):
|
||||
if "attributes" not in object:
|
||||
return
|
||||
return object.get("attributes", {})
|
||||
|
||||
def get_identifier(self, attributes: dict):
|
||||
if not attributes.get(self._source.object_uniqueness_field):
|
||||
return
|
||||
return flatten(attributes[self._source.object_uniqueness_field])
|
||||
|
||||
def search_paginator( # noqa: PLR0913
|
||||
self,
|
||||
search_base,
|
||||
|
61
authentik/sources/ldap/sync/forward_delete_groups.py
Normal file
61
authentik/sources/ldap/sync/forward_delete_groups.py
Normal file
@ -0,0 +1,61 @@
|
||||
from collections.abc import Generator
|
||||
from itertools import batched
|
||||
from uuid import uuid4
|
||||
|
||||
from ldap3 import SUBTREE
|
||||
|
||||
from authentik.core.models import Group
|
||||
from authentik.sources.ldap.models import GroupLDAPSourceConnection
|
||||
from authentik.sources.ldap.sync.base import BaseLDAPSynchronizer
|
||||
from authentik.sources.ldap.sync.forward_delete_users import DELETE_CHUNK_SIZE, UPDATE_CHUNK_SIZE
|
||||
|
||||
|
||||
class GroupLDAPForwardDeletion(BaseLDAPSynchronizer):
|
||||
"""Delete LDAP Groups from authentik"""
|
||||
|
||||
@staticmethod
|
||||
def name() -> str:
|
||||
return "group_deletions"
|
||||
|
||||
def get_objects(self, **kwargs) -> Generator:
|
||||
if not self._source.sync_groups or not self._source.delete_not_found_objects:
|
||||
self.message("Group syncing is disabled for this Source")
|
||||
return iter(())
|
||||
|
||||
uuid = uuid4()
|
||||
groups = self._source.connection().extend.standard.paged_search(
|
||||
search_base=self.base_dn_groups,
|
||||
search_filter=self._source.group_object_filter,
|
||||
search_scope=SUBTREE,
|
||||
attributes=[self._source.object_uniqueness_field],
|
||||
generator=True,
|
||||
**kwargs,
|
||||
)
|
||||
for batch in batched(groups, UPDATE_CHUNK_SIZE, strict=False):
|
||||
identifiers = []
|
||||
for group in batch:
|
||||
if not (attributes := self.get_attributes(group)):
|
||||
continue
|
||||
if identifier := self.get_identifier(attributes):
|
||||
identifiers.append(identifier)
|
||||
GroupLDAPSourceConnection.objects.filter(identifier__in=identifiers).update(
|
||||
validated_by=uuid
|
||||
)
|
||||
|
||||
return batched(
|
||||
GroupLDAPSourceConnection.objects.filter(source=self._source)
|
||||
.exclude(validated_by=uuid)
|
||||
.values_list("group", flat=True)
|
||||
.iterator(chunk_size=DELETE_CHUNK_SIZE),
|
||||
DELETE_CHUNK_SIZE,
|
||||
strict=False,
|
||||
)
|
||||
|
||||
def sync(self, group_pks: tuple) -> int:
|
||||
"""Delete authentik groups"""
|
||||
if not self._source.sync_groups or not self._source.delete_not_found_objects:
|
||||
self.message("Group syncing is disabled for this Source")
|
||||
return -1
|
||||
self._logger.debug("Deleting groups", group_pks=group_pks)
|
||||
_, deleted_per_type = Group.objects.filter(pk__in=group_pks).delete()
|
||||
return deleted_per_type.get(Group._meta.label, 0)
|
63
authentik/sources/ldap/sync/forward_delete_users.py
Normal file
63
authentik/sources/ldap/sync/forward_delete_users.py
Normal file
@ -0,0 +1,63 @@
|
||||
from collections.abc import Generator
|
||||
from itertools import batched
|
||||
from uuid import uuid4
|
||||
|
||||
from ldap3 import SUBTREE
|
||||
|
||||
from authentik.core.models import User
|
||||
from authentik.sources.ldap.models import UserLDAPSourceConnection
|
||||
from authentik.sources.ldap.sync.base import BaseLDAPSynchronizer
|
||||
|
||||
UPDATE_CHUNK_SIZE = 10_000
|
||||
DELETE_CHUNK_SIZE = 50
|
||||
|
||||
|
||||
class UserLDAPForwardDeletion(BaseLDAPSynchronizer):
|
||||
"""Delete LDAP Users from authentik"""
|
||||
|
||||
@staticmethod
|
||||
def name() -> str:
|
||||
return "user_deletions"
|
||||
|
||||
def get_objects(self, **kwargs) -> Generator:
|
||||
if not self._source.sync_users or not self._source.delete_not_found_objects:
|
||||
self.message("User syncing is disabled for this Source")
|
||||
return iter(())
|
||||
|
||||
uuid = uuid4()
|
||||
users = self._source.connection().extend.standard.paged_search(
|
||||
search_base=self.base_dn_users,
|
||||
search_filter=self._source.user_object_filter,
|
||||
search_scope=SUBTREE,
|
||||
attributes=[self._source.object_uniqueness_field],
|
||||
generator=True,
|
||||
**kwargs,
|
||||
)
|
||||
for batch in batched(users, UPDATE_CHUNK_SIZE, strict=False):
|
||||
identifiers = []
|
||||
for user in batch:
|
||||
if not (attributes := self.get_attributes(user)):
|
||||
continue
|
||||
if identifier := self.get_identifier(attributes):
|
||||
identifiers.append(identifier)
|
||||
UserLDAPSourceConnection.objects.filter(identifier__in=identifiers).update(
|
||||
validated_by=uuid
|
||||
)
|
||||
|
||||
return batched(
|
||||
UserLDAPSourceConnection.objects.filter(source=self._source)
|
||||
.exclude(validated_by=uuid)
|
||||
.values_list("user", flat=True)
|
||||
.iterator(chunk_size=DELETE_CHUNK_SIZE),
|
||||
DELETE_CHUNK_SIZE,
|
||||
strict=False,
|
||||
)
|
||||
|
||||
def sync(self, user_pks: tuple) -> int:
|
||||
"""Delete authentik users"""
|
||||
if not self._source.sync_users or not self._source.delete_not_found_objects:
|
||||
self.message("User syncing is disabled for this Source")
|
||||
return -1
|
||||
self._logger.debug("Deleting users", user_pks=user_pks)
|
||||
_, deleted_per_type = User.objects.filter(pk__in=user_pks).delete()
|
||||
return deleted_per_type.get(User._meta.label, 0)
|
@ -58,18 +58,16 @@ class GroupLDAPSynchronizer(BaseLDAPSynchronizer):
|
||||
return -1
|
||||
group_count = 0
|
||||
for group in page_data:
|
||||
if "attributes" not in group:
|
||||
if (attributes := self.get_attributes(group)) is None:
|
||||
continue
|
||||
attributes = group.get("attributes", {})
|
||||
group_dn = flatten(flatten(group.get("entryDN", group.get("dn"))))
|
||||
if not attributes.get(self._source.object_uniqueness_field):
|
||||
if not (uniq := self.get_identifier(attributes)):
|
||||
self.message(
|
||||
f"Uniqueness field not found/not set in attributes: '{group_dn}'",
|
||||
attributes=attributes.keys(),
|
||||
dn=group_dn,
|
||||
)
|
||||
continue
|
||||
uniq = flatten(attributes[self._source.object_uniqueness_field])
|
||||
try:
|
||||
defaults = {
|
||||
k: flatten(v)
|
||||
|
@ -63,25 +63,19 @@ class MembershipLDAPSynchronizer(BaseLDAPSynchronizer):
|
||||
group_member_dn = group_member.get("dn", {})
|
||||
members.append(group_member_dn)
|
||||
else:
|
||||
if "attributes" not in group:
|
||||
if (attributes := self.get_attributes(group)) is None:
|
||||
continue
|
||||
members = group.get("attributes", {}).get(self._source.group_membership_field, [])
|
||||
members = attributes.get(self._source.group_membership_field, [])
|
||||
|
||||
ak_group = self.get_group(group)
|
||||
if not ak_group:
|
||||
continue
|
||||
|
||||
membership_mapping_attribute = LDAP_DISTINGUISHED_NAME
|
||||
if self._source.group_membership_field == "memberUid":
|
||||
# If memberships are based on the posixGroup's 'memberUid'
|
||||
# attribute we use the RDN instead of the FDN to lookup members.
|
||||
membership_mapping_attribute = LDAP_UNIQUENESS
|
||||
|
||||
users = User.objects.filter(
|
||||
Q(**{f"attributes__{membership_mapping_attribute}__in": members})
|
||||
Q(**{f"attributes__{self._source.user_membership_attribute}__in": members})
|
||||
| Q(
|
||||
**{
|
||||
f"attributes__{membership_mapping_attribute}__isnull": True,
|
||||
f"attributes__{self._source.user_membership_attribute}__isnull": True,
|
||||
"ak_groups__in": [ak_group],
|
||||
}
|
||||
)
|
||||
|
@ -60,18 +60,16 @@ class UserLDAPSynchronizer(BaseLDAPSynchronizer):
|
||||
return -1
|
||||
user_count = 0
|
||||
for user in page_data:
|
||||
if "attributes" not in user:
|
||||
if (attributes := self.get_attributes(user)) is None:
|
||||
continue
|
||||
attributes = user.get("attributes", {})
|
||||
user_dn = flatten(user.get("entryDN", user.get("dn")))
|
||||
if not attributes.get(self._source.object_uniqueness_field):
|
||||
if not (uniq := self.get_identifier(attributes)):
|
||||
self.message(
|
||||
f"Uniqueness field not found/not set in attributes: '{user_dn}'",
|
||||
attributes=attributes.keys(),
|
||||
dn=user_dn,
|
||||
)
|
||||
continue
|
||||
uniq = flatten(attributes[self._source.object_uniqueness_field])
|
||||
try:
|
||||
defaults = {
|
||||
k: flatten(v)
|
||||
|
@ -17,6 +17,8 @@ from authentik.lib.utils.reflection import class_to_path, path_to_class
|
||||
from authentik.root.celery import CELERY_APP
|
||||
from authentik.sources.ldap.models import LDAPSource
|
||||
from authentik.sources.ldap.sync.base import BaseLDAPSynchronizer
|
||||
from authentik.sources.ldap.sync.forward_delete_groups import GroupLDAPForwardDeletion
|
||||
from authentik.sources.ldap.sync.forward_delete_users import UserLDAPForwardDeletion
|
||||
from authentik.sources.ldap.sync.groups import GroupLDAPSynchronizer
|
||||
from authentik.sources.ldap.sync.membership import MembershipLDAPSynchronizer
|
||||
from authentik.sources.ldap.sync.users import UserLDAPSynchronizer
|
||||
@ -52,11 +54,11 @@ def ldap_connectivity_check(pk: str | None = None):
|
||||
|
||||
|
||||
@CELERY_APP.task(
|
||||
# We take the configured hours timeout time by 2.5 as we run user and
|
||||
# group in parallel and then membership, so 2x is to cover the serial tasks,
|
||||
# We take the configured hours timeout time by 3.5 as we run user and
|
||||
# group in parallel and then membership, then deletions, so 3x is to cover the serial tasks,
|
||||
# and 0.5x on top of that to give some more leeway
|
||||
soft_time_limit=(60 * 60 * CONFIG.get_int("ldap.task_timeout_hours")) * 2.5,
|
||||
task_time_limit=(60 * 60 * CONFIG.get_int("ldap.task_timeout_hours")) * 2.5,
|
||||
soft_time_limit=(60 * 60 * CONFIG.get_int("ldap.task_timeout_hours")) * 3.5,
|
||||
task_time_limit=(60 * 60 * CONFIG.get_int("ldap.task_timeout_hours")) * 3.5,
|
||||
)
|
||||
def ldap_sync_single(source_pk: str):
|
||||
"""Sync a single source"""
|
||||
@ -79,6 +81,25 @@ def ldap_sync_single(source_pk: str):
|
||||
group(
|
||||
ldap_sync_paginator(source, MembershipLDAPSynchronizer),
|
||||
),
|
||||
# Finally, deletions. What we'd really like to do here is something like
|
||||
# ```
|
||||
# user_identifiers = <ldap query>
|
||||
# User.objects.exclude(
|
||||
# usersourceconnection__identifier__in=user_uniqueness_identifiers,
|
||||
# ).delete()
|
||||
# ```
|
||||
# This runs into performance issues in large installations. So instead we spread the
|
||||
# work out into three steps:
|
||||
# 1. Get every object from the LDAP source.
|
||||
# 2. Mark every object as "safe" in the database. This is quick, but any error could
|
||||
# mean deleting users which should not be deleted, so we do it immediately, in
|
||||
# large chunks, and only queue the deletion step afterwards.
|
||||
# 3. Delete every unmarked item. This is slow, so we spread it over many tasks in
|
||||
# small chunks.
|
||||
group(
|
||||
ldap_sync_paginator(source, UserLDAPForwardDeletion)
|
||||
+ ldap_sync_paginator(source, GroupLDAPForwardDeletion),
|
||||
),
|
||||
)
|
||||
task()
|
||||
|
||||
|
@ -2,6 +2,33 @@
|
||||
|
||||
from ldap3 import MOCK_SYNC, OFFLINE_SLAPD_2_4, Connection, Server
|
||||
|
||||
# The mock modifies these in place, so we have to define them per string
|
||||
user_in_slapd_dn = "cn=user_in_slapd_cn,ou=users,dc=goauthentik,dc=io"
|
||||
user_in_slapd_cn = "user_in_slapd_cn"
|
||||
user_in_slapd_uid = "user_in_slapd_uid"
|
||||
user_in_slapd_object_class = "person"
|
||||
user_in_slapd = {
|
||||
"dn": user_in_slapd_dn,
|
||||
"attributes": {
|
||||
"cn": user_in_slapd_cn,
|
||||
"uid": user_in_slapd_uid,
|
||||
"objectClass": user_in_slapd_object_class,
|
||||
},
|
||||
}
|
||||
group_in_slapd_dn = "cn=user_in_slapd_cn,ou=groups,dc=goauthentik,dc=io"
|
||||
group_in_slapd_cn = "group_in_slapd_cn"
|
||||
group_in_slapd_uid = "group_in_slapd_uid"
|
||||
group_in_slapd_object_class = "groupOfNames"
|
||||
group_in_slapd = {
|
||||
"dn": group_in_slapd_dn,
|
||||
"attributes": {
|
||||
"cn": group_in_slapd_cn,
|
||||
"uid": group_in_slapd_uid,
|
||||
"objectClass": group_in_slapd_object_class,
|
||||
"member": [user_in_slapd["dn"]],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def mock_slapd_connection(password: str) -> Connection:
|
||||
"""Create mock SLAPD connection"""
|
||||
@ -96,5 +123,14 @@ def mock_slapd_connection(password: str) -> Connection:
|
||||
"objectClass": "posixAccount",
|
||||
},
|
||||
)
|
||||
# Known user and group
|
||||
connection.strategy.add_entry(
|
||||
user_in_slapd["dn"],
|
||||
user_in_slapd["attributes"],
|
||||
)
|
||||
connection.strategy.add_entry(
|
||||
group_in_slapd["dn"],
|
||||
group_in_slapd["attributes"],
|
||||
)
|
||||
connection.bind()
|
||||
return connection
|
||||
|
@ -13,14 +13,26 @@ from authentik.events.system_tasks import TaskStatus
|
||||
from authentik.lib.generators import generate_id, generate_key
|
||||
from authentik.lib.sync.outgoing.exceptions import StopSync
|
||||
from authentik.lib.utils.reflection import class_to_path
|
||||
from authentik.sources.ldap.models import LDAPSource, LDAPSourcePropertyMapping
|
||||
from authentik.sources.ldap.models import (
|
||||
GroupLDAPSourceConnection,
|
||||
LDAPSource,
|
||||
LDAPSourcePropertyMapping,
|
||||
UserLDAPSourceConnection,
|
||||
)
|
||||
from authentik.sources.ldap.sync.forward_delete_users import DELETE_CHUNK_SIZE
|
||||
from authentik.sources.ldap.sync.groups import GroupLDAPSynchronizer
|
||||
from authentik.sources.ldap.sync.membership import MembershipLDAPSynchronizer
|
||||
from authentik.sources.ldap.sync.users import UserLDAPSynchronizer
|
||||
from authentik.sources.ldap.tasks import ldap_sync, ldap_sync_all
|
||||
from authentik.sources.ldap.tests.mock_ad import mock_ad_connection
|
||||
from authentik.sources.ldap.tests.mock_freeipa import mock_freeipa_connection
|
||||
from authentik.sources.ldap.tests.mock_slapd import mock_slapd_connection
|
||||
from authentik.sources.ldap.tests.mock_slapd import (
|
||||
group_in_slapd_cn,
|
||||
group_in_slapd_uid,
|
||||
mock_slapd_connection,
|
||||
user_in_slapd_cn,
|
||||
user_in_slapd_uid,
|
||||
)
|
||||
|
||||
LDAP_PASSWORD = generate_key()
|
||||
|
||||
@ -257,12 +269,56 @@ class LDAPSyncTests(TestCase):
|
||||
self.source.group_membership_field = "memberUid"
|
||||
self.source.user_object_filter = "(objectClass=posixAccount)"
|
||||
self.source.group_object_filter = "(objectClass=posixGroup)"
|
||||
self.source.user_membership_attribute = "uid"
|
||||
self.source.user_property_mappings.set(
|
||||
[
|
||||
*LDAPSourcePropertyMapping.objects.filter(
|
||||
Q(managed__startswith="goauthentik.io/sources/ldap/default")
|
||||
| Q(managed__startswith="goauthentik.io/sources/ldap/openldap")
|
||||
).all(),
|
||||
LDAPSourcePropertyMapping.objects.create(
|
||||
name="name",
|
||||
expression='return {"attributes": {"uid": list_flatten(ldap.get("uid"))}}',
|
||||
),
|
||||
]
|
||||
)
|
||||
self.source.group_property_mappings.set(
|
||||
LDAPSourcePropertyMapping.objects.filter(
|
||||
Q(managed__startswith="goauthentik.io/sources/ldap/default")
|
||||
| Q(managed__startswith="goauthentik.io/sources/ldap/openldap")
|
||||
managed="goauthentik.io/sources/ldap/openldap-cn"
|
||||
)
|
||||
)
|
||||
connection = MagicMock(return_value=mock_slapd_connection(LDAP_PASSWORD))
|
||||
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||
self.source.save()
|
||||
user_sync = UserLDAPSynchronizer(self.source)
|
||||
user_sync.sync_full()
|
||||
group_sync = GroupLDAPSynchronizer(self.source)
|
||||
group_sync.sync_full()
|
||||
membership_sync = MembershipLDAPSynchronizer(self.source)
|
||||
membership_sync.sync_full()
|
||||
# Test if membership mapping based on memberUid works.
|
||||
posix_group = Group.objects.filter(name="group-posix").first()
|
||||
self.assertTrue(posix_group.users.filter(name="user-posix").exists())
|
||||
|
||||
def test_sync_groups_openldap_posix_group_nonstandard_membership_attribute(self):
|
||||
"""Test posix group sync"""
|
||||
self.source.object_uniqueness_field = "cn"
|
||||
self.source.group_membership_field = "memberUid"
|
||||
self.source.user_object_filter = "(objectClass=posixAccount)"
|
||||
self.source.group_object_filter = "(objectClass=posixGroup)"
|
||||
self.source.user_membership_attribute = "cn"
|
||||
self.source.user_property_mappings.set(
|
||||
[
|
||||
*LDAPSourcePropertyMapping.objects.filter(
|
||||
Q(managed__startswith="goauthentik.io/sources/ldap/default")
|
||||
| Q(managed__startswith="goauthentik.io/sources/ldap/openldap")
|
||||
).all(),
|
||||
LDAPSourcePropertyMapping.objects.create(
|
||||
name="name",
|
||||
expression='return {"attributes": {"cn": list_flatten(ldap.get("cn"))}}',
|
||||
),
|
||||
]
|
||||
)
|
||||
self.source.group_property_mappings.set(
|
||||
LDAPSourcePropertyMapping.objects.filter(
|
||||
managed="goauthentik.io/sources/ldap/openldap-cn"
|
||||
@ -308,3 +364,160 @@ class LDAPSyncTests(TestCase):
|
||||
connection = MagicMock(return_value=mock_slapd_connection(LDAP_PASSWORD))
|
||||
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||
ldap_sync_all.delay().get()
|
||||
|
||||
def test_user_deletion(self):
|
||||
"""Test user deletion"""
|
||||
user = User.objects.create_user(username="not-in-the-source")
|
||||
UserLDAPSourceConnection.objects.create(
|
||||
user=user, source=self.source, identifier="not-in-the-source"
|
||||
)
|
||||
self.source.object_uniqueness_field = "uid"
|
||||
self.source.group_object_filter = "(objectClass=groupOfNames)"
|
||||
self.source.delete_not_found_objects = True
|
||||
self.source.save()
|
||||
|
||||
connection = MagicMock(return_value=mock_slapd_connection(LDAP_PASSWORD))
|
||||
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||
ldap_sync_all.delay().get()
|
||||
self.assertFalse(User.objects.filter(username="not-in-the-source").exists())
|
||||
|
||||
def test_user_deletion_still_in_source(self):
|
||||
"""Test that user is not deleted if it's still in the source"""
|
||||
username = user_in_slapd_cn
|
||||
identifier = user_in_slapd_uid
|
||||
user = User.objects.create_user(username=username)
|
||||
UserLDAPSourceConnection.objects.create(
|
||||
user=user, source=self.source, identifier=identifier
|
||||
)
|
||||
self.source.object_uniqueness_field = "uid"
|
||||
self.source.group_object_filter = "(objectClass=groupOfNames)"
|
||||
self.source.delete_not_found_objects = True
|
||||
self.source.save()
|
||||
|
||||
connection = MagicMock(return_value=mock_slapd_connection(LDAP_PASSWORD))
|
||||
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||
ldap_sync_all.delay().get()
|
||||
self.assertTrue(User.objects.filter(username=username).exists())
|
||||
|
||||
def test_user_deletion_no_sync(self):
|
||||
"""Test that user is not deleted if sync_users is False"""
|
||||
user = User.objects.create_user(username="not-in-the-source")
|
||||
UserLDAPSourceConnection.objects.create(
|
||||
user=user, source=self.source, identifier="not-in-the-source"
|
||||
)
|
||||
self.source.object_uniqueness_field = "uid"
|
||||
self.source.group_object_filter = "(objectClass=groupOfNames)"
|
||||
self.source.delete_not_found_objects = True
|
||||
self.source.sync_users = False
|
||||
self.source.save()
|
||||
|
||||
connection = MagicMock(return_value=mock_slapd_connection(LDAP_PASSWORD))
|
||||
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||
ldap_sync_all.delay().get()
|
||||
self.assertTrue(User.objects.filter(username="not-in-the-source").exists())
|
||||
|
||||
def test_user_deletion_no_delete(self):
|
||||
"""Test that user is not deleted if delete_not_found_objects is False"""
|
||||
user = User.objects.create_user(username="not-in-the-source")
|
||||
UserLDAPSourceConnection.objects.create(
|
||||
user=user, source=self.source, identifier="not-in-the-source"
|
||||
)
|
||||
self.source.object_uniqueness_field = "uid"
|
||||
self.source.group_object_filter = "(objectClass=groupOfNames)"
|
||||
self.source.save()
|
||||
|
||||
connection = MagicMock(return_value=mock_slapd_connection(LDAP_PASSWORD))
|
||||
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||
ldap_sync_all.delay().get()
|
||||
self.assertTrue(User.objects.filter(username="not-in-the-source").exists())
|
||||
|
||||
def test_group_deletion(self):
|
||||
"""Test group deletion"""
|
||||
group = Group.objects.create(name="not-in-the-source")
|
||||
GroupLDAPSourceConnection.objects.create(
|
||||
group=group, source=self.source, identifier="not-in-the-source"
|
||||
)
|
||||
self.source.object_uniqueness_field = "uid"
|
||||
self.source.group_object_filter = "(objectClass=groupOfNames)"
|
||||
self.source.delete_not_found_objects = True
|
||||
self.source.save()
|
||||
|
||||
connection = MagicMock(return_value=mock_slapd_connection(LDAP_PASSWORD))
|
||||
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||
ldap_sync_all.delay().get()
|
||||
self.assertFalse(Group.objects.filter(name="not-in-the-source").exists())
|
||||
|
||||
def test_group_deletion_still_in_source(self):
|
||||
"""Test that group is not deleted if it's still in the source"""
|
||||
groupname = group_in_slapd_cn
|
||||
identifier = group_in_slapd_uid
|
||||
group = Group.objects.create(name=groupname)
|
||||
GroupLDAPSourceConnection.objects.create(
|
||||
group=group, source=self.source, identifier=identifier
|
||||
)
|
||||
self.source.object_uniqueness_field = "uid"
|
||||
self.source.group_object_filter = "(objectClass=groupOfNames)"
|
||||
self.source.delete_not_found_objects = True
|
||||
self.source.save()
|
||||
|
||||
connection = MagicMock(return_value=mock_slapd_connection(LDAP_PASSWORD))
|
||||
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||
ldap_sync_all.delay().get()
|
||||
self.assertTrue(Group.objects.filter(name=groupname).exists())
|
||||
|
||||
def test_group_deletion_no_sync(self):
|
||||
"""Test that group is not deleted if sync_groups is False"""
|
||||
group = Group.objects.create(name="not-in-the-source")
|
||||
GroupLDAPSourceConnection.objects.create(
|
||||
group=group, source=self.source, identifier="not-in-the-source"
|
||||
)
|
||||
self.source.object_uniqueness_field = "uid"
|
||||
self.source.group_object_filter = "(objectClass=groupOfNames)"
|
||||
self.source.delete_not_found_objects = True
|
||||
self.source.sync_groups = False
|
||||
self.source.save()
|
||||
|
||||
connection = MagicMock(return_value=mock_slapd_connection(LDAP_PASSWORD))
|
||||
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||
ldap_sync_all.delay().get()
|
||||
self.assertTrue(Group.objects.filter(name="not-in-the-source").exists())
|
||||
|
||||
def test_group_deletion_no_delete(self):
|
||||
"""Test that group is not deleted if delete_not_found_objects is False"""
|
||||
group = Group.objects.create(name="not-in-the-source")
|
||||
GroupLDAPSourceConnection.objects.create(
|
||||
group=group, source=self.source, identifier="not-in-the-source"
|
||||
)
|
||||
self.source.object_uniqueness_field = "uid"
|
||||
self.source.group_object_filter = "(objectClass=groupOfNames)"
|
||||
self.source.save()
|
||||
|
||||
connection = MagicMock(return_value=mock_slapd_connection(LDAP_PASSWORD))
|
||||
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||
ldap_sync_all.delay().get()
|
||||
self.assertTrue(Group.objects.filter(name="not-in-the-source").exists())
|
||||
|
||||
def test_batch_deletion(self):
|
||||
"""Test batch deletion"""
|
||||
BATCH_SIZE = DELETE_CHUNK_SIZE + 1
|
||||
for i in range(BATCH_SIZE):
|
||||
user = User.objects.create_user(username=f"not-in-the-source-{i}")
|
||||
group = Group.objects.create(name=f"not-in-the-source-{i}")
|
||||
group.users.add(user)
|
||||
UserLDAPSourceConnection.objects.create(
|
||||
user=user, source=self.source, identifier=f"not-in-the-source-{i}-user"
|
||||
)
|
||||
GroupLDAPSourceConnection.objects.create(
|
||||
group=group, source=self.source, identifier=f"not-in-the-source-{i}-group"
|
||||
)
|
||||
self.source.object_uniqueness_field = "uid"
|
||||
self.source.group_object_filter = "(objectClass=groupOfNames)"
|
||||
self.source.delete_not_found_objects = True
|
||||
self.source.save()
|
||||
|
||||
connection = MagicMock(return_value=mock_slapd_connection(LDAP_PASSWORD))
|
||||
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||
ldap_sync_all.delay().get()
|
||||
|
||||
self.assertFalse(User.objects.filter(username__startswith="not-in-the-source").exists())
|
||||
self.assertFalse(Group.objects.filter(name__startswith="not-in-the-source").exists())
|
||||
|
@ -9,6 +9,7 @@ from django.http.response import HttpResponseBadRequest
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.utils.http import urlencode
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views import View
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from structlog.stdlib import get_logger
|
||||
@ -128,7 +129,9 @@ class InitiateView(View):
|
||||
# otherwise we default to POST_AUTO, with direct redirect
|
||||
if source.binding_type == SAMLBindingTypes.POST:
|
||||
injected_stages.append(in_memory_stage(ConsentStageView))
|
||||
plan_kwargs[PLAN_CONTEXT_CONSENT_HEADER] = f"Continue to {source.name}"
|
||||
plan_kwargs[PLAN_CONTEXT_CONSENT_HEADER] = _(
|
||||
"Continue to {source_name}".format(source_name=source.name)
|
||||
)
|
||||
injected_stages.append(in_memory_stage(AutosubmitStageView))
|
||||
return self.handle_login_flow(
|
||||
source,
|
||||
|
@ -97,7 +97,8 @@ class GroupsView(SCIMObjectView):
|
||||
self.logger.warning("Invalid group member", exc=exc)
|
||||
continue
|
||||
query |= Q(uuid=member.value)
|
||||
group.users.set(User.objects.filter(query))
|
||||
if query:
|
||||
group.users.set(User.objects.filter(query))
|
||||
if not connection:
|
||||
connection, _ = SCIMSourceGroup.objects.get_or_create(
|
||||
source=self.source,
|
||||
|
@ -151,9 +151,7 @@ class AuthenticatorValidateStageWebAuthnTests(FlowTestCase):
|
||||
webauthn_user_verification=UserVerification.PREFERRED,
|
||||
)
|
||||
stage.webauthn_allowed_device_types.set(
|
||||
WebAuthnDeviceType.objects.filter(
|
||||
description="Android Authenticator with SafetyNet Attestation"
|
||||
)
|
||||
WebAuthnDeviceType.objects.filter(description="YubiKey 5 Series")
|
||||
)
|
||||
session = self.client.session
|
||||
plan = FlowPlan(flow_pk=flow.pk.hex)
|
||||
@ -339,9 +337,7 @@ class AuthenticatorValidateStageWebAuthnTests(FlowTestCase):
|
||||
device_classes=[DeviceClasses.WEBAUTHN],
|
||||
)
|
||||
stage.webauthn_allowed_device_types.set(
|
||||
WebAuthnDeviceType.objects.filter(
|
||||
description="Android Authenticator with SafetyNet Attestation"
|
||||
)
|
||||
WebAuthnDeviceType.objects.filter(description="YubiKey 5 Series")
|
||||
)
|
||||
session = self.client.session
|
||||
plan = FlowPlan(flow_pk=flow.pk.hex)
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -141,9 +141,7 @@ class TestAuthenticatorWebAuthnStage(FlowTestCase):
|
||||
"""Test registration with restricted devices (fail)"""
|
||||
webauthn_mds_import.delay(force=True).get()
|
||||
self.stage.device_type_restrictions.set(
|
||||
WebAuthnDeviceType.objects.filter(
|
||||
description="Android Authenticator with SafetyNet Attestation"
|
||||
)
|
||||
WebAuthnDeviceType.objects.filter(description="YubiKey 5 Series")
|
||||
)
|
||||
|
||||
plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()])
|
||||
|
@ -4,6 +4,8 @@ from uuid import uuid4
|
||||
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext as _
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.fields import CharField
|
||||
|
||||
from authentik.core.api.utils import PassiveSerializer
|
||||
@ -47,6 +49,11 @@ class ConsentChallengeResponse(ChallengeResponse):
|
||||
component = CharField(default="ak-stage-consent")
|
||||
token = CharField(required=True)
|
||||
|
||||
def validate_token(self, token: str):
|
||||
if token != self.stage.executor.request.session[SESSION_KEY_CONSENT_TOKEN]:
|
||||
raise ValidationError(_("Invalid consent token, re-showing prompt"))
|
||||
return token
|
||||
|
||||
|
||||
class ConsentStageView(ChallengeStageView):
|
||||
"""Simple consent checker."""
|
||||
@ -120,9 +127,6 @@ class ConsentStageView(ChallengeStageView):
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
def challenge_valid(self, response: ChallengeResponse) -> HttpResponse:
|
||||
if response.data["token"] != self.request.session[SESSION_KEY_CONSENT_TOKEN]:
|
||||
self.logger.info("Invalid consent token, re-showing prompt")
|
||||
return self.get(self.request)
|
||||
if self.should_always_prompt():
|
||||
return self.executor.stage_ok()
|
||||
current_stage: ConsentStage = self.executor.current_stage
|
||||
|
@ -17,6 +17,7 @@ from authentik.flows.views.executor import SESSION_KEY_PLAN
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.stages.consent.models import ConsentMode, ConsentStage, UserConsent
|
||||
from authentik.stages.consent.stage import (
|
||||
PLAN_CONTEXT_CONSENT_HEADER,
|
||||
PLAN_CONTEXT_CONSENT_PERMISSIONS,
|
||||
SESSION_KEY_CONSENT_TOKEN,
|
||||
)
|
||||
@ -33,6 +34,40 @@ class TestConsentStage(FlowTestCase):
|
||||
slug=generate_id(),
|
||||
)
|
||||
|
||||
def test_mismatched_token(self):
|
||||
"""Test incorrect token"""
|
||||
flow = create_test_flow(FlowDesignation.AUTHENTICATION)
|
||||
stage = ConsentStage.objects.create(name=generate_id(), mode=ConsentMode.ALWAYS_REQUIRE)
|
||||
binding = FlowStageBinding.objects.create(target=flow, stage=stage, order=2)
|
||||
|
||||
plan = FlowPlan(flow_pk=flow.pk.hex, bindings=[binding], markers=[StageMarker()])
|
||||
session = self.client.session
|
||||
session[SESSION_KEY_PLAN] = plan
|
||||
session.save()
|
||||
response = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
session = self.client.session
|
||||
response = self.client.post(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
||||
{
|
||||
"token": generate_id(),
|
||||
},
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertStageResponse(
|
||||
response,
|
||||
flow,
|
||||
component="ak-stage-consent",
|
||||
response_errors={
|
||||
"token": [{"string": "Invalid consent token, re-showing prompt", "code": "invalid"}]
|
||||
},
|
||||
)
|
||||
self.assertFalse(UserConsent.objects.filter(user=self.user).exists())
|
||||
|
||||
def test_always_required(self):
|
||||
"""Test always required consent"""
|
||||
flow = create_test_flow(FlowDesignation.AUTHENTICATION)
|
||||
@ -158,6 +193,7 @@ class TestConsentStage(FlowTestCase):
|
||||
context={
|
||||
PLAN_CONTEXT_APPLICATION: self.application,
|
||||
PLAN_CONTEXT_CONSENT_PERMISSIONS: [PermissionDict(id="foo", name="foo-desc")],
|
||||
PLAN_CONTEXT_CONSENT_HEADER: "test header",
|
||||
},
|
||||
)
|
||||
session = self.client.session
|
||||
|
38
authentik/stages/email/flow.py
Normal file
38
authentik/stages/email/flow.py
Normal file
@ -0,0 +1,38 @@
|
||||
from base64 import b64encode
|
||||
from copy import deepcopy
|
||||
from pickle import dumps # nosec
|
||||
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from authentik.flows.models import FlowToken, in_memory_stage
|
||||
from authentik.flows.planner import PLAN_CONTEXT_IS_RESTORED, FlowPlan
|
||||
from authentik.stages.consent.stage import PLAN_CONTEXT_CONSENT_HEADER, ConsentStageView
|
||||
|
||||
|
||||
def pickle_flow_token_for_email(plan: FlowPlan):
|
||||
"""Insert a consent stage into the flow plan and pickle it for a FlowToken,
|
||||
to be sent via Email. This is to prevent automated email scanners, which sometimes
|
||||
open links in emails in a full browser from breaking the link."""
|
||||
plan_copy = deepcopy(plan)
|
||||
plan_copy.insert_stage(in_memory_stage(EmailTokenRevocationConsentStageView), index=0)
|
||||
plan_copy.context[PLAN_CONTEXT_CONSENT_HEADER] = _("Continue to confirm this email address.")
|
||||
data = dumps(plan_copy)
|
||||
return b64encode(data).decode()
|
||||
|
||||
|
||||
class EmailTokenRevocationConsentStageView(ConsentStageView):
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
token: FlowToken = self.executor.plan.context[PLAN_CONTEXT_IS_RESTORED]
|
||||
try:
|
||||
token.refresh_from_db()
|
||||
except FlowToken.DoesNotExist:
|
||||
return self.executor.stage_invalid(
|
||||
_("Link was already used, please request a new link.")
|
||||
)
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
def challenge_valid(self, response):
|
||||
token: FlowToken = self.executor.plan.context[PLAN_CONTEXT_IS_RESTORED]
|
||||
token.delete()
|
||||
return super().challenge_valid(response)
|
@ -23,6 +23,7 @@ from authentik.flows.stage import ChallengeStageView
|
||||
from authentik.flows.views.executor import QS_KEY_TOKEN, QS_QUERY
|
||||
from authentik.lib.utils.errors import exception_to_string
|
||||
from authentik.lib.utils.time import timedelta_from_string
|
||||
from authentik.stages.email.flow import pickle_flow_token_for_email
|
||||
from authentik.stages.email.models import EmailStage
|
||||
from authentik.stages.email.tasks import send_mails
|
||||
from authentik.stages.email.utils import TemplateEmailMessage
|
||||
@ -86,7 +87,8 @@ class EmailStageView(ChallengeStageView):
|
||||
user=pending_user,
|
||||
identifier=identifier,
|
||||
flow=self.executor.flow,
|
||||
_plan=FlowToken.pickle(self.executor.plan),
|
||||
_plan=pickle_flow_token_for_email(self.executor.plan),
|
||||
revoke_on_execution=False,
|
||||
)
|
||||
token = tokens.first()
|
||||
# Check if token is expired and rotate key if so
|
||||
|
@ -174,5 +174,5 @@ class TestEmailStageSending(FlowTestCase):
|
||||
response = self.client.post(url)
|
||||
response = self.client.post(url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(len(mail.outbox) >= 1)
|
||||
self.assertGreaterEqual(len(mail.outbox), 1)
|
||||
self.assertEqual(mail.outbox[0].subject, "authentik")
|
||||
|
@ -17,6 +17,7 @@ from authentik.flows.tests import FlowTestCase
|
||||
from authentik.flows.views.executor import QS_KEY_TOKEN, SESSION_KEY_PLAN, FlowExecutorView
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.stages.consent.stage import SESSION_KEY_CONSENT_TOKEN
|
||||
from authentik.stages.email.models import EmailStage
|
||||
from authentik.stages.email.stage import PLAN_CONTEXT_EMAIL_OVERRIDE, EmailStageView
|
||||
|
||||
@ -160,6 +161,17 @@ class TestEmailStage(FlowTestCase):
|
||||
kwargs={"flow_slug": self.flow.slug},
|
||||
)
|
||||
)
|
||||
self.assertStageResponse(response, self.flow, component="ak-stage-consent")
|
||||
response = self.client.post(
|
||||
reverse(
|
||||
"authentik_api:flow-executor",
|
||||
kwargs={"flow_slug": self.flow.slug},
|
||||
),
|
||||
data={
|
||||
"token": self.client.session[SESSION_KEY_CONSENT_TOKEN],
|
||||
},
|
||||
follow=True,
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||
@ -182,6 +194,7 @@ class TestEmailStage(FlowTestCase):
|
||||
# Set flow token user to a different user
|
||||
token: FlowToken = FlowToken.objects.get(user=self.user)
|
||||
token.user = create_test_admin_user()
|
||||
token.revoke_on_execution = True
|
||||
token.save()
|
||||
|
||||
with patch("authentik.flows.views.executor.FlowExecutorView.cancel", MagicMock()):
|
||||
|
@ -11,7 +11,7 @@ from rest_framework.fields import BooleanField, CharField
|
||||
from authentik.core.models import Session, User
|
||||
from authentik.events.middleware import audit_ignore
|
||||
from authentik.flows.challenge import ChallengeResponse, WithUserInfoChallenge
|
||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, PLAN_CONTEXT_SOURCE
|
||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
|
||||
from authentik.flows.stage import ChallengeStageView
|
||||
from authentik.lib.utils.time import timedelta_from_string
|
||||
from authentik.root.middleware import ClientIPMiddleware
|
||||
@ -108,10 +108,6 @@ class UserLoginStageView(ChallengeStageView):
|
||||
flow_slug=self.executor.flow.slug,
|
||||
session_duration=delta,
|
||||
)
|
||||
# Only show success message if we don't have a source in the flow
|
||||
# as sources show their own success messages
|
||||
if not self.executor.plan.context.get(PLAN_CONTEXT_SOURCE, None):
|
||||
messages.success(self.request, _("Successfully logged in!"))
|
||||
if self.executor.current_stage.terminate_other_sessions:
|
||||
Session.objects.filter(
|
||||
authenticatedsession__user=user,
|
||||
|
@ -2,7 +2,7 @@
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://goauthentik.io/blueprints/schema.json",
|
||||
"type": "object",
|
||||
"title": "authentik 2025.4.1 Blueprint schema",
|
||||
"title": "authentik 2025.6.0 Blueprint schema",
|
||||
"required": [
|
||||
"version",
|
||||
"entries"
|
||||
@ -8147,6 +8147,12 @@
|
||||
"title": "Group membership field",
|
||||
"description": "Field which contains members of a group."
|
||||
},
|
||||
"user_membership_attribute": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"title": "User membership attribute",
|
||||
"description": "Attribute which matches the value of `group_membership_field`."
|
||||
},
|
||||
"object_uniqueness_field": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
@ -8180,6 +8186,11 @@
|
||||
"type": "boolean",
|
||||
"title": "Lookup groups from user",
|
||||
"description": "Lookup group membership based on a user attribute instead of a group attribute. This allows nested group resolution on systems like FreeIPA and Active Directory"
|
||||
},
|
||||
"delete_not_found_objects": {
|
||||
"type": "boolean",
|
||||
"title": "Delete not found objects",
|
||||
"description": "Delete authentik users and groups which were previously supplied by this source, but are now missing from it."
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
|
@ -31,7 +31,7 @@ services:
|
||||
volumes:
|
||||
- redis:/data
|
||||
server:
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.4.1}
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.6.0}
|
||||
restart: unless-stopped
|
||||
command: server
|
||||
environment:
|
||||
@ -55,7 +55,7 @@ services:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
worker:
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.4.1}
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.6.0}
|
||||
restart: unless-stopped
|
||||
command: worker
|
||||
environment:
|
||||
|
2
go.mod
2
go.mod
@ -27,7 +27,7 @@ require (
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/wwt/guac v1.3.2
|
||||
goauthentik.io/api/v3 v3.2025041.2
|
||||
goauthentik.io/api/v3 v3.2025041.4
|
||||
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
|
||||
golang.org/x/oauth2 v0.30.0
|
||||
golang.org/x/sync v0.14.0
|
||||
|
4
go.sum
4
go.sum
@ -290,8 +290,8 @@ go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y
|
||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
goauthentik.io/api/v3 v3.2025041.2 h1:vFYYnhcDcxL95RczZwhzt3i4LptFXMvIRN+vgf8sQYg=
|
||||
goauthentik.io/api/v3 v3.2025041.2/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
||||
goauthentik.io/api/v3 v3.2025041.4 h1:cGqzWYnUHrWDoaXWDpIL/kWnX9sFrIhkYDye0P0OEAo=
|
||||
goauthentik.io/api/v3 v3.2025041.4/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=
|
||||
|
@ -33,4 +33,4 @@ func UserAgent() string {
|
||||
return fmt.Sprintf("authentik@%s", FullVersion())
|
||||
}
|
||||
|
||||
const VERSION = "2025.4.1"
|
||||
const VERSION = "2025.6.0"
|
||||
|
@ -28,16 +28,18 @@ func NewSessionBinder(si server.LDAPServerInstance, oldBinder bind.Binder) *Sess
|
||||
si: si,
|
||||
log: log.WithField("logger", "authentik.outpost.ldap.binder.session"),
|
||||
}
|
||||
if oldSb, ok := oldBinder.(*SessionBinder); ok {
|
||||
sb.DirectBinder = oldSb.DirectBinder
|
||||
sb.sessions = oldSb.sessions
|
||||
sb.log.Debug("re-initialised session binder")
|
||||
} else {
|
||||
sb.sessions = ttlcache.New(ttlcache.WithDisableTouchOnHit[Credentials, ldap.LDAPResultCode]())
|
||||
sb.DirectBinder = *direct.NewDirectBinder(si)
|
||||
go sb.sessions.Start()
|
||||
sb.log.Debug("initialised session binder")
|
||||
if oldBinder != nil {
|
||||
if oldSb, ok := oldBinder.(*SessionBinder); ok {
|
||||
sb.DirectBinder = oldSb.DirectBinder
|
||||
sb.sessions = oldSb.sessions
|
||||
sb.log.Debug("re-initialised session binder")
|
||||
return sb
|
||||
}
|
||||
}
|
||||
sb.sessions = ttlcache.New(ttlcache.WithDisableTouchOnHit[Credentials, ldap.LDAPResultCode]())
|
||||
sb.DirectBinder = *direct.NewDirectBinder(si)
|
||||
go sb.sessions.Start()
|
||||
sb.log.Debug("initialised session binder")
|
||||
return sb
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
memorybind "goauthentik.io/internal/outpost/ldap/bind/memory"
|
||||
"goauthentik.io/internal/outpost/ldap/constants"
|
||||
"goauthentik.io/internal/outpost/ldap/flags"
|
||||
"goauthentik.io/internal/outpost/ldap/search"
|
||||
directsearch "goauthentik.io/internal/outpost/ldap/search/direct"
|
||||
memorysearch "goauthentik.io/internal/outpost/ldap/search/memory"
|
||||
)
|
||||
@ -85,7 +86,11 @@ func (ls *LDAPServer) Refresh() error {
|
||||
providers[idx].certUUID = *kp
|
||||
}
|
||||
if *provider.SearchMode.Ptr() == api.LDAPAPIACCESSMODE_CACHED {
|
||||
providers[idx].searcher = memorysearch.NewMemorySearcher(providers[idx])
|
||||
var oldSearcher search.Searcher
|
||||
if existing != nil {
|
||||
oldSearcher = existing.searcher
|
||||
}
|
||||
providers[idx].searcher = memorysearch.NewMemorySearcher(providers[idx], oldSearcher)
|
||||
} else if *provider.SearchMode.Ptr() == api.LDAPAPIACCESSMODE_DIRECT {
|
||||
providers[idx].searcher = directsearch.NewDirectSearcher(providers[idx])
|
||||
}
|
||||
|
@ -31,13 +31,26 @@ type MemorySearcher struct {
|
||||
groups []api.Group
|
||||
}
|
||||
|
||||
func NewMemorySearcher(si server.LDAPServerInstance) *MemorySearcher {
|
||||
func NewMemorySearcher(si server.LDAPServerInstance, existing search.Searcher) *MemorySearcher {
|
||||
ms := &MemorySearcher{
|
||||
si: si,
|
||||
log: log.WithField("logger", "authentik.outpost.ldap.searcher.memory"),
|
||||
ds: direct.NewDirectSearcher(si),
|
||||
}
|
||||
if existing != nil {
|
||||
if ems, ok := existing.(*MemorySearcher); ok {
|
||||
ems.si = si
|
||||
ems.fetch()
|
||||
ems.log.Debug("re-initialised memory searcher")
|
||||
return ems
|
||||
}
|
||||
}
|
||||
ms.fetch()
|
||||
ms.log.Debug("initialised memory searcher")
|
||||
return ms
|
||||
}
|
||||
|
||||
func (ms *MemorySearcher) fetch() {
|
||||
// Error is not handled here, we get an empty/truncated list and the error is logged
|
||||
users, _ := ak.Paginator(ms.si.GetAPIClient().CoreApi.CoreUsersList(context.TODO()).IncludeGroups(true), ak.PaginatorOptions{
|
||||
PageSize: 100,
|
||||
@ -49,7 +62,6 @@ func NewMemorySearcher(si server.LDAPServerInstance) *MemorySearcher {
|
||||
Logger: ms.log,
|
||||
})
|
||||
ms.groups = groups
|
||||
return ms
|
||||
}
|
||||
|
||||
func (ms *MemorySearcher) SearchBase(req *search.Request) (ldap.ServerSearchResult, error) {
|
||||
|
@ -67,11 +67,15 @@ func (ws *WebServer) configureStatic() {
|
||||
|
||||
// Media files, if backend is file
|
||||
if config.Get().Storage.Media.Backend == "file" {
|
||||
fsMedia := http.StripPrefix("/media", http.FileServer(http.Dir(config.Get().Storage.Media.File.Path)))
|
||||
indexLessRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/media/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox")
|
||||
fsMedia.ServeHTTP(w, r)
|
||||
})
|
||||
fsMedia := http.FileServer(http.Dir(config.Get().Storage.Media.File.Path))
|
||||
indexLessRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/media/").Handler(pathStripper(
|
||||
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox")
|
||||
fsMedia.ServeHTTP(w, r)
|
||||
}),
|
||||
"media/",
|
||||
config.Get().Web.Path,
|
||||
))
|
||||
}
|
||||
|
||||
staticRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/if/help/").Handler(pathStripper(
|
||||
|
@ -26,7 +26,7 @@ Parameters:
|
||||
Description: authentik Docker image
|
||||
AuthentikVersion:
|
||||
Type: String
|
||||
Default: 2025.4.1
|
||||
Default: 2025.6.0
|
||||
Description: authentik Docker image tag
|
||||
AuthentikServerCPU:
|
||||
Type: Number
|
||||
|
Binary file not shown.
@ -32,15 +32,17 @@
|
||||
# datenschmutz, 2025
|
||||
# 97cce0ae0cad2a2cc552d3165d04643e_de3d740, 2025
|
||||
# Dominic Wagner <mail@dominic-wagner.de>, 2025
|
||||
# Till-Frederik Riechard, 2025
|
||||
# Alexander Mnich, 2025
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
|
||||
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: Dominic Wagner <mail@dominic-wagner.de>, 2025\n"
|
||||
"Last-Translator: Alexander Mnich, 2025\n"
|
||||
"Language-Team: German (https://app.transifex.com/authentik/teams/119923/de/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -132,6 +134,10 @@ msgstr ""
|
||||
msgid "Web Certificate used by the authentik Core webserver."
|
||||
msgstr "Vom Authentik-Core-Webserver verwendetes Zertifikat."
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Certificates used for client authentication."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Brand"
|
||||
msgstr "Marke"
|
||||
@ -405,7 +411,7 @@ msgstr "Eigenschaften"
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid "session data"
|
||||
msgstr ""
|
||||
msgstr "Sitzungsdaten"
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid "Session"
|
||||
@ -533,7 +539,7 @@ msgstr ""
|
||||
|
||||
#: authentik/enterprise/policies/unique_password/models.py
|
||||
msgid "Number of passwords to check against."
|
||||
msgstr ""
|
||||
msgstr "Anzahl Passwörter, gegen die geprüft wird."
|
||||
|
||||
#: authentik/enterprise/policies/unique_password/models.py
|
||||
#: authentik/policies/password/models.py
|
||||
@ -543,18 +549,20 @@ msgstr "Passwort nicht im Kontext festgelegt"
|
||||
#: authentik/enterprise/policies/unique_password/models.py
|
||||
msgid "This password has been used previously. Please choose a different one."
|
||||
msgstr ""
|
||||
"Dieses Passwort wurde in Vergangenheit bereits verwendet. Bitte nutzen Sie "
|
||||
"ein anderes."
|
||||
|
||||
#: authentik/enterprise/policies/unique_password/models.py
|
||||
msgid "Password Uniqueness Policy"
|
||||
msgstr ""
|
||||
msgstr "Passwort-Einzigartigkeits-Richtlinie"
|
||||
|
||||
#: authentik/enterprise/policies/unique_password/models.py
|
||||
msgid "Password Uniqueness Policies"
|
||||
msgstr ""
|
||||
msgstr "Passwort-Einzigartigkeits-Richtlinien"
|
||||
|
||||
#: authentik/enterprise/policies/unique_password/models.py
|
||||
msgid "User Password History"
|
||||
msgstr ""
|
||||
msgstr "Nutzer-Passwort-Historie"
|
||||
|
||||
#: authentik/enterprise/policy.py
|
||||
msgid "Enterprise required to access this feature."
|
||||
@ -693,6 +701,33 @@ msgstr "Endgeräte"
|
||||
msgid "Verifying your browser..."
|
||||
msgstr "Verifiziere deinen Browser..."
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid ""
|
||||
"Configure certificate authorities to validate the certificate against. This "
|
||||
"option has a higher priority than the `client_certificate` option on "
|
||||
"`Brand`."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Mutual TLS Stage"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Mutual TLS Stages"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Permissions to pass Certificates for outposts."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/stage.py
|
||||
msgid "Certificate required but no certificate was given."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/stage.py
|
||||
msgid "No user found for certificate."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/source/models.py
|
||||
msgid ""
|
||||
"Amount of time a user can take to return from the source to continue the "
|
||||
@ -988,7 +1023,7 @@ msgstr ""
|
||||
|
||||
#: authentik/flows/models.py
|
||||
msgid "Evaluate policies when the Stage is presented to the user."
|
||||
msgstr ""
|
||||
msgstr "Richtlinien auswerten, wenn die Phase dem Benutzer angezeigt wird."
|
||||
|
||||
#: authentik/flows/models.py
|
||||
msgid ""
|
||||
@ -1043,9 +1078,12 @@ msgid "Starting full provider sync"
|
||||
msgstr "Starte komplette Provider Synchronisation."
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
#, python-brace-format
|
||||
msgid "Syncing page {page} of users"
|
||||
msgstr "Synchonisiere Benutzer Seite {page}"
|
||||
msgid "Syncing users"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
msgid "Syncing groups"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
#, python-brace-format
|
||||
@ -1593,11 +1631,11 @@ msgstr "ES256 (Asymmetrische Verschlüsselung)"
|
||||
|
||||
#: authentik/providers/oauth2/models.py
|
||||
msgid "ES384 (Asymmetric Encryption)"
|
||||
msgstr ""
|
||||
msgstr "ES384 (Asymmetrische Verschlüsselung)"
|
||||
|
||||
#: authentik/providers/oauth2/models.py
|
||||
msgid "ES512 (Asymmetric Encryption)"
|
||||
msgstr ""
|
||||
msgstr "ES5122 (Asymmetrische Verschlüsselung)"
|
||||
|
||||
#: authentik/providers/oauth2/models.py
|
||||
msgid "Scope used by the client"
|
||||
@ -2183,11 +2221,11 @@ msgstr "Standard"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "AWS"
|
||||
msgstr ""
|
||||
msgstr "AWS"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "Slack"
|
||||
msgstr ""
|
||||
msgstr "Slack"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "Base URL to SCIM requests, usually ends in /v2"
|
||||
@ -2199,7 +2237,7 @@ msgstr "Authentifizierungstoken"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "SCIM Compatibility Mode"
|
||||
msgstr ""
|
||||
msgstr "SCIM Kompatibilitätsmodus"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "Alter authentik behavior for vendor-specific SCIM implementations."
|
||||
@ -2231,7 +2269,7 @@ msgstr "Rollen"
|
||||
|
||||
#: authentik/rbac/models.py
|
||||
msgid "Initial Permissions"
|
||||
msgstr ""
|
||||
msgstr "Initiale Berechtigungen"
|
||||
|
||||
#: authentik/rbac/models.py
|
||||
msgid "System permission"
|
||||
@ -2487,6 +2525,12 @@ msgid ""
|
||||
"Active Directory"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid ""
|
||||
"Delete authentik users and groups which were previously supplied by this "
|
||||
"source, but are now missing from it."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "LDAP Source"
|
||||
msgstr "LDAP Quelle"
|
||||
@ -2504,20 +2548,25 @@ msgid "LDAP Source Property Mappings"
|
||||
msgstr "LDAP Quelle Eigenschafts-Zuordnungen"
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "User LDAP Source Connection"
|
||||
msgid ""
|
||||
"Unique ID used while checking if this object still exists in the directory."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "User LDAP Source Connection"
|
||||
msgstr "Benutzer LDAP-Quellverbindung"
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "User LDAP Source Connections"
|
||||
msgstr ""
|
||||
msgstr "Benutzer LDAP-Quellverbindungen"
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "Group LDAP Source Connection"
|
||||
msgstr ""
|
||||
msgstr "LDAP Gruppen Quellverbindung"
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "Group LDAP Source Connections"
|
||||
msgstr ""
|
||||
msgstr "LDAP Gruppen Quellverbindungen"
|
||||
|
||||
#: authentik/sources/ldap/signals.py
|
||||
msgid "Password does not match Active Directory Complexity."
|
||||
@ -2530,7 +2579,7 @@ msgstr "Kein Token empfangen."
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "HTTP Basic Authentication"
|
||||
msgstr ""
|
||||
msgstr "HTTP Basic Authentifizierung"
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "Include the client ID and secret as request parameters"
|
||||
@ -2896,6 +2945,11 @@ msgstr "SAML Gruppen Quellverbindung"
|
||||
msgid "Group SAML Source Connections"
|
||||
msgstr "SAML Gruppen Quellverbindungen"
|
||||
|
||||
#: authentik/sources/saml/views.py
|
||||
#, python-brace-format
|
||||
msgid "Continue to {source_name}"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/scim/models.py
|
||||
msgid "SCIM Source"
|
||||
msgstr "SCIM Quelle"
|
||||
@ -2930,7 +2984,7 @@ msgstr "Duo Geräte"
|
||||
|
||||
#: authentik/stages/authenticator_email/models.py
|
||||
msgid "Email OTP"
|
||||
msgstr ""
|
||||
msgstr "E-Mail Einmalpasswort"
|
||||
|
||||
#: authentik/stages/authenticator_email/models.py
|
||||
#: authentik/stages/email/models.py
|
||||
@ -2963,11 +3017,11 @@ msgstr "Beim Rendern der E-Mail-Vorlage ist ein Fehler aufgetreten"
|
||||
|
||||
#: authentik/stages/authenticator_email/models.py
|
||||
msgid "Email Device"
|
||||
msgstr ""
|
||||
msgstr "E-Mail Gerät"
|
||||
|
||||
#: authentik/stages/authenticator_email/models.py
|
||||
msgid "Email Devices"
|
||||
msgstr ""
|
||||
msgstr "E-Mail Geräte"
|
||||
|
||||
#: authentik/stages/authenticator_email/stage.py
|
||||
#: authentik/stages/authenticator_sms/stage.py
|
||||
@ -2977,7 +3031,7 @@ msgstr "Code stimmt nicht überein"
|
||||
|
||||
#: authentik/stages/authenticator_email/stage.py
|
||||
msgid "Invalid email"
|
||||
msgstr ""
|
||||
msgstr "Ungültige E-Mail"
|
||||
|
||||
#: authentik/stages/authenticator_email/templates/email/email_otp.html
|
||||
#: authentik/stages/email/templates/email/password_reset.html
|
||||
@ -3273,6 +3327,10 @@ msgstr "Zustimmung der Benutzer"
|
||||
msgid "User Consents"
|
||||
msgstr "Zustimmungen der Benutzer"
|
||||
|
||||
#: authentik/stages/consent/stage.py
|
||||
msgid "Invalid consent token, re-showing prompt"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/deny/models.py
|
||||
msgid "Deny Stage"
|
||||
msgstr "Verweigerungsstufe"
|
||||
@ -3289,6 +3347,14 @@ msgstr "Dummy Stufe"
|
||||
msgid "Dummy Stages"
|
||||
msgstr "Dummy Stufen"
|
||||
|
||||
#: authentik/stages/email/flow.py
|
||||
msgid "Continue to confirm this email address."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/flow.py
|
||||
msgid "Link was already used, please request a new link."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/models.py
|
||||
msgid "Password Reset"
|
||||
msgstr "Passwort zurücksetzen"
|
||||
@ -3890,10 +3956,11 @@ msgstr ""
|
||||
#: authentik/tenants/models.py
|
||||
msgid "Reputation cannot decrease lower than this value. Zero or negative."
|
||||
msgstr ""
|
||||
"Reputation kann nicht niedriger als dieser Wert sein. Null oder negativ."
|
||||
|
||||
#: authentik/tenants/models.py
|
||||
msgid "Reputation cannot increase higher than this value. Zero or positive."
|
||||
msgstr ""
|
||||
msgstr "Reputation kann nicht höher als dieser Wert sein. Null oder positiv."
|
||||
|
||||
#: authentik/tenants/models.py
|
||||
msgid "The option configures the footer links on the flow executor pages."
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-05-20 00:10+0000\n"
|
||||
"POT-Creation-Date: 2025-06-02 00:12+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"
|
||||
@ -961,8 +961,11 @@ msgid "Starting full provider sync"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
#, python-brace-format
|
||||
msgid "Syncing page {page} of users"
|
||||
msgid "Syncing users"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
msgid "Syncing groups"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
@ -2223,6 +2226,10 @@ msgstr ""
|
||||
msgid "Consider Objects matching this filter to be Users."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "Attribute which matches the value of `group_membership_field`."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "Field which contains members of a group."
|
||||
msgstr ""
|
||||
@ -2252,6 +2259,12 @@ msgid ""
|
||||
"Active Directory"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid ""
|
||||
"Delete authentik users and groups which were previously supplied by this "
|
||||
"source, but are now missing from it."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "LDAP Source"
|
||||
msgstr ""
|
||||
@ -2268,6 +2281,11 @@ msgstr ""
|
||||
msgid "LDAP Source Property Mappings"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid ""
|
||||
"Unique ID used while checking if this object still exists in the directory."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "User LDAP Source Connection"
|
||||
msgstr ""
|
||||
@ -2639,6 +2657,11 @@ msgstr ""
|
||||
msgid "Group SAML Source Connections"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/saml/views.py
|
||||
#, python-brace-format
|
||||
msgid "Continue to {source_name}"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/scim/models.py
|
||||
msgid "SCIM Source"
|
||||
msgstr ""
|
||||
@ -2994,6 +3017,10 @@ msgstr ""
|
||||
msgid "User Consents"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/consent/stage.py
|
||||
msgid "Invalid consent token, re-showing prompt"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/deny/models.py
|
||||
msgid "Deny Stage"
|
||||
msgstr ""
|
||||
@ -3010,6 +3037,14 @@ msgstr ""
|
||||
msgid "Dummy Stages"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/flow.py
|
||||
msgid "Continue to confirm this email address."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/flow.py
|
||||
msgid "Link was already used, please request a new link."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/models.py
|
||||
msgid "Password Reset"
|
||||
msgstr ""
|
||||
@ -3462,10 +3497,6 @@ msgstr ""
|
||||
msgid "No Pending user to login."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/user_login/stage.py
|
||||
msgid "Successfully logged in!"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/user_logout/models.py
|
||||
msgid "User Logout Stage"
|
||||
msgstr ""
|
||||
|
Binary file not shown.
@ -15,7 +15,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
|
||||
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: Jens L. <jens@goauthentik.io>, 2025\n"
|
||||
"Language-Team: Spanish (https://app.transifex.com/authentik/teams/119923/es/)\n"
|
||||
@ -109,6 +109,10 @@ msgstr ""
|
||||
msgid "Web Certificate used by the authentik Core webserver."
|
||||
msgstr "Certificado Web usado por el servidor web Core de authentik"
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Certificates used for client authentication."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Brand"
|
||||
msgstr "Marca"
|
||||
@ -671,6 +675,33 @@ msgstr "Dispositivos de Punto de Conexión"
|
||||
msgid "Verifying your browser..."
|
||||
msgstr "Verificando tu navegador..."
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid ""
|
||||
"Configure certificate authorities to validate the certificate against. This "
|
||||
"option has a higher priority than the `client_certificate` option on "
|
||||
"`Brand`."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Mutual TLS Stage"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Mutual TLS Stages"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Permissions to pass Certificates for outposts."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/stage.py
|
||||
msgid "Certificate required but no certificate was given."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/stage.py
|
||||
msgid "No user found for certificate."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/source/models.py
|
||||
msgid ""
|
||||
"Amount of time a user can take to return from the source to continue the "
|
||||
@ -1009,9 +1040,12 @@ msgid "Starting full provider sync"
|
||||
msgstr "Iniciando sincronización completa de proveedor"
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
#, python-brace-format
|
||||
msgid "Syncing page {page} of users"
|
||||
msgstr "Sincronizando página {page} de usuarios"
|
||||
msgid "Syncing users"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
msgid "Syncing groups"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
#, python-brace-format
|
||||
@ -2452,6 +2486,12 @@ msgid ""
|
||||
"Active Directory"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid ""
|
||||
"Delete authentik users and groups which were previously supplied by this "
|
||||
"source, but are now missing from it."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "LDAP Source"
|
||||
msgstr "Fuente de LDAP"
|
||||
@ -2468,6 +2508,11 @@ msgstr "Asignación de Propiedades de Fuente de LDAP"
|
||||
msgid "LDAP Source Property Mappings"
|
||||
msgstr "Asignaciones de Propiedades de Fuente de LDAP"
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid ""
|
||||
"Unique ID used while checking if this object still exists in the directory."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "User LDAP Source Connection"
|
||||
msgstr ""
|
||||
@ -2859,6 +2904,11 @@ msgstr "Conexión de Fuente de SAML de Grupo"
|
||||
msgid "Group SAML Source Connections"
|
||||
msgstr "Conexiones de Fuente de SAML de Grupo"
|
||||
|
||||
#: authentik/sources/saml/views.py
|
||||
#, python-brace-format
|
||||
msgid "Continue to {source_name}"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/scim/models.py
|
||||
msgid "SCIM Source"
|
||||
msgstr "Fuente de SCIM"
|
||||
@ -3245,6 +3295,10 @@ msgstr "Consentimiento del usuario"
|
||||
msgid "User Consents"
|
||||
msgstr "Consentimientos del usuario"
|
||||
|
||||
#: authentik/stages/consent/stage.py
|
||||
msgid "Invalid consent token, re-showing prompt"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/deny/models.py
|
||||
msgid "Deny Stage"
|
||||
msgstr "Etapa de denegación"
|
||||
@ -3261,6 +3315,14 @@ msgstr "Escenario ficticio"
|
||||
msgid "Dummy Stages"
|
||||
msgstr "Etapas ficticias"
|
||||
|
||||
#: authentik/stages/email/flow.py
|
||||
msgid "Continue to confirm this email address."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/flow.py
|
||||
msgid "Link was already used, please request a new link."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/models.py
|
||||
msgid "Password Reset"
|
||||
msgstr "Restablecimiento de contraseña"
|
||||
|
Binary file not shown.
@ -15,7 +15,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
|
||||
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: Ville Ranki, 2025\n"
|
||||
"Language-Team: Finnish (https://app.transifex.com/authentik/teams/119923/fi/)\n"
|
||||
@ -106,6 +106,10 @@ msgstr ""
|
||||
msgid "Web Certificate used by the authentik Core webserver."
|
||||
msgstr "Web-sertifikaatti, jota authentik Core -verkkopalvelin käyttää."
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Certificates used for client authentication."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Brand"
|
||||
msgstr "Brändi"
|
||||
@ -658,6 +662,33 @@ msgstr "Päätelaitteet"
|
||||
msgid "Verifying your browser..."
|
||||
msgstr "Selaintasi varmennetaan..."
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid ""
|
||||
"Configure certificate authorities to validate the certificate against. This "
|
||||
"option has a higher priority than the `client_certificate` option on "
|
||||
"`Brand`."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Mutual TLS Stage"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Mutual TLS Stages"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Permissions to pass Certificates for outposts."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/stage.py
|
||||
msgid "Certificate required but no certificate was given."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/stage.py
|
||||
msgid "No user found for certificate."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/source/models.py
|
||||
msgid ""
|
||||
"Amount of time a user can take to return from the source to continue the "
|
||||
@ -996,9 +1027,12 @@ msgid "Starting full provider sync"
|
||||
msgstr "Käynnistetään palveluntarjoajan täysi synkronisointi"
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
#, python-brace-format
|
||||
msgid "Syncing page {page} of users"
|
||||
msgstr "Synkronoidaan käyttäjien sivua {page}"
|
||||
msgid "Syncing users"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
msgid "Syncing groups"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
#, python-brace-format
|
||||
@ -2429,6 +2463,12 @@ msgid ""
|
||||
"Active Directory"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid ""
|
||||
"Delete authentik users and groups which were previously supplied by this "
|
||||
"source, but are now missing from it."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "LDAP Source"
|
||||
msgstr "LDAP-lähde"
|
||||
@ -2445,6 +2485,11 @@ msgstr "LDAP-lähteen ominaisuuskytkentä"
|
||||
msgid "LDAP Source Property Mappings"
|
||||
msgstr "LDAP-lähteen ominaisuuskytkennät"
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid ""
|
||||
"Unique ID used while checking if this object still exists in the directory."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "User LDAP Source Connection"
|
||||
msgstr ""
|
||||
@ -2837,6 +2882,11 @@ msgstr "Ryhmän SAML-lähteen yhteys"
|
||||
msgid "Group SAML Source Connections"
|
||||
msgstr "Ryhmän SAML-lähteen yhteydet"
|
||||
|
||||
#: authentik/sources/saml/views.py
|
||||
#, python-brace-format
|
||||
msgid "Continue to {source_name}"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/scim/models.py
|
||||
msgid "SCIM Source"
|
||||
msgstr "SCIM-lähde"
|
||||
@ -3216,6 +3266,10 @@ msgstr "Käyttäjän hyväksyntä"
|
||||
msgid "User Consents"
|
||||
msgstr "Käyttäjän hyväksynnät"
|
||||
|
||||
#: authentik/stages/consent/stage.py
|
||||
msgid "Invalid consent token, re-showing prompt"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/deny/models.py
|
||||
msgid "Deny Stage"
|
||||
msgstr "Kieltovaihe"
|
||||
@ -3232,6 +3286,14 @@ msgstr "Valevaihe"
|
||||
msgid "Dummy Stages"
|
||||
msgstr "Valevaiheet"
|
||||
|
||||
#: authentik/stages/email/flow.py
|
||||
msgid "Continue to confirm this email address."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/flow.py
|
||||
msgid "Link was already used, please request a new link."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/models.py
|
||||
msgid "Password Reset"
|
||||
msgstr "Salasanan nollaus"
|
||||
|
@ -19,7 +19,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-05-20 00:10+0000\n"
|
||||
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: Marc Schmitt, 2025\n"
|
||||
"Language-Team: French (https://app.transifex.com/authentik/teams/119923/fr/)\n"
|
||||
@ -1056,9 +1056,12 @@ msgid "Starting full provider sync"
|
||||
msgstr "Démarrage d'une synchronisation complète du fournisseur"
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
#, python-brace-format
|
||||
msgid "Syncing page {page} of users"
|
||||
msgstr "Synchronisation de la page {page} d'utilisateurs"
|
||||
msgid "Syncing users"
|
||||
msgstr "Synchronisation des utilisateurs"
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
msgid "Syncing groups"
|
||||
msgstr "Synchronisation des groupes"
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
#, python-brace-format
|
||||
@ -2508,6 +2511,14 @@ msgstr ""
|
||||
"plutôt que sur un attribut de groupe. Cela permet la résolution des groupes "
|
||||
"imbriqués sur des systèmes tels que FreeIPA et Active Directory."
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid ""
|
||||
"Delete authentik users and groups which were previously supplied by this "
|
||||
"source, but are now missing from it."
|
||||
msgstr ""
|
||||
"Supprimer les utilisateurs et les groupes authentik qui étaient auparavant "
|
||||
"fournis par cette source, mais qui en sont maintenant absents."
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "LDAP Source"
|
||||
msgstr "Source LDAP"
|
||||
@ -2524,6 +2535,13 @@ msgstr "Mappage de propriété source LDAP"
|
||||
msgid "LDAP Source Property Mappings"
|
||||
msgstr "Mappages de propriété source LDAP"
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid ""
|
||||
"Unique ID used while checking if this object still exists in the directory."
|
||||
msgstr ""
|
||||
"ID unique utilisé pour vérifier si cet objet existe toujours dans le "
|
||||
"répertoire."
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "User LDAP Source Connection"
|
||||
msgstr "Connexion de l'utilisateur à la source LDAP"
|
||||
@ -2918,6 +2936,11 @@ msgstr "Connexion du groupe à la source SAML"
|
||||
msgid "Group SAML Source Connections"
|
||||
msgstr "Connexions du groupe à la source SAML"
|
||||
|
||||
#: authentik/sources/saml/views.py
|
||||
#, python-brace-format
|
||||
msgid "Continue to {source_name}"
|
||||
msgstr "Continuer vers {source_name}"
|
||||
|
||||
#: authentik/sources/scim/models.py
|
||||
msgid "SCIM Source"
|
||||
msgstr "Source SCIM"
|
||||
@ -3308,6 +3331,10 @@ msgstr "Consentement Utilisateur"
|
||||
msgid "User Consents"
|
||||
msgstr "Consentements Utilisateur"
|
||||
|
||||
#: authentik/stages/consent/stage.py
|
||||
msgid "Invalid consent token, re-showing prompt"
|
||||
msgstr "Jeton de consentement invalide, réaffichage de l'invite"
|
||||
|
||||
#: authentik/stages/deny/models.py
|
||||
msgid "Deny Stage"
|
||||
msgstr "Étape de Refus"
|
||||
@ -3324,6 +3351,14 @@ msgstr "Étape factice"
|
||||
msgid "Dummy Stages"
|
||||
msgstr "Étapes factices"
|
||||
|
||||
#: authentik/stages/email/flow.py
|
||||
msgid "Continue to confirm this email address."
|
||||
msgstr "Continuer pour confirmer cette adresse courriel."
|
||||
|
||||
#: authentik/stages/email/flow.py
|
||||
msgid "Link was already used, please request a new link."
|
||||
msgstr "Ce lien a déjà été utilisé, veuillez en demander un nouveau."
|
||||
|
||||
#: authentik/stages/email/models.py
|
||||
msgid "Password Reset"
|
||||
msgstr "Réinitialiser le Mot de Passe"
|
||||
|
@ -20,7 +20,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
|
||||
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: Kowalski Dragon (kowalski7cc) <kowalski.7cc@gmail.com>, 2025\n"
|
||||
"Language-Team: Italian (https://app.transifex.com/authentik/teams/119923/it/)\n"
|
||||
@ -114,6 +114,10 @@ msgstr ""
|
||||
msgid "Web Certificate used by the authentik Core webserver."
|
||||
msgstr "Certificato Web utilizzato dal server Web authentik Core."
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Certificates used for client authentication."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Brand"
|
||||
msgstr "Brand"
|
||||
@ -672,6 +676,33 @@ msgstr "Dispositivi di Accesso"
|
||||
msgid "Verifying your browser..."
|
||||
msgstr "Verifica del tuo browser..."
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid ""
|
||||
"Configure certificate authorities to validate the certificate against. This "
|
||||
"option has a higher priority than the `client_certificate` option on "
|
||||
"`Brand`."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Mutual TLS Stage"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Mutual TLS Stages"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Permissions to pass Certificates for outposts."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/stage.py
|
||||
msgid "Certificate required but no certificate was given."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/stage.py
|
||||
msgid "No user found for certificate."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/source/models.py
|
||||
msgid ""
|
||||
"Amount of time a user can take to return from the source to continue the "
|
||||
@ -1018,9 +1049,12 @@ msgid "Starting full provider sync"
|
||||
msgstr "Avvio della sincronizzazione completa del provider"
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
#, python-brace-format
|
||||
msgid "Syncing page {page} of users"
|
||||
msgstr "Sincronizzando pagina {page} degli utenti"
|
||||
msgid "Syncing users"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
msgid "Syncing groups"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
#, python-brace-format
|
||||
@ -2463,6 +2497,12 @@ msgstr ""
|
||||
"attributo di gruppo. Questo consente la risoluzione di gruppi nidificati su "
|
||||
"sistemi come FreeIPA e Active Directory."
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid ""
|
||||
"Delete authentik users and groups which were previously supplied by this "
|
||||
"source, but are now missing from it."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "LDAP Source"
|
||||
msgstr "Sorgente LDAP"
|
||||
@ -2479,6 +2519,11 @@ msgstr "Mappatura delle proprietà sorgente LDAP"
|
||||
msgid "LDAP Source Property Mappings"
|
||||
msgstr "Mappature delle proprietà della sorgente LDAP"
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid ""
|
||||
"Unique ID used while checking if this object still exists in the directory."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "User LDAP Source Connection"
|
||||
msgstr "Connessione Sorgente LDAP Utente"
|
||||
@ -2872,6 +2917,11 @@ msgstr "Connessione sorgente SAML di gruppo"
|
||||
msgid "Group SAML Source Connections"
|
||||
msgstr "Connessioni sorgente SAML di gruppo"
|
||||
|
||||
#: authentik/sources/saml/views.py
|
||||
#, python-brace-format
|
||||
msgid "Continue to {source_name}"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/scim/models.py
|
||||
msgid "SCIM Source"
|
||||
msgstr "Sorgente SCIM"
|
||||
@ -3269,6 +3319,10 @@ msgstr "Consenso utente"
|
||||
msgid "User Consents"
|
||||
msgstr "Consensi utente"
|
||||
|
||||
#: authentik/stages/consent/stage.py
|
||||
msgid "Invalid consent token, re-showing prompt"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/deny/models.py
|
||||
msgid "Deny Stage"
|
||||
msgstr "Fase di negazione"
|
||||
@ -3285,6 +3339,14 @@ msgstr "Fase fittizia"
|
||||
msgid "Dummy Stages"
|
||||
msgstr "Fasi fittizie"
|
||||
|
||||
#: authentik/stages/email/flow.py
|
||||
msgid "Continue to confirm this email address."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/flow.py
|
||||
msgid "Link was already used, please request a new link."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/models.py
|
||||
msgid "Password Reset"
|
||||
msgstr "Ripristino password"
|
||||
|
@ -12,7 +12,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
|
||||
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: NavyStack, 2023\n"
|
||||
"Language-Team: Korean (https://app.transifex.com/authentik/teams/119923/ko/)\n"
|
||||
@ -99,6 +99,10 @@ msgstr ""
|
||||
msgid "Web Certificate used by the authentik Core webserver."
|
||||
msgstr "Authentik Core 웹서버에서 사용하는 웹 인증서."
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Certificates used for client authentication."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Brand"
|
||||
msgstr ""
|
||||
@ -625,6 +629,33 @@ msgstr ""
|
||||
msgid "Verifying your browser..."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid ""
|
||||
"Configure certificate authorities to validate the certificate against. This "
|
||||
"option has a higher priority than the `client_certificate` option on "
|
||||
"`Brand`."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Mutual TLS Stage"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Mutual TLS Stages"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Permissions to pass Certificates for outposts."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/stage.py
|
||||
msgid "Certificate required but no certificate was given."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/stage.py
|
||||
msgid "No user found for certificate."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/source/models.py
|
||||
msgid ""
|
||||
"Amount of time a user can take to return from the source to continue the "
|
||||
@ -946,8 +977,11 @@ msgid "Starting full provider sync"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
#, python-brace-format
|
||||
msgid "Syncing page {page} of users"
|
||||
msgid "Syncing users"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
msgid "Syncing groups"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
@ -2263,6 +2297,12 @@ msgid ""
|
||||
"Active Directory"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid ""
|
||||
"Delete authentik users and groups which were previously supplied by this "
|
||||
"source, but are now missing from it."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "LDAP Source"
|
||||
msgstr "LDAP 소스"
|
||||
@ -2279,6 +2319,11 @@ msgstr ""
|
||||
msgid "LDAP Source Property Mappings"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid ""
|
||||
"Unique ID used while checking if this object still exists in the directory."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "User LDAP Source Connection"
|
||||
msgstr ""
|
||||
@ -2657,6 +2702,11 @@ msgstr ""
|
||||
msgid "Group SAML Source Connections"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/saml/views.py
|
||||
#, python-brace-format
|
||||
msgid "Continue to {source_name}"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/scim/models.py
|
||||
msgid "SCIM Source"
|
||||
msgstr ""
|
||||
@ -3017,6 +3067,10 @@ msgstr "사용자 동의"
|
||||
msgid "User Consents"
|
||||
msgstr "사용자 동의"
|
||||
|
||||
#: authentik/stages/consent/stage.py
|
||||
msgid "Invalid consent token, re-showing prompt"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/deny/models.py
|
||||
msgid "Deny Stage"
|
||||
msgstr "거부 스테이지"
|
||||
@ -3033,6 +3087,14 @@ msgstr "더미 스테이지"
|
||||
msgid "Dummy Stages"
|
||||
msgstr "더미 스테이지"
|
||||
|
||||
#: authentik/stages/email/flow.py
|
||||
msgid "Continue to confirm this email address."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/flow.py
|
||||
msgid "Link was already used, please request a new link."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/models.py
|
||||
msgid "Password Reset"
|
||||
msgstr "비밀번호 초기화"
|
||||
|
Binary file not shown.
@ -19,7 +19,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-04-11 00:10+0000\n"
|
||||
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: Dany Sluijk, 2025\n"
|
||||
"Language-Team: Dutch (https://app.transifex.com/authentik/teams/119923/nl/)\n"
|
||||
@ -113,6 +113,10 @@ msgstr ""
|
||||
msgid "Web Certificate used by the authentik Core webserver."
|
||||
msgstr "Webcertificaat gebruikt door de authentik Core-webserver."
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Certificates used for client authentication."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Brand"
|
||||
msgstr "Merk"
|
||||
@ -191,6 +195,7 @@ msgid "User's display name."
|
||||
msgstr "Weergavenaam van de gebruiker."
|
||||
|
||||
#: authentik/core/models.py authentik/providers/oauth2/models.py
|
||||
#: authentik/rbac/models.py
|
||||
msgid "User"
|
||||
msgstr "Gebruiker"
|
||||
|
||||
@ -379,6 +384,18 @@ msgstr "Eigenschapskoppeling"
|
||||
msgid "Property Mappings"
|
||||
msgstr "Eigenschapskoppelingen"
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid "session data"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid "Session"
|
||||
msgstr "Sessie"
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid "Sessions"
|
||||
msgstr "Sessies"
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid "Authenticated Session"
|
||||
msgstr "Geauthenticeerde Sessie"
|
||||
@ -486,6 +503,38 @@ msgstr "Licentie Gebruik"
|
||||
msgid "License Usage Records"
|
||||
msgstr "Licentie Gebruik Records"
|
||||
|
||||
#: authentik/enterprise/policies/unique_password/models.py
|
||||
#: authentik/policies/password/models.py
|
||||
msgid "Field key to check, field keys defined in Prompt stages are available."
|
||||
msgstr ""
|
||||
"Veldsleutel om te controleren, veldsleutels gedefinieerd in Prompt-stadia "
|
||||
"zijn beschikbaar."
|
||||
|
||||
#: authentik/enterprise/policies/unique_password/models.py
|
||||
msgid "Number of passwords to check against."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/policies/unique_password/models.py
|
||||
#: authentik/policies/password/models.py
|
||||
msgid "Password not set in context"
|
||||
msgstr "Wachtwoord niet ingesteld in context"
|
||||
|
||||
#: authentik/enterprise/policies/unique_password/models.py
|
||||
msgid "This password has been used previously. Please choose a different one."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/policies/unique_password/models.py
|
||||
msgid "Password Uniqueness Policy"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/policies/unique_password/models.py
|
||||
msgid "Password Uniqueness Policies"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/policies/unique_password/models.py
|
||||
msgid "User Password History"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/policy.py
|
||||
msgid "Enterprise required to access this feature."
|
||||
msgstr "Enterprise benodigd voor toegang tot deze functie."
|
||||
@ -622,6 +671,33 @@ msgstr ""
|
||||
msgid "Verifying your browser..."
|
||||
msgstr "Uw browser wordt geverifieerd..."
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid ""
|
||||
"Configure certificate authorities to validate the certificate against. This "
|
||||
"option has a higher priority than the `client_certificate` option on "
|
||||
"`Brand`."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Mutual TLS Stage"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Mutual TLS Stages"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Permissions to pass Certificates for outposts."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/stage.py
|
||||
msgid "Certificate required but no certificate was given."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/stage.py
|
||||
msgid "No user found for certificate."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/source/models.py
|
||||
msgid ""
|
||||
"Amount of time a user can take to return from the source to continue the "
|
||||
@ -963,8 +1039,11 @@ msgid "Starting full provider sync"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
#, python-brace-format
|
||||
msgid "Syncing page {page} of users"
|
||||
msgid "Syncing users"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
msgid "Syncing groups"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
@ -1265,12 +1344,6 @@ msgstr ""
|
||||
msgid "Clear Policy's cache metrics"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/policies/password/models.py
|
||||
msgid "Field key to check, field keys defined in Prompt stages are available."
|
||||
msgstr ""
|
||||
"Veldsleutel om te controleren, veldsleutels gedefinieerd in Prompt-stadia "
|
||||
"zijn beschikbaar."
|
||||
|
||||
#: authentik/policies/password/models.py
|
||||
msgid "How many times the password hash is allowed to be on haveibeenpwned"
|
||||
msgstr "Hoe vaak het wachtwoordhash op haveibeenpwned mag voorkomen"
|
||||
@ -1282,10 +1355,6 @@ msgstr ""
|
||||
"Als de zxcvbn-score gelijk is aan of lager is dan deze waarde, zal het "
|
||||
"beleid falen."
|
||||
|
||||
#: authentik/policies/password/models.py
|
||||
msgid "Password not set in context"
|
||||
msgstr "Wachtwoord niet ingesteld in context"
|
||||
|
||||
#: authentik/policies/password/models.py
|
||||
msgid "Invalid password."
|
||||
msgstr ""
|
||||
@ -1327,20 +1396,6 @@ msgstr "Reputatie Score"
|
||||
msgid "Reputation Scores"
|
||||
msgstr "Reputatie Scores"
|
||||
|
||||
#: authentik/policies/templates/policies/buffer.html
|
||||
msgid "Waiting for authentication..."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/policies/templates/policies/buffer.html
|
||||
msgid ""
|
||||
"You're already authenticating in another tab. This page will refresh once "
|
||||
"authentication is completed."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/policies/templates/policies/buffer.html
|
||||
msgid "Authenticate in this tab"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/policies/templates/policies/denied.html
|
||||
msgid "Permission denied"
|
||||
msgstr "Toestemming geweigerd"
|
||||
@ -2160,6 +2215,10 @@ msgstr ""
|
||||
msgid "Roles"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/rbac/models.py
|
||||
msgid "Initial Permissions"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/rbac/models.py
|
||||
msgid "System permission"
|
||||
msgstr ""
|
||||
@ -2392,6 +2451,12 @@ msgid ""
|
||||
"Active Directory"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid ""
|
||||
"Delete authentik users and groups which were previously supplied by this "
|
||||
"source, but are now missing from it."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "LDAP Source"
|
||||
msgstr "LDAP-bron"
|
||||
@ -2408,6 +2473,27 @@ msgstr ""
|
||||
msgid "LDAP Source Property Mappings"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid ""
|
||||
"Unique ID used while checking if this object still exists in the directory."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "User LDAP Source Connection"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "User LDAP Source Connections"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "Group LDAP Source Connection"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "Group LDAP Source Connections"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/signals.py
|
||||
msgid "Password does not match Active Directory Complexity."
|
||||
msgstr ""
|
||||
@ -2417,6 +2503,14 @@ msgstr ""
|
||||
msgid "No token received."
|
||||
msgstr "Geen token ontvangen."
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "HTTP Basic Authentication"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "Include the client ID and secret as request parameters"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "Request Token URL"
|
||||
msgstr "URL voor aanvragen van token"
|
||||
@ -2458,6 +2552,12 @@ msgstr ""
|
||||
msgid "Additional Scopes"
|
||||
msgstr "Aanvullende scopes"
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid ""
|
||||
"How to perform authentication during an authorization_code token request "
|
||||
"flow"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "OAuth Source"
|
||||
msgstr "OAuth-bron"
|
||||
@ -2769,6 +2869,11 @@ msgstr ""
|
||||
msgid "Group SAML Source Connections"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/saml/views.py
|
||||
#, python-brace-format
|
||||
msgid "Continue to {source_name}"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/scim/models.py
|
||||
msgid "SCIM Source"
|
||||
msgstr ""
|
||||
@ -3142,6 +3247,10 @@ msgstr "Gebruikerstoestemming"
|
||||
msgid "User Consents"
|
||||
msgstr "Gebruikersinstemmingen"
|
||||
|
||||
#: authentik/stages/consent/stage.py
|
||||
msgid "Invalid consent token, re-showing prompt"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/deny/models.py
|
||||
msgid "Deny Stage"
|
||||
msgstr "Weigerfase"
|
||||
@ -3158,6 +3267,14 @@ msgstr "Dummystadium"
|
||||
msgid "Dummy Stages"
|
||||
msgstr "Dummystadia"
|
||||
|
||||
#: authentik/stages/email/flow.py
|
||||
msgid "Continue to confirm this email address."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/flow.py
|
||||
msgid "Link was already used, please request a new link."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/models.py
|
||||
msgid "Password Reset"
|
||||
msgstr "Wachtwoordherstel"
|
||||
@ -3357,6 +3474,12 @@ msgstr ""
|
||||
"Wanneer ingeschakeld, slaagt de stap en gaat verder wanneer ongeldige "
|
||||
"gebruikersgegevens zijn ingevoerd."
|
||||
|
||||
#: authentik/stages/identification/models.py
|
||||
msgid ""
|
||||
"Show the user the 'Remember me on this device' toggle, allowing repeat users"
|
||||
" to skip straight to entering their password."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/identification/models.py
|
||||
msgid "Optional enrollment flow, which is linked at the bottom of the page."
|
||||
msgstr "Optionele inschrijvingsflow, die onderaan de pagina is gekoppeld."
|
||||
@ -3742,6 +3865,14 @@ msgstr ""
|
||||
"Gebeurtenissen worden verwijderd na deze duur. (Indeling: "
|
||||
"weken=3;dagen=2;uren=3;seconden=2)."
|
||||
|
||||
#: authentik/tenants/models.py
|
||||
msgid "Reputation cannot decrease lower than this value. Zero or negative."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/tenants/models.py
|
||||
msgid "Reputation cannot increase higher than this value. Zero or positive."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/tenants/models.py
|
||||
msgid "The option configures the footer links on the flow executor pages."
|
||||
msgstr "De optie stelt de voettekst links in op de flow uitvoer pagina's."
|
||||
|
@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
|
||||
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: Hugo Bicho, 2025\n"
|
||||
"Language-Team: Portuguese (https://app.transifex.com/authentik/teams/119923/pt/)\n"
|
||||
@ -105,6 +105,10 @@ msgstr ""
|
||||
msgid "Web Certificate used by the authentik Core webserver."
|
||||
msgstr "Certificado Web usado pelo servidor web authentik Core."
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Certificates used for client authentication."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Brand"
|
||||
msgstr "Marca"
|
||||
@ -662,6 +666,33 @@ msgstr "Dispositivos do ponto de ligação"
|
||||
msgid "Verifying your browser..."
|
||||
msgstr "A verificar o seu browser..."
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid ""
|
||||
"Configure certificate authorities to validate the certificate against. This "
|
||||
"option has a higher priority than the `client_certificate` option on "
|
||||
"`Brand`."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Mutual TLS Stage"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Mutual TLS Stages"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Permissions to pass Certificates for outposts."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/stage.py
|
||||
msgid "Certificate required but no certificate was given."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/stage.py
|
||||
msgid "No user found for certificate."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/source/models.py
|
||||
msgid ""
|
||||
"Amount of time a user can take to return from the source to continue the "
|
||||
@ -1007,9 +1038,12 @@ msgid "Starting full provider sync"
|
||||
msgstr "Iniciando a sincronização completa com o provedor"
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
#, python-brace-format
|
||||
msgid "Syncing page {page} of users"
|
||||
msgstr "A sincronizar a página {page} dos utilizadores"
|
||||
msgid "Syncing users"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
msgid "Syncing groups"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
#, python-brace-format
|
||||
@ -2456,6 +2490,12 @@ msgstr ""
|
||||
" um atributo do grupo. Isto permite a resolução de grupos hierárquicos em "
|
||||
"sistemas como o FreeIPA e Active Directory."
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid ""
|
||||
"Delete authentik users and groups which were previously supplied by this "
|
||||
"source, but are now missing from it."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "LDAP Source"
|
||||
msgstr "Fonte LDAP"
|
||||
@ -2472,6 +2512,11 @@ msgstr "Mapeamento de propriedades de fonte LDAP"
|
||||
msgid "LDAP Source Property Mappings"
|
||||
msgstr "Mapeamentos de propriedades de fonte LDAP"
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid ""
|
||||
"Unique ID used while checking if this object still exists in the directory."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "User LDAP Source Connection"
|
||||
msgstr "Ligação à fonte LDAP de Utilizador"
|
||||
@ -2865,6 +2910,11 @@ msgstr "Ligação à fonte SAML de Grupo"
|
||||
msgid "Group SAML Source Connections"
|
||||
msgstr "Ligações à fonte SAML de Grupo"
|
||||
|
||||
#: authentik/sources/saml/views.py
|
||||
#, python-brace-format
|
||||
msgid "Continue to {source_name}"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/scim/models.py
|
||||
msgid "SCIM Source"
|
||||
msgstr "Fonte SCIM"
|
||||
@ -3255,6 +3305,10 @@ msgstr "Consentimento do Utilizador"
|
||||
msgid "User Consents"
|
||||
msgstr "Consentimentos do Utilizador"
|
||||
|
||||
#: authentik/stages/consent/stage.py
|
||||
msgid "Invalid consent token, re-showing prompt"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/deny/models.py
|
||||
msgid "Deny Stage"
|
||||
msgstr "Etapa de negação"
|
||||
@ -3271,6 +3325,14 @@ msgstr "Etapa fictícia"
|
||||
msgid "Dummy Stages"
|
||||
msgstr "Etapas fictícias"
|
||||
|
||||
#: authentik/stages/email/flow.py
|
||||
msgid "Continue to confirm this email address."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/flow.py
|
||||
msgid "Link was already used, please request a new link."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/models.py
|
||||
msgid "Password Reset"
|
||||
msgstr "Redefinição de Palavra-Passe"
|
||||
|
Binary file not shown.
@ -8,19 +8,19 @@
|
||||
# Josenivaldo Benito Junior, 2023
|
||||
# Caio Lima, 2023
|
||||
# Hacklab, 2023
|
||||
# Wagner Santos, 2024
|
||||
# Rafael Mundel, 2024
|
||||
# Anderson Silva Andrade <anderson.asa89@gmail.com>, 2025
|
||||
# Gil Poiares-Oliveira, 2025
|
||||
# Wagner Santos, 2025
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
|
||||
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: Gil Poiares-Oliveira, 2025\n"
|
||||
"Last-Translator: Wagner Santos, 2025\n"
|
||||
"Language-Team: Portuguese (Brazil) (https://app.transifex.com/authentik/teams/119923/pt_BR/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -112,6 +112,10 @@ msgstr ""
|
||||
msgid "Web Certificate used by the authentik Core webserver."
|
||||
msgstr "Certificado da Web usado pelo servidor da web authentik Core."
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Certificates used for client authentication."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Brand"
|
||||
msgstr "Brand"
|
||||
@ -271,11 +275,11 @@ msgstr "Aplicativos"
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid "Application Entitlement"
|
||||
msgstr ""
|
||||
msgstr "Autorização de aplicação"
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid "Application Entitlements"
|
||||
msgstr ""
|
||||
msgstr "Autorizações de aplicação"
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid "Use the source-specific identifier"
|
||||
@ -379,15 +383,15 @@ msgstr "Mapeamentos de propriedades"
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid "session data"
|
||||
msgstr ""
|
||||
msgstr "dados de sessão"
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid "Session"
|
||||
msgstr ""
|
||||
msgstr "Sessão"
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid "Sessions"
|
||||
msgstr ""
|
||||
msgstr "Sessões"
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid "Authenticated Session"
|
||||
@ -505,7 +509,7 @@ msgstr ""
|
||||
|
||||
#: authentik/enterprise/policies/unique_password/models.py
|
||||
msgid "Number of passwords to check against."
|
||||
msgstr ""
|
||||
msgstr "Número de senhas para verificar."
|
||||
|
||||
#: authentik/enterprise/policies/unique_password/models.py
|
||||
#: authentik/policies/password/models.py
|
||||
@ -514,19 +518,19 @@ msgstr "Senha não definida no contexto"
|
||||
|
||||
#: authentik/enterprise/policies/unique_password/models.py
|
||||
msgid "This password has been used previously. Please choose a different one."
|
||||
msgstr ""
|
||||
msgstr "A senha já foi utilizada antes. Por favor, escolha uma diferente."
|
||||
|
||||
#: authentik/enterprise/policies/unique_password/models.py
|
||||
msgid "Password Uniqueness Policy"
|
||||
msgstr ""
|
||||
msgstr "Política de exclusividade de senha"
|
||||
|
||||
#: authentik/enterprise/policies/unique_password/models.py
|
||||
msgid "Password Uniqueness Policies"
|
||||
msgstr ""
|
||||
msgstr "Políticas de exclusividade de senha"
|
||||
|
||||
#: authentik/enterprise/policies/unique_password/models.py
|
||||
msgid "User Password History"
|
||||
msgstr ""
|
||||
msgstr "Histórico de senhas do usuário"
|
||||
|
||||
#: authentik/enterprise/policy.py
|
||||
msgid "Enterprise required to access this feature."
|
||||
@ -610,39 +614,39 @@ msgstr "Chave de Assinatura"
|
||||
|
||||
#: authentik/enterprise/providers/ssf/models.py
|
||||
msgid "Key used to sign the SSF Events."
|
||||
msgstr ""
|
||||
msgstr "Chave utilizada para assinar os eventos SSF."
|
||||
|
||||
#: authentik/enterprise/providers/ssf/models.py
|
||||
msgid "Shared Signals Framework Provider"
|
||||
msgstr ""
|
||||
msgstr "Provedor de Shared Signals Framework"
|
||||
|
||||
#: authentik/enterprise/providers/ssf/models.py
|
||||
msgid "Shared Signals Framework Providers"
|
||||
msgstr ""
|
||||
msgstr "Provedores de Shared Signals Framework"
|
||||
|
||||
#: authentik/enterprise/providers/ssf/models.py
|
||||
msgid "Add stream to SSF provider"
|
||||
msgstr ""
|
||||
msgstr "Adicionar stream ao fornecedor SSF"
|
||||
|
||||
#: authentik/enterprise/providers/ssf/models.py
|
||||
msgid "SSF Stream"
|
||||
msgstr ""
|
||||
msgstr "Stream SSF"
|
||||
|
||||
#: authentik/enterprise/providers/ssf/models.py
|
||||
msgid "SSF Streams"
|
||||
msgstr ""
|
||||
msgstr "Streams SSF"
|
||||
|
||||
#: authentik/enterprise/providers/ssf/models.py
|
||||
msgid "SSF Stream Event"
|
||||
msgstr ""
|
||||
msgstr "Evento de stream SSF"
|
||||
|
||||
#: authentik/enterprise/providers/ssf/models.py
|
||||
msgid "SSF Stream Events"
|
||||
msgstr ""
|
||||
msgstr "Eventos de stream SSF"
|
||||
|
||||
#: authentik/enterprise/providers/ssf/tasks.py
|
||||
msgid "Failed to send request"
|
||||
msgstr ""
|
||||
msgstr "Falha ao enviar requisição"
|
||||
|
||||
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/models.py
|
||||
msgid "Endpoint Authenticator Google Device Trust Connector Stage"
|
||||
@ -664,6 +668,33 @@ msgstr ""
|
||||
msgid "Verifying your browser..."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid ""
|
||||
"Configure certificate authorities to validate the certificate against. This "
|
||||
"option has a higher priority than the `client_certificate` option on "
|
||||
"`Brand`."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Mutual TLS Stage"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Mutual TLS Stages"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Permissions to pass Certificates for outposts."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/stage.py
|
||||
msgid "Certificate required but no certificate was given."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/stage.py
|
||||
msgid "No user found for certificate."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/source/models.py
|
||||
msgid ""
|
||||
"Amount of time a user can take to return from the source to continue the "
|
||||
@ -681,7 +712,7 @@ msgstr ""
|
||||
#: authentik/events/api/tasks.py
|
||||
#, python-brace-format
|
||||
msgid "Successfully started task {name}."
|
||||
msgstr ""
|
||||
msgstr "Tarefa {name} iniciada com sucesso."
|
||||
|
||||
#: authentik/events/models.py
|
||||
msgid "Event"
|
||||
@ -713,12 +744,16 @@ msgid ""
|
||||
"Customize the body of the request. Mapping should return data that is JSON-"
|
||||
"serializable."
|
||||
msgstr ""
|
||||
"Personalize o corpo do pedido. O mapeamento deve retornar dados que sejam "
|
||||
"serializáveis em JSON."
|
||||
|
||||
#: authentik/events/models.py
|
||||
msgid ""
|
||||
"Configure additional headers to be sent. Mapping should return a dictionary "
|
||||
"of key-value pairs"
|
||||
msgstr ""
|
||||
"Configurar cabeçalhos adicionais a serem enviados. O mapeamento deve "
|
||||
"retornar um dicionário de pares chave-valor"
|
||||
|
||||
#: authentik/events/models.py
|
||||
msgid ""
|
||||
@ -998,8 +1033,11 @@ msgid "Starting full provider sync"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
#, python-brace-format
|
||||
msgid "Syncing page {page} of users"
|
||||
msgid "Syncing users"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
msgid "Syncing groups"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
@ -1314,7 +1352,7 @@ msgstr ""
|
||||
#: authentik/policies/password/models.py
|
||||
#, python-brace-format
|
||||
msgid "Password exists on {count} online lists."
|
||||
msgstr ""
|
||||
msgstr "A senha está presente em {count} listas de senhas vulneráveis."
|
||||
|
||||
#: authentik/policies/password/models.py
|
||||
msgid "Password is too weak."
|
||||
@ -2396,6 +2434,12 @@ msgid ""
|
||||
"Active Directory"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid ""
|
||||
"Delete authentik users and groups which were previously supplied by this "
|
||||
"source, but are now missing from it."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "LDAP Source"
|
||||
msgstr "Fonte LDAP"
|
||||
@ -2412,6 +2456,11 @@ msgstr ""
|
||||
msgid "LDAP Source Property Mappings"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid ""
|
||||
"Unique ID used while checking if this object still exists in the directory."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "User LDAP Source Connection"
|
||||
msgstr ""
|
||||
@ -2802,6 +2851,11 @@ msgstr ""
|
||||
msgid "Group SAML Source Connections"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/saml/views.py
|
||||
#, python-brace-format
|
||||
msgid "Continue to {source_name}"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/scim/models.py
|
||||
msgid "SCIM Source"
|
||||
msgstr ""
|
||||
@ -3174,6 +3228,10 @@ msgstr "Consentimento do usuário"
|
||||
msgid "User Consents"
|
||||
msgstr "Consentimentos do usuário"
|
||||
|
||||
#: authentik/stages/consent/stage.py
|
||||
msgid "Invalid consent token, re-showing prompt"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/deny/models.py
|
||||
msgid "Deny Stage"
|
||||
msgstr "Negar Estágio"
|
||||
@ -3190,6 +3248,14 @@ msgstr "Palco fictício"
|
||||
msgid "Dummy Stages"
|
||||
msgstr "Fases fictícias"
|
||||
|
||||
#: authentik/stages/email/flow.py
|
||||
msgid "Continue to confirm this email address."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/flow.py
|
||||
msgid "Link was already used, please request a new link."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/models.py
|
||||
msgid "Password Reset"
|
||||
msgstr "Redefinição de senha"
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -18,7 +18,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
|
||||
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: Marc Schmitt, 2025\n"
|
||||
"Language-Team: Russian (https://app.transifex.com/authentik/teams/119923/ru/)\n"
|
||||
@ -111,6 +111,10 @@ msgstr ""
|
||||
msgid "Web Certificate used by the authentik Core webserver."
|
||||
msgstr "Web Certificate используемый для authentik Core webserver."
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Certificates used for client authentication."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Brand"
|
||||
msgstr "Бренд"
|
||||
@ -669,6 +673,33 @@ msgstr "Конечные устройства"
|
||||
msgid "Verifying your browser..."
|
||||
msgstr "Проверка вашего браузера..."
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid ""
|
||||
"Configure certificate authorities to validate the certificate against. This "
|
||||
"option has a higher priority than the `client_certificate` option on "
|
||||
"`Brand`."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Mutual TLS Stage"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Mutual TLS Stages"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Permissions to pass Certificates for outposts."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/stage.py
|
||||
msgid "Certificate required but no certificate was given."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/stage.py
|
||||
msgid "No user found for certificate."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/source/models.py
|
||||
msgid ""
|
||||
"Amount of time a user can take to return from the source to continue the "
|
||||
@ -1009,8 +1040,11 @@ msgid "Starting full provider sync"
|
||||
msgstr "Запуск полной синхронизации провайдера"
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
#, python-brace-format
|
||||
msgid "Syncing page {page} of users"
|
||||
msgid "Syncing users"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
msgid "Syncing groups"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
@ -2430,6 +2464,12 @@ msgid ""
|
||||
"Active Directory"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid ""
|
||||
"Delete authentik users and groups which were previously supplied by this "
|
||||
"source, but are now missing from it."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "LDAP Source"
|
||||
msgstr "Источник LDAP"
|
||||
@ -2446,6 +2486,11 @@ msgstr "Сопоставление свойства LDAP источника"
|
||||
msgid "LDAP Source Property Mappings"
|
||||
msgstr "Сопоставление свойств LDAP источника"
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid ""
|
||||
"Unique ID used while checking if this object still exists in the directory."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "User LDAP Source Connection"
|
||||
msgstr ""
|
||||
@ -2842,6 +2887,11 @@ msgstr "Групповое подключение к источнику SAML"
|
||||
msgid "Group SAML Source Connections"
|
||||
msgstr "Групповые подключения к источнику SAML"
|
||||
|
||||
#: authentik/sources/saml/views.py
|
||||
#, python-brace-format
|
||||
msgid "Continue to {source_name}"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/scim/models.py
|
||||
msgid "SCIM Source"
|
||||
msgstr "Источник SCIM"
|
||||
@ -3219,6 +3269,10 @@ msgstr "Согласие пользователя"
|
||||
msgid "User Consents"
|
||||
msgstr "Согласия пользователя"
|
||||
|
||||
#: authentik/stages/consent/stage.py
|
||||
msgid "Invalid consent token, re-showing prompt"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/deny/models.py
|
||||
msgid "Deny Stage"
|
||||
msgstr "Этап отказа"
|
||||
@ -3235,6 +3289,14 @@ msgstr "Фиктивный этап"
|
||||
msgid "Dummy Stages"
|
||||
msgstr "Фиктивные этапы"
|
||||
|
||||
#: authentik/stages/email/flow.py
|
||||
msgid "Continue to confirm this email address."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/flow.py
|
||||
msgid "Link was already used, please request a new link."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/models.py
|
||||
msgid "Password Reset"
|
||||
msgstr "Сброс пароля"
|
||||
|
@ -13,7 +13,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
|
||||
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: Jens L. <jens@goauthentik.io>, 2025\n"
|
||||
"Language-Team: Turkish (https://app.transifex.com/authentik/teams/119923/tr/)\n"
|
||||
@ -107,6 +107,10 @@ msgstr ""
|
||||
msgid "Web Certificate used by the authentik Core webserver."
|
||||
msgstr "Authentik Core web sunucusu tarafından kullanılan Web Sertifikası."
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Certificates used for client authentication."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Brand"
|
||||
msgstr "Marka"
|
||||
@ -659,6 +663,33 @@ msgstr "Uç Nokta Cihazları"
|
||||
msgid "Verifying your browser..."
|
||||
msgstr "Tarayıcınız doğrulanıyor..."
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid ""
|
||||
"Configure certificate authorities to validate the certificate against. This "
|
||||
"option has a higher priority than the `client_certificate` option on "
|
||||
"`Brand`."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Mutual TLS Stage"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Mutual TLS Stages"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Permissions to pass Certificates for outposts."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/stage.py
|
||||
msgid "Certificate required but no certificate was given."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/stage.py
|
||||
msgid "No user found for certificate."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/source/models.py
|
||||
msgid ""
|
||||
"Amount of time a user can take to return from the source to continue the "
|
||||
@ -1000,8 +1031,11 @@ msgid "Starting full provider sync"
|
||||
msgstr "Tam sağlayıcı senkronizasyonunu başlatma"
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
#, python-brace-format
|
||||
msgid "Syncing page {page} of users"
|
||||
msgid "Syncing users"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
msgid "Syncing groups"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
@ -2430,6 +2464,12 @@ msgid ""
|
||||
"Active Directory"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid ""
|
||||
"Delete authentik users and groups which were previously supplied by this "
|
||||
"source, but are now missing from it."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "LDAP Source"
|
||||
msgstr "LDAP Kaynağı"
|
||||
@ -2446,6 +2486,11 @@ msgstr "LDAP Kaynak Özellik Eşlemesi"
|
||||
msgid "LDAP Source Property Mappings"
|
||||
msgstr "LDAP Kaynak Özellik Eşlemeleri"
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid ""
|
||||
"Unique ID used while checking if this object still exists in the directory."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "User LDAP Source Connection"
|
||||
msgstr ""
|
||||
@ -2837,6 +2882,11 @@ msgstr "Grup SAML Kaynak Bağlantısı"
|
||||
msgid "Group SAML Source Connections"
|
||||
msgstr "Grup SAML Kaynak Bağlantıları"
|
||||
|
||||
#: authentik/sources/saml/views.py
|
||||
#, python-brace-format
|
||||
msgid "Continue to {source_name}"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/scim/models.py
|
||||
msgid "SCIM Source"
|
||||
msgstr "SCIM Kaynak"
|
||||
@ -3211,6 +3261,10 @@ msgstr "Kullanıcı Onayı"
|
||||
msgid "User Consents"
|
||||
msgstr "Kullanıcı Onayları"
|
||||
|
||||
#: authentik/stages/consent/stage.py
|
||||
msgid "Invalid consent token, re-showing prompt"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/deny/models.py
|
||||
msgid "Deny Stage"
|
||||
msgstr "Aşama Alanını Reddet"
|
||||
@ -3227,6 +3281,14 @@ msgstr "Kukla Aşaması"
|
||||
msgid "Dummy Stages"
|
||||
msgstr "Kukla Aşamaları"
|
||||
|
||||
#: authentik/stages/email/flow.py
|
||||
msgid "Continue to confirm this email address."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/flow.py
|
||||
msgid "Link was already used, please request a new link."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/models.py
|
||||
msgid "Password Reset"
|
||||
msgstr "Parola Sıfırlama"
|
||||
|
Binary file not shown.
@ -15,7 +15,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-05-20 00:10+0000\n"
|
||||
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: deluxghost, 2025\n"
|
||||
"Language-Team: Chinese Simplified (https://app.transifex.com/authentik/teams/119923/zh-Hans/)\n"
|
||||
@ -975,9 +975,12 @@ msgid "Starting full provider sync"
|
||||
msgstr "开始全量提供程序同步"
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
#, python-brace-format
|
||||
msgid "Syncing page {page} of users"
|
||||
msgstr "正在同步用户页面 {page}"
|
||||
msgid "Syncing users"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
msgid "Syncing groups"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
#, python-brace-format
|
||||
@ -2285,6 +2288,12 @@ msgid ""
|
||||
"Active Directory"
|
||||
msgstr "基于用户属性而非组属性查询组成员身份。这允许在 FreeIPA 或 Active Directory 等系统上支持嵌套组决策"
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid ""
|
||||
"Delete authentik users and groups which were previously supplied by this "
|
||||
"source, but are now missing from it."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "LDAP Source"
|
||||
msgstr "LDAP 源"
|
||||
@ -2301,6 +2310,11 @@ msgstr "LDAP 源属性映射"
|
||||
msgid "LDAP Source Property Mappings"
|
||||
msgstr "LDAP 源属性映射"
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid ""
|
||||
"Unique ID used while checking if this object still exists in the directory."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "User LDAP Source Connection"
|
||||
msgstr "用户 LDAP 源连接"
|
||||
@ -2678,6 +2692,11 @@ msgstr "组 SAML 源连接"
|
||||
msgid "Group SAML Source Connections"
|
||||
msgstr "组 SAML 源连接"
|
||||
|
||||
#: authentik/sources/saml/views.py
|
||||
#, python-brace-format
|
||||
msgid "Continue to {source_name}"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/scim/models.py
|
||||
msgid "SCIM Source"
|
||||
msgstr "SCIM 源"
|
||||
@ -3044,6 +3063,10 @@ msgstr "用户同意授权"
|
||||
msgid "User Consents"
|
||||
msgstr "用户同意授权"
|
||||
|
||||
#: authentik/stages/consent/stage.py
|
||||
msgid "Invalid consent token, re-showing prompt"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/deny/models.py
|
||||
msgid "Deny Stage"
|
||||
msgstr "拒绝阶段"
|
||||
@ -3060,6 +3083,14 @@ msgstr "虚拟阶段"
|
||||
msgid "Dummy Stages"
|
||||
msgstr "虚拟阶段"
|
||||
|
||||
#: authentik/stages/email/flow.py
|
||||
msgid "Continue to confirm this email address."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/flow.py
|
||||
msgid "Link was already used, please request a new link."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/models.py
|
||||
msgid "Password Reset"
|
||||
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: 2025-05-20 00:10+0000\n"
|
||||
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: deluxghost, 2025\n"
|
||||
"Language-Team: Chinese (China) (https://app.transifex.com/authentik/teams/119923/zh_CN/)\n"
|
||||
@ -974,9 +974,12 @@ msgid "Starting full provider sync"
|
||||
msgstr "开始全量提供程序同步"
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
#, python-brace-format
|
||||
msgid "Syncing page {page} of users"
|
||||
msgstr "正在同步用户页面 {page}"
|
||||
msgid "Syncing users"
|
||||
msgstr "正在同步用户"
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
msgid "Syncing groups"
|
||||
msgstr "正在同步组"
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
#, python-brace-format
|
||||
@ -2284,6 +2287,12 @@ msgid ""
|
||||
"Active Directory"
|
||||
msgstr "基于用户属性而非组属性查询组成员身份。这允许在 FreeIPA 或 Active Directory 等系统上支持嵌套组决策"
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid ""
|
||||
"Delete authentik users and groups which were previously supplied by this "
|
||||
"source, but are now missing from it."
|
||||
msgstr "删除之前由此源提供,但现已缺失的用户和组。"
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "LDAP Source"
|
||||
msgstr "LDAP 源"
|
||||
@ -2300,6 +2309,11 @@ msgstr "LDAP 源属性映射"
|
||||
msgid "LDAP Source Property Mappings"
|
||||
msgstr "LDAP 源属性映射"
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid ""
|
||||
"Unique ID used while checking if this object still exists in the directory."
|
||||
msgstr "检查此对象是否仍在目录中时使用的唯一 ID。"
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "User LDAP Source Connection"
|
||||
msgstr "用户 LDAP 源连接"
|
||||
@ -2677,6 +2691,11 @@ msgstr "组 SAML 源连接"
|
||||
msgid "Group SAML Source Connections"
|
||||
msgstr "组 SAML 源连接"
|
||||
|
||||
#: authentik/sources/saml/views.py
|
||||
#, python-brace-format
|
||||
msgid "Continue to {source_name}"
|
||||
msgstr "继续前往 {source_name}"
|
||||
|
||||
#: authentik/sources/scim/models.py
|
||||
msgid "SCIM Source"
|
||||
msgstr "SCIM 源"
|
||||
@ -3043,6 +3062,10 @@ msgstr "用户同意授权"
|
||||
msgid "User Consents"
|
||||
msgstr "用户同意授权"
|
||||
|
||||
#: authentik/stages/consent/stage.py
|
||||
msgid "Invalid consent token, re-showing prompt"
|
||||
msgstr "无效的同意令牌,将重新显示输入"
|
||||
|
||||
#: authentik/stages/deny/models.py
|
||||
msgid "Deny Stage"
|
||||
msgstr "拒绝阶段"
|
||||
@ -3059,6 +3082,14 @@ msgstr "虚拟阶段"
|
||||
msgid "Dummy Stages"
|
||||
msgstr "虚拟阶段"
|
||||
|
||||
#: authentik/stages/email/flow.py
|
||||
msgid "Continue to confirm this email address."
|
||||
msgstr "继续以确认电子邮件地址。"
|
||||
|
||||
#: authentik/stages/email/flow.py
|
||||
msgid "Link was already used, please request a new link."
|
||||
msgstr "链接已被使用,请申请一个新链接。"
|
||||
|
||||
#: authentik/stages/email/models.py
|
||||
msgid "Password Reset"
|
||||
msgstr "密码重置"
|
||||
|
@ -14,7 +14,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
|
||||
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: 刘松, 2025\n"
|
||||
"Language-Team: Chinese (Taiwan) (https://app.transifex.com/authentik/teams/119923/zh_TW/)\n"
|
||||
@ -101,6 +101,10 @@ msgstr ""
|
||||
msgid "Web Certificate used by the authentik Core webserver."
|
||||
msgstr "用於 authentik Core 網頁伺服器的網頁憑證。"
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Certificates used for client authentication."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Brand"
|
||||
msgstr "品牌"
|
||||
@ -625,6 +629,33 @@ msgstr ""
|
||||
msgid "Verifying your browser..."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid ""
|
||||
"Configure certificate authorities to validate the certificate against. This "
|
||||
"option has a higher priority than the `client_certificate` option on "
|
||||
"`Brand`."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Mutual TLS Stage"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Mutual TLS Stages"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/models.py
|
||||
msgid "Permissions to pass Certificates for outposts."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/stage.py
|
||||
msgid "Certificate required but no certificate was given."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/mtls/stage.py
|
||||
msgid "No user found for certificate."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/stages/source/models.py
|
||||
msgid ""
|
||||
"Amount of time a user can take to return from the source to continue the "
|
||||
@ -943,8 +974,11 @@ msgid "Starting full provider sync"
|
||||
msgstr "開始同步所有提供程式"
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
#, python-brace-format
|
||||
msgid "Syncing page {page} of users"
|
||||
msgid "Syncing users"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
msgid "Syncing groups"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
@ -2249,6 +2283,12 @@ msgid ""
|
||||
"Active Directory"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid ""
|
||||
"Delete authentik users and groups which were previously supplied by this "
|
||||
"source, but are now missing from it."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "LDAP Source"
|
||||
msgstr "LDAP 來源"
|
||||
@ -2265,6 +2305,11 @@ msgstr ""
|
||||
msgid "LDAP Source Property Mappings"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid ""
|
||||
"Unique ID used while checking if this object still exists in the directory."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "User LDAP Source Connection"
|
||||
msgstr ""
|
||||
@ -2642,6 +2687,11 @@ msgstr ""
|
||||
msgid "Group SAML Source Connections"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/saml/views.py
|
||||
#, python-brace-format
|
||||
msgid "Continue to {source_name}"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/scim/models.py
|
||||
msgid "SCIM Source"
|
||||
msgstr "SCIM 來源"
|
||||
@ -2998,6 +3048,10 @@ msgstr "使用者同意"
|
||||
msgid "User Consents"
|
||||
msgstr "使用者同意"
|
||||
|
||||
#: authentik/stages/consent/stage.py
|
||||
msgid "Invalid consent token, re-showing prompt"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/deny/models.py
|
||||
msgid "Deny Stage"
|
||||
msgstr "拒絕階段"
|
||||
@ -3014,6 +3068,14 @@ msgstr "假階段"
|
||||
msgid "Dummy Stages"
|
||||
msgstr "假階段"
|
||||
|
||||
#: authentik/stages/email/flow.py
|
||||
msgid "Continue to confirm this email address."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/flow.py
|
||||
msgid "Link was already used, please request a new link."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/models.py
|
||||
msgid "Password Reset"
|
||||
msgstr "重設密碼"
|
||||
|
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@goauthentik/authentik",
|
||||
"version": "2025.4.1",
|
||||
"version": "2025.6.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@goauthentik/authentik",
|
||||
"version": "2025.4.1",
|
||||
"version": "2025.6.0",
|
||||
"devDependencies": {
|
||||
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
||||
"prettier": "^3.3.3",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@goauthentik/authentik",
|
||||
"version": "2025.4.1",
|
||||
"version": "2025.6.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
|
8
packages/docusaurus-config/package-lock.json
generated
8
packages/docusaurus-config/package-lock.json
generated
@ -3710,9 +3710,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@goauthentik/prettier-config": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/prettier-config/-/prettier-config-1.0.4.tgz",
|
||||
"integrity": "sha512-CgUVAThlJHif7ZRXUPMbR/7/YLGzkJw7YbqEcleUjjKkvID0aykrypXx04td6cG76zigspTCgJKoXimKT41E7g==",
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/prettier-config/-/prettier-config-1.0.5.tgz",
|
||||
"integrity": "sha512-3W1uJvhzBPerDao53hSXhNzB7Ev8DbGYh+gVkuku1FaUZGBpiwD/6U3ah4sny8NoRiObGQ1geF4dhNLtlRbC/Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -3722,7 +3722,7 @@
|
||||
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-plugin-organize-imports": "^4.1.0",
|
||||
"prettier-plugin-packagejson": "^2.5.10"
|
||||
"prettier-plugin-packagejson": "^2.5.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@goauthentik/tsconfig": {
|
||||
|
106
packages/eslint-config/package-lock.json
generated
106
packages/eslint-config/package-lock.json
generated
@ -308,9 +308,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@goauthentik/prettier-config": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/prettier-config/-/prettier-config-1.0.1.tgz",
|
||||
"integrity": "sha512-6N0cCG3Uw3Nt+gTxRJ/FYFi/NfuL849CrQkrx307PvEBaG66OjxFFee4bhS/si4XvLdxFdog7oQsPwYmqZeZ+w==",
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/prettier-config/-/prettier-config-1.0.5.tgz",
|
||||
"integrity": "sha512-3W1uJvhzBPerDao53hSXhNzB7Ev8DbGYh+gVkuku1FaUZGBpiwD/6U3ah4sny8NoRiObGQ1geF4dhNLtlRbC/Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -320,13 +320,13 @@
|
||||
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-plugin-organize-imports": "^4.1.0",
|
||||
"prettier-plugin-packagejson": "^2.5.10"
|
||||
"prettier-plugin-packagejson": "^2.5.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@goauthentik/tsconfig": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/tsconfig/-/tsconfig-1.0.1.tgz",
|
||||
"integrity": "sha512-kxMDkgUHhAmQ2iIhUZJjrx/CgDb1AwvRoPtU4vrjAZu7x66+qczCjRTK+GzIGCeqB97GEpvCCjU8CThmozVFqA==",
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/tsconfig/-/tsconfig-1.0.4.tgz",
|
||||
"integrity": "sha512-BTGVpGh8SbCRHTULBf+2WTcw6OHJ8Ws9VtVfAMUUgcq8whbH/A7Q/n8WbkDaEeihzHUFkLk3JBenHKzEKAZWlw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -491,9 +491,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@pkgr/core": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.2.tgz",
|
||||
"integrity": "sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==",
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.4.tgz",
|
||||
"integrity": "sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
@ -501,7 +501,7 @@
|
||||
"node": "^12.20.0 || ^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/unts"
|
||||
"url": "https://opencollective.com/pkgr"
|
||||
}
|
||||
},
|
||||
"node_modules/@rtsao/scc": {
|
||||
@ -2045,20 +2045,6 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/get-stdin": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz",
|
||||
"integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/get-symbol-description": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz",
|
||||
@ -2077,9 +2063,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/git-hooks-list": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-3.2.0.tgz",
|
||||
"integrity": "sha512-ZHG9a1gEhUMX1TvGrLdyWb9kDopCBbTnI8z4JgRMYxsijWipgjSEYoPWqBuIB0DnRnvqlQSEeVmzpeuPm7NdFQ==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-4.1.1.tgz",
|
||||
"integrity": "sha512-cmP497iLq54AZnv4YRAEMnEyQ1eIn4tGKbmswqwmFV4GBnAqE8NLtWxxdXa++AalfgL5EBH4IxTPyquEuGY/jA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
@ -3219,15 +3205,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier-plugin-packagejson": {
|
||||
"version": "2.5.10",
|
||||
"resolved": "https://registry.npmjs.org/prettier-plugin-packagejson/-/prettier-plugin-packagejson-2.5.10.tgz",
|
||||
"integrity": "sha512-LUxATI5YsImIVSaaLJlJ3aE6wTD+nvots18U3GuQMJpUyClChaZlQrqx3dBnbhF20OnKWZyx8EgyZypQtBDtgQ==",
|
||||
"version": "2.5.14",
|
||||
"resolved": "https://registry.npmjs.org/prettier-plugin-packagejson/-/prettier-plugin-packagejson-2.5.14.tgz",
|
||||
"integrity": "sha512-h+3tSpr2nVpp+YOK1MDIYtYhHVXr8/0V59UUbJpIJFaqi3w4fvUokJo6eV8W+vELrUXIZzJ+DKm5G7lYzrMcKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"sort-package-json": "2.15.1",
|
||||
"synckit": "0.9.2"
|
||||
"sort-package-json": "3.2.1",
|
||||
"synckit": "0.11.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prettier": ">= 1.16.0"
|
||||
@ -3633,30 +3619,29 @@
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/sort-package-json": {
|
||||
"version": "2.15.1",
|
||||
"resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-2.15.1.tgz",
|
||||
"integrity": "sha512-9x9+o8krTT2saA9liI4BljNjwAbvUnWf11Wq+i/iZt8nl2UGYnf3TH5uBydE7VALmP7AGwlfszuEeL8BDyb0YA==",
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-3.2.1.tgz",
|
||||
"integrity": "sha512-rTfRdb20vuoAn7LDlEtCqOkYfl2X+Qze6cLbNOzcDpbmKEhJI30tTN44d5shbKJnXsvz24QQhlCm81Bag7EOKg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"detect-indent": "^7.0.1",
|
||||
"detect-newline": "^4.0.0",
|
||||
"get-stdin": "^9.0.0",
|
||||
"git-hooks-list": "^3.0.0",
|
||||
"detect-newline": "^4.0.1",
|
||||
"git-hooks-list": "^4.0.0",
|
||||
"is-plain-obj": "^4.1.0",
|
||||
"semver": "^7.6.0",
|
||||
"semver": "^7.7.1",
|
||||
"sort-object-keys": "^1.1.3",
|
||||
"tinyglobby": "^0.2.9"
|
||||
"tinyglobby": "^0.2.12"
|
||||
},
|
||||
"bin": {
|
||||
"sort-package-json": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/sort-package-json/node_modules/semver": {
|
||||
"version": "7.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
|
||||
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
@ -3806,32 +3791,31 @@
|
||||
}
|
||||
},
|
||||
"node_modules/synckit": {
|
||||
"version": "0.9.2",
|
||||
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz",
|
||||
"integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==",
|
||||
"version": "0.11.6",
|
||||
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.6.tgz",
|
||||
"integrity": "sha512-2pR2ubZSV64f/vqm9eLPz/KOvR9Dm+Co/5ChLgeHl0yEDRc6h5hXHoxEQH8Y5Ljycozd3p1k5TTSVdzYGkPvLw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@pkgr/core": "^0.1.0",
|
||||
"tslib": "^2.6.2"
|
||||
"@pkgr/core": "^0.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/unts"
|
||||
"url": "https://opencollective.com/synckit"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby": {
|
||||
"version": "0.2.12",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz",
|
||||
"integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==",
|
||||
"version": "0.2.13",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",
|
||||
"integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fdir": "^6.4.3",
|
||||
"fdir": "^6.4.4",
|
||||
"picomatch": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -3842,9 +3826,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby/node_modules/fdir": {
|
||||
"version": "6.4.3",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz",
|
||||
"integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==",
|
||||
"version": "6.4.4",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
|
||||
"integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
@ -3909,14 +3893,6 @@
|
||||
"strip-bom": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"dev": true,
|
||||
"license": "0BSD",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/type-check": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||
|
@ -31,8 +31,33 @@ export const AuthentikPrettierConfig = {
|
||||
trailingComma: "all",
|
||||
useTabs: false,
|
||||
vueIndentScriptAndStyle: false,
|
||||
plugins: ["prettier-plugin-packagejson", "@trivago/prettier-plugin-sort-imports"],
|
||||
importOrder: ["^(@?)lit(.*)$", "\\.css$", "^@goauthentik/api$", "^[./]"],
|
||||
plugins: [
|
||||
// ---
|
||||
"prettier-plugin-packagejson",
|
||||
"@trivago/prettier-plugin-sort-imports",
|
||||
],
|
||||
importOrder: [
|
||||
// ---
|
||||
|
||||
"^(@goauthentik/|#)common.+",
|
||||
"^(@goauthentik/|#)elements.+",
|
||||
"^(@goauthentik/|#)components.+",
|
||||
"^(@goauthentik/|#)user.+",
|
||||
"^(@goauthentik/|#)admin.+",
|
||||
"^(@goauthentik/|#)flow.+",
|
||||
"^(@goauthentik/|#)flow.+",
|
||||
|
||||
"^#.+",
|
||||
"^@goauthentik.+",
|
||||
|
||||
"<THIRD_PARTY_MODULES>",
|
||||
|
||||
"^(@?)lit(.*)$",
|
||||
"\\.css$",
|
||||
"^@goauthentik/api$",
|
||||
"^[./]",
|
||||
],
|
||||
importOrderSideEffects: false,
|
||||
importOrderSeparation: true,
|
||||
importOrderSortSpecifiers: true,
|
||||
importOrderParserPlugins: ["typescript", "jsx", "classProperties", "decorators-legacy"],
|
||||
|
10
packages/prettier-config/package-lock.json
generated
10
packages/prettier-config/package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@goauthentik/prettier-config",
|
||||
"version": "1.0.4",
|
||||
"version": "2.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@goauthentik/prettier-config",
|
||||
"version": "1.0.4",
|
||||
"version": "2.0.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@goauthentik/tsconfig": "^1.0.1",
|
||||
@ -143,9 +143,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@goauthentik/tsconfig": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/tsconfig/-/tsconfig-1.0.1.tgz",
|
||||
"integrity": "sha512-kxMDkgUHhAmQ2iIhUZJjrx/CgDb1AwvRoPtU4vrjAZu7x66+qczCjRTK+GzIGCeqB97GEpvCCjU8CThmozVFqA==",
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/tsconfig/-/tsconfig-1.0.4.tgz",
|
||||
"integrity": "sha512-BTGVpGh8SbCRHTULBf+2WTcw6OHJ8Ws9VtVfAMUUgcq8whbH/A7Q/n8WbkDaEeihzHUFkLk3JBenHKzEKAZWlw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@goauthentik/prettier-config",
|
||||
"version": "1.0.5",
|
||||
"version": "2.0.0",
|
||||
"description": "authentik's Prettier config",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "authentik"
|
||||
version = "2025.4.1"
|
||||
version = "2025.6.0"
|
||||
description = ""
|
||||
authors = [{ name = "authentik Team", email = "hello@goauthentik.io" }]
|
||||
requires-python = "==3.13.*"
|
||||
@ -9,7 +9,7 @@ dependencies = [
|
||||
"celery==5.5.2",
|
||||
"channels==4.2.2",
|
||||
"channels-redis==4.2.1",
|
||||
"cryptography==44.0.3",
|
||||
"cryptography==45.0.3",
|
||||
"dacite==1.9.2",
|
||||
"deepmerge==2.0",
|
||||
"defusedxml==0.7.1",
|
||||
@ -23,7 +23,7 @@ dependencies = [
|
||||
"django-prometheus==2.3.1",
|
||||
"django-redis==5.4.0",
|
||||
"django-storages[s3]==1.14.6",
|
||||
"django-tenants==3.7.0",
|
||||
"django-tenants==3.8.0",
|
||||
"djangorestframework==3.16.0",
|
||||
"djangorestframework-guardian==0.3.0",
|
||||
"docker==7.1.0",
|
||||
@ -35,7 +35,7 @@ dependencies = [
|
||||
"flower==2.0.1",
|
||||
"geoip2==5.1.0",
|
||||
"geopy==2.4.1",
|
||||
"google-api-python-client==2.169.0",
|
||||
"google-api-python-client==2.170.0",
|
||||
"gssapi==1.9.0",
|
||||
"gunicorn==23.0.0",
|
||||
"jsonpatch==1.33",
|
||||
@ -43,12 +43,12 @@ dependencies = [
|
||||
"kubernetes==32.0.1",
|
||||
"ldap3==2.9.1",
|
||||
"lxml==5.4.0",
|
||||
"msgraph-sdk==1.30.0",
|
||||
"msgraph-sdk==1.31.0",
|
||||
"opencontainers==0.0.14",
|
||||
"packaging==25.0",
|
||||
"paramiko==3.5.1",
|
||||
"psycopg[c,pool]==3.2.9",
|
||||
"pydantic==2.11.4",
|
||||
"pydantic==2.11.5",
|
||||
"pydantic-scim==0.0.8",
|
||||
"pyjwt==2.10.1",
|
||||
"pyrad==2.4",
|
||||
@ -114,7 +114,6 @@ no-binary-package = [
|
||||
]
|
||||
|
||||
[tool.uv.sources]
|
||||
django-tenants = { git = "https://github.com/goauthentik/django-tenants.git", branch = "authentik-fixes" }
|
||||
opencontainers = { git = "https://github.com/vsoch/oci-python", rev = "ceb4fcc090851717a3069d78e85ceb1e86c2740c" }
|
||||
djangorestframework = { git = "https://github.com/goauthentik/django-rest-framework", rev = "896722bab969fabc74a08b827da59409cf9f1a4e" }
|
||||
|
||||
|
33
schema.yml
33
schema.yml
@ -1,7 +1,7 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: authentik
|
||||
version: 2025.4.1
|
||||
version: 2025.6.0
|
||||
description: Making authentication simple.
|
||||
contact:
|
||||
email: hello@goauthentik.io
|
||||
@ -28473,6 +28473,10 @@ paths:
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
- in: query
|
||||
name: delete_not_found_objects
|
||||
schema:
|
||||
type: boolean
|
||||
- in: query
|
||||
name: enabled
|
||||
schema:
|
||||
@ -28577,6 +28581,10 @@ paths:
|
||||
name: sync_users_password
|
||||
schema:
|
||||
type: boolean
|
||||
- in: query
|
||||
name: user_membership_attribute
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: user_object_filter
|
||||
schema:
|
||||
@ -47889,6 +47897,9 @@ components:
|
||||
group_membership_field:
|
||||
type: string
|
||||
description: Field which contains members of a group.
|
||||
user_membership_attribute:
|
||||
type: string
|
||||
description: Attribute which matches the value of `group_membership_field`.
|
||||
object_uniqueness_field:
|
||||
type: string
|
||||
description: Field which contains a unique Identifier.
|
||||
@ -47922,6 +47933,10 @@ components:
|
||||
description: Lookup group membership based on a user attribute instead of
|
||||
a group attribute. This allows nested group resolution on systems like
|
||||
FreeIPA and Active Directory
|
||||
delete_not_found_objects:
|
||||
type: boolean
|
||||
description: Delete authentik users and groups which were previously supplied
|
||||
by this source, but are now missing from it.
|
||||
required:
|
||||
- base_dn
|
||||
- component
|
||||
@ -48098,6 +48113,10 @@ components:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: Field which contains members of a group.
|
||||
user_membership_attribute:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: Attribute which matches the value of `group_membership_field`.
|
||||
object_uniqueness_field:
|
||||
type: string
|
||||
minLength: 1
|
||||
@ -48123,6 +48142,10 @@ components:
|
||||
description: Lookup group membership based on a user attribute instead of
|
||||
a group attribute. This allows nested group resolution on systems like
|
||||
FreeIPA and Active Directory
|
||||
delete_not_found_objects:
|
||||
type: boolean
|
||||
description: Delete authentik users and groups which were previously supplied
|
||||
by this source, but are now missing from it.
|
||||
required:
|
||||
- base_dn
|
||||
- name
|
||||
@ -53431,6 +53454,10 @@ components:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: Field which contains members of a group.
|
||||
user_membership_attribute:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: Attribute which matches the value of `group_membership_field`.
|
||||
object_uniqueness_field:
|
||||
type: string
|
||||
minLength: 1
|
||||
@ -53456,6 +53483,10 @@ components:
|
||||
description: Lookup group membership based on a user attribute instead of
|
||||
a group attribute. This allows nested group resolution on systems like
|
||||
FreeIPA and Active Directory
|
||||
delete_not_found_objects:
|
||||
type: boolean
|
||||
description: Delete authentik users and groups which were previously supplied
|
||||
by this source, but are now missing from it.
|
||||
PatchedLicenseRequest:
|
||||
type: object
|
||||
description: License Serializer
|
||||
|
7
scripts/api-ts-templates/tsconfig.esm.mustache
Normal file
7
scripts/api-ts-templates/tsconfig.esm.mustache
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/esm",
|
||||
},
|
||||
}
|
23
scripts/api-ts-templates/tsconfig.mustache
Normal file
23
scripts/api-ts-templates/tsconfig.mustache
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"isolatedModules": true,
|
||||
"incremental": true,
|
||||
"baseUrl": ".",
|
||||
"rootDir": "src",
|
||||
"strict": true,
|
||||
"newLine": "lf",
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"outDir": "dist",
|
||||
"skipDefaultLibCheck": true,
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||
},
|
||||
"exclude": ["node_modules", "./out/**/*", "./dist/**/*"],
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
set -e -x -o pipefail
|
||||
hash="$(git rev-parse HEAD || openssl rand -base64 36 | sha256sum)"
|
||||
|
||||
|
@ -1,12 +1,13 @@
|
||||
services:
|
||||
chrome:
|
||||
platform: linux/x86_64
|
||||
image: docker.io/selenium/standalone-chrome:136.0
|
||||
volumes:
|
||||
- /dev/shm:/dev/shm
|
||||
network_mode: host
|
||||
restart: always
|
||||
mailpit:
|
||||
image: docker.io/axllent/mailpit:v1.25.0
|
||||
image: docker.io/axllent/mailpit:v1.25.1
|
||||
ports:
|
||||
- 1025:1025
|
||||
- 8025:8025
|
||||
|
@ -10,6 +10,7 @@ from authentik.blueprints.tests import apply_blueprint
|
||||
from authentik.core.models import User
|
||||
from authentik.flows.models import Flow
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.stages.identification.models import IdentificationStage
|
||||
from tests.e2e.utils import SeleniumTestCase, retry
|
||||
|
||||
@ -17,6 +18,10 @@ from tests.e2e.utils import SeleniumTestCase, retry
|
||||
class TestFlowsEnroll(SeleniumTestCase):
|
||||
"""Test Enroll flow"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.username = generate_id()
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
@ -39,8 +44,8 @@ class TestFlowsEnroll(SeleniumTestCase):
|
||||
self.initial_stages()
|
||||
sleep(2)
|
||||
|
||||
user = User.objects.get(username="foo")
|
||||
self.assertEqual(user.username, "foo")
|
||||
user = User.objects.get(username=self.username)
|
||||
self.assertEqual(user.username, self.username)
|
||||
self.assertEqual(user.name, "some name")
|
||||
self.assertEqual(user.email, "foo@bar.baz")
|
||||
|
||||
@ -87,7 +92,16 @@ class TestFlowsEnroll(SeleniumTestCase):
|
||||
|
||||
sleep(2)
|
||||
|
||||
self.assert_user(User.objects.get(username="foo"))
|
||||
flow_executor = self.get_shadow_root("ak-flow-executor")
|
||||
consent_stage = self.get_shadow_root("ak-stage-consent", flow_executor)
|
||||
consent_stage.find_element(
|
||||
By.CSS_SELECTOR,
|
||||
"[type=submit]",
|
||||
).click()
|
||||
|
||||
self.wait_for_url(self.if_user_url())
|
||||
|
||||
self.assert_user(User.objects.get(username=self.username))
|
||||
|
||||
def initial_stages(self):
|
||||
"""Fill out initial stages"""
|
||||
@ -105,7 +119,7 @@ class TestFlowsEnroll(SeleniumTestCase):
|
||||
wait = WebDriverWait(prompt_stage, self.wait_timeout)
|
||||
|
||||
wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "input[name=username]")))
|
||||
prompt_stage.find_element(By.CSS_SELECTOR, "input[name=username]").send_keys("foo")
|
||||
prompt_stage.find_element(By.CSS_SELECTOR, "input[name=username]").send_keys(self.username)
|
||||
prompt_stage.find_element(By.CSS_SELECTOR, "input[name=password]").send_keys(
|
||||
self.user.username
|
||||
)
|
||||
@ -124,3 +138,82 @@ class TestFlowsEnroll(SeleniumTestCase):
|
||||
prompt_stage.find_element(By.CSS_SELECTOR, "input[name=name]").send_keys("some name")
|
||||
prompt_stage.find_element(By.CSS_SELECTOR, "input[name=email]").send_keys("foo@bar.baz")
|
||||
prompt_stage.find_element(By.CSS_SELECTOR, ".pf-c-button").click()
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"example/flows-enrollment-email-verification.yaml",
|
||||
)
|
||||
@CONFIG.patch("email.port", 1025)
|
||||
def test_enroll_email_pretend_email_scanner(self):
|
||||
"""Test enroll with Email verification. Open the email link twice to pretend we have an
|
||||
email scanner that clicks on links"""
|
||||
# Attach enrollment flow to identification stage
|
||||
ident_stage: IdentificationStage = IdentificationStage.objects.get(
|
||||
name="default-authentication-identification"
|
||||
)
|
||||
ident_stage.enrollment_flow = Flow.objects.get(slug="default-enrollment-flow")
|
||||
ident_stage.save()
|
||||
|
||||
self.driver.get(self.live_server_url)
|
||||
self.initial_stages()
|
||||
|
||||
# Email stage
|
||||
flow_executor = self.get_shadow_root("ak-flow-executor")
|
||||
email_stage = self.get_shadow_root("ak-stage-email", flow_executor)
|
||||
|
||||
wait = WebDriverWait(email_stage, self.wait_timeout)
|
||||
|
||||
# Wait for the success message so we know the email is sent
|
||||
wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, ".pf-c-form p")))
|
||||
|
||||
# Open Mailpit
|
||||
self.driver.get("http://localhost:8025")
|
||||
|
||||
# Click on first message
|
||||
self.wait.until(ec.presence_of_element_located((By.CLASS_NAME, "message")))
|
||||
self.driver.find_element(By.CLASS_NAME, "message").click()
|
||||
self.driver.switch_to.frame(self.driver.find_element(By.ID, "preview-html"))
|
||||
confirmation_link = self.driver.find_element(By.ID, "confirm").get_attribute("href")
|
||||
|
||||
main_tab = self.driver.current_window_handle
|
||||
|
||||
self.driver.switch_to.new_window("tab")
|
||||
confirm_tab = self.driver.current_window_handle
|
||||
|
||||
# On the new tab, check that we have the confirmation screen
|
||||
self.driver.get(confirmation_link)
|
||||
self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "ak-flow-executor")))
|
||||
|
||||
flow_executor = self.get_shadow_root("ak-flow-executor")
|
||||
consent_stage = self.get_shadow_root("ak-stage-consent", flow_executor)
|
||||
|
||||
self.assertEqual(
|
||||
"Continue to confirm this email address.",
|
||||
consent_stage.find_element(By.CSS_SELECTOR, "#header-text").text,
|
||||
)
|
||||
|
||||
# Back on the main tab, confirm
|
||||
self.driver.switch_to.window(main_tab)
|
||||
self.driver.get(confirmation_link)
|
||||
|
||||
flow_executor = self.get_shadow_root("ak-flow-executor")
|
||||
consent_stage = self.get_shadow_root("ak-stage-consent", flow_executor)
|
||||
consent_stage.find_element(
|
||||
By.CSS_SELECTOR,
|
||||
"[type=submit]",
|
||||
).click()
|
||||
|
||||
self.wait_for_url(self.if_user_url())
|
||||
sleep(2)
|
||||
|
||||
self.assert_user(User.objects.get(username=self.username))
|
||||
|
||||
self.driver.switch_to.window(confirm_tab)
|
||||
self.driver.refresh()
|
||||
flow_executor = self.get_shadow_root("ak-flow-executor")
|
||||
wait = WebDriverWait(flow_executor, self.wait_timeout)
|
||||
wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "ak-stage-access-denied")))
|
||||
|
@ -84,6 +84,14 @@ class TestFlowsRecovery(SeleniumTestCase):
|
||||
self.driver.switch_to.window(self.driver.window_handles[0])
|
||||
|
||||
sleep(2)
|
||||
|
||||
flow_executor = self.get_shadow_root("ak-flow-executor")
|
||||
consent_stage = self.get_shadow_root("ak-stage-consent", flow_executor)
|
||||
consent_stage.find_element(
|
||||
By.CSS_SELECTOR,
|
||||
"[type=submit]",
|
||||
).click()
|
||||
|
||||
# We can now enter the new password
|
||||
flow_executor = self.get_shadow_root("ak-flow-executor")
|
||||
prompt_stage = self.get_shadow_root("ak-stage-prompt", flow_executor)
|
||||
|
@ -166,30 +166,35 @@ class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase):
|
||||
print("::group::authentik Logs", file=stderr)
|
||||
apps.get_app_config("authentik_tenants").ready()
|
||||
self.wait_timeout = 60
|
||||
self.logger = get_logger()
|
||||
self.driver = self._get_driver()
|
||||
self.driver.implicitly_wait(30)
|
||||
self.wait = WebDriverWait(self.driver, self.wait_timeout)
|
||||
self.logger = get_logger()
|
||||
self.user = create_test_admin_user()
|
||||
super().setUp()
|
||||
|
||||
def _get_driver(self) -> WebDriver:
|
||||
count = 0
|
||||
try:
|
||||
opts = webdriver.ChromeOptions()
|
||||
opts.add_argument("--disable-search-engine-choice-screen")
|
||||
return webdriver.Chrome(options=opts)
|
||||
except WebDriverException:
|
||||
pass
|
||||
opts = webdriver.ChromeOptions()
|
||||
opts.add_argument("--disable-search-engine-choice-screen")
|
||||
# This breaks selenium when running remotely...?
|
||||
# opts.set_capability("goog:loggingPrefs", {"browser": "ALL"})
|
||||
opts.add_experimental_option(
|
||||
"prefs",
|
||||
{
|
||||
"profile.password_manager_leak_detection": False,
|
||||
},
|
||||
)
|
||||
while count < RETRIES:
|
||||
try:
|
||||
driver = webdriver.Remote(
|
||||
command_executor="http://localhost:4444/wd/hub",
|
||||
options=webdriver.ChromeOptions(),
|
||||
options=opts,
|
||||
)
|
||||
driver.maximize_window()
|
||||
return driver
|
||||
except WebDriverException:
|
||||
except WebDriverException as exc:
|
||||
self.logger.warning("Failed to setup webdriver", exc=exc)
|
||||
count += 1
|
||||
raise ValueError(f"Webdriver failed after {RETRIES}.")
|
||||
|
||||
|
93
uv.lock
generated
93
uv.lock
generated
@ -164,7 +164,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "authentik"
|
||||
version = "2025.4.1"
|
||||
version = "2025.6.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "argon2-cffi" },
|
||||
@ -269,7 +269,7 @@ requires-dist = [
|
||||
{ name = "celery", specifier = "==5.5.2" },
|
||||
{ name = "channels", specifier = "==4.2.2" },
|
||||
{ name = "channels-redis", specifier = "==4.2.1" },
|
||||
{ name = "cryptography", specifier = "==44.0.3" },
|
||||
{ name = "cryptography", specifier = "==45.0.3" },
|
||||
{ name = "dacite", specifier = "==1.9.2" },
|
||||
{ name = "deepmerge", specifier = "==2.0" },
|
||||
{ name = "defusedxml", specifier = "==0.7.1" },
|
||||
@ -283,7 +283,7 @@ requires-dist = [
|
||||
{ name = "django-prometheus", specifier = "==2.3.1" },
|
||||
{ name = "django-redis", specifier = "==5.4.0" },
|
||||
{ name = "django-storages", extras = ["s3"], specifier = "==1.14.6" },
|
||||
{ name = "django-tenants", git = "https://github.com/goauthentik/django-tenants.git?branch=authentik-fixes" },
|
||||
{ name = "django-tenants", specifier = "==3.8.0" },
|
||||
{ name = "djangorestframework", git = "https://github.com/goauthentik/django-rest-framework?rev=896722bab969fabc74a08b827da59409cf9f1a4e" },
|
||||
{ name = "djangorestframework-guardian", specifier = "==0.3.0" },
|
||||
{ name = "docker", specifier = "==7.1.0" },
|
||||
@ -295,7 +295,7 @@ requires-dist = [
|
||||
{ name = "flower", specifier = "==2.0.1" },
|
||||
{ name = "geoip2", specifier = "==5.1.0" },
|
||||
{ name = "geopy", specifier = "==2.4.1" },
|
||||
{ name = "google-api-python-client", specifier = "==2.169.0" },
|
||||
{ name = "google-api-python-client", specifier = "==2.170.0" },
|
||||
{ name = "gssapi", specifier = "==1.9.0" },
|
||||
{ name = "gunicorn", specifier = "==23.0.0" },
|
||||
{ name = "jsonpatch", specifier = "==1.33" },
|
||||
@ -303,12 +303,12 @@ requires-dist = [
|
||||
{ name = "kubernetes", specifier = "==32.0.1" },
|
||||
{ name = "ldap3", specifier = "==2.9.1" },
|
||||
{ name = "lxml", specifier = "==5.4.0" },
|
||||
{ name = "msgraph-sdk", specifier = "==1.30.0" },
|
||||
{ name = "msgraph-sdk", specifier = "==1.31.0" },
|
||||
{ name = "opencontainers", git = "https://github.com/vsoch/oci-python?rev=ceb4fcc090851717a3069d78e85ceb1e86c2740c" },
|
||||
{ name = "packaging", specifier = "==25.0" },
|
||||
{ name = "paramiko", specifier = "==3.5.1" },
|
||||
{ name = "psycopg", extras = ["c", "pool"], specifier = "==3.2.9" },
|
||||
{ name = "pydantic", specifier = "==2.11.4" },
|
||||
{ name = "pydantic", specifier = "==2.11.5" },
|
||||
{ name = "pydantic-scim", specifier = "==0.0.8" },
|
||||
{ name = "pyjwt", specifier = "==2.10.1" },
|
||||
{ name = "pyrad", specifier = "==2.4" },
|
||||
@ -869,37 +869,37 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "cryptography"
|
||||
version = "44.0.3"
|
||||
version = "45.0.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/53/d6/1411ab4d6108ab167d06254c5be517681f1e331f90edf1379895bcb87020/cryptography-44.0.3.tar.gz", hash = "sha256:fe19d8bc5536a91a24a8133328880a41831b6c5df54599a8417b62fe015d3053", size = 711096, upload-time = "2025-05-02T19:36:04.667Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/13/1f/9fa001e74a1993a9cadd2333bb889e50c66327b8594ac538ab8a04f915b7/cryptography-45.0.3.tar.gz", hash = "sha256:ec21313dd335c51d7877baf2972569f40a4291b76a0ce51391523ae358d05899", size = 744738, upload-time = "2025-05-25T14:17:24.777Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/08/53/c776d80e9d26441bb3868457909b4e74dd9ccabd182e10b2b0ae7a07e265/cryptography-44.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:962bc30480a08d133e631e8dfd4783ab71cc9e33d5d7c1e192f0b7c06397bb88", size = 6670281, upload-time = "2025-05-02T19:34:50.665Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/06/af2cf8d56ef87c77319e9086601bef621bedf40f6f59069e1b6d1ec498c5/cryptography-44.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffc61e8f3bf5b60346d89cd3d37231019c17a081208dfbbd6e1605ba03fa137", size = 3959305, upload-time = "2025-05-02T19:34:53.042Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/01/80de3bec64627207d030f47bf3536889efee8913cd363e78ca9a09b13c8e/cryptography-44.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58968d331425a6f9eedcee087f77fd3c927c88f55368f43ff7e0a19891f2642c", size = 4171040, upload-time = "2025-05-02T19:34:54.675Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/48/bb16b7541d207a19d9ae8b541c70037a05e473ddc72ccb1386524d4f023c/cryptography-44.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e28d62e59a4dbd1d22e747f57d4f00c459af22181f0b2f787ea83f5a876d7c76", size = 3963411, upload-time = "2025-05-02T19:34:56.61Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/42/b2/7d31f2af5591d217d71d37d044ef5412945a8a8e98d5a2a8ae4fd9cd4489/cryptography-44.0.3-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af653022a0c25ef2e3ffb2c673a50e5a0d02fecc41608f4954176f1933b12359", size = 3689263, upload-time = "2025-05-02T19:34:58.591Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/25/50/c0dfb9d87ae88ccc01aad8eb93e23cfbcea6a6a106a9b63a7b14c1f93c75/cryptography-44.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:157f1f3b8d941c2bd8f3ffee0af9b049c9665c39d3da9db2dc338feca5e98a43", size = 4196198, upload-time = "2025-05-02T19:35:00.988Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/c9/55c6b8794a74da652690c898cb43906310a3e4e4f6ee0b5f8b3b3e70c441/cryptography-44.0.3-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:c6cd67722619e4d55fdb42ead64ed8843d64638e9c07f4011163e46bc512cf01", size = 3966502, upload-time = "2025-05-02T19:35:03.091Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/f7/7cb5488c682ca59a02a32ec5f975074084db4c983f849d47b7b67cc8697a/cryptography-44.0.3-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:b424563394c369a804ecbee9b06dfb34997f19d00b3518e39f83a5642618397d", size = 4196173, upload-time = "2025-05-02T19:35:05.018Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d2/0b/2f789a8403ae089b0b121f8f54f4a3e5228df756e2146efdf4a09a3d5083/cryptography-44.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c91fc8e8fd78af553f98bc7f2a1d8db977334e4eea302a4bfd75b9461c2d8904", size = 4087713, upload-time = "2025-05-02T19:35:07.187Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1d/aa/330c13655f1af398fc154089295cf259252f0ba5df93b4bc9d9c7d7f843e/cryptography-44.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:25cd194c39fa5a0aa4169125ee27d1172097857b27109a45fadc59653ec06f44", size = 4299064, upload-time = "2025-05-02T19:35:08.879Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/a8/8c540a421b44fd267a7d58a1fd5f072a552d72204a3f08194f98889de76d/cryptography-44.0.3-cp37-abi3-win32.whl", hash = "sha256:3be3f649d91cb182c3a6bd336de8b61a0a71965bd13d1a04a0e15b39c3d5809d", size = 2773887, upload-time = "2025-05-02T19:35:10.41Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b9/0d/c4b1657c39ead18d76bbd122da86bd95bdc4095413460d09544000a17d56/cryptography-44.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:3883076d5c4cc56dbef0b898a74eb6992fdac29a7b9013870b34efe4ddb39a0d", size = 3209737, upload-time = "2025-05-02T19:35:12.12Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/34/a3/ad08e0bcc34ad436013458d7528e83ac29910943cea42ad7dd4141a27bbb/cryptography-44.0.3-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:5639c2b16764c6f76eedf722dbad9a0914960d3489c0cc38694ddf9464f1bb2f", size = 6673501, upload-time = "2025-05-02T19:35:13.775Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/f0/7491d44bba8d28b464a5bc8cc709f25a51e3eac54c0a4444cf2473a57c37/cryptography-44.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3ffef566ac88f75967d7abd852ed5f182da252d23fac11b4766da3957766759", size = 3960307, upload-time = "2025-05-02T19:35:15.917Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f7/c8/e5c5d0e1364d3346a5747cdcd7ecbb23ca87e6dea4f942a44e88be349f06/cryptography-44.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:192ed30fac1728f7587c6f4613c29c584abdc565d7417c13904708db10206645", size = 4170876, upload-time = "2025-05-02T19:35:18.138Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/73/96/025cb26fc351d8c7d3a1c44e20cf9a01e9f7cf740353c9c7a17072e4b264/cryptography-44.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7d5fe7195c27c32a64955740b949070f21cba664604291c298518d2e255931d2", size = 3964127, upload-time = "2025-05-02T19:35:19.864Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/01/44/eb6522db7d9f84e8833ba3bf63313f8e257729cf3a8917379473fcfd6601/cryptography-44.0.3-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3f07943aa4d7dad689e3bb1638ddc4944cc5e0921e3c227486daae0e31a05e54", size = 3689164, upload-time = "2025-05-02T19:35:21.449Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/fb/d61a4defd0d6cee20b1b8a1ea8f5e25007e26aeb413ca53835f0cae2bcd1/cryptography-44.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cb90f60e03d563ca2445099edf605c16ed1d5b15182d21831f58460c48bffb93", size = 4198081, upload-time = "2025-05-02T19:35:23.187Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1b/50/457f6911d36432a8811c3ab8bd5a6090e8d18ce655c22820994913dd06ea/cryptography-44.0.3-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:ab0b005721cc0039e885ac3503825661bd9810b15d4f374e473f8c89b7d5460c", size = 3967716, upload-time = "2025-05-02T19:35:25.426Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/6e/dca39d553075980ccb631955c47b93d87d27f3596da8d48b1ae81463d915/cryptography-44.0.3-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:3bb0847e6363c037df8f6ede57d88eaf3410ca2267fb12275370a76f85786a6f", size = 4197398, upload-time = "2025-05-02T19:35:27.678Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/9d/d1f2fe681eabc682067c66a74addd46c887ebacf39038ba01f8860338d3d/cryptography-44.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b0cc66c74c797e1db750aaa842ad5b8b78e14805a9b5d1348dc603612d3e3ff5", size = 4087900, upload-time = "2025-05-02T19:35:29.312Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/f5/3599e48c5464580b73b236aafb20973b953cd2e7b44c7c2533de1d888446/cryptography-44.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6866df152b581f9429020320e5eb9794c8780e90f7ccb021940d7f50ee00ae0b", size = 4301067, upload-time = "2025-05-02T19:35:31.547Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/6c/d2c48c8137eb39d0c193274db5c04a75dab20d2f7c3f81a7dcc3a8897701/cryptography-44.0.3-cp39-abi3-win32.whl", hash = "sha256:c138abae3a12a94c75c10499f1cbae81294a6f983b3af066390adee73f433028", size = 2775467, upload-time = "2025-05-02T19:35:33.805Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/ad/51f212198681ea7b0deaaf8846ee10af99fba4e894f67b353524eab2bbe5/cryptography-44.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:5d186f32e52e66994dce4f766884bcb9c68b8da62d61d9d215bfe5fb56d21334", size = 3210375, upload-time = "2025-05-02T19:35:35.369Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/82/b2/2345dc595998caa6f68adf84e8f8b50d18e9fc4638d32b22ea8daedd4b7a/cryptography-45.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:7573d9eebaeceeb55285205dbbb8753ac1e962af3d9640791d12b36864065e71", size = 7056239, upload-time = "2025-05-25T14:16:12.22Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/3d/ac361649a0bfffc105e2298b720d8b862330a767dab27c06adc2ddbef96a/cryptography-45.0.3-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d377dde61c5d67eb4311eace661c3efda46c62113ff56bf05e2d679e02aebb5b", size = 4205541, upload-time = "2025-05-25T14:16:14.333Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/70/3e/c02a043750494d5c445f769e9c9f67e550d65060e0bfce52d91c1362693d/cryptography-45.0.3-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fae1e637f527750811588e4582988932c222f8251f7b7ea93739acb624e1487f", size = 4433275, upload-time = "2025-05-25T14:16:16.421Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/7a/9af0bfd48784e80eef3eb6fd6fde96fe706b4fc156751ce1b2b965dada70/cryptography-45.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ca932e11218bcc9ef812aa497cdf669484870ecbcf2d99b765d6c27a86000942", size = 4209173, upload-time = "2025-05-25T14:16:18.163Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/31/5f/d6f8753c8708912df52e67969e80ef70b8e8897306cd9eb8b98201f8c184/cryptography-45.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af3f92b1dc25621f5fad065288a44ac790c5798e986a34d393ab27d2b27fcff9", size = 3898150, upload-time = "2025-05-25T14:16:20.34Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/50/f256ab79c671fb066e47336706dc398c3b1e125f952e07d54ce82cf4011a/cryptography-45.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2f8f8f0b73b885ddd7f3d8c2b2234a7d3ba49002b0223f58cfde1bedd9563c56", size = 4466473, upload-time = "2025-05-25T14:16:22.605Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/e7/312428336bb2df0848d0768ab5a062e11a32d18139447a76dfc19ada8eed/cryptography-45.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9cc80ce69032ffa528b5e16d217fa4d8d4bb7d6ba8659c1b4d74a1b0f4235fca", size = 4211890, upload-time = "2025-05-25T14:16:24.738Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/53/8a130e22c1e432b3c14896ec5eb7ac01fb53c6737e1d705df7e0efb647c6/cryptography-45.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:c824c9281cb628015bfc3c59335163d4ca0540d49de4582d6c2637312907e4b1", size = 4466300, upload-time = "2025-05-25T14:16:26.768Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/75/6bb6579688ef805fd16a053005fce93944cdade465fc92ef32bbc5c40681/cryptography-45.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:5833bb4355cb377ebd880457663a972cd044e7f49585aee39245c0d592904578", size = 4332483, upload-time = "2025-05-25T14:16:28.316Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/11/2538f4e1ce05c6c4f81f43c1ef2bd6de7ae5e24ee284460ff6c77e42ca77/cryptography-45.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9bb5bf55dcb69f7067d80354d0a348368da907345a2c448b0babc4215ccd3497", size = 4573714, upload-time = "2025-05-25T14:16:30.474Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/bb/e86e9cf07f73a98d84a4084e8fd420b0e82330a901d9cac8149f994c3417/cryptography-45.0.3-cp311-abi3-win32.whl", hash = "sha256:3ad69eeb92a9de9421e1f6685e85a10fbcfb75c833b42cc9bc2ba9fb00da4710", size = 2934752, upload-time = "2025-05-25T14:16:32.204Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/75/063bc9ddc3d1c73e959054f1fc091b79572e716ef74d6caaa56e945b4af9/cryptography-45.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:97787952246a77d77934d41b62fb1b6f3581d83f71b44796a4158d93b8f5c490", size = 3412465, upload-time = "2025-05-25T14:16:33.888Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/9b/04ead6015229a9396890d7654ee35ef630860fb42dc9ff9ec27f72157952/cryptography-45.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:c92519d242703b675ccefd0f0562eb45e74d438e001f8ab52d628e885751fb06", size = 7031892, upload-time = "2025-05-25T14:16:36.214Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/c7/c7d05d0e133a09fc677b8a87953815c522697bdf025e5cac13ba419e7240/cryptography-45.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5edcb90da1843df85292ef3a313513766a78fbbb83f584a5a58fb001a5a9d57", size = 4196181, upload-time = "2025-05-25T14:16:37.934Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/7a/6ad3aa796b18a683657cef930a986fac0045417e2dc428fd336cfc45ba52/cryptography-45.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38deed72285c7ed699864f964a3f4cf11ab3fb38e8d39cfcd96710cd2b5bb716", size = 4423370, upload-time = "2025-05-25T14:16:39.502Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/58/ec1461bfcb393525f597ac6a10a63938d18775b7803324072974b41a926b/cryptography-45.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5555365a50efe1f486eed6ac7062c33b97ccef409f5970a0b6f205a7cfab59c8", size = 4197839, upload-time = "2025-05-25T14:16:41.322Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d4/3d/5185b117c32ad4f40846f579369a80e710d6146c2baa8ce09d01612750db/cryptography-45.0.3-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9e4253ed8f5948a3589b3caee7ad9a5bf218ffd16869c516535325fece163dcc", size = 3886324, upload-time = "2025-05-25T14:16:43.041Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/85/caba91a57d291a2ad46e74016d1f83ac294f08128b26e2a81e9b4f2d2555/cryptography-45.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cfd84777b4b6684955ce86156cfb5e08d75e80dc2585e10d69e47f014f0a5342", size = 4450447, upload-time = "2025-05-25T14:16:44.759Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/d1/164e3c9d559133a38279215c712b8ba38e77735d3412f37711b9f8f6f7e0/cryptography-45.0.3-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:a2b56de3417fd5f48773ad8e91abaa700b678dc7fe1e0c757e1ae340779acf7b", size = 4200576, upload-time = "2025-05-25T14:16:46.438Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/7a/e002d5ce624ed46dfc32abe1deff32190f3ac47ede911789ee936f5a4255/cryptography-45.0.3-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:57a6500d459e8035e813bd8b51b671977fb149a8c95ed814989da682314d0782", size = 4450308, upload-time = "2025-05-25T14:16:48.228Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/87/ad/3fbff9c28cf09b0a71e98af57d74f3662dea4a174b12acc493de00ea3f28/cryptography-45.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f22af3c78abfbc7cbcdf2c55d23c3e022e1a462ee2481011d518c7fb9c9f3d65", size = 4325125, upload-time = "2025-05-25T14:16:49.844Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/b4/51417d0cc01802304c1984d76e9592f15e4801abd44ef7ba657060520bf0/cryptography-45.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:232954730c362638544758a8160c4ee1b832dc011d2c41a306ad8f7cccc5bb0b", size = 4560038, upload-time = "2025-05-25T14:16:51.398Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/80/38/d572f6482d45789a7202fb87d052deb7a7b136bf17473ebff33536727a2c/cryptography-45.0.3-cp37-abi3-win32.whl", hash = "sha256:cb6ab89421bc90e0422aca911c69044c2912fc3debb19bb3c1bfe28ee3dff6ab", size = 2924070, upload-time = "2025-05-25T14:16:53.472Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/91/5a/61f39c0ff4443651cc64e626fa97ad3099249152039952be8f344d6b0c86/cryptography-45.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:d54ae41e6bd70ea23707843021c778f151ca258081586f0cfa31d936ae43d1b2", size = 3395005, upload-time = "2025-05-25T14:16:55.134Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1118,11 +1118,12 @@ s3 = [
|
||||
|
||||
[[package]]
|
||||
name = "django-tenants"
|
||||
version = "3.7.0"
|
||||
source = { git = "https://github.com/goauthentik/django-tenants.git?branch=authentik-fixes#156e53a6f5902d74b73dd9d0192fffaa2587a740" }
|
||||
version = "3.8.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "django" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a8/7b/22e3bb79d48e5a4fdcacdcdc27bbc5c2523a2b7892b440bfe229f313d823/django_tenants-3.8.0.tar.gz", hash = "sha256:07d009d5d01be2d65c3f5ddbf323d58d1228838fc1a64fded15c8e5c6f41cf8f", size = 154307, upload-time = "2025-05-23T16:07:24.307Z" }
|
||||
|
||||
[[package]]
|
||||
name = "djangorestframework"
|
||||
@ -1396,7 +1397,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "google-api-python-client"
|
||||
version = "2.169.0"
|
||||
version = "2.170.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "google-api-core" },
|
||||
@ -1405,9 +1406,9 @@ dependencies = [
|
||||
{ name = "httplib2" },
|
||||
{ name = "uritemplate" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/4f/e6/787c24738fc7c99de9289abe60bd64591800ae1cdf60db7b87e0e6ef9cdd/google_api_python_client-2.169.0.tar.gz", hash = "sha256:0585bb97bd5f5bf3ed8d4bf624593e4c5a14d06c811d1952b07a1f94b4d12c51", size = 12811341, upload-time = "2025-04-29T15:46:05.603Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/db/86/1bd09aea2664a46bc65713cb7876381ec8949a4b1e71be97dfc359c79781/google_api_python_client-2.170.0.tar.gz", hash = "sha256:75f3a1856f11418ea3723214e0abc59d9b217fd7ed43dcf743aab7f06ab9e2b1", size = 12971933, upload-time = "2025-05-22T20:39:52.802Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2d/bd/6aa93c38756cc9fc63262e0dc3d3f1ff7241ce6f413a25ad6e4a9c98b473/google_api_python_client-2.169.0-py3-none-any.whl", hash = "sha256:dae3e882dc0e6f28e60cf09c1f13fedfd881db84f824dd418aa9e44def2fe00d", size = 13323742, upload-time = "2025-04-29T15:46:02.521Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/ab/928fb4551ce9c791de96b0681924d46de9a5b50931394fd19850383a08a1/google_api_python_client-2.170.0-py3-none-any.whl", hash = "sha256:7bf518a0527ad23322f070fa69f4f24053170d5c766821dc970ff0571ec22748", size = 13490660, upload-time = "2025-05-22T20:39:49.834Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2065,7 +2066,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "msgraph-sdk"
|
||||
version = "1.30.0"
|
||||
version = "1.31.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "azure-identity" },
|
||||
@ -2075,9 +2076,9 @@ dependencies = [
|
||||
{ name = "microsoft-kiota-serialization-text" },
|
||||
{ name = "msgraph-core" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e9/4a/4ff19671f6ea06f98fb2405f73a90350e4719ccc692e85e9e0c2fa066826/msgraph_sdk-1.30.0.tar.gz", hash = "sha256:59e30af6d7244c9009146d620c331e169701b651317746b16f561e2e2452e73f", size = 6608744, upload-time = "2025-05-13T13:09:12.594Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d3/1c/5afdf21f92840c7029f0fdb6c2ead7373b1fcdc3c4279fe556a2fc3702a2/msgraph_sdk-1.31.0.tar.gz", hash = "sha256:7ae5f29152251f61c1fc19cca6389dd03b0120b179ddf39d8ab8cdfed7952dba", size = 6626610, upload-time = "2025-05-20T13:15:08.062Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/70/95/451ec4db8a924274a1f7260809ea03fe9c2b446d84dc5238e92e49a1b522/msgraph_sdk-1.30.0-py3-none-any.whl", hash = "sha256:6748f5cdb5ddbcff9e4f3fb073dd0a604cb00e1cf285dd0fea6969c93ba8282f", size = 27140767, upload-time = "2025-05-13T13:09:07.718Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/b9/099b28478575126ec26bd61ff0931fb291263ac813afb8baf4b4cc30c6fc/msgraph_sdk-1.31.0-py3-none-any.whl", hash = "sha256:bb2edfe17c377f37bbf2e155fc915171763d49e1cf93b665bafd721a85220dc5", size = 27185846, upload-time = "2025-05-20T13:15:05.307Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2473,7 +2474,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.11.4"
|
||||
version = "2.11.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "annotated-types" },
|
||||
@ -2481,9 +2482,9 @@ dependencies = [
|
||||
{ name = "typing-extensions" },
|
||||
{ name = "typing-inspection" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/77/ab/5250d56ad03884ab5efd07f734203943c8a8ab40d551e208af81d0257bf2/pydantic-2.11.4.tar.gz", hash = "sha256:32738d19d63a226a52eed76645a98ee07c1f410ee41d93b4afbfa85ed8111c2d", size = 786540, upload-time = "2025-04-29T20:38:55.02Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f0/86/8ce9040065e8f924d642c58e4a344e33163a07f6b57f836d0d734e0ad3fb/pydantic-2.11.5.tar.gz", hash = "sha256:7f853db3d0ce78ce8bbb148c401c2cdd6431b3473c0cdff2755c7690952a7b7a", size = 787102, upload-time = "2025-05-22T21:18:08.761Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/12/46b65f3534d099349e38ef6ec98b1a5a81f42536d17e0ba382c28c67ba67/pydantic-2.11.4-py3-none-any.whl", hash = "sha256:d9615eaa9ac5a063471da949c8fc16376a84afb5024688b3ff885693506764eb", size = 443900, upload-time = "2025-04-29T20:38:52.724Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/69/831ed22b38ff9b4b64b66569f0e5b7b97cf3638346eb95a2147fdb49ad5f/pydantic-2.11.5-py3-none-any.whl", hash = "sha256:f9c26ba06f9747749ca1e5c94d6a85cb84254577553c8785576fd38fa64dc0f7", size = 444229, upload-time = "2025-05-22T21:18:06.329Z" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
@ -2576,14 +2577,14 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "pyopenssl"
|
||||
version = "25.0.0"
|
||||
version = "25.1.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cryptography" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/9f/26/e25b4a374b4639e0c235527bbe31c0524f26eda701d79456a7e1877f4cc5/pyopenssl-25.0.0.tar.gz", hash = "sha256:cd2cef799efa3936bb08e8ccb9433a575722b9dd986023f1cabc4ae64e9dac16", size = 179573, upload-time = "2025-01-12T17:22:48.897Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/04/8c/cd89ad05804f8e3c17dea8f178c3f40eeab5694c30e0c9f5bcd49f576fc3/pyopenssl-25.1.0.tar.gz", hash = "sha256:8d031884482e0c67ee92bf9a4d8cceb08d92aba7136432ffb0703c5280fc205b", size = 179937, upload-time = "2025-05-17T16:28:31.31Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/d7/eb76863d2060dcbe7c7e6cccfd95ac02ea0b9acc37745a0d99ff6457aefb/pyOpenSSL-25.0.0-py3-none-any.whl", hash = "sha256:424c247065e46e76a37411b9ab1782541c23bb658bf003772c3405fbaa128e90", size = 56453, upload-time = "2025-01-12T17:22:43.44Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/80/28/2659c02301b9500751f8d42f9a6632e1508aa5120de5e43042b8b30f8d5d/pyopenssl-25.1.0-py3-none-any.whl", hash = "sha256:2b11f239acc47ac2e5aca04fd7fa829800aeee22a2eb30d744572a157bd8a1ab", size = 56771, upload-time = "2025-05-17T16:28:29.197Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4,30 +4,27 @@
|
||||
* @import {
|
||||
* OnLoadArgs,
|
||||
* OnLoadResult,
|
||||
* OnResolveArgs,
|
||||
* OnResolveResult,
|
||||
* Plugin,
|
||||
* PluginBuild
|
||||
* } from "esbuild"
|
||||
*/
|
||||
import { MonoRepoRoot } from "@goauthentik/core/paths/node";
|
||||
import * as fs from "node:fs/promises";
|
||||
import * as path from "node:path";
|
||||
|
||||
/**
|
||||
* @typedef {Omit<OnLoadArgs, 'pluginData'> & LoadDataFields} LoadData
|
||||
* Data passed to `onload`.
|
||||
* @typedef {Omit<OnLoadArgs, 'pluginData'> & LoadDataFields} LoadData Data passed to `onload`.
|
||||
*
|
||||
* @typedef LoadDataFields
|
||||
* Extra fields given in `data` to `onload`.
|
||||
* @property {PluginData | null | undefined} [pluginData]
|
||||
* Plugin data.
|
||||
* @typedef LoadDataFields Extra fields given in `data` to `onload`.
|
||||
* @property {PluginData | null | undefined} [pluginData] Plugin data.
|
||||
*
|
||||
*
|
||||
* @typedef PluginData
|
||||
* Extra data passed.
|
||||
* @property {Buffer | string | null | undefined} [contents]
|
||||
* File contents.
|
||||
* @typedef PluginData Extra data passed.
|
||||
* @property {Buffer | string | null | undefined} [contents] File contents.
|
||||
*/
|
||||
|
||||
const name = "mdx-plugin";
|
||||
const pluginName = "mdx-plugin";
|
||||
|
||||
/**
|
||||
* @typedef MDXPluginOptions
|
||||
@ -38,28 +35,35 @@ const name = "mdx-plugin";
|
||||
/**
|
||||
* Bundle MDX into JSON modules.
|
||||
*
|
||||
* @param {MDXPluginOptions} options Options.
|
||||
* @returns {Plugin} Plugin.
|
||||
* @param {MDXPluginOptions} options
|
||||
* @returns {Plugin}
|
||||
*/
|
||||
export function mdxPlugin({ root }) {
|
||||
return { name, setup };
|
||||
// TODO: Replace with `resolvePackage` after NPM Workspaces support is added.
|
||||
const docsPackageRoot = path.resolve(MonoRepoRoot, "website");
|
||||
|
||||
/**
|
||||
* @param {PluginBuild} build
|
||||
* Build.
|
||||
* @returns {undefined}
|
||||
* Nothing.
|
||||
*/
|
||||
function setup(build) {
|
||||
build.onLoad({ filter: /\.mdx?$/ }, onload);
|
||||
/**
|
||||
* @param {OnResolveArgs} args
|
||||
* @returns {Promise<OnResolveResult>}
|
||||
*/
|
||||
async function resolveListener(args) {
|
||||
if (!args.path.startsWith("~")) return args;
|
||||
|
||||
return {
|
||||
path: path.resolve(docsPackageRoot, args.path.slice(1)),
|
||||
pluginName,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {LoadData} data
|
||||
* Data.
|
||||
* @returns {Promise<OnLoadResult>}
|
||||
* Result.
|
||||
*/
|
||||
async function onload(data) {
|
||||
async function loadListener(data) {
|
||||
const content = String(
|
||||
data.pluginData &&
|
||||
data.pluginData.contents !== null &&
|
||||
@ -77,7 +81,16 @@ export function mdxPlugin({ root }) {
|
||||
return {
|
||||
contents: JSON.stringify({ content, publicPath, publicDirectory }),
|
||||
loader: "file",
|
||||
pluginName,
|
||||
};
|
||||
}
|
||||
|
||||
build.onResolve({ filter: /\.mdx?$/ }, resolveListener);
|
||||
build.onLoad({ filter: /\.mdx?$/ }, loadListener);
|
||||
}
|
||||
|
||||
return {
|
||||
name: pluginName,
|
||||
setup,
|
||||
};
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user