Compare commits
	
		
			75 Commits
		
	
	
		
			consistent
			...
			endpoints
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 19c5b28cb2 | |||
| 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 | 
							
								
								
									
										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.7 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) | ||||
|  | ||||
| @ -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" | ||||
|  | ||||
							
								
								
									
										0
									
								
								authentik/endpoints/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								authentik/endpoints/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										12
									
								
								authentik/endpoints/apps.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								authentik/endpoints/apps.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| """authentik endpoints app config""" | ||||
|  | ||||
| from authentik.blueprints.apps import ManagedAppConfig | ||||
|  | ||||
|  | ||||
| class AuthentikEndpointsConfig(ManagedAppConfig): | ||||
|     """authentik endpoints app config""" | ||||
|  | ||||
|     name = "authentik.endpoints" | ||||
|     label = "authentik_endpoints" | ||||
|     verbose_name = "authentik Endpoints" | ||||
|     default = True | ||||
							
								
								
									
										47
									
								
								authentik/endpoints/common_data.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								authentik/endpoints/common_data.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | ||||
| from enum import Enum | ||||
|  | ||||
| from pydantic import BaseModel | ||||
|  | ||||
|  | ||||
| class UNSUPPORTED(BaseModel): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| class OSFamily(Enum): | ||||
|  | ||||
|     linux = "linux" | ||||
|     unix = "unix" | ||||
|     bsd = "bsd" | ||||
|     windows = "windows" | ||||
|     macOS = "mac_os" | ||||
|     android = "android" | ||||
|     iOS = "i_os" | ||||
|     other = "other" | ||||
|  | ||||
| class CommonDeviceData(BaseModel): | ||||
|     class Disk(BaseModel): | ||||
|         encryption: bool | ||||
|  | ||||
|     class OS(BaseModel): | ||||
|         firewall_enabled: bool | ||||
|         family: OSFamily | ||||
|         name: str | ||||
|         version: str | ||||
|  | ||||
|     class Network(BaseModel): | ||||
|         hostname: str | ||||
|         dns_servers: list[str] | ||||
|  | ||||
|     class Hardware(BaseModel): | ||||
|         model: str | ||||
|         manufacturer: str | ||||
|  | ||||
|     class Software(BaseModel): | ||||
|         name: str | ||||
|         version: str | ||||
|  | ||||
|     os: OS | UNSUPPORTED | ||||
|     disks: list[Disk] | UNSUPPORTED | ||||
|     network: Network | UNSUPPORTED | ||||
|     hardware: Hardware | UNSUPPORTED | ||||
|     software: list[Software] | UNSUPPORTED | ||||
							
								
								
									
										16
									
								
								authentik/endpoints/connector.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								authentik/endpoints/connector.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| from authentik.blueprints import models | ||||
|  | ||||
|  | ||||
| class EnrollmentMethods(models.TextChoices): | ||||
|     AUTOMATIC_USER = "automatic_user"  # Automatically enrolled through user action | ||||
|     AUTOMATIC_API = "automatic_api"  # Automatically enrolled through connector integration | ||||
|     MANUAL_USER = "manual_user"  # Manually enrolled | ||||
|  | ||||
|  | ||||
| class BaseConnector: | ||||
|  | ||||
|     def __init__(self) -> None: | ||||
|         pass | ||||
|  | ||||
|     def supported_enrollment_methods(self) -> list[EnrollmentMethods]: | ||||
|         return [] | ||||
							
								
								
									
										0
									
								
								authentik/endpoints/connectors/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								authentik/endpoints/connectors/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| from authentik.endpoints.connector import BaseConnector, EnrollmentMethods | ||||
|  | ||||
|  | ||||
| class GoogleChromeConnector(BaseConnector): | ||||
|  | ||||
|     def supported_enrollment_methods(self) -> list[EnrollmentMethods]: | ||||
|         return [EnrollmentMethods.AUTOMATIC_USER] | ||||
							
								
								
									
										7
									
								
								authentik/endpoints/connectors/google_chrome/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								authentik/endpoints/connectors/google_chrome/models.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| from django.db import models | ||||
|  | ||||
| from authentik.endpoints.models import Connector | ||||
|  | ||||
|  | ||||
| class GoogleChromeConnector(Connector): | ||||
|     credentials = models.JSONField() | ||||
							
								
								
									
										125
									
								
								authentik/endpoints/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								authentik/endpoints/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,125 @@ | ||||
