Compare commits

..

10 Commits

252 changed files with 46635 additions and 73937 deletions

View File

@ -28,15 +28,15 @@ runs:
- name: Setup node
uses: actions/setup-node@v4
with:
node-version-file: web/package.json
node-version-file: package.json
cache: "npm"
cache-dependency-path: web/package-lock.json
cache-dependency-path: package-lock.json
- name: Setup go
uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- name: Setup docker cache
uses: AndreKurait/docker-cache@0fe76702a40db986d9663c24954fc14c6a6031b7
uses: ScribeMD/docker-cache@0.5.0
with:
key: docker-images-${{ runner.os }}-${{ hashFiles('.github/actions/setup/docker-compose.yml', 'Makefile') }}-${{ inputs.postgresql_version }}
- name: Setup dependencies
@ -44,7 +44,7 @@ runs:
run: |
export PSQL_TAG=${{ inputs.postgresql_version }}
docker compose -f .github/actions/setup/docker-compose.yml up -d
cd web && npm ci
npm ci
- name: Generate config
shell: uv run python {0}
run: |

View File

@ -20,8 +20,11 @@ jobs:
token: ${{ steps.generate_token.outputs.token }}
- uses: actions/setup-node@v4
with:
node-version-file: web/package.json
node-version-file: package.json
registry-url: "https://registry.npmjs.org"
- name: Prepare Dependencies
run: |
npm ci
- name: Generate API Client
run: make gen-client-ts
- name: Publish package
@ -32,15 +35,13 @@ jobs:
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
- name: Upgrade /web
working-directory: web
run: |
export VERSION=`node -e 'console.log(require("../gen-ts-api/package.json").version)'`
npm i @goauthentik/api@$VERSION
export VERSION=`node -e 'console.log(require("./gen-ts-api/package.json").version)'`
npm i @goauthentik/api@$VERSION -w @goauthentik/web
- name: Upgrade /web/packages/sfe
working-directory: web/packages/sfe
run: |
export VERSION=`node -e 'console.log(require("../gen-ts-api/package.json").version)'`
npm i @goauthentik/api@$VERSION
export VERSION=`node -e 'console.log(require("./gen-ts-api/package.json").version)'`
npm i @goauthentik/api@$VERSION -w @goauthentik/web-sfe
- uses: peter-evans/create-pull-request@v7
id: cpr
with:

View File

@ -62,7 +62,6 @@ jobs:
psql:
- 15-alpine
- 16-alpine
- 17-alpine
run_id: [1, 2, 3, 4, 5]
steps:
- uses: actions/checkout@v4
@ -117,7 +116,6 @@ jobs:
psql:
- 15-alpine
- 16-alpine
- 17-alpine
run_id: [1, 2, 3, 4, 5]
steps:
- uses: actions/checkout@v4
@ -195,23 +193,22 @@ jobs:
- uses: actions/checkout@v4
- name: Setup authentik env
uses: ./.github/actions/setup
- name: Setup e2e env (chrome, etc)
- name: Setup E2E environment (Chrome, etc)
run: |
docker compose -f tests/e2e/docker-compose.yml up -d --quiet-pull
- id: cache-web
uses: actions/cache@v4
with:
path: web/dist
key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'web/src/**', 'web/packages/sfe/src/**') }}-b
- name: prepare web ui
key: ${{ runner.os }}-web-${{ hashFiles('package-lock.json', 'web/src/**', 'web/packages/sfe/src/**') }}-b
- name: Prepare Web UI
if: steps.cache-web.outputs.cache-hit != 'true'
working-directory: web
run: |
npm ci
make -C .. gen-client-ts
npm run build
npm run build:sfe
- name: run e2e
make gen-client-ts
npm run build -w @goauthentik/web
npm run build -w @goauthentik/web-sfe
- name: Run E2E
run: |
uv run coverage run manage.py test ${{ matrix.job.glob }}
uv run coverage xml

View File

@ -26,7 +26,7 @@ jobs:
mkdir -p web/dist
mkdir -p website/help
touch web/dist/test website/help/test
- name: Generate API
- name: Generate Golang API Client
run: make gen-client-go
- name: golangci-lint
uses: golangci/golangci-lint-action@v8
@ -43,7 +43,7 @@ jobs:
go-version-file: "go.mod"
- name: Setup authentik env
uses: ./.github/actions/setup
- name: Generate API
- name: Generate Golang API Client
run: make gen-client-go
- name: Go unittests
run: |
@ -99,7 +99,7 @@ jobs:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Generate API
- name: Generate Golang API Client
run: make gen-client-go
- name: Build Docker Image
id: push
@ -145,16 +145,17 @@ jobs:
go-version-file: "go.mod"
- uses: actions/setup-node@v4
with:
node-version-file: web/package.json
node-version-file: package.json
cache: "npm"
cache-dependency-path: web/package-lock.json
- name: Generate API
cache-dependency-path: package-lock.json
- name: Generate Golang API Client
run: make gen-client-go
- name: Build web
working-directory: web/
- name: Prepare Dependencies
run: |
npm ci
npm run build-proxy
- name: Run ESBuild
run: |
npm run build-proxy -w @goauthentik/web
- name: Build outpost
run: |
set -x

View File

@ -19,47 +19,45 @@ jobs:
matrix:
command:
- lint
- lint:lockfile
- tsc
- prettier-check
project:
- web
include:
- command: tsc
project: web
- command: lit-analyse
project: web
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: ${{ matrix.project }}/package.json
node-version-file: package.json
cache: "npm"
cache-dependency-path: ${{ matrix.project }}/package-lock.json
- working-directory: ${{ matrix.project }}/
cache-dependency-path: package-lock.json
- name: Prepare Dependencies
run: |
npm ci
- name: Generate API
- name: Generate TypeScript API
run: make gen-client-ts
- name: Lint
working-directory: ${{ matrix.project }}/
run: npm run ${{ matrix.command }}
- name: Lint Project
run: |
npm run build-locales -w @goauthentik/web
npm run lint:types
- name: Lint Web
run: npm run ${{ matrix.command }} -w @goauthentik/web
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: web/package.json
node-version-file: package.json
cache: "npm"
cache-dependency-path: web/package-lock.json
- working-directory: web/
cache-dependency-path: package-lock.json
- name: Prepare Dependencies
run: npm ci
- name: Generate API
- name: Generate TypeScript API
run: make gen-client-ts
- name: build
working-directory: web/
run: npm run build
run: npm run build -w @goauthentik/web
ci-web-mark:
if: always()
needs:
@ -78,13 +76,12 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: web/package.json
node-version-file: package.json
cache: "npm"
cache-dependency-path: web/package-lock.json
- working-directory: web/
cache-dependency-path: package-lock.json
- name: Prepare Dependencies
run: npm ci
- name: Generate API
- name: Generate TypeScript API
run: make gen-client-ts
- name: test
working-directory: web/
run: npm run test || exit 0
run: npm run test -w @goauthentik/web || exit 0

View File

@ -14,53 +14,44 @@ on:
jobs:
lint:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
command:
- lint:lockfile
- prettier-check
steps:
- uses: actions/checkout@v4
- working-directory: website/
- uses: actions/setup-node@v4
with:
node-version-file: package.json
cache: "npm"
cache-dependency-path: package-lock.json
- name: Prepare Dependencies
run: npm ci
- name: Lint
working-directory: website/
run: npm run ${{ matrix.command }}
run: npm run prettier-check -w @goauthentik/docs
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: website/package.json
node-version-file: package.json
cache: "npm"
cache-dependency-path: website/package-lock.json
- working-directory: website/
cache-dependency-path: package-lock.json
- name: Prepare Dependencies
run: npm ci
- name: test
working-directory: website/
run: npm test
run: npm test -w @goauthentik/docs
build:
runs-on: ubuntu-latest
name: ${{ matrix.job }}
strategy:
fail-fast: false
matrix:
job:
- build
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: website/package.json
node-version-file: package.json
cache: "npm"
cache-dependency-path: website/package-lock.json
- working-directory: website/
cache-dependency-path: package-lock.json
- name: Prepare Dependencies
run: npm ci
- name: build
working-directory: website/
run: npm run ${{ matrix.job }}
- name: Run Docusaurus
run: npm run build -w @goauthentik/docs
ci-website-mark:
if: always()
needs:

View File

