Compare commits

..

50 Commits

Author SHA1 Message Date
2bc318d167 website: changelog for security releases (#15291)
* website: changelog for security releases

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

* format

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
# Conflicts:
#	website/docs/releases/2025/v2025.6.md
2025-06-27 15:43:34 +02:00
b34665fabd release: 2025.4.3 2025-06-27 15:34:22 +02:00
0e07414e97 security: fix CVE-2025-52553 (#15289)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
# Conflicts:
#	website/sidebars/docs.mjs
2025-06-27 15:28:27 +02:00
dcbf5f323c website/docs: fixes misplaced sentence (cherry-pick #14998) (#15181)
website/docs: fixes misplaced sentence (#14998)

fixes misplaced sentence

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: Tana M Berry <tana@goauthentik.io>
2025-06-22 00:58:42 +02:00
c3f1d6587d website/docs: fix egregious maintenance fail (cherry-pick #15176) (#15179)
website/docs: fix egregious maintenance fail (#15176)

fix egregious maintenance fail

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: Tana M Berry <tana@goauthentik.io>
2025-06-22 00:58:28 +02:00
7254c11cb9 website/docs: remove commented out config options (cherry-pick #15064) (#15066)
website/docs: remove commented out config options (#15064)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-06-16 16:56:22 +02:00
ca4e6a10f5 website/docs: also hide the postgres pool_options setting (cherry-pick #15023) (#15033)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-06-13 15:40:52 +02:00
bda30c5ad5 release: 2025.4.2 2025-06-04 15:27:50 +02:00
588a7ff2e1 website/docs: release notes for 2025.4.2 (cherry-pick #14868) (#14873)
website/docs: release notes for `2025.4.2` (#14868)

Co-authored-by: Simonyi Gergő <28359278+gergosimonyi@users.noreply.github.com>
2025-06-04 15:27:08 +02:00
599d0f701f website/docs: release notes for 2025.4.1 (cherry-pick #14526) (#14872)
website/docs: release notes for 2025.4.1 (#14526)

* website/docs: release notes for 2025.4.1



* format



---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-06-04 15:22:52 +02:00
967e4cce9d website/docs: fix leftover placeholder in release notes (cherry-pick #14377) (#14871)
website/docs: fix leftover placeholder in release notes (#14377)

Update v2025.4.md

changed download URL to match version 2025.4. Otherwise it will give a 404

Signed-off-by: finkerle <145992792+finkerle@users.noreply.github.com>
Co-authored-by: finkerle <145992792+finkerle@users.noreply.github.com>
2025-06-04 15:22:39 +02:00
f1c5f43419 website/docs: fix dry-run release highlight (cherry-pick #14337) (#14870)
website/docs: fix dry-run release highlight (#14337)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-06-04 15:22:00 +02:00
b5b68fc829 website/docs: clarify 2025.4 breaking Reputation changes (cherry-pick #14284) (#14869)
website/docs: clarify `2025.4` breaking Reputation changes (#14284)

* website/docs: clarify `2025.4` breaking Reputation changes

* Update website/docs/releases/2025/v2025.4.md



* change to bump build checks

* another tweak to bounce after rebase

---------

Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: Simonyi Gergő <28359278+gergosimonyi@users.noreply.github.com>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: Tana M Berry <tana@goauthentik.io>
2025-06-04 15:21:28 +02:00
1d7be5e770 core: Migrate permissions before deleteing OldAuthenticatedSession (cherry-pick #14788) (#14791)
core: Migrate permissions before deleteing OldAuthenticatedSession (#14788)

* add migrate_permissions_before_delete to authentik_core 0047 migration

* fix linting

* new approach

* fixup! new approach

---------

Co-authored-by: Marcelo Elizeche Landó <marcelo@goauthentik.io>
Co-authored-by: Simonyi Gergő <gergo@goauthentik.io>
2025-05-30 18:05:52 +02:00
489ef7a0a1 ci: fix broken cache (cherry-pick #14725) (#14792)
ci: fix broken cache (#14725)

* ci: fix broken cache



* fix commit hash



---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-05-30 17:28:51 +02:00
668f35cd5b sources/scim: fix all users being added to group when no members are given (cherry-pick #14645) (#14666)
sources/scim: fix all users being added to group when no members are given (#14645)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-05-23 17:24:27 +02:00
42f0528a1d lifecycle: fix arguments not being passed to worker command (cherry-pick #14574) (#14620)
Co-authored-by: Jens L. <jens@goauthentik.io>
fix arguments not being passed to worker command (#14574)
2025-05-22 13:51:08 +02:00
ae47624761 release: 2025.4.1 2025-05-15 19:10:52 +02:00
14a6430e21 core: fix unable to create group if no enable_group_superuser permission is given (cherry-pick #14510) (#14521)
core: fix unable to create group if no enable_group_superuser permission is given (#14510)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-05-15 15:05:04 +02:00
ed0a9d6a0a core: remove OldAuthenticatedSession content type (cherry-pick #14507) (#14509)
core: remove `OldAuthenticatedSession` content type (#14507)

* core: remove `OldAuthenticatedSession` content type

This was left out from https://github.com/goauthentik/authentik/pull/9736

* remove stale content types in `repair_permissions`



* run `remove_stale_contenttypes` for each tenant

---------

Co-authored-by: Simonyi Gergő <28359278+gergosimonyi@users.noreply.github.com>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2025-05-14 15:33:14 +02:00
53143e0c40 enterprise: fix expired license's users being counted (cherry-pick #14451) (#14496)
enterprise: fix expired license's users being counted (#14451)

* enterprise: fix expired license's users being counted



* tests to the rescue



* hmm



---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-05-13 16:07:08 +02:00
178e010ed4 root: backport SFE Build fix (#14495)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-05-13 14:32:13 +02:00
49b666fbde root: temporarily deactivate database pool option (cherry-pick #14443) (#14479)
root: temporarily deactivate database pool option (#14443)

* root: temporarily deactivate database pool option



* format



* deactivate tests



---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-05-12 19:13:09 +02:00
c343e3a7f4 core: fix session migration when old session can't be loaded (cherry-pick #14466) (#14480)
core: fix session migration when old session can't be loaded (#14466)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-05-12 16:09:05 +02:00
5febf3ce5b core: bump h11 from 0.14.0 to v0.16.0 (cherry-pick #14352) (#14472)
core: bump h11 from 0.14.0 to v0.16.0 (#14352)

Co-authored-by: Marcelo Elizeche Landó <marcelo@goauthentik.io>
2025-05-11 15:09:22 +02:00
b8c5bd678b web/flows/sfe: fix global background image not being loaded (cherry-pick #14442) (#14450)
web/flows/sfe: fix global background image not being loaded (#14442)

* web/flows/sfe: add initial loading spinner



* fix brand-level default flow background not working with SFE and loading original image with full flow interface

https://github.com/goauthentik/authentik/pull/13079#issuecomment-2853357407


---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-05-09 18:14:21 +02:00
4dd5eccbaa lifecycle: fix ak dump_config (cherry-pick #14445) (#14448)
lifecycle: fix ak dump_config (#14445)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-05-09 18:14:06 +02:00
2410884006 outposts: fix tmpdir in containers not being set (cherry-pick #14444) (#14449)
outposts: fix tmpdir in containers not being set (#14444)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-05-09 18:13:41 +02:00
3cb921b0f9 rbac: fix RoleObjectPermissionTable not showing add_user_to_group (cherry-pick #14312) (#14334)
rbac: fix RoleObjectPermissionTable not showing `add_user_to_group` (#14312)

fix RoleObjectPermissionTable not showing `add_user_to_group`

Co-authored-by: Simonyi Gergő <28359278+gergosimonyi@users.noreply.github.com>
2025-05-02 18:35:13 +02:00
535f92981f website/docs: docs about initial perms (cherry-pick #14263) (#14282)
website/docs: docs about initial perms (#14263)

* basic procedural steps

* more questions, more typos

* more typos

* tweaks

* more content, new links

* fixed link

* tweak

* fix things

* more fixes

* yet more fixes

* Apply suggestions from code review




* Update website/docs/users-sources/access-control/initial_permissions.mdx




* dewi's edits

* dominic's edits

* gergo edits and more dominic edits

* one more

* yet one more fix

* final gergo observation

* tweak

---------

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-05-01 22:25:36 +02:00
955d69d5b7 brands: fix CSS Migration not updating brands (cherry-pick #14306) (#14308)
brands: fix CSS Migration not updating brands (#14306)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-04-30 23:04:38 +02:00
fb01d8e96a website/docs: initial permissions: fix usage of term admin (cherry-pick #14300) (#14302)
Co-authored-by: Dominic R <dominic@sdko.org>
fix usage of term admin (#14300)
2025-04-30 15:44:44 +02:00
6d39efd3e3 ci: cleanup post uv migration (cherry-pick #13538) (#14297)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-04-30 15:05:46 +02:00
3020c31bcd website/docs: add gateway API to release notes and documentation (cherry-pick #14278) (#14298)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-04-30 14:52:38 +02:00
22412729e2 release: 2025.4.0 2025-04-29 16:16:32 -03:00
a02868a27d website/docs: Release notes 2025.4.0 (#14281)
* remove rc notice and enterprise tag for the span

* Edit sidebar and security.md

* Add api changes and minor fixes

* Fix linting

* fix netlify linter

* Update website/docs/releases/2025/v2025.4.md

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

* Update website/docs/releases/2025/v2025.4.md

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

* remove changelog entries that shouldn't be there

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

* Update v2025.4.md

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

* Update v2025.4.md

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

* fix linting

---------

Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-04-29 15:56:28 -03:00
bfbb4a8ebc website/docs: sessions in database (#13507)
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-04-29 15:56:21 -03:00
6c0e827677 website/docs: add LDAP 'Lookup using user attribute' docs (#13966)
* website/docs: add LDAP 'Lookup using user attribute' docs

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

* Updated the doc to new template, removed incorrect screenshot, clarified instructions

* Change in group field explanation as per Marc's comment

* Added examples for filters and changed some language.

* Removed additional info link

* fixup

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

* Minor formatting changes

* Update website/docs/users-sources/sources/protocols/ldap/index.md

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

* Update website/docs/users-sources/sources/directory-sync/active-directory/index.md

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

* Update website/docs/users-sources/sources/directory-sync/active-directory/index.md

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

* Added more information to service account creation and LDAPS testing

* Added examples for fields based on issue #3801

---------

Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>
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: Dominic R <dominic@sdko.org>
2025-04-29 15:56:13 -03:00
29884cbf81 website/docs: add postgres pool configuration (#14060)
* website/docs: add postgres pool configuration

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

* Update website/docs/install-config/configuration/configuration.mdx

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

---------

Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-04-29 15:56:01 -03:00
0f02985b0c website/docs: docs about initial perms (#14263)
* basic procedural steps

* more questions, more typos

* more typos

* tweaks

* more content, new links

* fixed link

* tweak

* fix things

* more fixes

* yet more fixes

* Apply suggestions from code review

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

* Update website/docs/users-sources/access-control/initial_permissions.mdx

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

* dewi's edits

* dominic's edits

* gergo edits and more dominic edits

* one more

* yet one more fix

* final gergo observation

* tweak

---------

Signed-off-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-04-29 15:55:54 -03:00
2244e026c2 website/docs: Revert "website/docs: revert token_expiry format in example blueprint… (#14280)
Revert "website/docs: revert token_expiry format in example blueprint (#13582)"

This reverts commit 9538cf4690.
2025-04-29 15:55:47 -03:00
429c03021c website/docs: Password Uniqueness Policy (#13686)
* First draft docs for policies/unique_password

* simplify documentation

* fix styling

* Add clarification about when this policy takes effect

* change wording in how it works

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Marcelo Elizeche Landó <marce@melizeche.com>

* Take the user by the hand and tell them where to go

* Improve wording in Configuration options

* add suggestion from PR

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Marcelo Elizeche Landó <marce@melizeche.com>

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

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

* Apply suggestions from code review

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Marcelo Elizeche Landó <marce@melizeche.com>

* fix linting and wording

* Add instructions for binding

* Remove conf options section, add to sidebar

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

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

---------

Signed-off-by: Marcelo Elizeche Landó <marce@melizeche.com>
Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: Dominic R <dominic@sdko.org>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
2025-04-29 15:55:41 -03:00
f47e8d9d72 docs/website: Update 2025.4 notes (#14272)
Fix styling
2025-04-29 15:55:35 -03:00
3e7d2587c4 website/docs: update 2025.4 release notes (#14251)
* Update release notes for 2025.4

* fix typo

* Add/improve highlights, features and descriptions

* Fix linting and remove API changes

* remove minor changes

* fix linting

* Add helm chart stuff and integrations guide

* fix linting

* Restore SECURITY.md and sidebar.js

* Update website/docs/releases/2025/v2025.4.md

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

* Update website/docs/releases/2025/v2025.4.md

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

* Update website/docs/releases/2025/v2025.4.md

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

* Update website/docs/releases/2025/v2025.4.md

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

* Update website/docs/releases/2025/v2025.4.md

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

* Update website/docs/releases/2025/v2025.4.md

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

* Update website/docs/releases/2025/v2025.4.md

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

* password history - add compliance note

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

* Update website/docs/releases/2025/v2025.4.md

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

* please the linter

* use current version

* add .md

* fix badges

---------

Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Fletcher Heisler <fheisler@users.noreply.github.com>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: Fletcher Heisler <fheisler@users.noreply.github.com>
2025-04-29 15:54:55 -03:00
55a38d4a36 rbac: add name to Permissions search (cherry-pick #14269) (#14270)
rbac: add `name` to Permissions search (#14269)

Co-authored-by: Simonyi Gergő <28359278+gergosimonyi@users.noreply.github.com>
2025-04-28 19:04:05 +02:00
6021bb932d web: fix bug that was causing charts to be too tall (cherry-pick #14253) (#14254)
Co-authored-by: Ken Sternberg <133134217+kensternberg-authentik@users.noreply.github.com>
fix bug that was causing charts to be too tall (#14253)
2025-04-28 13:51:49 +02:00
54a5d95717 release: 2025.4.0-rc2 2025-04-25 13:50:44 +02:00
a0a1275452 lifecycle: fix test-all in docker (cherry-pick #14244) (#14246)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
fix test-all in docker (#14244)
2025-04-25 13:50:27 +02:00
919aa5df59 core, web: update translations (cherry-pick #14243) (#14245)
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
Co-authored-by: rissson <18313093+rissson@users.noreply.github.com>
2025-04-25 13:39:50 +02:00
cedf7cf683 release: 2025.4.0-rc1 2025-04-25 01:53:53 -03:00
374 changed files with 9288 additions and 15338 deletions

View File

@ -1,5 +1,5 @@
[bumpversion]
current_version = 2025.4.1
current_version = 2025.4.3
tag = True
commit = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(?:-(?P<rc_t>[a-zA-Z-]+)(?P<rc_n>[1-9]\\d*))?

View File

@ -36,7 +36,7 @@ runs:
with:
go-version-file: "go.mod"
- name: Setup docker cache
uses: ScribeMD/docker-cache@0.5.0
uses: AndreKurait/docker-cache@0fe76702a40db986d9663c24954fc14c6a6031b7
with:
key: docker-images-${{ runner.os }}-${{ hashFiles('.github/actions/setup/docker-compose.yml', 'Makefile') }}-${{ inputs.postgresql_version }}
- name: Setup dependencies

View File

@ -118,15 +118,3 @@ updates:
prefix: "core:"
labels:
- dependencies
- package-ecosystem: docker-compose
directories:
# - /scripts # Maybe
- /tests/e2e
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 10
commit-message:
prefix: "core:"
labels:
- dependencies

View File

@ -200,7 +200,7 @@ jobs:
uses: actions/cache@v4
with:
path: web/dist
key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'web/src/**', 'web/packages/sfe/src/**') }}-b
key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'web/src/**') }}
- name: prepare web ui
if: steps.cache-web.outputs.cache-hit != 'true'
working-directory: web
@ -208,7 +208,6 @@ jobs:
npm ci
make -C .. gen-client-ts
npm run build
npm run build:sfe
- name: run e2e
run: |
uv run coverage run manage.py test ${{ matrix.job.glob }}

View File

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

View File

@ -16,7 +16,7 @@
],
"typescript.preferences.importModuleSpecifier": "non-relative",
"typescript.preferences.importModuleSpecifierEnding": "index",
"typescript.tsdk": "./node_modules/typescript/lib",
"typescript.tsdk": "./web/node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
"yaml.schemas": {
"./blueprints/schema.json": "blueprints/**/*.yaml"
@ -30,5 +30,7 @@
}
],
"go.testFlags": ["-count=1"],
"github-actions.workflows.pinned.workflows": [".github/workflows/ci-main.yml"]
"github-actions.workflows.pinned.workflows": [
".github/workflows/ci-main.yml"
]
}

View File

@ -86,17 +86,18 @@ FROM --platform=${BUILDPLATFORM} ghcr.io/maxmind/geoipupdate:v7.1.0 AS geoip
ENV GEOIPUPDATE_EDITION_IDS="GeoLite2-City GeoLite2-ASN"
ENV GEOIPUPDATE_VERBOSE="1"
ENV GEOIPUPDATE_ACCOUNT_ID_FILE="/run/secrets/GEOIPUPDATE_ACCOUNT_ID"
ENV GEOIPUPDATE_LICENSE_KEY_FILE="/run/secrets/GEOIPUPDATE_LICENSE_KEY"
USER root
RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
--mount=type=secret,id=GEOIPUPDATE_LICENSE_KEY \
mkdir -p /usr/share/GeoIP && \
/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"
/bin/sh -c "/usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0"
# Stage 5: Download uv
FROM ghcr.io/astral-sh/uv:0.7.4 AS uv
FROM ghcr.io/astral-sh/uv:0.6.16 AS uv
# Stage 6: Base python image
FROM ghcr.io/goauthentik/fips-python:3.13.3-slim-bookworm-fips AS python-base
FROM ghcr.io/goauthentik/fips-python:3.12.10-slim-bookworm-fips AS python-base
ENV VENV_PATH="/ak-root/.venv" \
PATH="/lifecycle:/ak-root/.venv/bin:$PATH" \

View File

@ -42,4 +42,4 @@ See [SECURITY.md](SECURITY.md)
## Adoption and Contributions
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).
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 [CONTRIBUTING.md file](./CONTRIBUTING.md).

View File

@ -2,7 +2,7 @@
from os import environ
__version__ = "2025.4.1"
__version__ = "2025.4.3"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"

View File

@ -54,7 +54,7 @@ def create_component(generator: SchemaGenerator, name, schema, type_=ResolvedCom
return component
def postprocess_schema_responses(result, generator: SchemaGenerator, **kwargs):
def postprocess_schema_responses(result, generator: SchemaGenerator, **kwargs): # noqa: W0613
"""Workaround to set a default response for endpoints.
Workaround suggested at
<https://github.com/tfranzel/drf-spectacular/issues/119#issuecomment-656970357>

View File

@ -164,7 +164,9 @@ class BlueprintEntry:
"""Get the blueprint model, with yaml tags resolved if present"""
return str(self.tag_resolver(self.model, blueprint))
def get_permissions(self, blueprint: "Blueprint") -> Generator[BlueprintEntryPermission]:
def get_permissions(
self, blueprint: "Blueprint"
) -> Generator[BlueprintEntryPermission, None, None]:
"""Get permissions of this entry, with all yaml tags resolved"""
for perm in self.permissions:
yield BlueprintEntryPermission(

View File

@ -5,10 +5,10 @@ from typing import Any
from django.db.models import F, Q
from django.db.models import Value as V
from django.http.request import HttpRequest
from sentry_sdk import get_current_span
from authentik import get_full_version
from authentik.brands.models import Brand
from authentik.lib.sentry import get_http_meta
from authentik.tenants.models import Tenant
_q_default = Q(default=True)
@ -32,9 +32,13 @@ def context_processor(request: HttpRequest) -> dict[str, Any]:
"""Context Processor that injects brand object into every template"""
brand = getattr(request, "brand", DEFAULT_BRAND)
tenant = getattr(request, "tenant", Tenant())
trace = ""
span = get_current_span()
if span:
trace = span.to_traceparent()
return {
"brand": brand,
"footer_links": tenant.footer_links,
"html_meta": {**get_http_meta()},
"sentry_trace": trace,
"version": get_full_version(),
}

View File

@ -79,6 +79,7 @@ def _migrate_session(
AuthenticatedSession.objects.using(db_alias).create(
session=session,
user=old_auth_session.user,
uuid=old_auth_session.uuid,
)

View File

@ -1,10 +1,81 @@
# Generated by Django 5.1.9 on 2025-05-14 11:15
from django.apps.registry import Apps
from django.apps.registry import Apps, apps as global_apps
from django.db import migrations
from django.contrib.contenttypes.management import create_contenttypes
from django.contrib.auth.management import create_permissions
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
def migrate_authenticated_session_permissions(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
"""Migrate permissions from OldAuthenticatedSession to AuthenticatedSession"""
db_alias = schema_editor.connection.alias
# `apps` here is just an instance of `django.db.migrations.state.AppConfigStub`, we need the
# real config for creating permissions and content types
authentik_core_config = global_apps.get_app_config("authentik_core")
# These are only ran by django after all migrations, but we need them right now.
# `global_apps` is needed,
create_permissions(authentik_core_config, using=db_alias, verbosity=1)
create_contenttypes(authentik_core_config, using=db_alias, verbosity=1)
# But from now on, this is just a regular migration, so use `apps`
Permission = apps.get_model("auth", "Permission")
ContentType = apps.get_model("contenttypes", "ContentType")
try:
old_ct = ContentType.objects.using(db_alias).get(
app_label="authentik_core", model="oldauthenticatedsession"
)
new_ct = ContentType.objects.using(db_alias).get(
app_label="authentik_core", model="authenticatedsession"
)
except ContentType.DoesNotExist:
# This should exist at this point, but if not, let's cut our losses
return
# Get all permissions for the old content type
old_perms = Permission.objects.using(db_alias).filter(content_type=old_ct)
# Create equivalent permissions for the new content type
for old_perm in old_perms:
new_perm = (
Permission.objects.using(db_alias)
.filter(
content_type=new_ct,
codename=old_perm.codename,
)
.first()
)
if not new_perm:
# This should exist at this point, but if not, let's cut our losses
continue
# Global user permissions
User = apps.get_model("authentik_core", "User")
User.user_permissions.through.objects.using(db_alias).filter(
permission=old_perm
).all().update(permission=new_perm)
# Global role permissions
DjangoGroup = apps.get_model("auth", "Group")
DjangoGroup.permissions.through.objects.using(db_alias).filter(
permission=old_perm
).all().update(permission=new_perm)
# Object user permissions
UserObjectPermission = apps.get_model("guardian", "UserObjectPermission")
UserObjectPermission.objects.using(db_alias).filter(permission=old_perm).all().update(
permission=new_perm, content_type=new_ct
)
# Object role permissions
GroupObjectPermission = apps.get_model("guardian", "GroupObjectPermission")
GroupObjectPermission.objects.using(db_alias).filter(permission=old_perm).all().update(
permission=new_perm, content_type=new_ct
)
def remove_old_authenticated_session_content_type(
apps: Apps, schema_editor: BaseDatabaseSchemaEditor
):
@ -21,7 +92,12 @@ class Migration(migrations.Migration):
]
operations = [
migrations.RunPython(
code=migrate_authenticated_session_permissions,
reverse_code=migrations.RunPython.noop,
),
migrations.RunPython(
code=remove_old_authenticated_session_content_type,
reverse_code=migrations.RunPython.noop,
),
]

View File

@ -21,9 +21,7 @@
<script src="{% versioned_script 'dist/standalone/loading/index-%v.js' %}" type="module"></script>
{% block head %}
{% endblock %}
{% for key, value in html_meta.items %}
<meta name="{{key}}" content="{{ value }}" />
{% endfor %}
<meta name="sentry-trace" content="{{ sentry_trace }}" />
</head>
<body>
{% block body %}

View File

@ -57,7 +57,7 @@ class LogEventSerializer(PassiveSerializer):
@contextmanager
def capture_logs(log_default_output=True) -> Generator[list[LogEvent]]:
def capture_logs(log_default_output=True) -> Generator[list[LogEvent], None, None]:
"""Capture log entries created"""
logs = []
cap = LogCapture()

View File

@ -7,7 +7,7 @@
{{ block.super }}
<link rel="prefetch" href="{{ flow_background_url }}" />
{% if flow.compatibility_mode and not inspector %}
<script>ShadyDOM = { force: true };</script>
<script>ShadyDOM = { force: !navigator.webdriver };</script>
{% endif %}
{% include "base/header_js.html" %}
<script>

View File

@ -17,7 +17,7 @@ from ldap3.core.exceptions import LDAPException
from redis.exceptions import ConnectionError as RedisConnectionError
from redis.exceptions import RedisError, ResponseError
from rest_framework.exceptions import APIException
from sentry_sdk import HttpTransport, get_current_scope
from sentry_sdk import HttpTransport
from sentry_sdk import init as sentry_sdk_init
from sentry_sdk.api import set_tag
from sentry_sdk.integrations.argv import ArgvIntegration
@ -27,7 +27,6 @@ from sentry_sdk.integrations.redis import RedisIntegration
from sentry_sdk.integrations.socket import SocketIntegration
from sentry_sdk.integrations.stdlib import StdlibIntegration
from sentry_sdk.integrations.threading import ThreadingIntegration
from sentry_sdk.tracing import BAGGAGE_HEADER_NAME, SENTRY_TRACE_HEADER_NAME
from structlog.stdlib import get_logger
from websockets.exceptions import WebSocketException
@ -96,8 +95,6 @@ def traces_sampler(sampling_context: dict) -> float:
return 0
if _type == "websocket":
return 0
if CONFIG.get_bool("debug"):
return 1
return float(CONFIG.get("error_reporting.sample_rate", 0.1))
@ -170,14 +167,3 @@ def before_send(event: dict, hint: dict) -> dict | None:
if settings.DEBUG:
return None
return event
def get_http_meta():
"""Get sentry-related meta key-values"""
scope = get_current_scope()
meta = {
SENTRY_TRACE_HEADER_NAME: scope.get_traceparent() or "",
}
if bag := scope.get_baggage():
meta[BAGGAGE_HEADER_NAME] = bag.serialize()
return meta

View File

@ -59,7 +59,7 @@ class PropertyMappingManager:
request: HttpRequest | None,
return_mapping: bool = False,
**kwargs,
) -> Generator[tuple[dict, PropertyMapping]]:
) -> Generator[tuple[dict, PropertyMapping], None]:
"""Iterate over all mappings that were pre-compiled and
execute all of them with the given context"""
if not self.__has_compiled:

View File

@ -66,7 +66,10 @@ class RACClientConsumer(AsyncWebsocketConsumer):
def init_outpost_connection(self):
"""Initialize guac connection settings"""
self.token = (
ConnectionToken.filter_not_expired(token=self.scope["url_route"]["kwargs"]["token"])
ConnectionToken.filter_not_expired(
token=self.scope["url_route"]["kwargs"]["token"],
session__session__session_key=self.scope["session"].session_key,
)
.select_related("endpoint", "provider", "session", "session__user")
.first()
)

View File

@ -87,3 +87,22 @@ class TestRACViews(APITestCase):
)
body = loads(flow_response.content)
self.assertEqual(body["component"], "ak-stage-access-denied")
def test_different_session(self):
"""Test request"""
self.client.force_login(self.user)
response = self.client.get(
reverse(
"authentik_providers_rac:start",
kwargs={"app": self.app.slug, "endpoint": str(self.endpoint.pk)},
)
)
self.assertEqual(response.status_code, 302)
flow_response = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
)
body = loads(flow_response.content)
next_url = body["to"]
self.client.logout()
final_response = self.client.get(next_url)
self.assertEqual(final_response.url, reverse("authentik_core:if-user"))

View File

@ -65,7 +65,10 @@ class RACInterface(InterfaceView):
def dispatch(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
# Early sanity check to ensure token still exists
token = ConnectionToken.filter_not_expired(token=self.kwargs["token"]).first()
token = ConnectionToken.filter_not_expired(
token=self.kwargs["token"],
session__session__session_key=request.session.session_key,
).first()
if not token:
return redirect("authentik_core:if-user")
self.token = token

View File

@ -199,7 +199,7 @@ class SCIMGroupClient(SCIMClient[Group, SCIMProviderGroup, SCIMGroupSchema]):
chunk_size = len(ops)
if len(ops) < 1:
return
for chunk in batched(ops, chunk_size, strict=False):
for chunk in batched(ops, chunk_size):
req = PatchRequest(Operations=list(chunk))
self._request(
"PATCH",

View File

@ -97,7 +97,8 @@ class GroupsView(SCIMObjectView):
self.logger.warning("Invalid group member", exc=exc)
continue
query |= Q(uuid=member.value)
group.users.set(User.objects.filter(query))
if query:
group.users.set(User.objects.filter(query))
if not connection:
connection, _ = SCIMSourceGroup.objects.get_or_create(
source=self.source,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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.4.1 Blueprint schema",
"title": "authentik 2025.4.3 Blueprint schema",
"required": [
"version",
"entries"

View File

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

16
go.mod
View File

@ -5,7 +5,7 @@ go 1.24.0
require (
beryju.io/ldap v0.1.0
github.com/coreos/go-oidc/v3 v3.14.1
github.com/getsentry/sentry-go v0.33.0
github.com/getsentry/sentry-go v0.32.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
@ -19,18 +19,18 @@ require (
github.com/jellydator/ttlcache/v3 v3.3.0
github.com/mitchellh/mapstructure v1.5.0
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484
github.com/pires/go-proxyproto v0.8.1
github.com/pires/go-proxyproto v0.8.0
github.com/prometheus/client_golang v1.22.0
github.com/redis/go-redis/v9 v9.8.0
github.com/sethvargo/go-envconfig v1.3.0
github.com/redis/go-redis/v9 v9.7.3
github.com/sethvargo/go-envconfig v1.2.0
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.9.1
github.com/stretchr/testify v1.10.0
github.com/wwt/guac v1.3.2
goauthentik.io/api/v3 v3.2025041.1
goauthentik.io/api/v3 v3.2025024.9
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
golang.org/x/oauth2 v0.30.0
golang.org/x/sync v0.14.0
golang.org/x/oauth2 v0.29.0
golang.org/x/sync v0.13.0
gopkg.in/yaml.v2 v2.4.0
layeh.com/radius v0.0.0-20210819152912-ad72663a72ab
)
@ -75,7 +75,7 @@ require (
go.opentelemetry.io/otel/trace v1.24.0 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/text v0.24.0 // indirect
golang.org/x/text v0.23.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

36
go.sum
View File

@ -69,8 +69,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
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.33.0 h1:YWyDii0KGVov3xOaamOnF0mjOrqSjBqwv48UEzn7QFg=
github.com/getsentry/sentry-go v0.33.0/go.mod h1:C55omcY9ChRQIUcVcGcs+Zdy4ZpQGvNJ7JYHIoSWOtE=
github.com/getsentry/sentry-go v0.32.0 h1:YKs+//QmwE3DcYtfKRH8/KyOOF/I6Qnx7qYGNHCGmCY=
github.com/getsentry/sentry-go v0.32.0/go.mod h1:CYNcMMz73YigoHljQRG+qPF+eMq8gG72XcGN/p71BAY=
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=
@ -230,8 +230,8 @@ github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0=
github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
github.com/pires/go-proxyproto v0.8.0 h1:5unRmEAPbHXHuLjDg01CxJWf91cw3lKHc/0xzKpXEe0=
github.com/pires/go-proxyproto v0.8.0/go.mod h1:iknsfgnH8EkjrMeMyvfKByp9TiBZCKZM0jx2xmKqnVY=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@ -245,14 +245,14 @@ github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI=
github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sethvargo/go-envconfig v1.3.0 h1:gJs+Fuv8+f05omTpwWIu6KmuseFAXKrIaOZSh8RMt0U=
github.com/sethvargo/go-envconfig v1.3.0/go.mod h1:JLd0KFWQYzyENqnEPWWZ49i4vzZo/6nRidxI8YvGiHw=
github.com/sethvargo/go-envconfig v1.2.0 h1:q3XkOZWkC+G1sMLCrw9oPGTjYexygLOXDmGUit1ti8Q=
github.com/sethvargo/go-envconfig v1.2.0/go.mod h1:JLd0KFWQYzyENqnEPWWZ49i4vzZo/6nRidxI8YvGiHw=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
@ -290,8 +290,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.2025041.1 h1:GAN6AoTmfnCGgx1SyM07jP4/LR/T3rkTEyShSBd3Co8=
goauthentik.io/api/v3 v3.2025041.1/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
goauthentik.io/api/v3 v3.2025024.9 h1:i3tbkyotE32ZpJ729BsPWTuLQUdtZ54Li4aP1amZzsM=
goauthentik.io/api/v3 v3.2025024.9/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -358,16 +358,16 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -376,8 +376,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -412,8 +412,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=

View File

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

View File

@ -83,7 +83,8 @@ if [[ "$1" == "server" ]]; then
run_authentik
elif [[ "$1" == "worker" ]]; then
set_mode "worker"
check_if_root "python -m manage worker"
shift
check_if_root "python -m manage worker $@"
elif [[ "$1" == "worker-status" ]]; then
wait_for_db
celery -A authentik.root.celery flower \

View File

@ -9,7 +9,7 @@
"version": "0.0.0",
"license": "MIT",
"devDependencies": {
"aws-cdk": "^2.1015.0",
"aws-cdk": "^2.1012.0",
"cross-env": "^7.0.3"
},
"engines": {
@ -17,9 +17,9 @@
}
},
"node_modules/aws-cdk": {
"version": "2.1015.0",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1015.0.tgz",
"integrity": "sha512-txd+yMVVybtLfiwT409+fahbP0SkiwhmQvQf6PVVYnWzDPSknxYlUNJHisHV4tJEcbHWn1QPsLmqqMT0bw8hBg==",
"version": "2.1012.0",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1012.0.tgz",
"integrity": "sha512-C6jSWkqP0hkY2Cs300VJHjspmTXDTMfB813kwZvRbd/OsKBfTBJBbYU16VoLAp1LVEOnQMf8otSlaSgzVF0X9A==",
"dev": true,
"license": "Apache-2.0",
"bin": {

View File

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

View File

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

Binary file not shown.

View File

@ -12,8 +12,8 @@
# tmassimi, 2024
# Marc Schmitt, 2024
# albanobattistella <albanobattistella@gmail.com>, 2024
# Matteo Piccina <altermatte@gmail.com>, 2025
# Kowalski Dragon (kowalski7cc) <kowalski.7cc@gmail.com>, 2025
# Matteo Piccina <altermatte@gmail.com>, 2025
#
#, fuzzy
msgid ""
@ -22,7 +22,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-04-23 09:00+0000\n"
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
"Last-Translator: Kowalski Dragon (kowalski7cc) <kowalski.7cc@gmail.com>, 2025\n"
"Last-Translator: Matteo Piccina <altermatte@gmail.com>, 2025\n"
"Language-Team: Italian (https://app.transifex.com/authentik/teams/119923/it/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@ -383,7 +383,7 @@ msgstr "Mappatura delle proprietà"
#: authentik/core/models.py
msgid "session data"
msgstr "dati sessione"
msgstr ""
#: authentik/core/models.py
msgid "Session"
@ -509,7 +509,7 @@ msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "Number of passwords to check against."
msgstr "Numero di password da verificare."
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py
@ -519,19 +519,18 @@ msgstr "Password non impostata nel contesto"
#: authentik/enterprise/policies/unique_password/models.py
msgid "This password has been used previously. Please choose a different one."
msgstr ""
"Questa password è già stata utilizzata in precedenza. Scegline una diversa."
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policy"
msgstr "Politica di unicità della password"
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policies"
msgstr "Criteri di unicità delle password"
msgstr ""
#: authentik/enterprise/policies/unique_password/models.py
msgid "User Password History"
msgstr "Cronologia password utente"
msgstr ""
#: authentik/enterprise/policy.py
msgid "Enterprise required to access this feature."
@ -2204,7 +2203,7 @@ msgstr "Ruoli"
#: authentik/rbac/models.py
msgid "Initial Permissions"
msgstr "Permessi Iniziali"
msgstr ""
#: authentik/rbac/models.py
msgid "System permission"
@ -2459,9 +2458,6 @@ msgid ""
"attribute. This allows nested group resolution on systems like FreeIPA and "
"Active Directory"
msgstr ""
"Cerca l'appartenenza al gruppo in base a un attributo utente anziché a un "
"attributo di gruppo. Questo consente la risoluzione di gruppi nidificati su "
"sistemi come FreeIPA e Active Directory."
#: authentik/sources/ldap/models.py
msgid "LDAP Source"
@ -2481,19 +2477,19 @@ msgstr "Mappature delle proprietà della sorgente LDAP"
#: authentik/sources/ldap/models.py
msgid "User LDAP Source Connection"
msgstr "Connessione Sorgente LDAP Utente"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "User LDAP Source Connections"
msgstr "Connessioni Sorgente LDAP Utente"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "Group LDAP Source Connection"
msgstr "Connessione Sorgente LDAP Gruppo"
msgstr ""
#: authentik/sources/ldap/models.py
msgid "Group LDAP Source Connections"
msgstr "Connessioni Sorgente LDAP Gruppo"
msgstr ""
#: authentik/sources/ldap/signals.py
msgid "Password does not match Active Directory Complexity."
@ -2505,11 +2501,11 @@ msgstr "Nessun token ricevuto."
#: authentik/sources/oauth/models.py
msgid "HTTP Basic Authentication"
msgstr "HTTP Basic Authentication"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "Include the client ID and secret as request parameters"
msgstr "Includi il client ID e il segreto come parametri di richiesta"
msgstr ""
#: authentik/sources/oauth/models.py
msgid "Request Token URL"
@ -2556,8 +2552,6 @@ msgid ""
"How to perform authentication during an authorization_code token request "
"flow"
msgstr ""
"Come eseguire l'autenticazione durante un flusso di richiesta del token "
"authorization_code"
#: authentik/sources/oauth/models.py
msgid "OAuth Source"
@ -3490,9 +3484,6 @@ msgid ""
"Show the user the 'Remember me on this device' toggle, allowing repeat users"
" to skip straight to entering their password."
msgstr ""
"Mostra all'utente il pulsante \"Ricordami su questo dispositivo\", "
"consentendo agli utenti abituali di passare direttamente all'inserimento "
"della password."
#: authentik/stages/identification/models.py
msgid "Optional enrollment flow, which is linked at the bottom of the page."
@ -3882,11 +3873,11 @@ msgstr ""
#: authentik/tenants/models.py
msgid "Reputation cannot decrease lower than this value. Zero or negative."
msgstr "La reputazione non può scendere sotto questo valore. Zero o negativo."
msgstr ""
#: authentik/tenants/models.py
msgid "Reputation cannot increase higher than this value. Zero or positive."
msgstr "La reputazione non può superare questo valore. Zero o positivo."
msgstr ""
#: authentik/tenants/models.py
msgid "The option configures the footer links on the flow executor pages."

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

538
package-lock.json generated
View File

@ -1,546 +1,12 @@
{
"name": "@goauthentik/authentik",
"version": "2025.4.0",
"version": "2025.2.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@goauthentik/authentik",
"version": "2025.4.0",
"devDependencies": {
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
"prettier": "^3.3.3",
"prettier-plugin-organize-imports": "^4.1.0",
"prettier-plugin-packagejson": "^2.5.10",
"typescript": "^5.6.2"
}
},
"node_modules/@babel/code-frame": {
"version": "7.26.2",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
"integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-validator-identifier": "^7.25.9",
"js-tokens": "^4.0.0",
"picocolors": "^1.0.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/generator": {
"version": "7.27.0",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz",
"integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.27.0",
"@babel/types": "^7.27.0",
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25",
"jsesc": "^3.0.2"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-string-parser": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
"integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
"integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/parser": {
"version": "7.27.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz",
"integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.27.0"
},
"bin": {
"parser": "bin/babel-parser.js"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@babel/template": {
"version": "7.27.0",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz",
"integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.26.2",
"@babel/parser": "^7.27.0",
"@babel/types": "^7.27.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
"version": "7.27.0",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz",
"integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.26.2",
"@babel/generator": "^7.27.0",
"@babel/parser": "^7.27.0",
"@babel/template": "^7.27.0",
"@babel/types": "^7.27.0",
"debug": "^4.3.1",
"globals": "^11.1.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/types": {
"version": "7.27.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
"integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.25.9",
"@babel/helper-validator-identifier": "^7.25.9"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.8",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
"integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/set-array": "^1.2.1",
"@jridgewell/sourcemap-codec": "^1.4.10",
"@jridgewell/trace-mapping": "^0.3.24"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/set-array": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.25",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@pkgr/core": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.2.tgz",
"integrity": "sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^12.20.0 || ^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/unts"
}
},
"node_modules/@trivago/prettier-plugin-sort-imports": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-5.2.2.tgz",
"integrity": "sha512-fYDQA9e6yTNmA13TLVSA+WMQRc5Bn/c0EUBditUHNfMMxN7M82c38b1kEggVE3pLpZ0FwkwJkUEKMiOi52JXFA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@babel/generator": "^7.26.5",
"@babel/parser": "^7.26.7",
"@babel/traverse": "^7.26.7",
"@babel/types": "^7.26.7",
"javascript-natural-sort": "^0.7.1",
"lodash": "^4.17.21"
},
"engines": {
"node": ">18.12"
},
"peerDependencies": {
"@vue/compiler-sfc": "3.x",
"prettier": "2.x - 3.x",
"prettier-plugin-svelte": "3.x",
"svelte": "4.x || 5.x"
},
"peerDependenciesMeta": {
"@vue/compiler-sfc": {
"optional": true
},
"prettier-plugin-svelte": {
"optional": true
},
"svelte": {
"optional": true
}
}
},
"node_modules/debug": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/detect-indent": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-7.0.1.tgz",
"integrity": "sha512-Mc7QhQ8s+cLrnUfU/Ji94vG/r8M26m8f++vyres4ZoojaRDpZ1eSIh/EpzLNwlWuvzSZ3UbDFspjFvTDXe6e/g==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12.20"
}
},
"node_modules/detect-newline": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-4.0.1.tgz",
"integrity": "sha512-qE3Veg1YXzGHQhlA6jzebZN2qVf6NX+A7m7qlhCGG30dJixrAQhYOsJjsnBjJkCSmuOPpCk30145fr8FV0bzog==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/fdir": {
"version": "6.4.4",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
"integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
}
},
"node_modules/get-stdin": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz",
"integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/git-hooks-list": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-3.2.0.tgz",
"integrity": "sha512-ZHG9a1gEhUMX1TvGrLdyWb9kDopCBbTnI8z4JgRMYxsijWipgjSEYoPWqBuIB0DnRnvqlQSEeVmzpeuPm7NdFQ==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/fisker/git-hooks-list?sponsor=1"
}
},
"node_modules/globals": {
"version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/is-plain-obj": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
"integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/javascript-natural-sort": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz",
"integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==",
"dev": true,
"license": "MIT"
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true,
"license": "MIT"
},
"node_modules/jsesc": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
"dev": true,
"license": "MIT",
"bin": {
"jsesc": "bin/jsesc"
},
"engines": {
"node": ">=6"
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true,
"license": "MIT"
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true,
"license": "MIT"
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true,
"license": "ISC"
},
"node_modules/picomatch": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/prettier": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
"integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
"dev": true,
"license": "MIT",
"bin": {
"prettier": "bin/prettier.cjs"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/prettier-plugin-organize-imports": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.1.0.tgz",
"integrity": "sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"prettier": ">=2.0",
"typescript": ">=2.9",
"vue-tsc": "^2.1.0"
},
"peerDependenciesMeta": {
"vue-tsc": {
"optional": true
}
}
},
"node_modules/prettier-plugin-packagejson": {
"version": "2.5.10",
"resolved": "https://registry.npmjs.org/prettier-plugin-packagejson/-/prettier-plugin-packagejson-2.5.10.tgz",
"integrity": "sha512-LUxATI5YsImIVSaaLJlJ3aE6wTD+nvots18U3GuQMJpUyClChaZlQrqx3dBnbhF20OnKWZyx8EgyZypQtBDtgQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"sort-package-json": "2.15.1",
"synckit": "0.9.2"
},
"peerDependencies": {
"prettier": ">= 1.16.0"
},
"peerDependenciesMeta": {
"prettier": {
"optional": true
}
}
},
"node_modules/semver": {
"version": "7.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
"dev": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/sort-object-keys": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-1.1.3.tgz",
"integrity": "sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg==",
"dev": true,
"license": "MIT"
},
"node_modules/sort-package-json": {
"version": "2.15.1",
"resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-2.15.1.tgz",
"integrity": "sha512-9x9+o8krTT2saA9liI4BljNjwAbvUnWf11Wq+i/iZt8nl2UGYnf3TH5uBydE7VALmP7AGwlfszuEeL8BDyb0YA==",
"dev": true,
"license": "MIT",
"dependencies": {
"detect-indent": "^7.0.1",
"detect-newline": "^4.0.0",
"get-stdin": "^9.0.0",
"git-hooks-list": "^3.0.0",
"is-plain-obj": "^4.1.0",
"semver": "^7.6.0",
"sort-object-keys": "^1.1.3",
"tinyglobby": "^0.2.9"
},
"bin": {
"sort-package-json": "cli.js"
}
},
"node_modules/synckit": {
"version": "0.9.2",
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz",
"integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@pkgr/core": "^0.1.0",
"tslib": "^2.6.2"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/unts"
}
},
"node_modules/tinyglobby": {
"version": "0.2.13",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",
"integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
"dev": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.4.4",
"picomatch": "^4.0.2"
},
"engines": {
"node": ">=12.0.0"
},
"funding": {
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"dev": true,
"license": "0BSD"
},
"node_modules/typescript": {
"version": "5.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
"version": "2025.2.1"
}
}
}

View File

@ -1,15 +1,5 @@
{
"name": "@goauthentik/authentik",
"version": "2025.4.1",
"private": true,
"type": "module",
"devDependencies": {
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
"prettier": "^3.3.3",
"prettier-plugin-organize-imports": "^4.1.0",
"prettier-plugin-packagejson": "^2.5.10",
"typescript": "^5.6.2"
},
"workspaces": [],
"prettier": "./packages/prettier-config/index.js"
"version": "2025.4.3",
"private": true
}

View File

@ -1,12 +1,12 @@
{
"name": "@goauthentik/docusaurus-config",
"version": "1.0.6",
"version": "1.0.5",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@goauthentik/docusaurus-config",
"version": "1.0.6",
"version": "1.0.5",
"license": "MIT",
"dependencies": {
"deepmerge-ts": "^7.1.5",

View File

@ -1,6 +1,6 @@
{
"name": "@goauthentik/docusaurus-config",
"version": "1.0.6",
"version": "1.0.5",
"description": "authentik's Docusaurus config",
"license": "MIT",
"scripts": {

View File

@ -2,3 +2,4 @@
This package contains utility scripts common to all TypeScript and JavaScript packages in the
`@goauthentik` monorepo.

View File

@ -0,0 +1,17 @@
/**
* @file Constants for JavaScript and TypeScript files.
*
*/
/**
* The current Node.js environment, defaulting to "development" when not set.
*
* Note, this should only be used during the build process.
*
* If you need to check the environment at runtime, use `process.env.NODE_ENV` to
* ensure that module tree-shaking works correctly.
*
*/
export const NodeEnvironment = /** @type {'development' | 'production'} */ (
process.env.NODE_ENV || "development"
);

View File

@ -1,6 +1,4 @@
/// <reference types="./types/global.js" />
export * from "./paths.js";
export * from "./environment.js";
export * from "./constants.js";
export * from "./version.js";
export * from "./scripting.js";

View File

@ -0,0 +1,19 @@
{
"name": "@goauthentik/monorepo",
"version": "1.0.0",
"description": "Utilities for the authentik monorepo.",
"private": true,
"license": "MIT",
"type": "module",
"exports": {
"./package.json": "./package.json",
".": {
"import": "./index.js",
"types": "./out/index.d.ts"
}
},
"types": "./out/index.d.ts",
"engines": {
"node": ">=20.11"
}
}

View File

@ -0,0 +1,30 @@
import { createRequire } from "node:module";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = dirname(fileURLToPath(import.meta.url));
/**
* @typedef {'~authentik'} MonoRepoRoot
*/
/**
* The root of the authentik monorepo.
*/
export const MonoRepoRoot = /** @type {MonoRepoRoot} */ (resolve(__dirname, "..", ".."));
const require = createRequire(import.meta.url);
/**
* Resolve a package name to its location in the monorepo to the single node_modules directory.
* @param {string} packageName
* @returns {string} The resolved path to the package.
* @throws {Error} If the package cannot be resolved.
*/
export function resolvePackage(packageName) {
const packageJSONPath = require.resolve(join(packageName, "package.json"), {
paths: [MonoRepoRoot],
});
return dirname(packageJSONPath);
}

View File

View File

@ -1,6 +1,6 @@
import { execSync } from "node:child_process";
import PackageJSON from "../../../package.json" with { type: "json" };
import PackageJSON from "../../package.json" with { type: "json" };
import { MonoRepoRoot } from "./paths.js";
/**

View File

@ -1,116 +1,104 @@
[project]
name = "authentik"
version = "2025.4.1"
version = "2025.4.3"
description = ""
authors = [{ name = "authentik Team", email = "hello@goauthentik.io" }]
requires-python = "==3.13.*"
requires-python = "==3.12.*"
dependencies = [
"argon2-cffi==23.1.0",
"celery==5.5.2",
"channels==4.2.2",
"channels-redis==4.2.1",
"cryptography==44.0.3",
"dacite==1.9.2",
"deepmerge==2.0",
"defusedxml==0.7.1",
"django==5.1.9",
"django-countries==7.6.1",
"django-cte==1.3.3",
"django-filter==25.1",
"django-guardian<3.0.0",
"django-model-utils==5.0.0",
"django-pglock==1.7.2",
"django-prometheus==2.3.1",
"django-redis==5.4.0",
"django-storages[s3]==1.14.6",
"django-tenants==3.7.0",
"djangorestframework==3.16.0",
"djangorestframework-guardian==0.3.0",
"docker==7.1.0",
"drf-orjson-renderer==1.7.3",
"drf-spectacular==0.28.0",
"dumb-init==1.2.5.post1",
"duo-client==5.5.0",
"fido2==1.2.0",
"flower==2.0.1",
"geoip2==5.1.0",
"geopy==2.4.1",
"google-api-python-client==2.169.0",
"gssapi==1.9.0",
"gunicorn==23.0.0",
"jsonpatch==1.33",
"jwcrypto==1.5.6",
"kubernetes==32.0.1",
"ldap3==2.9.1",
"lxml==5.4.0",
"msgraph-sdk==1.30.0",
"opencontainers==0.0.14",
"packaging==25.0",
"paramiko==3.5.1",
"psycopg[c,pool]==3.2.9",
"pydantic==2.11.4",
"pydantic-scim==0.0.8",
"pyjwt==2.10.1",
"pyrad==2.4",
"python-kadmin-rs==0.6.0",
"pyyaml==6.0.2",
"requests-oauthlib==2.0.0",
"scim2-filter-parser==0.7.0",
"sentry-sdk==2.28.0",
"service-identity==24.2.0",
"setproctitle==1.3.6",
"structlog==25.3.0",
"swagger-spec-validator==3.0.4",
"tenant-schemas-celery==4.0.1",
"twilio==9.6.1",
"ua-parser==1.0.1",
"unidecode==1.4.0",
"urllib3<3",
"uvicorn[standard]==0.34.2",
"watchdog==6.0.0",
"webauthn==2.5.2",
"wsproto==1.2.0",
"xmlsec==1.3.15",
"zxcvbn==4.5.0",
"argon2-cffi",
"celery",
"channels",
"channels-redis",
"cryptography",
"dacite",
"deepmerge",
"defusedxml",
"django",
"django-countries",
"django-cte",
"django-filter",
"django-guardian",
"django-model-utils",
"django-pglock",
"django-prometheus",
"django-redis",
"django-storages[s3]",
"django-tenants",
"djangorestframework",
"djangorestframework-guardian",
"docker",
"drf-orjson-renderer",
"drf-spectacular",
"dumb-init",
"duo-client",
"fido2",
"flower",
"geoip2",
"geopy",
"google-api-python-client",
"gssapi",
"gunicorn",
"jsonpatch",
"jwcrypto",
"kubernetes",
"ldap3",
"lxml",
"msgraph-sdk",
"opencontainers",
"packaging",
"paramiko",
"psycopg[c, pool]",
"pydantic",
"pydantic-scim",
"pyjwt",
"pyrad",
"python-kadmin-rs ==0.6.0",
"pyyaml",
"requests-oauthlib",
"scim2-filter-parser",
"sentry-sdk",
"service_identity",
"setproctitle",
"structlog",
"swagger-spec-validator",
"tenant-schemas-celery",
"twilio",
"ua-parser",
"unidecode",
"urllib3 <3",
"uvicorn[standard]",
"watchdog",
"webauthn",
"wsproto",
"xmlsec <= 1.3.14",
"zxcvbn",
]
[dependency-groups]
dev = [
"aws-cdk-lib==2.188.0",
"bandit==1.8.3",
"black==25.1.0",
"bump2version==1.0.1",
"channels[daphne]==4.2.2",
"codespell==2.4.1",
"colorama==0.4.6",
"constructs==10.4.2",
"coverage[toml]==7.8.0",
"debugpy==1.8.14",
"drf-jsonschema-serializer==3.0.0",
"freezegun==1.5.1",
"importlib-metadata==8.6.1",
"k5test==0.10.4",
"pdoc==15.0.3",
"pytest==8.3.5",
"pytest-django==4.11.1",
"pytest-github-actions-annotate-failures==0.3.0",
"pytest-randomly==3.16.0",
"pytest-timeout==2.4.0",
"requests-mock==1.12.1",
"ruff==0.11.9",
"selenium==4.32.0",
]
[tool.uv]
no-binary-package = [
# This differs from the no-binary packages in the Dockerfile. This is due to the fact
# that these packages are built from source for different reasons than cryptography and kadmin.
# These packages are built from source to link against the libxml2 on the system which is
# required for functionality and to stay up-to-date on both libraries.
# The other packages specified in the dockerfile are compiled from source to link against the
# correct FIPS OpenSSL libraries
"lxml",
"xmlsec",
"aws-cdk-lib",
"bandit",
"black",
"bump2version",
"channels[daphne]",
"codespell",
"colorama",
"constructs",
"coverage[toml]",
"debugpy",
"drf-jsonschema-serializer",
"freezegun",
"importlib-metadata",
"k5test",
"pdoc",
"pytest",
"pytest-django",
"pytest-github-actions-annotate-failures",
"pytest-randomly",
"pytest-timeout",
"requests-mock",
"ruff",
"selenium",
]
[tool.uv.sources]
@ -155,12 +143,12 @@ ignore-words = ".github/codespell-words.txt"
[tool.black]
line-length = 100
target-version = ['py313']
target-version = ['py312']
exclude = 'node_modules'
[tool.ruff]
line-length = 100
target-version = "py313"
target-version = "py312"
exclude = ["**/migrations/**", "**/node_modules/**"]
[tool.ruff.lint]

View File

@ -1,7 +1,7 @@
openapi: 3.0.3
info:
title: authentik
version: 2025.4.1
version: 2025.4.3
description: Making authentication simple.
contact:
email: hello@goauthentik.io

View File

@ -1,12 +1,12 @@
services:
chrome:
image: docker.io/selenium/standalone-chrome:136.0
image: docker.io/selenium/standalone-chrome:122.0
volumes:
- /dev/shm:/dev/shm
network_mode: host
restart: always
mailpit:
image: docker.io/axllent/mailpit:v1.24.2
image: docker.io/axllent/mailpit:v1.6.5
ports:
- 1025:1025
- 8025:8025

View File

@ -1,19 +1,12 @@
"""test default login flow"""
from authentik.blueprints.tests import apply_blueprint
from authentik.flows.models import Flow
from tests.e2e.utils import SeleniumTestCase, retry
class TestFlowsLogin(SeleniumTestCase):
"""test default login flow"""
def tearDown(self):
# Reset authentication flow's compatibility mode; we need to do this as its
# not specified in the blueprint
Flow.objects.filter(slug="default-authentication-flow").update(compatibility_mode=False)
return super().tearDown()
@retry()
@apply_blueprint(
"default/flow-default-authentication-flow.yaml",
@ -30,21 +23,3 @@ class TestFlowsLogin(SeleniumTestCase):
self.login()
self.wait_for_url(self.if_user_url("/library"))
self.assert_user(self.user)
@retry()
@apply_blueprint(
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
def test_login_compatibility_mode(self):
"""test default login flow with compatibility mode enabled"""
Flow.objects.filter(slug="default-authentication-flow").update(compatibility_mode=True)
self.driver.get(
self.url(
"authentik_core:if-flow",
flow_slug="default-authentication-flow",
)
)
self.login(shadow_dom=False)
self.wait_for_url(self.if_user_url("/library"))
self.assert_user(self.user)

View File

@ -1,51 +0,0 @@
"""test default login (using SFE interface) flow"""
from time import sleep
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from authentik.blueprints.tests import apply_blueprint
from tests.e2e.utils import SeleniumTestCase, retry
class TestFlowsLoginSFE(SeleniumTestCase):
"""test default login flow"""
def login(self):
"""Do entire login flow adjusted for SFE"""
flow_executor = self.driver.find_element(By.ID, "flow-sfe-container")
identification_stage = flow_executor.find_element(By.ID, "ident-form")
identification_stage.find_element(By.CSS_SELECTOR, "input[name=uid_field]").click()
identification_stage.find_element(By.CSS_SELECTOR, "input[name=uid_field]").send_keys(
self.user.username
)
identification_stage.find_element(By.CSS_SELECTOR, "input[name=uid_field]").send_keys(
Keys.ENTER
)
password_stage = flow_executor.find_element(By.ID, "password-form")
password_stage.find_element(By.CSS_SELECTOR, "input[name=password]").send_keys(
self.user.username
)
password_stage.find_element(By.CSS_SELECTOR, "input[name=password]").send_keys(Keys.ENTER)
sleep(1)
@retry()
@apply_blueprint(
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
def test_login(self):
"""test default login flow"""
self.driver.get(
self.url(
"authentik_core:if-flow",
flow_slug="default-authentication-flow",
query={"sfe": True},
)
)
self.login()
self.wait_for_url(self.if_user_url("/library"))
self.assert_user(self.user)

View File

@ -26,10 +26,8 @@ from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException, TimeoutException, WebDriverException
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.remote.command import Command
from selenium.webdriver.remote.webdriver import WebDriver
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.support.wait import WebDriverWait
from structlog.stdlib import get_logger
@ -38,8 +36,8 @@ from authentik.core.models import User
from authentik.core.tests.utils import create_test_admin_user
from authentik.lib.generators import generate_id
RETRIES = int(environ.get("RETRIES", "3"))
IS_CI = "CI" in environ
RETRIES = int(environ.get("RETRIES", "3")) if IS_CI else 1
def get_docker_tag() -> str:
@ -199,12 +197,7 @@ class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase):
super().tearDown()
if IS_CI:
print("::group::Browser logs")
# Very verbose way to get browser logs
# https://github.com/SeleniumHQ/selenium/pull/15641
# for some reason this removes the `get_log` API from Remote Webdriver
# and only keeps it on the local Chrome web driver, even when using
# a remote chrome driver...? (nvm the fact this was released as a minor version)
for line in self.driver.execute(Command.GET_LOG, {"type": "browser"})["value"]:
for line in self.driver.get_log("browser"):
print(line["message"])
if IS_CI:
print("::endgroup::")
@ -241,30 +234,10 @@ class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase):
element = self.driver.execute_script("return arguments[0].shadowRoot", shadow_root)
return element
def shady_dom(self) -> WebElement:
class wrapper:
def __init__(self, container: WebDriver):
self.container = container
def find_element(self, by: str, selector: str) -> WebElement:
return self.container.execute_script(
"return document.__shady_native_querySelector(arguments[0])", selector
)
return wrapper(self.driver)
def login(self, shadow_dom=True):
"""Do entire login flow"""
if shadow_dom:
flow_executor = self.get_shadow_root("ak-flow-executor")
identification_stage = self.get_shadow_root("ak-stage-identification", flow_executor)
else:
flow_executor = self.shady_dom()
identification_stage = self.shady_dom()
wait = WebDriverWait(identification_stage, self.wait_timeout)
wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "input[name=uidField]")))
def login(self):
"""Do entire login flow and check user afterwards"""
flow_executor = self.get_shadow_root("ak-flow-executor")
identification_stage = self.get_shadow_root("ak-stage-identification", flow_executor)
identification_stage.find_element(By.CSS_SELECTOR, "input[name=uidField]").click()
identification_stage.find_element(By.CSS_SELECTOR, "input[name=uidField]").send_keys(
@ -274,16 +247,8 @@ class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase):
Keys.ENTER
)
if shadow_dom:
flow_executor = self.get_shadow_root("ak-flow-executor")
password_stage = self.get_shadow_root("ak-stage-password", flow_executor)
else:
flow_executor = self.shady_dom()
password_stage = self.shady_dom()
wait = WebDriverWait(password_stage, self.wait_timeout)
wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "input[name=password]")))
flow_executor = self.get_shadow_root("ak-flow-executor")
password_stage = self.get_shadow_root("ak-stage-password", flow_executor)
password_stage.find_element(By.CSS_SELECTOR, "input[name=password]").send_keys(
self.user.username
)

View File

@ -1,28 +0,0 @@
// TypeScript Project Configuration
{
"extends": "./packages/tsconfig/tsconfig.json",
"compilerOptions": {
"baseUrl": "."
},
"watchOptions": {
"excludeDirectories": [
"**/.git", // Git
"**/.yarn", // Yarn
"**/.vscode", // VS Code
"**/.vscode-test-web", // VS Code Web Test
"**/dist", // Distributed build files
"**/out", // Output build files
"**/.drafts", // Drafts
"**/.github", // GitHub
"**/node_modules" // Node modules
]
},
// The root project has no sources of its own. By setting `files` to an empty
// list, TS won't automatically include all sources below root (the default).
"files": [],
"references": [
// Note that references are in the order we want them to be built.
// TODO: Left blank until TypeScript workspaces are complete.
]
}

2188
uv.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -2,11 +2,15 @@
node_modules
# don't lint build output (make sure it's set to your correct build folder name)
dist
out
# don't lint nyc coverage output
coverage
# Import order matters
poly.ts
src/locale-codes.ts
src/locales/
storybook-static/
# Prettier breaks the tsconfig file
tsconfig.json
.storybook/css-import-maps*
package.json
packages/**/package.json

View File

@ -0,0 +1,11 @@
import { create } from "@storybook/theming/create";
const isDarkMode = window.matchMedia("(prefers-color-scheme: dark)").matches;
export default create({
base: isDarkMode ? "dark" : "light",
brandTitle: "authentik Storybook",
brandUrl: "https://goauthentik.io",
brandImage: "https://goauthentik.io/img/icon_left_brand_colour.svg",
brandTarget: "_self",
});

View File

@ -1,63 +0,0 @@
/**
* @file Storybook configuration.
* @import { StorybookConfig } from "@storybook/web-components-vite";
* @import { InlineConfig, Plugin } from "vite";
*/
import { createBundleDefinitions } from "@goauthentik/web/scripts/esbuild/environment";
import postcssLit from "rollup-plugin-postcss-lit";
import tsconfigPaths from "vite-tsconfig-paths";
const CSSImportPattern = /import [\w$]+ from .+\.(css)/g;
const JavaScriptFilePattern = /\.m?(js|ts|tsx)$/;
/**
* @satisfies {Plugin<never>}
*/
const inlineCSSPlugin = {
name: "inline-css-plugin",
transform: (source, id) => {
if (!JavaScriptFilePattern.test(id)) return;
const code = source.replace(CSSImportPattern, (match) => {
return `${match}?inline`;
});
return {
code,
};
},
};
/**
* @satisfies {StorybookConfig}
*/
const config = {
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
addons: [
"@storybook/addon-controls",
"@storybook/addon-links",
"@storybook/addon-essentials",
"storybook-addon-mock",
],
framework: {
name: "@storybook/web-components-vite",
options: {},
},
docs: {
autodocs: "tag",
},
viteFinal({ plugins = [], ...config }) {
/**
* @satisfies {InlineConfig}
*/
const mergedConfig = {
...config,
define: createBundleDefinitions(),
plugins: [inlineCSSPlugin, ...plugins, postcssLit(), tsconfigPaths()],
};
return mergedConfig;
},
};
export default config;

81
web/.storybook/main.ts Normal file
View File

@ -0,0 +1,81 @@
import replace from "@rollup/plugin-replace";
import type { StorybookConfig } from "@storybook/web-components-vite";
import { cwd } from "process";
import modify from "rollup-plugin-modify";
import postcssLit from "rollup-plugin-postcss-lit";
import tsconfigPaths from "vite-tsconfig-paths";
export const isProdBuild = process.env.NODE_ENV === "production";
export const apiBasePath = process.env.AK_API_BASE_PATH || "";
const importInlinePatterns = [
'import AKGlobal from "(\\.\\./)*common/styles/authentik\\.css',
'import AKGlobal from "@goauthentik/common/styles/authentik\\.css',
'import PF.+ from "@patternfly/patternfly/\\S+\\.css',
'import ThemeDark from "@goauthentik/common/styles/theme-dark\\.css',
'import OneDark from "@goauthentik/common/styles/one-dark\\.css',
'import styles from "\\./LibraryPageImpl\\.css',
];
const importInlineRegexp = new RegExp(importInlinePatterns.map((a) => `(${a})`).join("|"));
const config: StorybookConfig = {
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
addons: [
"@storybook/addon-controls",
"@storybook/addon-links",
"@storybook/addon-essentials",
"storybook-addon-mock",
],
staticDirs: [
{
from: "../node_modules/@patternfly/patternfly/patternfly-base.css",
to: "@patternfly/patternfly/patternfly-base.css",
},
{
from: "../src/common/styles/authentik.css",
to: "@goauthentik/common/styles/authentik.css",
},
{
from: "../src/common/styles/theme-dark.css",
to: "@goauthentik/common/styles/theme-dark.css",
},
{
from: "../src/common/styles/one-dark.css",
to: "@goauthentik/common/styles/one-dark.css",
},
],
framework: {
name: "@storybook/web-components-vite",
options: {},
},
docs: {
autodocs: "tag",
},
async viteFinal(config) {
return {
...config,
plugins: [
modify({
find: importInlineRegexp,
replace: (match: RegExpMatchArray) => {
return `${match}?inline`;
},
}),
replace({
"process.env.NODE_ENV": JSON.stringify(
isProdBuild ? "production" : "development",
),
"process.env.CWD": JSON.stringify(cwd()),
"process.env.AK_API_BASE_PATH": JSON.stringify(apiBasePath),
"preventAssignment": true,
}),
...config.plugins,
postcssLit(),
tsconfigPaths(),
],
};
},
};
export default config;

View File

@ -1,38 +0,0 @@
/**
* @file Storybook manager configuration.
*
* @import { ThemeVarsPartial } from "storybook/internal/theming";
*/
import { createUIThemeEffect, resolveUITheme } from "@goauthentik/web/common/theme.ts";
import { addons } from "@storybook/manager-api";
import { create } from "@storybook/theming/create";
/**
* @satisfies {Partial<ThemeVarsPartial>}
*/
const baseTheme = {
brandTitle: "authentik Storybook",
brandUrl: "https://goauthentik.io",
brandImage: "https://goauthentik.io/img/icon_left_brand_colour.svg",
brandTarget: "_self",
};
const uiTheme = resolveUITheme();
addons.setConfig({
theme: create({
...baseTheme,
base: uiTheme,
}),
enableShortcuts: false,
});
createUIThemeEffect((nextUITheme) => {
addons.setConfig({
theme: create({
...baseTheme,
base: nextUITheme,
}),
enableShortcuts: false,
});
});

View File

@ -0,0 +1,9 @@
// .storybook/manager.js
import { addons } from "@storybook/manager-api";
import authentikTheme from "./authentikTheme";
addons.setConfig({
theme: authentikTheme,
enableShortcuts: false,
});

View File

@ -1,3 +1,5 @@
<link rel="stylesheet" href="@patternfly/patternfly/patternfly-base.css" />
<link rel="stylesheet" href="@goauthentik/common/styles/authentik.css" />
<style>
body {
overflow-y: scroll;

View File

@ -1,32 +0,0 @@
/// <reference types="../types/css.js" />
/**
* @file Storybook manager configuration.
*
* @import { Preview } from "@storybook/web-components";
*/
import { applyDocumentTheme } from "@goauthentik/web/common/theme.ts";
applyDocumentTheme();
/**
* @satisfies {Preview}
*/
const preview = {
parameters: {
options: {
storySort: {
method: "alphabetical",
},
},
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
},
};
export default preview;

30
web/.storybook/preview.ts Normal file
View File

@ -0,0 +1,30 @@
import type { Preview } from "@storybook/web-components";
import "@goauthentik/common/styles/authentik.css";
// import "@goauthentik/common/styles/theme-dark.css";
import "@patternfly/patternfly/components/Brand/brand.css";
import "@patternfly/patternfly/components/Page/page.css";
// .storybook/preview.js
import "@patternfly/patternfly/patternfly-base.css";
const preview: Preview = {
parameters: {
options: {
storySort: {
method: "alphabetical",
},
},
actions: { argTypesRegex: "^on[A-Z].*" },
cssUserPrefs: {
"prefers-color-scheme": "light",
},
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
},
};
export default preview;

3529
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,50 +1,6 @@
{
"name": "@goauthentik/web",
"version": "0.0.0",
"license": "MIT",
"private": true,
"scripts": {
"build": "wireit",
"build-locales": "wireit",
"build-locales:build": "wireit",
"build-proxy": "wireit",
"build:sfe": "wireit",
"esbuild:watch": "node scripts/build-web.mjs --watch",
"extract-locales": "wireit",
"format": "wireit",
"lint": "wireit",
"lint:imports": "wireit",
"lint:lockfile": "wireit",
"lint:nightmare": "wireit",
"lint:precommit": "wireit",
"lint:types": "wireit",
"lit-analyse": "wireit",
"precommit": "wireit",
"prettier": "wireit",
"prettier-check": "wireit",
"pseudolocalize": "wireit",
"storybook": "storybook dev -p 6006",
"storybook:build": "wireit",
"test": "wireit",
"test:e2e": "wireit",
"test:e2e:watch": "wireit",
"test:watch": "wireit",
"tsc": "wireit",
"watch": "run-s build-locales esbuild:watch"
},
"type": "module",
"exports": {
"./package.json": "./package.json",
"./paths": "./paths.js",
"./scripts/*": "./scripts/*.mjs",
"./elements/*": "./src/elements/*",
"./common/*": "./src/common/*",
"./components/*": "./src/components/*",
"./flow/*": "./src/flow/*",
"./locales/*": "./src/locales/*",
"./user/*": "./src/user/*",
"./admin/*": "./src/admin/*"
},
"dependencies": {
"@codemirror/lang-css": "^6.3.1",
"@codemirror/lang-html": "^6.4.9",
@ -56,7 +12,8 @@
"@floating-ui/dom": "^1.6.11",
"@formatjs/intl-listformat": "^7.5.7",
"@fortawesome/fontawesome-free": "^6.6.0",
"@goauthentik/api": "^2025.4.1-1747332783",
"@goauthentik/api": "^2025.2.4-1745519715",
"@lit-labs/ssr": "3.2.2",
"@lit/context": "^1.1.2",
"@lit/localize": "^0.12.2",
"@lit/reactive-element": "^2.0.4",
@ -97,7 +54,6 @@
"remark-gfm": "^4.0.1",
"remark-mdx-frontmatter": "^5.0.0",
"style-mod": "^4.1.2",
"trusted-types": "^2.0.0",
"ts-pattern": "^5.4.0",
"unist-util-visit": "^5.0.0",
"webcomponent-qr-code": "^1.2.0",
@ -106,20 +62,19 @@
"devDependencies": {
"@eslint/js": "^9.11.1",
"@goauthentik/esbuild-plugin-live-reload": "^1.0.4",
"@goauthentik/monorepo": "^1.0.0",
"@goauthentik/prettier-config": "^1.0.4",
"@goauthentik/tsconfig": "^1.0.4",
"@hcaptcha/types": "^1.0.4",
"@lit/localize-tools": "^0.8.0",
"@rollup/plugin-replace": "^6.0.1",
"@storybook/addon-essentials": "^8.6.12",
"@storybook/addon-links": "^8.6.12",
"@storybook/blocks": "^8.6.12",
"@storybook/experimental-addon-test": "^8.6.12",
"@storybook/manager-api": "^8.6.12",
"@storybook/test": "^8.6.12",
"@storybook/web-components": "^8.6.12",
"@storybook/web-components-vite": "^8.6.12",
"@storybook/addon-essentials": "^8.3.4",
"@storybook/addon-links": "^8.3.4",
"@storybook/api": "^7.6.17",
"@storybook/blocks": "^8.3.4",
"@storybook/builder-vite": "^8.3.4",
"@storybook/manager-api": "^8.3.4",
"@storybook/web-components": "^8.3.4",
"@storybook/web-components-vite": "^8.3.4",
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
"@types/chart.js": "^2.9.41",
"@types/codemirror": "^5.60.15",
@ -138,22 +93,24 @@
"@wdio/spec-reporter": "^9.1.2",
"chromedriver": "^131.0.1",
"esbuild": "^0.25.0",
"esbuild-plugin-copy": "^2.1.1",
"esbuild-plugin-polyfill-node": "^0.3.0",
"esbuild-plugins-node-modules-polyfill": "^1.7.0",
"eslint": "^9.11.1",
"eslint-plugin-lit": "^1.15.0",
"eslint-plugin-wc": "^2.1.1",
"github-slugger": "^2.0.0",
"glob": "^11.0.0",
"globals": "^15.10.0",
"knip": "^5.30.6",
"lit-analyzer": "^2.0.3",
"npm-run-all": "^4.1.5",
"prettier": "^3.3.3",
"pseudolocale": "^2.1.0",
"rollup-plugin-modify": "^3.0.0",
"rollup-plugin-postcss-lit": "^2.1.0",
"storybook": "^8.6.12",
"storybook": "^8.3.4",
"storybook-addon-mock": "^5.0.0",
"syncpack": "^13.0.0",
"turnstile-types": "^1.2.3",
"typescript": "^5.6.2",
"typescript-eslint": "^8.8.0",
@ -161,6 +118,10 @@
"vite-tsconfig-paths": "^5.0.1",
"wireit": "^0.14.9"
},
"engines": {
"node": ">=20"
},
"license": "MIT",
"optionalDependencies": {
"@esbuild/darwin-arm64": "^0.24.0",
"@esbuild/linux-amd64": "^0.18.11",
@ -169,6 +130,48 @@
"@rollup/rollup-linux-arm64-gnu": "4.23.0",
"@rollup/rollup-linux-x64-gnu": "4.23.0"
},
"overrides": {
"rapidoc": {
"@apitools/openapi-parser@": "0.0.37"
},
"chromedriver": {
"axios": "^1.8.4"
}
},
"prettier": "@goauthentik/prettier-config",
"private": true,
"scripts": {
"build": "wireit",
"build-locales": "wireit",
"build-locales:build": "wireit",
"build-proxy": "wireit",
"build:sfe": "wireit",
"esbuild:watch": "node scripts/build-web.mjs --watch",
"extract-locales": "wireit",
"format": "wireit",
"lint": "wireit",
"lint:imports": "wireit",
"lint:lockfile": "wireit",
"lint:nightmare": "wireit",
"lint:package": "wireit",
"lint:precommit": "wireit",
"lint:types": "wireit",
"lit-analyse": "wireit",
"postinstall": "bash scripts/patch-spotlight.sh",
"precommit": "wireit",
"prettier": "wireit",
"prettier-check": "wireit",
"pseudolocalize": "wireit",
"storybook": "storybook dev -p 6006",
"storybook:build": "wireit",
"test": "wireit",
"test:e2e": "wireit",
"test:e2e:watch": "wireit",
"test:watch": "wireit",
"tsc": "wireit",
"watch": "run-s build-locales esbuild:watch"
},
"type": "module",
"wireit": {
"build": {
"#comment": [
@ -245,7 +248,10 @@
"command": "lit-localize extract"
},
"format": {
"command": "prettier --write ."
"command": "prettier --write .",
"dependencies": [
"lint:package"
]
},
"format:packages": {
"dependencies": [
@ -284,6 +290,9 @@
"./packages/sfe:lint:lockfile"
]
},
"lint:package": {
"command": "syncpack format -i ' '"
},
"lint:nightmare": {
"command": "${NODE_RUNNER} ./scripts/eslint.mjs --nightmare",
"env": {
@ -314,6 +323,7 @@
"lint:types",
"lint:components",
"lint:spelling",
"lint:package",
"lint:lockfile",
"lint:lockfiles",
"lint:precommit",
@ -378,20 +388,8 @@
]
}
},
"engines": {
"node": ">=20"
},
"workspaces": [
".",
"./packages/*"
],
"prettier": "@goauthentik/prettier-config",
"overrides": {
"rapidoc": {
"@apitools/openapi-parser@": "0.0.37"
},
"chromedriver": {
"axios": "^1.8.4"
}
}
]
}

View File

@ -6,7 +6,7 @@
* @import { Message as ESBuildMessage } from "esbuild";
*/
const logPrefix = "authentik/dev/web: ";
const logPrefix = "👷 [ESBuild]";
const log = console.debug.bind(console, logPrefix);
/**
@ -21,7 +21,7 @@ const log = console.debug.bind(console, logPrefix);
* ESBuild may tree-shake it out of production builds.
*
* ```ts
* if (import.meta.env.NODE_ENV=== "development") {
* if (process.env.NODE_ENV === "development") {
* await import("@goauthentik/esbuild-plugin-live-reload/client")
* .catch(() => console.warn("Failed to import watcher"))
* }
@ -76,7 +76,7 @@ export class ESBuildObserver extends EventSource {
*/
#startListener = () => {
this.#trackActivity();
log("⏰ Build started...");
log("⏰ Build started...");
};
#internalErrorListener = () => {
@ -86,7 +86,7 @@ export class ESBuildObserver extends EventSource {
clearTimeout(this.#keepAliveInterval);
this.close();
log("⛔️ Closing connection");
log("⛔️ Closing connection");
}
};
@ -126,13 +126,13 @@ export class ESBuildObserver extends EventSource {
this.#trackActivity();
if (!this.online) {
log("🚫 Build finished while offline.");
log("🚫 Build finished while offline.");
this.deferredReload = true;
return;
}
log("🛎️ Build completed! Reloading...");
log("🛎️ Build completed! Reloading...");
// We use an animation frame to keep the reload from happening before the
// event loop has a chance to process the message.
@ -189,13 +189,13 @@ export class ESBuildObserver extends EventSource {
if (!this.deferredReload) return;
log("🛎️ Reloading after offline build...");
log("🛎️ Reloading after offline build...");
this.deferredReload = false;
window.location.reload();
});
log("🛎️ Listening for build changes...");
log("🛎️ Listening for build changes...");
this.#keepAliveInterval = setInterval(() => {
const now = Date.now();
@ -203,7 +203,7 @@ export class ESBuildObserver extends EventSource {
if (now - this.lastUpdatedAt < 10_000) return;
this.alive = false;
log("👋 Waiting for build to start...");
log("👋 Waiting for build to start...");
}, 15_000);
}

View File

@ -4,20 +4,15 @@
export {};
declare global {
/**
* Environment variables injected by ESBuild.
*/
interface ImportMetaEnv {
/**
* The injected watcher URL for ESBuild.
* This is used for live reloading in development mode.
*
* @format url
*/
readonly ESBUILD_WATCHER_URL?: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
readonly env: {
/**
* The injected watcher URL for ESBuild.
* This is used for live reloading in development mode.
*
* @format url
*/
ESBUILD_WATCHER_URL: string;
};
}
}

View File

@ -1,11 +1,22 @@
{
"name": "@goauthentik/esbuild-plugin-live-reload",
"version": "1.0.4",
"description": "ESBuild plugin to watch for file changes and trigger client-side reloads.",
"license": "MIT",
"private": true,
"main": "index.js",
"type": "module",
"version": "1.0.4",
"dependencies": {
"find-free-ports": "^3.1.1"
},
"devDependencies": {
"@goauthentik/prettier-config": "^1.0.4",
"@goauthentik/tsconfig": "^1.0.4",
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
"@types/node": "^22.14.1",
"esbuild": "^0.25.0",
"prettier": "^3.3.3",
"typescript": "^5.6.2"
},
"engines": {
"node": ">=20.11"
},
"exports": {
"./package.json": "./package.json",
".": {
@ -21,33 +32,22 @@
"import": "./plugin/index.js"
}
},
"dependencies": {
"find-free-ports": "^3.1.1"
},
"devDependencies": {
"@goauthentik/prettier-config": "^1.0.4",
"@goauthentik/tsconfig": "^1.0.4",
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
"@types/node": "^22.14.1",
"esbuild": "^0.25.0",
"prettier": "^3.3.3",
"typescript": "^5.6.2"
},
"peerDependencies": {
"esbuild": "^0.25.0"
},
"engines": {
"node": ">=20.11"
},
"types": "./out/index.d.ts",
"files": [
"./index.js",
"client/**/*",
"plugin/**/*",
"out/**/*"
],
"license": "MIT",
"main": "index.js",
"peerDependencies": {
"esbuild": "^0.25.0"
},
"prettier": "@goauthentik/prettier-config",
"private": true,
"publishConfig": {
"access": "public"
}
},
"type": "module",
"types": "./out/index.d.ts"
}

View File

@ -1,65 +0,0 @@
/**
* @file Utility functions for working with environment variables.
*/
/// <reference types="./types/global.js" />
//#region Constants
/**
* The current Node.js environment, defaulting to "development" when not set.
*
* Note, this should only be used during the build process.
*
* If you need to check the environment at runtime, use `process.env.NODE_ENV` to
* ensure that module tree-shaking works correctly.
*
*/
export const NodeEnvironment = process.env.NODE_ENV || "development";
/**
* A source environment variable, which can be a string, number, boolean, null, or undefined.
* @typedef {string | number | boolean | null | undefined} EnvironmentVariable
*/
/**
* A type helper for serializing environment variables.
*
* @template {EnvironmentVariable} T
* @typedef {T extends string ? `"${T}"` : T} JSONify
*/
//#endregion
//#region Utilities
/**
* Given an object of environment variables, returns a new object with the same keys and values, but
* with the values serialized as strings.
*
* @template {Record<string, EnvironmentVariable>} EnvRecord
* @template {string} [Prefix='import.meta.env.']
*
* @param {EnvRecord} input
* @param {Prefix} [prefix='import.meta.env.']
*
* @returns {{[K in keyof EnvRecord as `${Prefix}${K}`]: JSONify<EnvRecord[K]>}}
*/
export function serializeEnvironmentVars(
input,
prefix = /** @type {Prefix} */ ("import.meta.env."),
) {
/**
* @type {Record<string, string>}
*/
const env = {};
for (const [key, value] of Object.entries(input)) {
const namespaceKey = prefix + key;
env[namespaceKey] = JSON.stringify(value || "");
}
return /** @type {any} */ (env);
}
//#endregion

View File

@ -1,28 +0,0 @@
{
"name": "@goauthentik/monorepo",
"version": "1.0.0",
"description": "Utilities for the authentik monorepo.",
"license": "MIT",
"private": true,
"main": "index.js",
"type": "module",
"exports": {
"./package.json": "./package.json",
".": {
"types": "./out/index.d.ts",
"import": "./index.js"
}
},
"devDependencies": {
"@goauthentik/prettier-config": "^1.0.4",
"@goauthentik/tsconfig": "^1.0.4",
"@types/node": "^22.14.1",
"prettier": "^3.3.3",
"typescript": "^5.6.2"
},
"engines": {
"node": ">=20.11"
},
"types": "./out/index.d.ts",
"prettier": "@goauthentik/prettier-config"
}

View File

@ -1,45 +0,0 @@
import { createRequire } from "node:module";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const relativeDirname = dirname(fileURLToPath(import.meta.url));
/**
* @typedef {'~authentik'} MonoRepoRoot
*/
/**
* The root of the authentik monorepo.
*/
// TODO: Revise when this package is moved to the monorepo's `packages/monorepo` directory.
export const MonoRepoRoot = /** @type {MonoRepoRoot} */ (
resolve(relativeDirname, "..", "..", "..")
);
const require = createRequire(import.meta.url);
/**
* Resolve a package name to its location in the monorepo to the single node_modules directory.
* @param {string} packageName
*
* @returns {string} The resolved path to the package.
* @throws {Error} If the package cannot be resolved.
*/
export function resolvePackage(packageName) {
const relativePackageJSONPath = join(packageName, "package.json");
/** @type {string} */
let absolutePackageJSONPath;
try {
absolutePackageJSONPath = require.resolve(relativePackageJSONPath);
} catch (cause) {
const error = new Error(`Failed to resolve package "${packageName}"`);
error.cause = cause;
throw error;
}
return dirname(absolutePackageJSONPath);
}

View File

@ -1,15 +0,0 @@
declare module "process" {
global {
namespace NodeJS {
interface ProcessEnv {
/**
* An environment variable used to determine
* whether Node.js is running in production mode.
*
* @see {@link https://nodejs.org/en/learn/getting-started/nodejs-the-difference-between-development-and-production | The difference between development and production}
*/
NODE_ENV?: "production" | "development";
}
}
}
}

View File

@ -210,9 +210,6 @@ class PasswordStage extends Stage<PasswordChallenge> {
<form id="password-form">
<img class="mb-4 brand-icon" src="${ak().brand.branding_logo}" alt="">
<h1 class="h3 mb-3 fw-normal text-center">${this.challenge?.flowInfo?.title}</h1>
<div class="form-label-group my-3">
<input type="text" readonly class="form-control-plaintext" value="Welcome, ${this.challenge?.pendingUser}.">
</div>
<div class="form-label-group my-3 has-validation">
<input type="password" autofocus class="form-control ${this.error("password").length > 0 ? IS_INVALID : ""}" name="password" placeholder="Password">
${this.renderInputError("password")}

View File

@ -1,78 +0,0 @@
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const relativeDirname = dirname(fileURLToPath(import.meta.url));
//#region Base paths
/**
* @typedef {'@goauthentik/web'} WebPackageIdentifier
*/
/**
* The root of the web package.
*/
export const PackageRoot = /** @type {WebPackageIdentifier} */ (resolve(relativeDirname));
/**
* The name of the distribution directory.
*/
export const DistDirectoryName = "dist";
/**
* Path to the web package's distribution directory.
*
* This is where the built files are located after running the build process.
*/
export const DistDirectory = /** @type {`${WebPackageIdentifier}/${DistDirectoryName}`} */ (
resolve(relativeDirname, DistDirectoryName)
);
//#endregion
//#region Entry points
/**
* @typedef {{ in: string, out: string }} EntryPointTarget
*
* ESBuild entrypoint target.
* Matches the type defined in the ESBuild context.
*/
/**
* Entry points available for building.
*
* @satisfies {Record<string, EntryPointTarget>}
*/
export const EntryPoint = /** @type {const} */ ({
Admin: {
in: resolve(PackageRoot, "src", "admin", "AdminInterface", "index.entrypoint.ts"),
out: resolve(DistDirectory, "admin", "AdminInterface"),
},
User: {
in: resolve(PackageRoot, "src", "user", "index.entrypoint.ts"),
out: resolve(DistDirectory, "user", "UserInterface"),
},
Flow: {
in: resolve(PackageRoot, "src", "flow", "index.entrypoint.ts"),
out: resolve(DistDirectory, "flow", "FlowInterface"),
},
Standalone: {
in: resolve(PackageRoot, "src", "standalone", "api-browser/index.entrypoint.ts"),
out: resolve(DistDirectory, "standalone", "api-browser", "index"),
},
StandaloneLoading: {
in: resolve(PackageRoot, "src", "standalone", "loading/index.entrypoint.ts"),
out: resolve(DistDirectory, "standalone", "loading", "index"),
},
RAC: {
in: resolve(PackageRoot, "src", "rac", "index.entrypoint.ts"),
out: resolve(DistDirectory, "rac", "index"),
},
Polyfill: {
in: resolve(PackageRoot, "src", "polyfill", "index.entrypoint.ts"),
out: resolve(DistDirectory, "poly"),
},
});
//#endregion

View File

@ -1,32 +1,17 @@
import { spawnSync } from "child_process";
import fs from "fs";
import path from "path";
import process from "process";
/**
* @file Lit Localize build script.
*
* @remarks
* Determines if all the Xliff translation source files are present and if the Typescript source files generated from those sources are up-to-date.
*
* If they are not, it runs the locale building script,
* intercepting the long spew of "this string is not translated" and replacing it with a
* Determines if all the Xliff translation source files are present and if the Typescript source
* files generated from those sources are up-to-date. If they are not, it runs the locale building
* script, intercepting the long spew of "this string is not translated" and replacing it with a
* summary of how many strings are missing with respect to the source locale.
*
* @import { ConfigFile } from "@lit/localize-tools/lib/types/config"
*/
import { PackageRoot } from "@goauthentik/web/paths";
import { spawnSync } from "node:child_process";
import { readFileSync, statSync } from "node:fs";
import path from "node:path";
/**
* @type {ConfigFile}
*/
const localizeRules = JSON.parse(
readFileSync(path.join(PackageRoot, "lit-localize.json"), "utf-8"),
);
const localizeRules = JSON.parse(fs.readFileSync("./lit-localize.json", "utf-8"));
/**
*
* @param {string} loc
* @returns {boolean}
*/
function generatedFileIsUpToDateWithXliffSource(loc) {
const xliff = path.join("./xliff", `${loc}.xlf`);
const gened = path.join("./src/locales", `${loc}.ts`);
@ -37,7 +22,7 @@ function generatedFileIsUpToDateWithXliffSource(loc) {
// generates a unique error message and halts the build.
try {
var xlfStat = statSync(xliff);
var xlfStat = fs.statSync(xliff);
} catch (_error) {
console.error(`lit-localize expected '${loc}.xlf', but XLF file is not present`);
process.exit(1);
@ -45,7 +30,7 @@ function generatedFileIsUpToDateWithXliffSource(loc) {
// If the generated file doesn't exist, of course it's not up to date.
try {
var genedStat = statSync(gened);
var genedStat = fs.statSync(gened);
} catch (_error) {
return false;
}

View File

@ -1,87 +1,144 @@
/// <reference types="../types/esbuild.js" />
/**
* @file ESBuild script for building the authentik web UI.
*
* @import { BuildOptions } from "esbuild";
*/
import { liveReloadPlugin } from "@goauthentik/esbuild-plugin-live-reload/plugin";
import {
MonoRepoRoot,
NodeEnvironment,
readBuildIdentifier,
resolvePackage,
} from "@goauthentik/monorepo";
import { DistDirectory, DistDirectoryName, EntryPoint, PackageRoot } from "@goauthentik/web/paths";
import { execFileSync } from "child_process";
import { deepmerge } from "deepmerge-ts";
import esbuild from "esbuild";
import copy from "esbuild-plugin-copy";
import { polyfillNode } from "esbuild-plugin-polyfill-node";
import * as fs from "node:fs/promises";
import * as path from "node:path";
import { copyFileSync, mkdirSync, readFileSync, statSync } from "fs";
import { globSync } from "glob";
import * as path from "path";
import { cwd } from "process";
import process from "process";
import { fileURLToPath } from "url";
import { mdxPlugin } from "./esbuild/build-mdx-plugin.mjs";
import { createBundleDefinitions } from "./esbuild/environment.mjs";
const logPrefix = "[Build]";
const __dirname = fileURLToPath(new URL(".", import.meta.url));
let authentikProjectRoot = path.join(__dirname, "..", "..");
const patternflyPath = resolvePackage("@patternfly/patternfly");
try {
// Use the package.json file in the root folder, as it has the current version information.
authentikProjectRoot = execFileSync("git", ["rev-parse", "--show-toplevel"], {
encoding: "utf8",
}).replace("\n", "");
} catch (_error) {
// We probably don't have a .git folder, which could happen in container builds.
}
const packageJSONPath = path.join(authentikProjectRoot, "./package.json");
const rootPackage = JSON.parse(readFileSync(packageJSONPath, "utf8"));
const NODE_ENV = process.env.NODE_ENV || "development";
const AK_API_BASE_PATH = process.env.AK_API_BASE_PATH || "";
const environmentVars = new Map([
["NODE_ENV", NODE_ENV],
["CWD", cwd()],
["AK_API_BASE_PATH", AK_API_BASE_PATH],
]);
const definitions = Object.fromEntries(
Array.from(environmentVars).map(([key, value]) => {
return [`process.env.${key}`, JSON.stringify(value)];
}),
);
/**
* @type {Readonly<BuildOptions>}
* All is magic is just to make sure the assets are copied into the right places. This is a very
* stripped down version of what the rollup-copy-plugin does, without any of the features we don't
* use, and using globSync instead of globby since we already had globSync lying around thanks to
* Typescript. If there's a third argument in an array entry, it's used to replace the internal path
* before concatenating it all together as the destination target.
* @type {Array<[string, string, string?]>}
*/
const assetsFileMappings = [
["node_modules/@patternfly/patternfly/patternfly.min.css", "."],
["node_modules/@patternfly/patternfly/assets/**", ".", "node_modules/@patternfly/patternfly/"],
["src/common/styles/**", "."],
["src/assets/images/**", "./assets/images"],
["./icons/*", "./assets/icons"],
];
/**
* @param {string} filePath
*/
const isFile = (filePath) => statSync(filePath).isFile();
/**
* @param {string} src Source file
* @param {string} dest Destination folder
* @param {string} [strip] Path to strip from the source file
*/
function nameCopyTarget(src, dest, strip) {
const target = path.join(dest, strip ? src.replace(strip, "") : path.parse(src).base);
return [src, target];
}
for (const [source, rawdest, strip] of assetsFileMappings) {
const matchedPaths = globSync(source);
const dest = path.join("dist", rawdest);
const copyTargets = matchedPaths.map((path) => nameCopyTarget(path, dest, strip));
for (const [src, dest] of copyTargets) {
if (isFile(src)) {
mkdirSync(path.dirname(dest), { recursive: true });
copyFileSync(src, dest);
}
}
}
/**
* @typedef {[source: string, destination: string]} EntryPoint
*/
/**
* This starts the definitions used for esbuild: Our targets, our arguments, the function for
* running a build, and three options for building: watching, building, and building the proxy.
* Ordered by largest to smallest interface to build even faster
*
* @type {EntryPoint[]}
*/
const entryPoints = [
["admin/AdminInterface/AdminInterface.ts", "admin"],
["user/UserInterface.ts", "user"],
["flow/FlowInterface.ts", "flow"],
["standalone/api-browser/index.ts", "standalone/api-browser"],
["rac/index.ts", "rac"],
["standalone/loading/index.ts", "standalone/loading"],
["polyfill/poly.ts", "."],
];
/**
* @type {import("esbuild").BuildOptions}
*/
const BASE_ESBUILD_OPTIONS = {
entryNames: `[dir]/[name]-${readBuildIdentifier()}`,
chunkNames: "[dir]/chunks/[name]-[hash]",
assetNames: "assets/[dir]/[name]-[hash]",
publicPath: path.join("/static", DistDirectoryName),
outdir: DistDirectory,
bundle: true,
write: true,
sourcemap: true,
minify: NodeEnvironment === "production",
legalComments: "external",
minify: NODE_ENV === "production",
splitting: true,
treeShaking: true,
external: ["*.woff", "*.woff2"],
tsconfig: path.resolve(PackageRoot, "tsconfig.build.json"),
tsconfig: path.resolve(__dirname, "..", "tsconfig.build.json"),
loader: {
".css": "text",
},
plugins: [
copy({
assets: [
{
from: path.join(patternflyPath, "patternfly.min.css"),
to: ".",
},
{
from: path.join(patternflyPath, "assets", "**"),
to: "./assets",
},
{
from: path.resolve(PackageRoot, "src", "common", "styles", "**"),
to: ".",
},
{
from: path.resolve(PackageRoot, "src", "assets", "images", "**"),
to: "./assets/images",
},
{
from: path.resolve(PackageRoot, "icons", "*"),
to: "./assets/icons",
},
],
}),
polyfillNode({
polyfills: {
path: true,
},
}),
mdxPlugin({
root: MonoRepoRoot,
root: authentikProjectRoot,
}),
],
define: createBundleDefinitions(),
define: definitions,
format: "esm",
logOverride: {
/**
@ -94,43 +151,69 @@ const BASE_ESBUILD_OPTIONS = {
},
};
async function cleanDistDirectory() {
const timerLabel = `${logPrefix} ♻️ Cleaning previous builds...`;
/**
* Creates a version ID for the build.
* @returns {string}
*/
function composeVersionID() {
const { version } = rootPackage;
const buildHash = process.env.GIT_BUILD_HASH;
console.time(timerLabel);
if (buildHash) {
return `${version}+${buildHash}`;
}
await fs.rm(DistDirectory, {
recursive: true,
force: true,
});
await fs.mkdir(DistDirectory, {
recursive: true,
});
console.timeEnd(timerLabel);
return version;
}
/**
* Creates an ESBuild options, extending the base options with the given overrides.
* Build a single entry point.
*
* @param {BuildOptions} overrides
* @returns {BuildOptions}
* @param {EntryPoint} buildTarget
* @param {Partial<esbuild.BuildOptions>} [overrides]
* @throws {Error} on build failure
*/
export function createESBuildOptions(overrides) {
/**
* @type {BuildOptions}
*/
const mergedOptions = deepmerge(BASE_ESBUILD_OPTIONS, overrides);
function createEntryPointOptions([source, dest], overrides = {}) {
const outdir = path.join(__dirname, "..", "dist", dest);
return mergedOptions;
/**
* @type {esbuild.BuildOptions}
*/
const entryPointConfig = {
entryPoints: [`./src/${source}`],
entryNames: `[dir]/[name]-${composeVersionID()}`,
publicPath: path.join("/static", "dist", dest),
outdir,
};
/**
* @type {esbuild.BuildOptions}
*/
const mergedConfig = deepmerge(BASE_ESBUILD_OPTIONS, entryPointConfig, overrides);
return mergedConfig;
}
/**
* Build all entry points in parallel.
*
* @param {EntryPoint[]} entryPoints
* @returns {Promise<esbuild.BuildResult[]>}
*/
async function buildParallel(entryPoints) {
return Promise.all(
entryPoints.map((entryPoint) => {
return esbuild.build(createEntryPointOptions(entryPoint));
}),
);
}
function doHelp() {
console.log(`Build the authentik UI
options:
-w, --watch: Build all interfaces
-w, --watch: Build all ${entryPoints.length} interfaces
-p, --proxy: Build only the polyfills and the loading application
-h, --help: This help message
`);
@ -139,29 +222,27 @@ function doHelp() {
}
async function doWatch() {
console.group(`${logPrefix} 🤖 Watching entry points`);
console.log("Watching all entry points...");
const entryPoints = Object.entries(EntryPoint).map(([entrypointID, target]) => {
console.log(entrypointID);
const buildContexts = await Promise.all(
entryPoints.map((entryPoint) => {
return esbuild.context(
createEntryPointOptions(entryPoint, {
define: definitions,
plugins: [
liveReloadPlugin({
logPrefix: `Build Observer (${entryPoint[1]})`,
relativeRoot: path.join(__dirname, ".."),
}),
],
}),
);
}),
);
return target;
});
await Promise.all(buildContexts.map((context) => context.rebuild()));
console.groupEnd();
const buildOptions = createESBuildOptions({
entryPoints,
plugins: [
liveReloadPlugin({
relativeRoot: PackageRoot,
}),
],
});
const buildContext = await esbuild.context(buildOptions);
await buildContext.rebuild();
await buildContext.watch();
await Promise.allSettled(buildContexts.map((context) => context.watch()));
return /** @type {Promise<void>} */ (
new Promise((resolve) => {
@ -173,34 +254,15 @@ async function doWatch() {
}
async function doBuild() {
console.group(`${logPrefix} 🚀 Building entry points:`);
console.log("Building all entry points");
const entryPoints = Object.entries(EntryPoint).map(([entrypointID, target]) => {
console.log(entrypointID);
return target;
});
console.groupEnd();
const buildOptions = createESBuildOptions({
entryPoints,
});
await esbuild.build(buildOptions);
console.log("Build complete");
return buildParallel(entryPoints);
}
async function doProxy() {
const entryPoints = [EntryPoint.StandaloneLoading];
const buildOptions = createESBuildOptions({
entryPoints,
});
await esbuild.build(buildOptions);
console.log("Proxy build complete");
return buildParallel(
entryPoints.filter(([_, dest]) => ["standalone/loading", "."].includes(dest)),
);
}
async function delegateCommand() {
@ -222,16 +284,12 @@ async function delegateCommand() {
}
}
await cleanDistDirectory()
// ---
.then(() =>
delegateCommand()
.then(() => {
console.log("Build complete");
process.exit(0);
})
.catch((error) => {
console.error(error);
process.exit(1);
}),
);
await delegateCommand()
.then(() => {
console.log("Build complete");
process.exit(0);
})
.catch((error) => {
console.error(error);
process.exit(1);
});

View File

@ -1,29 +0,0 @@
/**
* @file ESBuild environment utilities.
*/
import { AuthentikVersion, NodeEnvironment, serializeEnvironmentVars } from "@goauthentik/monorepo";
/**
* Creates a mapping of environment variables to their respective runtime constants.
*/
export function createBundleDefinitions() {
const SerializedNodeEnvironment = /** @type {`"development"` | `"production"`} */ (
JSON.stringify(NodeEnvironment)
);
/**
* @satisfies {Record<ESBuildImportEnvKey, string>}
*/
const envRecord = {
AK_VERSION: AuthentikVersion,
AK_API_BASE_PATH: process.env.AK_API_BASE_PATH ?? "",
};
return {
...serializeEnvironmentVars(envRecord),
// We need to explicitly set this for NPM packages that use `process`
// to determine their environment.
"process.env.NODE_ENV": SerializedNodeEnvironment,
"import.meta.env.NODE_ENV": SerializedNodeEnvironment,
};
}

View File

@ -35,11 +35,6 @@ const __dirname = fileURLToPath(new URL(".", import.meta.url));
const projectRoot = path.join(__dirname, "..");
process.chdir(projectRoot);
/**
*
* @param {string[]} flags
* @returns
*/
const hasFlag = (flags) => process.argv.length > 1 && flags.includes(process.argv[2]);
const [configFile, files] = hasFlag(["-n", "--nightmare"])

View File

@ -0,0 +1,33 @@
#!/usr/bin/env bash
TARGET="./node_modules/@spotlightjs/overlay/dist/index-"[0-9a-f]*.js
if [[ $(grep -L "QX2" "$TARGET" > /dev/null 2> /dev/null) ]]; then
patch --forward -V none --no-backup-if-mismatch -p0 $TARGET <<EOF
TARGET=$(find "./node_modules/@spotlightjs/overlay/dist/" -name "index-[0-9a-f]*.js");
if ! grep -GL 'QX2 = ' "$TARGET" > /dev/null ; then
patch --forward --no-backup-if-mismatch -p0 "$TARGET" <<EOF
>>>>>>> main
--- a/index-5682ce90.js 2024-06-13 16:19:28
+++ b/index-5682ce90.js 2024-06-13 16:20:23
@@ -4958,11 +4958,10 @@
}
);
}
-const q2 = w.lazy(() => import("./main-3257b7fc.js").then((n) => n.m));
+const q2 = w.lazy(() => import("./main-3257b7fc.js").then((n) => n.m)), QX2 = () => {};
function Gp({
data: n,
- onUpdateData: a = () => {
- },
+ onUpdateData: a = QX2,
editingEnabled: s = !1,
clipboardEnabled: o = !1,
displayDataTypes: c = !1,
EOF
else
echo "spotlight overlay.js patch already applied"
fi

View File

@ -1,36 +1,22 @@
/**
* @file Pseudo-localization script.
*
* @import { ConfigFile } from "@lit/localize-tools/lib/types/config.js"
* @import { Config } from '@lit/localize-tools/lib/types/config.js';
* @import { ProgramMessage } from "@lit/localize-tools/src/messages.js"
* @import { Locale } from "@lit/localize-tools/src/types/locale.js"
*/
import { PackageRoot } from "@goauthentik/web/paths";
import { readFileSync } from "node:fs";
import path from "node:path";
import { readFileSync } from "fs";
import path from "path";
import pseudolocale from "pseudolocale";
import { fileURLToPath } from "url";
import { makeFormatter } from "@lit/localize-tools/lib/formatters/index.js";
import { sortProgramMessages } from "@lit/localize-tools/lib/messages.js";
import { TransformLitLocalizer } from "@lit/localize-tools/lib/modes/transform.js";
const pseudoLocale = /** @type {Locale} */ ("pseudo-LOCALE");
const __dirname = fileURLToPath(new URL(".", import.meta.url));
const pseudoLocale = "pseudo-LOCALE";
const targetLocales = [pseudoLocale];
/**
* @type {ConfigFile}
*/
const baseConfig = JSON.parse(readFileSync(path.join(PackageRoot, "lit-localize.json"), "utf-8"));
const baseConfig = JSON.parse(readFileSync(path.join(__dirname, "../lit-localize.json"), "utf-8"));
// Need to make some internal specifications to satisfy the transformer. It doesn't actually matter
// which Localizer we use (transformer or runtime), because all of the functionality we care about
// is in their common parent class, but I had to pick one. Everything else here is just pure
// exploitation of the lit/localize-tools internals.
/**
* @satisfies {Config}
*/
const config = {
...baseConfig,
baseDir: path.join(__dirname, ".."),
@ -42,11 +28,6 @@ const config = {
resolve: (path) => path,
};
/**
*
* @param {ProgramMessage} message
* @returns
*/
const pseudoMessagify = (message) => ({
name: message.name,
contents: message.contents.map((content) =>
@ -55,7 +36,7 @@ const pseudoMessagify = (message) => ({
});
const localizer = new TransformLitLocalizer(config);
const { messages } = localizer.extractSourceMessages();
const messages = localizer.extractSourceMessages().messages;
const translations = messages.map(pseudoMessagify);
const sorted = sortProgramMessages([...messages]);
const formatter = makeFormatter(config);

View File

@ -1,11 +1,11 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { VERSION } from "@goauthentik/common/constants";
import { globalAK } from "@goauthentik/common/global";
import { DefaultBrand } from "@goauthentik/common/ui/config";
import "@goauthentik/elements/EmptyState";
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider";
import { ModalButton } from "@goauthentik/elements/buttons/ModalButton";
import { DefaultBrand } from "@goauthentik/elements/sidebar/SidebarBrand";
import { msg } from "@lit/localize";
import { TemplateResult, css, html } from "lit";

View File

@ -9,12 +9,8 @@ import { configureSentry } from "@goauthentik/common/sentry";
import { me } from "@goauthentik/common/users";
import { WebsocketClient } from "@goauthentik/common/ws";
import { AuthenticatedInterface } from "@goauthentik/elements/Interface";
import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider.js";
import { SidebarToggleEventDetail } from "@goauthentik/elements/PageHeader";
import "@goauthentik/elements/ak-locale-context";
import "@goauthentik/elements/banner/EnterpriseStatusBanner";
import "@goauthentik/elements/banner/EnterpriseStatusBanner";
import "@goauthentik/elements/banner/VersionBanner";
import "@goauthentik/elements/banner/VersionBanner";
import "@goauthentik/elements/messages/MessageContainer";
import "@goauthentik/elements/messages/MessageContainer";
@ -25,124 +21,79 @@ import "@goauthentik/elements/router/RouterOutlet";
import "@goauthentik/elements/sidebar/Sidebar";
import "@goauthentik/elements/sidebar/SidebarItem";
import { CSSResult, TemplateResult, css, html, nothing } from "lit";
import { customElement, eventOptions, property, query } from "lit/decorators.js";
import { CSSResult, TemplateResult, css, html } from "lit";
import { customElement, property, query, state } from "lit/decorators.js";
import { classMap } from "lit/directives/class-map.js";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFDrawer from "@patternfly/patternfly/components/Drawer/drawer.css";
import PFNav from "@patternfly/patternfly/components/Nav/nav.css";
import PFPage from "@patternfly/patternfly/components/Page/page.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
import { LicenseSummaryStatusEnum, SessionUser, UiThemeEnum } from "@goauthentik/api";
import { SessionUser, UiThemeEnum } from "@goauthentik/api";
import {
AdminSidebarEnterpriseEntries,
AdminSidebarEntries,
renderSidebarItems,
} from "./AdminSidebar.js";
import "./AdminSidebar";
if (import.meta.env.NODE_ENV === "development") {
if (process.env.NODE_ENV === "development") {
await import("@goauthentik/esbuild-plugin-live-reload/client");
}
@customElement("ak-interface-admin")
export class AdminInterface extends WithLicenseSummary(AuthenticatedInterface) {
//#region Properties
export class AdminInterface extends AuthenticatedInterface {
@property({ type: Boolean })
notificationDrawerOpen = getURLParam("notificationDrawerOpen", false);
@property({ type: Boolean })
public notificationDrawerOpen = getURLParam("notificationDrawerOpen", false);
apiDrawerOpen = getURLParam("apiDrawerOpen", false);
@property({ type: Boolean })
public apiDrawerOpen = getURLParam("apiDrawerOpen", false);
ws: WebsocketClient;
protected readonly ws: WebsocketClient;
@property({
type: Object,
attribute: false,
reflect: false,
})
public user?: SessionUser;
@state()
user?: SessionUser;
@query("ak-about-modal")
public aboutModal?: AboutModal;
aboutModal?: AboutModal;
@property({ type: Boolean, reflect: true })
public sidebarOpen: boolean;
@eventOptions({ passive: true })
protected sidebarListener(event: CustomEvent<SidebarToggleEventDetail>) {
this.sidebarOpen = !!event.detail.open;
}
#sidebarMatcher: MediaQueryList;
#sidebarMediaQueryListener = (event: MediaQueryListEvent) => {
this.sidebarOpen = event.matches;
};
//#endregion
//#region Styles
static styles: CSSResult[] = [
PFBase,
PFPage,
PFButton,
PFDrawer,
PFNav,
css`
.pf-c-page__main,
.pf-c-drawer__content,
.pf-c-page__drawer {
z-index: auto !important;
background-color: transparent;
}
.display-none {
display: none;
}
.pf-c-page {
background-color: var(--pf-c-page--BackgroundColor) !important;
}
:host([theme="dark"]) {
/* Global page background colour */
static get styles(): CSSResult[] {
return [
PFBase,
PFPage,
PFButton,
PFDrawer,
css`
.pf-c-page__main,
.pf-c-drawer__content,
.pf-c-page__drawer {
z-index: auto !important;
background-color: transparent;
}
.display-none {
display: none;
}
.pf-c-page {
background-color: var(--pf-c-page--BackgroundColor) !important;
}
/* Global page background colour */
:host([theme="dark"]) .pf-c-page {
--pf-c-page--BackgroundColor: var(--ak-dark-background);
}
}
ak-page-navbar {
grid-area: header;
}
.ak-sidebar {
grid-area: nav;
}
.pf-c-drawer__panel {
z-index: var(--pf-global--ZIndex--xl);
}
`,
];
//#endregion
//#region Lifecycle
constructor() {
configureSentry(true);
super();
this.ws = new WebsocketClient();
this.#sidebarMatcher = window.matchMedia("(min-width: 1200px)");
this.sidebarOpen = this.#sidebarMatcher.matches;
ak-enterprise-status,
ak-version-banner {
grid-area: header;
}
ak-admin-sidebar {
grid-area: nav;
}
.pf-c-drawer__panel {
z-index: var(--pf-global--ZIndex--xl);
}
`,
];
}
public connectedCallback() {
super.connectedCallback();
constructor() {
super();
this.ws = new WebsocketClient();
window.addEventListener(EVENT_NOTIFICATION_DRAWER_TOGGLE, () => {
this.notificationDrawerOpen = !this.notificationDrawerOpen;
@ -157,25 +108,16 @@ export class AdminInterface extends WithLicenseSummary(AuthenticatedInterface) {
apiDrawerOpen: this.apiDrawerOpen,
});
});
this.#sidebarMatcher.addEventListener("change", this.#sidebarMediaQueryListener, {
passive: true,
});
}
public disconnectedCallback(): void {
super.disconnectedCallback();
this.#sidebarMatcher.removeEventListener("change", this.#sidebarMediaQueryListener);
}
async firstUpdated(): Promise<void> {
configureSentry(true);
this.user = await me();
const canAccessAdmin =
this.user.user.isSuperuser ||
// TODO: somehow add `access_admin_interface` to the API schema
this.user.user.systemPermissions.includes("access_admin_interface");
if (!canAccessAdmin && this.user.user.pk > 0) {
window.location.assign("/if/user/");
}
@ -183,14 +125,10 @@ export class AdminInterface extends WithLicenseSummary(AuthenticatedInterface) {
render(): TemplateResult {
const sidebarClasses = {
"pf-c-page__sidebar": true,
"pf-m-light": this.activeTheme === UiThemeEnum.Light,
"pf-m-expanded": this.sidebarOpen,
"pf-m-collapsed": !this.sidebarOpen,
};
const drawerOpen = this.notificationDrawerOpen || this.apiDrawerOpen;
const drawerClasses = {
"pf-m-expanded": drawerOpen,
"pf-m-collapsed": !drawerOpen,
@ -198,18 +136,11 @@ export class AdminInterface extends WithLicenseSummary(AuthenticatedInterface) {
return html` <ak-locale-context>
<div class="pf-c-page">
<ak-page-navbar ?open=${this.sidebarOpen} @sidebar-toggle=${this.sidebarListener}>
<ak-version-banner></ak-version-banner>
<ak-enterprise-status interface="admin"></ak-enterprise-status>
</ak-page-navbar>
<ak-sidebar class="${classMap(sidebarClasses)}">
${renderSidebarItems(AdminSidebarEntries)}
${this.licenseSummary?.status !== LicenseSummaryStatusEnum.Unlicensed
? renderSidebarItems(AdminSidebarEnterpriseEntries)
: nothing}
</ak-sidebar>
<ak-enterprise-status interface="admin"></ak-enterprise-status>
<ak-version-banner></ak-version-banner>
<ak-admin-sidebar
class="pf-c-page__sidebar ${classMap(sidebarClasses)}"
></ak-admin-sidebar>
<div class="pf-c-page__drawer">
<div class="pf-c-drawer ${classMap(drawerClasses)}">
<div class="pf-c-drawer__main">

View File

@ -1,97 +1,186 @@
import { EVENT_SIDEBAR_TOGGLE } from "@goauthentik/common/constants";
import { me } from "@goauthentik/common/users";
import { AKElement } from "@goauthentik/elements/Base";
import {
CapabilitiesEnum,
WithCapabilitiesConfig,
} from "@goauthentik/elements/Interface/capabilitiesProvider";
import { WithVersion } from "@goauthentik/elements/Interface/versionProvider";
import { ID_REGEX, SLUG_REGEX, UUID_REGEX } from "@goauthentik/elements/router/Route";
import { getRootStyle } from "@goauthentik/elements/utils/getRootStyle";
import { spread } from "@open-wc/lit-helpers";
import { msg } from "@lit/localize";
import { TemplateResult, html, nothing } from "lit";
import { repeat } from "lit/directives/repeat.js";
import { customElement, property, state } from "lit/decorators.js";
import { map } from "lit/directives/map.js";
// The second attribute type is of string[] to help with the 'activeWhen' control, which was
// commonplace and singular enough to merit its own handler.
type SidebarEntry = [
path: string | null,
label: string,
attributes?: Record<string, any> | string[] | null, // eslint-disable-line
children?: SidebarEntry[],
];
import { UiThemeEnum } from "@goauthentik/api";
import type { SessionUser, UserSelf } from "@goauthentik/api";
/**
* Recursively renders a sidebar entry.
*/
export function renderSidebarItem([
path,
label,
attributes,
children,
]: SidebarEntry): TemplateResult {
const properties = Array.isArray(attributes)
? { ".activeWhen": attributes }
: (attributes ?? {});
@customElement("ak-admin-sidebar")
export class AkAdminSidebar extends WithCapabilitiesConfig(WithVersion(AKElement)) {
@property({ type: Boolean, reflect: true })
open = true;
if (path) {
properties.path = path;
@state()
impersonation: UserSelf["username"] | null = null;
constructor() {
super();
me().then((user: SessionUser) => {
this.impersonation = user.original ? user.user.username : null;
});
this.toggleOpen = this.toggleOpen.bind(this);
this.checkWidth = this.checkWidth.bind(this);
}
return html`<ak-sidebar-item ${spread(properties)}>
${label ? html`<span slot="label">${label}</span>` : nothing}
${children ? renderSidebarItems(children) : nothing}
</ak-sidebar-item>`;
// This has to be a bound method so the event listener can be removed on disconnection as
// needed.
toggleOpen() {
this.open = !this.open;
}
checkWidth() {
// This works just fine, but it assumes that the `--ak-sidebar--minimum-auto-width` is in
// REMs. If that changes, this code will have to be adjusted as well.
const minWidth =
parseFloat(getRootStyle("--ak-sidebar--minimum-auto-width")) *
parseFloat(getRootStyle("font-size"));
this.open = window.innerWidth >= minWidth;
}
connectedCallback() {
super.connectedCallback();
window.addEventListener(EVENT_SIDEBAR_TOGGLE, this.toggleOpen);
window.addEventListener("resize", this.checkWidth);
// After connecting to the DOM, we can now perform this check to see if the sidebar should
// be open by default.
this.checkWidth();
}
// The symmetry (☟, ☝) here is critical in that you want to start adding these handlers after
// connection, and removing them before disconnection.
disconnectedCallback() {
window.removeEventListener(EVENT_SIDEBAR_TOGGLE, this.toggleOpen);
window.removeEventListener("resize", this.checkWidth);
super.disconnectedCallback();
}
render() {
return html`
<ak-sidebar
class="pf-c-page__sidebar ${this.open ? "pf-m-expanded" : "pf-m-collapsed"} ${this
.activeTheme === UiThemeEnum.Light
? "pf-m-light"
: ""}"
>
${this.renderSidebarItems()}
</ak-sidebar>
`;
}
updated() {
// This is permissible as`:host.classList` is not one of the properties Lit uses as a
// scheduling trigger. This sort of shenanigans can trigger an loop, in that it will trigger
// a browser reflow, which may trigger some other styling the application is monitoring,
// triggering a re-render which triggers a browser reflow, ad infinitum. But we've been
// living with that since jQuery, and it's both well-known and fortunately rare.
// eslint-disable-next-line wc/no-self-class
this.classList.remove("pf-m-expanded", "pf-m-collapsed");
// eslint-disable-next-line wc/no-self-class
this.classList.add(this.open ? "pf-m-expanded" : "pf-m-collapsed");
}
renderSidebarItems(): TemplateResult {
// The second attribute type is of string[] to help with the 'activeWhen' control, which was
// commonplace and singular enough to merit its own handler.
type SidebarEntry = [
path: string | null,
label: string,
attributes?: Record<string, any> | string[] | null, // eslint-disable-line
children?: SidebarEntry[],
];
// prettier-ignore
const sidebarContent: SidebarEntry[] = [
[null, msg("Dashboards"), { "?expanded": true }, [
["/administration/overview", msg("Overview")],
["/administration/dashboard/users", msg("User Statistics")],
["/administration/system-tasks", msg("System Tasks")]]],
[null, msg("Applications"), null, [
["/core/applications", msg("Applications"), [`^/core/applications/(?<slug>${SLUG_REGEX})$`]],
["/core/providers", msg("Providers"), [`^/core/providers/(?<id>${ID_REGEX})$`]],
["/outpost/outposts", msg("Outposts")]]],
[null, msg("Events"), null, [
["/events/log", msg("Logs"), [`^/events/log/(?<id>${UUID_REGEX})$`]],
["/events/rules", msg("Notification Rules")],
["/events/transports", msg("Notification Transports")]]],
[null, msg("Customization"), null, [
["/policy/policies", msg("Policies")],
["/core/property-mappings", msg("Property Mappings")],
["/blueprints/instances", msg("Blueprints")],
["/policy/reputation", msg("Reputation scores")]]],
[null, msg("Flows and Stages"), null, [
["/flow/flows", msg("Flows"), [`^/flow/flows/(?<slug>${SLUG_REGEX})$`]],
["/flow/stages", msg("Stages")],
["/flow/stages/prompts", msg("Prompts")]]],
[null, msg("Directory"), null, [
["/identity/users", msg("Users"), [`^/identity/users/(?<id>${ID_REGEX})$`]],
["/identity/groups", msg("Groups"), [`^/identity/groups/(?<id>${UUID_REGEX})$`]],
["/identity/roles", msg("Roles"), [`^/identity/roles/(?<id>${UUID_REGEX})$`]],
["/identity/initial-permissions", msg("Initial Permissions"), [`^/identity/initial-permissions/(?<id>${ID_REGEX})$`]],
["/core/sources", msg("Federation and Social login"), [`^/core/sources/(?<slug>${SLUG_REGEX})$`]],
["/core/tokens", msg("Tokens and App passwords")],
["/flow/stages/invitations", msg("Invitations")]]],
[null, msg("System"), null, [
["/core/brands", msg("Brands")],
["/crypto/certificates", msg("Certificates")],
["/outpost/integrations", msg("Outpost Integrations")],
["/admin/settings", msg("Settings")]]],
];
// Typescript requires the type here to correctly type the recursive path
type SidebarRenderer = (_: SidebarEntry) => TemplateResult;
const renderOneSidebarItem: SidebarRenderer = ([path, label, attributes, children]) => {
const properties = Array.isArray(attributes)
? { ".activeWhen": attributes }
: (attributes ?? {});
if (path) {
properties.path = path;
}
return html`<ak-sidebar-item ${spread(properties)}>
${label ? html`<span slot="label">${label}</span>` : nothing}
${map(children, renderOneSidebarItem)}
</ak-sidebar-item>`;
};
// prettier-ignore
return html`
${map(sidebarContent, renderOneSidebarItem)}
${this.renderEnterpriseMenu()}
`;
}
renderEnterpriseMenu() {
return this.can(CapabilitiesEnum.IsEnterprise)
? html`
<ak-sidebar-item>
<span slot="label">${msg("Enterprise")}</span>
<ak-sidebar-item path="/enterprise/licenses">
<span slot="label">${msg("Licenses")}</span>
</ak-sidebar-item>
</ak-sidebar-item>
`
: nothing;
}
}
/**
* Recursively renders a collection of sidebar entries.
*/
export function renderSidebarItems(entries: readonly SidebarEntry[]) {
return repeat(entries, ([path, label]) => path || label, renderSidebarItem);
declare global {
interface HTMLElementTagNameMap {
"ak-admin-sidebar": AkAdminSidebar;
}
}
// prettier-ignore
export const AdminSidebarEntries: readonly SidebarEntry[] = [
[null, msg("Dashboards"), { "?expanded": true }, [
["/administration/overview", msg("Overview")],
["/administration/dashboard/users", msg("User Statistics")],
["/administration/system-tasks", msg("System Tasks")]]
],
[null, msg("Applications"), null, [
["/core/applications", msg("Applications"), [`^/core/applications/(?<slug>${SLUG_REGEX})$`]],
["/core/providers", msg("Providers"), [`^/core/providers/(?<id>${ID_REGEX})$`]],
["/outpost/outposts", msg("Outposts")]]
],
[null, msg("Events"), null, [
["/events/log", msg("Logs"), [`^/events/log/(?<id>${UUID_REGEX})$`]],
["/events/rules", msg("Notification Rules")],
["/events/transports", msg("Notification Transports")]]
],
[null, msg("Customization"), null, [
["/policy/policies", msg("Policies")],
["/core/property-mappings", msg("Property Mappings")],
["/blueprints/instances", msg("Blueprints")],
["/policy/reputation", msg("Reputation scores")]]
],
[null, msg("Flows and Stages"), null, [
["/flow/flows", msg("Flows"), [`^/flow/flows/(?<slug>${SLUG_REGEX})$`]],
["/flow/stages", msg("Stages")],
["/flow/stages/prompts", msg("Prompts")]]
],
[null, msg("Directory"), null, [
["/identity/users", msg("Users"), [`^/identity/users/(?<id>${ID_REGEX})$`]],
["/identity/groups", msg("Groups"), [`^/identity/groups/(?<id>${UUID_REGEX})$`]],
["/identity/roles", msg("Roles"), [`^/identity/roles/(?<id>${UUID_REGEX})$`]],
["/identity/initial-permissions", msg("Initial Permissions"), [`^/identity/initial-permissions/(?<id>${ID_REGEX})$`]],
["/core/sources", msg("Federation and Social login"), [`^/core/sources/(?<slug>${SLUG_REGEX})$`]],
["/core/tokens", msg("Tokens and App passwords")],
["/flow/stages/invitations", msg("Invitations")]]
],
[null, msg("System"), null, [
["/core/brands", msg("Brands")],
["/crypto/certificates", msg("Certificates")],
["/outpost/integrations", msg("Outpost Integrations")],
["/admin/settings", msg("Settings")]]
],
];
// prettier-ignore
export const AdminSidebarEnterpriseEntries: readonly SidebarEntry[] = [
[null, msg("Enterprise"), null, [
["/enterprise/licenses", msg("Licenses"), null]
],
]]

View File

@ -0,0 +1,5 @@
import { AdminInterface } from "./AdminInterface";
import "./AdminInterface";
export { AdminInterface };
export default AdminInterface;

View File

@ -94,13 +94,10 @@ export class AdminOverviewPage extends AdminOverviewBase {
}
render(): TemplateResult {
const username = this.user?.user.name || this.user?.user.username;
const name = this.user?.user.name ?? this.user?.user.username;
return html` <ak-page-header
header=${msg(str`Welcome, ${username || ""}.`)}
description=${msg("General system status")}
?hasIcon=${false}
>
return html`<ak-page-header description=${msg("General system status")} ?hasIcon=${false}>
<span slot="header"> ${msg(str`Welcome, ${name || ""}.`)} </span>
</ak-page-header>
<section class="pf-c-page__main-section">
<div class="pf-l-grid pf-m-gutter">

View File

@ -1,4 +1,5 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
import "@goauthentik/components/ak-number-input";
import "@goauthentik/components/ak-switch-input";
import "@goauthentik/components/ak-text-input";
@ -183,14 +184,20 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
label=${msg("Reputation: lower limit")}
required
name="reputationLowerLimit"
value="${this._settings?.reputationLowerLimit ?? DEFAULT_REPUTATION_LOWER_LIMIT}"
value="${first(
this._settings?.reputationLowerLimit,
DEFAULT_REPUTATION_LOWER_LIMIT,
)}"
help=${msg("Reputation cannot decrease lower than this value. Zero or negative.")}
></ak-number-input>
<ak-number-input
label=${msg("Reputation: upper limit")}
required
name="reputationUpperLimit"
value="${this._settings?.reputationUpperLimit ?? DEFAULT_REPUTATION_UPPER_LIMIT}"
value="${first(
this._settings?.reputationUpperLimit,
DEFAULT_REPUTATION_UPPER_LIMIT,
)}"
help=${msg("Reputation cannot increase higher than this value. Zero or positive.")}
></ak-number-input>
<ak-form-element-horizontal label=${msg("Footer links")} name="footerLinks">
@ -250,7 +257,7 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
label=${msg("Default token length")}
required
name="defaultTokenLength"
value="${this._settings?.defaultTokenLength ?? 60}"
value="${first(this._settings?.defaultTokenLength, 60)}"
help=${msg("Default length of generated tokens")}
></ak-number-input>
`;

View File

@ -83,10 +83,13 @@ export class AdminSettingsPage extends AKElement {
}
render() {
if (!this.settings) return nothing;
if (!this.settings) {
return nothing;
}
return html`
<ak-page-header icon="fa fa-cog" header="${msg("System settings")}"> </ak-page-header>
<ak-page-header icon="fa fa-cog" header="" description="">
<span slot="header"> ${msg("System settings")} </span>
</ak-page-header>
<section class="pf-c-page__main-section pf-m-no-padding-mobile pf-l-grid pf-m-gutter">
<div class="pf-c-card">
<div class="pf-c-card__body">

View File

@ -1,6 +1,7 @@
import "@goauthentik/admin/applications/ProviderSelectModal";
import { iconHelperText } from "@goauthentik/admin/helperText";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
import "@goauthentik/components/ak-file-input";
import "@goauthentik/components/ak-radio-input";
import "@goauthentik/components/ak-switch-input";
@ -193,7 +194,7 @@ export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Applicatio
></ak-text-input>
<ak-switch-input
name="openInNewTab"
?checked=${this.instance?.openInNewTab ?? false}
?checked=${first(this.instance?.openInNewTab, false)}
label=${msg("Open in new tab")}
help=${msg(
"If checked, the launch URL will open in a new browser tab or window from the user's application library.",
@ -220,7 +221,7 @@ export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Applicatio
: html` <ak-text-input
label=${msg("Icon")}
name="metaIcon"
value=${this.instance?.metaIcon ?? ""}
value=${first(this.instance?.metaIcon, "")}
help=${iconHelperText}
>
</ak-text-input>`}

View File

@ -113,7 +113,8 @@ export class ApplicationViewPage extends AKElement {
renderApp(): TemplateResult {
if (!this.application) {
return html`<ak-empty-state loading header=${msg("Loading")}> </ak-empty-state>`;
return html`<ak-empty-state ?loading="${true}" header=${msg("Loading")}>
</ak-empty-state>`;
}
return html`<ak-tabs>
${this.missingOutpost

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