| # Generated by Django 5.0.9 on 2024-09-24 19:16 | ||||
|  | ||||
| import django.db.models.deletion | ||||
| import uuid | ||||
| from django.conf import settings | ||||
| from django.db import migrations, models | ||||
|  | ||||
|  | ||||
| class Migration(migrations.Migration): | ||||
|  | ||||
|     initial = True | ||||
|  | ||||
|     dependencies = [ | ||||
|         migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||||
|     ] | ||||
|  | ||||
|     operations = [ | ||||
|         migrations.CreateModel( | ||||
|             name="Connector", | ||||
|             fields=[ | ||||
|                 ( | ||||
|                     "id", | ||||
|                     models.AutoField( | ||||
|                         auto_created=True, primary_key=True, serialize=False, verbose_name="ID" | ||||
|                     ), | ||||
|                 ), | ||||
|                 ("connector_uuid", models.UUIDField(default=uuid.uuid4)), | ||||
|                 ("name", models.TextField()), | ||||
|                 ( | ||||
|                     "enrollment_method", | ||||
|                     models.TextField( | ||||
|                         choices=[ | ||||
|                             ("automatic_user", "Automatic User"), | ||||
|                             ("automatic_api", "Automatic Api"), | ||||
|                             ("manual_user", "Manual User"), | ||||
|                         ] | ||||
|                     ), | ||||
|                 ), | ||||
|             ], | ||||
|             options={ | ||||
|                 "abstract": False, | ||||
|             }, | ||||
|         ), | ||||
|         migrations.CreateModel( | ||||
|             name="Device", | ||||
|             fields=[ | ||||
|                 ( | ||||
|                     "id", | ||||
|                     models.AutoField( | ||||
|                         auto_created=True, primary_key=True, serialize=False, verbose_name="ID" | ||||
|                     ), | ||||
|                 ), | ||||
|                 ("device_uuid", models.UUIDField(default=uuid.uuid4)), | ||||
|                 ("identifier", models.TextField(unique=True)), | ||||
|             ], | ||||
|             options={ | ||||
|                 "abstract": False, | ||||
|             }, | ||||
|         ), | ||||
|         migrations.CreateModel( | ||||
|             name="DeviceConnection", | ||||
|             fields=[ | ||||
|                 ( | ||||
|                     "id", | ||||
|                     models.AutoField( | ||||
|                         auto_created=True, primary_key=True, serialize=False, verbose_name="ID" | ||||
|                     ), | ||||
|                 ), | ||||
|                 ("device_connection_uuid", models.UUIDField(default=uuid.uuid4)), | ||||
|                 ("data", models.JSONField(default=dict)), | ||||
|                 ( | ||||
|                     "connection", | ||||
|                     models.ForeignKey( | ||||
|                         on_delete=django.db.models.deletion.CASCADE, | ||||
|                         to="authentik_endpoints.connector", | ||||
|                     ), | ||||
|                 ), | ||||
|                 ( | ||||
|                     "device", | ||||
|                     models.ForeignKey( | ||||
|                         on_delete=django.db.models.deletion.CASCADE, to="authentik_endpoints.device" | ||||
|                     ), | ||||
|                 ), | ||||
|             ], | ||||
|         ), | ||||
|         migrations.AddField( | ||||
|             model_name="device", | ||||
|             name="connections", | ||||
|             field=models.ManyToManyField( | ||||
|                 through="authentik_endpoints.DeviceConnection", to="authentik_endpoints.connector" | ||||
|             ), | ||||
|         ), | ||||
|         migrations.CreateModel( | ||||
|             name="DeviceUser", | ||||
|             fields=[ | ||||
|                 ( | ||||
|                     "id", | ||||
|                     models.AutoField( | ||||
|                         auto_created=True, primary_key=True, serialize=False, verbose_name="ID" | ||||
|                     ), | ||||
|                 ), | ||||
|                 ("device_user_uuid", models.UUIDField(default=uuid.uuid4)), | ||||
|                 ("is_primary", models.BooleanField()), | ||||
|                 ( | ||||
|                     "device", | ||||
|                     models.ForeignKey( | ||||
|                         on_delete=django.db.models.deletion.CASCADE, to="authentik_endpoints.device" | ||||
|                     ), | ||||
|                 ), | ||||
|                 ( | ||||
|                     "user", | ||||
|                     models.ForeignKey( | ||||
|                         on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL | ||||
|                     ), | ||||
|                 ), | ||||
|             ], | ||||
|         ), | ||||
|         migrations.AddField( | ||||
|             model_name="device", | ||||
|             name="users", | ||||
|             field=models.ManyToManyField( | ||||
|                 through="authentik_endpoints.DeviceUser", to=settings.AUTH_USER_MODEL | ||||
|             ), | ||||
|         ), | ||||
|     ] | ||||
							
								
								
									
										0
									
								
								authentik/endpoints/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								authentik/endpoints/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										40
									
								
								authentik/endpoints/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								authentik/endpoints/models.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | ||||