@ -7,7 +7,7 @@ on:
- packages/eslint-config/**
- packages/prettier-config/**
- packages/tsconfig/**
- web/packages/esbuild-plugin-live-reload/**
- packages/web/esbuild-plugin-live-reload/**
workflow_dispatch:
jobs:
publish:
@ -21,7 +21,7 @@ jobs:
- packages/eslint-config
- packages/prettier-config
- packages/tsconfig
- web/packages/esbuild-plugin-live-reload
- packages/web/esbuild-plugin-live-reload
steps:
- uses: actions/checkout@v4
with:

View File

@ -106,14 +106,14 @@ jobs:
go-version-file: "go.mod"
- uses: actions/setup-node@v4
with:
node-version-file: web/package.json
node-version-file: package.json
cache: "npm"
cache-dependency-path: web/package-lock.json
- name: Build web
working-directory: web/
cache-dependency-path: package-lock.json
- name: Prepare Dependencies
run: npm ci
- name: Run ESBuild (Proxy)
run: |
npm ci
npm run build-proxy
npm run build-proxy -w @goauthentik/web
- name: Build outpost
run: |
set -x

View File

@ -32,15 +32,25 @@ jobs:
if: ${{ github.event_name == 'pull_request' }}
- name: Setup authentik env
uses: ./.github/actions/setup
- name: Generate API
- uses: actions/setup-node@v4
with:
node-version-file: package.json
cache: "npm"
cache-dependency-path: package-lock.json
- name: Prepare Dependencies
run: npm ci
- name: Generate TypeScript API
run: make gen-client-ts
- name: run extract
- name: Run extract
run: |
uv run make i18n-extract
- name: run compile
- name: Run UV compile
run: |
uv run ak compilemessages
make web-check-compile
- name: Lint Project
run: |
npm run build-locales -w @goauthentik/web
npm run lint:types
- name: Create Pull Request
if: ${{ github.event_name != 'pull_request' }}
uses: peter-evans/create-pull-request@v7

View File

@ -36,12 +36,19 @@ coverage
*.mdx
*.md
## Import order matters
poly.ts
src/locale-codes.ts
src/locales/
# Storybook
storybook-static/
.storybook/css-import-maps*
# JSON Schemas
schemas/**/*.json
blueprints/**/*.json
authentik/**/*.json
lifecycle/**/*.json
# Locales
web/src/locale-codes.ts
web/src/locales/
# Wireit's cache
.wireit

View File

@ -17,6 +17,6 @@
"ms-python.vscode-pylance",
"redhat.vscode-yaml",
"Tobermory.es6-string-html",
"unifiedjs.vscode-mdx",
"unifiedjs.vscode-mdx"
]
}

40
.vscode/tasks.json vendored
View File

