Compare commits

..

5 Commits

Author SHA1 Message Date
Connor Peshek
43937bebe9 update to main 2026-05-13 00:53:26 -05:00
Connor Peshek
43b73995ba cleanup 2026-02-27 19:18:57 -06:00
Connor Peshek
b0284256ea remove empty exception file 2026-02-26 16:53:51 -06:00
Connor Peshek
ad4f81e5b0 update imports 2026-02-26 04:27:17 -06:00
Connor Peshek
a98b8fccdf providers/saml: verify logoutrequest/response signatures 2026-02-26 03:29:43 -06:00
374 changed files with 6261 additions and 27540 deletions

View File

@@ -1,77 +0,0 @@
name: "Setup Node.js and NPM"
description: "Sets up Node.js with a specific NPM version via Corepack"
inputs:
working-directory:
description: "Path to the working directory containing the package.json file"
required: false
default: "."
dependencies:
required: false
description: "List of dependencies to setup"
default: "monorepo,working-directory"
node-version-file:
description: "Path to file containing the Node.js version"
required: false
default: "package.json"
cache-dependency-path:
description: "Path to dependency lock file for caching"
required: false
default: "package-lock.json"
cache:
description: "Package manager to cache"
default: "npm"
registry-url:
description: "npm registry URL"
default: "https://registry.npmjs.org"
runs:
using: "composite"
steps:
- name: Setup Node.js (Corepack bootstrap)
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v4
with:
node-version-file: ${{ inputs.node-version-file }}
registry-url: ${{ inputs.registry-url }}
cache: ${{ inputs.cache }}
cache-dependency-path: |
${{ inputs.cache-dependency-path }}
${{ inputs.working-directory }}/${{ inputs.cache-dependency-path }}
- name: Install Corepack
working-directory: ${{ github.workspace}}
shell: bash
run: | #shell
node ./scripts/node/setup-corepack.mjs --force
corepack enable
- name: Lint Node.js and NPM versions
shell: bash
run: node ./scripts/node/lint-runtime.mjs
- name: Setup Node.js (Monorepo Root)
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v4
with:
node-version-file: ${{ inputs.node-version-file }}
registry-url: ${{ inputs.registry-url }}
- name: Install monorepo dependencies
if: ${{ contains(inputs.dependencies, 'monorepo') }}
shell: bash
run: | #shell
node ./scripts/node/lint-lockfile.mjs
corepack npm ci
- name: Setup Node.js (Working Directory)
if: ${{ contains(inputs.dependencies, 'working-directory') }}
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v4
with:
node-version-file: ${{ inputs.working-directory }}/${{ inputs.node-version-file }}
registry-url: ${{ inputs.registry-url }}
- name: Install working directory dependencies
if: ${{ contains(inputs.dependencies, 'working-directory') }}
shell: bash
run: | # shell
corepack install
echo "node version: $(node --version)"
echo "npm version: $(corepack npm --version)"
node ./scripts/node/lint-lockfile.mjs ${{ inputs.working-directory }}
corepack npm ci --prefix ${{ inputs.working-directory }}

View File

@@ -25,7 +25,7 @@ runs:
if: ${{ contains(inputs.dependencies, 'system') || contains(inputs.dependencies, 'python') }}
uses: gerlero/apt-install@f4fa5265092af9e750549565d28c99aec7189639
with:
packages: libpq-dev openssl libxmlsec1-dev pkg-config gettext libclang-dev libkadm5clnt-mit12 libkadm5clnt7t64-heimdal libkrb5-dev krb5-kdc krb5-user krb5-admin-server
packages: libpq-dev openssl libxmlsec1-dev pkg-config gettext krb5-multidev libkrb5-dev heimdal-multidev libclang-dev krb5-kdc krb5-user krb5-admin-server
update: true
upgrade: false
install-recommends: false
@@ -52,26 +52,42 @@ runs:
run: uv sync --all-extras --dev --locked
- name: Setup rust (stable)
if: ${{ contains(inputs.dependencies, 'rust') && !contains(inputs.dependencies, 'rust-nightly') }}
uses: actions-rust-lang/setup-rust-toolchain@46268bd060767258de96ed93c1251119784f2ab6 # v1
uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 # v1
with:
rustflags: ""
- name: Setup rust (nightly)
if: ${{ contains(inputs.dependencies, 'rust-nightly') }}
uses: actions-rust-lang/setup-rust-toolchain@46268bd060767258de96ed93c1251119784f2ab6 # v1
uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 # v1
with:
toolchain: nightly
components: rustfmt
rustflags: ""
- name: Setup rust dependencies
if: ${{ contains(inputs.dependencies, 'rust') }}
uses: taiki-e/install-action@ec28e287910af896fd98e04056d31fa68607e7ad # v2
uses: taiki-e/install-action@711e1c3275189d76dcc4d34ddea63bf96ac49090 # v2
with:
tool: cargo-deny cargo-machete cargo-llvm-cov nextest
- name: Setup node (root, web)
- name: Setup node (web)
if: ${{ contains(inputs.dependencies, 'node') }}
uses: ./.github/actions/setup-node
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v4
with:
working-directory: web
node-version-file: "${{ inputs.working-directory }}web/package.json"
cache: "npm"
cache-dependency-path: "${{ inputs.working-directory }}web/package-lock.json"
registry-url: "https://registry.npmjs.org"
- name: Setup node (root)
if: ${{ contains(inputs.dependencies, 'node') }}
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v4
with:
node-version-file: "${{ inputs.working-directory }}package.json"
cache: "npm"
cache-dependency-path: "${{ inputs.working-directory }}package-lock.json"
registry-url: "https://registry.npmjs.org"
- name: Install Node deps
if: ${{ contains(inputs.dependencies, 'node') }}
shell: bash
working-directory: ${{ inputs.working-directory }}
run: npm ci
- name: Setup go
if: ${{ contains(inputs.dependencies, 'go') }}
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v5
@@ -89,7 +105,7 @@ runs:
run: |
export PSQL_TAG=${{ inputs.postgresql_version }}
docker compose -f .github/actions/setup/compose.yml up -d --wait
corepack npm ci --prefix web
cd web && npm ci
- name: Generate config
if: ${{ contains(inputs.dependencies, 'python') }}
shell: uv run python {0}

3
.github/codecov.yml vendored
View File

@@ -1,6 +1,3 @@
codecov:
notify:
wait_for_ci: true
coverage:
status:
project:

View File

@@ -67,16 +67,6 @@ jobs:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: ./.github/actions/setup-node
with:
working-directory: web
dependencies: "monorepo"
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # 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
id: push

View File

@@ -22,19 +22,25 @@ jobs:
- prettier-check
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: ./.github/actions/setup-node
with:
working-directory: website
- name: Install Dependencies
working-directory: website/
run: npm ci
- name: Lint
run: corepack npm run ${{ matrix.command }} --prefix website
working-directory: website/
run: npm run ${{ matrix.command }}
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: ./.github/actions/setup-node
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5
with:
working-directory: website
node-version-file: website/package.json
cache: "npm"
cache-dependency-path: website/package-lock.json
- working-directory: website/
name: Install Dependencies
run: npm ci
- uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v4
with:
path: |
@@ -48,7 +54,7 @@ jobs:
working-directory: website
env:
NODE_ENV: production
run: corepack npm run build -w api
run: npm run build -w api
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v4
with:
name: api-docs
@@ -65,9 +71,11 @@ jobs:
with:
name: api-docs
path: website/api/build
- uses: ./.github/actions/setup-node
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5
with:
working-directory: website
node-version-file: website/package.json
cache: "npm"
cache-dependency-path: website/package-lock.json
- name: Deploy Netlify (Production)
working-directory: website/api
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

View File

@@ -24,9 +24,14 @@ jobs:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- name: Setup authentik env
uses: ./.github/actions/setup
- uses: ./.github/actions/setup-node
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5
with:
working-directory: lifecycle/aws
node-version-file: lifecycle/aws/package.json
cache: "npm"
cache-dependency-path: lifecycle/aws/package-lock.json
- working-directory: lifecycle/aws/
run: |
npm ci
- name: Check changes have been applied
run: |
uv run make aws-cfn

View File

@@ -24,34 +24,46 @@ jobs:
- prettier-check
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: ./.github/actions/setup-node
with:
working-directory: website
- name: Install dependencies
working-directory: website/
run: npm ci
- name: Lint
run: corepack npm run ${{ matrix.command }} --prefix website
working-directory: website/
run: npm run ${{ matrix.command }}
build-docs:
runs-on: ubuntu-latest
env:
NODE_ENV: production
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: ./.github/actions/setup-node
name: Setup Node.js
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5
with:
working-directory: website
node-version-file: website/package.json
cache: "npm"
cache-dependency-path: website/package-lock.json
- working-directory: website/
name: Install Dependencies
run: npm ci
- name: Build Documentation via Docusaurus
run: corepack npm run build --prefix website
working-directory: website/
run: npm run build
build-integrations:
runs-on: ubuntu-latest
env:
NODE_ENV: production
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: ./.github/actions/setup-node
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5
with:
working-directory: website
node-version-file: website/package.json
cache: "npm"
cache-dependency-path: website/package-lock.json
- working-directory: website/
name: Install Dependencies
run: npm ci
- name: Build Integrations via Docusaurus
run: corepack npm run build -w integrations --prefix website
working-directory: website/
run: npm run build -w integrations
build-container:
runs-on: ubuntu-latest
permissions:

View File

@@ -109,13 +109,8 @@ jobs:
- name: checkout stable
run: |
set -e -o pipefail
cp -R .github ..
cp -R scripts ..
mkdir -p ../packages
cp -R packages/logger-js ../packages/logger-js
# Previous stable tag
prev_stable=$(git tag --sort=version:refname | grep '^version/' | grep -vE -- '-rc[0-9]+$' | tail -n1)
# Current version family based on
@@ -123,13 +118,10 @@ jobs:
if [[ -n $current_version_family ]]; then
prev_stable="version/${current_version_family}"
fi
echo "::notice::Checking out ${prev_stable} as stable version..."
git checkout ${prev_stable}
rm -rf .github/ scripts/ packages/logger-js/
rm -rf .github/ scripts/
mv ../.github ../scripts .
mv ../packages/logger-js ./packages/
- name: Setup authentik env (stable)
uses: ./.github/actions/setup
with:
@@ -260,7 +252,6 @@ jobs:
COMPOSE_PROFILES: ${{ matrix.job.profiles }}
run: |
docker compose -f tests/e2e/compose.yml up -d --quiet-pull
- uses: ./.github/actions/setup-node
- id: cache-web
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v4
if: contains(matrix.job.profiles, 'selenium')
@@ -270,12 +261,10 @@ jobs:
- name: prepare web ui
if: steps.cache-web.outputs.cache-hit != 'true' && contains(matrix.job.profiles, 'selenium')
working-directory: web
env:
NODE_ENV: "production"
run: |
corepack npm ci
corepack npm run build
corepack npm run build:sfe
npm ci
npm run build
npm run build:sfe
- name: run e2e
run: |
uv run coverage run manage.py test ${{ matrix.job.glob }}
@@ -324,12 +313,11 @@ jobs:
key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'web/src/**', 'web/packages/sfe/src/**') }}-b
- name: prepare web ui
if: steps.cache-web.outputs.cache-hit != 'true'
env:
NODE_ENV: "production"
working-directory: web
run: |
corepack npm ci --prefix web
corepack npm run build --prefix web
corepack npm run build:sfe --prefix web
npm ci
npm run build
npm run build:sfe
- name: run conformance
run: |
uv run coverage run manage.py test ${{ matrix.job.glob }}

View File

@@ -145,11 +145,16 @@ jobs:
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
with:
go-version-file: "go.mod"
- uses: ./.github/actions/setup-node
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5
with:
working-directory: web
node-version-file: web/package.json
cache: "npm"
cache-dependency-path: web/package-lock.json
- name: Build web
run: corepack npm run build-proxy --prefix web
working-directory: web/
run: |
npm ci
npm run build-proxy
- name: Build outpost
run: |
set -x

View File

@@ -15,34 +15,48 @@ on:
jobs:
lint:
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
command:
- lint
- lint:lockfile
- tsc
- prettier-check
project:
- web
include:
- command: tsc
project: web
- command: lit-analyse
project: web
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: ./.github/actions/setup-node
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5
with:
working-directory: web
node-version-file: ${{ matrix.project }}/package.json
cache: "npm"
cache-dependency-path: ${{ matrix.project }}/package-lock.json
- working-directory: ${{ matrix.project }}/
run: |
npm ci
- name: Lint
run: corepack npm run lint-check --prefix web
- name: Check types
run: corepack npm run tsc --prefix web
- name: Check formatting
run: corepack npm run prettier-check --prefix web
- name: Lit analyse
run: corepack npm run lit-analyse --prefix web
working-directory: ${{ matrix.project }}/
run: npm run ${{ matrix.command }}
build:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: ./.github/actions/setup-node
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5
with:
working-directory: web
node-version-file: web/package.json
cache: "npm"
cache-dependency-path: web/package-lock.json
- working-directory: web/
run: npm ci
- name: build
env:
NODE_ENV: "production"
working-directory: web/
run: corepack npm run build
run: npm run build
ci-web-mark:
if: always()
needs:
@@ -57,12 +71,15 @@ jobs:
needs:
- ci-web-mark
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: ./.github/actions/setup-node
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5
with:
working-directory: web
node-version-file: web/package.json
cache: "npm"
cache-dependency-path: web/package-lock.json
- working-directory: web/
run: npm ci
- name: test
working-directory: web/
run: corepack npm run test || exit 0
run: npm run test || exit 0

View File

@@ -35,19 +35,22 @@ jobs:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
with:
fetch-depth: 2
- uses: ./.github/actions/setup-node
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5
with:
working-directory: ${{ matrix.package }}
node-version-file: ${{ matrix.package }}/package.json
registry-url: "https://registry.npmjs.org"
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # 24d32ffd492484c1d75e0c0b894501ddb9d30d62
with:
files: |
${{ matrix.package }}/package.json
- name: Install Dependencies
run: npm ci
- name: Publish package
if: steps.changed-files.outputs.any_changed == 'true'
working-directory: ${{ matrix.package }}
run: |
corepack npm ci
corepack npm run build
corepack npm publish
npm ci
npm run build
npm publish

View File

@@ -28,10 +28,10 @@ jobs:
- name: Setup authentik env
uses: ./.github/actions/setup
- name: Initialize CodeQL
uses: github/codeql-action/init@v4.35.4
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v4.35.4
uses: github/codeql-action/autobuild@v4
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4.35.4
uses: github/codeql-action/analyze@v4

View File

