Compare commits

..

95 Commits

Author SHA1 Message Date
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
218 changed files with 2696 additions and 7325 deletions

View File

@@ -33,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

@@ -1 +0,0 @@
website/docs/developer-docs/index.md

4
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,4 @@
# Contributing to authentik
Thanks for your interest in contributing! Please see our [contributing guide](https://docs.goauthentik.io/docs/developer-docs/?utm_source=github) for more information.

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-bookworm AS go-builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.24-bookworm AS go-builder
ARG TARGETOS
ARG TARGETARCH
@@ -76,9 +76,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.8.13 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-bookworm-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" \

View File

@@ -15,15 +15,16 @@
## What is authentik?
authentik is an open-source Identity Provider that emphasizes flexibility and versatility, with support for a wide set of protocols.
authentik is an open-source Identity Provider (IdP) for modern SSO. It supports SAML, OAuth2/OIDC, LDAP, RADIUS, and more, designed for self-hosting from small labs to large production clusters.
Our [enterprise offer](https://goauthentik.io/pricing) can also be used as a self-hosted replacement for large-scale deployments of Okta/Auth0, Entra ID, Ping Identity, or other legacy IdPs for employees and B2B2C use.
Our [enterprise offering](https://goauthentik.io/pricing) is available for organizations to securely replace existing IdPs such as Okta, Auth0, Entra ID, and Ping Identity for robust, large-scale identity management.
## Installation
For small/test setups it is recommended to use Docker Compose; refer to the [documentation](https://goauthentik.io/docs/installation/docker-compose/?utm_source=github).
For bigger setups, there is a Helm Chart [here](https://github.com/goauthentik/helm). This is documented [here](https://goauthentik.io/docs/installation/kubernetes/?utm_source=github).
- Docker Compose: recommended for small/test setups. See the [documentation](https://docs.goauthentik.io/docs/install-config/install/docker-compose/).
- Kubernetes (Helm Chart): recommended for larger setups. See the [documentation](https://docs.goauthentik.io/docs/install-config/install/kubernetes/) and the Helm chart [repository](https://github.com/goauthentik/helm).
- AWS CloudFormation: deploy on AWS using our official templates. See the [documentation](https://docs.goauthentik.io/docs/install-config/install/aws/).
- DigitalOcean Marketplace: one-click deployment via the official Marketplace app. See the [app listing](https://marketplace.digitalocean.com/apps/authentik).
## Screenshots
@@ -32,14 +33,20 @@ For bigger setups, there is a Helm Chart [here](https://github.com/goauthentik/h
| ![](https://docs.goauthentik.io/img/screen_apps_light.jpg) | ![](https://docs.goauthentik.io/img/screen_apps_dark.jpg) |
| ![](https://docs.goauthentik.io/img/screen_admin_light.jpg) | ![](https://docs.goauthentik.io/img/screen_admin_dark.jpg) |
## Development
## Development and contributions
See [Developer Documentation](https://docs.goauthentik.io/docs/developer-docs/?utm_source=github)
See the [Developer Documentation](https://docs.goauthentik.io/docs/developer-docs/) for information about setting up local build environments, testing your contributions, and our contribution process.
## Security
See [SECURITY.md](SECURITY.md)
Please see [SECURITY.md](SECURITY.md).
## Adoption and Contributions
## Adoption
Your organization uses authentik? We'd love to add your logo to the readme and our website! Email us @ hello@goauthentik.io or open a GitHub Issue/PR! For more information on how to contribute to authentik, please refer to our [contribution guide](https://docs.goauthentik.io/docs/developer-docs?utm_source=github).
Using authentik? We'd love to hear your story and feature your logo. Email us at [hello@goauthentik.io](mailto:hello@goauthentik.io) or open a GitHub Issue/PR!
## License
[![MIT License](https://img.shields.io/badge/License-MIT-green?style=for-the-badge)](LICENSE)
[![CC BY-SA 4.0](https://img.shields.io/badge/License-CC%20BY--SA%204.0-lightgrey?style=for-the-badge)](website/LICENSE)
[![authentik EE License](https://img.shields.io/badge/License-EE-orange?style=for-the-badge)](authentik/enterprise/LICENSE)

View File

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

View File

@@ -38,6 +38,7 @@ from authentik.blueprints.v1.oci import OCI_PREFIX
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.models import Task
from authentik.tasks.schedules.models import Schedule
from authentik.tenants.models import Tenant
@@ -111,6 +112,7 @@ class BlueprintEventHandler(FileSystemEventHandler):
@actor(
description=_("Find blueprints as `blueprints_find` does, but return a safe dict."),
throws=(DatabaseError, ProgrammingError, InternalError),
priority=PRIORITY_HIGH,
)
def blueprints_find_dict():
blueprints = []

View File

@@ -328,6 +328,12 @@ class SessionUserSerializer(PassiveSerializer):
original = UserSelfSerializer(required=False)
class UserPasswordSetSerializer(PassiveSerializer):
"""Payload to set a users' password directly"""
password = CharField(required=True)
class UsersFilter(FilterSet):
"""Filter for users"""
@@ -585,12 +591,7 @@ class UserViewSet(UsedByMixin, ModelViewSet):
@permission_required("authentik_core.reset_user_password")
@extend_schema(
request=inline_serializer(
"UserPasswordSetSerializer",
{
"password": CharField(required=True),
},
),
request=UserPasswordSetSerializer,
responses={
204: OpenApiResponse(description="Successfully changed password"),
400: OpenApiResponse(description="Bad request"),
@@ -599,9 +600,11 @@ class UserViewSet(UsedByMixin, ModelViewSet):
@action(detail=True, methods=["POST"], permission_classes=[])
def set_password(self, request: Request, pk: int) -> Response:
"""Set password for user"""
data = UserPasswordSetSerializer(data=request.data)
data.is_valid(raise_exception=True)
user: User = self.get_object()
try:
user.set_password(request.data.get("password"), request=request)
user.set_password(data.validated_data["password"], request=request)
user.save()
except (ValidationError, IntegrityError) as exc:
LOGGER.debug("Failed to set password", exc=exc)

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

@@ -102,6 +102,16 @@ class TestUsersAPI(APITestCase):
self.admin.refresh_from_db()
self.assertTrue(self.admin.check_password(new_pw))
def test_set_password_blank(self):
"""Test Direct password set"""
self.client.force_login(self.admin)
response = self.client.post(
reverse("authentik_api:user-set-password", kwargs={"pk": self.admin.pk}),
data={"password": ""},
)
self.assertEqual(response.status_code, 400)
self.assertJSONEqual(response.content, {"password": ["This field may not be blank."]})
def test_recovery(self):
"""Test user recovery link"""
flow = create_test_flow(

View File

@@ -43,7 +43,9 @@ def structlog_configure():
structlog.stdlib.PositionalArgumentsFormatter(),
structlog.processors.TimeStamper(fmt="iso", utc=False),
structlog.processors.StackInfoRenderer(),
structlog.processors.dict_tracebacks,
structlog.processors.ExceptionRenderer(
structlog.processors.ExceptionDictTransformer(show_locals=CONFIG.get_bool("debug"))
),
structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
],
logger_factory=structlog.stdlib.LoggerFactory(),
@@ -65,7 +67,14 @@ def get_logger_config():
"json": {
"()": structlog.stdlib.ProcessorFormatter,
"processor": structlog.processors.JSONRenderer(sort_keys=True),
"foreign_pre_chain": LOG_PRE_CHAIN + [structlog.processors.dict_tracebacks],
"foreign_pre_chain": LOG_PRE_CHAIN
+ [
structlog.processors.ExceptionRenderer(
structlog.processors.ExceptionDictTransformer(
show_locals=CONFIG.get_bool("debug")
)
),
],
},
"console": {
"()": structlog.stdlib.ProcessorFormatter,

View File

@@ -1,4 +1,5 @@
from dramatiq.actor import Actor
from dramatiq.results.errors import ResultFailure
from drf_spectacular.utils import extend_schema
from rest_framework.decorators import action
from rest_framework.fields import BooleanField, CharField, ChoiceField
@@ -110,9 +111,13 @@ class OutgoingSyncProviderStatusMixin:
"override_dry_run": params.validated_data["override_dry_run"],
"pk": params.validated_data["sync_object_id"],
},
retries=0,
rel_obj=provider,
)
msg.get_result(block=True)
try:
msg.get_result(block=True)
except ResultFailure:
pass
task: Task = msg.options["task"]
task.refresh_from_db()
return Response(SyncObjectResultSerializer(instance={"messages": task._messages}).data)

View File

@@ -20,6 +20,7 @@ from authentik.lib.sync.outgoing.exceptions import (
TransientSyncException,
)
from authentik.lib.sync.outgoing.models import OutgoingSyncProvider
from authentik.lib.utils.errors import exception_to_dict
from authentik.lib.utils.reflection import class_to_path, path_to_class
from authentik.tasks.models import Task
@@ -164,16 +165,17 @@ class SyncTasks:
except BadRequestSyncException as exc:
self.logger.warning("failed to sync object", exc=exc, obj=obj)
task.warning(
f"Failed to sync {obj._meta.verbose_name} {str(obj)} due to error: {str(exc)}",
f"Failed to sync {str(obj)} due to error: {str(exc)}",
arguments=exc.args[1:],
obj=sanitize_item(obj),
exception=exception_to_dict(exc),
)
except TransientSyncException as exc:
self.logger.warning("failed to sync object", exc=exc, user=obj)
task.warning(
f"Failed to sync {obj._meta.verbose_name} {str(obj)} due to "
"transient error: {str(exc)}",
f"Failed to sync {str(obj)} due to " f"transient error: {str(exc)}",
obj=sanitize_item(obj),
exception=exception_to_dict(exc),
)
except StopSync as exc:
self.logger.warning("Stopping sync", exc=exc)

View File

@@ -26,7 +26,6 @@ HIST_POLICIES_EXECUTION_TIME = Histogram(
"binding_order",
"binding_target_type",
"binding_target_name",
"object_pk",
"object_type",
"mode",
],

View File

@@ -86,7 +86,6 @@ class PolicyEngine:
binding_order=binding.order,
binding_target_type=binding.target_type,
binding_target_name=binding.target_name,
object_pk=str(self.request.obj.pk),
object_type=class_to_path(self.request.obj.__class__),
mode="cache_retrieve",
).time():

View File

@@ -131,7 +131,6 @@ class PolicyProcess(PROCESS_CLASS):
binding_order=self.binding.order,
binding_target_type=self.binding.target_type,
binding_target_name=self.binding.target_name,
object_pk=str(self.request.obj.pk) if self.request.obj else "",
object_type=class_to_path(self.request.obj.__class__) if self.request.obj else "",
mode="execute_process",
).time(),

View File

@@ -4,17 +4,18 @@ import re
import uuid
from base64 import b64decode
from binascii import Error
from time import time
from typing import Any
from urllib.parse import urlparse
from django.http import HttpRequest, HttpResponse, JsonResponse
from django.http.response import HttpResponseRedirect
from django.utils.cache import patch_vary_headers
from django.utils.timezone import now
from structlog.stdlib import get_logger
from authentik.core.middleware import CTX_AUTH_VIA, KEY_USER
from authentik.events.models import Event, EventAction
from authentik.lib.utils.time import timedelta_from_string
from authentik.providers.oauth2.errors import BearerTokenError
from authentik.providers.oauth2.id_token import hash_session_key
from authentik.providers.oauth2.models import AccessToken, OAuth2Provider
@@ -229,11 +230,13 @@ def create_logout_token(
LOGGER.debug("Creating logout token", provider=provider, sub=sub)
_now = now()
# Create the logout token payload
payload = {
"iss": str(iss),
"aud": provider.client_id,
"iat": int(time()),
"iat": int(_now.timestamp()),
"exp": int((_now + timedelta_from_string(provider.access_token_validity)).timestamp()),
"jti": str(uuid.uuid4()),
"events": {
"http://schemas.openid.net/event/backchannel-logout": {},

View File

@@ -13,7 +13,7 @@ def migrate_sessions(apps, schema_editor):
for token in ConnectionToken.objects.using(db_alias).all():
token.session = (
AuthenticatedSession.objects.using(db_alias)
.filter(session_key=token.old_session.session_key)
.filter(session__session_key=token.old_session.session_key)
.first()
)
if token.session:

View File

@@ -27,3 +27,8 @@ class SCIMRequestException(TransientSyncException):
except ValidationError:
pass
return self._message
def __str__(self):
if self._response:
return self._response.text
return super().__str__()

View File

@@ -1,69 +0,0 @@
"""InitialPermissions middleware"""
from collections.abc import Callable
from contextvars import ContextVar
from functools import partial
from django.db.models import Model
from django.db.models.signals import post_save
from django.http import HttpRequest, HttpResponse
from authentik.core.models import User
from authentik.rbac.permissions import assign_initial_permissions
_CTX_REQUEST = ContextVar[HttpRequest | None]("authentik_initial_permissions_request", default=None)
class InitialPermissionsMiddleware:
"""Register a handler for duration of request-response that assigns InitialPermissions"""
get_response: Callable[[HttpRequest], HttpResponse]
def __init__(self, get_response: Callable[[HttpRequest], HttpResponse]):
self.get_response = get_response
def get_uid(self, request_id: str) -> str:
return f"InitialPermissionMiddleware-{request_id}"
def connect(self, request: HttpRequest):
if not hasattr(request, "request_id"):
return
post_save.connect(
partial(self.post_save_handler, request=request),
dispatch_uid=self.get_uid(request.request_id),
weak=False,
)
def disconnect(self, request: HttpRequest):
if not hasattr(request, "request_id"):
return
post_save.disconnect(dispatch_uid=self.get_uid(request.request_id))
def __call__(self, request: HttpRequest) -> HttpResponse:
_CTX_REQUEST.set(request)
self.connect(request)
response = self.get_response(request)
self.disconnect(request)
_CTX_REQUEST.set(None)
return response
def process_exception(self, request: HttpRequest, exception: Exception):
self.disconnect(request)
def post_save_handler(
self,
request: HttpRequest,
instance: Model,
created: bool,
**_,
):
if not created:
return
if request.request_id != _CTX_REQUEST.get().request_id:
return
user: User = request.user
if not user or user.is_anonymous:
return
assign_initial_permissions(user, instance)

View File

@@ -5,12 +5,9 @@ from django.db.models import Model
from guardian.shortcuts import assign_perm
from rest_framework.permissions import BasePermission, DjangoObjectPermissions
from rest_framework.request import Request
from structlog.stdlib import get_logger
from authentik.rbac.models import InitialPermissions, InitialPermissionsMode
LOGGER = get_logger()
class ObjectPermissions(DjangoObjectPermissions):
"""RBAC Permissions"""
@@ -74,10 +71,4 @@ def assign_initial_permissions(user, instance: Model):
if initial_permissions.mode == InitialPermissionsMode.USER
else initial_permissions.role.group
)
LOGGER.debug(
"Adding initial permission",
initial_permission=permission,
subject=assign_to,
object=instance,
)
assign_perm(permission, assign_to, instance)

View File

@@ -265,7 +265,6 @@ MIDDLEWARE = [
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"authentik.core.middleware.ImpersonateMiddleware",
"authentik.rbac.middleware.InitialPermissionsMiddleware",
]
MIDDLEWARE_LAST = [
"django_prometheus.middleware.PrometheusAfterMiddleware",

View File

@@ -5,6 +5,7 @@ from typing import Any
from django.db.models import Q
from ldap3 import SUBTREE
from ldap3.utils.conv import escape_filter_chars
from authentik.core.models import Group, User
from authentik.sources.ldap.models import LDAP_DISTINGUISHED_NAME, LDAP_UNIQUENESS, LDAPSource
@@ -52,7 +53,8 @@ class MembershipLDAPSynchronizer(BaseLDAPSynchronizer):
for group in page_data:
if self._source.lookup_groups_from_user:
group_dn = group.get("dn", {})
group_filter = f"({self._source.group_membership_field}={group_dn})"
escaped_dn = escape_filter_chars(group_dn)
group_filter = f"({self._source.group_membership_field}={escaped_dn})"
group_members = self._source.connection().extend.standard.paged_search(
search_base=self.base_dn_users,
search_filter=group_filter,

View File

@@ -4,6 +4,8 @@ from unittest.mock import MagicMock, patch
from django.db.models import Q
from django.test import TestCase
from ldap3.core.exceptions import LDAPInvalidFilterError
from ldap3.utils.conv import escape_filter_chars
from authentik.blueprints.tests import apply_blueprint
from authentik.core.models import Group, User
@@ -519,3 +521,89 @@ class LDAPSyncTests(TestCase):
self.assertFalse(User.objects.filter(username__startswith="not-in-the-source").exists())
self.assertFalse(Group.objects.filter(name__startswith="not-in-the-source").exists())
def test_membership_sync_special_chars_in_group_dn(self):
"""Test membership synchronization with special characters in group DN"""
self.source.object_uniqueness_field = "uid"
self.source.group_object_filter = "(objectClass=groupOfNames)"
self.source.lookup_groups_from_user = True
self.source.group_membership_field = "memberOf"
# Mock connection with group DN containing special characters
mock_conn = MagicMock()
# Simulate group with special characters in DN: parentheses, backslashes, asterisks
special_group_dn = "cn=test(group),ou=groups,dc=example,dc=com"
backslash_group_dn = "cn=test\\group,ou=groups,dc=example,dc=com"
asterisk_group_dn = "cn=test*group,ou=groups,dc=example,dc=com"
# Mock the paged_search method that would be called with the filter
mock_standard = MagicMock()
mock_conn.extend.standard = mock_standard
# Test case 1: Group DN with parentheses
with patch("authentik.sources.ldap.models.LDAPSource.connection", return_value=mock_conn):
membership_sync = MembershipLDAPSynchronizer(self.source, Task())
# Simulate group data with special characters in DN
page_data = [{"dn": special_group_dn}]
# This should not raise LDAPInvalidFilterError anymore
try:
membership_sync.sync(page_data)
# Verify that the filter was properly escaped
# The call should have been made with escaped characters
mock_standard.paged_search.assert_called()
call_args = mock_standard.paged_search.call_args
search_filter = call_args[1]["search_filter"]
# The parentheses should be escaped as \28 and \29
self.assertIn("\\28", search_filter) # Escaped (
self.assertIn("\\29", search_filter) # Escaped )
except LDAPInvalidFilterError:
self.fail("LDAPInvalidFilterError should not be raised with escaped filter")
# Test case 2: Group DN with backslashes
with patch("authentik.sources.ldap.models.LDAPSource.connection", return_value=mock_conn):
membership_sync = MembershipLDAPSynchronizer(self.source, Task())
page_data = [{"dn": backslash_group_dn}]
try:
membership_sync.sync(page_data)
call_args = mock_standard.paged_search.call_args
search_filter = call_args[1]["search_filter"]
# The backslash should be escaped as \5c
self.assertIn("\\5c", search_filter) # Escaped \
except LDAPInvalidFilterError:
self.fail("LDAPInvalidFilterError should not be raised with escaped filter")
# Test case 3: Group DN with asterisks
with patch("authentik.sources.ldap.models.LDAPSource.connection", return_value=mock_conn):
membership_sync = MembershipLDAPSynchronizer(self.source, Task())
page_data = [{"dn": asterisk_group_dn}]
try:
membership_sync.sync(page_data)
call_args = mock_standard.paged_search.call_args
search_filter = call_args[1]["search_filter"]
# The asterisk should be escaped as \2a
self.assertIn("\\2a", search_filter) # Escaped *
except LDAPInvalidFilterError:
self.fail("LDAPInvalidFilterError should not be raised with escaped filter")
def test_escape_filter_chars_function(self):
"""Test the escape_filter_chars function directly"""
# Test various special characters that need escaping
test_cases = [
("test(group)", "test\\28group\\29"), # parentheses
("test\\group", "test\\5cgroup"), # backslash
("test*group", "test\\2agroup"), # asterisk
("test(*)group", "test\\28\\2a\\29group"), # multiple special chars
("normalgroup", "normalgroup"), # no special chars
("", ""), # empty string
]
for input_str, expected in test_cases:
with self.subTest(input_str=input_str):
result = escape_filter_chars(input_str)
self.assertEqual(result, expected)

View File

@@ -96,7 +96,11 @@ class EntraIDType(SourceType):
}
def get_base_group_properties(self, source, group_id, **kwargs):
raw_group = kwargs["info"]["raw_groups"][group_id]
raw_groups = kwargs["info"]["raw_groups"]
if group_id in raw_groups:
name = raw_groups[group_id]["displayName"]
else:
name = group_id
return {
"name": raw_group["displayName"],
"name": name,
}

View File

@@ -198,10 +198,7 @@ class AuthenticatorDuoStageViewSet(UsedByMixin, ModelViewSet):
return {"error": "", "count": created}
except RuntimeError as exc:
LOGGER.warning("failed to get users from duo", exc=exc)
return {
"error": "An internal error occurred while importing devices.",
"count": created,
}
return {"error": str(exc), "count": created}
class DuoDeviceSerializer(ModelSerializer):

View File

@@ -168,8 +168,6 @@ class AuthenticatorDuoStageTests(FlowTestCase):
client_secret=generate_id(),
api_hostname=generate_id(),
)
# Test missing admin credentials
response = self.client.post(
reverse(
"authentik_api:authenticatorduostage-import-devices-automatic",
@@ -180,31 +178,6 @@ class AuthenticatorDuoStageTests(FlowTestCase):
)
self.assertEqual(response.status_code, 400)
# Test internal error handling
stage.admin_integration_key = generate_id()
stage.admin_secret_key = generate_id()
stage.save()
with patch(
"duo_client.admin.Admin.get_users_iterator",
MagicMock(side_effect=RuntimeError("Duo API error")),
):
response = self.client.post(
reverse(
"authentik_api:authenticatorduostage-import-devices-automatic",
kwargs={
"pk": str(stage.pk),
},
),
)
self.assertEqual(response.status_code, 400)
self.assertJSONEqual(
response.content,
{
"error": "An internal error occurred while importing devices.",
"count": 0,
},
)
def test_api_import_automatic(self):
"""test `import_devices_automatic`"""
self.client.force_login(self.user)

View File

@@ -2,6 +2,8 @@ from authentik.blueprints.apps import ManagedAppConfig
from authentik.lib.utils.time import fqdn_rand
from authentik.tasks.schedules.common import ScheduleSpec
PRIORITY_HIGH = 1000
class AuthentikTasksConfig(ManagedAppConfig):
name = "authentik.tasks"

View File

@@ -14,6 +14,7 @@ from django_redis import get_redis_connection
from dramatiq.broker import Broker
from dramatiq.message import Message
from dramatiq.middleware import Middleware
from psycopg.errors import Error
from redis.exceptions import RedisError
from structlog.stdlib import get_logger
@@ -26,6 +27,7 @@ from authentik.tenants.utils import get_current_tenant
LOGGER = get_logger()
HEALTHCHECK_LOGGER = get_logger("authentik.worker").bind()
DB_ERRORS = (OperationalError, Error, RedisError)
class TenantMiddleware(Middleware):
@@ -175,7 +177,7 @@ class _healthcheck_handler(BaseHTTPRequestHandler):
redis_conn = get_redis_connection()
redis_conn.ping()
self.send_response(200)
except (OperationalError, RedisError): # pragma: no cover
except DB_ERRORS: # pragma: no cover
self.send_response(503)
self.send_header("Content-Type", "text/plain; charset=utf-8")
self.send_header("Content-Length", "0")
@@ -216,6 +218,14 @@ class WorkerStatusMiddleware(Middleware):
hostname=socket.gethostname(),
version=authentik_full_version(),
)
while True:
try:
WorkerStatusMiddleware.keep(status)
except DB_ERRORS: # pragma: no cover
sleep(10)
pass
def keep(status: WorkerStatus):
lock_id = f"goauthentik.io/worker/status/{status.pk}"
with pglock.advisory(lock_id, side_effect=pglock.Raise):
while True:

View File

@@ -107,7 +107,6 @@ class ScheduleViewSet(
"rel_obj_content_type__app_label",
"rel_obj_content_type__model",
"rel_obj_id",
"description",
)
filterset_class = ScheduleFilter
ordering = (

View File

@@ -1,10 +0,0 @@
from django.core.management.base import BaseCommand
class Command(BaseCommand):
help = "ak createsuperuser should not be used. Instead, use ak create_admin_group"
def handle(self, *args, **options): # noqa: ANN001, D401
raise RuntimeError(
"ak createsuperuser should not be used. Instead, use ak create_admin_group"
)

View File

@@ -2,7 +2,7 @@
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "https://goauthentik.io/blueprints/schema.json",
"type": "object",
"title": "authentik 2025.10.0-rc1 Blueprint schema",
"title": "authentik 2025.8.2 Blueprint schema",
"required": [
"version",
"entries"

View File

@@ -48,7 +48,7 @@ services:
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?secret key required}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.10.0-rc1}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.8.2}
ports:
- ${COMPOSE_PORT_HTTP:-9000}:9000
- ${COMPOSE_PORT_HTTPS:-9443}:9443
@@ -72,7 +72,7 @@ services:
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?secret key required}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.10.0-rc1}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.8.2}
restart: unless-stopped
user: root
volumes:

8
go.mod
View File

@@ -6,7 +6,7 @@ require (
beryju.io/ldap v0.1.0
github.com/avast/retry-go/v4 v4.6.1
github.com/coreos/go-oidc/v3 v3.15.0
github.com/getsentry/sentry-go v0.35.1
github.com/getsentry/sentry-go v0.35.0
github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1
github.com/go-ldap/ldap/v3 v3.4.11
github.com/go-openapi/runtime v0.28.0
@@ -23,13 +23,13 @@ require (
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484
github.com/pires/go-proxyproto v0.8.1
github.com/prometheus/client_golang v1.23.0
github.com/redis/go-redis/v9 v9.12.1
github.com/redis/go-redis/v9 v9.11.0
github.com/sethvargo/go-envconfig v1.3.0
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.9.1
github.com/stretchr/testify v1.11.0
github.com/stretchr/testify v1.10.0
github.com/wwt/guac v1.3.2
goauthentik.io/api/v3 v3.2025100.2
goauthentik.io/api/v3 v3.2025064.8
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
golang.org/x/oauth2 v0.30.0
golang.org/x/sync v0.16.0

16
go.sum
View File

@@ -26,8 +26,8 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/getsentry/sentry-go v0.35.1 h1:iopow6UVLE2aXu46xKVIs8Z9D/YZkJrHkgozrxa+tOQ=
github.com/getsentry/sentry-go v0.35.1/go.mod h1:C55omcY9ChRQIUcVcGcs+Zdy4ZpQGvNJ7JYHIoSWOtE=
github.com/getsentry/sentry-go v0.35.0 h1:+FJNlnjJsZMG3g0/rmmP7GiKjQoUF5EXfEtBwtPtkzY=
github.com/getsentry/sentry-go v0.35.0/go.mod h1:C55omcY9ChRQIUcVcGcs+Zdy4ZpQGvNJ7JYHIoSWOtE=
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
@@ -148,8 +148,8 @@ github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/redis/go-redis/v9 v9.12.1 h1:k5iquqv27aBtnTm2tIkROUDp8JBXhXZIVu1InSgvovg=
github.com/redis/go-redis/v9 v9.12.1/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
github.com/redis/go-redis/v9 v9.11.0 h1:E3S08Gl/nJNn5vkxd2i78wZxWAPNZgUNTp8WIJUAiIs=
github.com/redis/go-redis/v9 v9.11.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -169,8 +169,8 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.0 h1:ib4sjIrwZKxE5u/Japgo/7SJV3PvgjGiRNAvTVGqQl8=
github.com/stretchr/testify v1.11.0/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/wwt/guac v1.3.2 h1:sH6OFGa/1tBs7ieWBVlZe7t6F5JAOWBry/tqQL/Vup4=
github.com/wwt/guac v1.3.2/go.mod h1:eKm+NrnK7A88l4UBEcYNpZQGMpZRryYKoz4D/0/n1C0=
go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80=
@@ -185,8 +185,8 @@ go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
goauthentik.io/api/v3 v3.2025100.2 h1:OF8qEpn6PzZFlB16RzL51RSIyFOY234gAWfd8/kjzhc=
goauthentik.io/api/v3 v3.2025100.2/go.mod h1:82lqAz4jxzl6Cg0YDbhNtvvTG2rm6605ZhdJFnbbsl8=
goauthentik.io/api/v3 v3.2025064.8 h1:wgegkPUtGSrOR7+Rnd0cxLVU0cEea87BatjESa6BJv0=
goauthentik.io/api/v3 v3.2025064.8/go.mod h1:82lqAz4jxzl6Cg0YDbhNtvvTG2rm6605ZhdJFnbbsl8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=

View File

@@ -1 +1 @@
2025.10.0-rc1
2025.8.2

View File

@@ -1,91 +0,0 @@
// https://github.com/gorilla/handlers/issues/259#issuecomment-2671695039
package web
import (
"bufio"
"net"
"net/http"
"github.com/gorilla/handlers"
)
// compressHandler is an HTTP handler that adds the Content-Encoding header
// back to responses when removed by the http.FileServer.
//
// handlers.CompressHandler(newCompressHandler(http.FileServer(...)))
type compressHandler struct {
// handler is an HTTP handler, usually an http.FileServer.
handler http.Handler
}
var _ http.Handler = &compressHandler{}
func NewCompressHandler(handler http.Handler) http.Handler {
h := &compressHandler{
handler: handler,
}
return handlers.CompressHandler(h)
}
func (h *compressHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// The wrapped response writer saves the incoming content encoding so
// it can be restored when writing the response headers.
cw := &compressedResponseWriter{
encoding: w.Header().Get("Content-Encoding"),
fixed: false,
responseWriter: w,
}
h.handler.ServeHTTP(cw, r)
}
// compressedResponseWriter is an http.ResponseWriter that ensures that a
// previously-set Content-Encoding header is in place before writing the
// response.
type compressedResponseWriter struct {
encoding string
fixed bool
responseWriter http.ResponseWriter
}
var _ http.ResponseWriter = &compressedResponseWriter{}
func (w *compressedResponseWriter) Header() http.Header {
return w.responseWriter.Header()
}
func (w *compressedResponseWriter) fixContentEncoding() {
if w.fixed {
return
}
w.fixed = true
// The Go 1.23 http.FileServer() removes headers like Content-Encoding
// from error responses. This breaks gzip and deflate encoding.
// https://github.com/gorilla/handlers/issues/259
// https://github.com/golang/go/issues/66343
if w.encoding == "gzip" || w.encoding == "deflate" {
if w.Header().Get("Content-Encoding") == "" {
w.Header().Set("Content-Encoding", w.encoding)
}
}
}
func (w *compressedResponseWriter) Write(data []byte) (int, error) {
w.fixContentEncoding()
return w.responseWriter.Write(data)
}
func (w *compressedResponseWriter) WriteHeader(statusCode int) {
w.fixContentEncoding()
w.responseWriter.WriteHeader(statusCode)
}
func (w *compressedResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
if hj, ok := w.responseWriter.(http.Hijacker); ok {
return hj.Hijack()
}
return nil, nil, http.ErrNotSupported
}
// Ensure our compressedResponseWriter implements the necessary interfaces.
var _ http.ResponseWriter = &compressedResponseWriter{}
var _ http.Hijacker = &compressedResponseWriter{}

View File

@@ -5,7 +5,6 @@ import (
"encoding/pem"
"errors"
"fmt"
"io"
"net/http"
"net/http/httputil"
"net/url"
@@ -16,7 +15,6 @@ import (
"goauthentik.io/internal/config"
"goauthentik.io/internal/utils/sentry"
"goauthentik.io/internal/utils/web"
staticWeb "goauthentik.io/web"
)
var (
@@ -90,81 +88,19 @@ func (ws *WebServer) configureProxy() {
}
func (ws *WebServer) proxyErrorHandler(rw http.ResponseWriter, req *http.Request, err error) {
accept := req.Header.Get("Accept")
header := rw.Header()
if errors.Is(err, ErrAuthentikStarting) {
header.Set("Retry-After", "5")
if strings.Contains(accept, "application/json") {
header.Set("Content-Type", "application/json")
err = json.NewEncoder(rw).Encode(map[string]string{
"error": "authentik starting",
})
if err != nil {
ws.log.WithError(err).Warning("failed to write error message")
return
}
} else if strings.Contains(accept, "text/html") {
header.Set("Content-Type", "text/html")
rw.WriteHeader(http.StatusServiceUnavailable)
loadingSplashFile, err := staticWeb.StaticDir.Open("standalone/loading/startup.html")
if err != nil {
ws.log.WithError(err).Warning("failed to open startup splash screen")
return
}
loadingSplashHTML, err := io.ReadAll(loadingSplashFile)
if err != nil {
ws.log.WithError(err).Warning("failed to read startup splash screen")
return
}
_, err = rw.Write(loadingSplashHTML)
if err != nil {
ws.log.WithError(err).Warning("failed to write startup splash screen")
return
}
} else {
header.Set("Content-Type", "text/plain")
rw.WriteHeader(http.StatusServiceUnavailable)
// Fallback to just a status message
_, err = rw.Write([]byte("authentik starting"))
if err != nil {
ws.log.WithError(err).Warning("failed to write initializing HTML")
}
}
return
if !errors.Is(err, ErrAuthentikStarting) {
ws.log.WithError(err).Warning("failed to proxy to backend")
}
ws.log.WithError(err).Warning("failed to proxy to backend")
rw.WriteHeader(http.StatusBadGateway)
em := fmt.Sprintf("failed to connect to authentik backend: %v", err)
if strings.Contains(accept, "application/json") {
header.Set("Content-Type", "application/json")
rw.WriteHeader(http.StatusBadGateway)
// return json if the client asks for json
if req.Header.Get("Accept") == "application/json" {
err = json.NewEncoder(rw).Encode(map[string]string{
"error": em,
})
} else {
header.Set("Content-Type", "text/plain")
rw.WriteHeader(http.StatusBadGateway)
_, err = rw.Write([]byte(em))
}
if err != nil {
ws.log.WithError(err).Warning("failed to write error message")
}

View File

@@ -17,7 +17,9 @@ func (ws *WebServer) configureStatic() {
// Setup routers
staticRouter := ws.loggingRouter.NewRoute().Subrouter()
staticRouter.Use(ws.staticHeaderMiddleware)
staticRouter.Use(web.DisableIndex)
indexLessRouter := staticRouter.NewRoute().Subrouter()
// Specifically disable index
indexLessRouter.Use(web.DisableIndex)
distFs := http.FileServer(http.Dir("./web/dist"))
@@ -29,18 +31,18 @@ func (ws *WebServer) configureStatic() {
return h
}
staticRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/static/dist/").Handler(pathStripper(
indexLessRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/static/dist/").Handler(pathStripper(
distFs,
"static/dist/",
config.Get().Web.Path,
))
staticRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/static/authentik/").Handler(pathStripper(
indexLessRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/static/authentik/").Handler(pathStripper(
http.FileServer(http.Dir("./web/authentik")),
"static/authentik/",
config.Get().Web.Path,
))
staticRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/if/flow/{flow_slug}/assets").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
indexLessRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/if/flow/{flow_slug}/assets").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
pathStripper(
@@ -49,9 +51,9 @@ func (ws *WebServer) configureStatic() {
config.Get().Web.Path,
).ServeHTTP(rw, r)
})
staticRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/if/admin/assets").Handler(http.StripPrefix(fmt.Sprintf("%sif/admin", config.Get().Web.Path), distFs))
staticRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/if/user/assets").Handler(http.StripPrefix(fmt.Sprintf("%sif/user", config.Get().Web.Path), distFs))
staticRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/if/rac/{app_slug}/assets").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
indexLessRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/if/admin/assets").Handler(http.StripPrefix(fmt.Sprintf("%sif/admin", config.Get().Web.Path), distFs))
indexLessRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/if/user/assets").Handler(http.StripPrefix(fmt.Sprintf("%sif/user", config.Get().Web.Path), distFs))
indexLessRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/if/rac/{app_slug}/assets").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
pathStripper(
@@ -64,7 +66,7 @@ func (ws *WebServer) configureStatic() {
// Media files, if backend is file
if config.Get().Storage.Media.Backend == "file" {
fsMedia := http.FileServer(http.Dir(config.Get().Storage.Media.File.Path))
staticRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/media/").Handler(pathStripper(
indexLessRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/media/").Handler(pathStripper(
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox")
fsMedia.ServeHTTP(w, r)

View File

@@ -12,6 +12,7 @@ import (
"path"
"time"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
"github.com/gorilla/securecookie"
"github.com/pires/go-proxyproto"
@@ -59,7 +60,7 @@ func NewWebServer() *WebServer {
l := log.WithField("logger", "authentik.router")
mainHandler := mux.NewRouter()
mainHandler.Use(web.ProxyHeaders())
mainHandler.Use(web.NewCompressHandler)
mainHandler.Use(handlers.CompressHandler)
loggingHandler := mainHandler.NewRoute().Subrouter()
loggingHandler.Use(web.NewLoggingHandler(l, nil))

View File

@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1
# Stage 1: Build
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25-bookworm AS builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.24-bookworm AS builder
ARG TARGETOS
ARG TARGETARCH

View File

@@ -69,7 +69,7 @@ function prepare_debug {
}
if [[ -z "${PROMETHEUS_MULTIPROC_DIR}" ]]; then
export PROMETHEUS_MULTIPROC_DIR="${TMPDIR:-/tmp}"
export PROMETHEUS_MULTIPROC_DIR="${TMPDIR:-/tmp}/authentik_prometheus_tmp"
fi
mkdir -p "${PROMETHEUS_MULTIPROC_DIR}"

View File

@@ -9,7 +9,7 @@
"version": "0.0.0",
"license": "MIT",
"devDependencies": {
"aws-cdk": "^2.1026.0",
"aws-cdk": "^2.1024.0",
"cross-env": "^10.0.0"
},
"engines": {
@@ -24,9 +24,9 @@
"license": "MIT"
},
"node_modules/aws-cdk": {
"version": "2.1026.0",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1026.0.tgz",
"integrity": "sha512-JdXR20s9gMHY3niweK5/D9tILLG8u2FOyJjWgSaNZGJ+pq9u0sBFxufXPO4VxJzDitGFOIW5VvQThXP+Y2VrVA==",
"version": "2.1024.0",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1024.0.tgz",
"integrity": "sha512-hY0iVT2gPX/QOQXL7RSP2sqIRI/4BYU27vSmbhZxLEj//c3pkMkd9QpIHj7gOhyWC2gf6n5JuYPw27Dgw8FEdA==",
"dev": true,
"license": "Apache-2.0",
"bin": {

View File

@@ -10,7 +10,7 @@
"node": ">=20"
},
"devDependencies": {
"aws-cdk": "^2.1026.0",
"aws-cdk": "^2.1024.0",
"cross-env": "^10.0.0"
}
}

View File

@@ -26,7 +26,7 @@ Parameters:
Description: authentik Docker image
AuthentikVersion:
Type: String
Default: 2025.10.0-rc1
Default: 2025.8.2
Description: authentik Docker image tag
AuthentikServerCPU:
Type: Number

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-08-18 00:11+0000\n"
"POT-Creation-Date: 2025-08-11 00:12+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -790,12 +790,6 @@ msgstr ""
msgid "Email"
msgstr ""
#: authentik/events/models.py
msgid ""
"Only send notification once, for example when sending a webhook into a chat "
"channel."
msgstr ""
#: authentik/events/models.py
msgid ""
"Customize the body of the request. Mapping should return data that is JSON-"
@@ -808,6 +802,12 @@ msgid ""
"of key-value pairs"
msgstr ""
#: authentik/events/models.py
msgid ""
"Only send notification once, for example when sending a webhook into a chat "
"channel."
msgstr ""
#: authentik/events/models.py
msgid "Severity"
msgstr ""
@@ -2905,6 +2905,10 @@ msgstr ""
msgid "Duo Devices"
msgstr ""
#: authentik/stages/authenticator_email/models.py
msgid "Email OTP"
msgstr ""
#: authentik/stages/authenticator_email/models.py
#: authentik/stages/email/models.py
msgid ""
@@ -3265,14 +3269,6 @@ msgstr ""
msgid "Account Confirmation"
msgstr ""
#: authentik/stages/email/models.py
msgid "Email OTP"
msgstr ""
#: authentik/stages/email/models.py
msgid "Event Notification"
msgstr ""
#: authentik/stages/email/models.py
msgid ""
"The time window used to count recent account recovery attempts. If the "

Binary file not shown.

Binary file not shown.

Binary file not shown.

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@goauthentik/authentik",
"version": "2025.10.0-rc1",
"version": "2025.8.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@goauthentik/authentik",
"version": "2025.10.0-rc1",
"version": "2025.8.2",
"dependencies": {
"@eslint/js": "^9.31.0",
"@typescript-eslint/eslint-plugin": "^8.38.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@goauthentik/authentik",
"version": "2025.10.0-rc1",
"version": "2025.8.2",
"private": true,
"type": "module",
"dependencies": {

View File

@@ -10,7 +10,8 @@
"license": "MIT",
"dependencies": {
"deepmerge-ts": "^7.1.5",
"prism-react-renderer": "^2.4.1"
"prism-react-renderer": "^2.4.1",
"react-dom": ">=18"
},
"devDependencies": {
"@docusaurus/theme-common": "^3.8.1",
@@ -34,8 +35,7 @@
"@docusaurus/theme-common": "^3.8.1",
"@docusaurus/theme-search-algolia": "^3.8.1",
"@docusaurus/types": "^3.8.0",
"react": ">=18",
"react-dom": ">=18"
"react": ">=18"
},
"peerDependenciesMeta": {
"@docusaurus/theme-search-algolia": {
@@ -43,9 +43,6 @@
},
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
},
@@ -4646,9 +4643,9 @@
"license": "MIT"
},
"node_modules/@types/react": {
"version": "19.1.10",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.10.tgz",
"integrity": "sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg==",
"version": "19.1.8",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz",
"integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==",
"dev": true,
"license": "MIT",
"dependencies": {

View File

@@ -132,9 +132,9 @@
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz",
"integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==",
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz",
"integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==",
"cpu": [
"ppc64"
],
@@ -149,9 +149,9 @@
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz",
"integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==",
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz",
"integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==",
"cpu": [
"arm"
],
@@ -166,9 +166,9 @@
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz",
"integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==",
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz",
"integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==",
"cpu": [
"arm64"
],
@@ -183,9 +183,9 @@
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz",
"integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==",
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz",
"integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==",
"cpu": [
"x64"
],
@@ -200,9 +200,9 @@
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz",
"integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==",
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz",
"integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==",
"cpu": [
"arm64"
],
@@ -217,9 +217,9 @@
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz",
"integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==",
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz",
"integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==",
"cpu": [
"x64"
],
@@ -234,9 +234,9 @@
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz",
"integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==",
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz",
"integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==",
"cpu": [
"arm64"
],
@@ -251,9 +251,9 @@
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz",
"integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==",
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz",
"integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==",
"cpu": [
"x64"
],
@@ -268,9 +268,9 @@
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz",
"integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==",
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz",
"integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==",
"cpu": [
"arm"
],
@@ -285,9 +285,9 @@
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz",
"integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==",
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz",
"integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==",
"cpu": [
"arm64"
],
@@ -302,9 +302,9 @@
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz",
"integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==",
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz",
"integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==",
"cpu": [
"ia32"
],
@@ -319,9 +319,9 @@
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz",
"integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==",
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz",
"integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==",
"cpu": [
"loong64"
],
@@ -336,9 +336,9 @@
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz",
"integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==",
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz",
"integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==",
"cpu": [
"mips64el"
],
@@ -353,9 +353,9 @@
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz",
"integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==",
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz",
"integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==",
"cpu": [
"ppc64"
],
@@ -370,9 +370,9 @@
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz",
"integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==",
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz",
"integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==",
"cpu": [
"riscv64"
],
@@ -387,9 +387,9 @@
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz",
"integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==",
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz",
"integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==",
"cpu": [
"s390x"
],
@@ -404,9 +404,9 @@
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz",
"integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==",
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz",
"integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==",
"cpu": [
"x64"
],
@@ -421,9 +421,9 @@
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz",
"integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==",
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz",
"integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==",
"cpu": [
"arm64"
],
@@ -438,9 +438,9 @@
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz",
"integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==",
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz",
"integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==",
"cpu": [
"x64"
],
@@ -455,9 +455,9 @@
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz",
"integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==",
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz",
"integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==",
"cpu": [
"arm64"
],
@@ -472,9 +472,9 @@
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz",
"integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==",
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz",
"integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==",
"cpu": [
"x64"
],
@@ -489,9 +489,9 @@
}
},
"node_modules/@esbuild/openharmony-arm64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz",
"integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==",
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz",
"integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==",
"cpu": [
"arm64"
],
@@ -506,9 +506,9 @@
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz",
"integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==",
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz",
"integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==",
"cpu": [
"x64"
],
@@ -523,9 +523,9 @@
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz",
"integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==",
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz",
"integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==",
"cpu": [
"arm64"
],
@@ -540,9 +540,9 @@
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz",
"integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==",
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz",
"integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==",
"cpu": [
"ia32"
],
@@ -557,9 +557,9 @@
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz",
"integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==",
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz",
"integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==",
"cpu": [
"x64"
],
@@ -904,9 +904,9 @@
}
},
"node_modules/@types/node": {
"version": "24.3.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz",
"integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==",
"version": "24.2.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.1.tgz",
"integrity": "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1269,9 +1269,9 @@
}
},
"node_modules/esbuild": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz",
"integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==",
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz",
"integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -1282,32 +1282,32 @@
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.25.9",
"@esbuild/android-arm": "0.25.9",
"@esbuild/android-arm64": "0.25.9",
"@esbuild/android-x64": "0.25.9",
"@esbuild/darwin-arm64": "0.25.9",
"@esbuild/darwin-x64": "0.25.9",
"@esbuild/freebsd-arm64": "0.25.9",
"@esbuild/freebsd-x64": "0.25.9",
"@esbuild/linux-arm": "0.25.9",
"@esbuild/linux-arm64": "0.25.9",
"@esbuild/linux-ia32": "0.25.9",
"@esbuild/linux-loong64": "0.25.9",
"@esbuild/linux-mips64el": "0.25.9",
"@esbuild/linux-ppc64": "0.25.9",
"@esbuild/linux-riscv64": "0.25.9",
"@esbuild/linux-s390x": "0.25.9",
"@esbuild/linux-x64": "0.25.9",
"@esbuild/netbsd-arm64": "0.25.9",
"@esbuild/netbsd-x64": "0.25.9",
"@esbuild/openbsd-arm64": "0.25.9",
"@esbuild/openbsd-x64": "0.25.9",
"@esbuild/openharmony-arm64": "0.25.9",
"@esbuild/sunos-x64": "0.25.9",
"@esbuild/win32-arm64": "0.25.9",
"@esbuild/win32-ia32": "0.25.9",
"@esbuild/win32-x64": "0.25.9"
"@esbuild/aix-ppc64": "0.25.8",
"@esbuild/android-arm": "0.25.8",
"@esbuild/android-arm64": "0.25.8",
"@esbuild/android-x64": "0.25.8",
"@esbuild/darwin-arm64": "0.25.8",
"@esbuild/darwin-x64": "0.25.8",
"@esbuild/freebsd-arm64": "0.25.8",
"@esbuild/freebsd-x64": "0.25.8",
"@esbuild/linux-arm": "0.25.8",
"@esbuild/linux-arm64": "0.25.8",
"@esbuild/linux-ia32": "0.25.8",
"@esbuild/linux-loong64": "0.25.8",
"@esbuild/linux-mips64el": "0.25.8",
"@esbuild/linux-ppc64": "0.25.8",
"@esbuild/linux-riscv64": "0.25.8",
"@esbuild/linux-s390x": "0.25.8",
"@esbuild/linux-x64": "0.25.8",
"@esbuild/netbsd-arm64": "0.25.8",
"@esbuild/netbsd-x64": "0.25.8",
"@esbuild/openbsd-arm64": "0.25.8",
"@esbuild/openbsd-x64": "0.25.8",
"@esbuild/openharmony-arm64": "0.25.8",
"@esbuild/sunos-x64": "0.25.8",
"@esbuild/win32-arm64": "0.25.8",
"@esbuild/win32-ia32": "0.25.8",
"@esbuild/win32-x64": "0.25.8"
}
},
"node_modules/escape-string-regexp": {
@@ -2728,9 +2728,9 @@
}
},
"node_modules/typedoc-plugin-markdown": {
"version": "4.8.1",
"resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-4.8.1.tgz",
"integrity": "sha512-ug7fc4j0SiJxSwBGLncpSo8tLvrT9VONvPUQqQDTKPxCoFQBADLli832RGPtj6sfSVJebNSrHZQRUdEryYH/7g==",
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-4.8.0.tgz",
"integrity": "sha512-BQqXnT9PETe6WEFf8bcsvvGEGQHbwTo/BFyY+RUIsSB05Y0Wn56iF+fK1PY2OKJJIhV4kp4dp7osaP9Bm5a0Zw==",
"dev": true,
"license": "MIT",
"engines": {

View File

@@ -502,17 +502,17 @@
"license": "MIT"
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.40.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.40.0.tgz",
"integrity": "sha512-w/EboPlBwnmOBtRbiOvzjD+wdiZdgFeo17lkltrtn7X37vagKKWJABvyfsJXTlHe6XBzugmYgd4A4nW+k8Mixw==",
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.39.0.tgz",
"integrity": "sha512-bhEz6OZeUR+O/6yx9Jk6ohX6H9JSFTaiY0v9/PuKT3oGK0rn0jNplLmyFUGV+a9gfYnVNwGDwS/UkLIuXNb2Rw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "8.40.0",
"@typescript-eslint/type-utils": "8.40.0",
"@typescript-eslint/utils": "8.40.0",
"@typescript-eslint/visitor-keys": "8.40.0",
"@typescript-eslint/scope-manager": "8.39.0",
"@typescript-eslint/type-utils": "8.39.0",
"@typescript-eslint/utils": "8.39.0",
"@typescript-eslint/visitor-keys": "8.39.0",
"graphemer": "^1.4.0",
"ignore": "^7.0.0",
"natural-compare": "^1.4.0",
@@ -526,7 +526,7 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"@typescript-eslint/parser": "^8.40.0",
"@typescript-eslint/parser": "^8.39.0",
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
@@ -542,16 +542,16 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "8.40.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.40.0.tgz",
"integrity": "sha512-jCNyAuXx8dr5KJMkecGmZ8KI61KBUhkCob+SD+C+I5+Y1FWI2Y3QmY4/cxMCC5WAsZqoEtEETVhUiUMIGCf6Bw==",
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.39.0.tgz",
"integrity": "sha512-g3WpVQHngx0aLXn6kfIYCZxM6rRJlWzEkVpqEFLT3SgEDsp9cpCbxxgwnE504q4H+ruSDh/VGS6nqZIDynP+vg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.40.0",
"@typescript-eslint/types": "8.40.0",
"@typescript-eslint/typescript-estree": "8.40.0",
"@typescript-eslint/visitor-keys": "8.40.0",
"@typescript-eslint/scope-manager": "8.39.0",
"@typescript-eslint/types": "8.39.0",
"@typescript-eslint/typescript-estree": "8.39.0",
"@typescript-eslint/visitor-keys": "8.39.0",
"debug": "^4.3.4"
},
"engines": {
@@ -567,14 +567,14 @@
}
},
"node_modules/@typescript-eslint/project-service": {
"version": "8.40.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.40.0.tgz",
"integrity": "sha512-/A89vz7Wf5DEXsGVvcGdYKbVM9F7DyFXj52lNYUDS1L9yJfqjW/fIp5PgMuEJL/KeqVTe2QSbXAGUZljDUpArw==",
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.0.tgz",
"integrity": "sha512-CTzJqaSq30V/Z2Og9jogzZt8lJRR5TKlAdXmWgdu4hgcC9Kww5flQ+xFvMxIBWVNdxJO7OifgdOK4PokMIWPew==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.40.0",
"@typescript-eslint/types": "^8.40.0",
"@typescript-eslint/tsconfig-utils": "^8.39.0",
"@typescript-eslint/types": "^8.39.0",
"debug": "^4.3.4"
},
"engines": {
@@ -589,14 +589,14 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "8.40.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.40.0.tgz",
"integrity": "sha512-y9ObStCcdCiZKzwqsE8CcpyuVMwRouJbbSrNuThDpv16dFAj429IkM6LNb1dZ2m7hK5fHyzNcErZf7CEeKXR4w==",
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.0.tgz",
"integrity": "sha512-8QOzff9UKxOh6npZQ/4FQu4mjdOCGSdO3p44ww0hk8Vu+IGbg0tB/H1LcTARRDzGCC8pDGbh2rissBuuoPgH8A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.40.0",
"@typescript-eslint/visitor-keys": "8.40.0"
"@typescript-eslint/types": "8.39.0",
"@typescript-eslint/visitor-keys": "8.39.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -607,9 +607,9 @@
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.40.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.40.0.tgz",
"integrity": "sha512-jtMytmUaG9d/9kqSl/W3E3xaWESo4hFDxAIHGVW/WKKtQhesnRIJSAJO6XckluuJ6KDB5woD1EiqknriCtAmcw==",
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.0.tgz",
"integrity": "sha512-Fd3/QjmFV2sKmvv3Mrj8r6N8CryYiCS8Wdb/6/rgOXAWGcFuc+VkQuG28uk/4kVNVZBQuuDHEDUpo/pQ32zsIQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -624,15 +624,15 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.40.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.40.0.tgz",
"integrity": "sha512-eE60cK4KzAc6ZrzlJnflXdrMqOBaugeukWICO2rB0KNvwdIMaEaYiywwHMzA1qFpTxrLhN9Lp4E/00EgWcD3Ow==",
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.39.0.tgz",
"integrity": "sha512-6B3z0c1DXVT2vYA9+z9axjtc09rqKUPRmijD5m9iv8iQpHBRYRMBcgxSiKTZKm6FwWw1/cI4v6em35OsKCiN5Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.40.0",
"@typescript-eslint/typescript-estree": "8.40.0",
"@typescript-eslint/utils": "8.40.0",
"@typescript-eslint/types": "8.39.0",
"@typescript-eslint/typescript-estree": "8.39.0",
"@typescript-eslint/utils": "8.39.0",
"debug": "^4.3.4",
"ts-api-utils": "^2.1.0"
},
@@ -649,9 +649,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "8.40.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.40.0.tgz",
"integrity": "sha512-ETdbFlgbAmXHyFPwqUIYrfc12ArvpBhEVgGAxVYSwli26dn8Ko+lIo4Su9vI9ykTZdJn+vJprs/0eZU0YMAEQg==",
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.0.tgz",
"integrity": "sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -663,16 +663,16 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "8.40.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.40.0.tgz",
"integrity": "sha512-k1z9+GJReVVOkc1WfVKs1vBrR5MIKKbdAjDTPvIK3L8De6KbFfPFt6BKpdkdk7rZS2GtC/m6yI5MYX+UsuvVYQ==",
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.0.tgz",
"integrity": "sha512-ndWdiflRMvfIgQRpckQQLiB5qAKQ7w++V4LlCHwp62eym1HLB/kw7D9f2e8ytONls/jt89TEasgvb+VwnRprsw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.40.0",
"@typescript-eslint/tsconfig-utils": "8.40.0",
"@typescript-eslint/types": "8.40.0",
"@typescript-eslint/visitor-keys": "8.40.0",
"@typescript-eslint/project-service": "8.39.0",
"@typescript-eslint/tsconfig-utils": "8.39.0",
"@typescript-eslint/types": "8.39.0",
"@typescript-eslint/visitor-keys": "8.39.0",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
@@ -731,16 +731,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "8.40.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.40.0.tgz",
"integrity": "sha512-Cgzi2MXSZyAUOY+BFwGs17s7ad/7L+gKt6Y8rAVVWS+7o6wrjeFN4nVfTpbE25MNcxyJ+iYUXflbs2xR9h4UBg==",
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.39.0.tgz",
"integrity": "sha512-4GVSvNA0Vx1Ktwvf4sFE+exxJ3QGUorQG1/A5mRfRNZtkBT2xrA/BCO2H0eALx/PnvCS6/vmYwRdDA41EoffkQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.7.0",
"@typescript-eslint/scope-manager": "8.40.0",
"@typescript-eslint/types": "8.40.0",
"@typescript-eslint/typescript-estree": "8.40.0"
"@typescript-eslint/scope-manager": "8.39.0",
"@typescript-eslint/types": "8.39.0",
"@typescript-eslint/typescript-estree": "8.39.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -755,13 +755,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "8.40.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.40.0.tgz",
"integrity": "sha512-8CZ47QwalyRjsypfwnbI3hKy5gJDPmrkLjkgMxhi0+DZZ2QNx2naS6/hWoVYUHU7LU2zleF68V9miaVZvhFfTA==",
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.0.tgz",
"integrity": "sha512-ldgiJ+VAhQCfIjeOgu8Kj5nSxds0ktPOSO9p4+0VDH2R2pLvQraaM5Oen2d7NxzMCm+Sn/vJT+mv2H5u6b/3fA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.40.0",
"@typescript-eslint/types": "8.39.0",
"eslint-visitor-keys": "^4.2.1"
},
"engines": {
@@ -4709,16 +4709,16 @@
}
},
"node_modules/typescript-eslint": {
"version": "8.40.0",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.40.0.tgz",
"integrity": "sha512-Xvd2l+ZmFDPEt4oj1QEXzA4A2uUK6opvKu3eGN9aGjB8au02lIVcLyi375w94hHyejTOmzIU77L8ol2sRg9n7Q==",
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.39.0.tgz",
"integrity": "sha512-lH8FvtdtzcHJCkMOKnN73LIn6SLTpoojgJqDAxPm1jCR14eWSGPX8ul/gggBdPMk/d5+u9V854vTYQ8T5jF/1Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/eslint-plugin": "8.40.0",
"@typescript-eslint/parser": "8.40.0",
"@typescript-eslint/typescript-estree": "8.40.0",
"@typescript-eslint/utils": "8.40.0"
"@typescript-eslint/eslint-plugin": "8.39.0",
"@typescript-eslint/parser": "8.39.0",
"@typescript-eslint/typescript-estree": "8.39.0",
"@typescript-eslint/utils": "8.39.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"

View File

@@ -385,9 +385,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "24.3.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz",
"integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==",
"version": "24.2.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.1.tgz",
"integrity": "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==",
"dev": true,
"license": "MIT",
"dependencies": {

View File

@@ -17,7 +17,7 @@ COPY web .
RUN npm run build-proxy
# Stage 2: Build
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25-bookworm AS builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.24-bookworm AS builder
ARG TARGETOS
ARG TARGETARCH

View File

@@ -1,18 +1,18 @@
[project]
name = "authentik"
version = "2025.10.0-rc1"
version = "2025.8.2"
description = ""
authors = [{ name = "authentik Team", email = "hello@goauthentik.io" }]
requires-python = "==3.13.*"
dependencies = [
"argon2-cffi==25.1.0",
"channels==4.3.1",
"channels==4.3.0",
"channels-redis==4.3.0",
"cryptography==45.0.5",
"dacite==1.9.2",
"deepmerge==2.0",
"defusedxml==0.7.1",
"django==5.1.11",
"django==5.1.12",
"django-countries==7.6.1",
"django-cte==2.0.0",
"django-dramatiq-postgres",
@@ -79,7 +79,7 @@ dev = [
"aws-cdk-lib==2.188.0",
"bandit==1.8.3",
"black==25.1.0",
"channels[daphne]==4.3.1",
"channels[daphne]==4.3.0",
"codespell==2.4.1",
"colorama==0.4.6",
"constructs==10.4.2",

View File

@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1
# Stage 1: Build
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25-bookworm AS builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.24-bookworm AS builder
ARG TARGETOS
ARG TARGETARCH

View File

@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1
# Stage 1: Build
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25-bookworm AS builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.24-bookworm AS builder
ARG TARGETOS
ARG TARGETARCH

View File

@@ -1,7 +1,7 @@
openapi: 3.0.3
info:
title: authentik
version: 2025.10.0-rc1
version: 2025.8.2
description: Making authentication simple.
contact:
email: hello@goauthentik.io
@@ -61879,6 +61879,7 @@ components:
- object_pk
UserPasswordSetRequest:
type: object
description: Payload to set a users' password directly
properties:
password:
type: string

View File

@@ -1,13 +1,13 @@
services:
chrome:
platform: linux/x86_64
image: docker.io/selenium/standalone-chrome:139.0
image: docker.io/selenium/standalone-chrome:138.0
volumes:
- /dev/shm:/dev/shm
network_mode: host
restart: always
mailpit:
image: docker.io/axllent/mailpit:v1.27.6
image: docker.io/axllent/mailpit:v1.27.4
ports:
- 1025:1025
- 8025:8025

View File

@@ -10,7 +10,6 @@ from docker.types.healthcheck import Healthcheck
from authentik.core.tests.utils import create_test_flow
from authentik.crypto.models import CertificateKeyPair
from authentik.lib.config import CONFIG
from authentik.outposts.models import (
DockerServiceConnection,
Outpost,
@@ -89,7 +88,6 @@ class TestProxyDocker(DockerTestCase, ChannelsLiveServerTestCase):
pass
@pytest.mark.timeout(120)
@CONFIG.patch("outposts.container_image_base", "ghcr.io/goauthentik/dev-proxy:gh-main")
def test_docker_controller(self):
"""test that deployment requires update"""
controller = DockerController(self.outpost, self.service_connection)

34
uv.lock generated
View File

@@ -1,5 +1,5 @@
version = 1
revision = 2
revision = 3
requires-python = "==3.13.*"
[manifest]
@@ -159,7 +159,7 @@ wheels = [
[[package]]
name = "authentik"
version = "2025.10.0rc1"
version = "2025.8.2"
source = { editable = "." }
dependencies = [
{ name = "argon2-cffi" },
@@ -260,13 +260,13 @@ dev = [
[package.metadata]
requires-dist = [
{ name = "argon2-cffi", specifier = "==25.1.0" },
{ name = "channels", specifier = "==4.3.1" },
{ name = "channels", specifier = "==4.3.0" },
{ name = "channels-redis", specifier = "==4.3.0" },
{ name = "cryptography", specifier = "==45.0.5" },
{ name = "dacite", specifier = "==1.9.2" },
{ name = "deepmerge", specifier = "==2.0" },
{ name = "defusedxml", specifier = "==0.7.1" },
{ name = "django", specifier = "==5.1.11" },
{ name = "django", specifier = "==5.1.12" },
{ name = "django-countries", specifier = "==7.6.1" },
{ name = "django-cte", specifier = "==2.0.0" },
{ name = "django-dramatiq-postgres", editable = "packages/django-dramatiq-postgres" },
@@ -333,7 +333,7 @@ dev = [
{ name = "aws-cdk-lib", specifier = "==2.188.0" },
{ name = "bandit", specifier = "==1.8.3" },
{ name = "black", specifier = "==25.1.0" },
{ name = "channels", extras = ["daphne"], specifier = "==4.3.1" },
{ name = "channels", extras = ["daphne"], specifier = "==4.3.0" },
{ name = "codespell", specifier = "==2.4.1" },
{ name = "colorama", specifier = "==0.4.6" },
{ name = "constructs", specifier = "==10.4.2" },
@@ -652,15 +652,15 @@ wheels = [
[[package]]
name = "channels"
version = "4.3.1"
version = "4.3.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "asgiref" },
{ name = "django" },
]
sdist = { url = "https://files.pythonhosted.org/packages/12/a0/46450fcf9e56af18a6b0440ba49db6635419bb7bc84142c35f4143b1a66c/channels-4.3.1.tar.gz", hash = "sha256:97413ffd674542db08e16a9ef09cd86ec0113e5f8125fbd33cf0854adcf27cdb", size = 26896, upload-time = "2025-08-01T13:25:19.952Z" }
sdist = { url = "https://files.pythonhosted.org/packages/72/04/6768c7a887f9c593c4d49f99130c8aec4ea06e750bc17c306b689f6caf3b/channels-4.3.0.tar.gz", hash = "sha256:7db32c61dcd88eada1647e6c6f6ad2eb724b75d4852eeff26ad1c51ccd1a37f7", size = 26816, upload-time = "2025-07-28T13:52:50.334Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/89/1c/eae1c2a8c195760376e7f65d0bdcc3e966695d29cfbe5c54841ce5c71408/channels-4.3.1-py3-none-any.whl", hash = "sha256:b091d4b26f91d807de3e84aead7ba785314f27eaf5bac31dd51b1c956b883859", size = 31286, upload-time = "2025-08-01T13:25:18.845Z" },
{ url = "https://files.pythonhosted.org/packages/7c/59/0866202ee593e1b0dab0b472ebb8169e1b2b7886ad3008d193da2bbe10cb/channels-4.3.0-py3-none-any.whl", hash = "sha256:0497f3affb95e621b37d6bae1b6a5d9e8e1e1221007a2566f280091cf30ffcce", size = 31238, upload-time = "2025-07-28T13:52:49.117Z" },
]
[package.optional-dependencies]
@@ -899,16 +899,16 @@ wheels = [
[[package]]
name = "django"
version = "5.1.11"
version = "5.1.12"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "asgiref" },
{ name = "sqlparse" },
{ name = "tzdata", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/83/80/bf0f9b0aa434fca2b46fc6a31c39b08ea714b87a0a72a16566f053fb05a8/django-5.1.11.tar.gz", hash = "sha256:3bcdbd40e4d4623b5e04f59c28834323f3086df583058e65ebce99f9982385ce", size = 10734926, upload-time = "2025-06-10T10:12:48.229Z" }
sdist = { url = "https://files.pythonhosted.org/packages/f0/99/a951d93a27a5bc59fb96edbcdbc03fb9bfac51177f1bc0110888de85af3f/django-5.1.12.tar.gz", hash = "sha256:8a8991b1ec052ef6a44fefd1ef336ab8daa221287bcb91a4a17d5e1abec5bbcc", size = 10737777, upload-time = "2025-09-03T13:09:45.855Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/59/91/2972ce330c6c0bd5b3200d4c2ad5cbf47eecff5243220c5a56444d3267a0/django-5.1.11-py3-none-any.whl", hash = "sha256:e48091f364007068728aca938e7450fbfe3f2217079bfd2b8af45122585acf64", size = 8277453, upload-time = "2025-06-10T10:12:42.236Z" },
{ url = "https://files.pythonhosted.org/packages/1e/1c/a9520c8263e980b0b9933c9b5ce8f22c9ddf007b062e4eb428b557ff0932/django-5.1.12-py3-none-any.whl", hash = "sha256:9eb695636cea3601b65690f1596993c042206729afb320ca0960b55f8ed4477b", size = 8277454, upload-time = "2025-09-03T13:09:30.997Z" },
]
[[package]]
@@ -1499,15 +1499,15 @@ wheels = [
[[package]]
name = "h2"
version = "4.2.0"
version = "4.3.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "hpack" },
{ name = "hyperframe" },
]
sdist = { url = "https://files.pythonhosted.org/packages/1b/38/d7f80fd13e6582fb8e0df8c9a653dcc02b03ca34f4d72f34869298c5baf8/h2-4.2.0.tar.gz", hash = "sha256:c8a52129695e88b1a0578d8d2cc6842bbd79128ac685463b887ee278126ad01f", size = 2150682, upload-time = "2025-02-02T07:43:51.815Z" }
sdist = { url = "https://files.pythonhosted.org/packages/1d/17/afa56379f94ad0fe8defd37d6eb3f89a25404ffc71d4d848893d270325fc/h2-4.3.0.tar.gz", hash = "sha256:6c59efe4323fa18b47a632221a1888bd7fde6249819beda254aeca909f221bf1", size = 2152026, upload-time = "2025-08-23T18:12:19.778Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d0/9e/984486f2d0a0bd2b024bf4bc1c62688fcafa9e61991f041fb0e2def4a982/h2-4.2.0-py3-none-any.whl", hash = "sha256:479a53ad425bb29af087f3458a61d30780bc818e4ebcf01f0b536ba916462ed0", size = 60957, upload-time = "2025-02-01T11:02:26.481Z" },
{ url = "https://files.pythonhosted.org/packages/69/b2/119f6e6dcbd96f9069ce9a2665e0146588dc9f88f29549711853645e736a/h2-4.3.0-py3-none-any.whl", hash = "sha256:c438f029a25f7945c69e0ccf0fb951dc3f73a5f6412981daee861431b70e2bdd", size = 61779, upload-time = "2025-08-23T18:12:17.779Z" },
]
[[package]]
@@ -2736,11 +2736,11 @@ wheels = [
[[package]]
name = "redis"
version = "6.3.0"
version = "6.2.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/21/cd/030274634a1a052b708756016283ea3d84e91ae45f74d7f5dcf55d753a0f/redis-6.3.0.tar.gz", hash = "sha256:3000dbe532babfb0999cdab7b3e5744bcb23e51923febcfaeb52c8cfb29632ef", size = 4647275, upload-time = "2025-08-05T08:12:31.648Z" }
sdist = { url = "https://files.pythonhosted.org/packages/ea/9a/0551e01ba52b944f97480721656578c8a7c46b51b99d66814f85fe3a4f3e/redis-6.2.0.tar.gz", hash = "sha256:e821f129b75dde6cb99dd35e5c76e8c49512a5a0d8dfdc560b2fbd44b85ca977", size = 4639129, upload-time = "2025-05-28T05:01:18.91Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/df/a7/2fe45801534a187543fc45d28b3844d84559c1589255bc2ece30d92dc205/redis-6.3.0-py3-none-any.whl", hash = "sha256:92f079d656ded871535e099080f70fab8e75273c0236797126ac60242d638e9b", size = 280018, upload-time = "2025-08-05T08:12:30.093Z" },
{ url = "https://files.pythonhosted.org/packages/13/67/e60968d3b0e077495a8fee89cf3f2373db98e528288a48f1ee44967f6e8c/redis-6.2.0-py3-none-any.whl", hash = "sha256:c8ddf316ee0aab65f04a11229e94a64b2618451dab7a67cb2f77eb799d872d5e", size = 278659, upload-time = "2025-05-28T05:01:16.955Z" },
]
[[package]]

1309
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "@goauthentik/web",
"version": "2025.10.0-rc1",
"version": "2025.8.2",
"license": "MIT",
"private": true,
"scripts": {
@@ -111,20 +111,20 @@
"@open-wc/lit-helpers": "^0.7.0",
"@openlayers-elements/core": "^0.4.0",
"@openlayers-elements/maps": "^0.4.0",
"@patternfly/elements": "^4.2.0",
"@patternfly/elements": "^4.1.0",
"@patternfly/patternfly": "^4.224.2",
"@sentry/browser": "^10.5.0",
"@sentry/browser": "^10.3.0",
"@spotlightjs/spotlight": "^3.0.2",
"@storybook/addon-docs": "^9.1.2",
"@storybook/addon-links": "^9.1.2",
"@storybook/web-components": "^9.1.2",
"@storybook/web-components-vite": "^9.1.2",
"@storybook/addon-docs": "^9.1.1",
"@storybook/addon-links": "^9.1.1",
"@storybook/web-components": "^9.1.1",
"@storybook/web-components-vite": "^9.1.1",
"@types/codemirror": "^5.60.16",
"@types/grecaptcha": "^3.0.9",
"@types/guacamole-common-js": "^1.5.4",
"@types/guacamole-common-js": "^1.5.3",
"@types/mocha": "^10.0.10",
"@types/node": "^24.3.0",
"@types/react": "^19.1.10",
"@types/node": "^24.2.1",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.7",
"@typescript-eslint/eslint-plugin": "^8.38.0",
"@typescript-eslint/parser": "^8.38.0",
@@ -135,12 +135,12 @@
"chartjs-adapter-date-fns": "^3.0.0",
"codemirror": "^6.0.2",
"construct-style-sheets-polyfill": "^3.1.0",
"core-js": "^3.45.1",
"core-js": "^3.44.0",
"country-flag-icons": "^1.5.19",
"date-fns": "^4.1.0",
"deepmerge-ts": "^7.1.5",
"dompurify": "^3.2.6",
"esbuild": "^0.25.9",
"esbuild": "^0.25.8",
"esbuild-plugin-copy": "^2.1.1",
"esbuild-plugin-polyfill-node": "^0.3.0",
"esbuild-plugins-node-modules-polyfill": "^1.7.1",
@@ -156,7 +156,7 @@
"lit": "^3.3.1",
"lit-analyzer": "^2.0.3",
"md-front-matter": "^1.0.4",
"mermaid": "^11.10.0",
"mermaid": "^11.9.0",
"npm-run-all": "^4.1.5",
"prettier": "^3.6.2",
"pseudolocale": "^2.1.0",
@@ -177,7 +177,7 @@
"ts-pattern": "^5.8.0",
"turnstile-types": "^1.2.3",
"typescript": "^5.8.3",
"typescript-eslint": "^8.40.0",
"typescript-eslint": "^8.39.0",
"unist-util-visit": "^5.0.0",
"webcomponent-qr-code": "^1.3.0",
"wireit": "^0.14.12",
@@ -187,14 +187,14 @@
"@esbuild/darwin-arm64": "^0.25.4",
"@esbuild/linux-arm64": "^0.25.4",
"@esbuild/linux-x64": "^0.25.4",
"@rollup/rollup-darwin-arm64": "^4.46.3",
"@rollup/rollup-linux-arm64-gnu": "^4.46.3",
"@rollup/rollup-linux-x64-gnu": "^4.46.3",
"@wdio/browser-runner": "^9.19.1",
"@wdio/cli": "^9.19.1",
"@wdio/spec-reporter": "^9.19.1",
"@rollup/rollup-darwin-arm64": "^4.46.2",
"@rollup/rollup-linux-arm64-gnu": "^4.46.2",
"@rollup/rollup-linux-x64-gnu": "^4.46.2",
"@wdio/browser-runner": "^9.19.0",
"@wdio/cli": "^9.19.0",
"@wdio/spec-reporter": "^9.19.0",
"@web/test-runner": "^0.20.2",
"chromedriver": "^139.0.1"
"chromedriver": "^139.0.0"
},
"wireit": {
"build": {

View File

@@ -47,7 +47,7 @@
"dependencies": {
"@goauthentik/prettier-config": "^3.1.0",
"@goauthentik/tsconfig": "^1.0.4",
"@types/node": "^24.3.0",
"@types/node": "^24.2.1",
"prettier": "^3.6.2",
"typescript": "^5.8.3"
},

View File

@@ -10,7 +10,7 @@
"watch": "rollup -w -c rollup.config.mjs --bundleConfigAsCjs"
},
"dependencies": {
"@goauthentik/api": "^2025.10.0-rc1-1755254677",
"@goauthentik/api": "^2024.6.0-1719577139",
"@goauthentik/core": "^1.0.0",
"@rollup/plugin-commonjs": "^28.0.6",
"@rollup/plugin-node-resolve": "^16.0.1",
@@ -23,7 +23,7 @@
"formdata-polyfill": "^4.0.10",
"jquery": "^3.7.1",
"prettier": "^3.5.3",
"rollup": "^4.46.3",
"rollup": "^4.46.2",
"rollup-plugin-copy": "^3.5.0",
"weakmap-polyfill": "^2.0.4"
},

View File

@@ -48,11 +48,6 @@ const BASE_ESBUILD_OPTIONS = {
plugins: [
copy({
assets: [
{
from: path.join(path.dirname(EntryPoint.StandaloneLoading.in), "startup", "**"),
to: path.dirname(EntryPoint.StandaloneLoading.out),
},
{
from: path.join(patternflyPath, "patternfly.min.css"),
to: ".",

View File

@@ -51,6 +51,8 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
}
async send(settingsRequest: SettingsRequest): Promise<Settings> {
settingsRequest.flags ??= this.settings.flags;
const result = await new AdminApi(DEFAULT_CONFIG).adminSettingsUpdate({
settingsRequest,
});
@@ -252,7 +254,7 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
<ak-form-element-horizontal label=${msg("Flags")} name="flags" required>
<ak-codemirror
mode=${CodeMirrorMode.YAML}
value="${YAML.stringify(settings?.flags ?? {})}"
value="${YAML.stringify(settings.flags ?? {})}"
>
</ak-codemirror>
<p class="pf-c-form__helper-text">

View File

@@ -5,10 +5,6 @@ import { groupBy } from "#common/utils";
import { AKElement } from "#elements/Base";
import { AKLabel } from "#components/ak-label";
import { IDGenerator } from "#packages/core/id";
import { Provider, ProvidersAllListRequest, ProvidersApi } from "@goauthentik/api";
import { html, nothing } from "lit";
@@ -42,13 +38,11 @@ export class AkProviderInput extends AKElement {
return this;
}
//#region Properties
@property({ type: String })
name!: string;
@property({ type: String })
label?: string;
label = "";
@property({ type: Number })
value?: number;
@@ -66,26 +60,14 @@ export class AkProviderInput extends AKElement {
super();
this.selected = this.selected.bind(this);
}
/**
* A unique ID to associate with the input and label.
* @property
*/
@property({ type: String, reflect: false })
public fieldID?: string = IDGenerator.elementID().toString();
selected(item: Provider) {
return this.value !== undefined && this.value === item.pk;
}
//#endregion
render() {
return html` <ak-form-element-horizontal name=${this.name}>
<div slot="label" class="pf-c-form__group-label">
${AKLabel({ htmlFor: this.fieldID, required: this.required }, this.label)}
</div>
return html` <ak-form-element-horizontal label=${this.label} name=${this.name}>
<ak-search-select
.fieldID=${this.fieldID}
.selected=${this.selected}
.fetchObjects=${fetch}
.renderElement=${renderElement}

View File

@@ -44,7 +44,6 @@ export class BrandForm extends ModelForm<Brand, string> {
}
async send(data: Brand): Promise<Brand> {
data.attributes ??= {};
if (this.instance?.brandUuid) {
return new CoreApi(DEFAULT_CONFIG).coreBrandsUpdate({
brandUuid: this.instance.brandUuid,

View File

@@ -53,7 +53,6 @@ export class GroupForm extends ModelForm<Group, string> {
}
async send(data: Group): Promise<Group> {
data.attributes ??= {};
if (this.instance?.pk) {
return new CoreApi(DEFAULT_CONFIG).coreGroupsPartialUpdate({
groupUuid: this.instance.pk,
@@ -146,7 +145,7 @@ export class GroupForm extends ModelForm<Group, string> {
)}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal label=${msg("Attributes")} name="attributes">
<ak-form-element-horizontal label=${msg("Attributes")} required name="attributes">
<ak-codemirror
mode=${CodeMirrorMode.YAML}
value="${YAML.stringify(this.instance?.attributes ?? {})}"

View File

@@ -1,8 +1,4 @@
import { APIError } from "#common/errors/network";
import { MessageLevel } from "#common/messages";
import { ModelForm } from "#elements/forms/ModelForm";
import { APIMessage } from "#elements/messages/Message";
import { msg } from "@lit/localize";
@@ -12,14 +8,4 @@ export abstract class BaseProviderForm<T> extends ModelForm<T, number> {
? msg("Successfully updated provider.")
: msg("Successfully created provider.");
}
protected override formatAPIErrorMessage(error: APIError): APIMessage {
return {
level: MessageLevel.error,
...super.formatAPIErrorMessage(error),
message: this.instance
? msg("An error occurred while updating the provider.")
: msg("An error occurred while creating the provider."),
};
}
}

View File

@@ -195,7 +195,7 @@ export function renderForm(
.redirectURI=${redirectURI}
name="oauth2-redirect-uri"
style="width: 100%"
input-id="redirect-uri-${idx}"
inputID="redirect-uri-${idx}"
></ak-provider-oauth2-redirect-uri>`;
}}
>

View File

@@ -71,10 +71,10 @@ export class SAMLProviderViewPage extends AKElement {
metadata?: SAMLMetadata;
@state()
signer: CertificateKeyPair | null = null;
signer?: CertificateKeyPair;
@state()
verifier: CertificateKeyPair | null = null;
verifier?: CertificateKeyPair;
@state()
previewUser?: User;
@@ -97,7 +97,7 @@ export class SAMLProviderViewPage extends AKElement {
super();
this.addEventListener(EVENT_REFRESH, () => {
if (!this.provider?.pk) return;
this.fetchProvider(this.provider.pk);
this.providerID = this.provider?.pk;
});
}
@@ -117,32 +117,20 @@ export class SAMLProviderViewPage extends AKElement {
}
fetchSigningCertificate(kpUuid: string) {
this.fetchCertificate(kpUuid).then((kp) => {
this.signer = kp;
this.requestUpdate("signer");
});
this.fetchCertificate(kpUuid).then((kp) => (this.signer = kp));
}
fetchVerificationCertificate(kpUuid: string) {
this.fetchCertificate(kpUuid).then((kp) => {
this.verifier = kp;
this.requestUpdate("verifier");
});
this.fetchCertificate(kpUuid).then((kp) => (this.verifier = kp));
}
fetchProvider(id: number) {
new ProvidersApi(DEFAULT_CONFIG).providersSamlRetrieve({ id }).then((prov) => {
this.provider = prov;
// Clear existing signing certificate if the provider has none
if (!this.provider.signingKp) {
this.signer = null;
} else {
if (this.provider.signingKp) {
this.fetchSigningCertificate(this.provider.signingKp);
}
// Clear existing verification certificate if the provider has none
if (!this.provider.verificationKp) {
this.verifier = null;
} else {
if (this.provider.verificationKp) {
this.fetchVerificationCertificate(this.provider.verificationKp);
}
});

View File

@@ -180,7 +180,11 @@ export class UserForm extends ModelForm<User, number> {
required
/>
</ak-form-element-horizontal>
<ak-form-element-horizontal label=${msg("Attributes")} name="attributes">
<ak-form-element-horizontal
label=${msg("Attributes")}
?required=${false}
name="attributes"
>
<ak-codemirror
mode=${CodeMirrorMode.YAML}
value="${YAML.stringify(

View File

@@ -1,29 +1,20 @@
import "#components/ak-text-input";
import { DEFAULT_CONFIG } from "#common/api/config";
import { MessageLevel } from "#common/messages";
import { globalAK } from "#common/global";
import { Form } from "#elements/forms/Form";
import { APIMessage } from "#elements/messages/Message";
import { CoreApi, ImpersonationRequest } from "@goauthentik/api";
import { msg, str } from "@lit/localize";
import { msg } from "@lit/localize";
import { html, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators.js";
@customElement("ak-user-impersonate-form")
export class UserImpersonateForm extends Form<ImpersonationRequest> {
@property({ type: Number })
public instancePk?: number;
protected override formatAPISuccessMessage(): APIMessage | null {
return {
level: MessageLevel.success,
message: msg(str`Impersonating user...`),
description: msg("This may take a few seconds."),
};
}
instancePk?: number;
async send(data: ImpersonationRequest): Promise<void> {
return new CoreApi(DEFAULT_CONFIG)
@@ -32,7 +23,7 @@ export class UserImpersonateForm extends Form<ImpersonationRequest> {
impersonationRequest: data,
})
.then(() => {
window.location.reload();
window.location.href = globalAK().api.base;
});
}
@@ -40,12 +31,7 @@ export class UserImpersonateForm extends Form<ImpersonationRequest> {
return html`<ak-text-input
name="reason"
label=${msg("Reason")}
autocomplete="off"
placeholder=${msg("Reason for impersonating the user")}
help=${msg(
"A brief explanation of why you are impersonating the user. This will be included in audit logs.",
)}
required
help=${msg("Reason for impersonating the user")}
></ak-text-input>`;
}
}

View File

@@ -6,8 +6,6 @@ import {
ValidationErrorFromJSON,
} from "@goauthentik/api";
import { sentenceCase } from "change-case";
//#region HTTP
/**
@@ -235,25 +233,3 @@ export async function parseAPIResponseError<T extends APIError = APIError>(
}
//#endregion
//#region Validation errors
/**
* Pluck a field error from a validation error.
*
* This is used to create a fallback error message when the API returns
* a validation error that isn't associated with field within the form.
*
* We can still show the error message, to at least give the user some feedback.
*/
export function pluckFallbackFieldErrors(parsedError: APIError): string[] {
for (const [fieldName, fieldErrors] of Object.entries(parsedError)) {
if (Array.isArray(fieldErrors)) {
return [`${sentenceCase(fieldName)}: ${fieldErrors.join(", ")}`];
}
}
return [];
}
//#endregion

View File

@@ -5,12 +5,12 @@ import { SlottedTemplateResult } from "../elements/types";
import { AKElement, type AKElementProps } from "#elements/Base";
import { ErrorProp } from "#components/ak-field-errors";
import { AKLabel } from "#components/ak-label";
import { IDGenerator } from "@goauthentik/core/id";
import { html, nothing, TemplateResult } from "lit";
import { property } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
export interface HorizontalLightComponentProps<T> extends AKElementProps {
name: string;
@@ -40,8 +40,6 @@ export abstract class HorizontalLightComponent<T>
return this;
}
//#region Properties
/**
* The name attribute for the form element
* @property
@@ -63,7 +61,7 @@ export abstract class HorizontalLightComponent<T>
* @attribute
*/
@property({ type: Boolean, reflect: true })
public required?: boolean;
required = false;
/**
* Help text to display below the form element. Optional
@@ -98,9 +96,10 @@ export abstract class HorizontalLightComponent<T>
* @property
*/
@property({ attribute: false })
public errorMessages?: ErrorProp[];
errorMessages: string[] = [];
/**
* @attribute
* @property
*/
@property({ attribute: false })
@@ -115,21 +114,11 @@ export abstract class HorizontalLightComponent<T>
@property({ type: String, attribute: "input-hint" })
inputHint?: string;
/**
* A unique ID to associate with the input and label.
* @property
*/
@property({ type: String, reflect: false })
public fieldID?: string = IDGenerator.elementID().toString();
protected renderControl() {
throw new Error("Must be implemented in a subclass");
}
//#endregion
//#region Rendering
/**
* Render the control element, e.g. an input, textarea, select, etc.
*/
protected abstract renderControl(): SlottedTemplateResult;
protected fieldID = IDGenerator.elementID().toString();
protected renderHelp(): SlottedTemplateResult | SlottedTemplateResult[] {
const bigHelp: SlottedTemplateResult[] = Array.isArray(this.bighelp)
@@ -144,19 +133,14 @@ export abstract class HorizontalLightComponent<T>
render() {
return html`<ak-form-element-horizontal
.fieldID=${this.fieldID}
fieldID=${this.fieldID}
label=${ifDefined(this.label)}
?required=${this.required}
?hidden=${this.hidden}
name=${this.name}
.errorMessages=${this.errorMessages}
>
<div slot="label" class="pf-c-form__group-label">
${AKLabel({ htmlFor: this.fieldID, required: this.required }, this.label)}
</div>
${this.renderControl()} ${this.renderHelp()}
</ak-form-element-horizontal> `;
}
//#endregion
}

View File

@@ -125,7 +125,6 @@ export class AkSlugInput extends HorizontalLightComponent<string> {
public override renderControl() {
return html`<input
id=${ifDefined(this.fieldID)}
@input=${(ev: Event) => this.handleTouch(ev)}
type="text"
value=${ifDefined(this.value)}

View File

@@ -17,7 +17,6 @@ export class AkTextareaInput extends HorizontalLightComponent<string> {
// Prevent the leading spaces added by Prettier's whitespace algo
// prettier-ignore
return html`<textarea
id=${ifDefined(this.fieldID)}
@input=${setValue}
class="pf-c-form-control"
?required=${this.required}

View File

@@ -1,10 +1,5 @@
import { EVENT_REFRESH } from "#common/constants";
import {
APIError,
parseAPIResponseError,
pluckErrorDetail,
pluckFallbackFieldErrors,
} from "#common/errors/network";
import { parseAPIResponseError, pluckErrorDetail } from "#common/errors/network";
import { MessageLevel } from "#common/messages";
import { dateToUTC } from "#common/temporal";
@@ -13,18 +8,17 @@ import { AKElement } from "#elements/Base";
import { reportValidityDeep } from "#elements/forms/FormGroup";
import { PreventFormSubmit } from "#elements/forms/helpers";
import { HorizontalFormElement } from "#elements/forms/HorizontalFormElement";
import { APIMessage } from "#elements/messages/Message";
import { showMessage } from "#elements/messages/MessageContainer";
import { SlottedTemplateResult } from "#elements/types";
import { createFileMap, isNamedElement, NamedElement } from "#elements/utils/inputs";
import { ErrorProp } from "#components/ak-field-errors";
import { instanceOfValidationError, ValidationError } from "@goauthentik/api";
import { instanceOfValidationError } from "@goauthentik/api";
import { snakeCase } from "change-case";
import { msg } from "@lit/localize";
import { msg, str } from "@lit/localize";
import { css, CSSResult, html, nothing, TemplateResult } from "lit";
import { property, state } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
@@ -149,41 +143,6 @@ export function serializeForm<T = Record<string, unknown>>(elements: Iterable<AK
return json as unknown as T;
}
//#region Validation Reporting
/**
* Assign all input-related errors to their respective elements.
*/
function reportInvalidFields(
parsedError: ValidationError,
elements: Iterable<HorizontalFormElement>,
): HorizontalFormElement[] {
const invalidFields: HorizontalFormElement[] = [];
for (const element of elements) {
element.requestUpdate();
const elementName = element.name;
if (!elementName) continue;
const snakeProperty = snakeCase(elementName);
const errorMessages: ErrorProp[] = parsedError[snakeProperty] ?? [];
element.errorMessages = errorMessages;
if (Array.isArray(errorMessages) && errorMessages.length) {
invalidFields.push(element);
}
}
return invalidFields;
}
//#endregion
//#region Form
/**
* Form
*
@@ -221,8 +180,8 @@ export abstract class Form<T = Record<string, unknown>> extends AKElement {
//#region Properties
@property({ type: String })
public successMessage?: string;
@property()
public successMessage = "";
@property({ type: String })
public autocomplete?: AutoFill;
@@ -267,38 +226,11 @@ export abstract class Form<T = Record<string, unknown>> extends AKElement {
/**
* An overridable method for returning a success message after a successful submission.
*
* @deprecated Use `formatAPISuccessMessage` instead.
*/
protected getSuccessMessage(): string | undefined {
protected getSuccessMessage(): string {
return this.successMessage;
}
/**
* An overridable method for returning a formatted message after a successful submission.
*/
protected formatAPISuccessMessage(response: unknown): APIMessage | null {
const message = this.getSuccessMessage();
if (!message) return null;
return {
level: MessageLevel.success,
message,
};
}
/**
* An overridable method for returning a formatted error message after a failed submission.
*/
protected formatAPIErrorMessage(error: APIError): APIMessage | null {
return {
message: msg("There was an error submitting the form."),
description: pluckErrorDetail(error, pluckFallbackFieldErrors(error)[0]),
level: MessageLevel.error,
};
}
//#region Public methods
public reset(): void {
@@ -362,7 +294,10 @@ export abstract class Form<T = Record<string, unknown>> extends AKElement {
return this.send(data)
.then((response) => {
showMessage(this.formatAPISuccessMessage(response));
showMessage({
level: MessageLevel.success,
message: this.getSuccessMessage(),
});
this.dispatchEvent(
new CustomEvent(EVENT_REFRESH, {
@@ -379,32 +314,81 @@ export abstract class Form<T = Record<string, unknown>> extends AKElement {
}
const parsedError = await parseAPIResponseError(error);
let errorMessage = pluckErrorDetail(error);
let focused = false;
//#region Validation errors
if (instanceOfValidationError(parsedError)) {
const invalidFields = reportInvalidFields(
parsedError,
this.renderRoot.querySelectorAll("ak-form-element-horizontal"),
);
// assign all input-related errors to their elements
const elements =
this.shadowRoot?.querySelectorAll<HorizontalFormElement>(
"ak-form-element-horizontal",
) || [];
const focusTarget = Iterator.from(invalidFields)
.map(({ focusTarget }) => focusTarget)
.find(Boolean);
for (const element of elements) {
element.requestUpdate();
if (focusTarget) {
requestAnimationFrame(() => focusTarget.focus());
} else if (Array.isArray(parsedError.nonFieldErrors)) {
const elementName = element.name;
if (!elementName) continue;
const snakeProperty = snakeCase(elementName);
const errorMessages: ErrorProp[] = parsedError[snakeProperty] ?? [];
element.errorMessages = errorMessages;
const { controlledElement } = element;
if (!focused && Array.isArray(errorMessages) && errorMessages.length) {
if (
controlledElement?.checkVisibility() &&
controlledElement instanceof HTMLElement
) {
focused = true;
requestAnimationFrame(() => {
return controlledElement.focus?.();
});
}
}
}
if (parsedError.nonFieldErrors) {
this.nonFieldErrors = parsedError.nonFieldErrors;
} else {
this.nonFieldErrors = pluckFallbackFieldErrors(parsedError);
} else if (!focused) {
// It's possible that the API has returned a field error that we're
// not aware of. We can still show the error message, to at least
// give the user some feedback.
for (const [fieldName, fieldErrors] of Object.entries(parsedError)) {
if (Array.isArray(fieldErrors)) {
this.nonFieldErrors = [
msg(str`${fieldName}: ${fieldErrors.join(", ")}`),
];
break;
}
}
console.error(
"authentik/forms: API rejected the form submission due to an invalid field that doesn't appear to be in the form. This is likely a bug in authentik.",
parsedError,
);
}
errorMessage = msg("Invalid update request.");
// Only change the message when we have `detail`.
// Everything else is handled in the form.
if ("detail" in parsedError) {
errorMessage = parsedError.detail;
}
}
showMessage(this.formatAPIErrorMessage(parsedError), true);
//#endregion
showMessage({
message: errorMessage,
level: MessageLevel.error,
});
// Rethrow the error so the form doesn't close.
throw error;

View File

@@ -0,0 +1,70 @@
import { AKElement } from "#elements/Base";
import { ErrorDetail } from "@goauthentik/api";
import { CSSResult, html, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators.js";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
/**
* This is used in two places outside of Flow, and in both cases is used primarily to
* display content, not take input. It displays the TOTP QR code, and the static
* recovery tokens. But it's used a lot in Flow.
*/
@customElement("ak-form-element")
export class FormElement extends AKElement {
static styles: CSSResult[] = [PFBase, PFForm, PFFormControl];
@property()
label?: string;
@property({ type: Boolean })
required = false;
@property({ attribute: false })
set errors(value: ErrorDetail[] | undefined) {
this._errors = value;
const hasError = (value || []).length > 0;
this.querySelectorAll("input").forEach((input) => {
input.setAttribute("aria-invalid", hasError.toString());
});
this.requestUpdate();
}
_errors?: ErrorDetail[];
updated(): void {
this.querySelectorAll<HTMLInputElement>("input[autofocus]").forEach((input) => {
input.focus();
});
}
render(): TemplateResult {
return html`<div class="pf-c-form__group">
<label class="pf-c-form__label">
<span class="pf-c-form__label-text">${this.label}</span>
${this.required
? html`<span class="pf-c-form__label-required" aria-hidden="true">*</span>`
: html``}
</label>
<slot></slot>
${(this._errors || []).map((error) => {
return html`<p class="pf-c-form__helper-text pf-m-error">
<span class="pf-c-form__helper-text-icon">
<i class="fas fa-exclamation-circle" aria-hidden="true"></i> </span
>${error.string}
</p>`;
})}
</div>`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ak-form-element": FormElement;
}
}

View File

@@ -70,20 +70,7 @@ export class HorizontalFormElement extends AKElement {
//#endregion
#controlledElement: AkControlElement | NamedElement | null = null;
/**
* The element that should be focused when the form is submitted.
*/
public get focusTarget(): AkControlElement | NamedElement<HTMLElement> | null {
if (!(this.#controlledElement instanceof HTMLElement)) {
return null;
}
if (!this.#controlledElement.checkVisibility()) return null;
return this.#controlledElement;
}
public controlledElement: NamedElement | AkControlElement | null = null;
//#region Lifecycle
@@ -92,8 +79,8 @@ export class HorizontalFormElement extends AKElement {
}
public override updated(changedProperties: PropertyValues<this>): void {
if (changedProperties.has("errorMessages") && this.#controlledElement) {
this.#controlledElement.setAttribute(
if (changedProperties.has("errorMessages") && this.controlledElement) {
this.controlledElement.setAttribute(
"aria-invalid",
this.errorMessages?.length ? "true" : "false",
);
@@ -112,13 +99,12 @@ export class HorizontalFormElement extends AKElement {
for (const element of this.querySelectorAll("*")) {
// Is this element capable of being named?
if (!isControlElement(element) && !isNameableElement(element)) continue;
// And does the element already match the name?
if (element.getAttribute("name") === this.name) continue;
this.#controlledElement = element;
if (element.getAttribute("name") !== this.name) {
element.setAttribute("name", this.name);
}
element.setAttribute("name", this.name);
this.controlledElement = element;
break;
}
}

View File

@@ -82,18 +82,7 @@ export class SearchSelectBase<T> extends AkControlElement<string> implements ISe
// Used to inform the form of the name of the object
@property()
public name?: string;
/**
* A unique ID to associate with the input and label.
* @property
*/
@property({ type: String, reflect: false })
public fieldID?: string;
// Used to inform the form of the input label.
@property()
public label?: string;
name?: string;
// The textual placeholder for the search's <input> object, if currently empty. Used as the
// native <input> object's `placeholder` field.
@@ -266,7 +255,6 @@ export class SearchSelectBase<T> extends AkControlElement<string> implements ISe
return html`<ak-search-select-view
managed
.fieldID=${this.fieldID}
.options=${options}
value=${ifDefined(value)}
?blankable=${this.blankable}

View File

@@ -27,11 +27,7 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
*
* @todo Consider making this a static method on singleton {@linkcode MessageContainer}
*/
export function showMessage(message: APIMessage | null, unique = false): void {
if (!message) {
return;
}
export function showMessage(message: APIMessage, unique = false): void {
const container = document.querySelector<MessageContainer>("ak-message-container");
if (!container) {
@@ -39,10 +35,7 @@ export function showMessage(message: APIMessage | null, unique = false): void {
}
if (!message.message.trim()) {
console.warn("authentik/messages: `showMessage` received an empty message", message);
message.message = msg("An unknown error occurred");
message.description ??= msg("Please check the browser console for more details.");
message.message = msg("Error");
}
container.addMessage(message, unique);

View File

@@ -45,7 +45,7 @@ export function isNameableElement(element: Element): element is NamedElement {
return false;
}
return NameableElements.has(element.tagName) || element.getAttribute("name") !== null;
return NameableElements.has(element.tagName);
}
/**

View File

@@ -1,10 +1,9 @@
import "#elements/forms/FormElement";
import { AKElement } from "#elements/Base";
import { bound } from "#elements/decorators/bound";
import { isActiveElement } from "#elements/utils/focus";
import { AKFormErrors, ErrorProp } from "#components/ak-field-errors";
import { AKLabel } from "#components/ak-label";
import { msg } from "@lit/localize";
import { html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators.js";
@@ -12,7 +11,6 @@ import { classMap } from "lit/directives/class-map.js";
import { createRef, ref, Ref } from "lit/directives/ref.js";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
import PFInputGroup from "@patternfly/patternfly/components/InputGroup/input-group.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
@@ -41,7 +39,7 @@ const Visibility = {
@customElement("ak-flow-input-password")
export class InputPassword extends AKElement {
static styles = [PFBase, PFForm, PFInputGroup, PFFormControl, PFButton];
static styles = [PFBase, PFInputGroup, PFFormControl, PFButton];
//#region Properties
@@ -51,7 +49,7 @@ export class InputPassword extends AKElement {
* @attr
*/
@property({ type: String, attribute: "input-id" })
public inputID = "ak-stage-password-input";
inputId = "ak-stage-password-input";
/**
* The name of the input field.
@@ -88,8 +86,8 @@ export class InputPassword extends AKElement {
/**
* The errors for the input field.
*/
@property({ attribute: false })
public errors?: ErrorProp[];
@property({ type: Object })
errors: Record<string, string> = {};
/**
* Whether to allow the user to toggle the visibility of the password.
@@ -308,32 +306,37 @@ export class InputPassword extends AKElement {
}
render() {
return html` ${AKLabel({ required: true, htmlFor: this.inputID }, this.label)}
<div class="pf-c-form__group">
<div class="pf-c-form__group-control">
<div class="pf-c-input-group">
<input
type=${this.passwordVisible ? "text" : "password"}
id=${this.inputID}
name=${this.name}
placeholder=${this.placeholder}
autocomplete="current-password"
class="${classMap({
"pf-c-form-control": true,
"pf-m-icon": true,
"pf-m-caps-lock": this.capsLock,
})}"
required
aria-invalid=${this.errors?.length ? "true" : "false"}
value=${this.initialValue}
${ref(this.inputRef)}
/>
return html` <ak-form-element
label="${this.label}"
required
class="pf-c-form__group"
.errors=${this.errors}
>
<div class="pf-c-form__group-control">
<div class="pf-c-input-group">
<input
type=${this.passwordVisible ? "text" : "password"}
id=${this.inputId}
name=${this.name}
placeholder=${this.placeholder}
autocomplete="current-password"
class="${classMap({
"pf-c-form-control": true,
"pf-m-icon": true,
"pf-m-caps-lock": this.capsLock,
})}"
required
aria-invalid=${this.errors?.length ? "true" : "false"}
value=${this.initialValue}
${ref(this.inputRef)}
/>
${this.renderVisibilityToggle()}
</div>
${AKFormErrors({ errors: this.errors })} ${this.renderHelperText()}
${this.renderVisibilityToggle()}
</div>
</div>`;
${this.renderHelperText()}
</div>
</ak-form-element>`;
}
//#endregion

View File

@@ -1,9 +1,7 @@
import "#elements/forms/FormElement";
import "#flow/FormStatic";
import "#flow/components/ak-flow-card";
import { AKFormErrors } from "#components/ak-field-errors";
import { AKLabel } from "#components/ak-label";
import { BaseStage } from "#flow/stages/base";
import {
@@ -18,7 +16,6 @@ import { customElement } from "lit/decorators.js";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
import PFInputGroup from "@patternfly/patternfly/components/InputGroup/input-group.css";
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
import PFTitle from "@patternfly/patternfly/components/Title/title.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
@@ -28,24 +25,15 @@ export class OAuth2DeviceCode extends BaseStage<
OAuthDeviceCodeChallenge,
OAuthDeviceCodeChallengeResponseRequest
> {
static styles: CSSResult[] = [
PFBase,
PFLogin,
PFForm,
PFFormControl,
PFTitle,
PFButton,
PFInputGroup,
];
static styles: CSSResult[] = [PFBase, PFLogin, PFForm, PFFormControl, PFTitle, PFButton];
render(): TemplateResult {
return html`<ak-flow-card .challenge=${this.challenge}>
<form class="pf-c-form" @submit=${this.submitForm}>
<div class="pf-c-form__group">
${AKLabel({ required: true, htmlFor: "device-code-input" }, msg("Device Code"))}
<form
class="pf-c-form"
@submit=${this.submitForm}
>
<input
id="device-code-input"
type="text"
name="code"
inputmode="numeric"
@@ -57,8 +45,7 @@ export class OAuth2DeviceCode extends BaseStage<
value=""
required
/>
${AKFormErrors({ errors: this.challenge.responseErrors?.code })}
</div>
</ak-form-element>
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">

View File

@@ -1,3 +1,4 @@
import "#elements/forms/FormElement";
import "#flow/FormStatic";
import "#flow/components/ak-flow-card";

View File

@@ -1,9 +1,7 @@
import "#elements/forms/FormElement";
import "#flow/FormStatic";
import "#flow/components/ak-flow-card";
import { AKFormErrors } from "#components/ak-field-errors";
import { AKLabel } from "#components/ak-label";
import { BaseStage } from "#flow/stages/base";
import {
@@ -20,7 +18,6 @@ import PFAlert from "@patternfly/patternfly/components/Alert/alert.css";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
import PFInputGroup from "@patternfly/patternfly/components/InputGroup/input-group.css";
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
import PFTitle from "@patternfly/patternfly/components/Title/title.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
@@ -36,7 +33,6 @@ export class AuthenticatorEmailStage extends BaseStage<
PFLogin,
PFForm,
PFFormControl,
PFInputGroup,
PFTitle,
PFButton,
];
@@ -55,13 +51,13 @@ export class AuthenticatorEmailStage extends BaseStage<
>
</div>
</ak-form-static>
<div class="pf-c-form__group">
${AKLabel(
{ required: true, htmlFor: "email-input" },
msg("Configure your email"),
)}
<ak-form-element
label="${msg("Configure your email")}"
required
class="pf-c-form__group"
.errors=${(this.challenge?.responseErrors || {}).email}
>
<input
id="email-input"
type="email"
name="email"
placeholder="${msg("Please enter your email address.")}"
@@ -70,8 +66,7 @@ export class AuthenticatorEmailStage extends BaseStage<
class="pf-c-form-control"
required
/>
${AKFormErrors({ errors: this.challenge.responseErrors?.email })}
</div>
</ak-form-element>
${this.renderNonFieldErrors()}
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
@@ -98,10 +93,13 @@ export class AuthenticatorEmailStage extends BaseStage<
A verification token has been sent to your configured email address
${ifDefined(this.challenge.email)}
<form class="pf-c-form" @submit=${this.submitForm}>
<div class="pf-c-form__group">
${AKLabel({ required: true, htmlFor: "code-input" }, msg("Code"))}
<ak-form-element
label="${msg("Code")}"
required
class="pf-c-form__group"
.errors=${(this.challenge?.responseErrors || {}).code}
>
<input
id="code-input"
type="text"
name="code"
inputmode="numeric"
@@ -112,8 +110,7 @@ export class AuthenticatorEmailStage extends BaseStage<
class="pf-c-form-control"
required
/>
${AKFormErrors({ errors: this.challenge.responseErrors?.code })}
</div>
</ak-form-element>
${this.renderNonFieldErrors()}
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">

View File

@@ -1,9 +1,7 @@
import "#elements/forms/FormElement";
import "#flow/FormStatic";
import "#flow/components/ak-flow-card";
import { AKFormErrors } from "#components/ak-field-errors";
import { AKLabel } from "#components/ak-label";
import { BaseStage } from "#flow/stages/base";
import {
@@ -20,7 +18,6 @@ import PFAlert from "@patternfly/patternfly/components/Alert/alert.css";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
import PFInputGroup from "@patternfly/patternfly/components/InputGroup/input-group.css";
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
import PFTitle from "@patternfly/patternfly/components/Title/title.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
@@ -36,18 +33,15 @@ export class AuthenticatorSMSStage extends BaseStage<
PFLogin,
PFForm,
PFFormControl,
PFInputGroup,
PFTitle,
PFButton,
];
renderPhoneNumber(): TemplateResult {
return html`<ak-flow-card .challenge=${this.challenge}>
<form class="pf-c-form" @submit=${this.submitForm}>
<ak-form-static
class="pf-c-form__group"
userAvatar=${this.challenge.pendingUserAvatar}
user=${this.challenge.pendingUser}
<form
class="pf-c-form"
@submit=${this.submitForm}
>
<div slot="link">
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}"
@@ -55,12 +49,12 @@ export class AuthenticatorSMSStage extends BaseStage<
>
</div>
</ak-form-static>
<div class="pf-c-form__group">
${AKLabel(
{ required: true, htmlFor: "phone-number-input" },
msg("Phone number"),
)}
<ak-form-element
label="${msg("Phone number")}"
required
class="pf-c-form__group"
.errors=${(this.challenge?.responseErrors || {}).phone_number}
>
<input
type="tel"
name="phoneNumber"
@@ -70,8 +64,7 @@ export class AuthenticatorSMSStage extends BaseStage<
class="pf-c-form-control"
required
/>
${AKFormErrors({ errors: this.challenge.responseErrors?.phone_number })}
</div>
</ak-form-element>
${this.renderNonFieldErrors()}
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
@@ -96,10 +89,13 @@ export class AuthenticatorSMSStage extends BaseStage<
>
</div>
</ak-form-static>
<div class="pf-c-form__group">
${AKLabel({ required: true, htmlFor: "sms-code-input" }, msg("Code"))}
<ak-form-element
label="${msg("Code")}"
required
class="pf-c-form__group"
.errors=${(this.challenge?.responseErrors || {}).code}
>
<input
id="sms-code-input"
type="text"
name="code"
inputmode="numeric"
@@ -110,8 +106,7 @@ export class AuthenticatorSMSStage extends BaseStage<
class="pf-c-form-control"
required
/>
${AKFormErrors({ errors: this.challenge.responseErrors?.code })}
</div>
</ak-form-element>
${this.renderNonFieldErrors()}
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">

View File

@@ -1,3 +1,4 @@
import "#elements/forms/FormElement";
import "#flow/FormStatic";
import "#flow/components/ak-flow-card";
@@ -16,7 +17,6 @@ import { ifDefined } from "lit/directives/if-defined.js";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
import PFInputGroup from "@patternfly/patternfly/components/InputGroup/input-group.css";
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
import PFTitle from "@patternfly/patternfly/components/Title/title.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
@@ -31,7 +31,6 @@ export class AuthenticatorStaticStage extends BaseStage<
PFLogin,
PFForm,
PFFormControl,
PFInputGroup,
PFTitle,
PFButton,
css`
@@ -65,13 +64,13 @@ export class AuthenticatorStaticStage extends BaseStage<
>
</div>
</ak-form-static>
<div class="pf-c-form__group">
<ak-form-element label="" class="pf-c-form__group">
<ul>
${this.challenge.codes.map((token) => {
return html`<li class="pf-m-monospace">${token}</li>`;
})}
</ul>
</div>
</ak-form-element>
<p>${msg("Make sure to keep these tokens in a safe place.")}</p>
<div class="pf-c-form__group pf-m-action">

View File

@@ -1,3 +1,4 @@
import "#elements/forms/FormElement";
import "#flow/FormStatic";
import "#flow/components/ak-flow-card";
import "webcomponent-qr-code";
@@ -6,9 +7,6 @@ import { MessageLevel } from "#common/messages";
import { showMessage } from "#elements/messages/MessageContainer";
import { AKFormErrors } from "#components/ak-field-errors";
import { AKLabel } from "#components/ak-label";
import { BaseStage } from "#flow/stages/base";
import {
@@ -24,7 +22,6 @@ import { ifDefined } from "lit/directives/if-defined.js";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
import PFInputGroup from "@patternfly/patternfly/components/InputGroup/input-group.css";
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
import PFTitle from "@patternfly/patternfly/components/Title/title.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
@@ -39,7 +36,6 @@ export class AuthenticatorTOTPStage extends BaseStage<
PFLogin,
PFForm,
PFFormControl,
PFInputGroup,
PFTitle,
PFButton,
css`
@@ -66,8 +62,7 @@ export class AuthenticatorTOTPStage extends BaseStage<
</div>
</ak-form-static>
<input type="hidden" name="otp_uri" value=${this.challenge.configUrl} />
<div class="pf-c-form__group">
<ak-form-element>
<div class="qr-container">
<qr-code data="${this.challenge.configUrl}"></qr-code>
<button
@@ -97,16 +92,20 @@ export class AuthenticatorTOTPStage extends BaseStage<
${msg("Copy")}
</button>
</div>
</div>
</ak-form-element>
<p>
${msg(
"Please scan the QR code above using the Microsoft Authenticator, Google Authenticator, or other authenticator apps on your device, and enter the code the device displays below to finish setting up the MFA device.",
)}
</p>
<div class="pf-c-form__group">
${AKLabel({ required: true, htmlFor: "totp-code-input" }, msg("Code"))}
<ak-form-element
label="${msg("Code")}"
required
class="pf-c-form__group"
.errors=${(this.challenge?.responseErrors || {}).code}
>
<!-- @ts-ignore -->
<input
id="totp-code-input"
type="text"
name="code"
inputmode="numeric"
@@ -118,8 +117,7 @@ export class AuthenticatorTOTPStage extends BaseStage<
spellcheck="false"
required
/>
${AKFormErrors({ errors: this.challenge.responseErrors?.code })}
</div>
</ak-form-element>
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">

View File

@@ -1,8 +1,6 @@
import "#elements/forms/FormElement";
import "#flow/components/ak-flow-card";
import { AKFormErrors } from "#components/ak-field-errors";
import { AKLabel } from "#components/ak-label";
import { BaseDeviceStage } from "#flow/stages/authenticator_validate/base";
import { PasswordManagerPrefill } from "#flow/stages/identification/IdentificationStage";
@@ -77,15 +75,16 @@ export class AuthenticatorValidateStageWebCode extends BaseDeviceStage<
<i class="fa ${this.deviceIcon()}" aria-hidden="true"></i>
<p>${this.deviceMessage()}</p>
</div>
<div class="pf-c-form__group">
${AKLabel(
{ required: true, htmlFor: "validation-code-input" },
this.deviceChallenge?.deviceClass === DeviceClassesEnum.Static
? msg("Static token")
: msg("Authentication code"),
)}
<ak-form-element
label="${this.deviceChallenge?.deviceClass === DeviceClassesEnum.Static
? msg("Static token")
: msg("Authentication code")}"
required
class="pf-c-form__group"
.errors=${(this.challenge?.responseErrors || {}).code}
>
<!-- @ts-ignore -->
<input
id="validation-code-input"
type="text"
name="code"
inputmode="${this.deviceChallenge?.deviceClass === DeviceClassesEnum.Static
@@ -101,8 +100,7 @@ export class AuthenticatorValidateStageWebCode extends BaseDeviceStage<
value="${PasswordManagerPrefill.totp || ""}"
required
/>
${AKFormErrors({ errors: this.challenge.responseErrors?.code })}
</div>
</ak-form-element>
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">

View File

@@ -1,4 +1,5 @@
import "#elements/EmptyState";
import "#elements/forms/FormElement";
import { BaseDeviceStage } from "#flow/stages/authenticator_validate/base";

View File

@@ -10,7 +10,6 @@ import { property } from "lit/decorators.js";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
import PFInputGroup from "@patternfly/patternfly/components/InputGroup/input-group.css";
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
import PFTitle from "@patternfly/patternfly/components/Title/title.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
@@ -30,7 +29,6 @@ export class BaseDeviceStage<
PFLogin,
PFForm,
PFFormControl,
PFInputGroup,
PFTitle,
PFButton,
css`

View File

@@ -1,3 +1,4 @@
import "#elements/forms/FormElement";
import "#flow/FormStatic";
import "#flow/components/ak-flow-card";

View File

@@ -1,12 +1,10 @@
import "#elements/Divider";
import "#elements/EmptyState";
import "#elements/forms/FormElement";
import "#flow/components/ak-flow-card";
import "#flow/components/ak-flow-password-input";
import "#flow/stages/captcha/CaptchaStage";
import { AKFormErrors } from "#components/ak-field-errors";
import { AKLabel } from "#components/ak-label";
import { renderSourceIcon } from "#admin/sources/utils";
import { BaseStage } from "#flow/stages/base";
@@ -22,7 +20,7 @@ import {
import { msg, str } from "@lit/localize";
import { css, CSSResult, html, nothing, PropertyValues, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import { customElement, state } from "lit/decorators.js";
import { createRef, ref } from "lit/directives/ref.js";
import PFAlert from "@patternfly/patternfly/components/Alert/alert.css";
@@ -95,14 +93,6 @@ export class IdentificationStage extends BaseStage<
`,
];
/**
* The ID of the input field.
*
* @attr
*/
@property({ type: String, attribute: "input-id" })
public inputID = "ak-identifier-input";
#form?: HTMLFormElement;
#rememberMe = new AkRememberMeController(this);
@@ -317,7 +307,6 @@ export class IdentificationStage extends BaseStage<
[UserFieldsEnum.Upn]: msg("UPN"),
};
const label = OR_LIST_FORMATTERS.format(fields.map((f) => uiFields[f]));
return html`${this.challenge.flowDesignation === FlowDesignationEnum.Recovery
? html`
<p>
@@ -327,10 +316,13 @@ export class IdentificationStage extends BaseStage<
</p>
`
: nothing}
<div class="pf-c-form__group">
${AKLabel({ required: true, htmlFor: this.inputID }, label)}
<ak-form-element
label=${label}
required
class="pf-c-form__group"
.errors=${(this.challenge.responseErrors || {}).uid_field}
>
<input
id=${this.inputID}
type=${type}
name="uidField"
placeholder=${label}
@@ -342,16 +334,15 @@ export class IdentificationStage extends BaseStage<
required
/>
${this.#rememberMe.render()}
${AKFormErrors({ errors: this.challenge.responseErrors?.uid_field })}
</div>
</ak-form-element>
${this.challenge.passwordFields
? html`
<ak-flow-input-password
label=${msg("Password")}
input-id="ak-stage-identification-password"
inputId="ak-stage-identification-password"
required
class="pf-c-form__group"
.errors=${this.challenge?.responseErrors?.password}
.errors=${(this.challenge?.responseErrors || {}).password}
?allow-show-password=${this.challenge.allowShowPassword}
prefill=${PasswordManagerPrefill.password ?? ""}
></ak-flow-input-password>
@@ -383,7 +374,9 @@ export class IdentificationStage extends BaseStage<
<div class="pf-c-form__group ${this.challenge.captchaStage ? "" : "pf-m-action"}">
<button
?disabled=${this.challenge.captchaStage && !this.captchaLoaded}
?disabled=${this.challenge.captchaStage &&
this.challenge.captchaStage.interactive &&
!this.captchaLoaded}
type="submit"
class="pf-c-button pf-m-primary pf-m-block"
>

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