@ -4,12 +4,7 @@
{
"label": "authentik/core: make",
"command": "uv",
"args": [
"run",
"make",
"lint-fix",
"lint"
],
"args": ["run", "make", "lint-fix", "lint"],
"presentation": {
"panel": "new"
},
@ -18,11 +13,7 @@
{
"label": "authentik/core: run",
"command": "uv",
"args": [
"run",
"ak",
"server"
],
"args": ["run", "ak", "server"],
"group": "build",
"presentation": {
"panel": "dedicated",
@ -32,17 +23,13 @@
{
"label": "authentik/web: make",
"command": "make",
"args": [
"web"
],
"args": ["web"],
"group": "build"
},
{
"label": "authentik/web: watch",
"command": "make",
"args": [
"web-watch"
],
"args": ["web-watch"],
"group": "build",
"presentation": {
"panel": "dedicated",
@ -52,26 +39,19 @@
{
"label": "authentik: install",
"command": "make",
"args": [
"install",
"-j4"
],
"args": ["install", "-j4"],
"group": "build"
},
{
"label": "authentik/website: make",
"command": "make",
"args": [
"website"
],
"args": ["website"],
"group": "build"
},
{
"label": "authentik/website: watch",
"command": "make",
"args": [
"website-watch"
],
"args": ["website-watch"],
"group": "build",
"presentation": {
"panel": "dedicated",
@ -81,11 +61,7 @@
{
"label": "authentik/api: generate",
"command": "uv",
"args": [
"run",
"make",
"gen"
],
"args": ["run", "make", "gen"],
"group": "build"
}
]

View File

@ -1,49 +1,41 @@
# syntax=docker/dockerfile:1
# Stage 1: Build website
FROM --platform=${BUILDPLATFORM} docker.io/library/node:24 AS website-builder
ENV NODE_ENV=production
WORKDIR /work/website
RUN --mount=type=bind,target=/work/website/package.json,src=./website/package.json \
--mount=type=bind,target=/work/website/package-lock.json,src=./website/package-lock.json \
--mount=type=cache,id=npm-website,sharing=shared,target=/root/.npm \
npm ci --include=dev
COPY ./website /work/website/
COPY ./blueprints /work/blueprints/
COPY ./schema.yml /work/
COPY ./SECURITY.md /work/
RUN npm run build-bundled
# Stage 2: Build webui
FROM --platform=${BUILDPLATFORM} docker.io/library/node:24 AS web-builder
# Stage 1: Build Node packages
FROM --platform=${BUILDPLATFORM} docker.io/library/node:24-slim AS node-packages
ARG GIT_BUILD_HASH
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
ENV NODE_ENV=production
WORKDIR /work/web
RUN --mount=type=bind,target=/work/web/package.json,src=./web/package.json \
--mount=type=bind,target=/work/web/package-lock.json,src=./web/package-lock.json \
--mount=type=bind,target=/work/web/packages/sfe/package.json,src=./web/packages/sfe/package.json \
--mount=type=bind,target=/work/web/scripts,src=./web/scripts \
--mount=type=cache,id=npm-web,sharing=shared,target=/root/.npm \
npm ci --include=dev
WORKDIR /work
COPY ./SECURITY.md /work
COPY ./schema.yml /work
COPY ./docker-compose.yml /work
COPY ./blueprints /work/blueprints/
COPY ./package.json /work
COPY ./package-lock.json /work
COPY ./tsconfig.json /work
COPY ./packages/ /work/packages/
COPY ./web /work/web/
COPY ./website /work/website/
COPY ./gen-ts-api /work/web/node_modules/@goauthentik/api
COPY ./gen-ts-api /work/gen-ts-api/
RUN npm run build && \
npm run build:sfe
RUN --mount=type=cache,id=npm-node,sharing=shared,target=/root/.npm \
npm ci
# Stage 3: Build go proxy
RUN cd ./gen-ts-api && npm link
RUN npm link @goauthentik/api -w @goauthentik/web
ENV NODE_ENV=production
RUN npm run build -w @goauthentik/web
RUN npm run build -w @goauthentik/web-sfe
RUN npm run build:api -w @goauthentik/docs
RUN npm run build:docusaurus -w @goauthentik/docs
# Stage 2: Build go proxy
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.24-bookworm AS go-builder
ARG TARGETOS
@ -68,8 +60,8 @@ RUN --mount=type=bind,target=/go/src/goauthentik.io/go.mod,src=./go.mod \
COPY ./cmd /go/src/goauthentik.io/cmd
COPY ./authentik/lib /go/src/goauthentik.io/authentik/lib
COPY ./web/static.go /go/src/goauthentik.io/web/static.go
COPY --from=web-builder /work/web/robots.txt /go/src/goauthentik.io/web/robots.txt
COPY --from=web-builder /work/web/security.txt /go/src/goauthentik.io/web/security.txt
COPY --from=node-packages /work/web/robots.txt /go/src/goauthentik.io/web/robots.txt
COPY --from=node-packages /work/web/security.txt /go/src/goauthentik.io/web/security.txt
COPY ./internal /go/src/goauthentik.io/internal
COPY ./go.mod /go/src/goauthentik.io/go.mod
COPY ./go.sum /go/src/goauthentik.io/go.sum
@ -80,7 +72,7 @@ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
CGO_ENABLED=1 GOFIPS140=latest GOARM="${TARGETVARIANT#v}" \
go build -o /go/authentik ./cmd/server
# Stage 4: MaxMind GeoIP
# Stage 3: MaxMind GeoIP
FROM --platform=${BUILDPLATFORM} ghcr.io/maxmind/geoipupdate:v7.1.0 AS geoip
ENV GEOIPUPDATE_EDITION_IDS="GeoLite2-City GeoLite2-ASN"
@ -93,9 +85,9 @@ RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
mkdir -p /usr/share/GeoIP && \
/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.8 AS uv
# Stage 6: Base python image
# Stage 4: Download uv
FROM ghcr.io/astral-sh/uv:0.7.6 AS uv
# Stage 5: Base python image
FROM ghcr.io/goauthentik/fips-python:3.13.3-slim-bookworm-fips AS python-base
ENV VENV_PATH="/ak-root/.venv" \
@ -109,7 +101,7 @@ WORKDIR /ak-root/
COPY --from=uv /uv /uvx /bin/
# Stage 7: Python dependencies
# Stage 6: Python dependencies
FROM python-base AS python-deps
ARG TARGETARCH
@ -144,7 +136,7 @@ RUN --mount=type=bind,target=pyproject.toml,src=pyproject.toml \
--mount=type=cache,target=/root/.cache/uv \
uv sync --frozen --no-install-project --no-dev
# Stage 8: Run
# Stage 7: Run
FROM python-base AS final-image
ARG VERSION
@ -187,9 +179,9 @@ COPY ./lifecycle/ /lifecycle
COPY ./authentik/sources/kerberos/krb5.conf /etc/krb5.conf
COPY --from=go-builder /go/authentik /bin/authentik
COPY --from=python-deps /ak-root/.venv /ak-root/.venv
COPY --from=web-builder /work/web/dist/ /web/dist/
COPY --from=web-builder /work/web/authentik/ /web/authentik/
COPY --from=website-builder /work/website/build/ /website/help/
COPY --from=node-packages /work/web/dist/ /web/dist/
COPY --from=node-packages /work/web/authentik/ /web/authentik/
COPY --from=node-packages /work/website/build/ /website/help/
COPY --from=geoip /usr/share/GeoIP /geoip
USER 1000

View File

@ -1,6 +1,6 @@
.PHONY: gen dev-reset all clean test web website
SHELL := /usr/bin/env bash
SHELL := /bin/bash
.SHELLFLAGS += ${SHELLFLAGS} -e -o pipefail
PWD = $(shell pwd)
UID = $(shell id -u)
@ -73,7 +73,7 @@ core-i18n-extract:
--ignore website \
-l en
install: web-install website-install core-install ## Install all requires dependencies for `web`, `website` and `core`
install: npm-install core-install ## Install all requires dependencies for `web`, `website` and `core`
dev-drop-db:
dropdb -U ${pg_user} -h ${pg_host} ${pg_name}
@ -146,9 +146,8 @@ gen-client-ts: gen-clean-ts ## Build and install the authentik API for Typescri
--additional-properties=npmVersion=${NPM_VERSION} \
--git-repo-id authentik \
--git-user-id goauthentik
mkdir -p web/node_modules/@goauthentik/api
cd ${PWD}/${GEN_API_TS} && npm i
\cp -rf ${PWD}/${GEN_API_TS}/* web/node_modules/@goauthentik/api
cd ./${GEN_API_TS} && npm link
npm link @goauthentik/api -w @goauthentik/web
gen-client-py: gen-clean-py ## Build and install the authentik API for Python
docker run \
@ -183,38 +182,34 @@ gen: gen-build gen-client-ts
## Web
#########################
web-build: web-install ## Build the Authentik UI
cd web && npm run build
web-build: npm-install ## Build the Authentik UI
npm run build -w @goauthentik/web
web: web-lint-fix web-lint web-check-compile ## Automatically fix formatting issues in the Authentik UI source code, lint the code, and compile it
web-install: ## Install the necessary libraries to build the Authentik UI
cd web && npm ci
npm-install: ## Install the necessary libraries to build the Authentik UI
npm ci
web-test: ## Run tests for the Authentik UI
cd web && npm run test
npm run test -w @goauthentik/web
web-watch: ## Build and watch the Authentik UI for changes, updating automatically
rm -rf web/dist/
mkdir web/dist/
touch web/dist/.gitkeep
cd web && npm run watch
npm run watch -w @goauthentik/web
web-storybook-watch: ## Build and run the storybook documentation server
cd web && npm run storybook
npm run storybook -w @goauthentik/web
web-lint-fix:
cd web && npm run prettier
npm run prettier -w @goauthentik/web
web-lint:
cd web && npm run lint
cd web && npm run lit-analyse
npm run lint -w @goauthentik/web
web-check-compile:
cd web && npm run tsc
npm run lint:types
web-i18n-extract:
cd web && npm run extract-locales
npm run extract-locales -w @goauthentik/web
#########################
## Website
@ -222,17 +217,14 @@ web-i18n-extract:
website: website-lint-fix website-build ## Automatically fix formatting issues in the Authentik website/docs source code, lint the code, and compile it
website-install:
cd website && npm ci
website-lint-fix: lint-codespell
cd website && npm run prettier
npm run prettier --prefix website
website-build:
cd website && npm run build
npm run build --prefix website
website-watch: ## Build and watch the documentation website, updating automatically
cd website && npm run watch
npm run watch --prefix website
#########################
## Docker

View File

@ -84,7 +84,6 @@ 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
@ -452,7 +451,7 @@ class UserViewSet(UsedByMixin, ModelViewSet):
def list(self, request, *args, **kwargs):
return super().list(request, *args, **kwargs)
def _create_recovery_link(self, for_email=False) -> tuple[str, Token]:
def _create_recovery_link(self) -> 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
@ -474,16 +473,12 @@ 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": _plan,
"revoke_on_execution": not for_email,
"_plan": FlowToken.pickle(plan),
},
)
querystring = urlencode({QS_KEY_TOKEN: token.key})
@ -653,7 +648,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(for_email=True)
link, token = self._create_recovery_link()
# Lookup the email stage to assure the current user can access it
stages = get_objects_for_user(
request.user, "authentik_stages_email.view_emailstage"

View File

@ -1,12 +0,0 @@
"""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

View File

@ -1,47 +0,0 @@
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

View File

@ -1,16 +0,0 @@
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 []

View File

@ -1,7 +0,0 @@
from authentik.endpoints.connector import BaseConnector, EnrollmentMethods
class GoogleChromeConnector(BaseConnector):
def supported_enrollment_methods(self) -> list[EnrollmentMethods]:
return [EnrollmentMethods.AUTOMATIC_USER]

View File

@ -1,7 +0,0 @@
from django.db import models
from authentik.endpoints.models import Connector
class GoogleChromeConnector(Connector):
credentials = models.JSONField()

View File

@ -1,125 +0,0 @@
# 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
),
),
]

View File

@ -1,40 +0,0 @@
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()

View File

@ -1,18 +0,0 @@
# 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),
),
]

View File

@ -303,10 +303,9 @@ 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: "FlowPlan") -> str:
def pickle(plan) -> str:
"""Pickle into string"""
data = dumps(plan)
return b64encode(data).decode()

View File

@ -99,10 +99,9 @@ class ChallengeStageView(StageView):
self.logger.debug("Got StageInvalidException", exc=exc)
return self.executor.stage_invalid()
if not challenge.is_valid():
self.logger.error(
self.logger.warning(
"f(ch): Invalid challenge",
errors=challenge.errors,
challenge=challenge.data,
)
return HttpChallengeResponse(challenge)

View File

@ -146,8 +146,7 @@ class FlowExecutorView(APIView):
except (AttributeError, EOFError, ImportError, IndexError) as exc:
LOGGER.warning("f(exec): Failed to restore token plan", exc=exc)
finally:
if token.revoke_on_execution:
token.delete()
token.delete()
if not isinstance(plan, FlowPlan):
return None
plan.context[PLAN_CONTEXT_IS_RESTORED] = token

View File

@ -1,7 +1,6 @@
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
@ -83,41 +82,21 @@ class SyncTasks:
self.logger.debug("Failed to acquire sync lock, skipping", provider=provider.name)
return
try:
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:
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(LogEvent(**msg))
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:
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(LogEvent(**msg))
except TransientSyncException as exc:
self.logger.warning("transient sync exception", exc=exc)
@ -153,15 +132,6 @@ 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:

View File

@ -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[3]
drop_msg = task.messages[2]
self.assertEqual(drop_msg["event"], "Dropping mutating request due to dry run")
self.assertIsNotNone(drop_msg["attributes"]["url"])
self.assertIsNotNone(drop_msg["attributes"]["body"])

View File

@ -73,7 +73,6 @@ TENANT_APPS = [
"authentik.admin",
"authentik.api",
"authentik.crypto",
"authentik.endpoints",
"authentik.flows",
"authentik.outposts",
"authentik.policies.dummy",
@ -425,7 +424,7 @@ else:
"BACKEND": "authentik.root.storages.FileStorage",
"OPTIONS": {
"location": Path(CONFIG.get("storage.media.file.path")),
"base_url": CONFIG.get("web.path", "/") + "media/",
"base_url": "/media/",
},
}
# Compatibility for apps not supporting top-level STORAGES

View File

@ -31,8 +31,6 @@ 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
@ -66,11 +64,6 @@ 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.

View File

@ -111,7 +111,6 @@ class LDAPSourceSerializer(SourceSerializer):
"sync_parent_group",
"connectivity",
"lookup_groups_from_user",
"delete_not_found_objects",
]
extra_kwargs = {"bind_password": {"write_only": True}}
@ -148,7 +147,6 @@ class LDAPSourceViewSet(UsedByMixin, ModelViewSet):
"user_property_mappings",
"group_property_mappings",
"lookup_groups_from_user",
"delete_not_found_objects",
]
search_fields = ["name", "slug"]
ordering = ["name"]

View File

@ -1,48 +0,0 @@
# 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"),
),
]

View File

@ -137,14 +137,6 @@ 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"
@ -329,12 +321,6 @@ 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 (
@ -346,18 +332,9 @@ 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 (
@ -369,6 +346,3 @@ class GroupLDAPSourceConnection(GroupSourceConnection):
class Meta:
verbose_name = _("Group LDAP Source Connection")
verbose_name_plural = _("Group LDAP Source Connections")
indexes = [
models.Index(fields=["validated_by"]),
]

View File

@ -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, flatten
from authentik.sources.ldap.models import LDAPSource
class BaseLDAPSynchronizer:
@ -77,16 +77,6 @@ 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,

View File

@ -1,61 +0,0 @@
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)

View File

@ -1,63 +0,0 @@
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)

View File

@ -58,16 +58,18 @@ class GroupLDAPSynchronizer(BaseLDAPSynchronizer):
return -1
group_count = 0
for group in page_data:
if (attributes := self.get_attributes(group)) is None:
if "attributes" not in group:
continue
attributes = group.get("attributes", {})
group_dn = flatten(flatten(group.get("entryDN", group.get("dn"))))
if not (uniq := self.get_identifier(attributes)):
if not attributes.get(self._source.object_uniqueness_field):
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)

View File

@ -63,9 +63,9 @@ class MembershipLDAPSynchronizer(BaseLDAPSynchronizer):
group_member_dn = group_member.get("dn", {})
members.append(group_member_dn)
else:
if (attributes := self.get_attributes(group)) is None:
if "attributes" not in group:
continue
members = attributes.get(self._source.group_membership_field, [])
members = group.get("attributes", {}).get(self._source.group_membership_field, [])
ak_group = self.get_group(group)
if not ak_group:

View File

@ -60,16 +60,18 @@ class UserLDAPSynchronizer(BaseLDAPSynchronizer):
return -1
user_count = 0
for user in page_data:
if (attributes := self.get_attributes(user)) is None:
if "attributes" not in user:
continue
attributes = user.get("attributes", {})
user_dn = flatten(user.get("entryDN", user.get("dn")))
if not (uniq := self.get_identifier(attributes)):
if not attributes.get(self._source.object_uniqueness_field):
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)

View File

@ -17,8 +17,6 @@ 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
@ -54,11 +52,11 @@ def ldap_connectivity_check(pk: str | None = None):
@CELERY_APP.task(
# 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,
# 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,
# and 0.5x on top of that to give some more leeway
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,
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,
)
def ldap_sync_single(source_pk: str):
"""Sync a single source"""
@ -81,25 +79,6 @@ 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()

View File

@ -2,33 +2,6 @@
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"""
@ -123,14 +96,5 @@ 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

View File

@ -13,26 +13,14 @@ 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 (
GroupLDAPSourceConnection,
LDAPSource,
LDAPSourcePropertyMapping,
UserLDAPSourceConnection,
)
from authentik.sources.ldap.sync.forward_delete_users import DELETE_CHUNK_SIZE
from authentik.sources.ldap.models import LDAPSource, LDAPSourcePropertyMapping
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 (
group_in_slapd_cn,
group_in_slapd_uid,
mock_slapd_connection,
user_in_slapd_cn,
user_in_slapd_uid,
)
from authentik.sources.ldap.tests.mock_slapd import mock_slapd_connection
LDAP_PASSWORD = generate_key()
@ -320,160 +308,3 @@ 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())

View File

@ -9,7 +9,6 @@ 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
@ -129,9 +128,7 @@ 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] = _(
"Continue to {source_name}".format(source_name=source.name)
)
plan_kwargs[PLAN_CONTEXT_CONSENT_HEADER] = f"Continue to {source.name}"
injected_stages.append(in_memory_stage(AutosubmitStageView))
return self.handle_login_flow(
source,

View File

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

View File

@ -4,8 +4,6 @@ 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
@ -49,11 +47,6 @@ 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."""
@ -127,6 +120,9 @@ 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

View File

@ -17,7 +17,6 @@ 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,
)
@ -34,40 +33,6 @@ 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)
@ -193,7 +158,6 @@ 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

View File

@ -1,38 +0,0 @@
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)

View File

@ -23,7 +23,6 @@ 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
@ -87,8 +86,7 @@ class EmailStageView(ChallengeStageView):
user=pending_user,
identifier=identifier,
flow=self.executor.flow,
_plan=pickle_flow_token_for_email(self.executor.plan),
revoke_on_execution=False,
_plan=FlowToken.pickle(self.executor.plan),
)
token = tokens.first()
# Check if token is expired and rotate key if so

View File

@ -174,5 +174,5 @@ class TestEmailStageSending(FlowTestCase):
response = self.client.post(url)
response = self.client.post(url)
self.assertEqual(response.status_code, 200)
self.assertGreaterEqual(len(mail.outbox), 1)
self.assertTrue(len(mail.outbox) >= 1)
self.assertEqual(mail.outbox[0].subject, "authentik")

View File

@ -17,7 +17,6 @@ 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
@ -161,17 +160,6 @@ 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"))
@ -194,7 +182,6 @@ 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()):

View File

@ -8180,11 +8180,6 @@
"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": []

10
eslint.config.mjs Normal file
View File

@ -0,0 +1,10 @@
import { createESLintPackageConfig } from "@goauthentik/eslint-config";
// @ts-check
/**
* ESLint configuration for authentik's monorepo.
*/
const ESLintConfig = createESLintPackageConfig();
export default ESLintConfig;

View File

@ -28,18 +28,16 @@ func NewSessionBinder(si server.LDAPServerInstance, oldBinder bind.Binder) *Sess
si: si,
log: log.WithField("logger", "authentik.outpost.ldap.binder.session"),
}
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
}
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")
}
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
}

View File

@ -16,7 +16,6 @@ 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"
)
@ -86,11 +85,7 @@ func (ls *LDAPServer) Refresh() error {
providers[idx].certUUID = *kp
}
if *provider.SearchMode.Ptr() == api.LDAPAPIACCESSMODE_CACHED {
var oldSearcher search.Searcher
if existing != nil {
oldSearcher = existing.searcher
}
providers[idx].searcher = memorysearch.NewMemorySearcher(providers[idx], oldSearcher)
providers[idx].searcher = memorysearch.NewMemorySearcher(providers[idx])
} else if *provider.SearchMode.Ptr() == api.LDAPAPIACCESSMODE_DIRECT {
providers[idx].searcher = directsearch.NewDirectSearcher(providers[idx])
}

View File

@ -31,26 +31,13 @@ type MemorySearcher struct {
groups []api.Group
}
func NewMemorySearcher(si server.LDAPServerInstance, existing search.Searcher) *MemorySearcher {
func NewMemorySearcher(si server.LDAPServerInstance) *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,
@ -62,6 +49,7 @@ func (ms *MemorySearcher) fetch() {
Logger: ms.log,
})
ms.groups = groups
return ms
}
func (ms *MemorySearcher) SearchBase(req *search.Request) (ldap.ServerSearchResult, error) {

View File

@ -67,15 +67,11 @@ func (ws *WebServer) configureStatic() {
// Media files, if backend is file
if config.Get().Storage.Media.Backend == "file" {
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,
))
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)
})
}
staticRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/if/help/").Handler(pathStripper(