| from uuid import uuid4 | ||||
|  | ||||
| from django.db import models | ||||
| from django.utils.functional import cached_property | ||||
|  | ||||
| from authentik.core.models import User | ||||
| from authentik.endpoints.common_data import CommonDeviceData | ||||
| from authentik.lib.models import SerializerModel | ||||
|  | ||||
|  | ||||
| class Device(SerializerModel): | ||||
|     device_uuid = models.UUIDField(default=uuid4) | ||||
|  | ||||
|     identifier = models.TextField(unique=True) | ||||
|     users = models.ManyToManyField(User, through="DeviceUser") | ||||
|     connections = models.ManyToManyField("Connector", through="DeviceConnection") | ||||
|  | ||||
|     @cached_property | ||||
|     def data(self) -> CommonDeviceData: | ||||
|         pass | ||||
|  | ||||
|  | ||||
| class DeviceUser(models.Model): | ||||
|     device_user_uuid = models.UUIDField(default=uuid4) | ||||
|     device = models.ForeignKey("Device", on_delete=models.CASCADE) | ||||
|     user = models.ForeignKey(User, on_delete=models.CASCADE) | ||||
|     is_primary = models.BooleanField() | ||||
|  | ||||
|  | ||||
| class DeviceConnection(models.Model): | ||||
|     device_connection_uuid = models.UUIDField(default=uuid4) | ||||
|     device = models.ForeignKey("Device", on_delete=models.CASCADE) | ||||
|     connection = models.ForeignKey("Connector", on_delete=models.CASCADE) | ||||
|     data = models.JSONField(default=dict) | ||||
|  | ||||
|  | ||||
| class Connector(SerializerModel): | ||||
|     connector_uuid = models.UUIDField(default=uuid4) | ||||
|  | ||||
|     name = models.TextField() | ||||
| @ -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 | ||||
|  | ||||
| @ -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) | ||||
| @ -132,6 +153,15 @@ 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 groups".format(page=page)), | ||||
|                     log_level="info", | ||||
|                     logger=f"{provider._meta.verbose_name}@{object_type}", | ||||
|                 ) | ||||
|             ) | ||||
|         ) | ||||
|         for obj in paginator.page(page).object_list: | ||||
|             obj: Model | ||||
|             try: | ||||
|  | ||||
| @ -384,7 +384,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"]) | ||||
|  | ||||
| @ -73,6 +73,7 @@ TENANT_APPS = [ | ||||
|     "authentik.admin", | ||||
|     "authentik.api", | ||||
|     "authentik.crypto", | ||||
|     "authentik.endpoints", | ||||
|     "authentik.flows", | ||||
|     "authentik.outposts", | ||||
|     "authentik.policies.dummy", | ||||
| @ -424,7 +425,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. | ||||
|  | ||||
| @ -111,6 +111,7 @@ class LDAPSourceSerializer(SourceSerializer): | ||||
|             "sync_parent_group", | ||||
|             "connectivity", | ||||
|             "lookup_groups_from_user", | ||||
|             "delete_not_found_objects", | ||||
|         ] | ||||
|         extra_kwargs = {"bind_password": {"write_only": True}} | ||||
|  | ||||
| @ -147,6 +148,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"), | ||||
|         ), | ||||
|     ] | ||||
| @ -137,6 +137,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 +329,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 +346,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 +369,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,9 +63,9 @@ 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: | ||||
|  | ||||
| @ -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() | ||||
|  | ||||
| @ -308,3 +320,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, | ||||
|  | ||||
| @ -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()): | ||||
|  | ||||
| @ -8180,6 +8180,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": [] | ||||
|  | ||||
| @ -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( | ||||
|  | ||||
										
											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-05-28 11:25+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 | ||||
| @ -2252,6 +2255,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 +2277,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 +2653,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 +3013,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 +3033,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.
										
									
								
							| @ -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 "" | ||||
|  | ||||
| #: 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 "" | ||||
|  | ||||
| #: 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 "重設密碼" | ||||
|  | ||||
							
								
								
									
										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": { | ||||
|  | ||||
| @ -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", | ||||
| @ -43,7 +43,7 @@ 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", | ||||
| @ -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" } | ||||
|  | ||||
|  | ||||
							
								
								
									
										16
									
								
								schema.yml
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								schema.yml
									
									
									
									
									
								
							| @ -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: | ||||
| @ -47922,6 +47926,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 | ||||
| @ -48123,6 +48131,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 | ||||
| @ -53456,6 +53468,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}.") | ||||
|  | ||||
|  | ||||
							
								
								
									
										75
									
								
								uv.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										75
									
								
								uv.lock
									
									
									
										generated
									
									
									
								
							| @ -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" }, | ||||
