Compare commits

...

109 Commits

Author SHA1 Message Date
e3f2ed0436 web: Prep for hash-less routing. 2025-04-03 15:19:19 +02:00
a5bb22a66a web: Move build observer. Prep. 2025-04-03 06:12:51 +02:00
ec49b2e0e0 website/integrations: calibre-web: document (#12477)
* website/integrations: calibre-web: add to sidebar

Adds the calibre-web integration to the sidebar.

Signed-off-by: 4d62 <github-user@sdko.org>

* website/integrations: calibre-web: init

Initializes the documentation with the placeholder. I have a feeling this is going to be funnnnnnnnnnnnnnnnn

Signed-off-by: 4d62 <github-user@sdko.org>

* website/integrations: calibre-web: service configuration

Adds configuration documentation for calibre-web

PS: Never setup a LDAP outpost before and I don't have calibre web so uhhh yea im gonna take care of this after the holidays (probably)

Signed-off-by: 4d62 <github-user@sdko.org>

* Update index.md

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

* Changed proider pair instructions to new version

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

* Update website/integrations/services/calibre-web/index.md

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

---------

Signed-off-by: 4d62 <github-user@sdko.org>
Signed-off-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>
Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
2025-04-02 12:57:53 -05:00
22ebe05706 website: bump image-size from 1.1.1 to 1.2.1 in /website (#13750)
Bumps [image-size](https://github.com/image-size/image-size) from 1.1.1 to 1.2.1.
- [Release notes](https://github.com/image-size/image-size/releases)
- [Commits](https://github.com/image-size/image-size/compare/v1.1.1...v1.2.1)

---
updated-dependencies:
- dependency-name: image-size
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-02 18:41:59 +02:00
f0e58a6f49 website/docs: sys-mgmt: service accounts (#13722)
* website/docs: ops: service accounts

* Update website/docs/sys-mgmt/service-accounts.md

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dominic R <dominic@sdko.org>

* Update website/docs/sys-mgmt/service-accounts.md

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dominic R <dominic@sdko.org>

* Update website/docs/sys-mgmt/service-accounts.md

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dominic R <dominic@sdko.org>

* Update website/docs/sys-mgmt/service-accounts.md

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dominic R <dominic@sdko.org>

* Update website/docs/sys-mgmt/service-accounts.md

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dominic R <dominic@sdko.org>

* Update website/docs/sys-mgmt/service-accounts.md

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dominic R <dominic@sdko.org>

* Update website/docs/sys-mgmt/service-accounts.md

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dominic R <dominic@sdko.org>

* Update website/docs/sys-mgmt/service-accounts.md

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dominic R <dominic@sdko.org>

* Update website/docs/sys-mgmt/service-accounts.md

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dominic R <dominic@sdko.org>

* Update website/docs/sys-mgmt/service-accounts.md

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dominic R <dominic@sdko.org>

* Dewi's suggestions

---------

Signed-off-by: Dominic R <dominic@sdko.org>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
2025-04-01 16:37:11 -05:00
Ben
a3d642c08e website/integrations: add mailcow (#13727)
* Add mailcow to Applications

* Update wording and layout

* Update website/integrations/services/mailcow/index.md

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Ben <bmfk_m@yahoo.de>

* Update website/integrations/services/mailcow/index.md

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Ben <bmfk_m@yahoo.de>

* Update website/integrations/services/mailcow/index.md

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Ben <bmfk_m@yahoo.de>

* lint

---------

Signed-off-by: Ben <bmfk_m@yahoo.de>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
2025-04-01 15:09:31 -05:00
5d42cb9185 website: edit menu items (#13747)
for review

Co-authored-by: Tana M Berry <tana@goauthentik.com>
2025-04-01 15:00:18 -05:00
1fd0cc5bb5 website/integrations: slack,pocketbase,tandoor: convert to new authentik configuration format (#13742)
* website/integrations-all: update authentik configuration template

* website/integrations: slack,pocketbase,tandoor: convert to new authentik configuration format

* Revert "website/integrations-all: update authentik configuration template"

Not for this PR. Don't want to cause merge conflicts later on.

This reverts commit 8378502090.
2025-04-01 13:31:07 -05:00
deef365ff5 website/integrations-all: update authentik configuration template (#13740) 2025-04-01 11:51:31 -05:00
d1ae6287f2 web/admin: fix custom scope mappings being selected by default in proxy provider (#13735)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-04-01 18:35:35 +02:00
2e152cd264 web: bump vite from 5.4.15 to 5.4.16 in /web (#13743)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.4.15 to 5.4.16.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v5.4.16/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.4.16/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 5.4.16
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-01 18:29:16 +02:00
f5941e403b translate: Updates for file locale/en/LC_MESSAGES/django.po in zh_CN (#13736)
Translate locale/en/LC_MESSAGES/django.po in zh_CN

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-04-01 18:18:59 +02:00
ff3cf8c10e core: bump goauthentik.io/api/v3 from 3.2025023.1 to 3.2025023.2 (#13746)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2025023.1 to 3.2025023.2.
- [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.2025023.1...v3.2025023.2)

---
updated-dependencies:
- dependency-name: goauthentik.io/api/v3
  dependency-version: 3.2025023.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-04-01 18:18:42 +02:00
bfa6328172 web/common: utils: fix infinite value handling in getRelativeTime function (#13564)
Squash sdko/closes-13562
2025-04-01 06:46:29 -07:00
4c9691c932 stages/authenticator_webauthn: Update FIDO MDS3 & Passkey aaguid blobs (#13744)
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-04-01 12:58:43 +02:00
a0f1566b4c web: bump API Client version (#13741)
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-04-01 02:15:47 +02:00
46261a4f42 */saml: allow for domainless SAML URLs (#13737) 2025-04-01 01:41:18 +02:00
8b42ff1e97 core: fix error when viewing used_by for built-in source (#13588)
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-03-31 16:36:14 +00:00
ca4cb0d251 translate: Updates for file locale/en/LC_MESSAGES/django.po in fr (#13738)
* Translate locale/en/LC_MESSAGES/django.po in fr

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

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

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

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-03-31 15:54:37 +00:00
a5a0fa79dd website/docs: style guide (#13704)
* new word choices, tweaks

* shockingly, a typo

* tweaks

* Update website/docs/developer-docs/docs/style-guide.mdx

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>

---------

Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: Tana M Berry <tana@goauthentik.com>
Co-authored-by: Dominic R <dominic@sdko.org>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2025-03-31 07:57:03 -05:00
c06a871f61 core: fix double slash in cache key (#13721) 2025-03-31 12:58:03 +02:00
4a3df67134 core, web: update translations (#13728)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: rissson <18313093+rissson@users.noreply.github.com>
2025-03-31 12:57:16 +02:00
422ccf61fa core: bump goauthentik.io/api/v3 from 3.2025022.6 to 3.2025023.1 (#13729)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2025022.6 to 3.2025023.1.
- [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.2025022.6...v3.2025023.1)

---
updated-dependencies:
- dependency-name: goauthentik.io/api/v3
  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-03-31 12:27:56 +02:00
d989f23907 website: bump the build group in /website with 3 updates (#13730)
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.2.8 to 1.3.0
- [Release notes](https://github.com/web-infra-dev/rspack/releases)
- [Commits](https://github.com/web-infra-dev/rspack/commits/v1.3.0/packages/rspack)

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

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

---
updated-dependencies:
- dependency-name: "@rspack/binding-darwin-arm64"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: build
- dependency-name: "@rspack/binding-linux-arm64-gnu"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: build
- dependency-name: "@rspack/binding-linux-x64-gnu"
  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-03-31 12:27:44 +02:00
059180edef core: bump astral-sh/uv from 0.6.10 to 0.6.11 (#13733)
Bumps [astral-sh/uv](https://github.com/astral-sh/uv) from 0.6.10 to 0.6.11.
- [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.6.10...0.6.11)

---
updated-dependencies:
- dependency-name: astral-sh/uv
  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-03-31 12:27:18 +02:00
22f30634a8 website/docs: Fix Caddy forward auth example (#13726) 2025-03-30 20:28:11 +02:00
35ff418c42 policies: buffered policy access view for concurrent authorization attempts when unauthenticated (#13629)
* policies: buffered policy access view for concurrent authorization attempts when unauthenticated

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

* better cleanup

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

* more polish

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

* more cleanup

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

* add tests

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

* fix multiple redirects, add e2e test

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

* unrelated: add sp initiated post test

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

* add SAML parallel test

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

* format

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

* optimise detection of when authentication is in progress

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

* better backoff timing

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-30 19:56:03 +02:00
7826e7a605 core: bump oss/go/microsoft/golang from 1.23-fips-bookworm to 1.24-fips-bookworm (#13027)
* core: bump oss/go/microsoft/golang

Bumps oss/go/microsoft/golang from 1.23-fips-bookworm to 1.24-fips-bookworm.

---
updated-dependencies:
- dependency-name: oss/go/microsoft/golang
  dependency-type: direct:production
...

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

* upstream docker image, use native fips

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

* bump go version

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-03-30 03:26:30 +02:00
64f1b8207d web: bump tar-fs from 2.1.1 to 2.1.2 in /web (#13713)
Bumps [tar-fs](https://github.com/mafintosh/tar-fs) from 2.1.1 to 2.1.2.
- [Commits](https://github.com/mafintosh/tar-fs/compare/v2.1.1...v2.1.2)

---
updated-dependencies:
- dependency-name: tar-fs
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-29 00:51:08 +01:00
b2c13f0614 core: fix flaky tests introduced with is_superuser API fix (#13709)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-28 22:14:15 +01:00
6965628020 root: bump python patch version to 3.12.9 (#13710)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-28 22:13:34 +01:00
608f63e9a2 website/docs: add reference to setting in CVE (#13707)
* website/docs: add reference to setting in CVE

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

* reword

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-28 17:42:45 +01:00
22fa3a7fba web: bump API Client version (#13708)
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-03-28 17:42:24 +01:00
bcfd6fefa7 release: 2025.2.3 (#13705)
* release: 2025.2.3

* fix uv lock not being bumped

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-28 17:08:57 +01:00
eae18d0016 website/docs: fix 2025 CVE category title (#13703)
* website/docs: fix 2025 CVE category title

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

* add sideeffect of changing session backend

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-28 14:55:56 +01:00
4a12a57c5f website/docs: update release notes for 2024.12 and 2025.2 (#13702)
* website/docs: update release notes for 2025.2 and 2024.12

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

* update

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

* update v2

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-03-28 14:49:35 +01:00
71294b7deb security: fix CVE-2025-29928 (#13695)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-28 14:20:09 +01:00
5af907db0c stages/identification: refresh captcha on failure (#13697)
* refactor cleanup behavior after stage form submit

* refresh captcha on failing Identification stage

* Revert "stages/identification: check captcha after checking authentication (#13533)"

This reverts commit b7beac6795.

Including a Captcha stage in an Identification stage is partially to
prevent password spraying attacks. The reverted commit negated this
feature to fix a UX bug. After 6fde42a9170, the functionality can now be
reinstated.

---------

Co-authored-by: Simonyi Gergő <gergo@goauthentik.io>
2025-03-28 14:16:13 +01:00
63a118a2ba core: fix non-exploitable open redirect (#13696)
discovered by @dominic-r

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-28 14:15:39 +01:00
d9a3c34a44 core: fix core/user is_superuser filter (#13693)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-28 14:00:13 +01:00
23bdad7574 website: bump @types/semver from 7.5.8 to 7.7.0 in /website (#13682)
Bumps [@types/semver](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/semver) from 7.5.8 to 7.7.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/semver)

---
updated-dependencies:
- dependency-name: "@types/semver"
  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-03-28 13:21:30 +01:00
8ee90826fc enterprise/stages/source: set is_redirected in flow source stage redirects to (#13604)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-27 19:07:36 +01:00
8c7d4d2f5e website/docs: Clarify frontend development. Document local overrides. (#13586)
* website/docs: Clarify setup flow. Document local overrides.

* Update website/docs/developer-docs/setup/frontend-dev-environment.md

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>

* Update website/docs/developer-docs/setup/frontend-dev-environment.md

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>

* Update website/docs/developer-docs/setup/frontend-dev-environment.md

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>

* Update website/docs/developer-docs/setup/frontend-dev-environment.md

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>

* Update website/docs/developer-docs/setup/frontend-dev-environment.md

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>

* Update website/docs/developer-docs/setup/frontend-dev-environment.md

Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>

* Update website/docs/developer-docs/setup/full-dev-environment.mdx

Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>

* Update website/docs/install-config/install/docker-compose.mdx

Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>

* Update website/docs/developer-docs/setup/frontend-dev-environment.md

Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>

* Update website/docs/developer-docs/setup/full-dev-environment.mdx

Signed-off-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>

* Update authentik/lib/default.yml

Signed-off-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>

* fix linting to please the ci check

---------

Signed-off-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: Dominic R <dominic@sdko.org>
Co-authored-by: Marcelo Elizeche Landó <marcelo@goauthentik.io>
2025-03-27 11:49:16 -05:00
d72def0368 web/admin: add sync status refresh button (#13678)
* web/admin: add refresh button to sync status card

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

* auto-expand if there's just one task

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-27 00:06:12 +01:00
5bcf501842 outposts/ldap: fix paginator going into infinite loop (#13677)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-27 00:05:43 +01:00
13fc216c68 website/integrations-all: convert authentik configuration to wizard (#13144)
* init

* 6 more

* tana...

* quick reformat

* welp only time for one change

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* Revert "wip"

This reverts commit e71f0d22e3f093350e8d12eaad5e5c0f9d38253c.

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* a
2025-03-26 16:38:57 -05:00
27aed4b315 web: ensure wizard modal closes on first cancel click (#13636)
The application wizard modal previously required two clicks of the cancel
button to close when opened from the User Interface.
This was caused by improper event handling where events
would propagate up the DOM tree potentially triggering multiple handlers.
2025-03-26 18:16:46 +01:00
84b5992e55 ci: bump golangci/golangci-lint-action from 6 to 7 (#13661)
* ci: bump golangci/golangci-lint-action from 6 to 7

Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 6 to 7.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v6...v7)

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

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

* fix lint

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

* fix v2

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

* fix v3

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-03-26 18:03:20 +01:00
7eb985f636 website: bump the build group in /website with 3 updates (#13660)
Bumps the build group in /website with 3 updates: [@swc/core-darwin-arm64](https://github.com/swc-project/swc), [@swc/core-linux-arm64-gnu](https://github.com/swc-project/swc) and [@swc/core-linux-x64-gnu](https://github.com/swc-project/swc).


Updates `@swc/core-darwin-arm64` from 1.11.12 to 1.11.13
- [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.11.12...v1.11.13)

Updates `@swc/core-linux-arm64-gnu` from 1.11.12 to 1.11.13
- [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.11.12...v1.11.13)

Updates `@swc/core-linux-x64-gnu` from 1.11.12 to 1.11.13
- [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.11.12...v1.11.13)

---
updated-dependencies:
- dependency-name: "@swc/core-darwin-arm64"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@swc/core-linux-arm64-gnu"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@swc/core-linux-x64-gnu"
  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-03-26 17:05:42 +01:00
d3172ae904 web: bump vite from 5.4.14 to 5.4.15 in /web (#13672)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.4.14 to 5.4.15.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v5.4.15/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.4.15/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-26 14:22:25 +01:00
88662b54c1 core: bump astral-sh/uv from 0.6.9 to 0.6.10 (#13669)
Bumps [astral-sh/uv](https://github.com/astral-sh/uv) from 0.6.9 to 0.6.10.
- [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.6.9...0.6.10)

---
updated-dependencies:
- dependency-name: astral-sh/uv
  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-03-26 12:01:43 +01:00
b38bc8c1c4 lifecycle/aws: bump aws-cdk from 2.1005.0 to 2.1006.0 in /lifecycle/aws (#13670)
Bumps [aws-cdk](https://github.com/aws/aws-cdk-cli/tree/HEAD/packages/aws-cdk) from 2.1005.0 to 2.1006.0.
- [Release notes](https://github.com/aws/aws-cdk-cli/releases)
- [Commits](https://github.com/aws/aws-cdk-cli/commits/aws-cdk@v2.1006.0/packages/aws-cdk)

---
updated-dependencies:
- dependency-name: aws-cdk
  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-03-26 12:00:06 +01:00
a9b648842a website/docs: Flesh out integrations copy changes. (#13619)
* website/docs: Flesh out integrations copy changes.

* Apply suggestions from code review

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>

* Lint.

* Revert removed section. Fix links.

* reorder integrations page sections

Signed-off-by: Fletcher Heisler <fheisler@users.noreply.github.com>

* add back page title

Signed-off-by: Fletcher Heisler <fheisler@users.noreply.github.com>

* move cards to very end of topic

* fix broken anchor link

---------

Signed-off-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
Signed-off-by: Fletcher Heisler <fheisler@users.noreply.github.com>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: Fletcher Heisler <fheisler@users.noreply.github.com>
Co-authored-by: Tana M Berry <tana@goauthentik.com>
2025-03-25 12:40:21 -05:00
5fda531e2b website/docs: add section on how to capture logs (#13662)
* Added logs file with basic instructions for capturing logs

* Included kubernetes instructions

* Fixed typos

* Fixed commands

* typo

* Updated kubernetes section

* updated as per suggestions from Dominic

* further changes to simplify the document

* Added section about Ctrl + C to stop logs

---------

Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
2025-03-25 12:28:57 -05:00
921a3e6eb8 website/docs: Add Fleet integration. (#13618)
* website/docs: Add Fleet integration.

* Apply suggestions from code review

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>

* Update website/integrations/services/fleet/index.md

Signed-off-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>

* Update index.md

Signed-off-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>

* website/docs: Reorder.

---------

Signed-off-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
2025-03-25 11:54:16 -05:00
fd898bea66 stages/email: Clean newline characters in TemplateEmailMessage (#13666)
* Clean new line characters in TemplateEmailMessage

* Use blankspace replace in names

* Use blankspace replace in names
2025-03-25 12:39:29 -04:00
cbf9ee55ae root: new issue template for Docs (#13659)
* new issue template for Docs

* added note about a PR

* Update .github/ISSUE_TEMPLATE/docs_issue.md

Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>

* Update .github/ISSUE_TEMPLATE/docs_issue.md

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

---------

Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
Co-authored-by: Tana M Berry <tana@goauthentik.com>
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-03-25 11:38:17 -05:00
590ee7d9d4 core, web: update translations (#13658)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: rissson <18313093+rissson@users.noreply.github.com>
2025-03-25 12:47:51 +01:00
b8cd1d1ae2 website/docs: fix referral of Paperless-ng (#13657)
Original description referred to Paperless-ngx as being a fork of Paperless-ngx instead of Paperless-ng (without x).

Signed-off-by: joeftiger <j.oeftiger@protonmail.com>
2025-03-24 18:44:08 -05:00
9f9524fbcb ci: stop publishing latest tag (#13245)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-24 20:23:55 +00:00
1df87cdf77 root: fix dependency install due to description-file (#13655)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-24 20:18:18 +00:00
6383550914 admin: fix system API when using bearer token (#13651)
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-03-24 14:42:20 +00:00
10771b4779 website: bump the build group in /website with 6 updates (#13645)
Bumps the build group in /website with 6 updates:

| Package | From | To |
| --- | --- | --- |
| [@swc/core-darwin-arm64](https://github.com/swc-project/swc) | `1.11.11` | `1.11.12` |
| [@swc/core-linux-arm64-gnu](https://github.com/swc-project/swc) | `1.11.11` | `1.11.12` |
| [@swc/core-linux-x64-gnu](https://github.com/swc-project/swc) | `1.11.11` | `1.11.12` |
| [@swc/html-darwin-arm64](https://github.com/swc-project/swc) | `1.11.11` | `1.11.13` |
| [@swc/html-linux-arm64-gnu](https://github.com/swc-project/swc) | `1.11.11` | `1.11.13` |
| [@swc/html-linux-x64-gnu](https://github.com/swc-project/swc) | `1.11.11` | `1.11.13` |


Updates `@swc/core-darwin-arm64` from 1.11.11 to 1.11.12
- [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.11.11...v1.11.12)

Updates `@swc/core-linux-arm64-gnu` from 1.11.11 to 1.11.12
- [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.11.11...v1.11.12)

Updates `@swc/core-linux-x64-gnu` from 1.11.11 to 1.11.12
- [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.11.11...v1.11.12)

Updates `@swc/html-darwin-arm64` from 1.11.11 to 1.11.13
- [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.11.11...v1.11.13)

Updates `@swc/html-linux-arm64-gnu` from 1.11.11 to 1.11.13
- [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.11.11...v1.11.13)

Updates `@swc/html-linux-x64-gnu` from 1.11.11 to 1.11.13
- [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.11.11...v1.11.13)

---
updated-dependencies:
- dependency-name: "@swc/core-darwin-arm64"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@swc/core-linux-arm64-gnu"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@swc/core-linux-x64-gnu"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@swc/html-darwin-arm64"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@swc/html-linux-arm64-gnu"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@swc/html-linux-x64-gnu"
  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-03-24 14:41:38 +00:00
fcaf1193ed core: bump goauthentik.io/api/v3 from 3.2025022.5 to 3.2025022.6 (#13646)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2025022.5 to 3.2025022.6.
- [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.2025022.5...v3.2025022.6)

---
updated-dependencies:
- dependency-name: goauthentik.io/api/v3
  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-03-24 14:41:27 +00:00
b9f6093e6f translate: Updates for file web/xliff/en.xlf in fr (#13653)
Translate web/xliff/en.xlf in fr

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-03-24 13:52:37 +00:00
47f6d59758 translate: Updates for file locale/en/LC_MESSAGES/django.po in fr (#13652)
Translate locale/en/LC_MESSAGES/django.po in fr

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-03-24 13:52:12 +00:00
59d20e3bc0 website/integrations: add tandoor (#13560)
* website/integrations: add tandoor

* Update website/integrations/services/tandoor/index.md

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <chwshka@outlook.com>

* Update website/integrations/services/tandoor/index.md

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <chwshka@outlook.com>

* Update website/integrations/services/tandoor/index.md

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <chwshka@outlook.com>

* Update website/integrations/services/tandoor/index.md

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <chwshka@outlook.com>

* Updated to match recommendations

* Update website/integrations/services/tandoor/index.md

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <chwshka@outlook.com>

* Further updates to match suggestions

* Updated to use <kbd> and <em> tags

* fixed codeblock

* Added explanatory text and removed extra EM tags

* fixed prettier issue

* moved ` to line 52

---------

Signed-off-by: Dewi Roberts <chwshka@outlook.com>
Co-authored-by: Dominic R <dominic@sdko.org>
2025-03-24 07:55:34 -05:00
ae347cd1c5 core, web: update translations (#13642)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: rissson <18313093+rissson@users.noreply.github.com>
2025-03-24 11:08:44 +01:00
7653a35caa providers/scim: fix group membership check failing (#13644)
closes #12917

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-24 02:51:04 +00:00
dc9b12fd37 ci: add semgrep (#13643)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-24 00:40:41 +00:00
b7dac0674a flows: fix API not returning configured background (#13641)
* flows: fix API not returning configured background

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

* add test

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-24 00:10:45 +00:00
5a17dea765 translate: Updates for file locale/en/LC_MESSAGES/django.po in zh_CN (#13631)
Translate locale/en/LC_MESSAGES/django.po in zh_CN

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-03-23 20:08:15 +00:00
044547c316 translate: Updates for file locale/en/LC_MESSAGES/django.po in zh-Hans (#13633)
Translate django.po in zh-Hans

100% translated source file: 'django.po'
on 'zh-Hans'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-03-23 20:07:53 +00:00
6a84e7e6b0 translate: Updates for file web/xliff/en.xlf in zh_CN (#13632)
Translate web/xliff/en.xlf in zh_CN

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-03-23 20:07:51 +00:00
6d4bb77960 translate: Updates for file web/xliff/en.xlf in zh-Hans (#13634)
Translate web/xliff/en.xlf in zh-Hans

100% translated source file: 'web/xliff/en.xlf'
on 'zh-Hans'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-03-23 20:07:37 +00:00
1b588b98bc brands: fix migration 0008 by removing incorrect context manager usage (#13635)
core: brands: fix migration 0008
2025-03-23 19:42:33 +00:00
3eccef88aa web: Fix prop. (#13630) 2025-03-22 17:18:07 +01:00
8f50dfa0c5 core, web: update translations (#13628)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: rissson <18313093+rissson@users.noreply.github.com>
2025-03-22 13:28:36 +00:00
8417d8508f web/admin: reworked sync status card (#13625)
* reworked sync status

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

* update imports

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

* add story and fix import

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-03-21 22:48:28 +00:00
b2c2fc001b core: bump github.com/golang-jwt/jwt/v5 from 5.2.1 to 5.2.2 (#13626)
Bumps [github.com/golang-jwt/jwt/v5](https://github.com/golang-jwt/jwt) from 5.2.1 to 5.2.2.
- [Release notes](https://github.com/golang-jwt/jwt/releases)
- [Changelog](https://github.com/golang-jwt/jwt/blob/main/VERSION_HISTORY.md)
- [Commits](https://github.com/golang-jwt/jwt/compare/v5.2.1...v5.2.2)

---
updated-dependencies:
- dependency-name: github.com/golang-jwt/jwt/v5
  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-03-21 22:48:16 +00:00
f60312cbbc translate: Updates for file web/xliff/en.xlf in zh-Hans (#13622)
Translate web/xliff/en.xlf in zh-Hans

100% translated source file: 'web/xliff/en.xlf'
on 'zh-Hans'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-03-21 20:16:25 +00:00
7614b17a05 web: bump API Client version (#13623)
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-03-21 20:16:17 +00:00
8947376edb translate: Updates for file web/xliff/en.xlf in zh_CN (#13621)
Translate web/xliff/en.xlf in zh_CN

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-03-21 20:16:12 +00:00
ce23209ae8 events: add configurable headers to webhooks (#13602)
* events: add configurable headers to webhooks

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

* make it a full thing

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

* fix migration

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-21 19:37:15 +00:00
0b806b7130 web: Client-side MDX rendering (#13610)
* web: Allow build errors to propagate.

* web: Refactor MDX for client-side rendering.

* Remove override

Signed-off-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>

* revert css for links and tables

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

* web: Move Markdown specific styles.

---------

Signed-off-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2025-03-21 19:32:52 +00:00
9538cf4690 website/docs: revert token_expiry format in example blueprint (#13582)
* revert token_expiry format in example blueprint

Signed-off-by: Marcelo Elizeche Landó <marcelo@goauthentik.io>

* Revert blueprint change, use hard link to the previous version in the docs

---------

Signed-off-by: Marcelo Elizeche Landó <marcelo@goauthentik.io>
2025-03-21 19:13:35 +01:00
63da458fb3 website: update header to match goauthentik.io (#13616)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-21 17:33:44 +00:00
873dab29a9 translate: Updates for file web/xliff/en.xlf in fr (#13615)
Translate web/xliff/en.xlf in fr

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-03-21 16:28:58 +00:00
1e96c80593 core, web: update translations (#13608)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: rissson <18313093+rissson@users.noreply.github.com>
2025-03-21 17:14:08 +01:00
ee4a922234 core: bump astral-sh/uv from 0.6.8 to 0.6.9 (#13612)
Bumps [astral-sh/uv](https://github.com/astral-sh/uv) from 0.6.8 to 0.6.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.6.8...0.6.9)

---
updated-dependencies:
- dependency-name: astral-sh/uv
  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-03-21 17:11:54 +01:00
37a2eff716 website: components: add multilinecodeblock component (#13179)
* wip

* wip

* wip

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

wip

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

wip

* wip

* wip

* move css to same folder

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2025-03-21 10:08:24 -05:00
50e2f1c474 website/docs: add clarification about formatting URLs to Style Guide (#13601)
* a few additions

* codespell fixes??

* clarify URL formatting

* tweak

* Update website/docs/developer-docs/docs/style-guide.mdx

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>

* Update website/docs/developer-docs/docs/style-guide.mdx

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>

* conflict of course

* bump build

---------

Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: Tana M Berry <tana@goauthentik.com>
Co-authored-by: Dominic R <dominic@sdko.org>
2025-03-20 20:10:34 -05:00
ab7338b50e core: bump github.com/redis/go-redis/v9 from 9.7.1 to 9.7.3 (#13603)
Bumps [github.com/redis/go-redis/v9](https://github.com/redis/go-redis) from 9.7.1 to 9.7.3.
- [Release notes](https://github.com/redis/go-redis/releases)
- [Changelog](https://github.com/redis/go-redis/blob/master/CHANGELOG.md)
- [Commits](https://github.com/redis/go-redis/compare/v9.7.1...v9.7.3)

---
updated-dependencies:
- dependency-name: github.com/redis/go-redis/v9
  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-03-20 19:41:12 +00:00
bcdc6fcd36 lib: set a default HTTP timeout on outgoing requests (#13599)
* lib: set a default HTTP timeout on outgoing requests

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

* add config

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-20 16:44:12 +00:00
98c3e0d68b website/integrations: add miniflux (#13559)
* website/integrations: add miniflux

* fixes authentik capitalisation

* Updated to match suggestions

* Update website/integrations/services/miniflux/index.md

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <chwshka@outlook.com>

* Updated to include <kbd> and <em> tags

* fixed codeblock

* Added explanatory text and removed extra EM tags

* fixed prettier issue

* Update website/integrations/services/miniflux/index.md

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dewi Roberts <chwshka@outlook.com>

* Update website/integrations/services/miniflux/index.md

Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>

* Update website/integrations/services/miniflux/index.md

Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>

---------

Signed-off-by: Dewi Roberts <chwshka@outlook.com>
Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: Dominic R <dominic@sdko.org>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
2025-03-20 08:52:49 -05:00
a2b82b6448 web: bump API Client version (#13585)
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-03-20 13:08:26 +00:00
0456ace646 core: bump goauthentik.io/api/v3 from 3.2025022.3 to 3.2025022.5 (#13594)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2025022.3 to 3.2025022.5.
- [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.2025022.3...v3.2025022.5)

---
updated-dependencies:
- dependency-name: goauthentik.io/api/v3
  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-03-20 13:08:13 +00:00
d3a11ce810 website/docs: style guide & integration template: revamp (#12929)
* website/docs: style guide: revamp

* fix anchor

* Update style-guide.mdx

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dominic R <dominic@sdko.org>

* Update style-guide.mdx

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dominic R <dominic@sdko.org>

* Update style-guide.mdx

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dominic R <dominic@sdko.org>

* Update website/docs/developer-docs/docs/style-guide.mdx

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dominic R <dominic@sdko.org>

* Update website/docs/developer-docs/docs/style-guide.mdx

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dominic R <dominic@sdko.org>

* Update website/docs/developer-docs/docs/style-guide.mdx

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dominic R <dominic@sdko.org>

* Update website/docs/developer-docs/docs/style-guide.mdx

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dominic R <dominic@sdko.org>

* Tana's suggested format 

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

* lint

* wip

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

* wip

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

* wip

lint

* Update website/docs/developer-docs/docs/style-guide.mdx

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

* tana

* fix

---------

Signed-off-by: Dominic R <dominic@sdko.org>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
2025-03-20 07:47:00 -05:00
bfd1445c69 translate: Updates for file locale/en/LC_MESSAGES/django.po in zh-Hans (#13592)
Translate django.po in zh-Hans

100% translated source file: 'django.po'
on 'zh-Hans'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-03-20 07:58:17 +00:00
c2b3e9b05c translate: Updates for file web/xliff/en.xlf in fr (#13597)
Translate web/xliff/en.xlf in fr

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-03-20 07:57:31 +00:00
2c7d841e4a translate: Updates for file locale/en/LC_MESSAGES/django.po in fr (#13596)
Translate locale/en/LC_MESSAGES/django.po in fr

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-03-20 07:55:47 +00:00
c5d13c4a15 translate: Updates for file web/xliff/en.xlf in zh-Hans (#13593)
Translate web/xliff/en.xlf in zh-Hans

100% translated source file: 'web/xliff/en.xlf'
on 'zh-Hans'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-03-20 08:40:22 +01:00
079ef6e114 translate: Updates for file web/xliff/en.xlf in zh_CN (#13591)
Translate web/xliff/en.xlf in zh_CN

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-03-20 08:40:02 +01:00
98bfca0b4d translate: Updates for file locale/en/LC_MESSAGES/django.po in zh_CN (#13590)
Translate locale/en/LC_MESSAGES/django.po in zh_CN

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-03-20 08:40:00 +01:00
a247bd5b9f core, web: update translations (#13584)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: rissson <18313093+rissson@users.noreply.github.com>
2025-03-20 01:07:24 +00:00
27856ec301 brands: add option to set global default flow background (#13079)
* brands: add option to set global default flow background

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

* test

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-20 01:07:05 +00:00
e4a8c05d25 web/admin: fix diff showing previous false as "-" (#13580)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-19 23:06:37 +00:00
cb2e0c6d54 web: bump API Client version (#13581)
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-03-19 23:04:21 +00:00
f37e1ca642 brands: migrate custom CSS to brands (#13172)
* brands: migrate custom CSS to brands

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

* fix missing default

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

* fix tests

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

* simpler migration

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

* add css to brand form

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

* fix

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-19 22:52:38 +00:00
336 changed files with 8721 additions and 6154 deletions

View File

@ -1,5 +1,5 @@
[bumpversion]
current_version = 2025.2.2
current_version = 2025.2.3
tag = True
commit = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(?:-(?P<rc_t>[a-zA-Z-]+)(?P<rc_n>[1-9]\\d*))?
@ -17,6 +17,8 @@ optional_value = final
[bumpversion:file:pyproject.toml]
[bumpversion:file:uv.lock]
[bumpversion:file:package.json]
[bumpversion:file:docker-compose.yml]

22
.github/ISSUE_TEMPLATE/docs_issue.md vendored Normal file
View File

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

@ -44,7 +44,6 @@ if is_release:
]
if not prerelease:
image_tags += [
f"{name}:latest",
f"{name}:{version_family}",
]
else:

View File

@ -29,7 +29,7 @@ jobs:
- name: Generate API
run: make gen-client-go
- name: golangci-lint
uses: golangci/golangci-lint-action@v6
uses: golangci/golangci-lint-action@v7
with:
version: latest
args: --timeout 5000s --verbose

27
.github/workflows/semgrep.yml vendored Normal file
View File

@ -0,0 +1,27 @@
name: authentik-semgrep
on:
workflow_dispatch: {}
pull_request: {}
push:
branches:
- main
- master
paths:
- .github/workflows/semgrep.yml
schedule:
# random HH:MM to avoid a load spike on GitHub Actions at 00:00
- cron: '12 15 * * *'
jobs:
semgrep:
name: semgrep/ci
runs-on: ubuntu-latest
permissions:
contents: read
env:
SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
container:
image: semgrep/semgrep
if: (github.actor != 'dependabot[bot]')
steps:
- uses: actions/checkout@v4
- run: semgrep ci

View File

@ -43,7 +43,7 @@ COPY ./gen-ts-api /work/web/node_modules/@goauthentik/api
RUN npm run build
# Stage 3: Build go proxy
FROM --platform=${BUILDPLATFORM} mcr.microsoft.com/oss/go/microsoft/golang:1.23-fips-bookworm AS go-builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.24-bookworm AS go-builder
ARG TARGETOS
ARG TARGETARCH
@ -76,7 +76,7 @@ COPY ./go.sum /go/src/goauthentik.io/go.sum
RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
--mount=type=cache,id=go-build-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/root/.cache/go-build \
if [ "$TARGETARCH" = "arm64" ]; then export CC=aarch64-linux-gnu-gcc && export CC_FOR_TARGET=gcc-aarch64-linux-gnu; fi && \
CGO_ENABLED=1 GOEXPERIMENT="systemcrypto" GOFLAGS="-tags=requirefips" GOARM="${TARGETVARIANT#v}" \
CGO_ENABLED=1 GOFIPS140=latest GOARM="${TARGETVARIANT#v}" \
go build -o /go/authentik ./cmd/server
# Stage 4: MaxMind GeoIP
@ -94,9 +94,9 @@ RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
/bin/sh -c "/usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0"
# Stage 5: Download uv
FROM ghcr.io/astral-sh/uv:0.6.8 AS uv
FROM ghcr.io/astral-sh/uv:0.6.11 AS uv
# Stage 6: Base python image
FROM ghcr.io/goauthentik/fips-python:3.12.8-slim-bookworm-fips AS python-base
FROM ghcr.io/goauthentik/fips-python:3.12.9-slim-bookworm-fips AS python-base
ENV VENV_PATH="/ak-root/.venv" \
PATH="/lifecycle:/ak-root/.venv/bin:$PATH" \

View File

@ -2,7 +2,7 @@
from os import environ
__version__ = "2025.2.2"
__version__ = "2025.2.3"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"

View File

@ -59,7 +59,7 @@ class SystemInfoSerializer(PassiveSerializer):
if not isinstance(value, str):
continue
actual_value = value
if raw_session in actual_value:
if raw_session is not None and raw_session in actual_value:
actual_value = actual_value.replace(
raw_session, SafeExceptionReporterFilter.cleansed_substitute
)

View File

@ -49,6 +49,8 @@ class BrandSerializer(ModelSerializer):
"branding_title",
"branding_logo",
"branding_favicon",
"branding_custom_css",
"branding_default_flow_background",
"flow_authentication",
"flow_invalidation",
"flow_recovery",
@ -86,6 +88,7 @@ class CurrentBrandSerializer(PassiveSerializer):
branding_title = CharField()
branding_logo = CharField(source="branding_logo_url")
branding_favicon = CharField(source="branding_favicon_url")
branding_custom_css = CharField()
ui_footer_links = ListField(
child=FooterLinkSerializer(),
read_only=True,
@ -125,6 +128,7 @@ class BrandViewSet(UsedByMixin, ModelViewSet):
"branding_title",
"branding_logo",
"branding_favicon",
"branding_default_flow_background",
"flow_authentication",
"flow_invalidation",
"flow_recovery",

View File

@ -0,0 +1,35 @@
# Generated by Django 5.0.12 on 2025-02-22 01:51
from pathlib import Path
from django.db import migrations, models
from django.apps.registry import Apps
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
def migrate_custom_css(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
Brand = apps.get_model("authentik_brands", "brand")
db_alias = schema_editor.connection.alias
path = Path("/web/dist/custom.css")
if not path.exists():
return
css = path.read_text()
Brand.objects.using(db_alias).update(branding_custom_css=css)
class Migration(migrations.Migration):
dependencies = [
("authentik_brands", "0007_brand_default_application"),
]
operations = [
migrations.AddField(
model_name="brand",
name="branding_custom_css",
field=models.TextField(blank=True, default=""),
),
migrations.RunPython(migrate_custom_css),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 5.0.13 on 2025-03-19 22:54
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_brands", "0008_brand_branding_custom_css"),
]
operations = [
migrations.AddField(
model_name="brand",
name="branding_default_flow_background",
field=models.TextField(default="/static/dist/assets/images/flow_background.jpg"),
),
]

View File

@ -33,6 +33,10 @@ class Brand(SerializerModel):
branding_logo = models.TextField(default="/static/dist/assets/icons/icon_left_brand.svg")
branding_favicon = models.TextField(default="/static/dist/assets/icons/icon.png")
branding_custom_css = models.TextField(default="", blank=True)
branding_default_flow_background = models.TextField(
default="/static/dist/assets/images/flow_background.jpg"
)
flow_authentication = models.ForeignKey(
Flow, null=True, on_delete=models.SET_NULL, related_name="brand_authentication"
@ -84,6 +88,12 @@ class Brand(SerializerModel):
return CONFIG.get("web.path", "/")[:-1] + self.branding_favicon
return self.branding_favicon
def branding_default_flow_background_url(self) -> str:
"""Get branding_default_flow_background with the correct prefix"""
if self.branding_default_flow_background.startswith("/static"):
return CONFIG.get("web.path", "/")[:-1] + self.branding_default_flow_background
return self.branding_default_flow_background
@property
def serializer(self) -> Serializer:
from authentik.brands.api import BrandSerializer

View File

@ -24,6 +24,7 @@ class TestBrands(APITestCase):
"branding_logo": "/static/dist/assets/icons/icon_left_brand.svg",
"branding_favicon": "/static/dist/assets/icons/icon.png",
"branding_title": "authentik",
"branding_custom_css": "",
"matched_domain": brand.domain,
"ui_footer_links": [],
"ui_theme": Themes.AUTOMATIC,
@ -43,6 +44,7 @@ class TestBrands(APITestCase):
"branding_logo": "/static/dist/assets/icons/icon_left_brand.svg",
"branding_favicon": "/static/dist/assets/icons/icon.png",
"branding_title": "custom",
"branding_custom_css": "",
"matched_domain": "bar.baz",
"ui_footer_links": [],
"ui_theme": Themes.AUTOMATIC,
@ -59,6 +61,7 @@ class TestBrands(APITestCase):
"branding_logo": "/static/dist/assets/icons/icon_left_brand.svg",
"branding_favicon": "/static/dist/assets/icons/icon.png",
"branding_title": "authentik",
"branding_custom_css": "",
"matched_domain": "fallback",
"ui_footer_links": [],
"ui_theme": Themes.AUTOMATIC,
@ -121,3 +124,27 @@ class TestBrands(APITestCase):
"subject": None,
},
)
def test_branding_url(self):
"""Test branding attributes return correct values"""
brand = create_test_brand()
brand.branding_default_flow_background = "https://goauthentik.io/img/icon.png"
brand.branding_favicon = "https://goauthentik.io/img/icon.png"
brand.branding_logo = "https://goauthentik.io/img/icon.png"
brand.save()
self.assertEqual(
brand.branding_default_flow_background_url(), "https://goauthentik.io/img/icon.png"
)
self.assertJSONEqual(
self.client.get(reverse("authentik_api:brand-current")).content.decode(),
{
"branding_logo": "https://goauthentik.io/img/icon.png",
"branding_favicon": "https://goauthentik.io/img/icon.png",
"branding_title": "authentik",
"branding_custom_css": "",
"matched_domain": brand.domain,
"ui_footer_links": [],
"ui_theme": Themes.AUTOMATIC,
"default_locale": "",
},
)

View File

@ -46,7 +46,7 @@ LOGGER = get_logger()
def user_app_cache_key(user_pk: str, page_number: int | None = None) -> str:
"""Cache key where application list for user is saved"""
key = f"{CACHE_PREFIX}/app_access/{user_pk}"
key = f"{CACHE_PREFIX}app_access/{user_pk}"
if page_number:
key += f"/{page_number}"
return key

View File

@ -1,13 +1,14 @@
"""User API Views"""
from datetime import timedelta
from importlib import import_module
from json import loads
from typing import Any
from django.conf import settings
from django.contrib.auth import update_session_auth_hash
from django.contrib.auth.models import Permission
from django.contrib.sessions.backends.cache import KEY_PREFIX
from django.core.cache import cache
from django.contrib.sessions.backends.base import SessionBase
from django.db.models.functions import ExtractHour
from django.db.transaction import atomic
from django.db.utils import IntegrityError
@ -91,6 +92,7 @@ from authentik.stages.email.tasks import send_mails
from authentik.stages.email.utils import TemplateEmailMessage
LOGGER = get_logger()
SessionStore: SessionBase = import_module(settings.SESSION_ENGINE).SessionStore
class UserGroupSerializer(ModelSerializer):
@ -373,7 +375,7 @@ class UsersFilter(FilterSet):
method="filter_attributes",
)
is_superuser = BooleanFilter(field_name="ak_groups", lookup_expr="is_superuser")
is_superuser = BooleanFilter(field_name="ak_groups", method="filter_is_superuser")
uuid = UUIDFilter(field_name="uuid")
path = CharFilter(field_name="path")
@ -391,6 +393,11 @@ class UsersFilter(FilterSet):
queryset=Group.objects.all().order_by("name"),
)
def filter_is_superuser(self, queryset, name, value):
if value:
return queryset.filter(ak_groups__is_superuser=True).distinct()
return queryset.exclude(ak_groups__is_superuser=True).distinct()
def filter_attributes(self, queryset, name, value):
"""Filter attributes by query args"""
try:
@ -769,7 +776,8 @@ class UserViewSet(UsedByMixin, ModelViewSet):
if not instance.is_active:
sessions = AuthenticatedSession.objects.filter(user=instance)
session_ids = sessions.values_list("session_key", flat=True)
cache.delete_many(f"{KEY_PREFIX}{session}" for session in session_ids)
for session in session_ids:
SessionStore(session).delete()
sessions.delete()
LOGGER.debug("Deleted user's sessions", user=instance.username)
return response

View File

@ -761,11 +761,17 @@ class Source(ManagedModel, SerializerModel, PolicyBindingModel):
@property
def component(self) -> str:
"""Return component used to edit this object"""
if self.managed == self.MANAGED_INBUILT:
return ""
raise NotImplementedError
@property
def property_mapping_type(self) -> "type[PropertyMapping]":
"""Return property mapping type used by this object"""
if self.managed == self.MANAGED_INBUILT:
from authentik.core.models import PropertyMapping
return PropertyMapping
raise NotImplementedError
def ui_login_button(self, request: HttpRequest) -> UILoginButton | None:
@ -780,10 +786,14 @@ class Source(ManagedModel, SerializerModel, PolicyBindingModel):
def get_base_user_properties(self, **kwargs) -> dict[str, Any | dict[str, Any]]:
"""Get base properties for a user to build final properties upon."""
if self.managed == self.MANAGED_INBUILT:
return {}
raise NotImplementedError
def get_base_group_properties(self, **kwargs) -> dict[str, Any | dict[str, Any]]:
"""Get base properties for a group to build final properties upon."""
if self.managed == self.MANAGED_INBUILT:
return {}
raise NotImplementedError
def __str__(self):

View File

@ -1,7 +1,10 @@
"""authentik core signals"""
from importlib import import_module
from django.conf import settings
from django.contrib.auth.signals import user_logged_in, user_logged_out
from django.contrib.sessions.backends.cache import KEY_PREFIX
from django.contrib.sessions.backends.base import SessionBase
from django.core.cache import cache
from django.core.signals import Signal
from django.db.models import Model
@ -25,6 +28,7 @@ password_changed = Signal()
login_failed = Signal()
LOGGER = get_logger()
SessionStore: SessionBase = import_module(settings.SESSION_ENGINE).SessionStore
@receiver(post_save, sender=Application)
@ -60,8 +64,7 @@ def user_logged_out_session(sender, request: HttpRequest, user: User, **_):
@receiver(pre_delete, sender=AuthenticatedSession)
def authenticated_session_delete(sender: type[Model], instance: "AuthenticatedSession", **_):
"""Delete session when authenticated session is deleted"""
cache_key = f"{KEY_PREFIX}{instance.session_key}"
cache.delete(cache_key)
SessionStore(instance.session_key).delete()
@receiver(pre_save)

View File

@ -36,6 +36,7 @@ from authentik.flows.planner import (
)
from authentik.flows.stage import StageView
from authentik.flows.views.executor import NEXT_ARG_NAME, SESSION_KEY_GET
from authentik.lib.utils.urls import is_url_absolute
from authentik.lib.views import bad_request_message
from authentik.policies.denied import AccessDeniedResponse
from authentik.policies.utils import delete_none_values
@ -48,6 +49,7 @@ LOGGER = get_logger()
PLAN_CONTEXT_SOURCE_GROUPS = "source_groups"
SESSION_KEY_SOURCE_FLOW_STAGES = "authentik/flows/source_flow_stages"
SESSION_KEY_SOURCE_FLOW_CONTEXT = "authentik/flows/source_flow_context"
SESSION_KEY_OVERRIDE_FLOW_TOKEN = "authentik/flows/source_override_flow_token" # nosec
@ -208,6 +210,8 @@ class SourceFlowManager:
final_redirect = self.request.session.get(SESSION_KEY_GET, {}).get(
NEXT_ARG_NAME, "authentik_core:if-user"
)
if not is_url_absolute(final_redirect):
final_redirect = "authentik_core:if-user"
flow_context.update(
{
# Since we authenticate the user by their token, they have no backend set
@ -261,6 +265,7 @@ class SourceFlowManager:
plan.append_stage(stage)
for stage in self.request.session.get(SESSION_KEY_SOURCE_FLOW_STAGES, []):
plan.append_stage(stage)
plan.context.update(self.request.session.get(SESSION_KEY_SOURCE_FLOW_CONTEXT, {}))
return plan.to_redirect(self.request, flow)
def handle_auth(

View File

@ -16,7 +16,7 @@
{% block head_before %}
{% endblock %}
<link rel="stylesheet" type="text/css" href="{% static 'dist/authentik.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'dist/custom.css' %}" data-inject>
<style>{{ brand.branding_custom_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

@ -4,7 +4,7 @@
{% load i18n %}
{% block head_before %}
<link rel="prefetch" href="{% static 'dist/assets/images/flow_background.jpg' %}" />
<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" %}
@ -13,7 +13,7 @@
{% block head %}
<style>
:root {
--ak-flow-background: url("{% static 'dist/assets/images/flow_background.jpg' %}");
--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);

View File

@ -0,0 +1,19 @@
from django.apps import apps
from django.urls import reverse
from rest_framework.test import APITestCase
from authentik.core.tests.utils import create_test_admin_user
class TestSourceAPI(APITestCase):
def setUp(self) -> None:
self.user = create_test_admin_user()
self.client.force_login(self.user)
def test_builtin_source_used_by(self):
"""Test Providers's types endpoint"""
apps.get_app_config("authentik_core").source_inbuilt()
response = self.client.get(
reverse("authentik_api:source-used-by", kwargs={"slug": "authentik-built-in"}),
)
self.assertEqual(response.status_code, 200)

View File

@ -1,6 +1,7 @@
"""Test Users API"""
from datetime import datetime
from json import loads
from django.contrib.sessions.backends.cache import KEY_PREFIX
from django.core.cache import cache
@ -15,7 +16,12 @@ from authentik.core.models import (
User,
UserTypes,
)
from authentik.core.tests.utils import create_test_admin_user, create_test_brand, create_test_flow
from authentik.core.tests.utils import (
create_test_admin_user,
create_test_brand,
create_test_flow,
create_test_user,
)
from authentik.flows.models import FlowDesignation
from authentik.lib.generators import generate_id, generate_key
from authentik.stages.email.models import EmailStage
@ -26,7 +32,7 @@ class TestUsersAPI(APITestCase):
def setUp(self) -> None:
self.admin = create_test_admin_user()
self.user = User.objects.create(username="test-user")
self.user = create_test_user()
def test_filter_type(self):
"""Test API filtering by type"""
@ -41,6 +47,35 @@ class TestUsersAPI(APITestCase):
)
self.assertEqual(response.status_code, 200)
def test_filter_is_superuser(self):
"""Test API filtering by superuser status"""
User.objects.all().delete()
admin = create_test_admin_user()
self.client.force_login(admin)
# Test superuser
response = self.client.get(
reverse("authentik_api:user-list"),
data={
"is_superuser": True,
},
)
self.assertEqual(response.status_code, 200)
body = loads(response.content)
self.assertEqual(len(body["results"]), 1)
self.assertEqual(body["results"][0]["username"], admin.username)
# Test non-superuser
user = create_test_user()
response = self.client.get(
reverse("authentik_api:user-list"),
data={
"is_superuser": False,
},
)
self.assertEqual(response.status_code, 200)
body = loads(response.content)
self.assertEqual(len(body["results"]), 1, body)
self.assertEqual(body["results"][0]["username"], user.username)
def test_list_with_groups(self):
"""Test listing with groups"""
self.client.force_login(self.admin)
@ -99,6 +134,8 @@ class TestUsersAPI(APITestCase):
def test_recovery_email_no_flow(self):
"""Test user recovery link (no recovery flow set)"""
self.client.force_login(self.admin)
self.user.email = ""
self.user.save()
response = self.client.post(
reverse("authentik_api:user-recovery-email", kwargs={"pk": self.user.pk})
)

View File

@ -11,13 +11,14 @@ from guardian.shortcuts import get_anonymous_user
from authentik.core.models import Source, User
from authentik.core.sources.flow_manager import (
SESSION_KEY_OVERRIDE_FLOW_TOKEN,
SESSION_KEY_SOURCE_FLOW_CONTEXT,
SESSION_KEY_SOURCE_FLOW_STAGES,
)
from authentik.core.types import UILoginButton
from authentik.enterprise.stages.source.models import SourceStage
from authentik.flows.challenge import Challenge, ChallengeResponse
from authentik.flows.models import FlowToken, in_memory_stage
from authentik.flows.planner import PLAN_CONTEXT_IS_RESTORED
from authentik.flows.planner import PLAN_CONTEXT_IS_REDIRECTED, PLAN_CONTEXT_IS_RESTORED
from authentik.flows.stage import ChallengeStageView, StageView
from authentik.lib.utils.time import timedelta_from_string
@ -53,6 +54,9 @@ class SourceStageView(ChallengeStageView):
resume_token = self.create_flow_token()
self.request.session[SESSION_KEY_OVERRIDE_FLOW_TOKEN] = resume_token
self.request.session[SESSION_KEY_SOURCE_FLOW_STAGES] = [in_memory_stage(SourceStageFinal)]
self.request.session[SESSION_KEY_SOURCE_FLOW_CONTEXT] = {
PLAN_CONTEXT_IS_REDIRECTED: self.executor.flow,
}
return self.login_button.challenge
def create_flow_token(self) -> FlowToken:

View File

@ -50,7 +50,8 @@ class NotificationTransportSerializer(ModelSerializer):
"mode",
"mode_verbose",
"webhook_url",
"webhook_mapping",
"webhook_mapping_body",
"webhook_mapping_headers",
"send_once",
]

View File

@ -0,0 +1,43 @@
# Generated by Django 5.0.13 on 2025-03-20 19:54
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_events", "0008_event_authentik_e_expires_8c73a8_idx_and_more"),
]
operations = [
migrations.RenameField(
model_name="notificationtransport",
old_name="webhook_mapping",
new_name="webhook_mapping_body",
),
migrations.AlterField(
model_name="notificationtransport",
name="webhook_mapping_body",
field=models.ForeignKey(
default=None,
help_text="Customize the body of the request. Mapping should return data that is JSON-serializable.",
null=True,
on_delete=django.db.models.deletion.SET_DEFAULT,
related_name="+",
to="authentik_events.notificationwebhookmapping",
),
),
migrations.AddField(
model_name="notificationtransport",
name="webhook_mapping_headers",
field=models.ForeignKey(
default=None,
help_text="Configure additional headers to be sent. Mapping should return a dictionary of key-value pairs",
null=True,
on_delete=django.db.models.deletion.SET_DEFAULT,
related_name="+",
to="authentik_events.notificationwebhookmapping",
),
),
]

View File

@ -336,8 +336,27 @@ class NotificationTransport(SerializerModel):
mode = models.TextField(choices=TransportMode.choices, default=TransportMode.LOCAL)
webhook_url = models.TextField(blank=True, validators=[DomainlessURLValidator()])
webhook_mapping = models.ForeignKey(
"NotificationWebhookMapping", on_delete=models.SET_DEFAULT, null=True, default=None
webhook_mapping_body = models.ForeignKey(
"NotificationWebhookMapping",
on_delete=models.SET_DEFAULT,
null=True,
default=None,
related_name="+",
help_text=_(
"Customize the body of the request. "
"Mapping should return data that is JSON-serializable."
),
)
webhook_mapping_headers = models.ForeignKey(
"NotificationWebhookMapping",
on_delete=models.SET_DEFAULT,
null=True,
default=None,
related_name="+",
help_text=_(
"Configure additional headers to be sent. "
"Mapping should return a dictionary of key-value pairs"
),
)
send_once = models.BooleanField(
default=False,
@ -360,8 +379,8 @@ class NotificationTransport(SerializerModel):
def send_local(self, notification: "Notification") -> list[str]:
"""Local notification delivery"""
if self.webhook_mapping:
self.webhook_mapping.evaluate(
if self.webhook_mapping_body:
self.webhook_mapping_body.evaluate(
user=notification.user,
request=None,
notification=notification,
@ -380,9 +399,18 @@ class NotificationTransport(SerializerModel):
if notification.event and notification.event.user:
default_body["event_user_email"] = notification.event.user.get("email", None)
default_body["event_user_username"] = notification.event.user.get("username", None)
if self.webhook_mapping:
headers = {}
if self.webhook_mapping_body:
default_body = sanitize_item(
self.webhook_mapping.evaluate(
self.webhook_mapping_body.evaluate(
user=notification.user,
request=None,
notification=notification,
)
)
if self.webhook_mapping_headers:
headers = sanitize_item(
self.webhook_mapping_headers.evaluate(
user=notification.user,
request=None,
notification=notification,
@ -392,6 +420,7 @@ class NotificationTransport(SerializerModel):
response = get_http_session().post(
self.webhook_url,
json=default_body,
headers=headers,
)
response.raise_for_status()
except RequestException as exc:

View File

@ -120,7 +120,7 @@ class TestEventsNotifications(APITestCase):
)
transport = NotificationTransport.objects.create(
name=generate_id(), webhook_mapping=mapping, mode=TransportMode.LOCAL
name=generate_id(), webhook_mapping_body=mapping, mode=TransportMode.LOCAL
)
NotificationRule.objects.filter(name__startswith="default").delete()
trigger = NotificationRule.objects.create(name=generate_id(), group=self.group)

View File

@ -60,20 +60,25 @@ class TestEventTransports(TestCase):
def test_transport_webhook_mapping(self):
"""Test webhook transport with custom mapping"""
mapping = NotificationWebhookMapping.objects.create(
mapping_body = NotificationWebhookMapping.objects.create(
name=generate_id(), expression="return request.user"
)
mapping_headers = NotificationWebhookMapping.objects.create(
name=generate_id(), expression="""return {"foo": "bar"}"""
)
transport: NotificationTransport = NotificationTransport.objects.create(
name=generate_id(),
mode=TransportMode.WEBHOOK,
webhook_url="http://localhost:1234/test",
webhook_mapping=mapping,
webhook_mapping_body=mapping_body,
webhook_mapping_headers=mapping_headers,
)
with Mocker() as mocker:
mocker.post("http://localhost:1234/test")
transport.send(self.notification)
self.assertEqual(mocker.call_count, 1)
self.assertEqual(mocker.request_history[0].method, "POST")
self.assertEqual(mocker.request_history[0].headers["foo"], "bar")
self.assertJSONEqual(
mocker.request_history[0].body.decode(),
{"email": self.user.email, "pk": self.user.pk, "username": self.user.username},

View File

@ -6,6 +6,7 @@ from typing import TYPE_CHECKING
from uuid import uuid4
from django.db import models
from django.http import HttpRequest
from django.utils.translation import gettext_lazy as _
from model_utils.managers import InheritanceManager
from rest_framework.serializers import BaseSerializer
@ -178,11 +179,12 @@ class Flow(SerializerModel, PolicyBindingModel):
help_text=_("Required level of authentication and authorization to access a flow."),
)
@property
def background_url(self) -> str:
def background_url(self, request: HttpRequest | None = None) -> str:
"""Get the URL to the background image. If the name is /static or starts with http
it is returned as-is"""
if not self.background:
if request:
return request.brand.branding_default_flow_background_url()
return (
CONFIG.get("web.path", "/")[:-1] + "/static/dist/assets/images/flow_background.jpg"
)

View File

@ -184,7 +184,7 @@ class ChallengeStageView(StageView):
flow_info = ContextualFlowInfo(
data={
"title": self.format_title(),
"background": self.executor.flow.background_url,
"background": self.executor.flow.background_url(self.request),
"cancel_url": reverse("authentik_flows:cancel"),
"layout": self.executor.flow.layout,
}

View File

@ -27,7 +27,6 @@ class FlowTestCase(APITestCase):
self.assertIsNotNone(raw_response["component"])
if flow:
self.assertIn("flow_info", raw_response)
self.assertEqual(raw_response["flow_info"]["background"], flow.background_url)
self.assertEqual(
raw_response["flow_info"]["cancel_url"], reverse("authentik_flows:cancel")
)

View File

@ -1,9 +1,11 @@
"""API flow tests"""
from json import loads
from django.urls import reverse
from rest_framework.test import APITestCase
from authentik.core.tests.utils import create_test_admin_user
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
from authentik.flows.api.stages import StageSerializer, StageViewSet
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding, Stage
from authentik.lib.generators import generate_id
@ -77,6 +79,22 @@ class TestFlowsAPI(APITestCase):
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(response.content, {"diagram": DIAGRAM_EXPECTED})
def test_api_background(self):
"""Test custom background"""
user = create_test_admin_user()
self.client.force_login(user)
flow = create_test_flow()
response = self.client.get(reverse("authentik_api:flow-detail", kwargs={"slug": flow.slug}))
body = loads(response.content.decode())
self.assertEqual(body["background"], "/static/dist/assets/images/flow_background.jpg")
flow.background = "https://goauthentik.io/img/icon.png"
flow.save()
response = self.client.get(reverse("authentik_api:flow-detail", kwargs={"slug": flow.slug}))
body = loads(response.content.decode())
self.assertEqual(body["background"], "https://goauthentik.io/img/icon.png")
def test_api_diagram_no_stages(self):
"""Test flow diagram with no stages."""
user = create_test_admin_user()

View File

@ -49,7 +49,7 @@ class TestFlowInspector(APITestCase):
"captcha_stage": None,
"component": "ak-stage-identification",
"flow_info": {
"background": flow.background_url,
"background": "/static/dist/assets/images/flow_background.jpg",
"cancel_url": reverse("authentik_flows:cancel"),
"title": flow.title,
"layout": "stacked",

View File

@ -69,6 +69,7 @@ SESSION_KEY_APPLICATION_PRE = "authentik/flows/application_pre"
SESSION_KEY_GET = "authentik/flows/get"
SESSION_KEY_POST = "authentik/flows/post"
SESSION_KEY_HISTORY = "authentik/flows/history"
SESSION_KEY_AUTH_STARTED = "authentik/flows/auth_started"
QS_KEY_TOKEN = "flow_token" # nosec
QS_QUERY = "query"
@ -453,6 +454,7 @@ class FlowExecutorView(APIView):
SESSION_KEY_APPLICATION_PRE,
SESSION_KEY_PLAN,
SESSION_KEY_GET,
SESSION_KEY_AUTH_STARTED,
# We might need the initial POST payloads for later requests
# SESSION_KEY_POST,
# We don't delete the history on purpose, as a user might

View File

@ -6,14 +6,22 @@ from django.shortcuts import get_object_or_404
from ua_parser.user_agent_parser import Parse
from authentik.core.views.interface import InterfaceView
from authentik.flows.models import Flow
from authentik.flows.models import Flow, FlowDesignation
from authentik.flows.views.executor import SESSION_KEY_AUTH_STARTED
class FlowInterfaceView(InterfaceView):
"""Flow interface"""
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
kwargs["flow"] = get_object_or_404(Flow, slug=self.kwargs.get("flow_slug"))
flow = get_object_or_404(Flow, slug=self.kwargs.get("flow_slug"))
kwargs["flow"] = flow
if (
not self.request.user.is_authenticated
and flow.designation == FlowDesignation.AUTHENTICATION
):
self.request.session[SESSION_KEY_AUTH_STARTED] = True
self.request.session.save()
kwargs["inspector"] = "inspector" in self.request.GET
return super().get_context_data(**kwargs)

View File

@ -1,5 +1,20 @@
# update website/docs/install-config/configuration/configuration.mdx
# This is the default configuration file
# authentik configuration
#
# https://docs.goauthentik.io/docs/install-config/configuration/
#
# To override the settings in this file, run the following command from the repository root:
#
# ```shell
# make gen-dev-config
# ```
#
# You may edit the generated file to override the configuration below.
#
# When making modifying the default configuration file,
# ensure that the corresponding documentation is updated to match.
#
# @see {@link ../../website/docs/install-config/configuration/configuration.mdx Configuration documentation} for more information.
postgresql:
host: localhost
name: authentik
@ -45,6 +60,8 @@ redis:
# url: ""
# transport_options: ""
http_timeout: 30
cache:
# url: ""
timeout: 300

View File

@ -18,6 +18,15 @@ class SerializerModel(models.Model):
@property
def serializer(self) -> type[BaseSerializer]:
"""Get serializer for this model"""
# Special handling for built-in source
if (
hasattr(self, "managed")
and hasattr(self, "MANAGED_INBUILT")
and self.managed == self.MANAGED_INBUILT
):
from authentik.core.api.sources import SourceSerializer
return SourceSerializer
raise NotImplementedError

View File

@ -16,7 +16,40 @@ def authentik_user_agent() -> str:
return f"authentik@{get_full_version()}"
class DebugSession(Session):
class TimeoutSession(Session):
"""Always set a default HTTP request timeout"""
def __init__(self, default_timeout=None):
super().__init__()
self.timeout = default_timeout
def send(
self,
request,
*,
stream=...,
verify=...,
proxies=...,
cert=...,
timeout=...,
allow_redirects=...,
**kwargs,
):
if not timeout and self.timeout:
timeout = self.timeout
return super().send(
request,
stream=stream,
verify=verify,
proxies=proxies,
cert=cert,
timeout=timeout,
allow_redirects=allow_redirects,
**kwargs,
)
class DebugSession(TimeoutSession):
"""requests session which logs http requests and responses"""
def send(self, req: PreparedRequest, *args, **kwargs):
@ -42,8 +75,9 @@ class DebugSession(Session):
def get_http_session() -> Session:
"""Get a requests session with common headers"""
session = Session()
session = TimeoutSession()
if CONFIG.get_bool("debug") or CONFIG.get("log_level") == "trace":
session = DebugSession()
session.headers["User-Agent"] = authentik_user_agent()
session.timeout = CONFIG.get_optional_int("http_timeout")
return session

View File

@ -13,6 +13,7 @@ from paramiko.ssh_exception import SSHException
from structlog.stdlib import get_logger
from yaml import safe_dump
from authentik import __version__
from authentik.outposts.apps import MANAGED_OUTPOST
from authentik.outposts.controllers.base import BaseClient, BaseController, ControllerException
from authentik.outposts.docker_ssh import DockerInlineSSH, SSHManagedExternallyException
@ -184,7 +185,7 @@ class DockerController(BaseController):
try:
self.client.images.pull(image)
except DockerException: # pragma: no cover
image = f"ghcr.io/goauthentik/{self.outpost.type}:latest"
image = f"ghcr.io/goauthentik/{self.outpost.type}:{__version__}"
self.client.images.pull(image)
return image

View File

@ -35,3 +35,4 @@ class AuthentikPoliciesConfig(ManagedAppConfig):
label = "authentik_policies"
verbose_name = "authentik Policies"
default = True
mountpoint = "policy/"

View File

@ -0,0 +1,89 @@
{% extends 'login/base_full.html' %}
{% load static %}
{% load i18n %}
{% block head %}
{{ block.super }}
<script>
let redirecting = false;
const checkAuth = async () => {
if (redirecting) return true;
const url = "{{ check_auth_url }}";
console.debug("authentik/policies/buffer: Checking authentication...");
try {
const result = await fetch(url, {
method: "HEAD",
});
if (result.status >= 400) {
return false
}
console.debug("authentik/policies/buffer: Continuing");
redirecting = true;
if ("{{ auth_req_method }}" === "post") {
document.querySelector("form").submit();
} else {
window.location.assign("{{ continue_url|escapejs }}");
}
} catch {
return false;
}
};
let timeout = 100;
let offset = 20;
let attempt = 0;
const main = async () => {
attempt += 1;
await checkAuth();
console.debug(`authentik/policies/buffer: Waiting ${timeout}ms...`);
setTimeout(main, timeout);
timeout += (offset * attempt);
if (timeout >= 2000) {
timeout = 2000;
}
}
document.addEventListener("visibilitychange", async () => {
if (document.hidden) return;
console.debug("authentik/policies/buffer: Checking authentication on tab activate...");
await checkAuth();
});
main();
</script>
{% endblock %}
{% block title %}
{% trans 'Waiting for authentication...' %} - {{ brand.branding_title }}
{% endblock %}
{% block card_title %}
{% trans 'Waiting for authentication...' %}
{% endblock %}
{% block card %}
<form class="pf-c-form" method="{{ auth_req_method }}" action="{{ continue_url }}">
{% if auth_req_method == "post" %}
{% for key, value in auth_req_body.items %}
<input type="hidden" name="{{ key }}" value="{{ value }}" />
{% endfor %}
{% endif %}
<div class="pf-c-empty-state">
<div class="pf-c-empty-state__content">
<div class="pf-c-empty-state__icon">
<span class="pf-c-spinner pf-m-xl" role="progressbar">
<span class="pf-c-spinner__clipper"></span>
<span class="pf-c-spinner__lead-ball"></span>
<span class="pf-c-spinner__tail-ball"></span>
</span>
</div>
<h1 class="pf-c-title pf-m-lg">
{% trans "You're already authenticating in another tab. This page will refresh once authentication is completed." %}
</h1>
</div>
</div>
<div class="pf-c-form__group pf-m-action">
<a href="{{ auth_req_url }}" class="pf-c-button pf-m-primary pf-m-block">
{% trans "Authenticate in this tab" %}
</a>
</div>
</form>
{% endblock %}

View File

@ -0,0 +1,121 @@
from django.contrib.auth.models import AnonymousUser
from django.contrib.sessions.middleware import SessionMiddleware
from django.http import HttpResponse
from django.test import RequestFactory, TestCase
from django.urls import reverse
from authentik.core.models import Application, Provider
from authentik.core.tests.utils import create_test_flow, create_test_user
from authentik.flows.models import FlowDesignation
from authentik.flows.planner import FlowPlan
from authentik.flows.views.executor import SESSION_KEY_PLAN
from authentik.lib.generators import generate_id
from authentik.lib.tests.utils import dummy_get_response
from authentik.policies.views import (
QS_BUFFER_ID,
SESSION_KEY_BUFFER,
BufferedPolicyAccessView,
BufferView,
PolicyAccessView,
)
class TestPolicyViews(TestCase):
"""Test PolicyAccessView"""
def setUp(self):
super().setUp()
self.factory = RequestFactory()
self.user = create_test_user()
def test_pav(self):
"""Test simple policy access view"""
provider = Provider.objects.create(
name=generate_id(),
)
app = Application.objects.create(name=generate_id(), slug=generate_id(), provider=provider)
class TestView(PolicyAccessView):
def resolve_provider_application(self):
self.provider = provider
self.application = app
def get(self, *args, **kwargs):
return HttpResponse("foo")
req = self.factory.get("/")
req.user = self.user
res = TestView.as_view()(req)
self.assertEqual(res.status_code, 200)
self.assertEqual(res.content, b"foo")
def test_pav_buffer(self):
"""Test simple policy access view"""
provider = Provider.objects.create(
name=generate_id(),
)
app = Application.objects.create(name=generate_id(), slug=generate_id(), provider=provider)
flow = create_test_flow(FlowDesignation.AUTHENTICATION)
class TestView(BufferedPolicyAccessView):
def resolve_provider_application(self):
self.provider = provider
self.application = app
def get(self, *args, **kwargs):
return HttpResponse("foo")
req = self.factory.get("/")
req.user = AnonymousUser()
middleware = SessionMiddleware(dummy_get_response)
middleware.process_request(req)
req.session[SESSION_KEY_PLAN] = FlowPlan(flow.pk)
req.session.save()
res = TestView.as_view()(req)
self.assertEqual(res.status_code, 302)
self.assertTrue(res.url.startswith(reverse("authentik_policies:buffer")))
def test_pav_buffer_skip(self):
"""Test simple policy access view (skip buffer)"""
provider = Provider.objects.create(
name=generate_id(),
)
app = Application.objects.create(name=generate_id(), slug=generate_id(), provider=provider)
flow = create_test_flow(FlowDesignation.AUTHENTICATION)
class TestView(BufferedPolicyAccessView):
def resolve_provider_application(self):
self.provider = provider
self.application = app
def get(self, *args, **kwargs):
return HttpResponse("foo")
req = self.factory.get("/?skip_buffer=true")
req.user = AnonymousUser()
middleware = SessionMiddleware(dummy_get_response)
middleware.process_request(req)
req.session[SESSION_KEY_PLAN] = FlowPlan(flow.pk)
req.session.save()
res = TestView.as_view()(req)
self.assertEqual(res.status_code, 302)
self.assertTrue(res.url.startswith(reverse("authentik_flows:default-authentication")))
def test_buffer(self):
"""Test buffer view"""
uid = generate_id()
req = self.factory.get(f"/?{QS_BUFFER_ID}={uid}")
req.user = AnonymousUser()
middleware = SessionMiddleware(dummy_get_response)
middleware.process_request(req)
ts = generate_id()
req.session[SESSION_KEY_BUFFER % uid] = {
"method": "get",
"body": {},
"url": f"/{ts}",
}
req.session.save()
res = BufferView.as_view()(req)
self.assertEqual(res.status_code, 200)
self.assertIn(ts, res.render().content.decode())

View File

@ -1,7 +1,14 @@
"""API URLs"""
from django.urls import path
from authentik.policies.api.bindings import PolicyBindingViewSet
from authentik.policies.api.policies import PolicyViewSet
from authentik.policies.views import BufferView
urlpatterns = [
path("buffer", BufferView.as_view(), name="buffer"),
]
api_urlpatterns = [
("policies/all", PolicyViewSet),

View File

@ -1,23 +1,37 @@
"""authentik access helper classes"""
from typing import Any
from uuid import uuid4
from django.contrib import messages
from django.contrib.auth.mixins import AccessMixin
from django.contrib.auth.views import redirect_to_login
from django.http import HttpRequest, HttpResponse
from django.http import HttpRequest, HttpResponse, QueryDict
from django.shortcuts import redirect
from django.urls import reverse
from django.utils.http import urlencode
from django.utils.translation import gettext as _
from django.views.generic.base import View
from django.views.generic.base import TemplateView, View
from structlog.stdlib import get_logger
from authentik.core.models import Application, Provider, User
from authentik.flows.views.executor import SESSION_KEY_APPLICATION_PRE, SESSION_KEY_POST
from authentik.flows.models import Flow, FlowDesignation
from authentik.flows.planner import FlowPlan
from authentik.flows.views.executor import (
SESSION_KEY_APPLICATION_PRE,
SESSION_KEY_AUTH_STARTED,
SESSION_KEY_PLAN,
SESSION_KEY_POST,
)
from authentik.lib.sentry import SentryIgnoredException
from authentik.policies.denied import AccessDeniedResponse
from authentik.policies.engine import PolicyEngine
from authentik.policies.types import PolicyRequest, PolicyResult
LOGGER = get_logger()
QS_BUFFER_ID = "af_bf_id"
QS_SKIP_BUFFER = "skip_buffer"
SESSION_KEY_BUFFER = "authentik/policies/pav_buffer/%s"
class RequestValidationError(SentryIgnoredException):
@ -125,3 +139,65 @@ class PolicyAccessView(AccessMixin, View):
for message in result.messages:
messages.error(self.request, _(message))
return result
def url_with_qs(url: str, **kwargs):
"""Update/set querystring of `url` with the parameters in `kwargs`. Original query string
parameters are retained"""
if "?" not in url:
return url + f"?{urlencode(kwargs)}"
url, _, qs = url.partition("?")
qs = QueryDict(qs, mutable=True)
qs.update(kwargs)
return url + f"?{urlencode(qs.items())}"
class BufferView(TemplateView):
"""Buffer view"""
template_name = "policies/buffer.html"
def get_context_data(self, **kwargs):
buf_id = self.request.GET.get(QS_BUFFER_ID)
buffer: dict = self.request.session.get(SESSION_KEY_BUFFER % buf_id)
kwargs["auth_req_method"] = buffer["method"]
kwargs["auth_req_body"] = buffer["body"]
kwargs["auth_req_url"] = url_with_qs(buffer["url"], **{QS_SKIP_BUFFER: True})
kwargs["check_auth_url"] = reverse("authentik_api:user-me")
kwargs["continue_url"] = url_with_qs(buffer["url"], **{QS_BUFFER_ID: buf_id})
return super().get_context_data(**kwargs)
class BufferedPolicyAccessView(PolicyAccessView):
"""PolicyAccessView which buffers access requests in case the user is not logged in"""
def handle_no_permission(self):
plan: FlowPlan | None = self.request.session.get(SESSION_KEY_PLAN)
authenticating = self.request.session.get(SESSION_KEY_AUTH_STARTED)
if plan:
flow = Flow.objects.filter(pk=plan.flow_pk).first()
if not flow or flow.designation != FlowDesignation.AUTHENTICATION:
LOGGER.debug("Not buffering request, no flow or flow not for authentication")
return super().handle_no_permission()
if not plan and authenticating is None:
LOGGER.debug("Not buffering request, no flow plan active")
return super().handle_no_permission()
if self.request.GET.get(QS_SKIP_BUFFER):
LOGGER.debug("Not buffering request, explicit skip")
return super().handle_no_permission()
buffer_id = str(uuid4())
LOGGER.debug("Buffering access request", bf_id=buffer_id)
self.request.session[SESSION_KEY_BUFFER % buffer_id] = {
"body": self.request.POST,
"url": self.request.build_absolute_uri(self.request.get_full_path()),
"method": self.request.method.lower(),
}
return redirect(
url_with_qs(reverse("authentik_policies:buffer"), **{QS_BUFFER_ID: buffer_id})
)
def dispatch(self, request, *args, **kwargs):
response = super().dispatch(request, *args, **kwargs)
if QS_BUFFER_ID in self.request.GET:
self.request.session.pop(SESSION_KEY_BUFFER % self.request.GET[QS_BUFFER_ID], None)
return response

View File

@ -30,7 +30,7 @@ from authentik.flows.stage import StageView
from authentik.lib.utils.time import timedelta_from_string
from authentik.lib.views import bad_request_message
from authentik.policies.types import PolicyRequest
from authentik.policies.views import PolicyAccessView, RequestValidationError
from authentik.policies.views import BufferedPolicyAccessView, RequestValidationError
from authentik.providers.oauth2.constants import (
PKCE_METHOD_PLAIN,
PKCE_METHOD_S256,
@ -328,7 +328,7 @@ class OAuthAuthorizationParams:
return code
class AuthorizationFlowInitView(PolicyAccessView):
class AuthorizationFlowInitView(BufferedPolicyAccessView):
"""OAuth2 Flow initializer, checks access to application and starts flow"""
params: OAuthAuthorizationParams

View File

@ -18,11 +18,11 @@ from authentik.flows.planner import PLAN_CONTEXT_APPLICATION, FlowPlanner
from authentik.flows.stage import RedirectStage
from authentik.lib.utils.time import timedelta_from_string
from authentik.policies.engine import PolicyEngine
from authentik.policies.views import PolicyAccessView
from authentik.policies.views import BufferedPolicyAccessView
from authentik.providers.rac.models import ConnectionToken, Endpoint, RACProvider
class RACStartView(PolicyAccessView):
class RACStartView(BufferedPolicyAccessView):
"""Start a RAC connection by checking access and creating a connection token"""
endpoint: Endpoint

View File

@ -0,0 +1,22 @@
# Generated by Django 5.0.13 on 2025-03-31 13:50
import authentik.lib.models
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_providers_saml", "0017_samlprovider_authn_context_class_ref_mapping"),
]
operations = [
migrations.AlterField(
model_name="samlprovider",
name="acs_url",
field=models.TextField(
validators=[authentik.lib.models.DomainlessURLValidator(schemes=("http", "https"))],
verbose_name="ACS URL",
),
),
]

View File

@ -10,6 +10,7 @@ from structlog.stdlib import get_logger
from authentik.core.api.object_types import CreatableType
from authentik.core.models import PropertyMapping, Provider
from authentik.crypto.models import CertificateKeyPair
from authentik.lib.models import DomainlessURLValidator
from authentik.lib.utils.time import timedelta_string_validator
from authentik.sources.saml.processors.constants import (
DSA_SHA1,
@ -40,7 +41,9 @@ class SAMLBindings(models.TextChoices):
class SAMLProvider(Provider):
"""SAML 2.0 Endpoint for applications which support SAML."""
acs_url = models.URLField(verbose_name=_("ACS URL"))
acs_url = models.TextField(
validators=[DomainlessURLValidator(schemes=("http", "https"))], verbose_name=_("ACS URL")
)
audience = models.TextField(
default="",
blank=True,

View File

@ -15,7 +15,7 @@ from authentik.flows.models import in_memory_stage
from authentik.flows.planner import PLAN_CONTEXT_APPLICATION, PLAN_CONTEXT_SSO, FlowPlanner
from authentik.flows.views.executor import SESSION_KEY_POST
from authentik.lib.views import bad_request_message
from authentik.policies.views import PolicyAccessView
from authentik.policies.views import BufferedPolicyAccessView
from authentik.providers.saml.exceptions import CannotHandleAssertion
from authentik.providers.saml.models import SAMLBindings, SAMLProvider
from authentik.providers.saml.processors.authn_request_parser import AuthNRequestParser
@ -35,7 +35,7 @@ from authentik.stages.consent.stage import (
LOGGER = get_logger()
class SAMLSSOView(PolicyAccessView):
class SAMLSSOView(BufferedPolicyAccessView):
"""SAML SSO Base View, which plans a flow and injects our final stage.
Calls get/post handler."""
@ -83,7 +83,7 @@ class SAMLSSOView(PolicyAccessView):
def post(self, request: HttpRequest, application_slug: str) -> HttpResponse:
"""GET and POST use the same handler, but we can't
override .dispatch easily because PolicyAccessView's dispatch"""
override .dispatch easily because BufferedPolicyAccessView's dispatch"""
return self.get(request, application_slug)

View File

@ -243,6 +243,7 @@ class SCIMGroupClient(SCIMClient[Group, SCIMProviderGroup, SCIMGroupSchema]):
if user.value not in users_should:
users_to_remove.append(user.value)
# Check users that should be in the group and add them
if current_group.members is not None:
for user in users_should:
if len([x for x in current_group.members if x.value == user]) < 1:
users_to_add.append(user)

View File

@ -0,0 +1,35 @@
# Generated by Django 5.0.13 on 2025-03-31 13:53
import authentik.lib.models
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_sources_saml", "0017_fix_x509subjectname"),
]
operations = [
migrations.AlterField(
model_name="samlsource",
name="slo_url",
field=models.TextField(
blank=True,
default=None,
help_text="Optional URL if your IDP supports Single-Logout.",
null=True,
validators=[authentik.lib.models.DomainlessURLValidator(schemes=("http", "https"))],
verbose_name="SLO URL",
),
),
migrations.AlterField(
model_name="samlsource",
name="sso_url",
field=models.TextField(
help_text="URL that the initial Login request is sent to.",
validators=[authentik.lib.models.DomainlessURLValidator(schemes=("http", "https"))],
verbose_name="SSO URL",
),
),
]

View File

@ -20,6 +20,7 @@ from authentik.crypto.models import CertificateKeyPair
from authentik.flows.challenge import RedirectChallenge
from authentik.flows.models import Flow
from authentik.lib.expression.evaluator import BaseEvaluator
from authentik.lib.models import DomainlessURLValidator
from authentik.lib.utils.time import timedelta_string_validator
from authentik.sources.saml.processors.constants import (
DSA_SHA1,
@ -91,11 +92,13 @@ class SAMLSource(Source):
help_text=_("Also known as Entity ID. Defaults the Metadata URL."),
)
sso_url = models.URLField(
sso_url = models.TextField(
validators=[DomainlessURLValidator(schemes=("http", "https"))],
verbose_name=_("SSO URL"),
help_text=_("URL that the initial Login request is sent to."),
)
slo_url = models.URLField(
slo_url = models.TextField(
validators=[DomainlessURLValidator(schemes=("http", "https"))],
default=None,
blank=True,
null=True,

View File

@ -33,6 +33,7 @@ from authentik.flows.planner import (
)
from authentik.flows.stage import ChallengeStageView
from authentik.flows.views.executor import NEXT_ARG_NAME, SESSION_KEY_GET, SESSION_KEY_PLAN
from authentik.lib.utils.urls import is_url_absolute
from authentik.lib.views import bad_request_message
from authentik.providers.saml.utils.encoding import nice64
from authentik.sources.saml.exceptions import MissingSAMLResponse, UnsupportedNameIDFormat
@ -73,6 +74,8 @@ class InitiateView(View):
final_redirect = self.request.session.get(SESSION_KEY_GET, {}).get(
NEXT_ARG_NAME, "authentik_core:if-user"
)
if not is_url_absolute(final_redirect):
final_redirect = "authentik_core:if-user"
kwargs.update(
{
PLAN_CONTEXT_SSO: True,

File diff suppressed because one or more lines are too long

View File

@ -8,7 +8,7 @@ from django.core.mail.backends.locmem import EmailBackend
from django.urls import reverse
from authentik.core.models import User
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
from authentik.core.tests.utils import create_test_admin_user, create_test_flow, create_test_user
from authentik.events.models import Event, EventAction
from authentik.flows.markers import StageMarker
from authentik.flows.models import FlowDesignation, FlowStageBinding
@ -67,6 +67,36 @@ class TestEmailStageSending(FlowTestCase):
self.assertEqual(event.context["to_email"], [f"{self.user.name} <{self.user.email}>"])
self.assertEqual(event.context["from_email"], "system@authentik.local")
def test_newlines_long_name(self):
"""Test with pending user"""
plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()])
long_user = create_test_user()
long_user.name = "Test User\r\n Many Words\r\n"
long_user.save()
plan.context[PLAN_CONTEXT_PENDING_USER] = long_user
session = self.client.session
session[SESSION_KEY_PLAN] = plan
session.save()
Event.objects.filter(action=EventAction.EMAIL_SENT).delete()
url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
with patch(
"authentik.stages.email.models.EmailStage.backend_class",
PropertyMock(return_value=EmailBackend),
):
response = self.client.post(url)
self.assertEqual(response.status_code, 200)
self.assertStageResponse(
response,
self.flow,
response_errors={
"non_field_errors": [{"string": "email-sent", "code": "email-sent"}]
},
)
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(mail.outbox[0].subject, "authentik")
self.assertEqual(mail.outbox[0].to, [f"Test User Many Words <{long_user.email}>"])
def test_pending_fake_user(self):
"""Test with pending (fake) user"""
self.flow.designation = FlowDesignation.RECOVERY

View File

@ -32,7 +32,14 @@ class TemplateEmailMessage(EmailMultiAlternatives):
sanitized_to = []
# Ensure that all recipients are valid
for recipient_name, recipient_email in to:
sanitized_to.append(sanitize_address((recipient_name, recipient_email), "utf-8"))
# Remove any newline characters from name and email before sanitizing
clean_name = (
recipient_name.replace("\n", " ").replace("\r", " ") if recipient_name else ""
)
clean_email = (
recipient_email.replace("\n", "").replace("\r", "") if recipient_email else ""
)
sanitized_to.append(sanitize_address((clean_name, clean_email), "utf-8"))
super().__init__(to=sanitized_to, **kwargs)
if not template_name:
return

View File

@ -142,8 +142,18 @@ class IdentificationChallengeResponse(ChallengeResponse):
raise ValidationError("Failed to authenticate.")
self.pre_user = pre_user
# Captcha check
if captcha_stage := current_stage.captcha_stage:
captcha_token = attrs.get("captcha_token", None)
if not captcha_token:
self.stage.logger.warning("Token not set for captcha attempt")
verify_captcha_token(captcha_stage, captcha_token, client_ip)
# Password check
if current_stage.password_stage:
if not current_stage.password_stage:
# No password stage select, don't validate the password
return attrs
password = attrs.get("password", None)
if not password:
self.stage.logger.warning("Password not set for ident+auth attempt")
@ -164,13 +174,6 @@ class IdentificationChallengeResponse(ChallengeResponse):
self.pre_user = user
except PermissionDenied as exc:
raise ValidationError(str(exc)) from exc
# Captcha check
if captcha_stage := current_stage.captcha_stage:
captcha_token = attrs.get("captcha_token", None)
if not captcha_token:
self.stage.logger.warning("Token not set for captcha attempt")
verify_captcha_token(captcha_stage, captcha_token, client_ip)
return attrs

View File

@ -2,7 +2,7 @@
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "https://goauthentik.io/blueprints/schema.json",
"type": "object",
"title": "authentik 2025.2.2 Blueprint schema",
"title": "authentik 2025.2.3 Blueprint schema",
"required": [
"version",
"entries"
@ -6423,8 +6423,6 @@
},
"acs_url": {
"type": "string",
"format": "uri",
"maxLength": 200,
"minLength": 1,
"title": "ACS URL"
},
@ -8733,8 +8731,6 @@
},
"sso_url": {
"type": "string",
"format": "uri",
"maxLength": 200,
"minLength": 1,
"title": "SSO URL",
"description": "URL that the initial Login request is sent to."
@ -8744,8 +8740,6 @@
"string",
"null"
],
"format": "uri",
"maxLength": 200,
"title": "SLO URL",
"description": "Optional URL if your IDP supports Single-Logout."
},
@ -13016,6 +13010,15 @@
"minLength": 1,
"title": "Branding favicon"
},
"branding_custom_css": {
"type": "string",
"title": "Branding custom css"
},
"branding_default_flow_background": {
"type": "string",
"minLength": 1,
"title": "Branding default flow background"
},
"flow_authentication": {
"type": "string",
"format": "uuid",
@ -14897,9 +14900,15 @@
"type": "string",
"title": "Webhook url"
},
"webhook_mapping": {
"webhook_mapping_body": {
"type": "integer",
"title": "Webhook mapping"
"title": "Webhook mapping body",
"description": "Customize the body of the request. Mapping should return data that is JSON-serializable."
},
"webhook_mapping_headers": {
"type": "integer",
"title": "Webhook mapping headers",
"description": "Configure additional headers to be sent. Mapping should return a dictionary of key-value pairs"
},
"send_once": {
"type": "boolean",

View File

@ -31,7 +31,7 @@ services:
volumes:
- redis:/data
server:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.2.2}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.2.3}
restart: unless-stopped
command: server
environment:
@ -54,7 +54,7 @@ services:
redis:
condition: service_healthy
worker:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.2.2}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.2.3}
restart: unless-stopped
command: worker
environment:

11
go.mod
View File

@ -1,9 +1,6 @@
module goauthentik.io
go 1.23.0
toolchain go1.24.0
go 1.24.0
require (
beryju.io/ldap v0.1.0
github.com/coreos/go-oidc/v3 v3.13.0
@ -11,7 +8,7 @@ require (
github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1
github.com/go-ldap/ldap/v3 v3.4.10
github.com/go-openapi/runtime v0.28.0
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/golang-jwt/jwt/v5 v5.2.2
github.com/google/uuid v1.6.0
github.com/gorilla/handlers v1.5.2
github.com/gorilla/mux v1.8.1
@ -23,13 +20,13 @@ require (
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484
github.com/pires/go-proxyproto v0.8.0
github.com/prometheus/client_golang v1.21.1
github.com/redis/go-redis/v9 v9.7.1
github.com/redis/go-redis/v9 v9.7.3
github.com/sethvargo/go-envconfig v1.1.1
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.9.1
github.com/stretchr/testify v1.10.0
github.com/wwt/guac v1.3.2
goauthentik.io/api/v3 v3.2025022.3
goauthentik.io/api/v3 v3.2025023.2
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
golang.org/x/oauth2 v0.28.0
golang.org/x/sync v0.12.0

12
go.sum
View File

@ -113,8 +113,8 @@ github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+Gr
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58=
github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@ -248,8 +248,8 @@ github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/redis/go-redis/v9 v9.7.1 h1:4LhKRCIduqXqtvCUlaq9c8bdHOkICjDMrr1+Zb3osAc=
github.com/redis/go-redis/v9 v9.7.1/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
@ -299,8 +299,8 @@ go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
goauthentik.io/api/v3 v3.2025022.3 h1:cipaxl0il4/s1fU2f6+CD7nzgAktbV0XD7r5qHh0fUc=
goauthentik.io/api/v3 v3.2025022.3/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
goauthentik.io/api/v3 v3.2025023.2 h1:4XHlnykN5jQH78liQ4cp2Jf8eigvQImIJp+A+bsq1nA=
goauthentik.io/api/v3 v3.2025023.2/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=

View File

@ -162,13 +162,14 @@ func (c *Config) parseScheme(rawVal string) string {
if err != nil {
return rawVal
}
if u.Scheme == "env" {
switch u.Scheme {
case "env":
e, ok := os.LookupEnv(u.Host)
if ok {
return e
}
return u.RawQuery
} else if u.Scheme == "file" {
case "file":
d, err := os.ReadFile(u.Path)
if err != nil {
return u.RawQuery

View File

@ -10,7 +10,7 @@ import (
)
func TestConfigEnv(t *testing.T) {
os.Setenv("AUTHENTIK_SECRET_KEY", "bar")
assert.NoError(t, os.Setenv("AUTHENTIK_SECRET_KEY", "bar"))
cfg = nil
if err := Get().fromEnv(); err != nil {
panic(err)
@ -19,8 +19,8 @@ func TestConfigEnv(t *testing.T) {
}
func TestConfigEnv_Scheme(t *testing.T) {
os.Setenv("foo", "bar")
os.Setenv("AUTHENTIK_SECRET_KEY", "env://foo")
assert.NoError(t, os.Setenv("foo", "bar"))
assert.NoError(t, os.Setenv("AUTHENTIK_SECRET_KEY", "env://foo"))
cfg = nil
if err := Get().fromEnv(); err != nil {
panic(err)
@ -33,13 +33,15 @@ func TestConfigEnv_File(t *testing.T) {
if err != nil {
log.Fatal(err)
}
defer os.Remove(file.Name())
defer func() {
assert.NoError(t, os.Remove(file.Name()))
}()
_, err = file.Write([]byte("bar"))
if err != nil {
panic(err)
}
os.Setenv("AUTHENTIK_SECRET_KEY", fmt.Sprintf("file://%s", file.Name()))
assert.NoError(t, os.Setenv("AUTHENTIK_SECRET_KEY", fmt.Sprintf("file://%s", file.Name())))
cfg = nil
if err := Get().fromEnv(); err != nil {
panic(err)

View File

@ -29,4 +29,4 @@ func UserAgent() string {
return fmt.Sprintf("authentik@%s", FullVersion())
}
const VERSION = "2025.2.2"
const VERSION = "2025.2.3"

View File

@ -1,5 +0,0 @@
//go:build requirefips
package backend
var FipsEnabled = true

View File

@ -1,5 +0,0 @@
//go:build !requirefips
package backend
var FipsEnabled = false

View File

@ -35,7 +35,7 @@ func EnableDebugServer() {
if err != nil {
return nil
}
_, err = w.Write([]byte(fmt.Sprintf("<a href='%[1]s'>%[1]s</a><br>", tpl)))
_, err = fmt.Fprintf(w, "<a href='%[1]s'>%[1]s</a><br>", tpl)
if err != nil {
l.WithError(err).Warning("failed to write index")
return nil

View File

@ -44,10 +44,11 @@ func New(healthcheck func() bool) *GoUnicorn {
signal.Notify(c, syscall.SIGHUP, syscall.SIGUSR2)
go func() {
for sig := range c {
if sig == syscall.SIGHUP {
switch sig {
case syscall.SIGHUP:
g.log.Info("SIGHUP received, forwarding to gunicorn")
g.Reload()
} else if sig == syscall.SIGUSR2 {
case syscall.SIGUSR2:
g.log.Info("SIGUSR2 received, restarting gunicorn")
g.Restart()
}

View File

@ -2,6 +2,7 @@ package ak
import (
"context"
"crypto/fips140"
"fmt"
"math/rand"
"net/http"
@ -203,7 +204,7 @@ func (a *APIController) getWebsocketPingArgs() map[string]interface{} {
"golangVersion": runtime.Version(),
"opensslEnabled": cryptobackend.OpensslEnabled,
"opensslVersion": cryptobackend.OpensslVersion(),
"fipsEnabled": cryptobackend.FipsEnabled,
"fipsEnabled": fips140.Enabled(),
}
hostname, err := os.Hostname()
if err == nil {

View File

@ -35,13 +35,19 @@ func Paginator[Tobj any, Treq any, Tres PaginatorResponse[Tobj]](
req PaginatorRequest[Treq, Tres],
opts PaginatorOptions,
) ([]Tobj, error) {
if opts.Logger == nil {
opts.Logger = log.NewEntry(log.StandardLogger())
}
var bfreq, cfreq interface{}
fetchOffset := func(page int32) (Tres, error) {
bfreq = req.Page(page)
cfreq = bfreq.(PaginatorRequest[Treq, Tres]).PageSize(int32(opts.PageSize))
res, _, err := cfreq.(PaginatorRequest[Treq, Tres]).Execute()
res, hres, err := cfreq.(PaginatorRequest[Treq, Tres]).Execute()
if err != nil {
opts.Logger.WithError(err).WithField("page", page).Warning("failed to fetch page")
if hres != nil && hres.StatusCode >= 400 && hres.StatusCode < 500 {
return res, err
}
}
return res, err
}
@ -51,6 +57,9 @@ func Paginator[Tobj any, Treq any, Tres PaginatorResponse[Tobj]](
for {
apiObjects, err := fetchOffset(page)
if err != nil {
if page == 1 {
return objects, err
}
errs = append(errs, err)
continue
}

View File

@ -1,5 +1,64 @@
package ak
import (
"errors"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"goauthentik.io/api/v3"
)
type fakeAPIType struct{}
type fakeAPIResponse struct {
results []fakeAPIType
pagination api.Pagination
}
func (fapi *fakeAPIResponse) GetResults() []fakeAPIType { return fapi.results }
func (fapi *fakeAPIResponse) GetPagination() api.Pagination { return fapi.pagination }
type fakeAPIRequest struct {
res *fakeAPIResponse
http *http.Response
err error
}
func (fapi *fakeAPIRequest) Page(page int32) *fakeAPIRequest { return fapi }
func (fapi *fakeAPIRequest) PageSize(size int32) *fakeAPIRequest { return fapi }
func (fapi *fakeAPIRequest) Execute() (*fakeAPIResponse, *http.Response, error) {
return fapi.res, fapi.http, fapi.err
}
func Test_Simple(t *testing.T) {
req := &fakeAPIRequest{
res: &fakeAPIResponse{
results: []fakeAPIType{
{},
},
pagination: api.Pagination{
TotalPages: 1,
},
},
}
res, err := Paginator(req, PaginatorOptions{})
assert.NoError(t, err)
assert.Len(t, res, 1)
}
func Test_BadRequest(t *testing.T) {
req := &fakeAPIRequest{
http: &http.Response{
StatusCode: 400,
},
err: errors.New("foo"),
}
res, err := Paginator(req, PaginatorOptions{})
assert.Error(t, err)
assert.Equal(t, []fakeAPIType{}, res)
}
// func Test_PaginatorCompile(t *testing.T) {
// req := api.ApiCoreUsersListRequest{}
// Paginator(req, PaginatorOptions{

View File

@ -148,7 +148,8 @@ func (ac *APIController) startWSHandler() {
"outpost_type": ac.Server.Type(),
"uuid": ac.instanceUUID.String(),
}).Set(1)
if wsMsg.Instruction == WebsocketInstructionTriggerUpdate {
switch wsMsg.Instruction {
case WebsocketInstructionTriggerUpdate:
time.Sleep(ac.reloadOffset)
logger.Debug("Got update trigger...")
err := ac.OnRefresh()
@ -163,7 +164,7 @@ func (ac *APIController) startWSHandler() {
"build": constants.BUILD(""),
}).SetToCurrentTime()
}
} else if wsMsg.Instruction == WebsocketInstructionProviderSpecific {
case WebsocketInstructionProviderSpecific:
for _, h := range ac.wsHandlers {
h(context.Background(), wsMsg.Args)
}

View File

@ -66,7 +66,12 @@ func (ls *LDAPServer) StartLDAPServer() error {
return err
}
proxyListener := &proxyproto.Listener{Listener: ln, ConnPolicy: utils.GetProxyConnectionPolicy()}
defer proxyListener.Close()
defer func() {
err := proxyListener.Close()
if err != nil {
ls.log.WithError(err).Warning("failed to close proxy listener")
}
}()
ls.log.WithField("listen", listen).Info("Starting LDAP server")
err = ls.s.Serve(proxyListener)

View File

@ -49,7 +49,12 @@ func (ls *LDAPServer) StartLDAPTLSServer() error {
}
proxyListener := &proxyproto.Listener{Listener: ln, ConnPolicy: utils.GetProxyConnectionPolicy()}
defer proxyListener.Close()
defer func() {
err := proxyListener.Close()
if err != nil {
ls.log.WithError(err).Warning("failed to close proxy listener")
}
}()
tln := tls.NewListener(proxyListener, tlsConfig)

View File

@ -98,7 +98,7 @@ func (ms *MemorySearcher) Search(req *search.Request) (ldap.ServerSearchResult,
entries := make([]*ldap.Entry, 0)
scope := req.SearchRequest.Scope
scope := req.Scope
needUsers, needGroups := ms.si.GetNeededObjects(scope, req.BaseDN, req.FilterObjectClass)
if scope >= 0 && strings.EqualFold(req.BaseDN, baseDN) {

View File

@ -56,7 +56,7 @@ func GetOIDCEndpoint(p api.ProxyOutpostConfig, authentikHost string, embedded bo
if !embedded && hostBrowser == "" {
return ep
}
var newHost *url.URL = aku
var newHost = aku
var newBrowserHost *url.URL
if embedded {
if authentikHost == "" {

View File

@ -130,7 +130,12 @@ func (ps *ProxyServer) ServeHTTP() {
return
}
proxyListener := &proxyproto.Listener{Listener: listener, ConnPolicy: utils.GetProxyConnectionPolicy()}
defer proxyListener.Close()
defer func() {
err := proxyListener.Close()
if err != nil {
ps.log.WithError(err).Warning("failed to close proxy listener")
}
}()
ps.log.WithField("listen", listenAddress).Info("Starting HTTP server")
ps.serve(proxyListener)
@ -149,7 +154,12 @@ func (ps *ProxyServer) ServeHTTPS() {
return
}
proxyListener := &proxyproto.Listener{Listener: web.TCPKeepAliveListener{TCPListener: ln.(*net.TCPListener)}, ConnPolicy: utils.GetProxyConnectionPolicy()}
defer proxyListener.Close()
defer func() {
err := proxyListener.Close()
if err != nil {
ps.log.WithError(err).Warning("failed to close proxy listener")
}
}()
tlsListener := tls.NewListener(proxyListener, tlsConfig)
ps.log.WithField("listen", listenAddress).Info("Starting HTTPS server")

View File

@ -72,12 +72,14 @@ func (s *RedisStore) New(r *http.Request, name string) (*sessions.Session, error
session.ID = c.Value
err = s.load(r.Context(), session)
if err == nil {
session.IsNew = false
} else if err == redis.Nil {
err = nil // no data stored
if err != nil {
if errors.Is(err, redis.Nil) {
return session, nil
}
return session, err
}
session.IsNew = false
return session, err
}
// Save adds a single session to the response.

View File

@ -8,7 +8,6 @@
<link rel="shortcut icon" type="image/png" href="/outpost.goauthentik.io/static/dist/assets/icons/icon.png">
<link rel="stylesheet" type="text/css" href="/outpost.goauthentik.io/static/dist/patternfly.min.css">
<link rel="stylesheet" type="text/css" href="/outpost.goauthentik.io/static/dist/authentik.css">
<link rel="stylesheet" type="text/css" href="/outpost.goauthentik.io/static/dist/custom.css">
<link rel="prefetch" href="/outpost.goauthentik.io/static/dist/assets/images/flow_background.jpg" />
<style>
.pf-c-background-image::before {

View File

@ -156,7 +156,12 @@ func (ws *WebServer) listenPlain() {
return
}
proxyListener := &proxyproto.Listener{Listener: ln, ConnPolicy: utils.GetProxyConnectionPolicy()}
defer proxyListener.Close()
defer func() {
err := proxyListener.Close()
if err != nil {
ws.log.WithError(err).Warning("failed to close proxy listener")
}
}()
ws.log.WithField("listen", config.Get().Listen.HTTP).Info("Starting HTTP server")
ws.serve(proxyListener)

View File

@ -46,7 +46,12 @@ func (ws *WebServer) listenTLS() {
return
}
proxyListener := &proxyproto.Listener{Listener: web.TCPKeepAliveListener{TCPListener: ln.(*net.TCPListener)}, ConnPolicy: utils.GetProxyConnectionPolicy()}
defer proxyListener.Close()
defer func() {
err := proxyListener.Close()
if err != nil {
ws.log.WithError(err).Warning("failed to close proxy listener")
}
}()
tlsListener := tls.NewListener(proxyListener, tlsConfig)
ws.log.WithField("listen", config.Get().Listen.HTTPS).Info("Starting HTTPS server")

View File

@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1
# Stage 1: Build
FROM --platform=${BUILDPLATFORM} mcr.microsoft.com/oss/go/microsoft/golang:1.23-fips-bookworm AS builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.24-bookworm AS builder
ARG TARGETOS
ARG TARGETARCH
@ -27,7 +27,7 @@ COPY . .
RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
--mount=type=cache,id=go-build-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/root/.cache/go-build \
if [ "$TARGETARCH" = "arm64" ]; then export CC=aarch64-linux-gnu-gcc && export CC_FOR_TARGET=gcc-aarch64-linux-gnu; fi && \
CGO_ENABLED=1 GOEXPERIMENT="systemcrypto" GOFLAGS="-tags=requirefips" GOARM="${TARGETVARIANT#v}" \
CGO_ENABLED=1 GOFIPS140=latest GOARM="${TARGETVARIANT#v}" \
go build -o /go/ldap ./cmd/ldap
# Stage 2: Run

View File

@ -9,7 +9,7 @@
"version": "0.0.0",
"license": "MIT",
"devDependencies": {
"aws-cdk": "^2.1005.0",
"aws-cdk": "^2.1006.0",
"cross-env": "^7.0.3"
},
"engines": {
@ -17,9 +17,9 @@
}
},
"node_modules/aws-cdk": {
"version": "2.1005.0",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1005.0.tgz",
"integrity": "sha512-4ejfGGrGCEl0pg1xcqkxK0lpBEZqNI48wtrXhk6dYOFYPYMZtqn1kdla29ONN+eO2unewkNF4nLP1lPYhlf9Pg==",
"version": "2.1006.0",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1006.0.tgz",
"integrity": "sha512-6qYnCt4mBN+3i/5F+FC2yMETkDHY/IL7gt3EuqKVPcaAO4jU7oXfVSlR60CYRkZWL4fnAurUV14RkJuJyVG/IA==",
"dev": true,
"license": "Apache-2.0",
"bin": {

View File

@ -10,7 +10,7 @@
"node": ">=20"
},
"devDependencies": {
"aws-cdk": "^2.1005.0",
"aws-cdk": "^2.1006.0",
"cross-env": "^7.0.3"
}
}

View File

@ -26,7 +26,7 @@ Parameters:
Description: authentik Docker image
AuthentikVersion:
Type: String
Default: 2025.2.2
Default: 2025.2.3
Description: authentik Docker image tag
AuthentikServerCPU:
Type: Number

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-03-13 00:10+0000\n"
"POT-Creation-Date: 2025-03-31 00:10+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -616,6 +616,18 @@ msgstr ""
msgid "Email"
msgstr ""
#: authentik/events/models.py
msgid ""
"Customize the body of the request. Mapping should return data that is JSON-"
"serializable."
msgstr ""
#: authentik/events/models.py
msgid ""
"Configure additional headers to be sent. Mapping should return a dictionary "
"of key-value pairs"
msgstr ""
#: authentik/events/models.py
msgid ""
"Only send notification once, for example when sending a webhook into a chat "
@ -1208,6 +1220,20 @@ msgstr ""
msgid "Reputation Scores"
msgstr ""
#: authentik/policies/templates/policies/buffer.html
msgid "Waiting for authentication..."
msgstr ""
#: authentik/policies/templates/policies/buffer.html
msgid ""
"You're already authenticating in another tab. This page will refresh once "
"authentication is completed."
msgstr ""
#: authentik/policies/templates/policies/buffer.html
msgid "Authenticate in this tab"
msgstr ""
#: authentik/policies/templates/policies/denied.html
msgid "Permission denied"
msgstr ""
@ -1756,6 +1782,17 @@ msgid ""
"NameIDPolicy of the incoming request will be considered"
msgstr ""
#: authentik/providers/saml/models.py
msgid "AuthnContextClassRef Property Mapping"
msgstr ""
#: authentik/providers/saml/models.py
msgid ""
"Configure how the AuthnContextClassRef value will be created. When left "
"empty, the AuthnContextClassRef will be set based on which authentication "
"methods the user used to authenticate."
msgstr ""
#: authentik/providers/saml/models.py
msgid ""
"Assertion valid not before current time + this value (Format: hours=-1;"

View File

@ -10,8 +10,8 @@
# Manuel Viens, 2023
# Mordecai, 2023
# nerdinator <florian.dupret@gmail.com>, 2024
# Tina, 2024
# Charles Leclerc, 2025
# Tina, 2025
# Marc Schmitt, 2025
#
#, fuzzy
@ -19,7 +19,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-03-13 00:10+0000\n"
"POT-Creation-Date: 2025-03-31 00:10+0000\n"
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
"Last-Translator: Marc Schmitt, 2025\n"
"Language-Team: French (https://app.transifex.com/authentik/teams/119923/fr/)\n"
@ -676,6 +676,22 @@ msgstr "Webhook Slack (ou Discord)"
msgid "Email"
msgstr "Courriel"
#: authentik/events/models.py
msgid ""
"Customize the body of the request. Mapping should return data that is JSON-"
"serializable."
msgstr ""
"Personnalise le corps de la requête. Le mappage doit renvoyer des données "
"sérialisables en JSON."
#: authentik/events/models.py
msgid ""
"Configure additional headers to be sent. Mapping should return a dictionary "
"of key-value pairs"
msgstr ""
"Configure les en-têtes supplémentaires à envoyer. Le mappage doit renvoyer "
"un dictionnaire de paires clé-valeur."
#: authentik/events/models.py
msgid ""
"Only send notification once, for example when sending a webhook into a chat "
@ -1331,6 +1347,22 @@ msgstr "Score de Réputation"
msgid "Reputation Scores"
msgstr "Scores de Réputation"
#: authentik/policies/templates/policies/buffer.html
msgid "Waiting for authentication..."
msgstr "En attente de l'authentification..."
#: authentik/policies/templates/policies/buffer.html
msgid ""
"You're already authenticating in another tab. This page will refresh once "
"authentication is completed."
msgstr ""
"Vous êtes déjà en cours d'authentification dans un autre onglet. Cette page "
"se rafraîchira lorsque l'authentification sera terminée."
#: authentik/policies/templates/policies/buffer.html
msgid "Authenticate in this tab"
msgstr "S'authentifier dans cet onglet"
#: authentik/policies/templates/policies/denied.html
msgid "Permission denied"
msgstr "Permission refusée"
@ -1956,6 +1988,20 @@ msgstr ""
"Configure la manière dont la valeur NameID sera créée. Si laissé vide, la "
"NameIDPolicy de la requête entrante sera prise en compte"
#: authentik/providers/saml/models.py
msgid "AuthnContextClassRef Property Mapping"
msgstr "Mappage de propriété AuthnContextClassRef"
#: authentik/providers/saml/models.py
msgid ""
"Configure how the AuthnContextClassRef value will be created. When left "
"empty, the AuthnContextClassRef will be set based on which authentication "
"methods the user used to authenticate."
msgstr ""
"Configure comment la valeur AuthnContextClassRef sera créée. Lorsque non "
"sélectionné, AuthnContextClassRef sera défini en fonction de quelle méthode "
"d'authentification l'utilisateur a utilisé pour s'authentifier."
#: authentik/providers/saml/models.py
msgid ""
"Assertion valid not before current time + this value (Format: "

Binary file not shown.

View File

@ -15,7 +15,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-03-13 00:10+0000\n"
"POT-Creation-Date: 2025-03-22 00:10+0000\n"
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
"Last-Translator: deluxghost, 2025\n"
"Language-Team: Chinese Simplified (https://app.transifex.com/authentik/teams/119923/zh-Hans/)\n"
@ -627,6 +627,18 @@ msgstr "Slack WebhookSlack/Discord"
msgid "Email"
msgstr "电子邮箱"
#: authentik/events/models.py
msgid ""
"Customize the body of the request. Mapping should return data that is JSON-"
"serializable."
msgstr "自定义请求体。映射应该返回 JSON 序列化的数据。"
#: authentik/events/models.py
msgid ""
"Configure additional headers to be sent. Mapping should return a dictionary "
"of key-value pairs"
msgstr "配置要发送的额外标头。映射应该返回键值对字典。"
#: authentik/events/models.py
msgid ""
"Only send notification once, for example when sending a webhook into a chat "
@ -1782,6 +1794,18 @@ msgid ""
"NameIDPolicy of the incoming request will be considered"
msgstr "配置如何创建 NameID 值。如果留空,将考虑传入请求的 NameIDPolicy"
#: authentik/providers/saml/models.py
msgid "AuthnContextClassRef Property Mapping"
msgstr "AuthnContextClassRef 属性映射"
#: authentik/providers/saml/models.py
msgid ""
"Configure how the AuthnContextClassRef value will be created. When left "
"empty, the AuthnContextClassRef will be set based on which authentication "
"methods the user used to authenticate."
msgstr ""
"配置如何创建 AuthnContextClassRef 值。留空时AuthnContextClassRef 会基于用户使用的身份验证方式设置。"
#: authentik/providers/saml/models.py
msgid ""
"Assertion valid not before current time + this value (Format: "

Binary file not shown.

View File

@ -14,7 +14,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-03-13 00:10+0000\n"
"POT-Creation-Date: 2025-03-31 00:10+0000\n"
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
"Last-Translator: deluxghost, 2025\n"
"Language-Team: Chinese (China) (https://app.transifex.com/authentik/teams/119923/zh_CN/)\n"
@ -626,6 +626,18 @@ msgstr "Slack WebhookSlack/Discord"
msgid "Email"
msgstr "电子邮箱"
#: authentik/events/models.py
msgid ""
"Customize the body of the request. Mapping should return data that is JSON-"
"serializable."
msgstr "自定义请求体。映射应该返回 JSON 序列化的数据。"
#: authentik/events/models.py
msgid ""
"Configure additional headers to be sent. Mapping should return a dictionary "
"of key-value pairs"
msgstr "配置要发送的额外标头。映射应该返回键值对字典。"
#: authentik/events/models.py
msgid ""
"Only send notification once, for example when sending a webhook into a chat "
@ -1222,6 +1234,20 @@ msgstr "信誉分数"
msgid "Reputation Scores"
msgstr "信誉分数"
#: authentik/policies/templates/policies/buffer.html
msgid "Waiting for authentication..."
msgstr "正在等待身份验证…"
#: authentik/policies/templates/policies/buffer.html
msgid ""
"You're already authenticating in another tab. This page will refresh once "
"authentication is completed."
msgstr "您正在另一个标签页中验证身份。身份验证完成后,此页面会刷新。"
#: authentik/policies/templates/policies/buffer.html
msgid "Authenticate in this tab"
msgstr "在此标签页中验证身份"
#: authentik/policies/templates/policies/denied.html
msgid "Permission denied"
msgstr "权限被拒绝"
@ -1781,6 +1807,18 @@ msgid ""
"NameIDPolicy of the incoming request will be considered"
msgstr "配置如何创建 NameID 值。如果留空,将考虑传入请求的 NameIDPolicy"
#: authentik/providers/saml/models.py
msgid "AuthnContextClassRef Property Mapping"
msgstr "AuthnContextClassRef 属性映射"
#: authentik/providers/saml/models.py
msgid ""
"Configure how the AuthnContextClassRef value will be created. When left "
"empty, the AuthnContextClassRef will be set based on which authentication "
"methods the user used to authenticate."
msgstr ""
"配置如何创建 AuthnContextClassRef 值。留空时AuthnContextClassRef 会基于用户使用的身份验证方式设置。"
#: authentik/providers/saml/models.py
msgid ""
"Assertion valid not before current time + this value (Format: "

View File

@ -1,5 +1,5 @@
{
"name": "@goauthentik/authentik",
"version": "2025.2.2",
"version": "2025.2.3",
"private": true
}

View File

@ -17,7 +17,7 @@ COPY web .
RUN npm run build-proxy
# Stage 2: Build
FROM --platform=${BUILDPLATFORM} mcr.microsoft.com/oss/go/microsoft/golang:1.23-fips-bookworm AS builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.24-bookworm AS builder
ARG TARGETOS
ARG TARGETARCH
@ -43,7 +43,7 @@ COPY . .
RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
--mount=type=cache,id=go-build-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/root/.cache/go-build \
if [ "$TARGETARCH" = "arm64" ]; then export CC=aarch64-linux-gnu-gcc && export CC_FOR_TARGET=gcc-aarch64-linux-gnu; fi && \
CGO_ENABLED=1 GOEXPERIMENT="systemcrypto" GOFLAGS="-tags=requirefips" GOARM="${TARGETVARIANT#v}" \
CGO_ENABLED=1 GOFIPS140=latest GOARM="${TARGETVARIANT#v}" \
go build -o /go/proxy ./cmd/proxy
# Stage 3: Run

View File

@ -1,6 +1,6 @@
[project]
name = "authentik"
version = "2025.2.2"
version = "2025.2.3"
description = ""
authors = [{ name = "authentik Team", email = "hello@goauthentik.io" }]
requires-python = "==3.12.*"
@ -103,7 +103,7 @@ dev = [
[tool.uv.sources]
django-tenants = { git = "https://github.com/rissson/django-tenants.git", branch = "authentik-fixes" }
opencontainers = { git = "https://github.com/vsoch/oci-python", rev = "20d69d9cc50a0fef31605b46f06da0c94f1ec3cf" }
opencontainers = { git = "https://github.com/BeryJu/oci-python", rev = "c791b19056769cd67957322806809ab70f5bead8" }
[project.scripts]
ak = "lifecycle.ak:main"

View File

@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1
# Stage 1: Build
FROM --platform=${BUILDPLATFORM} mcr.microsoft.com/oss/go/microsoft/golang:1.23-fips-bookworm AS builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.24-bookworm AS builder
ARG TARGETOS
ARG TARGETARCH
@ -27,7 +27,7 @@ COPY . .
RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
--mount=type=cache,id=go-build-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/root/.cache/go-build \
if [ "$TARGETARCH" = "arm64" ]; then export CC=aarch64-linux-gnu-gcc && export CC_FOR_TARGET=gcc-aarch64-linux-gnu; fi && \
CGO_ENABLED=1 GOEXPERIMENT="systemcrypto" GOFLAGS="-tags=requirefips" GOARM="${TARGETVARIANT#v}" \
CGO_ENABLED=1 GOFIPS140=latest GOARM="${TARGETVARIANT#v}" \
go build -o /go/rac ./cmd/rac
# Stage 2: Run

View File

@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1
# Stage 1: Build
FROM --platform=${BUILDPLATFORM} mcr.microsoft.com/oss/go/microsoft/golang:1.23-fips-bookworm AS builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.24-bookworm AS builder
ARG TARGETOS
ARG TARGETARCH
@ -27,7 +27,7 @@ COPY . .
RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
--mount=type=cache,id=go-build-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/root/.cache/go-build \
if [ "$TARGETARCH" = "arm64" ]; then export CC=aarch64-linux-gnu-gcc && export CC_FOR_TARGET=gcc-aarch64-linux-gnu; fi && \
CGO_ENABLED=1 GOEXPERIMENT="systemcrypto" GOFLAGS="-tags=requirefips" GOARM="${TARGETVARIANT#v}" \
CGO_ENABLED=1 GOFIPS140=latest GOARM="${TARGETVARIANT#v}" \
go build -o /go/radius ./cmd/radius
# Stage 2: Run

View File

@ -1,7 +1,7 @@
openapi: 3.0.3
info:
title: authentik
version: 2025.2.2
version: 2025.2.3
description: Making authentication simple.
contact:
email: hello@goauthentik.io
@ -4447,6 +4447,10 @@ paths:
schema:
type: string
format: uuid
- in: query
name: branding_default_flow_background
schema:
type: string
- in: query
name: branding_favicon
schema:
@ -41145,6 +41149,10 @@ components:
type: string
branding_favicon:
type: string
branding_custom_css:
type: string
branding_default_flow_background:
type: string
flow_authentication:
type: string
format: uuid
@ -41204,6 +41212,11 @@ components:
branding_favicon:
type: string
minLength: 1
branding_custom_css:
type: string
branding_default_flow_background:
type: string
minLength: 1
flow_authentication:
type: string
format: uuid
@ -42096,6 +42109,8 @@ components:
type: string
branding_favicon:
type: string
branding_custom_css:
type: string
ui_footer_links:
type: array
items:
@ -42122,6 +42137,7 @@ components:
type: string
readOnly: true
required:
- branding_custom_css
- branding_favicon
- branding_logo
- branding_title
@ -46874,10 +46890,18 @@ components:
webhook_url:
type: string
format: uri
webhook_mapping:
webhook_mapping_body:
type: string
format: uuid
nullable: true
description: Customize the body of the request. Mapping should return data
that is JSON-serializable.
webhook_mapping_headers:
type: string
format: uuid
nullable: true
description: Configure additional headers to be sent. Mapping should return
a dictionary of key-value pairs
send_once:
type: boolean
description: Only send notification once, for example when sending a webhook
@ -46905,10 +46929,18 @@ components:
webhook_url:
type: string
format: uri
webhook_mapping:
webhook_mapping_body:
type: string
format: uuid
nullable: true
description: Customize the body of the request. Mapping should return data
that is JSON-serializable.
webhook_mapping_headers:
type: string
format: uuid
nullable: true
description: Configure additional headers to be sent. Mapping should return
a dictionary of key-value pairs
send_once:
type: boolean
description: Only send notification once, for example when sending a webhook
@ -50125,6 +50157,11 @@ components:
branding_favicon:
type: string
minLength: 1
branding_custom_css:
type: string
branding_default_flow_background:
type: string
minLength: 1
flow_authentication:
type: string
format: uuid
@ -51337,10 +51374,18 @@ components:
webhook_url:
type: string
format: uri
webhook_mapping:
webhook_mapping_body:
type: string
format: uuid
nullable: true
description: Customize the body of the request. Mapping should return data
that is JSON-serializable.
webhook_mapping_headers:
type: string
format: uuid
nullable: true
description: Configure additional headers to be sent. Mapping should return
a dictionary of key-value pairs
send_once:
type: boolean
description: Only send notification once, for example when sending a webhook
@ -52200,9 +52245,8 @@ components:
format: uuid
acs_url:
type: string
format: uri
minLength: 1
maxLength: 200
format: uri
audience:
type: string
description: Value of the audience restriction field of the assertion. When
@ -52359,16 +52403,14 @@ components:
description: Also known as Entity ID. Defaults the Metadata URL.
sso_url:
type: string
format: uri
minLength: 1
description: URL that the initial Login request is sent to.
maxLength: 200
format: uri
slo_url:
type: string
format: uri
nullable: true
description: Optional URL if your IDP supports Single-Logout.
maxLength: 200
format: uri
allow_idp_initiated:
type: boolean
description: Allows authentication flows initiated by the IdP. This can
@ -55169,7 +55211,6 @@ components:
acs_url:
type: string
format: uri
maxLength: 200
audience:
type: string
description: Value of the audience restriction field of the assertion. When
@ -55336,9 +55377,8 @@ components:
format: uuid
acs_url:
type: string
format: uri
minLength: 1
maxLength: 200
format: uri
audience:
type: string
description: Value of the audience restriction field of the assertion. When
@ -55511,15 +55551,13 @@ components:
description: Also known as Entity ID. Defaults the Metadata URL.
sso_url:
type: string
format: uri
description: URL that the initial Login request is sent to.
maxLength: 200
format: uri
slo_url:
type: string
format: uri
nullable: true
description: Optional URL if your IDP supports Single-Logout.
maxLength: 200
format: uri
allow_idp_initiated:
type: boolean
description: Allows authentication flows initiated by the IdP. This can
@ -55702,16 +55740,14 @@ components:
description: Also known as Entity ID. Defaults the Metadata URL.
sso_url:
type: string
format: uri
minLength: 1
description: URL that the initial Login request is sent to.
maxLength: 200
format: uri
slo_url:
type: string
format: uri
nullable: true
description: Optional URL if your IDP supports Single-Logout.
maxLength: 200
format: uri
allow_idp_initiated:
type: boolean
description: Allows authentication flows initiated by the IdP. This can

View File

@ -5,9 +5,12 @@ from yaml import safe_dump
from authentik.lib.generators import generate_id
with open("local.env.yml", "w", encoding="utf-8") as _config:
safe_dump(
{
def generate_local_config():
"""Generate a local development configuration"""
# TODO: This should be generated and validated against a schema, such as Pydantic.
return {
"debug": True,
"log_level": "debug",
"secret_key": generate_id(),
@ -43,7 +46,44 @@ with open("local.env.yml", "w", encoding="utf-8") as _config:
"enabled": False,
"api_key": generate_id(),
},
},
}
if __name__ == "__main__":
config_file_name = "local.env.yml"
with open(config_file_name, "w", encoding="utf-8") as _config:
_config.write(
"""
# Local authentik configuration overrides
#
# https://docs.goauthentik.io/docs/install-config/configuration/
#
# To regenerate this file, run the following command from the repository root:
#
# ```shell
# make gen-dev-config
# ```
"""
)
safe_dump(
generate_local_config(),
_config,
default_flow_style=False,
)
print(
f"""
---
Generated configuration file: {config_file_name}
For more information on how to use this configuration, see:
https://docs.goauthentik.io/docs/install-config/configuration/
---
"""
)

View File

@ -410,3 +410,77 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
self.driver.find_element(By.CSS_SELECTOR, "header > h1").text,
"Permission denied",
)
@retry()
@apply_blueprint(
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint("default/flow-default-provider-authorization-implicit-consent.yaml")
@apply_blueprint("system/providers-oauth2.yaml")
@reconcile_app("authentik_crypto")
def test_authorization_consent_implied_parallel(self):
"""test OpenID Provider flow (default authorization flow with implied consent)"""
# Bootstrap all needed objects
authorization_flow = Flow.objects.get(
slug="default-provider-authorization-implicit-consent"
)
provider = OAuth2Provider.objects.create(
name=generate_id(),
client_type=ClientTypes.CONFIDENTIAL,
client_id=self.client_id,
client_secret=self.client_secret,
signing_key=create_test_cert(),
redirect_uris=[
RedirectURI(
RedirectURIMatchingMode.STRICT, "http://localhost:3000/login/generic_oauth"
)
],
authorization_flow=authorization_flow,
)
provider.property_mappings.set(
ScopeMapping.objects.filter(
scope_name__in=[
SCOPE_OPENID,
SCOPE_OPENID_EMAIL,
SCOPE_OPENID_PROFILE,
SCOPE_OFFLINE_ACCESS,
]
)
)
Application.objects.create(
name=generate_id(),
slug=self.app_slug,
provider=provider,
)
self.driver.get(self.live_server_url)
login_window = self.driver.current_window_handle
self.driver.switch_to.new_window("tab")
grafana_window = self.driver.current_window_handle
self.driver.get("http://localhost:3000")
self.driver.find_element(By.CLASS_NAME, "btn-service--oauth").click()
self.driver.switch_to.window(login_window)
self.login()
self.driver.switch_to.window(grafana_window)
self.wait_for_url("http://localhost:3000/?orgId=1")
self.driver.get("http://localhost:3000/profile")
self.assertEqual(
self.driver.find_element(By.CLASS_NAME, "page-header__title").text,
self.user.name,
)
self.assertEqual(
self.driver.find_element(By.CSS_SELECTOR, "input[name=name]").get_attribute("value"),
self.user.name,
)
self.assertEqual(
self.driver.find_element(By.CSS_SELECTOR, "input[name=email]").get_attribute("value"),
self.user.email,
)
self.assertEqual(
self.driver.find_element(By.CSS_SELECTOR, "input[name=login]").get_attribute("value"),
self.user.email,
)

View File

@ -20,7 +20,7 @@ from tests.e2e.utils import SeleniumTestCase, retry
class TestProviderSAML(SeleniumTestCase):
"""test SAML Provider flow"""
def setup_client(self, provider: SAMLProvider, force_post: bool = False):
def setup_client(self, provider: SAMLProvider, force_post: bool = False, **kwargs):
"""Setup client saml-sp container which we test SAML against"""
metadata_url = (
self.url(
@ -40,6 +40,7 @@ class TestProviderSAML(SeleniumTestCase):
"SP_ENTITY_ID": provider.issuer,
"SP_SSO_BINDING": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
"SP_METADATA_URL": metadata_url,
**kwargs,
},
)
@ -111,6 +112,74 @@ class TestProviderSAML(SeleniumTestCase):
[self.user.email],
)
@retry()
@apply_blueprint(
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"system/providers-saml.yaml",
)
@reconcile_app("authentik_crypto")
def test_sp_initiated_implicit_post(self):
"""test SAML Provider flow SP-initiated flow (implicit consent)"""
# Bootstrap all needed objects
authorization_flow = Flow.objects.get(
slug="default-provider-authorization-implicit-consent"
)
provider: SAMLProvider = SAMLProvider.objects.create(
name="saml-test",
acs_url="http://localhost:9009/saml/acs",
audience="authentik-e2e",
issuer="authentik-e2e",
sp_binding=SAMLBindings.POST,
authorization_flow=authorization_flow,
signing_kp=create_test_cert(),
)
provider.property_mappings.set(SAMLPropertyMapping.objects.all())
provider.save()
Application.objects.create(
name="SAML",
slug="authentik-saml",
provider=provider,
)
self.setup_client(provider, True)
self.driver.get("http://localhost:9009")
self.login()
self.wait_for_url("http://localhost:9009/")
body = loads(self.driver.find_element(By.CSS_SELECTOR, "pre").text)
self.assertEqual(
body["attr"]["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"],
[self.user.name],
)
self.assertEqual(
body["attr"][
"http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"
],
[self.user.username],
)
self.assertEqual(
body["attr"]["http://schemas.goauthentik.io/2021/02/saml/username"],
[self.user.username],
)
self.assertEqual(
body["attr"]["http://schemas.goauthentik.io/2021/02/saml/uid"],
[str(self.user.pk)],
)
self.assertEqual(
body["attr"]["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"],
[self.user.email],
)
self.assertEqual(
body["attr"]["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"],
[self.user.email],
)
@retry()
@apply_blueprint(
"default/flow-default-authentication-flow.yaml",
@ -450,3 +519,81 @@ class TestProviderSAML(SeleniumTestCase):
lambda driver: driver.current_url.startswith(should_url),
f"URL {self.driver.current_url} doesn't match expected URL {should_url}",
)
@retry()
@apply_blueprint(
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"system/providers-saml.yaml",
)
@reconcile_app("authentik_crypto")
def test_sp_initiated_implicit_post_buffer(self):
"""test SAML Provider flow SP-initiated flow (implicit consent)"""
# Bootstrap all needed objects
authorization_flow = Flow.objects.get(
slug="default-provider-authorization-implicit-consent"
)
provider: SAMLProvider = SAMLProvider.objects.create(
name="saml-test",
acs_url=f"http://{self.host}:9009/saml/acs",
audience="authentik-e2e",
issuer="authentik-e2e",
sp_binding=SAMLBindings.POST,
authorization_flow=authorization_flow,
signing_kp=create_test_cert(),
)
provider.property_mappings.set(SAMLPropertyMapping.objects.all())
provider.save()
Application.objects.create(
name="SAML",
slug="authentik-saml",
provider=provider,
)
self.setup_client(provider, True, SP_ROOT_URL=f"http://{self.host}:9009")
self.driver.get(self.live_server_url)
login_window = self.driver.current_window_handle
self.driver.switch_to.new_window("tab")
client_window = self.driver.current_window_handle
# We need to access the SP on the same host as the IdP for SameSite cookies
self.driver.get(f"http://{self.host}:9009")
self.driver.switch_to.window(login_window)
self.login()
self.driver.switch_to.window(client_window)
self.wait_for_url(f"http://{self.host}:9009/")
body = loads(self.driver.find_element(By.CSS_SELECTOR, "pre").text)
self.assertEqual(
body["attr"]["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"],
[self.user.name],
)
self.assertEqual(
body["attr"][
"http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"
],
[self.user.username],
)
self.assertEqual(
body["attr"]["http://schemas.goauthentik.io/2021/02/saml/username"],
[self.user.username],
)
self.assertEqual(
body["attr"]["http://schemas.goauthentik.io/2021/02/saml/uid"],
[str(self.user.pk)],
)
self.assertEqual(
body["attr"]["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"],
[self.user.email],
)
self.assertEqual(
body["attr"]["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"],
[self.user.email],
)

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