Binary file not shown.

View File

@ -32,17 +32,15 @@
# 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-05-28 11:25+0000\n"
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
"Last-Translator: Alexander Mnich, 2025\n"
"Last-Translator: Dominic Wagner <mail@dominic-wagner.de>, 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"
@ -134,10 +132,6 @@ 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"
@ -411,7 +405,7 @@ msgstr "Eigenschaften"
#: authentik/core/models.py
msgid "session data"
msgstr "Sitzungsdaten"
msgstr ""
#: authentik/core/models.py
msgid "Session"
@ -539,7 +533,7 @@ msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "Number of passwords to check against."
msgstr "Anzahl Passwörter, gegen die geprüft wird."
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py
@ -549,20 +543,18 @@ 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 "Passwort-Einzigartigkeits-Richtlinie"
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policies"
msgstr "Passwort-Einzigartigkeits-Richtlinien"
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "User Password History"
msgstr "Nutzer-Passwort-Historie"
msgstr ""
#: authentik/enterprise/policy.py
msgid "Enterprise required to access this feature."
@ -701,33 +693,6 @@ 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 "
@ -1023,7 +988,7 @@ msgstr ""
#: authentik/flows/models.py
msgid "Evaluate policies when the Stage is presented to the user."
msgstr "Richtlinien auswerten, wenn die Phase dem Benutzer angezeigt wird."
msgstr ""
#: authentik/flows/models.py
msgid ""
@ -1078,12 +1043,9 @@ msgid "Starting full provider sync"
msgstr "Starte komplette Provider Synchronisation."
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing users"
msgstr ""
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing groups"
msgstr ""
#, python-brace-format
msgid "Syncing page {page} of users"
msgstr "Synchonisiere Benutzer Seite {page}"
#: authentik/lib/sync/outgoing/tasks.py
#, python-brace-format
@ -1631,11 +1593,11 @@ msgstr "ES256 (Asymmetrische Verschlüsselung)"
#: authentik/providers/oauth2/models.py
msgid "ES384 (Asymmetric Encryption)"
msgstr "ES384 (Asymmetrische Verschlüsselung)"
msgstr ""
#: authentik/providers/oauth2/models.py
msgid "ES512 (Asymmetric Encryption)"
msgstr "ES5122 (Asymmetrische Verschlüsselung)"
msgstr ""
#: authentik/providers/oauth2/models.py
msgid "Scope used by the client"
@ -2221,11 +2183,11 @@ msgstr "Standard"
#: authentik/providers/scim/models.py
msgid "AWS"
msgstr "AWS"
msgstr ""
#: authentik/providers/scim/models.py
msgid "Slack"
msgstr "Slack"
msgstr ""
#: authentik/providers/scim/models.py
msgid "Base URL to SCIM requests, usually ends in /v2"
@ -2237,7 +2199,7 @@ msgstr "Authentifizierungstoken"
#: authentik/providers/scim/models.py
msgid "SCIM Compatibility Mode"
msgstr "SCIM Kompatibilitätsmodus"
msgstr ""
#: authentik/providers/scim/models.py
msgid "Alter authentik behavior for vendor-specific SCIM implementations."
@ -2269,7 +2231,7 @@ msgstr "Rollen"
#: authentik/rbac/models.py
msgid "Initial Permissions"
msgstr "Initiale Berechtigungen"
msgstr ""
#: authentik/rbac/models.py
msgid "System permission"
@ -2525,12 +2487,6 @@ 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"
@ -2548,25 +2504,20 @@ msgid "LDAP Source Property Mappings"
msgstr "LDAP Quelle Eigenschafts-Zuordnungen"
#: authentik/sources/ldap/models.py
msgid ""
"Unique ID used while checking if this object still exists in the directory."
msgid "User LDAP Source Connection"
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 "Benutzer LDAP-Quellverbindungen"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "Group LDAP Source Connection"
msgstr "LDAP Gruppen Quellverbindung"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "Group LDAP Source Connections"
msgstr "LDAP Gruppen Quellverbindungen"
msgstr ""
#: authentik/sources/ldap/signals.py
msgid "Password does not match Active Directory Complexity."
@ -2579,7 +2530,7 @@ msgstr "Kein Token empfangen."
#: authentik/sources/oauth/models.py
msgid "HTTP Basic Authentication"
msgstr "HTTP Basic Authentifizierung"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "Include the client ID and secret as request parameters"
@ -2945,11 +2896,6 @@ 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"
@ -2984,7 +2930,7 @@ msgstr "Duo Geräte"
#: authentik/stages/authenticator_email/models.py
msgid "Email OTP"
msgstr "E-Mail Einmalpasswort"
msgstr ""
#: authentik/stages/authenticator_email/models.py
#: authentik/stages/email/models.py
@ -3017,11 +2963,11 @@ msgstr "Beim Rendern der E-Mail-Vorlage ist ein Fehler aufgetreten"
#: authentik/stages/authenticator_email/models.py
msgid "Email Device"
msgstr "E-Mail Gerät"
msgstr ""
#: authentik/stages/authenticator_email/models.py
msgid "Email Devices"
msgstr "E-Mail Geräte"
msgstr ""
#: authentik/stages/authenticator_email/stage.py
#: authentik/stages/authenticator_sms/stage.py
@ -3031,7 +2977,7 @@ msgstr "Code stimmt nicht überein"
#: authentik/stages/authenticator_email/stage.py
msgid "Invalid email"
msgstr "Ungültige E-Mail"
msgstr ""
#: authentik/stages/authenticator_email/templates/email/email_otp.html
#: authentik/stages/email/templates/email/password_reset.html
@ -3327,10 +3273,6 @@ 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"
@ -3347,14 +3289,6 @@ 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"
@ -3956,11 +3890,10 @@ 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 "Reputation kann nicht höher als dieser Wert sein. Null oder positiv."
msgstr ""
#: authentik/tenants/models.py
msgid "The option configures the footer links on the flow executor pages."

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
"POT-Creation-Date: 2025-05-20 00:10+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,11 +961,8 @@ msgid "Starting full provider sync"
msgstr ""
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing users"
msgstr ""
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing groups"
#, python-brace-format
msgid "Syncing page {page} of users"
msgstr ""
#: authentik/lib/sync/outgoing/tasks.py
@ -2255,12 +2252,6 @@ 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 ""
@ -2277,11 +2268,6 @@ 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 ""
@ -2653,11 +2639,6 @@ 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 ""
@ -3013,10 +2994,6 @@ 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,14 +3010,6 @@ 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.