@@ -5,7 +5,7 @@ on:
workflow_dispatch:
inputs:
next_version:
description: Next version (for example, if you're currently releasing 2026.5, then enter 2026.8)
description: Next major version (for example, if releasing 2042.2, this is 2042.4)
required: true
type: string
@@ -68,14 +68,10 @@ jobs:
token: ${{ steps.generate_token.outputs.token }}
- name: Setup authentik env
uses: ./.github/actions/setup
with:
dependencies: "system,python,go,node,runtime,rust-nightly"
- name: Run migrations
run: make migrate
- name: Bump version
run: "make bump version=${{ inputs.next_version }}.0-rc1"
- name: Re-generate API Clients
run: make gen
- name: Create pull request
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v7
with:

View File

@@ -87,9 +87,11 @@ jobs:
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
with:
go-version-file: "go.mod"
- uses: ./.github/actions/setup-node
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5
with:
working-directory: web
node-version-file: web/package.json
cache: "npm"
cache-dependency-path: web/package-lock.json
- name: Set up QEMU
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
- name: Set up Docker Buildx
@@ -149,9 +151,15 @@ jobs:
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
with:
go-version-file: "go.mod"
- uses: ./.github/actions/setup-node
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5
with:
working-directory: web
node-version-file: web/package.json
cache: "npm"
cache-dependency-path: web/package-lock.json
- name: Install web dependencies
working-directory: web/
run: |
npm ci
- name: Build web
working-directory: web/
run: |
@@ -183,7 +191,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
- uses: aws-actions/configure-aws-credentials@d979d5b3a71173a29b74b5b88418bfda9437d885 # v6.1.1
- uses: aws-actions/configure-aws-credentials@ec61189d14ec14c8efccab744f656cffd0e33f37 # v6.1.0
with:
role-to-assume: "arn:aws:iam::016170277896:role/github_goauthentik_authentik"
aws-region: ${{ env.AWS_REGION }}

View File

@@ -82,14 +82,10 @@ jobs:
token: "${{ steps.app-token.outputs.token }}"
- name: Setup authentik env
uses: ./.github/actions/setup
with:
dependencies: "system,python,go,node,runtime,rust-nightly"
- name: Run migrations
run: make migrate
- name: Bump version
run: "make bump version=${{ inputs.version }}"
- name: Re-generate API Clients
run: make gen
- name: Commit and push
run: |
# ID from https://api.github.com/users/authentik-automation[bot]

View File

@@ -27,6 +27,8 @@ jobs:
exempt-issue-labels: pinned,security,pr_wanted,enhancement,bug/confirmed,enhancement/confirmed,question,status/reviewing
stale-issue-label: status/stale
stale-issue-message: >
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Don't stale PRs, so only apply to PRs with a non-existent label
only-pr-labels: foo

2
.gitignore vendored
View File

@@ -14,8 +14,6 @@ media
# Node
node_modules
corepack.tgz
.corepack
.cspellcache
cspell-report.*

20
.npmrc
View File

@@ -1,20 +0,0 @@
# Block lifecycle scripts (preinstall/install/postinstall/prepare) from dependencies.
# This neutralizes the dominant npm supply-chain attack vector.
#
# Packages that legitimately need a build step (e.g. esbuild, chromedriver, tree-sitter)
# must be rebuilt explicitly:
#
# npm rebuild --foreground-scripts esbuild chromedriver tree-sitter tree-sitter-json
ignore-scripts=true
# Fail fast if the active Node/npm doesn't match the "engines" field.
engine-strict=true
# Pin exact versions so `npm install <pkg>` writes "1.2.3" not "^1.2.3".
save-exact=true
# Surface CVE warnings during install; doesn't block.
audit=true
# Suppress funding banners.
fund=false

View File

@@ -20,9 +20,6 @@
},
"todo-tree.tree.showCountsInTree": true,
"todo-tree.tree.showBadges": true,
"yaml.format.printWidth": 200,
"yaml.format.singleQuote": true,
"yaml.format.bracketSpacing": false,
"yaml.customTags": [
"!Condition sequence",
"!Context scalar",

View File

@@ -34,7 +34,6 @@ packages/django-channels-postgres @goauthentik/backend
packages/django-postgres-cache @goauthentik/backend
packages/django-dramatiq-postgres @goauthentik/backend
# Web packages
.npmrc @goauthentik/frontend
tsconfig.json @goauthentik/frontend
package.json @goauthentik/frontend
package-lock.json @goauthentik/frontend

28
Cargo.lock generated
View File

@@ -1744,6 +1744,16 @@ dependencies = [
"serde",
]
[[package]]
name = "iri-string"
version = "0.7.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8e7418f59cc01c88316161279a7f665217ae316b388e58a0d10e29f54f1e5eb"
dependencies = [
"memchr",
"serde",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
@@ -3193,9 +3203,9 @@ checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
[[package]]
name = "sentry"
version = "0.48.1"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b93b3e19f45495ddd41d8222a152c48c84f6ba45abe9c69e2527e9cdea29bb5b"
checksum = "e8ac94aab850a23d7507307cc505332ed2bafd36c65930dfc5c43610f9e9b477"
dependencies = [
"cfg_aliases",
"httpdate",
@@ -3390,9 +3400,9 @@ dependencies = [
[[package]]
name = "serde_with"
version = "3.19.0"
version = "3.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f05839ce67618e14a09b286535c0d9c94e85ef25469b0e13cb4f844e5593eb19"
checksum = "dd5414fad8e6907dbdd5bc441a50ae8d6e26151a03b1de04d89a5576de61d01f"
dependencies = [
"base64 0.22.1",
"chrono",
@@ -3924,9 +3934,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.52.3"
version = "1.52.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe"
checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6"
dependencies = [
"bytes",
"libc",
@@ -4072,21 +4082,21 @@ dependencies = [
[[package]]
name = "tower-http"
version = "0.6.10"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68d6fdd9f81c2819c9a8b0e0cd91660e7746a8e6ea2ba7c6b2b057985f6bcb51"
checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8"
dependencies = [
"bitflags 2.11.0",
"bytes",
"futures-util",
"http",
"http-body",
"iri-string",
"pin-project-lite",
"tokio",
"tower",
"tower-layer",
"tower-service",
"url",
]
[[package]]

View File

@@ -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.0", default-features = false, features = [
"backtrace",
"contexts",
"debug-images",
@@ -80,7 +80,7 @@ sentry = { version = "= 0.48.1", default-features = false, features = [
serde = { version = "= 1.0.228", features = ["derive"] }
serde_json = "= 1.0.149"
serde_repr = "= 0.1.20"
serde_with = { version = "= 3.19.0", default-features = false, features = [
serde_with = { version = "= 3.18.0", default-features = false, features = [
"base64",
] }
sqlx = { version = "= 0.8.6", default-features = false, features = [
@@ -97,12 +97,12 @@ sqlx = { version = "= 0.8.6", default-features = false, features = [
tempfile = "= 3.27.0"
thiserror = "= 2.0.18"
time = { version = "= 0.3.47", features = ["macros"] }
tokio = { version = "= 1.52.3", features = ["full", "tracing"] }
tokio = { version = "= 1.52.1", features = ["full", "tracing"] }
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.8", features = ["timeout"] }
tracing = "= 0.1.44"
tracing-error = "= 0.2.1"
tracing-subscriber = { version = "= 0.3.23", features = [

View File

@@ -106,9 +106,8 @@ migrate: ## Run the Authentik Django server's migrations
i18n-extract: core-i18n-extract web-i18n-extract ## Extract strings that require translation into files to send to a translation service
aws-cfn: node-install
corepack npm install --prefix lifecycle/aws
$(UV) run corepack npm run aws-cfn --prefix lifecycle/aws
aws-cfn:
cd lifecycle/aws && npm i && $(UV) run npm run aws-cfn
run: ## Run the main authentik server and worker processes
$(UV) run ak allinone
@@ -126,7 +125,7 @@ core-i18n-extract:
--ignore website \
-l en
install: node-install web-install core-install ## Install all requires dependencies for `node`, `web` and `core`
install: node-install docs-install core-install ## Install all requires dependencies for `node`, `docs` and `core`
dev-drop-db:
$(eval pg_user := $(shell $(UV) run python -m authentik.lib.config postgresql.user 2>/dev/null))
@@ -229,55 +228,39 @@ gen-dev-config: ## Generate a local development config file
## Node.js
#########################
# Packages whose install/postinstall scripts are required for correct
# operation (binary downloads, native bindings). The root .npmrc sets
# `ignore-scripts=true` to block dependency lifecycle scripts by default;
# this list is rebuilt explicitly with scripts re-enabled. Audit any
# additions: each entry runs arbitrary code at install time.
TRUSTED_INSTALL_SCRIPTS := esbuild chromedriver tree-sitter tree-sitter-json
node-preinstall: ## Install corepack and lint the runtime to ensure the correct Node.js version is being used before installing dependencies.
node ./scripts/node/setup-corepack.mjs
node ./scripts/node/lint-runtime.mjs
node-install: node-preinstall ## Install the necessary libraries to build Node.js packages
corepack npm ci
node-install: ## Install the necessary libraries to build Node.js packages
npm ci
npm ci --prefix web
#########################
## Web
#########################
web-install: ## Install the necessary libraries to build the Authentik UI
corepack npm ci --prefix web
web-postinstall: ## Trigger postinstall scripts for packages with native bindings or binary downloads, which are blocked by default for security reasons.
corepack npm rebuild --prefix web --ignore-scripts=false --foreground-scripts $(TRUSTED_INSTALL_SCRIPTS)
web-build: node-install ## Build the Authentik UI
corepack npm run --prefix web build
npm run --prefix web build
web: web-lint-fix web-lint web-check-compile ## Automatically fix formatting issues in the Authentik UI source code, lint the code, and compile it
web-test: ## Run tests for the Authentik UI
corepack npm run --prefix web test
npm run --prefix web test
web-watch: ## Build and watch the Authentik UI for changes, updating automatically
corepack npm run --prefix web watch
npm run --prefix web watch
web-storybook-watch: ## Build and run the storybook documentation server
corepack npm run --prefix web storybook
npm run --prefix web storybook
web-lint-fix:
corepack npm run --prefix web prettier
npm run --prefix web prettier
web-lint:
corepack npm run --prefix web lint
corepack npm run --prefix web lit-analyse
npm run --prefix web lint
npm run --prefix web lit-analyse
web-check-compile:
corepack npm run --prefix web tsc
npm run --prefix web tsc
web-i18n-extract:
corepack npm run --prefix web extract-locales
npm run --prefix web extract-locales
#########################
## Docs
@@ -285,36 +268,35 @@ web-i18n-extract:
docs: docs-lint-fix docs-build ## Automatically fix formatting issues in the Authentik docs source code, lint the code, and compile it
docs-install: node-install ## Install the necessary libraries to build the Authentik documentation
corepack npm ci --prefix website
docs-install:
npm ci --prefix website
docs-lint-fix: lint-spellcheck
corepack npm run --prefix website prettier
npm run --prefix website prettier
docs-build:
node ./scripts/node/lint-runtime.mjs website
corepack npm run --prefix website build
npm run --prefix website build
docs-watch: ## Build and watch the topics documentation
corepack npm run --prefix website start
npm run --prefix website start
integrations: docs-lint-fix integrations-build ## Fix formatting issues in the integrations source code, lint the code, and compile it
integrations-build:
corepack npm run --prefix website -w integrations build
npm run --prefix website -w integrations build
integrations-watch: ## Build and watch the Integrations documentation
corepack npm run --prefix website -w integrations start
npm run --prefix website -w integrations start
docs-api-build:
corepack npm run --prefix website -w api build
npm run --prefix website -w api build
docs-api-watch: ## Build and watch the API documentation
corepack npm run --prefix website -w api generate
corepack npm run --prefix website -w api start
npm run --prefix website -w api generate
npm run --prefix website -w api start
docs-api-clean: ## Clean generated API documentation
corepack npm run --prefix website -w api build:api:clean
npm run --prefix website -w api build:api:clean
#########################
## Docker

View File

@@ -42,29 +42,11 @@ def validate_auth(header: bytes, format="bearer") -> str | None:
return auth_credentials
class VirtualUser(AnonymousUser):
is_active = True
@property
def type(self):
return UserTypes.INTERNAL_SERVICE_ACCOUNT
@property
def is_anonymous(self):
return False
@property
def is_authenticated(self):
return True
def all_roles(self):
return []
class IPCUser(VirtualUser):
class IPCUser(AnonymousUser):
"""'Virtual' user for IPC communication between authentik core and the authentik router"""
username = "authentik:system"
is_active = True
is_superuser = True
@property
@@ -80,6 +62,17 @@ class IPCUser(VirtualUser):
def has_module_perms(self, module):
return True
@property
def is_anonymous(self):
return False
@property
def is_authenticated(self):
return True
def all_roles(self):
return []
class TokenAuthentication(BaseAuthentication):
"""Token-based authentication using HTTP Bearer authentication"""

View File

@@ -1,4 +1,4 @@
"""authentik SAML IDP Exceptions"""
"""Common SAML Exceptions"""
from authentik.lib.sentry import SentryIgnoredException

View File

@@ -0,0 +1,81 @@
"""LogoutResponse parser"""
import binascii
import zlib
from base64 import b64decode
from dataclasses import dataclass
from defusedxml import ElementTree
from authentik.common.saml.constants import NS_SAML_ASSERTION, NS_SAML_PROTOCOL, SAML_STATUS_SUCCESS
from authentik.common.saml.exceptions import CannotHandleAssertion
@dataclass(slots=True)
class LogoutResponse:
"""Logout Response"""
id: str | None = None
in_response_to: str | None = None
issuer: str | None = None
status: str | None = None
relay_state: str | None = None
class LogoutResponseParser:
"""LogoutResponse Parser"""
def _parse_xml(
self, decoded_xml: str | bytes, relay_state: str | None = None
) -> LogoutResponse:
root = ElementTree.fromstring(decoded_xml)
response = LogoutResponse(
id=root.attrib.get("ID"),
in_response_to=root.attrib.get("InResponseTo"),
)
# Extract Issuer
issuers = root.findall(f"{{{NS_SAML_ASSERTION}}}Issuer")
if not issuers:
issuers = root.findall(f"{{{NS_SAML_PROTOCOL}}}Issuer")
if len(issuers) > 0:
response.issuer = issuers[0].text
# Extract Status
status_elements = root.findall(f"{{{NS_SAML_PROTOCOL}}}Status")
if len(status_elements) > 0:
status_codes = status_elements[0].findall(f"{{{NS_SAML_PROTOCOL}}}StatusCode")
if len(status_codes) > 0:
response.status = status_codes[0].attrib.get("Value")
response.relay_state = relay_state
return response
def parse(self, saml_response: str, relay_state: str | None = None) -> LogoutResponse:
"""Validate and parse raw response with enveloped signature (POST binding)."""
try:
decoded_xml = b64decode(saml_response.encode())
except UnicodeDecodeError, binascii.Error:
raise CannotHandleAssertion("Cannot decode SAML response.") from None
return self._parse_xml(decoded_xml, relay_state)
def parse_detached(self, saml_response: str, relay_state: str | None = None) -> LogoutResponse:
"""Validate and parse raw response with detached signature (Redirect binding)."""
try:
decoded_data = b64decode(saml_response)
try:
decoded_xml = zlib.decompress(decoded_data, -15).decode("utf-8")
except zlib.error:
decoded_xml = decoded_data.decode("utf-8")
except UnicodeDecodeError, binascii.Error, zlib.error:
raise CannotHandleAssertion("Cannot decode SAML response.") from None
return self._parse_xml(decoded_xml, relay_state)
def verify_status(self, response: LogoutResponse):
"""Verify that the LogoutResponse has a successful status."""
if response.status != SAML_STATUS_SUCCESS:
raise CannotHandleAssertion(f"LogoutResponse status is not success: {response.status}")

View File

@@ -0,0 +1,93 @@
"""Shared SAML signature verification utilities"""
from base64 import b64decode
from urllib.parse import quote_plus
import xmlsec
from authentik.common.saml.constants import (
NS_MAP,
SIGN_ALGORITHM_TRANSFORM_MAP,
)
from authentik.common.saml.exceptions import CannotHandleAssertion
from authentik.lib.xml import lxml_from_string
def verify_enveloped_signature(raw_xml: bytes, verification_kp, xpath: str):
"""Verify an enveloped XML signature.
Args:
raw_xml: The raw XML bytes
verification_kp: CertificateKeyPair with certificate_data
xpath: XPath to signature node, e.g. '/samlp:LogoutRequest/ds:Signature'
"""
root = lxml_from_string(raw_xml)
xmlsec.tree.add_ids(root, ["ID"])
signature_nodes = root.xpath(xpath, namespaces=NS_MAP)
if len(signature_nodes) < 1:
raise CannotHandleAssertion(
"Verification Certificate configured, but message is not signed."
)
signature_node = signature_nodes[0]
try:
ctx = xmlsec.SignatureContext()
key = xmlsec.Key.from_memory(
verification_kp.certificate_data,
xmlsec.constants.KeyDataFormatCertPem,
None,
)
ctx.key = key
ctx.verify(signature_node)
except xmlsec.Error as exc:
raise CannotHandleAssertion("Failed to verify signature") from exc
def verify_detached_signature(
saml_param_name: str,
saml_value: str,
relay_state: str | None,
signature: str | None,
sig_alg: str | None,
verification_kp,
):
"""Verify a detached redirect-binding signature.
Args:
saml_param_name: "SAMLRequest" or "SAMLResponse"
saml_value: The raw base64+deflated SAML message value
relay_state: RelayState value, if present
signature: Base64-encoded signature from query params
sig_alg: Signature algorithm URI from query params
verification_kp: CertificateKeyPair with certificate_data
"""
if not (signature and sig_alg):
raise CannotHandleAssertion(
"Verification Certificate configured, but message is not signed."
)
querystring = f"{saml_param_name}={quote_plus(saml_value)}&"
if relay_state is not None:
querystring += f"RelayState={quote_plus(relay_state)}&"
querystring += f"SigAlg={quote_plus(sig_alg)}"
dsig_ctx = xmlsec.SignatureContext()
key = xmlsec.Key.from_memory(
verification_kp.certificate_data, xmlsec.constants.KeyDataFormatCertPem, None
)
dsig_ctx.key = key
sign_algorithm_transform = SIGN_ALGORITHM_TRANSFORM_MAP.get(
sig_alg, xmlsec.constants.TransformRsaSha1
)
try:
dsig_ctx.verify_binary(
querystring.encode("utf-8"),
sign_algorithm_transform,
b64decode(signature),
)
except xmlsec.Error as exc:
raise CannotHandleAssertion("Failed to verify signature") from exc

View File

@@ -246,25 +246,6 @@ class GroupSerializer(ModelSerializer):
)
return superuser
def validate_users(self, users: list) -> list:
"""Require add_user_to_group permission when adding new members via group PATCH."""
request: Request = self.context.get("request", None)
if not request:
return users
if not self.instance:
return users
# BulkManyRelatedField returns raw PKs, not model instances
current_user_pks = set(self.instance.users.values_list("pk", flat=True))
new_users = [u for u in users if u not in current_user_pks]
if not new_users:
return users
has_perm = request.user.has_perm(
"authentik_core.add_user_to_group"
) or request.user.has_perm("authentik_core.add_user_to_group", self.instance)
if not has_perm:
raise ValidationError(_("User does not have permission to add members to this group."))
return users
class Meta:
model = Group
fields = [

View File

@@ -297,36 +297,6 @@ class UserSerializer(ModelSerializer):
raise ValidationError(_("Setting a user to internal service account is not allowed."))
return user_type
def validate_groups(self, groups: list) -> list:
"""Require enable_group_superuser permission when adding a user to a superuser group."""
request: Request = self.context.get("request", None)
if not request:
return groups
current_groups = set(self.instance.groups.all()) if self.instance else set()
for group in groups:
if not group.is_superuser:
continue
if group in current_groups:
continue
if not request.user.has_perm("authentik_core.enable_group_superuser"):
raise ValidationError(
_("User does not have permission to add members to a superuser group.")
)
return groups
def validate_roles(self, roles: list) -> list:
"""Require change_role permission when assigning new roles to a user."""
request: Request = self.context.get("request", None)
if not request:
return roles
current_roles = set(self.instance.roles.all()) if self.instance else set()
new_roles = [r for r in roles if r not in current_roles]
if not new_roles:
return roles
if not request.user.has_perm("authentik_rbac.change_role"):
raise ValidationError(_("User does not have permission to assign roles."))
return roles
def validate(self, attrs: dict) -> dict:
if self.instance and self.instance.type == UserTypes.INTERNAL_SERVICE_ACCOUNT:
raise ValidationError(_("Can't modify internal service account users"))

View File

@@ -158,58 +158,3 @@ class TestGroupsAPI(APITestCase):
data={"name": generate_id(), "is_superuser": True},
)
self.assertEqual(res.status_code, 201)
def test_patch_users_no_perm(self):
"""PATCH group with new users without add_user_to_group must be rejected."""
group = Group.objects.create(name=generate_id())
self.login_user.assign_perms_to_managed_role("authentik_core.view_group", group)
self.login_user.assign_perms_to_managed_role("authentik_core.change_group", group)
self.client.force_login(self.login_user)
res = self.client.patch(
reverse("authentik_api:group-detail", kwargs={"pk": group.pk}),
data={"users": [self.user.pk]},
content_type="application/json",
)
self.assertEqual(res.status_code, 400)
def test_patch_users_with_global_perm(self):
"""PATCH group with new users with global add_user_to_group must succeed."""
group = Group.objects.create(name=generate_id())
self.login_user.assign_perms_to_managed_role("authentik_core.view_group", group)
self.login_user.assign_perms_to_managed_role("authentik_core.change_group", group)
self.login_user.assign_perms_to_managed_role("authentik_core.add_user_to_group")
self.client.force_login(self.login_user)
res = self.client.patch(
reverse("authentik_api:group-detail", kwargs={"pk": group.pk}),
data={"users": [self.user.pk]},
content_type="application/json",
)
self.assertEqual(res.status_code, 200)
def test_patch_users_with_obj_perm(self):
"""PATCH group with new users with object-level add_user_to_group must succeed."""
group = Group.objects.create(name=generate_id())
self.login_user.assign_perms_to_managed_role("authentik_core.view_group", group)
self.login_user.assign_perms_to_managed_role("authentik_core.change_group", group)
self.login_user.assign_perms_to_managed_role("authentik_core.add_user_to_group", group)
self.client.force_login(self.login_user)
res = self.client.patch(
reverse("authentik_api:group-detail", kwargs={"pk": group.pk}),
data={"users": [self.user.pk]},
content_type="application/json",
)
self.assertEqual(res.status_code, 200)
def test_patch_existing_users_no_perm(self):
"""PATCH group keeping existing membership without add_user_to_group must succeed."""
group = Group.objects.create(name=generate_id())
group.users.add(self.user)
self.login_user.assign_perms_to_managed_role("authentik_core.view_group", group)
self.login_user.assign_perms_to_managed_role("authentik_core.change_group", group)
self.client.force_login(self.login_user)
res = self.client.patch(
reverse("authentik_api:group-detail", kwargs={"pk": group.pk}),
data={"users": [self.user.pk]},
content_type="application/json",
)
self.assertEqual(res.status_code, 200)

View File

@@ -12,7 +12,6 @@ from authentik.brands.models import Brand
from authentik.core.models import (
USER_ATTRIBUTE_TOKEN_EXPIRING,
AuthenticatedSession,
Group,
Session,
Token,
User,
@@ -26,7 +25,6 @@ from authentik.core.tests.utils import (
)
from authentik.flows.models import FlowAuthenticationRequirement, FlowDesignation
from authentik.lib.generators import generate_id, generate_key
from authentik.rbac.models import Role
from authentik.stages.email.models import EmailStage
INVALID_PASSWORD_HASH = "not-a-valid-hash"
@@ -941,79 +939,3 @@ class TestUsersAPI(APITestCase):
self.assertIn(user2.pk, pks)
# Verify user2 comes before user1 in descending order
self.assertLess(pks.index(user2.pk), pks.index(user1.pk))
class TestUsersAPIGroupRoleValidation(APITestCase):
"""Test that PATCH /api/v3/core/users/{pk}/ enforces group and role permission checks."""
def setUp(self) -> None:
self.actor = create_test_user()
self.target = create_test_user()
def _patch(self, data: dict):
self.client.force_login(self.actor)
return self.client.patch(
reverse("authentik_api:user-detail", kwargs={"pk": self.target.pk}),
data=data,
content_type="application/json",
)
def test_patch_superuser_group_no_perm(self):
"""Assigning a superuser group without enable_group_superuser must be rejected."""
self.actor.assign_perms_to_managed_role("authentik_core.view_user")
self.actor.assign_perms_to_managed_role("authentik_core.change_user", self.target)
group = Group.objects.create(name=generate_id(), is_superuser=True)
res = self._patch({"groups": [str(group.pk)]})
self.assertEqual(res.status_code, 400)
def test_patch_superuser_group_with_perm(self):
"""Assigning a superuser group with enable_group_superuser must succeed."""
self.actor.assign_perms_to_managed_role("authentik_core.view_user")
self.actor.assign_perms_to_managed_role("authentik_core.change_user", self.target)
self.actor.assign_perms_to_managed_role("authentik_core.enable_group_superuser")
group = Group.objects.create(name=generate_id(), is_superuser=True)
res = self._patch({"groups": [str(group.pk)]})
self.assertEqual(res.status_code, 200)
def test_patch_non_superuser_group_no_perm(self):
"""Assigning a non-superuser group without special permission must succeed."""
self.actor.assign_perms_to_managed_role("authentik_core.view_user")
self.actor.assign_perms_to_managed_role("authentik_core.change_user", self.target)
group = Group.objects.create(name=generate_id(), is_superuser=False)
res = self._patch({"groups": [str(group.pk)]})
self.assertEqual(res.status_code, 200)
def test_patch_existing_superuser_group_no_perm(self):
"""Keeping an existing superuser group membership without the permission must succeed."""
self.actor.assign_perms_to_managed_role("authentik_core.view_user")
self.actor.assign_perms_to_managed_role("authentik_core.change_user", self.target)
group = Group.objects.create(name=generate_id(), is_superuser=True)
self.target.groups.add(group)
res = self._patch({"groups": [str(group.pk)]})
self.assertEqual(res.status_code, 200)
def test_patch_role_no_perm(self):
"""Assigning a new role without change_role must be rejected."""
self.actor.assign_perms_to_managed_role("authentik_core.view_user")
self.actor.assign_perms_to_managed_role("authentik_core.change_user", self.target)
role = Role.objects.create(name=generate_id())
res = self._patch({"roles": [str(role.pk)]})
self.assertEqual(res.status_code, 400)
def test_patch_role_with_perm(self):
"""Assigning a new role with change_role must succeed."""
self.actor.assign_perms_to_managed_role("authentik_core.view_user")
self.actor.assign_perms_to_managed_role("authentik_core.change_user", self.target)
self.actor.assign_perms_to_managed_role("authentik_rbac.change_role")
role = Role.objects.create(name=generate_id())
res = self._patch({"roles": [str(role.pk)]})
self.assertEqual(res.status_code, 200)
def test_patch_existing_role_no_perm(self):
"""Keeping an existing role without change_role must succeed."""
self.actor.assign_perms_to_managed_role("authentik_core.view_user")
self.actor.assign_perms_to_managed_role("authentik_core.change_user", self.target)
role = Role.objects.create(name=generate_id())
self.target.roles.add(role)
res = self._patch({"roles": [str(role.pk)]})
self.assertEqual(res.status_code, 200)

View File

@@ -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
@@ -44,6 +44,7 @@ from authentik.stages.password.stage import PLAN_CONTEXT_METHOD, PLAN_CONTEXT_ME
class AgentConnectorSerializer(ConnectorSerializer):
class Meta(ConnectorSerializer.Meta):
model = AgentConnector
fields = ConnectorSerializer.Meta.fields + [
@@ -62,6 +63,7 @@ class AgentConnectorSerializer(ConnectorSerializer):
class MDMConfigSerializer(PassiveSerializer):
platform = ChoiceField(choices=OSFamily.choices)
enrollment_token = PrimaryKeyRelatedField(
queryset=EnrollmentToken.objects.including_expired().all()
@@ -87,6 +89,7 @@ class AgentConnectorViewSet(
UsedByMixin,
ModelViewSet,
):
queryset = AgentConnector.objects.all()
serializer_class = AgentConnectorSerializer
search_fields = ["name"]
@@ -118,8 +121,6 @@ class AgentConnectorViewSet(
methods=["POST"],
detail=False,
authentication_classes=[AgentEnrollmentAuth],
# Permissions are handled via AgentEnrollmentAuth
permission_classes=[AllowAny],
)
def enroll(self, request: Request):
token: EnrollmentToken = request.auth
@@ -150,13 +151,7 @@ class AgentConnectorViewSet(
request=OpenApiTypes.NONE,
responses=AgentConfigSerializer(),
)
@action(
methods=["GET"],
detail=False,
authentication_classes=[AgentAuth],
# Permissions are handled via AgentAuth
permission_classes=[AllowAny],
)
@action(methods=["GET"], detail=False, authentication_classes=[AgentAuth])
def agent_config(self, request: Request):
token: DeviceToken = request.auth
connector: AgentConnector = token.device.connector.agentconnector
@@ -170,13 +165,7 @@ class AgentConnectorViewSet(
request=DeviceFacts(),
responses={204: OpenApiResponse(description="Successfully checked in")},
)
@action(
methods=["POST"],
detail=False,
authentication_classes=[AgentAuth],
# Permissions are handled via AgentAuth
permission_classes=[AllowAny],
)
@action(methods=["POST"], detail=False, authentication_classes=[AgentAuth])
def check_in(self, request: Request):
token: DeviceToken = request.auth
data = DeviceFacts(data=request.data)

View File

@@ -1,6 +1,5 @@
from typing import Any
from django.db.models import Model
from django.http import HttpRequest
from django.utils.timezone import now
from drf_spectacular.extensions import OpenApiAuthenticationExtension
@@ -10,7 +9,7 @@ from rest_framework.exceptions import PermissionDenied
from rest_framework.request import Request
from structlog.stdlib import get_logger
from authentik.api.authentication import VirtualUser, validate_auth
from authentik.api.authentication import IPCUser, validate_auth
from authentik.core.middleware import CTX_AUTH_VIA
from authentik.core.models import User
from authentik.crypto.apps import MANAGED_KEY
@@ -26,18 +25,9 @@ LOGGER = get_logger()
PLATFORM_ISSUER = "goauthentik.io/platform"
class DeviceUser(VirtualUser):
class DeviceUser(IPCUser):
username = "authentik:endpoints:device"
def has_perm(self, perm: str, obj: Model | None = None) -> bool:
if perm in [
"authentik_core.view_user",
"authentik_core.view_group",
]:
return True
return False
class AgentEnrollmentAuth(BaseAuthentication):

View File

@@ -223,17 +223,3 @@ class TestAgentAPI(APITestCase):
data={"platform": OSFamily.macOS, "enrollment_token": self.token.pk},
)
self.assertEqual(res.status_code, 200)
def test_users_list(self):
response = self.client.get(
reverse("authentik_api:user-list"),
HTTP_AUTHORIZATION=f"Bearer+agent {self.device_token.key}",
)
self.assertEqual(response.status_code, 200)
def test_other_api_forbidden(self):
response = self.client.get(
reverse("authentik_api:application-list"),
HTTP_AUTHORIZATION=f"Bearer+agent {self.device_token.key}",
)
self.assertEqual(response.status_code, 403)

View File

@@ -2,7 +2,6 @@ from django.urls import reverse
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema
from rest_framework.decorators import action
from rest_framework.permissions import AllowAny
from rest_framework.request import Request
from rest_framework.response import Response
from structlog.stdlib import get_logger
@@ -26,13 +25,7 @@ class AgentConnectorViewSetMixin:
request=OpenApiTypes.NONE,
responses=AgentAuthenticationResponse(),
)
@action(
methods=["POST"],
detail=False,
authentication_classes=[AgentAuth],
# Permissions are handled via AgentAuth
permission_classes=[AllowAny],
)
@action(methods=["POST"], detail=False, authentication_classes=[AgentAuth])
@enterprise_action
def auth_ia(self, request: Request) -> Response:
token: DeviceToken = request.auth

View File

@@ -1,72 +1,14 @@
from datetime import datetime
from django.urls import reverse
from django.utils.translation import gettext as _
from rest_framework.exceptions import ValidationError
from authentik.enterprise.license import LicenseKey
from authentik.providers.scim.models import SCIMAuthenticationMode, SCIMProvider
from authentik.sources.oauth.models import UserOAuthSourceConnection
from authentik.providers.scim.models import SCIMAuthenticationMode
class SCIMProviderSerializerMixin:
def _get_token(self, instance: SCIMProvider) -> UserOAuthSourceConnection | None:
user = instance.auth_oauth_user
conn = UserOAuthSourceConnection.objects.filter(
user=user, source=instance.auth_oauth
).first()
return conn
def get_auth_oauth_token_last_updated(self, instance: SCIMProvider) -> datetime | None:
conn = self._get_token(instance)
return conn.last_updated if conn else None
def get_auth_oauth_token_expires(self, instance: SCIMProvider) -> datetime | None:
conn = self._get_token(instance)
return conn.expires if conn else None
def get_auth_oauth_url_callback(self, instance: SCIMProvider) -> str | None:
if (
instance.auth_mode
in [
SCIMAuthenticationMode.TOKEN,
SCIMAuthenticationMode.OAUTH_SILENT,
]
or not instance.backchannel_application
):
return None
relative_url = reverse(
"authentik_enterprise_providers_scim:callback",
kwargs={"application_slug": instance.backchannel_application.slug},
)
if "request" not in self.context:
return relative_url
return self.context["request"].build_absolute_uri(relative_url)
def get_auth_oauth_url_start(self, instance: SCIMProvider) -> str | None:
if (
instance.auth_mode
in [
SCIMAuthenticationMode.TOKEN,
SCIMAuthenticationMode.OAUTH_SILENT,
]
or not instance.backchannel_application
):
return None
relative_url = reverse(
"authentik_enterprise_providers_scim:start",
kwargs={"application_slug": instance.backchannel_application.slug},
)
if "request" not in self.context:
return relative_url
return self.context["request"].build_absolute_uri(relative_url)
def validate_auth_mode(self, auth_mode: SCIMAuthenticationMode) -> SCIMAuthenticationMode:
if auth_mode in [
SCIMAuthenticationMode.OAUTH_SILENT,
SCIMAuthenticationMode.OAUTH_INTERACTIVE,
]:
if auth_mode == SCIMAuthenticationMode.OAUTH:
if not LicenseKey.cached_summary().status.is_valid:
raise ValidationError(_("Enterprise is required to use the OAuth mode."))
return auth_mode

View File

@@ -7,4 +7,3 @@ class AuthentikEnterpriseProviderSCIMConfig(EnterpriseConfig):
label = "authentik_enterprise_providers_scim"
verbose_name = "authentik Enterprise.Providers.SCIM"
default = True
mountpoint = "application/scim/"

View File

@@ -1,14 +1,12 @@
from datetime import timedelta
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING
from django.utils.timezone import now
from requests import Request, RequestException
from structlog.stdlib import get_logger
from authentik.common.oauth.constants import GRANT_TYPE_PASSWORD, GRANT_TYPE_REFRESH_TOKEN
from authentik.providers.scim.clients.exceptions import SCIMRequestException
from authentik.providers.scim.models import SCIMAuthenticationMode
from authentik.sources.oauth.clients.base import BaseOAuthClient
from authentik.sources.oauth.clients.oauth2 import OAuth2Client
from authentik.sources.oauth.models import OAuthSource, UserOAuthSourceConnection
if TYPE_CHECKING:
@@ -20,26 +18,23 @@ class SCIMOAuthException(SCIMRequestException):
class SCIMOAuthAuth:
def __init__(self, provider: SCIMProvider):
self.provider = provider
self.user = provider.auth_oauth_user
self.logger = get_logger().bind()
self.connection = self.get_connection()
def retrieve_token(self, conn: UserOAuthSourceConnection | None) -> dict[str, Any]:
def retrieve_token(self):
if not self.provider.auth_oauth:
return None
source: OAuthSource = self.provider.auth_oauth
client: BaseOAuthClient = source.source_type.callback_view(request=None).get_client(source)
client = OAuth2Client(source, None)
access_token_url = source.source_type.access_token_url or ""
if source.source_type.urls_customizable and source.access_token_url:
access_token_url = source.access_token_url
data = client.get_access_token_args(None, None)
if self.provider.auth_mode == SCIMAuthenticationMode.OAUTH_SILENT:
data["grant_type"] = GRANT_TYPE_PASSWORD
elif self.provider.auth_mode == SCIMAuthenticationMode.OAUTH_INTERACTIVE:
data["grant_type"] = GRANT_TYPE_REFRESH_TOKEN
if not conn:
raise SCIMOAuthException(None, "Could not refresh SCIM OAuth token")
data["refresh_token"] = conn.refresh_token
data["grant_type"] = "password"
data.update(self.provider.auth_oauth_params)
try:
response = client.do_request(
@@ -59,14 +54,12 @@ class SCIMOAuthAuth:
raise SCIMOAuthException(exc.response, message="Failed to get OAuth token") from exc
def get_connection(self):
if not self.provider.auth_oauth:
return None
conn = UserOAuthSourceConnection.objects.filter(
source=self.provider.auth_oauth, user=self.user
token = UserOAuthSourceConnection.objects.filter(
source=self.provider.auth_oauth, user=self.user, expires__gt=now()
).first()
if conn and conn.access_token and conn.expires > now():
return conn
token = self.retrieve_token(conn)
if token and token.access_token:
return token
token = self.retrieve_token()
access_token = token["access_token"]
expires_in = int(token.get("expires_in", 0))
token, _ = UserOAuthSourceConnection.objects.update_or_create(
@@ -74,10 +67,7 @@ class SCIMOAuthAuth:
user=self.user,
defaults={
"access_token": access_token,
"refresh_token": token.get("refresh_token"),
"expires": now() + timedelta(seconds=expires_in),
# When using `update_or_create`, `last_updated` is not updated
"last_updated": now(),
},
)
return token

View File

@@ -14,10 +14,7 @@ def scim_provider_post_save(sender: type[Model], instance: SCIMProvider, created
"""Create service account before provider is saved"""
identifier = f"ak-providers-scim-{instance.pk}"
with audit_ignore():
if instance.auth_mode in [
SCIMAuthenticationMode.OAUTH_SILENT,
SCIMAuthenticationMode.OAUTH_INTERACTIVE,
]:
if instance.auth_mode == SCIMAuthenticationMode.OAUTH:
user, user_created = User.objects.update_or_create(
username=identifier,
defaults={

View File

@@ -2,7 +2,7 @@
from base64 import b64encode
from datetime import timedelta
from urllib.parse import parse_qs, urlencode, urlparse
from unittest.mock import MagicMock, PropertyMock, patch
from django.urls import reverse
from django.utils.timezone import now
@@ -11,14 +11,17 @@ from rest_framework.test import APITestCase
from authentik.blueprints.tests import apply_blueprint
from authentik.core.models import Application, Group, User
from authentik.core.tests.utils import create_test_admin_user
from authentik.enterprise.license import LicenseKey
from authentik.enterprise.models import License
from authentik.enterprise.tests.test_license import expiry_valid
from authentik.lib.generators import generate_id
from authentik.providers.scim.models import SCIMAuthenticationMode, SCIMMapping, SCIMProvider
from authentik.sources.oauth.models import OAuthSource, UserOAuthSourceConnection
from authentik.tenants.models import Tenant
from tests.live import create_test_admin_user
class TestSCIMOAuthToken(APITestCase):
class SCIMOAuthTests(APITestCase):
"""SCIM User tests"""
@apply_blueprint("system/providers-scim.yaml")
@@ -39,7 +42,7 @@ class TestSCIMOAuthToken(APITestCase):
self.provider = SCIMProvider.objects.create(
name=generate_id(),
url="https://localhost",
auth_mode=SCIMAuthenticationMode.OAUTH_SILENT,
auth_mode=SCIMAuthenticationMode.OAUTH,
auth_oauth=self.source,
auth_oauth_params={
"foo": "bar",
@@ -57,9 +60,8 @@ class TestSCIMOAuthToken(APITestCase):
self.provider.property_mappings_group.add(
SCIMMapping.objects.get(managed="goauthentik.io/providers/scim/group")
)
self.admin = create_test_admin_user()
def test_retrieve_token_silent(self):
def test_retrieve_token(self):
"""Test token retrieval"""
with Mocker() as mocker:
token = generate_id()
@@ -84,44 +86,6 @@ class TestSCIMOAuthToken(APITestCase):
)
self.assertEqual(mocker.request_history[0].body, "grant_type=password&foo=bar")
def test_retrieve_token_interactive(self):
"""Test token retrieval"""
self.provider.auth_mode = SCIMAuthenticationMode.OAUTH_INTERACTIVE
self.provider.save()
refresh_token = generate_id()
access_token = generate_id()
UserOAuthSourceConnection.objects.create(
user=self.provider.auth_oauth_user,
source=self.source,
refresh_token=refresh_token,
access_token=access_token,
)
with Mocker() as mocker:
token = generate_id()
mocker.post("http://localhost/token", json={"access_token": token, "expires_in": 3600})
self.provider.scim_auth()
conn = UserOAuthSourceConnection.objects.filter(
source=self.source,
user=self.provider.auth_oauth_user,
).first()
self.assertIsNotNone(conn)
self.assertTrue(conn.is_valid)
auth = (
b64encode(
b":".join((self.source.consumer_key.encode(), self.source.consumer_secret.encode()))
)
.strip()
.decode()
)
self.assertEqual(
mocker.request_history[0].headers["Authorization"],
f"Basic {auth}",
)
self.assertEqual(
mocker.request_history[0].body,
f"grant_type=refresh_token&refresh_token={refresh_token}&foo=bar",
)
def test_existing_token(self):
"""Test existing token"""
UserOAuthSourceConnection.objects.create(
@@ -134,54 +98,96 @@ class TestSCIMOAuthToken(APITestCase):
self.provider.scim_auth()
self.assertEqual(len(mocker.request_history), 0)
def test_interactive_start(self):
self.client.force_login(self.admin)
res = self.client.get(
reverse(
"authentik_enterprise_providers_scim:start",
kwargs={
"application_slug": self.app.slug,
@Mocker()
def test_user_create(self, mock: Mocker):
"""Test user creation"""
scim_id = generate_id()
token = generate_id()
mock.post("http://localhost/token", json={"access_token": token, "expires_in": 3600})
mock.get(
"https://localhost/ServiceProviderConfig",
json={},
)
mock.post(
"https://localhost/Users",
json={
"id": scim_id,
},
)
uid = generate_id()
user = User.objects.create(
username=uid,
name=f"{uid} {uid}",
email=f"{uid}@goauthentik.io",
)
self.assertEqual(mock.call_count, 3)
self.assertEqual(mock.request_history[1].method, "GET")
self.assertEqual(mock.request_history[2].method, "POST")
self.assertJSONEqual(
mock.request_history[2].body,
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"active": True,
"emails": [
{
"primary": True,
"type": "other",
"value": f"{uid}@goauthentik.io",
}
],
"externalId": user.uid,
"name": {
"familyName": uid,
"formatted": f"{uid} {uid}",
"givenName": uid,
},
)
"displayName": f"{uid} {uid}",
"userName": uid,
},
)
self.assertEqual(res.status_code, 302)
query = parse_qs(urlparse(res.url).query)
self.assertEqual(query["client_id"], [self.source.consumer_key])
self.assertEqual(
query["redirect_uri"],
[f"http://testserver/application/scim/{self.app.slug}/oauth2/callback/"],
)
self.assertEqual(query["response_type"], ["code"])
def test_interactive_callback(self):
self.client.force_login(self.admin)
res = self.client.get(
reverse(
"authentik_enterprise_providers_scim:start",
kwargs={
"application_slug": self.app.slug,
},
@patch(
"authentik.enterprise.license.LicenseKey.validate",
MagicMock(
return_value=LicenseKey(
aud="",
exp=expiry_valid,
name=generate_id(),
internal_users=100,
external_users=100,
)
),
)
def test_api_create(self):
License.objects.create(key=generate_id())
self.client.force_login(create_test_admin_user())
res = self.client.post(
reverse("authentik_api:scimprovider-list"),
{
"name": generate_id(),
"url": "http://localhost",
"auth_mode": "oauth",
"auth_oauth": str(self.source.pk),
},
)
self.assertEqual(res.status_code, 302)
query = parse_qs(urlparse(res.url).query)
self.assertEqual(res.status_code, 201)
with Mocker() as mock:
token = generate_id()
mock.post("http://localhost/token", json={"access_token": token, "expires_in": 3600})
res = self.client.get(
reverse(
"authentik_enterprise_providers_scim:callback",
kwargs={
"application_slug": self.app.slug,
},
)
+ "?"
+ urlencode({"state": query["state"][0], "code": generate_id()})
)
self.assertEqual(res.status_code, 302)
conn = UserOAuthSourceConnection.objects.filter(source=self.source).first()
self.assertIsNotNone(conn)
self.assertTrue(conn.is_valid)
@patch(
"authentik.enterprise.models.LicenseUsageStatus.is_valid",
PropertyMock(return_value=False),
)
def test_api_create_no_license(self):
self.client.force_login(create_test_admin_user())
res = self.client.post(
reverse("authentik_api:scimprovider-list"),
{
"name": generate_id(),
"url": "http://localhost",
"auth_mode": "oauth",
"auth_oauth": str(self.source.pk),
},
)
self.assertEqual(res.status_code, 400)
self.assertJSONEqual(
res.content, {"auth_mode": ["Enterprise is required to use the OAuth mode."]}
)

View File

@@ -1,73 +0,0 @@
"""SCIM OAuth tests"""
from unittest.mock import MagicMock, PropertyMock, patch
from django.urls import reverse
from rest_framework.test import APITestCase
from authentik.core.tests.utils import create_test_admin_user
from authentik.enterprise.license import LicenseKey
from authentik.enterprise.models import License
from authentik.enterprise.tests.test_license import expiry_valid
from authentik.lib.generators import generate_id
from authentik.sources.oauth.models import OAuthSource
class TestSCIMOAuthAPI(APITestCase):
"""SCIM User tests"""
def setUp(self):
self.source = OAuthSource.objects.create(
name=generate_id(),
slug=generate_id(),
access_token_url="http://localhost/token", # nosec
consumer_key=generate_id(),
consumer_secret=generate_id(),
provider_type="openidconnect",
)
@patch(
"authentik.enterprise.license.LicenseKey.validate",
MagicMock(
return_value=LicenseKey(
aud="",
exp=expiry_valid,
name=generate_id(),
internal_users=100,
external_users=100,
)
),
)
def test_api_create(self):
License.objects.create(key=generate_id())
self.client.force_login(create_test_admin_user())
res = self.client.post(
reverse("authentik_api:scimprovider-list"),
{
"name": generate_id(),
"url": "http://localhost",
"auth_mode": "oauth",
"auth_oauth": str(self.source.pk),
},
)
self.assertEqual(res.status_code, 201)
@patch(
"authentik.enterprise.models.LicenseUsageStatus.is_valid",
PropertyMock(return_value=False),
)
def test_api_create_no_license(self):
self.client.force_login(create_test_admin_user())
res = self.client.post(
reverse("authentik_api:scimprovider-list"),
{
"name": generate_id(),
"url": "http://localhost",
"auth_mode": "oauth",
"auth_oauth": str(self.source.pk),
},
)
self.assertEqual(res.status_code, 400)
self.assertJSONEqual(
res.content, {"auth_mode": ["Enterprise is required to use the OAuth mode."]}
)

View File

@@ -1,100 +0,0 @@
"""SCIM OAuth tests"""
from requests_mock import Mocker
from rest_framework.test import APITestCase
from authentik.blueprints.tests import apply_blueprint
from authentik.core.models import Application, Group, User
from authentik.lib.generators import generate_id
from authentik.providers.scim.models import SCIMAuthenticationMode, SCIMMapping, SCIMProvider
from authentik.sources.oauth.models import OAuthSource
from authentik.tenants.models import Tenant
class TestSCIMOAuthAuth(APITestCase):
"""SCIM User tests"""
@apply_blueprint("system/providers-scim.yaml")
def setUp(self) -> None:
# Delete all users and groups as the mocked HTTP responses only return one ID
# which will cause errors with multiple users
Tenant.objects.update(avatars="none")
User.objects.all().exclude_anonymous().delete()
Group.objects.all().delete()
self.source = OAuthSource.objects.create(
name=generate_id(),
slug=generate_id(),
access_token_url="http://localhost/token", # nosec
consumer_key=generate_id(),
consumer_secret=generate_id(),
provider_type="openidconnect",
)
self.provider = SCIMProvider.objects.create(
name=generate_id(),
url="https://localhost",
auth_mode=SCIMAuthenticationMode.OAUTH_SILENT,
auth_oauth=self.source,
auth_oauth_params={
"foo": "bar",
},
exclude_users_service_account=True,
)
self.app: Application = Application.objects.create(
name=generate_id(),
slug=generate_id(),
)
self.app.backchannel_providers.add(self.provider)
self.provider.property_mappings.add(
SCIMMapping.objects.get(managed="goauthentik.io/providers/scim/user")
)
self.provider.property_mappings_group.add(
SCIMMapping.objects.get(managed="goauthentik.io/providers/scim/group")
)
@Mocker()
def test_user_create(self, mock: Mocker):
"""Test user creation"""
scim_id = generate_id()
token = generate_id()
mock.post("http://localhost/token", json={"access_token": token, "expires_in": 3600})
mock.get(
"https://localhost/ServiceProviderConfig",
json={},
)
mock.post(
"https://localhost/Users",
json={
"id": scim_id,
},
)
uid = generate_id()
user = User.objects.create(
username=uid,
name=f"{uid} {uid}",
email=f"{uid}@goauthentik.io",
)
self.assertEqual(mock.call_count, 3)
self.assertEqual(mock.request_history[1].method, "GET")
self.assertEqual(mock.request_history[2].method, "POST")
self.assertJSONEqual(
mock.request_history[2].body,
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"active": True,
"emails": [
{
"primary": True,
"type": "other",
"value": f"{uid}@goauthentik.io",
}
],
"externalId": user.uid,
"name": {
"familyName": uid,
"formatted": f"{uid} {uid}",
"givenName": uid,
},
"displayName": f"{uid} {uid}",
"userName": uid,
},
)

View File

@@ -1,10 +0,0 @@
from django.urls import path
from authentik.enterprise.providers.scim.views import SCIMOAuthStart, SCIMRedirectCallback
urlpatterns = [
path("<slug:application_slug>/oauth2/start/", SCIMOAuthStart.as_view(), name="start"),
path(
"<slug:application_slug>/oauth2/callback/", SCIMRedirectCallback.as_view(), name="callback"
),
]

View File

@@ -1,70 +0,0 @@
from datetime import timedelta
from django.core.exceptions import PermissionDenied
from django.http import HttpRequest
from django.shortcuts import redirect
from django.urls import reverse
from django.utils.timezone import now
from authentik.core.models import Application
from authentik.providers.scim.models import SCIMProvider
from authentik.sources.oauth.clients.base import BaseOAuthClient
from authentik.sources.oauth.models import OAuthSource, UserOAuthSourceConnection
from authentik.sources.oauth.types.registry import RequestKind, registry
from authentik.sources.oauth.views.callback import OAuthCallback
from authentik.sources.oauth.views.redirect import OAuthRedirect
class SCIMOAuthViewMixin:
provider: SCIMProvider
def get_client(self, source: OAuthSource, **kwargs) -> BaseOAuthClient:
source: OAuthSource = self.provider.auth_oauth
source_cls = registry.find(source.provider_type, kind=RequestKind.CALLBACK)
if not source_cls.client_class:
return super().get_client(source, **kwargs)
return source_cls.client_class(source, self.request, **kwargs)
def _get_scim_provider(self, app_slug: str):
app = Application.objects.filter(slug=app_slug).first()
if not app:
return None
provider = SCIMProvider.objects.filter(backchannel_application=app)
return provider.first()
def dispatch(self, request: HttpRequest, application_slug: str):
if not request.user.is_authenticated:
raise PermissionDenied()
provider = self._get_scim_provider(application_slug)
if not provider or not provider.auth_oauth:
raise PermissionDenied()
if not request.user.has_perm(
"authentik_providers_scim.change_scimprovider",
provider,
):
raise PermissionDenied()
self.provider = provider
return super().dispatch(request, source_slug=provider.auth_oauth.slug)
class SCIMOAuthStart(SCIMOAuthViewMixin, OAuthRedirect):
def get_callback_url(self, source: OAuthSource):
return reverse("authentik_enterprise_providers_scim:callback", kwargs=self.kwargs)
class SCIMRedirectCallback(SCIMOAuthViewMixin, OAuthCallback):
def redirect_flow_manager(self, client: BaseOAuthClient):
expires_in = int(self.token.get("expires_in", 0))
UserOAuthSourceConnection.objects.update_or_create(
source=self.provider.auth_oauth,
user=self.provider.auth_oauth_user,
defaults={
"access_token": self.token.get("access_token"),
"refresh_token": self.token.get("refresh_token"),
"expires": now() + timedelta(seconds=expires_in),
},
)
return redirect("authentik_core:if-admin")

View File

@@ -1,5 +1,4 @@
from dataclasses import dataclass
from urllib.parse import urlparse
from django.http import HttpRequest
from django.shortcuts import get_object_or_404
@@ -56,9 +55,7 @@ class SignInRequest:
_, provider = req.get_app_provider()
if not req.wreply:
req.wreply = provider.acs_url
reply = urlparse(req.wreply)
configured = urlparse(provider.acs_url)
if not (reply[:2] == configured[:2] and reply.path.startswith(configured.path)):
if not req.wreply.startswith(provider.acs_url):
raise ValueError("Invalid wreply")
return req

View File

@@ -1,5 +1,4 @@
from dataclasses import dataclass
from urllib.parse import urlparse
from django.http import HttpRequest
from django.shortcuts import get_object_or_404
@@ -33,9 +32,7 @@ class SignOutRequest:
_, provider = req.get_app_provider()
if not req.wreply:
req.wreply = provider.acs_url
reply = urlparse(req.wreply)
configured = urlparse(provider.acs_url)
if not (reply[:2] == configured[:2] and reply.path.startswith(configured.path)):
if not req.wreply.startswith(provider.acs_url):
raise ValueError("Invalid wreply")
return req

View File

@@ -27,27 +27,12 @@ class TestWSFedSignIn(TestCase):
name=generate_id(),
authorization_flow=self.flow,
signing_kp=self.cert,
acs_url="https://t.goauthentik.io",
audience="foo",
)
self.app = Application.objects.create(
name=generate_id(), slug=generate_id(), provider=self.provider
)
self.factory = RequestFactory()
def test_wreply(self):
request = self.factory.get(
"/?wreply=https://t.goauthentik.io/foo&wa=wsignin1.0&wtrealm=foo",
user=get_anonymous_user(),
)
SignInRequest.parse(request)
with self.assertRaises(ValueError):
request = self.factory.get(
"/?wreply=https://t.goauthentik.io.invalid.com&wa=wsignin1.0&wtrealm=foo",
user=get_anonymous_user(),
)
SignInRequest.parse(request)
def test_token_gen(self):
request = self.factory.get("/", user=get_anonymous_user())
proc = SignInProcessor(

View File

@@ -9,10 +9,10 @@ from rest_framework.fields import CharField, ListField, SerializerMethodField
from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework.viewsets import GenericViewSet
from authentik.core.api.providers import ProviderSerializer
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.users import UserSerializer
from authentik.core.api.utils import MetaNameSerializer, ModelSerializer
from authentik.providers.oauth2.api.providers import OAuth2ProviderSerializer
from authentik.providers.oauth2.models import AccessToken, AuthorizationCode, RefreshToken
@@ -20,7 +20,7 @@ class ExpiringBaseGrantModelSerializer(ModelSerializer, MetaNameSerializer):
"""Serializer for BaseGrantModel and ExpiringBaseGrant"""
user = UserSerializer()
provider = ProviderSerializer()
provider = OAuth2ProviderSerializer()
scope = ListField(child=CharField())
class Meta:

View File

@@ -19,18 +19,12 @@ from authentik.common.saml.constants import (
RSA_SHA512,
SAML_NAME_ID_FORMAT_UNSPECIFIED,
)
from authentik.common.saml.exceptions import CannotHandleAssertion
from authentik.lib.xml import lxml_from_string
from authentik.providers.saml.exceptions import CannotHandleAssertion
from authentik.providers.saml.models import SAMLProvider
from authentik.providers.saml.utils.encoding import decode_base64_and_inflate
from authentik.sources.saml.models import SAMLNameIDPolicy
ERROR_CANNOT_DECODE_REQUEST = "Cannot decode SAML request."
ERROR_SIGNATURE_REQUIRED_BUT_ABSENT = (
"Verification Certificate configured, but request is not signed."
)
ERROR_FAILED_TO_VERIFY = "Failed to verify signature"
@dataclass(slots=True)
class AuthNRequest:
@@ -88,7 +82,7 @@ class AuthNRequestParser:
try:
decoded_xml = b64decode(saml_request.encode())
except UnicodeDecodeError:
raise CannotHandleAssertion(ERROR_CANNOT_DECODE_REQUEST) from None
raise CannotHandleAssertion("Cannot decode SAML request.") from None
verifier = self.provider.verification_kp
if not verifier:
@@ -99,7 +93,9 @@ class AuthNRequestParser:
signature_nodes = root.xpath("/samlp:AuthnRequest/ds:Signature", namespaces=NS_MAP)
# No signatures, no verifier configured -> decode xml directly
if len(signature_nodes) < 1:
raise CannotHandleAssertion(ERROR_SIGNATURE_REQUIRED_BUT_ABSENT)
raise CannotHandleAssertion(
"Verification Certificate configured, but request is not signed."
)
signature_node = signature_nodes[0]
@@ -114,7 +110,7 @@ class AuthNRequestParser:
ctx.key = key
ctx.verify(signature_node)
except xmlsec.Error as exc:
raise CannotHandleAssertion(ERROR_FAILED_TO_VERIFY) from exc
raise CannotHandleAssertion("Failed to verify signature") from exc
return self._parse_xml(decoded_xml, relay_state)
@@ -129,14 +125,16 @@ class AuthNRequestParser:
try:
decoded_xml = decode_base64_and_inflate(saml_request)
except UnicodeDecodeError:
raise CannotHandleAssertion(ERROR_CANNOT_DECODE_REQUEST) from None
raise CannotHandleAssertion("Cannot decode SAML request.") from None
verifier = self.provider.verification_kp
if not verifier:
return self._parse_xml(decoded_xml, relay_state)
if verifier and not (signature and sig_alg):
raise CannotHandleAssertion(ERROR_SIGNATURE_REQUIRED_BUT_ABSENT)
raise CannotHandleAssertion(
"Verification Certificate configured, but request is not signed."
)
if signature and sig_alg:
querystring = f"SAMLRequest={quote_plus(saml_request)}&"
@@ -168,11 +166,11 @@ class AuthNRequestParser:
b64decode(signature),
)
except xmlsec.Error as exc:
raise CannotHandleAssertion(ERROR_FAILED_TO_VERIFY) from exc
raise CannotHandleAssertion("Failed to verify signature") from exc
try:
return self._parse_xml(decoded_xml, relay_state)
except ParseError as exc:
raise CannotHandleAssertion(ERROR_FAILED_TO_VERIFY) from exc
raise CannotHandleAssertion("Failed to verify signature") from exc
def idp_initiated(self) -> AuthNRequest:
"""Create IdP Initiated AuthNRequest"""

View File

@@ -6,9 +6,8 @@ from dataclasses import dataclass
from defusedxml import ElementTree
from authentik.common.saml.constants import NS_SAML_ASSERTION, NS_SAML_PROTOCOL
from authentik.providers.saml.exceptions import CannotHandleAssertion
from authentik.common.saml.exceptions import CannotHandleAssertion
from authentik.providers.saml.models import SAMLProvider
from authentik.providers.saml.processors.authn_request_parser import ERROR_CANNOT_DECODE_REQUEST
from authentik.providers.saml.utils.encoding import decode_base64_and_inflate
@@ -74,7 +73,7 @@ class LogoutRequestParser:
try:
decoded_xml = b64decode(saml_request.encode())
except UnicodeDecodeError:
raise CannotHandleAssertion(ERROR_CANNOT_DECODE_REQUEST) from None
raise CannotHandleAssertion("Cannot decode SAML request.") from None
return self._parse_xml(decoded_xml, relay_state)
def parse_detached(
@@ -86,6 +85,6 @@ class LogoutRequestParser:
try:
decoded_xml = decode_base64_and_inflate(saml_request)
except UnicodeDecodeError:
raise CannotHandleAssertion(ERROR_CANNOT_DECODE_REQUEST) from None
raise CannotHandleAssertion("Cannot decode SAML request.") from None
return self._parse_xml(decoded_xml, relay_state)

View File

@@ -7,13 +7,15 @@ from django.test import RequestFactory, TestCase
from django.urls import reverse
from authentik.common.saml.constants import SAML_NAME_ID_FORMAT_EMAIL
from authentik.common.saml.exceptions import CannotHandleAssertion
from authentik.core.models import Application
from authentik.core.tests.utils import create_test_brand, create_test_cert, create_test_flow
from authentik.flows.planner import FlowPlan
from authentik.flows.views.executor import SESSION_KEY_PLAN
from authentik.providers.saml.exceptions import CannotHandleAssertion
from authentik.providers.saml.models import SAMLBindings, SAMLLogoutMethods, SAMLProvider
from authentik.providers.saml.processors.logout_request import LogoutRequestProcessor
from authentik.providers.saml.processors.logout_request_parser import LogoutRequest
from authentik.providers.saml.processors.logout_response_processor import LogoutResponseProcessor
from authentik.providers.saml.views.flows import (
PLAN_CONTEXT_SAML_RELAY_STATE,
)
@@ -63,6 +65,13 @@ class TestSPInitiatedSLOViews(TestCase):
relay_state="https://sp.example.com/return",
)
# Create a LogoutResponseProcessor for generating valid test responses
self._response_processor = LogoutResponseProcessor(
provider=self.provider,
logout_request=LogoutRequest(id="test-id", issuer="https://sp.example.com"),
destination="https://idp.example.com/sls",
)
def test_redirect_view_handles_logout_request(self):
"""Test that redirect view properly handles a logout request"""
# Generate encoded logout request
@@ -96,13 +105,14 @@ class TestSPInitiatedSLOViews(TestCase):
def test_redirect_view_handles_logout_response_with_plan_context(self):
"""Test that redirect view always redirects to plan context URL, ignoring RelayState"""
plan_relay_state = "https://idp.example.com/flow/return"
relay_state = "https://somewhere-else.example.com/return"
# Create request with SAML logout response
request = self.factory.get(
f"/slo/redirect/{self.application.slug}/",
{
"SAMLResponse": "dummy-response",
"RelayState": "https://somewhere-else.example.com/return",
"SAMLResponse": self._response_processor.encode_redirect(),
"RelayState": relay_state,
},
)
plan = FlowPlan(flow_pk="test-flow")
@@ -126,7 +136,7 @@ class TestSPInitiatedSLOViews(TestCase):
request = self.factory.get(
f"/slo/redirect/{self.application.slug}/",
{
"SAMLResponse": "dummy-response",
"SAMLResponse": self._response_processor.encode_redirect(),
"RelayState": relay_state,
},
)
@@ -149,7 +159,7 @@ class TestSPInitiatedSLOViews(TestCase):
request = self.factory.get(
f"/slo/redirect/{self.application.slug}/",
{
"SAMLResponse": "dummy-response",
"SAMLResponse": self._response_processor.encode_redirect(),
},
)
# Create a flow plan with the return URL
@@ -172,7 +182,7 @@ class TestSPInitiatedSLOViews(TestCase):
request = self.factory.get(
f"/slo/redirect/{self.application.slug}/",
{
"SAMLResponse": "dummy-response",
"SAMLResponse": self._response_processor.encode_redirect(),
},
)
request.session = {}
@@ -234,13 +244,14 @@ class TestSPInitiatedSLOViews(TestCase):
def test_post_view_handles_logout_response_with_plan_context(self):
"""Test that POST view always redirects to plan context URL, ignoring RelayState"""
plan_relay_state = "https://idp.example.com/flow/return"
relay_state = "https://somewhere-else.example.com/return"
# Create POST request with SAML logout response
request = self.factory.post(
f"/slo/post/{self.application.slug}/",
{
"SAMLResponse": "dummy-response",
"RelayState": "https://somewhere-else.example.com/return",
"SAMLResponse": self._response_processor.encode_post(),
"RelayState": relay_state,
},
)
plan = FlowPlan(flow_pk="test-flow")
@@ -264,7 +275,7 @@ class TestSPInitiatedSLOViews(TestCase):
request = self.factory.post(
f"/slo/post/{self.application.slug}/",
{
"SAMLResponse": "dummy-response",
"SAMLResponse": self._response_processor.encode_post(),
},
)
# Create a flow plan with the return URL
@@ -426,7 +437,7 @@ class TestSPInitiatedSLOViews(TestCase):
request = self.factory.get(
f"/slo/redirect/{self.application.slug}/",
{
"SAMLResponse": "dummy-response",
"SAMLResponse": self._response_processor.encode_redirect(),
"RelayState": "/some/invalid/path", # Use a path that starts with /
},
)
@@ -791,3 +802,406 @@ class TestSPInitiatedSLOLogoutMethods(TestCase):
# Verify relay state was captured
logout_request = view.plan_context.get("authentik/providers/saml/logout_request")
self.assertEqual(logout_request.relay_state, expected_relay_state)
class TestSignatureVerification(TestCase):
"""Test SAML signature verification for LogoutRequest and LogoutResponse"""
def setUp(self):
"""Set up test fixtures"""
self.factory = RequestFactory()
self.brand = create_test_brand()
self.flow = create_test_flow()
self.invalidation_flow = create_test_flow()
self.cert = create_test_cert()
# Create provider with signing and verification keypairs
self.provider = SAMLProvider.objects.create(
name="test-sig-provider",
authorization_flow=self.flow,
invalidation_flow=self.invalidation_flow,
acs_url="https://sp.example.com/acs",
sls_url="https://sp.example.com/sls",
issuer="https://idp.example.com",
sp_binding="redirect",
sls_binding="redirect",
signing_kp=self.cert,
sign_logout_request=True,
sign_logout_response=True,
)
self.application = Application.objects.create(
name="test-sig-app",
slug="test-sig-app",
provider=self.provider,
)
def test_logout_response_redirect_no_verification_kp_accepted(self):
"""LogoutResponse without verification_kp should be accepted without signature"""
# Provider has no verification_kp — should accept unsigned response
self.provider.verification_kp = None
self.provider.save()
# Generate a valid logout response
logout_request = LogoutRequest(id="test-id", issuer="https://sp.example.com")
processor = LogoutResponseProcessor(
provider=self.provider,
logout_request=logout_request,
destination="https://idp.example.com/sls",
)
encoded_response = processor.encode_redirect()
request = self.factory.get(
f"/slo/redirect/{self.application.slug}/",
{
"SAMLResponse": encoded_response,
"RelayState": "https://sp.example.com/return",
},
)
request.session = {}
request.brand = self.brand
view = SPInitiatedSLOBindingRedirectView()
view.setup(request, application_slug=self.application.slug)
response = view.dispatch(request, application_slug=self.application.slug)
# Should redirect to relay state (accepted)
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, "https://sp.example.com/return")
def test_logout_response_redirect_verification_kp_no_signature_rejected(self):
"""LogoutResponse with verification_kp but no signature should be rejected"""
self.provider.verification_kp = self.cert
self.provider.save()
# Generate an unsigned logout response
logout_request = LogoutRequest(id="test-id", issuer="https://sp.example.com")
processor = LogoutResponseProcessor(
provider=self.provider,
logout_request=logout_request,
destination="https://idp.example.com/sls",
)
# encode_redirect() does NOT add signature to XML (it's detached for redirect)
encoded_response = processor.encode_redirect()
request = self.factory.get(
f"/slo/redirect/{self.application.slug}/",
{
"SAMLResponse": encoded_response,
"RelayState": "https://sp.example.com/return",
# No Signature or SigAlg params
},
)
request.session = {}
request.brand = self.brand
view = SPInitiatedSLOBindingRedirectView()
view.setup(request, application_slug=self.application.slug)
response = view.dispatch(request, application_slug=self.application.slug)
# Should redirect to root (rejected)
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, reverse("authentik_core:root-redirect"))
def test_logout_response_redirect_valid_signature_accepted(self):
"""LogoutResponse with valid detached signature should be accepted"""
self.provider.verification_kp = self.cert
self.provider.save()
# Generate a signed logout response URL (has Signature + SigAlg params)
logout_request = LogoutRequest(id="test-id", issuer="https://sp.example.com")
processor = LogoutResponseProcessor(
provider=self.provider,
logout_request=logout_request,
destination=f"https://idp.example.com/slo/redirect/{self.application.slug}/",
relay_state="https://sp.example.com/return",
)
redirect_url = processor.get_redirect_url()
# Parse the URL to get query params
from urllib.parse import parse_qs, urlparse
parsed = urlparse(redirect_url)
params = parse_qs(parsed.query)
request = self.factory.get(
f"/slo/redirect/{self.application.slug}/",
{
"SAMLResponse": params["SAMLResponse"][0],
"RelayState": params["RelayState"][0],
"Signature": params["Signature"][0],
"SigAlg": params["SigAlg"][0],
},
)
request.session = {}
request.brand = self.brand
view = SPInitiatedSLOBindingRedirectView()
view.setup(request, application_slug=self.application.slug)
response = view.dispatch(request, application_slug=self.application.slug)
# Should redirect to relay state (accepted)
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, "https://sp.example.com/return")
def test_logout_response_redirect_garbage_rejected(self):
"""LogoutResponse with garbage SAMLResponse should be rejected gracefully"""
self.provider.verification_kp = None
self.provider.save()
request = self.factory.get(
f"/slo/redirect/{self.application.slug}/",
{
"SAMLResponse": "not-valid-base64-!!!",
"RelayState": "https://sp.example.com/return",
},
)
request.session = {}
request.brand = self.brand
view = SPInitiatedSLOBindingRedirectView()
view.setup(request, application_slug=self.application.slug)
response = view.dispatch(request, application_slug=self.application.slug)
# Should redirect to root (rejected gracefully)
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, reverse("authentik_core:root-redirect"))
def test_logout_request_redirect_verification_kp_no_signature_rejected(self):
"""LogoutRequest with verification_kp but no Signature/SigAlg should be rejected"""
self.provider.verification_kp = self.cert
self.provider.save()
# Generate an unsigned logout request
processor = LogoutRequestProcessor(
provider=self.provider,
user=None,
destination="https://idp.example.com/sls",
name_id="test@example.com",
name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
session_index="test-session-123",
)
encoded_request = processor.encode_redirect()
request = self.factory.get(
f"/slo/redirect/{self.application.slug}/",
{
"SAMLRequest": encoded_request,
"RelayState": "https://sp.example.com/return",
# No Signature or SigAlg params
},
)
request.session = {}
request.brand = self.brand
view = SPInitiatedSLOBindingRedirectView()
view.setup(request, application_slug=self.application.slug)
view.resolve_provider_application()
result = view.check_saml_request()
self.assertIsNotNone(result)
self.assertEqual(result.status_code, 400)
def test_logout_request_redirect_valid_signature_accepted(self):
"""LogoutRequest with valid detached signature should be accepted"""
self.provider.verification_kp = self.cert
self.provider.save()
# Generate a signed logout request URL
processor = LogoutRequestProcessor(
provider=self.provider,
user=None,
destination="https://idp.example.com/sls",
name_id="test@example.com",
name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
session_index="test-session-123",
relay_state="https://sp.example.com/return",
)
redirect_url = processor.get_redirect_url()
# Parse the URL to get query params
from urllib.parse import parse_qs, urlparse
parsed = urlparse(redirect_url)
params = parse_qs(parsed.query)
request = self.factory.get(
f"/slo/redirect/{self.application.slug}/",
{
"SAMLRequest": params["SAMLRequest"][0],
"RelayState": params["RelayState"][0],
"Signature": params["Signature"][0],
"SigAlg": params["SigAlg"][0],
},
)
request.session = {}
request.brand = self.brand
view = SPInitiatedSLOBindingRedirectView()
view.setup(request, application_slug=self.application.slug)
view.resolve_provider_application()
result = view.check_saml_request()
self.assertIsNone(result) # None means success
def test_logout_response_post_no_verification_kp_accepted(self):
"""POST LogoutResponse without verification_kp should be accepted"""
self.provider.verification_kp = None
self.provider.save()
logout_request = LogoutRequest(id="test-id", issuer="https://sp.example.com")
processor = LogoutResponseProcessor(
provider=self.provider,
logout_request=logout_request,
destination="https://idp.example.com/sls",
)
encoded_response = processor.encode_post()
request = self.factory.post(
f"/slo/post/{self.application.slug}/",
{
"SAMLResponse": encoded_response,
"RelayState": "https://sp.example.com/return",
},
)
request.session = {}
request.brand = self.brand
view = SPInitiatedSLOBindingPOSTView()
view.setup(request, application_slug=self.application.slug)
response = view.dispatch(request, application_slug=self.application.slug)
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, "https://sp.example.com/return")
def test_logout_response_post_verification_kp_no_signature_rejected(self):
"""POST LogoutResponse with verification_kp but no enveloped signature should fail"""
self.provider.verification_kp = self.cert
# Disable signing so the response won't have a signature
self.provider.sign_logout_response = False
self.provider.save()
logout_request = LogoutRequest(id="test-id", issuer="https://sp.example.com")
processor = LogoutResponseProcessor(
provider=self.provider,
logout_request=logout_request,
destination="https://idp.example.com/sls",
)
encoded_response = processor.encode_post()
request = self.factory.post(
f"/slo/post/{self.application.slug}/",
{
"SAMLResponse": encoded_response,
"RelayState": "https://sp.example.com/return",
},
)
request.session = {}
request.brand = self.brand
view = SPInitiatedSLOBindingPOSTView()
view.setup(request, application_slug=self.application.slug)
response = view.dispatch(request, application_slug=self.application.slug)
# Should redirect to root (rejected)
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, reverse("authentik_core:root-redirect"))
def test_logout_response_post_valid_signature_accepted(self):
"""POST LogoutResponse with valid enveloped signature should be accepted"""
self.provider.verification_kp = self.cert
self.provider.sign_logout_response = True
self.provider.save()
logout_request = LogoutRequest(id="test-id", issuer="https://sp.example.com")
processor = LogoutResponseProcessor(
provider=self.provider,
logout_request=logout_request,
destination="https://idp.example.com/sls",
)
encoded_response = processor.encode_post()
request = self.factory.post(
f"/slo/post/{self.application.slug}/",
{
"SAMLResponse": encoded_response,
"RelayState": "https://sp.example.com/return",
},
)
request.session = {}
request.brand = self.brand
view = SPInitiatedSLOBindingPOSTView()
view.setup(request, application_slug=self.application.slug)
response = view.dispatch(request, application_slug=self.application.slug)
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, "https://sp.example.com/return")
def test_logout_request_post_verification_kp_no_signature_rejected(self):
"""POST LogoutRequest with verification_kp but no signature should be rejected"""
self.provider.verification_kp = self.cert
# Disable signing so the request won't have a signature
self.provider.sign_logout_request = False
self.provider.save()
processor = LogoutRequestProcessor(
provider=self.provider,
user=None,
destination="https://idp.example.com/sls",
name_id="test@example.com",
name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
session_index="test-session-123",
)
encoded_request = processor.encode_post()
request = self.factory.post(
f"/slo/post/{self.application.slug}/",
{
"SAMLRequest": encoded_request,
"RelayState": "https://sp.example.com/return",
},
)
request.session = {}
request.brand = self.brand
view = SPInitiatedSLOBindingPOSTView()
view.setup(request, application_slug=self.application.slug)
view.resolve_provider_application()
result = view.check_saml_request()
self.assertIsNotNone(result)
self.assertEqual(result.status_code, 400)
def test_logout_request_post_valid_signature_accepted(self):
"""POST LogoutRequest with valid enveloped signature should be accepted"""
self.provider.verification_kp = self.cert
self.provider.sign_logout_request = True
self.provider.save()
processor = LogoutRequestProcessor(
provider=self.provider,
user=None,
destination="https://idp.example.com/sls",
name_id="test@example.com",
name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
session_index="test-session-123",
)
encoded_request = processor.encode_post()
request = self.factory.post(
f"/slo/post/{self.application.slug}/",
{
"SAMLRequest": encoded_request,
"RelayState": "https://sp.example.com/return",
},
)
request.session = {}
request.brand = self.brand
view = SPInitiatedSLOBindingPOSTView()
view.setup(request, application_slug=self.application.slug)
view.resolve_provider_application()
result = view.check_saml_request()
self.assertIsNone(result) # None means success

View File

@@ -1,5 +1,7 @@
"""SP-initiated SAML Single Logout Views"""
from base64 import b64decode
from django.http import Http404, HttpRequest, HttpResponse
from django.shortcuts import get_object_or_404, redirect
from django.utils.decorators import method_decorator
@@ -7,6 +9,12 @@ from django.views.decorators.clickjacking import xframe_options_sameorigin
from django.views.decorators.csrf import csrf_exempt
from structlog.stdlib import get_logger
from authentik.common.saml.exceptions import CannotHandleAssertion
from authentik.common.saml.parsers.logout_response import LogoutResponseParser
from authentik.common.saml.parsers.verify import (
verify_detached_signature,
verify_enveloped_signature,
)
from authentik.core.models import Application, AuthenticatedSession
from authentik.events.models import Event, EventAction
from authentik.flows.models import Flow, in_memory_stage
@@ -16,7 +24,6 @@ from authentik.flows.views.executor import SESSION_KEY_PLAN
from authentik.lib.views import bad_request_message
from authentik.policies.views import PolicyAccessView
from authentik.providers.iframe_logout import IframeLogoutStageView
from authentik.providers.saml.exceptions import CannotHandleAssertion
from authentik.providers.saml.models import (
SAMLBindings,
SAMLLogoutMethods,
@@ -36,6 +43,8 @@ from authentik.providers.saml.views.flows import (
REQUEST_KEY_RELAY_STATE,
REQUEST_KEY_SAML_REQUEST,
REQUEST_KEY_SAML_RESPONSE,
REQUEST_KEY_SAML_SIG_ALG,
REQUEST_KEY_SAML_SIGNATURE,
)
LOGGER = get_logger()
@@ -236,6 +245,34 @@ class SPInitiatedSLOBindingRedirectView(SPInitiatedSLOView):
# IDP SLO, so we want to redirect to our next provider
if REQUEST_KEY_SAML_RESPONSE in request.GET:
relay_state = request.GET.get(REQUEST_KEY_RELAY_STATE, "")
# Resolve provider for signature verification
try:
application = Application.objects.get(slug=kwargs.get("application_slug", ""))
provider = SAMLProvider.objects.get(pk=application.provider_id)
except (Application.DoesNotExist, SAMLProvider.DoesNotExist):
return redirect("authentik_core:root-redirect")
try:
parser = LogoutResponseParser()
logout_response = parser.parse_detached(
request.GET[REQUEST_KEY_SAML_RESPONSE],
relay_state=relay_state or None,
)
parser.verify_status(logout_response)
if provider.verification_kp:
verify_detached_signature(
"SAMLResponse",
request.GET[REQUEST_KEY_SAML_RESPONSE],
relay_state or None,
request.GET.get(REQUEST_KEY_SAML_SIGNATURE),
request.GET.get(REQUEST_KEY_SAML_SIG_ALG),
provider.verification_kp,
)
except CannotHandleAssertion as exc:
LOGGER.warning("Failed to verify SAML LogoutResponse", exc=str(exc))
return redirect("authentik_core:root-redirect")
redirect_url = _get_redirect_url(request, relay_state)
if redirect_url:
return redirect(redirect_url)
@@ -255,6 +292,15 @@ class SPInitiatedSLOBindingRedirectView(SPInitiatedSLOView):
self.request.GET[REQUEST_KEY_SAML_REQUEST],
relay_state=self.request.GET.get(REQUEST_KEY_RELAY_STATE, None),
)
if self.provider.verification_kp:
verify_detached_signature(
"SAMLRequest",
self.request.GET[REQUEST_KEY_SAML_REQUEST],
self.request.GET.get(REQUEST_KEY_RELAY_STATE),
self.request.GET.get(REQUEST_KEY_SAML_SIGNATURE),
self.request.GET.get(REQUEST_KEY_SAML_SIG_ALG),
self.provider.verification_kp,
)
self.plan_context[PLAN_CONTEXT_SAML_LOGOUT_REQUEST] = logout_request
except CannotHandleAssertion as exc:
Event.new(
@@ -279,6 +325,30 @@ class SPInitiatedSLOBindingPOSTView(SPInitiatedSLOView):
# IDP SLO, so we want to redirect to our next provider
if REQUEST_KEY_SAML_RESPONSE in request.POST:
relay_state = request.POST.get(REQUEST_KEY_RELAY_STATE, "")
try:
application = Application.objects.get(slug=kwargs.get("application_slug", ""))
provider = SAMLProvider.objects.get(pk=application.provider_id)
except (Application.DoesNotExist, SAMLProvider.DoesNotExist):
return redirect("authentik_core:root-redirect")
try:
parser = LogoutResponseParser()
logout_response = parser.parse(
request.POST[REQUEST_KEY_SAML_RESPONSE],
relay_state=relay_state or None,
)
parser.verify_status(logout_response)
if provider.verification_kp:
verify_enveloped_signature(
b64decode(request.POST[REQUEST_KEY_SAML_RESPONSE].encode()),
provider.verification_kp,
"/samlp:LogoutResponse/ds:Signature",
)
except CannotHandleAssertion as exc:
LOGGER.warning("Failed to verify SAML LogoutResponse", exc=str(exc))
return redirect("authentik_core:root-redirect")
redirect_url = _get_redirect_url(request, relay_state)
if redirect_url:
return redirect(redirect_url)
@@ -299,6 +369,12 @@ class SPInitiatedSLOBindingPOSTView(SPInitiatedSLOView):
payload[REQUEST_KEY_SAML_REQUEST],
relay_state=payload.get(REQUEST_KEY_RELAY_STATE, None),
)
if self.provider.verification_kp:
verify_enveloped_signature(
b64decode(payload[REQUEST_KEY_SAML_REQUEST].encode()),
self.provider.verification_kp,
"/samlp:LogoutRequest/ds:Signature",
)
self.plan_context[PLAN_CONTEXT_SAML_LOGOUT_REQUEST] = logout_request
except CannotHandleAssertion as exc:
LOGGER.info(str(exc))

View File

@@ -8,6 +8,7 @@ from django.views.decorators.clickjacking import xframe_options_sameorigin
from django.views.decorators.csrf import csrf_exempt
from structlog.stdlib import get_logger
from authentik.common.saml.exceptions import CannotHandleAssertion
from authentik.core.models import Application
from authentik.events.models import Event, EventAction
from authentik.flows.exceptions import FlowNonApplicableException
@@ -16,7 +17,6 @@ from authentik.flows.planner import PLAN_CONTEXT_APPLICATION, PLAN_CONTEXT_SSO,
from authentik.flows.views.executor import SESSION_KEY_POST
from authentik.lib.views import bad_request_message
from authentik.policies.views import PolicyAccessView
from authentik.providers.saml.exceptions import CannotHandleAssertion
from authentik.providers.saml.models import SAMLBindings, SAMLProvider
from authentik.providers.saml.processors.authn_request_parser import AuthNRequestParser
from authentik.providers.saml.views.flows import (

View File

@@ -1,6 +1,5 @@
"""SCIM Provider API Views"""
from rest_framework.fields import SerializerMethodField
from rest_framework.viewsets import ModelViewSet
from authentik.core.api.providers import ProviderSerializer
@@ -17,11 +16,6 @@ class SCIMProviderSerializer(
):
"""SCIMProvider Serializer"""
auth_oauth_token_last_updated = SerializerMethodField()
auth_oauth_token_expires = SerializerMethodField()
auth_oauth_url_callback = SerializerMethodField()
auth_oauth_url_start = SerializerMethodField()
class Meta:
model = SCIMProvider
fields = [
@@ -41,10 +35,6 @@ class SCIMProviderSerializer(
"auth_mode",
"auth_oauth",
"auth_oauth_params",
"auth_oauth_token_last_updated",
"auth_oauth_token_expires",
"auth_oauth_url_callback",
"auth_oauth_url_start",
"compatibility_mode",
"service_provider_config_cache_timeout",
"exclude_users_service_account",

View File

@@ -102,16 +102,4 @@ class Migration(migrations.Migration):
verbose_name="SCIM Compatibility Mode",
),
),
migrations.AlterField(
model_name="scimprovider",
name="auth_mode",
field=models.TextField(
choices=[
("token", "Token"),
("oauth", "OAuth (Silent)"),
("oauth_interactive", "OAuth (interactive)"),
],
default="token",
),
),
]

View File

@@ -72,8 +72,7 @@ class SCIMAuthenticationMode(models.TextChoices):
"""SCIM authentication modes"""
TOKEN = "token", _("Token")
OAUTH_SILENT = "oauth", _("OAuth (Silent)")
OAUTH_INTERACTIVE = "oauth_interactive", _("OAuth (interactive)")
OAUTH = "oauth", _("OAuth")
class SCIMCompatibilityMode(models.TextChoices):
@@ -145,10 +144,7 @@ class SCIMProvider(OutgoingSyncProvider, BackchannelProvider):
)
def scim_auth(self) -> AuthBase:
if self.auth_mode in [
SCIMAuthenticationMode.OAUTH_SILENT,
SCIMAuthenticationMode.OAUTH_INTERACTIVE,
]:
if self.auth_mode == SCIMAuthenticationMode.OAUTH:
try:
from authentik.enterprise.providers.scim.auth_oauth2 import SCIMOAuthAuth

View File

@@ -1,5 +1,6 @@
"""Source type manager"""
from collections.abc import Callable
from enum import Enum
from typing import Any
@@ -113,7 +114,7 @@ class SourceTypeRegistry:
)
return found_type
def find(self, type_name: str, kind: RequestKind) -> type[OAuthCallback | OAuthRedirect]:
def find(self, type_name: str, kind: RequestKind) -> Callable:
"""Find fitting Source Type"""
found_type = self.find_type(type_name)
if kind == RequestKind.CALLBACK:

View File

@@ -15,7 +15,6 @@ from structlog.stdlib import get_logger
from authentik.core.sources.flow_manager import SourceFlowManager
from authentik.events.models import Event, EventAction
from authentik.sources.oauth.clients.base import BaseOAuthClient
from authentik.sources.oauth.models import (
GroupOAuthSourceConnection,
OAuthSource,
@@ -30,7 +29,7 @@ class OAuthCallback(OAuthClientMixin, View):
"Base OAuth callback view."
source: OAuthSource
token: dict[str, Any] | None = None
token: dict | None = None
def dispatch(self, request: HttpRequest, *_, **kwargs) -> HttpResponse:
"""View Get handler"""
@@ -50,31 +49,20 @@ class OAuthCallback(OAuthClientMixin, View):
if "error" in self.token:
return self.handle_login_failure(self.token["error"])
# Fetch profile info
try:
res = self.redirect_flow_manager(client)
except ValueError as exc:
# if we're authenticated and not in a source stage and this new flag is enabled,
# just continue
if self.request.user.is_authenticated:
pass
return self.handle_login_failure(exc.args[0])
return res
def redirect_flow_manager(self, client: BaseOAuthClient) -> HttpResponse:
try:
raw_info = client.get_profile_info(self.token)
if raw_info is None:
raise ValueError("Could not retrieve profile.")
return self.handle_login_failure("Could not retrieve profile.")
except JSONDecodeError as exc:
Event.new(
EventAction.CONFIGURATION_ERROR,
message="Failed to JSON-decode profile.",
raw_profile=exc.doc,
).from_http(self.request)
raise ValueError("Could not retrieve profile.") from None
return self.handle_login_failure("Could not retrieve profile.")
identifier = self.get_user_id(info=raw_info)
if identifier is None:
raise ValueError("Could not determine id.")
return self.handle_login_failure("Could not determine id.")
sfm = OAuthSourceFlowManager(
source=self.source,
request=self.request,

View File

@@ -1,7 +1,6 @@
"""authentik saml source processor"""
from base64 import b64decode
from datetime import UTC, datetime
from time import mktime
from typing import TYPE_CHECKING
@@ -41,7 +40,6 @@ from authentik.sources.saml.exceptions import (
InvalidSignature,
MismatchedRequestID,
MissingSAMLResponse,
SAMLException,
UnsupportedNameIDFormat,
)
from authentik.sources.saml.models import (
@@ -97,7 +95,6 @@ class ResponseProcessor:
self._verify_request_id()
self._verify_status()
self._verify_conditions()
def _decrypt_response(self):
"""Decrypt SAMLResponse EncryptedAssertion Element"""
@@ -129,20 +126,6 @@ class ResponseProcessor:
)
self._assertion = decrypted_assertion
def _verify_conditions(self):
conditions = self.get_assertion().find(f"{{{NS_SAML_ASSERTION}}}Conditions")
if conditions is None:
return
_now = now()
before = conditions.attrib.get("NotBefore")
if before:
if datetime.fromisoformat(before).replace(tzinfo=UTC) > _now:
raise SAMLException("Assertion is not valid yet or expired.")
on_or_after = conditions.attrib.get("NotOnOrAfter")
if on_or_after:
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"])
@@ -232,9 +215,10 @@ class ResponseProcessor:
user has an attribute that refers to our Source for cleanup. The user is also deleted
on logout and periodically."""
# Create a temporary User
name_id_el, name_id = self._get_name_id()
name_id = self._get_name_id()
username = name_id.text
# trim username to ensure it is max 150 chars
username = f"ak-{name_id[: USERNAME_MAX_LENGTH - 14]}-transient"
username = f"ak-{username[: USERNAME_MAX_LENGTH - 14]}-transient"
expiry = mktime(
(now() + timedelta_from_string(self._source.temporary_user_delete_after)).timetuple()
)
@@ -250,18 +234,20 @@ class ResponseProcessor:
},
path=self._source.get_user_path(),
)
LOGGER.debug("Created temporary user for NameID Transient", username=name_id)
LOGGER.debug("Created temporary user for NameID Transient", username=name_id.text)
user.set_unusable_password()
user.save()
UserSAMLSourceConnection.objects.create(source=self._source, user=user, identifier=name_id)
UserSAMLSourceConnection.objects.create(
source=self._source, user=user, identifier=name_id.text
)
return SAMLSourceFlowManager(
source=self._source,
request=self._http_request,
identifier=str(name_id),
identifier=str(name_id.text),
user_info={
"root": self._root,
"assertion": self.get_assertion(),
"name_id": name_id_el,
"name_id": name_id,
},
policy_context={},
)
@@ -272,7 +258,7 @@ class ResponseProcessor:
return self._assertion
return self._root.find(f"{{{NS_SAML_ASSERTION}}}Assertion")
def _get_name_id(self) -> tuple[Element, str]:
def _get_name_id(self) -> Element:
"""Get NameID Element"""
assertion = self.get_assertion()
if assertion is None:
@@ -283,11 +269,12 @@ class ResponseProcessor:
name_id = subject.find(f"{{{NS_SAML_ASSERTION}}}NameID")
if name_id is None:
raise ValueError("NameID element not found")
return name_id, "".join(name_id.itertext())
return name_id
def _get_name_id_filter(self) -> dict[str, str]:
"""Returns the subject's NameID as a Filter for the `User`"""
name_id_el, name_id = self._get_name_id()
name_id_el = self._get_name_id()
name_id = name_id_el.text
if not name_id:
raise UnsupportedNameIDFormat("Subject's NameID is empty.")
_format = name_id_el.attrib["Format"]
@@ -308,26 +295,26 @@ class ResponseProcessor:
def prepare_flow_manager(self) -> SourceFlowManager:
"""Prepare flow plan depending on whether or not the user exists"""
name_id_el, name_id = self._get_name_id()
name_id = self._get_name_id()
# Sanity check, show a warning if NameIDPolicy doesn't match what we go
if self._source.name_id_policy != name_id_el.attrib["Format"]:
if self._source.name_id_policy != name_id.attrib["Format"]:
LOGGER.warning(
"NameID from IdP doesn't match our policy",
expected=self._source.name_id_policy,
got=name_id_el.attrib["Format"],
got=name_id.attrib["Format"],
)
# transient NameIDs are handled separately as they don't have to go through flows.
if name_id_el.attrib["Format"] == SAML_NAME_ID_FORMAT_TRANSIENT:
if name_id.attrib["Format"] == SAML_NAME_ID_FORMAT_TRANSIENT:
return self._handle_name_id_transient()
return SAMLSourceFlowManager(
source=self._source,
request=self._http_request,
identifier=str(name_id),
identifier=str(name_id.text),
user_info={
"root": self._root,
"assertion": self.get_assertion(),
"name_id": name_id_el,
"name_id": name_id,
},
policy_context={
"saml_response": etree.tostring(self._root),

View File

@@ -4,7 +4,6 @@ from base64 import b64encode
from defusedxml.lxml import fromstring
from django.test import TestCase
from freezegun import freeze_time
from authentik.common.saml.constants import NS_SAML_ASSERTION
from authentik.core.tests.utils import RequestFactory, create_test_flow
@@ -35,7 +34,6 @@ class TestPropertyMappings(TestCase):
pre_authentication_flow=create_test_flow(),
)
@freeze_time("2022-10-14T14:15:00")
def test_user_base_properties(self):
"""Test user base properties"""
properties = self.source.get_base_user_properties(
@@ -63,7 +61,6 @@ class TestPropertyMappings(TestCase):
properties = self.source.get_base_group_properties(root=ROOT, group_id=group_id)
self.assertEqual(properties, {"name": group_id})
@freeze_time("2022-10-14T14:15:00")
def test_user_property_mappings(self):
"""Test user property mappings"""
self.source.user_property_mappings.add(
@@ -97,7 +94,6 @@ class TestPropertyMappings(TestCase):
},
)
@freeze_time("2022-10-14T14:15:00")
def test_group_property_mappings(self):
"""Test group property mappings"""
self.source.group_property_mappings.add(

View File

@@ -3,7 +3,6 @@
from base64 import b64encode
from django.test import TestCase
from freezegun import freeze_time
from authentik.core.tests.utils import RequestFactory, create_test_cert, create_test_flow
from authentik.crypto.models import CertificateKeyPair
@@ -47,7 +46,6 @@ class TestResponseProcessor(TestCase):
):
ResponseProcessor(self.source, request).parse()
@freeze_time("2022-10-14T14:15:00")
def test_success(self):
"""Test success"""
request = self.factory.post(
@@ -74,7 +72,6 @@ class TestResponseProcessor(TestCase):
},
)
@freeze_time("2022-10-14T14:16:40Z")
def test_success_with_status_message_and_detail(self):
"""Test success with StatusMessage and StatusDetail present (should not raise error)"""
request = self.factory.post(
@@ -91,7 +88,6 @@ class TestResponseProcessor(TestCase):
sfm = parser.prepare_flow_manager()
self.assertEqual(sfm.user_properties["username"], "jens@goauthentik.io")
@freeze_time("2022-10-14T14:16:40Z")
def test_error_with_message_and_detail(self):
"""Test error status with StatusMessage and StatusDetail includes both in error"""
request = self.factory.post(
@@ -109,7 +105,6 @@ class TestResponseProcessor(TestCase):
self.assertIn("User account is disabled", str(ctx.exception))
self.assertIn("Authentication failed", str(ctx.exception))
@freeze_time("2024-08-07T15:48:09.325Z")
def test_encrypted_correct(self):
"""Test encrypted"""
key = load_fixture("fixtures/encrypted-key.pem")
@@ -147,7 +142,6 @@ class TestResponseProcessor(TestCase):
with self.assertRaises(InvalidEncryption):
parser.parse()
@freeze_time("2022-10-14T14:16:40Z")
def test_verification_assertion(self):
"""Test verifying signature inside assertion"""
key = load_fixture("fixtures/signature_cert.pem")
@@ -170,7 +164,6 @@ class TestResponseProcessor(TestCase):
parser = ResponseProcessor(self.source, request)
parser.parse()
@freeze_time("2014-07-17T01:02:18Z")
def test_verification_assertion_duplicate(self):
"""Test verifying signature inside assertion, where the response has another assertion
before our signed assertion"""
@@ -193,35 +186,9 @@ class TestResponseProcessor(TestCase):
parser = ResponseProcessor(self.source, request)
parser.parse()
self.assertNotEqual(parser._get_name_id()[1], "bad")
self.assertEqual(parser._get_name_id()[1], "_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7")
self.assertNotEqual(parser._get_name_id().text, "bad")
self.assertEqual(parser._get_name_id().text, "_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7")
@freeze_time("2022-10-14T14:15:00")
def test_name_id_comment(self):
"""Test comment in name ID"""
fixture = load_fixture("fixtures/response_signed_assertion_dup.xml")
fixture = fixture.replace(
"_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7",
"_ce3d2948b4cf20146dee0a0b3dd6f<!--x-->69b6cf86f62d7",
)
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(fixture.encode()).decode()},
)
parser = ResponseProcessor(self.source, request)
parser.parse()
self.assertEqual(parser._get_name_id()[1], "_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7")
@freeze_time("2014-07-17T01:02:18Z")
def test_verification_response(self):
"""Test verifying signature inside response"""
key = load_fixture("fixtures/signature_cert.pem")
@@ -244,7 +211,6 @@ class TestResponseProcessor(TestCase):
parser = ResponseProcessor(self.source, request)
parser.parse()
@freeze_time("2024-01-18T06:20:48Z")
def test_verification_response_and_assertion(self):
"""Test verifying signature inside response and assertion"""
key = load_fixture("fixtures/signature_cert.pem")
@@ -291,7 +257,6 @@ class TestResponseProcessor(TestCase):
with self.assertRaisesMessage(InvalidSignature, ""):
parser.parse()
@freeze_time("2022-10-14T14:15:00")
def test_verification_no_signature(self):
"""Test rejecting response without signature when signed_assertion is True"""
key = load_fixture("fixtures/signature_cert.pem")
@@ -338,7 +303,6 @@ class TestResponseProcessor(TestCase):
with self.assertRaisesMessage(InvalidSignature, ""):
parser.parse()
@freeze_time("2025-10-30T05:45:47.619Z")
def test_signed_encrypted_response(self):
"""Test signed & encrypted response"""
verification_key = load_fixture("fixtures/signature_cert2.pem")
@@ -366,7 +330,6 @@ class TestResponseProcessor(TestCase):
parser = ResponseProcessor(self.source, request)
parser.parse()
@freeze_time("2026-01-21T14:23")
def test_transient(self):
"""Test SAML transient NameID"""
verification_key = load_fixture("fixtures/signature_cert2.pem")

View File

@@ -4,7 +4,6 @@ from base64 import b64encode
from django.test import RequestFactory, TestCase
from django.urls import reverse
from freezegun import freeze_time
from authentik.core.tests.utils import create_test_flow
from authentik.flows.planner import PLAN_CONTEXT_REDIRECT, FlowPlan
@@ -27,7 +26,6 @@ class TestViews(TestCase):
pre_authentication_flow=create_test_flow(),
)
@freeze_time("2022-10-14T14:15:00")
def test_enroll(self):
"""Enroll"""
flow = create_test_flow()
@@ -54,7 +52,6 @@ class TestViews(TestCase):
plan: FlowPlan = self.client.session.get(SESSION_KEY_PLAN)
self.assertIsNotNone(plan)
@freeze_time("2022-10-14T14:15:00")
def test_enroll_redirect(self):
"""Enroll when attempting to access a provider"""
initial_redirect = f"http://{generate_id()}"

File diff suppressed because one or more lines are too long

View File

@@ -36,10 +36,14 @@ entries:
attrs:
order: 50
initial_value: |
actor_uuid = str(getattr(http_request.user, "pk", ""))
pending_user = user if getattr(user, "is_authenticated", False) else None
target_uuid = str(getattr(pending_user, "pk", ""))
is_self_service = not target_uuid or target_uuid == actor_uuid
target_uuid = (http_request.session.get("authentik/flows/get", {}) or {}).get("user_uuid")
current_user_uuid = str(getattr(user, "pk", "") or getattr(http_request.user, "pk", ""))
is_self_service = not target_uuid or target_uuid == current_user_uuid
pending_user = None
if target_uuid and not is_self_service:
from authentik.core.models import User
pending_user = User.objects.filter(pk=target_uuid).first()
if is_self_service:
return (
"<p><strong>You are about to lock down your own account.</strong></p>"
@@ -59,15 +63,14 @@ entries:
from django.utils.html import escape
if pending_user:
detail = pending_user.email or pending_user.name
user_html = f"<code>{escape(pending_user.username)}</code>"
if detail and detail != pending_user.username:
user_html = f"{user_html} ({escape(detail)})"
email = escape(pending_user.email or pending_user.name or "No email")
user_html = f"<p><code>{escape(pending_user.username)}</code> ({email})</p>"
else:
user_html = "the account selected when this one-time lockdown link was created"
user_html = "<p>the account selected when this one-time lockdown link was created</p>"
return (
f"<p><strong>You are about to lock down the following account:</strong> {user_html}</p>"
"<p><strong>You are about to lock down the following account:</strong></p>"
f"{user_html}"
"<p>This is an emergency action for cutting off access to the account right away. "
"It does not lock the administrator who opened this page.</p>"
"<p><strong>This will immediately:</strong></p>"
@@ -96,9 +99,9 @@ entries:
attrs:
order: 100
initial_value: |
actor_uuid = str(getattr(http_request.user, "pk", ""))
target_uuid = str(getattr(user, "pk", ""))
is_self_service = not target_uuid or target_uuid == actor_uuid
target_uuid = (http_request.session.get("authentik/flows/get", {}) or {}).get("user_uuid")
current_user_uuid = str(getattr(user, "pk", "") or getattr(http_request.user, "pk", ""))
is_self_service = not target_uuid or target_uuid == current_user_uuid
if is_self_service:
info = (
"Use this if you no longer trust your current password or sessions. "
@@ -131,9 +134,9 @@ entries:
attrs:
order: 200
placeholder: |
actor_uuid = str(getattr(http_request.user, "pk", ""))
target_uuid = str(getattr(user, "pk", ""))
is_self_service = not target_uuid or target_uuid == actor_uuid
target_uuid = (http_request.session.get("authentik/flows/get", {}) or {}).get("user_uuid")
current_user_uuid = str(getattr(user, "pk", "") or getattr(http_request.user, "pk", ""))
is_self_service = not target_uuid or target_uuid == current_user_uuid
if is_self_service:
return "Describe why you are locking your account..."
return "Describe why this account is being locked down..."
@@ -181,10 +184,14 @@ entries:
attrs:
order: 300
initial_value: |
target_uuid = (http_request.session.get("authentik/flows/get", {}) or {}).get("user_uuid")
from django.utils.html import escape
from authentik.core.models import User
if getattr(user, "is_authenticated", False):
return f"<p><code>{escape(user.username)}</code> has been locked down.</p>"
if target_uuid:
target = User.objects.filter(pk=target_uuid).first()
if target:
return f"<p><code>{escape(target.username)}</code> has been locked down.</p>"
return "<p>The selected account has been locked down.</p>"
initial_value_expression: true
@@ -214,9 +221,9 @@ entries:
attrs:
name: default-account-lockdown-admin-policy
expression: |
actor_uuid = str(getattr(request.http_request.user, "pk", ""))
target_uuid = str(getattr(request.user, "pk", ""))
return bool(target_uuid) and target_uuid != actor_uuid
target_uuid = (request.http_request.session.get("authentik/flows/get", {}) or {}).get("user_uuid")
current_user_uuid = str(getattr(request.user, "pk", "") or getattr(request.http_request.user, "pk", ""))
return bool(target_uuid) and target_uuid != current_user_uuid
identifiers:
name: default-account-lockdown-admin-policy
id: admin-policy

View File

@@ -11203,8 +11203,7 @@
"type": "string",
"enum": [
"token",
"oauth",
"oauth_interactive"
"oauth"
],
"title": "Auth mode"
},

14
go.mod
View File

@@ -7,10 +7,10 @@ require (
beryju.io/radius-eap v0.1.0
github.com/avast/retry-go/v4 v4.7.0
github.com/coreos/go-oidc/v3 v3.18.0
github.com/getsentry/sentry-go v0.46.2
github.com/getsentry/sentry-go v0.46.1
github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1
github.com/go-ldap/ldap/v3 v3.4.13
github.com/go-openapi/runtime v0.29.5
github.com/go-openapi/runtime v0.29.4
github.com/golang-jwt/jwt/v5 v5.3.1
github.com/google/uuid v1.6.0
github.com/gorilla/handlers v1.5.2
@@ -57,7 +57,7 @@ require (
github.com/go-openapi/jsonreference v0.21.5 // indirect
github.com/go-openapi/loads v0.23.3 // indirect
github.com/go-openapi/spec v0.22.4 // indirect
github.com/go-openapi/strfmt v0.26.2 // indirect
github.com/go-openapi/strfmt v0.26.1 // indirect
github.com/go-openapi/swag/conv v0.26.0 // indirect
github.com/go-openapi/swag/fileutils v0.26.0 // indirect
github.com/go-openapi/swag/jsonname v0.25.5 // indirect
@@ -90,10 +90,10 @@ require (
go.opentelemetry.io/otel/trace v1.43.0 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.50.0 // indirect
golang.org/x/net v0.53.0 // indirect
golang.org/x/sys v0.43.0 // indirect
golang.org/x/text v0.36.0 // indirect
golang.org/x/crypto v0.49.0 // indirect
golang.org/x/net v0.52.0 // indirect
golang.org/x/sys v0.42.0 // indirect
golang.org/x/text v0.35.0 // indirect
google.golang.org/protobuf v1.36.8 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

36
go.sum
View File

@@ -20,8 +20,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/getsentry/sentry-go v0.46.2 h1:1jhYwrKGa3sIpo/y5iDNXS5wDoT7I1KNzMHrnK6ojns=
github.com/getsentry/sentry-go v0.46.2/go.mod h1:evVbw2qotNUdYG8KxXbAdjOQWWvWIwKxpjdZZIvcIPw=
github.com/getsentry/sentry-go v0.46.1 h1:mZyQFaQYkPxAdDG4HR8gDg6j4CnKYVWt4TF92N7i3XY=
github.com/getsentry/sentry-go v0.46.1/go.mod h1:evVbw2qotNUdYG8KxXbAdjOQWWvWIwKxpjdZZIvcIPw=
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
@@ -51,12 +51,12 @@ github.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe
github.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw=
github.com/go-openapi/loads v0.23.3 h1:g5Xap1JfwKkUnZdn+S0L3SzBDpcTIYzZ5Qaag0YDkKQ=
github.com/go-openapi/loads v0.23.3/go.mod h1:NOH07zLajXo8y55hom0omlHWDVVvCwBM/S+csCK8LqA=
github.com/go-openapi/runtime v0.29.5 h1:uc5+/TtqLIfDBTUxnF3uppoGMt+9DzonwUWsviINlrY=
github.com/go-openapi/runtime v0.29.5/go.mod h1:D9IUbWccdYv+km8QwmAm90FZvDcQk47vP2Y7y5as/D8=
github.com/go-openapi/runtime v0.29.4 h1:k2lDxrGoSAJRdhFG2tONKMpkizY/4X1cciSdtzk4Jjo=
github.com/go-openapi/runtime v0.29.4/go.mod h1:K0k/2raY6oqXJnZAgWJB2i/12QKrhUKpZcH4PfV9P18=
github.com/go-openapi/spec v0.22.4 h1:4pxGjipMKu0FzFiu/DPwN3CTBRlVM2yLf/YTWorYfDQ=
github.com/go-openapi/spec v0.22.4/go.mod h1:WQ6Ai0VPWMZgMT4XySjlRIE6GP1bGQOtEThn3gcWLtQ=
github.com/go-openapi/strfmt v0.26.2 h1:ysjheCh4i1rmFEo2LanhELDNucNzfWTZhUDKgWWPaFM=
github.com/go-openapi/strfmt v0.26.2/go.mod h1:fXh1e449cyUn2NYuz+wb3wARBUdMl7qPEZwX00nqivY=
github.com/go-openapi/strfmt v0.26.1 h1:7zGCHji7zSYDC2tCXIusoxYQz/48jAf2q+sF6wXTG+c=
github.com/go-openapi/strfmt v0.26.1/go.mod h1:Zslk5VZPOISLwmWTMBIS7oiVFem1o1EI6zULY8Uer7Y=
github.com/go-openapi/swag/conv v0.26.0 h1:5yGGsPYI1ZCva93U0AoKi/iZrNhaJEjr324YVsiD89I=
github.com/go-openapi/swag/conv v0.26.0/go.mod h1:tpAmIL7X58VPnHHiSO4uE3jBeRamGsFsfdDeDtb5ECE=
github.com/go-openapi/swag/fileutils v0.26.0 h1:WJoPRvsA7QRiiWluowkLJa9jaYR7FCuxmDvnCgaRRxU=
@@ -77,10 +77,10 @@ github.com/go-openapi/swag/typeutils v0.26.0 h1:2kdEwdiNWy+JJdOvu5MA2IIg2SylWAFu
github.com/go-openapi/swag/typeutils v0.26.0/go.mod h1:oovDuIUvTrEHVMqWilQzKzV4YlSKgyZmFh7AlfABNVE=
github.com/go-openapi/swag/yamlutils v0.25.5 h1:kASCIS+oIeoc55j28T4o8KwlV2S4ZLPT6G0iq2SSbVQ=
github.com/go-openapi/swag/yamlutils v0.25.5/go.mod h1:Gek1/SjjfbYvM+Iq4QGwa/2lEXde9n2j4a3wI3pNuOQ=
github.com/go-openapi/testify/enable/yaml/v2 v2.5.0 h1:3hZD1fwydvCx/cc1R2uYNQirHqf2s6lqpKV3FcNTURA=
github.com/go-openapi/testify/enable/yaml/v2 v2.5.0/go.mod h1:TvDZKBH7ZbMaF3EqH2AwTvNQCmzyZq8K1agRjf1B+Nk=
github.com/go-openapi/testify/v2 v2.5.0 h1:UOCr63aAsMIDydZbZGqo5Ev01D4eydItRbekDuZMJLw=
github.com/go-openapi/testify/v2 v2.5.0/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw=
github.com/go-openapi/testify/enable/yaml/v2 v2.4.2 h1:5zRca5jw7lzVREKCZVNBpysDNBjj74rBh0N2BGQbSR0=
github.com/go-openapi/testify/enable/yaml/v2 v2.4.2/go.mod h1:XVevPw5hUXuV+5AkI1u1PeAm27EQVrhXTTCPAF85LmE=
github.com/go-openapi/testify/v2 v2.4.2 h1:tiByHpvE9uHrrKjOszax7ZvKB7QOgizBWGBLuq0ePx4=
github.com/go-openapi/testify/v2 v2.4.2/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw=
github.com/go-openapi/validate v0.25.2 h1:12NsfLAwGegqbGWr2CnvT65X/Q2USJipmJ9b7xDJZz0=
github.com/go-openapi/validate v0.25.2/go.mod h1:Pgl1LpPPGFnZ+ys4/hTlDiRYQdI1ocKypgE+8Q8BLfY=
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
@@ -216,8 +216,8 @@ go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab h1:628ME69lBm9C6JY2wXhAph/yjN3jezx1z7BIDLUwxjo=
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
@@ -227,8 +227,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -245,8 +245,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -258,8 +258,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=

View File

@@ -110,6 +110,17 @@ func (a *Application) getTraefikForwardUrl(r *http.Request) (*url.URL, error) {
// getNginxForwardUrl See https://github.com/kubernetes/ingress-nginx/blob/main/rootfs/etc/nginx/template/nginx.tmpl
func (a *Application) getNginxForwardUrl(r *http.Request) (*url.URL, error) {
ou := r.Header.Get("X-Original-URI")
if ou != "" {
// Turn this full URL into a relative URL
u := &url.URL{
Host: "",
Scheme: "",
Path: ou,
}
a.log.WithField("url", u.String()).Info("building forward URL from X-Original-URI")
return u, nil
}
h := r.Header.Get("X-Original-URL")
if len(h) < 1 {
return nil, errors.New("no forward URL found")

View File

@@ -5,8 +5,10 @@ import (
"net/http/httptest"
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"goauthentik.io/internal/outpost/proxyv2/constants"
"goauthentik.io/internal/outpost/proxyv2/types"
api "goauthentik.io/packages/client-go"
)
@@ -45,6 +47,67 @@ func TestForwardHandleNginx_Single_Headers(t *testing.T) {
assert.Equal(t, "http://test.goauthentik.io/app", s.Values[constants.SessionRedirect])
}
func TestForwardHandleNginx_Single_URI(t *testing.T) {
a := newTestApplication()
req, _ := http.NewRequest("GET", "https://foo.bar/outpost.goauthentik.io/auth/nginx", nil)
req.Header.Set("X-Original-URI", "/app")
rr := httptest.NewRecorder()
a.forwardHandleNginx(rr, req)
assert.Equal(t, http.StatusUnauthorized, rr.Code)
s, _ := a.sessions.Get(req, a.SessionName())
assert.Equal(t, "/app", s.Values[constants.SessionRedirect])
}
func TestForwardHandleNginx_Single_Claims(t *testing.T) {
a := newTestApplication()
req, _ := http.NewRequest("GET", "/outpost.goauthentik.io/auth/nginx", nil)
req.Header.Set("X-Original-URI", "/")
rr := httptest.NewRecorder()
a.forwardHandleNginx(rr, req)
s, _ := a.sessions.Get(req, a.SessionName())
s.ID = uuid.New().String()
s.Options.MaxAge = 86400
s.Values[constants.SessionClaims] = types.Claims{
Sub: "foo",
Proxy: &types.ProxyClaims{
UserAttributes: map[string]any{
"username": "foo",
"password": "bar",
"additionalHeaders": map[string]any{
"foo": "bar",
},
},
},
}
err := a.sessions.Save(req, rr, s)
if err != nil {
panic(err)
}
rr = httptest.NewRecorder()
a.forwardHandleNginx(rr, req)
h := rr.Result().Header
assert.Equal(t, []string{"Basic Zm9vOmJhcg=="}, h["Authorization"])
assert.Equal(t, []string{"bar"}, h["Foo"])
assert.Equal(t, []string{""}, h["User-Agent"])
assert.Equal(t, []string{""}, h["X-Authentik-Email"])
assert.Equal(t, []string{""}, h["X-Authentik-Groups"])
assert.Equal(t, []string{""}, h["X-Authentik-Jwt"])
assert.Equal(t, []string{""}, h["X-Authentik-Meta-App"])
assert.Equal(t, []string{""}, h["X-Authentik-Meta-Jwks"])
assert.Equal(t, []string{""}, h["X-Authentik-Meta-Outpost"])
assert.Equal(t, []string{""}, h["X-Authentik-Name"])
assert.Equal(t, []string{"foo"}, h["X-Authentik-Uid"])
assert.Equal(t, []string{""}, h["X-Authentik-Username"])
}
func TestForwardHandleNginx_Domain_Blank(t *testing.T) {
a := newTestApplication()
a.proxyConfig.Mode = api.PROXYMODE_FORWARD_DOMAIN.Ptr()

View File

@@ -38,10 +38,6 @@ function run_authentik {
echo cargo run -- "$@"
fi
;;
manage)
shift 1
echo python -m manage "$@"
;;
*)
echo "$@"
;;

View File

@@ -13,8 +13,8 @@
"cross-env": "^10.1.0"
},
"engines": {
"node": ">=24",
"npm": ">=11.10.1"
"node": ">=20",
"npm": ">=11.6.2"
}
},
"node_modules/@epic-web/invariant": {

View File

@@ -11,20 +11,7 @@
"cross-env": "^10.1.0"
},
"engines": {
"node": ">=24",
"npm": ">=11.14.1"
},
"devEngines": {
"runtime": {
"name": "node",
"onFail": "warn",
"version": ">=24"
},
"packageManager": {
"name": "npm",
"version": ">=11.14.1",
"onFail": "warn"
}
},
"packageManager": "npm@11.14.1+sha512.6a8a4d67478497a2dbc6815cad72e64c43f33413717e242756047d466241ab39bee61e691683a64658e94496ec5f1a1c05e4a5ec62dcc773280dfd949443a367"
"node": ">=20",
"npm": ">=11.6.2"
}
}

View File

@@ -1,23 +1,12 @@
# syntax=docker/dockerfile:1
# Stage: Build webui
FROM --platform=${BUILDPLATFORM} docker.io/library/node:24-trixie-slim@sha256:4f2b45e32dc7d2caf66b6dbd59fac50e32f8077769efe0ef4d4c3f114672537d AS node-builder
FROM --platform=${BUILDPLATFORM} docker.io/library/node:24-trixie-slim@sha256:735dd688da64d22ebd9dd374b3e7e5a874635668fd2a6ec20ca1f99264294086 AS node-builder
ARG GIT_BUILD_HASH
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
ENV NODE_ENV=production
WORKDIR /work
RUN --mount=type=bind,target=/work/package.json,src=./package.json \
--mount=type=bind,target=/work/package-lock.json,src=./package-lock.json \
--mount=type=bind,target=/work/web/package.json,src=./web/package.json \
--mount=type=bind,target=/work/web/package-lock.json,src=./web/package-lock.json \
--mount=type=bind,target=/work/scripts/node/,src=./scripts/node/ \
--mount=type=bind,target=/work/packages/logger-js/,src=./packages/logger-js/ \
node ./scripts/node/setup-corepack.mjs --force && \
node ./scripts/node/lint-runtime.mjs ./web
WORKDIR /work/web
# These files need to be copied and cannot be mounted as `npm ci` will build the client's typescript
@@ -29,7 +18,7 @@ RUN --mount=type=bind,target=/work/web/package.json,src=./web/package.json \
--mount=type=bind,target=/work/web/packages/sfe/package.json,src=./web/packages/sfe/package.json \
--mount=type=bind,target=/work/web/scripts,src=./web/scripts \
--mount=type=cache,id=npm-ak,sharing=shared,target=/root/.npm \
corepack npm ci
npm ci
COPY ./package.json /work
COPY ./web /work/web/
@@ -239,7 +228,8 @@ RUN apt-get update && \
# Required for runtime
apt-get install -y --no-install-recommends \
libpq5 libmaxminddb0 ca-certificates \
libkadm5clnt-mit12 libkadm5clnt7t64-heimdal \
krb5-multidev libkrb5-3 libkdb5-10 libkadm5clnt-mit12 \
heimdal-multidev libkadm5clnt7t64-heimdal \
libltdl7 libxslt1.1 && \
# Required for bootstrap & healtcheck
apt-get install -y --no-install-recommends runit && \

View File

@@ -10,22 +10,12 @@ WORKDIR /static
COPY ./packages /packages
COPY ./web/packages /static/packages
RUN --mount=type=bind,target=/static/package.json,src=./package.json \
--mount=type=bind,target=/static/package-lock.json,src=./package-lock.json \
--mount=type=bind,target=/static/web/package.json,src=./web/package.json \
--mount=type=bind,target=/static/web/package-lock.json,src=./web/package-lock.json \
--mount=type=bind,target=/static/scripts/node/,src=./scripts/node/ \
--mount=type=bind,target=/static/packages/logger-js/,src=./packages/logger-js/ \
node ./scripts/node/setup-corepack.mjs --force && \
node ./scripts/node/lint-runtime.mjs ./web
COPY package.json /
RUN --mount=type=bind,target=/static/package.json,src=./web/package.json \
--mount=type=bind,target=/static/package-lock.json,src=./web/package-lock.json \
--mount=type=bind,target=/static/scripts,src=./web/scripts \
--mount=type=cache,target=/root/.npm \
corepack npm ci
npm ci
COPY web .
RUN npm run build-proxy

Binary file not shown.

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-05-13 05:39+0000\n"
"POT-Creation-Date: 2026-05-06 00:27+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -226,10 +226,6 @@ msgstr ""
msgid "The slug '{slug}' is reserved and cannot be used for applications."
msgstr ""
#: authentik/core/api/groups.py
msgid "User does not have permission to add members to this group."
msgstr ""
#: authentik/core/api/providers.py
msgid ""
"When not set all providers are returned. When set to true, only backchannel "
@@ -260,14 +256,6 @@ msgstr ""
msgid "Setting a user to internal service account is not allowed."
msgstr ""
#: authentik/core/api/users.py
msgid "User does not have permission to add members to a superuser group."
msgstr ""
#: authentik/core/api/users.py
msgid "User does not have permission to assign roles."
msgstr ""
#: authentik/core/api/users.py
msgid "Can't modify internal service account users"
msgstr ""

View File

@@ -11,4 +11,3 @@ Naur
Wärting
Aadit
Kilby
Kahmen

View File

@@ -164,4 +164,3 @@ yamltags
zxcvbn
~uuid
~uuids
wreply

Binary file not shown.

Binary file not shown.

17773
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,9 +3,9 @@
"version": "2026.8.0-rc1",
"private": true,
"scripts": {
"lint": "run-s lint:runtime lint:spellcheck lint:lockfile",
"lint:lockfile": "node ./scripts/node/lint-lockfile.mjs",
"lint:runtime": "node ./scripts/node/lint-runtime.mjs",
"lint": "run-s lint:spellcheck lint:lockfile",
"lint:lockfile": "echo 'Skipping lockfile linting'",
"lint:node": "echo 'Skipping node linting'",
"lint:spellcheck": "cspell . --config cspell.config.jsonc"
},
"type": "module",
@@ -14,7 +14,6 @@
},
"dependencies": {
"@eslint/js": "^9.39.3",
"@goauthentik/docusaurus-config": "./packages/docusaurus-config",
"@goauthentik/eslint-config": "./packages/eslint-config",
"@goauthentik/logger-js": "./packages/logger-js",
"@goauthentik/prettier-config": "./packages/prettier-config",
@@ -24,15 +23,15 @@
"npm-run-all": "^4.1.5",
"pino": "^10.3.1",
"pino-pretty": "^13.1.2",
"prettier": "^3.8.3",
"prettier": "^3.8.1",
"prettier-plugin-packagejson": "^3.0.0",
"typescript": "^6.0.3",
"typescript-eslint": "^8.59.3"
"typescript": "^6.0.2",
"typescript-eslint": "^8.57.2"
},
"workspaces": [],
"engines": {
"node": ">=24",
"npm": ">=11.14.1"
"npm": ">=11.10.1"
},
"devEngines": {
"runtime": {
@@ -42,11 +41,11 @@
},
"packageManager": {
"name": "npm",
"version": ">=11.14.1",
"version": ">=11.10.1",
"onFail": "warn"
}
},
"packageManager": "npm@11.14.1+sha512.6a8a4d67478497a2dbc6815cad72e64c43f33413717e242756047d466241ab39bee61e691683a64658e94496ec5f1a1c05e4a5ec62dcc773280dfd949443a367",
"packageManager": "npm@11.11.0+sha512.f36811c4aae1fde639527368ae44c571d050006a608d67a191f195a801a52637a312d259186254aa3a3799b05335b7390539cf28656d18f0591a1125ba35f973",
"prettier": "@goauthentik/prettier-config",
"overrides": {
"@typescript-eslint/eslint-plugin": {

View File

@@ -12,8 +12,8 @@
* Do not edit the class manually.
*/
import type { Provider } from "./Provider";
import { ProviderFromJSON, ProviderToJSON } from "./Provider";
import type { OAuth2Provider } from "./OAuth2Provider";
import { OAuth2ProviderFromJSON, OAuth2ProviderToJSON } from "./OAuth2Provider";
import type { User } from "./User";
import { UserFromJSON, UserToJSON } from "./User";
@@ -31,10 +31,10 @@ export interface ExpiringBaseGrantModel {
readonly pk: number;
/**
*
* @type {Provider}
* @type {OAuth2Provider}
* @memberof ExpiringBaseGrantModel
*/
provider: Provider;
provider: OAuth2Provider;
/**
*
* @type {User}
@@ -86,7 +86,7 @@ export function ExpiringBaseGrantModelFromJSONTyped(
}
return {
pk: json["pk"],
provider: ProviderFromJSON(json["provider"]),
provider: OAuth2ProviderFromJSON(json["provider"]),
user: UserFromJSON(json["user"]),
isExpired: json["is_expired"],
expires: json["expires"] == null ? undefined : new Date(json["expires"]),
@@ -107,7 +107,7 @@ export function ExpiringBaseGrantModelToJSONTyped(
}
return {
provider: ProviderToJSON(value["provider"]),
provider: OAuth2ProviderToJSON(value["provider"]),
user: UserToJSON(value["user"]),
expires: value["expires"] == null ? value["expires"] : value["expires"].toISOString(),
scope: value["scope"],

View File

@@ -19,7 +19,6 @@
export const SCIMAuthenticationModeEnum = {
Token: "token",
Oauth: "oauth",
OauthInteractive: "oauth_interactive",
UnknownDefaultOpenApi: "11184809",
} as const;
export type SCIMAuthenticationModeEnum =

View File

@@ -125,30 +125,6 @@ export interface SCIMProvider {
* @memberof SCIMProvider
*/
authOauthParams?: { [key: string]: any };
/**
*
* @type {Date}
* @memberof SCIMProvider
*/
readonly authOauthTokenLastUpdated: Date | null;
/**
*
* @type {Date}
* @memberof SCIMProvider
*/
readonly authOauthTokenExpires: Date | null;
/**
*
* @type {string}
* @memberof SCIMProvider
*/
readonly authOauthUrlCallback: string | null;
/**
*
* @type {string}
* @memberof SCIMProvider
*/
readonly authOauthUrlStart: string | null;
/**
* Alter authentik behavior for vendor-specific SCIM implementations.
* @type {CompatibilityModeEnum}
@@ -214,13 +190,6 @@ export function instanceOfSCIMProvider(value: object): value is SCIMProvider {
if (!("verboseNamePlural" in value) || value["verboseNamePlural"] === undefined) return false;
if (!("metaModelName" in value) || value["metaModelName"] === undefined) return false;
if (!("url" in value) || value["url"] === undefined) return false;
if (!("authOauthTokenLastUpdated" in value) || value["authOauthTokenLastUpdated"] === undefined)
return false;
if (!("authOauthTokenExpires" in value) || value["authOauthTokenExpires"] === undefined)
return false;
if (!("authOauthUrlCallback" in value) || value["authOauthUrlCallback"] === undefined)
return false;
if (!("authOauthUrlStart" in value) || value["authOauthUrlStart"] === undefined) return false;
return true;
}
@@ -254,16 +223,6 @@ export function SCIMProviderFromJSONTyped(json: any, ignoreDiscriminator: boolea
: SCIMAuthenticationModeEnumFromJSON(json["auth_mode"]),
authOauth: json["auth_oauth"] == null ? undefined : json["auth_oauth"],
authOauthParams: json["auth_oauth_params"] == null ? undefined : json["auth_oauth_params"],
authOauthTokenLastUpdated:
json["auth_oauth_token_last_updated"] == null
? null
: new Date(json["auth_oauth_token_last_updated"]),
authOauthTokenExpires:
json["auth_oauth_token_expires"] == null
? null
: new Date(json["auth_oauth_token_expires"]),
authOauthUrlCallback: json["auth_oauth_url_callback"],
authOauthUrlStart: json["auth_oauth_url_start"],
compatibilityMode:
json["compatibility_mode"] == null
? undefined
@@ -297,10 +256,6 @@ export function SCIMProviderToJSONTyped(
| "verbose_name"
| "verbose_name_plural"
| "meta_model_name"
| "auth_oauth_token_last_updated"
| "auth_oauth_token_expires"
| "auth_oauth_url_callback"
| "auth_oauth_url_start"
> | null,
ignoreDiscriminator: boolean = false,
): any {

View File

@@ -12,8 +12,8 @@
* Do not edit the class manually.
*/
import type { Provider } from "./Provider";
import { ProviderFromJSON, ProviderToJSON } from "./Provider";
import type { OAuth2Provider } from "./OAuth2Provider";
import { OAuth2ProviderFromJSON, OAuth2ProviderToJSON } from "./OAuth2Provider";
import type { User } from "./User";
import { UserFromJSON, UserToJSON } from "./User";
@@ -31,10 +31,10 @@ export interface TokenModel {
readonly pk: number;
/**
*
* @type {Provider}
* @type {OAuth2Provider}
* @memberof TokenModel
*/
provider: Provider;
provider: OAuth2Provider;
/**
*
* @type {User}
@@ -96,7 +96,7 @@ export function TokenModelFromJSONTyped(json: any, ignoreDiscriminator: boolean)
}
return {
pk: json["pk"],
provider: ProviderFromJSON(json["provider"]),
provider: OAuth2ProviderFromJSON(json["provider"]),
user: UserFromJSON(json["user"]),
isExpired: json["is_expired"],
expires: json["expires"] == null ? undefined : new Date(json["expires"]),
@@ -119,7 +119,7 @@ export function TokenModelToJSONTyped(
}
return {
provider: ProviderToJSON(value["provider"]),
provider: OAuth2ProviderToJSON(value["provider"]),
user: UserToJSON(value["user"]),
expires: value["expires"] == null ? value["expires"] : value["expires"].toISOString(),
scope: value["scope"],

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "@goauthentik/docusaurus-config",
"version": "3.0.0",
"version": "2.7.0",
"description": "authentik's Docusaurus config",
"license": "MIT",
"repository": {
@@ -30,33 +30,33 @@
"prism-react-renderer": "^2.4.1"
},
"devDependencies": {
"@docusaurus/theme-common": "^3.10.0",
"@docusaurus/theme-common": "3.10.0",
"@docusaurus/theme-search-algolia": "^3.10.0",
"@docusaurus/types": "^3.10.0",
"@eslint/js": "^9.39.3",
"@goauthentik/eslint-config": "../eslint-config",
"@goauthentik/prettier-config": "../prettier-config",
"@goauthentik/tsconfig": "../tsconfig",
"@types/node": "^25.7.0",
"@types/node": "^25.5.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"eslint": "^9.39.3",
"pino": "^10.3.1",
"prettier": "^3.8.3",
"prettier": "^3.8.1",
"prettier-plugin-packagejson": "^3.0.2",
"typescript": "^6.0.3",
"typescript-eslint": "^8.59.3"
"typescript": "^6.0.2",
"typescript-eslint": "^8.57.2"
},
"peerDependencies": {
"@docusaurus/theme-common": "^3.10.1",
"@docusaurus/theme-search-algolia": "^3.10.1",
"@docusaurus/types": "^3.10.1",
"react": ">=19",
"react-dom": ">=19"
"@docusaurus/theme-common": "^3.9.2",
"@docusaurus/theme-search-algolia": "^3.9.2",
"@docusaurus/types": "^3.9.2",
"react": ">=18",
"react-dom": ">=18"
},
"optionalDependencies": {
"react": ">=19",
"react-dom": ">=19"
"react": ">=18",
"react-dom": ">=18"
},
"files": [
"./index.js",
@@ -66,7 +66,7 @@
],
"engines": {
"node": ">=24",
"npm": ">=11.14.1"
"npm": ">=11.10.1"
},
"devEngines": {
"runtime": {
@@ -76,7 +76,7 @@
},
"packageManager": {
"name": "npm",
"version": ">=11.14.1",
"version": ">=11.10.1",
"onFail": "warn"
}
},

View File

@@ -17,20 +17,20 @@
"@goauthentik/logger-js": "../logger-js",
"@goauthentik/prettier-config": "../prettier-config",
"@goauthentik/tsconfig": "../tsconfig",
"@types/node": "^25.7.0",
"@types/node": "^25.3.0",
"esbuild": "^0.27.4",
"eslint": "^9.39.3",
"pino": "^10.3.1",
"prettier": "^3.8.3",
"prettier": "^3.8.1",
"prettier-plugin-packagejson": "^3.0.2",
"typedoc": "^0.28.18",
"typedoc-plugin-markdown": "^4.11.0",
"typescript": "^6.0.3",
"typescript-eslint": "^8.59.3"
"typescript": "^6.0.2",
"typescript-eslint": "^8.57.2"
},
"engines": {
"node": ">=24",
"npm": ">=11.14.1"
"npm": ">=11.10.1"
},
"peerDependencies": {
"@goauthentik/logger-js": "^1.0.1",
@@ -44,7 +44,7 @@
},
"../eslint-config": {
"name": "@goauthentik/eslint-config",
"version": "2.0.0",
"version": "1.3.0",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -52,26 +52,26 @@
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-lit": "^2.2.1",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-wc": "^3.1.0"
},
"devDependencies": {
"@goauthentik/prettier-config": "../prettier-config",
"@goauthentik/tsconfig": "../tsconfig",
"@types/eslint": "^9.6.1",
"@types/node": "^25.7.0",
"typescript": "^6.0.3",
"typescript-eslint": "^8.59.3"
"@types/node": "^25.5.0",
"typescript": "^6.0.2",
"typescript-eslint": "^8.57.2"
},
"engines": {
"node": ">=24",
"npm": ">=11.14.1"
"npm": ">=11.10.1"
},
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0",
"typescript": "^6.0.3 || ^7.0.0",
"typescript-eslint": "^8.59.3"
"typescript": "^5.9.3 || ^6.0.2",
"typescript-eslint": "^8.57.2"
},
"peerDependenciesMeta": {
"react": {
@@ -84,25 +84,25 @@
},
"../logger-js": {
"name": "@goauthentik/logger-js",
"version": "2.0.0",
"version": "1.1.1",
"dev": true,
"license": "MIT",
"devDependencies": {
"@eslint/js": "^9.39.3",
"@goauthentik/prettier-config": "../prettier-config",
"@goauthentik/tsconfig": "../tsconfig",
"@types/node": "^25.7.0",
"@types/node": "^25.5.0",
"eslint": "^9.39.3",
"pino": "^10.3.1",
"pino-pretty": "^13.1.2",
"prettier": "^3.8.3",
"prettier": "^3.8.1",
"prettier-plugin-packagejson": "^3.0.2",
"typescript": "^6.0.3",
"typescript-eslint": "^8.59.3"
"typescript": "^6.0.2",
"typescript-eslint": "^8.57.2"
},
"engines": {
"node": ">=24",
"npm": ">=11.14.1"
"npm": ">=11.10.1"
},
"peerDependencies": {
"pino": "^10.3.1",
@@ -119,7 +119,7 @@
},
"../prettier-config": {
"name": "@goauthentik/prettier-config",
"version": "4.0.0",
"version": "3.5.0",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -129,30 +129,30 @@
"@eslint/js": "^9.39.3",
"@goauthentik/eslint-config": "../eslint-config",
"@goauthentik/tsconfig": "../tsconfig",
"@types/node": "^25.7.0",
"@types/node": "^25.0.0",
"eslint": "^9.39.3",
"prettier": "^3.8.3",
"prettier": "^3.8.1",
"prettier-plugin-packagejson": "^3.0.2",
"typescript": "^6.0.3",
"typescript-eslint": "^8.59.3"
"typescript": "^6.0.2",
"typescript-eslint": "^8.57.2"
},
"engines": {
"node": ">=24",
"npm": ">=11.14.1"
"npm": ">=11.10.1"
},
"peerDependencies": {
"prettier": "^3.8.3",
"prettier": "^3.8.1",
"prettier-plugin-packagejson": "^3.0.2"
}
},
"../tsconfig": {
"name": "@goauthentik/tsconfig",
"version": "1.0.9",
"version": "1.0.8",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=24",
"npm": ">=11.14.1"
"npm": ">=11.10.1"
}
},
"node_modules/@esbuild/aix-ppc64": {
@@ -904,13 +904,13 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "25.7.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.7.0.tgz",
"integrity": "sha512-z+pdZyxE+RTQE9AcboAZCb4otwcrvgHD+GlBpPgn0emDVt0ohrTMhAwlr2Wd9nZ+nihhYFxO2pThz3C5qSu2Eg==",
"version": "25.5.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz",
"integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~7.21.0"
"undici-types": "~7.18.0"
}
},
"node_modules/@types/unist": {
@@ -921,20 +921,20 @@
"license": "MIT"
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.3.tgz",
"integrity": "sha512-PwFvSKsXGShKGW6n5bZOhGHEcCZXM8HofLK9fNsEwZXzFRjoY+XT1Vsf1zgyXdwTr0ZYz1/2tkZ0DBTT9jZjhw==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.2.tgz",
"integrity": "sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.12.2",
"@typescript-eslint/scope-manager": "8.59.3",
"@typescript-eslint/type-utils": "8.59.3",
"@typescript-eslint/utils": "8.59.3",
"@typescript-eslint/visitor-keys": "8.59.3",
"@typescript-eslint/scope-manager": "8.57.2",
"@typescript-eslint/type-utils": "8.57.2",
"@typescript-eslint/utils": "8.57.2",
"@typescript-eslint/visitor-keys": "8.57.2",
"ignore": "^7.0.5",
"natural-compare": "^1.4.0",
"ts-api-utils": "^2.5.0"
"ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -944,9 +944,9 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"@typescript-eslint/parser": "^8.59.3",
"@typescript-eslint/parser": "^8.57.2",
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
@@ -960,16 +960,16 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.3.tgz",
"integrity": "sha512-HPwA+hVkfcriajbNvTmZv4VRauibay+cWArYUYq7u7W7PmGShMxbPxLvrwDme55a6d5alG3nrYfhyJ/G28XlLg==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.2.tgz",
"integrity": "sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.59.3",
"@typescript-eslint/types": "8.59.3",
"@typescript-eslint/typescript-estree": "8.59.3",
"@typescript-eslint/visitor-keys": "8.59.3",
"@typescript-eslint/scope-manager": "8.57.2",
"@typescript-eslint/types": "8.57.2",
"@typescript-eslint/typescript-estree": "8.57.2",
"@typescript-eslint/visitor-keys": "8.57.2",
"debug": "^4.4.3"
},
"engines": {
@@ -981,18 +981,18 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/project-service": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.3.tgz",
"integrity": "sha512-ECiUWa/KYRGDFUqTNehaRgzDshnJfkTABJxVemHk4ko22gcr0ukloKjWvyQ64g8YCV/UI47kN1dbmjf/GaQYng==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.2.tgz",
"integrity": "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.59.3",
"@typescript-eslint/types": "^8.59.3",
"@typescript-eslint/tsconfig-utils": "^8.57.2",
"@typescript-eslint/types": "^8.57.2",
"debug": "^4.4.3"
},
"engines": {
@@ -1003,18 +1003,18 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.3.tgz",
"integrity": "sha512-t2LvZnoEfzKtnPjgeEu41xw5gxq9mQVfYy4OoZ4Vlt0sk3JwxmhCca/AR7DwOiHrjWgjAj6as4AhRLKSDfvZIA==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz",
"integrity": "sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.59.3",
"@typescript-eslint/visitor-keys": "8.59.3"
"@typescript-eslint/types": "8.57.2",
"@typescript-eslint/visitor-keys": "8.57.2"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1025,9 +1025,9 @@
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.3.tgz",
"integrity": "sha512-PcIJHjmaREXLgIAIzLnSY9VucEzz8FKXsRgFa1DmdGCK/5tJpW03TKJF01Q6VZd1lLdz2sIKPWaDUZN9dp//dw==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz",
"integrity": "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1038,21 +1038,21 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.3.tgz",
"integrity": "sha512-g71d8QD8UaiHGvrJwyIS1hCX5r63w6Jll+4VEYhEAHXTDIqX1JgxhTAbEHtKntL9kuc4jRo7/GWw5xfCepSccQ==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.2.tgz",
"integrity": "sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.59.3",
"@typescript-eslint/typescript-estree": "8.59.3",
"@typescript-eslint/utils": "8.59.3",
"@typescript-eslint/types": "8.57.2",
"@typescript-eslint/typescript-estree": "8.57.2",
"@typescript-eslint/utils": "8.57.2",
"debug": "^4.4.3",
"ts-api-utils": "^2.5.0"
"ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1063,13 +1063,13 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/types": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.3.tgz",
"integrity": "sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz",
"integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1081,21 +1081,21 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.3.tgz",
"integrity": "sha512-CbRjVRAf7Lr9Kr8RopKcbY45p2VfmmHrm0ygOCYFi7oU8q19m0Fs/6iHS7kNOmwpp+ob07ZVcAqlxUod9lYdmg==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz",
"integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.59.3",
"@typescript-eslint/tsconfig-utils": "8.59.3",
"@typescript-eslint/types": "8.59.3",
"@typescript-eslint/visitor-keys": "8.59.3",
"@typescript-eslint/project-service": "8.57.2",
"@typescript-eslint/tsconfig-utils": "8.57.2",
"@typescript-eslint/types": "8.57.2",
"@typescript-eslint/visitor-keys": "8.57.2",
"debug": "^4.4.3",
"minimatch": "^10.2.2",
"semver": "^7.7.3",
"tinyglobby": "^0.2.15",
"ts-api-utils": "^2.5.0"
"ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1105,7 +1105,7 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": {
@@ -1119,9 +1119,9 @@
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
"version": "5.0.6",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
"integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
"integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1132,13 +1132,13 @@
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
"version": "10.2.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
"integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
"version": "10.2.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
"integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"brace-expansion": "^5.0.5"
"brace-expansion": "^5.0.2"
},
"engines": {
"node": "18 || 20 || >=22"
@@ -1148,16 +1148,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.3.tgz",
"integrity": "sha512-JAvT14goBzRzzzZyqq3P9BLArIxTtQURUtFgQ/V7FO+eU+Gg6ES+5ymOPP1wRxXcxAYeivCk4uS3jCKWI1K8Zg==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.2.tgz",
"integrity": "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.9.1",
"@typescript-eslint/scope-manager": "8.59.3",
"@typescript-eslint/types": "8.59.3",
"@typescript-eslint/typescript-estree": "8.59.3"
"@typescript-eslint/scope-manager": "8.57.2",
"@typescript-eslint/types": "8.57.2",
"@typescript-eslint/typescript-estree": "8.57.2"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1168,17 +1168,17 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.3.tgz",
"integrity": "sha512-f1UQF7ggd42YiwI5wGrRaPsa+P0CINBlrkLPmGfpq/u/I/oVtecoEIfFR9ag/oa1sLOsRNZ6xehf6qMZhQGBDg==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz",
"integrity": "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.59.3",
"@typescript-eslint/types": "8.57.2",
"eslint-visitor-keys": "^5.0.0"
},
"engines": {
@@ -2164,9 +2164,9 @@
}
},
"node_modules/prettier": {
"version": "3.8.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz",
"integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==",
"version": "3.8.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz",
"integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
"dev": true,
"license": "MIT",
"bin": {
@@ -2515,9 +2515,9 @@
}
},
"node_modules/typescript": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz",
"integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -2529,16 +2529,16 @@
}
},
"node_modules/typescript-eslint": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.3.tgz",
"integrity": "sha512-KgusgyDgG4LI8Ih/sWaCtZ06tckLAS5CvT5A4D1Q7bYVoAAyzwiZvE4BmwDHkhRVkvhRBepKeASoFzQetha7Fg==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.2.tgz",
"integrity": "sha512-VEPQ0iPgWO/sBaZOU1xo4nuNdODVOajPnTIbog2GKYr31nIlZ0fWPoCQgGfF3ETyBl1vn63F/p50Um9Z4J8O8A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/eslint-plugin": "8.59.3",
"@typescript-eslint/parser": "8.59.3",
"@typescript-eslint/typescript-estree": "8.59.3",
"@typescript-eslint/utils": "8.59.3"
"@typescript-eslint/eslint-plugin": "8.57.2",
"@typescript-eslint/parser": "8.57.2",
"@typescript-eslint/typescript-estree": "8.57.2",
"@typescript-eslint/utils": "8.57.2"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2549,7 +2549,7 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/uc.micro": {
@@ -2560,9 +2560,9 @@
"license": "MIT"
},
"node_modules/undici-types": {
"version": "7.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.21.0.tgz",
"integrity": "sha512-w9IMgQrz4O0YN1LtB7K5P63vhlIOvC7opSmouCJ+ZywlPAlO9gIkJ+otk6LvGpAs2wg4econaCz3TvQ9xPoyuQ==",
"version": "7.18.2",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
"dev": true,
"license": "MIT"
},

View File

@@ -49,16 +49,16 @@
"@goauthentik/logger-js": "../logger-js",
"@goauthentik/prettier-config": "../prettier-config",
"@goauthentik/tsconfig": "../tsconfig",
"@types/node": "^25.7.0",
"@types/node": "^25.3.0",
"esbuild": "^0.27.4",
"eslint": "^9.39.3",
"pino": "^10.3.1",
"prettier": "^3.8.3",
"prettier": "^3.8.1",
"prettier-plugin-packagejson": "^3.0.2",
"typedoc": "^0.28.18",
"typedoc-plugin-markdown": "^4.11.0",
"typescript": "^6.0.3",
"typescript-eslint": "^8.59.3"
"typescript": "^6.0.2",
"typescript-eslint": "^8.57.2"
},
"peerDependencies": {
"@goauthentik/logger-js": "^1.0.1",
@@ -73,7 +73,7 @@
],
"engines": {
"node": ">=24",
"npm": ">=11.14.1"
"npm": ">=11.10.1"
},
"devEngines": {
"runtime": {
@@ -83,7 +83,7 @@
},
"packageManager": {
"name": "npm",
"version": ">=11.14.1",
"version": ">=11.10.1",
"onFail": "warn"
}
},

View File

@@ -1,38 +1,38 @@
{
"name": "@goauthentik/eslint-config",
"version": "2.0.0",
"version": "1.3.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@goauthentik/eslint-config",
"version": "2.0.0",
"version": "1.3.0",
"license": "MIT",
"dependencies": {
"eslint": "^9.39.3",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-lit": "^2.3.1",
"eslint-plugin-lit": "^2.2.1",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-wc": "^3.1.0"
},
"devDependencies": {
"@goauthentik/prettier-config": "../prettier-config",
"@goauthentik/tsconfig": "../tsconfig",
"@types/eslint": "^9.6.1",
"@types/node": "^25.7.0",
"typescript": "^6.0.3",
"typescript-eslint": "^8.59.3"
"@types/node": "^25.5.0",
"typescript": "^6.0.2",
"typescript-eslint": "^8.57.2"
},
"engines": {
"node": ">=24",
"npm": ">=11.14.1"
"npm": ">=11.10.1"
},
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0",
"typescript": "^6.0.3 || ^7.0.0",
"typescript-eslint": "^8.59.3"
"typescript": "^5.9.3 || ^6.0.2",
"typescript-eslint": "^8.57.2"
},
"peerDependenciesMeta": {
"react": {
@@ -45,7 +45,7 @@
},
"../prettier-config": {
"name": "@goauthentik/prettier-config",
"version": "4.0.0",
"version": "3.5.0",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -55,30 +55,30 @@
"@eslint/js": "^9.39.3",
"@goauthentik/eslint-config": "../eslint-config",
"@goauthentik/tsconfig": "../tsconfig",
"@types/node": "^25.7.0",
"@types/node": "^25.0.0",
"eslint": "^9.39.3",
"prettier": "^3.8.3",
"prettier": "^3.8.1",
"prettier-plugin-packagejson": "^3.0.2",
"typescript": "^6.0.3",
"typescript-eslint": "^8.59.3"
"typescript": "^6.0.2",
"typescript-eslint": "^8.57.2"
},
"engines": {
"node": ">=24",
"npm": ">=11.14.1"
"npm": ">=11.10.1"
},
"peerDependencies": {
"prettier": "^3.8.3",
"prettier": "^3.8.1",
"prettier-plugin-packagejson": "^3.0.2"
}
},
"../tsconfig": {
"name": "@goauthentik/tsconfig",
"version": "2.0.0",
"version": "1.0.8",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=24",
"npm": ">=11.14.1"
"npm": ">=11.10.1"
}
},
"node_modules/@babel/code-frame": {
@@ -576,30 +576,30 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "25.7.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.7.0.tgz",
"integrity": "sha512-z+pdZyxE+RTQE9AcboAZCb4otwcrvgHD+GlBpPgn0emDVt0ohrTMhAwlr2Wd9nZ+nihhYFxO2pThz3C5qSu2Eg==",
"version": "25.5.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz",
"integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~7.21.0"
"undici-types": "~7.18.0"
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.3.tgz",
"integrity": "sha512-PwFvSKsXGShKGW6n5bZOhGHEcCZXM8HofLK9fNsEwZXzFRjoY+XT1Vsf1zgyXdwTr0ZYz1/2tkZ0DBTT9jZjhw==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.2.tgz",
"integrity": "sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.12.2",
"@typescript-eslint/scope-manager": "8.59.3",
"@typescript-eslint/type-utils": "8.59.3",
"@typescript-eslint/utils": "8.59.3",
"@typescript-eslint/visitor-keys": "8.59.3",
"@typescript-eslint/scope-manager": "8.57.2",
"@typescript-eslint/type-utils": "8.57.2",
"@typescript-eslint/utils": "8.57.2",
"@typescript-eslint/visitor-keys": "8.57.2",
"ignore": "^7.0.5",
"natural-compare": "^1.4.0",
"ts-api-utils": "^2.5.0"
"ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -609,9 +609,9 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"@typescript-eslint/parser": "^8.59.3",
"@typescript-eslint/parser": "^8.57.2",
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
@@ -625,16 +625,16 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.3.tgz",
"integrity": "sha512-HPwA+hVkfcriajbNvTmZv4VRauibay+cWArYUYq7u7W7PmGShMxbPxLvrwDme55a6d5alG3nrYfhyJ/G28XlLg==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.2.tgz",
"integrity": "sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.59.3",
"@typescript-eslint/types": "8.59.3",
"@typescript-eslint/typescript-estree": "8.59.3",
"@typescript-eslint/visitor-keys": "8.59.3",
"@typescript-eslint/scope-manager": "8.57.2",
"@typescript-eslint/types": "8.57.2",
"@typescript-eslint/typescript-estree": "8.57.2",
"@typescript-eslint/visitor-keys": "8.57.2",
"debug": "^4.4.3"
},
"engines": {
@@ -646,18 +646,18 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/project-service": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.3.tgz",
"integrity": "sha512-ECiUWa/KYRGDFUqTNehaRgzDshnJfkTABJxVemHk4ko22gcr0ukloKjWvyQ64g8YCV/UI47kN1dbmjf/GaQYng==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.2.tgz",
"integrity": "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.59.3",
"@typescript-eslint/types": "^8.59.3",
"@typescript-eslint/tsconfig-utils": "^8.57.2",
"@typescript-eslint/types": "^8.57.2",
"debug": "^4.4.3"
},
"engines": {
@@ -668,18 +668,18 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.3.tgz",
"integrity": "sha512-t2LvZnoEfzKtnPjgeEu41xw5gxq9mQVfYy4OoZ4Vlt0sk3JwxmhCca/AR7DwOiHrjWgjAj6as4AhRLKSDfvZIA==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz",
"integrity": "sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.59.3",
"@typescript-eslint/visitor-keys": "8.59.3"
"@typescript-eslint/types": "8.57.2",
"@typescript-eslint/visitor-keys": "8.57.2"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -690,9 +690,9 @@
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.3.tgz",
"integrity": "sha512-PcIJHjmaREXLgIAIzLnSY9VucEzz8FKXsRgFa1DmdGCK/5tJpW03TKJF01Q6VZd1lLdz2sIKPWaDUZN9dp//dw==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz",
"integrity": "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -703,21 +703,21 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.3.tgz",
"integrity": "sha512-g71d8QD8UaiHGvrJwyIS1hCX5r63w6Jll+4VEYhEAHXTDIqX1JgxhTAbEHtKntL9kuc4jRo7/GWw5xfCepSccQ==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.2.tgz",
"integrity": "sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.59.3",
"@typescript-eslint/typescript-estree": "8.59.3",
"@typescript-eslint/utils": "8.59.3",
"@typescript-eslint/types": "8.57.2",
"@typescript-eslint/typescript-estree": "8.57.2",
"@typescript-eslint/utils": "8.57.2",
"debug": "^4.4.3",
"ts-api-utils": "^2.5.0"
"ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -728,13 +728,13 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/types": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.3.tgz",
"integrity": "sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz",
"integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -746,21 +746,21 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.3.tgz",
"integrity": "sha512-CbRjVRAf7Lr9Kr8RopKcbY45p2VfmmHrm0ygOCYFi7oU8q19m0Fs/6iHS7kNOmwpp+ob07ZVcAqlxUod9lYdmg==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz",
"integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.59.3",
"@typescript-eslint/tsconfig-utils": "8.59.3",
"@typescript-eslint/types": "8.59.3",
"@typescript-eslint/visitor-keys": "8.59.3",
"@typescript-eslint/project-service": "8.57.2",
"@typescript-eslint/tsconfig-utils": "8.57.2",
"@typescript-eslint/types": "8.57.2",
"@typescript-eslint/visitor-keys": "8.57.2",
"debug": "^4.4.3",
"minimatch": "^10.2.2",
"semver": "^7.7.3",
"tinyglobby": "^0.2.15",
"ts-api-utils": "^2.5.0"
"ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -770,7 +770,7 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": {
@@ -784,9 +784,9 @@
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
"version": "5.0.6",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
"integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
"integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -797,13 +797,13 @@
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
"version": "10.2.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
"integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
"version": "10.2.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
"integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"brace-expansion": "^5.0.5"
"brace-expansion": "^5.0.2"
},
"engines": {
"node": "18 || 20 || >=22"
@@ -813,9 +813,9 @@
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
"version": "7.8.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz",
"integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==",
"version": "7.7.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"dev": true,
"license": "ISC",
"bin": {
@@ -826,16 +826,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.3.tgz",
"integrity": "sha512-JAvT14goBzRzzzZyqq3P9BLArIxTtQURUtFgQ/V7FO+eU+Gg6ES+5ymOPP1wRxXcxAYeivCk4uS3jCKWI1K8Zg==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.2.tgz",
"integrity": "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.9.1",
"@typescript-eslint/scope-manager": "8.59.3",
"@typescript-eslint/types": "8.59.3",
"@typescript-eslint/typescript-estree": "8.59.3"
"@typescript-eslint/scope-manager": "8.57.2",
"@typescript-eslint/types": "8.57.2",
"@typescript-eslint/typescript-estree": "8.57.2"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -846,17 +846,17 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.3.tgz",
"integrity": "sha512-f1UQF7ggd42YiwI5wGrRaPsa+P0CINBlrkLPmGfpq/u/I/oVtecoEIfFR9ag/oa1sLOsRNZ6xehf6qMZhQGBDg==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz",
"integrity": "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.59.3",
"@typescript-eslint/types": "8.57.2",
"eslint-visitor-keys": "^5.0.0"
},
"engines": {
@@ -1431,37 +1431,6 @@
"node": ">=0.10.0"
}
},
"node_modules/domelementtype": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-3.0.0.tgz",
"integrity": "sha512-umCQid3jKbDmVjx8jGaW7uUykm4DEUeyV21hPxNMo2nV955DhUThwqyOIDtreepP31hl84X7G5U9ZfsWvIB3Pg==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
],
"license": "BSD-2-Clause",
"engines": {
"node": ">=20.19.0"
}
},
"node_modules/domhandler": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-6.0.1.tgz",
"integrity": "sha512-gYzvtM72ZtxQO0T048kd6HWSbbGCNOUwcnfQ01cqIJ4X2IYKFFHZ5mKvrQETcFXxsRObZulDaKmy//R7TPtsBg==",
"license": "BSD-2-Clause",
"dependencies": {
"domelementtype": "^3.0.0"
},
"engines": {
"node": ">=20.19.0"
},
"funding": {
"type": "github",
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -1482,18 +1451,6 @@
"integrity": "sha512-PwfIw7WQSt3xX7yOf5OE/unLzsK9CaN2f/FvV3WjPR1Knoc1T9vePRVV4W1EM301JzzysK51K7FNKcusCr0zYA==",
"license": "ISC"
},
"node_modules/entities": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz",
"integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=20.19.0"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/es-abstract": {
"version": "1.24.1",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz",
@@ -1833,13 +1790,13 @@
}
},
"node_modules/eslint-plugin-lit": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-lit/-/eslint-plugin-lit-2.3.1.tgz",
"integrity": "sha512-wAqDOOWQzUoY5uK124ZR+h7Snx0+KdZd3Knv6133gRHEcu03jbqnouK4GBwVl6PkGOkNy40zSchOp9VbBh4yHg==",
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-lit/-/eslint-plugin-lit-2.2.1.tgz",
"integrity": "sha512-mnqqwpWF4PBF/YjlGt9mbHwrWCGMtaqdpnqISv3nGcTl8iStaAt9UGieMY3i8vwKfSSWtkEfBZUcRKFGys6yiw==",
"license": "MIT",
"dependencies": {
"parse5": "^8.0.1",
"parse5-htmlparser2-tree-adapter": "^8.0.1"
"parse5": "^6.0.1",
"parse5-htmlparser2-tree-adapter": "^6.0.1"
},
"engines": {
"node": ">= 18"
@@ -1881,9 +1838,9 @@
}
},
"node_modules/eslint-plugin-react-hooks": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz",
"integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==",
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz",
"integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==",
"license": "MIT",
"dependencies": {
"@babel/core": "^7.24.4",
@@ -1896,7 +1853,7 @@
"node": ">=18"
},
"peerDependencies": {
"eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0"
"eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
}
},
"node_modules/eslint-plugin-react/node_modules/resolve": {
@@ -3237,28 +3194,18 @@
}
},
"node_modules/parse5": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz",
"integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==",
"license": "MIT",
"dependencies": {
"entities": "^8.0.0"
},
"funding": {
"url": "https://github.com/inikulin/parse5?sponsor=1"
}
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
"integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
"license": "MIT"
},
"node_modules/parse5-htmlparser2-tree-adapter": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-8.0.1.tgz",
"integrity": "sha512-aqkH+xQxX+QGZtP5GIMmqqp16Y0v9muEf2VVeypv35kFsD5bqP1UeBanSshdfjMY+RTh2vN4mmK6nEOVRMEvHg==",
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz",
"integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==",
"license": "MIT",
"dependencies": {
"domhandler": "^6.0.1",
"parse5": "^8.0.0"
},
"funding": {
"url": "https://github.com/inikulin/parse5?sponsor=1"
"parse5": "^6.0.1"
}
},
"node_modules/path-exists": {
@@ -3771,14 +3718,14 @@
}
},
"node_modules/tinyglobby": {
"version": "0.2.16",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
"integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==",
"version": "0.2.15",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.5.0",
"picomatch": "^4.0.4"
"picomatch": "^4.0.3"
},
"engines": {
"node": ">=12.0.0"
@@ -3911,9 +3858,9 @@
}
},
"node_modules/typescript": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz",
"integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -3925,16 +3872,16 @@
}
},
"node_modules/typescript-eslint": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.3.tgz",
"integrity": "sha512-KgusgyDgG4LI8Ih/sWaCtZ06tckLAS5CvT5A4D1Q7bYVoAAyzwiZvE4BmwDHkhRVkvhRBepKeASoFzQetha7Fg==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.2.tgz",
"integrity": "sha512-VEPQ0iPgWO/sBaZOU1xo4nuNdODVOajPnTIbog2GKYr31nIlZ0fWPoCQgGfF3ETyBl1vn63F/p50Um9Z4J8O8A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/eslint-plugin": "8.59.3",
"@typescript-eslint/parser": "8.59.3",
"@typescript-eslint/typescript-estree": "8.59.3",
"@typescript-eslint/utils": "8.59.3"
"@typescript-eslint/eslint-plugin": "8.57.2",
"@typescript-eslint/parser": "8.57.2",
"@typescript-eslint/typescript-estree": "8.57.2",
"@typescript-eslint/utils": "8.57.2"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -3945,7 +3892,7 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/unbox-primitive": {
@@ -3967,9 +3914,9 @@
}
},
"node_modules/undici-types": {
"version": "7.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.21.0.tgz",
"integrity": "sha512-w9IMgQrz4O0YN1LtB7K5P63vhlIOvC7opSmouCJ+ZywlPAlO9gIkJ+otk6LvGpAs2wg4econaCz3TvQ9xPoyuQ==",
"version": "7.18.2",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
"dev": true,
"license": "MIT"
},

View File

@@ -1,6 +1,6 @@
{
"name": "@goauthentik/eslint-config",
"version": "2.0.0",
"version": "1.3.0",
"description": "authentik's ESLint config",
"license": "MIT",
"repository": {
@@ -39,24 +39,24 @@
"dependencies": {
"eslint": "^9.39.3",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-lit": "^2.3.1",
"eslint-plugin-lit": "^2.2.1",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-wc": "^3.1.0"
},
"devDependencies": {
"@goauthentik/prettier-config": "../prettier-config",
"@goauthentik/tsconfig": "../tsconfig",
"@types/eslint": "^9.6.1",
"@types/node": "^25.7.0",
"typescript": "^6.0.3",
"typescript-eslint": "^8.59.3"
"@types/node": "^25.5.0",
"typescript": "^6.0.2",
"typescript-eslint": "^8.57.2"
},
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0",
"typescript": "^6.0.3 || ^7.0.0",
"typescript-eslint": "^8.59.3"
"typescript": "^5.9.3 || ^6.0.2",
"typescript-eslint": "^8.57.2"
},
"files": [
"./index.js",
@@ -65,7 +65,7 @@
],
"engines": {
"node": ">=24",
"npm": ">=11.14.1"
"npm": ">=11.10.1"
},
"devEngines": {
"runtime": {
@@ -75,7 +75,7 @@
},
"packageManager": {
"name": "npm",
"version": ">=11.14.1",
"version": ">=11.10.1",
"onFail": "warn"
}
},

View File

@@ -60,7 +60,7 @@ export const LogLevels = /** @type {Level[]} */ (Object.keys(LogLevelLabel));
/**
* @callback LoggerFactory
* @param {string | null} [prefix]
* @param {...string[]} args
* @param {...string} args
* @returns {Logger}
*/
@@ -207,7 +207,7 @@ export function pinoLight(options) {
* Creates a logger with the given prefix.
*
* @param {string} [prefix]
* @param {...string[]} args
* @param {...string} args
* @returns {Logger}
*
*/

View File

@@ -1,29 +1,29 @@
{
"name": "@goauthentik/logger-js",
"version": "2.0.0",
"version": "1.1.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@goauthentik/logger-js",
"version": "2.0.0",
"version": "1.1.1",
"license": "MIT",
"devDependencies": {
"@eslint/js": "^9.39.3",
"@goauthentik/prettier-config": "../prettier-config",
"@goauthentik/tsconfig": "../tsconfig",
"@types/node": "^25.7.0",
"@types/node": "^25.5.0",
"eslint": "^9.39.3",
"pino": "^10.3.1",
"pino-pretty": "^13.1.2",
"prettier": "^3.8.3",
"prettier": "^3.8.1",
"prettier-plugin-packagejson": "^3.0.2",
"typescript": "^6.0.3",
"typescript-eslint": "^8.59.3"
"typescript": "^6.0.2",
"typescript-eslint": "^8.57.2"
},
"engines": {
"node": ">=24",
"npm": ">=11.14.1"
"npm": ">=11.10.1"
},
"peerDependencies": {
"pino": "^10.3.1",
@@ -40,7 +40,7 @@
},
"../prettier-config": {
"name": "@goauthentik/prettier-config",
"version": "4.0.0",
"version": "3.5.0",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -50,30 +50,30 @@
"@eslint/js": "^9.39.3",
"@goauthentik/eslint-config": "../eslint-config",
"@goauthentik/tsconfig": "../tsconfig",
"@types/node": "^25.7.0",
"@types/node": "^25.0.0",
"eslint": "^9.39.3",
"prettier": "^3.8.3",
"prettier": "^3.8.1",
"prettier-plugin-packagejson": "^3.0.2",
"typescript": "^6.0.3",
"typescript-eslint": "^8.59.3"
"typescript": "^6.0.2",
"typescript-eslint": "^8.57.2"
},
"engines": {
"node": ">=24",
"npm": ">=11.14.1"
"npm": ">=11.10.1"
},
"peerDependencies": {
"prettier": "^3.8.3",
"prettier": "^3.8.1",
"prettier-plugin-packagejson": "^3.0.2"
}
},
"../tsconfig": {
"name": "@goauthentik/tsconfig",
"version": "2.0.0",
"version": "1.0.8",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=24",
"npm": ">=11.14.1"
"npm": ">=11.10.1"
}
},
"node_modules/@eslint-community/eslint-utils": {
@@ -361,30 +361,30 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "25.7.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.7.0.tgz",
"integrity": "sha512-z+pdZyxE+RTQE9AcboAZCb4otwcrvgHD+GlBpPgn0emDVt0ohrTMhAwlr2Wd9nZ+nihhYFxO2pThz3C5qSu2Eg==",
"version": "25.5.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz",
"integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~7.21.0"
"undici-types": "~7.18.0"
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.3.tgz",
"integrity": "sha512-PwFvSKsXGShKGW6n5bZOhGHEcCZXM8HofLK9fNsEwZXzFRjoY+XT1Vsf1zgyXdwTr0ZYz1/2tkZ0DBTT9jZjhw==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.2.tgz",
"integrity": "sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.12.2",
"@typescript-eslint/scope-manager": "8.59.3",
"@typescript-eslint/type-utils": "8.59.3",
"@typescript-eslint/utils": "8.59.3",
"@typescript-eslint/visitor-keys": "8.59.3",
"@typescript-eslint/scope-manager": "8.57.2",
"@typescript-eslint/type-utils": "8.57.2",
"@typescript-eslint/utils": "8.57.2",
"@typescript-eslint/visitor-keys": "8.57.2",
"ignore": "^7.0.5",
"natural-compare": "^1.4.0",
"ts-api-utils": "^2.5.0"
"ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -394,22 +394,22 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"@typescript-eslint/parser": "^8.59.3",
"@typescript-eslint/parser": "^8.57.2",
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/parser": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.3.tgz",
"integrity": "sha512-HPwA+hVkfcriajbNvTmZv4VRauibay+cWArYUYq7u7W7PmGShMxbPxLvrwDme55a6d5alG3nrYfhyJ/G28XlLg==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.2.tgz",
"integrity": "sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.59.3",
"@typescript-eslint/types": "8.59.3",
"@typescript-eslint/typescript-estree": "8.59.3",
"@typescript-eslint/visitor-keys": "8.59.3",
"@typescript-eslint/scope-manager": "8.57.2",
"@typescript-eslint/types": "8.57.2",
"@typescript-eslint/typescript-estree": "8.57.2",
"@typescript-eslint/visitor-keys": "8.57.2",
"debug": "^4.4.3"
},
"engines": {
@@ -421,18 +421,18 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/project-service": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.3.tgz",
"integrity": "sha512-ECiUWa/KYRGDFUqTNehaRgzDshnJfkTABJxVemHk4ko22gcr0ukloKjWvyQ64g8YCV/UI47kN1dbmjf/GaQYng==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.2.tgz",
"integrity": "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.59.3",
"@typescript-eslint/types": "^8.59.3",
"@typescript-eslint/tsconfig-utils": "^8.57.2",
"@typescript-eslint/types": "^8.57.2",
"debug": "^4.4.3"
},
"engines": {
@@ -443,18 +443,18 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.3.tgz",
"integrity": "sha512-t2LvZnoEfzKtnPjgeEu41xw5gxq9mQVfYy4OoZ4Vlt0sk3JwxmhCca/AR7DwOiHrjWgjAj6as4AhRLKSDfvZIA==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz",
"integrity": "sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.59.3",
"@typescript-eslint/visitor-keys": "8.59.3"
"@typescript-eslint/types": "8.57.2",
"@typescript-eslint/visitor-keys": "8.57.2"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -465,9 +465,9 @@
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.3.tgz",
"integrity": "sha512-PcIJHjmaREXLgIAIzLnSY9VucEzz8FKXsRgFa1DmdGCK/5tJpW03TKJF01Q6VZd1lLdz2sIKPWaDUZN9dp//dw==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz",
"integrity": "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -478,21 +478,21 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.3.tgz",
"integrity": "sha512-g71d8QD8UaiHGvrJwyIS1hCX5r63w6Jll+4VEYhEAHXTDIqX1JgxhTAbEHtKntL9kuc4jRo7/GWw5xfCepSccQ==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.2.tgz",
"integrity": "sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.59.3",
"@typescript-eslint/typescript-estree": "8.59.3",
"@typescript-eslint/utils": "8.59.3",
"@typescript-eslint/types": "8.57.2",
"@typescript-eslint/typescript-estree": "8.57.2",
"@typescript-eslint/utils": "8.57.2",
"debug": "^4.4.3",
"ts-api-utils": "^2.5.0"
"ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -503,13 +503,13 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/types": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.3.tgz",
"integrity": "sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz",
"integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -521,21 +521,21 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.3.tgz",
"integrity": "sha512-CbRjVRAf7Lr9Kr8RopKcbY45p2VfmmHrm0ygOCYFi7oU8q19m0Fs/6iHS7kNOmwpp+ob07ZVcAqlxUod9lYdmg==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz",
"integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.59.3",
"@typescript-eslint/tsconfig-utils": "8.59.3",
"@typescript-eslint/types": "8.59.3",
"@typescript-eslint/visitor-keys": "8.59.3",
"@typescript-eslint/project-service": "8.57.2",
"@typescript-eslint/tsconfig-utils": "8.57.2",
"@typescript-eslint/types": "8.57.2",
"@typescript-eslint/visitor-keys": "8.57.2",
"debug": "^4.4.3",
"minimatch": "^10.2.2",
"semver": "^7.7.3",
"tinyglobby": "^0.2.15",
"ts-api-utils": "^2.5.0"
"ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -545,20 +545,20 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/utils": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.3.tgz",
"integrity": "sha512-JAvT14goBzRzzzZyqq3P9BLArIxTtQURUtFgQ/V7FO+eU+Gg6ES+5ymOPP1wRxXcxAYeivCk4uS3jCKWI1K8Zg==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.2.tgz",
"integrity": "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.9.1",
"@typescript-eslint/scope-manager": "8.59.3",
"@typescript-eslint/types": "8.59.3",
"@typescript-eslint/typescript-estree": "8.59.3"
"@typescript-eslint/scope-manager": "8.57.2",
"@typescript-eslint/types": "8.57.2",
"@typescript-eslint/typescript-estree": "8.57.2"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -569,17 +569,17 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.3.tgz",
"integrity": "sha512-f1UQF7ggd42YiwI5wGrRaPsa+P0CINBlrkLPmGfpq/u/I/oVtecoEIfFR9ag/oa1sLOsRNZ6xehf6qMZhQGBDg==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz",
"integrity": "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.59.3",
"@typescript-eslint/types": "8.57.2",
"eslint-visitor-keys": "^5.0.0"
},
"engines": {
@@ -687,9 +687,9 @@
}
},
"node_modules/brace-expansion": {
"version": "5.0.6",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
"integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
"integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1409,13 +1409,13 @@
"license": "MIT"
},
"node_modules/minimatch": {
"version": "10.2.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
"integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
"version": "10.2.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
"integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"brace-expansion": "^5.0.5"
"brace-expansion": "^5.0.2"
},
"engines": {
"node": "18 || 20 || >=22"
@@ -1653,9 +1653,9 @@
}
},
"node_modules/prettier": {
"version": "3.8.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz",
"integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==",
"version": "3.8.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz",
"integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
"dev": true,
"license": "MIT",
"bin": {
@@ -1946,9 +1946,9 @@
}
},
"node_modules/typescript": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz",
"integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -1960,16 +1960,16 @@
}
},
"node_modules/typescript-eslint": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.3.tgz",
"integrity": "sha512-KgusgyDgG4LI8Ih/sWaCtZ06tckLAS5CvT5A4D1Q7bYVoAAyzwiZvE4BmwDHkhRVkvhRBepKeASoFzQetha7Fg==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.2.tgz",
"integrity": "sha512-VEPQ0iPgWO/sBaZOU1xo4nuNdODVOajPnTIbog2GKYr31nIlZ0fWPoCQgGfF3ETyBl1vn63F/p50Um9Z4J8O8A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/eslint-plugin": "8.59.3",
"@typescript-eslint/parser": "8.59.3",
"@typescript-eslint/typescript-estree": "8.59.3",
"@typescript-eslint/utils": "8.59.3"
"@typescript-eslint/eslint-plugin": "8.57.2",
"@typescript-eslint/parser": "8.57.2",
"@typescript-eslint/typescript-estree": "8.57.2",
"@typescript-eslint/utils": "8.57.2"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1980,13 +1980,13 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/undici-types": {
"version": "7.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.21.0.tgz",
"integrity": "sha512-w9IMgQrz4O0YN1LtB7K5P63vhlIOvC7opSmouCJ+ZywlPAlO9gIkJ+otk6LvGpAs2wg4econaCz3TvQ9xPoyuQ==",
"version": "7.18.2",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
"dev": true,
"license": "MIT"
},

View File

@@ -1,6 +1,6 @@
{
"name": "@goauthentik/logger-js",
"version": "2.0.0",
"version": "1.1.1",
"description": "Pino-based logger for authentik",
"license": "MIT",
"repository": {
@@ -31,14 +31,14 @@
"@eslint/js": "^9.39.3",
"@goauthentik/prettier-config": "../prettier-config",
"@goauthentik/tsconfig": "../tsconfig",
"@types/node": "^25.7.0",
"@types/node": "^25.5.0",
"eslint": "^9.39.3",
"pino": "^10.3.1",
"pino-pretty": "^13.1.2",
"prettier": "^3.8.3",
"prettier": "^3.8.1",
"prettier-plugin-packagejson": "^3.0.2",
"typescript": "^6.0.3",
"typescript-eslint": "^8.59.3"
"typescript": "^6.0.2",
"typescript-eslint": "^8.57.2"
},
"peerDependencies": {
"pino": "^10.3.1",
@@ -51,7 +51,7 @@
],
"engines": {
"node": ">=24",
"npm": ">=11.14.1"
"npm": ">=11.10.1"
},
"devEngines": {
"runtime": {
@@ -61,7 +61,7 @@
},
"packageManager": {
"name": "npm",
"version": ">=11.14.1",
"version": ">=11.10.1",
"onFail": "ignore"
}
},

View File

@@ -1,12 +1,12 @@
{
"name": "@goauthentik/prettier-config",
"version": "4.0.0",
"version": "3.5.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@goauthentik/prettier-config",
"version": "4.0.0",
"version": "3.5.0",
"license": "MIT",
"dependencies": {
"format-imports": "^4.0.8"
@@ -15,25 +15,25 @@
"@eslint/js": "^9.39.3",
"@goauthentik/eslint-config": "../eslint-config",
"@goauthentik/tsconfig": "../tsconfig",
"@types/node": "^25.7.0",
"@types/node": "^25.0.0",
"eslint": "^9.39.3",
"prettier": "^3.8.3",
"prettier": "^3.8.1",
"prettier-plugin-packagejson": "^3.0.2",
"typescript": "^6.0.3",
"typescript-eslint": "^8.59.3"
"typescript": "^6.0.2",
"typescript-eslint": "^8.57.2"
},
"engines": {
"node": ">=24",
"npm": ">=11.14.1"
"npm": ">=11.10.1"
},
"peerDependencies": {
"prettier": "^3.8.3",
"prettier": "^3.8.1",
"prettier-plugin-packagejson": "^3.0.2"
}
},
"../eslint-config": {
"name": "@goauthentik/eslint-config",
"version": "2.0.0",
"version": "1.3.0",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -41,26 +41,26 @@
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-lit": "^2.2.1",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-wc": "^3.1.0"
},
"devDependencies": {
"@goauthentik/prettier-config": "../prettier-config",
"@goauthentik/tsconfig": "../tsconfig",
"@types/eslint": "^9.6.1",
"@types/node": "^25.7.0",
"typescript": "^6.0.3",
"typescript-eslint": "^8.59.3"
"@types/node": "^25.3.0",
"typescript": "^6.0.2",
"typescript-eslint": "^8.57.2"
},
"engines": {
"node": ">=24",
"npm": ">=11.14.1"
"npm": ">=11.10.1"
},
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0",
"typescript": "^6.0.3 || ^7.0.0",
"typescript-eslint": "^8.59.3"
"typescript": "^5.9.3 || ^6.0.2",
"typescript-eslint": "^8.57.2"
},
"peerDependenciesMeta": {
"react": {
@@ -73,12 +73,12 @@
},
"../tsconfig": {
"name": "@goauthentik/tsconfig",
"version": "1.0.9",
"version": "1.0.8",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=24",
"npm": ">=11.14.1"
"npm": ">=11.10.1"
}
},
"node_modules/@babel/helper-string-parser": {
@@ -382,30 +382,30 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "25.7.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.7.0.tgz",
"integrity": "sha512-z+pdZyxE+RTQE9AcboAZCb4otwcrvgHD+GlBpPgn0emDVt0ohrTMhAwlr2Wd9nZ+nihhYFxO2pThz3C5qSu2Eg==",
"version": "25.5.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz",
"integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~7.21.0"
"undici-types": "~7.18.0"
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.3.tgz",
"integrity": "sha512-PwFvSKsXGShKGW6n5bZOhGHEcCZXM8HofLK9fNsEwZXzFRjoY+XT1Vsf1zgyXdwTr0ZYz1/2tkZ0DBTT9jZjhw==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.2.tgz",
"integrity": "sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.12.2",
"@typescript-eslint/scope-manager": "8.59.3",
"@typescript-eslint/type-utils": "8.59.3",
"@typescript-eslint/utils": "8.59.3",
"@typescript-eslint/visitor-keys": "8.59.3",
"@typescript-eslint/scope-manager": "8.57.2",
"@typescript-eslint/type-utils": "8.57.2",
"@typescript-eslint/utils": "8.57.2",
"@typescript-eslint/visitor-keys": "8.57.2",
"ignore": "^7.0.5",
"natural-compare": "^1.4.0",
"ts-api-utils": "^2.5.0"
"ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -415,9 +415,9 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"@typescript-eslint/parser": "^8.59.3",
"@typescript-eslint/parser": "^8.57.2",
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
@@ -431,16 +431,16 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.3.tgz",
"integrity": "sha512-HPwA+hVkfcriajbNvTmZv4VRauibay+cWArYUYq7u7W7PmGShMxbPxLvrwDme55a6d5alG3nrYfhyJ/G28XlLg==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.2.tgz",
"integrity": "sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.59.3",
"@typescript-eslint/types": "8.59.3",
"@typescript-eslint/typescript-estree": "8.59.3",
"@typescript-eslint/visitor-keys": "8.59.3",
"@typescript-eslint/scope-manager": "8.57.2",
"@typescript-eslint/types": "8.57.2",
"@typescript-eslint/typescript-estree": "8.57.2",
"@typescript-eslint/visitor-keys": "8.57.2",
"debug": "^4.4.3"
},
"engines": {
@@ -452,18 +452,18 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/project-service": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.3.tgz",
"integrity": "sha512-ECiUWa/KYRGDFUqTNehaRgzDshnJfkTABJxVemHk4ko22gcr0ukloKjWvyQ64g8YCV/UI47kN1dbmjf/GaQYng==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.2.tgz",
"integrity": "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.59.3",
"@typescript-eslint/types": "^8.59.3",
"@typescript-eslint/tsconfig-utils": "^8.57.2",
"@typescript-eslint/types": "^8.57.2",
"debug": "^4.4.3"
},
"engines": {
@@ -474,18 +474,18 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.3.tgz",
"integrity": "sha512-t2LvZnoEfzKtnPjgeEu41xw5gxq9mQVfYy4OoZ4Vlt0sk3JwxmhCca/AR7DwOiHrjWgjAj6as4AhRLKSDfvZIA==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz",
"integrity": "sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.59.3",
"@typescript-eslint/visitor-keys": "8.59.3"
"@typescript-eslint/types": "8.57.2",
"@typescript-eslint/visitor-keys": "8.57.2"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -496,9 +496,9 @@
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.3.tgz",
"integrity": "sha512-PcIJHjmaREXLgIAIzLnSY9VucEzz8FKXsRgFa1DmdGCK/5tJpW03TKJF01Q6VZd1lLdz2sIKPWaDUZN9dp//dw==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz",
"integrity": "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -509,21 +509,21 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.3.tgz",
"integrity": "sha512-g71d8QD8UaiHGvrJwyIS1hCX5r63w6Jll+4VEYhEAHXTDIqX1JgxhTAbEHtKntL9kuc4jRo7/GWw5xfCepSccQ==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.2.tgz",
"integrity": "sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.59.3",
"@typescript-eslint/typescript-estree": "8.59.3",
"@typescript-eslint/utils": "8.59.3",
"@typescript-eslint/types": "8.57.2",
"@typescript-eslint/typescript-estree": "8.57.2",
"@typescript-eslint/utils": "8.57.2",
"debug": "^4.4.3",
"ts-api-utils": "^2.5.0"
"ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -534,13 +534,13 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/types": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.3.tgz",
"integrity": "sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz",
"integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -552,21 +552,21 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.3.tgz",
"integrity": "sha512-CbRjVRAf7Lr9Kr8RopKcbY45p2VfmmHrm0ygOCYFi7oU8q19m0Fs/6iHS7kNOmwpp+ob07ZVcAqlxUod9lYdmg==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz",
"integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.59.3",
"@typescript-eslint/tsconfig-utils": "8.59.3",
"@typescript-eslint/types": "8.59.3",
"@typescript-eslint/visitor-keys": "8.59.3",
"@typescript-eslint/project-service": "8.57.2",
"@typescript-eslint/tsconfig-utils": "8.57.2",
"@typescript-eslint/types": "8.57.2",
"@typescript-eslint/visitor-keys": "8.57.2",
"debug": "^4.4.3",
"minimatch": "^10.2.2",
"semver": "^7.7.3",
"tinyglobby": "^0.2.15",
"ts-api-utils": "^2.5.0"
"ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -576,7 +576,7 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": {
@@ -590,9 +590,9 @@
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
"version": "5.0.6",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
"integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
"integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -603,13 +603,13 @@
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
"version": "10.2.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
"integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
"version": "10.2.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
"integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"brace-expansion": "^5.0.5"
"brace-expansion": "^5.0.2"
},
"engines": {
"node": "18 || 20 || >=22"
@@ -619,16 +619,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.3.tgz",
"integrity": "sha512-JAvT14goBzRzzzZyqq3P9BLArIxTtQURUtFgQ/V7FO+eU+Gg6ES+5ymOPP1wRxXcxAYeivCk4uS3jCKWI1K8Zg==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.2.tgz",
"integrity": "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.9.1",
"@typescript-eslint/scope-manager": "8.59.3",
"@typescript-eslint/types": "8.59.3",
"@typescript-eslint/typescript-estree": "8.59.3"
"@typescript-eslint/scope-manager": "8.57.2",
"@typescript-eslint/types": "8.57.2",
"@typescript-eslint/typescript-estree": "8.57.2"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -639,17 +639,17 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.3.tgz",
"integrity": "sha512-f1UQF7ggd42YiwI5wGrRaPsa+P0CINBlrkLPmGfpq/u/I/oVtecoEIfFR9ag/oa1sLOsRNZ6xehf6qMZhQGBDg==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz",
"integrity": "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.59.3",
"@typescript-eslint/types": "8.57.2",
"eslint-visitor-keys": "^5.0.0"
},
"engines": {
@@ -1745,9 +1745,9 @@
}
},
"node_modules/prettier": {
"version": "3.8.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz",
"integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==",
"version": "3.8.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz",
"integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
"license": "MIT",
"bin": {
"prettier": "bin/prettier.cjs"
@@ -2001,9 +2001,9 @@
}
},
"node_modules/typescript": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz",
"integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -2015,16 +2015,16 @@
}
},
"node_modules/typescript-eslint": {
"version": "8.59.3",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.3.tgz",
"integrity": "sha512-KgusgyDgG4LI8Ih/sWaCtZ06tckLAS5CvT5A4D1Q7bYVoAAyzwiZvE4BmwDHkhRVkvhRBepKeASoFzQetha7Fg==",
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.2.tgz",
"integrity": "sha512-VEPQ0iPgWO/sBaZOU1xo4nuNdODVOajPnTIbog2GKYr31nIlZ0fWPoCQgGfF3ETyBl1vn63F/p50Um9Z4J8O8A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/eslint-plugin": "8.59.3",
"@typescript-eslint/parser": "8.59.3",
"@typescript-eslint/typescript-estree": "8.59.3",
"@typescript-eslint/utils": "8.59.3"
"@typescript-eslint/eslint-plugin": "8.57.2",
"@typescript-eslint/parser": "8.57.2",
"@typescript-eslint/typescript-estree": "8.57.2",
"@typescript-eslint/utils": "8.57.2"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2035,13 +2035,13 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/undici-types": {
"version": "7.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.21.0.tgz",
"integrity": "sha512-w9IMgQrz4O0YN1LtB7K5P63vhlIOvC7opSmouCJ+ZywlPAlO9gIkJ+otk6LvGpAs2wg4econaCz3TvQ9xPoyuQ==",
"version": "7.18.2",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
"dev": true,
"license": "MIT"
},

View File

@@ -1,6 +1,6 @@
{
"name": "@goauthentik/prettier-config",
"version": "4.0.0",
"version": "3.5.0",
"description": "authentik's Prettier config",
"license": "MIT",
"repository": {
@@ -39,15 +39,15 @@
"@eslint/js": "^9.39.3",
"@goauthentik/eslint-config": "../eslint-config",
"@goauthentik/tsconfig": "../tsconfig",
"@types/node": "^25.7.0",
"@types/node": "^25.0.0",
"eslint": "^9.39.3",
"prettier": "^3.8.3",
"prettier": "^3.8.1",
"prettier-plugin-packagejson": "^3.0.2",
"typescript": "^6.0.3",
"typescript-eslint": "^8.59.3"
"typescript": "^6.0.2",
"typescript-eslint": "^8.57.2"
},
"peerDependencies": {
"prettier": "^3.8.3",
"prettier": "^3.8.1",
"prettier-plugin-packagejson": "^3.0.2"
},
"files": [
@@ -57,7 +57,7 @@
],
"engines": {
"node": ">=24",
"npm": ">=11.14.1"
"npm": ">=11.10.1"
},
"devEngines": {
"runtime": {
@@ -67,7 +67,7 @@
},
"packageManager": {
"name": "npm",
"version": ">=11.14.1",
"version": ">=11.10.1",
"onFail": "warn"
}
},

View File

@@ -1,16 +1,16 @@
{
"name": "@goauthentik/tsconfig",
"version": "2.0.0",
"version": "1.0.9",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@goauthentik/tsconfig",
"version": "2.0.0",
"version": "1.0.9",
"license": "MIT",
"engines": {
"node": ">=24",
"npm": ">=11.14.1"
"npm": ">=11.10.1"
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@goauthentik/tsconfig",
"version": "2.0.0",
"version": "1.0.9",
"description": "authentik's base TypeScript configuration.",
"license": "MIT",
"repository": {
@@ -15,7 +15,7 @@
"type": "module",
"engines": {
"node": ">=24",
"npm": ">=11.14.1"
"npm": ">=11.10.1"
},
"devEngines": {
"runtime": {
@@ -25,7 +25,7 @@
},
"packageManager": {
"name": "npm",
"version": ">=11.14.1",
"version": ">=11.10.1",
"onFail": "warn"
}
},

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