Merge branch 'main' into stages/user_login/dbsc

This commit is contained in:
Jens Langhammer
2026-06-10 18:39:20 +02:00
952 changed files with 25443 additions and 8361 deletions
+25 -17
View File
@@ -20,12 +20,22 @@ runs:
shell: bash
env:
GITHUB_TOKEN: ${{ inputs.token }}
# Untrusted/event-derived values are passed via the environment (never
# interpolated into the script body) to avoid template injection.
EVENT_NAME: ${{ github.event_name }}
REPOSITORY: ${{ github.repository }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
LABEL_NAME_CTX: ${{ github.event.label.name }}
PR_NUMBER_CTX: ${{ github.event.pull_request.number }}
MERGE_COMMIT_SHA_CTX: ${{ github.event.pull_request.merge_commit_sha }}
EVENT_ACTION: ${{ github.event.action }}
PR_MERGED_CTX: ${{ github.event.pull_request.merged }}
run: |
set -e -o pipefail
# For issues events, check if it's actually a PR
if [ "${{ github.event_name }}" = "issues" ]; then
if [ "$EVENT_NAME" = "issues" ]; then
# Check if this issue is actually a PR
PR_DATA=$(gh api repos/${{ github.repository }}/pulls/${{ github.event.issue.number }} 2>/dev/null || echo "null")
PR_DATA=$(gh api "repos/${REPOSITORY}/pulls/${ISSUE_NUMBER}" 2>/dev/null || echo "null")
if [ "$PR_DATA" = "null" ]; then
echo "should_run=false" >> $GITHUB_OUTPUT
echo "reason=not_a_pr" >> $GITHUB_OUTPUT
@@ -35,11 +45,11 @@ runs:
# Get PR data
PR_MERGED=$(echo "$PR_DATA" | jq -r '.merged')
PR_NUMBER="${{ github.event.issue.number }}"
PR_NUMBER="$ISSUE_NUMBER"
MERGE_COMMIT_SHA=$(echo "$PR_DATA" | jq -r '.merge_commit_sha')
# Check if it's a backport label
LABEL_NAME="${{ github.event.label.name }}"
LABEL_NAME="$LABEL_NAME_CTX"
if [[ "$LABEL_NAME" =~ ^backport/(.+)$ ]]; then
if [ "$PR_MERGED" = "true" ]; then
echo "should_run=true" >> $GITHUB_OUTPUT
@@ -61,11 +71,11 @@ runs:
fi
# For pull_request and pull_request_target events
PR_NUMBER="${{ github.event.pull_request.number }}"
MERGE_COMMIT_SHA="${{ github.event.pull_request.merge_commit_sha }}"
PR_NUMBER="$PR_NUMBER_CTX"
MERGE_COMMIT_SHA="$MERGE_COMMIT_SHA_CTX"
# Case 1: PR was just merged (closed + merged = true)
if [ "${{ github.event.action }}" = "closed" ] && [ "${{ github.event.pull_request.merged }}" = "true" ]; then
if [ "$EVENT_ACTION" = "closed" ] && [ "$PR_MERGED_CTX" = "true" ]; then
echo "should_run=true" >> $GITHUB_OUTPUT
echo "reason=pr_merged" >> $GITHUB_OUTPUT
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
@@ -74,12 +84,12 @@ runs:
fi
# Case 2: Label was added
if [ "${{ github.event.action }}" = "labeled" ]; then
LABEL_NAME="${{ github.event.label.name }}"
if [ "$EVENT_ACTION" = "labeled" ]; then
LABEL_NAME="$LABEL_NAME_CTX"
# Check if it's a backport label
if [[ "$LABEL_NAME" =~ ^backport/(.+)$ ]]; then
# Check if PR is already merged
if [ "${{ github.event.pull_request.merged }}" = "true" ]; then
if [ "$PR_MERGED_CTX" = "true" ]; then
echo "should_run=true" >> $GITHUB_OUTPUT
echo "reason=label_added_to_merged_pr" >> $GITHUB_OUTPUT
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
@@ -117,17 +127,14 @@ runs:
GITHUB_TOKEN: ${{ inputs.token }}
PR_NUMBER: ${{ steps.should_run.outputs.pr_number }}
REASON: ${{ steps.should_run.outputs.reason }}
EVENT_NAME: ${{ github.event_name }}
LABEL_NAME_CTX: ${{ github.event.label.name }}
run: |
set -e -o pipefail
# Determine which labels to process
if [ "${REASON}" = "label_added_to_merged_pr" ]; then
# Only process the specific label that was just added
if [ "${{ github.event_name }}" = "issues" ]; then
LABEL_NAME="${{ github.event.label.name }}"
else
LABEL_NAME="${{ github.event.label.name }}"
fi
LABEL_NAME="$LABEL_NAME_CTX"
if [[ "$LABEL_NAME" =~ ^backport/(.+)$ ]]; then
echo "labels=$LABEL_NAME" >> $GITHUB_OUTPUT
@@ -150,10 +157,11 @@ runs:
PR_TITLE: ${{ github.event.pull_request.title }}
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
LABELS: '${{ steps.pr_details.outputs.labels }}'
REASON: '${{ steps.should_run.outputs.reason }}'
run: |
set -e -o pipefail
echo "Processing PR #$PR_NUMBER (reason: ${{ steps.should_run.outputs.reason }})"
echo "Processing PR #$PR_NUMBER (reason: ${REASON})"
echo "Found backport labels: $LABELS"
# Process each backport label
+3 -3
View File
@@ -37,7 +37,7 @@ runs:
sudo rsync -a --delete /tmp/empty/ /usr/local/lib/android/
- name: Install uv
if: ${{ contains(inputs.dependencies, 'python') }}
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v5
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v5
with:
enable-cache: true
- name: Setup python
@@ -64,7 +64,7 @@ runs:
rustflags: ""
- name: Setup rust dependencies
if: ${{ contains(inputs.dependencies, 'rust') }}
uses: taiki-e/install-action@ec28e287910af896fd98e04056d31fa68607e7ad # v2
uses: taiki-e/install-action@56545b37b57562edd73171cb6c62cc509db4c34e # v2
with:
tool: cargo-deny cargo-machete cargo-llvm-cov nextest
- name: Setup node (root, web)
@@ -79,7 +79,7 @@ runs:
go-version-file: "${{ inputs.working-directory }}go.mod"
- name: Setup docker cache
if: ${{ contains(inputs.dependencies, 'runtime') }}
uses: AndreKurait/docker-cache@0fe76702a40db986d9663c24954fc14c6a6031b7
uses: AndreKurait/docker-cache@7a3887908bdb97935395833df69b060cfcca0f7f
with:
key: docker-images-${{ runner.os }}-${{ hashFiles('.github/actions/setup/compose.yml', 'Makefile') }}-${{ inputs.postgresql_version }}
- name: Setup dependencies
+2 -2
View File
@@ -10,12 +10,12 @@ inputs:
runs:
using: "composite"
steps:
- uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v5
- uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v5
with:
files: ${{ inputs.files }}
flags: ${{ inputs.flags }}
use_oidc: true
- uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v5
- uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v5
with:
files: ${{ inputs.files }}
flags: ${{ inputs.flags }}
+4
View File
@@ -307,6 +307,10 @@ updates:
- "lxml"
- "psycopg"
- "pyopenssl"
groups:
types:
patterns:
- types-*
#endregion
+13 -25
View File
@@ -1,38 +1,26 @@
<!--
👋 Hi there! Welcome.
👋 Hi there! Welcome. Please check the contributing guidelines: https://docs.goauthentik.io/docs/developer-docs/#how-can-i-contribute
Please check the Contributing guidelines: https://docs.goauthentik.io/docs/developer-docs/#how-can-i-contribute
⚠️ IMPORTANT: Make sure you are opening this PR from a FEATURE BRANCH, not from your main branch!
If you opened this PR from your main branch, please close it and create a new feature branch instead.
For more information, see: https://docs.goauthentik.io/developer-docs/contributing/#always-use-feature-branches
⚠️ Open this PR from a feature branch, not from main: https://docs.goauthentik.io/developer-docs/contributing/#always-use-feature-branches
-->
## Details
<!--
Explain what this PR changes, what the rationale behind the change is, if any new requirements are introduced or any breaking changes caused by this PR.
### What does this PR change?
Ideally also link an Issue for context that this PR will close using `closes #`
### Why is this change needed?
### How was this tested?
### Linked issues
<!--
Use `closes #N` to auto-close an issue on merge. Use `refs #N` for related issues that this PR does not close.
-->
REPLACE ME
---
## Checklist
- [ ] Local tests pass (`ak test authentik/`)
- [ ] The code has been formatted (`make lint-fix`)
If an API change has been made
- [ ] The API schema and clients have been updated (`make gen`)
If changes to the frontend have been made
- [ ] The code has been formatted (`make web`)
If applicable
- [ ] The documentation has been updated
- [ ] The documentation has been formatted (`make docs`)
- [ ] The project has been linted, built, and tested (`make all`)
- [ ] The documentation has been updated and formatted (`make docs`)
@@ -42,9 +42,9 @@ jobs:
# Needed for checkout
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
- uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
- uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
- name: prepare variables
uses: ./.github/actions/docker-push-variables
id: ev
@@ -56,13 +56,13 @@ jobs:
release: ${{ inputs.release }}
- name: Login to Docker Hub
if: ${{ inputs.registry_dockerhub }}
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
username: ${{ secrets.DOCKER_CORP_USERNAME }}
password: ${{ secrets.DOCKER_CORP_PASSWORD }}
- name: Login to GitHub Container Registry
if: ${{ inputs.registry_ghcr }}
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
@@ -71,14 +71,14 @@ jobs:
with:
working-directory: web
dependencies: "monorepo"
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
with:
go-version-file: "go.mod"
- name: Generate API Clients
run: |
make gen-client-ts
- name: Build Docker Image
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
id: push
with:
context: .
+6 -6
View File
@@ -36,7 +36,7 @@ jobs:
with:
image_name: ${{ inputs.image_name }}
image_arch: arm64
runs-on: ubuntu-22.04-arm
runs-on: ubuntu-24.04-arm
registry_dockerhub: ${{ inputs.registry_dockerhub }}
registry_ghcr: ${{ inputs.registry_ghcr }}
release: ${{ inputs.release }}
@@ -49,7 +49,7 @@ jobs:
tags: ${{ steps.ev.outputs.imageTagsJSON }}
shouldPush: ${{ steps.ev.outputs.shouldPush }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- name: prepare variables
uses: ./.github/actions/docker-push-variables
id: ev
@@ -69,7 +69,7 @@ jobs:
matrix:
tag: ${{ fromJson(needs.get-tags.outputs.tags) }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- name: prepare variables
uses: ./.github/actions/docker-push-variables
id: ev
@@ -79,18 +79,18 @@ jobs:
image-name: ${{ inputs.image_name }}
- name: Login to Docker Hub
if: ${{ inputs.registry_dockerhub }}
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
username: ${{ secrets.DOCKER_CORP_USERNAME }}
password: ${{ secrets.DOCKER_CORP_PASSWORD }}
- name: Login to GitHub Container Registry
if: ${{ inputs.registry_ghcr }}
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: int128/docker-manifest-create-action@fa55f72001a6c74b0f4997dca65c70d334905180 # v2
- uses: int128/docker-manifest-create-action@126c2b2195800ebc112cffe9ad6c2e2cce16eff2 # v2
id: build
with:
tags: ${{ matrix.tag }}
+3 -3
View File
@@ -21,7 +21,7 @@ jobs:
command:
- prettier-check
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- uses: ./.github/actions/setup-node
with:
working-directory: website
@@ -31,7 +31,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- uses: ./.github/actions/setup-node
with:
working-directory: website
@@ -60,7 +60,7 @@ jobs:
- lint
- build
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v5
with:
name: api-docs
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
check-changes-applied:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- name: Setup authentik env
uses: ./.github/actions/setup
- uses: ./.github/actions/setup-node
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 120
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- name: Setup authentik env
uses: ./.github/actions/setup
- name: generate docs
+8 -8
View File
@@ -23,7 +23,7 @@ jobs:
command:
- prettier-check
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- uses: ./.github/actions/setup-node
with:
working-directory: website
@@ -34,7 +34,7 @@ jobs:
env:
NODE_ENV: production
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- uses: ./.github/actions/setup-node
name: Setup Node.js
with:
@@ -46,7 +46,7 @@ jobs:
env:
NODE_ENV: production
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- uses: ./.github/actions/setup-node
with:
working-directory: website
@@ -61,13 +61,13 @@ jobs:
id-token: write
attestations: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Set up QEMU
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
- name: prepare variables
uses: ./.github/actions/docker-push-variables
id: ev
@@ -77,14 +77,14 @@ jobs:
image-name: ghcr.io/goauthentik/dev-docs
- name: Login to Container Registry
if: ${{ steps.ev.outputs.shouldPush == 'true' }}
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Docker Image
id: push
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
with:
tags: ${{ steps.ev.outputs.imageTags }}
file: website/Dockerfile
+3 -8
View File
@@ -19,20 +19,15 @@ jobs:
matrix:
version:
- docs
- version-2025-12
- version-2026-2
- version-2026-5
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- run: |
set -euo pipefail
current="$(pwd)"
dir="/tmp/authentik/${{ matrix.version }}"
# 2025.12 still serves the legacy docker-compose filename; newer sites use compose.yml.
compose_path="compose.yml"
if [ "${{ matrix.version }}" = "version-2025-12" ]; then
compose_path="docker-compose.yml"
fi
mkdir -p "${dir}/lifecycle/container"
cd "${dir}"
wget "https://${{ matrix.version }}.goauthentik.io/${compose_path}" -O "${dir}/lifecycle/container/compose.yml"
wget "https://${{ matrix.version }}.goauthentik.io/compose.yml" -O "${dir}/lifecycle/container/compose.yml"
"${current}/scripts/test_docker.sh"
+10 -10
View File
@@ -51,7 +51,7 @@ jobs:
deps: rust-nightly
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- name: Setup authentik env
uses: ./.github/actions/setup
with:
@@ -61,7 +61,7 @@ jobs:
test-gen:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Setup authentik env
@@ -77,7 +77,7 @@ jobs:
test-migrations:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- name: Setup authentik env
uses: ./.github/actions/setup
- name: run migrations
@@ -103,7 +103,7 @@ jobs:
- 18-alpine
run_id: [1, 2, 3, 4, 5]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
with:
fetch-depth: 0
- name: checkout stable
@@ -179,7 +179,7 @@ jobs:
- 18-alpine
run_id: [1, 2, 3, 4, 5]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- name: Setup authentik env
uses: ./.github/actions/setup
with:
@@ -199,7 +199,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- name: Setup authentik env
uses: ./.github/actions/setup
- name: Create k8s Kind Cluster
@@ -252,7 +252,7 @@ jobs:
glob: tests/e2e/test_endpoints_*
profiles: selenium
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- name: Setup authentik env
uses: ./.github/actions/setup
- name: Setup e2e env
@@ -306,7 +306,7 @@ jobs:
- name: ssf_transmitter
glob: tests/openid_conformance/test_ssf_transmitter.py
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- name: Setup authentik env
uses: ./.github/actions/setup
- name: Setup e2e env (chrome, etc)
@@ -348,7 +348,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- name: Setup authentik env
uses: ./.github/actions/setup
with:
@@ -407,7 +407,7 @@ jobs:
pull-requests: write
timeout-minutes: 120
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: prepare variables
+9 -9
View File
@@ -21,7 +21,7 @@ jobs:
lint-golint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
with:
go-version-file: "go.mod"
@@ -32,7 +32,7 @@ jobs:
mkdir -p website/help
touch web/dist/test website/help/test
- name: golangci-lint
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v8
uses: golangci/golangci-lint-action@82606bf257cbaff209d206a39f5134f0cfbfd2ee # v8
with:
version: latest
args: --timeout 5000s --verbose
@@ -40,7 +40,7 @@ jobs:
test-unittest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
with:
go-version-file: "go.mod"
@@ -82,13 +82,13 @@ jobs:
id-token: write
attestations: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Set up QEMU
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
- name: prepare variables
uses: ./.github/actions/docker-push-variables
id: ev
@@ -98,14 +98,14 @@ jobs:
image-name: ghcr.io/goauthentik/dev-${{ matrix.type }}
- name: Login to Container Registry
if: ${{ steps.ev.outputs.shouldPush == 'true' }}
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Docker Image
id: push
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
with:
tags: ${{ steps.ev.outputs.imageTags }}
file: lifecycle/container/${{ matrix.type }}.Dockerfile
@@ -139,7 +139,7 @@ jobs:
goos: [linux]
goarch: [amd64, arm64]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
with:
ref: ${{ github.event.pull_request.head.sha }}
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
+3 -3
View File
@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- uses: ./.github/actions/setup-node
with:
working-directory: web
@@ -34,7 +34,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- uses: ./.github/actions/setup-node
with:
working-directory: web
@@ -59,7 +59,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- uses: ./.github/actions/setup-node
with:
working-directory: web
+4 -4
View File
@@ -29,16 +29,16 @@ jobs:
github.event.pull_request.head.repo.full_name == github.repository)
steps:
- id: generate_token
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v2
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v2
with:
app-id: ${{ secrets.GH_APP_ID }}
client-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
with:
token: ${{ steps.generate_token.outputs.token }}
- name: Compress images
id: compress
uses: calibreapp/image-actions@e2cc8db5d49c849e00844dfebf01438318e96fa2 # main
uses: calibreapp/image-actions@9d037c06280028c110ff61c433ad4dc7d33c3c43 # main
with:
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
compressOnly: ${{ github.event_name != 'pull_request' }}
@@ -16,11 +16,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- id: generate_token
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v2
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v2
with:
app-id: ${{ secrets.GH_APP_ID }}
client-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
with:
token: ${{ steps.generate_token.outputs.token }}
- name: Setup authentik env
+3 -3
View File
@@ -10,14 +10,14 @@ jobs:
steps:
- id: app-token
name: Generate app token
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v2
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v2
if: ${{ env.GH_APP_ID != '' }}
with:
app-id: ${{ secrets.GH_APP_ID }}
client-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
env:
GH_APP_ID: ${{ secrets.GH_APP_ID }}
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
if: ${{ steps.app-token.outcome != 'skipped' }}
with:
fetch-depth: 0
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- name: Cleanup
run: |
+2 -2
View File
@@ -16,12 +16,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- id: generate_token
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v2
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v2
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
- name: Delete 'dev' containers older than a week
uses: snok/container-retention-policy@3b0972b2276b171b212f8c4efbca59ebba26eceb # v3.0.1
uses: snok/container-retention-policy@d3bdcf5ce9b05f685154e4a16c39233b245e3d53 # v3.1.0
with:
image-names: dev-server,dev-ldap,dev-proxy
image-tags: "!gh-next,!gh-main"
+1 -1
View File
@@ -32,7 +32,7 @@ jobs:
- packages/logger-js
- packages/esbuild-plugin-live-reload
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
with:
fetch-depth: 2
- uses: ./.github/actions/setup-node
+4 -4
View File
@@ -24,14 +24,14 @@ jobs:
language: ["go", "javascript", "python"]
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- name: Setup authentik env
uses: ./.github/actions/setup
- name: Initialize CodeQL
uses: github/codeql-action/init@v4.35.4
uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v4.35.4
uses: github/codeql-action/autobuild@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4.35.4
uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
@@ -0,0 +1,32 @@
---
name: QA - Dependency review
# Blocks PRs that introduce a dependency with a known vulnerability above
# the configured severity threshold. Covers every ecosystem GitHub's
# Advisory Database supports — for this repo that's npm, Go modules,
# Python (pip/uv), Cargo, and GitHub Actions.
on:
pull_request:
branches: [main]
permissions:
contents: read
pull-requests: write
jobs:
dependency-review:
name: dependency-review
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
with:
# Block PRs that introduce a *new* dependency with a known
# high/critical vuln. We don't fail on existing vulns — those are
# surfaced separately via Dependabot.
fail-on-severity: high
# Surface a summary comment on the PR itself, in addition to the
# check status, so reviewers can see the diff at a glance.
comment-summary-in-pr: on-failure
+1 -1
View File
@@ -26,5 +26,5 @@ jobs:
image: semgrep/semgrep
if: (github.actor != 'dependabot[bot]')
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- run: semgrep ci
+6 -6
View File
@@ -29,12 +29,12 @@ jobs:
steps:
- id: app-token
name: Generate app token
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v2
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v2
with:
app-id: ${{ secrets.GH_APP_ID }}
client-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
- name: Checkout main
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
with:
ref: main
token: "${{ steps.app-token.outputs.token }}"
@@ -57,12 +57,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- id: generate_token
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v2
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v2
with:
app-id: ${{ secrets.GH_APP_ID }}
client-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
- name: Checkout main
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
with:
ref: main
token: ${{ steps.generate_token.outputs.token }}
+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
environment: internal-production
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
with:
ref: main
- run: |
+17 -17
View File
@@ -31,11 +31,11 @@ jobs:
id-token: write
attestations: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- name: Set up QEMU
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
- name: prepare variables
uses: ./.github/actions/docker-push-variables
id: ev
@@ -44,14 +44,14 @@ jobs:
with:
image-name: ghcr.io/goauthentik/docs
- name: Login to GitHub Container Registry
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Docker Image
id: push
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
with:
tags: ${{ steps.ev.outputs.imageTags }}
file: website/Dockerfile
@@ -83,7 +83,7 @@ jobs:
- radius
- rac
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
with:
go-version-file: "go.mod"
@@ -91,9 +91,9 @@ jobs:
with:
working-directory: web
- name: Set up QEMU
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
- name: prepare variables
uses: ./.github/actions/docker-push-variables
id: ev
@@ -102,18 +102,18 @@ jobs:
with:
image-name: ghcr.io/goauthentik/${{ matrix.type }},authentik/${{ matrix.type }}
- name: Docker Login Registry
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
username: ${{ secrets.DOCKER_CORP_USERNAME }}
password: ${{ secrets.DOCKER_CORP_PASSWORD }}
- name: Login to GitHub Container Registry
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Docker Image
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
id: push
with:
push: true
@@ -145,7 +145,7 @@ jobs:
goos: [linux, darwin]
goarch: [amd64, arm64]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
with:
go-version-file: "go.mod"
@@ -182,8 +182,8 @@ jobs:
AWS_REGION: eu-central-1
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: aws-actions/configure-aws-credentials@d979d5b3a71173a29b74b5b88418bfda9437d885 # v6.1.1
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- uses: aws-actions/configure-aws-credentials@e7f100cf4c008499ea8adda475de1042d6975c7b # v6.2.0
with:
role-to-assume: "arn:aws:iam::016170277896:role/github_goauthentik_authentik"
aws-region: ${{ env.AWS_REGION }}
@@ -198,7 +198,7 @@ jobs:
- build-outpost-binary
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- name: Run test suite in final docker images
run: |
echo "PG_PASS=$(openssl rand 32 | base64 -w 0)" >> lifecycle/container/.env
@@ -214,7 +214,7 @@ jobs:
- build-outpost-binary
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
- name: prepare variables
uses: ./.github/actions/docker-push-variables
id: ev
@@ -228,7 +228,7 @@ jobs:
container=$(docker container create ${{ steps.ev.outputs.imageMainName }})
docker cp ${container}:web/ .
- name: Create a Sentry.io release
uses: getsentry/action-release@5657c9e888b4e2cc85f4d29143ea4131fde4a73a # v3
uses: getsentry/action-release@ff07929a6537bac57790c3451cf4d364aca38528 # v3
continue-on-error: true
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
+10 -10
View File
@@ -52,7 +52,7 @@ jobs:
needs:
- check-inputs
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
with:
ref: "version-${{ needs.check-inputs.outputs.major_version }}"
- name: Setup authentik env
@@ -67,16 +67,16 @@ jobs:
steps:
- id: app-token
name: Generate app token
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v2
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v2
with:
app-id: ${{ secrets.GH_APP_ID }}
client-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
- id: get-user-id
name: Get GitHub app user ID
run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: "${{ steps.app-token.outputs.token }}"
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
with:
ref: "version-${{ needs.check-inputs.outputs.major_version }}"
token: "${{ steps.app-token.outputs.token }}"
@@ -119,9 +119,9 @@ jobs:
steps:
- id: app-token
name: Generate app token
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v2
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v2
with:
app-id: ${{ secrets.GH_APP_ID }}
client-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
repositories: helm
- id: get-user-id
@@ -129,7 +129,7 @@ jobs:
run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: "${{ steps.app-token.outputs.token }}"
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
with:
repository: "${{ github.repository_owner }}/helm"
token: "${{ steps.app-token.outputs.token }}"
@@ -161,9 +161,9 @@ jobs:
steps:
- id: app-token
name: Generate app token
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v2
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v2
with:
app-id: ${{ secrets.GH_APP_ID }}
client-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
repositories: version
- id: get-user-id
@@ -171,7 +171,7 @@ jobs:
run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: "${{ steps.app-token.outputs.token }}"
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
with:
repository: "${{ github.repository_owner }}/version"
token: "${{ steps.app-token.outputs.token }}"
+3 -3
View File
@@ -15,11 +15,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- id: generate_token
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v2
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v2
with:
app-id: ${{ secrets.GH_APP_ID }}
client-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10
- uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10
with:
repo-token: ${{ steps.generate_token.outputs.token }}
days-before-stale: 60
@@ -21,15 +21,15 @@ jobs:
steps:
- id: generate_token
if: ${{ github.event_name != 'pull_request' }}
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v2
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v2
with:
app-id: ${{ secrets.GH_APP_ID }}
client-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
if: ${{ github.event_name != 'pull_request' }}
with:
token: ${{ steps.generate_token.outputs.token }}
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v5
if: ${{ github.event_name == 'pull_request' }}
- name: Setup authentik env
uses: ./.github/actions/setup
+9 -5
View File
@@ -20,6 +20,10 @@ corepack.tgz
.cspellcache
cspell-report.*
# Release generation artifacts
/changelog.md
/diff.md
# If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/
# in your Git repository. Update and uncomment the following line accordingly.
# <django-project-name>/staticfiles/
@@ -231,11 +235,11 @@ source_docs/
### Golang ###
/vendor/
server
proxy
ldap
rac
radius
/server
/proxy
/ldap
/rac
/radius
### Docker ###
tests/openid_conformance/exports/*.zip
+1
View File
@@ -51,6 +51,7 @@ packages/tsconfig @goauthentik/frontend
web/ @goauthentik/frontend
# Locale
/locale/ @goauthentik/backend @goauthentik/frontend
/locale/*/dictionaries @goauthentik/frontend @goauthentik/docs
web/xliff/ @goauthentik/backend @goauthentik/frontend
# Docs
website/ @goauthentik/docs
Generated
+52 -42
View File
@@ -184,7 +184,7 @@ dependencies = [
"hyper-util",
"metrics",
"metrics-exporter-prometheus",
"nix 0.31.2",
"nix 0.31.3",
"pyo3",
"pyo3-build-config",
"sqlx",
@@ -244,7 +244,7 @@ dependencies = [
"glob",
"ipnet",
"json-subscriber",
"nix 0.31.2",
"nix 0.31.3",
"notify",
"pin-project-lite",
"reqwest",
@@ -288,9 +288,9 @@ dependencies = [
[[package]]
name = "aws-lc-rs"
version = "1.16.3"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ec6fb3fe69024a75fa7e1bfb48aa6cf59706a101658ea01bfd33b2b248a038f"
checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00"
dependencies = [
"aws-lc-fips-sys",
"aws-lc-sys",
@@ -300,9 +300,9 @@ dependencies = [
[[package]]
name = "aws-lc-sys"
version = "0.40.0"
version = "0.41.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f50037ee5e1e41e7b8f9d161680a725bd1626cb6f8c7e901f91f942850852fe7"
checksum = "1a2f9779ce85b93ab6170dd940ad0169b5766ff848247aff13bb788b832fe3f4"
dependencies = [
"cc",
"cmake",
@@ -485,6 +485,15 @@ dependencies = [
"objc2",
]
[[package]]
name = "bs58"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
dependencies = [
"tinyvec",
]
[[package]]
name = "bumpalo"
version = "3.20.2"
@@ -1892,9 +1901,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.183"
version = "0.2.186"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "libloading"
@@ -2013,9 +2022,9 @@ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
[[package]]
name = "metrics"
version = "0.24.5"
version = "0.24.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff56c2e7dce6bd462e3b8919986a617027481b1dcc703175b58cf9dd98a2f071"
checksum = "89550ee9f79e88fef3119de263694973a8adb26c21d75322164fb8c493039fe2"
dependencies = [
"portable-atomic",
"rapidhash",
@@ -2110,9 +2119,9 @@ dependencies = [
[[package]]
name = "nix"
version = "0.31.2"
version = "0.31.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3"
checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
dependencies = [
"bitflags 2.11.0",
"cfg-if",
@@ -2916,9 +2925,9 @@ checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
[[package]]
name = "reqwest"
version = "0.13.3"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62e0021ea2c22aed41653bc7e1419abb2c97e038ff2c33d0e1309e49a97deec0"
checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3"
dependencies = [
"base64 0.22.1",
"bytes",
@@ -2962,9 +2971,9 @@ dependencies = [
[[package]]
name = "reqwest-middleware"
version = "0.5.1"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "199dda04a536b532d0cc04d7979e39b1c763ea749bf91507017069c00b96056f"
checksum = "07bc3f1384cffa4f274dad2d4ddd73aed32fed8f786d96c6be8aa4e5fd3c3b58"
dependencies = [
"anyhow",
"async-trait",
@@ -3193,9 +3202,9 @@ checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
[[package]]
name = "sentry"
version = "0.48.1"
version = "0.48.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b93b3e19f45495ddd41d8222a152c48c84f6ba45abe9c69e2527e9cdea29bb5b"
checksum = "931a20b0da02350676e3d6d3c9028d58eaa448cf42a866712eec5845a505421e"
dependencies = [
"cfg_aliases",
"httpdate",
@@ -3214,9 +3223,9 @@ dependencies = [
[[package]]
name = "sentry-backtrace"
version = "0.48.1"
version = "0.48.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc84c325ace9ca2388e510fe7d6672b5d60cd8b3bd0eb4bb4ee8314c323cd686"
checksum = "911ee36abf5b7fa335fccd5f54361ba9c16baea5f0c3bb361a687b6c195c21cf"
dependencies = [
"backtrace",
"regex",
@@ -3225,9 +3234,9 @@ dependencies = [
[[package]]
name = "sentry-contexts"
version = "0.48.1"
version = "0.48.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "896c1ab62dbfe1746fb262bbf72e6feb2fb9dfb2c14709077bf71beb532e44b2"
checksum = "9b9d7d469e9e22741c17ca23fb8b42d79861590eb7cf330f3da34fc1e4bc1bc6"
dependencies = [
"hostname",
"libc",
@@ -3239,9 +3248,9 @@ dependencies = [
[[package]]
name = "sentry-core"
version = "0.48.1"
version = "0.48.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5f5abf20c42cb1593ec1638976e2647da55f79bccac956444c1707b6cce259a"
checksum = "545dc562b6758d646ac19e1407f4ebc26d452111386743e03323464bc48bb2e0"
dependencies = [
"rand 0.9.4",
"sentry-types",
@@ -3252,9 +3261,9 @@ dependencies = [
[[package]]
name = "sentry-debug-images"
version = "0.48.1"
version = "0.48.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b88bbe6a760d5724bb40689827e82e8db1e275947df2c59abe171bfc30bb671"
checksum = "660e9def38a573a869a182f7e90f58aaaa460f38b92b31fd1755ec537193bb48"
dependencies = [
"findshlibs",
"sentry-core",
@@ -3262,9 +3271,9 @@ dependencies = [
[[package]]
name = "sentry-panic"
version = "0.48.1"
version = "0.48.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0260dcb52562b6a79ae7702312a26dba94b79fb5baee7301087529e5ca4e872e"
checksum = "772d9de150c8ca910c835353c85f434457348fdd21208f9b3da3574202b1dc5d"
dependencies = [
"sentry-backtrace",
"sentry-core",
@@ -3272,9 +3281,9 @@ dependencies = [
[[package]]
name = "sentry-tower"
version = "0.48.1"
version = "0.48.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d669616d5d5279b5712febfc80c343acc3695e499de0d101ed70fceacadf37f2"
checksum = "2abea154597936d5df2d39fbe8aac16d584de6b3572c70c39558764d9d2efe15"
dependencies = [
"sentry-core",
"tower-layer",
@@ -3283,9 +3292,9 @@ dependencies = [
[[package]]
name = "sentry-tracing"
version = "0.48.1"
version = "0.48.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1c035f3a0a8671ae1a231c5b457abb68b71acba2bf3054dab2a09a9d4ea487e"
checksum = "c51ec9620a4d398dcdf7ee90effbf8d8691cfa24e91978bfa8565cac039d4980"
dependencies = [
"bitflags 2.11.0",
"sentry-backtrace",
@@ -3296,9 +3305,9 @@ dependencies = [
[[package]]
name = "sentry-types"
version = "0.48.1"
version = "0.48.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82d8e81058ec155992191f61c7b29bfa7b2cf12012131e7cdc0678020898a7c9"
checksum = "041359745a44dd2e14fe21b7510fe7ca8b5beffce6636a0b52e5bc7d5f736887"
dependencies = [
"debugid",
"hex",
@@ -3343,9 +3352,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.149"
version = "1.0.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
dependencies = [
"itoa",
"memchr",
@@ -3390,11 +3399,12 @@ dependencies = [
[[package]]
name = "serde_with"
version = "3.19.0"
version = "3.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f05839ce67618e14a09b286535c0d9c94e85ef25469b0e13cb4f844e5593eb19"
checksum = "e72c1c2cb7b223fafb600a619537a871c2818583d619401b785e7c0b746ccde2"
dependencies = [
"base64 0.22.1",
"bs58",
"chrono",
"hex",
"serde_core",
@@ -4072,9 +4082,9 @@ dependencies = [
[[package]]
name = "tower-http"
version = "0.6.10"
version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68d6fdd9f81c2819c9a8b0e0cd91660e7746a8e6ea2ba7c6b2b057985f6bcb51"
checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840"
dependencies = [
"bitflags 2.11.0",
"bytes",
@@ -4336,9 +4346,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
version = "1.23.1"
version = "1.23.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76"
checksum = "d258b83ceec21034727ecee8c382cfa6c3e133699b0742c64571814fb420c9f7"
dependencies = [
"getrandom 0.4.2",
"js-sys",
+10 -10
View File
@@ -22,7 +22,7 @@ publish = false
arc-swap = "= 1.9.1"
argh = "= 0.1.19"
axum-server = { version = "= 0.8.0", features = ["tls-rustls-no-provider"] }
aws-lc-rs = { version = "= 1.16.3", features = ["fips"] }
aws-lc-rs = { version = "= 1.17.0", features = ["fips"] }
axum = { version = "= 0.8.9", features = ["http2", "macros", "ws"] }
clap = { version = "= 4.6.1", features = ["derive", "env"] }
client-ip = { version = "0.2.1", features = ["forwarded-header"] }
@@ -43,15 +43,15 @@ hyper-unix-socket = "= 0.6.1"
hyper-util = "= 0.1.20"
ipnet = { version = "= 2.12.0", features = ["serde"] }
json-subscriber = "= 0.2.8"
metrics = "= 0.24.5"
metrics = "= 0.24.6"
metrics-exporter-prometheus = { version = "= 0.18.3", default-features = false }
nix = { version = "= 0.31.2", features = ["hostname", "signal"] }
nix = { version = "= 0.31.3", features = ["hostname", "signal"] }
notify = "= 8.2.0"
pin-project-lite = "= 0.2.17"
pyo3 = "= 0.28.3"
pyo3-build-config = "= 0.28.3"
regex = "= 1.12.3"
reqwest = { version = "= 0.13.3", features = [
reqwest = { version = "= 0.13.4", features = [
"form",
"json",
"multipart",
@@ -59,7 +59,7 @@ reqwest = { version = "= 0.13.3", features = [
"rustls",
"stream",
] }
reqwest-middleware = { version = "= 0.5.1", features = [
reqwest-middleware = { version = "= 0.5.2", features = [
"form",
"json",
"multipart",
@@ -67,7 +67,7 @@ reqwest-middleware = { version = "= 0.5.1", features = [
"rustls",
] }
rustls = { version = "= 0.23.40", features = ["fips"] }
sentry = { version = "= 0.48.1", default-features = false, features = [
sentry = { version = "= 0.48.2", default-features = false, features = [
"backtrace",
"contexts",
"debug-images",
@@ -78,9 +78,9 @@ sentry = { version = "= 0.48.1", default-features = false, features = [
"tracing",
] }
serde = { version = "= 1.0.228", features = ["derive"] }
serde_json = "= 1.0.149"
serde_json = "= 1.0.150"
serde_repr = "= 0.1.20"
serde_with = { version = "= 3.19.0", default-features = false, features = [
serde_with = { version = "= 3.20.0", default-features = false, features = [
"base64",
] }
sqlx = { version = "= 0.8.6", default-features = false, features = [
@@ -102,7 +102,7 @@ tokio-retry2 = "= 0.9.1"
tokio-rustls = "= 0.26.4"
tokio-util = { version = "= 0.7.18", features = ["full"] }
tower = "= 0.5.3"
tower-http = { version = "= 0.6.10", features = ["timeout"] }
tower-http = { version = "= 0.6.11", features = ["timeout"] }
tracing = "= 0.1.44"
tracing-error = "= 0.2.1"
tracing-subscriber = { version = "= 0.3.23", features = [
@@ -112,7 +112,7 @@ tracing-subscriber = { version = "= 0.3.23", features = [
"tracing-log",
] }
url = "= 2.5.8"
uuid = { version = "= 1.23.1", features = ["serde", "v4"] }
uuid = { version = "= 1.23.2", features = ["serde", "v4"] }
which = "= 8.0.2"
ak-axum = { package = "authentik-axum", version = "2026.8.0-rc1", path = "./packages/ak-axum" }
+4 -4
View File
@@ -73,7 +73,7 @@ rust-test: ## Run the Rust tests
$(CARGO) nextest run --workspace
test: ## Run the server tests and produce a coverage report (locally)
$(UV) run coverage run manage.py test --keepdb $(or $(filter-out $@,$(MAKECMDGOALS)),authentik)
$(UV) run coverage run manage.py test --keepdb $(or $(filter-out $@ all,$(MAKECMDGOALS)),authentik)
$(UV) run coverage combine
$(UV) run coverage html
$(UV) run coverage report
@@ -186,7 +186,7 @@ gen-changelog: ## (Release) generate the changelog based from the commits since
git log --pretty=format:"- %s" $(shell git merge-base ${last_version} ${current_commit})...${current_commit} > merged_to_current
git log --pretty=format:"- %s" $(shell git merge-base ${last_version} ${current_commit})...${last_version} > merged_to_last
grep -Eo 'cherry-pick (#\d+)' merged_to_last | cut -d ' ' -f 2 | sed 's/.*/(&)$$/' > cherry_picked_to_last
grep -vf cherry_picked_to_last merged_to_current | sort > changelog.md
grep -vf cherry_picked_to_last merged_to_current | grep -vE '^- (ci:|website)' | sort > changelog.md
rm merged_to_current
rm merged_to_last
rm cherry_picked_to_last
@@ -201,8 +201,8 @@ gen-diff: ## (Release) generate the changelog diff between the current schema a
/local/schema-old.yml \
/local/schema.yml
rm schema-old.yml
$(SED_INPLACE) 's/{/&#123;/g' diff.md
$(SED_INPLACE) 's/}/&#125;/g' diff.md
$(SED_INPLACE) 's/{/\&#123;/g' diff.md
$(SED_INPLACE) 's/}/\&#125;/g' diff.md
npx prettier --write diff.md
gen-client-go: ## Build and install the authentik API for Golang
+2
View File
@@ -36,6 +36,8 @@ Our [enterprise offering](https://goauthentik.io/pricing) is available for organ
See the [Developer Documentation](https://docs.goauthentik.io/docs/developer-docs/) for information about setting up local build environments, testing your contributions, and our contribution process.
When you contribute documentation, either to accompany a code change or as a standalone contribution, please be sure to follow our documentation [Style Guide](website/docs/developer-docs/docs/style-guide.mdx).
## Security
Please see [SECURITY.md](SECURITY.md).
+2 -2
View File
@@ -20,8 +20,8 @@ Even if the issue is not a CVE, we still greatly appreciate your help in hardeni
| Version | Supported |
| --------- | --------- |
| 2025.12.x | ✅ |
| 2026.2.x | ✅ |
| 2025.2.x | ✅ |
| 2026.5.x | ✅ |
## Reporting a Vulnerability
@@ -31,7 +31,7 @@ entries:
slug: "%(uid)s-source"
attrs:
name: "%(uid)s-source"
provider_type: azuread
provider_type: entraid
consumer_key: "%(uid)s"
consumer_secret: "%(uid)s"
icon: https://goauthentik.io/img/icon.png
+39 -1
View File
@@ -323,6 +323,42 @@ class Importer:
serializer.instance = model_instance
return serializer
def _save_with_retry(
self, serializer: BaseSerializer, entry: BlueprintEntry, raise_errors: bool
) -> Model | None:
"""Save a serializer, retrying once on IntegrityError by re-fetching the existing instance.
Returns the saved instance, or None when recovery failed and raise_errors is False.
Raises EntryInvalidError / IntegrityError when raise_errors is True and recovery
is not possible.
"""
try:
with atomic():
return serializer.save()
except IntegrityError:
self.logger.debug(
"Integrity error during save, retrying after re-fetching instance",
entry=entry,
)
# Race condition: another process committed the same object between our
# SELECT and INSERT. Re-validate so we pick up the now-existing instance.
try:
retry_serializer = self._validate_single(entry)
except EntryInvalidError as exc:
self.logger.warning(f"Entry invalid on retry: {exc}", entry=entry, error=exc)
if raise_errors:
raise exc
return None
if not retry_serializer:
return None
try:
return retry_serializer.save()
except IntegrityError:
self.logger.warning("Integrity error persists on retry", entry=entry)
if raise_errors:
raise
return None
def _apply_permissions(self, instance: Model, entry: BlueprintEntry):
"""Apply object-level permissions for an entry"""
for perm in entry.get_permissions(self._import):
@@ -393,7 +429,9 @@ class Importer:
pk=instance.pk,
)
else:
instance = serializer.save()
instance = self._save_with_retry(serializer, entry, raise_errors)
if instance is None:
return False
self.logger.debug("Updated model", model=instance)
if "pk" in entry.identifiers:
self.__pk_map[entry.identifiers["pk"]] = instance.pk
+2
View File
@@ -133,6 +133,7 @@ class UserSourceConnectionSerializer(SourceSerializer):
"last_updated",
]
extra_kwargs = {
"user": {"read_only": True},
"created": {"read_only": True},
"last_updated": {"read_only": True},
}
@@ -173,6 +174,7 @@ class GroupSourceConnectionSerializer(SourceSerializer):
"last_updated",
]
extra_kwargs = {
"group": {"read_only": True},
"created": {"read_only": True},
"last_updated": {"read_only": True},
}
+1 -1
View File
@@ -12,7 +12,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
{# Darkreader breaks the site regardless of theme as its not compatible with webcomponents, and we default to a dark theme based on preferred colour-scheme #}
{# Darkreader breaks the site regardless of theme as its not compatible with webcomponents, and we default to a dark theme based on preferred color-scheme #}
<meta name="darkreader-lock">
<title>{% block title %}{% trans title|default:brand.branding_title %}{% endblock %}</title>
<link rel="icon" href="{{ brand.branding_favicon_url }}">
@@ -7,7 +7,7 @@ from drf_spectacular.utils import OpenApiParameter, OpenApiResponse, extend_sche
from rest_framework.decorators import action
from rest_framework.exceptions import PermissionDenied, ValidationError
from rest_framework.fields import ChoiceField
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.permissions import IsAuthenticated
from rest_framework.relations import PrimaryKeyRelatedField
from rest_framework.request import Request
from rest_framework.response import Response
@@ -118,8 +118,7 @@ class AgentConnectorViewSet(
methods=["POST"],
detail=False,
authentication_classes=[AgentEnrollmentAuth],
# Permissions are handled via AgentEnrollmentAuth
permission_classes=[AllowAny],
permission_classes=[IsAuthenticated],
)
def enroll(self, request: Request):
token: EnrollmentToken = request.auth
@@ -154,8 +153,7 @@ class AgentConnectorViewSet(
methods=["GET"],
detail=False,
authentication_classes=[AgentAuth],
# Permissions are handled via AgentAuth
permission_classes=[AllowAny],
permission_classes=[IsAuthenticated],
)
def agent_config(self, request: Request):
token: DeviceToken = request.auth
@@ -174,8 +172,7 @@ class AgentConnectorViewSet(
methods=["POST"],
detail=False,
authentication_classes=[AgentAuth],
# Permissions are handled via AgentAuth
permission_classes=[AllowAny],
permission_classes=[IsAuthenticated],
)
def check_in(self, request: Request):
token: DeviceToken = request.auth
+17 -2
View File
@@ -1,6 +1,6 @@
from typing import Any
from django.db.models import Model
from django.db.models import Model, Q
from django.http import HttpRequest
from django.utils.timezone import now
from drf_spectacular.extensions import OpenApiAuthenticationExtension
@@ -101,7 +101,22 @@ class DeviceAuthFedAuthentication(BaseAuthentication):
if not raw_token:
LOGGER.warning("Missing token")
return None
device = Device.objects.filter(name=request.query_params.get("device")).first()
device = (
Device.objects.filter(
Q(
name=request.query_params.get("device"),
)
| Q(
**{
"deviceconnection__devicefactsnapshot__"
"data__vendor__goauthentik.io/platform__"
"ssh_host_keys__contains": request.query_params.get("device"),
}
)
)
.distinct()
.first()
)
if not device:
LOGGER.warning("Couldn't find device")
return None
@@ -124,6 +124,14 @@ class TestAgentAPI(APITestCase):
)
self.assertEqual(response.status_code, 403)
@reconcile_app("authentik_crypto")
def test_config_none(self):
response = self.client.get(
reverse("authentik_api:agentconnector-agent-config"),
HTTP_AUTHORIZATION="Bearer foo",
)
self.assertEqual(response.status_code, 403)
def test_check_in(self):
response = self.client.post(
reverse("authentik_api:agentconnector-check-in"),
@@ -19,6 +19,7 @@ from authentik.providers.oauth2.models import AccessToken, OAuth2Provider
class TestConnectorAuthFed(APITestCase):
def setUp(self):
self.ssh_host_key = generate_id()
self.connector = AgentConnector.objects.create(name=generate_id())
self.token = EnrollmentToken.objects.create(name=generate_id(), connector=self.connector)
self.device = Device.objects.create(
@@ -29,6 +30,19 @@ class TestConnectorAuthFed(APITestCase):
device=self.device,
connector=self.connector,
)
self.connection.create_snapshot(
data={
"vendor": {
"goauthentik.io/platform": {
"ssh_host_keys": [
"foo",
self.ssh_host_key,
"baz",
]
}
}
}
)
self.user = create_test_user()
self.provider = OAuth2Provider.objects.create(
name=generate_id(), signing_key=create_test_cert()
@@ -40,13 +54,34 @@ class TestConnectorAuthFed(APITestCase):
self.connector.jwt_federation_providers.add(self.provider)
@reconcile_app("authentik_crypto")
def test_auth_fed(self):
def test_auth_fed_no_access(self):
response = self.client.post(
reverse("authentik_api:agentconnector-auth-fed") + f"?device={self.device.name}",
HTTP_AUTHORIZATION=f"Bearer {self.raw_token}",
)
self.assertEqual(response.status_code, 400)
@reconcile_app("authentik_crypto")
def test_auth_fed_policy_group_by_host_key(self):
device_group = DeviceAccessGroup.objects.create(name=generate_id())
self.device.access_group = device_group
self.device.save()
group = Group.objects.create(name=generate_id())
group.users.add(self.user)
PolicyBinding.objects.create(target=device_group, group=group, order=0)
response = self.client.post(
reverse("authentik_api:agentconnector-auth-fed") + f"?device={self.ssh_host_key}",
HTTP_AUTHORIZATION=f"Bearer {self.raw_token}",
)
self.assertEqual(response.status_code, 200)
res = loads(response.content)
token = decode(res["token"], options={"verify_signature": False})
self.assertEqual(token["iss"], "goauthentik.io/platform")
self.assertEqual(token["aud"], str(self.device.pk))
@reconcile_app("authentik_crypto")
def test_auth_fed_policy_group(self):
device_group = DeviceAccessGroup.objects.create(name=generate_id())
@@ -68,13 +68,16 @@ class SCIMOAuthAuth:
return conn
token = self.retrieve_token(conn)
access_token = token["access_token"]
refresh_token = token.get("refresh_token")
if not refresh_token and conn:
refresh_token = conn.refresh_token
expires_in = int(token.get("expires_in", 0))
token, _ = UserOAuthSourceConnection.objects.update_or_create(
source=self.provider.auth_oauth,
user=self.user,
defaults={
"access_token": access_token,
"refresh_token": token.get("refresh_token"),
"refresh_token": refresh_token,
"expires": now() + timedelta(seconds=expires_in),
# When using `update_or_create`, `last_updated` is not updated
"last_updated": now(),
@@ -104,6 +104,7 @@ class TestSCIMOAuthToken(APITestCase):
source=self.source,
user=self.provider.auth_oauth_user,
).first()
self.assertEqual(conn.refresh_token, refresh_token)
self.assertIsNotNone(conn)
self.assertTrue(conn.is_valid)
auth = (
@@ -65,6 +65,7 @@ class SCIMRedirectCallback(SCIMOAuthViewMixin, OAuthCallback):
"access_token": self.token.get("access_token"),
"refresh_token": self.token.get("refresh_token"),
"expires": now() + timedelta(seconds=expires_in),
"last_updated": now(),
},
)
return redirect("authentik_core:if-admin")
+4 -3
View File
@@ -14,6 +14,7 @@ from cryptography.x509 import (
load_pem_x509_certificate,
)
from cryptography.x509.verification import PolicyBuilder, Store, VerificationError
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
from rest_framework.exceptions import PermissionDenied
@@ -138,9 +139,9 @@ class MTLSStageView(ChallengeStageView):
authorities_cert = [x.certificate for x in authorities]
for _cert in certs:
try:
PolicyBuilder().store(Store(authorities_cert)).build_client_verifier().verify(
_cert, []
)
PolicyBuilder().store(Store(authorities_cert)).time(
now()
).build_client_verifier().verify(_cert, [])
return _cert
except (
InvalidSignature,
@@ -3,6 +3,7 @@ from unittest.mock import MagicMock, patch
from urllib.parse import quote_plus
from django.urls import reverse
from freezegun import freeze_time
from authentik.core.models import User
from authentik.core.tests.utils import (
@@ -28,6 +29,7 @@ from authentik.outposts.models import Outpost, OutpostType
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
@freeze_time("2026-05-10 12:38:46")
class MTLSStageTests(FlowTestCase):
def setUp(self):
+3 -2
View File
@@ -16,7 +16,7 @@ from authentik.core.sources.flow_manager import (
)
from authentik.core.types import UILoginButton
from authentik.enterprise.stages.source.models import SourceStage
from authentik.flows.challenge import Challenge, ChallengeResponse
from authentik.flows.challenge import Challenge, ChallengeResponse, HttpChallengeResponse
from authentik.flows.models import FlowToken, in_memory_stage
from authentik.flows.planner import PLAN_CONTEXT_IS_REDIRECTED, PLAN_CONTEXT_IS_RESTORED
from authentik.flows.stage import ChallengeStageView, StageView
@@ -84,7 +84,8 @@ class SourceStageView(ChallengeStageView):
return token
def challenge_valid(self, response: ChallengeResponse) -> HttpResponse:
return self.executor.stage_ok()
# Completion happens via dispatch(), not here.
return HttpChallengeResponse(self._get_challenge())
class SourceStageFinal(StageView):
-7
View File
@@ -7,13 +7,6 @@ from authentik.lib.config import CONFIG, ENV_PREFIX
from authentik.lib.utils.time import fqdn_rand
from authentik.tasks.schedules.common import ScheduleSpec
# TODO: Deprecated metric - remove in 2024.2 or later
GAUGE_TASKS = Gauge(
"authentik_system_tasks",
"System tasks and their status",
["tenant", "task_name", "task_uid", "status"],
)
SYSTEM_TASK_TIME = Histogram(
"authentik_system_tasks_time_seconds",
"Runtime of system tasks",
-9
View File
@@ -49,15 +49,6 @@ class LogEventSerializer(PassiveSerializer):
event = CharField()
attributes = DictField()
# TODO(2024.6?): This is a migration helper to return a correct API response for logs that
# have been saved in an older format (mostly just list[str] with just the messages)
def to_representation(self, instance):
if isinstance(instance, str):
instance = LogEvent(instance, "", "")
elif isinstance(instance, list):
instance = [LogEvent(x, "", "") for x in instance]
return super().to_representation(instance)
@contextmanager
def capture_logs(log_default_output=True) -> Generator[list[LogEvent]]:
@@ -17,7 +17,7 @@ class Migration(migrations.Migration):
name="webhook_ca",
field=models.ForeignKey(
default=None,
help_text="When set, the selected ceritifcate is used to validate the certificate of the webhook server.",
help_text="When set, the selected certificate is used to validate the certificate of the webhook server.",
null=True,
on_delete=django.db.models.deletion.SET_DEFAULT,
to="authentik_crypto.certificatekeypair",
+2 -1
View File
@@ -258,6 +258,7 @@ class Event(SerializerModel, ExpiringModel):
action=EventAction.CONFIGURATION_WARNING,
context__deprecation=identifier,
)
cause = str(cause)
if cause:
query &= Q(context__cause=cause)
if Event.objects.filter(query).exists():
@@ -360,7 +361,7 @@ class NotificationTransport(TasksModel, SerializerModel):
default=None,
on_delete=models.SET_DEFAULT,
help_text=_(
"When set, the selected ceritifcate is used to "
"When set, the selected certificate is used to "
"validate the certificate of the webhook server."
),
)
+11
View File
@@ -225,3 +225,14 @@ class TestEvents(TestCase):
new_count = Event.objects.filter(action=EventAction.PASSWORD_SET, user__pk=user.pk).count()
self.assertEqual(new_count, old_count + 1)
def test_log_deprecation(self):
"""Test Event.log_deprecation"""
Event.log_deprecation(self.__module__, "Test deprecation")
Event.log_deprecation(self.__module__, "Test deprecation")
Event.log_deprecation(self.__module__, "Test deprecation")
Event.log_deprecation(self.__module__, "Test deprecation", cause=create_test_user())
logs = Event.objects.filter(
action=EventAction.CONFIGURATION_WARNING, context__deprecation=self.__module__
)
self.assertEqual(logs.count(), 2)
+1
View File
@@ -128,6 +128,7 @@ class SessionEndChallenge(WithUserInfoChallenge):
application_launch_url = CharField(required=False)
invalidation_flow_url = CharField(required=False)
overview_url = CharField(required=False)
brand_name = CharField(required=True)
+5 -1
View File
@@ -16,7 +16,7 @@ from sentry_sdk import start_span
from structlog.stdlib import BoundLogger, get_logger
from authentik.common.oauth.constants import PLAN_CONTEXT_POST_LOGOUT_REDIRECT_URI
from authentik.core.models import Application, User
from authentik.core.models import Application, User, UserTypes
from authentik.flows.challenge import (
AccessDeniedChallenge,
Challenge,
@@ -331,6 +331,10 @@ class SessionEndStage(ChallengeStageView):
"component": "ak-stage-session-end",
"brand_name": self.request.brand.branding_title,
}
if self.get_pending_user().type == UserTypes.INTERNAL:
data["overview_url"] = self.request.build_absolute_uri(
reverse("authentik_core:root-redirect")
)
if application:
data["application_name"] = application.name
data["application_launch_url"] = application.get_launch_url(self.get_pending_user())
+1 -1
View File
@@ -196,7 +196,7 @@ class FlowExecutorView(APIView):
return self.handle_invalid_flow(exc)
except EmptyFlowException as exc:
self._logger.warning("f(exec): Flow is empty", exc=exc)
# To match behaviour with loading an empty flow plan from cache,
# To match behavior with loading an empty flow plan from cache,
# we don't show an error message here, but rather call _flow_done()
return self._flow_done()
# We don't save the Plan after getting the next stage
+1 -1
View File
@@ -59,7 +59,7 @@ def avatar_mode_gravatar(user: User, mode: str) -> str | None:
def generate_colors(text: str) -> tuple[str, str]:
"""Generate colours based on `text`"""
"""Generate colors based on `text`"""
color = (
int(md5(text.lower().encode("utf-8"), usedforsecurity=False).hexdigest(), 16) % 0xFFFFFF
) # nosec
+1 -1
View File
@@ -276,7 +276,7 @@ class ConfigLoader:
try:
return int(value)
except (ValueError, TypeError) as exc:
if value is None or (isinstance(value, str) and value.lower() == "null"):
if value is None or (isinstance(value, str) and value.lower() in ("", "null", "none")):
return None
self.log("warning", "Failed to parse config as int", path=path, exc=str(exc))
return default
+1
View File
@@ -22,6 +22,7 @@ postgresql:
port: 5432
password: "env://POSTGRES_PASSWORD"
sslmode: disable
conn_max_age: 0
conn_health_checks: false
use_pool: False
test:
+1 -1
View File
@@ -315,7 +315,7 @@ class TestConfig(TestCase):
{
"default": {
"DISABLE_SERVER_SIDE_CURSORS": True,
"CONN_MAX_AGE": None,
"CONN_MAX_AGE": 0,
"CONN_HEALTH_CHECKS": False,
"ENGINE": "psqlextra.backend",
"HOST": "foo",
+2 -1
View File
@@ -23,6 +23,7 @@ from authentik.lib.utils.time import timedelta_from_string, timedelta_string_val
from authentik.outposts.api.service_connections import ServiceConnectionSerializer
from authentik.outposts.apps import MANAGED_OUTPOST, MANAGED_OUTPOST_NAME
from authentik.outposts.models import (
OUR_VERSION,
Outpost,
OutpostConfig,
OutpostType,
@@ -188,7 +189,7 @@ class OutpostViewSet(UsedByMixin, ModelViewSet):
"uid": state.uid,
"last_seen": state.last_seen,
"version": state.version,
"version_should": state.version_should,
"version_should": OUR_VERSION,
"version_outdated": state.version_outdated,
"build_hash": state.build_hash,
"golang_version": state.golang_version,
+1 -2
View File
@@ -13,7 +13,7 @@ from django.db import IntegrityError, models, transaction
from django.db.models.base import Model
from django.utils.translation import gettext_lazy as _
from model_utils.managers import InheritanceManager
from packaging.version import Version, parse
from packaging.version import parse
from rest_framework.serializers import Serializer
from structlog.stdlib import get_logger
@@ -464,7 +464,6 @@ class OutpostState:
uid: str
last_seen: datetime | None = field(default=None)
version: str | None = field(default=None)
version_should: Version = field(default=OUR_VERSION)
build_hash: str = field(default="")
golang_version: str = field(default="")
openssl_enabled: bool = field(default=False)
+1 -1
View File
@@ -91,7 +91,7 @@ class LDAPOutpostConfigSerializer(ModelSerializer):
unbind_flow_slug = SerializerMethodField()
def get_application_slug(self, instance: LDAPProvider) -> str:
"""Prioritise backchannel slug over direct application slug"""
"""Prioritize backchannel slug over direct application slug"""
if instance.backchannel_application:
return instance.backchannel_application.slug
return instance.application.slug
+6
View File
@@ -88,6 +88,12 @@ class GrantType(models.TextChoices):
DEVICE_CODE = GRANT_TYPE_DEVICE_CODE
# Fallback for decoding previous sessions from 2026.2 to 2026.5
# https://github.com/goauthentik/authentik/issues/22588
# TODO: Remove after 2026.8
GrantTypes = GrantType
class ResponseMode(models.TextChoices):
"""https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#OAuth.Post"""
@@ -84,8 +84,7 @@ class EndSessionView(PolicyAccessView):
"id_token_hint_decode_failed"
) from None
# Validate post_logout_redirect_uri against registered URIs
if request_redirect_uri:
if request_redirect_uri and self.provider.post_logout_redirect_uris:
# OIDC Certification: id_token_hint required with post_logout_redirect_uri
if not id_token_hint:
raise TokenError("invalid_request").with_cause("id_token_hint_missing")
@@ -53,6 +53,7 @@ class ServiceProviderMetadata:
)
provider.sp_binding = self.acs_binding
provider.acs_url = self.acs_location
provider.audience = self.entity_id
provider.default_name_id_policy = self.name_id_policy
# Single Logout Service
if self.sls_location:
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<saml2p:AuthnRequest xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" ID="_1fac8a70c9d2766aed298165ed66dcae" Version="2.0" IssueInstant="2026-05-19T01:01:53.461Z">
<saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">https://sp.example.invalid/saml</saml2:Issuer>
</saml2p:AuthnRequest>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<saml2p:LogoutRequest xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" ID="_logout" Version="2.0" IssueInstant="2026-05-19T01:01:53.461Z">
<saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">https://sp.example.invalid/saml</saml2:Issuer>
</saml2p:LogoutRequest>
@@ -103,7 +103,7 @@ class TestServiceProviderMetadataParser(TestCase):
provider.verification_kp.certificate_data, load_fixture("fixtures/cert.pem")
)
self.assertIsNotNone(provider.signing_kp)
self.assertEqual(provider.audience, "")
self.assertEqual(provider.audience, "http://localhost:8080/apps/user_saml/saml/metadata")
def test_with_signing_cert_invalid_signature(self):
"""Test Metadata with signing cert (invalid signature)"""
@@ -0,0 +1,37 @@
"""Test unified SAML endpoint helpers."""
from django.test import SimpleTestCase
from authentik.lib.tests.utils import load_fixture
from authentik.providers.saml.utils.encoding import deflate_and_base64_encode
from authentik.providers.saml.views.unified import (
SAML_MESSAGE_TYPE_AUTHN_REQUEST,
SAML_MESSAGE_TYPE_LOGOUT_REQUEST,
detect_saml_message_type,
)
class TestDetectSAMLMessageType(SimpleTestCase):
"""Test SAML request type detection."""
def test_redirect_authn_request_with_xml_declaration(self):
"""Detect redirect-binding AuthnRequest with an XML declaration."""
request = deflate_and_base64_encode(
load_fixture("fixtures/authn_request_xml_declaration.xml")
)
self.assertEqual(
detect_saml_message_type(request, is_post_binding=False),
SAML_MESSAGE_TYPE_AUTHN_REQUEST,
)
def test_redirect_logout_request_with_xml_declaration(self):
"""Detect redirect-binding LogoutRequest with an XML declaration."""
request = deflate_and_base64_encode(
load_fixture("fixtures/logout_request_xml_declaration.xml")
)
self.assertEqual(
detect_saml_message_type(request, is_post_binding=False),
SAML_MESSAGE_TYPE_LOGOUT_REQUEST,
)
+1 -1
View File
@@ -127,7 +127,7 @@ class SAMLFlowFinalView(ChallengeStageView):
"Redirect binding for Service Provider binding is deprecated "
"and will be removed in a future version. Use Post binding instead."
),
cause=provider,
cause=provider.name,
)
url_args = {
REQUEST_KEY_SAML_RESPONSE: deflate_and_base64_encode(response),
@@ -42,6 +42,9 @@ def detect_saml_message_type(saml_request: str, is_post_binding: bool) -> str |
else:
decoded_xml = decode_base64_and_inflate(saml_request)
if isinstance(decoded_xml, str):
decoded_xml = decoded_xml.encode()
root = fromstring(decoded_xml)
if len(root.xpath("//samlp:AuthnRequest", namespaces=NS_MAP)):
return SAML_MESSAGE_TYPE_AUTHN_REQUEST
+1 -1
View File
@@ -22,7 +22,7 @@ class ObjectPermissions(DjangoObjectPermissions):
lookup = getattr(view, "lookup_url_kwarg", None) or getattr(view, "lookup_field", None)
if lookup and lookup in view.kwargs:
return True
# Legacy behaviour:
# Legacy behavior:
# Allow creation of objects even without explicit permission
queryset = self._queryset(view)
required_perms = self.get_required_permissions(request.method, queryset.model)
+12
View File
@@ -19,6 +19,18 @@ def pytest_sessionstart(*_, **__):
"""Clear the console ahead of the pytest output starting"""
if not IS_CI:
print("\x1b[2J\x1b[H")
# Pre-warm cryptography's PyO3 PyDateTime type cache with the real
# datetime class. If the first extraction happens under @freeze_time
# (e.g. in MTLSStageTests), PyO3 caches freezegun's FakeDatetime,
# which breaks every later test that passes a real datetime into
# cryptography ("TypeError: 'datetime' object is not an instance
# of 'FakeDatetime'"). The discard is intentional — only side
# effect needed is the type-cache initialization.
from datetime import UTC, datetime
from cryptography.x509.verification import PolicyBuilder
PolicyBuilder().time(datetime.now(tz=UTC))
yield
+3
View File
@@ -5,6 +5,7 @@ from argparse import ArgumentParser
from unittest import TestCase
from unittest.mock import patch
import freezegun
import pytest
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
@@ -91,6 +92,8 @@ class PytestTestRunner(DiscoverRunner): # pragma: no cover
self.task_broker = use_test_broker()
freezegun.configure(extend_ignore_list=["cryptography"])
# Send startup signals
pre_startup.send(sender=self, mode="test")
startup.send(sender=self, mode="test")
-1
View File
@@ -10,7 +10,6 @@ LOGGER = get_logger()
AUTHENTIK_SOURCES_OAUTH_TYPES = [
"authentik.sources.oauth.types.apple",
"authentik.sources.oauth.types.azure_ad",
"authentik.sources.oauth.types.discord",
"authentik.sources.oauth.types.entra_id",
"authentik.sources.oauth.types.facebook",
@@ -0,0 +1,23 @@
# Generated by Django 5.2.14 on 2026-05-09 19:01
from django.db import migrations
def migrate_azuread_to_entraid(apps, schema_editor):
OAuthSource = apps.get_model("authentik_sources_oauth", "OAuthSource")
db_alias = schema_editor.connection.alias
OAuthSource.objects.using(db_alias).filter(provider_type="azuread").update(
provider_type="entraid"
)
class Migration(migrations.Migration):
dependencies = [
("authentik_sources_oauth", "0013_useroauthsourceconnection_refresh_token"),
]
operations = [
migrations.RunPython(migrate_azuread_to_entraid, migrations.RunPython.noop),
]
-11
View File
@@ -251,17 +251,6 @@ class GoogleOAuthSource(CreatableType, OAuthSource):
verbose_name_plural = _("Google OAuth Sources")
class AzureADOAuthSource(CreatableType, OAuthSource):
"""(Deprecated) Social Login using Azure AD."""
class Meta:
abstract = True
verbose_name = _("Azure AD OAuth Source")
verbose_name_plural = _("Azure AD OAuth Sources")
# TODO: When removing this, add a migration for OAuthSource that sets
# provider_type to `entraid` if it is currently `azuread`
class EntraIDOAuthSource(CreatableType, OAuthSource):
"""Social Login using Entra ID."""
-17
View File
@@ -1,17 +0,0 @@
"""AzureAD OAuth2 Views"""
from authentik.sources.oauth.types.entra_id import EntraIDType
from authentik.sources.oauth.types.registry import registry
# TODO: When removing this, add a migration for OAuthSource that sets
# provider_type to `entraid` if it is currently `azuread`
@registry.register()
class AzureADType(EntraIDType):
"""Azure AD Type definition"""
verbose_name = "Azure AD"
name = "azuread"
urls_customizable = True
+1 -1
View File
@@ -76,7 +76,7 @@ class GitHubType(SourceType):
chosen_email = info.get("email")
if not chosen_email:
# The GitHub Userprofile API only returns an email address if the profile
# has a public email address set (despite us asking for user:email, this behaviour
# has a public email address set (despite us asking for user:email, this behavior
# doesn't change.). So we fetch all the user's email addresses
emails = client.get_github_emails(token)
for email in emails:
+1 -1
View File
@@ -67,7 +67,7 @@ class OpenIDConnectOAuth2Callback(OAuthCallback):
client_class = OpenIDConnectClient
def get_user_id(self, info: dict[str, str]) -> str:
return info.get("sub", None)
return str(info.get("sub") or info.get("id"))
@registry.register()
+22 -12
View File
@@ -143,9 +143,21 @@ class ResponseProcessor:
if datetime.fromisoformat(on_or_after).replace(tzinfo=UTC) < _now:
raise SAMLException("Assertion is not valid yet or expired.")
def _verify_signature(self, signature_node: _Element):
"""Verify a single signature node"""
xmlsec.tree.add_ids(self._root, ["ID"])
def _verify_signature(self, signature_node: _Element, target: _Element):
"""Verify a single signature node against the given target element."""
target_id = target.attrib.get("ID")
if not target_id:
raise InvalidSignature("Signed element is missing an ID attribute.")
refs = signature_node.xpath("./ds:SignedInfo/ds:Reference", namespaces=NS_MAP)
if len(refs) != 1:
raise InvalidSignature("Signature must contain exactly one Reference.")
ref_uri = refs[0].get("URI", "")
if ref_uri not in ("", f"#{target_id}"):
raise InvalidSignature(
"Signature Reference URI does not match the signed element's ID."
)
xmlsec.tree.add_ids(target, ["ID"])
ctx = xmlsec.SignatureContext()
key = xmlsec.Key.from_memory(
@@ -168,24 +180,22 @@ class ResponseProcessor:
signature_nodes = self._root.xpath("/samlp:Response/ds:Signature", namespaces=NS_MAP)
if len(signature_nodes) != 1:
raise InvalidSignature("No Signature exists in the Response element.")
raise InvalidSignature("Expected exactly one Signature in the Response element.")
self._verify_signature(signature_nodes[0])
self._verify_signature(signature_nodes[0], self._root)
def _verify_assertion_signature(self):
"""Verify SAML Assertion's Signature (after decryption)"""
signature_nodes = self._root.xpath(
"/samlp:Response/saml:Assertion/ds:Signature", namespaces=NS_MAP
)
if len(signature_nodes) != 1:
raise InvalidSignature("No Signature exists in the Assertion element.")
raise InvalidSignature("Expected exactly one signed Assertion in the Response.")
signature_node = signature_nodes[0]
assertion = signature_node.getparent()
self._verify_signature(signature_nodes[0])
parent = signature_nodes[0].getparent()
if parent is None or parent.tag != f"{{{NS_SAML_ASSERTION}}}Assertion":
raise InvalidSignature("No Signature exists in the Assertion element.")
self._assertion = parent
self._verify_signature(signature_node, assertion)
self._assertion = assertion
def _verify_request_id(self):
if self._source.allow_idp_initiated:
@@ -0,0 +1,41 @@
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_resp_assert_uri_empty" Version="2.0" IssueInstant="2014-07-17T01:01:48Z" Destination="http://sp.example.com/demo1/index.php?acs" InResponseTo="ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685"><saml:Issuer>http://idp.example.com/metadata.php</saml:Issuer><samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></samlp:Status><saml:Assertion ID="_assert" Version="2.0" IssueInstant="2014-07-17T01:01:48Z"><saml:Issuer>http://idp.example.com/metadata.php</saml:Issuer><Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<DigestValue>6AZlgEwsPEWwPPioj5HiNRStaPAm70zIRSBGnYaRc+E=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>fei+JUaTHlQuy6/icjyZKUh5kx9qvL5xEqUfwOa1kiLtlfCbMCcEFHMEjePb1uPW
fAePa/v9mIl8pV7TrALI1xicdwRPvvM6xgiWe5hQDU+MKd88bHuU/O/0DUktku+e
ANR4kYQUAgkmmMCSPSruD3zIgVTAI8AMEpTtDNuHr8C12phsDkqaRQ1OP/ptC//2
s6eeJ0DizMWkv/UHrqN8PSoTSgIl30Ffq30t/9TY644lBjgcQZD4h0uvpaRo/M0/
yxcgfP6U3ec9ucePFJKprNXvkmNSh/DGbA0BPx1zoB7xf1nhyIYZI76GqC1NP0rh
YQ1BinW++XE19PvY66MnIA==</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>MIIC0jCCAbqgAwIBAgIUXHr2/LJAqtsJ4CcXkFjMwJo8HmQwDQYJKoZIhvcNAQEL
BQAwIzEhMB8GA1UEAwwYVVJJLWVtcHR5IGFzc2VydGlvbiB0ZXN0MB4XDTE0MDEw
MTAwMDAwMFoXDTMwMDEwMTAwMDAwMFowIzEhMB8GA1UEAwwYVVJJLWVtcHR5IGFz
c2VydGlvbiB0ZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArzIB
E4WYhfFNJk2VcvxTX9+cdyJ+v+GCAU4BxSJ1/97BPf3UN4yob23qohnnnMlGAO2x
QBK2VBQKjNZo3qwy2t5xhsa0YLjjWGxAEL1s5K8cQlkYwkciTy+RFpMXmM80kk8p
ZdZFgrjf5ltincH4QuhJcN3/fsEibHQibymWeb/0I3Mba6Uh+gssMN82NYETl677
I+5HV4wgJWMh1vaZFbid+YFBeWWJoIAIdbTQEAwIJriTA43lgbcK0Lo9A8/RCH1O
RVsQSkpA3kfs9yJ9AvKglBIThapR1iRgtVsC9LdiauHmiNU+8POSHXWByXaWOK0o
Izfg/lI+xKSWJerhUwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQA+/3kRE6jYFfgy
vHZdOX2cFOA5Y0N/RZmdt34tsfCiqP2vHtfQUje8gQAhjmV6dFk4wptPF1FsP601
bMp+9LsRsb4y6pxy5m7xKVK9P/EI33N2zZZL9tlJ7CPIA81DPi53lYOvX54UIi2I
GpF6QyYMX2HTs/KVxo4gYnOnkyqPw6QrKaWpJLQndQd1rTn4/ybWW/9XU46RYf7/
7Z8H8t4n4lhPYm5WGer4eG+k+F3R04yhwSm3Wi91gkQwQdGPgFSPe1z8TusDw/Q/
1ax1a/mNoN9NCcZgg3L0xZgbtDnzBr/Gd/MWBQdDgRM7DpcWaVVcXq5GRLLeAvwM
uF73araE
</X509Certificate>
</X509Data>
</KeyInfo>
</Signature><saml:Subject><saml:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient">test_user</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData NotOnOrAfter="2024-01-18T06:21:48Z" Recipient="http://sp.example.com/demo1/index.php?acs" InResponseTo="ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="2014-07-17T01:01:18Z" NotOnOrAfter="2024-01-18T06:21:48Z"><saml:AudienceRestriction><saml:Audience>http://sp.example.com/demo1/metadata.php</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AuthnStatement AuthnInstant="2014-07-17T01:01:48Z" SessionIndex="_session"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement></saml:Assertion></samlp:Response>
@@ -0,0 +1,72 @@
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_8e8dc5f69a98cc4c1ff3427e5ce34606fd672f91e6" Version="2.0" IssueInstant="2014-07-17T01:01:48Z" Destination="http://sp.example.com/demo1/index.php?acs" InResponseTo="ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685">
<saml:Issuer>http://idp.example.com/metadata.php</saml:Issuer>
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</samlp:Status>
<saml:Assertion xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" ID="_evil" Version="2.0" IssueInstant="2014-07-17T01:01:48Z">
<saml:Issuer>http://idp.example.com/metadata.php</saml:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI="#pfxa06693ef-cec7-f4a6-cb7f-ad074445a1a3"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>zNDuGxwP4gVkv/Dzt7kiKo/4gzk=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>GLP/vE8uxerB0uDpPslUgLPBL6ePQB619MoQ0I2Y5lAtFE6CB1zh8BnzChRx/bFjNy4byfOe8mFfM0r7WUi1PJOFWyUPoatdLl7wHHBIRTnPpYmu3Tb2Gz0sOP0F8wW7JkBft5gJfVw49nk5si9/3Q3o52jnJZ7dPtqfIOh8uNeopikK0HLF6sU05qCCtjcXfniEnLQFNBFMo9uY5GQqmR5n3nqPz1wYyyfFOAbVmGgBIoO2PfGX2GVLQhltc9qf2JMhks4jgZsZ8iLUIiH1lcLGWZEEs94k8k0P6gSv1uZ7Vbhksd/N9Jq9pCVuEJ/jRPcAdVjzbxqKQAj6ELwr8O6fepTzA+CAdwEolBnx/C6TmSbVZ+IWk6QUGe4x4+IAukC+0hkKENlO0ELOScksvyhpgHbxNA4rp+DhGupCaO/I2RrsQkmvavbqm+wSEspK7scK112SDunjDvqPHsPYgukD33T/97PxTLorg2kKP9HHJwPJKoXXeyOGcA6vwK+RqrAlZ2dLGAgcXo+sJcdCLuvxDNz9VXofBjBZIKVKdmYhm0QJaPYHtuQsAyFavQhdOBOmGHb7QX3YE3Xy4dX4LymtT+Jlb1I4FJSht/9HUIHW1FdhfDak4f7gUgjuMamMddLD0jVgeESupSREzFv/gj2IrctkbgjAO0iuuiBgKMg=</ds:SignatureValue>
<ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIIFUzCCAzugAwIBAgIRAL6tbNcE9Ej9gNlbGKswfFMwDQYJKoZIhvcNAQELBQAwHTEbMBkGA1UEAwwSYXV0aGVudGlrIDIwMjUuNi4zMB4XDTI1MDcxNTE4MDQzNloXDTI2MDcxNjE4MDQzNlowVjEqMCgGA1UEAwwhYXV0aGVudGlrIFNlbGYtc2lnbmVkIENlcnRpZmljYXRlMRIwEAYDVQQKDAlhdXRoZW50aWsxFDASBgNVBAsMC1NlbGYtc2lnbmVkMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjmut/+bBRLlyrbf+WIfg8ZTw9t6VnsiU1n04nPTulpRAz4nBOoOHNRIruSpZyFeFa6x9jwn4Ma5EFUH7HqnRvhoujm8U17OglXWZt0DLCZ6S5xPmdMogFXjJDmg9okIcI/cb9VbR6I8uvm1oiaOWCr36RTiqZ6rmdjQcuUPLr1+V/LxWQI463S+5QA2HZxAGalp45MJAz2sa9iczktKMgyYlfjj1cruFARxxeheu5qIK7aQWfyPj1QlMb9mi4VQaxUwGrAui4Tq614ivRJY2SkZb0Aq/LLSQoQWYHtYyQIasrOXJm0JuPDqhINPBDowyhu8DihC3uzOpmTXLKc5UoIQk+Q1h5iH74A3/kxOJUw13FXzRiDxC/yGthPYLyFHsDiJolscMKSCqlDvEMcpM4mxFeud9sKUb71SZr8sqmJl3qtvZmKpkR4y8pN2c00p10t0htqONmr5kyPxmhz0HCrosiPYB4olNjaydKviNTtPJ7TtnPyeA3iXGzCP1e80XzUoJrDqON5/GcpYgqsP/kGj8Qvqesa4Fez+1+5pAGHN2VzQbkHAgK3s4YRXrGLTs7wg27F9T0RE28Mm0RYBkYpdp4/5PuTTulthB9mkUBSJMgENmQAYkapvonFDsJkTi39qnsddbZusOLT4z3hsA38eFEwRqnbNZVUGPIp/O1SsCAwEAAaNVMFMwUQYDVR0RAQH/BEcwRYJDRUZ4QXVLRzV6SlVUTWpWNTJoMkRJMUQ5MXdLblZKaXFwNmpwRTRTTy5zZWxmLXNpZ25lZC5nb2F1dGhlbnRpay5pbzANBgkqhkiG9w0BAQsFAAOCAgEAYLThxDVpA1OIAVK/buueRJExIWr6y4s6NtpuR8UQEcfq5hfoc4zMFGHR5+u1WFIb5siK25xh/OnS7bLdLic6AkjZSrx91+0v2Jn9gfUqbs5AJ040XzAAdx/Mb4s0+537yhB+/JXPylR1QxhGbO7koXQ5JDhAXWKCw2O1C+80mN8dbhQvDkEtsXrHrtXclcqf2TT89XAzc5HAC8NmP4SF+FafAREQB1KdaG4QAbc/gnjsX2YJD89SDL+3jMp6F7R1Ym+bWt5oWqx2tkm6HGXd3fbpfQlnfrRN60tMjjLmw1cDMhOhpdragY5zokniEUL2pKVtrxFp7V1ZpoMI0Kt5MKkOXrezi542NWSgkGehlsDLD9wtuCNem2arR0mNnMLdYkMG7G0dpAq3Tl32dgfMfyKnNyE2O/6/EeEuzUH2NfTU1p7AUQfLrf4rtNcJEs9OAPuC9vy7w9YEpF997T+FhR2Ub1C423NQj4bwlS/9f7MIBkSi1EgnQuiSGB5epxAKI3oOVrmzOpTuvr6wZXV9pM3zdfbcoGuFWP6Ix7W8G5vg+0WvoSjc2fwGXYlidEK3xlQSMAaQ4CMClpPsKLScRq1nrQGzPYoiL1DYubsOWx9ohll6+jNjKI6f79WwbHYrW4EeRIOz38+m46EDjAWZBMgrE7J/3DhgeLEVJYBA5K0=</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature>
<saml:Subject>
<saml:NameID SPNameQualifier="http://sp.example.com/demo1/metadata.php" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient">ATTACKER_FORGED_NAMEID</saml:NameID>
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml:SubjectConfirmationData NotOnOrAfter="2024-01-18T06:21:48Z" Recipient="http://sp.example.com/demo1/index.php?acs" InResponseTo="ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685"/>
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Conditions NotBefore="2014-07-17T01:01:18Z" NotOnOrAfter="2024-01-18T06:21:48Z">
<saml:AudienceRestriction>
<saml:Audience>http://sp.example.com/demo1/metadata.php</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<saml:AuthnStatement AuthnInstant="2014-07-17T01:01:48Z" SessionNotOnOrAfter="2024-07-17T09:01:48Z" SessionIndex="_be9967abd904ddcae3c0eb4189adbe3f71e327cf93">
<saml:AuthnContext>
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml:AuthnContextClassRef>
</saml:AuthnContext>
</saml:AuthnStatement>
<saml:AttributeStatement>
<saml:Attribute Name="uid" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xsi:type="xs:string">ATTACKER_FORGED</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="mail" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xsi:type="xs:string">ATTACKER_FORGED</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="eduPersonAffiliation" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xsi:type="xs:string">ATTACKER_FORGED</saml:AttributeValue>
<saml:AttributeValue xsi:type="xs:string">ATTACKER_FORGED</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
<saml:Assertion xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" ID="pfxa06693ef-cec7-f4a6-cb7f-ad074445a1a3" Version="2.0" IssueInstant="2014-07-17T01:01:48Z">
<saml:Issuer>http://idp.example.com/metadata.php</saml:Issuer>
<saml:Subject>
<saml:NameID SPNameQualifier="http://sp.example.com/demo1/metadata.php" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient">_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7</saml:NameID>
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml:SubjectConfirmationData NotOnOrAfter="2024-01-18T06:21:48Z" Recipient="http://sp.example.com/demo1/index.php?acs" InResponseTo="ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685"/>
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Conditions NotBefore="2014-07-17T01:01:18Z" NotOnOrAfter="2024-01-18T06:21:48Z">
<saml:AudienceRestriction>
<saml:Audience>http://sp.example.com/demo1/metadata.php</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<saml:AuthnStatement AuthnInstant="2014-07-17T01:01:48Z" SessionNotOnOrAfter="2024-07-17T09:01:48Z" SessionIndex="_be9967abd904ddcae3c0eb4189adbe3f71e327cf93">
<saml:AuthnContext>
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml:AuthnContextClassRef>
</saml:AuthnContext>
</saml:AuthnStatement>
<saml:AttributeStatement>
<saml:Attribute Name="uid" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xsi:type="xs:string">test</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="mail" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xsi:type="xs:string">test@example.com</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="eduPersonAffiliation" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xsi:type="xs:string">users</saml:AttributeValue>
<saml:AttributeValue xsi:type="xs:string">examplerole1</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
</samlp:Response>
@@ -0,0 +1,35 @@
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_resp" Version="2.0" IssueInstant="2014-07-17T01:01:48Z" Destination="http://sp.example.com/demo1/index.php?acs" InResponseTo="ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685"><saml:Issuer>http://idp.example.com/metadata.php</saml:Issuer><samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></samlp:Status><saml:Assertion ID="pfxa06693ef-cec7-f4a6-cb7f-ad074445a1a3" Version="2.0" IssueInstant="2014-07-17T01:01:48Z"><saml:Issuer>http://idp.example.com/metadata.php</saml:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI="#pfxa06693ef-cec7-f4a6-cb7f-ad074445a1a3"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>zNDuGxwP4gVkv/Dzt7kiKo/4gzk=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>GLP/vE8uxerB0uDpPslUgLPBL6ePQB619MoQ0I2Y5lAtFE6CB1zh8BnzChRx/bFjNy4byfOe8mFfM0r7WUi1PJOFWyUPoatdLl7wHHBIRTnPpYmu3Tb2Gz0sOP0F8wW7JkBft5gJfVw49nk5si9/3Q3o52jnJZ7dPtqfIOh8uNeopikK0HLF6sU05qCCtjcXfniEnLQFNBFMo9uY5GQqmR5n3nqPz1wYyyfFOAbVmGgBIoO2PfGX2GVLQhltc9qf2JMhks4jgZsZ8iLUIiH1lcLGWZEEs94k8k0P6gSv1uZ7Vbhksd/N9Jq9pCVuEJ/jRPcAdVjzbxqKQAj6ELwr8O6fepTzA+CAdwEolBnx/C6TmSbVZ+IWk6QUGe4x4+IAukC+0hkKENlO0ELOScksvyhpgHbxNA4rp+DhGupCaO/I2RrsQkmvavbqm+wSEspK7scK112SDunjDvqPHsPYgukD33T/97PxTLorg2kKP9HHJwPJKoXXeyOGcA6vwK+RqrAlZ2dLGAgcXo+sJcdCLuvxDNz9VXofBjBZIKVKdmYhm0QJaPYHtuQsAyFavQhdOBOmGHb7QX3YE3Xy4dX4LymtT+Jlb1I4FJSht/9HUIHW1FdhfDak4f7gUgjuMamMddLD0jVgeESupSREzFv/gj2IrctkbgjAO0iuuiBgKMg=</ds:SignatureValue>
<ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIIFUzCCAzugAwIBAgIRAL6tbNcE9Ej9gNlbGKswfFMwDQYJKoZIhvcNAQELBQAwHTEbMBkGA1UEAwwSYXV0aGVudGlrIDIwMjUuNi4zMB4XDTI1MDcxNTE4MDQzNloXDTI2MDcxNjE4MDQzNlowVjEqMCgGA1UEAwwhYXV0aGVudGlrIFNlbGYtc2lnbmVkIENlcnRpZmljYXRlMRIwEAYDVQQKDAlhdXRoZW50aWsxFDASBgNVBAsMC1NlbGYtc2lnbmVkMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjmut/+bBRLlyrbf+WIfg8ZTw9t6VnsiU1n04nPTulpRAz4nBOoOHNRIruSpZyFeFa6x9jwn4Ma5EFUH7HqnRvhoujm8U17OglXWZt0DLCZ6S5xPmdMogFXjJDmg9okIcI/cb9VbR6I8uvm1oiaOWCr36RTiqZ6rmdjQcuUPLr1+V/LxWQI463S+5QA2HZxAGalp45MJAz2sa9iczktKMgyYlfjj1cruFARxxeheu5qIK7aQWfyPj1QlMb9mi4VQaxUwGrAui4Tq614ivRJY2SkZb0Aq/LLSQoQWYHtYyQIasrOXJm0JuPDqhINPBDowyhu8DihC3uzOpmTXLKc5UoIQk+Q1h5iH74A3/kxOJUw13FXzRiDxC/yGthPYLyFHsDiJolscMKSCqlDvEMcpM4mxFeud9sKUb71SZr8sqmJl3qtvZmKpkR4y8pN2c00p10t0htqONmr5kyPxmhz0HCrosiPYB4olNjaydKviNTtPJ7TtnPyeA3iXGzCP1e80XzUoJrDqON5/GcpYgqsP/kGj8Qvqesa4Fez+1+5pAGHN2VzQbkHAgK3s4YRXrGLTs7wg27F9T0RE28Mm0RYBkYpdp4/5PuTTulthB9mkUBSJMgENmQAYkapvonFDsJkTi39qnsddbZusOLT4z3hsA38eFEwRqnbNZVUGPIp/O1SsCAwEAAaNVMFMwUQYDVR0RAQH/BEcwRYJDRUZ4QXVLRzV6SlVUTWpWNTJoMkRJMUQ5MXdLblZKaXFwNmpwRTRTTy5zZWxmLXNpZ25lZC5nb2F1dGhlbnRpay5pbzANBgkqhkiG9w0BAQsFAAOCAgEAYLThxDVpA1OIAVK/buueRJExIWr6y4s6NtpuR8UQEcfq5hfoc4zMFGHR5+u1WFIb5siK25xh/OnS7bLdLic6AkjZSrx91+0v2Jn9gfUqbs5AJ040XzAAdx/Mb4s0+537yhB+/JXPylR1QxhGbO7koXQ5JDhAXWKCw2O1C+80mN8dbhQvDkEtsXrHrtXclcqf2TT89XAzc5HAC8NmP4SF+FafAREQB1KdaG4QAbc/gnjsX2YJD89SDL+3jMp6F7R1Ym+bWt5oWqx2tkm6HGXd3fbpfQlnfrRN60tMjjLmw1cDMhOhpdragY5zokniEUL2pKVtrxFp7V1ZpoMI0Kt5MKkOXrezi542NWSgkGehlsDLD9wtuCNem2arR0mNnMLdYkMG7G0dpAq3Tl32dgfMfyKnNyE2O/6/EeEuzUH2NfTU1p7AUQfLrf4rtNcJEs9OAPuC9vy7w9YEpF997T+FhR2Ub1C423NQj4bwlS/9f7MIBkSi1EgnQuiSGB5epxAKI3oOVrmzOpTuvr6wZXV9pM3zdfbcoGuFWP6Ix7W8G5vg+0WvoSjc2fwGXYlidEK3xlQSMAaQ4CMClpPsKLScRq1nrQGzPYoiL1DYubsOWx9ohll6+jNjKI6f79WwbHYrW4EeRIOz38+m46EDjAWZBMgrE7J/3DhgeLEVJYBA5K0=</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature><saml:Subject><saml:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient">FORGED_VICTIM</saml:NameID></saml:Subject><saml:Conditions NotBefore="2014-07-17T01:01:18Z" NotOnOrAfter="2024-01-18T06:21:48Z"><saml:AudienceRestriction><saml:Audience>http://sp.example.com/demo1/metadata.php</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:Advice><saml:Assertion xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" ID="pfxa06693ef-cec7-f4a6-cb7f-ad074445a1a3" Version="2.0" IssueInstant="2014-07-17T01:01:48Z">
<saml:Issuer>http://idp.example.com/metadata.php</saml:Issuer>
<saml:Subject>
<saml:NameID SPNameQualifier="http://sp.example.com/demo1/metadata.php" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient">_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7</saml:NameID>
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml:SubjectConfirmationData NotOnOrAfter="2024-01-18T06:21:48Z" Recipient="http://sp.example.com/demo1/index.php?acs" InResponseTo="ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685"/>
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Conditions NotBefore="2014-07-17T01:01:18Z" NotOnOrAfter="2024-01-18T06:21:48Z">
<saml:AudienceRestriction>
<saml:Audience>http://sp.example.com/demo1/metadata.php</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<saml:AuthnStatement AuthnInstant="2014-07-17T01:01:48Z" SessionNotOnOrAfter="2024-07-17T09:01:48Z" SessionIndex="_be9967abd904ddcae3c0eb4189adbe3f71e327cf93">
<saml:AuthnContext>
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml:AuthnContextClassRef>
</saml:AuthnContext>
</saml:AuthnStatement>
<saml:AttributeStatement>
<saml:Attribute Name="uid" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xsi:type="xs:string">test</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="mail" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xsi:type="xs:string">test@example.com</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="eduPersonAffiliation" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xsi:type="xs:string">users</saml:AttributeValue>
<saml:AttributeValue xsi:type="xs:string">examplerole1</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion></saml:Advice></saml:Assertion></samlp:Response>
@@ -0,0 +1,40 @@
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_resp_uri_empty" Version="2.0" IssueInstant="2014-07-17T01:01:48Z" Destination="http://sp.example.com/demo1/index.php?acs" InResponseTo="ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685"><saml:Issuer>http://idp.example.com/metadata.php</saml:Issuer><Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<DigestValue>h6La1VQZ6VO/Bi/a3g2Uhkp27kV1ijJvIB6xlgLGu7M=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>ItqDe3xfN9sN44z9pudYI+GdVEz7MnLXvG3+afjS8ws52c8fUryoK0lgH3l0i3mY
cFKiQwiuRx86AB6uGH13ToOoXq3peK911PeGZ/fCt15x14x6v69r9vG+Mw4KAc+y
7J/a3BOltEoU9SfNHBAIkEgbHP35bF5iYSVlIfLho8BRCVT4rMv/K/n65Hd88Fas
Es2oxS95hYUf1of9pHDOuvEYM+VpMZw3OuMci7X8ZszCxdJbgP6D4m7w9wWBbAAi
xWWuLnU2sFkLNeBh5KuumLNLG1Dreqiz0d4/tu+P9F4NcP2FAdG79/Y+Ga9d0Wf7
yf05WcieZgV4hhHNwQcauw==</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>MIICvjCCAaagAwIBAgIUGjmjYk5z7ya8VH2Faah5vvVmEHIwDQYJKoZIhvcNAQEL
BQAwGTEXMBUGA1UEAwwOVVJJLWVtcHR5IHRlc3QwHhcNMTQwMTAxMDAwMDAwWhcN
MzAwMTAxMDAwMDAwWjAZMRcwFQYDVQQDDA5VUkktZW1wdHkgdGVzdDCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKEuJi9Gny9OfQScZEG1R9Gy5pqbM7it
pGda6gm4PXuFQisMapdg0DwO3odY1grTQE6gFEjSCYtEoGyuJMSUNdILvc7sfCWa
QkuXCnkx7nXgyiHLNuhgkhw6kDXgRMzdaI/B7IlZkZYnfyXWi6A6lQ6dLJRU1p0o
C+JawKngvmOAAKvhaC1keEbfUh//8UhK/YkjJShV1c0ijFlucj4eYDQyf9x9lYw7
oukjnVFKmhqiRO/SxwhYjYbyTppVRzC4sCJz0KRn4qwnGV5x2+jDqUvFUxJc5W7f
N+utrfNWeszmP5Elw+ezIsq+3D9ss+nHmtXIDxVNA3dp8tZQKzQ4WzcCAwEAATAN
BgkqhkiG9w0BAQsFAAOCAQEART2PW/rXPU5d+NTJnP2mdIi5Ft01gpaRgY1iBsFm
+D6zXT/k9wr56GEDLWUf2qOJeXsNc+f9FKI0BCVb/5vVoB60AAMHKbb+NxwoXLHi
dT6DaEPK1VGdEBnpL5uOII3pO42FMPewBUNuoUmb9zipyPoL6zbc7khRcBBIYMlr
05Y2tqJqECNAtGQ9+v5CBcECP3QI4L0UmCJMwpj7XH5TrfKfLPZuUEvQaET63dXb
ioi+P6KEpuxblOL0Uj2e2erhJYavqCnoxt+0eUDDBsrwuk7/sRtbBO0XjkgEtJZ8
n3OS74cjqIwTx+PqObGThECnyBYwONY7RWU5G4r6kqMXBg==
</X509Certificate>
</X509Data>
</KeyInfo>
</Signature><samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></samlp:Status><saml:Assertion ID="_assert" Version="2.0" IssueInstant="2014-07-17T01:01:48Z"><saml:Issuer>http://idp.example.com/metadata.php</saml:Issuer><saml:Subject><saml:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient">test_user</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData NotOnOrAfter="2024-01-18T06:21:48Z" Recipient="http://sp.example.com/demo1/index.php?acs" InResponseTo="ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="2014-07-17T01:01:18Z" NotOnOrAfter="2024-01-18T06:21:48Z"><saml:AudienceRestriction><saml:Audience>http://sp.example.com/demo1/metadata.php</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AuthnStatement AuthnInstant="2014-07-17T01:01:48Z" SessionIndex="_session"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement></saml:Assertion></samlp:Response>
@@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC0jCCAbqgAwIBAgIUXHr2/LJAqtsJ4CcXkFjMwJo8HmQwDQYJKoZIhvcNAQEL
BQAwIzEhMB8GA1UEAwwYVVJJLWVtcHR5IGFzc2VydGlvbiB0ZXN0MB4XDTE0MDEw
MTAwMDAwMFoXDTMwMDEwMTAwMDAwMFowIzEhMB8GA1UEAwwYVVJJLWVtcHR5IGFz
c2VydGlvbiB0ZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArzIB
E4WYhfFNJk2VcvxTX9+cdyJ+v+GCAU4BxSJ1/97BPf3UN4yob23qohnnnMlGAO2x
QBK2VBQKjNZo3qwy2t5xhsa0YLjjWGxAEL1s5K8cQlkYwkciTy+RFpMXmM80kk8p
ZdZFgrjf5ltincH4QuhJcN3/fsEibHQibymWeb/0I3Mba6Uh+gssMN82NYETl677
I+5HV4wgJWMh1vaZFbid+YFBeWWJoIAIdbTQEAwIJriTA43lgbcK0Lo9A8/RCH1O
RVsQSkpA3kfs9yJ9AvKglBIThapR1iRgtVsC9LdiauHmiNU+8POSHXWByXaWOK0o
Izfg/lI+xKSWJerhUwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQA+/3kRE6jYFfgy
vHZdOX2cFOA5Y0N/RZmdt34tsfCiqP2vHtfQUje8gQAhjmV6dFk4wptPF1FsP601
bMp+9LsRsb4y6pxy5m7xKVK9P/EI33N2zZZL9tlJ7CPIA81DPi53lYOvX54UIi2I
GpF6QyYMX2HTs/KVxo4gYnOnkyqPw6QrKaWpJLQndQd1rTn4/ybWW/9XU46RYf7/
7Z8H8t4n4lhPYm5WGer4eG+k+F3R04yhwSm3Wi91gkQwQdGPgFSPe1z8TusDw/Q/
1ax1a/mNoN9NCcZgg3L0xZgbtDnzBr/Gd/MWBQdDgRM7DpcWaVVcXq5GRLLeAvwM
uF73araE
-----END CERTIFICATE-----
@@ -0,0 +1,17 @@
-----BEGIN CERTIFICATE-----
MIICvjCCAaagAwIBAgIUGjmjYk5z7ya8VH2Faah5vvVmEHIwDQYJKoZIhvcNAQEL
BQAwGTEXMBUGA1UEAwwOVVJJLWVtcHR5IHRlc3QwHhcNMTQwMTAxMDAwMDAwWhcN
MzAwMTAxMDAwMDAwWjAZMRcwFQYDVQQDDA5VUkktZW1wdHkgdGVzdDCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKEuJi9Gny9OfQScZEG1R9Gy5pqbM7it
pGda6gm4PXuFQisMapdg0DwO3odY1grTQE6gFEjSCYtEoGyuJMSUNdILvc7sfCWa
QkuXCnkx7nXgyiHLNuhgkhw6kDXgRMzdaI/B7IlZkZYnfyXWi6A6lQ6dLJRU1p0o
C+JawKngvmOAAKvhaC1keEbfUh//8UhK/YkjJShV1c0ijFlucj4eYDQyf9x9lYw7
oukjnVFKmhqiRO/SxwhYjYbyTppVRzC4sCJz0KRn4qwnGV5x2+jDqUvFUxJc5W7f
N+utrfNWeszmP5Elw+ezIsq+3D9ss+nHmtXIDxVNA3dp8tZQKzQ4WzcCAwEAATAN
BgkqhkiG9w0BAQsFAAOCAQEART2PW/rXPU5d+NTJnP2mdIi5Ft01gpaRgY1iBsFm
+D6zXT/k9wr56GEDLWUf2qOJeXsNc+f9FKI0BCVb/5vVoB60AAMHKbb+NxwoXLHi
dT6DaEPK1VGdEBnpL5uOII3pO42FMPewBUNuoUmb9zipyPoL6zbc7khRcBBIYMlr
05Y2tqJqECNAtGQ9+v5CBcECP3QI4L0UmCJMwpj7XH5TrfKfLPZuUEvQaET63dXb
ioi+P6KEpuxblOL0Uj2e2erhJYavqCnoxt+0eUDDBsrwuk7/sRtbBO0XjkgEtJZ8
n3OS74cjqIwTx+PqObGThECnyBYwONY7RWU5G4r6kqMXBg==
-----END CERTIFICATE-----
+114 -2
View File
@@ -196,10 +196,122 @@ class TestResponseProcessor(TestCase):
self.assertNotEqual(parser._get_name_id()[1], "bad")
self.assertEqual(parser._get_name_id()[1], "_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7")
@freeze_time("2022-10-14T14:15:00")
@freeze_time("2014-07-17T01:02:18Z")
def test_verification_assertion_xsw_nested_duplicate_id(self):
"""Nested-duplicate-ID XSW: a forged outer Assertion shares its ID with a
nested copy of the original signed Assertion (placed inside <saml:Advice>),
so the Signature's Reference URI (#ORIG_ID) matches the outer Assertion's
ID *and* dereferences to legitimately-signed content. Must be rejected."""
key = load_fixture("fixtures/signature_cert.pem")
kp = CertificateKeyPair.objects.create(
name=generate_id(),
certificate_data=key,
)
self.source.verification_kp = kp
self.source.signed_assertion = True
self.source.signed_response = False
request = self.factory.post(
"/",
data={
"SAMLResponse": b64encode(
load_fixture("fixtures/response_signed_assertion_xsw_nested.xml").encode()
).decode()
},
)
parser = ResponseProcessor(self.source, request)
with self.assertRaises(InvalidSignature):
parser.parse()
@freeze_time("2014-07-17T01:02:18Z")
def test_verification_response_uri_empty(self):
"""Some real-world IdPs (notably some Okta dev-tenant configurations
observed in the gosaml2 testdata corpus at saml.oktadev.com) sign the
Response with ds:Reference URI="" instead of URI="#<ID>". Per xmldsig
§4.4.3.2, URI="" covers the entire enclosing document via the
enveloped-signature transform strictly more attested content than
"#<ID>" so consuming the target is a subset of what was signed."""
key = load_fixture("fixtures/signature_cert_uri_empty.pem")
kp = CertificateKeyPair.objects.create(
name=generate_id(),
certificate_data=key,
)
self.source.verification_kp = kp
self.source.signed_response = True
self.source.signed_assertion = False
request = self.factory.post(
"/",
data={
"SAMLResponse": b64encode(
load_fixture("fixtures/response_signed_response_uri_empty.xml").encode()
).decode()
},
)
parser = ResponseProcessor(self.source, request)
parser.parse()
@freeze_time("2014-07-17T01:02:18Z")
def test_verification_assertion_uri_empty(self):
"""Symmetric to test_verification_response_uri_empty but for an
Assertion-level signature: the same xmldsig "this document" semantics
still cover the whole enclosing document, so the Assertion we then
consume is part of the attested content. We have no real-world IdP
samples emitting this configuration, but the pre-fix code accepted it
and the cryptographic guarantee holds, so keep accepting it rather
than risk breaking an IdP we haven't sampled."""
key = load_fixture("fixtures/signature_cert_assertion_uri_empty.pem")
kp = CertificateKeyPair.objects.create(
name=generate_id(),
certificate_data=key,
)
self.source.verification_kp = kp
self.source.signed_assertion = True
self.source.signed_response = False
request = self.factory.post(
"/",
data={
"SAMLResponse": b64encode(
load_fixture("fixtures/response_signed_assertion_uri_empty.xml").encode()
).decode()
},
)
parser = ResponseProcessor(self.source, request)
parser.parse()
@freeze_time("2014-07-17T01:02:18Z")
def test_verification_assertion_xsw3(self):
"""XSW-3 (signature relocation): a forged Assertion contains a Signature whose
ds:Reference URI points to a second Assertion in the document. The signature
verifies (because the digest matches the legitimate referenced Assertion),
but the verifier must NOT then consume the forged Assertion as if it were
signed."""
key = load_fixture("fixtures/signature_cert.pem")
kp = CertificateKeyPair.objects.create(
name=generate_id(),
certificate_data=key,
)
self.source.verification_kp = kp
self.source.signed_assertion = True
self.source.signed_response = False
request = self.factory.post(
"/",
data={
"SAMLResponse": b64encode(
load_fixture("fixtures/response_signed_assertion_xsw3.xml").encode()
).decode()
},
)
parser = ResponseProcessor(self.source, request)
with self.assertRaises(InvalidSignature):
parser.parse()
@freeze_time("2014-07-17T01:02:18Z")
def test_name_id_comment(self):
"""Test comment in name ID"""
fixture = load_fixture("fixtures/response_signed_assertion_dup.xml")
fixture = load_fixture("fixtures/response_signed_assertion.xml")
fixture = fixture.replace(
"_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7",
"_ce3d2948b4cf20146dee0a0b3dd6f<!--x-->69b6cf86f62d7",
+1 -1
View File
@@ -291,7 +291,7 @@ class VerifyNotAllowed:
class ThrottlingMixin(models.Model):
"""
Mixin class for models that want throttling behaviour.
Mixin class for models that want throttling behavior.
This implements exponential back-off for verifying tokens. Subclasses must
implement :meth:`get_throttle_factor`, and must use the
+1 -1
View File
@@ -362,7 +362,7 @@ class AuthenticatorSMSStageTests(FlowTestCase):
class TestSMSDeviceThrottling(ThrottlingTestMixin, TestCase):
"""Test ThrottlingMixin behaviour on SMSDevice.verify_token"""
"""Test ThrottlingMixin behavior on SMSDevice.verify_token"""
def setUp(self):
super().setUp()
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+5 -3
View File
@@ -4,6 +4,7 @@ from email.mime.image import MIMEImage
from functools import lru_cache
from pathlib import Path
from django.conf import settings
from django.core.mail import EmailMultiAlternatives
from django.core.mail.message import sanitize_address
from django.template.exceptions import TemplateDoesNotExist
@@ -14,9 +15,10 @@ from django.utils import translation
@lru_cache
def logo_data() -> MIMEImage:
"""Get logo as MIME Image for emails"""
path = Path("web/icons/icon_left_brand.png")
if not path.exists():
path = Path("web/dist/assets/icons/icon_left_brand.png")
path = Path("web/dist/assets/icons/icon_left_brand.png")
# When running tests, assets might not exist, so fallback to a different icon
if settings.TEST:
path = Path("web/authentik/sources/saml.png")
with open(path, "rb") as _logo_file:
logo = MIMEImage(_logo_file.read())
logo.add_header("Content-ID", "<logo>")
+1 -1
View File
@@ -18,7 +18,7 @@ PLAN_CONTEXT_INVITATION = "invitation"
class InvitationStageView(StageView):
"""Finalise Authentication flow by logging the user in"""
"""Finalize Authentication flow by logging the user in"""
def get_token(self) -> str | None:
"""Get token from saved get-arguments or prompt_data"""
+1 -1
View File
@@ -10,7 +10,7 @@ from authentik.flows.stage import StageView
class UserDeleteStageView(StageView):
"""Finalise unenrollment flow by deleting the user object."""
"""Finalize unenrollment flow by deleting the user object."""
def dispatch(self, request: HttpRequest) -> HttpResponse:
"""Delete currently pending user"""
+1 -1
View File
@@ -53,7 +53,7 @@ class UserLoginChallengeResponse(ChallengeResponse):
class UserLoginStageView(ChallengeStageView):
"""Finalise Authentication flow by logging the user in"""
"""Finalize Authentication flow by logging the user in"""
response_class = UserLoginChallengeResponse

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