View File

@ -15,7 +15,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
"POT-Creation-Date: 2025-04-23 09:00+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,10 +109,6 @@ 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"
@ -675,33 +671,6 @@ 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 "
@ -1040,12 +1009,9 @@ msgid "Starting full provider sync"
msgstr "Iniciando sincronización completa de proveedor"
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing users"
msgstr ""
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing groups"
msgstr ""
#, python-brace-format
msgid "Syncing page {page} of users"
msgstr "Sincronizando página {page} de usuarios"
#: authentik/lib/sync/outgoing/tasks.py
#, python-brace-format
@ -2486,12 +2452,6 @@ 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"
@ -2508,11 +2468,6 @@ msgstr "Asignación de Propiedades de Fuente de LDAP"
msgid "LDAP Source Property Mappings"
msgstr "Asignaciones de Propiedades de Fuente de LDAP"
#: authentik/sources/ldap/models.py
msgid ""
"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 ""
@ -2904,11 +2859,6 @@ 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"
@ -3295,10 +3245,6 @@ 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"
@ -3315,14 +3261,6 @@ 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.

View File

@ -15,7 +15,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
"POT-Creation-Date: 2025-04-23 09:00+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,10 +106,6 @@ 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"
@ -662,33 +658,6 @@ 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 "
@ -1027,12 +996,9 @@ msgid "Starting full provider sync"
msgstr "Käynnistetään palveluntarjoajan täysi synkronisointi"
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing users"
msgstr ""
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing groups"
msgstr ""
#, python-brace-format
msgid "Syncing page {page} of users"
msgstr "Synkronoidaan käyttäjien sivua {page}"
#: authentik/lib/sync/outgoing/tasks.py
#, python-brace-format
@ -2463,12 +2429,6 @@ 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"
@ -2485,11 +2445,6 @@ 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 ""
@ -2882,11 +2837,6 @@ 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"
@ -3266,10 +3216,6 @@ 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"
@ -3286,14 +3232,6 @@ 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"

View File

@ -19,7 +19,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
"POT-Creation-Date: 2025-05-20 00:10+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,12 +1056,9 @@ msgid "Starting full provider sync"
msgstr "Démarrage d'une synchronisation complète du fournisseur"
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing users"
msgstr "Synchronisation des utilisateurs"
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing groups"
msgstr "Synchronisation des groupes"
#, python-brace-format
msgid "Syncing page {page} of users"
msgstr "Synchronisation de la page {page} d'utilisateurs"
#: authentik/lib/sync/outgoing/tasks.py
#, python-brace-format
@ -2511,14 +2508,6 @@ 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"
@ -2535,13 +2524,6 @@ 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"
@ -2936,11 +2918,6 @@ 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"
@ -3331,10 +3308,6 @@ 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"
@ -3351,14 +3324,6 @@ 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"

View File

@ -20,7 +20,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
"POT-Creation-Date: 2025-04-23 09:00+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,10 +114,6 @@ 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"
@ -676,33 +672,6 @@ 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 "
@ -1049,12 +1018,9 @@ msgid "Starting full provider sync"
msgstr "Avvio della sincronizzazione completa del provider"
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing users"
msgstr ""
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing groups"
msgstr ""
#, python-brace-format
msgid "Syncing page {page} of users"
msgstr "Sincronizzando pagina {page} degli utenti"
#: authentik/lib/sync/outgoing/tasks.py
#, python-brace-format
@ -2497,12 +2463,6 @@ 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"
@ -2519,11 +2479,6 @@ msgstr "Mappatura delle proprietà sorgente LDAP"
msgid "LDAP Source Property Mappings"
msgstr "Mappature delle proprietà della sorgente LDAP"
#: authentik/sources/ldap/models.py
msgid ""
"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"
@ -2917,11 +2872,6 @@ 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"
@ -3319,10 +3269,6 @@ 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"
@ -3339,14 +3285,6 @@ 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"

View File

@ -12,7 +12,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
"POT-Creation-Date: 2025-04-23 09:00+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,10 +99,6 @@ 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 ""
@ -629,33 +625,6 @@ 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 "
@ -977,11 +946,8 @@ msgid "Starting full provider sync"
msgstr ""
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing users"
msgstr ""
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing groups"
#, python-brace-format
msgid "Syncing page {page} of users"
msgstr ""
#: authentik/lib/sync/outgoing/tasks.py
@ -2297,12 +2263,6 @@ 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 소스"
@ -2319,11 +2279,6 @@ 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 ""
@ -2702,11 +2657,6 @@ 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 ""
@ -3067,10 +3017,6 @@ 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 "거부 스테이지"
@ -3087,14 +3033,6 @@ 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.

View File