| @ -303,7 +303,7 @@ 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" }, | ||||
| @ -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" | ||||
| @ -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]] | ||||
| @ -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, | ||||
|     }; | ||||
| } | ||||
|  | ||||
							
								
								
									
										2522
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2522
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -67,6 +67,7 @@ | ||||
|         "#admin/*": "./src/admin/*.js", | ||||
|         "#flow/*.css": "./src/flow/*.css", | ||||
|         "#flow/*": "./src/flow/*.js", | ||||
|         "#locales/*": "./src/locales/*.js", | ||||
|         "#stories/*": "./src/stories/*.js", | ||||
|         "#*/browser": { | ||||
|             "types": "./out/*/browser.d.ts", | ||||
| @ -90,9 +91,9 @@ | ||||
|         "@codemirror/legacy-modes": "^6.4.1", | ||||
|         "@codemirror/theme-one-dark": "^6.1.2", | ||||
|         "@floating-ui/dom": "^1.6.11", | ||||
|         "@formatjs/intl-listformat": "^7.5.7", | ||||
|         "@formatjs/intl-listformat": "^7.7.11", | ||||
|         "@fortawesome/fontawesome-free": "^6.6.0", | ||||
|         "@goauthentik/api": "^2025.4.1-1747687715", | ||||
|         "@goauthentik/api": "^2025.4.1-1748431399", | ||||
|         "@lit/context": "^1.1.2", | ||||
|         "@lit/localize": "^0.12.2", | ||||
|         "@lit/reactive-element": "^2.0.4", | ||||
| @ -110,12 +111,12 @@ | ||||
|         "chartjs-adapter-date-fns": "^3.0.0", | ||||
|         "codemirror": "^6.0.1", | ||||
|         "construct-style-sheets-polyfill": "^3.1.0", | ||||
|         "core-js": "^3.38.1", | ||||
|         "core-js": "^3.42.0", | ||||
|         "country-flag-icons": "^1.5.19", | ||||
|         "date-fns": "^4.1.0", | ||||
|         "deepmerge-ts": "^7.1.5", | ||||
|         "dompurify": "^3.2.6", | ||||
|         "fuse.js": "^7.0.0", | ||||
|         "fuse.js": "^7.1.0", | ||||
|         "guacamole-common-js": "^1.5.0", | ||||
|         "hastscript": "^9.0.1", | ||||
|         "lit": "^3.2.0", | ||||
| @ -143,14 +144,15 @@ | ||||
|         "@eslint/js": "^9.27.0", | ||||
|         "@goauthentik/core": "^1.0.0", | ||||
|         "@goauthentik/esbuild-plugin-live-reload": "^1.0.4", | ||||
|         "@goauthentik/eslint-config": "^1.0.4", | ||||
|         "@goauthentik/prettier-config": "^1.0.4", | ||||
|         "@goauthentik/eslint-config": "^1.0.5", | ||||
|         "@goauthentik/prettier-config": "^1.0.5", | ||||
|         "@goauthentik/tsconfig": "^1.0.4", | ||||
|         "@hcaptcha/types": "^1.0.4", | ||||
|         "@lit/localize-tools": "^0.8.0", | ||||
|         "@storybook/addon-essentials": "^8.6.14", | ||||
|         "@storybook/addon-links": "^8.6.14", | ||||
|         "@storybook/blocks": "^8.6.12", | ||||
|         "@storybook/channels": "^8.6.14", | ||||
|         "@storybook/experimental-addon-test": "^8.6.14", | ||||
|         "@storybook/manager-api": "^8.6.14", | ||||
|         "@storybook/test": "^8.6.14", | ||||
| @ -182,7 +184,7 @@ | ||||
|         "eslint-plugin-wc": "^3.0.1", | ||||
|         "github-slugger": "^2.0.0", | ||||
|         "globals": "^15.10.0", | ||||
|         "knip": "^5.30.6", | ||||
|         "knip": "^5.58.0", | ||||
|         "lit-analyzer": "^2.0.3", | ||||
|         "npm-run-all": "^4.1.5", | ||||
|         "prettier": "^3.3.3", | ||||
| @ -201,9 +203,9 @@ | ||||
|         "@esbuild/darwin-arm64": "^0.25.4", | ||||
|         "@esbuild/linux-arm64": "^0.25.4", | ||||
|         "@esbuild/linux-x64": "^0.25.4", | ||||
|         "@rollup/rollup-darwin-arm64": "^4.41.0", | ||||
|         "@rollup/rollup-linux-arm64-gnu": "^4.41.0", | ||||
|         "@rollup/rollup-linux-x64-gnu": "^4.41.0" | ||||
|         "@rollup/rollup-darwin-arm64": "^4.41.1", | ||||
|         "@rollup/rollup-linux-arm64-gnu": "^4.41.1", | ||||
|         "@rollup/rollup-linux-x64-gnu": "^4.41.1" | ||||
|     }, | ||||
|     "wireit": { | ||||
|         "build": { | ||||
|  | ||||
| @ -45,7 +45,7 @@ | ||||
|         } | ||||
|     }, | ||||
|     "devDependencies": { | ||||
|         "@goauthentik/prettier-config": "^1.0.4", | ||||
|         "@goauthentik/prettier-config": "^1.0.5", | ||||
|         "@goauthentik/tsconfig": "^1.0.4", | ||||
|         "@types/node": "^22.15.21", | ||||
|         "prettier": "^3.3.3", | ||||
|  | ||||
							
								
								
									
										59
									
								
								web/packages/esbuild-plugin-live-reload/.github/README.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								web/packages/esbuild-plugin-live-reload/.github/README.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,59 @@ | ||||
| _An ESBuild development plugin that watches for file changes and triggers automatic browser refreshes._ | ||||
|  | ||||
| ## Quick start | ||||
|  | ||||
| ```sh | ||||
| npm install -D @goauthentik/esbuild-plugin-live-reload | ||||
| # Or with Yarn: | ||||
| yarn add -D @goauthentik/esbuild-plugin-live-reload | ||||
| ``` | ||||
|  | ||||
| ### 1. Configure ESBuild | ||||
|  | ||||
| ```js | ||||
| import { liveReloadPlugin } from "@goauthentik/esbuild-plugin-live-reload"; | ||||
| import esbuild from "esbuild"; | ||||
|  | ||||
| const NodeEnvironment = process.env.NODE_ENV || "development"; | ||||
|  | ||||
| /** | ||||
|  * @type {esbuild.BuildOptions} | ||||
|  */ | ||||
| const buildOptions = { | ||||
|     // ... Your build options. | ||||
|     define: { | ||||
|         "process.env.NODE_ENV": JSON.stringify(NodeEnvironment), | ||||
|     }, | ||||
|     plugins: [ | ||||
|         /** @see {@link LiveReloadPluginOptions} */ | ||||
|         liveReloadPlugin(), | ||||
|     ], | ||||
| }; | ||||
|  | ||||
| const buildContext = await esbuild.context(buildOptions); | ||||
|  | ||||
| await buildContext.rebuild(); | ||||
| await buildContext.watch(); | ||||
| ``` | ||||
|  | ||||
| ### 2. Connect your browser | ||||
|  | ||||
| Add the following import near the beginning of your application's entry point. | ||||
|  | ||||
| ```js | ||||
| if (process.env.NODE_ENV === "development") { | ||||
|     await import("@goauthentik/esbuild-plugin-live-reload/client"); | ||||
| } | ||||
| ``` | ||||
|  | ||||
| That's it! Your browser will now automatically refresh whenever ESBuild finishes rebuilding your code. | ||||
|  | ||||
| ## About authentik | ||||
|  | ||||
| [authentik](https://goauthentik.io) is an open source Identity Provider that unifies your identity needs into a single platform, replacing Okta, Active Directory, and Auth0. | ||||
|  | ||||
| We built this plugin to streamline our development workflow, and we're sharing it with the community. If you have any questions, feature requests, or bug reports, please [open an issue](https://github.com/goauthentik/authentik/issues/new/choose). | ||||
|  | ||||
| ## License | ||||
|  | ||||
| This code is licensed under the [MIT License](https://www.tldrlegal.com/license/mit-license) | ||||
							
								
								
									
										3
									
								
								web/packages/esbuild-plugin-live-reload/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								web/packages/esbuild-plugin-live-reload/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| README.md | ||||
| node_modules | ||||
| _media | ||||
							
								
								
									
										3
									
								
								web/packages/esbuild-plugin-live-reload/.prettierignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								web/packages/esbuild-plugin-live-reload/.prettierignore
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| node_modules | ||||
| ./README.md | ||||
| out | ||||
							
								
								
									
										18
									
								
								web/packages/esbuild-plugin-live-reload/LICENSE.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								web/packages/esbuild-plugin-live-reload/LICENSE.txt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| The MIT License (MIT) | ||||
|  | ||||
| Copyright (c) 2025 Authentik Security, Inc. | ||||
|  | ||||
| Permission is hereby granted, free of charge, to any person obtaining a copy of this software and | ||||
| associated documentation files (the "Software"), to deal in the Software without restriction, | ||||
| including without limitation the rights to use, copy, modify, merge, publish, distribute, | ||||
| sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is | ||||
| furnished to do so, subject to the following conditions: | ||||
|  | ||||
| The above copyright notice and this permission notice shall be included in all copies or substantial | ||||
| portions of the Software. | ||||
|  | ||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT | ||||
| NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||||
| NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES | ||||
| OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||||
| CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
| @ -1,40 +0,0 @@ | ||||
| # `@goauthentik/esbuild-plugin-live-reload` | ||||
|  | ||||
| _A plugin that enables live reloading of ESBuild during development._ | ||||
|  | ||||
| ## Usage | ||||
|  | ||||
| ### Node.js setup | ||||
|  | ||||
| ```js | ||||
| import { liveReloadPlugin } from "@goauthentik/esbuild-plugin-live-reload"; | ||||
| import esbuild from "esbuild"; | ||||
|  | ||||
| const NodeEnvironment = process.env.NODE_ENV || "development"; | ||||
|  | ||||
| /** | ||||
|  * @type {esbuild.BuildOptions} | ||||
|  */ | ||||
| const buildOptions = { | ||||
|     // ... Your build options. | ||||
|     define: { | ||||
|         "process.env.NODE_ENV": JSON.stringify(NodeEnvironment), | ||||
|     }, | ||||
|     plugins: [liveReloadPlugin(/** @see {@link LiveReloadPluginOptions} */)], | ||||
| }; | ||||
|  | ||||
| const buildContext = await esbuild.context(buildOptions); | ||||
|  | ||||
| await buildContext.rebuild(); | ||||
| await buildContext.watch(); | ||||
| ``` | ||||
|  | ||||
| ### Browser setup | ||||
|  | ||||
| ```js | ||||
| // Place this at the beginning of your application's entry point. | ||||
|  | ||||
| if (process.env.NODE_ENV === "development") { | ||||
|     await import("@goauthentik/esbuild-plugin-live-reload/client"); | ||||
| } | ||||
| ``` | ||||
| @ -28,6 +28,8 @@ const log = console.debug.bind(console, logPrefix); | ||||
|  * ``` | ||||
|  * | ||||
|  * @implements {Disposable} | ||||
|  * @category Plugin | ||||
|  * runtime browser | ||||
|  */ | ||||
| export class ESBuildObserver extends EventSource { | ||||
|     /** | ||||
|  | ||||
| @ -1,2 +1,6 @@ | ||||
| /** | ||||
|  * @remarks Live reload plugin for ESBuild. | ||||
|  */ | ||||
|  | ||||
| export * from "./client/index.js"; | ||||
| export * from "./plugin/index.js"; | ||||
|  | ||||
| @ -19,10 +19,12 @@ | ||||
|                 "esbuild": "^0.25.4", | ||||
|                 "prettier": "^3.5.3", | ||||
|                 "prettier-plugin-packagejson": "^2.5.14", | ||||
|                 "typedoc": "^0.28.5", | ||||
|                 "typedoc-plugin-markdown": "^4.6.3", | ||||
|                 "typescript": "^5.8.3" | ||||
|             }, | ||||
|             "engines": { | ||||
|                 "node": ">=22.15.1" | ||||
|                 "node": ">=22" | ||||
|             }, | ||||
|             "peerDependencies": { | ||||
|                 "esbuild": "^0.25.4" | ||||
| @ -569,6 +571,20 @@ | ||||
|                 "node": ">=18" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/@gerrit0/mini-shiki": { | ||||
|             "version": "3.4.2", | ||||
|             "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-3.4.2.tgz", | ||||
|             "integrity": "sha512-3jXo5bNjvvimvdbIhKGfFxSnKCX+MA8wzHv55ptzk/cx8wOzT+BRcYgj8aFN3yTiTs+zvQQiaZFr7Jce1ZG3fw==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "dependencies": { | ||||
|                 "@shikijs/engine-oniguruma": "^3.4.2", | ||||
|                 "@shikijs/langs": "^3.4.2", | ||||
|                 "@shikijs/themes": "^3.4.2", | ||||
|                 "@shikijs/types": "^3.4.2", | ||||
|                 "@shikijs/vscode-textmate": "^10.0.2" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/@goauthentik/prettier-config": { | ||||
|             "version": "1.0.5", | ||||
|             "resolved": "https://registry.npmjs.org/@goauthentik/prettier-config/-/prettier-config-1.0.5.tgz", | ||||
| @ -659,6 +675,55 @@ | ||||
|                 "url": "https://opencollective.com/pkgr" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/@shikijs/engine-oniguruma": { | ||||
|             "version": "3.4.2", | ||||
|             "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.4.2.tgz", | ||||
|             "integrity": "sha512-zcZKMnNndgRa3ORja6Iemsr3DrLtkX3cAF7lTJkdMB6v9alhlBsX9uNiCpqofNrXOvpA3h6lHcLJxgCIhVOU5Q==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "dependencies": { | ||||
|                 "@shikijs/types": "3.4.2", | ||||
|                 "@shikijs/vscode-textmate": "^10.0.2" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/@shikijs/langs": { | ||||
|             "version": "3.4.2", | ||||
|             "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.4.2.tgz", | ||||
|             "integrity": "sha512-H6azIAM+OXD98yztIfs/KH5H4PU39t+SREhmM8LaNXyUrqj2mx+zVkr8MWYqjceSjDw9I1jawm1WdFqU806rMA==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "dependencies": { | ||||
|                 "@shikijs/types": "3.4.2" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/@shikijs/themes": { | ||||
|             "version": "3.4.2", | ||||
|             "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.4.2.tgz", | ||||
|             "integrity": "sha512-qAEuAQh+brd8Jyej2UDDf+b4V2g1Rm8aBIdvt32XhDPrHvDkEnpb7Kzc9hSuHUxz0Iuflmq7elaDuQAP9bHIhg==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "dependencies": { | ||||
|                 "@shikijs/types": "3.4.2" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/@shikijs/types": { | ||||
|             "version": "3.4.2", | ||||
|             "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.4.2.tgz", | ||||
|             "integrity": "sha512-zHC1l7L+eQlDXLnxvM9R91Efh2V4+rN3oMVS2swCBssbj2U/FBwybD1eeLaq8yl/iwT+zih8iUbTBCgGZOYlVg==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "dependencies": { | ||||
|                 "@shikijs/vscode-textmate": "^10.0.2", | ||||
|                 "@types/hast": "^3.0.4" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/@shikijs/vscode-textmate": { | ||||
|             "version": "10.0.2", | ||||
|             "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", | ||||
|             "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", | ||||
|             "dev": true, | ||||
|             "license": "MIT" | ||||
|         }, | ||||
|         "node_modules/@trivago/prettier-plugin-sort-imports": { | ||||
|             "version": "5.2.2", | ||||
|             "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-5.2.2.tgz", | ||||
| @ -694,6 +759,16 @@ | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/@types/hast": { | ||||
|             "version": "3.0.4", | ||||
|             "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", | ||||
|             "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "dependencies": { | ||||
|                 "@types/unist": "*" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/@types/node": { | ||||
|             "version": "22.15.21", | ||||
|             "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz", | ||||
| @ -704,6 +779,37 @@ | ||||
|                 "undici-types": "~6.21.0" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/@types/unist": { | ||||
|             "version": "3.0.3", | ||||
|             "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", | ||||
|             "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", | ||||
|             "dev": true, | ||||
|             "license": "MIT" | ||||
|         }, | ||||
|         "node_modules/argparse": { | ||||
|             "version": "2.0.1", | ||||
|             "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", | ||||
|             "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", | ||||
|             "dev": true, | ||||
|             "license": "Python-2.0" | ||||
|         }, | ||||
|         "node_modules/balanced-match": { | ||||
|             "version": "1.0.2", | ||||
|             "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", | ||||
|             "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", | ||||
|             "dev": true, | ||||
|             "license": "MIT" | ||||
|         }, | ||||
|         "node_modules/brace-expansion": { | ||||
|             "version": "2.0.1", | ||||
|             "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", | ||||
|             "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "dependencies": { | ||||
|                 "balanced-match": "^1.0.0" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/debug": { | ||||
|             "version": "4.4.1", | ||||
|             "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", | ||||
| @ -745,6 +851,19 @@ | ||||
|                 "url": "https://github.com/sponsors/sindresorhus" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/entities": { | ||||
|             "version": "4.5.0", | ||||
|             "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", | ||||
|             "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", | ||||
|             "dev": true, | ||||
|             "license": "BSD-2-Clause", | ||||
|             "engines": { | ||||
|                 "node": ">=0.12" | ||||
|             }, | ||||
|             "funding": { | ||||
|                 "url": "https://github.com/fb55/entities?sponsor=1" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/esbuild": { | ||||
|             "version": "0.25.4", | ||||
|             "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz", | ||||
| @ -865,6 +984,16 @@ | ||||
|                 "node": ">=6" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/linkify-it": { | ||||
|             "version": "5.0.0", | ||||
|             "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", | ||||
|             "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "dependencies": { | ||||
|                 "uc.micro": "^2.0.0" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/lodash": { | ||||
|             "version": "4.17.21", | ||||
|             "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", | ||||
| @ -872,6 +1001,54 @@ | ||||
|             "dev": true, | ||||
|             "license": "MIT" | ||||
|         }, | ||||
|         "node_modules/lunr": { | ||||
|             "version": "2.3.9", | ||||
|             "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", | ||||
|             "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", | ||||
|             "dev": true, | ||||
|             "license": "MIT" | ||||
|         }, | ||||
|         "node_modules/markdown-it": { | ||||
|             "version": "14.1.0", | ||||
|             "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", | ||||
|             "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "dependencies": { | ||||
|                 "argparse": "^2.0.1", | ||||
|                 "entities": "^4.4.0", | ||||
|                 "linkify-it": "^5.0.0", | ||||
|                 "mdurl": "^2.0.0", | ||||
|                 "punycode.js": "^2.3.1", | ||||
|                 "uc.micro": "^2.1.0" | ||||
|             }, | ||||
|             "bin": { | ||||
|                 "markdown-it": "bin/markdown-it.mjs" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/mdurl": { | ||||
|             "version": "2.0.0", | ||||
|             "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", | ||||
|             "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", | ||||
|             "dev": true, | ||||
|             "license": "MIT" | ||||
|         }, | ||||
|         "node_modules/minimatch": { | ||||
|             "version": "9.0.5", | ||||
|             "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", | ||||
|             "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", | ||||
|             "dev": true, | ||||
|             "license": "ISC", | ||||
|             "dependencies": { | ||||
|                 "brace-expansion": "^2.0.1" | ||||
|             }, | ||||
|             "engines": { | ||||
|                 "node": ">=16 || 14 >=14.17" | ||||
|             }, | ||||
|             "funding": { | ||||
|                 "url": "https://github.com/sponsors/isaacs" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/ms": { | ||||
|             "version": "2.1.3", | ||||
|             "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", | ||||
| @ -950,6 +1127,16 @@ | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/punycode.js": { | ||||
|             "version": "2.3.1", | ||||
|             "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", | ||||
|             "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "engines": { | ||||
|                 "node": ">=6" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/semver": { | ||||
|             "version": "7.7.2", | ||||
|             "dev": true, | ||||
| @ -1016,6 +1203,43 @@ | ||||
|                 "url": "https://github.com/sponsors/SuperchupuDev" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/typedoc": { | ||||
|             "version": "0.28.5", | ||||
|             "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.28.5.tgz", | ||||
|             "integrity": "sha512-5PzUddaA9FbaarUzIsEc4wNXCiO4Ot3bJNeMF2qKpYlTmM9TTaSHQ7162w756ERCkXER/+o2purRG6YOAv6EMA==", | ||||
|             "dev": true, | ||||
|             "license": "Apache-2.0", | ||||
|             "dependencies": { | ||||
|                 "@gerrit0/mini-shiki": "^3.2.2", | ||||
|                 "lunr": "^2.3.9", | ||||
|                 "markdown-it": "^14.1.0", | ||||
|                 "minimatch": "^9.0.5", | ||||
|                 "yaml": "^2.7.1" | ||||
|             }, | ||||
|             "bin": { | ||||
|                 "typedoc": "bin/typedoc" | ||||
|             }, | ||||
|             "engines": { | ||||
|                 "node": ">= 18", | ||||
|                 "pnpm": ">= 10" | ||||
|             }, | ||||
|             "peerDependencies": { | ||||
|                 "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/typedoc-plugin-markdown": { | ||||
|             "version": "4.6.3", | ||||
|             "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-4.6.3.tgz", | ||||
|             "integrity": "sha512-86oODyM2zajXwLs4Wok2mwVEfCwCnp756QyhLGX2IfsdRYr1DXLCgJgnLndaMUjJD7FBhnLk2okbNE9PdLxYRw==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "engines": { | ||||
|                 "node": ">= 18" | ||||
|             }, | ||||
|             "peerDependencies": { | ||||
|                 "typedoc": "0.28.x" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/typescript": { | ||||
|             "version": "5.8.3", | ||||
|             "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", | ||||
| @ -1030,10 +1254,30 @@ | ||||
|                 "node": ">=14.17" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/uc.micro": { | ||||
|             "version": "2.1.0", | ||||
|             "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", | ||||
|             "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", | ||||
|             "dev": true, | ||||
|             "license": "MIT" | ||||
|         }, | ||||
|         "node_modules/undici-types": { | ||||
|             "version": "6.21.0", | ||||
|             "dev": true, | ||||
|             "license": "MIT" | ||||
|         }, | ||||
|         "node_modules/yaml": { | ||||
|             "version": "2.8.0", | ||||
|             "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", | ||||
|             "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", | ||||
|             "dev": true, | ||||
|             "license": "ISC", | ||||
|             "bin": { | ||||
|                 "yaml": "bin.mjs" | ||||
|             }, | ||||
|             "engines": { | ||||
|                 "node": ">= 14.6" | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user
	