Compare commits

..

168 Commits

Author SHA1 Message Date
Marc 'risson' Schmitt
49b46b5338 ci: pull latest changes before tagging new version (cherry-pick #20413 to version-2025.8) (#20417) 2026-02-19 14:32:34 +01:00
authentik-automation[bot]
cfcdc70542 root: do not rely on npm cli for version bump (cherry-pick #20276 to version-2025.8) (#20318)
root: do not rely on npm cli for version bump

Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2026-02-17 13:02:29 +01:00
Marc 'risson' Schmitt
471f0d65e0 ci: fix binary outpost build on release (cherry-pick #20248 to version-2025.8) (#20282)
fix binary outpost build on release (#20248)
2026-02-13 13:38:45 +01:00
authentik-automation[bot]
3c0731ab6d website/docs: 2025.8.6 release notes (cherry-pick #20243 to version-2025.8) (#20254)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2026-02-12 16:57:03 +01:00
authentik-automation[bot]
bce6ba2afa release: 2025.8.6 2026-02-12 15:27:43 +00:00
Marc 'risson' Schmitt
1b490c1b5a website/docs: fix lint
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2026-02-12 15:41:11 +01:00
Marc 'risson' Schmitt
9a038aa0fe lib: fix lint
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2026-02-12 15:41:07 +01:00
authentik-automation[bot]
c80112e5f8 security: CVE-2026-25227 (#20233)
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2026-02-12 15:24:09 +01:00
authentik-automation[bot]
65ae736fe2 security: CVE-2026-25748 (#20234)
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2026-02-12 15:23:47 +01:00
authentik-automation[bot]
271e7eae1c security: CVE-2026-25922 (#20235)
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2026-02-12 15:20:41 +01:00
authentik-automation[bot]
8bf66b8987 website/docs: generate CVE sidebar (cherry-pick #20098 to version-2025.8) (#20099)
website/docs: generate CVE sidebar (#20098)

* website/docs: generate CVE sidebar



* docs



* slightly less warnings



---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2026-02-08 17:09:40 +01:00
Marc 'risson' Schmitt
6f9369283c root: update client-go generation (cherry-pick #19762 and #19906 to version-2025.8) (#19934)
* root: update client-go generation (cherry-pick #19762 to version-2025.12) (#19791)

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

* ci: always generate API clients (#19906)

* ci: always generate API clients

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

* fix missing respective actions

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

* fix flaky test

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

* mount generated client

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

* sigh

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

---------

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

* ci: fix test_docker.sh

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

* bump go

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

* fix dind

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

---------

Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
Co-authored-by: Jens L. <jens@goauthentik.io>
2026-02-07 23:00:10 +01:00
authentik-automation[bot]
859ef3a722 website/docs: Remove stale 2024 version directives (cherry-pick #19888 to version-2025.8) (#20021)
* Cherry-pick #19888 to version-2025.8 (with conflicts)

This cherry-pick has conflicts that need manual resolution.

Original PR: #19888
Original commit: 469bc0b6b4

* fix conflicts

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

* sigh

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

* not fail

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2026-02-04 22:20:48 +01:00
authentik-automation[bot]
b47ed8894c release: 2025.8.5 2025-11-19 15:05:03 +00:00
authentik-automation[bot]
8c1f08fecb website/docs: add 2025.8.5 and 2025.10.2 release notes (cherry-pick #18268 to version-2025.8) (#18269)
Cherry-pick #18268 to version-2025.8 (with conflicts)

This cherry-pick has conflicts that need manual resolution.

Original PR: #18268
Original commit: cd1693be28

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-11-19 15:30:29 +01:00
authentik-automation[bot]
cc676793b0 internal: Automated internal backport: 5000-sidebar.sec.patch to authentik-2025.8 (#18263)
Automated internal backport of patch 5000-sidebar.sec.patch to authentik-2025.8

Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-11-19 15:21:03 +01:00
authentik-automation[bot]
76815cd5b9 internal: Automated internal backport: 1487-invitation-expiry.sec.patch to authentik-2025.8 (#18261)
* Automated internal backport of patch 1487-invitation-expiry.sec.patch to authentik-2025.8

* lint

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2025-11-19 15:10:10 +01:00
authentik-automation[bot]
57a378bbd0 internal: Automated internal backport: 1498-oauth2-cc-user-active.sec.patch to authentik-2025.8 (#18262)
Automated internal backport of patch 1498-oauth2-cc-user-active.sec.patch to authentik-2025.8

Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2025-11-19 15:01:25 +01:00
Marcelo Elizeche Landó
f542a0d415 core: bump Django from 5.1.13 to 5.1.14 for 2025.8 (#17968)
* upgrade django from 5.1.13 to 5.1.14

* longer urls

* sync package-lock.json

* fix go deprecation for +build

* update package.lock
2025-11-11 12:20:45 +01:00
Jens L.
f69bbbe85e ci: fix migrate-from-stable for old versions (#18018)
* ci: fix migrate-from-stable for old versions

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

* version from authentik

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

* npm i

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

* npm i on linux

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

* fix outpost lint

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

* gha notice

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

* idk man I hate node

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-11-07 17:45:12 +01:00
Jens L.
0845e23337 ci: rework internal repo (#17797) (#17830)
* ci: rework internal repo (#17797)

* ci: rework internal repo

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

* also fix retention workflow

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
# Conflicts:
#	.github/workflows/gh-ghcr-retention.yml
#	.github/workflows/repo-mirror-cleanup.yml
#	.github/workflows/repo-mirror.yml

* fix package

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-11-01 20:08:15 +01:00
authentik-automation[bot]
de6d2a02b2 website/docs: update SAML provider docs (cherry-pick #15887 to version-2025.8) (#17583)
* Cherry-pick #15887 to version-2025.8 (with conflicts)

This cherry-pick has conflicts that need manual resolution.

Original PR: #15887
Original commit: 83603a528f

* Fix sidebar conflict

* Remove SAML logout link

---------

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: dewi-tik <dewi@goauthentik.io>
2025-10-22 18:37:48 +02:00
authentik-automation[bot]
73be5321a9 website: add powershell syntax highlighting and bump package (cherry-pick #16683) (#16721)
website: add powershell syntax highlighting and bump package (#16683)

Add powershell syntax highlighting and bump package

Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
2025-10-10 13:43:24 +02:00
authentik-automation[bot]
ff8ad31a6b website/docs: add email config section (cherry-pick #16727 to version-2025.8) (#17364)
website/docs: add email config section (#16727)

* Add email section and link to it from install guide



* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* Typo

* WIP

* Apply suggestion

* Added TLS email config

* Apply suggestions

* Apply suggestions

* fix linting

* fix broken anchor

* Apply suggestions

* Fix extra line

---------

Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
Co-authored-by: Marcelo Elizeche Landó <marcelo@goauthentik.io>
2025-10-09 22:19:40 +00:00
authentik-automation[bot]
ad2cedac98 website/docs: add entra id scim source (cherry-pick #17357 to version-2025.8) (#17362)
* Cherry-pick #17357 to version-2025.8 (with conflicts)

This cherry-pick has conflicts that need manual resolution.

Original PR: #17357
Original commit: 2e03270dcf

* Conflict fix

---------

Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
2025-10-09 18:40:47 +00:00
authentik-automation[bot]
7a0bd0d0c1 web/admin: fix incorrect placeholder for scim provider (cherry-pick #17308 to version-2025.8) (#17309)
* Cherry-pick #17308 to version-2025.8 (with conflicts)

This cherry-pick has conflicts that need manual resolution.

Original PR: #17308
Original commit: 88583ae46b

* Update LDAPProviderFormForm.ts

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

* Update LDAPProviderFormForm.ts

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

---------

Signed-off-by: Jens L. <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-10-08 16:54:18 +02:00
authentik-automation[bot]
269551cf4d blueprints: ensure tasks retry on database errors (cherry-pick #17333 to version-2025.8) (#17334)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-10-08 16:48:04 +02:00
authentik-automation[bot]
23a6ab915b lib/sync/outgoing: revert reduce number of db queries made (revert #14177) (cherry-pick #17306 to version-2025.8) (#17330)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-10-08 11:30:08 +00:00
authentik-automation[bot]
0c77e5c33e web: Fix behavior for modals configured with closeAfterSuccessfulSubmit (cherry-pick #17277 to version-2025.8) (#17299)
web: Fix behavior for modals configured with closeAfterSuccessfulSubmit (#17277)

when a form inside a modal submits successfully, it dispatches an EVENT_REFRESH event that bubbles up through the DOM. Parent components like TablePage listen for this event to refresh their data.
so, when the parent component refreshes/re-renders in response to EVENT_REFRESH, it destroys and recreates the entire row including the modal element and that causes the modal to disappear even
though the ModalForm component never explicitly closed it.

Co-authored-by: Dominic R <dominic@sdko.org>
2025-10-07 13:58:44 -04:00
authentik-automation[bot]
93aeae5405 core: fix absolute and relative path file uploads (cherry-pick #17269 to version-2025.8) (#17272)
core: fix absolute and relative path file uploads (#17269)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-10-06 19:05:09 +02:00
authentik-automation[bot]
cd3ff47221 tasks/middlewares/messages: make sure exceptions are always logged (cherry-pick #17237 to version-2025.8) (#17248)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-10-04 16:42:26 +02:00
authentik-automation[bot]
a370a54db8 packages/django-dramatiq-postgres: fix error when updating task with no changes (cherry-pick #16728 to version-2025.8) (#17238)
packages/django-dramatiq-postgres: fix error when updating task with no changes (#16728)

Co-authored-by: Jens L. <jens@goauthentik.io>
2025-10-03 17:12:02 +02:00
authentik-automation[bot]
1d0e45abe8 packages/django-dramatiq-postgres: broker: fix task expiration (cherry-pick #17178 to version-2025.8) (#17217)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-10-02 14:49:57 +02:00
authentik-automation[bot]
a501b627eb build(deps): bump django from 5.1.12 to 5.1.13 (cherry-pick #17198 to version-2025.8) (#17199)
build(deps): bump django from 5.1.12 to 5.1.13 (#17198)

* build(deps): bump django from 5.1.12 to 5.1.13

Bumps [django](https://github.com/django/django) from 5.1.12 to 5.1.13.
- [Commits](https://github.com/django/django/compare/5.1.12...5.1.13)

---
updated-dependencies:
- dependency-name: django
  dependency-version: 5.1.13
  dependency-type: direct:production
...



* lock



---------

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-10-02 11:57:19 +02:00
authentik-automation[bot]
78a4c08fc8 website/docs: developer docs: adjust sentence for writing docs (cherry-pick #17137 to version-2025.8) (#17142)
website/docs: developer docs: adjust sentence for writing docs (#17137)

As per Tana's request

Signed-off-by: Dominic R <dominic@sdko.org>
Co-authored-by: Dominic R <dominic@sdko.org>
2025-09-30 15:52:22 +00:00
authentik-automation[bot]
8d3a289d12 release: 2025.8.4 2025-09-30 00:02:15 +00:00
Jens Langhammer
99e92bf998 root: fix rustup error during build when buildcache version is outdated
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-09-30 01:32:01 +02:00
authentik-automation[bot]
1df7b22d29 release: 2025.8.4 2025-09-29 21:52:52 +00:00
authentik-automation[bot]
af484738f6 website/docs: 2025.8.4 release notes (cherry-pick #17119 to version-2025.8) (#17120) 2025-09-29 23:44:03 +02:00
authentik-automation[bot]
19ba3c8453 tasks: reduce default number of retries and max backoff (cherry-pick #17107 to version-2025.8) (#17109)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-09-29 17:33:19 +00:00
authentik-automation[bot]
9d1b384baa packages/django-dramatiq-postgres: broker: fix new messages not being picked up when too many messages are waiting (cherry-pick #17106 to version-2025.8) (#17108)
packages/django-dramatiq-postgres: broker: fix new messages not being picked up when too many messages are waiting (#17106)

Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-09-29 17:11:11 +00:00
authentik-automation[bot]
8a702b148f providers/oauth2: fix authentication error with identical app passwords (cherry-pick #17100 to version-2025.8) (#17103)
providers/oauth2: fix authentication error with identical app passwords (#17100)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-09-29 17:29:14 +02:00
authentik-automation[bot]
2db42a3a04 stages/identification: fix mismatched error messages (cherry-pick #17090 to version-2025.8) (#17104)
stages/identification: fix mismatched error messages (#17090)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-09-29 17:29:03 +02:00
authentik-automation[bot]
4403baaa28 outposts/ldap: add pwdChangeTime attribute (cherry-pick #17010 to version-2025.8) (#17101)
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
fix ldap tests following #17010 (#17021)
2025-09-29 15:44:35 +02:00
authentik-automation[bot]
8030d4d734 cmd/server/healthcheck: info log success instead of debug (cherry-pick #17093 to version-2025.8) (#17097)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-09-29 15:26:48 +02:00
authentik-automation[bot]
4b7a78453c web: Fix layout class for row in LibraryPage (cherry-pick #16752 to version-2025.8) (#17091)
web: Fix layout class for 'row' in LibraryPage (#16752)

Fix layout class for 'row' in LibraryPage

Signed-off-by: Jérôme W. <jerome@wnetworks.org>
Co-authored-by: Jérôme W. <jerome@wnetworks.org>
2025-09-29 14:56:15 +02:00
authentik-automation[bot]
b9746106a0 rbac: optimize rbac assigned by users query (cherry-pick #17015 to version-2025.8) (#17092)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-09-29 12:47:45 +00:00
authentik-automation[bot]
faa47670ef web/admin: fix federation sources automatically selected (cherry-pick #17069 to version-2025.8) (#17070)
web/admin: fix federation sources automatically selected (#17069)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-09-28 20:30:19 +02:00
authentik-automation[bot]
add5f4e0cb tasks: fix logger name (cherry-pick #17009 to version-2025.8) (#17060)
* tasks: fix logger name (#17009)

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

* fix tests

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-09-27 02:33:23 +02:00
authentik-automation[bot]
7636c038d9 */bindings: order by pk (cherry-pick #17027) (#17053)
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-09-26 18:37:29 +02:00
authentik-automation[bot]
557f781f5c lib/config: fix listen settings (cherry-pick #17005) (#17023)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
fix listen settings (#17005)
2025-09-26 16:59:23 +02:00
authentik-automation[bot]
1997011328 website/docs: Update Github expression to handle non-OAuth sources gracefully (cherry-pick #17014) (#17029)
website/docs: Update Github expression to handle non-OAuth sources gracefully (#17014)

Co-authored-by: Patrick <tradiuz@gmail.com>
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-09-26 14:24:10 +02:00
authentik-automation[bot]
dfdd5ebc8c lib: match exception_to_dict locals behaviour (cherry-pick #17006) (#17016)
lib: match exception_to_dict locals behaviour (#17006)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-09-25 17:53:36 +02:00
authentik-automation[bot]
42977caa6a core: add index on Group.is_superuser (cherry-pick #17011) (#17017)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-09-25 15:29:15 +00:00
authentik-automation[bot]
bce46c880d rbac: fix typo (cherry-pick #16476) (#17018)
Co-authored-by: Simonyi Gergő <28359278+gergosimonyi@users.noreply.github.com>
fix typo (#16476)
2025-09-25 15:12:03 +00:00
authentik-automation[bot]
9c987c5694 website/docs: improve discord policies when also bound to non-oauth sources (cherry-pick #17008) (#17012)
website/docs: improve discord policies when also bound to non-oauth sources (#17008)

Co-authored-by: Jens L. <jens@goauthentik.io>
2025-09-25 15:53:40 +02:00
authentik-automation[bot]
f33e50c28c webiste/docs: add missing oauth endpoints (cherry-pick #16995) (#16999)
Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
2025-09-25 15:00:21 +02:00
authentik-automation[bot]
280d6febf7 website/docs: oauth provider: Add device and introspect to reserved slugs (cherry-pick #16994) (#16998)
Co-authored-by: Dominic R <dominic@sdko.org>
2025-09-25 14:59:56 +02:00
authentik-automation[bot]
3a32918ceb providers/ldap: add include_children parameter to cached search mode (cherry-pick #16918) (#17000)
Co-authored-by: Daniel Adu-Gyan <26229521+danieladugyan@users.noreply.github.com>
2025-09-25 12:58:24 +00:00
authentik-automation[bot]
723bd4bbbc website/developer docs: What domain for what doc version (cherry-pick #16987) (#16996)
website/developer docs: What domain for what doc version (#16987)

* website/developer docs: What domain for what doc version

Closes: AUTH-1316

* Apply suggestions from code review




---------

Signed-off-by: Dominic R <dominic@sdko.org>
Co-authored-by: Dominic R <dominic@sdko.org>
Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
2025-09-25 08:35:30 -04:00
authentik-automation[bot]
7b130e1832 website/docs: update url to docker-compose.yml (cherry-pick #16901) (#16986)
website/docs: update url to docker-compose.yml (#16901)

Update URL of docker-compose.yml from "https://goauthentik.io/docker-compose.yml" to "https://docs.goauthentik.io/docker-compose.yml"

Co-authored-by: Nuno Alves <nuno.alves02@gmail.com>
2025-09-24 17:41:40 -04:00
authentik-automation[bot]
3aa969d64e website/docs: add docs for source switch expression policy (cherry-pick #16878) (#16972)
website/docs: add docs for source switch expression policy (#16878)

* draft for source switch docs

* add python

* add sidebar entry

* Update website/docs/customize/policies/expression/source_switch.md




* Update website/docs/customize/policies/index.md




* Update website/docs/customize/policies/working_with_policies.md




* Update website/docs/customize/policies/working_with_policies.md




* add sectin about binding the policy

* tweak

* Update website/docs/customize/policies/index.md




* Update website/docs/customize/policies/working_with_policies.md




* Update website/docs/customize/policies/working_with_policies.md




* Update website/docs/customize/policies/working_with_policies.md




* Update website/docs/customize/policies/working_with_policies.md




* Update website/docs/customize/policies/expression.mdx




---------

Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: Tana M Berry <tana@goauthentik.io>
Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-09-24 13:24:33 +01:00
authentik-automation[bot]
04688c86b3 website/docs: random typo fixes (cherry-pick #16956) (#16959)
website/docs: random typo fixes (#16956)

Co-authored-by: Jared Harrison <50091069+Jared-Is-Coding@users.noreply.github.com>
2025-09-23 23:57:14 +02:00
authentik-automation[bot]
4f8dbb5efb website/docs: fix capitalization (cherry-pick #16944) (#16958)
website/docs: fix capitalization (#16944)

* fix capitalization

* tweak

* Update website/docs/add-secure-apps/outposts/manual-deploy-docker-compose.md




---------

Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: Tana M Berry <tana@goauthentik.io>
Co-authored-by: Dominic R <dominic@sdko.org>
2025-09-23 21:15:57 +01:00
authentik-automation[bot]
3b382199eb website/docs: 2025.8: fix worker concurrency setting rename (cherry-pick #16946) (#16950)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
fix worker concurrency setting rename (#16946)
2025-09-23 19:52:20 +02:00
authentik-automation[bot]
db50991e9c providers/scim: fix string formatting for SCIM user filter (cherry-pick #16465) (#16852)
providers/scim: fix string formatting for SCIM user filter (#16465)

* providers/scim: fix string formatting for SCIM user filter



* format

---------

Signed-off-by: Adam Shirt <adamshirt@outlook.com>
Co-authored-by: Adam Shirt <adamshirt@outlook.com>
Co-authored-by: Jens Langhammer <jens.langhammer@beryju.org>
2025-09-17 13:48:18 +02:00
authentik-automation[bot]
88036ebe14 webiste/docs: improve user ref doc (cherry-pick #16779) (#16839)
webiste/docs: improve user ref doc (#16779)

* WIP

* WIP

* Update website/docs/users-sources/user/user_ref.mdx




* Language change

* Update website/docs/users-sources/user/user_ref.mdx




---------

Signed-off-by: Dewi Roberts <dewi@goauthentik.io>
Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
2025-09-16 22:17:41 +00:00
authentik-automation[bot]
680feaefa1 release: 2025.8.3 2025-09-16 15:09:16 +00:00
authentik-automation[bot]
8676cd3a43 website/docs: 2025.8.3 release notes (cherry-pick #16809) (#16810)
website/docs: 2025.8.3 release notes (#16809)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-09-16 16:40:32 +02:00
authentik-automation[bot]
53e0f6b734 stages/email_authenticator: Fix email mfa loop (cherry-pick #16579) (#16807)
stages/email_authenticator: Fix email mfa loop (#16579)

* Return error message instead of infinite loop

* Remove unused code

* check for existing email device

* revert to initial behaviour

* Add test for failing when the user already has an email device

Co-authored-by: Marcelo Elizeche Landó <marcelo@goauthentik.io>
2025-09-16 16:20:34 +02:00
authentik-automation[bot]
eaee475662 website/docs: update create oauth provider page (cherry-pick #16617) (#16806)
website/docs: update create oauth provider page (#16617)

* Updated the page to be more consistent with upcoming changes to the saml page

* Add note

Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
2025-09-16 13:33:53 +00:00
authentik-automation[bot]
19b672b3bc lifecycle: fix permission error when running worker as root (cherry-pick #16735) (#16784)
lifecycle: fix permission error when running worker as root (#16735)

* lifecycle: fix permission error when running worker as root



* fix maybe?



---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-09-16 14:49:31 +02:00
authentik-automation[bot]
bf0a31ce86 website/docs: fix docker tabs not rendering properly (cherry-pick #16799) (#16802)
website/docs: fix docker tabs not rendering properly (#16799)

docs: fix docker tabs not rendering properly

Co-authored-by: Connor Peshek <connor@connorpeshek.me>
2025-09-16 11:00:27 +01:00
authentik-automation[bot]
28ff561400 release: 2025.8.2 2025-09-15 15:47:14 +00:00
authentik-automation[bot]
ff2472a551 website/docs: 2025.8.2 release notes (cherry-pick #16773) (#16778)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-09-15 17:15:44 +02:00
authentik-automation[bot]
dac302e8be sources/oauth/entra_id: do not assume group_id comes from entra (cherry-pick #16456) (#16777)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-09-15 16:43:34 +02:00
authentik-automation[bot]
930a6f7c6f lib/logging: only show locals when in debug mode (cherry-pick #16772) (#16774)
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-09-15 15:34:52 +02:00
authentik-automation[bot]
b661b0bb39 website/docs: update ssh rac doc (cherry-pick #16695) (#16720)
website/docs: update ssh rac doc (#16695)

* Added linebreak preservation and changed blocks to yaml syntax

* Update website/docs/add-secure-apps/providers/rac/rac-public-key.md




* Update website/docs/add-secure-apps/providers/rac/rac-public-key.md




---------

Signed-off-by: Dewi Roberts <dewi@goauthentik.io>
Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
Co-authored-by: Dominic R <dominic@sdko.org>
2025-09-12 16:05:57 +00:00
authentik-automation[bot]
5203713ca0 website/docs: re-fix sentence about Go (backport of #16736) (#16737)
* Cherry-pick #16736 to version-2025.8 (with conflicts)

This cherry-pick has conflicts that need manual resolution.

Original PR: #16736
Original commit: 515a065831

* fixed conflict

---------

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: Tana M Berry <tana@goauthentik.io>
2025-09-12 03:33:56 -05:00
authentik-automation[bot]
94794b106e web: Docker versioning compatibility (cherry-pick #16139) (#16732)
web: Docker versioning compatibility (#16139)

* website/docs: update frontend dev docs to always use latest dev images



* website: Clarify instructions.

---------

Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
Co-authored-by: Teffen Ellis <teffen@goauthentik.io>
2025-09-11 14:39:12 -05:00
authentik-automation[bot]
eb8c21cf04 website/docs: dev-docs: Minimal landing + landing for dev env (cherry-pick #15246) (#16734)
website/docs: dev-docs: Minimal landing + landing for dev env (#15246)

* wip

* Update index.mdx



* Update website/docs/developer-docs/contributing.md



* Update website/docs/developer-docs/contributing.md



---------

Signed-off-by: Dominic R <dominic@sdko.org>
Co-authored-by: Dominic R <dominic@sdko.org>
2025-09-11 14:06:48 -05:00
authentik-automation[bot]
8a501377f2 website/docs: fix typos (cherry-pick #16716) (#16719)
website/docs: fix typos (#16716)

Fix typos

Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
2025-09-11 14:17:33 +00:00
authentik-automation[bot]
5c67a0fecd website/docs: moves display source notes (cherry-pick #16704) (#16718)
website/docs: moves display source notes (#16704)

Moves display source note location to better location

Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
2025-09-11 13:37:43 +00:00
authentik-automation[bot]
145e6a3a4f website/docs: clarify docker compose install (cherry-pick #16696) (#16699)
website/docs: clarify docker compose install (#16696)

* Change order

* WIP

Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
2025-09-10 21:28:56 +01:00
authentik-automation[bot]
0219ed73f5 website/docs: add missing notification rule example doc to sidebar (cherry-pick #16478) (#16479)
website/docs: add missing notification rule example doc to sidebar (#16478)

Adds missing doc to sidebar

Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
2025-09-10 15:54:29 +00:00
authentik-automation[bot]
88ccb7857b website: Redirect Azure to Entra. Add tags for search indexing. (cherry-pick #16474) (#16483)
website: Redirect Azure to Entra. Add tags for search indexing. (#16474)

* website: Add Azure redirects, tags for  search indexing.

* website: Fix redirects tab alignment.

Co-authored-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
2025-09-10 14:38:57 +00:00
authentik-automation[bot]
e33d6becba website/docs: add rate limiting info to Email stage docs (cherry-pick #16668) (#16691)
website/docs: add rate limiting info to Email stage docs (#16668)

* add rate limiting info

* added Jens' edits

* Update website/docs/add-secure-apps/flows-stages/stages/email/index.mdx




* Update website/docs/add-secure-apps/flows-stages/stages/email/index.mdx




* Update website/docs/add-secure-apps/flows-stages/stages/email/index.mdx




---------

Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: Tana M Berry <tana@goauthentik.io>
Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
2025-09-10 10:35:15 +00:00
authentik-automation[bot]
41417affc0 website/docs: fix typo (cherry-pick #16681) (#16682)
website/docs: fix typo (#16681)

Fix typo

Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
2025-09-09 13:28:51 +00:00
authentik-automation[bot]
1b4a6c3f6d tasks: fix status and healthcheck breaking with connection issues (cherry-pick #16504) (#16673)
tasks: fix status and healthcheck breaking with connection issues (#16504)

* add high priority for inline tasks in API



* also catch psycopg errors directly



* retry locking worker state after failure



* format



---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-09-08 17:04:17 -05:00
authentik-automation[bot]
aa98edd661 providers/scim: improve error message when object fails to sync (cherry-pick #16625) (#16643)
providers/scim: improve error message when object fails to sync (#16625)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-09-05 15:14:20 +02:00
authentik-automation[bot]
cb70331c82 providers/oauth2: add missing exp claim for logout token (cherry-pick #16593) (#16598)
providers/oauth2: add missing exp claim for logout token (#16593)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-09-04 00:23:20 +02:00
authentik-automation[bot]
31e105b190 web/flows: only disable login button when interactive captcha is configured and not loaded (cherry-pick #16586) (#16590)
web/flows: only disable login button when interactive captcha is configured and not loaded (#16586)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-09-03 18:26:33 +02:00
authentik-automation[bot]
08d2615f71 website: Update license text to use "directory" (cherry-pick #16589) (#16592)
website: Update license text to use "directory" (#16589)

To remove any potential confusion

Signed-off-by: Dominic R <dominic@sdko.org>
Co-authored-by: Dominic R <dominic@sdko.org>
2025-09-03 16:08:50 +00:00
authentik-automation[bot]
bb9e8b1c42 core: bump django from 5.1.11 to v5.1.12 (cherry-pick #16584) (#16591)
core: bump django from 5.1.11 to v5.1.12 (#16584)

bump django from 5.1.11 to 5.1.12

Co-authored-by: Marcelo Elizeche Landó <marcelo@goauthentik.io>
2025-09-03 17:39:33 +02:00
authentik-automation[bot]
6b8d7376a6 sources/ldap: fix malformed filter error with special characters in group DN (cherry-pick #16243) (#16585)
sources/ldap: fix malformed filter error with special characters in group DN (#16243)

* sources/ldap: fix malformed filter error with special characters in group DN

Escape special characters in LDAP group DN when constructing membership filters using ldap3's escape_filter_chars() function to prevent LDAPInvalidFilterError exceptions.

* sources/ldap: add tests for special characters in group DN filter escaping

Add comprehensive tests to verify that LDAP group DN values with special characters (parentheses, backslashes, asterisks) are properly escaped when constructing LDAP filters. Tests cover both the membership synchronization process and the escape_filter_chars function directly to ensure malformed filter errors are prevented.

* sources/ldap: fix line length for ruff linting compliance

Split long line in group filter construction to comply with 100 character limit.

* sources/ldap: move escape_filter_chars import to top-level in tests; audit other filter usages

---------

Co-authored-by: Dongwoo Jeong <dongwoo@duck.com>
Co-authored-by: Dongwoo Jeong <dongwoo.jeong@lge.com>
2025-09-03 17:20:34 +02:00
authentik-automation[bot]
4f680d8c06 root: clean up README (cherry-pick #16286) (#16573)
root: clean up README (#16286)

* wip





* Delete website/LICENSE



---------

Signed-off-by: Dominic R <dominic@sdko.org>
Co-authored-by: Dominic R <dominic@sdko.org>
Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
2025-09-03 15:49:26 +02:00
authentik-automation[bot]
3eeb741975 website: Add text copy of license (cherry-pick #16559) (#16574)
website: Add text copy of license (#16559)

* website: Add License

According to the root LICENSE, the website is licensed under Creative Commons: CC BY-SA 4.0 license. To match the root of the project and the EE dir, a text copy of the license is now included.

Updates AUTH-1246



* Update LICENSE



* Create LICENSE for proprietary logo assets

Added license information for proprietary assets.



---------

Signed-off-by: Dominic R <dominic@sdko.org>
Co-authored-by: Dominic R <dominic@sdko.org>
Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
2025-09-03 09:56:28 +00:00
authentik-automation[bot]
39cb638132 website/docs: remove base providers redirect. (cherry-pick #16576) (#16580)
website/docs: remove base providers redirect. (#16576)

Co-authored-by: Connor Peshek <connor@connorpeshek.me>
2025-09-03 10:26:19 +01:00
authentik-automation[bot]
be8f5d21cb website/docs: changes all -> arrows to > (cherry-pick #16569) (#16571)
website/docs: changes all -> arrows to > (#16569)

Changes all -> arrows to > as per style guide. Also fixes some bolding and capitalization

Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
2025-09-02 22:27:26 +00:00
authentik-automation[bot]
acf18836e8 website/docs: update external user information (cherry-pick #16493) (#16568)
website/docs: update external user information (#16493)

Updated information

Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
2025-09-02 14:09:52 +00:00
authentik-automation[bot]
d688621de4 website/docs: improve customize your instance page layout (cherry-pick #16367) (#16566)
website/docs: improve customize your instance page layout (#16367)

* Changes bullet points to a table. WIP

* Finished sentence and added punctuation

* Updated to match other overview/landing pages

Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
2025-09-02 14:27:14 +01:00
authentik-automation[bot]
5173d09191 website/docs: update how to rac and outpost landing page (cherry-pick #16442) (#16498)
website/docs: update how to rac and outpost landing page (#16442)

* Adds outpost deployment to how to rac guide and updates the outpost landing page

* Applied suggestions

* Apply suggestion

* Update website/docs/add-secure-apps/providers/rac/how-to-rac.md




* Update website/docs/add-secure-apps/outposts/index.mdx



* Removed cards

---------

Signed-off-by: Dewi Roberts <dewi@goauthentik.io>
Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
Co-authored-by: Dominic R <dominic@sdko.org>
2025-09-01 08:00:48 -05:00
authentik-automation[bot]
45bab4d32f website/integrations: bitwarden: fix ent notice (cherry-pick #16502) (#16511)
website/integrations: bitwarden: fix ent notice (#16502)

Signed-off-by: Dominic R <dominic@sdko.org>
Co-authored-by: Dominic R <dominic@sdko.org>
2025-09-01 08:27:39 +00:00
authentik-automation[bot]
f383e54c72 policies: remove object pk from authentik_policies_execution_time to reduce cardinality (cherry-pick #16500) (#16501)
policies: remove object pk from authentik_policies_execution_time to reduce cardinality (#16500)

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

#16386

Co-authored-by: Jens L. <jens@goauthentik.io>
2025-08-31 14:16:24 +01:00
authentik-automation[bot]
ab6b9b27cc website: Page redirect guide, documentation (backport of #16466) (#16475)
* website/docs: merge docs development environment and writing docs pages (#16402)

* Merges the docs development environment doc into the writing documentation guide to avoid duplication. Also updates the formatting of the writing docs page to make it easier to copy commands.

* Updates sidebar

* Update website/docs/developer-docs/docs/writing-documentation.md

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Update website/docs/developer-docs/docs/writing-documentation.md

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Update website/docs/developer-docs/docs/writing-documentation.md

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Update language on -watch commands

---------

Signed-off-by: Dewi Roberts <dewi@goauthentik.io>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>

* website: Unify Netlify redirects with Docusaurus's client-side router. (backport of #16430) (#16472)

Cherry-pick #16430 to version-2025.8 (with conflicts)

This cherry-pick has conflicts that need manual resolution.

Original PR: #16430
Original commit: 6d81aea5aa

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

* Cherry-pick #16466 to version-2025.8 (with conflicts)

This cherry-pick has conflicts that need manual resolution.

Original PR: #16466
Original commit: 66e17fe280

---------

Signed-off-by: Dewi Roberts <dewi@goauthentik.io>
Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
Co-authored-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
2025-08-29 15:13:32 +00:00
authentik-automation[bot]
db3fb0bf2e website: Unify Netlify redirects with Docusaurus's client-side router. (backport of #16430) (#16472)
Cherry-pick #16430 to version-2025.8 (with conflicts)

This cherry-pick has conflicts that need manual resolution.

Original PR: #16430
Original commit: 6d81aea5aa

Co-authored-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
2025-08-29 14:08:36 +00:00
authentik-automation[bot]
5859e6a5e5 core: fix client-side only validation allowing admin to set blank user password (cherry-pick #16467) (#16469)
Co-authored-by: Jens L. <jens@goauthentik.io>
fix client-side only validation allowing admin to set blank user password (#16467)
2025-08-29 15:20:46 +02:00
authentik-automation[bot]
1d3271fec7 lib/sync/outgoing: fix single object sync timeout (cherry-pick #16447) (#16453)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
fix single object sync timeout (#16447)
2025-08-28 19:35:33 +02:00
authentik-automation[bot]
673c8ef62c core: bump h2 from 4.2.0 to 4.3.0 (cherry-pick #16446) (#16449)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-08-28 17:14:51 +02:00
authentik-automation[bot]
4614ae320f website/docs: capitalized proper name of stages, removed old version references. (cherry-pick #16414) (#16450)
website/docs: capitalized proper name of stages, removed old version references. (#16414)

* capitalized proper name of stages, removed old version references.

* ken and dominics edits

---------

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: Tana M Berry <tana@goauthentik.io>
2025-08-28 09:52:59 -05:00
authentik-automation[bot]
10b103c0bf lib/sync: fix missing f for string (cherry-pick #16423) (#16427)
Co-authored-by: Jens L. <jens@goauthentik.io>
fix missing f for string (#16423)
2025-08-28 15:38:24 +02:00
authentik-automation[bot]
361e64a8a1 website/docs: update internal vs external user information (cherry-pick #16359) (#16421)
website/docs: update internal vs external user information (#16359)

* Update external vs internal user information

* Language updates

* Spelling

* Applied suggestion

Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
2025-08-27 19:45:48 +01:00
authentik-automation[bot]
e56081b863 providers/rac: fix AuthenticatedSession migration (cherry-pick #16400) (#16419)
Co-authored-by: Simonyi Gergő <28359278+gergosimonyi@users.noreply.github.com>
fix `AuthenticatedSession` migration (#16400)
2025-08-27 18:43:31 +02:00
authentik-automation[bot]
01a44b281b root: fix security.md (cherry-pick #16345) (#16415)
Co-authored-by: Dominic R <dominic@sdko.org>
fix security.md (#16345)
2025-08-27 18:15:05 +02:00
authentik-automation[bot]
7a6631c6e8 website/docs: adds details to certificates doc (cherry-pick #16335) (#16394)
website/docs: adds details to certificates doc (#16335)

* Clarifies certs directory mounting and adds instruction for manually re-triggering discovery.

* Fixed mounting info

* Update website/docs/sys-mgmt/certificates.md




* Update website/docs/sys-mgmt/certificates.md




---------

Signed-off-by: Dewi Roberts <dewi@goauthentik.io>
Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
Co-authored-by: Dominic R <dominic@sdko.org>
2025-08-27 16:14:45 +00:00
authentik-automation[bot]
ada973dd44 tasks/schedules: fix api search fields (cherry-pick #16405) (#16410)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
fix api search fields (#16405)
2025-08-27 17:04:32 +02:00
authentik-automation[bot]
3a7e962bde lifecycle: fix PROMETHEUS_MULTIPROC_DIR missing suffix (cherry-pick #16401) (#16407)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
fix PROMETHEUS_MULTIPROC_DIR missing suffix (#16401)
2025-08-27 15:32:23 +02:00
authentik-automation[bot]
ae297e2f60 root: update security.md with github reporting link (cherry-pick #16332) (#16395)
Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
2025-08-27 14:31:06 +02:00
Teffen Ellis
2bc2b6bd41 website: cherry-pick 2025.8/client side redirects (#16372)
website: Fix stale links that depend on initial route not triggering redirects. (#16369)

website: Fix issue where stale links that depend on initial route are
not redirected.
2025-08-26 14:36:58 -05:00
Marc 'risson' Schmitt
8199371172 providers/oauth2: avoid deadlock during session migration (cherry-pick #16361) (#16364) 2025-08-25 18:29:53 +02:00
Marc 'risson' Schmitt
dac1879de5 website/docs: 2025.8.1 release notes (cherry-pick #16343) (#16344) 2025-08-22 16:53:58 +02:00
authentik-automation[bot]
dd7c6b29d9 release: 2025.8.1 2025-08-22 14:40:58 +00:00
Marc 'risson' Schmitt
41b7e05f59 packages/django-dramatiq-postgres: broker: fix various timing issues (cherry-pick #16340) (#16342)
fix various timing issues (#16340)
2025-08-22 16:08:53 +02:00
Teffen Ellis
c5f5714e02 website: Hotfix version 2025.8/website versioning (#16336)
Co-authored-by: Dominic R <dominic@sdko.org>
Fix version origin detection, build-time URLs  (#15774)
2025-08-22 16:08:34 +02:00
Marc 'risson' Schmitt
d20d8322af outposts: allow ingress path type configuration (cherry-pick #16339) (#16341) 2025-08-22 15:49:33 +02:00
Marc 'risson' Schmitt
288f5d5015 outposts: fix service connection update task arguments (cherry-pick #16312) (#16313) 2025-08-22 14:31:56 +02:00
Marc 'risson' Schmitt
a640eb9180 packages/django-dramatiq-postgres: middleware: fix listening on hosts where ipv6 is not supported (cherry-pick #16308) (#16305) 2025-08-22 14:26:49 +02:00
Marc 'risson' Schmitt
7d48baab3e core: use email backend for test_email management command (cherry-pick #16311) (#16337)
Co-authored-by: Marcelo Elizeche Landó <marcelo@goauthentik.io>
2025-08-22 14:25:06 +02:00
Tana M Berry
b1cd6d34fc website/docs: add link in 2025.8 rel notes to back-channel logout doc (cherry pick #16306) (#16318)
Co-authored-by: Tana M Berry <tana@goauthentik.io>
2025-08-21 22:35:36 +02:00
Teffen Ellis
f14d033cef web: Username truncation, field alignment. (cherry-pick #16283) (#16284) 2025-08-21 18:03:55 +02:00
Teffen Ellis
ec75e161e2 web: Fix form group slots (cherry-pick #16276) (#16277) 2025-08-21 17:56:36 +02:00
Teffen Ellis
2e8fb8f2c6 web: Improvements to ReCaptcha resizing (cherry-pick #16171) (#16281) 2025-08-21 17:56:11 +02:00
Teffen Ellis
362bf22139 web: Fix issue where clicking a list item scrolls container (cherry-pick #16174) (#16278) 2025-08-21 17:49:43 +02:00
Marc 'risson' Schmitt
890da9b287 lifecycle: set PROMETHEUS_MULTIPROC_DIR as early as possible (cherry-pick #16298) (#16302) 2025-08-21 16:48:50 +02:00
Marc 'risson' Schmitt
7b8dadf945 providers/oauth2: fix logout token missing sid, fix wrong sub mode used (cherry-pick #16295) (#16299)
fix logout token missing sid, fix wrong sub mode used (#16295)
2025-08-21 16:39:18 +02:00
Teffen Ellis
8f70dbb963 web: Fix hidden textarea required attribute. (cherry-pick #16168) (#16275)
Fix hidden textarea `required` attribute. (#16168)
2025-08-21 14:50:36 +02:00
Teffen Ellis
15505f5caf web: fix "Explore integrations" link in Quick actions (cherry-pick #16274) (#16285)
Co-authored-by: Max <mail@heavygale.de>
fix "Explore integrations" link in Quick actions (#16274)
2025-08-21 14:20:07 +02:00
Marc 'risson' Schmitt
b2d770c0a4 *: Fix dead doc link (cherry-pick #16288) (#16297)
Co-authored-by: Dominic R <dominic@sdko.org>
Fix dead doc link (#16288)
2025-08-21 14:10:33 +02:00
authentik-automation[bot]
f7e25fff1a release: 2025.8.0 2025-08-20 18:01:34 +00:00
Marc 'risson' Schmitt
688b3a9f8b tasks: add rel_obj to system task exception event (cherry-pick #16270) (#16272) 2025-08-20 19:31:02 +02:00
Marc 'risson' Schmitt
8f48e18854 website/docs: update 2025.8 release notes (cherry-pick #16269) (#16271) 2025-08-20 19:20:18 +02:00
Marc 'risson' Schmitt
306f75be59 security: Bump supported versions (cherry-pick #16261) (#16267)
Co-authored-by: Dominic R <dominic@sdko.org>
2025-08-20 19:17:08 +02:00
Tana M Berry
982c3cf4dc website/docs: sys-mgmt/s3: Clean up and improve (cherry-pick #16242) (#16266)
Co-authored-by: Dominic R <dominic@sdko.org>
Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
2025-08-20 13:24:51 +02:00
Tana M Berry
156cda6cb6 website/docs: update Advanced Queries docs and Rel Notes with new Int Guide (cherry-pick #16191) (#16258)
website/docs: Advanced queries, remove reference to QL and add more examples (#16191)

* remove reference to QL

* add Jens' examples

* tweak

* Update website/docs/users-sources/user/user_basic_operations.md




* Update website/docs/users-sources/user/user_basic_operations.md




* add note about UX ticks

* tweak

* argh

* clarify there are more values

* add link to Event actions list

* tweaks, typo

* Update website/docs/users-sources/user/user_basic_operations.md




* Update website/docs/sys-mgmt/events/logging-events.md




* jens edits

---------

Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: Tana M Berry <tana@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
2025-08-20 04:36:47 -05:00
authentik-automation[bot]
9f5125cf6b release: 2025.8.0-rc7 2025-08-18 18:44:42 +00:00
Marc 'risson' Schmitt
a84411363e web: Fix ak-flow-card footer alignment. (cherry-pick #16236) (#16238)
Co-authored-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
Fix ak-flow-card footer alignment. (#16236)
2025-08-18 20:15:21 +02:00
Marc 'risson' Schmitt
9f3bb0210b brands: revert sort matched brand by match length (revert #15413) (cherry-pick #16233) (#16235) 2025-08-18 19:28:19 +02:00
Marc 'risson' Schmitt
d1271502ef web: bump API Client version (cherry-pick #16203) (#16226)
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2025-08-18 16:42:35 +02:00
Marc 'risson' Schmitt
d1065b2d49 policies/password: Fix amount_uppercase in password policy check (cherry-pick #16197) (#16228)
Co-authored-by: M-Slanec <mcslanec@gmail.com>
Co-authored-by: Matthew Slanec <matthewslanec@Matthews-MacBook-Pro.local>
Fix amount_uppercase in password policy check (#16197)
2025-08-18 16:42:26 +02:00
Marc 'risson' Schmitt
aa19227e30 web/a11y: QL Search Input (cherry-pick #16198) (#16229)
Co-authored-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
2025-08-18 16:41:50 +02:00
Marc 'risson' Schmitt
d7a2861bbe stages/authenticator_webauthn: Update FIDO MDS3 & Passkey aaguid blobs (cherry-pick #16196) (#16227)
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2025-08-18 16:41:33 +02:00
Marc 'risson' Schmitt
5aae9c9afa core: Add email template selector (cherry-pick #16170) (#16225)
Co-authored-by: Marcelo Elizeche Landó <marcelo@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2025-08-18 16:41:22 +02:00
Tana M Berry
93cb48c928 website/docs: add content about new Advanced Query searches (cherry-pick #16019) (#16188)
Co-authored-by: Tana M Berry <tana@goauthentik.io>
2025-08-14 18:32:05 +02:00
Marc 'risson' Schmitt
55f7f93a24 web/admin: fix settings saving (cherry-pick #16184) (#16187)
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2025-08-14 13:02:30 +00:00
authentik-automation[bot]
5a608a4235 release: 2025.8.0-rc6 2025-08-14 11:29:06 +00:00
Marc 'risson' Schmitt
77d023758f tasks: add sentry dramatiq integration (cherry-pick #16167) (#16183)
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-08-14 13:05:59 +02:00
Marc 'risson' Schmitt
f9edafd374 ci: release tag: fix missing env variables (cherry-pick #16172) (#16173) 2025-08-13 21:31:01 +02:00
Marc 'risson' Schmitt
d94219eb0e ci: release: consolidation bump version and on tag (cherry-pick #16164) (#16169)
Co-authored-by: Dominic R <dominic@sdko.org>
2025-08-13 18:22:11 +02:00
Marc 'risson' Schmitt
0871aa2cf3 ci: fix docker hub credentials (cherry-pick #16165) (#16166) 2025-08-13 15:20:03 +02:00
authentik-automation[bot]
e0fe99d0b8 release: 2025.8.0-rc5 2025-08-13 00:21:34 +00:00
Marc 'risson' Schmitt
c1acf53585 root: fix custom packages installation in docker (cherry-pick #16157) (#16158) 2025-08-13 02:08:56 +02:00
authentik-automation[bot]
baf4eed0d9 release: 2025.8.0-rc4 2025-08-12 22:26:57 +00:00
Marc 'risson' Schmitt
60e1192a7a ci: release publish: fix missing permissions (cherry-pick #16155) (#16156) 2025-08-13 00:20:52 +02:00
authentik-automation[bot]
2608e02d6e release: 2025.8.0-rc3 2025-08-12 21:49:51 +00:00
Marc 'risson' Schmitt
0a5928fbcb ci: docker push: fix version missing dash (cherry-pick #16153) (#16154) 2025-08-12 23:44:01 +02:00
authentik-automation[bot]
7b99b02b4a release: 2025.8.0-rc2 2025-08-12 21:12:07 +00:00
Marc 'risson' Schmitt
dfeadc9ebe root: fix custom packages installation in docker (cherry-pick #16150) (#16151) 2025-08-12 23:08:40 +02:00
authentik-automation[bot]
7ff64fbd09 release: 2025.8.0-rc1 2025-08-12 20:31:40 +00:00
1407 changed files with 49401 additions and 68796 deletions

View File

@@ -1,267 +0,0 @@
name: "Cherry-picker"
description: "Cherry-pick PRs based on their labels"
inputs:
token:
description: "GitHub Token"
required: true
git_user:
description: "Git user for pushing the cherry-pick PR"
required: true
git_user_email:
description: "Git user email for pushing the cherry-pick PR"
required: true
runs:
using: "composite"
steps:
- name: Check if workflow should run
id: should_run
shell: bash
env:
GITHUB_TOKEN: ${{ inputs.token }}
run: |
set -e -o pipefail
# For issues events, check if it's actually a PR
if [ "${{ github.event_name }}" = "issues" ]; then
# Check if this issue is actually a PR
PR_DATA=$(gh api repos/${{ github.repository }}/pulls/${{ github.event.issue.number }} 2>/dev/null || echo "null")
if [ "$PR_DATA" = "null" ]; then
echo "should_run=false" >> $GITHUB_OUTPUT
echo "reason=not_a_pr" >> $GITHUB_OUTPUT
echo "This is an issue, not a PR. Skipping."
exit 0
fi
# Get PR data
PR_MERGED=$(echo "$PR_DATA" | jq -r '.merged')
PR_NUMBER="${{ github.event.issue.number }}"
MERGE_COMMIT_SHA=$(echo "$PR_DATA" | jq -r '.merge_commit_sha')
# Check if it's a backport label
LABEL_NAME="${{ github.event.label.name }}"
if [[ "$LABEL_NAME" =~ ^backport/(.+)$ ]]; then
if [ "$PR_MERGED" = "true" ]; then
echo "should_run=true" >> $GITHUB_OUTPUT
echo "reason=label_added_to_merged_pr" >> $GITHUB_OUTPUT
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
echo "merge_commit_sha=$MERGE_COMMIT_SHA" >> $GITHUB_OUTPUT
exit 0
else
echo "should_run=false" >> $GITHUB_OUTPUT
echo "reason=label_added_to_open_pr" >> $GITHUB_OUTPUT
echo "Backport label added to open PR. Will run after PR is merged."
exit 0
fi
else
echo "should_run=false" >> $GITHUB_OUTPUT
echo "reason=non_backport_label" >> $GITHUB_OUTPUT
exit 0
fi
fi
# For pull_request and pull_request_target events
PR_NUMBER="${{ github.event.pull_request.number }}"
MERGE_COMMIT_SHA="${{ github.event.pull_request.merge_commit_sha }}"
# Case 1: PR was just merged (closed + merged = true)
if [ "${{ github.event.action }}" = "closed" ] && [ "${{ github.event.pull_request.merged }}" = "true" ]; then
echo "should_run=true" >> $GITHUB_OUTPUT
echo "reason=pr_merged" >> $GITHUB_OUTPUT
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
echo "merge_commit_sha=$MERGE_COMMIT_SHA" >> $GITHUB_OUTPUT
exit 0
fi
# Case 2: Label was added
if [ "${{ github.event.action }}" = "labeled" ]; then
LABEL_NAME="${{ github.event.label.name }}"
# Check if it's a backport label
if [[ "$LABEL_NAME" =~ ^backport/(.+)$ ]]; then
# Check if PR is already merged
if [ "${{ github.event.pull_request.merged }}" = "true" ]; then
echo "should_run=true" >> $GITHUB_OUTPUT
echo "reason=label_added_to_merged_pr" >> $GITHUB_OUTPUT
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
echo "merge_commit_sha=$MERGE_COMMIT_SHA" >> $GITHUB_OUTPUT
exit 0
else
echo "should_run=false" >> $GITHUB_OUTPUT
echo "reason=label_added_to_open_pr" >> $GITHUB_OUTPUT
echo "Backport label added to open PR. Will run after PR is merged."
exit 0
fi
else
echo "should_run=false" >> $GITHUB_OUTPUT
echo "reason=non_backport_label" >> $GITHUB_OUTPUT
exit 0
fi
fi
echo "should_run=false" >> $GITHUB_OUTPUT
echo "reason=unknown" >> $GITHUB_OUTPUT
- name: Configure Git
if: steps.should_run.outputs.should_run == 'true'
shell: bash
env:
user: ${{ inputs.git_user }}
email: ${{ inputs.git_user_email }}
run: |
git config --global user.name "${user}"
git config --global user.email "${email}"
- name: Get PR details and extract backport labels
if: steps.should_run.outputs.should_run == 'true'
id: pr_details
shell: bash
env:
GITHUB_TOKEN: ${{ inputs.token }}
run: |
set -e -o pipefail
PR_NUMBER="${{ steps.should_run.outputs.pr_number }}"
# Get PR details
PR_DATA=$(gh api repos/${{ github.repository }}/pulls/$PR_NUMBER)
PR_TITLE=$(echo "$PR_DATA" | jq -r '.title')
PR_AUTHOR=$(echo "$PR_DATA" | jq -r '.user.login')
echo "pr_title=$PR_TITLE" >> $GITHUB_OUTPUT
echo "pr_author=$PR_AUTHOR" >> $GITHUB_OUTPUT
# Determine which labels to process
if [ "${{ steps.should_run.outputs.reason }}" = "label_added_to_merged_pr" ]; then
# Only process the specific label that was just added
if [ "${{ github.event_name }}" = "issues" ]; then
LABEL_NAME="${{ github.event.label.name }}"
else
LABEL_NAME="${{ github.event.label.name }}"
fi
if [[ "$LABEL_NAME" =~ ^backport/(.+)$ ]]; then
echo "labels=$LABEL_NAME" >> $GITHUB_OUTPUT
else
echo "Label $LABEL_NAME does not match backport pattern"
echo "labels=" >> $GITHUB_OUTPUT
fi
else
# PR was just merged, process all backport labels
LABELS=$(gh pr view $PR_NUMBER --json labels --jq '.labels[].name' | grep '^backport/' | tr '\n' ' ' || true)
echo "labels=$LABELS" >> $GITHUB_OUTPUT
fi
- name: Cherry-pick to target branches
if: steps.should_run.outputs.should_run == 'true' && steps.pr_details.outputs.labels != ''
shell: bash
env:
GITHUB_TOKEN: ${{ inputs.token }}
run: |
set -e -o pipefail
PR_NUMBER='${{ steps.should_run.outputs.pr_number }}'
COMMIT_SHA='${{ steps.should_run.outputs.merge_commit_sha }}'
PR_TITLE='${{ steps.pr_details.outputs.pr_title }}'
PR_AUTHOR='${{ steps.pr_details.outputs.pr_author }}'
LABELS='${{ steps.pr_details.outputs.labels }}'
echo "Processing PR #$PR_NUMBER (reason: ${{ steps.should_run.outputs.reason }})"
echo "Found backport labels: $LABELS"
# Process each backport label
for label in $LABELS; do
if [[ "$label" =~ ^backport/(.+)$ ]]; then
TARGET_BRANCH="${BASH_REMATCH[1]}"
echo "Processing backport to branch: $TARGET_BRANCH"
# Check if target branch exists
if ! git ls-remote --heads origin "$TARGET_BRANCH" | grep -q "$TARGET_BRANCH"; then
echo "❌ Target branch $TARGET_BRANCH does not exist, skipping"
# Comment on the original PR about the missing branch
gh pr comment $PR_NUMBER --body "⚠️ Cannot backport to \`$TARGET_BRANCH\`: branch does not exist."
continue
fi
# Create a unique branch name for the cherry-pick
CHERRY_PICK_BRANCH="cherry-pick/${PR_NUMBER}-to-${TARGET_BRANCH}"
# Check if a cherry-pick PR already exists
EXISTING_PR=$(gh pr list --head "$CHERRY_PICK_BRANCH" --json number --jq '.[0].number' 2>/dev/null || echo "")
if [ -n "$EXISTING_PR" ]; then
echo "⚠️ Cherry-pick PR already exists: #$EXISTING_PR"
gh pr comment $PR_NUMBER --body "Cherry-pick to \`$TARGET_BRANCH\` already exists: #$EXISTING_PR"
continue
fi
# Fetch and checkout target branch
git fetch origin "$TARGET_BRANCH"
git checkout -b "$CHERRY_PICK_BRANCH" "origin/$TARGET_BRANCH"
# Attempt cherry-pick
if git cherry-pick "$COMMIT_SHA"; then
echo "✅ Cherry-pick successful for $TARGET_BRANCH"
# Push the cherry-pick branch
git push origin "$CHERRY_PICK_BRANCH"
# Create PR for the cherry-pick
CHERRY_PICK_TITLE="$PR_TITLE (cherry-pick #$PR_NUMBER to $TARGET_BRANCH)"
CHERRY_PICK_BODY="Cherry-pick of #$PR_NUMBER to \`$TARGET_BRANCH\` branch.
**Original PR:** #$PR_NUMBER
**Original Author:** @$PR_AUTHOR
**Cherry-picked commit:** $COMMIT_SHA"
NEW_PR=$(gh pr create \
--title "$CHERRY_PICK_TITLE" \
--body "$CHERRY_PICK_BODY" \
--base "$TARGET_BRANCH" \
--head "$CHERRY_PICK_BRANCH" \
--label "cherry-pick")
echo "✅ Created cherry-pick PR $NEW_PR for $TARGET_BRANCH"
# Comment on original PR
gh pr comment $PR_NUMBER --body "🍒 Cherry-pick to \`$TARGET_BRANCH\` created: $NEW_PR"
else
echo "⚠️ Cherry-pick failed for $TARGET_BRANCH, creating conflict resolution PR"
# Add conflicted files and commit
git add .
git commit -m "Cherry-pick #$PR_NUMBER to $TARGET_BRANCH (with conflicts)
This cherry-pick has conflicts that need manual resolution.
Original PR: #$PR_NUMBER
Original commit: $COMMIT_SHA"
# Push the branch with conflicts
git push origin "$CHERRY_PICK_BRANCH"
# Create PR with conflict notice
CONFLICT_TITLE="$PR_TITLE (cherry-pick #$PR_NUMBER to $TARGET_BRANCH)"
CONFLICT_BODY="⚠️ **This cherry-pick has conflicts that require manual resolution.**
Cherry-pick of #$PR_NUMBER to \`$TARGET_BRANCH\` branch.
**Original PR:** #$PR_NUMBER
**Original Author:** @$PR_AUTHOR
**Cherry-picked commit:** $COMMIT_SHA
**Please resolve the conflicts in this PR before merging.**"
NEW_PR=$(gh pr create \
--title "$CONFLICT_TITLE" \
--body "$CONFLICT_BODY" \
--base "$TARGET_BRANCH" \
--head "$CHERRY_PICK_BRANCH" \
--label "cherry-pick")
echo "⚠️ Created conflict resolution PR $NEW_PR for $TARGET_BRANCH"
# Comment on original PR
gh pr comment $PR_NUMBER --body "⚠️ Cherry-pick to \`$TARGET_BRANCH\` has conflicts: $NEW_PR"
fi
# Clean up - go back to main branch
git checkout main
git branch -D "$CHERRY_PICK_BRANCH" 2>/dev/null || true
fi
done

View File

@@ -10,14 +10,14 @@ runs:
using: "composite"
steps:
- name: Find Comment
uses: peter-evans/find-comment@a54c31d7fa095754bfef525c0c8e5e5674c4b4b1 # v2
uses: peter-evans/find-comment@v2
id: fc
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: "github-actions[bot]"
body-includes: authentik PR Installation instructions
- name: Create or update comment
uses: peter-evans/create-or-update-comment@67dcc547d311b736a8e6c5c236542148a47adc3d # v2
uses: peter-evans/create-or-update-comment@v2
with:
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}

View File

@@ -2,28 +2,16 @@
import os
from json import dumps
from sys import exit as sysexit
from time import time
from authentik import authentik_version
def must_or_fail(input: str | None, error: str) -> str:
if not input:
print(f"::error::{error}")
sysexit(1)
return input
# Decide if we should push the image or not
should_push = True
if len(os.environ.get("DOCKER_USERNAME", "")) < 1:
# Don't push if we don't have DOCKER_USERNAME, i.e. no secrets are available
should_push = False
if (
must_or_fail(os.environ.get("GITHUB_REPOSITORY"), "Repo required").lower()
== "goauthentik/authentik-internal"
):
if os.environ.get("GITHUB_REPOSITORY").lower() == "goauthentik/authentik-internal":
# Don't push on the internal repo
should_push = False
@@ -32,16 +20,13 @@ if os.environ.get("GITHUB_HEAD_REF", "") != "":
branch_name = os.environ["GITHUB_HEAD_REF"]
safe_branch_name = branch_name.replace("refs/heads/", "").replace("/", "-").replace("'", "-")
image_names = must_or_fail(os.getenv("IMAGE_NAME"), "Image name required").split(",")
image_names = os.getenv("IMAGE_NAME").split(",")
image_arch = os.getenv("IMAGE_ARCH") or None
is_pull_request = bool(os.getenv("PR_HEAD_SHA"))
is_release = "dev" not in image_names[0]
sha = must_or_fail(
os.environ["GITHUB_SHA"] if not is_pull_request else os.getenv("PR_HEAD_SHA"),
"could not determine SHA",
)
sha = os.environ["GITHUB_SHA"] if not is_pull_request else os.getenv("PR_HEAD_SHA")
# 2042.1.0 or 2042.1.0-rc1
version = authentik_version()
@@ -73,7 +58,7 @@ else:
image_main_tag = image_tags[0].split(":")[-1]
def get_attest_image_names(image_with_tags: list[str]) -> str:
def get_attest_image_names(image_with_tags: list[str]):
"""Attestation only for GHCR"""
image_tags = []
for image_name in set(name.split(":")[0] for name in image_with_tags):
@@ -97,6 +82,7 @@ if os.getenv("RELEASE", "false").lower() == "true":
image_build_args = [f"VERSION={os.getenv('REF')}"]
else:
image_build_args = [f"GIT_BUILD_HASH={sha}"]
image_build_args = "\n".join(image_build_args)
with open(os.environ["GITHUB_OUTPUT"], "a+", encoding="utf-8") as _output:
print(f"shouldPush={str(should_push).lower()}", file=_output)
@@ -109,4 +95,4 @@ with open(os.environ["GITHUB_OUTPUT"], "a+", encoding="utf-8") as _output:
print(f"imageMainTag={image_main_tag}", file=_output)
print(f"imageMainName={image_tags[0]}", file=_output)
print(f"cacheTo={cache_to}", file=_output)
print(f"imageBuildArgs={"\n".join(image_build_args)}", file=_output)
print(f"imageBuildArgs={image_build_args}", file=_output)

View File

@@ -21,12 +21,12 @@ runs:
sudo apt-get install --no-install-recommends -y libpq-dev openssl libxmlsec1-dev pkg-config gettext libkrb5-dev krb5-kdc krb5-user krb5-admin-server
- name: Install uv
if: ${{ contains(inputs.dependencies, 'python') }}
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
uses: astral-sh/setup-uv@v5
with:
enable-cache: true
- name: Setup python
if: ${{ contains(inputs.dependencies, 'python') }}
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
uses: actions/setup-python@v5
with:
python-version-file: "pyproject.toml"
- name: Install Python deps
@@ -35,15 +35,14 @@ runs:
run: uv sync --all-extras --dev --frozen
- name: Setup node
if: ${{ contains(inputs.dependencies, 'node') }}
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
uses: actions/setup-node@v4
with:
node-version-file: web/package.json
cache: "npm"
cache-dependency-path: web/package-lock.json
registry-url: 'https://registry.npmjs.org'
- name: Setup go
if: ${{ contains(inputs.dependencies, 'go') }}
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5
uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- name: Setup docker cache
@@ -57,7 +56,7 @@ runs:
run: |
export PSQL_TAG=${{ inputs.postgresql_version }}
docker compose -f .github/actions/setup/docker-compose.yml up -d
cd web && npm i
cd web && npm ci
- name: Generate config
if: ${{ contains(inputs.dependencies, 'python') }}
shell: uv run python {0}

View File

@@ -3,7 +3,6 @@ services:
image: docker.io/library/postgres:${PSQL_TAG:-16}
volumes:
- db-data:/var/lib/postgresql/data
command: "-c log_statement=all"
environment:
POSTGRES_USER: authentik
POSTGRES_PASSWORD: "EK-5jnKfjrGRm<77"

View File

@@ -1,28 +0,0 @@
name: "Process test results"
description: Convert test results to JUnit, add them to GitHub Actions and codecov
inputs:
flags:
description: Codecov flags
runs:
using: "composite"
steps:
- uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5
with:
flags: ${{ inputs.flags }}
use_oidc: true
- uses: codecov/test-results-action@47f89e9acb64b76debcd5ea40642d25a4adced9f # v1
with:
flags: ${{ inputs.flags }}
file: unittest.xml
use_oidc: true
- name: PostgreSQL Logs
shell: bash
run: |
if [[ $ACTIONS_RUNNER_DEBUG == 'true' || $ACTIONS_STEP_DEBUG == 'true' ]]; then
docker stop setup-postgresql-1
echo "::group::PostgreSQL Logs"
docker logs setup-postgresql-1
echo "::endgroup::"
fi

2
.github/cherry-pick-bot.yml vendored Normal file
View File

@@ -0,0 +1,2 @@
enabled: true
preservePullRequestTitle: true

View File

@@ -77,12 +77,6 @@ updates:
goauthentik:
patterns:
- "@goauthentik/*"
react:
patterns:
- "react"
- "react-dom"
- "@types/react"
- "@types/react-dom"
- package-ecosystem: npm
directory: "/website"
schedule:
@@ -146,7 +140,6 @@ updates:
- package-ecosystem: docker-compose
directories:
# - /scripts # Maybe
- /scripts/api
- /tests/e2e
schedule:
interval: daily

View File

@@ -42,9 +42,9 @@ jobs:
# Needed for checkout
contents: read
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3
- uses: actions/checkout@v5
- uses: docker/setup-qemu-action@v3.6.0
- uses: docker/setup-buildx-action@v3
- name: prepare variables
uses: ./.github/actions/docker-push-variables
id: ev
@@ -56,34 +56,33 @@ jobs:
release: ${{ inputs.release }}
- name: Login to Docker Hub
if: ${{ inputs.registry_dockerhub }}
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_CORP_USERNAME }}
password: ${{ secrets.DOCKER_CORP_PASSWORD }}
- name: Login to GitHub Container Registry
if: ${{ inputs.registry_ghcr }}
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: make empty clients
if: ${{ inputs.release }}
run: |
mkdir -p ./gen-ts-api
mkdir -p ./gen-go-api
- name: Setup node
if: ${{ !inputs.release }}
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v5
uses: actions/setup-node@v4
with:
node-version-file: web/package.json
cache: "npm"
cache-dependency-path: web/package-lock.json
- name: generate ts client
if: ${{ !inputs.release }}
run: make gen-client-ts
- name: Setup go
uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- name: Generate API Clients
run: |
make gen-client-ts
make gen-client-go
- name: Build Docker Image
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
uses: docker/build-push-action@v6
id: push
with:
context: .
@@ -97,7 +96,7 @@ jobs:
platforms: linux/${{ inputs.image_arch }}
cache-from: type=registry,ref=${{ steps.ev.outputs.attestImageNames }}:buildcache-${{ inputs.image_arch }}
cache-to: ${{ steps.ev.outputs.cacheTo }}
- uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3
- uses: actions/attest-build-provenance@v2
id: attest
if: ${{ steps.ev.outputs.shouldPush == 'true' }}
with:

View File

@@ -21,7 +21,7 @@ on:
jobs:
build-server-amd64:
uses: ./.github/workflows/_reusable-docker-build-single.yml
uses: ./.github/workflows/_reusable-docker-build-single.yaml
secrets: inherit
with:
image_name: ${{ inputs.image_name }}
@@ -31,7 +31,7 @@ jobs:
registry_ghcr: ${{ inputs.registry_ghcr }}
release: ${{ inputs.release }}
build-server-arm64:
uses: ./.github/workflows/_reusable-docker-build-single.yml
uses: ./.github/workflows/_reusable-docker-build-single.yaml
secrets: inherit
with:
image_name: ${{ inputs.image_name }}
@@ -49,7 +49,7 @@ jobs:
tags: ${{ steps.ev.outputs.imageTagsJSON }}
shouldPush: ${{ steps.ev.outputs.shouldPush }}
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/checkout@v5
- name: prepare variables
uses: ./.github/actions/docker-push-variables
id: ev
@@ -69,7 +69,7 @@ jobs:
matrix:
tag: ${{ fromJson(needs.get-tags.outputs.tags) }}
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/checkout@v5
- name: prepare variables
uses: ./.github/actions/docker-push-variables
id: ev
@@ -79,25 +79,25 @@ jobs:
image-name: ${{ inputs.image_name }}
- name: Login to Docker Hub
if: ${{ inputs.registry_dockerhub }}
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_CORP_USERNAME }}
password: ${{ secrets.DOCKER_CORP_PASSWORD }}
- name: Login to GitHub Container Registry
if: ${{ inputs.registry_ghcr }}
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: int128/docker-manifest-create-action@b60433fd4312d7a64a56d769b76ebe3f45cf36b4 # v2
- uses: int128/docker-manifest-create-action@v2
id: build
with:
tags: ${{ matrix.tag }}
sources: |
${{ steps.ev.outputs.attestImageNames }}@${{ needs.build-server-amd64.outputs.image-digest }}
${{ steps.ev.outputs.attestImageNames }}@${{ needs.build-server-arm64.outputs.image-digest }}
- uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3
- uses: actions/attest-build-provenance@v2
id: attest
with:
subject-name: ${{ steps.ev.outputs.attestImageNames }}

68
.github/workflows/api-py-publish.yml vendored Normal file
View File

@@ -0,0 +1,68 @@
---
name: API - Publish Python client
on:
push:
branches: [main]
paths:
- "schema.yml"
workflow_dispatch:
jobs:
build:
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
runs-on: ubuntu-latest
permissions:
id-token: write
steps:
- id: generate_token
uses: tibdex/github-app-token@v2
with:
app_id: ${{ secrets.GH_APP_ID }}
private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- uses: actions/checkout@v5
with:
token: ${{ steps.generate_token.outputs.token }}
- name: Install poetry & deps
shell: bash
run: |
pipx install poetry || true
sudo apt-get update
sudo apt-get install --no-install-recommends -y libpq-dev openssl libxmlsec1-dev pkg-config gettext
- name: Setup python and restore poetry
uses: actions/setup-python@v5
with:
python-version-file: "pyproject.toml"
- name: Generate API Client
run: make gen-client-py
- name: Publish package
working-directory: gen-py-api/
run: |
poetry build
- name: Publish package to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: gen-py-api/dist/
# We can't easily upgrade the API client being used due to poetry being poetry
# so we'll have to rely on dependabot
# - name: Upgrade /
# run: |
# export VERSION=$(cd gen-py-api && poetry version -s)
# poetry add "authentik_client=$VERSION" --allow-prereleases --lock
# - uses: peter-evans/create-pull-request@v6
# id: cpr
# with:
# token: ${{ steps.generate_token.outputs.token }}
# branch: update-root-api-client
# commit-message: "root: bump API Client version"
# title: "root: bump API Client version"
# body: "root: bump API Client version"
# delete-branch: true
# signoff: true
# # ID from https://api.github.com/users/authentik-automation[bot]
# author: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
# - uses: peter-evans/enable-pull-request-automerge@v3
# with:
# token: ${{ steps.generate_token.outputs.token }}
# pull-request-number: ${{ steps.cpr.outputs.pull-request-number }}
# merge-method: squash

View File

@@ -8,25 +8,19 @@ on:
- "schema.yml"
workflow_dispatch:
permissions:
# Required for NPM OIDC trusted publisher
id-token: write
contents: read
jobs:
build:
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
runs-on: ubuntu-latest
steps:
- id: generate_token
uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2
uses: tibdex/github-app-token@v2
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
app_id: ${{ secrets.GH_APP_ID }}
private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- uses: actions/checkout@v5
with:
token: ${{ steps.generate_token.outputs.token }}
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v5
- uses: actions/setup-node@v4
with:
node-version-file: web/package.json
registry-url: "https://registry.npmjs.org"
@@ -37,6 +31,8 @@ jobs:
run: |
npm i
npm publish --tag generated
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
- name: Upgrade /web
working-directory: web
run: |
@@ -47,7 +43,7 @@ jobs:
run: |
export VERSION=`node -e 'console.log(require("../gen-ts-api/package.json").version)'`
npm i @goauthentik/api@$VERSION
- uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7
- uses: peter-evans/create-pull-request@v7
id: cpr
with:
token: ${{ steps.generate_token.outputs.token }}
@@ -60,7 +56,7 @@ jobs:
# ID from https://api.github.com/users/authentik-automation[bot]
author: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
labels: dependencies
- uses: peter-evans/enable-pull-request-automerge@a660677d5469627102a1c1e11409dd063606628d # v3
- uses: peter-evans/enable-pull-request-automerge@v3
with:
token: ${{ steps.generate_token.outputs.token }}
pull-request-number: ${{ steps.cpr.outputs.pull-request-number }}

View File

@@ -21,7 +21,7 @@ jobs:
command:
- prettier-check
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/checkout@v5
- name: Install Dependencies
working-directory: website/
run: npm ci
@@ -32,8 +32,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v5
- uses: actions/checkout@v5
- uses: actions/setup-node@v4
with:
node-version-file: website/package.json
cache: "npm"
@@ -41,7 +41,7 @@ jobs:
- working-directory: website/
name: Install Dependencies
run: npm ci
- uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
- uses: actions/cache@v4
with:
path: |
${{ github.workspace }}/website/api/.docusaurus
@@ -55,7 +55,7 @@ jobs:
env:
NODE_ENV: production
run: npm run build -w api
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
- uses: actions/upload-artifact@v4
with:
name: api-docs
path: website/api/build
@@ -66,12 +66,12 @@ jobs:
- lint
- build
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5
- uses: actions/checkout@v5
- uses: actions/download-artifact@v5
with:
name: api-docs
path: website/api/build
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v5
- uses: actions/setup-node@v4
with:
node-version-file: website/package.json
cache: "npm"

View File

@@ -21,10 +21,10 @@ jobs:
check-changes-applied:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/checkout@v5
- name: Setup authentik env
uses: ./.github/actions/setup
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v5
- uses: actions/setup-node@v4
with:
node-version-file: lifecycle/aws/package.json
cache: "npm"
@@ -35,7 +35,7 @@ jobs:
- name: Check changes have been applied
run: |
uv run make aws-cfn
git diff --exit-code lifecycle/aws/template.yaml
git diff --exit-code
ci-aws-cfn-mark:
if: always()
needs:

View File

@@ -13,11 +13,10 @@ env:
jobs:
publish-source-docs:
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
runs-on: ubuntu-latest
timeout-minutes: 120
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/checkout@v5
- name: Setup authentik env
uses: ./.github/actions/setup
- name: generate docs
@@ -25,9 +24,9 @@ jobs:
uv run make migrate
uv run ak build_source_docs
- name: Publish
uses: netlify/actions/cli@master
with:
args: deploy --dir=source_docs --prod
env:
NETLIFY_SITE_ID: eb246b7b-1d83-4f69-89f7-01a936b4ca59
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
run: |
npm install -g netlify-cli
netlify deploy --dir=source_docs --prod

View File

@@ -21,7 +21,7 @@ jobs:
command:
- prettier-check
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/checkout@v5
- name: Install dependencies
working-directory: website/
run: npm ci
@@ -32,8 +32,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v5
- uses: actions/checkout@v5
- uses: actions/setup-node@v4
with:
node-version-file: website/package.json
cache: "npm"
@@ -48,8 +48,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v5
- uses: actions/checkout@v5
- uses: actions/setup-node@v4
with:
node-version-file: website/package.json
cache: "npm"
@@ -61,7 +61,6 @@ jobs:
working-directory: website/
run: npm run build -w integrations
build-container:
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
runs-on: ubuntu-latest
permissions:
# Needed to upload container images to ghcr.io
@@ -70,13 +69,13 @@ jobs:
id-token: write
attestations: write
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/checkout@v5
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Set up QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
uses: docker/setup-qemu-action@v3.6.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3
uses: docker/setup-buildx-action@v3
- name: prepare variables
uses: ./.github/actions/docker-push-variables
id: ev
@@ -86,14 +85,14 @@ jobs:
image-name: ghcr.io/goauthentik/dev-docs
- name: Login to Container Registry
if: ${{ steps.ev.outputs.shouldPush == 'true' }}
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Docker Image
id: push
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
uses: docker/build-push-action@v6
with:
tags: ${{ steps.ev.outputs.imageTags }}
file: website/Dockerfile
@@ -102,7 +101,7 @@ jobs:
context: .
cache-from: type=registry,ref=ghcr.io/goauthentik/dev-docs:buildcache
cache-to: ${{ steps.ev.outputs.shouldPush == 'true' && 'type=registry,ref=ghcr.io/goauthentik/dev-docs:buildcache,mode=max' || '' }}
- uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3
- uses: actions/attest-build-provenance@v2
id: attest
if: ${{ steps.ev.outputs.shouldPush == 'true' }}
with:
@@ -121,4 +120,3 @@ jobs:
- uses: re-actors/alls-green@release/v1
with:
jobs: ${{ toJSON(needs) }}
allowed-skips: ${{ github.repository == 'goauthentik/authentik-internal' && 'build-container' || '[]' }}

View File

@@ -9,7 +9,6 @@ on:
jobs:
test-container:
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
@@ -19,7 +18,7 @@ jobs:
- version-2025-4
- version-2025-2
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/checkout@v5
- run: |
current="$(pwd)"
dir="/tmp/authentik/${{ matrix.version }}"

View File

@@ -34,10 +34,9 @@ jobs:
- codespell
- pending-migrations
- ruff
- mypy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/checkout@v5
- name: Setup authentik env
uses: ./.github/actions/setup
- name: run job
@@ -45,7 +44,7 @@ jobs:
test-migrations:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/checkout@v5
- name: Setup authentik env
uses: ./.github/actions/setup
- name: run migrations
@@ -61,17 +60,18 @@ jobs:
test-migrations-from-stable:
name: test-migrations-from-stable - PostgreSQL ${{ matrix.psql }} - Run ${{ matrix.run_id }}/5
runs-on: ubuntu-latest
timeout-minutes: 30
timeout-minutes: 20
needs: test-make-seed
strategy:
fail-fast: false
matrix:
psql:
- 14-alpine
- 18-alpine
- 15-alpine
- 16-alpine
- 17-alpine
run_id: [1, 2, 3, 4, 5]
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: checkout stable
@@ -80,7 +80,15 @@ jobs:
cp authentik/lib/default.yml local.env.yml
cp -R .github ..
cp -R scripts ..
git checkout $(git tag --sort=version:refname | grep '^version/' | grep -vE -- '-rc[0-9]+$' | tail -n1)
# Previous stable tag
prev_stable=$(git tag --sort=version:refname | grep '^version/' | grep -vE -- '-rc[0-9]+$' | tail -n1)
# Current version family based on
current_version_family=$(python -c "from authentik import VERSION; print(VERSION)" | grep -vE -- 'rc[0-9]+$')
if [[ -n $current_version_family ]]; then
prev_stable=$current_version_family
fi
echo "::notice::Checking out ${prev_stable} as stable version..."
git checkout $(prev_stable)
rm -rf .github/ scripts/
mv ../.github ../scripts .
- name: Setup authentik env (stable)
@@ -112,24 +120,21 @@ jobs:
CI_TOTAL_RUNS: "5"
run: |
uv run make ci-test
- uses: ./.github/actions/test-results
if: ${{ always() }}
with:
flags: unit-migrate
test-unittest:
name: test-unittest - PostgreSQL ${{ matrix.psql }} - Run ${{ matrix.run_id }}/5
runs-on: ubuntu-latest
timeout-minutes: 30
timeout-minutes: 20
needs: test-make-seed
strategy:
fail-fast: false
matrix:
psql:
- 14-alpine
- 18-alpine
- 15-alpine
- 16-alpine
- 17-alpine
run_id: [1, 2, 3, 4, 5]
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/checkout@v5
- name: Setup authentik env
uses: ./.github/actions/setup
with:
@@ -141,27 +146,41 @@ jobs:
CI_TOTAL_RUNS: "5"
run: |
uv run make ci-test
- uses: ./.github/actions/test-results
if: ${{ always() }}
- if: ${{ always() }}
uses: codecov/codecov-action@v5
with:
flags: unit
use_oidc: true
- if: ${{ !cancelled() }}
uses: codecov/test-results-action@v1
with:
flags: unit
file: unittest.xml
use_oidc: true
test-integration:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/checkout@v5
- name: Setup authentik env
uses: ./.github/actions/setup
- name: Create k8s Kind Cluster
uses: helm/kind-action@a1b0e391336a6ee6713a0583f8c6240d70863de3 # v1.12.0
uses: helm/kind-action@v1.12.0
- name: run integration
run: |
uv run coverage run manage.py test tests/integration
uv run coverage xml
- uses: ./.github/actions/test-results
if: ${{ always() }}
- if: ${{ always() }}
uses: codecov/codecov-action@v5
with:
flags: integration
use_oidc: true
- if: ${{ !cancelled() }}
uses: codecov/test-results-action@v1
with:
flags: integration
file: unittest.xml
use_oidc: true
test-e2e:
name: test-e2e (${{ matrix.job.name }})
runs-on: ubuntu-latest
@@ -187,14 +206,14 @@ jobs:
- name: flows
glob: tests/e2e/test_flows*
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/checkout@v5
- name: Setup authentik env
uses: ./.github/actions/setup
- name: Setup e2e env (chrome, etc)
run: |
docker compose -f tests/e2e/docker-compose.yml up -d --quiet-pull
- id: cache-web
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
uses: actions/cache@v4
with:
path: web/dist
key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'package-lock.json', 'web/src/**', 'web/packages/sfe/src/**') }}-b
@@ -210,10 +229,17 @@ jobs:
run: |
uv run coverage run manage.py test ${{ matrix.job.glob }}
uv run coverage xml
- uses: ./.github/actions/test-results
if: ${{ always() }}
- if: ${{ always() }}
uses: codecov/codecov-action@v5
with:
flags: e2e
use_oidc: true
- if: ${{ !cancelled() }}
uses: codecov/test-results-action@v1
with:
flags: e2e
file: unittest.xml
use_oidc: true
ci-core-mark:
if: always()
needs:
@@ -238,7 +264,7 @@ jobs:
# Needed for checkout
contents: read
needs: ci-core-mark
uses: ./.github/workflows/_reusable-docker-build.yml
uses: ./.github/workflows/_reusable-docker-build.yaml
secrets: inherit
with:
image_name: ${{ github.repository == 'goauthentik/authentik-internal' && 'ghcr.io/goauthentik/internal-server' || 'ghcr.io/goauthentik/dev-server' }}
@@ -253,7 +279,7 @@ jobs:
pull-requests: write
timeout-minutes: 120
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/checkout@v5
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: prepare variables

View File

@@ -12,17 +12,12 @@ on:
- main
- version-*
env:
POSTGRES_DB: authentik
POSTGRES_USER: authentik
POSTGRES_PASSWORD: "EK-5jnKfjrGRm<77"
jobs:
lint-golint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6
- uses: actions/checkout@v5
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- name: Prepare and generate API
@@ -34,7 +29,7 @@ jobs:
- name: Generate API
run: make gen-client-go
- name: golangci-lint
uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8
uses: golangci/golangci-lint-action@v8
with:
version: latest
args: --timeout 5000s --verbose
@@ -42,17 +37,14 @@ jobs:
test-unittest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6
- uses: actions/checkout@v5
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- name: Setup authentik env
uses: ./.github/actions/setup
- name: Generate API
run: make gen-client-go
- name: prepare database
run: |
uv run make migrate
- name: Go unittests
run: |
go test -timeout 0 -v -race -coverprofile=coverage.out -covermode=atomic -cover ./...
@@ -67,7 +59,6 @@ jobs:
with:
jobs: ${{ toJSON(needs) }}
build-container:
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
timeout-minutes: 120
needs:
- ci-outpost-mark
@@ -87,13 +78,13 @@ jobs:
id-token: write
attestations: write
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/checkout@v5
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Set up QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
uses: docker/setup-qemu-action@v3.6.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3
uses: docker/setup-buildx-action@v3
- name: prepare variables
uses: ./.github/actions/docker-push-variables
id: ev
@@ -103,7 +94,7 @@ jobs:
image-name: ghcr.io/goauthentik/dev-${{ matrix.type }}
- name: Login to Container Registry
if: ${{ steps.ev.outputs.shouldPush == 'true' }}
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
@@ -112,7 +103,7 @@ jobs:
run: make gen-client-go
- name: Build Docker Image
id: push
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
uses: docker/build-push-action@v6
with:
tags: ${{ steps.ev.outputs.imageTags }}
file: ${{ matrix.type }}.Dockerfile
@@ -123,7 +114,7 @@ jobs:
context: .
cache-from: type=registry,ref=ghcr.io/goauthentik/dev-${{ matrix.type }}:buildcache
cache-to: ${{ steps.ev.outputs.shouldPush == 'true' && format('type=registry,ref=ghcr.io/goauthentik/dev-{0}:buildcache,mode=max', matrix.type) || '' }}
- uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3
- uses: actions/attest-build-provenance@v2
id: attest
if: ${{ steps.ev.outputs.shouldPush == 'true' }}
with:
@@ -146,13 +137,13 @@ jobs:
goos: [linux]
goarch: [amd64, arm64]
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/checkout@v5
with:
ref: ${{ github.event.pull_request.head.sha }}
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v5
- uses: actions/setup-node@v4
with:
node-version-file: web/package.json
cache: "npm"

View File

@@ -31,8 +31,8 @@ jobs:
- command: lit-analyse
project: web
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v5
- uses: actions/checkout@v5
- uses: actions/setup-node@v4
with:
node-version-file: ${{ matrix.project }}/package.json
cache: "npm"
@@ -48,8 +48,8 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v5
- uses: actions/checkout@v5
- uses: actions/setup-node@v4
with:
node-version-file: web/package.json
cache: "npm"
@@ -76,8 +76,8 @@ jobs:
- ci-web-mark
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v5
- uses: actions/checkout@v5
- uses: actions/setup-node@v4
with:
node-version-file: web/package.json
cache: "npm"

View File

@@ -29,32 +29,32 @@ jobs:
github.event.pull_request.head.repo.full_name == github.repository)
steps:
- id: generate_token
uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2
uses: tibdex/github-app-token@v2
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
app_id: ${{ secrets.GH_APP_ID }}
private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- uses: actions/checkout@v5
with:
token: ${{ steps.generate_token.outputs.token }}
- name: Compress images
id: compress
uses: calibreapp/image-actions@05b1cf44e88c3b041b841452482df9497f046ef7 # main
uses: calibreapp/image-actions@main
with:
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
githubToken: ${{ steps.generate_token.outputs.token }}
compressOnly: ${{ github.event_name != 'pull_request' }}
- uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7
- uses: peter-evans/create-pull-request@v7
if: "${{ github.event_name != 'pull_request' && steps.compress.outputs.markdown != '' }}"
id: cpr
with:
token: ${{ steps.generate_token.outputs.token }}
title: "*: Auto compress images"
branch-suffix: timestamp
commit-message: "*: compress images"
commit-messsage: "*: compress images"
body: ${{ steps.compress.outputs.markdown }}
delete-branch: true
signoff: true
labels: dependencies
- uses: peter-evans/enable-pull-request-automerge@a660677d5469627102a1c1e11409dd063606628d # v3
- uses: peter-evans/enable-pull-request-automerge@v3
if: "${{ github.event_name != 'pull_request' && steps.compress.outputs.markdown != '' }}"
with:
token: ${{ steps.generate_token.outputs.token }}

View File

@@ -13,21 +13,20 @@ env:
jobs:
build:
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
runs-on: ubuntu-latest
steps:
- id: generate_token
uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2
uses: tibdex/github-app-token@v2
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
app_id: ${{ secrets.GH_APP_ID }}
private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- uses: actions/checkout@v5
with:
token: ${{ steps.generate_token.outputs.token }}
- name: Setup authentik env
uses: ./.github/actions/setup
- run: uv run ak update_webauthn_mds
- uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7
- uses: peter-evans/create-pull-request@v7
id: cpr
with:
token: ${{ steps.generate_token.outputs.token }}
@@ -40,7 +39,7 @@ jobs:
# ID from https://api.github.com/users/authentik-automation[bot]
author: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
labels: dependencies
- uses: peter-evans/enable-pull-request-automerge@a660677d5469627102a1c1e11409dd063606628d # v3
- uses: peter-evans/enable-pull-request-automerge@v3
with:
token: ${{ steps.generate_token.outputs.token }}
pull-request-number: ${{ steps.cpr.outputs.pull-request-number }}

View File

@@ -1,36 +0,0 @@
name: GH - Cherry-pick
on:
pull_request_target:
types: [closed, labeled]
jobs:
cherry-pick:
runs-on: ubuntu-latest
steps:
- id: app-token
name: Generate app token
uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2
if: ${{ env.GH_APP_ID != '' }}
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
env:
GH_APP_ID: ${{ secrets.GH_APP_ID }}
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
if: ${{ steps.app-token.outcome != 'skipped' }}
with:
fetch-depth: 0
token: "${{ steps.app-token.outputs.token }}"
- id: get-user-id
if: ${{ steps.app-token.outcome != 'skipped' }}
name: Get GitHub app user ID
run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: "${{ steps.app-token.outputs.token }}"
- uses: ./.github/actions/cherry-pick
if: ${{ steps.app-token.outcome != 'skipped' }}
with:
token: ${{ steps.app-token.outputs.token }}
git_user: ${{ steps.app-token.outputs.app-slug }}[bot]
git_user_email: '${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com'

View File

@@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
uses: actions/checkout@v5
- name: Cleanup
run: |

View File

@@ -5,25 +5,28 @@ on:
# schedule:
# - cron: "0 0 * * *" # every day at midnight
workflow_dispatch:
inputs:
dry-run:
type: boolean
description: Enable dry-run mode
jobs:
clean-ghcr:
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
name: Delete old unused container images
runs-on: ubuntu-latest
steps:
- id: generate_token
uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2
uses: tibdex/github-app-token@v2
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
app_id: ${{ secrets.GH_APP_ID }}
private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- name: Delete 'dev' containers older than a week
uses: snok/container-retention-policy@3b0972b2276b171b212f8c4efbca59ebba26eceb # v2
uses: snok/container-retention-policy@3b0972b2276b171b212f8c4efbca59ebba26eceb # v3.0.1
with:
image-names: dev-server,dev-ldap,dev-proxy
image-tags: "!gh-next,!gh-main"
cut-off: One week ago UTC
account-type: org
org-name: goauthentik
untagged-only: false
account: goauthentik
tag-selection: untagged
token: ${{ steps.generate_token.outputs.token }}
skip-tags: gh-next,gh-main
dry-run: ${{ inputs.dry-run }}

View File

@@ -12,14 +12,8 @@ on:
- packages/esbuild-plugin-live-reload/**
workflow_dispatch:
permissions:
# Required for NPM OIDC trusted publisher
id-token: write
contents: read
jobs:
publish:
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
@@ -31,16 +25,16 @@ jobs:
- packages/tsconfig
- packages/esbuild-plugin-live-reload
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/checkout@v5
with:
fetch-depth: 2
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v5
- uses: actions/setup-node@v4
with:
node-version-file: ${{ matrix.package }}/package.json
registry-url: "https://registry.npmjs.org"
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@24d32ffd492484c1d75e0c0b894501ddb9d30d62 # 24d32ffd492484c1d75e0c0b894501ddb9d30d62
uses: tj-actions/changed-files@ed68ef82c095e0d48ec87eccea555d944a631a4c
with:
files: |
${{ matrix.package }}/package.json
@@ -51,3 +45,5 @@ jobs:
npm ci
npm run build
npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}

View File

@@ -24,14 +24,14 @@ jobs:
language: ["go", "javascript", "python"]
steps:
- name: Checkout repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
uses: actions/checkout@v5
- name: Setup authentik env
uses: ./.github/actions/setup
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v4
uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
uses: github/codeql-action/analyze@v3

View File

@@ -26,5 +26,5 @@ jobs:
image: semgrep/semgrep
if: (github.actor != 'dependabot[bot]')
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/checkout@v5
- run: semgrep ci

View File

@@ -29,12 +29,12 @@ jobs:
steps:
- id: app-token
name: Generate app token
uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- name: Checkout main
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
uses: actions/checkout@v5
with:
ref: main
token: "${{ steps.app-token.outputs.token }}"
@@ -43,13 +43,10 @@ jobs:
with:
dependencies: python
- name: Create version branch
env:
GH_TOKEN: "${{ steps.app-token.outputs.token }}"
run: |
current_major_version="$(uv version --short | grep -oE "^[0-9]{4}\.[0-9]{1,2}")"
git checkout -b "version-${current_major_version}"
git push origin "version-${current_major_version}"
gh label create "backport/version-${current_major_version}" --description "Add this label to PRs to backport changes to version-${current_major_version}" --color "fbca04"
bump-version-pr:
name: Open version bump PR
needs:
@@ -57,12 +54,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- id: generate_token
uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2
uses: tibdex/github-app-token@v2
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
app_id: ${{ secrets.GH_APP_ID }}
private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- name: Checkout main
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
uses: actions/checkout@v5
with:
ref: main
token: ${{ steps.generate_token.outputs.token }}
@@ -73,7 +70,7 @@ jobs:
- name: Bump version
run: "make bump version=${{ inputs.next_version }}.0-rc1"
- name: Create pull request
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7
uses: peter-evans/create-pull-request@v7
with:
token: ${{ steps.generate_token.outputs.token }}
branch: release-bump-${{ inputs.next_version }}

View File

@@ -12,11 +12,10 @@ permissions:
jobs:
update-next:
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
runs-on: ubuntu-latest
environment: internal-production
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/checkout@v5
with:
ref: main
- run: |

View File

@@ -7,7 +7,7 @@ on:
jobs:
build-server:
uses: ./.github/workflows/_reusable-docker-build.yml
uses: ./.github/workflows/_reusable-docker-build.yaml
secrets: inherit
permissions:
contents: read
@@ -31,11 +31,11 @@ jobs:
id-token: write
attestations: write
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/checkout@v5
- name: Set up QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
uses: docker/setup-qemu-action@v3.6.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3
uses: docker/setup-buildx-action@v3
- name: prepare variables
uses: ./.github/actions/docker-push-variables
id: ev
@@ -44,21 +44,21 @@ jobs:
with:
image-name: ghcr.io/goauthentik/docs
- name: Login to GitHub Container Registry
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Docker Image
id: push
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
uses: docker/build-push-action@v6
with:
tags: ${{ steps.ev.outputs.imageTags }}
file: website/Dockerfile
push: true
platforms: linux/amd64,linux/arm64
context: .
- uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3
- uses: actions/attest-build-provenance@v2
id: attest
if: true
with:
@@ -83,14 +83,19 @@ jobs:
- radius
- rac
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6
- uses: actions/checkout@v5
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- uses: actions/setup-node@v5
with:
node-version-file: web/package.json
cache: "npm"
cache-dependency-path: web/package-lock.json
- name: Set up QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
uses: docker/setup-qemu-action@v3.6.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3
uses: docker/setup-buildx-action@v3
- name: prepare variables
uses: ./.github/actions/docker-push-variables
id: ev
@@ -98,23 +103,23 @@ jobs:
DOCKER_USERNAME: ${{ secrets.DOCKER_CORP_USERNAME }}
with:
image-name: ghcr.io/goauthentik/${{ matrix.type }},authentik/${{ matrix.type }}
- name: make empty clients
- name: Generate API Clients
run: |
mkdir -p ./gen-ts-api
mkdir -p ./gen-go-api
make gen-client-ts
make gen-client-go
- name: Docker Login Registry
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_CORP_USERNAME }}
password: ${{ secrets.DOCKER_CORP_PASSWORD }}
- name: Login to GitHub Container Registry
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Docker Image
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
uses: docker/build-push-action@v6
id: push
with:
push: true
@@ -124,7 +129,7 @@ jobs:
file: ${{ matrix.type }}.Dockerfile
platforms: linux/amd64,linux/arm64
context: .
- uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3
- uses: actions/attest-build-provenance@v2
id: attest
with:
subject-name: ${{ steps.ev.outputs.attestImageNames }}
@@ -146,19 +151,26 @@ jobs:
goos: [linux, darwin]
goarch: [amd64, arm64]
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6
- uses: actions/checkout@v5
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v5
- uses: actions/setup-node@v4
with:
node-version-file: web/package.json
cache: "npm"
cache-dependency-path: web/package-lock.json
- name: Build web
- name: Install web dependencies
working-directory: web/
run: |
npm ci
- name: Generate API Clients
run: |
make gen-client-ts
make gen-client-go
- name: Build web
working-directory: web/
run: |
npm run build-proxy
- name: Build outpost
run: |
@@ -168,7 +180,7 @@ jobs:
export CGO_ENABLED=0
go build -tags=outpost_static_embed -v -o ./authentik-outpost-${{ matrix.type }}_${{ matrix.goos }}_${{ matrix.goarch }} ./cmd/${{ matrix.type }}
- name: Upload binaries to release
uses: svenstaro/upload-release-action@81c65b7cd4de9b2570615ce3aad67a41de5b1a13 # v2
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ./authentik-outpost-${{ matrix.type }}_${{ matrix.goos }}_${{ matrix.goarch }}
@@ -186,8 +198,8 @@ jobs:
AWS_REGION: eu-central-1
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: aws-actions/configure-aws-credentials@00943011d9042930efac3dcd3a170e4273319bc8 # v5
- uses: actions/checkout@v5
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: "arn:aws:iam::016170277896:role/github_goauthentik_authentik"
aws-region: ${{ env.AWS_REGION }}
@@ -202,14 +214,14 @@ jobs:
- build-outpost-binary
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/checkout@v5
- name: Run test suite in final docker images
run: |
echo "PG_PASS=$(openssl rand 32 | base64 -w 0)" >> .env
echo "AUTHENTIK_SECRET_KEY=$(openssl rand 32 | base64 -w 0)" >> .env
docker compose pull -q
docker compose up --no-start
docker compose start postgresql
docker compose start postgresql redis
docker compose run -u root server test-all
sentry-release:
needs:
@@ -218,7 +230,7 @@ jobs:
- build-outpost-binary
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/checkout@v5
- name: prepare variables
uses: ./.github/actions/docker-push-variables
id: ev
@@ -232,7 +244,7 @@ jobs:
container=$(docker container create ${{ steps.ev.outputs.imageMainName }})
docker cp ${container}:web/ .
- name: Create a Sentry.io release
uses: getsentry/action-release@4f502acc1df792390abe36f2dcb03612ef144818 # v3
uses: getsentry/action-release@v3
continue-on-error: true
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}

View File

@@ -47,8 +47,14 @@ jobs:
test:
name: Pre-release test
runs-on: ubuntu-latest
needs:
- check-inputs
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/checkout@v5
with:
ref: "version-${{ needs.check-inputs.outputs.major_version }}"
- name: Setup authentik env
uses: ./.github/actions/setup
- run: make test-docker
bump-authentik:
name: Bump authentik version
@@ -59,7 +65,7 @@ jobs:
steps:
- id: app-token
name: Generate app token
uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
@@ -68,7 +74,7 @@ jobs:
run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: "${{ steps.app-token.outputs.token }}"
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/checkout@v5
with:
ref: "version-${{ needs.check-inputs.outputs.major_version }}"
token: "${{ steps.app-token.outputs.token }}"
@@ -83,11 +89,12 @@ jobs:
# ID from https://api.github.com/users/authentik-automation[bot]
git config --global user.name '${{ steps.app-token.outputs.app-slug }}[bot]'
git config --global user.email '${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com'
git pull
git commit -a -m "release: ${{ inputs.version }}" --allow-empty
git tag "version/${{ inputs.version }}" HEAD -m "version/${{ inputs.version }}"
git push --follow-tags
- name: Create Release
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2
uses: softprops/action-gh-release@v2
with:
token: "${{ steps.app-token.outputs.token }}"
tag_name: "version/${{ inputs.version }}"
@@ -106,7 +113,7 @@ jobs:
steps:
- id: app-token
name: Generate app token
uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
@@ -116,7 +123,7 @@ jobs:
run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: "${{ steps.app-token.outputs.token }}"
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/checkout@v5
with:
repository: "${{ github.repository_owner }}/helm"
token: "${{ steps.app-token.outputs.token }}"
@@ -128,7 +135,7 @@ jobs:
sed -E -i 's/[0-9]{4}\.[0-9]{1,2}\.[0-9]+$/${{ inputs.version }}/' charts/authentik/Chart.yaml
./scripts/helm-docs.sh
- name: Create pull request
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7
uses: peter-evans/create-pull-request@v7
with:
token: "${{ steps.app-token.outputs.token }}"
branch: bump-${{ inputs.version }}
@@ -148,7 +155,7 @@ jobs:
steps:
- id: app-token
name: Generate app token
uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
@@ -158,7 +165,7 @@ jobs:
run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: "${{ steps.app-token.outputs.token }}"
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/checkout@v5
with:
repository: "${{ github.repository_owner }}/version"
token: "${{ steps.app-token.outputs.token }}"
@@ -183,7 +190,7 @@ jobs:
'.stable.version = $version | .stable.changelog = $changelog | .stable.changelog_url = $changelog_url' version.json > version.new.json
mv version.new.json version.json
- name: Create pull request
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7
uses: peter-evans/create-pull-request@v7
with:
token: "${{ steps.app-token.outputs.token }}"
branch: bump-${{ inputs.version }}

View File

@@ -1,22 +0,0 @@
---
name: Repo - Cleanup internal mirror
on:
workflow_dispatch:
jobs:
to_internal:
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
with:
fetch-depth: 0
- if: ${{ env.MIRROR_KEY != '' }}
uses: BeryJu/repository-mirroring-action@5cf300935bc2e068f73ea69bcc411a8a997208eb # 5cf300935bc2e068f73ea69bcc411a8a997208eb
with:
target_repo_url: git@github.com:goauthentik/authentik-internal.git
ssh_private_key: ${{ secrets.GH_MIRROR_KEY }}
args: --tags --force --prune
env:
MIRROR_KEY: ${{ secrets.GH_MIRROR_KEY }}

View File

@@ -1,21 +0,0 @@
---
name: Repo - Mirror to internal
on: [push, delete]
jobs:
to_internal:
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
with:
fetch-depth: 0
- if: ${{ env.MIRROR_KEY != '' }}
uses: BeryJu/repository-mirroring-action@5cf300935bc2e068f73ea69bcc411a8a997208eb # 5cf300935bc2e068f73ea69bcc411a8a997208eb
with:
target_repo_url: git@github.com:goauthentik/authentik-internal.git
ssh_private_key: ${{ secrets.GH_MIRROR_KEY }}
args: --tags --force
env:
MIRROR_KEY: ${{ secrets.GH_MIRROR_KEY }}

View File

@@ -12,15 +12,14 @@ permissions:
jobs:
stale:
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
runs-on: ubuntu-latest
steps:
- id: generate_token
uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2
uses: tibdex/github-app-token@v2
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10
app_id: ${{ secrets.GH_APP_ID }}
private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- uses: actions/stale@v9
with:
repo-token: ${{ steps.generate_token.outputs.token }}
days-before-stale: 60

View File

@@ -20,14 +20,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Find Comment
uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad # v4
uses: peter-evans/find-comment@v3
id: fc
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: "github-actions[bot]"
body-includes: authentik translations instructions
- name: Create or update comment
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5
uses: peter-evans/create-or-update-comment@v4
with:
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}

View File

@@ -17,20 +17,19 @@ env:
jobs:
compile:
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
runs-on: ubuntu-latest
steps:
- id: generate_token
if: ${{ github.event_name != 'pull_request' }}
uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2
uses: tibdex/github-app-token@v2
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
app_id: ${{ secrets.GH_APP_ID }}
private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- uses: actions/checkout@v5
if: ${{ github.event_name != 'pull_request' }}
with:
token: ${{ steps.generate_token.outputs.token }}
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/checkout@v5
if: ${{ github.event_name == 'pull_request' }}
- name: Setup authentik env
uses: ./.github/actions/setup
@@ -45,7 +44,7 @@ jobs:
make web-check-compile
- name: Create Pull Request
if: ${{ github.event_name != 'pull_request' }}
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7
uses: peter-evans/create-pull-request@v7
with:
token: ${{ steps.generate_token.outputs.token }}
branch: extract-compile-backend-translation

View File

@@ -16,12 +16,12 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.event.pull_request.user.login == 'transifex-integration[bot]'}}
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/checkout@v5
- id: generate_token
uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2
uses: tibdex/github-app-token@v2
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
app_id: ${{ secrets.GH_APP_ID }}
private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- name: Get current title
id: title
env:
@@ -34,7 +34,7 @@ jobs:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
run: |
gh pr edit ${{ github.event.pull_request.number }} -t "translate: ${{ steps.title.outputs.title }}" --add-label dependencies
- uses: peter-evans/enable-pull-request-automerge@a660677d5469627102a1c1e11409dd063606628d # v3
- uses: peter-evans/enable-pull-request-automerge@v3
with:
token: ${{ steps.generate_token.outputs.token }}
pull-request-number: ${{ github.event.pull_request.number }}

2
.gitignore vendored
View File

@@ -72,7 +72,7 @@ unittest.xml
# Translations
# Have to include binary mo files as they are annoying to compile at build time
# since a full postgres instance is required
# since a full postgres and redis instance are required
# *.mo
# Django stuff:

15
.vscode/settings.json vendored
View File

@@ -1,16 +1,4 @@
{
"[css]": {
"editor.minimap.markSectionHeaderRegex": "#\\bregion\\s*(?<separator>-?)\\s*(?<label>.*)\\*/$"
},
"[makefile]": {
"editor.minimap.markSectionHeaderRegex": "^#{25}\n##\\s\\s*(?<separator>-?)\\s*(?<label>[^\n]*)\n#{25}$"
},
"[dockerfile]": {
"editor.minimap.markSectionHeaderRegex": "\\bStage\\s*\\d:(?<separator>-?)\\s*(?<label>.*)$"
},
"[jsonc]": {
"editor.minimap.markSectionHeaderRegex": "#\\bregion\\s*(?<separator>-?)\\s*(?<label>.*)$"
},
"todo-tree.tree.showCountsInTree": true,
"todo-tree.tree.showBadges": true,
"yaml.customTags": [
@@ -49,9 +37,6 @@
"go.testFlags": [
"-count=1"
],
"go.testEnvVars": {
"WORKSPACE_DIR": "${workspaceFolder}"
},
"github-actions.workflows.pinned.workflows": [
".github/workflows/ci-main.yml"
]

View File

@@ -24,8 +24,6 @@ Makefile @goauthentik/infrastructure
.editorconfig @goauthentik/infrastructure
CODEOWNERS @goauthentik/infrastructure
# Backend packages
packages/django-channels-postgres @goauthentik/backend
packages/django-postgres-cache @goauthentik/backend
packages/django-dramatiq-postgres @goauthentik/backend
# Web packages
packages/docusaurus-config @goauthentik/frontend
@@ -35,12 +33,17 @@ packages/prettier-config @goauthentik/frontend
packages/tsconfig @goauthentik/frontend
# Web
web/ @goauthentik/frontend
tests/wdio/ @goauthentik/frontend
# Locale
locale/ @goauthentik/backend @goauthentik/frontend
web/xliff/ @goauthentik/backend @goauthentik/frontend
# Docs
# Docs & Website
docs/ @goauthentik/docs
# TODO Remove after moving website to docs
website/ @goauthentik/docs
CODE_OF_CONDUCT.md @goauthentik/docs
# Security
SECURITY.md @goauthentik/security @goauthentik/docs
# TODO Remove after moving website to docs
website/security/ @goauthentik/security @goauthentik/docs
docs/security/ @goauthentik/security @goauthentik/docs

View File

@@ -26,7 +26,7 @@ RUN npm run build && \
npm run build:sfe
# Stage 2: Build go proxy
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.3-bookworm AS go-builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25-bookworm AS go-builder
ARG TARGETOS
ARG TARGETARCH
@@ -44,6 +44,7 @@ RUN --mount=type=cache,id=apt-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/v
RUN --mount=type=bind,target=/go/src/goauthentik.io/go.mod,src=./go.mod \
--mount=type=bind,target=/go/src/goauthentik.io/go.sum,src=./go.sum \
--mount=type=bind,target=/go/src/goauthentik.io/gen-go-api,src=./gen-go-api \
--mount=type=cache,target=/go/pkg/mod \
go mod download
@@ -57,6 +58,7 @@ COPY ./go.mod /go/src/goauthentik.io/go.mod
COPY ./go.sum /go/src/goauthentik.io/go.sum
RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
--mount=type=bind,target=/go/src/goauthentik.io/gen-go-api,src=./gen-go-api \
--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 GOFIPS140=latest GOARM="${TARGETVARIANT#v}" \
@@ -76,9 +78,9 @@ RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
/bin/sh -c "GEOIPUPDATE_LICENSE_KEY_FILE=/run/secrets/GEOIPUPDATE_LICENSE_KEY /usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0"
# Stage 4: Download uv
FROM ghcr.io/astral-sh/uv:0.9.4 AS uv
FROM ghcr.io/astral-sh/uv:0.8.8 AS uv
# Stage 5: Base python image
FROM ghcr.io/goauthentik/fips-python:3.13.7-slim-trixie-fips AS python-base
FROM ghcr.io/goauthentik/fips-python:3.13.6-slim-bookworm-fips AS python-base
ENV VENV_PATH="/ak-root/.venv" \
PATH="/lifecycle:/ak-root/.venv/bin:$PATH" \

130
Makefile
View File

@@ -16,25 +16,9 @@ GEN_API_GO = gen-go-api
pg_user := $(shell uv run python -m authentik.lib.config postgresql.user 2>/dev/null)
pg_host := $(shell uv run python -m authentik.lib.config postgresql.host 2>/dev/null)
pg_name := $(shell uv run python -m authentik.lib.config postgresql.name 2>/dev/null)
redis_db := $(shell uv run python -m authentik.lib.config redis.db 2>/dev/null)
UNAME := $(shell uname)
# For macOS users, add the libxml2 installed from brew libxmlsec1 to the build path
# to prevent SAML-related tests from failing and ensure correct pip dependency compilation
ifeq ($(UNAME), Darwin)
# Only add for brew users who installed libxmlsec1
BREW_EXISTS := $(shell command -v brew 2> /dev/null)
ifdef BREW_EXISTS
LIBXML2_EXISTS := $(shell brew list libxml2 2> /dev/null)
ifdef LIBXML2_EXISTS
BREW_LDFLAGS := -L$(shell brew --prefix libxml2)/lib $(LDFLAGS)
BREW_CPPFLAGS := -I$(shell brew --prefix libxml2)/include $(CPPFLAGS)
BREW_PKG_CONFIG_PATH := $(shell brew --prefix libxml2)/lib/pkgconfig:$(PKG_CONFIG_PATH)
endif
endif
endif
all: lint-fix lint gen web test ## Lint, build, and test everything
all: lint-fix lint test gen web ## Lint, build, and test everything
HELP_WIDTH := $(shell grep -h '^[a-z][^ ]*:.*\#\#' $(MAKEFILE_LIST) 2>/dev/null | \
cut -d':' -f1 | awk '{printf "%d\n", length}' | sort -rn | head -1)
@@ -66,14 +50,7 @@ lint: ## Lint the python and golang sources
golangci-lint run -v
core-install:
ifdef LIBXML2_EXISTS
# Clear cache to ensure fresh compilation
uv cache clean
# Force compilation from source for lxml and xmlsec with correct environment
LDFLAGS="$(BREW_LDFLAGS)" CPPFLAGS="$(BREW_CPPFLAGS)" PKG_CONFIG_PATH="$(BREW_PKG_CONFIG_PATH)" uv sync --frozen --reinstall-package lxml --reinstall-package xmlsec --no-binary-package lxml --no-binary-package xmlsec
else
uv sync --frozen
endif
migrate: ## Run the Authentik Django server's migrations
uv run python -m lifecycle.migrate
@@ -106,6 +83,7 @@ dev-drop-db:
dropdb -U ${pg_user} -h ${pg_host} ${pg_name} || true
# Also remove the test-db if it exists
dropdb -U ${pg_user} -h ${pg_host} test_${pg_name} || true
redis-cli -n ${redis_db} flushall
dev-create-db:
createdb -U ${pg_user} -h ${pg_host} ${pg_name}
@@ -120,11 +98,11 @@ bump: ## Bump authentik version. Usage: make bump version=20xx.xx.xx
ifndef version
$(error Usage: make bump version=20xx.xx.xx )
endif
$(eval current_version := $(shell cat ${PWD}/internal/constants/VERSION))
sed -i 's/^version = ".*"/version = "$(version)"/' pyproject.toml
sed -i 's/^VERSION = ".*"/VERSION = "$(version)"/' authentik/__init__.py
$(MAKE) gen-build gen-compose aws-cfn
npm version --no-git-tag-version --allow-same-version $(version)
cd ${PWD}/web && npm version --no-git-tag-version --allow-same-version $(version)
sed -i "s/\"${current_version}\"/\"$(version)\"/" ${PWD}/package.json ${PWD}/package-lock.json ${PWD}/web/package.json ${PWD}/web/package-lock.json
echo -n $(version) > ${PWD}/internal/constants/VERSION
#########################
@@ -149,13 +127,14 @@ gen-changelog: ## (Release) generate the changelog based from the commits since
npx prettier --write changelog.md
gen-diff: ## (Release) generate the changelog diff between the current schema and the last tag
git show $(shell git describe --tags $(shell git rev-list --tags --max-count=1)):schema.yml > schema-old.yml
docker compose -f scripts/api/docker-compose.yml run --rm --user "${UID}:${GID}" diff \
--markdown \
/local/diff.md \
/local/schema-old.yml \
/local/schema.yml
rm schema-old.yml
git show $(shell git describe --tags $(shell git rev-list --tags --max-count=1)):schema.yml > old_schema.yml
docker run \
--rm -v ${PWD}:/local \
--user ${UID}:${GID} \
docker.io/openapitools/openapi-diff:2.1.0-beta.8 \
--markdown /local/diff.md \
/local/old_schema.yml /local/schema.yml
rm old_schema.yml
sed -i 's/{/&#123;/g' diff.md
sed -i 's/}/&#125;/g' diff.md
npx prettier --write diff.md
@@ -164,48 +143,48 @@ gen-clean-ts: ## Remove generated API client for TypeScript
rm -rf ${PWD}/${GEN_API_TS}/
rm -rf ${PWD}/web/node_modules/@goauthentik/api/
gen-clean-py: ## Remove generated API client for Python
rm -rf ${PWD}/${GEN_API_PY}
gen-clean-go: ## Remove generated API client for Go
rm -rf ${PWD}/${GEN_API_GO}
gen-clean-py: ## Remove generated API client for Python
rm -rf ${PWD}/${GEN_API_PY}/
gen-clean: gen-clean-ts gen-clean-go gen-clean-py ## Remove generated API clients
gen-client-ts: gen-clean-ts ## Build and install the authentik API for Typescript into the authentik UI Application
docker compose -f scripts/api/docker-compose.yml run --rm --user "${UID}:${GID}" gen \
generate \
docker run \
--rm -v ${PWD}:/local \
--user ${UID}:${GID} \
docker.io/openapitools/openapi-generator-cli:v7.11.0 generate \
-i /local/schema.yml \
-g typescript-fetch \
-o /local/${GEN_API_TS} \
-c /local/scripts/api/ts-config.yaml \
-c /local/scripts/api-ts-config.yaml \
--additional-properties=npmVersion=${NPM_VERSION} \
--git-repo-id authentik \
--git-user-id goauthentik
cd ${PWD}/${GEN_API_TS} && npm i
cd ${PWD}/${GEN_API_TS} && npm link
cd ${PWD}/web && npm link @goauthentik/api
gen-client-py: gen-clean-py ## Build and install the authentik API for Python
mkdir -p ${PWD}/${GEN_API_PY}
ifeq ($(wildcard ${PWD}/${GEN_API_PY}/.*),)
git clone --depth 1 https://github.com/goauthentik/client-python.git ${PWD}/${GEN_API_PY}
else
cd ${PWD}/${GEN_API_PY} && git pull
endif
cp ${PWD}/schema.yml ${PWD}/${GEN_API_PY}
make -C ${PWD}/${GEN_API_PY} build version=${NPM_VERSION}
docker run \
--rm -v ${PWD}:/local \
--user ${UID}:${GID} \
docker.io/openapitools/openapi-generator-cli:v7.11.0 generate \
-i /local/schema.yml \
-g python \
-o /local/${GEN_API_PY} \
-c /local/scripts/api-py-config.yaml \
--additional-properties=packageVersion=${NPM_VERSION} \
--git-repo-id authentik \
--git-user-id goauthentik
gen-client-go: gen-clean-go ## Build and install the authentik API for Golang
mkdir -p ${PWD}/${GEN_API_GO}
ifeq ($(wildcard ${PWD}/${GEN_API_GO}/.*),)
git clone --depth 1 https://github.com/goauthentik/client-go.git ${PWD}/${GEN_API_GO}
else
cd ${PWD}/${GEN_API_GO} && git pull
endif
cp ${PWD}/schema.yml ${PWD}/${GEN_API_GO}
make -C ${PWD}/${GEN_API_GO} build
make -C ${PWD}/${GEN_API_GO} build version=${NPM_VERSION}
go mod edit -replace goauthentik.io/api/v3=./${GEN_API_GO}
gen-dev-config: ## Generate a local development config file
@@ -226,30 +205,34 @@ node-install: ## Install the necessary libraries to build Node.js packages
#########################
web-build: node-install ## Build the Authentik UI
npm run --prefix web build
cd web && npm run build
web: web-lint-fix web-lint web-check-compile ## Automatically fix formatting issues in the Authentik UI source code, lint the code, and compile it
web-test: ## Run tests for the Authentik UI
npm run --prefix web test
cd web && npm run test
web-watch: ## Build and watch the Authentik UI for changes, updating automatically
npm run --prefix web watch
rm -rf web/dist/
mkdir web/dist/
touch web/dist/.gitkeep
cd web && npm run watch
web-storybook-watch: ## Build and run the storybook documentation server
npm run --prefix web storybook
cd web && npm run storybook
web-lint-fix:
npm run --prefix web prettier
cd web && npm run prettier
web-lint:
npm run --prefix web lint
npm run --prefix web lit-analyse
cd web && npm run lint
cd web && npm run lit-analyse
web-check-compile:
npm run --prefix web tsc
cd web && npm run tsc
web-i18n-extract:
npm run --prefix web extract-locales
cd web && npm run extract-locales
#########################
## Docs
@@ -261,31 +244,31 @@ docs-install:
npm ci --prefix website
docs-lint-fix: lint-codespell
npm run --prefix website prettier
npm run prettier --prefix website
docs-build:
npm run --prefix website build
npm run build --prefix website
docs-watch: ## Build and watch the topics documentation
npm run --prefix website start
npm run start --prefix website
integrations: docs-lint-fix integrations-build ## Fix formatting issues in the integrations source code, lint the code, and compile it
integrations-build:
npm run --prefix website -w integrations build
npm run build --prefix website -w integrations
integrations-watch: ## Build and watch the Integrations documentation
npm run --prefix website -w integrations start
npm run start --prefix website -w integrations
docs-api-build:
npm run --prefix website -w api build
npm run build --prefix website -w api
docs-api-watch: ## Build and watch the API documentation
npm run --prefix website -w api build:api
npm run --prefix website -w api start
npm run build:api --prefix website -w api
npm run start --prefix website -w api
docs-api-clean: ## Clean generated API documentation
npm run --prefix website -w api build:api:clean
npm run build:api:clean --prefix website -w api
#########################
## Docker
@@ -308,9 +291,6 @@ ci--meta-debug:
python -V
node --version
ci-mypy: ci--meta-debug
uv run mypy --strict $(PY_SOURCES)
ci-black: ci--meta-debug
uv run black --check $(PY_SOURCES)

View File

@@ -9,6 +9,7 @@
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/goauthentik/authentik/ci-outpost.yml?branch=main&label=outpost%20build&style=for-the-badge)](https://github.com/goauthentik/authentik/actions/workflows/ci-outpost.yml)
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/goauthentik/authentik/ci-web.yml?branch=main&label=web%20build&style=for-the-badge)](https://github.com/goauthentik/authentik/actions/workflows/ci-web.yml)
[![Code Coverage](https://img.shields.io/codecov/c/gh/goauthentik/authentik?style=for-the-badge)](https://codecov.io/gh/goauthentik/authentik)
![Docker pulls](https://img.shields.io/docker/pulls/authentik/server.svg?style=for-the-badge)
![Latest version](https://img.shields.io/docker/v/authentik/server?sort=semver&style=for-the-badge)
[![](https://img.shields.io/badge/Help%20translate-transifex-blue?style=for-the-badge)](https://www.transifex.com/authentik/authentik/)

View File

@@ -3,7 +3,7 @@
from functools import lru_cache
from os import environ
VERSION = "2025.10.0-rc1"
VERSION = "2025.8.6"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"

View File

@@ -1,9 +0,0 @@
from django.dispatch import receiver
from authentik.admin.tasks import _set_prom_info
from authentik.root.signals import post_startup
@receiver(post_startup)
def post_startup_admin_metrics(sender, **_):
_set_prom_info()

View File

@@ -2,6 +2,7 @@
from django.core.cache import cache
from django.utils.translation import gettext_lazy as _
from django_dramatiq_postgres.middleware import CurrentTask
from dramatiq import actor
from packaging.version import parse
from requests import RequestException
@@ -12,7 +13,7 @@ from authentik.admin.apps import PROM_INFO
from authentik.events.models import Event, EventAction
from authentik.lib.config import CONFIG
from authentik.lib.utils.http import get_http_session
from authentik.tasks.middleware import CurrentTask
from authentik.tasks.models import Task
LOGGER = get_logger()
VERSION_NULL = "0.0.0"
@@ -34,7 +35,7 @@ def _set_prom_info():
@actor(description=_("Update latest version info."))
def update_latest_version():
self = CurrentTask.get_task()
self: Task = CurrentTask.get_task()
if CONFIG.get_bool("disable_update_check"):
cache.set(VERSION_CACHE_KEY, VERSION_NULL, VERSION_CACHE_TIMEOUT)
self.info("Version check disabled.")
@@ -71,3 +72,6 @@ def update_latest_version():
except (RequestException, IndexError) as exc:
cache.set(VERSION_CACHE_KEY, VERSION_NULL, VERSION_CACHE_TIMEOUT)
raise exc
_set_prom_info()

View File

@@ -1,10 +1,44 @@
"""Pagination which includes total pages and current page"""
from drf_spectacular.plumbing import build_object_type
from rest_framework import pagination
from rest_framework.response import Response
from authentik.api.v3.schema.response import PAGINATION
PAGINATION_COMPONENT_NAME = "Pagination"
PAGINATION_SCHEMA = {
"type": "object",
"properties": {
"next": {
"type": "number",
},
"previous": {
"type": "number",
},
"count": {
"type": "number",
},
"current": {
"type": "number",
},
"total_pages": {
"type": "number",
},
"start_index": {
"type": "number",
},
"end_index": {
"type": "number",
},
},
"required": [
"next",
"previous",
"count",
"current",
"total_pages",
"start_index",
"end_index",
],
}
class Pagination(pagination.PageNumberPagination):
@@ -36,13 +70,14 @@ class Pagination(pagination.PageNumberPagination):
)
def get_paginated_response_schema(self, schema):
return build_object_type(
properties={
"pagination": PAGINATION.ref,
return {
"type": "object",
"properties": {
"pagination": {"$ref": f"#/components/schemas/{PAGINATION_COMPONENT_NAME}"},
"results": schema,
},
required=["pagination", "results"],
)
"required": ["pagination", "results"],
}
class SmallerPagination(Pagination):

View File

@@ -1,60 +1,96 @@
"""Error Response schema, from https://github.com/axnsan12/drf-yasg/issues/224"""
from collections.abc import Callable
from typing import Any
from django.utils.translation import gettext_lazy as _
from drf_spectacular.generators import SchemaGenerator
from drf_spectacular.plumbing import ResolvedComponent
from drf_spectacular.renderers import OpenApiJsonRenderer
from drf_spectacular.plumbing import (
ResolvedComponent,
build_array_type,
build_basic_type,
build_object_type,
)
from drf_spectacular.settings import spectacular_settings
from structlog.stdlib import get_logger
from drf_spectacular.types import OpenApiTypes
from rest_framework.settings import api_settings
from authentik.api.apps import AuthentikAPIConfig
from authentik.api.v3.schema.query import QUERY_PARAMS
from authentik.api.v3.schema.response import (
GENERIC_ERROR,
GENERIC_ERROR_RESPONSE,
PAGINATION,
VALIDATION_ERROR,
VALIDATION_ERROR_RESPONSE,
from authentik.api.pagination import PAGINATION_COMPONENT_NAME, PAGINATION_SCHEMA
def build_standard_type(obj, **kwargs):
"""Build a basic type with optional add owns."""
schema = build_basic_type(obj)
schema.update(kwargs)
return schema
GENERIC_ERROR = build_object_type(
description=_("Generic API Error"),
properties={
"detail": build_standard_type(OpenApiTypes.STR),
"code": build_standard_type(OpenApiTypes.STR),
},
required=["detail"],
)
VALIDATION_ERROR = build_object_type(
description=_("Validation Error"),
properties={
api_settings.NON_FIELD_ERRORS_KEY: build_array_type(build_standard_type(OpenApiTypes.STR)),
"code": build_standard_type(OpenApiTypes.STR),
},
required=[],
additionalProperties={},
)
LOGGER = get_logger()
def create_component(generator: SchemaGenerator, name, schema, type_=ResolvedComponent.SCHEMA):
"""Register a component and return a reference to it."""
component = ResolvedComponent(
name=name,
type=type_,
schema=schema,
object=name,
)
generator.registry.register_on_missing(component)
return component
def preprocess_schema_exclude_non_api(endpoints: list[tuple[str, Any, Any, Callable]], **kwargs):
"""Filter out all API Views which are not mounted under /api"""
return [
(path, path_regex, method, callback)
for path, path_regex, method, callback in endpoints
if path.startswith("/" + AuthentikAPIConfig.mountpoint)
]
def postprocess_schema_responses(result, generator: SchemaGenerator, **kwargs):
"""Workaround to set a default response for endpoints.
Workaround suggested at
<https://github.com/tfranzel/drf-spectacular/issues/119#issuecomment-656970357>
for the missing drf-spectacular feature discussed in
<https://github.com/tfranzel/drf-spectacular/issues/101>.
"""
create_component(generator, PAGINATION_COMPONENT_NAME, PAGINATION_SCHEMA)
def postprocess_schema_register(
result: dict[str, Any], generator: SchemaGenerator, **kwargs
) -> dict[str, Any]:
"""Register custom schema components"""
LOGGER.debug("Registering custom schemas")
generator.registry.register_on_missing(PAGINATION)
generator.registry.register_on_missing(GENERIC_ERROR)
generator.registry.register_on_missing(GENERIC_ERROR_RESPONSE)
generator.registry.register_on_missing(VALIDATION_ERROR)
generator.registry.register_on_missing(VALIDATION_ERROR_RESPONSE)
for query in QUERY_PARAMS.values():
generator.registry.register_on_missing(query)
return result
generic_error = create_component(generator, "GenericError", GENERIC_ERROR)
validation_error = create_component(generator, "ValidationError", VALIDATION_ERROR)
def postprocess_schema_responses(
result: dict[str, Any], generator: SchemaGenerator, **kwargs
) -> dict[str, Any]:
"""Default error responses"""
LOGGER.debug("Adding default error responses")
for path in result["paths"].values():
for method in path.values():
method["responses"].setdefault("400", VALIDATION_ERROR_RESPONSE.ref)
method["responses"].setdefault("403", GENERIC_ERROR_RESPONSE.ref)
method["responses"].setdefault(
"400",
{
"content": {
"application/json": {
"schema": validation_error.ref,
}
},
"description": "",
},
)
method["responses"].setdefault(
"403",
{
"content": {
"application/json": {
"schema": generic_error.ref,
}
},
"description": "",
},
)
result["components"] = generator.registry.build(spectacular_settings.APPEND_COMPONENTS)
@@ -68,36 +104,10 @@ def postprocess_schema_responses(
return result
def postprocess_schema_query_params(
result: dict[str, Any], generator: SchemaGenerator, **kwargs
) -> dict[str, Any]:
"""Optimise pagination parameters, instead of redeclaring parameters for each endpoint
declare them globally and refer to them"""
LOGGER.debug("Deduplicating query parameters")
for path in result["paths"].values():
for method in path.values():
for idx, param in enumerate(method.get("parameters", [])):
if param["name"] not in QUERY_PARAMS:
continue
method["parameters"][idx] = QUERY_PARAMS[param["name"]].ref
return result
def postprocess_schema_remove_unused(
result: dict[str, Any], generator: SchemaGenerator, **kwargs
) -> dict[str, Any]:
"""Remove unused components"""
# To check if the schema is used, render it to JSON and then substring check that
# less efficient than walking through the tree but a lot simpler and no
# possibility that we miss something
raw = OpenApiJsonRenderer().render(result, renderer_context={}).decode()
count = 0
for key in result["components"][ResolvedComponent.SCHEMA].keys():
schema_usages = raw.count(f"#/components/{ResolvedComponent.SCHEMA}/{key}")
if schema_usages >= 1:
continue
del generator.registry[(key, ResolvedComponent.SCHEMA)]
count += 1
LOGGER.debug("Removing unused components", count=count)
result["components"] = generator.registry.build(spectacular_settings.APPEND_COMPONENTS)
return result
def preprocess_schema_exclude_non_api(endpoints, **kwargs):
"""Filter out all API Views which are not mounted under /api"""
return [
(path, path_regex, method, callback)
for path, path_regex, method, callback in endpoints
if path.startswith("/" + AuthentikAPIConfig.mountpoint)
]

View File

@@ -56,6 +56,7 @@ class ConfigSerializer(PassiveSerializer):
cache_timeout = IntegerField(required=True)
cache_timeout_flows = IntegerField(required=True)
cache_timeout_policies = IntegerField(required=True)
cache_timeout_reputation = IntegerField(required=True)
class ConfigView(APIView):
@@ -102,6 +103,7 @@ class ConfigView(APIView):
"cache_timeout": CONFIG.get_int("cache.timeout"),
"cache_timeout_flows": CONFIG.get_int("cache.timeout_flows"),
"cache_timeout_policies": CONFIG.get_int("cache.timeout_policies"),
"cache_timeout_reputation": CONFIG.get_int("cache.timeout_reputation"),
}
)

View File

@@ -1,65 +0,0 @@
from django.utils.translation import gettext_lazy as _
from drf_spectacular.plumbing import (
ResolvedComponent,
build_basic_type,
build_parameter_type,
)
from drf_spectacular.types import OpenApiTypes
QUERY_PARAMS = {
"ordering": ResolvedComponent(
name="QueryPaginationOrdering",
type=ResolvedComponent.PARAMETER,
object="QueryPaginationOrdering",
schema=build_parameter_type(
name="ordering",
schema=build_basic_type(OpenApiTypes.STR),
location="query",
description=_("Which field to use when ordering the results."),
),
),
"page": ResolvedComponent(
name="QueryPaginationPage",
type=ResolvedComponent.PARAMETER,
object="QueryPaginationPage",
schema=build_parameter_type(
name="page",
schema=build_basic_type(OpenApiTypes.INT),
location="query",
description=_("A page number within the paginated result set."),
),
),
"page_size": ResolvedComponent(
name="QueryPaginationPageSize",
type=ResolvedComponent.PARAMETER,
object="QueryPaginationPageSize",
schema=build_parameter_type(
name="page_size",
schema=build_basic_type(OpenApiTypes.INT),
location="query",
description=_("Number of results to return per page."),
),
),
"search": ResolvedComponent(
name="QuerySearch",
type=ResolvedComponent.PARAMETER,
object="QuerySearch",
schema=build_parameter_type(
name="search",
schema=build_basic_type(OpenApiTypes.STR),
location="query",
description=_("A search term."),
),
),
# Not related to pagination but a very common query param
"name": ResolvedComponent(
name="QueryName",
type=ResolvedComponent.PARAMETER,
object="QueryName",
schema=build_parameter_type(
name="name",
schema=build_basic_type(OpenApiTypes.STR),
location="query",
),
),
}

View File

@@ -1,84 +0,0 @@
from django.utils.translation import gettext_lazy as _
from drf_spectacular.plumbing import (
ResolvedComponent,
build_array_type,
build_basic_type,
build_object_type,
)
from drf_spectacular.types import OpenApiTypes
from rest_framework.settings import api_settings
GENERIC_ERROR = ResolvedComponent(
name="GenericError",
type=ResolvedComponent.SCHEMA,
object="GenericError",
schema=build_object_type(
description=_("Generic API Error"),
properties={
"detail": build_basic_type(OpenApiTypes.STR),
"code": build_basic_type(OpenApiTypes.STR),
},
required=["detail"],
),
)
GENERIC_ERROR_RESPONSE = ResolvedComponent(
name="GenericErrorResponse",
type=ResolvedComponent.RESPONSE,
object="GenericErrorResponse",
schema={
"content": {"application/json": {"schema": GENERIC_ERROR.ref}},
"description": "",
},
)
VALIDATION_ERROR = ResolvedComponent(
"ValidationError",
object="ValidationError",
type=ResolvedComponent.SCHEMA,
schema=build_object_type(
description=_("Validation Error"),
properties={
api_settings.NON_FIELD_ERRORS_KEY: build_array_type(build_basic_type(OpenApiTypes.STR)),
"code": build_basic_type(OpenApiTypes.STR),
},
required=[],
additionalProperties={},
),
)
VALIDATION_ERROR_RESPONSE = ResolvedComponent(
name="ValidationErrorResponse",
type=ResolvedComponent.RESPONSE,
object="ValidationErrorResponse",
schema={
"content": {
"application/json": {
"schema": VALIDATION_ERROR.ref,
}
},
"description": "",
},
)
PAGINATION = ResolvedComponent(
name="Pagination",
type=ResolvedComponent.SCHEMA,
object="Pagination",
schema=build_object_type(
properties={
"next": build_basic_type(OpenApiTypes.NUMBER),
"previous": build_basic_type(OpenApiTypes.NUMBER),
"count": build_basic_type(OpenApiTypes.NUMBER),
"current": build_basic_type(OpenApiTypes.NUMBER),
"total_pages": build_basic_type(OpenApiTypes.NUMBER),
"start_index": build_basic_type(OpenApiTypes.NUMBER),
"end_index": build_basic_type(OpenApiTypes.NUMBER),
},
required=[
"next",
"previous",
"count",
"current",
"total_pages",
"start_index",
"end_index",
],
),
)

View File

@@ -15,7 +15,6 @@ from django.db.models import Model
from django.db.models.query_utils import Q
from django.db.transaction import atomic
from django.db.utils import IntegrityError
from django_channels_postgres.models import GroupChannel, Message
from guardian.models import UserObjectPermission
from guardian.shortcuts import assign_perm
from rest_framework.exceptions import ValidationError
@@ -72,15 +71,12 @@ from authentik.providers.oauth2.models import (
DeviceToken,
RefreshToken,
)
from authentik.providers.proxy.models import ProxySession
from authentik.providers.rac.models import ConnectionToken
from authentik.providers.saml.models import SAMLSession
from authentik.providers.scim.models import SCIMProviderGroup, SCIMProviderUser
from authentik.rbac.models import Role
from authentik.sources.scim.models import SCIMSourceGroup, SCIMSourceUser
from authentik.stages.authenticator_webauthn.models import WebAuthnDeviceType
from authentik.stages.consent.models import UserConsent
from authentik.tasks.models import Task, TaskLog
from authentik.tasks.models import Task
from authentik.tenants.models import Tenant
# Context set when the serializer is created in a blueprint context
@@ -123,12 +119,10 @@ def excluded_models() -> list[type[Model]]:
SCIMProviderUser,
Tenant,
Task,
TaskLog,
ConnectionToken,
AuthorizationCode,
AccessToken,
RefreshToken,
ProxySession,
Reputation,
WebAuthnDeviceType,
SCIMSourceUser,
@@ -141,10 +135,6 @@ def excluded_models() -> list[type[Model]]:
EndpointDeviceConnection,
DeviceToken,
StreamEvent,
UserConsent,
SAMLSession,
Message,
GroupChannel,
)

View File

@@ -12,7 +12,7 @@ from django.db import DatabaseError, InternalError, ProgrammingError
from django.utils.text import slugify
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
from django_dramatiq_postgres.middleware import CurrentTaskNotFound
from django_dramatiq_postgres.middleware import CurrentTask, CurrentTaskNotFound
from dramatiq.actor import actor
from dramatiq.middleware import Middleware
from structlog.stdlib import get_logger
@@ -39,7 +39,6 @@ from authentik.events.logs import capture_logs
from authentik.events.utils import sanitize_dict
from authentik.lib.config import CONFIG
from authentik.tasks.apps import PRIORITY_HIGH
from authentik.tasks.middleware import CurrentTask
from authentik.tasks.models import Task
from authentik.tasks.schedules.models import Schedule
from authentik.tenants.models import Tenant
@@ -152,7 +151,7 @@ def blueprints_find() -> list[BlueprintFile]:
@actor(description=_("Find blueprints and check if they need to be created in the database."))
def blueprints_discovery(path: str | None = None):
self = CurrentTask.get_task()
self: Task = CurrentTask.get_task()
count = 0
for blueprint in blueprints_find():
if path and blueprint.path != path:
@@ -192,7 +191,7 @@ def check_blueprint_v1_file(blueprint: BlueprintFile):
@actor(description=_("Apply single blueprint."))
def apply_blueprint(instance_pk: UUID):
try:
self = CurrentTask.get_task()
self: Task = CurrentTask.get_task()
except CurrentTaskNotFound:
self = Task()
self.set_uid(str(instance_pk))

View File

@@ -113,7 +113,7 @@ class Brand(SerializerModel):
try:
return self.attributes.get("settings", {}).get("locale", "")
except Exception as exc: # noqa
except Exception as exc:
LOGGER.warning("Failed to get default locale", exc=exc)
return ""

View File

@@ -1,8 +0,0 @@
from authentik.blueprints.apps import ManagedAppConfig
class AuthentikCommandsConfig(ManagedAppConfig):
name = "authentik.commands"
label = "authentik_commands"
verbose_name = "authentik Commands"
default = True

View File

@@ -1,8 +0,0 @@
from django.db.migrations.autodetector import MigrationAutodetector as BaseMigrationAutodetector
from pgtrigger.migrations import MigrationAutodetectorMixin
MigrationAutodetector = type(
"MigrationAutodetector",
(MigrationAutodetectorMixin, BaseMigrationAutodetector),
{},
)

View File

@@ -1,7 +0,0 @@
from django.core.management.commands.makemigrations import Command as BaseCommand
from authentik.commands.management.commands import MigrationAutodetector
class Command(BaseCommand):
autodetector = MigrationAutodetector

View File

@@ -1,7 +0,0 @@
from django_tenants.management.commands.migrate import Command as BaseCommand
from authentik.commands.management.commands import MigrationAutodetector
class Command(BaseCommand):
autodetector = MigrationAutodetector # type: ignore[assignment]

View File

@@ -1,7 +0,0 @@
from django_tenants.management.commands.migrate_schemas import Command as BaseCommand
from authentik.commands.management.commands import MigrationAutodetector
class Command(BaseCommand):
autodetector = MigrationAutodetector # type: ignore[assignment]

View File

@@ -29,8 +29,8 @@ from authentik.rbac.api.roles import RoleSerializer
from authentik.rbac.decorators import permission_required
class PartialUserSerializer(ModelSerializer):
"""Partial User Serializer, does not include child relations."""
class GroupMemberSerializer(ModelSerializer):
"""Stripped down user serializer to show relevant users for groups"""
attributes = JSONDictField(required=False)
uid = CharField(read_only=True)
@@ -94,11 +94,11 @@ class GroupSerializer(ModelSerializer):
return True
return str(request.query_params.get("include_children", "false")).lower() == "true"
@extend_schema_field(PartialUserSerializer(many=True))
def get_users_obj(self, instance: Group) -> list[PartialUserSerializer] | None:
@extend_schema_field(GroupMemberSerializer(many=True))
def get_users_obj(self, instance: Group) -> list[GroupMemberSerializer] | None:
if not self._should_include_users:
return None
return PartialUserSerializer(instance.users, many=True).data
return GroupMemberSerializer(instance.users, many=True).data
@extend_schema_field(GroupChildSerializer(many=True))
def get_children_obj(self, instance: Group) -> list[GroupChildSerializer] | None:
@@ -228,19 +228,6 @@ class GroupViewSet(UsedByMixin, ModelViewSet):
filterset_class = GroupFilter
ordering = ["name"]
def get_ql_fields(self):
from djangoql.schema import BoolField, StrField
from authentik.enterprise.search.fields import (
JSONSearchField,
)
return [
StrField(Group, "name"),
BoolField(Group, "is_superuser", nullable=True),
JSONSearchField(Group, "attributes", suggest_nested=False),
]
def get_queryset(self):
base_qs = Group.objects.all().select_related("parent").prefetch_related("roles")
@@ -308,7 +295,7 @@ class GroupViewSet(UsedByMixin, ModelViewSet):
@extend_schema(
request=UserAccountSerializer,
responses={
204: OpenApiResponse(description="User removed"),
204: OpenApiResponse(description="User added"),
404: OpenApiResponse(description="User not found"),
},
)
@@ -320,7 +307,7 @@ class GroupViewSet(UsedByMixin, ModelViewSet):
permission_classes=[],
)
def remove_user(self, request: Request, pk: str) -> Response:
"""Remove user from group"""
"""Add user to group"""
group: Group = self.get_object()
user: User = (
get_objects_for_user(request.user, "authentik_core.view_user")

View File

@@ -171,7 +171,7 @@ class PropertyMappingViewSet(
except PropertyMappingExpressionException as exc:
response_data["result"] = exception_to_string(exc.exc)
response_data["successful"] = False
except Exception as exc: # noqa
except Exception as exc:
response_data["result"] = exception_to_string(exc)
response_data["successful"] = False
response = PropertyMappingTestResultSerializer(response_data)

View File

@@ -18,10 +18,14 @@ from authentik.core.models import Provider
class ProviderSerializer(ModelSerializer, MetaNameSerializer):
"""Provider Serializer"""
assigned_application_slug = ReadOnlyField(source="application.slug")
assigned_application_name = ReadOnlyField(source="application.name")
assigned_backchannel_application_slug = ReadOnlyField(source="backchannel_application.slug")
assigned_backchannel_application_name = ReadOnlyField(source="backchannel_application.name")
assigned_application_slug = ReadOnlyField(source="application.slug", allow_null=True)
assigned_application_name = ReadOnlyField(source="application.name", allow_null=True)
assigned_backchannel_application_slug = ReadOnlyField(
source="backchannel_application.slug", allow_null=True
)
assigned_backchannel_application_name = ReadOnlyField(
source="backchannel_application.name", allow_null=True
)
component = SerializerMethodField()

View File

@@ -97,8 +97,8 @@ class ParamUserSerializer(PassiveSerializer):
user = PrimaryKeyRelatedField(queryset=User.objects.all().exclude_anonymous(), required=False)
class PartialGroupSerializer(ModelSerializer):
"""Partial Group Serializer, does not include child relations."""
class UserGroupSerializer(ModelSerializer):
"""Simplified Group Serializer for user's groups"""
attributes = JSONDictField(required=False)
parent_name = CharField(source="parent.name", read_only=True, allow_null=True)
@@ -143,11 +143,11 @@ class UserSerializer(ModelSerializer):
return True
return str(request.query_params.get("include_groups", "true")).lower() == "true"
@extend_schema_field(PartialGroupSerializer(many=True))
def get_groups_obj(self, instance: User) -> list[PartialGroupSerializer] | None:
@extend_schema_field(UserGroupSerializer(many=True))
def get_groups_obj(self, instance: User) -> list[UserGroupSerializer] | None:
if not self._should_include_groups:
return None
return PartialGroupSerializer(instance.ak_groups, many=True).data
return UserGroupSerializer(instance.ak_groups, many=True).data
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -334,21 +334,6 @@ class UserPasswordSetSerializer(PassiveSerializer):
password = CharField(required=True)
class UserServiceAccountSerializer(PassiveSerializer):
"""Payload to create a service account"""
name = CharField(
required=True,
validators=[UniqueValidator(queryset=User.objects.all().order_by("username"))],
)
create_group = BooleanField(default=False)
expiring = BooleanField(default=True)
expires = DateTimeField(
required=False,
help_text="If not provided, valid for 360 days",
)
class UsersFilter(FilterSet):
"""Filter for users"""
@@ -509,7 +494,18 @@ class UserViewSet(UsedByMixin, ModelViewSet):
@permission_required(None, ["authentik_core.add_user", "authentik_core.add_token"])
@extend_schema(
request=UserServiceAccountSerializer,
request=inline_serializer(
"UserServiceAccountSerializer",
{
"name": CharField(required=True),
"create_group": BooleanField(default=False),
"expiring": BooleanField(default=True),
"expires": DateTimeField(
required=False,
help_text="If not provided, valid for 360 days",
),
},
),
responses={
200: inline_serializer(
"UserServiceAccountResponse",
@@ -531,12 +527,11 @@ class UserViewSet(UsedByMixin, ModelViewSet):
)
def service_account(self, request: Request) -> Response:
"""Create a new user account that is marked as a service account"""
data = UserServiceAccountSerializer(data=request.data)
data.is_valid(raise_exception=True)
expires = data.validated_data.get("expires", now() + timedelta(days=360))
username = request.data.get("name")
create_group = request.data.get("create_group", False)
expiring = request.data.get("expiring", True)
expires = request.data.get("expires", now() + timedelta(days=360))
username = data.validated_data["name"]
expiring = data.validated_data["expiring"]
with atomic():
try:
user: User = User.objects.create(
@@ -554,10 +549,10 @@ class UserViewSet(UsedByMixin, ModelViewSet):
"user_uid": user.uid,
"user_pk": user.pk,
}
if data.validated_data["create_group"] and self.request.user.has_perm(
"authentik_core.add_group"
):
group = Group.objects.create(name=username)
if create_group and self.request.user.has_perm("authentik_core.add_group"):
group = Group.objects.create(
name=username,
)
group.users.add(user)
response["group_pk"] = str(group.pk)
token = Token.objects.create(
@@ -570,29 +565,7 @@ class UserViewSet(UsedByMixin, ModelViewSet):
response["token"] = token.key
return Response(response)
except IntegrityError as exc:
error_msg = str(exc).lower()
if "unique" in error_msg:
return Response(
data={
"non_field_errors": [
_("A user/group with these details already exists")
]
},
status=400,
)
else:
LOGGER.warning("Service account creation failed", exc=exc)
return Response(
data={"non_field_errors": [_("Unable to create user")]},
status=400,
)
except (ValueError, TypeError) as exc:
LOGGER.error("Unexpected error during service account creation", exc=exc)
return Response(
data={"non_field_errors": [_("Unknown error occurred")]},
status=500,
)
return Response(data={"non_field_errors": [str(exc)]}, status=400)
@extend_schema(responses={200: SessionUserSerializer(many=False)})
@action(
@@ -708,7 +681,8 @@ class UserViewSet(UsedByMixin, ModelViewSet):
},
),
responses={
204: OpenApiResponse(description="Successfully started impersonation"),
"204": OpenApiResponse(description="Successfully started impersonation"),
"401": OpenApiResponse(description="Access denied"),
},
)
@action(detail=True, methods=["POST"], permission_classes=[])
@@ -727,7 +701,7 @@ class UserViewSet(UsedByMixin, ModelViewSet):
"User attempted to impersonate without permissions",
user=request.user,
)
return Response(status=403)
return Response(status=401)
if user_to_be.pk == self.request.user.pk:
LOGGER.debug("User attempted to impersonate themselves", user=request.user)
return Response(status=401)
@@ -736,19 +710,19 @@ class UserViewSet(UsedByMixin, ModelViewSet):
"User attempted to impersonate without providing a reason",
user=request.user,
)
raise ValidationError({"reason": _("This field is required.")})
return Response(status=401)
request.session[SESSION_KEY_IMPERSONATE_ORIGINAL_USER] = request.user
request.session[SESSION_KEY_IMPERSONATE_USER] = user_to_be
Event.new(EventAction.IMPERSONATION_STARTED, reason=reason).from_http(request, user_to_be)
return Response(status=204)
return Response(status=201)
@extend_schema(
request=None,
request=OpenApiTypes.NONE,
responses={
"204": OpenApiResponse(description="Successfully ended impersonation"),
"204": OpenApiResponse(description="Successfully started impersonation"),
},
)
@action(detail=False, methods=["GET"])

View File

@@ -21,6 +21,8 @@ from rest_framework.serializers import (
raise_errors_on_nested_writes,
)
from authentik.rbac.permissions import assign_initial_permissions
def is_dict(value: Any):
"""Ensure a value is a dictionary, useful for JSONFields"""
@@ -50,6 +52,15 @@ class ModelSerializer(BaseModelSerializer):
serializer_field_mapping = BaseModelSerializer.serializer_field_mapping.copy()
serializer_field_mapping[models.JSONField] = JSONDictField
def create(self, validated_data):
instance = super().create(validated_data)
request = self.context.get("request")
if request and hasattr(request, "user") and not request.user.is_anonymous:
assign_initial_permissions(request.user, instance)
return instance
def update(self, instance: Model, validated_data):
raise_errors_on_nested_writes("update", self, validated_data)
info = model_meta.get_field_info(instance)

View File

@@ -1,6 +1,6 @@
"""custom runserver command"""
from io import StringIO
from typing import TextIO
from daphne.management.commands.runserver import Command as RunServer
from daphne.server import Server
@@ -33,4 +33,4 @@ class Command(RunServer):
super().__init__(*args, **kwargs)
# Redirect standard stdout banner from Daphne into the void
# as there are a couple more steps that happen before startup is fully done
self.stdout = StringIO()
self.stdout = TextIO()

View File

@@ -1,9 +1,13 @@
"""authentik shell command"""
import code
import platform
import sys
import traceback
from pprint import pprint
from django.core.management.commands.shell import Command as BaseCommand
from django.apps import apps
from django.core.management.base import BaseCommand
from django.db.models import Model
from django.db.models.signals import post_save, pre_delete
@@ -22,12 +26,29 @@ def get_banner_text(shell_type="shell") -> str:
class Command(BaseCommand):
"""Start the Django shell with all authentik models already imported"""
def get_namespace(self, **options):
return {
**super().get_namespace(**options),
django_models = {}
def add_arguments(self, parser):
parser.add_argument(
"-c",
"--command",
help="Python code to execute (instead of starting an interactive shell)",
)
def get_namespace(self):
"""Prepare namespace with all models"""
namespace = {
"pprint": pprint,
}
# Gather Django models and constants from each app
for app in apps.get_app_configs():
# Load models from each app
for model in app.get_models():
namespace[model.__name__] = model
return namespace
@staticmethod
def post_save_handler(sender, instance: Model, created: bool, **_):
"""Signal handler for all object's post_save"""
@@ -58,9 +79,41 @@ class Command(BaseCommand):
).save()
def handle(self, **options):
namespace = self.get_namespace()
post_save.connect(Command.post_save_handler)
pre_delete.connect(Command.pre_delete_handler)
print(get_banner_text())
# If Python code has been passed, execute it and exit.
if options["command"]:
super().handle(**options)
exec(options["command"], namespace) # nosec # noqa
return
try:
hook = sys.__interactivehook__
except AttributeError:
# Match the behavior of the cpython shell where a missing
# sys.__interactivehook__ is ignored.
pass
else:
try:
hook()
except Exception:
# Match the behavior of the cpython shell where an error in
# sys.__interactivehook__ prints a warning and the exception
# and continues.
print("Failed calling sys.__interactivehook__")
traceback.print_exc()
# Try to enable tab-complete
try:
import readline
import rlcompleter
except ModuleNotFoundError:
pass
else:
readline.set_completer(rlcompleter.Completer(namespace).complete)
readline.parse_and_bind("tab: complete")
# Run interactive shell
code.interact(banner=get_banner_text(), local=namespace)

View File

@@ -13,6 +13,14 @@ import authentik.core.models
import authentik.lib.models
def migrate_sessions(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
from django.contrib.sessions.backends.cache import KEY_PREFIX
from django.core.cache import cache
session_keys = cache.keys(KEY_PREFIX + "*")
cache.delete_many(session_keys)
def fix_duplicates(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
db_alias = schema_editor.connection.alias
Token = apps.get_model("authentik_core", "token")
@@ -143,6 +151,9 @@ class Migration(migrations.Migration):
"abstract": False,
},
),
migrations.RunPython(
code=migrate_sessions,
),
migrations.AlterField(
model_name="application",
name="meta_launch_url",

View File

@@ -7,10 +7,15 @@ from django.contrib.auth import BACKEND_SESSION_KEY, HASH_SESSION_KEY, SESSION_K
from django.db import migrations, models
import django.db.models.deletion
from django.conf import settings
from django.contrib.sessions.backends.cache import KEY_PREFIX
from django.utils.timezone import now, timedelta
from authentik.lib.migrations import progress_bar
from authentik.root.middleware import ClientIPMiddleware
SESSION_CACHE_ALIAS = "default"
class PickleSerializer:
"""
Simple wrapper around pickle to be used in signing.dumps()/loads() and
@@ -78,6 +83,27 @@ def _migrate_session(
)
def migrate_redis_sessions(apps, schema_editor):
from django.core.cache import caches
db_alias = schema_editor.connection.alias
cache = caches[SESSION_CACHE_ALIAS]
# Not a redis cache, skipping
if not hasattr(cache, "keys"):
return
print("\nMigrating Redis sessions to database, this might take a couple of minutes...")
for key, session_data in progress_bar(cache.get_many(cache.keys(f"{KEY_PREFIX}*")).items()):
_migrate_session(
apps=apps,
db_alias=db_alias,
session_key=key.removeprefix(KEY_PREFIX),
session_data=session_data,
expires=now() + timedelta(seconds=cache.ttl(key)),
)
def migrate_database_sessions(apps, schema_editor):
DjangoSession = apps.get_model("sessions", "Session")
db_alias = schema_editor.connection.alias
@@ -205,6 +231,10 @@ class Migration(migrations.Migration):
"verbose_name_plural": "Authenticated Sessions",
},
),
migrations.RunPython(
code=migrate_redis_sessions,
reverse_code=migrations.RunPython.noop,
),
migrations.RunPython(
code=migrate_database_sessions,
reverse_code=migrations.RunPython.noop,

View File

@@ -115,21 +115,15 @@ class AttributesMixin(models.Model):
def update_attributes(self, properties: dict[str, Any]):
"""Update fields and attributes, but correctly by merging dicts"""
needs_update = False
for key, value in properties.items():
if key == "attributes":
continue
if getattr(self, key, None) != value:
setattr(self, key, value)
needs_update = True
setattr(self, key, value)
final_attributes = {}
MERGE_LIST_UNIQUE.merge(final_attributes, self.attributes)
MERGE_LIST_UNIQUE.merge(final_attributes, properties.get("attributes", {}))
if self.attributes != final_attributes:
self.attributes = final_attributes
needs_update = True
if needs_update:
self.save()
self.attributes = final_attributes
self.save()
@classmethod
def update_or_create_attributes(
@@ -407,12 +401,10 @@ class User(SerializerModel, GuardianUserMixin, AttributesMixin, AbstractUser):
def locale(self, request: HttpRequest | None = None) -> str:
"""Get the locale the user has configured"""
if request and hasattr(request, "LANGUAGE_CODE"):
return request.LANGUAGE_CODE
try:
return self.attributes.get("settings", {}).get("locale", "")
except Exception as exc: # noqa
except Exception as exc:
LOGGER.warning("Failed to get default locale", exc=exc)
if request:
return request.brand.locale
@@ -577,8 +569,6 @@ class Application(SerializerModel, PolicyBindingModel):
return None
if self.meta_icon.name.startswith("http"):
return self.meta_icon.name
if self.meta_icon.name.startswith("fa://"):
return self.meta_icon.name
if self.meta_icon.name.startswith("/"):
return CONFIG.get("web.path", "/")[:-1] + self.meta_icon.name
return self.meta_icon.url
@@ -597,7 +587,7 @@ class Application(SerializerModel, PolicyBindingModel):
try:
return url % user.__dict__
except Exception as exc: # noqa
except Exception as exc:
LOGGER.warning("Failed to format launch url", exc=exc)
return url
return url
@@ -784,8 +774,6 @@ class Source(ManagedModel, SerializerModel, PolicyBindingModel):
return None
if self.icon.name.startswith("http"):
return self.icon.name
if self.icon.name.startswith("fa://"):
return self.icon.name
if self.icon.name.startswith("/"):
return CONFIG.get("web.path", "/")[:-1] + self.icon.name
return self.icon.url
@@ -797,7 +785,7 @@ class Source(ManagedModel, SerializerModel, PolicyBindingModel):
"slug": self.slug,
}
except Exception as exc: # noqa
except Exception as exc:
LOGGER.warning("Failed to template user path", exc=exc, source=self)
return User.default_path()

View File

@@ -2,9 +2,10 @@
from django.contrib.auth.signals import user_logged_in
from django.core.cache import cache
from django.core.signals import Signal
from django.db.models import Model
from django.db.models.signals import post_delete, post_save, pre_save
from django.dispatch import Signal, receiver
from django.dispatch import receiver
from django.http.request import HttpRequest
from structlog.stdlib import get_logger

View File

@@ -4,8 +4,7 @@ from datetime import datetime, timedelta
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
from django_channels_postgres.models import GroupChannel, Message
from django_postgres_cache.tasks import clear_expired_cache
from django_dramatiq_postgres.middleware import CurrentTask
from dramatiq.actor import actor
from structlog.stdlib import get_logger
@@ -15,33 +14,29 @@ from authentik.core.models import (
ExpiringModel,
User,
)
from authentik.lib.utils.db import chunked_queryset
from authentik.tasks.middleware import CurrentTask
from authentik.tasks.models import Task
LOGGER = get_logger()
@actor(description=_("Remove expired objects."))
def clean_expired_models():
self = CurrentTask.get_task()
self: Task = CurrentTask.get_task()
for cls in ExpiringModel.__subclasses__():
cls: ExpiringModel
objects = (
cls.objects.all().exclude(expiring=False).exclude(expiring=True, expires__gt=now())
)
amount = objects.count()
for obj in chunked_queryset(objects):
for obj in objects:
obj.expire_action()
LOGGER.debug("Expired models", model=cls, amount=amount)
self.info(f"Expired {amount} {cls._meta.verbose_name_plural}")
clear_expired_cache()
Message.delete_expired()
GroupChannel.delete_expired()
@actor(description=_("Remove temporary users created by SAML Sources."))
def clean_temporary_users():
self = CurrentTask.get_task()
self: Task = CurrentTask.get_task()
_now = datetime.now()
deleted_users = 0
for user in User.objects.filter(**{f"attributes__{USER_ATTRIBUTE_GENERATED}": True}):

View File

@@ -18,6 +18,8 @@
{% include "base/theme.html" %}
<link rel="stylesheet" type="text/css" href="{% static 'dist/authentik.css' %}">
<style>{{ brand_css }}</style>
<script src="{% versioned_script 'dist/poly-%v.js' %}" type="module"></script>
<script src="{% versioned_script 'dist/standalone/loading/index-%v.js' %}" type="module"></script>

View File

@@ -1,13 +1,6 @@
{% load static %}
<link rel="stylesheet" type="text/css" href="{% static 'dist/layers/global.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'dist/authentik.css' %}">
{% if ui_theme == "dark" %}
<meta name="color-scheme" content="dark" />
<meta name="theme-color" content="#18191a">
<link rel="stylesheet" type="text/css" href="{% static 'dist/layers/global.dark.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'dist/theme-dark.css' %}">
{% elif ui_theme == "light" %}
<meta name="color-scheme" content="light" />
<meta name="theme-color" content="#ffffff">
@@ -15,7 +8,4 @@
<meta name="color-scheme" content="light dark" />
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#18191a" media="(prefers-color-scheme: dark)">
<link rel="stylesheet" type="text/css" href="{% static 'dist/layers/global.dark.css' %}" media="(prefers-color-scheme: dark)">
<link rel="stylesheet" type="text/css" href="{% static 'dist/theme-dark.css' %}" media="(prefers-color-scheme: dark)">
{% endif %}

View File

@@ -8,7 +8,6 @@
{% endblock %}
{% block body %}
<ak-skip-to-content></ak-skip-to-content>
<ak-message-container alignment="bottom"></ak-message-container>
<ak-interface-admin>
<ak-loading></ak-loading>

View File

@@ -8,7 +8,6 @@
{% endblock %}
{% block body %}
<ak-skip-to-content></ak-skip-to-content>
<ak-message-container></ak-message-container>
<ak-interface-user>
<ak-loading></ak-loading>

View File

@@ -6,16 +6,20 @@
{% block head_before %}
<link rel="prefetch" href="{{ request.brand.branding_default_flow_background_url }}" />
<link rel="stylesheet" type="text/css" href="{% static 'dist/patternfly.min.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'dist/theme-dark.css' %}" media="(prefers-color-scheme: dark)">
{% include "base/header_js.html" %}
{% endblock %}
{% block head %}
<style>
:root {
--ak-global--background-color: var(--pf-global--BackgroundColor--150);
--ak-global--background-image: url("{{ request.brand.branding_default_flow_background_url }}");
}
:root {
--ak-flow-background: url("{{ request.brand.branding_default_flow_background_url }}");
--pf-c-background-image--BackgroundImage: var(--ak-flow-background);
--pf-c-background-image--BackgroundImage-2x: var(--ak-flow-background);
--pf-c-background-image--BackgroundImage--sm: var(--ak-flow-background);
--pf-c-background-image--BackgroundImage--sm-2x: var(--ak-flow-background);
--pf-c-background-image--BackgroundImage--lg: var(--ak-flow-background);
}
/* Form with user */
.form-control-static {
margin-top: var(--pf-global--spacer--sm);
@@ -39,37 +43,40 @@
{% endblock %}
{% block body %}
<ak-skip-to-content></ak-skip-to-content>
<div class="pf-c-background-image">
</div>
<ak-message-container></ak-message-container>
<div class="pf-c-login">
<main class="pf-c-login__main">
<div part="branding" class="pf-c-login__main-header pf-c-brand">
<img part="branding-logo" src="{{ brand.branding_logo_url }}" alt="authentik Logo" />
</div>
<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">
{% block card_title %}
<div class="pf-c-login stacked">
<div class="ak-login-container">
<main class="pf-c-login__main">
<div class="pf-c-login__main-header pf-c-brand ak-brand">
<img src="{{ brand.branding_logo_url }}" alt="authentik Logo" />
</div>
<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">
{% block card_title %}
{% endblock %}
</h1>
</header>
<div class="pf-c-login__main-body">
{% block card %}
{% endblock %}
</h1>
</header>
<div class="pf-c-login__main-body">
{% block card %}
{% endblock %}
</div>
</main>
<footer class="pf-c-login__footer pf-m-dark">
<ul class="pf-c-list pf-m-inline">
{% for link in footer_links %}
<li>
<a href="{{ link.href }}">{{ link.name }}</a>
</li>
{% endfor %}
<li>
<span>
{% trans 'Powered by authentik' %}
</span>
</li>
</ul>
</footer>
</div>
</main>
<footer class="pf-c-login__footer">
<ul class="pf-c-list pf-m-inline">
{% for link in footer_links %}
<li>
<a href="{{ link.href }}">{{ link.name }}</a>
</li>
{% endfor %}
<li>
<span>
{% trans 'Powered by authentik' %}
</span>
</li>
</ul>
</footer>
</div>
</div>
{% endblock %}

View File

@@ -127,21 +127,6 @@ class TestApplicationsAPI(APITestCase):
self.allowed.refresh_from_db()
self.assertEqual(self.allowed.get_meta_icon, "https://authentik.company/img.png")
def test_set_icon_fa(self):
"""Test set_icon (url)"""
self.client.force_login(self.user)
response = self.client.post(
reverse(
"authentik_api:application-set-icon-url",
kwargs={"slug": self.allowed.slug},
),
data={"url": "fa://fa-check-circle"},
)
self.assertEqual(response.status_code, 200)
self.allowed.refresh_from_db()
self.assertEqual(self.allowed.get_meta_icon, "fa://fa-check-circle")
def test_check_access(self):
"""Test check_access operation"""
self.client.force_login(self.user)
@@ -194,6 +179,8 @@ class TestApplicationsAPI(APITestCase):
"provider_obj": {
"assigned_application_name": "allowed",
"assigned_application_slug": "allowed",
"assigned_backchannel_application_name": None,
"assigned_backchannel_application_slug": None,
"authentication_flow": None,
"invalidation_flow": None,
"authorization_flow": str(self.provider.authorization_flow.pk),
@@ -248,6 +235,8 @@ class TestApplicationsAPI(APITestCase):
"provider_obj": {
"assigned_application_name": "allowed",
"assigned_application_slug": "allowed",
"assigned_backchannel_application_name": None,
"assigned_backchannel_application_slug": None,
"authentication_flow": None,
"invalidation_flow": None,
"authorization_flow": str(self.provider.authorization_flow.pk),

View File

@@ -59,7 +59,7 @@ class TestImpersonation(APITestCase):
),
data={"reason": "some reason"},
)
self.assertEqual(response.status_code, 204)
self.assertEqual(response.status_code, 201)
response = self.client.get(reverse("authentik_api:user-me"))
response_body = loads(response.content.decode())
@@ -80,7 +80,7 @@ class TestImpersonation(APITestCase):
),
data={"reason": "some reason"},
)
self.assertEqual(response.status_code, 204)
self.assertEqual(response.status_code, 201)
response = self.client.get(reverse("authentik_api:user-me"))
response_body = loads(response.content.decode())
@@ -137,10 +137,10 @@ class TestImpersonation(APITestCase):
self.client.force_login(self.user)
response = self.client.post(
reverse("authentik_api:user-impersonate", kwargs={"pk": self.other_user.pk}),
reverse("authentik_api:user-impersonate", kwargs={"pk": self.user.pk}),
data={"reason": ""},
)
self.assertEqual(response.status_code, 400)
self.assertEqual(response.status_code, 401)
response = self.client.get(reverse("authentik_api:user-me"))
response_body = loads(response.content.decode())

View File

@@ -469,274 +469,3 @@ class TestUsersAPI(APITestCase):
body = loads(response.content)
self.assertEqual(len(body["results"]), 2)
self.assertEqual(body["results"][0]["pk"], user.pk)
def test_service_account_validation_empty_username(self):
"""Test service account creation with empty/blank username validation"""
self.client.force_login(self.admin)
# Test with empty string
response = self.client.post(
reverse("authentik_api:user-service-account"),
data={
"name": "",
"create_group": True,
},
)
self.assertEqual(response.status_code, 400)
self.assertJSONEqual(
response.content,
{"name": ["This field may not be blank."]},
)
# Test with only whitespace
response = self.client.post(
reverse("authentik_api:user-service-account"),
data={
"name": " ",
"create_group": True,
},
)
self.assertEqual(response.status_code, 400)
self.assertJSONEqual(
response.content,
{"name": ["This field may not be blank."]},
)
# Test with tab and newline characters
response = self.client.post(
reverse("authentik_api:user-service-account"),
data={
"name": "\t\n",
"create_group": True,
},
)
self.assertEqual(response.status_code, 400)
self.assertJSONEqual(
response.content,
{"name": ["This field may not be blank."]},
)
def test_service_account_validation_valid_username(self):
"""Test service account creation with valid username"""
self.client.force_login(self.admin)
# Test with valid username
response = self.client.post(
reverse("authentik_api:user-service-account"),
data={
"name": "valid-service-account",
"create_group": True,
},
)
self.assertEqual(response.status_code, 200)
# Verify response structure
body = loads(response.content)
self.assertIn("username", body)
self.assertIn("user_uid", body)
self.assertIn("user_pk", body)
self.assertIn("group_pk", body) # Should exist since create_group=True
self.assertIn("token", body)
# Verify field types
self.assertEqual(body["username"], "valid-service-account")
self.assertIsInstance(body["user_pk"], int)
self.assertIsInstance(body["user_uid"], str)
self.assertIsInstance(body["token"], str)
self.assertIsInstance(body["group_pk"], str)
def test_service_account_validation_without_group(self):
"""Test service account creation without creating a group"""
self.client.force_login(self.admin)
response = self.client.post(
reverse("authentik_api:user-service-account"),
data={
"name": "no-group-service-account",
"create_group": False,
},
)
self.assertEqual(response.status_code, 200)
body = loads(response.content)
self.assertIn("username", body)
self.assertIn("user_uid", body)
self.assertIn("user_pk", body)
self.assertIn("token", body)
# Should NOT have group_pk when create_group=False
self.assertNotIn("group_pk", body)
def test_service_account_validation_duplicate_username(self):
"""Test service account creation with duplicate username"""
self.client.force_login(self.admin)
# Create first service account
response = self.client.post(
reverse("authentik_api:user-service-account"),
data={
"name": "duplicate-test",
"create_group": True,
},
)
self.assertEqual(response.status_code, 200)
# Attempt to create second with same username
response = self.client.post(
reverse("authentik_api:user-service-account"),
data={
"name": "duplicate-test",
"create_group": True,
},
)
self.assertEqual(response.status_code, 400)
self.assertJSONEqual(
response.content,
{"name": ["This field must be unique."]},
)
def test_service_account_validation_invalid_create_group(self):
"""Test service account creation with invalid create_group field"""
self.client.force_login(self.admin)
# Test with string instead of boolean
response = self.client.post(
reverse("authentik_api:user-service-account"),
data={
"name": "test-sa",
"create_group": "invalid",
},
)
self.assertEqual(response.status_code, 400)
self.assertJSONEqual(
response.content,
{"create_group": ["Must be a valid boolean."]},
)
# Test with number instead of boolean
response = self.client.post(
reverse("authentik_api:user-service-account"),
data={
"name": "test-sa",
"create_group": 123,
},
)
self.assertEqual(response.status_code, 400)
self.assertJSONEqual(
response.content,
{"create_group": ["Must be a valid boolean."]},
)
def test_service_account_validation_invalid_expiring(self):
"""Test service account creation with invalid expiring field"""
self.client.force_login(self.admin)
# Test with string instead of boolean
response = self.client.post(
reverse("authentik_api:user-service-account"),
data={
"name": "test-sa",
"expiring": "invalid",
},
)
self.assertEqual(response.status_code, 400)
self.assertJSONEqual(
response.content,
{"expiring": ["Must be a valid boolean."]},
)
def test_service_account_validation_invalid_expires(self):
"""Test service account creation with invalid expires field"""
self.client.force_login(self.admin)
# Test with invalid datetime string
response = self.client.post(
reverse("authentik_api:user-service-account"),
data={
"name": "test-sa",
"expires": "invalid-datetime",
},
)
self.assertEqual(response.status_code, 400)
self.assertJSONEqual(
response.content,
{
"expires": [
"Datetime has wrong format. Use one of these formats instead: "
"YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z]."
]
},
)
# Test with invalid format
response = self.client.post(
reverse("authentik_api:user-service-account"),
data={
"name": "test-sa",
"expires": "2024-13-45", # Invalid month/day
},
)
self.assertEqual(response.status_code, 400)
self.assertJSONEqual(
response.content,
{
"expires": [
"Datetime has wrong format. Use one of these formats instead: "
"YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z]."
]
},
)
def test_service_account_validation_multiple_errors(self):
"""Test service account creation with multiple validation errors"""
self.client.force_login(self.admin)
response = self.client.post(
reverse("authentik_api:user-service-account"),
data={
"name": "", # Empty username
"create_group": "invalid", # Invalid boolean
"expiring": 123, # Invalid boolean
"expires": "not-a-date", # Invalid datetime
},
)
self.assertEqual(response.status_code, 400)
self.assertJSONEqual(
response.content,
{
"name": ["This field may not be blank."],
"create_group": ["Must be a valid boolean."],
"expiring": ["Must be a valid boolean."],
"expires": [
"Datetime has wrong format. Use one of these formats instead: "
"YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z]."
],
},
)
def test_service_account_validation_user_friendly_duplicate_error(self):
"""Test that duplicate username returns user-friendly error, not database error"""
self.client.force_login(self.admin)
# Create first service account
response = self.client.post(
reverse("authentik_api:user-service-account"),
data={
"name": "duplicate-username-test",
"create_group": True,
},
)
self.assertEqual(response.status_code, 200)
# Attempt to create second with same username
response = self.client.post(
reverse("authentik_api:user-service-account"),
data={
"name": "duplicate-username-test",
"create_group": True,
},
)
self.assertEqual(response.status_code, 400)
self.assertJSONEqual(
response.content,
{"name": ["This field must be unique."]},
)

View File

@@ -30,7 +30,6 @@ from authentik.flows.views.interface import FlowInterfaceView
from authentik.root.asgi_middleware import AuthMiddlewareStack
from authentik.root.messages.consumer import MessageConsumer
from authentik.root.middleware import ChannelsLoggingMiddleware
from authentik.tenants.channels import TenantsAwareMiddleware
urlpatterns = [
path(
@@ -98,9 +97,7 @@ api_urlpatterns = [
websocket_urlpatterns = [
path(
"ws/client/",
ChannelsLoggingMiddleware(
TenantsAwareMiddleware(AuthMiddlewareStack(MessageConsumer.as_asgi()))
),
ChannelsLoggingMiddleware(AuthMiddlewareStack(MessageConsumer.as_asgi())),
),
]

View File

@@ -20,11 +20,6 @@ from authentik.lib.models import CreatedUpdatedModel, SerializerModel
LOGGER = get_logger()
def fingerprint_sha256(cert: Certificate) -> str:
"""Get SHA256 Fingerprint of certificate"""
return hexlify(cert.fingerprint(hashes.SHA256()), ":").decode("utf-8")
class CertificateKeyPair(SerializerModel, ManagedModel, CreatedUpdatedModel):
"""CertificateKeyPair that can be used for signing or encrypting if `key_data`
is set, otherwise it can be used to verify remote data."""
@@ -87,7 +82,7 @@ class CertificateKeyPair(SerializerModel, ManagedModel, CreatedUpdatedModel):
@property
def fingerprint_sha256(self) -> str:
"""Get SHA256 Fingerprint of certificate_data"""
return fingerprint_sha256(self.certificate)
return hexlify(self.certificate.fingerprint(hashes.SHA256()), ":").decode("utf-8")
@property
def fingerprint_sha1(self) -> str:

View File

@@ -7,12 +7,13 @@ from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.x509.base import load_pem_x509_certificate
from django.utils.translation import gettext_lazy as _
from django_dramatiq_postgres.middleware import CurrentTask
from dramatiq.actor import actor
from structlog.stdlib import get_logger
from authentik.crypto.models import CertificateKeyPair
from authentik.lib.config import CONFIG
from authentik.tasks.middleware import CurrentTask
from authentik.tasks.models import Task
LOGGER = get_logger()
@@ -37,7 +38,7 @@ def ensure_certificate_valid(body: str):
@actor(description=_("Discover, import and update certificates from the filesystem."))
def certificate_discovery():
self = CurrentTask.get_task()
self: Task = CurrentTask.get_task()
certs = {}
private_keys = {}
discovered = 0

View File

@@ -27,7 +27,7 @@ class TestCrypto(APITestCase):
def test_model_private(self):
"""Test model private key"""
cert = CertificateKeyPair.objects.create(
name=generate_id(),
name="test",
certificate_data="foo",
key_data="foo",
)
@@ -271,7 +271,7 @@ class TestCrypto(APITestCase):
keypair = create_test_cert()
provider = OAuth2Provider.objects.create(
name=generate_id(),
client_id=generate_id(),
client_id="test",
client_secret=generate_key(),
authorization_flow=create_test_flow(),
redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "http://localhost")],
@@ -303,7 +303,7 @@ class TestCrypto(APITestCase):
keypair = create_test_cert()
OAuth2Provider.objects.create(
name=generate_id(),
client_id=generate_id(),
client_id="test",
client_secret=generate_key(),
authorization_flow=create_test_flow(),
redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "http://localhost")],

View File

@@ -1,5 +1,6 @@
from django.db.models.aggregates import Count
from django.utils.translation import gettext_lazy as _
from django_dramatiq_postgres.middleware import CurrentTask
from dramatiq.actor import actor
from structlog import get_logger
@@ -7,7 +8,7 @@ from authentik.enterprise.policies.unique_password.models import (
UniquePasswordPolicy,
UserPasswordHistory,
)
from authentik.tasks.middleware import CurrentTask
from authentik.tasks.models import Task
LOGGER = get_logger()
@@ -18,7 +19,7 @@ LOGGER = get_logger()
)
)
def check_and_purge_password_history():
self = CurrentTask.get_task()
self: Task = CurrentTask.get_task()
if not UniquePasswordPolicy.objects.exists():
UserPasswordHistory.objects.all().delete()
@@ -38,7 +39,7 @@ def trim_password_histories():
UniquePasswordPolicy policies.
"""
self = CurrentTask.get_task()
self: Task = CurrentTask.get_task()
# No policy, we'll let the cleanup above do its thing
if not UniquePasswordPolicy.objects.exists():

View File

@@ -4,7 +4,7 @@ from rest_framework import mixins
from rest_framework.viewsets import GenericViewSet
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.users import PartialGroupSerializer
from authentik.core.api.users import UserGroupSerializer
from authentik.core.api.utils import ModelSerializer
from authentik.enterprise.providers.google_workspace.models import GoogleWorkspaceProviderGroup
from authentik.lib.sync.outgoing.api import OutgoingSyncConnectionCreateMixin
@@ -13,7 +13,7 @@ from authentik.lib.sync.outgoing.api import OutgoingSyncConnectionCreateMixin
class GoogleWorkspaceProviderGroupSerializer(ModelSerializer):
"""GoogleWorkspaceProviderGroup Serializer"""
group_obj = PartialGroupSerializer(source="group", read_only=True)
group_obj = UserGroupSerializer(source="group", read_only=True)
class Meta:

View File

@@ -3,7 +3,7 @@
from rest_framework import mixins
from rest_framework.viewsets import GenericViewSet
from authentik.core.api.groups import PartialUserSerializer
from authentik.core.api.groups import GroupMemberSerializer
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import ModelSerializer
from authentik.enterprise.providers.google_workspace.models import GoogleWorkspaceProviderUser
@@ -13,7 +13,7 @@ from authentik.lib.sync.outgoing.api import OutgoingSyncConnectionCreateMixin
class GoogleWorkspaceProviderUserSerializer(ModelSerializer):
"""GoogleWorkspaceProviderUser Serializer"""
user_obj = PartialUserSerializer(source="user", read_only=True)
user_obj = GroupMemberSerializer(source="user", read_only=True)
class Meta:

View File

@@ -208,11 +208,11 @@ class GoogleWorkspaceGroupClient(
)
if not matching_authentik_group:
return
GoogleWorkspaceProviderGroup.objects.update_or_create(
GoogleWorkspaceProviderGroup.objects.get_or_create(
provider=self.provider,
group=matching_authentik_group,
google_id=google_id,
defaults={"attributes": group},
attributes=group,
)
def update_single_attribute(self, connection: GoogleWorkspaceProviderUser):

View File

@@ -113,11 +113,11 @@ class GoogleWorkspaceUserClient(GoogleWorkspaceSyncClient[User, GoogleWorkspaceP
matching_authentik_user = self.provider.get_object_qs(User).filter(email=email).first()
if not matching_authentik_user:
return
GoogleWorkspaceProviderUser.objects.update_or_create(
GoogleWorkspaceProviderUser.objects.get_or_create(
provider=self.provider,
user=matching_authentik_user,
google_id=email,
defaults={"attributes": user},
attributes=user,
)
def update_single_attribute(self, connection: GoogleWorkspaceProviderUser):

View File

@@ -292,7 +292,7 @@ class GoogleWorkspaceGroupTests(TestCase):
).exists()
)
def test_sync_discover(self):
def test_sync_task(self):
"""Test group discovery"""
uid = generate_id()
http = MockHTTP()
@@ -332,57 +332,3 @@ class GoogleWorkspaceGroupTests(TestCase):
)
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())
self.assertEqual(len(http.requests()), 5)
def test_sync_discover_multiple(self):
"""Test group discovery"""
uid = generate_id()
http = MockHTTP()
http.add_response(
f"https://admin.googleapis.com/admin/directory/v1/customer/my_customer/domains?key={self.api_key}&alt=json",
domains_list_v1_mock,
)
http.add_response(
f"https://admin.googleapis.com/admin/directory/v1/users?customer=my_customer&maxResults=500&orderBy=email&key={self.api_key}&alt=json",
method="GET",
body={"users": []},
)
http.add_response(
f"https://admin.googleapis.com/admin/directory/v1/groups?customer=my_customer&maxResults=500&orderBy=email&key={self.api_key}&alt=json",
method="GET",
body={"groups": [{"id": uid, "name": uid}]},
)
http.add_response(
f"https://admin.googleapis.com/admin/directory/v1/groups/{uid}?key={self.api_key}&alt=json",
method="PUT",
body={"id": uid},
)
self.app.backchannel_providers.remove(self.provider)
different_group = Group.objects.create(
name=uid,
)
self.app.backchannel_providers.add(self.provider)
with patch(
"authentik.enterprise.providers.google_workspace.models.GoogleWorkspaceProvider.google_credentials",
MagicMock(return_value={"developerKey": self.api_key, "http": http}),
):
google_workspace_sync.send(self.provider.pk).get_result()
self.assertTrue(
GoogleWorkspaceProviderGroup.objects.filter(
group=different_group, provider=self.provider
).exists()
)
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())
self.assertEqual(len(http.requests()), 5)
# Change response to trigger update
http.add_response(
f"https://admin.googleapis.com/admin/directory/v1/groups?customer=my_customer&maxResults=500&orderBy=email&key={self.api_key}&alt=json",
method="GET",
body={"groups": [{"id": uid, "name": uid, "bar": "baz"}]},
)
google_workspace_sync.send(self.provider.pk).get_result()
self.assertTrue(
GoogleWorkspaceProviderGroup.objects.filter(
group=different_group, provider=self.provider
).exists()
)
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())

View File

@@ -269,7 +269,7 @@ class GoogleWorkspaceUserTests(TestCase):
).exists()
)
def test_sync_discover(self):
def test_sync_task(self):
"""Test user discovery"""
uid = generate_id()
http = MockHTTP()
@@ -310,63 +310,3 @@ class GoogleWorkspaceUserTests(TestCase):
)
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())
self.assertEqual(len(http.requests()), 5)
def test_sync_discover_multiple(self):
"""Test user discovery, running multiple times"""
uid = generate_id()
http = MockHTTP()
http.add_response(
f"https://admin.googleapis.com/admin/directory/v1/customer/my_customer/domains?key={self.api_key}&alt=json",
domains_list_v1_mock,
)
http.add_response(
f"https://admin.googleapis.com/admin/directory/v1/users?customer=my_customer&maxResults=500&orderBy=email&key={self.api_key}&alt=json",
method="GET",
body={"users": [{"primaryEmail": f"{uid}@goauthentik.io"}]},
)
http.add_response(
f"https://admin.googleapis.com/admin/directory/v1/groups?customer=my_customer&maxResults=500&orderBy=email&key={self.api_key}&alt=json",
method="GET",
body={"groups": []},
)
http.add_response(
f"https://admin.googleapis.com/admin/directory/v1/users/{uid}%40goauthentik.io?key={self.api_key}&alt=json",
method="PUT",
body={"primaryEmail": f"{uid}@goauthentik.io"},
)
self.app.backchannel_providers.remove(self.provider)
different_user = User.objects.create(
username=uid,
email=f"{uid}@goauthentik.io",
)
self.app.backchannel_providers.add(self.provider)
# Sync once
with patch(
"authentik.enterprise.providers.google_workspace.models.GoogleWorkspaceProvider.google_credentials",
MagicMock(return_value={"developerKey": self.api_key, "http": http}),
):
google_workspace_sync.send(self.provider.pk).get_result()
self.assertTrue(
GoogleWorkspaceProviderUser.objects.filter(
user=different_user, provider=self.provider
).exists()
)
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())
self.assertEqual(len(http.requests()), 5)
# Change response, which will trigger a discovery update
http.add_response(
f"https://admin.googleapis.com/admin/directory/v1/users?customer=my_customer&maxResults=500&orderBy=email&key={self.api_key}&alt=json",
method="GET",
body={
"users": [
{"primaryEmail": f"{uid}@goauthentik.io", "foo": "bar"},
]
},
)
google_workspace_sync.send(self.provider.pk).get_result()
self.assertTrue(
GoogleWorkspaceProviderUser.objects.filter(
user=different_user, provider=self.provider
).exists()
)
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())

View File

@@ -4,7 +4,7 @@ from rest_framework import mixins
from rest_framework.viewsets import GenericViewSet
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.users import PartialGroupSerializer
from authentik.core.api.users import UserGroupSerializer
from authentik.core.api.utils import ModelSerializer
from authentik.enterprise.providers.microsoft_entra.models import MicrosoftEntraProviderGroup
from authentik.lib.sync.outgoing.api import OutgoingSyncConnectionCreateMixin
@@ -13,7 +13,7 @@ from authentik.lib.sync.outgoing.api import OutgoingSyncConnectionCreateMixin
class MicrosoftEntraProviderGroupSerializer(ModelSerializer):
"""MicrosoftEntraProviderGroup Serializer"""
group_obj = PartialGroupSerializer(source="group", read_only=True)
group_obj = UserGroupSerializer(source="group", read_only=True)
class Meta:

View File

@@ -3,7 +3,7 @@
from rest_framework import mixins
from rest_framework.viewsets import GenericViewSet
from authentik.core.api.groups import PartialUserSerializer
from authentik.core.api.groups import GroupMemberSerializer
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import ModelSerializer
from authentik.enterprise.providers.microsoft_entra.models import MicrosoftEntraProviderUser
@@ -13,7 +13,7 @@ from authentik.lib.sync.outgoing.api import OutgoingSyncConnectionCreateMixin
class MicrosoftEntraProviderUserSerializer(ModelSerializer):
"""MicrosoftEntraProviderUser Serializer"""
user_obj = PartialUserSerializer(source="user", read_only=True)
user_obj = GroupMemberSerializer(source="user", read_only=True)
class Meta:

View File

@@ -220,11 +220,11 @@ class MicrosoftEntraGroupClient(
)
if not matching_authentik_group:
return
MicrosoftEntraProviderGroup.objects.update_or_create(
MicrosoftEntraProviderGroup.objects.get_or_create(
provider=self.provider,
group=matching_authentik_group,
microsoft_id=group.id,
defaults={"attributes": self.entity_as_dict(group)},
attributes=self.entity_as_dict(group),
)
def update_single_attribute(self, connection: MicrosoftEntraProviderGroup):

View File

@@ -159,11 +159,11 @@ class MicrosoftEntraUserClient(MicrosoftEntraSyncClient[User, MicrosoftEntraProv
matching_authentik_user = self.provider.get_object_qs(User).filter(email=user.mail).first()
if not matching_authentik_user:
return
MicrosoftEntraProviderUser.objects.update_or_create(
MicrosoftEntraProviderUser.objects.get_or_create(
provider=self.provider,
user=matching_authentik_user,
microsoft_id=user.id,
defaults={"attributes": self.entity_as_dict(user)},
attributes=self.entity_as_dict(user),
)
def update_single_attribute(self, connection: MicrosoftEntraProviderUser):

View File

@@ -369,7 +369,7 @@ class MicrosoftEntraGroupTests(TestCase):
group_create.assert_called_once()
group_delete.assert_not_called()
def test_sync_discover(self):
def test_sync_task(self):
"""Test group discovery"""
uid = generate_id()
self.app.backchannel_providers.remove(self.provider)
@@ -430,84 +430,3 @@ class MicrosoftEntraGroupTests(TestCase):
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())
user_list.assert_called_once()
group_list.assert_called_once()
def test_sync_discover_multiple(self):
"""Test group discovery"""
uid = generate_id()
self.app.backchannel_providers.remove(self.provider)
different_group = Group.objects.create(
name=uid,
)
self.app.backchannel_providers.add(self.provider)
with (
patch(
"authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials",
MagicMock(return_value={"credentials": self.creds}),
),
patch(
"msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get",
AsyncMock(
return_value=OrganizationCollectionResponse(
value=[
Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")])
]
)
),
),
patch(
"msgraph.generated.users.item.user_item_request_builder.UserItemRequestBuilder.patch",
AsyncMock(return_value=MSUser(id=generate_id())),
),
patch(
"msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post",
AsyncMock(return_value=MSGroup(id=generate_id())),
),
patch(
"msgraph.generated.groups.item.group_item_request_builder.GroupItemRequestBuilder.patch",
AsyncMock(return_value=MSGroup(id=uid)),
),
patch(
"msgraph.generated.users.users_request_builder.UsersRequestBuilder.get",
AsyncMock(
return_value=UserCollectionResponse(
value=[MSUser(mail=f"{uid}@goauthentik.io", id=uid)]
)
),
) as user_list,
patch(
"msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.get",
AsyncMock(
return_value=GroupCollectionResponse(
value=[MSGroup(display_name=uid, unique_name=uid, id=uid)]
)
),
) as group_list,
):
microsoft_entra_sync.send(self.provider.pk).get_result()
self.assertTrue(
MicrosoftEntraProviderGroup.objects.filter(
group=different_group, provider=self.provider
).exists()
)
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())
user_list.assert_called_once()
group_list.assert_called_once()
with patch(
"msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.get",
AsyncMock(
return_value=GroupCollectionResponse(
value=[
MSGroup(display_name=uid, unique_name=uid, id=uid, description="foo")
]
)
),
) as mod_group_list:
microsoft_entra_sync.send(self.provider.pk).get_result()
self.assertTrue(
MicrosoftEntraProviderGroup.objects.filter(
group=different_group, provider=self.provider
).exists()
)
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())
mod_group_list.assert_called_once()

View File

@@ -356,7 +356,7 @@ class MicrosoftEntraUserTests(APITestCase):
user_patch.assert_not_called()
user_delete.assert_not_called()
def test_sync_discover(self):
def test_sync_task(self):
"""Test user discovery"""
uid = generate_id()
self.app.backchannel_providers.remove(self.provider)
@@ -406,73 +406,6 @@ class MicrosoftEntraUserTests(APITestCase):
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())
user_list.assert_called_once()
def test_sync_discover_multiple(self):
"""Test user discovery (multiple times)"""
uid = generate_id()
self.app.backchannel_providers.remove(self.provider)
different_user = User.objects.create(
username=uid,
email=f"{uid}@goauthentik.io",
)
self.app.backchannel_providers.add(self.provider)
with (
patch(
"authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials",
MagicMock(return_value={"credentials": self.creds}),
),
patch(
"msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get",
AsyncMock(
return_value=OrganizationCollectionResponse(
value=[
Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")])
]
)
),
),
patch(
"msgraph.generated.users.item.user_item_request_builder.UserItemRequestBuilder.patch",
AsyncMock(return_value=MSUser(id=generate_id())),
),
patch(
"msgraph.generated.users.users_request_builder.UsersRequestBuilder.get",
AsyncMock(
return_value=UserCollectionResponse(
value=[MSUser(mail=f"{uid}@goauthentik.io", id=uid)]
)
),
) as user_list,
patch(
"msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.get",
AsyncMock(return_value=GroupCollectionResponse(value=[])),
),
):
microsoft_entra_sync.send(self.provider.pk).get_result()
self.assertTrue(
MicrosoftEntraProviderUser.objects.filter(
user=different_user, provider=self.provider
).exists()
)
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())
user_list.assert_called_once()
with patch(
"msgraph.generated.users.users_request_builder.UsersRequestBuilder.get",
AsyncMock(
return_value=UserCollectionResponse(
value=[MSUser(mail=f"{uid}@goauthentik.io", id=uid, about_me="foo")]
)
),
) as mod_user_list:
microsoft_entra_sync.send(self.provider.pk).get_result()
self.assertTrue(
MicrosoftEntraProviderUser.objects.filter(
user=different_user, provider=self.provider
).exists()
)
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())
mod_user_list.assert_called_once()
def test_connect_manual(self):
"""test manual user connection"""
uid = generate_id()

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