@ -19,7 +19,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
"POT-Creation-Date: 2025-04-11 00:10+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,10 +113,6 @@ 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"
@ -195,7 +191,6 @@ 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"
@ -384,18 +379,6 @@ 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"
@ -503,38 +486,6 @@ 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."
@ -671,33 +622,6 @@ 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 "
@ -1039,11 +963,8 @@ msgid "Starting full provider sync"
msgstr ""
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing users"
msgstr ""
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing groups"
#, python-brace-format
msgid "Syncing page {page} of users"
msgstr ""
#: authentik/lib/sync/outgoing/tasks.py
@ -1344,6 +1265,12 @@ msgstr ""
msgid "Clear Policy's cache metrics"
msgstr ""
#: authentik/policies/password/models.py
msgid "Field key to check, field keys defined in Prompt stages are available."
msgstr ""
"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"
@ -1355,6 +1282,10 @@ 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 ""
@ -1396,6 +1327,20 @@ 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"
@ -2215,10 +2160,6 @@ msgstr ""
msgid "Roles"
msgstr ""
#: authentik/rbac/models.py
msgid "Initial Permissions"
msgstr ""
#: authentik/rbac/models.py
msgid "System permission"
msgstr ""
@ -2451,12 +2392,6 @@ 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"
@ -2473,27 +2408,6 @@ 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 ""
@ -2503,14 +2417,6 @@ 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"
@ -2552,12 +2458,6 @@ 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"
@ -2869,11 +2769,6 @@ 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 ""
@ -3247,10 +3142,6 @@ 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"
@ -3267,14 +3158,6 @@ 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"
@ -3474,12 +3357,6 @@ 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."
@ -3865,14 +3742,6 @@ 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."

View File

@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
"POT-Creation-Date: 2025-04-23 09:00+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,10 +105,6 @@ 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"
@ -666,33 +662,6 @@ 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 "
@ -1038,12 +1007,9 @@ msgid "Starting full provider sync"
msgstr "Iniciando a sincronização completa com o provedor"
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing users"
msgstr ""
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing groups"
msgstr ""
#, python-brace-format
msgid "Syncing page {page} of users"
msgstr "A sincronizar a página {page} dos utilizadores"
#: authentik/lib/sync/outgoing/tasks.py
#, python-brace-format
@ -2490,12 +2456,6 @@ 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"
@ -2512,11 +2472,6 @@ 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"
@ -2910,11 +2865,6 @@ 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"
@ -3305,10 +3255,6 @@ 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"
@ -3325,14 +3271,6 @@ 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.

View File

@ -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-05-28 11:25+0000\n"
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
"Last-Translator: Wagner Santos, 2025\n"
"Last-Translator: Gil Poiares-Oliveira, 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,10 +112,6 @@ 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"
@ -275,11 +271,11 @@ msgstr "Aplicativos"
#: authentik/core/models.py
msgid "Application Entitlement"
msgstr "Autorização de aplicação"
msgstr ""
#: authentik/core/models.py
msgid "Application Entitlements"
msgstr "Autorizações de aplicação"
msgstr ""
#: authentik/core/models.py
msgid "Use the source-specific identifier"
@ -383,15 +379,15 @@ msgstr "Mapeamentos de propriedades"
#: authentik/core/models.py
msgid "session data"
msgstr "dados de sessão"
msgstr ""
#: authentik/core/models.py
msgid "Session"
msgstr "Sessão"
msgstr ""
#: authentik/core/models.py
msgid "Sessions"
msgstr "Sessões"
msgstr ""
#: authentik/core/models.py
msgid "Authenticated Session"
@ -509,7 +505,7 @@ msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "Number of passwords to check against."
msgstr "Número de senhas para verificar."
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py
@ -518,19 +514,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 "A senha já foi utilizada antes. Por favor, escolha uma diferente."
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policy"
msgstr "Política de exclusividade de senha"
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policies"
msgstr "Políticas de exclusividade de senha"
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "User Password History"
msgstr "Histórico de senhas do usuário"
msgstr ""
#: authentik/enterprise/policy.py
msgid "Enterprise required to access this feature."
@ -614,39 +610,39 @@ msgstr "Chave de Assinatura"
#: authentik/enterprise/providers/ssf/models.py
msgid "Key used to sign the SSF Events."
msgstr "Chave utilizada para assinar os eventos SSF."
msgstr ""
#: authentik/enterprise/providers/ssf/models.py
msgid "Shared Signals Framework Provider"
msgstr "Provedor de Shared Signals Framework"
msgstr ""
#: authentik/enterprise/providers/ssf/models.py
msgid "Shared Signals Framework Providers"
msgstr "Provedores de Shared Signals Framework"
msgstr ""
#: authentik/enterprise/providers/ssf/models.py
msgid "Add stream to SSF provider"
msgstr "Adicionar stream ao fornecedor SSF"
msgstr ""
#: authentik/enterprise/providers/ssf/models.py
msgid "SSF Stream"
msgstr "Stream SSF"
msgstr ""
#: authentik/enterprise/providers/ssf/models.py
msgid "SSF Streams"
msgstr "Streams SSF"
msgstr ""
#: authentik/enterprise/providers/ssf/models.py
msgid "SSF Stream Event"
msgstr "Evento de stream SSF"
msgstr ""
#: authentik/enterprise/providers/ssf/models.py
msgid "SSF Stream Events"
msgstr "Eventos de stream SSF"
msgstr ""
#: authentik/enterprise/providers/ssf/tasks.py
msgid "Failed to send request"
msgstr "Falha ao enviar requisição"
msgstr ""
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/models.py
msgid "Endpoint Authenticator Google Device Trust Connector Stage"
@ -668,33 +664,6 @@ 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 "
@ -712,7 +681,7 @@ msgstr ""
#: authentik/events/api/tasks.py
#, python-brace-format
msgid "Successfully started task {name}."
msgstr "Tarefa {name} iniciada com sucesso."
msgstr ""
#: authentik/events/models.py
msgid "Event"
@ -744,16 +713,12 @@ 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 ""
@ -1033,11 +998,8 @@ msgid "Starting full provider sync"
msgstr ""
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing users"
msgstr ""
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing groups"
#, python-brace-format
msgid "Syncing page {page} of users"
msgstr ""
#: authentik/lib/sync/outgoing/tasks.py
@ -1352,7 +1314,7 @@ msgstr ""
#: authentik/policies/password/models.py
#, python-brace-format
msgid "Password exists on {count} online lists."
msgstr "A senha está presente em {count} listas de senhas vulneráveis."
msgstr ""
#: authentik/policies/password/models.py
msgid "Password is too weak."
@ -2434,12 +2396,6 @@ 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"
@ -2456,11 +2412,6 @@ 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 ""
@ -2851,11 +2802,6 @@ 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 ""
@ -3228,10 +3174,6 @@ 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"
@ -3248,14 +3190,6 @@ 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

View File

@ -18,7 +18,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
"POT-Creation-Date: 2025-04-23 09:00+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,10 +111,6 @@ 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 "Бренд"
@ -673,33 +669,6 @@ 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 "
@ -1040,11 +1009,8 @@ msgid "Starting full provider sync"
msgstr "Запуск полной синхронизации провайдера"
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing users"
msgstr ""
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing groups"
#, python-brace-format
msgid "Syncing page {page} of users"
msgstr ""
#: authentik/lib/sync/outgoing/tasks.py
@ -2464,12 +2430,6 @@ 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"
@ -2486,11 +2446,6 @@ 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 ""
@ -2887,11 +2842,6 @@ 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"
@ -3269,10 +3219,6 @@ 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 "Этап отказа"
@ -3289,14 +3235,6 @@ 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 "Сброс пароля"

View File

@ -13,7 +13,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
"POT-Creation-Date: 2025-04-23 09:00+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,10 +107,6 @@ 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"
@ -663,33 +659,6 @@ 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 "
@ -1031,11 +1000,8 @@ msgid "Starting full provider sync"
msgstr "Tam sağlayıcı senkronizasyonunu başlatma"
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing users"
msgstr ""
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing groups"
#, python-brace-format
msgid "Syncing page {page} of users"
msgstr ""
#: authentik/lib/sync/outgoing/tasks.py
@ -2464,12 +2430,6 @@ 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ğı"
@ -2486,11 +2446,6 @@ 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 ""
@ -2882,11 +2837,6 @@ 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"
@ -3261,10 +3211,6 @@ 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"
@ -3281,14 +3227,6 @@ 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.

View File

@ -15,7 +15,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
"POT-Creation-Date: 2025-05-20 00:10+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,12 +975,9 @@ msgid "Starting full provider sync"
msgstr "开始全量提供程序同步"
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing users"
msgstr ""
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing groups"
msgstr ""
#, python-brace-format
msgid "Syncing page {page} of users"
msgstr "正在同步用户页面 {page}"
#: authentik/lib/sync/outgoing/tasks.py
#, python-brace-format
@ -2288,12 +2285,6 @@ 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 源"
@ -2310,11 +2301,6 @@ 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 源连接"
@ -2692,11 +2678,6 @@ 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 源"
@ -3063,10 +3044,6 @@ 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 "拒绝阶段"
@ -3083,14 +3060,6 @@ 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.

View File

@ -14,7 +14,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
"POT-Creation-Date: 2025-05-20 00:10+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,12 +974,9 @@ msgid "Starting full provider sync"
msgstr "开始全量提供程序同步"
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing users"
msgstr ""
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing groups"
msgstr ""
#, python-brace-format
msgid "Syncing page {page} of users"
msgstr "正在同步用户页面 {page}"
#: authentik/lib/sync/outgoing/tasks.py
#, python-brace-format
@ -2287,12 +2284,6 @@ 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 源"
@ -2309,11 +2300,6 @@ 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 源连接"
@ -2691,11 +2677,6 @@ 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 源"
@ -3062,10 +3043,6 @@ 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 "拒绝阶段"
@ -3082,14 +3059,6 @@ 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 "密码重置"

