Compare commits

..

97 Commits

Author SHA1 Message Date
Teffen Ellis
eb4163a428 Fix issues surrounding race conditions, stale files. 2025-11-14 23:05:13 +01:00
Teffen Ellis
1525bff851 Prep locale directory for clean up. 2025-11-14 21:43:36 +01:00
Teffen Ellis
e16136f086 Fix order of commands. 2025-11-14 21:35:46 +01:00
Teffen Ellis
0ed9e3b636 Fix hanging process. 2025-11-14 21:35:27 +01:00
Teffen Ellis
0c707a7d44 Fix missing directory constant. 2025-11-14 20:18:22 +01:00
Teffen Ellis
83699cdc69 Add color errors. 2025-11-14 20:18:14 +01:00
Teffen Ellis
8aa5814d91 Fix stale promise. 2025-11-14 20:08:43 +01:00
Teffen Ellis
c9c13665f6 Fix issue where ESBuild context persists after shutdown. 2025-11-14 19:27:16 +01:00
Teffen Ellis
25f1be496f Clean up default language behavior. 2025-11-14 18:51:57 +01:00
Teffen Ellis
37f64ca773 Flesh out clean up. 2025-11-14 18:09:53 +01:00
Marc 'risson' Schmitt
779d98a808 packages/django-channels-postgres/layer: fix query when subscribed to multiple channels (#18152)
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-11-14 15:30:19 +01:00
Dominic R
5389709ab7 core: deduplicate user attribute constant definitions (#18138)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-11-14 13:59:55 +00:00
dependabot[bot]
650c1f93f2 web: bump @trivago/prettier-plugin-sort-imports from 5.2.2 to 6.0.0 in /web (#18146)
web: bump @trivago/prettier-plugin-sort-imports in /web

Bumps [@trivago/prettier-plugin-sort-imports](https://github.com/trivago/prettier-plugin-sort-imports) from 5.2.2 to 6.0.0.
- [Release notes](https://github.com/trivago/prettier-plugin-sort-imports/releases)
- [Changelog](https://github.com/trivago/prettier-plugin-sort-imports/blob/main/CHANGELOG.md)
- [Commits](https://github.com/trivago/prettier-plugin-sort-imports/compare/v5.2.2...v6.0.0)

---
updated-dependencies:
- dependency-name: "@trivago/prettier-plugin-sort-imports"
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-14 14:59:34 +01:00
Connor Peshek
2b794e616d crypto: update certificates on fs event (#18129)
Co-authored-by: connor peshek <connorpeshek@connors-MacBook-Pro.local>
2025-11-14 14:37:00 +01:00
Dewi Roberts
fd7cea1344 github: converts issue templates to forms (#18133) 2025-11-14 14:30:05 +01:00
dependabot[bot]
8fdc6e44eb core: bump github.com/getsentry/sentry-go from 0.36.2 to 0.37.0 (#18140)
Bumps [github.com/getsentry/sentry-go](https://github.com/getsentry/sentry-go) from 0.36.2 to 0.37.0.
- [Release notes](https://github.com/getsentry/sentry-go/releases)
- [Changelog](https://github.com/getsentry/sentry-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-go/compare/v0.36.2...v0.37.0)

---
updated-dependencies:
- dependency-name: github.com/getsentry/sentry-go
  dependency-version: 0.37.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-14 13:56:16 +01:00
dependabot[bot]
fb76f3e1ac web: bump type-fest from 5.1.0 to 5.2.0 in /web (#18144)
Bumps [type-fest](https://github.com/sindresorhus/type-fest) from 5.1.0 to 5.2.0.
- [Release notes](https://github.com/sindresorhus/type-fest/releases)
- [Commits](https://github.com/sindresorhus/type-fest/compare/v5.1.0...v5.2.0)

---
updated-dependencies:
- dependency-name: type-fest
  dependency-version: 5.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-14 13:56:02 +01:00
dependabot[bot]
f7e04b8bcf web: bump vite from 7.1.12 to 7.2.2 in /web (#18143)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.1.12 to 7.2.2.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.2.2/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.2.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-14 13:55:52 +01:00
dependabot[bot]
1026601085 website: bump the build group in /website with 3 updates (#18141)
Bumps the build group in /website with 3 updates: [@rspack/binding-darwin-arm64](https://github.com/web-infra-dev/rspack/tree/HEAD/packages/rspack), [@rspack/binding-linux-arm64-gnu](https://github.com/web-infra-dev/rspack/tree/HEAD/packages/rspack) and [@rspack/binding-linux-x64-gnu](https://github.com/web-infra-dev/rspack/tree/HEAD/packages/rspack).


Updates `@rspack/binding-darwin-arm64` from 1.6.2 to 1.6.3
- [Release notes](https://github.com/web-infra-dev/rspack/releases)
- [Commits](https://github.com/web-infra-dev/rspack/commits/v1.6.3/packages/rspack)

Updates `@rspack/binding-linux-arm64-gnu` from 1.6.2 to 1.6.3
- [Release notes](https://github.com/web-infra-dev/rspack/releases)
- [Commits](https://github.com/web-infra-dev/rspack/commits/v1.6.3/packages/rspack)

Updates `@rspack/binding-linux-x64-gnu` from 1.6.2 to 1.6.3
- [Release notes](https://github.com/web-infra-dev/rspack/releases)
- [Commits](https://github.com/web-infra-dev/rspack/commits/v1.6.3/packages/rspack)

---
updated-dependencies:
- dependency-name: "@rspack/binding-darwin-arm64"
  dependency-version: 1.6.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@rspack/binding-linux-arm64-gnu"
  dependency-version: 1.6.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@rspack/binding-linux-x64-gnu"
  dependency-version: 1.6.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-14 13:50:50 +01:00
dependabot[bot]
8b7cceadec web: bump globals from 16.4.0 to 16.5.0 in /web (#18145)
Bumps [globals](https://github.com/sindresorhus/globals) from 16.4.0 to 16.5.0.
- [Release notes](https://github.com/sindresorhus/globals/releases)
- [Commits](https://github.com/sindresorhus/globals/compare/v16.4.0...v16.5.0)

---
updated-dependencies:
- dependency-name: globals
  dependency-version: 16.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-14 13:50:39 +01:00
dependabot[bot]
232bc52960 core: bump astral-sh/uv from 0.9.8 to 0.9.9 (#18148)
Bumps [astral-sh/uv](https://github.com/astral-sh/uv) from 0.9.8 to 0.9.9.
- [Release notes](https://github.com/astral-sh/uv/releases)
- [Changelog](https://github.com/astral-sh/uv/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/uv/compare/0.9.8...0.9.9)

---
updated-dependencies:
- dependency-name: astral-sh/uv
  dependency-version: 0.9.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-14 13:49:54 +01:00
dependabot[bot]
1c282c339b core: bump goauthentik/fips-debian from 5017d65 to 40a1f32 (#18149)
Bumps goauthentik/fips-debian from `5017d65` to `40a1f32`.

---
updated-dependencies:
- dependency-name: goauthentik/fips-debian
  dependency-version: trixie-slim-fips
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-14 13:43:30 +01:00
mayswind
e99e5b7355 website/integrations: Add ezBookkeeping integration (#18040)
Co-authored-by: dewi-tik <dewi@goauthentik.io>
Co-authored-by: Dominic R <dominic@sdko.org>
2025-11-14 04:26:41 +00:00
Daniel Tihanyi
9e1244f764 website/integrations: Add Joplin (#18042)
Co-authored-by: Dominic R <dominic@sdko.org>
2025-11-14 04:05:15 +00:00
Teffen Ellis
1115e6f82f web: Disable library <datalist> on Firefox. (#18103)
web: Disable library autocomplete on Firefox.
2025-11-13 22:09:25 +01:00
Jens L.
3d66864735 web/admin: link to user on invitation list page (#18132)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-11-13 22:09:17 +01:00
Dewi Roberts
1cc5fa1412 web/admin: update stage descriptions (#18118)
* Updates the description for stages to standardize lanauge and clarify.

* Add "based"

Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Remove "currently"

Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Fix user_write stage

Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

---------

Signed-off-by: Dewi Roberts <dewi@goauthentik.io>
2025-11-13 20:40:13 +00:00
Christoph Dyllick-Brenzinger
60adbd9245 website/integrations: add SeaTable (#18115)
* website/integrations: add description for SeaTable

* fix wrong display of links in the section additional resources

* fix typo

* Change wording, update formatting, change curl command, add section headers, and bring in-line with style guide

* Spelling

* Apply suggestions from code review

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Apply suggestions from code review

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Apply suggestion from @dominic-r

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Apply suggestions from code review

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Fix missing backticks

Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Remove indentation

Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Remove "the"

Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Add intro to certs section

Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Apply suggestion from @dominic-r

Signed-off-by: Dominic R <dominic@sdko.org>

---------

Signed-off-by: Dewi Roberts <dewi@goauthentik.io>
Signed-off-by: Dominic R <dominic@sdko.org>
Co-authored-by: dewi-tik <dewi@goauthentik.io>
Co-authored-by: Dominic R <dominic@sdko.org>
2025-11-13 20:38:31 +00:00
Dominic R
d4e0694cbe website/integrations: stripe: fix markdown (#18126)
* website/integrations: stripe: fix markdown

Signed-off-by: Dominic R <dominic@sdko.org>

* Linting

---------

Signed-off-by: Dominic R <dominic@sdko.org>
Co-authored-by: dewi-tik <dewi@goauthentik.io>
2025-11-13 19:57:37 +00:00
Tealk
4821198f92 web/flows: improvements for hCaptcha (#16882)
* improvements for hCaptcha
Issue #16755

* web: Format.

---------

Co-authored-by: Teffen Ellis <teffen@goauthentik.io>
2025-11-13 20:23:53 +01:00
Dewi Roberts
b791737c47 website/docs: update application description (#18125)
Update due to 2025.10 changes
2025-11-13 19:09:20 +00:00
Dewi Roberts
f419173029 revert: github: convert issue templates to forms (#18121)
Revert "github: convert issue templates to forms (#18117)"

This reverts commit 75d4c2d2ee.
2025-11-13 16:57:27 +00:00
Dewi Roberts
75d4c2d2ee github: convert issue templates to forms (#18117)
* Updates templates to forms, changes project and label handling, mentions discussions and discord for questions rather than issues.

* Apply suggestion from @dominic-r

Signed-off-by: Dominic R <dominic@sdko.org>

---------

Signed-off-by: Dominic R <dominic@sdko.org>
Co-authored-by: Dominic R <dominic@sdko.org>
2025-11-13 15:53:35 +00:00
Dewi Roberts
deb7765dee website/docs: fix wording in stages overview (#18061)
Change flow to stage
2025-11-13 15:43:03 +00:00
Marc 'risson' Schmitt
acf5b5f2ff packages/django-dramatiq-postgres: broker: ensure locking happens with the same connection (#18095) 2025-11-13 16:37:29 +01:00
Daniel Lo Nigro
9295d876a7 website/integrations: Frappe: update instructions (#18029)
* Update Frappe integration page

- Make it clearer that `provider` has to be replaced with the actual provider name.
- Add `ERPNext` into the title for SEO and clarity.

Signed-off-by: Daniel Lo Nigro <d@d.sb>

* Changed formatting of the note and added it to the authentik section

---------

Signed-off-by: Daniel Lo Nigro <d@d.sb>
Co-authored-by: dewi-tik <dewi@goauthentik.io>
2025-11-13 14:52:30 +00:00
dependabot[bot]
acbecff09c website: bump @types/react from 19.2.3 to 19.2.4 in /website (#18108)
Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 19.2.3 to 19.2.4.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

---
updated-dependencies:
- dependency-name: "@types/react"
  dependency-version: 19.2.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-13 13:58:41 +01:00
dependabot[bot]
d7514c022a web: bump @types/node from 24.10.0 to 24.10.1 in /packages/esbuild-plugin-live-reload (#18111)
web: bump @types/node in /packages/esbuild-plugin-live-reload

Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 24.10.0 to 24.10.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.10.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-13 13:05:01 +01:00
dependabot[bot]
d53f6e1035 website: bump the build group in /website with 3 updates (#18106)
Bumps the build group in /website with 3 updates: [@rspack/binding-darwin-arm64](https://github.com/web-infra-dev/rspack/tree/HEAD/packages/rspack), [@rspack/binding-linux-arm64-gnu](https://github.com/web-infra-dev/rspack/tree/HEAD/packages/rspack) and [@rspack/binding-linux-x64-gnu](https://github.com/web-infra-dev/rspack/tree/HEAD/packages/rspack).


Updates `@rspack/binding-darwin-arm64` from 1.6.1 to 1.6.2
- [Release notes](https://github.com/web-infra-dev/rspack/releases)
- [Commits](https://github.com/web-infra-dev/rspack/commits/v1.6.2/packages/rspack)

Updates `@rspack/binding-linux-arm64-gnu` from 1.6.1 to 1.6.2
- [Release notes](https://github.com/web-infra-dev/rspack/releases)
- [Commits](https://github.com/web-infra-dev/rspack/commits/v1.6.2/packages/rspack)

Updates `@rspack/binding-linux-x64-gnu` from 1.6.1 to 1.6.2
- [Release notes](https://github.com/web-infra-dev/rspack/releases)
- [Commits](https://github.com/web-infra-dev/rspack/commits/v1.6.2/packages/rspack)

---
updated-dependencies:
- dependency-name: "@rspack/binding-darwin-arm64"
  dependency-version: 1.6.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@rspack/binding-linux-arm64-gnu"
  dependency-version: 1.6.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@rspack/binding-linux-x64-gnu"
  dependency-version: 1.6.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-13 12:47:58 +01:00
dependabot[bot]
e4e9062096 web: bump the react group across 2 directories with 2 updates (#18110)
Bumps the react group with 2 updates in the /packages/docusaurus-config directory: [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) and [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom).
Bumps the react group with 2 updates in the /web directory: [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) and [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom).


Updates `@types/react` from 19.2.3 to 19.2.4
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

Updates `@types/react-dom` from 19.2.2 to 19.2.3
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom)

Updates `@types/react` from 19.2.3 to 19.2.4
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

Updates `@types/react-dom` from 19.2.2 to 19.2.3
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom)

---
updated-dependencies:
- dependency-name: "@types/react"
  dependency-version: 19.2.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: react
- dependency-name: "@types/react-dom"
  dependency-version: 19.2.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: react
- dependency-name: "@types/react"
  dependency-version: 19.2.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: react
- dependency-name: "@types/react-dom"
  dependency-version: 19.2.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: react
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-13 12:47:07 +01:00
transifex-integration[bot]
d5bdea68c6 translate: Updates for file locale/en/LC_MESSAGES/django.po in fi (#18105)
Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-11-13 12:46:31 +01:00
dependabot[bot]
5415c7660f website: bump @types/react-dom from 19.2.2 to 19.2.3 in /website (#18107)
Bumps [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) from 19.2.2 to 19.2.3.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom)

---
updated-dependencies:
- dependency-name: "@types/react-dom"
  dependency-version: 19.2.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-13 12:46:17 +01:00
dependabot[bot]
769e22c7ed web: bump @types/node from 24.10.0 to 24.10.1 in /packages/prettier-config (#18112)
web: bump @types/node in /packages/prettier-config

Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 24.10.0 to 24.10.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.10.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-13 12:43:58 +01:00
dependabot[bot]
21b085b586 web: bump @types/node from 22.15.19 to 24.10.1 in /web (#18113)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.15.19 to 24.10.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.10.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-13 12:43:41 +01:00
transifex-integration[bot]
44ac0d0bda translate: Updates for file locale/en/LC_MESSAGES/django.po in fi (#18060)
* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate web/xliff/en.xlf in ja

100% translated source file: 'web/xliff/en.xlf'
on 'ja'.

* Translate locale/en/LC_MESSAGES/django.po in ja

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'ja'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

* Update translation units. Prep Japanese.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
Co-authored-by: Teffen Ellis <teffen@goauthentik.io>
2025-11-13 03:48:07 +00:00
Teffen Ellis
c37ef6d728 web/i18n: Japanese Locale(日本語ロケール) (#17938)
web: Activate Japanese locale.
2025-11-13 03:06:31 +00:00
Teffen Ellis
7743774905 web/i18n: Locale message fixes (#17913)
* web: Fix issue where messages defined outside elements are not translated.

* web: Apply locale to Captchas.
2025-11-12 22:04:48 -05:00
Teffen Ellis
7bb593da22 web: Lit Session Context (#17903)
web: Flesh out client-side session context.
2025-11-13 02:15:32 +00:00
Teffen Ellis
bbacea5b9a web: Fix tab activation, blank provider URLs (#18031)
web: Fix tab activation.
2025-11-13 01:51:19 +00:00
Teffen Ellis
02072bda93 web: Fix RAC modal visibility. (#17941)
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2025-11-12 18:49:50 +01:00
Marc 'risson' Schmitt
a8327101c6 cmd/server/healthcheck: remove worker HTTP healthcheck (#18090)
* cmd/server/healthcheck: remove worker HTTP healthcheck

Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>

* lint

Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>

---------

Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-11-12 14:42:30 +00:00
dependabot[bot]
1905fbb58f web: bump @sentry/browser from 10.24.0 to 10.25.0 in /web in the sentry group across 1 directory (#18079)
web: bump @sentry/browser in /web in the sentry group across 1 directory

Bumps the sentry group with 1 update in the /web directory: [@sentry/browser](https://github.com/getsentry/sentry-javascript).


Updates `@sentry/browser` from 10.24.0 to 10.25.0
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/10.24.0...10.25.0)

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  dependency-version: 10.25.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: sentry
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-12 13:26:55 +01:00
dependabot[bot]
5c05050de7 website: bump @types/react from 19.2.2 to 19.2.3 in /website (#18077)
Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 19.2.2 to 19.2.3.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

---
updated-dependencies:
- dependency-name: "@types/react"
  dependency-version: 19.2.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-12 12:15:30 +01:00
dependabot[bot]
5bdd6815bf website: bump @types/node from 24.10.0 to 24.10.1 in /website (#18078)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 24.10.0 to 24.10.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.10.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-12 12:14:45 +01:00
dependabot[bot]
40645b8abe web: bump the storybook group across 1 directory with 5 updates (#18080)
Bumps the storybook group with 4 updates in the /web directory: [@storybook/addon-docs](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/docs), [@storybook/addon-links](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/links), [@storybook/web-components](https://github.com/storybookjs/storybook/tree/HEAD/code/renderers/web-components) and [@storybook/web-components-vite](https://github.com/storybookjs/storybook/tree/HEAD/code/frameworks/web-components-vite).


Updates `@storybook/addon-docs` from 10.0.6 to 10.0.7
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v10.0.7/code/addons/docs)

Updates `@storybook/addon-links` from 10.0.6 to 10.0.7
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v10.0.7/code/addons/links)

Updates `@storybook/web-components` from 10.0.6 to 10.0.7
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v10.0.7/code/renderers/web-components)

Updates `@storybook/web-components-vite` from 10.0.6 to 10.0.7
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v10.0.7/code/frameworks/web-components-vite)

Updates `storybook` from 10.0.6 to 10.0.7
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v10.0.7/code/core)

---
updated-dependencies:
- dependency-name: "@storybook/addon-docs"
  dependency-version: 10.0.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-links"
  dependency-version: 10.0.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/web-components"
  dependency-version: 10.0.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/web-components-vite"
  dependency-version: 10.0.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: storybook
  dependency-version: 10.0.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-12 12:14:35 +01:00
dependabot[bot]
74742ff288 web: bump the rollup group across 1 directory with 4 updates (#18082)
Bumps the rollup group with 4 updates in the /web directory: [@rollup/rollup-darwin-arm64](https://github.com/rollup/rollup), [@rollup/rollup-linux-arm64-gnu](https://github.com/rollup/rollup), [@rollup/rollup-linux-x64-gnu](https://github.com/rollup/rollup) and [rollup](https://github.com/rollup/rollup).


Updates `@rollup/rollup-darwin-arm64` from 4.52.5 to 4.53.2
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.52.5...v4.53.2)

Updates `@rollup/rollup-linux-arm64-gnu` from 4.52.5 to 4.53.2
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.52.5...v4.53.2)

Updates `@rollup/rollup-linux-x64-gnu` from 4.52.5 to 4.53.2
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.52.5...v4.53.2)

Updates `rollup` from 4.52.5 to 4.53.2
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.52.5...v4.53.2)

---
updated-dependencies:
- dependency-name: "@rollup/rollup-darwin-arm64"
  dependency-version: 4.53.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: rollup
- dependency-name: "@rollup/rollup-linux-arm64-gnu"
  dependency-version: 4.53.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: rollup
- dependency-name: "@rollup/rollup-linux-x64-gnu"
  dependency-version: 4.53.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: rollup
- dependency-name: rollup
  dependency-version: 4.53.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: rollup
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-12 12:14:16 +01:00
dependabot[bot]
d2dfcf5883 web: bump the react group across 2 directories with 1 update (#18083)
Bumps the react group with 1 update in the /packages/docusaurus-config directory: [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react).
Bumps the react group with 1 update in the /web directory: [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react).


Updates `@types/react` from 19.2.2 to 19.2.3
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

Updates `@types/react` from 19.2.2 to 19.2.3
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

---
updated-dependencies:
- dependency-name: "@types/react"
  dependency-version: 19.2.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: react
- dependency-name: "@types/react"
  dependency-version: 19.2.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: react
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-12 12:13:45 +01:00
dependabot[bot]
ac5097dd14 core: bump goauthentik/fips-debian from f3228f8 to 5017d65 (#18084)
Bumps goauthentik/fips-debian from `f3228f8` to `5017d65`.

---
updated-dependencies:
- dependency-name: goauthentik/fips-debian
  dependency-version: trixie-slim-fips
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-12 12:13:35 +01:00
Jens L.
b682299edd core: improve app launch URL formatting (#18076)
* core: improve app launch URL formatting

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* format

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-11-12 12:06:16 +01:00
Jens L.
53426293aa providers/scim: allow custom schema data (#18073)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-11-12 00:20:46 +01:00
Teffen Ellis
e426f88401 web: Fix application of global styles in style roots. (#17444)
* web: Separate global styles from element roots.

* web: Flesh out style strategy plugin, fixes for imported styles.

* web: Clean up applying of dark theme.

web: unminify.

* web: Fix alignment, rendering on high contrast.

web: Apply footer resize.

web: Fix application of global styles in style roots.

web: Fix missing layout attribute.

web: Normalize background alignment.

web: Fix layout issues, color overrides.

web: Fix alignment, colors, jank.

web: Separate method into function.

web: Clean up alignment, reflow.

web: Fix colors, compatibility mode.

web: Add content left/right support.

web: Fix colors, compatibility mode overrides.

* Fix issue where missing config throws runtime error.

* web: Refactor.

* Update tests.

* web: Fix Storybook imports.

* Fix order of theme application.

* web: Fix storybook asset paths.

* web: Flesh out tests surrounding source buttons, fix alignment,
contrast.

* Update tests, clarify errors.

* Update test selectors, assertions.

* Clarify redirect handling.

* Adjust user check.

* Update logs.

* web: Fix selector timing.

* Fix alignment.

* Fix selectors, timing.

* Log current URL content.

* Refine shadow selector, add delay.

* Replace IDs with named elements.

* Fix overlay color.

* Fix footer padding.

* Fix contrast.

* Add selectable name to button.

* Fix alignment, mobile layout.

* web: Spread exported parts to stages.

* Fix z-index order.

* Tidy colors, behaviors, layout.

* Fix overflow scroll.

* Clean up duplicate color styles.

* Clarify selector order. Fix overrides, color contrast.

* Attempt to read JSON multiple times.

* Clarify error.

* web: Fix timeouts, URL changes.

* web: Fix disabled styles.

* Fix color flip.

* Fix selector.

* Fix issue where hidden tables will alter test URLs.

* Use DOM to look for connection, rather than API. Update selectors.

* Immediately navigate to tab.

* Upgrade Dex.

* Ensure Dex redirects.

* Use same host during tests.

* web: Update package-lock.json

* Add delay.
2025-11-11 15:49:00 -05:00
Jens L.
8ff1fc10ca events: fix timezone not set for log events (#18067)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-11-11 18:49:22 +01:00
Jens L.
c11f3d9f72 ci: attempt to fix integration tests using dind (#18066)
* ci: attempt to fix integration tests using dind

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* bump dind version

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-11-11 17:31:07 +01:00
Jens L.
364ca70724 ci: revert to upstream GHA for release (#18058)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-11-11 13:17:42 +01:00
dependabot[bot]
89bcfef363 web: bump the swc group across 1 directory with 12 updates (#17998)
Bumps the swc group with 2 updates in the /web directory: [@swc/cli](https://github.com/swc-project/pkgs) and [@swc/core](https://github.com/swc-project/swc).


Updates `@swc/cli` from 0.7.8 to 0.7.9
- [Commits](https://github.com/swc-project/pkgs/commits)

Updates `@swc/core` from 1.13.19 to 1.15.0
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.13.19...v1.15.0)

Updates `@swc/core-darwin-arm64` from 1.13.19 to 1.15.0
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.13.19...v1.15.0)

Updates `@swc/core-darwin-x64` from 1.13.19 to 1.15.0
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.13.19...v1.15.0)

Updates `@swc/core-linux-arm-gnueabihf` from 1.13.19 to 1.15.0
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.13.19...v1.15.0)

Updates `@swc/core-linux-arm64-gnu` from 1.13.19 to 1.15.0
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.13.19...v1.15.0)

Updates `@swc/core-linux-arm64-musl` from 1.13.19 to 1.15.0
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.13.19...v1.15.0)

Updates `@swc/core-linux-x64-gnu` from 1.13.19 to 1.15.0
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.13.19...v1.15.0)

Updates `@swc/core-linux-x64-musl` from 1.13.19 to 1.15.0
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.13.19...v1.15.0)

Updates `@swc/core-win32-arm64-msvc` from 1.13.19 to 1.15.0
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.13.19...v1.15.0)

Updates `@swc/core-win32-ia32-msvc` from 1.13.19 to 1.15.0
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.13.19...v1.15.0)

Updates `@swc/core-win32-x64-msvc` from 1.13.19 to 1.15.0
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.13.19...v1.15.0)

---
updated-dependencies:
- dependency-name: "@swc/cli"
  dependency-version: 0.7.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core"
  dependency-version: 1.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: swc
- dependency-name: "@swc/core-darwin-arm64"
  dependency-version: 1.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: swc
- dependency-name: "@swc/core-darwin-x64"
  dependency-version: 1.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: swc
- dependency-name: "@swc/core-linux-arm-gnueabihf"
  dependency-version: 1.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: swc
- dependency-name: "@swc/core-linux-arm64-gnu"
  dependency-version: 1.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: swc
- dependency-name: "@swc/core-linux-arm64-musl"
  dependency-version: 1.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: swc
- dependency-name: "@swc/core-linux-x64-gnu"
  dependency-version: 1.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: swc
- dependency-name: "@swc/core-linux-x64-musl"
  dependency-version: 1.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: swc
- dependency-name: "@swc/core-win32-arm64-msvc"
  dependency-version: 1.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: swc
- dependency-name: "@swc/core-win32-ia32-msvc"
  dependency-version: 1.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: swc
- dependency-name: "@swc/core-win32-x64-msvc"
  dependency-version: 1.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: swc
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-11 13:02:19 +01:00
TMUniversal
80b79ab901 website/docs: update discord social login script example (#18026)
update the guild membership example to no longer cause an exception from a missing import.

Closes #18025

Signed-off-by: TMUniversal <10200399+TMUniversal@users.noreply.github.com>
2025-11-11 12:55:01 +01:00
dependabot[bot]
17f229e575 core: bump goauthentik/fips-debian from 9b4cedf to f3228f8 (#17819)
Bumps goauthentik/fips-debian from `9b4cedf` to `f3228f8`.

---
updated-dependencies:
- dependency-name: goauthentik/fips-debian
  dependency-version: trixie-slim-fips
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-11 12:52:35 +01:00
Teffen Ellis
efc8822469 web: Make Spotlight optional. (#17904)
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2025-11-11 12:37:50 +01:00
dependabot[bot]
c04b491125 website: bump @types/node from 24.9.2 to 24.10.0 in /website (#17909)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 24.9.2 to 24.10.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-11 12:22:10 +01:00
dependabot[bot]
0809927def web: bump @types/node from 24.9.1 to 24.10.0 in /packages/esbuild-plugin-live-reload (#17948)
web: bump @types/node in /packages/esbuild-plugin-live-reload

Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 24.9.1 to 24.10.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.10.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-11 12:21:03 +01:00
dependabot[bot]
ec80106ee5 ci: bump calibreapp/image-actions from 05b1cf44e88c3b041b841452482df9497f046ef7 to 420075c115b26f8785e293c5bd5bef0911c506e5 (#17953)
ci: bump calibreapp/image-actions

Bumps [calibreapp/image-actions](https://github.com/calibreapp/image-actions) from 05b1cf44e88c3b041b841452482df9497f046ef7 to 420075c115b26f8785e293c5bd5bef0911c506e5.
- [Release notes](https://github.com/calibreapp/image-actions/releases)
- [Commits](05b1cf44e8...420075c115)

---
updated-dependencies:
- dependency-name: calibreapp/image-actions
  dependency-version: 420075c115b26f8785e293c5bd5bef0911c506e5
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-11 12:20:54 +01:00
Marcelo Elizeche Landó
57ea7a3454 core: bump googleapis-common-protos from 1.70.0 to v1.71.0 (#17979) 2025-11-11 12:20:32 +01:00
dependabot[bot]
39c83c5048 web: bump the sentry group across 1 directory with 2 updates (#17997)
Bumps the sentry group with 2 updates in the /web directory: [@sentry/browser](https://github.com/getsentry/sentry-javascript) and @spotlightjs/spotlight.


Updates `@sentry/browser` from 10.22.0 to 10.23.0
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/10.22.0...10.23.0)

Updates `@spotlightjs/spotlight` from 4.3.0 to 4.5.0

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  dependency-version: 10.23.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: sentry
- dependency-name: "@spotlightjs/spotlight"
  dependency-version: 4.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: sentry
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-11 12:20:13 +01:00
dependabot[bot]
413ee2dbb8 website: bump the build group across 1 directory with 9 updates (#17995)
Bumps the build group with 9 updates in the /website directory:

| Package | From | To |
| --- | --- | --- |
| [@rspack/binding-darwin-arm64](https://github.com/web-infra-dev/rspack/tree/HEAD/packages/rspack) | `1.6.0` | `1.6.1` |
| [@rspack/binding-linux-arm64-gnu](https://github.com/web-infra-dev/rspack/tree/HEAD/packages/rspack) | `1.6.0` | `1.6.1` |
| [@rspack/binding-linux-x64-gnu](https://github.com/web-infra-dev/rspack/tree/HEAD/packages/rspack) | `1.6.0` | `1.6.1` |
| [@swc/core-darwin-arm64](https://github.com/swc-project/swc) | `1.14.0` | `1.15.0` |
| [@swc/core-linux-arm64-gnu](https://github.com/swc-project/swc) | `1.14.0` | `1.15.0` |
| [@swc/core-linux-x64-gnu](https://github.com/swc-project/swc) | `1.14.0` | `1.15.0` |
| [@swc/html-darwin-arm64](https://github.com/swc-project/swc) | `1.14.0` | `1.15.0` |
| [@swc/html-linux-arm64-gnu](https://github.com/swc-project/swc) | `1.14.0` | `1.15.0` |
| [@swc/html-linux-x64-gnu](https://github.com/swc-project/swc) | `1.14.0` | `1.15.0` |



Updates `@rspack/binding-darwin-arm64` from 1.6.0 to 1.6.1
- [Release notes](https://github.com/web-infra-dev/rspack/releases)
- [Commits](https://github.com/web-infra-dev/rspack/commits/v1.6.1/packages/rspack)

Updates `@rspack/binding-linux-arm64-gnu` from 1.6.0 to 1.6.1
- [Release notes](https://github.com/web-infra-dev/rspack/releases)
- [Commits](https://github.com/web-infra-dev/rspack/commits/v1.6.1/packages/rspack)

Updates `@rspack/binding-linux-x64-gnu` from 1.6.0 to 1.6.1
- [Release notes](https://github.com/web-infra-dev/rspack/releases)
- [Commits](https://github.com/web-infra-dev/rspack/commits/v1.6.1/packages/rspack)

Updates `@swc/core-darwin-arm64` from 1.14.0 to 1.15.0
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.14.0...v1.15.0)

Updates `@swc/core-linux-arm64-gnu` from 1.14.0 to 1.15.0
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.14.0...v1.15.0)

Updates `@swc/core-linux-x64-gnu` from 1.14.0 to 1.15.0
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.14.0...v1.15.0)

Updates `@swc/html-darwin-arm64` from 1.14.0 to 1.15.0
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.14.0...v1.15.0)

Updates `@swc/html-linux-arm64-gnu` from 1.14.0 to 1.15.0
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.14.0...v1.15.0)

Updates `@swc/html-linux-x64-gnu` from 1.14.0 to 1.15.0
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.14.0...v1.15.0)

---
updated-dependencies:
- dependency-name: "@rspack/binding-darwin-arm64"
  dependency-version: 1.6.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@rspack/binding-linux-arm64-gnu"
  dependency-version: 1.6.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@rspack/binding-linux-x64-gnu"
  dependency-version: 1.6.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@swc/core-darwin-arm64"
  dependency-version: 1.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: build
- dependency-name: "@swc/core-linux-arm64-gnu"
  dependency-version: 1.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: build
- dependency-name: "@swc/core-linux-x64-gnu"
  dependency-version: 1.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: build
- dependency-name: "@swc/html-darwin-arm64"
  dependency-version: 1.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: build
- dependency-name: "@swc/html-linux-arm64-gnu"
  dependency-version: 1.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: build
- dependency-name: "@swc/html-linux-x64-gnu"
  dependency-version: 1.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: build
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-11 12:19:33 +01:00
transifex-integration[bot]
566969c7bf translate: Updates for file locale/en/LC_MESSAGES/django.po in fi (#18056)
Translate locale/en/LC_MESSAGES/django.po in fi

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fi'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-11-11 12:02:40 +01:00
dependabot[bot]
91650ea180 core: bump selenium/standalone-chromium from 141.0 to 142.0 in /tests/e2e (#17910)
core: bump selenium/standalone-chromium in /tests/e2e

Bumps selenium/standalone-chromium from 141.0 to 142.0.

---
updated-dependencies:
- dependency-name: selenium/standalone-chromium
  dependency-version: '142.0'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-11 12:00:09 +01:00
dependabot[bot]
70e70c9203 website: bump the eslint group across 1 directory with 5 updates (#17928)
Bumps the eslint group with 5 updates in the /website directory:

| Package | From | To |
| --- | --- | --- |
| [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js) | `9.38.0` | `9.39.1` |
| [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `8.46.2` | `8.46.3` |
| [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) | `8.46.2` | `8.46.3` |
| [eslint](https://github.com/eslint/eslint) | `9.38.0` | `9.39.1` |
| [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.46.2` | `8.46.3` |



Updates `@eslint/js` from 9.38.0 to 9.39.1
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/commits/v9.39.1/packages/js)

Updates `@typescript-eslint/eslint-plugin` from 8.46.2 to 8.46.3
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.46.3/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.46.2 to 8.46.3
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.46.3/packages/parser)

Updates `eslint` from 9.38.0 to 9.39.1
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v9.38.0...v9.39.1)

Updates `typescript-eslint` from 8.46.2 to 8.46.3
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.46.3/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: "@eslint/js"
  dependency-version: 9.39.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: eslint
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.46.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: eslint
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.46.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: eslint
- dependency-name: eslint
  dependency-version: 9.39.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: eslint
- dependency-name: typescript-eslint
  dependency-version: 8.46.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: eslint
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-11 11:59:50 +01:00
dependabot[bot]
e3a374f9c0 core: bump github.com/go-openapi/runtime from 0.29.0 to 0.29.2 (#18048)
Bumps [github.com/go-openapi/runtime](https://github.com/go-openapi/runtime) from 0.29.0 to 0.29.2.
- [Release notes](https://github.com/go-openapi/runtime/releases)
- [Commits](https://github.com/go-openapi/runtime/compare/v0.29.0...v0.29.2)

---
updated-dependencies:
- dependency-name: github.com/go-openapi/runtime
  dependency-version: 0.29.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-11 11:59:28 +01:00
dependabot[bot]
090f73b1f9 core: bump gorm.io/gorm from 1.31.0 to 1.31.1 (#17907)
Bumps [gorm.io/gorm](https://github.com/go-gorm/gorm) from 1.31.0 to 1.31.1.
- [Release notes](https://github.com/go-gorm/gorm/releases)
- [Commits](https://github.com/go-gorm/gorm/compare/v1.31.0...v1.31.1)

---
updated-dependencies:
- dependency-name: gorm.io/gorm
  dependency-version: 1.31.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-11 11:58:34 +01:00
dependabot[bot]
6538e94961 core: bump github.com/getsentry/sentry-go from 0.36.1 to 0.36.2 (#17785)
Bumps [github.com/getsentry/sentry-go](https://github.com/getsentry/sentry-go) from 0.36.1 to 0.36.2.
- [Release notes](https://github.com/getsentry/sentry-go/releases)
- [Changelog](https://github.com/getsentry/sentry-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-go/compare/v0.36.1...v0.36.2)

---
updated-dependencies:
- dependency-name: github.com/getsentry/sentry-go
  dependency-version: 0.36.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-11 11:58:20 +01:00
dependabot[bot]
0c9de742fc web: bump the eslint group across 2 directories with 5 updates (#18049)
Bumps the eslint group with 2 updates in the /packages/eslint-config directory: [eslint](https://github.com/eslint/eslint) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).
Bumps the eslint group with 2 updates in the /web directory: [eslint](https://github.com/eslint/eslint) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).


Updates `eslint` from 9.38.0 to 9.39.1
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v9.38.0...v9.39.1)

Updates `typescript-eslint` from 8.46.2 to 8.46.4
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.46.4/packages/typescript-eslint)

Updates `eslint` from 9.38.0 to 9.39.1
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v9.38.0...v9.39.1)

Updates `typescript-eslint` from 8.46.2 to 8.46.4
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.46.4/packages/typescript-eslint)

Updates `@eslint/js` from 9.38.0 to 9.39.1
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/commits/v9.39.1/packages/js)

Updates `@typescript-eslint/eslint-plugin` from 8.46.2 to 8.46.4
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.46.4/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.46.2 to 8.46.4
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.46.4/packages/parser)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 9.39.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: eslint
- dependency-name: typescript-eslint
  dependency-version: 8.46.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: eslint
- dependency-name: eslint
  dependency-version: 9.39.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: eslint
- dependency-name: typescript-eslint
  dependency-version: 8.46.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: eslint
- dependency-name: "@eslint/js"
  dependency-version: 9.39.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: eslint
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.46.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: eslint
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.46.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: eslint
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-11 11:58:02 +01:00
dependabot[bot]
5b450d07f6 web: bump the storybook group across 1 directory with 5 updates (#18050)
Bumps the storybook group with 4 updates in the /web directory: [@storybook/addon-docs](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/docs), [@storybook/addon-links](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/links), [@storybook/web-components](https://github.com/storybookjs/storybook/tree/HEAD/code/renderers/web-components) and [@storybook/web-components-vite](https://github.com/storybookjs/storybook/tree/HEAD/code/frameworks/web-components-vite).


Updates `@storybook/addon-docs` from 10.0.3 to 10.0.6
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v10.0.6/code/addons/docs)

Updates `@storybook/addon-links` from 10.0.3 to 10.0.6
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v10.0.6/code/addons/links)

Updates `@storybook/web-components` from 10.0.3 to 10.0.6
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v10.0.6/code/renderers/web-components)

Updates `@storybook/web-components-vite` from 10.0.3 to 10.0.6
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v10.0.6/code/frameworks/web-components-vite)

Updates `storybook` from 10.0.3 to 10.0.6
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v10.0.6/code/core)

---
updated-dependencies:
- dependency-name: "@storybook/addon-docs"
  dependency-version: 10.0.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-links"
  dependency-version: 10.0.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/web-components"
  dependency-version: 10.0.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/web-components-vite"
  dependency-version: 10.0.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: storybook
  dependency-version: 10.0.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-11 11:57:16 +01:00
dependabot[bot]
2ce0db0515 ci: bump astral-sh/setup-uv from 7.1.2 to 7.1.3 in /.github/actions/setup (#18053)
ci: bump astral-sh/setup-uv in /.github/actions/setup

Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 7.1.2 to 7.1.3.
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](85856786d1...5a7eac68fb)

---
updated-dependencies:
- dependency-name: astral-sh/setup-uv
  dependency-version: 7.1.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-11 11:54:03 +01:00
Ken Sternberg
cbc1351ef2 website/release notes: fix broken urls (#18041)
* website: fix bad escaping of URLs in release notes

## What

Fixes bad escaping of URLs in the release notes that resulted in mangled output.

v2024.6.4 had entries that looked like this:

```
##### `GET` /providers/google_workspace/{#123;id}#125;/
```

v2025.4.md had entries that looked like this:

```
##### `GET` /policies/unique_password/{#125;#123;policy_uuid}/
```

A couple of straightforward search-and-replaces has fixed the issue.

## Notes

Two of the release notes had bad escaping of URLs. I'm not sure how the error was made or got past,
but it was obvious when visiting the page.

@Beryju suggested that the bug is due to our using `{...}` to symbolize parameters in a URL while
Docusaurus wants to interpret `{...}` as an internal template instruction, resulting in odd
behavior. In either case, docusarus interpreted the hashtagged entries as links to unrelated issues
in Github (the same two issues, which were "bump version of pylint" and "bump version of sentry"),
which could be very confusing.

The inconsistencies between the two releases, and the working releases, suggests that the error was
introduced manually.
2025-11-10 09:55:30 -08:00
authentik-automation[bot]
69000ea849 core, web: update translations (#17943)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2025-11-10 16:10:04 +01:00
dependabot[bot]
2aeb1b2448 web: bump @types/node from 24.9.1 to 24.10.0 in /packages/prettier-config (#17949)
web: bump @types/node in /packages/prettier-config

Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 24.9.1 to 24.10.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.10.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-10 16:09:46 +01:00
dependabot[bot]
b513baf980 core: bump library/nginx from f547e3d to 1beed3c in /website (#17955)
Bumps library/nginx from `f547e3d` to `1beed3c`.

---
updated-dependencies:
- dependency-name: library/nginx
  dependency-version: 1.29-trixie
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-10 16:09:31 +01:00
dependabot[bot]
4506b9703d core: bump goauthentik.io/api/v3 from 3.2025120.2 to 3.2025120.3 (#17945)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2025120.2 to 3.2025120.3.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Changelog](https://github.com/goauthentik/client-go/blob/main/model_version_history.go)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2025120.2...v3.2025120.3)

---
updated-dependencies:
- dependency-name: goauthentik.io/api/v3
  dependency-version: 3.2025120.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-10 16:09:11 +01:00
dependabot[bot]
daf3cf9ce3 web: bump @types/node from 22.15.19 to 24.10.0 in /web (#17950)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.15.19 to 24.10.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.10.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-10 16:08:58 +01:00
dependabot[bot]
abf5575001 ci: bump docker/setup-qemu-action from 3.6.0 to 3.7.0 (#17999)
Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.6.0 to 3.7.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](29109295f8...c7c5346462)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 3.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-10 15:57:48 +01:00
dependabot[bot]
dded84769f lifecycle/aws: bump aws-cdk from 2.1031.1 to 2.1031.2 in /lifecycle/aws (#18014)
Bumps [aws-cdk](https://github.com/aws/aws-cdk-cli/tree/HEAD/packages/aws-cdk) from 2.1031.1 to 2.1031.2.
- [Release notes](https://github.com/aws/aws-cdk-cli/releases)
- [Commits](https://github.com/aws/aws-cdk-cli/commits/aws-cdk@v2.1031.2/packages/aws-cdk)

---
updated-dependencies:
- dependency-name: aws-cdk
  dependency-version: 2.1031.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-10 15:56:59 +01:00
dependabot[bot]
fd44a206d9 core: bump golang.org/x/sync from 0.17.0 to 0.18.0 (#18033)
Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.17.0 to 0.18.0.
- [Commits](https://github.com/golang/sync/compare/v0.17.0...v0.18.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sync
  dependency-version: 0.18.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-10 15:56:00 +01:00
dependabot[bot]
9625270aed core: bump astral-sh/uv from 0.9.7 to 0.9.8 (#18037)
Bumps [astral-sh/uv](https://github.com/astral-sh/uv) from 0.9.7 to 0.9.8.
- [Release notes](https://github.com/astral-sh/uv/releases)
- [Changelog](https://github.com/astral-sh/uv/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/uv/compare/0.9.7...0.9.8)

---
updated-dependencies:
- dependency-name: astral-sh/uv
  dependency-version: 0.9.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-10 14:41:35 +01:00
dependabot[bot]
2888f35e54 core: bump golang.org/x/oauth2 from 0.32.0 to 0.33.0 (#18034)
Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.32.0 to 0.33.0.
- [Commits](https://github.com/golang/oauth2/compare/v0.32.0...v0.33.0)

---
updated-dependencies:
- dependency-name: golang.org/x/oauth2
  dependency-version: 0.33.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-10 14:40:57 +01:00
dependabot[bot]
8ed53627ff core: bump axllent/mailpit from v1.27.10 to v1.27.11 in /tests/e2e (#18035)
Bumps axllent/mailpit from v1.27.10 to v1.27.11.

---
updated-dependencies:
- dependency-name: axllent/mailpit
  dependency-version: v1.27.11
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-10 14:40:42 +01:00
dependabot[bot]
7ce46ac301 ci: bump golangci/golangci-lint-action from 8.0.0 to 9.0.0 (#18036)
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 8.0.0 to 9.0.0.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](4afd733a84...0a35821d5c)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-version: 9.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-10 14:40:28 +01:00
dependabot[bot]
60dbaf1fa1 core: bump library/golang from a13297b to 27e1c92 (#18038)
* core: bump library/golang from `a13297b` to `27e1c92`

Bumps library/golang from `a13297b` to `27e1c92`.

---
updated-dependencies:
- dependency-name: library/golang
  dependency-version: 1.25.4-trixie
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* dont require python

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix error code

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2025-11-10 14:40:11 +01:00
292 changed files with 15893 additions and 19043 deletions

View File

@@ -1,63 +0,0 @@
# syntax=docker/dockerfile:1
# Start from the same FIPS Python base as production (python-base stage)
FROM ghcr.io/goauthentik/fips-python:3.13.9-slim-trixie-fips@sha256:700fc8c1e290bd14e5eaca50b1d8e8c748c820010559cbfb4c4f8dfbe2c4c9ff
USER root
# Setup environment matching production python-base stage
ENV VENV_PATH="/ak-root/.venv" \
PATH="/lifecycle:/ak-root/.venv/bin:$PATH" \
UV_COMPILE_BYTECODE=1 \
UV_LINK_MODE=copy \
UV_NATIVE_TLS=1 \
UV_PYTHON_DOWNLOADS=0
WORKDIR /ak-root
# Copy uv package manager
COPY --from=ghcr.io/astral-sh/uv:0.9.7@sha256:ba4857bf2a068e9bc0e64eed8563b065908a4cd6bfb66b531a9c424c8e25e142 /uv /uvx /bin/
# Install build dependencies
RUN rm -f /etc/apt/apt.conf.d/docker-clean && \
echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache && \
apt-get update && \
apt-get install -y --no-install-recommends \
# Build essentials
build-essential pkg-config libffi-dev git binutils \
# cryptography
curl \
# libxml
libxslt-dev zlib1g-dev \
# postgresql
libpq-dev \
# python-kadmin-rs and kerberos testing
clang libkrb5-dev sccache krb5-kdc krb5-admin-server \
# xmlsec
libltdl-dev \
# runit (for chpst command used by lifecycle/ak)
runit \
# sudo (required by devcontainer features)
sudo && \
rm -rf /var/lib/apt/lists/*
# Environment for building native Python packages
ENV UV_NO_BINARY_PACKAGE="cryptography lxml python-kadmin-rs xmlsec" \
RUSTUP_PERMIT_COPY_RENAME="true"
# Create authentik user with proper home directory (required for devcontainer features)
RUN adduser --disabled-password --gecos "" --uid 1000 --home /home/authentik authentik && \
mkdir -p /certs /media /ak-root && \
chown -R authentik:authentik /certs /media /ak-root /home/authentik && \
echo "authentik ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/authentik
# FIPS configuration for Go development
# Don't set GOFIPS/GOFIPS140 globally to avoid breaking Go tools like docker-compose
# These will be set when building/running authentik Go code (see lifecycle/ak and Makefile)
ENV CGO_ENABLED=1
# Set TMPDIR for PID files and temp data
# Use /tmp instead of /dev/shm for development because go run needs to execute binaries
ENV TMPDIR=/tmp
USER authentik

View File

@@ -1,68 +0,0 @@
{
"name": "authentik",
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"workspaceFolder": "/ak-root",
"containerUser": "authentik",
"remoteUser": "authentik",
"shutdownAction": "stopCompose",
"containerEnv": {
"LOCAL_PROJECT_DIR": "/ak-root"
},
"features": {
"ghcr.io/devcontainers/features/go:1": {
"version": "1.24"
},
"ghcr.io/devcontainers/features/node:1": {
"version": "22"
},
"ghcr.io/devcontainers/features/rust:1": {
"version": "latest"
},
"ghcr.io/devcontainers/features/docker-in-docker:2": {
"version": "latest",
"moby": false
}
},
"mounts": [],
"forwardPorts": [9000, 9443],
"portsAttributes": {
"8000": {
"onAutoForward": "ignore"
},
"3963": {
"onAutoForward": "ignore"
},
"35151": {
"onAutoForward": "ignore"
},
"9901": {
"onAutoForward": "ignore"
}
},
"postCreateCommand": "bash .devcontainer/setup.sh",
"customizations": {
"vscode": {
"extensions": [
"EditorConfig.EditorConfig",
"bashmish.es6-string-css",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"golang.go",
"Gruntfuggly.todo-tree",
"ms-python.black-formatter",
"ms-python.isort",
"ms-python.pylint",
"ms-python.python",
"ms-python.vscode-pylance",
"redhat.vscode-yaml",
"Tobermory.es6-string-html",
"charliermarsh.ruff"
],
"settings": {
"python.defaultInterpreterPath": "/ak-root/.venv/bin/python",
"python.terminal.activateEnvironment": true
}
}
}
}

View File

@@ -1,50 +0,0 @@
services:
app:
build:
context: ..
dockerfile: .devcontainer/Dockerfile
user: authentik
privileged: true
volumes:
- ../:/ak-root
entrypoint: []
command: sleep infinity
depends_on:
postgresql:
condition: service_healthy
env_file: .env
environment:
PATH: "/ak-root/.venv/bin:${PATH}"
ports:
- "9000:9000"
- "9443:9443"
postgresql:
image: docker.io/library/postgres:16
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -d authentik -U postgres"]
interval: 10s
timeout: 5s
retries: 5
start_period: 20s
volumes:
- postgres-data:/var/lib/postgresql/data
env_file: .env
command: ["postgres", "-c", "log_statement=all", "-c", "log_destination=stderr"]
s3:
image: docker.io/zenko/cloudserver
env_file: .env
environment:
REMOTE_MANAGEMENT_DISABLE: "1"
ports:
- "8020:8000"
volumes:
- s3-data:/usr/src/app/localData
- s3-metadata:/usr/src/app/localMetadata
volumes:
postgres-data:
s3-data:
s3-metadata:

View File

@@ -1,37 +0,0 @@
#!/usr/bin/env bash
set -e
echo "======================================"
echo "Running authentik devcontainer setup"
echo "======================================"
echo ""
echo "Step 1/5: Installing dependencies"
make install
echo ""
echo "Step 2/5: Generating development config"
make gen-dev-config
echo ""
echo "Step 3/5: Running database migrations"
make migrate
echo ""
echo "Step 4/5: Generating API clients"
make gen
echo ""
echo "Step 5/5: Building web assets"
make web
echo ""
echo "======================================"
echo "Setup complete!"
echo "======================================"
echo ""
echo "You can now run:"
echo " - 'make run-server' to start the backend server"
echo " - 'make run-worker' to start the worker (must be ran once after initial setup)"
echo " - 'make web-watch' for live web development"
echo ""

81
.github/ISSUE_TEMPLATE/1-bug-report.yml vendored Normal file
View File

@@ -0,0 +1,81 @@
name: Bug report
description: Create a report to help us improve
labels: ["bug", "triage"]
type: bug
body:
- type: markdown
attributes:
value: |
Thank you for taking the time to fill out this bug report!
- type: textarea
id: describe-the-bug
attributes:
label: Describe the bug
description: "A clear and concise description of what the bug is."
placeholder: "Describe the issue"
validations:
required: true
- type: textarea
id: how-to-reproduce
attributes:
label: How to reproduce
description: "Steps to reproduce the behavior."
placeholder: |
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
validations:
required: true
- type: textarea
id: expected-behavior
attributes:
label: Expected behavior
description: "A clear and concise description of what you expected to happen."
placeholder: "The behavior that I expect to see is [...]"
validations:
required: true
- type: textarea
id: screenshots
attributes:
label: Screenshots
description: "If applicable, add screenshots to help explain your problem."
validations:
required: false
- type: textarea
id: additional-context
attributes:
label: Additional context
description: "Add any other context about the problem here."
placeholder: "Also note that [...]"
validations:
required: false
- type: dropdown
id: deployment-method
attributes:
label: Deployment Method
description: "What deployment method are you using for authentik? Only Docker, Kubernetes and AWS CloudFormation are supported."
options:
- Docker
- Kubernetes
- AWS CloudFormation
- Other (please specify)
default: 0
validations:
required: true
- type: input
id: version
attributes:
label: Version
description: "What version of authentik are you using?"
placeholder: "[e.g. 2025.10.1]"
validations:
required: true
- type: textarea
id: logs
attributes:
label: Relevant log output
description: "Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks."
render: shell
validations:
required: false

49
.github/ISSUE_TEMPLATE/2-docs-issue.yml vendored Normal file
View File

@@ -0,0 +1,49 @@
name: Documentation suggestion/problem
description: Suggest an improvement or report a problem in our docs
labels: ["area: docs", "triage"]
type: task
body:
- type: markdown
attributes:
value: |
Thank you for taking the time to fill out this documentation issue!
- type: markdown
attributes:
value: |
**Consider opening a PR!**
If the issue is one that you can fix, or even make a good pass at, we'd appreciate a PR.
For more information about making a contribution to the docs, and using our Style Guide and our templates, refer to ["Writing documentation"](https://docs.goauthentik.io/docs/developer-docs/docs/writing-documentation).
- type: textarea
id: issue
attributes:
label: Do you see an area that can be clarified or expanded, a technical inaccuracy, or a broken link?
description: "A clear and concise description of what the problem is, or where the document can be improved."
placeholder: "I believe we need more details about [...]"
validations:
required: true
- type: input
id: link
attributes:
label: Link
description: "Provide the URL or link to the exact page in the documentation to which you are referring."
placeholder: "If there are multiple pages, list them all"
validations:
required: true
- type: textarea
id: solution
attributes:
label: Solution
description: "A clear and concise description of what you suggest as a solution"
placeholder: "This issue could be resolved by [...]"
validations:
required: true
- type: textarea
id: additional-context
attributes:
label: Additional context
description: "Add any other context or screenshots about the documentation issue here."
placeholder: "Also note that [...]"
validations:
required: false

View File

@@ -0,0 +1,41 @@
name: Feature request
description: Suggest an idea for a feature
labels: ["enhancement", "triage"]
type: feature
body:
- type: markdown
attributes:
value: |
Thank you for taking the time to fill out this feature request!
- type: textarea
id: related-to-problem
attributes:
label: Is your feature request related to a problem?
description: "A clear and concise description of what the problem is."
placeholder: "I'm always frustrated when [...]"
validations:
required: true
- type: textarea
id: feature
attributes:
label: Describe the solution you'd like
description: A clear and concise description of what you want to happen.
placeholder: "I'd like authentik to have [...]"
validations:
required: false
- type: textarea
id: alternatives
attributes:
label: Describe alternatives that you've considered
description: "A clear and concise description of any alternative solutions or features you've considered."
placeholder: "I've tried this but [...]"
validations:
required: true
- type: textarea
id: additional-context
attributes:
label: Additional context
description: "Add any other context or screenshots about the feature request here."
placeholder: "Also note that [...]"
validations:
required: false

View File

@@ -1,39 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ""
labels: bug
assignees: ""
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Logs**
Output of docker-compose logs or kubectl logs respectively
**Version and Deployment (please complete the following information):**
<!--
Notice: authentik supports installation via Docker, Kubernetes, and AWS CloudFormation only. Support is not available for other methods. For detailed installation and configuration instructions, please refer to the official documentation at https://docs.goauthentik.io/docs/install-config/.
-->
- authentik version: [e.g. 2025.2.0]
- Deployment: [e.g. docker-compose, helm]
**Additional context**
Add any other context about the problem here.

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Question
url: https://github.com/goauthentik/authentik/discussions
about: Please ask questions via GitHub Discussions rather than creating issues.
- name: authentik Discord
url: https://discord.com/invite/jg33eMhnj6
about: For community support, visit our Discord server.

View File

@@ -1,22 +0,0 @@
---
name: Documentation issue
about: Suggest an improvement or report a problem
title: ""
labels: documentation
assignees: ""
---
**Do you see an area that can be clarified or expanded, a technical inaccuracy, or a broken link? Please describe.**
A clear and concise description of what the problem is, or where the document can be improved. Ex. I believe we need more details about [...]
**Provide the URL or link to the exact page in the documentation to which you are referring.**
If there are multiple pages, list them all, and be sure to state the header or section where the content is.
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Additional context**
Add any other context or screenshots about the documentation issue here.
**Consider opening a PR!**
If the issue is one that you can fix, or even make a good pass at, we'd appreciate a PR. For more information about making a contribution to the docs, and using our Style Guide and our templates, refer to ["Writing documentation"](https://docs.goauthentik.io/docs/developer-docs/docs/writing-documentation).

View File

@@ -1,19 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ""
labels: enhancement
assignees: ""
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@@ -1,17 +0,0 @@
---
name: Hackathon Idea
about: Propose an idea for the hackathon
title: ""
labels: hackathon
assignees: ""
---
**Describe the idea**
A clear concise description of the idea you want to implement
You're also free to work on existing GitHub issues, whether they be feature requests or bugs, just link the existing GitHub issue here.
<!-- Don't modify below here -->
If you want to help working on this idea or want to contribute in any other way, react to this issue with a :rocket:

View File

@@ -0,0 +1,7 @@
---
name: Blank issue
about: This issue type is only for internal use
title:
labels:
assignees:
---

View File

@@ -1,32 +0,0 @@
---
name: Question
about: Ask a question about a feature or specific configuration
title: ""
labels: question
assignees: ""
---
**Describe your question/**
A clear and concise description of what you're trying to do.
**Relevant info**
i.e. Version of other software you're using, specifics of your setup
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Logs**
Output of docker-compose logs or kubectl logs respectively
**Version and Deployment (please complete the following information):**
<!--
Notice: authentik supports installation via Docker, Kubernetes, and AWS CloudFormation only. Support is not available for other methods. For detailed installation and configuration instructions, please refer to the official documentation at https://docs.goauthentik.io/docs/install-config/.
-->
- authentik version: [e.g. 2025.2.0]
- Deployment: [e.g. docker-compose, helm]
**Additional context**
Add any other context about the problem here.

View File

@@ -21,7 +21,7 @@ runs:
sudo apt-get install --no-install-recommends -y libpq-dev openssl libxmlsec1-dev pkg-config gettext libkrb5-dev krb5-kdc krb5-user krb5-admin-server
- name: Install uv
if: ${{ contains(inputs.dependencies, 'python') }}
uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 # v5
uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v5
with:
enable-cache: true
- name: Setup python

View File

@@ -43,7 +43,7 @@ jobs:
contents: read
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3
- name: prepare variables
uses: ./.github/actions/docker-push-variables
@@ -87,7 +87,6 @@ jobs:
id: push
with:
context: .
file: lifecycle/container/Dockerfile
push: ${{ steps.ev.outputs.shouldPush == 'true' }}
secrets: |
GEOIPUPDATE_ACCOUNT_ID=${{ secrets.GEOIPUPDATE_ACCOUNT_ID }}

View File

@@ -73,7 +73,7 @@ jobs:
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Set up QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3
- name: prepare variables

View File

@@ -76,6 +76,7 @@ jobs:
fetch-depth: 0
- name: checkout stable
run: |
set -e -o pipefail
# Copy current, latest config to local
cp authentik/lib/default.yml local.env.yml
cp -R .github ..
@@ -83,7 +84,7 @@ jobs:
# Previous stable tag
prev_stable=$(git tag --sort=version:refname | grep '^version/' | grep -vE -- '-rc[0-9]+$' | tail -n1)
# Current version family based on
current_version_family=$(python -c "from authentik import VERSION; print(VERSION)" | grep -vE -- 'rc[0-9]+$')
current_version_family=$(cat internal/constants/VERSION | grep -vE -- 'rc[0-9]+$' || true)
if [[ -n $current_version_family ]]; then
prev_stable=$current_version_family
fi

View File

@@ -34,7 +34,7 @@ jobs:
- name: Generate API
run: make gen-client-go
- name: golangci-lint
uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8
uses: golangci/golangci-lint-action@0a35821d5c230e903fcfe077583637dea1b27b47 # v8
with:
version: latest
args: --timeout 5000s --verbose
@@ -90,7 +90,7 @@ jobs:
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Set up QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3
- name: prepare variables
@@ -114,7 +114,7 @@ jobs:
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
with:
tags: ${{ steps.ev.outputs.imageTags }}
file: lifecycle/container/${{ matrix.type }}.Dockerfile
file: ${{ matrix.type }}.Dockerfile
push: ${{ steps.ev.outputs.shouldPush == 'true' }}
build-args: |
GIT_BUILD_HASH=${{ steps.ev.outputs.sha }}

View File

@@ -38,7 +38,7 @@ jobs:
token: ${{ steps.generate_token.outputs.token }}
- name: Compress images
id: compress
uses: calibreapp/image-actions@05b1cf44e88c3b041b841452482df9497f046ef7 # main
uses: calibreapp/image-actions@420075c115b26f8785e293c5bd5bef0911c506e5 # main
with:
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
compressOnly: ${{ github.event_name != 'pull_request' }}

View File

@@ -33,7 +33,7 @@ jobs:
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- name: Set up QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3
- name: prepare variables
@@ -88,7 +88,7 @@ jobs:
with:
go-version-file: "go.mod"
- name: Set up QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3
- name: prepare variables
@@ -121,7 +121,7 @@ jobs:
build-args: |
VERSION=${{ github.ref }}
tags: ${{ steps.ev.outputs.imageTags }}
file: lifecycle/container/${{ matrix.type }}.Dockerfile
file: ${{ matrix.type }}.Dockerfile
platforms: linux/amd64,linux/arm64
context: .
- uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3

View File

@@ -89,7 +89,7 @@ jobs:
git tag "version/${{ inputs.version }}" HEAD -m "version/${{ inputs.version }}"
git push --follow-tags
- name: Create Release
uses: goauthentik/action-gh-release@84da137b91a625a58fe8a34f3bd6bdb034a49138
uses: softprops/action-gh-release@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe # v2.4.2
with:
token: "${{ steps.app-token.outputs.token }}"
tag_name: "version/${{ inputs.version }}"

2
.gitignore vendored
View File

@@ -211,4 +211,4 @@ source_docs/
/vendor/
### Docker ###
/lifecycle/container/docker-compose.override.yml
docker-compose.override.yml

View File

@@ -16,8 +16,10 @@ go.sum @goauthentik/backend
# Infrastructure
.github/ @goauthentik/infrastructure
lifecycle/aws/ @goauthentik/infrastructure
lifecycle/container/ @goauthentik/infrastructure
Dockerfile @goauthentik/infrastructure
*Dockerfile @goauthentik/infrastructure
.dockerignore @goauthentik/infrastructure
docker-compose.yml @goauthentik/infrastructure
Makefile @goauthentik/infrastructure
.editorconfig @goauthentik/infrastructure
CODEOWNERS @goauthentik/infrastructure

View File

@@ -26,7 +26,7 @@ RUN npm run build && \
npm run build:sfe
# Stage 2: Build go proxy
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.4-trixie@sha256:a13297bfdd45d9702badd83b45c6db1f8fee638bba6ece0c359ce51260577bbe AS go-builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.4-trixie@sha256:27e1c927a07ed2c7295d39941d6d881424739dbde9ae3055d0d3013699ed35e8 AS go-builder
ARG TARGETOS
ARG TARGETARCH
@@ -76,7 +76,7 @@ RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
/bin/sh -c "GEOIPUPDATE_LICENSE_KEY_FILE=/run/secrets/GEOIPUPDATE_LICENSE_KEY /usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0"
# Stage 4: Download uv
FROM ghcr.io/astral-sh/uv:0.9.7@sha256:ba4857bf2a068e9bc0e64eed8563b065908a4cd6bfb66b531a9c424c8e25e142 AS uv
FROM ghcr.io/astral-sh/uv:0.9.9@sha256:f6e3549ed287fee0ddde2460a2a74a2d74366f84b04aaa34c1f19fec40da8652 AS uv
# Stage 5: Base python image
FROM ghcr.io/goauthentik/fips-python:3.13.9-slim-trixie-fips@sha256:700fc8c1e290bd14e5eaca50b1d8e8c748c820010559cbfb4c4f8dfbe2c4c9ff AS python-base

View File

@@ -47,7 +47,7 @@ help: ## Show this help
@echo ""
go-test:
GOFIPS140=latest CGO_ENABLED=1 go test -timeout 0 -v -race -cover ./...
go test -timeout 0 -v -race -cover ./...
test: ## Run the server tests and produce a coverage report (locally)
uv run coverage run manage.py test --keepdb authentik
@@ -293,7 +293,7 @@ docs-api-clean: ## Clean generated API documentation
docker: ## Build a docker image of the current source tree
mkdir -p ${GEN_API_TS}
DOCKER_BUILDKIT=1 docker build . -f lifecycle/container/Dockerfile --progress plain --tag ${DOCKER_IMAGE}
DOCKER_BUILDKIT=1 docker build . --progress plain --tag ${DOCKER_IMAGE}
test-docker:
BUILD=true ${PWD}/scripts/test_docker.sh

View File

@@ -15,7 +15,7 @@ from django.db import models
from django.db.models import Q, QuerySet, options
from django.db.models.constants import LOOKUP_SEP
from django.http import HttpRequest
from django.utils.functional import SimpleLazyObject, cached_property
from django.utils.functional import cached_property
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
from django_cte import CTE, with_cte
@@ -44,18 +44,19 @@ from authentik.tenants.models import DEFAULT_TOKEN_DURATION, DEFAULT_TOKEN_LENGT
from authentik.tenants.utils import get_current_tenant, get_unique_identifier
LOGGER = get_logger()
USER_ATTRIBUTE_DEBUG = "goauthentik.io/user/debug"
USER_ATTRIBUTE_GENERATED = "goauthentik.io/user/generated"
USER_ATTRIBUTE_EXPIRES = "goauthentik.io/user/expires"
USER_ATTRIBUTE_DELETE_ON_LOGOUT = "goauthentik.io/user/delete-on-logout"
USER_ATTRIBUTE_SOURCES = "goauthentik.io/user/sources"
USER_ATTRIBUTE_TOKEN_EXPIRING = "goauthentik.io/user/token-expires" # nosec
USER_ATTRIBUTE_TOKEN_MAXIMUM_LIFETIME = "goauthentik.io/user/token-maximum-lifetime" # nosec
USER_ATTRIBUTE_CHANGE_USERNAME = "goauthentik.io/user/can-change-username"
USER_ATTRIBUTE_CHANGE_NAME = "goauthentik.io/user/can-change-name"
USER_ATTRIBUTE_CHANGE_EMAIL = "goauthentik.io/user/can-change-email"
USER_PATH_SYSTEM_PREFIX = "goauthentik.io"
USER_PATH_SERVICE_ACCOUNT = USER_PATH_SYSTEM_PREFIX + "/service-accounts"
_USER_ATTR_PREFIX = f"{USER_PATH_SYSTEM_PREFIX}/user"
USER_ATTRIBUTE_DEBUG = f"{_USER_ATTR_PREFIX}/debug"
USER_ATTRIBUTE_GENERATED = f"{_USER_ATTR_PREFIX}/generated"
USER_ATTRIBUTE_EXPIRES = f"{_USER_ATTR_PREFIX}/expires"
USER_ATTRIBUTE_DELETE_ON_LOGOUT = f"{_USER_ATTR_PREFIX}/delete-on-logout"
USER_ATTRIBUTE_SOURCES = f"{_USER_ATTR_PREFIX}/sources"
USER_ATTRIBUTE_TOKEN_EXPIRING = f"{_USER_ATTR_PREFIX}/token-expires" # nosec
USER_ATTRIBUTE_TOKEN_MAXIMUM_LIFETIME = f"{_USER_ATTR_PREFIX}/token-maximum-lifetime" # nosec
USER_ATTRIBUTE_CHANGE_USERNAME = f"{_USER_ATTR_PREFIX}/can-change-username"
USER_ATTRIBUTE_CHANGE_NAME = f"{_USER_ATTR_PREFIX}/can-change-name"
USER_ATTRIBUTE_CHANGE_EMAIL = f"{_USER_ATTR_PREFIX}/can-change-email"
USER_PATH_SERVICE_ACCOUNT = f"{USER_PATH_SYSTEM_PREFIX}/service-accounts"
options.DEFAULT_NAMES = options.DEFAULT_NAMES + (
# used_by API that allows models to specify if they shadow an object
@@ -585,18 +586,16 @@ class Application(SerializerModel, PolicyBindingModel):
def get_launch_url(self, user: Optional["User"] = None) -> str | None:
"""Get launch URL if set, otherwise attempt to get launch URL based on provider."""
from authentik.core.api.users import UserSerializer
url = None
if self.meta_launch_url:
url = self.meta_launch_url
elif provider := self.get_provider():
url = provider.launch_url
if user and url:
if isinstance(user, SimpleLazyObject):
user._setup()
user = user._wrapped
try:
return url % user.__dict__
return url % UserSerializer(instance=user).data
except Exception as exc: # noqa
LOGGER.warning("Failed to format launch url", exc=exc)
return url

View File

@@ -1,11 +1,13 @@
{% load i18n %}
{% get_current_language as LANGUAGE_CODE %}
<script>
<script data-id="authentik-config">
"use strict";
window.authentik = {
locale: "{{ LANGUAGE_CODE }}",
config: JSON.parse('{{ config_json|escapejs }}'),
brand: JSON.parse('{{ brand_json|escapejs }}'),
config: JSON.parse('{{ config_json|escapejs }}' || "{}"),
brand: JSON.parse('{{ brand_json|escapejs }}' || "{}"),
versionFamily: "{{ version_family }}",
versionSubdomain: "{{ version_subdomain }}",
build: "{{ build }}",

View File

@@ -3,8 +3,10 @@
{% load authentik_core %}
<!DOCTYPE html>
<html>
<html
data-theme="{% if ui_theme == "dark" %}dark{% else %}light{% endif %}"
data-theme-choice="{% if ui_theme == "dark" %}dark{% elif ui_theme == "light" %}light{% else %}auto{% endif %}"
>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
@@ -18,9 +20,7 @@
{% include "base/theme.html" %}
<link rel="stylesheet" type="text/css" href="{% static 'dist/authentik.css' %}">
<style>{{ brand_css }}</style>
<style data-id="brand-css">{{ brand_css }}</style>
<script src="{% versioned_script 'dist/poly-%v.js' %}" type="module"></script>
<script src="{% versioned_script 'dist/standalone/loading/index-%v.js' %}" type="module"></script>
{% block head %}

View File

@@ -1,10 +1,45 @@
{% load static %}
{% load authentik_core %}
<link rel="stylesheet" type="text/css" href="{% versioned_script 'dist/styles/interface-%v.css' %}" />
{% if ui_theme == "dark" %}
<meta name="color-scheme" content="dark" />
<meta name="theme-color" content="#18191a">
{% elif ui_theme == "light" %}
{% elif ui_theme == "light" %}
<meta name="color-scheme" content="light" />
<meta name="theme-color" content="#ffffff">
{% else %}
<script data-id="theme-script">
"use strict";
(function () {
try {
const initialThemeChoice =
new URLSearchParams(window.location.search).get("theme") ||
window.localStorage?.getItem("theme");
const themeChoice =
initialThemeChoice || document.documentElement.dataset.themeChoice || "auto";
document.documentElement.dataset.themeChoice = themeChoice;
if (themeChoice === "auto") {
document.documentElement.dataset.theme = window.matchMedia(
"(prefers-color-scheme: dark)",
).matches
? "dark"
: "light";
} else {
document.documentElement.dataset.theme = themeChoice;
}
} catch (e) {
console.error("Failed to apply theme", e);
}
})();
</script>
<meta name="color-scheme" content="light dark" />
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#18191a" media="(prefers-color-scheme: dark)">

View File

@@ -12,10 +12,13 @@
{% endblock %}
{% block card %}
<form method="POST" class="pf-c-form">
<div class="pf-c-form">
<p>{% trans message %}</p>
<a id="ak-back-home" href="{% url 'authentik_core:root-redirect' %}" class="pf-c-button pf-m-primary">
{% trans 'Go home' %}
</a>
</form>
<div class="pf-c-form__group">
<a id="ak-back-home" href="{% url 'authentik_core:root-redirect' %}" class="pf-c-button pf-m-primary pf-m-block">
{% trans 'Go home' %}
</a>
</div>
</div>
{% endblock %}

View File

@@ -2,82 +2,67 @@
{% load static %}
{% load i18n %}
{% load authentik_core %}
{% block head_before %}
<link rel="prefetch" href="{{ request.brand.branding_default_flow_background_url }}" />
<link rel="stylesheet" type="text/css" href="{% static 'dist/patternfly.min.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'dist/theme-dark.css' %}" media="(prefers-color-scheme: dark)">
{% include "base/header_js.html" %}
{% endblock %}
{% block head %}
<style>
:root {
--ak-flow-background: url("{{ request.brand.branding_default_flow_background_url }}");
--pf-c-background-image--BackgroundImage: var(--ak-flow-background);
--pf-c-background-image--BackgroundImage-2x: var(--ak-flow-background);
--pf-c-background-image--BackgroundImage--sm: var(--ak-flow-background);
--pf-c-background-image--BackgroundImage--sm-2x: var(--ak-flow-background);
--pf-c-background-image--BackgroundImage--lg: var(--ak-flow-background);
}
/* Form with user */
.form-control-static {
margin-top: var(--pf-global--spacer--sm);
display: flex;
align-items: center;
justify-content: space-between;
}
.form-control-static .avatar {
display: flex;
align-items: center;
}
.form-control-static img {
margin-right: var(--pf-global--spacer--xs);
}
.form-control-static a {
padding-top: var(--pf-global--spacer--xs);
padding-bottom: var(--pf-global--spacer--xs);
line-height: var(--pf-global--spacer--xl);
}
<style data-id="static-styles">
:root {
--ak-global--background-color: var(--pf-global--BackgroundColor--150);
--ak-global--background-image: url("{{ request.brand.branding_default_flow_background_url }}");
}
</style>
<link rel="stylesheet" type="text/css" href="{% versioned_script 'dist/styles/static-%v.css' %}" />
{% endblock %}
{% block body %}
<div class="pf-c-background-image">
</div>
<ak-skip-to-content></ak-skip-to-content>
<ak-message-container></ak-message-container>
<div class="pf-c-login stacked">
<div class="ak-login-container">
<main class="pf-c-login__main">
<div class="pf-c-login__main-header pf-c-brand ak-brand">
<img src="{{ brand.branding_logo_url }}" alt="authentik Logo" />
<div class="pf-c-page__drawer">
<div class="pf-c-drawer pf-m-collapsed">
<div class="pf-c-drawer__main">
<div class="pf-c-drawer__content">
<div class="pf-c-drawer__body">
<div class="pf-c-login" data-layout="stacked">
<main class="pf-c-login__main" aria-label="Authentication form">
<div class="pf-c-login__main-header pf-c-brand">
<img class="branding-logo" src="{{ brand.branding_logo_url }}" alt="authentik Logo" />
</div>
<div class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl" data-test-id="card-title">
{% block card_title %}
{% endblock %}
</h1>
</div>
<div class="pf-c-login__main-body">
{% block card %}
{% endblock %}
</div>
</main>
<footer aria-label="Site footer" class="pf-c-login__footer pf-m-dark">
<ul class="pf-c-list pf-m-inline">
{% for link in footer_links %}
<li>
<a href="{{ link.href }}">{{ link.name }}</a>
</li>
{% endfor %}
<li>
<span>
{% trans 'Powered by authentik' %}
</span>
</li>
</ul>
</footer>
</div>
</div>
</div>
<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">
{% block card_title %}
{% endblock %}
</h1>
</header>
<div class="pf-c-login__main-body">
{% block card %}
{% endblock %}
</div>
</main>
<footer class="pf-c-login__footer">
<ul class="pf-c-list pf-m-inline">
{% for link in footer_links %}
<li>
<a href="{{ link.href }}">{{ link.name }}</a>
</li>
{% endfor %}
<li>
<span>
{% trans 'Powered by authentik' %}
</span>
</li>
</ul>
</footer>
</div>
</div>
</div>
{% endblock %}

View File

@@ -2,6 +2,8 @@
from datetime import UTC, datetime
from dramatiq.broker import get_broker
from authentik.blueprints.apps import ManagedAppConfig
from authentik.lib.generators import generate_id
from authentik.lib.utils.time import fqdn_rand
@@ -70,6 +72,12 @@ class AuthentikCryptoConfig(ManagedAppConfig):
},
)
@ManagedAppConfig.reconcile_global
def tasks_middlewares(self):
from authentik.crypto.tasks import CertificateWatcherMiddleware
get_broker().add_middleware(CertificateWatcherMiddleware())
@property
def tenant_schedule_specs(self) -> list[ScheduleSpec]:
from authentik.crypto.tasks import certificate_discovery

View File

@@ -2,17 +2,29 @@
from glob import glob
from pathlib import Path
from sys import platform
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.x509.base import load_pem_x509_certificate
from django.conf import settings
from django.utils.translation import gettext_lazy as _
from dramatiq.actor import actor
from dramatiq.middleware import Middleware
from structlog.stdlib import get_logger
from watchdog.events import (
FileCreatedEvent,
FileModifiedEvent,
FileSystemEvent,
FileSystemEventHandler,
)
from watchdog.observers import Observer
from authentik.crypto.models import CertificateKeyPair
from authentik.lib.config import CONFIG
from authentik.tasks.middleware import CurrentTask
from authentik.tasks.schedules.models import Schedule
from authentik.tenants.models import Tenant
LOGGER = get_logger()
@@ -35,6 +47,65 @@ def ensure_certificate_valid(body: str):
return body
class CertificateWatcherMiddleware(Middleware):
"""Middleware to start certificate file watcher"""
def start_certificate_watcher(self):
"""Start certificate file watcher"""
observer = Observer()
kwargs = {}
if platform.startswith("linux"):
kwargs["event_filter"] = (FileCreatedEvent, FileModifiedEvent)
observer.schedule(
CertificateEventHandler(),
CONFIG.get("cert_discovery_dir"),
recursive=True,
**kwargs,
)
observer.start()
def after_worker_boot(self, broker, worker):
if not settings.TEST:
self.start_certificate_watcher()
class CertificateEventHandler(FileSystemEventHandler):
"""Event handler for certificate file events"""
# We only ever get creation and modification events.
# See the creation of the Observer instance above for the event filtering.
# Even though we filter to only get file events, we might still get
# directory events as some implementations such as inotify do not support
# filtering on file/directory.
def dispatch(self, event: FileSystemEvent) -> None:
"""Call specific event handler method. Ignores directory changes."""
if event.is_directory:
return None
return super().dispatch(event)
def on_created(self, event: FileSystemEvent):
"""Process certificate file creation"""
LOGGER.debug(
"Certificate file created, triggering discovery",
file=event.src_path,
)
for tenant in Tenant.objects.filter(ready=True):
with tenant:
Schedule.dispatch_by_actor(certificate_discovery)
def on_modified(self, event: FileSystemEvent):
"""Process certificate file modification"""
LOGGER.debug(
"Certificate file modified, triggering discovery",
file=event.src_path,
)
for tenant in Tenant.objects.filter(ready=True):
with tenant:
Schedule.dispatch_by_actor(certificate_discovery)
@actor(description=_("Discover, import and update certificates from the filesystem."))
def certificate_discovery():
self = CurrentTask.get_task()
@@ -66,12 +137,35 @@ def certificate_discovery():
except (OSError, ValueError) as exc:
LOGGER.warning("Failed to open file or invalid format", exc=exc, file=path)
for name, cert_data in certs.items():
# First, try to find by filename-based managed field
cert = CertificateKeyPair.objects.filter(managed=MANAGED_DISCOVERED % name).first()
# If not found by filename and we have a private key, check for existing key match
if not cert and name in private_keys:
existing_with_key = (
CertificateKeyPair.objects.filter(
managed__startswith="goauthentik.io/crypto/discovered/",
key_data=private_keys[name],
)
.exclude(key_data="")
.first()
)
if existing_with_key:
cert = existing_with_key
# Update name and managed field to reflect the new filename
if cert.name != name:
cert.name = name
cert.managed = MANAGED_DISCOVERED % name
cert.save()
# Create new certificate if not found
if not cert:
cert = CertificateKeyPair(
name=name,
managed=MANAGED_DISCOVERED % name,
)
# Update certificate data if changed
dirty = False
if cert.certificate_data != cert_data:
cert.certificate_data = cert_data

View File

@@ -2,6 +2,7 @@
from json import loads
from os import makedirs
from pathlib import Path
from tempfile import TemporaryDirectory
from cryptography.x509.extensions import SubjectAlternativeName
@@ -319,6 +320,8 @@ class TestCrypto(APITestCase):
def test_discovery(self):
"""Test certificate discovery"""
# This test generates 2 separate cert/key combinations
# and verifies they both import properly
name = generate_id()
builder = CertificateBuilder(name)
with self.assertRaises(ValueError):
@@ -327,6 +330,15 @@ class TestCrypto(APITestCase):
subject_alt_names=[],
validity_days=3,
)
name2 = generate_id()
builder2 = CertificateBuilder(name2)
with self.assertRaises(ValueError):
builder2.save()
builder2.build(
subject_alt_names=[],
validity_days=3,
)
with TemporaryDirectory() as temp_dir:
with open(f"{temp_dir}/foo.pem", "w+", encoding="utf-8") as _cert:
_cert.write(builder.certificate)
@@ -334,9 +346,9 @@ class TestCrypto(APITestCase):
_key.write(builder.private_key)
makedirs(f"{temp_dir}/foo.bar", exist_ok=True)
with open(f"{temp_dir}/foo.bar/fullchain.pem", "w+", encoding="utf-8") as _cert:
_cert.write(builder.certificate)
_cert.write(builder2.certificate)
with open(f"{temp_dir}/foo.bar/privkey.pem", "w+", encoding="utf-8") as _key:
_key.write(builder.private_key)
_key.write(builder2.private_key)
with CONFIG.patch("cert_discovery_dir", temp_dir):
certificate_discovery.send()
keypair: CertificateKeyPair = CertificateKeyPair.objects.filter(
@@ -348,3 +360,58 @@ class TestCrypto(APITestCase):
self.assertTrue(
CertificateKeyPair.objects.filter(managed=MANAGED_DISCOVERED % "foo.bar").exists()
)
def test_discovery_updating_same_private_key(self):
"""Test certificate discovery updating certs with matching private keys"""
name = generate_id()
builder = CertificateBuilder(name)
builder.build(
subject_alt_names=[],
validity_days=3,
)
with TemporaryDirectory() as temp_dir:
# First discovery: write cert as "original"
with open(f"{temp_dir}/original.pem", "w+", encoding="utf-8") as _cert:
_cert.write(builder.certificate)
with open(f"{temp_dir}/original.key", "w+", encoding="utf-8") as _key:
_key.write(builder.private_key)
with CONFIG.patch("cert_discovery_dir", temp_dir):
certificate_discovery.send()
# Verify "original" cert was created
original = CertificateKeyPair.objects.filter(
managed=MANAGED_DISCOVERED % "original"
).first()
self.assertIsNotNone(original)
self.assertEqual(original.name, "original")
self.assertIsNotNone(original.private_key)
# Second discovery: write same cert/key as "renamed"
Path(f"{temp_dir}/original.pem").unlink()
Path(f"{temp_dir}/original.key").unlink()
with open(f"{temp_dir}/renamed.pem", "w+", encoding="utf-8") as _cert:
_cert.write(builder.certificate)
with open(f"{temp_dir}/renamed.key", "w+", encoding="utf-8") as _key:
_key.write(builder.private_key)
with CONFIG.patch("cert_discovery_dir", temp_dir):
certificate_discovery.send()
# Verify the cert was updated
renamed = CertificateKeyPair.objects.filter(
managed=MANAGED_DISCOVERED % "renamed"
).first()
self.assertIsNotNone(renamed, "Renamed certificate should exist")
self.assertEqual(renamed.name, "renamed")
self.assertEqual(renamed.pk, original.pk, "Should be same database object")
# Verify no new cert was created
final_count = CertificateKeyPair.objects.filter(
managed__startswith="goauthentik.io/crypto/discovered/"
).count()
self.assertEqual(
1, final_count, "Should not create duplicate cert for same private key"
)

View File

@@ -16,7 +16,7 @@ from authentik.stages.authenticator.models import Device
class AuthenticatorEndpointGDTCStage(ConfigurableStage, FriendlyNamedStage, Stage):
"""Setup Google Chrome Device Trust connection"""
"""Verify Google Chrome Device Trust connection for the user's browser."""
credentials = models.JSONField()

View File

@@ -10,7 +10,7 @@ from authentik.lib.utils.time import timedelta_string_validator
class SourceStage(Stage):
"""Suspend the current flow execution and send the user to a source,
"""Suspend the current flow execution and send the user to a federated source,
after which this flow execution is resumed."""
source = models.ForeignKey("authentik_core.Source", on_delete=models.CASCADE)

View File

@@ -1,7 +1,7 @@
from collections.abc import Generator
from contextlib import contextmanager
from dataclasses import dataclass, field
from datetime import datetime
from datetime import UTC, datetime
from typing import Any
from django.utils.timezone import now
@@ -28,7 +28,7 @@ class LogEvent:
def from_event_dict(item: EventDict) -> "LogEvent":
event = item.pop("event")
log_level = item.pop("level").lower()
timestamp = datetime.fromisoformat(item.pop("timestamp"))
timestamp = datetime.fromisoformat(item.pop("timestamp")).replace(tzinfo=UTC)
item.pop("pid", None)
# Sometimes log entries have both `level` and `log_level` set, but `level` is always set
item.pop("log_level", None)

View File

@@ -17,7 +17,7 @@
<meta name="sentry-trace" content="{{ sentry_trace }}" />
<link rel="prefetch" href="{{ flow_background_url }}" />
{% include "base/header_js.html" %}
<style>
<style data-id="flow-sfe">
html,
body {
height: 100%;

View File

@@ -7,22 +7,26 @@
{{ block.super }}
<link rel="prefetch" href="{{ flow_background_url }}" />
{% if flow.compatibility_mode and not inspector %}
<script>ShadyDOM = { force: true };</script>
<link rel="stylesheet" type="text/css" href="{% versioned_script 'dist/styles/static-%v.css' %}" />
<script data-id="shady-dom">ShadyDOM = { force: true };</script>
{% endif %}
{% include "base/header_js.html" %}
<script>
window.authentik.flow = {
"layout": "{{ flow.layout }}",
};
<script data-id="flow-config">
"use strict";
window.authentik.flow = {
"layout": "{{ flow.layout }}",
};
</script>
{% endblock %}
{% block head %}
<script src="{% versioned_script 'dist/flow/FlowInterface-%v.js' %}" type="module"></script>
<style>
:root {
--ak-flow-background: url("{{ flow_background_url }}");
}
<style data-id="flow-css">
:root {
--ak-global--background-color: var(--pf-global--BackgroundColor--150);
--ak-global--background-image: url("{{ flow_background_url }}");
}
</style>
{% endblock %}

View File

@@ -145,7 +145,6 @@ worker:
consumer_listen_timeout: "seconds=30"
task_max_retries: 5
task_default_time_limit: "minutes=10"
lock_purge_interval: "minutes=1"
task_purge_interval: "days=1"
task_expiration: "days=30"
scheduler_interval: "seconds=60"

View File

@@ -12,8 +12,7 @@
{% endblock %}
{% block card %}
<form class="pf-c-form">
{% csrf_token %}
<div class="pf-c-form">
{% if user.is_authenticated %}
<div class="pf-c-form__group">
<div class="form-control-static">
@@ -29,7 +28,7 @@
{% endif %}
<div class="pf-c-form__group">
<p>
<i class="pf-icon pf-icon-error-circle-o"></i>
<i class="pf-icon pf-icon-error-circle-o pf-u-font-size-2xl" role="img" aria-label="{% trans 'Error' %}"></i>
{% trans 'Request has been denied.' %}
</p>
{% if error %}
@@ -71,5 +70,11 @@
{% endif %}
{% endif %}
</div>
</form>
<div class="pf-c-form__group">
<a id="ak-back-home" href="{% url 'authentik_core:root-redirect' %}" class="pf-c-button pf-m-primary pf-m-block">
{% trans 'Go home' %}
</a>
</div>
</div>
{% endblock %}

View File

@@ -83,7 +83,7 @@ class EnterpriseUser(BaseModel):
class User(BaseUser):
"""Modified User schema with added externalId field"""
model_config = ConfigDict(serialize_by_alias=True)
model_config = ConfigDict(serialize_by_alias=True, extra="allow")
id: str | int | None = None
schemas: list[str] = [SCIM_USER_SCHEMA]
@@ -106,6 +106,8 @@ class User(BaseUser):
class Group(BaseGroup):
"""Modified Group schema with added externalId field"""
model_config = ConfigDict(extra="allow")
id: str | int | None = None
schemas: list[str] = [SCIM_GROUP_SCHEMA]
externalId: str | None = None

View File

@@ -95,7 +95,12 @@ class SCIMUserTests(TestCase):
"""Test user creation with custom schema"""
schema = SCIMMapping.objects.create(
name="custom_schema",
expression="""return {"schemas": ["foo"]}""",
expression="""return {
"schemas": ["urn:ietf:params:scim:schemas:extension:slack:profile:2.0:User"],
"urn:ietf:params:scim:schemas:extension:slack:profile:2.0:User": {
"startDate": "2024-04-10T00:00:00+0000",
},
}""",
)
self.provider.property_mappings.add(schema)
scim_id = generate_id()
@@ -121,7 +126,10 @@ class SCIMUserTests(TestCase):
self.assertJSONEqual(
mock.request_history[1].body,
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User", "foo"],
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:User",
"urn:ietf:params:scim:schemas:extension:slack:profile:2.0:User",
],
"active": True,
"emails": [
{
@@ -138,6 +146,9 @@ class SCIMUserTests(TestCase):
},
"displayName": f"{uid} {uid}",
"userName": uid,
"urn:ietf:params:scim:schemas:extension:slack:profile:2.0:User": {
"startDate": "2024-04-10T00:00:00+0000",
},
},
)

View File

@@ -380,9 +380,6 @@ DRAMATIQ = {
"broker_class": "authentik.tasks.broker.Broker",
"channel_prefix": "authentik",
"task_model": "authentik.tasks.models.Task",
"lock_purge_interval": timedelta_from_string(
CONFIG.get("worker.lock_purge_interval")
).total_seconds(),
"task_purge_interval": timedelta_from_string(
CONFIG.get("worker.task_purge_interval")
).total_seconds(),

View File

@@ -16,7 +16,7 @@ from authentik.stages.authenticator.models import Device
class AuthenticatorDuoStage(ConfigurableStage, FriendlyNamedStage, Stage):
"""Setup Duo authenticator devices"""
"""Setup Duo authentication for the user."""
api_hostname = models.TextField()

View File

@@ -20,7 +20,7 @@ from authentik.stages.email.utils import TemplateEmailMessage
class AuthenticatorEmailStage(ConfigurableStage, FriendlyNamedStage, Stage):
"""Use Email-based authentication instead of authenticator-based."""
"""Setup Email-based authentication for the user."""
use_global_settings = models.BooleanField(
default=False,

View File

@@ -16,7 +16,7 @@ from authentik.stages.authenticator.models import Device, ThrottlingMixin
class AuthenticatorStaticStage(ConfigurableStage, FriendlyNamedStage, Stage):
"""Generate static tokens for the user as a backup."""
"""Setup static token based authentication for the user."""
token_count = models.PositiveIntegerField(default=6)
token_length = models.PositiveIntegerField(default=12)

View File

@@ -27,7 +27,7 @@ class TOTPDigits(models.TextChoices):
class AuthenticatorTOTPStage(ConfigurableStage, FriendlyNamedStage, Stage):
"""Enroll a user's device into Time-based OTP."""
"""Setup Time-based OTP authentication for the user."""
digits = models.IntegerField(choices=TOTPDigits.choices)

View File

@@ -36,7 +36,7 @@ def default_device_classes() -> list:
class AuthenticatorValidateStage(Stage):
"""Validate user's configured OTP Device."""
"""Validate user's configured Multi Factor Authentication."""
not_configured_action = models.TextField(
choices=NotConfiguredAction.choices, default=NotConfiguredAction.SKIP

View File

@@ -68,7 +68,7 @@ class AuthenticatorAttachment(models.TextChoices):
class AuthenticatorWebAuthnStage(ConfigurableStage, FriendlyNamedStage, Stage):
"""Stage to enroll WebAuthn-based authenticators."""
"""Setup WebAuthn-based authentication for the user."""
user_verification = models.TextField(
choices=UserVerification.choices,

View File

@@ -62,7 +62,7 @@ def get_template_choices():
class EmailStage(Stage):
"""Sends an Email to the user with a token to confirm their Email address."""
"""Send an Email to the user with a token to confirm their Email address."""
use_global_settings = models.BooleanField(
default=False,

View File

@@ -21,7 +21,7 @@ class UserFields(models.TextChoices):
class IdentificationStage(Stage):
"""Allows the user to identify themselves for authentication."""
"""Identify the user for authentication."""
user_fields = ArrayField(
models.CharField(max_length=100, choices=UserFields.choices),

View File

@@ -39,7 +39,7 @@ def get_authentication_backends():
class PasswordStage(ConfigurableStage, Stage):
"""Prompts the user for their password, and validates it against the configured backends."""
"""Prompt the user for their password, and validate it against the configured backends."""
backends = ArrayField(
models.TextField(choices=get_authentication_backends()),

View File

@@ -329,7 +329,7 @@ class Prompt(SerializerModel):
class PromptStage(Stage):
"""Define arbitrary prompts for the user."""
"""Prompt the user to enter information."""
fields = models.ManyToManyField(Prompt)

View File

@@ -16,7 +16,7 @@ class RedirectMode(models.TextChoices):
class RedirectStage(Stage):
"""Redirect the user to another flow, potentially with all gathered context"""
"""Redirect the user to another flow, potentially with all gathered context."""
keep_context = models.BooleanField(default=True)
mode = models.TextField(choices=RedirectMode.choices)

View File

@@ -8,7 +8,7 @@ from authentik.flows.models import Stage
class UserDeleteStage(Stage):
"""Deletes the currently pending user without confirmation.
"""Delete the pending user without confirmation.
Use with caution."""
@property

View File

@@ -30,7 +30,7 @@ class GeoIPBinding(models.TextChoices):
class UserLoginStage(Stage):
"""Attaches the currently pending user to the current session."""
"""Attach the pending user to the current session."""
session_duration = models.TextField(
default="seconds=0",

View File

@@ -8,7 +8,7 @@ from authentik.flows.models import Stage
class UserLogoutStage(Stage):
"""Resets the users current session."""
"""Ends the user's session."""
@property
def serializer(self) -> type[BaseSerializer]:

View File

@@ -18,8 +18,8 @@ class UserCreationMode(models.TextChoices):
class UserWriteStage(Stage):
"""Writes currently pending data into the pending user, or if no user exists,
creates a new user with the data."""
"""Write pending data into the pending user, or if no user exists,
create a new user with the data."""
user_creation_mode = models.TextField(
choices=UserCreationMode.choices,

View File

@@ -60,22 +60,6 @@ func checkServer() int {
return 0
}
func splitHostPort(address string) (host, port string) {
lastColon := strings.LastIndex(address, ":")
if lastColon == -1 {
return address, ""
}
host = address[:lastColon]
port = address[lastColon+1:]
if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") {
host = host[1 : len(host)-1]
}
return host, port
}
func checkWorker() int {
pidB, err := os.ReadFile(workerPidFile)
if err != nil {
@@ -98,41 +82,6 @@ func checkWorker() int {
log.WithError(err).Warning("failed to signal worker process")
return 1
}
h := &http.Client{
Transport: web.NewUserAgentTransport("goauthentik.io/healthcheck", http.DefaultTransport),
}
host, port := splitHostPort(config.Get().Listen.HTTP)
if host == "0.0.0.0" || host == "::" {
url := fmt.Sprintf("http://%s:%s/-/health/ready/", "::1", port)
_, err := h.Head(url)
if err != nil {
log.WithError(err).WithField("url", url).Warning("failed to send healthcheck request")
url := fmt.Sprintf("http://%s:%s/-/health/ready/", "127.0.0.1", port)
res, err := h.Head(url)
if err != nil {
log.WithError(err).WithField("url", url).Warning("failed to send healthcheck request")
return 1
}
if res.StatusCode >= 400 {
log.WithField("status", res.StatusCode).Warning("unhealthy status code")
return 1
}
}
} else {
url := fmt.Sprintf("http://%s:%s/-/health/ready/", host, port)
res, err := h.Head(url)
if err != nil {
log.WithError(err).Warning("failed to send healthcheck request")
return 1
}
if res.StatusCode >= 400 {
log.WithField("status", res.StatusCode).Warning("unhealthy status code")
return 1
}
}
log.Info("successfully checked health")
return 0
}

37
go.mod
View File

@@ -9,10 +9,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.16.0
github.com/getsentry/sentry-go v0.36.1
github.com/getsentry/sentry-go v0.37.0
github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1
github.com/go-ldap/ldap/v3 v3.4.12
github.com/go-openapi/runtime v0.29.0
github.com/go-openapi/runtime v0.29.2
github.com/golang-jwt/jwt/v5 v5.3.0
github.com/google/uuid v1.6.0
github.com/gorilla/handlers v1.5.2
@@ -32,19 +32,18 @@ require (
github.com/spf13/cobra v1.10.1
github.com/stretchr/testify v1.11.1
github.com/wwt/guac v1.3.2
goauthentik.io/api/v3 v3.2025120.2
goauthentik.io/api/v3 v3.2025120.3
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
golang.org/x/oauth2 v0.32.0
golang.org/x/sync v0.17.0
golang.org/x/oauth2 v0.33.0
golang.org/x/sync v0.18.0
gopkg.in/yaml.v2 v2.4.0
gorm.io/driver/postgres v1.6.0
gorm.io/gorm v1.31.0
gorm.io/gorm v1.31.1
layeh.com/radius v0.0.0-20231213012653-1006025d24f8
)
require (
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
@@ -55,13 +54,13 @@ require (
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/analysis v0.24.0 // indirect
github.com/go-openapi/errors v0.22.3 // indirect
github.com/go-openapi/analysis v0.24.1 // indirect
github.com/go-openapi/errors v0.22.4 // indirect
github.com/go-openapi/jsonpointer v0.22.1 // indirect
github.com/go-openapi/jsonreference v0.21.2 // indirect
github.com/go-openapi/loads v0.23.1 // indirect
github.com/go-openapi/spec v0.22.0 // indirect
github.com/go-openapi/strfmt v0.24.0 // indirect
github.com/go-openapi/jsonreference v0.21.3 // indirect
github.com/go-openapi/loads v0.23.2 // indirect
github.com/go-openapi/spec v0.22.1 // indirect
github.com/go-openapi/strfmt v0.25.0 // indirect
github.com/go-openapi/swag/conv v0.25.1 // indirect
github.com/go-openapi/swag/fileutils v0.25.1 // indirect
github.com/go-openapi/swag/jsonname v0.25.1 // indirect
@@ -71,7 +70,7 @@ require (
github.com/go-openapi/swag/stringutils v0.25.1 // indirect
github.com/go-openapi/swag/typeutils v0.25.1 // indirect
github.com/go-openapi/swag/yamlutils v0.25.1 // indirect
github.com/go-openapi/validate v0.25.0 // indirect
github.com/go-openapi/validate v0.25.1 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/grafana/pyroscope-go/godeltaprof v0.1.9 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
@@ -88,17 +87,17 @@ require (
github.com/prometheus/common v0.66.1 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
github.com/spf13/pflag v1.0.9 // indirect
go.mongodb.org/mongo-driver v1.17.4 // indirect
go.mongodb.org/mongo-driver v1.17.6 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel v1.38.0 // indirect
go.opentelemetry.io/otel/metric v1.38.0 // indirect
go.opentelemetry.io/otel/trace v1.38.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.42.0 // indirect
golang.org/x/net v0.44.0 // indirect
golang.org/x/sys v0.36.0 // indirect
golang.org/x/text v0.29.0 // indirect
golang.org/x/crypto v0.43.0 // indirect
golang.org/x/net v0.46.0 // indirect
golang.org/x/sys v0.37.0 // indirect
golang.org/x/text v0.30.0 // indirect
google.golang.org/protobuf v1.36.8 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

78
go.sum
View File

@@ -6,8 +6,6 @@ github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e h1:4dAU9FXIyQktpoUAgOJK3OTFc/xug0PCXYCqU0FgDKI=
github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/avast/retry-go/v4 v4.7.0 h1:yjDs35SlGvKwRNSykujfjdMxMhMQQM0TnIjJaHB+Zio=
github.com/avast/retry-go/v4 v4.7.0/go.mod h1:ZMPDa3sY2bKgpLtap9JRUgk2yTAba7cgiFhqxY2Sg6Q=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@@ -22,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.36.1 h1:kMJt0WWsxWATUxkvFgVBZdIeHSk/Oiv5P0jZ9e5m/Lw=
github.com/getsentry/sentry-go v0.36.1/go.mod h1:p5Im24mJBeruET8Q4bbcMfCQ+F+Iadc4L48tB1apo2c=
github.com/getsentry/sentry-go v0.37.0 h1:5bavywHxVkU/9aOIF4fn3s5RTJX5Hdw6K2W6jLYtM98=
github.com/getsentry/sentry-go v0.37.0/go.mod h1:eRXCoh3uvmjQLY6qu63BjUZnaBu5L5WhMV1RwYO8W5s=
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=
@@ -43,22 +41,22 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-openapi/analysis v0.24.0 h1:vE/VFFkICKyYuTWYnplQ+aVr45vlG6NcZKC7BdIXhsA=
github.com/go-openapi/analysis v0.24.0/go.mod h1:GLyoJA+bvmGGaHgpfeDh8ldpGo69fAJg7eeMDMRCIrw=
github.com/go-openapi/errors v0.22.3 h1:k6Hxa5Jg1TUyZnOwV2Lh81j8ayNw5VVYLvKrp4zFKFs=
github.com/go-openapi/errors v0.22.3/go.mod h1:+WvbaBBULWCOna//9B9TbLNGSFOfF8lY9dw4hGiEiKQ=
github.com/go-openapi/analysis v0.24.1 h1:Xp+7Yn/KOnVWYG8d+hPksOYnCYImE3TieBa7rBOesYM=
github.com/go-openapi/analysis v0.24.1/go.mod h1:dU+qxX7QGU1rl7IYhBC8bIfmWQdX4Buoea4TGtxXY84=
github.com/go-openapi/errors v0.22.4 h1:oi2K9mHTOb5DPW2Zjdzs/NIvwi2N3fARKaTJLdNabaM=
github.com/go-openapi/errors v0.22.4/go.mod h1:z9S8ASTUqx7+CP1Q8dD8ewGH/1JWFFLX/2PmAYNQLgk=
github.com/go-openapi/jsonpointer v0.22.1 h1:sHYI1He3b9NqJ4wXLoJDKmUmHkWy/L7rtEo92JUxBNk=
github.com/go-openapi/jsonpointer v0.22.1/go.mod h1:pQT9OsLkfz1yWoMgYFy4x3U5GY5nUlsOn1qSBH5MkCM=
github.com/go-openapi/jsonreference v0.21.2 h1:Wxjda4M/BBQllegefXrY/9aq1fxBA8sI5M/lFU6tSWU=
github.com/go-openapi/jsonreference v0.21.2/go.mod h1:pp3PEjIsJ9CZDGCNOyXIQxsNuroxm8FAJ/+quA0yKzQ=
github.com/go-openapi/loads v0.23.1 h1:H8A0dX2KDHxDzc797h0+uiCZ5kwE2+VojaQVaTlXvS0=
github.com/go-openapi/loads v0.23.1/go.mod h1:hZSXkyACCWzWPQqizAv/Ye0yhi2zzHwMmoXQ6YQml44=
github.com/go-openapi/runtime v0.29.0 h1:Y7iDTFarS9XaFQ+fA+lBLngMwH6nYfqig1G+pHxMRO0=
github.com/go-openapi/runtime v0.29.0/go.mod h1:52HOkEmLL/fE4Pg3Kf9nxc9fYQn0UsIWyGjGIJE9dkg=
github.com/go-openapi/spec v0.22.0 h1:xT/EsX4frL3U09QviRIZXvkh80yibxQmtoEvyqug0Tw=
github.com/go-openapi/spec v0.22.0/go.mod h1:K0FhKxkez8YNS94XzF8YKEMULbFrRw4m15i2YUht4L0=
github.com/go-openapi/strfmt v0.24.0 h1:dDsopqbI3wrrlIzeXRbqMihRNnjzGC+ez4NQaAAJLuc=
github.com/go-openapi/strfmt v0.24.0/go.mod h1:Lnn1Bk9rZjXxU9VMADbEEOo7D7CDyKGLsSKekhFr7s4=
github.com/go-openapi/jsonreference v0.21.3 h1:96Dn+MRPa0nYAR8DR1E03SblB5FJvh7W6krPI0Z7qMc=
github.com/go-openapi/jsonreference v0.21.3/go.mod h1:RqkUP0MrLf37HqxZxrIAtTWW4ZJIK1VzduhXYBEeGc4=
github.com/go-openapi/loads v0.23.2 h1:rJXAcP7g1+lWyBHC7iTY+WAF0rprtM+pm8Jxv1uQJp4=
github.com/go-openapi/loads v0.23.2/go.mod h1:IEVw1GfRt/P2Pplkelxzj9BYFajiWOtY2nHZNj4UnWY=
github.com/go-openapi/runtime v0.29.2 h1:UmwSGWNmWQqKm1c2MGgXVpC2FTGwPDQeUsBMufc5Yj0=
github.com/go-openapi/runtime v0.29.2/go.mod h1:biq5kJXRJKBJxTDJXAa00DOTa/anflQPhT0/wmjuy+0=
github.com/go-openapi/spec v0.22.1 h1:beZMa5AVQzRspNjvhe5aG1/XyBSMeX1eEOs7dMoXh/k=
github.com/go-openapi/spec v0.22.1/go.mod h1:c7aeIQT175dVowfp7FeCvXXnjN/MrpaONStibD2WtDA=
github.com/go-openapi/strfmt v0.25.0 h1:7R0RX7mbKLa9EYCTHRcCuIPcaqlyQiWNPTXwClK0saQ=
github.com/go-openapi/strfmt v0.25.0/go.mod h1:nNXct7OzbwrMY9+5tLX4I21pzcmE6ccMGXl3jFdPfn8=
github.com/go-openapi/swag/conv v0.25.1 h1:+9o8YUg6QuqqBM5X6rYL/p1dpWeZRhoIt9x7CCP+he0=
github.com/go-openapi/swag/conv v0.25.1/go.mod h1:Z1mFEGPfyIKPu0806khI3zF+/EUXde+fdeksUl2NiDs=
github.com/go-openapi/swag/fileutils v0.25.1 h1:rSRXapjQequt7kqalKXdcpIegIShhTPXx7yw0kek2uU=
@@ -79,8 +77,12 @@ github.com/go-openapi/swag/typeutils v0.25.1 h1:rD/9HsEQieewNt6/k+JBwkxuAHktFtH3
github.com/go-openapi/swag/typeutils v0.25.1/go.mod h1:9McMC/oCdS4BKwk2shEB7x17P6HmMmA6dQRtAkSnNb8=
github.com/go-openapi/swag/yamlutils v0.25.1 h1:mry5ez8joJwzvMbaTGLhw8pXUnhDK91oSJLDPF1bmGk=
github.com/go-openapi/swag/yamlutils v0.25.1/go.mod h1:cm9ywbzncy3y6uPm/97ysW8+wZ09qsks+9RS8fLWKqg=
github.com/go-openapi/validate v0.25.0 h1:JD9eGX81hDTjoY3WOzh6WqxVBVl7xjsLnvDo1GL5WPU=
github.com/go-openapi/validate v0.25.0/go.mod h1:SUY7vKrN5FiwK6LyvSwKjDfLNirSfWwHNgxd2l29Mmw=
github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4=
github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg=
github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls=
github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
github.com/go-openapi/validate v0.25.1 h1:sSACUI6Jcnbo5IWqbYHgjibrhhmt3vR6lCzKZnmAgBw=
github.com/go-openapi/validate v0.25.1/go.mod h1:RMVyVFYte0gbSTaZ0N4KmTn6u/kClvAFp+mAVfS/DQc=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
@@ -194,8 +196,8 @@ github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD
github.com/wwt/guac v1.3.2 h1:sH6OFGa/1tBs7ieWBVlZe7t6F5JAOWBry/tqQL/Vup4=
github.com/wwt/guac v1.3.2/go.mod h1:eKm+NrnK7A88l4UBEcYNpZQGMpZRryYKoz4D/0/n1C0=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw=
go.mongodb.org/mongo-driver v1.17.4/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
go.mongodb.org/mongo-driver v1.17.6 h1:87JUG1wZfWsr6rIz3ZmpH90rL5tea7O3IHuSwHUpsss=
go.mongodb.org/mongo-driver v1.17.6/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
@@ -212,13 +214,13 @@ go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
goauthentik.io/api/v3 v3.2025120.2 h1:FOUJCEI+qqBXmeB7DfStwfB6SXuoLUQrubX4AXsaf/E=
goauthentik.io/api/v3 v3.2025120.2/go.mod h1:82lqAz4jxzl6Cg0YDbhNtvvTG2rm6605ZhdJFnbbsl8=
goauthentik.io/api/v3 v3.2025120.3 h1:a/azcVyRED1sCfp2sbtv8Bl9ChR6BHRsb5rQHc449xQ=
goauthentik.io/api/v3 v3.2025120.3/go.mod h1:82lqAz4jxzl6Cg0YDbhNtvvTG2rm6605ZhdJFnbbsl8=
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.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
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=
@@ -228,15 +230,15 @@ 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.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY=
golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo=
golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -247,8 +249,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.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
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=
@@ -260,8 +262,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.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
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=
@@ -279,7 +281,7 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
gorm.io/gorm v1.31.0 h1:0VlycGreVhK7RF/Bwt51Fk8v0xLiiiFdbGDPIZQ7mJY=
gorm.io/gorm v1.31.0/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
layeh.com/radius v0.0.0-20231213012653-1006025d24f8 h1:orYXpi6BJZdvgytfHH4ybOe4wHnLbbS71Cmd8mWdZjs=
layeh.com/radius v0.0.0-20231213012653-1006025d24f8/go.mod h1:QRf+8aRqXc019kHkpcs/CTgyWXFzf+bxlsyuo2nAl1o=

View File

@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1
# Stage 1: Build
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.4-trixie@sha256:a13297bfdd45d9702badd83b45c6db1f8fee638bba6ece0c359ce51260577bbe AS builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.4-trixie@sha256:27e1c927a07ed2c7295d39941d6d881424739dbde9ae3055d0d3013699ed35e8 AS builder
ARG TARGETOS
ARG TARGETARCH
@@ -31,7 +31,7 @@ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
go build -o /go/ldap ./cmd/ldap
# Stage 2: Run
FROM ghcr.io/goauthentik/fips-debian:trixie-slim-fips@sha256:9b4cedf932e97194f1825124830f2eec14254d90162dad28f97e505971543115
FROM ghcr.io/goauthentik/fips-debian:trixie-slim-fips@sha256:40a1f32b318c9d4a488580bf156c408ec2e076898d40ada9256b7c219af79e19
ARG VERSION
ARG GIT_BUILD_HASH

View File

@@ -38,14 +38,14 @@ function check_if_root {
chown -R authentik:authentik /media /certs "${PROMETHEUS_MULTIPROC_DIR}"
chmod ug+rwx /media
chmod ug+rx /certs
exec chpst -u authentik:$GROUP env HOME=/authentik PATH="$PATH" $1
exec chpst -u authentik:$GROUP env HOME=/authentik $1
}
function run_authentik {
if [[ -x "$(command -v authentik)" ]]; then
exec authentik $@
else
exec env GOFIPS140=latest CGO_ENABLED=1 go run -v ./cmd/server/ $@
exec go run -v ./cmd/server/ $@
fi
}

View File

@@ -9,7 +9,7 @@
"version": "0.0.0",
"license": "MIT",
"devDependencies": {
"aws-cdk": "^2.1031.1",
"aws-cdk": "^2.1031.2",
"cross-env": "^10.1.0"
},
"engines": {
@@ -24,9 +24,9 @@
"license": "MIT"
},
"node_modules/aws-cdk": {
"version": "2.1031.1",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1031.1.tgz",
"integrity": "sha512-y/ovZDtOKvgJFtwE7NDFKRgJB3stgUUrAu6b31icuFO8+vnZ+4BIt0McQfbVR8ky5QOj2TW1G1sk55k8Yv+BnQ==",
"version": "2.1031.2",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1031.2.tgz",
"integrity": "sha512-FI8XkslwC1Vatjdu5MXu2ww++FcZPkPt45/DJklApxMF+aGcCKOuLf+COc12QYK88GOrLBeCED6lDjNc9m/ueA==",
"dev": true,
"license": "Apache-2.0",
"bin": {

View File

@@ -10,7 +10,7 @@
"node": ">=20"
},
"devDependencies": {
"aws-cdk": "^2.1031.1",
"aws-cdk": "^2.1031.2",
"cross-env": "^10.1.0"
}
}

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-10-28 00:10+0000\n"
"POT-Creation-Date: 2025-11-13 03:19+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"
@@ -446,6 +446,7 @@ msgid "Remove temporary users created by SAML Sources."
msgstr ""
#: authentik/core/templates/if/error.html
#: authentik/policies/templates/policies/denied.html
msgid "Go home"
msgstr ""
@@ -477,6 +478,26 @@ msgstr ""
msgid "ecdsa"
msgstr ""
#: authentik/crypto/models.py
msgid "RSA"
msgstr ""
#: authentik/crypto/models.py
msgid "Elliptic Curve"
msgstr ""
#: authentik/crypto/models.py
msgid "DSA"
msgstr ""
#: authentik/crypto/models.py
msgid "Ed25519"
msgstr ""
#: authentik/crypto/models.py
msgid "Ed448"
msgstr ""
#: authentik/crypto/models.py
msgid "PEM-encoded Certificate data"
msgstr ""
@@ -1473,6 +1494,10 @@ msgstr ""
msgid "Not you?"
msgstr ""
#: authentik/policies/templates/policies/denied.html
msgid "Error"
msgstr ""
#: authentik/policies/templates/policies/denied.html
msgid "Request has been denied."
msgstr ""
@@ -1716,8 +1741,8 @@ msgstr ""
#: authentik/providers/oauth2/models.py
msgid ""
"Tokens not valid on or after current time + this value (Format: hours=1;"
"minutes=2;seconds=3)."
"Tokens not valid on or after current time + this value (Format: "
"hours=1;minutes=2;seconds=3)."
msgstr ""
#: authentik/providers/oauth2/models.py
@@ -2028,16 +2053,6 @@ msgstr ""
msgid "ACS URL"
msgstr ""
#: authentik/providers/saml/models.py
msgid ""
"Value of the audience restriction field of the assertion. When left empty, "
"no audience restriction will be added."
msgstr ""
#: authentik/providers/saml/models.py
msgid "Also known as EntityID"
msgstr ""
#: authentik/providers/saml/models.py
msgid "Service Provider Binding"
msgstr ""
@@ -2048,6 +2063,16 @@ msgid ""
"Provider."
msgstr ""
#: authentik/providers/saml/models.py
msgid ""
"Value of the audience restriction field of the assertion. When left empty, "
"no audience restriction will be added."
msgstr ""
#: authentik/providers/saml/models.py
msgid "Also known as EntityID"
msgstr ""
#: authentik/providers/saml/models.py
msgid "SLS URL"
msgstr ""
@@ -2098,20 +2123,20 @@ msgstr ""
#: authentik/providers/saml/models.py
msgid ""
"Assertion valid not before current time + this value (Format: hours=-1;"
"minutes=-2;seconds=-3)."
"Assertion valid not before current time + this value (Format: "
"hours=-1;minutes=-2;seconds=-3)."
msgstr ""
#: authentik/providers/saml/models.py
msgid ""
"Assertion not valid on or after current time + this value (Format: hours=1;"
"minutes=2;seconds=3)."
"Assertion not valid on or after current time + this value (Format: "
"hours=1;minutes=2;seconds=3)."
msgstr ""
#: authentik/providers/saml/models.py
msgid ""
"Session not valid on or after current time + this value (Format: hours=1;"
"minutes=2;seconds=3)."
"Session not valid on or after current time + this value (Format: "
"hours=1;minutes=2;seconds=3)."
msgstr ""
#: authentik/providers/saml/models.py authentik/sources/saml/models.py
@@ -3956,8 +3981,8 @@ msgstr ""
#: authentik/stages/user_login/models.py
msgid ""
"When set to a non-zero value, authentik will save a cookie with a longer "
"expiry,to remember the device the user is logging in from. (Format: hours=-1;"
"minutes=-2;seconds=-3)"
"expiry,to remember the device the user is logging in from. (Format: "
"hours=-1;minutes=-2;seconds=-3)"
msgstr ""
#: authentik/stages/user_login/models.py
@@ -4077,8 +4102,8 @@ msgstr ""
#: authentik/tenants/models.py
msgid ""
"Events will be deleted after this duration.(Format: weeks=3;days=2;hours=3,"
"seconds=2)."
"Events will be deleted after this duration.(Format: "
"weeks=3;days=2;hours=3,seconds=2)."
msgstr ""
#: authentik/tenants/models.py

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -473,7 +473,7 @@ class PostgresChannelLayerReceiver:
"""
DELETE
FROM {table}
WHERE {table}.{channel} IN (%s)
WHERE {table}.{channel} = ANY(%s)
AND {table}.{expires} >= %s
RETURNING {table}.{id}, {table}.{channel}, {table}.{message}
"""
@@ -484,7 +484,7 @@ class PostgresChannelLayerReceiver:
expires=sql.Identifier("expires"),
message=sql.Identifier("message"),
),
(tuple(self._subscribed_to), now()),
(list(self._subscribed_to), now()),
)
async for row in cursor:
message_id, channel, message = row

View File

@@ -61,6 +61,7 @@ def raise_connection_error(func: Callable[P, R]) -> Callable[P, R]:
try:
return func(*args, **kwargs)
except DATABASE_ERRORS as exc:
logger.warning("Database error encountered", exc=exc)
raise ConnectionError(str(exc)) from exc # type: ignore[no-untyped-call]
return wrapper
@@ -239,15 +240,18 @@ class _PostgresConsumer(Consumer):
self.in_processing: set[str] = set()
self.prefetch = prefetch
self.misses = 0
# We have two different connections here. One for locks and one for listening to
# notifications. We can't use the same connection for both as the listen connection might
# be blocked with pending notifications. We also can't use a Django connection as we can't
# be sure we'll get the same one every time to be able to release locks from the same
# connection.
self._locks_connection: DatabaseWrapper | None = None
self._listen_connection: DatabaseWrapper | None = None
self.postgres_channel = channel_name(self.queue_name, ChannelIdentifier.ENQUEUE)
# Override because dramatiq doesn't allow us setting this manually
self.timeout = Conf().worker["consumer_listen_timeout"]
self.lock_purge_interval = timedelta(seconds=Conf().lock_purge_interval)
self.lock_purge_last_run = timezone.now()
self.task_purge_interval = timedelta(seconds=Conf().task_purge_interval)
self.task_purge_last_run = timezone.now() - self.task_purge_interval
@@ -258,14 +262,17 @@ class _PostgresConsumer(Consumer):
self.scheduler_interval = timedelta(seconds=Conf().scheduler_interval)
self.scheduler_last_run = timezone.now() - self.scheduler_interval
@property
def connection(self) -> DatabaseWrapper:
return cast(DatabaseWrapper, connections[self.db_alias])
@property
def query_set(self) -> QuerySet[TaskBase]:
return self.broker.query_set
@property
def locks_connection(self) -> DatabaseWrapper:
if self._locks_connection is not None and self._locks_connection.is_usable():
return self._locks_connection
self._locks_connection = cast(DatabaseWrapper, connections.create_connection(self.db_alias))
return self._locks_connection
@property
def listen_connection(self) -> DatabaseWrapper:
if self._listen_connection is not None and self._listen_connection.is_usable():
@@ -320,21 +327,40 @@ class _PostgresConsumer(Consumer):
self.logger.debug("Message already consumed by self", message_id=message_id)
return None
lock_result = (
self.query_set.filter(message_id=message_id)
.exclude(state__in=(TaskState.DONE, TaskState.REJECTED))
.exclude(eta__gte=timezone.now() + timedelta(seconds=self.timeout))
.extra(
where=["pg_try_advisory_lock(%s)"],
params=[self._get_message_lock_id(message_id)],
with self.locks_connection.cursor() as cursor:
cursor.execute(
sql.SQL(
"""
UPDATE {table}
SET {state} = %(state)s, {mtime} = %(mtime)s
WHERE
{table}.{message_id} = %(message_id)s
AND
{table}.{state} != ALL(%(excluded_states)s)
AND
({table}.{eta} < %(maximum_eta)s OR {table}.{eta} IS NULL)
AND
pg_try_advisory_lock(%(lock_id)s)
"""
).format(
table=sql.Identifier(self.query_set.model._meta.db_table),
state=sql.Identifier("state"),
mtime=sql.Identifier("mtime"),
message_id=sql.Identifier("message_id"),
eta=sql.Identifier("eta"),
),
{
"state": TaskState.CONSUMED.value,
"mtime": timezone.now(),
"message_id": message_id,
"excluded_states": [TaskState.DONE.value, TaskState.REJECTED.value],
"maximum_eta": timezone.now() + timedelta(seconds=self.timeout),
"lock_id": self._get_message_lock_id(message_id),
},
)
.update(
state=TaskState.CONSUMED,
mtime=timezone.now(),
)
)
if lock_result != 1:
return None
if cursor.rowcount != 1:
self._unlock_message(message_id)
return None
task: TaskBase | None = (
self.query_set.defer(None).defer("result").filter(message_id=message_id).first()
@@ -405,9 +431,10 @@ class _PostgresConsumer(Consumer):
def _unlock_message(self, message_id: str) -> bool:
self.logger.debug("Unlocking message", message_id=message_id)
try:
with self.connection.cursor() as cursor:
with self.locks_connection.cursor() as cursor:
cursor.execute(
"SELECT pg_advisory_unlock(%s)", (self._get_message_lock_id(message_id),)
"SELECT pg_advisory_unlock(%s)",
(self._get_message_lock_id(message_id),),
)
return True
except DATABASE_ERRORS:
@@ -420,7 +447,7 @@ class _PostgresConsumer(Consumer):
self.in_processing.remove(str(message.message_id))
except KeyError:
pass
self._unlock_message(str(message.message_id))
self.to_unlock.add(str(message.message_id))
task = message.options.pop("task", None)
self.query_set.filter(
message_id=message.message_id,
@@ -453,7 +480,6 @@ class _PostgresConsumer(Consumer):
for message in messages:
self.to_unlock.add(str(message.message_id))
self.in_processing.remove(str(message.message_id))
self._purge_locks()
def _scheduler(self) -> None:
if not self.scheduler:
@@ -464,8 +490,6 @@ class _PostgresConsumer(Consumer):
self.schedule_last_run = timezone.now()
def _purge_locks(self) -> None:
if timezone.now() - self.lock_purge_last_run < self.lock_purge_interval:
return
while True:
try:
message_id = self.to_unlock.pop()
@@ -473,7 +497,6 @@ class _PostgresConsumer(Consumer):
break
if not self._unlock_message(str(message_id)):
return
self.lock_purge_last_run = timezone.now()
def _auto_purge(self) -> None:
if timezone.now() - self.task_purge_last_run < self.task_purge_interval:
@@ -492,15 +515,17 @@ class _PostgresConsumer(Consumer):
try:
self._purge_locks()
finally:
try:
self.connection.close()
except DATABASE_ERRORS:
pass
finally:
if self._listen_connection is not None:
conn = self._listen_connection
self._listen_connection = None
try:
conn.close()
except DATABASE_ERRORS:
pass
if self._locks_connection is not None:
conn = self._locks_connection
self._locks_connection = None
try:
conn.close()
except DATABASE_ERRORS:
pass
if self._listen_connection is not None:
conn = self._listen_connection
self._listen_connection = None
try:
conn.close()
except DATABASE_ERRORS:
pass

View File

@@ -63,10 +63,6 @@ class Conf:
def task_model(self) -> str:
return cast(str, self.conf["task_model"])
@property
def lock_purge_interval(self) -> int:
return cast(int, self.conf.get("lock_purge_interval", 60))
@property
def task_purge_interval(self) -> int:
# 24 hours

View File

@@ -4646,9 +4646,9 @@
"license": "MIT"
},
"node_modules/@types/react": {
"version": "19.2.2",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz",
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
"version": "19.2.4",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.4.tgz",
"integrity": "sha512-tBFxBp9Nfyy5rsmefN+WXc1JeW/j2BpBHFdLZbEVfs9wn3E3NRFxwV0pJg8M1qQAexFpvz73hJXFofV0ZAu92A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4656,9 +4656,9 @@
}
},
"node_modules/@types/react-dom": {
"version": "19.2.2",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.2.tgz",
"integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==",
"version": "19.2.3",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
"dev": true,
"license": "MIT",
"peerDependencies": {

View File

@@ -912,9 +912,9 @@
}
},
"node_modules/@types/node": {
"version": "24.9.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.1.tgz",
"integrity": "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==",
"version": "24.10.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
"dev": true,
"license": "MIT",
"dependencies": {

View File

@@ -383,21 +383,21 @@
}
},
"node_modules/@eslint/config-helpers": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.1.tgz",
"integrity": "sha512-csZAzkNhsgwb0I/UAV6/RGFTbiakPCf0ZrGmrIxQpYvGZ00PhTkSnyKNolphgIvmnJeGw6rcGVEXfTzUnFuEvw==",
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
"integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
"license": "Apache-2.0",
"dependencies": {
"@eslint/core": "^0.16.0"
"@eslint/core": "^0.17.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/core": {
"version": "0.16.0",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz",
"integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==",
"version": "0.17.0",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
"integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
"license": "Apache-2.0",
"dependencies": {
"@types/json-schema": "^7.0.15"
@@ -430,9 +430,9 @@
}
},
"node_modules/@eslint/js": {
"version": "9.38.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.38.0.tgz",
"integrity": "sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A==",
"version": "9.39.1",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz",
"integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==",
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -451,12 +451,12 @@
}
},
"node_modules/@eslint/plugin-kit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz",
"integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==",
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
"integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
"license": "Apache-2.0",
"dependencies": {
"@eslint/core": "^0.16.0",
"@eslint/core": "^0.17.0",
"levn": "^0.4.1"
},
"engines": {
@@ -731,17 +731,17 @@
"license": "MIT"
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.46.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.2.tgz",
"integrity": "sha512-ZGBMToy857/NIPaaCucIUQgqueOiq7HeAKkhlvqVV4lm089zUFW6ikRySx2v+cAhKeUCPuWVHeimyk6Dw1iY3w==",
"version": "8.46.4",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.4.tgz",
"integrity": "sha512-R48VhmTJqplNyDxCyqqVkFSZIx1qX6PzwqgcXn1olLrzxcSBDlOsbtcnQuQhNtnNiJ4Xe5gREI1foajYaYU2Vg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "8.46.2",
"@typescript-eslint/type-utils": "8.46.2",
"@typescript-eslint/utils": "8.46.2",
"@typescript-eslint/visitor-keys": "8.46.2",
"@typescript-eslint/scope-manager": "8.46.4",
"@typescript-eslint/type-utils": "8.46.4",
"@typescript-eslint/utils": "8.46.4",
"@typescript-eslint/visitor-keys": "8.46.4",
"graphemer": "^1.4.0",
"ignore": "^7.0.0",
"natural-compare": "^1.4.0",
@@ -755,7 +755,7 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"@typescript-eslint/parser": "^8.46.2",
"@typescript-eslint/parser": "^8.46.4",
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
@@ -771,16 +771,16 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "8.46.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.2.tgz",
"integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==",
"version": "8.46.4",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.4.tgz",
"integrity": "sha512-tK3GPFWbirvNgsNKto+UmB/cRtn6TZfyw0D6IKrW55n6Vbs7KJoZtI//kpTKzE/DUmmnAFD8/Ca46s7Obs92/w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.46.2",
"@typescript-eslint/types": "8.46.2",
"@typescript-eslint/typescript-estree": "8.46.2",
"@typescript-eslint/visitor-keys": "8.46.2",
"@typescript-eslint/scope-manager": "8.46.4",
"@typescript-eslint/types": "8.46.4",
"@typescript-eslint/typescript-estree": "8.46.4",
"@typescript-eslint/visitor-keys": "8.46.4",
"debug": "^4.3.4"
},
"engines": {
@@ -796,14 +796,14 @@
}
},
"node_modules/@typescript-eslint/project-service": {
"version": "8.46.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.2.tgz",
"integrity": "sha512-PULOLZ9iqwI7hXcmL4fVfIsBi6AN9YxRc0frbvmg8f+4hQAjQ5GYNKK0DIArNo+rOKmR/iBYwkpBmnIwin4wBg==",
"version": "8.46.4",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.4.tgz",
"integrity": "sha512-nPiRSKuvtTN+no/2N1kt2tUh/HoFzeEgOm9fQ6XQk4/ApGqjx0zFIIaLJ6wooR1HIoozvj2j6vTi/1fgAz7UYQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.46.2",
"@typescript-eslint/types": "^8.46.2",
"@typescript-eslint/tsconfig-utils": "^8.46.4",
"@typescript-eslint/types": "^8.46.4",
"debug": "^4.3.4"
},
"engines": {
@@ -818,14 +818,14 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "8.46.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.2.tgz",
"integrity": "sha512-LF4b/NmGvdWEHD2H4MsHD8ny6JpiVNDzrSZr3CsckEgCbAGZbYM4Cqxvi9L+WqDMT+51Ozy7lt2M+d0JLEuBqA==",
"version": "8.46.4",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.4.tgz",
"integrity": "sha512-tMDbLGXb1wC+McN1M6QeDx7P7c0UWO5z9CXqp7J8E+xGcJuUuevWKxuG8j41FoweS3+L41SkyKKkia16jpX7CA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.46.2",
"@typescript-eslint/visitor-keys": "8.46.2"
"@typescript-eslint/types": "8.46.4",
"@typescript-eslint/visitor-keys": "8.46.4"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -836,9 +836,9 @@
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.46.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.2.tgz",
"integrity": "sha512-a7QH6fw4S57+F5y2FIxxSDyi5M4UfGF+Jl1bCGd7+L4KsaUY80GsiF/t0UoRFDHAguKlBaACWJRmdrc6Xfkkag==",
"version": "8.46.4",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.4.tgz",
"integrity": "sha512-+/XqaZPIAk6Cjg7NWgSGe27X4zMGqrFqZ8atJsX3CWxH/jACqWnrWI68h7nHQld0y+k9eTTjb9r+KU4twLoo9A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -853,15 +853,15 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.46.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.2.tgz",
"integrity": "sha512-HbPM4LbaAAt/DjxXaG9yiS9brOOz6fabal4uvUmaUYe6l3K1phQDMQKBRUrr06BQkxkvIZVVHttqiybM9nJsLA==",
"version": "8.46.4",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.4.tgz",
"integrity": "sha512-V4QC8h3fdT5Wro6vANk6eojqfbv5bpwHuMsBcJUJkqs2z5XnYhJzyz9Y02eUmF9u3PgXEUiOt4w4KHR3P+z0PQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.46.2",
"@typescript-eslint/typescript-estree": "8.46.2",
"@typescript-eslint/utils": "8.46.2",
"@typescript-eslint/types": "8.46.4",
"@typescript-eslint/typescript-estree": "8.46.4",
"@typescript-eslint/utils": "8.46.4",
"debug": "^4.3.4",
"ts-api-utils": "^2.1.0"
},
@@ -878,9 +878,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "8.46.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.2.tgz",
"integrity": "sha512-lNCWCbq7rpg7qDsQrd3D6NyWYu+gkTENkG5IKYhUIcxSb59SQC/hEQ+MrG4sTgBVghTonNWq42bA/d4yYumldQ==",
"version": "8.46.4",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.4.tgz",
"integrity": "sha512-USjyxm3gQEePdUwJBFjjGNG18xY9A2grDVGuk7/9AkjIF1L+ZrVnwR5VAU5JXtUnBL/Nwt3H31KlRDaksnM7/w==",
"dev": true,
"license": "MIT",
"engines": {
@@ -892,16 +892,16 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "8.46.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.2.tgz",
"integrity": "sha512-f7rW7LJ2b7Uh2EiQ+7sza6RDZnajbNbemn54Ob6fRwQbgcIn+GWfyuHDHRYgRoZu1P4AayVScrRW+YfbTvPQoQ==",
"version": "8.46.4",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.4.tgz",
"integrity": "sha512-7oV2qEOr1d4NWNmpXLR35LvCfOkTNymY9oyW+lUHkmCno7aOmIf/hMaydnJBUTBMRCOGZh8YjkFOc8dadEoNGA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.46.2",
"@typescript-eslint/tsconfig-utils": "8.46.2",
"@typescript-eslint/types": "8.46.2",
"@typescript-eslint/visitor-keys": "8.46.2",
"@typescript-eslint/project-service": "8.46.4",
"@typescript-eslint/tsconfig-utils": "8.46.4",
"@typescript-eslint/types": "8.46.4",
"@typescript-eslint/visitor-keys": "8.46.4",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
@@ -960,16 +960,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "8.46.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.2.tgz",
"integrity": "sha512-sExxzucx0Tud5tE0XqR0lT0psBQvEpnpiul9XbGUB1QwpWJJAps1O/Z7hJxLGiZLBKMCutjTzDgmd1muEhBnVg==",
"version": "8.46.4",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.4.tgz",
"integrity": "sha512-AbSv11fklGXV6T28dp2Me04Uw90R2iJ30g2bgLz529Koehrmkbs1r7paFqr1vPCZi7hHwYxYtxfyQMRC8QaVSg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.7.0",
"@typescript-eslint/scope-manager": "8.46.2",
"@typescript-eslint/types": "8.46.2",
"@typescript-eslint/typescript-estree": "8.46.2"
"@typescript-eslint/scope-manager": "8.46.4",
"@typescript-eslint/types": "8.46.4",
"@typescript-eslint/typescript-estree": "8.46.4"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -984,13 +984,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "8.46.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.2.tgz",
"integrity": "sha512-tUFMXI4gxzzMXt4xpGJEsBsTox0XbNQ1y94EwlD/CuZwFcQP79xfQqMhau9HsRc/J0cAPA/HZt1dZPtGn9V/7w==",
"version": "8.46.4",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.4.tgz",
"integrity": "sha512-/++5CYLQqsO9HFGLI7APrxBJYo+5OCMpViuhV8q5/Qa3o5mMrF//eQHks+PXcsAVaLdn817fMuS7zqoXNNZGaw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.46.2",
"@typescript-eslint/types": "8.46.4",
"eslint-visitor-keys": "^4.2.1"
},
"engines": {
@@ -1923,19 +1923,19 @@
}
},
"node_modules/eslint": {
"version": "9.38.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.38.0.tgz",
"integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==",
"version": "9.39.1",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz",
"integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
"@eslint/config-array": "^0.21.1",
"@eslint/config-helpers": "^0.4.1",
"@eslint/core": "^0.16.0",
"@eslint/config-helpers": "^0.4.2",
"@eslint/core": "^0.17.0",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "9.38.0",
"@eslint/plugin-kit": "^0.4.0",
"@eslint/js": "9.39.1",
"@eslint/plugin-kit": "^0.4.1",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.4.2",
@@ -5077,16 +5077,16 @@
}
},
"node_modules/typescript-eslint": {
"version": "8.46.2",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.46.2.tgz",
"integrity": "sha512-vbw8bOmiuYNdzzV3lsiWv6sRwjyuKJMQqWulBOU7M0RrxedXledX8G8kBbQeiOYDnTfiXz0Y4081E1QMNB6iQg==",
"version": "8.46.4",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.46.4.tgz",
"integrity": "sha512-KALyxkpYV5Ix7UhvjTwJXZv76VWsHG+NjNlt/z+a17SOQSiOcBdUXdbJdyXi7RPxrBFECtFOiPwUJQusJuCqrg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/eslint-plugin": "8.46.2",
"@typescript-eslint/parser": "8.46.2",
"@typescript-eslint/typescript-estree": "8.46.2",
"@typescript-eslint/utils": "8.46.2"
"@typescript-eslint/eslint-plugin": "8.46.4",
"@typescript-eslint/parser": "8.46.4",
"@typescript-eslint/typescript-estree": "8.46.4",
"@typescript-eslint/utils": "8.46.4"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"

View File

@@ -172,21 +172,21 @@
}
},
"node_modules/@eslint/config-helpers": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.1.tgz",
"integrity": "sha512-csZAzkNhsgwb0I/UAV6/RGFTbiakPCf0ZrGmrIxQpYvGZ00PhTkSnyKNolphgIvmnJeGw6rcGVEXfTzUnFuEvw==",
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
"integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
"license": "Apache-2.0",
"dependencies": {
"@eslint/core": "^0.16.0"
"@eslint/core": "^0.17.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/core": {
"version": "0.16.0",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz",
"integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==",
"version": "0.17.0",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
"integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
"license": "Apache-2.0",
"dependencies": {
"@types/json-schema": "^7.0.15"
@@ -231,9 +231,9 @@
}
},
"node_modules/@eslint/js": {
"version": "9.38.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.38.0.tgz",
"integrity": "sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A==",
"version": "9.39.1",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz",
"integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==",
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -252,12 +252,12 @@
}
},
"node_modules/@eslint/plugin-kit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz",
"integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==",
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
"integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
"license": "Apache-2.0",
"dependencies": {
"@eslint/core": "^0.16.0",
"@eslint/core": "^0.17.0",
"levn": "^0.4.1"
},
"engines": {
@@ -388,9 +388,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "24.9.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.1.tgz",
"integrity": "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==",
"version": "24.10.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -692,19 +692,19 @@
}
},
"node_modules/eslint": {
"version": "9.38.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.38.0.tgz",
"integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==",
"version": "9.39.1",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz",
"integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
"@eslint/config-array": "^0.21.1",
"@eslint/config-helpers": "^0.4.1",
"@eslint/core": "^0.16.0",
"@eslint/config-helpers": "^0.4.2",
"@eslint/core": "^0.17.0",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "9.38.0",
"@eslint/plugin-kit": "^0.4.0",
"@eslint/js": "9.39.1",
"@eslint/plugin-kit": "^0.4.1",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.4.2",

View File

@@ -17,7 +17,7 @@ COPY web .
RUN npm run build-proxy
# Stage 2: Build
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.4-trixie@sha256:a13297bfdd45d9702badd83b45c6db1f8fee638bba6ece0c359ce51260577bbe AS builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.4-trixie@sha256:27e1c927a07ed2c7295d39941d6d881424739dbde9ae3055d0d3013699ed35e8 AS builder
ARG TARGETOS
ARG TARGETARCH
@@ -47,7 +47,7 @@ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
go build -o /go/proxy ./cmd/proxy
# Stage 3: Run
FROM ghcr.io/goauthentik/fips-debian:trixie-slim-fips@sha256:9b4cedf932e97194f1825124830f2eec14254d90162dad28f97e505971543115
FROM ghcr.io/goauthentik/fips-debian:trixie-slim-fips@sha256:40a1f32b318c9d4a488580bf156c408ec2e076898d40ada9256b7c219af79e19
ARG VERSION
ARG GIT_BUILD_HASH

View File

@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1
# Stage 1: Build
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.4-trixie@sha256:a13297bfdd45d9702badd83b45c6db1f8fee638bba6ece0c359ce51260577bbe AS builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.4-trixie@sha256:27e1c927a07ed2c7295d39941d6d881424739dbde9ae3055d0d3013699ed35e8 AS builder
ARG TARGETOS
ARG TARGETARCH

View File

@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1
# Stage 1: Build
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.4-trixie@sha256:a13297bfdd45d9702badd83b45c6db1f8fee638bba6ece0c359ce51260577bbe AS builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.4-trixie@sha256:27e1c927a07ed2c7295d39941d6d881424739dbde9ae3055d0d3013699ed35e8 AS builder
ARG TARGETOS
ARG TARGETARCH
@@ -31,7 +31,7 @@ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
go build -o /go/radius ./cmd/radius
# Stage 2: Run
FROM ghcr.io/goauthentik/fips-debian:trixie-slim-fips@sha256:9b4cedf932e97194f1825124830f2eec14254d90162dad28f97e505971543115
FROM ghcr.io/goauthentik/fips-debian:trixie-slim-fips@sha256:40a1f32b318c9d4a488580bf156c408ec2e076898d40ada9256b7c219af79e19
ARG VERSION
ARG GIT_BUILD_HASH

View File

@@ -5,11 +5,11 @@ services:
restart: never
network_mode: none
volumes:
- ${LOCAL_PROJECT_DIR:-../../}:/local
- ../../:/local
gen:
image: docker.io/openapitools/openapi-generator-cli:v7.16.0
restart: never
network_mode: none
volumes:
- ${LOCAL_PROJECT_DIR:-../../}:/local
- ../../:/local

View File

@@ -75,5 +75,5 @@ base = {
},
}
with open("lifecycle/container/docker-compose.yml", "w") as _compose:
with open("docker-compose.yml", "w") as _compose:
safe_dump(base, _compose)

View File

@@ -5,29 +5,29 @@ hash="$(git rev-parse HEAD || openssl rand -base64 36 | sha256sum)"
AUTHENTIK_IMAGE="xghcr.io/goauthentik/server"
AUTHENTIK_TAG="$(echo "$hash" | cut -c1-15)"
if [ -f lifecycle/container/.env ]; then
if [ -f .env ]; then
echo "Existing .env file, aborting"
exit 1
fi
echo PG_PASS="$(openssl rand -base64 36 | tr -d '\n')" >lifecycle/container/.env
echo AUTHENTIK_SECRET_KEY="$(openssl rand -base64 60 | tr -d '\n')" >>lifecycle/container/.env
echo PG_PASS="$(openssl rand -base64 36 | tr -d '\n')" >.env
echo AUTHENTIK_SECRET_KEY="$(openssl rand -base64 60 | tr -d '\n')" >>.env
export COMPOSE_PROJECT_NAME="authentik-test-${AUTHENTIK_TAG}"
if [[ -v BUILD ]]; then
echo AUTHENTIK_IMAGE="${AUTHENTIK_IMAGE}" >>lifecycle/container/.env
echo AUTHENTIK_TAG="${AUTHENTIK_TAG}" >>lifecycle/container/.env
echo AUTHENTIK_IMAGE="${AUTHENTIK_IMAGE}" >>.env
echo AUTHENTIK_TAG="${AUTHENTIK_TAG}" >>.env
# Ensure buildx is installed
docker buildx install
# For release builds we have an empty client here as we use the NPM package
mkdir -p ./gen-ts-api
touch lifecycle/container/.env
touch .env
docker build -t "${AUTHENTIK_IMAGE}:${AUTHENTIK_TAG}" .
fi
docker compose -f lifecycle/container/docker-compose.yml up --no-start
docker compose -f lifecycle/container/docker-compose.yml start postgresql redis
docker compose -f lifecycle/container/docker-compose.yml run -u root server test-all
docker compose -f lifecycle/container/docker-compose.yml down -v
docker compose up --no-start
docker compose start postgresql
docker compose run -u root server test-all
docker compose down -v

View File

@@ -1,11 +1,11 @@
services:
chromium:
image: docker.io/selenium/standalone-chromium:141.0
image: docker.io/selenium/standalone-chromium:142.0
shm_size: 2g
network_mode: host
restart: always
mailpit:
image: docker.io/axllent/mailpit:v1.27.10
image: docker.io/axllent/mailpit:v1.27.11
ports:
- 1025:1025
- 8025:8025

View File

@@ -1,6 +1,6 @@
---
enablePasswordDB: true
issuer: http://127.0.0.1:5556/dex
issuer: http://{{ .Env.AK_HOST }}:5556/dex
logger:
level: debug
staticClients:

View File

@@ -110,8 +110,8 @@ class TestFlowsEnroll(SeleniumTestCase):
identification_stage = self.get_shadow_root("ak-stage-identification", flow_executor)
wait = WebDriverWait(identification_stage, self.wait_timeout)
wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "#enroll")))
identification_stage.find_element(By.CSS_SELECTOR, "#enroll").click()
wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "a[name='enroll']")))
identification_stage.find_element(By.CSS_SELECTOR, "a[name='enroll']").click()
# First prompt stage
flow_executor = self.get_shadow_root("ak-flow-executor")
@@ -193,7 +193,7 @@ class TestFlowsEnroll(SeleniumTestCase):
self.assertEqual(
"Continue to confirm this email address.",
consent_stage.find_element(By.CSS_SELECTOR, "#header-text").text,
consent_stage.find_element(By.CSS_SELECTOR, "[data-test-id='stage-heading']").text,
)
# Back on the main tab, confirm

View File

@@ -26,8 +26,8 @@ class TestFlowsRecovery(SeleniumTestCase):
identification_stage = self.get_shadow_root("ak-stage-identification", flow_executor)
wait = WebDriverWait(identification_stage, self.wait_timeout)
wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "#recovery")))
identification_stage.find_element(By.CSS_SELECTOR, "#recovery").click()
wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "a[name='recovery']")))
identification_stage.find_element(By.CSS_SELECTOR, "a[name='recovery']").click()
# First prompt stage
flow_executor = self.get_shadow_root("ak-flow-executor")

View File

@@ -158,7 +158,7 @@ class TestProviderOAuth2Github(SeleniumTestCase):
self.assertIn(
app.name,
consent_stage.find_element(By.CSS_SELECTOR, "#header-text").text,
consent_stage.find_element(By.CSS_SELECTOR, "[data-test-id='stage-heading']").text,
)
self.assertEqual(
"GitHub Compatibility: Access you Email addresses",
@@ -228,8 +228,10 @@ class TestProviderOAuth2Github(SeleniumTestCase):
self.driver.find_element(By.CLASS_NAME, "btn-service--github").click()
self.login()
self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "header > h1")))
self.wait.until(
ec.presence_of_element_located((By.CSS_SELECTOR, "[data-test-id='card-title']"))
)
self.assertEqual(
self.driver.find_element(By.CSS_SELECTOR, "header > h1").text,
self.driver.find_element(By.CSS_SELECTOR, "[data-test-id='card-title']").text,
"Permission denied",
)

View File

@@ -327,7 +327,7 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
self.assertIn(
app.name,
consent_stage.find_element(By.CSS_SELECTOR, "#header-text").text,
consent_stage.find_element(By.CSS_SELECTOR, "[data-test-id='stage-heading']").text,
)
consent_stage.find_element(
By.CSS_SELECTOR,
@@ -407,9 +407,11 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
self.driver.find_element(By.CLASS_NAME, "btn-service--oauth").click()
self.login()
self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "header > h1")))
self.wait.until(
ec.presence_of_element_located((By.CSS_SELECTOR, "[data-test-id='card-title']"))
)
self.assertEqual(
self.driver.find_element(By.CSS_SELECTOR, "header > h1").text,
self.driver.find_element(By.CSS_SELECTOR, "[data-test-id='card-title']").text,
"Permission denied",
)

View File

@@ -1,6 +1,6 @@
"""test OAuth2 OpenID Provider flow"""
from json import loads
from json import dumps
from time import sleep
from selenium.webdriver.common.by import By
@@ -146,6 +146,7 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
self.driver.get("http://localhost:9009")
self.login()
self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "ak-flow-executor")))
flow_executor = self.get_shadow_root("ak-flow-executor")
@@ -153,26 +154,63 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
self.assertIn(
app.name,
consent_stage.find_element(By.CSS_SELECTOR, "#header-text").text,
consent_stage.find_element(By.CSS_SELECTOR, "[data-test-id='stage-heading']").text,
)
current_url = self.driver.current_url
consent_stage.find_element(
By.CSS_SELECTOR,
"[type=submit]",
).click()
self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "pre")))
self.wait.until(ec.text_to_be_present_in_element((By.CSS_SELECTOR, "pre"), "{"))
body = loads(self.driver.find_element(By.CSS_SELECTOR, "pre").text)
self.wait.until(ec.url_changes(current_url))
self.assertEqual(body["IDTokenClaims"]["nickname"], self.user.username)
self.assertEqual(body["IDTokenClaims"]["amr"], ["pwd"])
self.assertEqual(body["UserInfo"]["nickname"], self.user.username)
body = self.parse_json_content()
snippet = dumps(body, indent=2)[:500].replace("\n", " ")
self.assertEqual(body["IDTokenClaims"]["name"], self.user.name)
self.assertEqual(body["UserInfo"]["name"], self.user.name)
self.assertEqual(
body.get("IDTokenClaims", {}).get("nickname"),
self.user.username,
f"IDTokenClaims.nickname mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(body["IDTokenClaims"]["email"], self.user.email)
self.assertEqual(body["UserInfo"]["email"], self.user.email)
self.assertEqual(
body.get("IDTokenClaims", {}).get("amr"),
["pwd"],
f"IDTokenClaims.amr mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body.get("IDTokenClaims", {}).get("name"),
self.user.name,
f"IDTokenClaims.name mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body.get("IDTokenClaims", {}).get("email"),
self.user.email,
f"IDTokenClaims.email mismatch at {self.driver.current_url}: {snippet}",
)
# UserInfo assertions
self.assertEqual(
body.get("UserInfo", {}).get("nickname"),
self.user.username,
f"UserInfo.nickname mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body.get("UserInfo", {}).get("name"),
self.user.name,
f"UserInfo.name mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body.get("UserInfo", {}).get("email"),
self.user.email,
f"UserInfo.email mismatch at {self.driver.current_url}: {snippet}",
)
@retry()
@apply_blueprint(
@@ -227,25 +265,56 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
self.assertIn(
app.name,
consent_stage.find_element(By.CSS_SELECTOR, "#header-text").text,
consent_stage.find_element(By.CSS_SELECTOR, "[data-test-id='stage-heading']").text,
)
current_url = self.driver.current_url
consent_stage.find_element(
By.CSS_SELECTOR,
"[type=submit]",
).click()
self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "pre")))
self.wait.until(ec.text_to_be_present_in_element((By.CSS_SELECTOR, "pre"), "{"))
body = loads(self.driver.find_element(By.CSS_SELECTOR, "pre").text)
self.wait.until(ec.url_changes(current_url))
self.assertEqual(body["IDTokenClaims"]["nickname"], self.user.username)
self.assertEqual(body["UserInfo"]["nickname"], self.user.username)
body = self.parse_json_content()
snippet = dumps(body, indent=2)[:500].replace("\n", " ")
self.assertEqual(body["IDTokenClaims"]["name"], self.user.name)
self.assertEqual(body["UserInfo"]["name"], self.user.name)
id_token_claims = body.get("IDTokenClaims", {})
user_info = body.get("UserInfo", {})
self.assertEqual(body["IDTokenClaims"]["email"], self.user.email)
self.assertEqual(body["UserInfo"]["email"], self.user.email)
self.assertEqual(
id_token_claims.get("nickname"),
self.user.username,
f"IDTokenClaims.nickname mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
user_info.get("nickname"),
self.user.username,
f"UserInfo.nickname mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
id_token_claims.get("name"),
self.user.name,
f"IDTokenClaims.name mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
user_info.get("name"),
self.user.name,
f"UserInfo.name mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
id_token_claims.get("email"),
self.user.email,
f"IDTokenClaims.email mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
user_info.get("email"),
self.user.email,
f"UserInfo.email mismatch at {self.driver.current_url}: {snippet}",
)
@retry()
@apply_blueprint(
@@ -297,8 +366,10 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
self.setup_client()
self.driver.get("http://localhost:9009")
self.login()
self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "header > h1")))
self.wait.until(
ec.presence_of_element_located((By.CSS_SELECTOR, "[data-test-id='card-title']"))
)
self.assertEqual(
self.driver.find_element(By.CSS_SELECTOR, "header > h1").text,
self.driver.find_element(By.CSS_SELECTOR, "[data-test-id='card-title']").text,
"Permission denied",
)

View File

@@ -1,6 +1,6 @@
"""test OAuth2 OpenID Provider flow"""
from json import loads
from json import dumps
from time import sleep
from selenium.webdriver.common.by import By
@@ -149,12 +149,29 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
self.driver.get("http://localhost:9009/implicit/")
self.wait.until(ec.title_contains("authentik"))
self.login()
self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "pre")))
self.wait.until(ec.text_to_be_present_in_element((By.CSS_SELECTOR, "pre"), "{"))
body = loads(self.driver.find_element(By.CSS_SELECTOR, "pre").text)
self.assertEqual(body["profile"]["nickname"], self.user.username)
self.assertEqual(body["profile"]["name"], self.user.name)
self.assertEqual(body["profile"]["email"], self.user.email)
body = self.parse_json_content()
snippet = dumps(body, indent=2)[:500].replace("\n", " ")
profile = body.get("profile", {})
self.assertEqual(
profile.get("nickname"),
self.user.username,
f"Nickname mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
profile.get("name"),
self.user.name,
f"Name mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
profile.get("email"),
self.user.email,
f"Email mismatch at {self.driver.current_url}: {snippet}",
)
@retry()
@apply_blueprint(
@@ -211,20 +228,40 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
self.assertIn(
app.name,
consent_stage.find_element(By.CSS_SELECTOR, "#header-text").text,
consent_stage.find_element(By.CSS_SELECTOR, "[data-test-id='stage-heading']").text,
)
current_url = self.driver.current_url
consent_stage.find_element(
By.CSS_SELECTOR,
"[type=submit]",
).click()
self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "pre")))
self.wait.until(ec.text_to_be_present_in_element((By.CSS_SELECTOR, "pre"), "{"))
body = loads(self.driver.find_element(By.CSS_SELECTOR, "pre").text)
self.wait.until(ec.url_changes(current_url))
self.assertEqual(body["profile"]["nickname"], self.user.username)
self.assertEqual(body["profile"]["name"], self.user.name)
self.assertEqual(body["profile"]["email"], self.user.email)
body = self.parse_json_content()
snippet = dumps(body, indent=2)[:500].replace("\n", " ")
profile = body.get("profile", {})
self.assertEqual(
profile.get("nickname"),
self.user.username,
f"Nickname mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
profile.get("name"),
self.user.name,
f"Name mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
profile.get("email"),
self.user.email,
f"Email mismatch at {self.driver.current_url}: {snippet}",
)
@retry()
@apply_blueprint(
@@ -278,8 +315,10 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
self.driver.get("http://localhost:9009/implicit/")
self.wait.until(ec.title_contains("authentik"))
self.login()
self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "header > h1")))
self.wait.until(
ec.presence_of_element_located((By.CSS_SELECTOR, "[data-test-id='card-title']"))
)
self.assertEqual(
self.driver.find_element(By.CSS_SELECTOR, "header > h1").text,
self.driver.find_element(By.CSS_SELECTOR, "[data-test-id='card-title']").text,
"Permission denied",
)

View File

@@ -2,7 +2,7 @@
from base64 import b64encode
from dataclasses import asdict
from json import loads
from json import dumps
from sys import platform
from time import sleep
from unittest.case import skip, skipUnless
@@ -94,24 +94,39 @@ class TestProviderProxy(SeleniumTestCase):
self.login()
sleep(1)
full_body_text = self.driver.find_element(By.CSS_SELECTOR, "pre").text
body = loads(full_body_text)
body = self.parse_json_content()
headers = body.get("headers", {})
snippet = dumps(body, indent=2)[:500].replace("\n", " ")
self.assertEqual(body["headers"]["X-Authentik-Username"], [self.user.username])
self.assertEqual(body["headers"]["X-Foo"], ["bar"])
raw_jwt: str = body["headers"]["X-Authentik-Jwt"][0]
self.assertEqual(
headers.get("X-Authentik-Username"),
[self.user.username],
f"X-Authentik-Username header mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
headers.get("X-Foo"),
["bar"],
f"X-Foo header mismatch at {self.driver.current_url}: {snippet}",
)
raw_jwt: str = headers.get("X-Authentik-Jwt", [None])[0]
jwt = decode(raw_jwt, options={"verify_signature": False})
self.assertIsNotNone(jwt["sid"])
self.assertIsNotNone(jwt["ak_proxy"])
self.assertIsNotNone(jwt["sid"], "Missing 'sid' in JWT")
self.assertIsNotNone(jwt["ak_proxy"], "Missing 'ak_proxy' in JWT")
self.driver.get("http://localhost:9000/outpost.goauthentik.io/sign_out")
sleep(2)
flow_executor = self.get_shadow_root("ak-flow-executor")
session_end_stage = self.get_shadow_root("ak-stage-session-end", flow_executor)
flow_card = self.get_shadow_root("ak-flow-card", session_end_stage)
title = flow_card.find_element(By.CSS_SELECTOR, ".pf-c-title.pf-m-3xl").text
self.assertIn("You've logged out of", title)
self.assertIn(
"You've logged out of",
title,
f"Logout title mismatch at {self.driver.current_url}: {title}",
)
@retry()
@apply_blueprint(
@@ -167,20 +182,37 @@ class TestProviderProxy(SeleniumTestCase):
self.login()
sleep(1)
full_body_text = self.driver.find_element(By.CSS_SELECTOR, "pre").text
body = loads(full_body_text)
body = self.parse_json_content()
headers = body.get("headers", {})
snippet = dumps(body, indent=2)[:500].replace("\n", " ")
self.assertEqual(
headers.get("X-Authentik-Username"),
[self.user.username],
f"X-Authentik-Username header mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(body["headers"]["X-Authentik-Username"], [self.user.username])
auth_header = b64encode(f"{cred}:{cred}".encode()).decode()
self.assertEqual(body["headers"]["Authorization"], [f"Basic {auth_header}"])
self.assertEqual(
headers.get("Authorization"),
[f"Basic {auth_header}"],
f"Authorization header mismatch at {self.driver.current_url}: {snippet}",
)
self.driver.get("http://localhost:9000/outpost.goauthentik.io/sign_out")
sleep(2)
flow_executor = self.get_shadow_root("ak-flow-executor")
session_end_stage = self.get_shadow_root("ak-stage-session-end", flow_executor)
flow_card = self.get_shadow_root("ak-flow-card", session_end_stage)
title = flow_card.find_element(By.CSS_SELECTOR, ".pf-c-title.pf-m-3xl").text
self.assertIn("You've logged out of", title)
self.assertIn(
"You've logged out of",
title,
f"Logout title mismatch at {self.driver.current_url}: {title}",
)
# TODO: Fix flaky test

View File

@@ -1,6 +1,6 @@
"""Proxy and Outpost e2e tests"""
from json import loads
from json import dumps
from pathlib import Path
from time import sleep
from unittest import skip
@@ -101,10 +101,14 @@ class TestProviderProxyForward(SeleniumTestCase):
self.login()
sleep(1)
full_body_text = self.driver.find_element(By.CSS_SELECTOR, "pre").text
body = loads(full_body_text)
body_json = self.parse_json_content()
snippet = dumps(body_json, indent=2)[:500].replace("\n", " ")
self.assertEqual(body["headers"]["X-Authentik-Username"], [self.user.username])
self.assertEqual(
body_json.get("headers", {}).get("X-Authentik-Username"),
[self.user.username],
f"X-Authentik-Username header mismatch at {self.driver.current_url}: {snippet}",
)
self.driver.get("http://localhost/outpost.goauthentik.io/sign_out")
sleep(2)
@@ -137,10 +141,14 @@ class TestProviderProxyForward(SeleniumTestCase):
self.login()
sleep(1)
full_body_text = self.driver.find_element(By.CSS_SELECTOR, "pre").text
body = loads(full_body_text)
body_json = self.parse_json_content()
snippet = dumps(body_json, indent=2)[:500].replace("\n", " ")
self.assertEqual(body["headers"]["X-Authentik-Username"], [self.user.username])
self.assertEqual(
body_json.get("headers", {}).get("X-Authentik-Username"),
[self.user.username],
f"X-Authentik-Username header mismatch at {self.driver.current_url}: {snippet}",
)
self.driver.get("http://localhost/outpost.goauthentik.io/sign_out")
sleep(2)
@@ -171,10 +179,14 @@ class TestProviderProxyForward(SeleniumTestCase):
self.login()
sleep(1)
full_body_text = self.driver.find_element(By.CSS_SELECTOR, "pre").text
body = loads(full_body_text)
body_json = self.parse_json_content()
snippet = dumps(body_json, indent=2)[:500].replace("\n", " ")
self.assertEqual(body["headers"]["X-Authentik-Username"], [self.user.username])
self.assertEqual(
body_json.get("headers", {}).get("X-Authentik-Username"),
[self.user.username],
f"X-Authentik-Username header mismatch at {self.driver.current_url}: {snippet}",
)
self.driver.get("http://localhost/outpost.goauthentik.io/sign_out")
sleep(2)
@@ -208,10 +220,14 @@ class TestProviderProxyForward(SeleniumTestCase):
self.login()
sleep(1)
full_body_text = self.driver.find_element(By.CSS_SELECTOR, "pre").text
body = loads(full_body_text)
body_json = self.parse_json_content()
snippet = dumps(body_json, indent=2)[:500].replace("\n", " ")
self.assertEqual(body["headers"]["X-Authentik-Username"], [self.user.username])
self.assertEqual(
body_json.get("headers", {}).get("X-Authentik-Username"),
[self.user.username],
f"X-Authentik-Username header mismatch at {self.driver.current_url}: {snippet}",
)
self.driver.get("http://localhost/outpost.goauthentik.io/sign_out")
sleep(2)

View File

@@ -1,6 +1,6 @@
"""test SAML Provider flow"""
from json import loads
from json import dumps
from time import sleep
from selenium.webdriver.common.by import By
@@ -86,33 +86,44 @@ class TestProviderSAML(SeleniumTestCase):
self.login()
self.wait_for_url("http://localhost:9009/")
body = loads(self.driver.find_element(By.CSS_SELECTOR, "pre").text)
body = self.parse_json_content()
snippet = dumps(body, indent=2)[:500].replace("\n", " ")
attrs = body.get("attr", {})
self.assertEqual(
body["attr"]["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"],
attrs.get("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"),
[self.user.name],
f"Claim 'name' mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body["attr"][
"http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"
],
attrs.get("http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"),
[self.user.username],
f"Claim 'windowsaccountname' mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body["attr"]["http://schemas.goauthentik.io/2021/02/saml/username"],
attrs.get("http://schemas.goauthentik.io/2021/02/saml/username"),
[self.user.username],
f"Claim 'saml/username' mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body["attr"]["http://schemas.goauthentik.io/2021/02/saml/uid"],
attrs.get("http://schemas.goauthentik.io/2021/02/saml/uid"),
[str(self.user.pk)],
f"Claim 'saml/uid' mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body["attr"]["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"],
attrs.get("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"),
[self.user.email],
f"Claim 'emailaddress' mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body["attr"]["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"],
attrs.get("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"),
[self.user.email],
f"Claim 'upn' mismatch at {self.driver.current_url}: {snippet}",
)
@retry()
@@ -154,33 +165,44 @@ class TestProviderSAML(SeleniumTestCase):
self.login()
self.wait_for_url("http://localhost:9009/")
body = loads(self.driver.find_element(By.CSS_SELECTOR, "pre").text)
body = self.parse_json_content()
snippet = dumps(body, indent=2)[:500].replace("\n", " ")
attrs = body.get("attr", {})
self.assertEqual(
body["attr"]["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"],
attrs.get("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"),
[self.user.name],
f"Claim 'name' mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body["attr"][
"http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"
],
attrs.get("http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"),
[self.user.username],
f"Claim 'windowsaccountname' mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body["attr"]["http://schemas.goauthentik.io/2021/02/saml/username"],
attrs.get("http://schemas.goauthentik.io/2021/02/saml/username"),
[self.user.username],
f"Claim 'saml/username' mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body["attr"]["http://schemas.goauthentik.io/2021/02/saml/uid"],
attrs.get("http://schemas.goauthentik.io/2021/02/saml/uid"),
[str(self.user.pk)],
f"Claim 'saml/uid' mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body["attr"]["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"],
attrs.get("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"),
[self.user.email],
f"Claim 'emailaddress' mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body["attr"]["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"],
attrs.get("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"),
[self.user.email],
f"Claim 'upn' mismatch at {self.driver.current_url}: {snippet}",
)
@retry()
@@ -228,7 +250,8 @@ class TestProviderSAML(SeleniumTestCase):
self.assertIn(
app.name,
consent_stage.find_element(By.CSS_SELECTOR, "#header-text").text,
consent_stage.find_element(By.CSS_SELECTOR, "[data-test-id='stage-heading']").text,
"Consent stage header mismatch",
)
consent_stage.find_element(
By.CSS_SELECTOR,
@@ -237,33 +260,44 @@ class TestProviderSAML(SeleniumTestCase):
self.wait_for_url("http://localhost:9009/")
body = loads(self.driver.find_element(By.CSS_SELECTOR, "pre").text)
body = self.parse_json_content()
snippet = dumps(body, indent=2)[:500].replace("\n", " ")
attrs = body.get("attr", {})
self.assertEqual(
body["attr"]["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"],
attrs.get("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"),
[self.user.name],
f"Claim 'name' mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body["attr"][
"http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"
],
attrs.get("http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"),
[self.user.username],
f"Claim 'windowsaccountname' mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body["attr"]["http://schemas.goauthentik.io/2021/02/saml/username"],
attrs.get("http://schemas.goauthentik.io/2021/02/saml/username"),
[self.user.username],
f"Claim 'saml/username' mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body["attr"]["http://schemas.goauthentik.io/2021/02/saml/uid"],
attrs.get("http://schemas.goauthentik.io/2021/02/saml/uid"),
[str(self.user.pk)],
f"Claim 'saml/uid' mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body["attr"]["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"],
attrs.get("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"),
[self.user.email],
f"Claim 'emailaddress' mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body["attr"]["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"],
attrs.get("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"),
[self.user.email],
f"Claim 'upn' mismatch at {self.driver.current_url}: {snippet}",
)
@retry()
@@ -311,7 +345,7 @@ class TestProviderSAML(SeleniumTestCase):
self.assertIn(
app.name,
consent_stage.find_element(By.CSS_SELECTOR, "#header-text").text,
consent_stage.find_element(By.CSS_SELECTOR, "[data-test-id='stage-heading']").text,
)
consent_stage.find_element(
By.CSS_SELECTOR,
@@ -320,33 +354,44 @@ class TestProviderSAML(SeleniumTestCase):
self.wait_for_url("http://localhost:9009/")
body = loads(self.driver.find_element(By.CSS_SELECTOR, "pre").text)
body = self.parse_json_content()
snippet = dumps(body, indent=2)[:500].replace("\n", " ")
attrs = body.get("attr", {})
self.assertEqual(
body["attr"]["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"],
attrs.get("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"),
[self.user.name],
f"Claim 'name' mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body["attr"][
"http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"
],
attrs.get("http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"),
[self.user.username],
f"Claim 'windowsaccountname' mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body["attr"]["http://schemas.goauthentik.io/2021/02/saml/username"],
attrs.get("http://schemas.goauthentik.io/2021/02/saml/username"),
[self.user.username],
f"Claim 'username' mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body["attr"]["http://schemas.goauthentik.io/2021/02/saml/uid"],
attrs.get("http://schemas.goauthentik.io/2021/02/saml/uid"),
[str(self.user.pk)],
f"Claim 'uid' mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body["attr"]["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"],
attrs.get("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"),
[self.user.email],
f"Claim 'emailaddress' mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body["attr"]["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"],
attrs.get("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"),
[self.user.email],
f"Claim 'upn' mismatch at {self.driver.current_url}: {snippet}",
)
@retry()
@@ -394,33 +439,44 @@ class TestProviderSAML(SeleniumTestCase):
sleep(1)
self.wait_for_url("http://localhost:9009/")
body = loads(self.driver.find_element(By.CSS_SELECTOR, "pre").text)
body = self.parse_json_content()
snippet = dumps(body, indent=2)[:500].replace("\n", " ")
attrs = body.get("attr", {})
self.assertEqual(
body["attr"]["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"],
attrs.get("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"),
[self.user.name],
f"Claim 'name' mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body["attr"][
"http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"
],
attrs.get("http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"),
[self.user.username],
f"Claim 'windowsaccountname' mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body["attr"]["http://schemas.goauthentik.io/2021/02/saml/username"],
attrs.get("http://schemas.goauthentik.io/2021/02/saml/username"),
[self.user.username],
f"Claim 'username' mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body["attr"]["http://schemas.goauthentik.io/2021/02/saml/uid"],
attrs.get("http://schemas.goauthentik.io/2021/02/saml/uid"),
[str(self.user.pk)],
f"Claim 'uid' mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body["attr"]["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"],
attrs.get("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"),
[self.user.email],
f"Claim 'emailaddress' mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body["attr"]["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"],
attrs.get("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"),
[self.user.email],
f"Claim 'upn' mismatch at {self.driver.current_url}: {snippet}",
)
@retry()
@@ -465,9 +521,12 @@ class TestProviderSAML(SeleniumTestCase):
self.driver.get("http://localhost:9009/")
self.login()
self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "header > h1")))
self.wait.until(
ec.presence_of_element_located((By.CSS_SELECTOR, "[data-test-id='card-title']"))
)
self.assertEqual(
self.driver.find_element(By.CSS_SELECTOR, "header > h1").text,
self.driver.find_element(By.CSS_SELECTOR, "[data-test-id='card-title']").text,
"Permission denied",
)
@@ -589,31 +648,42 @@ class TestProviderSAML(SeleniumTestCase):
self.wait_for_url(f"http://{self.host}:9009/")
body = loads(self.driver.find_element(By.CSS_SELECTOR, "pre").text)
body = self.parse_json_content()
snippet = dumps(body, indent=2)[:500].replace("\n", " ")
attrs = body.get("attr", {})
self.assertEqual(
body["attr"]["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"],
attrs.get("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"),
[self.user.name],
f"Claim 'name' mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body["attr"][
"http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"
],
attrs.get("http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"),
[self.user.username],
f"Claim 'windowsaccountname' mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body["attr"]["http://schemas.goauthentik.io/2021/02/saml/username"],
attrs.get("http://schemas.goauthentik.io/2021/02/saml/username"),
[self.user.username],
f"Claim 'username' mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body["attr"]["http://schemas.goauthentik.io/2021/02/saml/uid"],
attrs.get("http://schemas.goauthentik.io/2021/02/saml/uid"),
[str(self.user.pk)],
f"Claim 'uid' mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body["attr"]["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"],
attrs.get("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"),
[self.user.email],
f"Claim 'emailaddress' mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
body["attr"]["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"],
attrs.get("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"),
[self.user.email],
f"Claim 'upn' mismatch at {self.driver.current_url}: {snippet}",
)

View File

@@ -109,11 +109,11 @@ class TestSourceOAuth1(SeleniumTestCase):
wait.until(
ec.presence_of_element_located(
(By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button")
(By.CSS_SELECTOR, "fieldset[name='login-sources'] button")
)
)
identification_stage.find_element(
By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button"
By.CSS_SELECTOR, "fieldset[name='login-sources'] button"
).click()
# Now we should be at the IDP, wait for the login field

View File

@@ -1,6 +1,5 @@
"""test OAuth Source"""
from json import loads
from pathlib import Path
from time import sleep
@@ -16,7 +15,10 @@ from authentik.flows.models import Flow
from authentik.lib.generators import generate_id
from authentik.sources.oauth.models import OAuthSource
from authentik.stages.identification.models import IdentificationStage
from tests.e2e.utils import SeleniumTestCase, retry
from tests.e2e.utils import NoSuchElementException, SeleniumTestCase, TimeoutException, retry
MAX_REFRESH_RETRIES = 5
INTERFACE_TIMEOUT = 10
class TestSourceOAuth2(SeleniumTestCase):
@@ -27,7 +29,7 @@ class TestSourceOAuth2(SeleniumTestCase):
self.slug = generate_id()
super().setUp()
self.run_container(
image="ghcr.io/dexidp/dex:v2.28.1",
image="ghcr.io/dexidp/dex:v2.44.0",
ports={"5556": "5556"},
healthcheck=Healthcheck(
test=["CMD", "wget", "--spider", "http://localhost:5556/dex/healthz"],
@@ -35,6 +37,7 @@ class TestSourceOAuth2(SeleniumTestCase):
start_period=1 * 1_000 * 1_000_000,
),
environment={
"AK_HOST": self.host,
"AK_REDIRECT_URL": self.url(
"authentik_sources_oauth:oauth-client-callback",
source_slug=self.slug,
@@ -48,6 +51,69 @@ class TestSourceOAuth2(SeleniumTestCase):
},
)
def find_settings_tab_panel(self, tab_name: str, panel_content_selector: str):
"""Find a settings tab panel by name"""
url_after_login = self.driver.current_url
user_settings_url = self.if_user_url("/settings")
hash_route = ';%7B"page"%3A"page-' + tab_name + '"%7D'
self.driver.get(user_settings_url + hash_route)
# A refresh is required because the hash change doesn't always trigger a reload.
self.driver.refresh()
try:
self.wait.until(ec.url_contains(user_settings_url))
except TimeoutException:
self.fail(
f"Timed out waiting for user settings page"
f"Initial URL after OAuth linking: {url_after_login} "
f"Current URL: {self.driver.current_url} "
f"Expected URL: {user_settings_url})"
)
try:
self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "ak-interface-user")))
except TimeoutException:
context = self.driver.find_element(By.TAG_NAME, "body")
inner_html = context.get_attribute("innerHTML") or ""
snippet = context.text.strip()[:1000].replace("\n", " ")
self.fail(
f"Timed out waiting for element text to appear at {self.driver.current_url}. "
f"Current content: {snippet or '<empty>'}"
f"{inner_html or '<empty>'}"
)
interface = self.driver.find_element(By.CSS_SELECTOR, "ak-interface-user").shadow_root
interface_wait = WebDriverWait(interface, INTERFACE_TIMEOUT)
try:
interface_wait.until(
ec.presence_of_element_located((By.CSS_SELECTOR, "ak-interface-user-presentation"))
)
except TimeoutException:
snippet = context.text.strip()[:1000].replace("\n", " ")
self.fail(
f"Timed out waiting for element text to appear at {self.driver.current_url}. "
f"Current content: {snippet or '<empty>'}"
)
interface_presentation = interface.find_element(
By.CSS_SELECTOR, "ak-interface-user-presentation"
).shadow_root
user_settings = interface_presentation.find_element(
By.CSS_SELECTOR, "ak-user-settings"
).shadow_root
tab_panel = user_settings.find_element(By.CSS_SELECTOR, panel_content_selector).shadow_root
return tab_panel
def create_objects(self):
"""Create required objects"""
# Bootstrap all needed objects
@@ -60,9 +126,9 @@ class TestSourceOAuth2(SeleniumTestCase):
authentication_flow=authentication_flow,
enrollment_flow=enrollment_flow,
provider_type="openidconnect",
authorization_url="http://127.0.0.1:5556/dex/auth",
access_token_url="http://127.0.0.1:5556/dex/token",
profile_url="http://127.0.0.1:5556/dex/userinfo",
authorization_url=f"http://{self.host}:5556/dex/auth",
access_token_url=f"http://{self.host}:5556/dex/token",
profile_url=f"http://{self.host}:5556/dex/userinfo",
consumer_key="example-app",
consumer_secret=self.client_secret,
)
@@ -70,6 +136,28 @@ class TestSourceOAuth2(SeleniumTestCase):
ident_stage.sources.set([source])
ident_stage.save()
def login_via_oauth_provider(self):
"""Perform login at the OAuth provider (Dex)"""
self.wait.until(ec.presence_of_element_located((By.ID, "login")))
initial_provider_url = self.driver.current_url
self.driver.find_element(By.ID, "login").send_keys("admin@example.com")
self.driver.find_element(By.ID, "password").send_keys("password")
self.driver.find_element(By.ID, "password").send_keys(Keys.ENTER)
self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "button[type=submit]")))
self.driver.find_element(By.CSS_SELECTOR, "button[type=submit]").click()
self.wait.until(ec.url_changes(initial_provider_url))
self.assertNotEqual(
initial_provider_url,
self.driver.current_url,
"Expected to be redirected after login at OAuth provider",
)
@retry()
@apply_blueprint(
"default/flow-default-authentication-flow.yaml",
@@ -91,22 +179,14 @@ class TestSourceOAuth2(SeleniumTestCase):
wait.until(
ec.presence_of_element_located(
(By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button")
(By.CSS_SELECTOR, "fieldset[name='login-sources'] button")
)
)
identification_stage.find_element(
By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button"
By.CSS_SELECTOR, "fieldset[name='login-sources'] button"
).click()
# Now we should be at the IDP, wait for the login field
self.wait.until(ec.presence_of_element_located((By.ID, "login")))
self.driver.find_element(By.ID, "login").send_keys("admin@example.com")
self.driver.find_element(By.ID, "password").send_keys("password")
self.driver.find_element(By.ID, "password").send_keys(Keys.ENTER)
# Wait until we're logged in
self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "button[type=submit]")))
self.driver.find_element(By.CSS_SELECTOR, "button[type=submit]").click()
self.login_via_oauth_provider()
# At this point we've been redirected back
# and we're asked for the username
@@ -135,25 +215,16 @@ class TestSourceOAuth2(SeleniumTestCase):
wait.until(
ec.presence_of_element_located(
(By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button")
(By.CSS_SELECTOR, "fieldset[name='login-sources'] button")
)
)
identification_stage.find_element(
By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button"
By.CSS_SELECTOR, "fieldset[name='login-sources'] button"
).click()
# Now we should be at the IDP, wait for the login field
self.wait.until(ec.presence_of_element_located((By.ID, "login")))
self.driver.find_element(By.ID, "login").send_keys("admin@example.com")
self.driver.find_element(By.ID, "password").send_keys("password")
self.driver.find_element(By.ID, "password").send_keys(Keys.ENTER)
self.login_via_oauth_provider()
# Wait until we're logged in
self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "button[type=submit]")))
self.driver.find_element(By.CSS_SELECTOR, "button[type=submit]").click()
# Wait until we've logged in
self.wait_for_url(self.if_user_url())
self.wait.until(ec.url_matches(self.if_user_url()))
self.assert_user(User(username="foo", name="admin", email="admin@example.com"))
@@ -167,30 +238,93 @@ class TestSourceOAuth2(SeleniumTestCase):
"default/flow-default-source-enrollment.yaml",
"default/flow-default-source-pre-authentication.yaml",
)
def test_oauth_link(self):
"""test OAuth Source link OIDC"""
def test_oauth_link(self) -> None:
"""
Test OAuth Source link OIDC
This test will enroll the user via OAuth, then log in as admin and link the OAuth
source to the admin user.
"""
self.create_objects()
self.driver.get(self.live_server_url)
self.login()
# Ensure that a stable session is created before linking.
sleep(3)
self.driver.get(
self.url("authentik_sources_oauth:oauth-client-login", source_slug=self.slug)
)
# Now we should be at the IDP, wait for the login field
self.wait.until(ec.presence_of_element_located((By.ID, "login")))
self.driver.find_element(By.ID, "login").send_keys("admin@example.com")
self.driver.find_element(By.ID, "password").send_keys("password")
self.driver.find_element(By.ID, "password").send_keys(Keys.ENTER)
self.login_via_oauth_provider()
# Wait until we're logged in
self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "button[type=submit]")))
self.driver.find_element(By.CSS_SELECTOR, "button[type=submit]").click()
post_login_expected_url = self.if_user_url("/settings;page-sources")
self.driver.get(self.url("authentik_api:usersourceconnection-list") + "?format=json")
body_json = loads(self.driver.find_element(By.CSS_SELECTOR, "pre").text)
results = body_json["results"]
self.assertEqual(len(results), 1)
connection = results[0]
self.assertEqual(connection["source_obj"]["slug"], self.slug)
self.assertEqual(connection["user"], self.user.pk)
self.assertEqual(
self.driver.current_url,
post_login_expected_url,
"Expected to be redirected to user settings after linking OAuth source",
)
selector = f"[data-test-id=source-settings-list-item][data-slug='{self.slug}']"
sourceElement = None
for attempt in range(MAX_REFRESH_RETRIES):
source_settings_tab_panel = self.find_settings_tab_panel(
"sources", "ak-user-settings-source"
)
try:
sourceElement = source_settings_tab_panel.find_element(By.CSS_SELECTOR, selector)
except NoSuchElementException:
sourceElement = None
if sourceElement:
break
if attempt < MAX_REFRESH_RETRIES - 1:
self.logger.debug(
f"[Attempt {attempt + 1}/{MAX_REFRESH_RETRIES}] No results yet, sleeping 1s… "
f"(Current URL: {self.driver.current_url})"
)
sleep(1)
if not sourceElement:
context = self.driver.find_element(By.TAG_NAME, "body")
inner_html = context.get_attribute("innerHTML") or ""
snippet = context.text.strip()[:1000].replace("\n", " ")
self.fail(
f"Selector '{selector}' not found at {self.driver.current_url}"
f" after {MAX_REFRESH_RETRIES} retries. "
f"Current content: {snippet or '<empty>'}"
f"{inner_html or '<empty>'}"
)
data_source_component_attribute = sourceElement.get_attribute("data-source-component")
self.assertIsNotNone(
data_source_component_attribute,
f"Source Component not found in source element at {self.driver.current_url}",
)
self.assertEqual(
data_source_component_attribute,
"ak-user-settings-source-oauth",
"Unexpected source component",
)
connection_user_pk_attribute = sourceElement.get_attribute("data-connection-user-pk")
self.assertIsNotNone(
connection_user_pk_attribute,
f"Connection User PK not found in source element at {self.driver.current_url}",
)
self.assertEqual(
int(connection_user_pk_attribute),
self.user.pk,
f"Unexpected user {self.driver.current_url}",
)

View File

@@ -104,6 +104,15 @@ class TestSourceSAML(SeleniumTestCase):
},
)
def login_via_saml_source(self):
"""Perform login at the SAML IDP"""
self.wait.until(ec.presence_of_element_located((By.ID, "username")))
self.driver.find_element(By.ID, "username").send_keys("user1")
self.driver.find_element(By.ID, "password").send_keys("user1pass")
self.driver.find_element(By.ID, "password").send_keys(Keys.ENTER)
self.wait_for_url(self.if_user_url())
@retry()
@apply_blueprint(
"default/flow-default-authentication-flow.yaml",
@@ -149,21 +158,14 @@ class TestSourceSAML(SeleniumTestCase):
wait.until(
ec.presence_of_element_located(
(By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button")
(By.CSS_SELECTOR, "fieldset[name='login-sources'] button")
)
)
identification_stage.find_element(
By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button"
By.CSS_SELECTOR, "fieldset[name='login-sources'] button"
).click()
# Now we should be at the IDP, wait for the username field
self.wait.until(ec.presence_of_element_located((By.ID, "username")))
self.driver.find_element(By.ID, "username").send_keys("user1")
self.driver.find_element(By.ID, "password").send_keys("user1pass")
self.driver.find_element(By.ID, "password").send_keys(Keys.ENTER)
# Wait until we're logged in
self.wait_for_url(self.if_user_url())
self.login_via_saml_source()
self.assert_user(
User.objects.exclude(username="akadmin")
@@ -218,11 +220,11 @@ class TestSourceSAML(SeleniumTestCase):
wait.until(
ec.presence_of_element_located(
(By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button")
(By.CSS_SELECTOR, "fieldset[name='login-sources'] button")
)
)
identification_stage.find_element(
By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button"
By.CSS_SELECTOR, "fieldset[name='login-sources'] button"
).click()
sleep(1)
@@ -231,21 +233,14 @@ class TestSourceSAML(SeleniumTestCase):
self.assertIn(
source.name,
consent_stage.find_element(By.CSS_SELECTOR, "#header-text").text,
consent_stage.find_element(By.CSS_SELECTOR, "[data-test-id='stage-heading']").text,
)
consent_stage.find_element(
By.CSS_SELECTOR,
"[type=submit]",
).click()
# Now we should be at the IDP, wait for the username field
self.wait.until(ec.presence_of_element_located((By.ID, "username")))
self.driver.find_element(By.ID, "username").send_keys("user1")
self.driver.find_element(By.ID, "password").send_keys("user1pass")
self.driver.find_element(By.ID, "password").send_keys(Keys.ENTER)
# Wait until we're logged in
self.wait_for_url(self.if_user_url())
self.login_via_saml_source()
self.assert_user(
User.objects.exclude(username="akadmin")
@@ -300,21 +295,14 @@ class TestSourceSAML(SeleniumTestCase):
wait.until(
ec.presence_of_element_located(
(By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button")
(By.CSS_SELECTOR, "fieldset[name='login-sources'] button")
)
)
identification_stage.find_element(
By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button"
By.CSS_SELECTOR, "fieldset[name='login-sources'] button"
).click()
# Now we should be at the IDP, wait for the username field
self.wait.until(ec.presence_of_element_located((By.ID, "username")))
self.driver.find_element(By.ID, "username").send_keys("user1")
self.driver.find_element(By.ID, "password").send_keys("user1pass")
self.driver.find_element(By.ID, "password").send_keys(Keys.ENTER)
# Wait until we're logged in
self.wait_for_url(self.if_user_url())
self.login_via_saml_source()
self.assert_user(
User.objects.exclude(username="akadmin")
@@ -369,21 +357,14 @@ class TestSourceSAML(SeleniumTestCase):
wait.until(
ec.presence_of_element_located(
(By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button")
(By.CSS_SELECTOR, "fieldset[name='login-sources'] button")
)
)
identification_stage.find_element(
By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button"
By.CSS_SELECTOR, "fieldset[name='login-sources'] button"
).click()
# Now we should be at the IDP, wait for the username field
self.wait.until(ec.presence_of_element_located((By.ID, "username")))
self.driver.find_element(By.ID, "username").send_keys("user1")
self.driver.find_element(By.ID, "password").send_keys("user1pass")
self.driver.find_element(By.ID, "password").send_keys(Keys.ENTER)
# Wait until we're logged in
self.wait_for_url(self.if_user_url())
self.login_via_saml_source()
self.assert_user(
User.objects.exclude(username="akadmin")
@@ -403,21 +384,14 @@ class TestSourceSAML(SeleniumTestCase):
wait.until(
ec.presence_of_element_located(
(By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button")
(By.CSS_SELECTOR, "fieldset[name='login-sources'] button")
)
)
identification_stage.find_element(
By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button"
By.CSS_SELECTOR, "fieldset[name='login-sources'] button"
).click()
# Now we should be at the IDP, wait for the username field
self.wait.until(ec.presence_of_element_located((By.ID, "username")))
self.driver.find_element(By.ID, "username").send_keys("user1")
self.driver.find_element(By.ID, "password").send_keys("user1pass")
self.driver.find_element(By.ID, "password").send_keys(Keys.ENTER)
# Wait until we're logged in
self.wait_for_url(self.if_user_url())
self.login_via_saml_source()
# sleep(999999)
self.assert_user(

View File

@@ -1,9 +1,9 @@
"""authentik e2e testing utilities"""
import json
import socket
from collections.abc import Callable
from functools import lru_cache, wraps
from json import JSONDecodeError, dumps, loads
from os import environ, getenv
from sys import stderr
from time import sleep
@@ -22,7 +22,12 @@ from docker.errors import DockerException
from docker.models.containers import Container
from docker.models.networks import Network
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException, TimeoutException, WebDriverException
from selenium.common.exceptions import (
NoSuchElementException,
NoSuchShadowRootException,
TimeoutException,
WebDriverException,
)
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.remote.command import Command
@@ -40,6 +45,9 @@ from authentik.root.test_runner import get_docker_tag
IS_CI = "CI" in environ
RETRIES = int(environ.get("RETRIES", "3")) if IS_CI else 1
SHADOW_ROOT_RETRIES = 5
JSONType = dict[str, Any] | list[Any] | str | int | float | bool | None
def get_local_ip() -> str:
@@ -206,14 +214,16 @@ class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase):
print("::endgroup::")
self.driver.quit()
def wait_for_url(self, desired_url):
def wait_for_url(self, desired_url: str):
"""Wait until URL is `desired_url`."""
self.wait.until(
lambda driver: driver.current_url == desired_url,
f"URL {self.driver.current_url} doesn't match expected URL {desired_url}",
f"URL {self.driver.current_url} doesn't match expected URL {desired_url}. "
f"HTML: {self.driver.page_source[:1000]}",
)
def url(self, view, query: dict | None = None, **kwargs) -> str:
def url(self, view: str, query: dict | None = None, **kwargs) -> str:
"""reverse `view` with `**kwargs` into full URL using live_server_url"""
url = self.live_server_url + reverse(view, kwargs=kwargs)
if query:
@@ -227,14 +237,112 @@ class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase):
return f"{url}#{path}"
return url
def parse_json_content(
self, context: WebElement | None = None, timeout: float | None = 10
) -> JSONType:
"""
Parse JSON from a Selenium element's text content.
If `context` is not provided, defaults to the <body> element.
Raises a clear test failure if the element isn't found, the text doesn't appear
within `timeout` seconds, or the text is not valid JSON.
"""
try:
if context is None:
context = self.driver.find_element(By.TAG_NAME, "body")
except NoSuchElementException:
self.fail(
f"No element found (defaulted to <body>). Current URL: {self.driver.current_url}"
)
wait_timeout = timeout or self.wait_timeout
wait = WebDriverWait(context, wait_timeout)
try:
wait.until(lambda d: len(d.text.strip()) != 0)
except TimeoutException:
snippet = context.text.strip()[:500].replace("\n", " ")
self.fail(
f"Timed out waiting for element text to appear at {self.driver.current_url}. "
f"Current content: {snippet or '<empty>'}"
)
body_text = context.text.strip()
inner_html = context.get_attribute("innerHTML") or ""
if "redirecting" in inner_html.lower():
try:
wait.until(lambda d: "redirecting" not in d.get_attribute("innerHTML").lower())
except TimeoutException:
snippet = context.text.strip()[:500].replace("\n", " ")
inner_html = context.get_attribute("innerHTML") or ""
self.fail(
f"Timed out waiting for redirect to finish at {self.driver.current_url}. "
f"Current content: {snippet or '<empty>'}"
f"{inner_html or '<empty>'}"
)
inner_html = context.get_attribute("innerHTML") or ""
body_text = context.text.strip()
snippet = body_text[:500].replace("\n", " ")
if not body_text.startswith("{") and not body_text.startswith("["):
self.fail(
f"Expected JSON content but got non-JSON text at {self.driver.current_url}: "
f"{snippet or '<empty>'}"
f"{inner_html or '<empty>'}"
)
try:
body_json = loads(body_text)
except JSONDecodeError as e:
self.fail(
f"Expected JSON but got invalid content at {self.driver.current_url}: "
f"{snippet or '<empty>'}"
f"{inner_html or '<empty>'}"
f"(JSON error: {e})"
)
return body_json
def get_shadow_root(
self, selector: str, container: WebElement | WebDriver | None = None
self, selector: str, container: WebElement | WebDriver | None = None, timeout: float = 10
) -> WebElement:
"""Get shadow root element's inner shadowRoot"""
"""Get the shadow root of a web component specified by `selector`."""
if not container:
container = self.driver
el = container.find_element(By.CSS_SELECTOR, selector)
return el.shadow_root
wait = WebDriverWait(container, timeout)
host: WebElement | None = None
try:
host = wait.until(lambda c: c.find_element(By.CSS_SELECTOR, selector))
except TimeoutException:
self.fail(f"Timed out waiting for shadow host {selector} to appear")
attempts = 0
while attempts < SHADOW_ROOT_RETRIES:
try:
return host.shadow_root
except NoSuchShadowRootException:
attempts += 1
sleep(0.2)
# re-find host in case it was re-attached
try:
host = container.find_element(By.CSS_SELECTOR, selector)
except NoSuchElementException:
# loop and retry finding host
pass
inner_html = host.get_attribute("innerHTML") or "<no host>"
raise RuntimeError(
f"Failed to obtain shadow root for {selector} after {attempts} attempts. "
f"Host innerHTML: {inner_html}"
)
def shady_dom(self) -> WebElement:
class wrapper:
@@ -249,7 +357,7 @@ class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase):
return wrapper(self.driver)
def login(self, shadow_dom=True):
"""Do entire login flow"""
"""Perform the entire authentik login flow."""
if shadow_dom:
flow_executor = self.get_shadow_root("ak-flow-executor")
@@ -287,13 +395,42 @@ class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase):
def assert_user(self, expected_user: User):
"""Check users/me API and assert it matches expected_user"""
self.driver.get(self.url("authentik_api:user-me") + "?format=json")
user_json = self.driver.find_element(By.CSS_SELECTOR, "pre").text
user = UserSerializer(data=json.loads(user_json)["user"])
expected_url = self.url("authentik_api:user-me") + "?format=json"
self.driver.get(expected_url)
self.wait.until(lambda d: d.current_url == expected_url)
user_json = self.parse_json_content()
data = user_json.get("user")
snippet = dumps(user_json, indent=2)[:500].replace("\n", " ")
self.assertIsNotNone(
data,
f"Missing 'user' key in response at {self.driver.current_url}: {snippet}",
)
user = UserSerializer(data=data)
user.is_valid()
self.assertEqual(user["username"].value, expected_user.username)
self.assertEqual(user["name"].value, expected_user.name)
self.assertEqual(user["email"].value, expected_user.email)
self.assertEqual(
user["username"].value,
expected_user.username,
f"Username mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
user["name"].value,
expected_user.name,
f"Name mismatch at {self.driver.current_url}: {snippet}",
)
self.assertEqual(
user["email"].value,
expected_user.email,
f"Email mismatch at {self.driver.current_url}: {snippet}",
)
@lru_cache

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