Compare commits

...

18 Commits

Author SHA1 Message Date
44f4ba3385 wip
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-05-26 15:27:02 +02:00
bfbe8b8038 web: bump knip from 5.33.0 to 5.58.0 in /web (#14685)
Bumps [knip](https://github.com/webpro-nl/knip/tree/HEAD/packages/knip) from 5.33.0 to 5.58.0.
- [Release notes](https://github.com/webpro-nl/knip/releases)
- [Changelog](https://github.com/webpro-nl/knip/blob/main/packages/knip/.release-it.json)
- [Commits](https://github.com/webpro-nl/knip/commits/5.58.0/packages/knip)

---
updated-dependencies:
- dependency-name: knip
  dependency-version: 5.58.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-26 15:06:28 +02:00
36ba8bc4e7 web: bump fuse.js from 7.0.0 to 7.1.0 in /web (#14687)
Bumps [fuse.js](https://github.com/krisk/Fuse) from 7.0.0 to 7.1.0.
- [Release notes](https://github.com/krisk/Fuse/releases)
- [Changelog](https://github.com/krisk/Fuse/blob/main/CHANGELOG.md)
- [Commits](https://github.com/krisk/Fuse/compare/v7.0.0...v7.1.0)

---
updated-dependencies:
- dependency-name: fuse.js
  dependency-version: 7.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-26 15:06:17 +02:00
dd5edf7fd9 web: bump @formatjs/intl-listformat from 7.5.7 to 7.7.11 in /web (#14689)
Bumps [@formatjs/intl-listformat](https://github.com/formatjs/formatjs) from 7.5.7 to 7.7.11.
- [Release notes](https://github.com/formatjs/formatjs/releases)
- [Commits](https://github.com/formatjs/formatjs/compare/@formatjs/intl-listformat@7.5.7...@formatjs/intl-listformat@7.7.11)

---
updated-dependencies:
- dependency-name: "@formatjs/intl-listformat"
  dependency-version: 7.7.11
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-26 15:06:07 +02:00
da1b252f3b root: do not use /bin/bash directly (#14698) 2025-05-26 14:38:29 +02:00
a8e543972a website/integrations: minio: notice about sso deprecation on CE (#14679)
* website/integrations: minio: notice about sso deprecation on CE

Starting with RELEASE.2025-05-24T17-08-30Z, MinIO has limited SSO support to their enterprise edition. This pr adds a warning to inform users and recommends sticking with earlier versions to retain SSO functionality.


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

* sugg

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

* tweak

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

---------

Signed-off-by: Dominic R <dominic@sdko.org>
2025-05-26 07:37:49 -05:00
6e03045d1f core: bump cryptography from 44.0.3 to 45.0.3 (#14690)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-26 12:37:44 +00:00
f4b39e7465 core: bump django-tenants from 3.7.0 to 3.8.0 (#14691)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-26 12:36:49 +00:00
e7cd5880b5 core: bump astral-sh/uv from 0.7.7 to 0.7.8 (#14681)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-26 12:26:25 +00:00
d8c6a2417d core: bump axllent/mailpit from v1.25.0 to v1.25.1 in /tests/e2e (#14693)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-26 14:25:34 +02:00
a1fe471a59 core: Publish web packages. (#14648) 2025-05-26 08:25:20 -04:00
054dfda73f website/integrations: add push security (#14429)
* Updates integrations sidebar and adds push security doc. WIP

* Partially added push instructions

* Added final instructions

* Fixed broken link

* Added few lines and changed formatting.

* Update website/integrations/services/push-security/index.mdx

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

* Update website/integrations/services/push-security/index.mdx

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

* Update website/integrations/services/push-security/index.mdx

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

* Update website/integrations/services/push-security/index.mdx

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

* Update website/integrations/services/push-security/index.mdx

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

* Update website/integrations/services/push-security/index.mdx

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

* Update website/integrations/services/push-security/index.mdx

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

* Update website/integrations/services/push-security/index.mdx

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

* Update website/integrations/services/push-security/index.mdx

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

* Update website/integrations/services/push-security/index.mdx

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

* Update website/integrations/services/push-security/index.mdx

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

* Update website/integrations/services/push-security/index.mdx

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

* Update website/integrations/services/push-security/index.mdx

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

* Added note about login from push login URL, and added suggestions from Dominic

* Applied suggestions from Dominic

* Fixed verification cert line

* Added note to recommend users follow the extra verificaton step

* Update website/integrations/services/push-security/index.mdx

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

* Update website/integrations/services/push-security/index.mdx

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

* Update website/integrations/services/push-security/index.mdx

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

* Update website/integrations/services/push-security/index.mdx

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

* Update website/integrations/services/push-security/index.mdx

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

* Update website/integrations/services/push-security/index.mdx

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

* Update website/integrations/services/push-security/index.mdx

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

* Update website/integrations/services/push-security/index.mdx

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

* Update website/integrations/services/push-security/index.mdx

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

* Update website/integrations/services/push-security/index.mdx

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

* moved resouces section to end of document

---------

Signed-off-by: Dewi Roberts <dewi@goauthentik.io>
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-05-26 07:23:40 -05:00
2e5e8f5c58 docs: fix typos in developer and user documentation (#14680) 2025-05-26 11:51:17 +00:00
c28b65a3f2 Web: Controllers cleanup (#14616)
* web: Fix issues surrounding availability of controllers during init.

web: Fix edgecase where flow does not have brand.

* web: Fix import path.

* web: Clean up mixin/controller paths.

* web: Prepare for consistent import styling.

- Prep for Storybook fixes.

* web: Update MDX types.

* web: Fix issues surrounding async imports, MDX typing, relative paths.

* web: Format. Clarify.

* web: Group module types.
2025-05-26 07:06:14 -04:00
afc9847e36 website: Fix issue where OpenAPI docs template generates semi-synthet… (#14674)
* website: Fix issue where OpenAPI docs template generates semi-synthetic title.

* website: Clarify linter behavior. Tidy components.
2025-05-26 10:50:45 +00:00
620c95dfa1 web: bump the goauthentik group across 4 directories with 3 updates (#14640)
Bumps the goauthentik group with 1 update in the /packages/docusaurus-config directory: @goauthentik/prettier-config.
Bumps the goauthentik group with 2 updates in the /packages/eslint-config directory: @goauthentik/prettier-config and @goauthentik/tsconfig.
Bumps the goauthentik group with 1 update in the /packages/prettier-config directory: @goauthentik/tsconfig.
Bumps the goauthentik group with 2 updates in the /web directory: @goauthentik/prettier-config and @goauthentik/eslint-config.


Updates `@goauthentik/prettier-config` from 1.0.4 to 1.0.5

Updates `@goauthentik/prettier-config` from 1.0.1 to 1.0.5

Updates `@goauthentik/tsconfig` from 1.0.1 to 1.0.4

Updates `@goauthentik/tsconfig` from 1.0.1 to 1.0.4

Updates `@goauthentik/prettier-config` from 1.0.4 to 1.0.5

Updates `@goauthentik/eslint-config` from 1.0.4 to 1.0.5

---
updated-dependencies:
- dependency-name: "@goauthentik/prettier-config"
  dependency-version: 1.0.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: goauthentik
- dependency-name: "@goauthentik/prettier-config"
  dependency-version: 1.0.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: goauthentik
- dependency-name: "@goauthentik/tsconfig"
  dependency-version: 1.0.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: goauthentik
- dependency-name: "@goauthentik/tsconfig"
  dependency-version: 1.0.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: goauthentik
- dependency-name: "@goauthentik/prettier-config"
  dependency-version: 1.0.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: goauthentik
- dependency-name: "@goauthentik/eslint-config"
  dependency-version: 1.0.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: goauthentik
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-26 03:52:23 +02:00
15c7a0a9be core, web: update translations (#14676)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2025-05-26 00:30:51 +02:00
9cf2f89bf6 root: switch to channels-postgres
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-04-10 15:35:09 +02:00
106 changed files with 1708 additions and 1370 deletions

View File

@ -17,27 +17,28 @@ jobs:
fail-fast: false
matrix:
package:
- docusaurus-config
- eslint-config
- prettier-config
- tsconfig
- packages/docusaurus-config
- packages/eslint-config
- packages/prettier-config
- packages/tsconfig
- packages/web/esbuild-plugin-live-reload
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2
- uses: actions/setup-node@v4
with:
node-version-file: packages/${{ matrix.package }}/package.json
node-version-file: ${{ matrix.package }}/package.json
registry-url: "https://registry.npmjs.org"
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@ed68ef82c095e0d48ec87eccea555d944a631a4c
with:
files: |
packages/${{ matrix.package }}/package.json
${{ matrix.package }}/package.json
- name: Publish package
if: steps.changed-files.outputs.any_changed == 'true'
working-directory: packages/${{ matrix.package}}
working-directory: ${{ matrix.package }}
run: |
npm ci
npm run build

View File

@ -94,7 +94,7 @@ RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
/bin/sh -c "GEOIPUPDATE_LICENSE_KEY_FILE=/run/secrets/GEOIPUPDATE_LICENSE_KEY /usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0"
# Stage 5: Download uv
FROM ghcr.io/astral-sh/uv:0.7.7 AS uv
FROM ghcr.io/astral-sh/uv:0.7.8 AS uv
# Stage 6: Base python image
FROM ghcr.io/goauthentik/fips-python:3.13.3-slim-bookworm-fips AS python-base

View File

@ -1,6 +1,6 @@
.PHONY: gen dev-reset all clean test web website
SHELL := /bin/bash
SHELL := /usr/bin/env bash
.SHELLFLAGS += ${SHELLFLAGS} -e -o pipefail
PWD = $(shell pwd)
UID = $(shell id -u)

View File

@ -5,7 +5,6 @@ from typing import Any
from billiard.exceptions import SoftTimeLimitExceeded, WorkerLostError
from celery.exceptions import CeleryError
from channels_redis.core import ChannelFull
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation, ValidationError
from django.db import DatabaseError, InternalError, OperationalError, ProgrammingError
@ -127,7 +126,6 @@ def before_send(event: dict, hint: dict) -> dict | None:
RedisError,
ResponseError,
# websocket errors
ChannelFull,
WebSocketException,
LocalProtocolError,
# rest_framework error

View File

@ -65,6 +65,7 @@ SHARED_APPS = [
"pgactivity",
"pglock",
"channels",
"channels_postgres",
]
TENANT_APPS = [
"django.contrib.auth",
@ -278,16 +279,6 @@ TEMPLATES = [
ASGI_APPLICATION = "authentik.root.asgi.application"
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.pubsub.RedisPubSubChannelLayer",
"CONFIG": {
"hosts": [CONFIG.get("channel.url") or redis_url(CONFIG.get("redis.db"))],
"prefix": "authentik_channels_",
},
},
}
# Database
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases
@ -300,6 +291,16 @@ DATABASE_ROUTERS = (
"django_tenants.routers.TenantSyncRouter",
)
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_postgres.core.PostgresChannelLayer",
"CONFIG": {
**DATABASES["default"],
"TIME_ZONE": None,
},
},
}
# Email
# These values should never actually be used, emails are only sent from email stages, which
# loads the config directly from CONFIG

Binary file not shown.

Binary file not shown.

View File

@ -3710,9 +3710,9 @@
}
},
"node_modules/@goauthentik/prettier-config": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@goauthentik/prettier-config/-/prettier-config-1.0.4.tgz",
"integrity": "sha512-CgUVAThlJHif7ZRXUPMbR/7/YLGzkJw7YbqEcleUjjKkvID0aykrypXx04td6cG76zigspTCgJKoXimKT41E7g==",
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@goauthentik/prettier-config/-/prettier-config-1.0.5.tgz",
"integrity": "sha512-3W1uJvhzBPerDao53hSXhNzB7Ev8DbGYh+gVkuku1FaUZGBpiwD/6U3ah4sny8NoRiObGQ1geF4dhNLtlRbC/Q==",
"dev": true,
"license": "MIT",
"engines": {
@ -3722,7 +3722,7 @@
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
"prettier": "^3.5.3",
"prettier-plugin-organize-imports": "^4.1.0",
"prettier-plugin-packagejson": "^2.5.10"
"prettier-plugin-packagejson": "^2.5.14"
}
},
"node_modules/@goauthentik/tsconfig": {

View File

@ -308,9 +308,9 @@
}
},
"node_modules/@goauthentik/prettier-config": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@goauthentik/prettier-config/-/prettier-config-1.0.1.tgz",
"integrity": "sha512-6N0cCG3Uw3Nt+gTxRJ/FYFi/NfuL849CrQkrx307PvEBaG66OjxFFee4bhS/si4XvLdxFdog7oQsPwYmqZeZ+w==",
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@goauthentik/prettier-config/-/prettier-config-1.0.5.tgz",
"integrity": "sha512-3W1uJvhzBPerDao53hSXhNzB7Ev8DbGYh+gVkuku1FaUZGBpiwD/6U3ah4sny8NoRiObGQ1geF4dhNLtlRbC/Q==",
"dev": true,
"license": "MIT",
"engines": {
@ -320,13 +320,13 @@
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
"prettier": "^3.5.3",
"prettier-plugin-organize-imports": "^4.1.0",
"prettier-plugin-packagejson": "^2.5.10"
"prettier-plugin-packagejson": "^2.5.14"
}
},
"node_modules/@goauthentik/tsconfig": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@goauthentik/tsconfig/-/tsconfig-1.0.1.tgz",
"integrity": "sha512-kxMDkgUHhAmQ2iIhUZJjrx/CgDb1AwvRoPtU4vrjAZu7x66+qczCjRTK+GzIGCeqB97GEpvCCjU8CThmozVFqA==",
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@goauthentik/tsconfig/-/tsconfig-1.0.4.tgz",
"integrity": "sha512-BTGVpGh8SbCRHTULBf+2WTcw6OHJ8Ws9VtVfAMUUgcq8whbH/A7Q/n8WbkDaEeihzHUFkLk3JBenHKzEKAZWlw==",
"dev": true,
"license": "MIT",
"engines": {
@ -491,9 +491,9 @@
}
},
"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==",
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.4.tgz",
"integrity": "sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw==",
"dev": true,
"license": "MIT",
"peer": true,
@ -501,7 +501,7 @@
"node": "^12.20.0 || ^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/unts"
"url": "https://opencollective.com/pkgr"
}
},
"node_modules/@rtsao/scc": {
@ -2045,20 +2045,6 @@
"node": ">= 0.4"
}
},
"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",
"peer": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/get-symbol-description": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz",
@ -2077,9 +2063,9 @@
}
},
"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==",
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-4.1.1.tgz",
"integrity": "sha512-cmP497iLq54AZnv4YRAEMnEyQ1eIn4tGKbmswqwmFV4GBnAqE8NLtWxxdXa++AalfgL5EBH4IxTPyquEuGY/jA==",
"dev": true,
"license": "MIT",
"peer": true,
@ -3219,15 +3205,15 @@
}
},
"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==",
"version": "2.5.14",
"resolved": "https://registry.npmjs.org/prettier-plugin-packagejson/-/prettier-plugin-packagejson-2.5.14.tgz",
"integrity": "sha512-h+3tSpr2nVpp+YOK1MDIYtYhHVXr8/0V59UUbJpIJFaqi3w4fvUokJo6eV8W+vELrUXIZzJ+DKm5G7lYzrMcKQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"sort-package-json": "2.15.1",
"synckit": "0.9.2"
"sort-package-json": "3.2.1",
"synckit": "0.11.6"
},
"peerDependencies": {
"prettier": ">= 1.16.0"
@ -3633,30 +3619,29 @@
"peer": true
},
"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==",
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-3.2.1.tgz",
"integrity": "sha512-rTfRdb20vuoAn7LDlEtCqOkYfl2X+Qze6cLbNOzcDpbmKEhJI30tTN44d5shbKJnXsvz24QQhlCm81Bag7EOKg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"detect-indent": "^7.0.1",
"detect-newline": "^4.0.0",
"get-stdin": "^9.0.0",
"git-hooks-list": "^3.0.0",
"detect-newline": "^4.0.1",
"git-hooks-list": "^4.0.0",
"is-plain-obj": "^4.1.0",
"semver": "^7.6.0",
"semver": "^7.7.1",
"sort-object-keys": "^1.1.3",
"tinyglobby": "^0.2.9"
"tinyglobby": "^0.2.12"
},
"bin": {
"sort-package-json": "cli.js"
}
},
"node_modules/sort-package-json/node_modules/semver": {
"version": "7.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"dev": true,
"license": "ISC",
"peer": true,
@ -3806,32 +3791,31 @@
}
},
"node_modules/synckit": {
"version": "0.9.2",
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz",
"integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==",
"version": "0.11.6",
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.6.tgz",
"integrity": "sha512-2pR2ubZSV64f/vqm9eLPz/KOvR9Dm+Co/5ChLgeHl0yEDRc6h5hXHoxEQH8Y5Ljycozd3p1k5TTSVdzYGkPvLw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@pkgr/core": "^0.1.0",
"tslib": "^2.6.2"
"@pkgr/core": "^0.2.4"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/unts"
"url": "https://opencollective.com/synckit"
}
},
"node_modules/tinyglobby": {
"version": "0.2.12",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz",
"integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==",
"version": "0.2.13",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",
"integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"fdir": "^6.4.3",
"fdir": "^6.4.4",
"picomatch": "^4.0.2"
},
"engines": {
@ -3842,9 +3826,9 @@
}
},
"node_modules/tinyglobby/node_modules/fdir": {
"version": "6.4.3",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz",
"integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==",
"version": "6.4.4",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
"integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
"dev": true,
"license": "MIT",
"peer": true,
@ -3909,14 +3893,6 @@
"strip-bom": "^3.0.0"
}
},
"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",
"peer": true
},
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",

View File

@ -31,8 +31,33 @@ export const AuthentikPrettierConfig = {
trailingComma: "all",
useTabs: false,
vueIndentScriptAndStyle: false,
plugins: ["prettier-plugin-packagejson", "@trivago/prettier-plugin-sort-imports"],
importOrder: ["^(@?)lit(.*)$", "\\.css$", "^@goauthentik/api$", "^[./]"],
plugins: [
// ---
"prettier-plugin-packagejson",
"@trivago/prettier-plugin-sort-imports",
],
importOrder: [
// ---
"^(@goauthentik/|#)common.+",
"^(@goauthentik/|#)elements.+",
"^(@goauthentik/|#)components.+",
"^(@goauthentik/|#)user.+",
"^(@goauthentik/|#)admin.+",
"^(@goauthentik/|#)flow.+",
"^(@goauthentik/|#)flow.+",
"^#.+",
"^@goauthentik.+",
"<THIRD_PARTY_MODULES>",
"^(@?)lit(.*)$",
"\\.css$",
"^@goauthentik/api$",
"^[./]",
],
importOrderSideEffects: false,
importOrderSeparation: true,
importOrderSortSpecifiers: true,
importOrderParserPlugins: ["typescript", "jsx", "classProperties", "decorators-legacy"],

View File

@ -1,12 +1,12 @@
{
"name": "@goauthentik/prettier-config",
"version": "1.0.4",
"version": "2.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@goauthentik/prettier-config",
"version": "1.0.4",
"version": "2.0.0",
"license": "MIT",
"devDependencies": {
"@goauthentik/tsconfig": "^1.0.1",
@ -143,9 +143,9 @@
}
},
"node_modules/@goauthentik/tsconfig": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@goauthentik/tsconfig/-/tsconfig-1.0.1.tgz",
"integrity": "sha512-kxMDkgUHhAmQ2iIhUZJjrx/CgDb1AwvRoPtU4vrjAZu7x66+qczCjRTK+GzIGCeqB97GEpvCCjU8CThmozVFqA==",
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@goauthentik/tsconfig/-/tsconfig-1.0.4.tgz",
"integrity": "sha512-BTGVpGh8SbCRHTULBf+2WTcw6OHJ8Ws9VtVfAMUUgcq8whbH/A7Q/n8WbkDaEeihzHUFkLk3JBenHKzEKAZWlw==",
"dev": true,
"license": "MIT",
"engines": {

View File

@ -1,6 +1,6 @@
{
"name": "@goauthentik/prettier-config",
"version": "1.0.5",
"version": "2.0.0",
"description": "authentik's Prettier config",
"license": "MIT",
"scripts": {

View File

@ -8,8 +8,8 @@ dependencies = [
"argon2-cffi==23.1.0",
"celery==5.5.2",
"channels==4.2.2",
"channels-redis==4.2.1",
"cryptography==44.0.3",
"channels-postgres==1.1.2",
"cryptography==45.0.3",
"dacite==1.9.2",
"deepmerge==2.0",
"defusedxml==0.7.1",
@ -23,7 +23,7 @@ dependencies = [
"django-prometheus==2.3.1",
"django-redis==5.4.0",
"django-storages[s3]==1.14.6",
"django-tenants==3.7.0",
"django-tenants==3.8.0",
"djangorestframework==3.16.0",
"djangorestframework-guardian==0.3.0",
"docker==7.1.0",
@ -114,7 +114,6 @@ no-binary-package = [
]
[tool.uv.sources]
django-tenants = { git = "https://github.com/goauthentik/django-tenants.git", branch = "authentik-fixes" }
opencontainers = { git = "https://github.com/vsoch/oci-python", rev = "ceb4fcc090851717a3069d78e85ceb1e86c2740c" }
djangorestframework = { git = "https://github.com/goauthentik/django-rest-framework", rev = "896722bab969fabc74a08b827da59409cf9f1a4e" }

View File

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
set -e -x -o pipefail
hash="$(git rev-parse HEAD || openssl rand -base64 36 | sha256sum)"

View File

@ -6,7 +6,7 @@ services:
network_mode: host
restart: always
mailpit:
image: docker.io/axllent/mailpit:v1.25.0
image: docker.io/axllent/mailpit:v1.25.1
ports:
- 1025:1025
- 8025:8025

81
uv.lock generated
View File

@ -170,7 +170,7 @@ dependencies = [
{ name = "argon2-cffi" },
{ name = "celery" },
{ name = "channels" },
{ name = "channels-redis" },
{ name = "channels-postgres" },
{ name = "cryptography" },
{ name = "dacite" },
{ name = "deepmerge" },
@ -268,8 +268,8 @@ requires-dist = [
{ name = "argon2-cffi", specifier = "==23.1.0" },
{ name = "celery", specifier = "==5.5.2" },
{ name = "channels", specifier = "==4.2.2" },
{ name = "channels-redis", specifier = "==4.2.1" },
{ name = "cryptography", specifier = "==44.0.3" },
{ name = "channels-postgres", specifier = "==1.1.2" },
{ name = "cryptography", specifier = "==45.0.3" },
{ name = "dacite", specifier = "==1.9.2" },
{ name = "deepmerge", specifier = "==2.0" },
{ name = "defusedxml", specifier = "==0.7.1" },
@ -283,7 +283,7 @@ requires-dist = [
{ name = "django-prometheus", specifier = "==2.3.1" },
{ name = "django-redis", specifier = "==5.4.0" },
{ name = "django-storages", extras = ["s3"], specifier = "==1.14.6" },
{ name = "django-tenants", git = "https://github.com/goauthentik/django-tenants.git?branch=authentik-fixes" },
{ name = "django-tenants", specifier = "==3.8.0" },
{ name = "djangorestframework", git = "https://github.com/goauthentik/django-rest-framework?rev=896722bab969fabc74a08b827da59409cf9f1a4e" },
{ name = "djangorestframework-guardian", specifier = "==0.3.0" },
{ name = "docker", specifier = "==7.1.0" },
@ -712,18 +712,18 @@ daphne = [
]
[[package]]
name = "channels-redis"
version = "4.2.1"
name = "channels-postgres"
version = "1.1.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "asgiref" },
{ name = "channels" },
{ name = "msgpack" },
{ name = "redis" },
{ name = "psycopg", extra = ["pool"] },
]
sdist = { url = "https://files.pythonhosted.org/packages/c7/6d/c379c9feea4522cbdb4eba9b3d23a6270ba8cbd94e847b21834d898109d6/channels_redis-4.2.1.tar.gz", hash = "sha256:8375e81493e684792efe6e6eca60ef3d7782ef76c6664057d2e5c31e80d636dd", size = 31152, upload-time = "2024-11-15T12:58:49.836Z" }
sdist = { url = "https://files.pythonhosted.org/packages/f5/21/c74be89ec2f93fa832f9c5b47f08c73e5e07279aab176a1f7ef4a240722d/channels_postgres-1.1.2.tar.gz", hash = "sha256:bfdf580ba65aab0d5e944d38ddd807d3521939153b786b53d6f932b6f3defd8e", size = 19696, upload-time = "2025-04-20T16:09:01.83Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a6/aa/981d08ae9627c3b9d8dd150f0fe644122a351abc1f47bcf53d2bfff80d91/channels_redis-4.2.1-py3-none-any.whl", hash = "sha256:2ca33105b3a04b5a327a9c47dd762b546f30b76a0cd3f3f593a23d91d346b6f4", size = 20487, upload-time = "2024-11-15T12:58:47.847Z" },
{ url = "https://files.pythonhosted.org/packages/fd/0b/1ec2c7357ebff7ff457f39d51ccb74be56ac796595dfc228658ba23974a1/channels_postgres-1.1.2-py3-none-any.whl", hash = "sha256:301e1980f23e325e289ac93265baf54ceae289c19f1893d1b55e40fe73149f67", size = 16176, upload-time = "2025-04-20T16:09:00.383Z" },
]
[[package]]
@ -869,37 +869,37 @@ wheels = [
[[package]]
name = "cryptography"
version = "44.0.3"
version = "45.0.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/53/d6/1411ab4d6108ab167d06254c5be517681f1e331f90edf1379895bcb87020/cryptography-44.0.3.tar.gz", hash = "sha256:fe19d8bc5536a91a24a8133328880a41831b6c5df54599a8417b62fe015d3053", size = 711096, upload-time = "2025-05-02T19:36:04.667Z" }
sdist = { url = "https://files.pythonhosted.org/packages/13/1f/9fa001e74a1993a9cadd2333bb889e50c66327b8594ac538ab8a04f915b7/cryptography-45.0.3.tar.gz", hash = "sha256:ec21313dd335c51d7877baf2972569f40a4291b76a0ce51391523ae358d05899", size = 744738, upload-time = "2025-05-25T14:17:24.777Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/08/53/c776d80e9d26441bb3868457909b4e74dd9ccabd182e10b2b0ae7a07e265/cryptography-44.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:962bc30480a08d133e631e8dfd4783ab71cc9e33d5d7c1e192f0b7c06397bb88", size = 6670281, upload-time = "2025-05-02T19:34:50.665Z" },
{ url = "https://files.pythonhosted.org/packages/6a/06/af2cf8d56ef87c77319e9086601bef621bedf40f6f59069e1b6d1ec498c5/cryptography-44.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffc61e8f3bf5b60346d89cd3d37231019c17a081208dfbbd6e1605ba03fa137", size = 3959305, upload-time = "2025-05-02T19:34:53.042Z" },
{ url = "https://files.pythonhosted.org/packages/ae/01/80de3bec64627207d030f47bf3536889efee8913cd363e78ca9a09b13c8e/cryptography-44.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58968d331425a6f9eedcee087f77fd3c927c88f55368f43ff7e0a19891f2642c", size = 4171040, upload-time = "2025-05-02T19:34:54.675Z" },
{ url = "https://files.pythonhosted.org/packages/bd/48/bb16b7541d207a19d9ae8b541c70037a05e473ddc72ccb1386524d4f023c/cryptography-44.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e28d62e59a4dbd1d22e747f57d4f00c459af22181f0b2f787ea83f5a876d7c76", size = 3963411, upload-time = "2025-05-02T19:34:56.61Z" },
{ url = "https://files.pythonhosted.org/packages/42/b2/7d31f2af5591d217d71d37d044ef5412945a8a8e98d5a2a8ae4fd9cd4489/cryptography-44.0.3-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af653022a0c25ef2e3ffb2c673a50e5a0d02fecc41608f4954176f1933b12359", size = 3689263, upload-time = "2025-05-02T19:34:58.591Z" },
{ url = "https://files.pythonhosted.org/packages/25/50/c0dfb9d87ae88ccc01aad8eb93e23cfbcea6a6a106a9b63a7b14c1f93c75/cryptography-44.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:157f1f3b8d941c2bd8f3ffee0af9b049c9665c39d3da9db2dc338feca5e98a43", size = 4196198, upload-time = "2025-05-02T19:35:00.988Z" },
{ url = "https://files.pythonhosted.org/packages/66/c9/55c6b8794a74da652690c898cb43906310a3e4e4f6ee0b5f8b3b3e70c441/cryptography-44.0.3-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:c6cd67722619e4d55fdb42ead64ed8843d64638e9c07f4011163e46bc512cf01", size = 3966502, upload-time = "2025-05-02T19:35:03.091Z" },
{ url = "https://files.pythonhosted.org/packages/b6/f7/7cb5488c682ca59a02a32ec5f975074084db4c983f849d47b7b67cc8697a/cryptography-44.0.3-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:b424563394c369a804ecbee9b06dfb34997f19d00b3518e39f83a5642618397d", size = 4196173, upload-time = "2025-05-02T19:35:05.018Z" },
{ url = "https://files.pythonhosted.org/packages/d2/0b/2f789a8403ae089b0b121f8f54f4a3e5228df756e2146efdf4a09a3d5083/cryptography-44.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c91fc8e8fd78af553f98bc7f2a1d8db977334e4eea302a4bfd75b9461c2d8904", size = 4087713, upload-time = "2025-05-02T19:35:07.187Z" },
{ url = "https://files.pythonhosted.org/packages/1d/aa/330c13655f1af398fc154089295cf259252f0ba5df93b4bc9d9c7d7f843e/cryptography-44.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:25cd194c39fa5a0aa4169125ee27d1172097857b27109a45fadc59653ec06f44", size = 4299064, upload-time = "2025-05-02T19:35:08.879Z" },
{ url = "https://files.pythonhosted.org/packages/10/a8/8c540a421b44fd267a7d58a1fd5f072a552d72204a3f08194f98889de76d/cryptography-44.0.3-cp37-abi3-win32.whl", hash = "sha256:3be3f649d91cb182c3a6bd336de8b61a0a71965bd13d1a04a0e15b39c3d5809d", size = 2773887, upload-time = "2025-05-02T19:35:10.41Z" },
{ url = "https://files.pythonhosted.org/packages/b9/0d/c4b1657c39ead18d76bbd122da86bd95bdc4095413460d09544000a17d56/cryptography-44.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:3883076d5c4cc56dbef0b898a74eb6992fdac29a7b9013870b34efe4ddb39a0d", size = 3209737, upload-time = "2025-05-02T19:35:12.12Z" },
{ url = "https://files.pythonhosted.org/packages/34/a3/ad08e0bcc34ad436013458d7528e83ac29910943cea42ad7dd4141a27bbb/cryptography-44.0.3-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:5639c2b16764c6f76eedf722dbad9a0914960d3489c0cc38694ddf9464f1bb2f", size = 6673501, upload-time = "2025-05-02T19:35:13.775Z" },
{ url = "https://files.pythonhosted.org/packages/b1/f0/7491d44bba8d28b464a5bc8cc709f25a51e3eac54c0a4444cf2473a57c37/cryptography-44.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3ffef566ac88f75967d7abd852ed5f182da252d23fac11b4766da3957766759", size = 3960307, upload-time = "2025-05-02T19:35:15.917Z" },
{ url = "https://files.pythonhosted.org/packages/f7/c8/e5c5d0e1364d3346a5747cdcd7ecbb23ca87e6dea4f942a44e88be349f06/cryptography-44.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:192ed30fac1728f7587c6f4613c29c584abdc565d7417c13904708db10206645", size = 4170876, upload-time = "2025-05-02T19:35:18.138Z" },
{ url = "https://files.pythonhosted.org/packages/73/96/025cb26fc351d8c7d3a1c44e20cf9a01e9f7cf740353c9c7a17072e4b264/cryptography-44.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7d5fe7195c27c32a64955740b949070f21cba664604291c298518d2e255931d2", size = 3964127, upload-time = "2025-05-02T19:35:19.864Z" },
{ url = "https://files.pythonhosted.org/packages/01/44/eb6522db7d9f84e8833ba3bf63313f8e257729cf3a8917379473fcfd6601/cryptography-44.0.3-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3f07943aa4d7dad689e3bb1638ddc4944cc5e0921e3c227486daae0e31a05e54", size = 3689164, upload-time = "2025-05-02T19:35:21.449Z" },
{ url = "https://files.pythonhosted.org/packages/68/fb/d61a4defd0d6cee20b1b8a1ea8f5e25007e26aeb413ca53835f0cae2bcd1/cryptography-44.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cb90f60e03d563ca2445099edf605c16ed1d5b15182d21831f58460c48bffb93", size = 4198081, upload-time = "2025-05-02T19:35:23.187Z" },
{ url = "https://files.pythonhosted.org/packages/1b/50/457f6911d36432a8811c3ab8bd5a6090e8d18ce655c22820994913dd06ea/cryptography-44.0.3-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:ab0b005721cc0039e885ac3503825661bd9810b15d4f374e473f8c89b7d5460c", size = 3967716, upload-time = "2025-05-02T19:35:25.426Z" },
{ url = "https://files.pythonhosted.org/packages/35/6e/dca39d553075980ccb631955c47b93d87d27f3596da8d48b1ae81463d915/cryptography-44.0.3-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:3bb0847e6363c037df8f6ede57d88eaf3410ca2267fb12275370a76f85786a6f", size = 4197398, upload-time = "2025-05-02T19:35:27.678Z" },
{ url = "https://files.pythonhosted.org/packages/9b/9d/d1f2fe681eabc682067c66a74addd46c887ebacf39038ba01f8860338d3d/cryptography-44.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b0cc66c74c797e1db750aaa842ad5b8b78e14805a9b5d1348dc603612d3e3ff5", size = 4087900, upload-time = "2025-05-02T19:35:29.312Z" },
{ url = "https://files.pythonhosted.org/packages/c4/f5/3599e48c5464580b73b236aafb20973b953cd2e7b44c7c2533de1d888446/cryptography-44.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6866df152b581f9429020320e5eb9794c8780e90f7ccb021940d7f50ee00ae0b", size = 4301067, upload-time = "2025-05-02T19:35:31.547Z" },
{ url = "https://files.pythonhosted.org/packages/a7/6c/d2c48c8137eb39d0c193274db5c04a75dab20d2f7c3f81a7dcc3a8897701/cryptography-44.0.3-cp39-abi3-win32.whl", hash = "sha256:c138abae3a12a94c75c10499f1cbae81294a6f983b3af066390adee73f433028", size = 2775467, upload-time = "2025-05-02T19:35:33.805Z" },
{ url = "https://files.pythonhosted.org/packages/c9/ad/51f212198681ea7b0deaaf8846ee10af99fba4e894f67b353524eab2bbe5/cryptography-44.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:5d186f32e52e66994dce4f766884bcb9c68b8da62d61d9d215bfe5fb56d21334", size = 3210375, upload-time = "2025-05-02T19:35:35.369Z" },
{ url = "https://files.pythonhosted.org/packages/82/b2/2345dc595998caa6f68adf84e8f8b50d18e9fc4638d32b22ea8daedd4b7a/cryptography-45.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:7573d9eebaeceeb55285205dbbb8753ac1e962af3d9640791d12b36864065e71", size = 7056239, upload-time = "2025-05-25T14:16:12.22Z" },
{ url = "https://files.pythonhosted.org/packages/71/3d/ac361649a0bfffc105e2298b720d8b862330a767dab27c06adc2ddbef96a/cryptography-45.0.3-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d377dde61c5d67eb4311eace661c3efda46c62113ff56bf05e2d679e02aebb5b", size = 4205541, upload-time = "2025-05-25T14:16:14.333Z" },
{ url = "https://files.pythonhosted.org/packages/70/3e/c02a043750494d5c445f769e9c9f67e550d65060e0bfce52d91c1362693d/cryptography-45.0.3-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fae1e637f527750811588e4582988932c222f8251f7b7ea93739acb624e1487f", size = 4433275, upload-time = "2025-05-25T14:16:16.421Z" },
{ url = "https://files.pythonhosted.org/packages/40/7a/9af0bfd48784e80eef3eb6fd6fde96fe706b4fc156751ce1b2b965dada70/cryptography-45.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ca932e11218bcc9ef812aa497cdf669484870ecbcf2d99b765d6c27a86000942", size = 4209173, upload-time = "2025-05-25T14:16:18.163Z" },
{ url = "https://files.pythonhosted.org/packages/31/5f/d6f8753c8708912df52e67969e80ef70b8e8897306cd9eb8b98201f8c184/cryptography-45.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af3f92b1dc25621f5fad065288a44ac790c5798e986a34d393ab27d2b27fcff9", size = 3898150, upload-time = "2025-05-25T14:16:20.34Z" },
{ url = "https://files.pythonhosted.org/packages/8b/50/f256ab79c671fb066e47336706dc398c3b1e125f952e07d54ce82cf4011a/cryptography-45.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2f8f8f0b73b885ddd7f3d8c2b2234a7d3ba49002b0223f58cfde1bedd9563c56", size = 4466473, upload-time = "2025-05-25T14:16:22.605Z" },
{ url = "https://files.pythonhosted.org/packages/62/e7/312428336bb2df0848d0768ab5a062e11a32d18139447a76dfc19ada8eed/cryptography-45.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9cc80ce69032ffa528b5e16d217fa4d8d4bb7d6ba8659c1b4d74a1b0f4235fca", size = 4211890, upload-time = "2025-05-25T14:16:24.738Z" },
{ url = "https://files.pythonhosted.org/packages/e7/53/8a130e22c1e432b3c14896ec5eb7ac01fb53c6737e1d705df7e0efb647c6/cryptography-45.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:c824c9281cb628015bfc3c59335163d4ca0540d49de4582d6c2637312907e4b1", size = 4466300, upload-time = "2025-05-25T14:16:26.768Z" },
{ url = "https://files.pythonhosted.org/packages/ba/75/6bb6579688ef805fd16a053005fce93944cdade465fc92ef32bbc5c40681/cryptography-45.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:5833bb4355cb377ebd880457663a972cd044e7f49585aee39245c0d592904578", size = 4332483, upload-time = "2025-05-25T14:16:28.316Z" },
{ url = "https://files.pythonhosted.org/packages/2f/11/2538f4e1ce05c6c4f81f43c1ef2bd6de7ae5e24ee284460ff6c77e42ca77/cryptography-45.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9bb5bf55dcb69f7067d80354d0a348368da907345a2c448b0babc4215ccd3497", size = 4573714, upload-time = "2025-05-25T14:16:30.474Z" },
{ url = "https://files.pythonhosted.org/packages/f5/bb/e86e9cf07f73a98d84a4084e8fd420b0e82330a901d9cac8149f994c3417/cryptography-45.0.3-cp311-abi3-win32.whl", hash = "sha256:3ad69eeb92a9de9421e1f6685e85a10fbcfb75c833b42cc9bc2ba9fb00da4710", size = 2934752, upload-time = "2025-05-25T14:16:32.204Z" },
{ url = "https://files.pythonhosted.org/packages/c7/75/063bc9ddc3d1c73e959054f1fc091b79572e716ef74d6caaa56e945b4af9/cryptography-45.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:97787952246a77d77934d41b62fb1b6f3581d83f71b44796a4158d93b8f5c490", size = 3412465, upload-time = "2025-05-25T14:16:33.888Z" },
{ url = "https://files.pythonhosted.org/packages/71/9b/04ead6015229a9396890d7654ee35ef630860fb42dc9ff9ec27f72157952/cryptography-45.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:c92519d242703b675ccefd0f0562eb45e74d438e001f8ab52d628e885751fb06", size = 7031892, upload-time = "2025-05-25T14:16:36.214Z" },
{ url = "https://files.pythonhosted.org/packages/46/c7/c7d05d0e133a09fc677b8a87953815c522697bdf025e5cac13ba419e7240/cryptography-45.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5edcb90da1843df85292ef3a313513766a78fbbb83f584a5a58fb001a5a9d57", size = 4196181, upload-time = "2025-05-25T14:16:37.934Z" },
{ url = "https://files.pythonhosted.org/packages/08/7a/6ad3aa796b18a683657cef930a986fac0045417e2dc428fd336cfc45ba52/cryptography-45.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38deed72285c7ed699864f964a3f4cf11ab3fb38e8d39cfcd96710cd2b5bb716", size = 4423370, upload-time = "2025-05-25T14:16:39.502Z" },
{ url = "https://files.pythonhosted.org/packages/4f/58/ec1461bfcb393525f597ac6a10a63938d18775b7803324072974b41a926b/cryptography-45.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5555365a50efe1f486eed6ac7062c33b97ccef409f5970a0b6f205a7cfab59c8", size = 4197839, upload-time = "2025-05-25T14:16:41.322Z" },
{ url = "https://files.pythonhosted.org/packages/d4/3d/5185b117c32ad4f40846f579369a80e710d6146c2baa8ce09d01612750db/cryptography-45.0.3-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9e4253ed8f5948a3589b3caee7ad9a5bf218ffd16869c516535325fece163dcc", size = 3886324, upload-time = "2025-05-25T14:16:43.041Z" },
{ url = "https://files.pythonhosted.org/packages/67/85/caba91a57d291a2ad46e74016d1f83ac294f08128b26e2a81e9b4f2d2555/cryptography-45.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cfd84777b4b6684955ce86156cfb5e08d75e80dc2585e10d69e47f014f0a5342", size = 4450447, upload-time = "2025-05-25T14:16:44.759Z" },
{ url = "https://files.pythonhosted.org/packages/ae/d1/164e3c9d559133a38279215c712b8ba38e77735d3412f37711b9f8f6f7e0/cryptography-45.0.3-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:a2b56de3417fd5f48773ad8e91abaa700b678dc7fe1e0c757e1ae340779acf7b", size = 4200576, upload-time = "2025-05-25T14:16:46.438Z" },
{ url = "https://files.pythonhosted.org/packages/71/7a/e002d5ce624ed46dfc32abe1deff32190f3ac47ede911789ee936f5a4255/cryptography-45.0.3-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:57a6500d459e8035e813bd8b51b671977fb149a8c95ed814989da682314d0782", size = 4450308, upload-time = "2025-05-25T14:16:48.228Z" },
{ url = "https://files.pythonhosted.org/packages/87/ad/3fbff9c28cf09b0a71e98af57d74f3662dea4a174b12acc493de00ea3f28/cryptography-45.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f22af3c78abfbc7cbcdf2c55d23c3e022e1a462ee2481011d518c7fb9c9f3d65", size = 4325125, upload-time = "2025-05-25T14:16:49.844Z" },
{ url = "https://files.pythonhosted.org/packages/f5/b4/51417d0cc01802304c1984d76e9592f15e4801abd44ef7ba657060520bf0/cryptography-45.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:232954730c362638544758a8160c4ee1b832dc011d2c41a306ad8f7cccc5bb0b", size = 4560038, upload-time = "2025-05-25T14:16:51.398Z" },
{ url = "https://files.pythonhosted.org/packages/80/38/d572f6482d45789a7202fb87d052deb7a7b136bf17473ebff33536727a2c/cryptography-45.0.3-cp37-abi3-win32.whl", hash = "sha256:cb6ab89421bc90e0422aca911c69044c2912fc3debb19bb3c1bfe28ee3dff6ab", size = 2924070, upload-time = "2025-05-25T14:16:53.472Z" },
{ url = "https://files.pythonhosted.org/packages/91/5a/61f39c0ff4443651cc64e626fa97ad3099249152039952be8f344d6b0c86/cryptography-45.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:d54ae41e6bd70ea23707843021c778f151ca258081586f0cfa31d936ae43d1b2", size = 3395005, upload-time = "2025-05-25T14:16:55.134Z" },
]
[[package]]
@ -1118,11 +1118,12 @@ s3 = [
[[package]]
name = "django-tenants"
version = "3.7.0"
source = { git = "https://github.com/goauthentik/django-tenants.git?branch=authentik-fixes#156e53a6f5902d74b73dd9d0192fffaa2587a740" }
version = "3.8.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "django" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a8/7b/22e3bb79d48e5a4fdcacdcdc27bbc5c2523a2b7892b440bfe229f313d823/django_tenants-3.8.0.tar.gz", hash = "sha256:07d009d5d01be2d65c3f5ddbf323d58d1228838fc1a64fded15c8e5c6f41cf8f", size = 154307, upload-time = "2025-05-23T16:07:24.307Z" }
[[package]]
name = "djangorestframework"
@ -2576,14 +2577,14 @@ wheels = [
[[package]]
name = "pyopenssl"
version = "25.0.0"
version = "25.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cryptography" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9f/26/e25b4a374b4639e0c235527bbe31c0524f26eda701d79456a7e1877f4cc5/pyopenssl-25.0.0.tar.gz", hash = "sha256:cd2cef799efa3936bb08e8ccb9433a575722b9dd986023f1cabc4ae64e9dac16", size = 179573, upload-time = "2025-01-12T17:22:48.897Z" }
sdist = { url = "https://files.pythonhosted.org/packages/04/8c/cd89ad05804f8e3c17dea8f178c3f40eeab5694c30e0c9f5bcd49f576fc3/pyopenssl-25.1.0.tar.gz", hash = "sha256:8d031884482e0c67ee92bf9a4d8cceb08d92aba7136432ffb0703c5280fc205b", size = 179937, upload-time = "2025-05-17T16:28:31.31Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ca/d7/eb76863d2060dcbe7c7e6cccfd95ac02ea0b9acc37745a0d99ff6457aefb/pyOpenSSL-25.0.0-py3-none-any.whl", hash = "sha256:424c247065e46e76a37411b9ab1782541c23bb658bf003772c3405fbaa128e90", size = 56453, upload-time = "2025-01-12T17:22:43.44Z" },
{ url = "https://files.pythonhosted.org/packages/80/28/2659c02301b9500751f8d42f9a6632e1508aa5120de5e43042b8b30f8d5d/pyopenssl-25.1.0-py3-none-any.whl", hash = "sha256:2b11f239acc47ac2e5aca04fd7fa829800aeee22a2eb30d744572a157bd8a1ab", size = 56771, upload-time = "2025-05-17T16:28:29.197Z" },
]
[[package]]

View File

@ -4,30 +4,27 @@
* @import {
* OnLoadArgs,
* OnLoadResult,
* OnResolveArgs,
* OnResolveResult,
* Plugin,
* PluginBuild
* } from "esbuild"
*/
import { MonoRepoRoot } from "@goauthentik/core/paths/node";
import * as fs from "node:fs/promises";
import * as path from "node:path";
/**
* @typedef {Omit<OnLoadArgs, 'pluginData'> & LoadDataFields} LoadData
* Data passed to `onload`.
* @typedef {Omit<OnLoadArgs, 'pluginData'> & LoadDataFields} LoadData Data passed to `onload`.
*
* @typedef LoadDataFields
* Extra fields given in `data` to `onload`.
* @property {PluginData | null | undefined} [pluginData]
* Plugin data.
* @typedef LoadDataFields Extra fields given in `data` to `onload`.
* @property {PluginData | null | undefined} [pluginData] Plugin data.
*
*
* @typedef PluginData
* Extra data passed.
* @property {Buffer | string | null | undefined} [contents]
* File contents.
* @typedef PluginData Extra data passed.
* @property {Buffer | string | null | undefined} [contents] File contents.
*/
const name = "mdx-plugin";
const pluginName = "mdx-plugin";
/**
* @typedef MDXPluginOptions
@ -38,28 +35,35 @@ const name = "mdx-plugin";
/**
* Bundle MDX into JSON modules.
*
* @param {MDXPluginOptions} options Options.
* @returns {Plugin} Plugin.
* @param {MDXPluginOptions} options
* @returns {Plugin}
*/
export function mdxPlugin({ root }) {
return { name, setup };
// TODO: Replace with `resolvePackage` after NPM Workspaces support is added.
const docsPackageRoot = path.resolve(MonoRepoRoot, "website");
/**
* @param {PluginBuild} build
* Build.
* @returns {undefined}
* Nothing.
*/
function setup(build) {
build.onLoad({ filter: /\.mdx?$/ }, onload);
/**
* @param {OnResolveArgs} args
* @returns {Promise<OnResolveResult>}
*/
async function resolveListener(args) {
if (!args.path.startsWith("~")) return args;
return {
path: path.resolve(docsPackageRoot, args.path.slice(1)),
pluginName,
};
}
/**
* @param {LoadData} data
* Data.
* @returns {Promise<OnLoadResult>}
* Result.
*/
async function onload(data) {
async function loadListener(data) {
const content = String(
data.pluginData &&
data.pluginData.contents !== null &&
@ -77,7 +81,16 @@ export function mdxPlugin({ root }) {
return {
contents: JSON.stringify({ content, publicPath, publicDirectory }),
loader: "file",
pluginName,
};
}
build.onResolve({ filter: /\.mdx?$/ }, resolveListener);
build.onLoad({ filter: /\.mdx?$/ }, loadListener);
}
return {
name: pluginName,
setup,
};
}

786
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -67,6 +67,7 @@
"#admin/*": "./src/admin/*.js",
"#flow/*.css": "./src/flow/*.css",
"#flow/*": "./src/flow/*.js",
"#locales/*": "./src/locales/*.js",
"#stories/*": "./src/stories/*.js",
"#*/browser": {
"types": "./out/*/browser.d.ts",
@ -90,7 +91,7 @@
"@codemirror/legacy-modes": "^6.4.1",
"@codemirror/theme-one-dark": "^6.1.2",
"@floating-ui/dom": "^1.6.11",
"@formatjs/intl-listformat": "^7.5.7",
"@formatjs/intl-listformat": "^7.7.11",
"@fortawesome/fontawesome-free": "^6.6.0",
"@goauthentik/api": "^2025.4.1-1747687715",
"@lit/context": "^1.1.2",
@ -115,7 +116,7 @@
"date-fns": "^4.1.0",
"deepmerge-ts": "^7.1.5",
"dompurify": "^3.2.6",
"fuse.js": "^7.0.0",
"fuse.js": "^7.1.0",
"guacamole-common-js": "^1.5.0",
"hastscript": "^9.0.1",
"lit": "^3.2.0",
@ -143,8 +144,8 @@
"@eslint/js": "^9.27.0",
"@goauthentik/core": "^1.0.0",
"@goauthentik/esbuild-plugin-live-reload": "^1.0.4",
"@goauthentik/eslint-config": "^1.0.4",
"@goauthentik/prettier-config": "^1.0.4",
"@goauthentik/eslint-config": "^1.0.5",
"@goauthentik/prettier-config": "^1.0.5",
"@goauthentik/tsconfig": "^1.0.4",
"@hcaptcha/types": "^1.0.4",
"@lit/localize-tools": "^0.8.0",
@ -182,7 +183,7 @@
"eslint-plugin-wc": "^3.0.1",
"github-slugger": "^2.0.0",
"globals": "^15.10.0",
"knip": "^5.30.6",
"knip": "^5.58.0",
"lit-analyzer": "^2.0.3",
"npm-run-all": "^4.1.5",
"prettier": "^3.3.3",

View File

@ -45,7 +45,7 @@
}
},
"devDependencies": {
"@goauthentik/prettier-config": "^1.0.4",
"@goauthentik/prettier-config": "^1.0.5",
"@goauthentik/tsconfig": "^1.0.4",
"@types/node": "^22.15.21",
"prettier": "^3.3.3",

19
web/paths/index.js Normal file
View File

@ -0,0 +1,19 @@
/**
* @file Paths used by the web package.
*
* @runtime common
*/
/**
* The name of the distribution directory.
*
* @runtime common
*/
export const DistDirectoryName = "dist";
/**
* The name of the static file directory.
*
* @runtime common
*/
export const StaticDirectoryName = "static";

View File

@ -1,3 +1,9 @@
/**
* @file Paths used by the web package.
*
* @runtime node
*/
import { DistDirectoryName } from "#paths";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
@ -11,18 +17,17 @@ const relativeDirname = dirname(fileURLToPath(import.meta.url));
/**
* The root of the web package.
*
* @runtime node
*/
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.
*
* @runtime node
*/
export const DistDirectory = /** @type {`${WebPackageIdentifier}/${DistDirectoryName}`} */ (
resolve(PackageRoot, DistDirectoryName)
@ -43,6 +48,8 @@ export const DistDirectory = /** @type {`${WebPackageIdentifier}/${DistDirectory
* Entry points available for building.
*
* @satisfies {Record<string, EntryPointTarget>}
*
* @runtime node
*/
export const EntryPoint = /** @type {const} */ ({
Admin: {

View File

@ -6,7 +6,8 @@
*/
import { mdxPlugin } from "#bundler/mdx-plugin/node";
import { createBundleDefinitions } from "#bundler/utils/node";
import { DistDirectory, DistDirectoryName, EntryPoint, PackageRoot } from "#paths/node";
import { DistDirectoryName } from "#paths";
import { DistDirectory, EntryPoint, PackageRoot } from "#paths/node";
import { NodeEnvironment } from "@goauthentik/core/environment/node";
import { MonoRepoRoot, resolvePackage } from "@goauthentik/core/paths/node";
import { readBuildIdentifier } from "@goauthentik/core/version/node";
@ -26,7 +27,7 @@ const patternflyPath = resolvePackage("@patternfly/patternfly", import.meta);
*/
const BASE_ESBUILD_OPTIONS = {
entryNames: `[dir]/[name]-${readBuildIdentifier()}`,
chunkNames: "[dir]/chunks/[name]-[hash]",
chunkNames: "[dir]/chunks/[hash]",
assetNames: "assets/[dir]/[name]-[hash]",
publicPath: path.join("/static", DistDirectoryName),
outdir: DistDirectory,

View File

@ -1,9 +1,8 @@
import { WithBrandConfig } from "#elements/mixins/branding";
import { WithLicenseSummary } from "#elements/mixins/license";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
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 { msg } from "@lit/localize";
@ -57,7 +56,8 @@ export class AboutModal extends WithLicenseSummary(WithBrandConfig(ModalButton))
}
renderModal() {
let product = globalAK().brand.brandingTitle || DefaultBrand.brandingTitle;
let product = this.brandingTitle;
if (this.licenseSummary.status !== LicenseSummaryStatusEnum.Unlicensed) {
product += ` ${msg("Enterprise")}`;
}
@ -73,7 +73,7 @@ export class AboutModal extends WithLicenseSummary(WithBrandConfig(ModalButton))
<div class="pf-c-about-modal-box__brand">
<img
class="pf-c-about-modal-box__brand-image"
src=${this.brand?.brandingFavicon ?? DefaultBrand.brandingFavicon}
src=${this.brandingFavicon}
alt="${msg("authentik Logo")}"
/>
</div>

View File

@ -1,9 +1,12 @@
import "#admin/AdminInterface/AboutModal";
import type { AboutModal } from "#admin/AdminInterface/AboutModal";
import { ROUTES } from "#admin/Routes";
import { EVENT_API_DRAWER_TOGGLE, EVENT_NOTIFICATION_DRAWER_TOGGLE } from "#common/constants";
import { configureSentry } from "#common/sentry/index";
import { me } from "#common/users";
import { WebsocketClient } from "#common/ws";
import { AuthenticatedInterface } from "#elements/Interface/Interface";
import { WithLicenseSummary } from "#elements/Interface/licenseSummaryProvider";
import { SidebarToggleEventDetail } from "#components/ak-page-header";
import { AuthenticatedInterface } from "#elements/AuthenticatedInterface";
import "#elements/ak-locale-context/ak-locale-context";
import "#elements/banner/EnterpriseStatusBanner";
import "#elements/banner/EnterpriseStatusBanner";
@ -11,16 +14,13 @@ import "#elements/banner/VersionBanner";
import "#elements/banner/VersionBanner";
import "#elements/messages/MessageContainer";
import "#elements/messages/MessageContainer";
import { WithCapabilitiesConfig } from "#elements/mixins/capabilities";
import "#elements/notifications/APIDrawer";
import "#elements/notifications/NotificationDrawer";
import { getURLParam, updateURLParams } from "#elements/router/RouteMatch";
import "#elements/router/RouterOutlet";
import "#elements/sidebar/Sidebar";
import "#elements/sidebar/SidebarItem";
import "@goauthentik/admin/AdminInterface/AboutModal";
import type { AboutModal } from "@goauthentik/admin/AdminInterface/AboutModal";
import { ROUTES } from "@goauthentik/admin/Routes";
import { SidebarToggleEventDetail } from "@goauthentik/components/ak-page-header.js";
import { CSSResult, TemplateResult, css, html, nothing } from "lit";
import { customElement, eventOptions, property, query } from "lit/decorators.js";
@ -45,7 +45,7 @@ if (process.env.NODE_ENV === "development") {
}
@customElement("ak-interface-admin")
export class AdminInterface extends WithLicenseSummary(AuthenticatedInterface) {
export class AdminInterface extends WithCapabilitiesConfig(AuthenticatedInterface) {
//#region Properties
@property({ type: Boolean })
@ -202,7 +202,7 @@ export class AdminInterface extends WithLicenseSummary(AuthenticatedInterface) {
<ak-sidebar class="${classMap(sidebarClasses)}">
${renderSidebarItems(AdminSidebarEntries)}
${this.config?.capabilities.includes(CapabilitiesEnum.IsEnterprise)
${this.can(CapabilitiesEnum.IsEnterprise)
? renderSidebarItems(AdminSidebarEnterpriseEntries)
: nothing}
</ak-sidebar>

View File

@ -11,10 +11,10 @@ import "#admin/admin-overview/charts/SyncStatusChart";
import { me } from "#common/users";
import "#components/ak-page-header";
import { AKElement } from "#elements/Base";
import { WithLicenseSummary } from "#elements/Interface/licenseSummaryProvider";
import "#elements/cards/AggregatePromiseCard";
import type { QuickAction } from "#elements/cards/QuickActionsCard";
import "#elements/cards/QuickActionsCard";
import { WithLicenseSummary } from "#elements/mixins/license";
import { paramURL } from "#elements/router/RouterOutlet";
import { createReleaseNotesURL } from "@goauthentik/core/version";

View File

@ -1,3 +1,4 @@
import { CapabilitiesEnum, WithCapabilitiesConfig } from "#elements/mixins/capabilities";
import "@goauthentik/admin/applications/ProviderSelectModal";
import { iconHelperText } from "@goauthentik/admin/helperText";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
@ -6,18 +7,14 @@ import "@goauthentik/components/ak-radio-input";
import "@goauthentik/components/ak-switch-input";
import "@goauthentik/components/ak-text-input";
import "@goauthentik/components/ak-textarea-input";
import "@goauthentik/elements/Alert.js";
import {
CapabilitiesEnum,
WithCapabilitiesConfig,
} from "@goauthentik/elements/Interface/capabilitiesProvider";
import "@goauthentik/elements/Alert";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
import "@goauthentik/elements/forms/ModalForm";
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import "@goauthentik/elements/forms/ProxyForm";
import "@goauthentik/elements/forms/Radio";
import "@goauthentik/elements/forms/SearchSelect";
import "@goauthentik/elements/forms/SearchSelect/ak-search-select";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg } from "@lit/localize";

View File

@ -1,17 +1,17 @@
import "@goauthentik/admin/applications/ApplicationForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import MDApplication from "@goauthentik/docs/add-secure-apps/applications/index.md";
import "@goauthentik/elements/AppIcon.js";
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
import "@goauthentik/elements/ak-mdx";
import "@goauthentik/elements/buttons/SpinnerButton";
import "@goauthentik/elements/forms/DeleteBulkForm";
import "@goauthentik/elements/forms/ModalForm";
import { getURLParam } from "@goauthentik/elements/router/RouteMatch";
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { TableColumn } from "@goauthentik/elements/table/Table";
import { TablePage } from "@goauthentik/elements/table/TablePage";
import "#admin/applications/ApplicationForm";
import { DEFAULT_CONFIG } from "#common/api/config";
import "#elements/AppIcon";
import "#elements/ak-mdx/ak-mdx";
import "#elements/buttons/SpinnerButton/ak-spinner-button";
import "#elements/forms/DeleteBulkForm";
import "#elements/forms/ModalForm";
import { WithBrandConfig } from "#elements/mixins/branding";
import { getURLParam } from "#elements/router/RouteMatch";
import { PaginatedResponse } from "#elements/table/Table";
import { TableColumn } from "#elements/table/Table";
import { TablePage } from "#elements/table/TablePage";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import MDApplication from "~docs/add-secure-apps/applications/index.md";
import { msg, str } from "@lit/localize";
import { CSSResult, TemplateResult, css, html } from "lit";
@ -22,7 +22,7 @@ import PFCard from "@patternfly/patternfly/components/Card/card.css";
import { Application, CoreApi, PoliciesApi } from "@goauthentik/api";
import "./ApplicationWizardHint";
import "./ApplicationWizardHint.js";
export const applicationListStyle = css`
/* Fix alignment issues with images in tables */
@ -50,7 +50,7 @@ export class ApplicationListPage extends WithBrandConfig(TablePage<Application>)
}
pageDescription(): string {
return msg(
str`External applications that use ${this.brand?.brandingTitle ?? "authentik"} as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.`,
str`External applications that use ${this.brandingTitle} as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.`,
);
}
pageIcon(): string {

View File

@ -3,5 +3,5 @@ import { createContext } from "@lit/context";
import { LocalTypeCreate } from "./steps/ProviderChoices.js";
export const applicationWizardProvidersContext = createContext<LocalTypeCreate[]>(
Symbol("ak-application-wizard-providers-context"),
Symbol.for("ak-application-wizard-providers-context"),
);

View File

@ -1,8 +1,8 @@
import { WithLicenseSummary } from "#elements/mixins/license";
import { ApplicationWizardStep } from "@goauthentik/admin/applications/wizard/ApplicationWizardStep.js";
import "@goauthentik/admin/applications/wizard/ak-wizard-title.js";
import type { NavigableButton, WizardButton } from "@goauthentik/components/ak-wizard/types";
import "@goauthentik/elements/EmptyState.js";
import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider.js";
import { bound } from "@goauthentik/elements/decorators/bound.js";
import "@goauthentik/elements/forms/FormGroup.js";
import "@goauthentik/elements/forms/HorizontalFormElement.js";

View File

@ -1,7 +1,7 @@
import { WithBrandConfig } from "#elements/mixins/branding";
import "@goauthentik/admin/applications/wizard/ak-wizard-title.js";
import { ValidationRecord } from "@goauthentik/admin/applications/wizard/types";
import { renderForm } from "@goauthentik/admin/providers/ldap/LDAPProviderFormForm.js";
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider.js";
import { msg } from "@lit/localize";
import { html } from "lit";

View File

@ -1,7 +1,7 @@
import { WithBrandConfig } from "#elements/mixins/branding";
import "@goauthentik/admin/applications/wizard/ak-wizard-title.js";
import { ValidationRecord } from "@goauthentik/admin/applications/wizard/types";
import { renderForm } from "@goauthentik/admin/providers/radius/RadiusProviderFormForm.js";
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
import { msg } from "@lit/localize";
import { customElement } from "@lit/reactive-element/decorators.js";

View File

@ -1,6 +1,6 @@
import { WithLicenseSummary } from "#elements/mixins/license";
import "@goauthentik/elements/Alert";
import { AKElement } from "@goauthentik/elements/Base";
import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider";
import { msg } from "@lit/localize";
import { html, nothing } from "lit";

View File

@ -1,10 +1,7 @@
import { CapabilitiesEnum, WithCapabilitiesConfig } from "#elements/mixins/capabilities";
import { DesignationToLabel, LayoutToLabel } from "@goauthentik/admin/flows/utils";
import { AuthenticationEnum } from "@goauthentik/api/dist/models/AuthenticationEnum";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import {
CapabilitiesEnum,
WithCapabilitiesConfig,
} from "@goauthentik/elements/Interface/capabilitiesProvider";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";

View File

@ -1,3 +1,5 @@
import { WithBrandConfig } from "#elements/mixins/branding";
import { CapabilitiesEnum, WithCapabilitiesConfig } from "#elements/mixins/capabilities";
import "@goauthentik/admin/users/ServiceAccountForm";
import "@goauthentik/admin/users/UserActiveForm";
import "@goauthentik/admin/users/UserForm";
@ -11,11 +13,6 @@ import { MessageLevel } from "@goauthentik/common/messages";
import { formatElapsedTime } from "@goauthentik/common/temporal";
import { me } from "@goauthentik/common/users";
import "@goauthentik/components/ak-status-label";
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
import {
CapabilitiesEnum,
WithCapabilitiesConfig,
} from "@goauthentik/elements/Interface/capabilitiesProvider";
import "@goauthentik/elements/buttons/ActionButton";
import "@goauthentik/elements/buttons/Dropdown";
import "@goauthentik/elements/forms/DeleteBulkForm";
@ -295,7 +292,7 @@ export class RelatedUserList extends WithBrandConfig(WithCapabilitiesConfig(Tabl
${msg("Set password")}
</button>
</ak-forms-modal>
${this.brand?.flowRecovery
${this.brand.flowRecovery
? html`
<ak-action-button
class="pf-m-secondary"

View File

@ -1,8 +1,8 @@
import { WithBrandConfig } from "#elements/mixins/branding";
import "@goauthentik/admin/common/ak-crypto-certificate-search";
import "@goauthentik/admin/common/ak-flow-search/ak-branded-flow-search";
import { BaseProviderForm } from "@goauthentik/admin/providers/BaseProviderForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
import { customElement } from "lit/decorators.js";

View File

@ -4,7 +4,6 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EVENT_REFRESH } from "@goauthentik/common/constants";
import renderDescriptionList from "@goauthentik/components/DescriptionList";
import "@goauthentik/components/events/ObjectChangelog";
import MDProviderOAuth2 from "@goauthentik/docs/add-secure-apps/providers/oauth2/index.mdx";
import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/CodeMirror";
import "@goauthentik/elements/EmptyState";
@ -12,6 +11,7 @@ import "@goauthentik/elements/Tabs";
import "@goauthentik/elements/ak-mdx";
import "@goauthentik/elements/buttons/ModalButton";
import "@goauthentik/elements/buttons/SpinnerButton";
import MDProviderOAuth2 from "~docs/add-secure-apps/providers/oauth2/index.mdx";
import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, html } from "lit";

View File

@ -5,14 +5,6 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EVENT_REFRESH } from "@goauthentik/common/constants";
import "@goauthentik/components/ak-status-label";
import "@goauthentik/components/events/ObjectChangelog";
import MDCaddyStandalone from "@goauthentik/docs/add-secure-apps/providers/proxy/_caddy_standalone.md";
import MDNginxIngress from "@goauthentik/docs/add-secure-apps/providers/proxy/_nginx_ingress.md";
import MDNginxPM from "@goauthentik/docs/add-secure-apps/providers/proxy/_nginx_proxy_manager.md";
import MDNginxStandalone from "@goauthentik/docs/add-secure-apps/providers/proxy/_nginx_standalone.md";
import MDTraefikCompose from "@goauthentik/docs/add-secure-apps/providers/proxy/_traefik_compose.md";
import MDTraefikIngress from "@goauthentik/docs/add-secure-apps/providers/proxy/_traefik_ingress.md";
import MDTraefikStandalone from "@goauthentik/docs/add-secure-apps/providers/proxy/_traefik_standalone.md";
import MDHeaderAuthentication from "@goauthentik/docs/add-secure-apps/providers/proxy/header_authentication.mdx";
import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/CodeMirror";
import "@goauthentik/elements/Tabs";
@ -22,6 +14,14 @@ import "@goauthentik/elements/buttons/ModalButton";
import "@goauthentik/elements/buttons/SpinnerButton";
import { getURLParam } from "@goauthentik/elements/router/RouteMatch";
import { formatSlug } from "@goauthentik/elements/router/utils.js";
import MDCaddyStandalone from "~docs/add-secure-apps/providers/proxy/_caddy_standalone.md";
import MDNginxIngress from "~docs/add-secure-apps/providers/proxy/_nginx_ingress.md";
import MDNginxPM from "~docs/add-secure-apps/providers/proxy/_nginx_proxy_manager.md";
import MDNginxStandalone from "~docs/add-secure-apps/providers/proxy/_nginx_standalone.md";
import MDTraefikCompose from "~docs/add-secure-apps/providers/proxy/_traefik_compose.md";
import MDTraefikIngress from "~docs/add-secure-apps/providers/proxy/_traefik_ingress.md";
import MDTraefikStandalone from "~docs/add-secure-apps/providers/proxy/_traefik_standalone.md";
import MDHeaderAuthentication from "~docs/add-secure-apps/providers/proxy/header_authentication.mdx";
import { msg } from "@lit/localize";
import { CSSResult, PropertyValues, TemplateResult, css, html } from "lit";

View File

@ -1,6 +1,6 @@
import { WithBrandConfig } from "#elements/mixins/branding";
import { BaseProviderForm } from "@goauthentik/admin/providers/BaseProviderForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
import { customElement } from "lit/decorators.js";

View File

@ -7,13 +7,13 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EVENT_REFRESH } from "@goauthentik/common/constants";
import "@goauthentik/components/ak-status-label";
import "@goauthentik/components/events/ObjectChangelog";
import MDSCIMProvider from "@goauthentik/docs/add-secure-apps/providers/scim/index.md";
import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/Tabs";
import "@goauthentik/elements/ak-mdx";
import "@goauthentik/elements/buttons/ActionButton";
import "@goauthentik/elements/buttons/ModalButton";
import "@goauthentik/elements/sync/SyncStatusCard";
import MDSCIMProvider from "~docs/add-secure-apps/providers/scim/index.md";
import { msg } from "@lit/localize";
import { CSSResult, PropertyValues, TemplateResult, html } from "lit";

View File

@ -1,3 +1,4 @@
import { CapabilitiesEnum, WithCapabilitiesConfig } from "#elements/mixins/capabilities";
import "@goauthentik/admin/common/ak-flow-search/ak-source-flow-search";
import { iconHelperText, placeholderHelperText } from "@goauthentik/admin/helperText";
import { BaseSourceForm } from "@goauthentik/admin/sources/BaseSourceForm";
@ -9,10 +10,6 @@ import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
import "@goauthentik/components/ak-switch-input";
import "@goauthentik/components/ak-text-input";
import "@goauthentik/components/ak-textarea-input";
import {
CapabilitiesEnum,
WithCapabilitiesConfig,
} from "@goauthentik/elements/Interface/capabilitiesProvider";
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";

View File

@ -4,7 +4,6 @@ import "@goauthentik/admin/sources/kerberos/KerberosSourceForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EVENT_REFRESH } from "@goauthentik/common/constants";
import "@goauthentik/components/events/ObjectChangelog";
import MDSourceKerberosBrowser from "@goauthentik/docs/users-sources/sources/protocols/kerberos/browser.md";
import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/CodeMirror";
import "@goauthentik/elements/Tabs";
@ -13,6 +12,7 @@ import "@goauthentik/elements/buttons/ActionButton";
import "@goauthentik/elements/buttons/SpinnerButton";
import "@goauthentik/elements/forms/ModalForm";
import "@goauthentik/elements/sync/SyncStatusCard";
import MDSourceKerberosBrowser from "~docs/users-sources/sources/protocols/kerberos/browser.md";
import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, html } from "lit";

View File

@ -1,3 +1,4 @@
import { CapabilitiesEnum, WithCapabilitiesConfig } from "#elements/mixins/capabilities";
import "@goauthentik/admin/common/ak-flow-search/ak-source-flow-search";
import { iconHelperText, placeholderHelperText } from "@goauthentik/admin/helperText";
import { BaseSourceForm } from "@goauthentik/admin/sources/BaseSourceForm";
@ -9,10 +10,6 @@ import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
import "@goauthentik/components/ak-radio-input";
import "@goauthentik/elements/CodeMirror";
import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";
import {
CapabilitiesEnum,
WithCapabilitiesConfig,
} from "@goauthentik/elements/Interface/capabilitiesProvider";
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";

View File

@ -1,3 +1,4 @@
import { CapabilitiesEnum, WithCapabilitiesConfig } from "#elements/mixins/capabilities";
import "@goauthentik/admin/common/ak-flow-search/ak-source-flow-search";
import { iconHelperText, placeholderHelperText } from "@goauthentik/admin/helperText";
import { BaseSourceForm } from "@goauthentik/admin/sources/BaseSourceForm";
@ -8,10 +9,6 @@ import {
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { PlexAPIClient, PlexResource, popupCenterScreen } from "@goauthentik/common/helpers/plex";
import { ascii_letters, digits, randomString } from "@goauthentik/common/utils";
import {
CapabilitiesEnum,
WithCapabilitiesConfig,
} from "@goauthentik/elements/Interface/capabilitiesProvider";
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
import "@goauthentik/elements/ak-dual-select/ak-dual-select-provider.js";

View File

@ -1,3 +1,4 @@
import { CapabilitiesEnum, WithCapabilitiesConfig } from "#elements/mixins/capabilities";
import "@goauthentik/admin/common/ak-crypto-certificate-search";
import "@goauthentik/admin/common/ak-flow-search/ak-source-flow-search";
import { iconHelperText, placeholderHelperText } from "@goauthentik/admin/helperText";
@ -7,10 +8,6 @@ import {
UserMatchingModeToLabel,
} from "@goauthentik/admin/sources/oauth/utils";
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
import {
CapabilitiesEnum,
WithCapabilitiesConfig,
} from "@goauthentik/elements/Interface/capabilitiesProvider";
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";

View File

@ -1,3 +1,5 @@
import { WithBrandConfig } from "#elements/mixins/branding";
import { CapabilitiesEnum, WithCapabilitiesConfig } from "#elements/mixins/capabilities";
import type { AdminInterface } from "@goauthentik/admin/AdminInterface/index.entrypoint.js";
import "@goauthentik/admin/users/ServiceAccountForm";
import "@goauthentik/admin/users/UserActiveForm";
@ -11,15 +13,10 @@ import { parseAPIResponseError } from "@goauthentik/common/errors/network";
import { userTypeToLabel } from "@goauthentik/common/labels";
import { MessageLevel } from "@goauthentik/common/messages";
import { formatElapsedTime } from "@goauthentik/common/temporal";
import { rootInterface } from "@goauthentik/common/theme";
import { DefaultUIConfig, uiConfig } from "@goauthentik/common/ui/config";
import { me } from "@goauthentik/common/users";
import "@goauthentik/components/ak-status-label";
import { rootInterface } from "@goauthentik/elements/Base";
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
import {
CapabilitiesEnum,
WithCapabilitiesConfig,
} from "@goauthentik/elements/Interface/capabilitiesProvider";
import "@goauthentik/elements/TreeView";
import "@goauthentik/elements/buttons/ActionButton";
import "@goauthentik/elements/forms/DeleteBulkForm";

View File

@ -22,11 +22,11 @@ import "#components/events/ObjectChangelog";
import "#components/events/UserEvents";
import { AKElement } from "#elements/Base";
import "#elements/CodeMirror";
import { WithCapabilitiesConfig } from "#elements/Interface/capabilitiesProvider";
import "#elements/Tabs";
import "#elements/buttons/ActionButton/ak-action-button";
import "#elements/buttons/SpinnerButton/ak-spinner-button";
import "#elements/forms/ModalForm";
import { WithCapabilitiesConfig } from "#elements/mixins/capabilities";
import "#elements/oauth/UserAccessTokenList";
import "#elements/oauth/UserRefreshTokenList";
import "#elements/user/SessionList";

View File

@ -18,7 +18,6 @@ export const CURRENT_CLASS = "pf-m-current";
//#region Application
export const TITLE_DEFAULT = "authentik";
/**
* The delimiter used to parse the URL for the current route.
*

View File

@ -26,8 +26,12 @@ export const HTTPStatusCodeTransformer: Record<number, HTTPErrorJSONTransformer>
[HTTPStatusCode.Forbidden]: GenericErrorFromJSON,
} as const;
//#endregion
//#region Type Predicates
/**
* Type guard to check if a response contains a JSON body.
* Type predicate to check if a response contains a JSON body.
*
* This is useful to guard against parsing errors when attempting to read the response body.
*/
@ -35,6 +39,24 @@ export function isJSONResponse(response: Response): boolean {
return Boolean(response.headers.get("content-type")?.includes("application/json"));
}
/**
* An error originating from an aborted request.
*
* @see {@linkcode isAbortError} to check if an error originates from an aborted request.
*/
export interface AbortErrorLike extends DOMException {
name: "AbortError";
}
/**
* Type predicate to check if an error originates from an aborted request.
*
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort | MDN}
*/
export function isAbortError(error: unknown): error is AbortErrorLike {
return error instanceof DOMException && error.name === "AbortError";
}
//#endregion
//#region API

View File

@ -2,13 +2,12 @@
* @file Theme utilities.
*/
import { type StyleRoot, createStyleSheetUnsafe, setAdoptedStyleSheets } from "#common/stylesheets";
import { UIConfig } from "#common/ui/config";
import AKBase from "#common/styles/authentik.css";
import AKBaseDark from "#common/styles/theme-dark.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
import { Config, CurrentBrand, UiThemeEnum } from "@goauthentik/api";
import { UiThemeEnum } from "@goauthentik/api";
//#region Stylesheet Exports
@ -259,6 +258,8 @@ export function applyUITheme(
export function applyDocumentTheme(hint: CSSColorSchemeValue | UIThemeHint = "auto"): void {
const preferredColorScheme = formatColorScheme(hint);
if (document.documentElement.dataset.theme === preferredColorScheme) return;
const applyStyleSheets: UIThemeListener = (currentUITheme) => {
console.debug(`authentik/theme (document): switching to ${currentUITheme} theme`);
@ -285,36 +286,20 @@ export function applyDocumentTheme(hint: CSSColorSchemeValue | UIThemeHint = "au
applyStyleSheets(preferredColorScheme);
}
/**
* An element that can be themed.
*/
export interface ThemedElement extends HTMLElement {
/**
* The brand information for the current theme.
*/
readonly brand?: CurrentBrand;
/**
* The UI configuration for the current theme,
* typically injected through a Lit Mixin.
*
* @see {@linkcode UIConfig} for details.
*/
readonly uiConfig?: UIConfig;
/**
* An authentik configuration initially provided by the server.
*/
readonly config?: Config;
activeTheme: ResolvedUITheme;
}
/**
* Returns the root interface element of the page.
*
* @todo Can this be handled with a Lit Mixin?
*/
export function rootInterface<T extends ThemedElement = ThemedElement>(): T | null {
export function rootInterface<T extends HTMLElement = HTMLElement>(): T {
const element = document.body.querySelector<T>("[data-ak-interface-root]");
if (!element) {
throw new Error(
`Could not find root interface element. Was this element added before the parent interface element?`,
);
}
return element;
}

View File

@ -1,12 +1,11 @@
import { EVENT_WS_MESSAGE, TITLE_DEFAULT } from "#common/constants";
import { EVENT_WS_MESSAGE } from "#common/constants";
import { globalAK } from "#common/global";
import { UIConfig, UserDisplay, getConfigForUser } from "#common/ui/config";
import { DefaultBrand } from "#common/ui/config";
import { me } from "#common/users";
import "#components/ak-nav-buttons";
import type { PageHeaderInit, SidebarToggleEventDetail } from "#components/ak-page-header";
import { AKElement } from "#elements/Base";
import { WithBrandConfig } from "#elements/Interface/brandProvider";
import { WithBrandConfig } from "#elements/mixins/branding";
import { isAdminRoute } from "#elements/router/utils";
import { themeImage } from "#elements/utils/images";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
@ -290,7 +289,7 @@ export class AKPageNavbar
//#region Private Methods
#setTitle(header?: string) {
let title = this.brand?.brandingTitle || TITLE_DEFAULT;
let title = this.brandingTitle;
if (isAdminRoute()) {
title = `${msg("Admin")} - ${title}`;
@ -368,9 +367,7 @@ export class AKPageNavbar
<a href="#/">
<div class="logo">
<img
src=${themeImage(
this.brand?.brandingLogo ?? DefaultBrand.brandingLogo,
)}
src=${themeImage(this.brandingLogo)}
alt="${msg("authentik Logo")}"
loading="lazy"
/>

View File

@ -3,5 +3,5 @@ import { createContext } from "@lit/context";
import type { WizardStepState } from "./types";
export const wizardStepContext = createContext<WizardStepState>(
Symbol("authentik-wizard-step-labels"),
Symbol.for("authentik-wizard-step-labels"),
);

View File

@ -0,0 +1,12 @@
import { Interface } from "#elements/Interface";
import { LicenseContextController } from "#elements/controllers/EnterpriseContextController";
import { VersionContextController } from "#elements/controllers/VersionContextController";
export class AuthenticatedInterface extends Interface {
constructor() {
super();
this.addController(new LicenseContextController(this));
this.addController(new VersionContextController(this));
}
}

View File

@ -1,17 +0,0 @@
import { createContext } from "@lit/context";
import type { Config, CurrentBrand, LicenseSummary, SessionUser, Version } from "@goauthentik/api";
export const authentikConfigContext = createContext<Config>(Symbol("authentik-config-context"));
export const authentikUserContext = createContext<SessionUser>(Symbol("authentik-user-context"));
export const authentikEnterpriseContext = createContext<LicenseSummary>(
Symbol("authentik-enterprise-context"),
);
export const authentikBrandContext = createContext<CurrentBrand>(Symbol("authentik-brand-context"));
export const authentikVersionContext = createContext<Version>(Symbol("authentik-version-context"));
export default authentikConfigContext;

View File

@ -1,19 +1,14 @@
import { globalAK } from "@goauthentik/common/global.js";
import {
StyleRoot,
createCSSResult,
createStyleSheetUnsafe,
} from "@goauthentik/common/stylesheets.js";
import { globalAK } from "#common/global";
import { StyleRoot, createCSSResult, createStyleSheetUnsafe } from "#common/stylesheets";
import {
$AKBase,
CSSColorSchemeValue,
ResolvedUITheme,
ThemedElement,
applyUITheme,
createUIThemeEffect,
formatColorScheme,
resolveUITheme,
} from "@goauthentik/common/theme.js";
} from "#common/theme";
import { localized } from "@lit/localize";
import { CSSResult, CSSResultGroup, CSSResultOrNative, LitElement } from "lit";
@ -21,11 +16,8 @@ import { property } from "lit/decorators.js";
import { UiThemeEnum } from "@goauthentik/api";
// Re-export the theme helpers
export { rootInterface } from "@goauthentik/common/theme";
@localized()
export class AKElement extends LitElement implements ThemedElement {
export class AKElement extends LitElement {
//#region Static Properties
public static styles?: Array<CSSResult | CSSModule>;

View File

@ -0,0 +1,33 @@
import { globalAK } from "#common/global";
import { applyDocumentTheme } from "#common/theme";
import { AKElement } from "#elements/Base";
import { BrandingContextController } from "#elements/controllers/BrandContextController";
import { ConfigContextController } from "#elements/controllers/ConfigContextController";
import { ModalOrchestrationController } from "#elements/controllers/ModalOrchestrationController";
import { WithAuthentikConfig } from "#elements/mixins/config";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
/**
* The base interface element for the application.
*/
export abstract class Interface extends WithAuthentikConfig(AKElement) {
static styles = [PFBase];
constructor() {
super();
const { config, brand } = globalAK();
applyDocumentTheme(brand.uiTheme);
this.addController(new ConfigContextController(this, config));
this.addController(new BrandingContextController(this, brand));
this.addController(new ModalOrchestrationController());
}
public connectedCallback(): void {
super.connectedCallback();
this.dataset.akInterfaceRoot = this.tagName.toLowerCase();
}
}

View File

@ -1,50 +0,0 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EVENT_REFRESH } from "@goauthentik/common/constants";
import { ThemedElement } from "@goauthentik/common/theme";
import { authentikBrandContext } from "@goauthentik/elements/AuthentikContexts";
import type { ReactiveElementHost } from "@goauthentik/elements/types.js";
import { ContextProvider } from "@lit/context";
import type { ReactiveController } from "lit";
import type { CurrentBrand } from "@goauthentik/api";
import { CoreApi } from "@goauthentik/api";
export class BrandContextController implements ReactiveController {
host!: ReactiveElementHost<ThemedElement>;
context!: ContextProvider<{ __context__: CurrentBrand | undefined }>;
constructor(host: ReactiveElementHost<ThemedElement>) {
this.host = host;
this.context = new ContextProvider(this.host, {
context: authentikBrandContext,
initialValue: undefined,
});
this.fetch = this.fetch.bind(this);
this.fetch();
}
fetch() {
new CoreApi(DEFAULT_CONFIG).coreBrandsCurrentRetrieve().then((brand) => {
this.context.setValue(brand);
this.host.brand = brand;
});
}
hostConnected() {
window.addEventListener(EVENT_REFRESH, this.fetch);
}
hostDisconnected() {
window.removeEventListener(EVENT_REFRESH, this.fetch);
}
hostUpdate() {
// If the Interface changes its brand information for some reason,
// we should notify all users of the context of that change. doesn't
if (this.host.brand !== this.context.value) {
this.context.setValue(this.host.brand);
}
}
}

View File

@ -1,55 +0,0 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EVENT_REFRESH } from "@goauthentik/common/constants";
import { globalAK } from "@goauthentik/common/global";
import { ThemedElement } from "@goauthentik/common/theme";
import { authentikConfigContext } from "@goauthentik/elements/AuthentikContexts";
import type { ReactiveElementHost } from "@goauthentik/elements/types.js";
import { ContextProvider } from "@lit/context";
import type { ReactiveController } from "lit";
import type { Config } from "@goauthentik/api";
import { RootApi } from "@goauthentik/api";
export class ConfigContextController implements ReactiveController {
host!: ReactiveElementHost<ThemedElement>;
context!: ContextProvider<{ __context__: Config | undefined }>;
constructor(host: ReactiveElementHost<ThemedElement>) {
this.host = host;
this.context = new ContextProvider(this.host, {
context: authentikConfigContext,
initialValue: undefined,
});
// Pre-hydrate from template-embedded config
this.context.setValue(globalAK().config);
this.host.config = globalAK().config;
this.fetch = this.fetch.bind(this);
this.fetch();
}
fetch() {
new RootApi(DEFAULT_CONFIG).rootConfigRetrieve().then((config) => {
this.context.setValue(config);
this.host.config = config;
});
}
hostConnected() {
window.addEventListener(EVENT_REFRESH, this.fetch);
}
hostDisconnected() {
window.removeEventListener(EVENT_REFRESH, this.fetch);
}
hostUpdate() {
// If the Interface changes its config information, we should notify all
// users of the context of that change, without creating an infinite
// loop of resets.
if (this.host.config !== this.context.value) {
this.context.setValue(this.host.config);
}
}
}

View File

@ -1,52 +0,0 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EVENT_REFRESH_ENTERPRISE } from "@goauthentik/common/constants";
import { authentikEnterpriseContext } from "@goauthentik/elements/AuthentikContexts";
import type { ReactiveElementHost } from "@goauthentik/elements/types.js";
import { ContextProvider } from "@lit/context";
import type { ReactiveController } from "lit";
import type { LicenseSummary } from "@goauthentik/api";
import { EnterpriseApi } from "@goauthentik/api";
import type { AkAuthenticatedInterface } from "./Interface";
export class EnterpriseContextController implements ReactiveController {
host!: ReactiveElementHost<AkAuthenticatedInterface>;
context!: ContextProvider<{ __context__: LicenseSummary | undefined }>;
constructor(host: ReactiveElementHost<AkAuthenticatedInterface>) {
this.host = host;
this.context = new ContextProvider(this.host, {
context: authentikEnterpriseContext,
initialValue: undefined,
});
this.fetch = this.fetch.bind(this);
this.fetch();
}
fetch() {
new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseSummaryRetrieve().then((enterprise) => {
this.context.setValue(enterprise);
this.host.licenseSummary = enterprise;
});
}
hostConnected() {
window.addEventListener(EVENT_REFRESH_ENTERPRISE, this.fetch);
}
hostDisconnected() {
window.removeEventListener(EVENT_REFRESH_ENTERPRISE, this.fetch);
}
hostUpdate() {
// If the Interface changes its config information, we should notify all
// users of the context of that change, without creating an infinite
// loop of resets.
if (this.host.licenseSummary !== this.context.value) {
this.context.setValue(this.host.licenseSummary);
}
}
}

View File

@ -1,85 +0,0 @@
import { globalAK } from "@goauthentik/common/global.js";
import { ThemedElement, applyDocumentTheme } from "@goauthentik/common/theme.js";
import { UIConfig } from "@goauthentik/common/ui/config.js";
import { AKElement } from "@goauthentik/elements/Base.js";
import { VersionContextController } from "@goauthentik/elements/Interface/VersionContextController.js";
import { ModalOrchestrationController } from "@goauthentik/elements/controllers/ModalOrchestrationController.js";
import { state } from "lit/decorators.js";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
import {
type Config,
type CurrentBrand,
type LicenseSummary,
type Version,
} from "@goauthentik/api";
import { BrandContextController } from "./BrandContextController.js";
import { ConfigContextController } from "./ConfigContextController.js";
import { EnterpriseContextController } from "./EnterpriseContextController.js";
const configContext = Symbol("configContext");
const modalController = Symbol("modalController");
const versionContext = Symbol("versionContext");
export abstract class LightInterface extends AKElement implements ThemedElement {
constructor() {
super();
this.dataset.akInterfaceRoot = this.tagName.toLowerCase();
if (!document.documentElement.dataset.theme) {
applyDocumentTheme(globalAK().brand.uiTheme);
}
}
}
export abstract class Interface extends LightInterface implements ThemedElement {
static styles = [PFBase];
protected [configContext]: ConfigContextController;
protected [modalController]: ModalOrchestrationController;
@state()
public config?: Config;
@state()
public brand?: CurrentBrand;
constructor() {
super();
this.addController(new BrandContextController(this));
this[configContext] = new ConfigContextController(this);
this[modalController] = new ModalOrchestrationController(this);
}
}
export interface AkAuthenticatedInterface extends ThemedElement {
licenseSummary?: LicenseSummary;
version?: Version;
}
const enterpriseContext = Symbol("enterpriseContext");
export class AuthenticatedInterface extends Interface implements AkAuthenticatedInterface {
[enterpriseContext]!: EnterpriseContextController;
[versionContext]!: VersionContextController;
@state()
public uiConfig?: UIConfig;
@state()
public licenseSummary?: LicenseSummary;
@state()
public version?: Version;
constructor() {
super();
this[enterpriseContext] = new EnterpriseContextController(this);
this[versionContext] = new VersionContextController(this);
}
}

View File

@ -1,51 +0,0 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EVENT_REFRESH } from "@goauthentik/common/constants";
import { authentikVersionContext } from "@goauthentik/elements/AuthentikContexts";
import type { ReactiveElementHost } from "@goauthentik/elements/types.js";
import { ContextProvider } from "@lit/context";
import type { ReactiveController } from "lit";
import type { Version } from "@goauthentik/api";
import { AdminApi } from "@goauthentik/api";
import type { AkAuthenticatedInterface } from "./Interface";
export class VersionContextController implements ReactiveController {
host!: ReactiveElementHost<AkAuthenticatedInterface>;
context!: ContextProvider<{ __context__: Version | undefined }>;
constructor(host: ReactiveElementHost<AkAuthenticatedInterface>) {
this.host = host;
this.context = new ContextProvider(this.host, {
context: authentikVersionContext,
initialValue: undefined,
});
this.fetch = this.fetch.bind(this);
this.fetch();
}
fetch() {
new AdminApi(DEFAULT_CONFIG).adminVersionRetrieve().then((version) => {
this.context.setValue(version);
this.host.version = version;
});
}
hostConnected() {
window.addEventListener(EVENT_REFRESH, this.fetch);
}
hostDisconnected() {
window.removeEventListener(EVENT_REFRESH, this.fetch);
}
hostUpdate() {
// If the Interface changes its version information for some reason,
// we should notify all users of the context of that change. doesn't
if (this.host.version !== this.context.value) {
this.context.setValue(this.host.version);
}
}
}

View File

@ -1,48 +0,0 @@
import { authentikBrandContext } from "@goauthentik/elements/AuthentikContexts";
import { createMixin } from "@goauthentik/elements/types";
import { consume } from "@lit/context";
import { state } from "lit/decorators.js";
import type { CurrentBrand } from "@goauthentik/api";
/**
* A mixin that provides the current brand to the element.
*/
export interface StyleBrandMixin {
/**
* The current style branding configuration.
*/
brand: CurrentBrand;
}
/**
* A mixin that provides the current brand to the element.
*
* @category Mixin
*
* @see {@link https://lit.dev/docs/composition/mixins/#mixins-in-typescript | Lit Mixins}
*/
export const WithBrandConfig = createMixin<StyleBrandMixin>(
({
/**
* The superclass constructor to extend.
*/
SuperClass,
/**
* Whether or not to subscribe to the context.
*/
subscribe = true,
}) => {
abstract class StyleBrandProvider extends SuperClass implements StyleBrandMixin {
@consume({
context: authentikBrandContext,
subscribe,
})
@state()
public brand!: CurrentBrand;
}
return StyleBrandProvider;
},
);

View File

@ -1,4 +0,0 @@
import { AuthenticatedInterface, Interface, LightInterface } from "./Interface";
export { Interface, AuthenticatedInterface, LightInterface };
export default Interface;

View File

@ -1,35 +0,0 @@
import { authentikVersionContext } from "@goauthentik/elements/AuthentikContexts";
import { consume } from "@lit/context";
import { Constructor } from "@lit/reactive-element/decorators/base.js";
import type { LitElement } from "lit";
import type { Version } from "@goauthentik/api";
/**
* A consumer that provides version information to the element.
*/
export declare class VersionConsumer {
/**
* The current version of the application.
*/
public readonly version: Version;
}
export function WithVersion<T extends Constructor<LitElement>>(
/**
* The superclass constructor to extend.
*/
SuperClass: T,
/**
* Whether or not to subscribe to the context.
*/
subscribe = true,
) {
class VersionProvider extends SuperClass {
@consume({ context: authentikVersionContext, subscribe })
public version!: Version;
}
return VersionProvider as Constructor<VersionConsumer> & T;
}

View File

@ -5,7 +5,7 @@ import { customEvent } from "@goauthentik/elements/utils/customEvents";
import { html } from "lit";
import { customElement, property } from "lit/decorators.js";
import { WithBrandConfig } from "../Interface/brandProvider";
import { WithBrandConfig } from "../mixins/branding";
import { initializeLocalization } from "./configureLocale";
import type { LocaleGetter, LocaleSetter } from "./configureLocale";
import { DEFAULT_LOCALE, autoDetectLanguage, getBestMatchLocale } from "./helpers";
@ -70,7 +70,7 @@ export class LocaleContext extends WithBrandConfig(AKElement) {
}
updateLocale(requestedLocale: string | undefined = undefined) {
const localeRequest = autoDetectLanguage(requestedLocale, this.brand?.defaultLocale);
const localeRequest = autoDetectLanguage(requestedLocale, this.brand.defaultLocale);
const locale = getBestMatchLocale(localeRequest);
if (!locale) {
console.warn(`authentik/locale: failed to find locale for code ${localeRequest}`);

View File

@ -1,22 +1,5 @@
import { createContext, useContext } from "react";
/**
* A parsed JSON module containing MDX content and metadata from ESBuild.
*/
export interface MDXModule {
/**
* The Markdown content of the module.
*/
content: string;
/**
* The public path of the module, typically identical to the docs page path.
*/
publicPath?: string;
/**
* The public directory of the module, used to resolve relative links.
*/
publicDirectory?: string;
}
import type { MDXModule } from "~docs/types";
/**
* Fetches an MDX module from a URL or ESBuild static asset.

View File

@ -1,15 +1,14 @@
import "@goauthentik/elements/Alert";
import { AKElement } from "@goauthentik/elements/Base";
import {
MDXModule,
MDXModuleContext,
fetchMDXModule,
} from "@goauthentik/elements/ak-mdx/MDXModuleContext";
import { MDXAnchor } from "@goauthentik/elements/ak-mdx/components/MDXAnchor";
import { MDXWrapper } from "@goauthentik/elements/ak-mdx/components/MDXWrapper";
import { remarkAdmonition } from "@goauthentik/elements/ak-mdx/remark/remark-admonition";
import { remarkHeadings } from "@goauthentik/elements/ak-mdx/remark/remark-headings";
import { remarkLists } from "@goauthentik/elements/ak-mdx/remark/remark-lists";
import { globalAK } from "#common/global";
import "#elements/Alert";
import { AKElement } from "#elements/Base";
import { MDXModuleContext, fetchMDXModule } from "#elements/ak-mdx/MDXModuleContext";
import { MDXAnchor } from "#elements/ak-mdx/components/MDXAnchor";
import { MDXWrapper } from "#elements/ak-mdx/components/MDXWrapper";
import { remarkAdmonition } from "#elements/ak-mdx/remark/remark-admonition";
import { remarkHeadings } from "#elements/ak-mdx/remark/remark-headings";
import { remarkLists } from "#elements/ak-mdx/remark/remark-lists";
import { WithAuthentikConfig } from "#elements/mixins/config";
import { DistDirectoryName, StaticDirectoryName } from "#paths";
import { compile as compileMDX, run as runMDX } from "@mdx-js/mdx";
import apacheGrammar from "highlight.js/lib/languages/apache";
import diffGrammar from "highlight.js/lib/languages/diff";
@ -26,11 +25,12 @@ import remarkFrontmatter from "remark-frontmatter";
import remarkGFM from "remark-gfm";
import remarkMdxFrontmatter from "remark-mdx-frontmatter";
import remarkParse from "remark-parse";
import type { MDXModule } from "~docs/types";
import { css } from "lit";
import { customElement, property } from "lit/decorators.js";
import OneDark from "@goauthentik/common/styles/one-dark.css";
import OneDark from "#common/styles/one-dark.css";
import PFContent from "@patternfly/patternfly/components/Content/content.css";
import PFList from "@patternfly/patternfly/components/List/list.css";
import PFTable from "@patternfly/patternfly/components/Table/table.css";
@ -54,7 +54,7 @@ const highlightThemeOptions: HighlightOptions = {
export type Replacer = (input: string) => string;
@customElement("ak-mdx")
export class AKMDX extends AKElement {
export class AKMDX extends WithAuthentikConfig(AKElement) {
@property({
reflect: true,
})
@ -166,9 +166,17 @@ export class AKMDX extends AKElement {
this.#reactRoot = createRoot(this.shadowRoot!);
let nextMDXModule: MDXModule | undefined;
const { relBase } = globalAK().api;
if (this.url) {
nextMDXModule = await fetchMDXModule(this.url);
const pathname =
relBase +
StaticDirectoryName +
"/" +
DistDirectoryName +
this.url.slice(this.url.indexOf("/assets"));
nextMDXModule = await fetchMDXModule(pathname);
} else {
nextMDXModule = {
content: this.content,

View File

@ -1,6 +1,6 @@
import { WithLicenseSummary } from "#elements/mixins/license";
import { globalAK } from "@goauthentik/common/global";
import { AKElement } from "@goauthentik/elements/Base";
import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider";
import { msg } from "@lit/localize";
import { html, nothing } from "lit";

View File

@ -1,5 +1,5 @@
import { WithVersion } from "#elements/mixins/version";
import { AKElement } from "@goauthentik/elements/Base";
import { WithVersion } from "@goauthentik/elements/Interface/versionProvider";
import { msg, str } from "@lit/localize";
import { html, nothing } from "lit";

View File

@ -0,0 +1,74 @@
import { DEFAULT_CONFIG } from "#common/api/config";
import { EVENT_REFRESH } from "#common/constants";
import { isAbortError } from "#common/errors/network";
import { BrandingContext, BrandingMixin } from "#elements/mixins/branding";
import type { ReactiveElementHost } from "#elements/types";
import { Context, ContextProvider } from "@lit/context";
import type { ReactiveController } from "lit";
import { CoreApi, CurrentBrand } from "@goauthentik/api";
export class BrandingContextController implements ReactiveController {
#log = console.debug.bind(console, `authentik/controller/branding`);
#abortController: null | AbortController = null;
#host: ReactiveElementHost<BrandingMixin>;
#context: ContextProvider<Context<unknown, CurrentBrand>>;
constructor(host: ReactiveElementHost<BrandingMixin>, initialValue: CurrentBrand) {
this.#host = host;
this.#context = new ContextProvider(this.#host, {
context: BrandingContext,
initialValue,
});
this.#host.brand = initialValue;
}
#fetch = () => {
this.#log("Fetching configuration...");
this.#abortController?.abort();
this.#abortController = new AbortController();
return new CoreApi(DEFAULT_CONFIG)
.coreBrandsCurrentRetrieve({
signal: this.#abortController.signal,
})
.then((brand) => {
this.#context.setValue(brand);
this.#host.brand = brand;
})
.catch((error: unknown) => {
if (isAbortError(error)) {
this.#log("Aborted fetching brand");
return;
}
throw error;
})
.finally(() => {
this.#abortController = null;
});
};
public hostConnected() {
window.addEventListener(EVENT_REFRESH, this.#fetch);
this.#fetch();
}
public hostDisconnected() {
window.removeEventListener(EVENT_REFRESH, this.#fetch);
this.#abortController?.abort();
}
public hostUpdate() {
// If the Interface changes its brand information for some reason,
// we should notify all users of the context of that change. doesn't
if (this.#host.brand && this.#host.brand !== this.#context.value) {
this.#context.setValue(this.#host.brand);
}
}
}

View File

@ -0,0 +1,79 @@
import { DEFAULT_CONFIG } from "#common/api/config";
import { EVENT_REFRESH } from "#common/constants";
import { isAbortError } from "#common/errors/network";
import { AKConfigMixin, AuthentikConfigContext } from "#elements/mixins/config";
import type { ReactiveElementHost } from "#elements/types";
import { Context, ContextProvider } from "@lit/context";
import type { ReactiveController } from "lit";
import { Config, RootApi } from "@goauthentik/api";
/**
* A controller that provides the application configuration to the element.
*/
export class ConfigContextController implements ReactiveController {
#log = console.debug.bind(console, `authentik/controller/config`);
#abortController: null | AbortController = null;
#host: ReactiveElementHost<AKConfigMixin>;
#context: ContextProvider<Context<unknown, Config>>;
constructor(host: ReactiveElementHost<AKConfigMixin>, initialValue: Config) {
this.#host = host;
this.#context = new ContextProvider(this.#host, {
context: AuthentikConfigContext,
initialValue,
});
this.#host.authentikConfig = initialValue;
}
#fetch = () => {
this.#log("Fetching configuration...");
this.#abortController?.abort();
this.#abortController = new AbortController();
return new RootApi(DEFAULT_CONFIG)
.rootConfigRetrieve({
signal: this.#abortController.signal,
})
.then((authentikConfig) => {
this.#context.setValue(authentikConfig);
this.#host.authentikConfig = authentikConfig;
})
.catch((error: unknown) => {
if (isAbortError(error)) {
this.#log("Aborted fetching configuration");
return;
}
throw error;
})
.finally(() => {
this.#abortController = null;
});
};
public hostConnected() {
window.addEventListener(EVENT_REFRESH, this.#fetch);
this.#fetch();
}
public hostDisconnected() {
window.removeEventListener(EVENT_REFRESH, this.#fetch);
this.#abortController?.abort();
}
public hostUpdate() {
// If the Interface changes its config information, we should notify all
// users of the context of that change, without creating an infinite
// loop of resets.
if (this.#host.authentikConfig && this.#host.authentikConfig !== this.#context.value) {
this.#context.setValue(this.#host.authentikConfig);
}
}
}

View File

@ -0,0 +1,77 @@
import { DEFAULT_CONFIG } from "#common/api/config";
import { EVENT_REFRESH_ENTERPRISE } from "#common/constants";
import { isAbortError } from "#common/errors/network";
import { LicenseContext, LicenseMixin } from "#elements/mixins/license";
import type { ReactiveElementHost } from "#elements/types";
import { Context, ContextProvider } from "@lit/context";
import type { ReactiveController } from "lit";
import { EnterpriseApi, LicenseSummary } from "@goauthentik/api";
export class LicenseContextController implements ReactiveController {
#log = console.debug.bind(console, `authentik/controller/license`);
#abortController: null | AbortController = null;
#host: ReactiveElementHost<LicenseMixin>;
#context: ContextProvider<Context<unknown, LicenseSummary>>;
constructor(host: ReactiveElementHost<LicenseMixin>, initialValue?: LicenseSummary) {
this.#host = host;
this.#context = new ContextProvider(this.#host, {
context: LicenseContext,
initialValue: initialValue,
});
}
#fetch = () => {
this.#log("Fetching license summary...");
this.#abortController?.abort();
this.#abortController = new AbortController();
return new EnterpriseApi(DEFAULT_CONFIG)
.enterpriseLicenseSummaryRetrieve(
{},
{
signal: this.#abortController.signal,
},
)
.then((enterprise) => {
this.#context.setValue(enterprise);
this.#host.licenseSummary = enterprise;
})
.catch((error: unknown) => {
if (isAbortError(error)) {
this.#log("Aborted fetching license summary");
return;
}
throw error;
})
.finally(() => {
this.#abortController = null;
});
};
public hostConnected() {
window.addEventListener(EVENT_REFRESH_ENTERPRISE, this.#fetch);
this.#fetch();
}
public hostDisconnected() {
window.removeEventListener(EVENT_REFRESH_ENTERPRISE, this.#fetch);
this.#abortController?.abort();
}
public hostUpdate() {
// If the Interface changes its config information, we should notify all
// users of the context of that change, without creating an infinite
// loop of resets.
if (this.#host.licenseSummary && this.#host.licenseSummary !== this.#context.value) {
this.#context.setValue(this.#host.licenseSummary);
}
}
}

View File

@ -1,10 +1,8 @@
import { bound } from "@goauthentik/elements/decorators/bound.js";
import { LitElement, ReactiveController } from "lit";
import { LitElement, ReactiveController, ReactiveControllerHost } from "lit";
type ReactiveElementHost = Partial<ReactiveControllerHost> & LitElement;
type ModalElement = LitElement & { closeModal(): void | boolean };
interface ModalElement extends LitElement {
closeModal(): void | boolean;
}
export class ModalShowEvent extends Event {
modal: ModalElement;
@ -50,75 +48,70 @@ const modalIsLive = (modal: ModalElement) => modal.isConnected && modal.checkVis
*/
export class ModalOrchestrationController implements ReactiveController {
host!: ReactiveElementHost;
#knownModals: ModalElement[] = [];
knownModals: ModalElement[] = [];
constructor(host: ReactiveElementHost) {
this.host = host;
host.addController(this);
}
hostConnected() {
public hostConnected() {
window.addEventListener("keyup", this.handleKeyup);
window.addEventListener("ak-modal-show", this.addModal);
window.addEventListener("ak-modal-show", this.#addModal);
window.addEventListener("ak-modal-hide", this.closeModal);
}
hostDisconnected() {
public hostDisconnected() {
window.removeEventListener("keyup", this.handleKeyup);
window.removeEventListener("ak-modal-show", this.addModal);
window.removeEventListener("ak-modal-show", this.#addModal);
window.removeEventListener("ak-modal-hide", this.closeModal);
}
@bound
addModal(e: ModalShowEvent) {
this.knownModals = [...this.knownModals, e.modal];
}
#addModal = (e: ModalShowEvent) => {
this.#knownModals = [...this.#knownModals, e.modal];
};
scheduleCleanup(modal: ModalElement) {
setTimeout(() => {
this.knownModals = this.knownModals.filter((m) => modalIsLive(m) && modal !== m);
}, 0);
}
#cleanupFrameID = -1;
@bound
closeModal(e: ModalHideEvent) {
#scheduleCleanup = (modal: ModalElement) => {
cancelAnimationFrame(this.#cleanupFrameID);
this.#cleanupFrameID = requestAnimationFrame(() => {
this.#knownModals = this.#knownModals.filter((m) => modalIsLive(m) && modal !== m);
});
};
closeModal = (e: ModalHideEvent) => {
const modal = e.modal;
if (!modalIsLive(modal)) {
return;
}
if (modal.closeModal() !== false) {
this.scheduleCleanup(modal);
}
}
removeTopmostModal() {
const knownModals = [...this.knownModals];
if (!modalIsLive(modal)) return;
if (modal.closeModal() !== false) {
this.#scheduleCleanup(modal);
}
};
#removeTopmostModal = () => {
const knownModals = [...this.#knownModals];
// Pop off modals until you find the first live one, schedule it to be closed, and make that
// cleaned list the current state. Since this is our *only* state object, this has the
// effect of creating a new "knownModals" collection with some semantics.
while (true) {
const modal = knownModals.pop();
if (!modal) {
break;
}
if (!modalIsLive(modal)) {
continue;
}
if (!modal) break;
if (!modalIsLive(modal)) continue;
if (modal.closeModal() !== false) {
this.scheduleCleanup(modal);
this.#scheduleCleanup(modal);
}
break;
}
this.knownModals = knownModals;
}
this.#knownModals = knownModals;
};
@bound
handleKeyup(e: KeyboardEvent) {
handleKeyup = ({ key }: KeyboardEvent) => {
// The latter handles Firefox 37 and earlier.
if (e.key === "Escape" || e.key === "Esc") {
this.removeTopmostModal();
if (key === "Escape" || key === "Esc") {
this.#removeTopmostModal();
}
}
};
}

View File

@ -0,0 +1,73 @@
import { DEFAULT_CONFIG } from "#common/api/config";
import { EVENT_REFRESH } from "#common/constants";
import { isAbortError } from "#common/errors/network";
import { VersionContext, VersionMixin } from "#elements/mixins/version";
import type { ReactiveElementHost } from "#elements/types";
import { Context, ContextProvider } from "@lit/context";
import type { ReactiveController } from "lit";
import { AdminApi, Version } from "@goauthentik/api";
export class VersionContextController implements ReactiveController {
#log = console.debug.bind(console, `authentik/controller/version`);
#abortController: null | AbortController = null;
#host: ReactiveElementHost<VersionMixin>;
#context: ContextProvider<Context<unknown, Version>>;
constructor(host: ReactiveElementHost<VersionMixin>, initialValue?: Version) {
this.#host = host;
this.#context = new ContextProvider(this.#host, {
context: VersionContext,
initialValue,
});
}
#fetch = () => {
this.#log("Fetching latest version...");
this.#abortController?.abort();
this.#abortController = new AbortController();
return new AdminApi(DEFAULT_CONFIG)
.adminVersionRetrieve({
signal: this.#abortController.signal,
})
.then((version) => {
this.#context.setValue(version);
this.#host.version = version;
})
.catch((error: unknown) => {
if (isAbortError(error)) {
this.#log("Aborted fetching license summary");
return;
}
throw error;
})
.finally(() => {
this.#abortController = null;
});
};
public hostConnected() {
window.addEventListener(EVENT_REFRESH, this.#fetch);
this.#fetch();
}
public hostDisconnected() {
window.removeEventListener(EVENT_REFRESH, this.#fetch);
this.#abortController?.abort();
}
public hostUpdate() {
// If the Interface changes its version information for some reason,
// we should notify all users of the context of that change. doesn't
if (this.#host.version && this.#host.version !== this.#context.value) {
this.#context.setValue(this.#host.version);
}
}
}

View File

@ -0,0 +1,80 @@
import { DefaultBrand } from "#common/ui/config";
import { createMixin } from "#elements/types";
import { consume, createContext } from "@lit/context";
import type { CurrentBrand, FooterLink } from "@goauthentik/api";
/**
* The Lit context for application branding.
*
* @category Context
* @see {@linkcode BrandingMixin}
* @see {@linkcode WithBrandConfig}
*/
export const BrandingContext = createContext<CurrentBrand>(
Symbol.for("authentik-branding-context"),
);
/**
* A mixin that provides the current brand to the element.
*
* @see {@linkcode WithBrandConfig}
*/
export interface BrandingMixin {
/**
* The current style branding configuration.
*/
readonly brand: Readonly<CurrentBrand>;
readonly brandingTitle: string;
readonly brandingLogo: string;
readonly brandingFavicon: string;
readonly brandingFooterLinks: FooterLink[];
}
/**
* A mixin that provides the current brand to the element.
*
* @category Mixin
*
* @see {@link https://lit.dev/docs/composition/mixins/#mixins-in-typescript | Lit Mixins}
*/
export const WithBrandConfig = createMixin<BrandingMixin>(
({
/**
* The superclass constructor to extend.
*/
SuperClass,
/**
* Whether or not to subscribe to the context.
*/
subscribe = true,
}) => {
abstract class BrandingProvider extends SuperClass implements BrandingMixin {
@consume({
context: BrandingContext,
subscribe,
})
public brand!: CurrentBrand;
public get brandingTitle(): string {
return this.brand.brandingTitle ?? DefaultBrand.brandingTitle;
}
public get brandingLogo(): string {
return this.brand.brandingLogo ?? DefaultBrand.brandingLogo;
}
public get brandingFavicon(): string {
return this.brand.brandingFavicon ?? DefaultBrand.brandingFavicon;
}
public get brandingFooterLinks(): FooterLink[] {
return this.brand.uiFooterLinks ?? DefaultBrand.uiFooterLinks;
}
}
return BrandingProvider;
},
);

View File

@ -1,10 +1,7 @@
import { authentikConfigContext } from "@goauthentik/elements/AuthentikContexts";
import { AKConfigMixin } from "#elements/mixins/config";
import { createMixin } from "@goauthentik/elements/types";
import { consume } from "@lit/context";
import { CapabilitiesEnum } from "@goauthentik/api";
import { Config } from "@goauthentik/api";
/**
* A consumer that provides the capability methods to the element.
@ -22,13 +19,6 @@ export interface CapabilitiesMixin {
): boolean;
}
/**
* Lexically-scoped symbol for the capabilities configuration.
*
* @internal
*/
const kCapabilitiesConfig: unique symbol = Symbol("capabilitiesConfig");
/**
* A mixin that provides the capability methods to the element.
*
@ -37,14 +27,14 @@ const kCapabilitiesConfig: unique symbol = Symbol("capabilitiesConfig");
* After importing, simply mixin this function:
*
* ```ts
* export class AkMyNiftyNewFeature extends withCapabilitiesContext(AKElement) {
* export class AkMyNiftyNewFeature extends WithCapabilitiesConfig(AKElement) {
* }
* ```
*
* And then if you need to check on a capability:
*
* ```ts
* if (this.can(CapabilitiesEnum.IsEnterprise) { ... }
* if (this.can(CapabilitiesEnum.IsEnterprise)) { ... }
* ```
*
*
@ -53,21 +43,15 @@ const kCapabilitiesConfig: unique symbol = Symbol("capabilitiesConfig");
* @category Mixin
*
*/
export const WithCapabilitiesConfig = createMixin<CapabilitiesMixin>(
({ SuperClass, subscribe = true }) => {
export const WithCapabilitiesConfig = createMixin<CapabilitiesMixin, AKConfigMixin>(
({ SuperClass }) => {
abstract class CapabilitiesProvider extends SuperClass implements CapabilitiesMixin {
@consume({
context: authentikConfigContext,
subscribe,
})
private readonly [kCapabilitiesConfig]: Config | undefined;
public can(capability: CapabilitiesEnum) {
const config = this[kCapabilitiesConfig];
const config = this.authentikConfig;
if (!config) {
throw new Error(
"ConfigContext: Attempted to access site configuration before initialization.",
`ConfigContext: Attempted to check capability "${capability}" before initialization. Does the element have the AuthentikConfigMixin applied?`,
);
}

View File

@ -1,12 +1,23 @@
import { authentikConfigContext } from "@goauthentik/elements/AuthentikContexts";
import { createMixin } from "@goauthentik/elements/types";
import { consume } from "@lit/context";
import { consume, createContext } from "@lit/context";
import type { Config } from "@goauthentik/api";
/**
* The Lit context for the application configuration.
*
* @category Context
* @see {@linkcode AKConfigMixin}
* @see {@linkcode WithAuthentikConfig}
*/
export const AuthentikConfigContext = createContext<Config>(Symbol.for("authentik-config-context"));
/**
* A consumer that provides the application configuration to the element.
*
* @category Mixin
* @see {@linkcode WithAuthentikConfig}
*/
export interface AKConfigMixin {
/**
@ -33,7 +44,7 @@ export const WithAuthentikConfig = createMixin<AKConfigMixin>(
}) => {
abstract class AKConfigProvider extends SuperClass implements AKConfigMixin {
@consume({
context: authentikConfigContext,
context: AuthentikConfigContext,
subscribe,
})
public readonly authentikConfig!: Readonly<Config>;

View File

@ -1,10 +1,13 @@
import { authentikEnterpriseContext } from "@goauthentik/elements/AuthentikContexts";
import { createMixin } from "@goauthentik/elements/types";
import { createMixin } from "#elements/types";
import { consume } from "@lit/context";
import { consume, createContext } from "@lit/context";
import { type LicenseSummary, LicenseSummaryStatusEnum } from "@goauthentik/api";
export const LicenseContext = createContext<LicenseSummary>(
Symbol.for("authentik-license-context"),
);
/**
* A consumer that provides license information to the element.
*/
@ -26,7 +29,7 @@ export interface LicenseMixin {
export const WithLicenseSummary = createMixin<LicenseMixin>(({ SuperClass, subscribe = true }) => {
abstract class LicenseProvider extends SuperClass implements LicenseMixin {
@consume({
context: authentikEnterpriseContext,
context: LicenseContext,
subscribe,
})
public readonly licenseSummary!: LicenseSummary;

View File

@ -0,0 +1,61 @@
import { createMixin } from "#elements/types";
import { consume, createContext } from "@lit/context";
import { state } from "lit/decorators.js";
import type { Version } from "@goauthentik/api";
/**
* The Lit context for application branding.
*
* @category Context
* @see {@linkcode VersionMixin}
* @see {@linkcode WithVersion}
*/
export const VersionContext = createContext<Version>(Symbol.for("authentik-version-context"));
/**
* A mixin that provides the current version to the element.
*
* @see {@linkcode WithVersion}
*/
export interface VersionMixin {
/**
* The current version of the application.
*
* @format semver
*/
readonly version: Version;
}
/**
* A mixin that provides the current authentik version to the element.
*
* @category Mixin
*
* @see {@link https://lit.dev/docs/composition/mixins/#mixins-in-typescript | Lit Mixins}
*/
export const WithVersion = createMixin<VersionMixin>(
({
/**
* The superclass constructor to extend.
*/
SuperClass,
/**
* Whether or not to subscribe to the context.
*/
subscribe = true,
}) => {
abstract class VersionProvider extends SuperClass implements VersionMixin {
@consume({
context: VersionContext,
subscribe,
})
@state()
public version!: Version;
}
return VersionProvider;
},
);

View File

@ -1,9 +1,10 @@
import type { AdminInterface } from "@goauthentik/admin/AdminInterface/index.entrypoint.js";
import { WithLicenseSummary } from "#elements/mixins/license";
import { WithVersion } from "#elements/mixins/version";
import type { AdminInterface } from "@goauthentik/admin/AdminInterface/index.entrypoint";
import { globalAK } from "@goauthentik/common/global";
import { rootInterface } from "@goauthentik/common/theme";
import { DefaultBrand } from "@goauthentik/common/ui/config";
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider";
import { WithVersion } from "@goauthentik/elements/Interface/versionProvider";
import { AKElement } from "@goauthentik/elements/Base";
import { msg, str } from "@lit/localize";
import { CSSResult, css, html, nothing } from "lit";

View File

@ -15,10 +15,13 @@ export type Writeable<T> = { -readonly [P in keyof T]: T[P] };
*/
export type ReactiveElementHost<T> = Partial<ReactiveControllerHost & Writeable<T>> & HTMLElement;
export type AbstractLitElementConstructor = abstract new (...args: never[]) => LitElement;
export type AbstractLitElementConstructor<T = unknown> = abstract new (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
...args: any[]
) => LitElement & T;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type LitElementConstructor = new (...args: any[]) => LitElement;
export type LitElementConstructor<T = unknown> = new (...args: any[]) => LitElement & T;
/**
* A constructor that has been extended with a mixin.
@ -37,11 +40,11 @@ export type ConstructorWithMixin<SuperClass, Mixin> =
/**
* The init object passed to the `createMixin` callback.
*/
export interface CreateMixinInit<T extends LitElementConstructor = LitElementConstructor> {
export interface CreateMixinInit<C = unknown> {
/**
* The superclass constructor to extend.
*/
SuperClass: T;
SuperClass: LitElementConstructor<C>;
/**
* Whether or not to subscribe to the context.
*
@ -58,7 +61,9 @@ export interface CreateMixinInit<T extends LitElementConstructor = LitElementCon
* @param mixinCallback The callback that will be called to create the mixin.
* @template Mixin The mixin class to union with the superclass.
*/
export function createMixin<Mixin>(mixinCallback: (init: CreateMixinInit) => unknown) {
export function createMixin<Mixin, C = unknown>(
mixinCallback: (init: CreateMixinInit<C>) => unknown,
) {
return <T extends LitElementConstructor | AbstractLitElementConstructor>(
/**
* The superclass constructor to extend.
@ -73,7 +78,7 @@ export function createMixin<Mixin>(mixinCallback: (init: CreateMixinInit) => unk
subscribe?: boolean,
) => {
const MixinClass = mixinCallback({
SuperClass: SuperClass as LitElementConstructor,
SuperClass: SuperClass as LitElementConstructor<C>,
subscribe,
});

View File

@ -1,8 +1,8 @@
import { resolveUITheme } from "@goauthentik/common/theme";
import { rootInterface } from "@goauthentik/elements/Base";
import { resolveUITheme, rootInterface } from "#common/theme";
import type { AKElement } from "#elements/Base";
export function themeImage(rawPath: string) {
const enabledTheme = rootInterface()?.activeTheme || resolveUITheme();
const enabledTheme = rootInterface<AKElement>()?.activeTheme || resolveUITheme();
return rawPath.replaceAll("%(theme)s", enabledTheme);
}

View File

@ -1,5 +1,5 @@
import { WithLicenseSummary } from "#elements/mixins/license";
import "@goauthentik/admin/common/ak-license-notice";
import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider";
import { WizardPage } from "@goauthentik/elements/wizard/WizardPage";
import { msg, str } from "@lit/localize";

View File

@ -1,24 +1,21 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import {
EVENT_FLOW_ADVANCE,
EVENT_FLOW_INSPECTOR_TOGGLE,
TITLE_DEFAULT,
} from "@goauthentik/common/constants";
import { globalAK } from "@goauthentik/common/global";
import { configureSentry } from "@goauthentik/common/sentry";
import { DefaultBrand } from "@goauthentik/common/ui/config";
import { WebsocketClient } from "@goauthentik/common/ws";
import { Interface } from "@goauthentik/elements/Interface";
import "@goauthentik/elements/LoadingOverlay";
import "@goauthentik/elements/ak-locale-context";
import { themeImage } from "@goauthentik/elements/utils/images";
import "@goauthentik/flow/components/ak-brand-footer";
import "@goauthentik/flow/sources/apple/AppleLoginInit";
import "@goauthentik/flow/sources/plex/PlexLoginInit";
import "@goauthentik/flow/stages/FlowErrorStage";
import "@goauthentik/flow/stages/FlowFrameStage";
import "@goauthentik/flow/stages/RedirectStage";
import { StageHost, SubmitOptions } from "@goauthentik/flow/stages/base";
import { DEFAULT_CONFIG } from "#common/api/config";
import { EVENT_FLOW_ADVANCE, EVENT_FLOW_INSPECTOR_TOGGLE } from "#common/constants";
import { globalAK } from "#common/global";
import { configureSentry } from "#common/sentry/index";
import { WebsocketClient } from "#common/ws";
import { Interface } from "#elements/Interface";
import "#elements/LoadingOverlay";
import "#elements/ak-locale-context/ak-locale-context";
import { WithBrandConfig } from "#elements/mixins/branding";
import { WithCapabilitiesConfig } from "#elements/mixins/capabilities";
import { themeImage } from "#elements/utils/images";
import "#flow/components/ak-brand-footer";
import "#flow/sources/apple/AppleLoginInit";
import "#flow/sources/plex/PlexLoginInit";
import "#flow/stages/FlowErrorStage";
import "#flow/stages/FlowFrameStage";
import "#flow/stages/RedirectStage";
import { StageHost, SubmitOptions } from "#flow/stages/base";
import { msg } from "@lit/localize";
import { CSSResult, PropertyValues, TemplateResult, css, html, nothing } from "lit";
@ -48,7 +45,10 @@ import {
} from "@goauthentik/api";
@customElement("ak-flow-executor")
export class FlowExecutor extends Interface implements StageHost {
export class FlowExecutor
extends WithCapabilitiesConfig(WithBrandConfig(Interface))
implements StageHost
{
@property()
flowSlug: string = window.location.pathname.split("/")[3];
@ -58,9 +58,9 @@ export class FlowExecutor extends Interface implements StageHost {
set challenge(value: ChallengeTypes | undefined) {
this._challenge = value;
if (value?.flowInfo?.title) {
document.title = `${value.flowInfo?.title} - ${this.brand?.brandingTitle}`;
document.title = `${value.flowInfo?.title} - ${this.brandingTitle}`;
} else {
document.title = this.brand?.brandingTitle || TITLE_DEFAULT;
document.title = this.brandingTitle;
}
this.requestUpdate();
}
@ -238,7 +238,7 @@ export class FlowExecutor extends Interface implements StageHost {
}
async firstUpdated(): Promise<void> {
if (this.config?.capabilities.includes(CapabilitiesEnum.CanDebug)) {
if (this.can(CapabilitiesEnum.CanDebug)) {
this.inspectorAvailable = true;
}
this.loading = true;
@ -519,11 +519,7 @@ export class FlowExecutor extends Interface implements StageHost {
class="pf-c-login__main-header pf-c-brand ak-brand"
>
<img
src="${themeImage(
this.brand?.brandingLogo ??
globalAK()?.brand.brandingLogo ??
DefaultBrand.brandingLogo,
)}"
src="${themeImage(this.brand.brandingLogo)}"
alt="${msg("authentik Logo")}"
/>
</div>
@ -531,7 +527,7 @@ export class FlowExecutor extends Interface implements StageHost {
</div>
<ak-brand-links
class="pf-c-login__footer"
.links=${this.brand?.uiFooterLinks ?? []}
.links=${this.brandingFooterLinks}
></ak-brand-links>
</div>
</div>

View File

@ -1,9 +1,6 @@
import { CapabilitiesEnum, WithCapabilitiesConfig } from "#elements/mixins/capabilities";
import "@goauthentik/elements/Divider";
import "@goauthentik/elements/EmptyState";
import {
CapabilitiesEnum,
WithCapabilitiesConfig,
} from "@goauthentik/elements/Interface/capabilitiesProvider";
import { LOCALES } from "@goauthentik/elements/ak-locale-context/definitions";
import "@goauthentik/elements/forms/FormElement";
import { BaseStage } from "@goauthentik/flow/stages/base";

View File

@ -1,13 +1,13 @@
import { TITLE_DEFAULT } from "@goauthentik/common/constants";
import { Interface } from "@goauthentik/elements/Interface";
import "@goauthentik/elements/LoadingOverlay";
import { Interface } from "#elements/Interface";
import "#elements/LoadingOverlay";
import { WithBrandConfig } from "#elements/mixins/branding";
import Guacamole from "guacamole-common-js";
import { msg, str } from "@lit/localize";
import { CSSResult, TemplateResult, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import AKGlobal from "@goauthentik/common/styles/authentik.css";
import AKGlobal from "#common/styles/authentik.css";
import PFContent from "@patternfly/patternfly/components/Content/content.css";
import PFPage from "@patternfly/patternfly/components/Page/page.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
@ -43,7 +43,7 @@ const RECONNECT_ATTEMPTS_INITIAL = 5;
const RECONNECT_ATTEMPTS = 5;
@customElement("ak-rac")
export class RacInterface extends Interface {
export class RacInterface extends WithBrandConfig(Interface) {
static get styles(): CSSResult[] {
return [
PFBase,
@ -231,10 +231,12 @@ export class RacInterface extends Interface {
}
updateTitle(): void {
let title = this.brand?.brandingTitle || TITLE_DEFAULT;
let title = this.brandingTitle;
if (this.endpointName) {
title = `${this.endpointName} - ${title}`;
}
document.title = `${title}`;
}

View File

@ -1,13 +1,13 @@
// sort-imports-ignore
import "rapidoc";
import "@goauthentik/elements/ak-locale-context/index.js";
import "#elements/ak-locale-context/index";
import { CSRFHeaderName } from "@goauthentik/common/api/middleware.js";
import { EVENT_THEME_CHANGE } from "@goauthentik/common/constants.js";
import { getCookie } from "@goauthentik/common/utils.js";
import { Interface } from "@goauthentik/elements/Interface/Interface.js";
import { DefaultBrand } from "@goauthentik/common/ui/config.js";
import { themeImage } from "@goauthentik/elements/utils/images.js";
import { CSRFHeaderName } from "#common/api/middleware";
import { EVENT_THEME_CHANGE } from "#common/constants";
import { getCookie } from "#common/utils";
import { Interface } from "#elements/Interface";
import { WithBrandConfig } from "#elements/mixins/branding";
import { themeImage } from "#elements/utils/images";
import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, css, html } from "lit";
@ -17,7 +17,7 @@ import { ifDefined } from "lit/directives/if-defined.js";
import { UiThemeEnum } from "@goauthentik/api";
@customElement("ak-api-browser")
export class APIBrowser extends Interface {
export class APIBrowser extends WithBrandConfig(Interface) {
@property()
schemaPath?: string;
@ -102,9 +102,7 @@ export class APIBrowser extends Interface {
<img
alt="${msg("authentik Logo")}"
class="logo"
src="${themeImage(
this.brand?.brandingLogo ?? DefaultBrand.brandingLogo,
)}"
src="${themeImage(this.brandingLogo)}"
/>
</div>
</rapi-doc>

View File

@ -1,4 +1,6 @@
import { LightInterface } from "@goauthentik/elements/Interface";
import { globalAK } from "#common/global";
import { applyDocumentTheme } from "#common/theme";
import { AKElement } from "#elements/Base";
import { msg } from "@lit/localize";
import { TemplateResult, css, html } from "lit";
@ -10,7 +12,7 @@ import PFSpinner from "@patternfly/patternfly/components/Spinner/spinner.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
@customElement("ak-loading")
export class Loading extends LightInterface {
export class Loading extends AKElement {
static styles = [
PFBase,
PFPage,
@ -23,6 +25,16 @@ export class Loading extends LightInterface {
`,
];
constructor() {
super();
applyDocumentTheme(globalAK().brand.uiTheme);
}
public connectedCallback(): void {
this.dataset.akInterfaceRoot = this.tagName.toLowerCase();
}
render(): TemplateResult {
return html` <section
class="ak-static-page pf-c-page__main-section pf-m-no-padding-mobile pf-m-xl"

View File

@ -1,4 +1,4 @@
import { Interface } from "@goauthentik/elements/Interface";
import { Interface } from "#elements/Interface";
import { customElement } from "lit/decorators.js";

View File

@ -1,8 +1,9 @@
import { PFSize } from "@goauthentik/common/enums.js";
import { globalAK } from "@goauthentik/common/global";
import { rootInterface } from "@goauthentik/common/theme";
import { truncateWords } from "@goauthentik/common/utils";
import "@goauthentik/elements/AppIcon";
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/Expand";
import "@goauthentik/user/LibraryApplication/RACLaunchEndpointModal";
import type { RACLaunchEndpointModal } from "@goauthentik/user/LibraryApplication/RACLaunchEndpointModal";
@ -71,14 +72,14 @@ export class LibraryApplication extends AKElement {
}
renderExpansion(application: Application) {
const me = rootInterface<UserInterface>()?.me;
const { me, uiConfig } = rootInterface<UserInterface>();
return html`<ak-expand textOpen=${msg("Fewer details")} textClosed=${msg("More details")}>
<div class="pf-c-content">
<small>${application.metaPublisher}</small>
</div>
${truncateWords(application.metaDescription || "", 10)}
${rootInterface()?.uiConfig?.enabledFeatures.applicationEdit && me?.user.isSuperuser
${uiConfig?.enabledFeatures.applicationEdit && me?.user.isSuperuser
? html`
<a
class="pf-c-button pf-m-control pf-m-small pf-m-block"
@ -148,9 +149,10 @@ export class LibraryApplication extends AKElement {
return html`<ak-spinner></ak-spinner>`;
}
const me = rootInterface<UserInterface>()?.me;
const { me, uiConfig } = rootInterface<UserInterface>();
const expandable =
(rootInterface()?.uiConfig?.enabledFeatures.applicationEdit && me?.user.isSuperuser) ||
(uiConfig?.enabledFeatures.applicationEdit && me?.user.isSuperuser) ||
this.application.metaPublisher !== "" ||
this.application.metaDescription !== "";

View File

@ -1,7 +1,9 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { rootInterface } from "@goauthentik/common/theme";
import { me } from "@goauthentik/common/users";
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/EmptyState";
import type { UserInterface } from "@goauthentik/user/index.entrypoint";
import { localized, msg } from "@lit/localize";
import { html } from "lit";
@ -47,7 +49,8 @@ export class LibraryPage extends AKElement {
constructor() {
super();
const uiConfig = rootInterface()?.uiConfig;
const { uiConfig } = rootInterface<UserInterface>();
if (!uiConfig) {
throw new Error("Could not retrieve uiConfig. Reason: unknown. Check logs.");
}

View File

@ -1,30 +1,31 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { DEFAULT_CONFIG } from "#common/api/config";
import {
EVENT_API_DRAWER_TOGGLE,
EVENT_NOTIFICATION_DRAWER_TOGGLE,
EVENT_WS_MESSAGE,
} from "@goauthentik/common/constants";
import { globalAK } from "@goauthentik/common/global";
import { configureSentry } from "@goauthentik/common/sentry";
import { UIConfig, getConfigForUser } from "@goauthentik/common/ui/config";
import { DefaultBrand } from "@goauthentik/common/ui/config";
import { me } from "@goauthentik/common/users";
import { WebsocketClient } from "@goauthentik/common/ws";
import "@goauthentik/components/ak-nav-buttons";
import { AKElement } from "@goauthentik/elements/Base";
import { AuthenticatedInterface } from "@goauthentik/elements/Interface";
import "@goauthentik/elements/ak-locale-context";
import "@goauthentik/elements/banner/EnterpriseStatusBanner";
import "@goauthentik/elements/buttons/ActionButton";
import "@goauthentik/elements/messages/MessageContainer";
import "@goauthentik/elements/notifications/APIDrawer";
import "@goauthentik/elements/notifications/NotificationDrawer";
import { getURLParam, updateURLParams } from "@goauthentik/elements/router/RouteMatch";
import "@goauthentik/elements/router/RouterOutlet";
import "@goauthentik/elements/sidebar/Sidebar";
import "@goauthentik/elements/sidebar/SidebarItem";
import { themeImage } from "@goauthentik/elements/utils/images";
import { ROUTES } from "@goauthentik/user/Routes";
} from "#common/constants";
import { globalAK } from "#common/global";
import { configureSentry } from "#common/sentry/index";
import { UIConfig, getConfigForUser } from "#common/ui/config";
import { DefaultBrand } from "#common/ui/config";
import { me } from "#common/users";
import { WebsocketClient } from "#common/ws";
import "#components/ak-nav-buttons";
import { AuthenticatedInterface } from "#elements/AuthenticatedInterface";
import { AKElement } from "#elements/Base";
import "#elements/ak-locale-context/ak-locale-context";
import "#elements/banner/EnterpriseStatusBanner";
import "#elements/buttons/ActionButton/ak-action-button";
import "#elements/messages/MessageContainer";
import { WithBrandConfig } from "#elements/mixins/branding";
import "#elements/notifications/APIDrawer";
import "#elements/notifications/NotificationDrawer";
import { getURLParam, updateURLParams } from "#elements/router/RouteMatch";
import "#elements/router/RouterOutlet";
import "#elements/sidebar/Sidebar";
import "#elements/sidebar/SidebarItem";
import { themeImage } from "#elements/utils/images";
import { ROUTES } from "#user/Routes";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg } from "@lit/localize";
@ -41,7 +42,7 @@ import PFPage from "@patternfly/patternfly/components/Page/page.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css";
import { CurrentBrand, EventsApi, SessionUser } from "@goauthentik/api";
import { EventsApi, SessionUser } from "@goauthentik/api";
if (process.env.NODE_ENV === "development") {
await import("@goauthentik/esbuild-plugin-live-reload/client");
@ -117,21 +118,19 @@ const customStyles = css`
@customElement("ak-interface-user-presentation")
// @ts-ignore
class UserInterfacePresentation extends AKElement {
static get styles() {
return [
PFBase,
PFDisplay,
PFBrand,
PFPage,
PFAvatar,
PFButton,
PFDrawer,
PFDropdown,
PFNotificationBadge,
customStyles,
];
}
class UserInterfacePresentation extends WithBrandConfig(AKElement) {
static styles = [
PFBase,
PFDisplay,
PFBrand,
PFPage,
PFAvatar,
PFButton,
PFDrawer,
PFDropdown,
PFNotificationBadge,
customStyles,
];
@property({ type: Object })
uiConfig!: UIConfig;
@ -148,9 +147,6 @@ class UserInterfacePresentation extends AKElement {
@property({ type: Number })
notificationsCount = 0;
@property({ type: Object })
brand!: CurrentBrand;
get canAccessAdmin() {
return (
this.me.user.isSuperuser ||
@ -206,8 +202,8 @@ class UserInterfacePresentation extends AKElement {
<a href="#/" class="pf-c-page__header-brand-link">
<img
class="pf-c-brand"
src="${themeImage(this.brand.brandingLogo)}"
alt="${this.brand.brandingTitle}"
src="${themeImage(this.brandingLogo)}"
alt="${this.brandingTitle}"
/>
</a>
</div>
@ -265,7 +261,7 @@ class UserInterfacePresentation extends AKElement {
//
//
@customElement("ak-interface-user")
export class UserInterface extends AuthenticatedInterface {
export class UserInterface extends WithBrandConfig(AuthenticatedInterface) {
@property({ type: Boolean })
notificationDrawerOpen = getURLParam("notificationDrawerOpen", false);
@ -278,7 +274,10 @@ export class UserInterface extends AuthenticatedInterface {
notificationsCount = 0;
@state()
me?: SessionUser;
me: SessionUser | null = null;
@state()
uiConfig: UIConfig | null = null;
constructor() {
configureSentry(true);

View File

@ -1,6 +1,7 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EVENT_REFRESH } from "@goauthentik/common/constants";
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
import { rootInterface } from "@goauthentik/common/theme";
import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/Tabs";
import "@goauthentik/elements/user/SessionList";
import "@goauthentik/elements/user/UserConsentList";

View File

@ -1,18 +1,14 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EVENT_REFRESH } from "@goauthentik/common/constants";
import {
APIError,
parseAPIResponseError,
pluckErrorDetail,
} from "@goauthentik/common/errors/network";
import { globalAK } from "@goauthentik/common/global";
import { MessageLevel } from "@goauthentik/common/messages";
import { refreshMe } from "@goauthentik/common/users";
import { AKElement } from "@goauthentik/elements/Base";
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
import { showMessage } from "@goauthentik/elements/messages/MessageContainer";
import { StageHost } from "@goauthentik/flow/stages/base";
import "@goauthentik/user/user-settings/details/stages/prompt/PromptStage";
import { DEFAULT_CONFIG } from "#common/api/config";
import { EVENT_REFRESH } from "#common/constants";
import { APIError, parseAPIResponseError, pluckErrorDetail } from "#common/errors/network";
import { globalAK } from "#common/global";
import { MessageLevel } from "#common/messages";
import { refreshMe } from "#common/users";
import { AKElement } from "#elements/Base";
import { showMessage } from "#elements/messages/MessageContainer";
import { WithBrandConfig } from "#elements/mixins/branding";
import { StageHost } from "#flow/stages/base";
import "#user/user-settings/details/stages/prompt/PromptStage";
import { msg } from "@lit/localize";
import { CSSResult, PropertyValues, TemplateResult, html } from "lit";
@ -92,10 +88,10 @@ export class UserSettingsFlowExecutor
updated(changedProperties: PropertyValues<this>): void {
if (changedProperties.has("brand") && this.brand) {
this.flowSlug = this.brand?.flowUserSettings;
if (!this.flowSlug) {
return;
}
this.flowSlug = this.brand.flowUserSettings;
if (!this.flowSlug) return;
this.nextChallenge();
}
}

View File

@ -21,7 +21,6 @@
"@goauthentik/admin/*": ["./src/admin/*"],
"@goauthentik/common/*": ["./src/common/*"],
"@goauthentik/components/*": ["./src/components/*"],
"@goauthentik/docs/*": ["../website/docs/*"],
"@goauthentik/elements/*": ["./src/elements/*"],
"@goauthentik/flow/*": ["./src/flow/*"],
"@goauthentik/locales/*": ["./src/locales/*"],

View File

@ -24,7 +24,6 @@
"@goauthentik/admin/*": ["./src/admin/*"],
"@goauthentik/common/*": ["./src/common/*"],
"@goauthentik/components/*": ["./src/components/*"],
"@goauthentik/docs/*": ["../website/docs/*"],
"@goauthentik/elements/*": ["./src/elements/*"],
"@goauthentik/flow/*": ["./src/flow/*"],
"@goauthentik/locales/*": ["./src/locales/*"],

28
web/types/mdx.d.ts vendored
View File

@ -1,4 +1,28 @@
declare module "*.md" {
/**
* @file Provides types for ESBuild "virtual modules" generated from MDX files.
*/
declare module "~docs/types" {
/**
* A parsed JSON module containing MDX content and metadata from ESBuild.
*/
export interface MDXModule {
/**
* The Markdown content of the module.
*/
content: string;
/**
* The public path of the module, typically identical to the docs page path.
*/
publicPath?: string;
/**
* The public directory of the module, used to resolve relative links.
*/
publicDirectory?: string;
}
}
declare module "~docs/*.md" {
/**
* The serialized JSON content of an MD file.
*/
@ -6,7 +30,7 @@ declare module "*.md" {
export default serializedJSON;
}
declare module "*.mdx" {
declare module "~docs/*.mdx" {
/**
* The serialized JSON content of an MDX file.
*/

View File

@ -30,7 +30,7 @@ In this section, inform the reader of anything they need to do, or have configur
## Overview of steps/workflow (optional section)
If the task is quite long or complex, it might be good to add a bullet list of the main steps, or even a diagram of the workflow, just so that the reader can first familairize themselves with the 50,000 meter view before they dive into the detailed steps.
If the task is quite long or complex, it might be good to add a bullet list of the main steps, or even a diagram of the workflow, just so that the reader can first familiarize themselves with the 50,000 meter view before they dive into the detailed steps.
## First several group steps

View File

@ -22,7 +22,7 @@ In this section, inform the reader of anything they need to do, or have configur
## Overview of steps/workflow (optional section)
If the task is quite long or complex, it might be good to add a bullet list of the main steps, or even a diagram of the workflow, just so that the reader can first familairize themselves with the 50,000 meter view before they dive into the detailed steps.
If the task is quite long or complex, it might be good to add a bullet list of the main steps, or even a diagram of the workflow, just so that the reader can first familiarize themselves with the 50,000 meter view before they dive into the detailed steps.
## First several group steps

View File

@ -114,7 +114,7 @@ When you are creating an enhancement suggestion, please fill in [the template](h
#### Local development
authentik can be run locally, all though depending on which part you want to work on, different pre-requisites are required.
authentik can be run locally, although depending on which part you want to work on, different pre-requisites are required.
This is documented in the [developer docs](./setup/frontend-dev-environment.md).

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