View File

@ -14,7 +14,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
"POT-Creation-Date: 2025-04-23 09:00+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,10 +101,6 @@ 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 "品牌"
@ -629,33 +625,6 @@ 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 "
@ -974,11 +943,8 @@ msgid "Starting full provider sync"
msgstr "開始同步所有提供程式"
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing users"
msgstr ""
#: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing groups"
#, python-brace-format
msgid "Syncing page {page} of users"
msgstr ""
#: authentik/lib/sync/outgoing/tasks.py
@ -2283,12 +2249,6 @@ 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 來源"
@ -2305,11 +2265,6 @@ 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 ""
@ -2687,11 +2642,6 @@ 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 來源"
@ -3048,10 +2998,6 @@ 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 "拒絕階段"
@ -3068,14 +3014,6 @@ 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 "重設密碼"

44361
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -2,14 +2,33 @@
"name": "@goauthentik/authentik",
"version": "2025.4.1",
"private": true,
"scripts": {
"lint": "eslint --fix .",
"lint-check": "eslint --max-warnings 0 .",
"lint:types": "NODE_OPTIONS=\"--max-old-space-size=3000\" tsc -b .",
"prettier": "prettier --cache --write -u .",
"prettier-check": "prettier --cache --check -u ."
},
"type": "module",
"devDependencies": {
"@eslint/js": "^9.11.1",
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
"prettier": "^3.3.3",
"prettier-plugin-organize-imports": "^4.1.0",
"prettier-plugin-packagejson": "^2.5.10",
"typescript": "^5.6.2"
"@typescript-eslint/eslint-plugin": "^8.28.0",
"@typescript-eslint/parser": "^8.28.0",
"eslint": "^9.23.0",
"eslint-plugin-lit": "^2.0.0",
"eslint-plugin-wc": "^3.0.0",
"npm-run-all": "^4.1.5",
"prettier": "^3.5.3",
"prettier-plugin-packagejson": "^2.5.13",
"typescript": "^5.8.3",
"typescript-eslint": "^8.29.0"
},
"workspaces": [],
"workspaces": [
"./packages/*",
"./web/packages/*",
"./web",
"./website"
],
"prettier": "./packages/prettier-config/index.js"
}

View File

@ -3710,9 +3710,9 @@
}
},
"node_modules/@goauthentik/prettier-config": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@goauthentik/prettier-config/-/prettier-config-1.0.5.tgz",
"integrity": "sha512-3W1uJvhzBPerDao53hSXhNzB7Ev8DbGYh+gVkuku1FaUZGBpiwD/6U3ah4sny8NoRiObGQ1geF4dhNLtlRbC/Q==",
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@goauthentik/prettier-config/-/prettier-config-1.0.4.tgz",
"integrity": "sha512-CgUVAThlJHif7ZRXUPMbR/7/YLGzkJw7YbqEcleUjjKkvID0aykrypXx04td6cG76zigspTCgJKoXimKT41E7g==",
"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.14"
"prettier-plugin-packagejson": "^2.5.10"
}
},
"node_modules/@goauthentik/tsconfig": {

View File

@ -23,6 +23,7 @@ export const DefaultIgnorePatterns = [
"**/out",
"**/dist",
"**/.wireit",
"**/.venv",
"website/build/**",
"website/.docusaurus/**",
"**/node_modules",
@ -58,16 +59,44 @@ export function createESLintPackageConfig({ ignorePatterns = DefaultIgnorePatter
...reactConfig,
//#region TODO Incomplete Rules
{
// The following rules are disabled because the changes needed to satisfy them are
// are large enough to warrant several follow-up PRs.
rules: {
"no-console": "off",
// TODO: High priority, common and easy to fix.
"eqeqeq": "off",
// TODO: High priority, common and easy to fix.
"no-sparse-arrays": "off",
// TODO: High priority, common and easy to fix.
"no-lonely-if": "off",
// TODO: Reconsider this rule.
"dot-notation": "off",
// TODO: Reconsider this rule.
"no-implicit-coercion": "off",
// TODO: Reconsider this rule.
"prefer-template": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/no-use-before-define": "off",
"array-callback-return": "off",
"block-scoped-var": "off",
"consistent-return": "off",
"func-names": "off",
"guard-for-in": "off",
"no-bitwise": "off",
"no-div-regex": "off",
"no-else-return": "off",
"no-empty-function": "off",
"no-param-reassign": "off",
"no-throw-literal": "off",
"no-var": "error",
"prefer-arrow-callback": "off",
"react/jsx-no-leaked-render": "off",
"vars-on-top": "off",
},
files: [
// ---
"**/scripts/**/*",
"**/test/**/*",
"**/tests/**/*",
],
},
//#endregion
);
}

View File

@ -116,7 +116,6 @@ export const javaScriptConfig = tseslint.config({
"no-useless-call": "error",
"no-dupe-class-members": "error",
"no-var": "error",
"no-void": "error",
"no-with": "error",
"prefer-arrow-callback": "error",
"prefer-const": "error",
@ -131,7 +130,6 @@ export const javaScriptConfig = tseslint.config({
"vars-on-top": "error",
"yoda": ["error", "never"],
"no-console": ["error", { allow: ["debug", "warn", "error"] }],
// SonarJS is not yet compatible with ESLint 9. Commenting these out
// until it is.
// "sonarjs/cognitive-complexity": ["off", MAX_COGNITIVE_COMPLEXITY],

View File

@ -308,9 +308,9 @@
}
},
"node_modules/@goauthentik/prettier-config": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@goauthentik/prettier-config/-/prettier-config-1.0.5.tgz",
"integrity": "sha512-3W1uJvhzBPerDao53hSXhNzB7Ev8DbGYh+gVkuku1FaUZGBpiwD/6U3ah4sny8NoRiObGQ1geF4dhNLtlRbC/Q==",
"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==",
"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.14"
"prettier-plugin-packagejson": "^2.5.10"
}
},
"node_modules/@goauthentik/tsconfig": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@goauthentik/tsconfig/-/tsconfig-1.0.4.tgz",
"integrity": "sha512-BTGVpGh8SbCRHTULBf+2WTcw6OHJ8Ws9VtVfAMUUgcq8whbH/A7Q/n8WbkDaEeihzHUFkLk3JBenHKzEKAZWlw==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@goauthentik/tsconfig/-/tsconfig-1.0.1.tgz",
"integrity": "sha512-kxMDkgUHhAmQ2iIhUZJjrx/CgDb1AwvRoPtU4vrjAZu7x66+qczCjRTK+GzIGCeqB97GEpvCCjU8CThmozVFqA==",
"dev": true,
"license": "MIT",
"engines": {
@ -491,9 +491,9 @@
}
},
"node_modules/@pkgr/core": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.4.tgz",
"integrity": "sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw==",
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.2.tgz",
"integrity": "sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==",
"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/pkgr"
"url": "https://opencollective.com/unts"
}
},
"node_modules/@rtsao/scc": {
@ -2045,6 +2045,20 @@
"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",
@ -2063,9 +2077,9 @@
}
},
"node_modules/git-hooks-list": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-4.1.1.tgz",
"integrity": "sha512-cmP497iLq54AZnv4YRAEMnEyQ1eIn4tGKbmswqwmFV4GBnAqE8NLtWxxdXa++AalfgL5EBH4IxTPyquEuGY/jA==",
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-3.2.0.tgz",
"integrity": "sha512-ZHG9a1gEhUMX1TvGrLdyWb9kDopCBbTnI8z4JgRMYxsijWipgjSEYoPWqBuIB0DnRnvqlQSEeVmzpeuPm7NdFQ==",
"dev": true,
"license": "MIT",
"peer": true,
@ -3205,15 +3219,15 @@
}
},
"node_modules/prettier-plugin-packagejson": {
"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==",
"version": "2.5.10",
"resolved": "https://registry.npmjs.org/prettier-plugin-packagejson/-/prettier-plugin-packagejson-2.5.10.tgz",
"integrity": "sha512-LUxATI5YsImIVSaaLJlJ3aE6wTD+nvots18U3GuQMJpUyClChaZlQrqx3dBnbhF20OnKWZyx8EgyZypQtBDtgQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"sort-package-json": "3.2.1",
"synckit": "0.11.6"
"sort-package-json": "2.15.1",
"synckit": "0.9.2"
},
"peerDependencies": {
"prettier": ">= 1.16.0"
@ -3619,29 +3633,30 @@
"peer": true
},
"node_modules/sort-package-json": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-3.2.1.tgz",
"integrity": "sha512-rTfRdb20vuoAn7LDlEtCqOkYfl2X+Qze6cLbNOzcDpbmKEhJI30tTN44d5shbKJnXsvz24QQhlCm81Bag7EOKg==",
"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==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"detect-indent": "^7.0.1",
"detect-newline": "^4.0.1",
"git-hooks-list": "^4.0.0",
"detect-newline": "^4.0.0",
"get-stdin": "^9.0.0",
"git-hooks-list": "^3.0.0",
"is-plain-obj": "^4.1.0",
"semver": "^7.7.1",
"semver": "^7.6.0",
"sort-object-keys": "^1.1.3",
"tinyglobby": "^0.2.12"
"tinyglobby": "^0.2.9"
},
"bin": {
"sort-package-json": "cli.js"
}
},
"node_modules/sort-package-json/node_modules/semver": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"version": "7.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
"dev": true,
"license": "ISC",
"peer": true,
@ -3791,31 +3806,32 @@
}
},
"node_modules/synckit": {
"version": "0.11.6",
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.6.tgz",
"integrity": "sha512-2pR2ubZSV64f/vqm9eLPz/KOvR9Dm+Co/5ChLgeHl0yEDRc6h5hXHoxEQH8Y5Ljycozd3p1k5TTSVdzYGkPvLw==",
"version": "0.9.2",
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz",
"integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@pkgr/core": "^0.2.4"
"@pkgr/core": "^0.1.0",
"tslib": "^2.6.2"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/synckit"
"url": "https://opencollective.com/unts"
}
},
"node_modules/tinyglobby": {
"version": "0.2.13",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",
"integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
"version": "0.2.12",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz",
"integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"fdir": "^6.4.4",
"fdir": "^6.4.3",
"picomatch": "^4.0.2"
},
"engines": {
@ -3826,9 +3842,9 @@
}
},
"node_modules/tinyglobby/node_modules/fdir": {
"version": "6.4.4",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
"integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
"version": "6.4.3",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz",
"integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==",
"dev": true,
"license": "MIT",
"peer": true,
@ -3893,6 +3909,14 @@
"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",

View File

@ -31,33 +31,8 @@ export const AuthentikPrettierConfig = {
trailingComma: "all",
useTabs: false,
vueIndentScriptAndStyle: false,
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,
plugins: ["prettier-plugin-packagejson", "@trivago/prettier-plugin-sort-imports"],
importOrder: ["^(@?)lit(.*)$", "\\.css$", "^@goauthentik/api$", "^[./]"],
importOrderSeparation: true,
importOrderSortSpecifiers: true,
importOrderParserPlugins: ["typescript", "jsx", "classProperties", "decorators-legacy"],

View File

@ -1,12 +1,12 @@
{
"name": "@goauthentik/prettier-config",
"version": "2.0.0",
"version": "1.0.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@goauthentik/prettier-config",
"version": "2.0.0",
"version": "1.0.4",
"license": "MIT",
"devDependencies": {
"@goauthentik/tsconfig": "^1.0.1",
@ -143,9 +143,9 @@
}
},
"node_modules/@goauthentik/tsconfig": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@goauthentik/tsconfig/-/tsconfig-1.0.4.tgz",
"integrity": "sha512-BTGVpGh8SbCRHTULBf+2WTcw6OHJ8Ws9VtVfAMUUgcq8whbH/A7Q/n8WbkDaEeihzHUFkLk3JBenHKzEKAZWlw==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@goauthentik/tsconfig/-/tsconfig-1.0.1.tgz",
"integrity": "sha512-kxMDkgUHhAmQ2iIhUZJjrx/CgDb1AwvRoPtU4vrjAZu7x66+qczCjRTK+GzIGCeqB97GEpvCCjU8CThmozVFqA==",
"dev": true,
"license": "MIT",
"engines": {

View File

@ -1,6 +1,6 @@
{
"name": "@goauthentik/prettier-config",
"version": "2.0.0",
"version": "1.0.5",
"description": "authentik's Prettier config",
"license": "MIT",
"scripts": {

View File

@ -2,19 +2,19 @@
"name": "@goauthentik/tsconfig",
"version": "1.0.4",
"description": "authentik's base TypeScript configuration.",
"keywords": [
"tsconfig",
"typescript"
],
"license": "MIT",
"scripts": {
"build": ""
},
"type": "module",
"main": "tsconfig.json",
"type": "module",
"engines": {
"node": ">=20.11"
},
"keywords": [
"tsconfig",
"typescript"
],
"publishConfig": {
"access": "public"
}

View File

@ -1,20 +1,22 @@
# syntax=docker/dockerfile:1
# Stage 1: Build web
FROM --platform=${BUILDPLATFORM} docker.io/library/node:24 AS web-builder
FROM --platform=${BUILDPLATFORM} docker.io/library/node:24-slim AS web-builder
WORKDIR /work
COPY ./package.json /work
COPY ./package-lock.json /work
COPY ./tsconfig.json /work
COPY ./packages/ /work/packages/
COPY ./web /work/web/
RUN --mount=type=cache,id=npm-node,sharing=shared,target=/root/.npm \
npm ci
ENV NODE_ENV=production
WORKDIR /static
COPY package.json /
RUN --mount=type=bind,target=/static/package.json,src=./web/package.json \
--mount=type=bind,target=/static/package-lock.json,src=./web/package-lock.json \
--mount=type=bind,target=/static/scripts,src=./web/scripts \
--mount=type=cache,target=/root/.npm \
npm ci --include=dev
COPY web .
RUN npm run build-proxy
RUN npm run build-proxy -w @goauthentik/web
# Stage 2: Build
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.24-bookworm AS builder
@ -65,10 +67,10 @@ RUN apt-get update && \
rm -rf /tmp/* /var/lib/apt/lists/*
COPY --from=builder /go/proxy /
COPY --from=web-builder /static/robots.txt /web/robots.txt
COPY --from=web-builder /static/security.txt /web/security.txt
COPY --from=web-builder /static/dist/ /web/dist/
COPY --from=web-builder /static/authentik/ /web/authentik/
COPY --from=web-builder /work/web/robots.txt /web/robots.txt
COPY --from=web-builder /work/web/security.txt /web/security.txt
COPY --from=web-builder /work/web/dist/ /web/dist/
COPY --from=web-builder /work/web/authentik/ /web/authentik/
HEALTHCHECK --interval=5s --retries=20 --start-period=3s CMD [ "/proxy", "healthcheck" ]

View File

@ -9,7 +9,7 @@ dependencies = [
"celery==5.5.2",
"channels==4.2.2",
"channels-redis==4.2.1",
"cryptography==45.0.3",
"cryptography==44.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.8.0",
"django-tenants==3.7.0",
"djangorestframework==3.16.0",
"djangorestframework-guardian==0.3.0",
"docker==7.1.0",
@ -35,7 +35,7 @@ dependencies = [
"flower==2.0.1",
"geoip2==5.1.0",
"geopy==2.4.1",
"google-api-python-client==2.170.0",
"google-api-python-client==2.169.0",
"gssapi==1.9.0",
"gunicorn==23.0.0",
"jsonpatch==1.33",
@ -43,12 +43,12 @@ dependencies = [
"kubernetes==32.0.1",
"ldap3==2.9.1",
"lxml==5.4.0",
"msgraph-sdk==1.31.0",
"msgraph-sdk==1.30.0",
"opencontainers==0.0.14",
"packaging==25.0",
"paramiko==3.5.1",
"psycopg[c,pool]==3.2.9",
"pydantic==2.11.5",
"pydantic==2.11.4",
"pydantic-scim==0.0.8",
"pyjwt==2.10.1",
"pyrad==2.4",
@ -114,6 +114,7 @@ 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" }

View File

@ -28473,10 +28473,6 @@ paths:
schema:
type: string
format: uuid
- in: query
name: delete_not_found_objects
schema:
type: boolean
- in: query
name: enabled
schema:
@ -47926,10 +47922,6 @@ 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
@ -48131,10 +48123,6 @@ 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
@ -53468,10 +53456,6 @@ 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

View File

@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/bin/bash
set -e -x -o pipefail
hash="$(git rev-parse HEAD || openssl rand -base64 36 | sha256sum)"

View File

@ -1,13 +1,12 @@
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.1
image: docker.io/axllent/mailpit:v1.25.0
ports:
- 1025:1025
- 8025:8025

View File

@ -10,7 +10,6 @@ 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
@ -18,10 +17,6 @@ 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",
@ -44,8 +39,8 @@ class TestFlowsEnroll(SeleniumTestCase):
self.initial_stages()
sleep(2)
user = User.objects.get(username=self.username)
self.assertEqual(user.username, self.username)
user = User.objects.get(username="foo")
self.assertEqual(user.username, "foo")
self.assertEqual(user.name, "some name")
self.assertEqual(user.email, "foo@bar.baz")
@ -92,16 +87,7 @@ class TestFlowsEnroll(SeleniumTestCase):
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()
self.wait_for_url(self.if_user_url())
self.assert_user(User.objects.get(username=self.username))
self.assert_user(User.objects.get(username="foo"))
def initial_stages(self):
"""Fill out initial stages"""
@ -119,7 +105,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(self.username)
prompt_stage.find_element(By.CSS_SELECTOR, "input[name=username]").send_keys("foo")
prompt_stage.find_element(By.CSS_SELECTOR, "input[name=password]").send_keys(
self.user.username
)
@ -138,82 +124,3 @@ 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")))

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