Compare commits

..

2 Commits

Author SHA1 Message Date
29a3117a94 web: Clean up Sentry usage. Reduce API calls.
web: Format.

web: Disable sentry via server context.
2025-05-03 02:26:43 +02:00
66e40720c5 web: Clean up usage of server injected values. 2025-05-03 02:25:55 +02:00
173 changed files with 3799 additions and 9266 deletions

View File

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

View File

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

View File

@ -42,4 +42,4 @@ See [SECURITY.md](SECURITY.md)
## Adoption and Contributions
Your organization uses authentik? We'd love to add your logo to the readme and our website! Email us @ hello@goauthentik.io or open a GitHub Issue/PR! For more information on how to contribute to authentik, please refer to our [contribution guide](https://docs.goauthentik.io/docs/developer-docs?utm_source=github).
Your organization uses authentik? We'd love to add your logo to the readme and our website! Email us @ hello@goauthentik.io or open a GitHub Issue/PR! For more information on how to contribute to authentik, please refer to our [CONTRIBUTING.md file](./CONTRIBUTING.md).

View File

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

View File

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

View File

@ -1,5 +1,14 @@
{% load i18n %}
{% get_current_language as LANGUAGE_CODE %}
{{ config_json|json_script:":ak-config:" }}
{{ brand_json|json_script:":ak-brand:" }}
<meta name="ak-version-family" content="{{ version_family }}">
<meta name="ak-version-subdomain" content="{{ version_subdomain }}">
<meta name="ak-build" content="{{ build }}">
<meta name="ak-base-url" content="{{ base_url }}">
<meta name="ak-base-url-rel" content="{{ base_url_rel }}">
<script>
window.authentik = {

View File

@ -1,6 +1,5 @@
"""Interface views"""
from json import dumps
from typing import Any
from django.http import HttpRequest
@ -46,14 +45,19 @@ class InterfaceView(TemplateView):
"""Base interface view"""
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
kwargs["config_json"] = dumps(ConfigView(request=Request(self.request)).get_config().data)
kwargs["brand_json"] = dumps(CurrentBrandSerializer(self.request.brand).data)
"""Add common context data to all interface views"""
kwargs["config_json"] = ConfigView(request=Request(self.request)).get_config().data
kwargs["brand_json"] = CurrentBrandSerializer(self.request.brand).data
kwargs["version_family"] = f"{LOCAL_VERSION.major}.{LOCAL_VERSION.minor}"
kwargs["version_subdomain"] = f"version-{LOCAL_VERSION.major}-{LOCAL_VERSION.minor}"
kwargs["build"] = get_build_hash()
kwargs["url_kwargs"] = self.kwargs
kwargs["base_url"] = self.request.build_absolute_uri(CONFIG.get("web.path", "/"))
kwargs["base_url_rel"] = CONFIG.get("web.path", "/")
return super().get_context_data(**kwargs)

View File

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

View File

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

View File

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

8
go.mod
View File

@ -19,7 +19,7 @@ require (
github.com/jellydator/ttlcache/v3 v3.3.0
github.com/mitchellh/mapstructure v1.5.0
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484
github.com/pires/go-proxyproto v0.8.1
github.com/pires/go-proxyproto v0.8.0
github.com/prometheus/client_golang v1.22.0
github.com/redis/go-redis/v9 v9.8.0
github.com/sethvargo/go-envconfig v1.3.0
@ -29,8 +29,8 @@ require (
github.com/wwt/guac v1.3.2
goauthentik.io/api/v3 v3.2025040.1
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
golang.org/x/oauth2 v0.30.0
golang.org/x/sync v0.14.0
golang.org/x/oauth2 v0.29.0
golang.org/x/sync v0.13.0
gopkg.in/yaml.v2 v2.4.0
layeh.com/radius v0.0.0-20210819152912-ad72663a72ab
)
@ -75,7 +75,7 @@ require (
go.opentelemetry.io/otel/trace v1.24.0 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/text v0.24.0 // indirect
golang.org/x/text v0.23.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

20
go.sum
View File

@ -230,8 +230,8 @@ github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0=
github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
github.com/pires/go-proxyproto v0.8.0 h1:5unRmEAPbHXHuLjDg01CxJWf91cw3lKHc/0xzKpXEe0=
github.com/pires/go-proxyproto v0.8.0/go.mod h1:iknsfgnH8EkjrMeMyvfKByp9TiBZCKZM0jx2xmKqnVY=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@ -358,16 +358,16 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -376,8 +376,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -412,8 +412,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -3,7 +3,7 @@ name = "authentik"
version = "2025.4.0"
description = ""
authors = [{ name = "authentik Team", email = "hello@goauthentik.io" }]
requires-python = "==3.13.*"
requires-python = "==3.12.*"
dependencies = [
"argon2-cffi",
"celery",
@ -52,7 +52,7 @@ dependencies = [
"pydantic-scim",
"pyjwt",
"pyrad",
"python-kadmin-rs",
"python-kadmin-rs ==0.6.0",
"pyyaml",
"requests-oauthlib",
"scim2-filter-parser",
@ -70,7 +70,7 @@ dependencies = [
"watchdog",
"webauthn",
"wsproto",
"xmlsec",
"xmlsec <= 1.3.14",
"zxcvbn",
]
@ -101,18 +101,6 @@ dev = [
"selenium",
]
[tool.uv]
no-binary-package = [
# This differs from the no-binary packages in the Dockerfile. This is due to the fact
# that these packages are built from source for different reasons than cryptography and kadmin.
# These packages are built from source to link against the libxml2 on the system which is
# required for functionality and to stay up-to-date on both libraries.
# The other packages specified in the dockerfile are compiled from source to link against the
# correct FIPS OpenSSL libraries
"lxml",
"xmlsec",
]
[tool.uv.sources]
django-tenants = { git = "https://github.com/rissson/django-tenants.git", branch = "authentik-fixes" }
opencontainers = { git = "https://github.com/BeryJu/oci-python", rev = "c791b19056769cd67957322806809ab70f5bead8" }
@ -155,12 +143,12 @@ ignore-words = ".github/codespell-words.txt"
[tool.black]
line-length = 100
target-version = ['py313']
target-version = ['py312']
exclude = 'node_modules'
[tool.ruff]
line-length = 100
target-version = "py313"
target-version = "py312"
exclude = ["**/migrations/**", "**/node_modules/**"]
[tool.ruff.lint]

View File

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

View File

@ -26,7 +26,6 @@ from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException, TimeoutException, WebDriverException
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.remote.command import Command
from selenium.webdriver.remote.webdriver import WebDriver
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.support.wait import WebDriverWait
@ -198,12 +197,7 @@ class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase):
super().tearDown()
if IS_CI:
print("::group::Browser logs")
# Very verbose way to get browser logs
# https://github.com/SeleniumHQ/selenium/pull/15641
# for some reason this removes the `get_log` API from Remote Webdriver
# and only keeps it on the local Chrome web driver, even when using
# a remote chrome driver...? (nvm the fact this was released as a minor version)
for line in self.driver.execute(Command.GET_LOG, {"type": "browser"})["value"]:
for line in self.driver.get_log("browser"):
print(line["message"])
if IS_CI:
print("::endgroup::")

1993
uv.lock generated

File diff suppressed because it is too large Load Diff

9
web/package-lock.json generated
View File

@ -9472,9 +9472,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001716",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001716.tgz",
"integrity": "sha512-49/c1+x3Kwz7ZIWt+4DvK3aMJy9oYXXG6/97JKsnjdCk/6n9vVyWL8NAwVt95Lwt9eigI10Hl782kDfZUUlRXw==",
"version": "1.0.30001667",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001667.tgz",
"integrity": "sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw==",
"dev": true,
"funding": [
{
@ -9489,8 +9489,7 @@
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "CC-BY-4.0"
]
},
"node_modules/ccount": {
"version": "2.0.1",

View File

@ -1,6 +1,6 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { VERSION } from "@goauthentik/common/constants";
import { globalAK } from "@goauthentik/common/global";
import { ServerContext } from "@goauthentik/common/server-context";
import { DefaultBrand } from "@goauthentik/common/ui/config";
import "@goauthentik/elements/EmptyState";
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
@ -33,7 +33,7 @@ export class AboutModal extends WithLicenseSummary(WithBrandConfig(ModalButton))
const status = await new AdminApi(DEFAULT_CONFIG).adminSystemRetrieve();
const version = await new AdminApi(DEFAULT_CONFIG).adminVersionRetrieve();
let build: string | TemplateResult = msg("Release");
if (globalAK().config.capabilities.includes(CapabilitiesEnum.CanDebug)) {
if (ServerContext.config.capabilities.includes(CapabilitiesEnum.CanDebug)) {
build = msg("Development");
} else if (version.buildHash !== "") {
build = html`<a
@ -58,10 +58,12 @@ export class AboutModal extends WithLicenseSummary(WithBrandConfig(ModalButton))
}
renderModal() {
let product = globalAK().brand.brandingTitle || DefaultBrand.brandingTitle;
let product = ServerContext.brand.brandingTitle || DefaultBrand.brandingTitle;
if (this.licenseSummary.status != LicenseSummaryStatusEnum.Unlicensed) {
product += ` ${msg("Enterprise")}`;
}
return html`<div
class="pf-c-backdrop"
@click=${(e: PointerEvent) => {

View File

@ -6,7 +6,8 @@ import {
EVENT_NOTIFICATION_DRAWER_TOGGLE,
EVENT_SIDEBAR_TOGGLE,
} from "@goauthentik/common/constants";
import { configureSentry } from "@goauthentik/common/sentry";
import { setSentryPII, tryInitializeSentry } from "@goauthentik/common/sentry";
import { ServerContext } from "@goauthentik/common/server-context";
import { me } from "@goauthentik/common/users";
import { WebsocketClient } from "@goauthentik/common/ws";
import { AuthenticatedInterface } from "@goauthentik/elements/Interface";
@ -167,15 +168,21 @@ export class AdminInterface extends WithLicenseSummary(AuthenticatedInterface) {
}
async firstUpdated(): Promise<void> {
configureSentry(true);
tryInitializeSentry(ServerContext.config);
this.user = await me();
setSentryPII(this.user.user);
const canAccessAdmin =
this.user.user.isSuperuser ||
// TODO: somehow add `access_admin_interface` to the API schema
this.user.user.systemPermissions.includes("access_admin_interface");
if (!canAccessAdmin && this.user.user.pk > 0) {
console.debug(
"authentik/admin: User does not have access to admin interface. Redirecting...",
);
window.location.assign("/if/user/");
}
}

View File

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

View File

@ -1,5 +1,5 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { docLink } from "@goauthentik/common/global";
import { docLink } from "@goauthentik/common/server-context";
import "@goauthentik/components/ak-toggle-group";
import "@goauthentik/elements/CodeMirror";
import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";

View File

@ -1,4 +1,4 @@
import { docLink } from "@goauthentik/common/global";
import { docLink } from "@goauthentik/common/server-context";
import { ModalButton } from "@goauthentik/elements/buttons/ModalButton";
import "@goauthentik/elements/buttons/TokenCopyButton";

View File

@ -1,5 +1,5 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { docLink } from "@goauthentik/common/global";
import { docLink } from "@goauthentik/common/server-context";
import { groupBy } from "@goauthentik/common/utils";
import "@goauthentik/elements/CodeMirror";
import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";

View File

@ -1,6 +1,6 @@
import { BasePolicyForm } from "@goauthentik/admin/policies/BasePolicyForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { docLink } from "@goauthentik/common/global";
import { docLink } from "@goauthentik/common/server-context";
import "@goauthentik/elements/CodeMirror";
import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";
import "@goauthentik/elements/forms/FormGroup";

View File

@ -1,4 +1,4 @@
import { docLink } from "@goauthentik/common/global";
import { docLink } from "@goauthentik/common/server-context";
import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";

View File

@ -1,6 +1,6 @@
import { BasePropertyMappingForm } from "@goauthentik/admin/property-mappings/BasePropertyMappingForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { docLink } from "@goauthentik/common/global";
import { docLink } from "@goauthentik/common/server-context";
import "@goauthentik/elements/CodeMirror";
import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";
import "@goauthentik/elements/forms/FormGroup";

View File

@ -42,7 +42,7 @@ export class ProviderViewPage extends AKElement {
renderProvider(): TemplateResult {
if (!this.provider) {
return html`<ak-empty-state loading ?fullHeight=${true}></ak-empty-state>`;
return html`<ak-empty-state ?loading=${true} ?fullHeight=${true}></ak-empty-state>`;
}
switch (this.provider?.component) {
case "ak-provider-saml-form":

View File

@ -432,7 +432,7 @@ export class OAuth2ProviderViewPage extends AKElement {
<div class="pf-c-card__body">
${this.preview
? html`<pre>${JSON.stringify(this.preview?.preview, null, 4)}</pre>`
: html` <ak-empty-state loading></ak-empty-state> `}
: html` <ak-empty-state ?loading=${true}></ak-empty-state> `}
</div>
</div>
</div>`;

View File

@ -502,7 +502,7 @@ export class SAMLProviderViewPage extends AKElement {
renderTabPreview(): TemplateResult {
if (!this.preview) {
return html`<ak-empty-state loading></ak-empty-state>`;
return html`<ak-empty-state ?loading=${true}></ak-empty-state>`;
}
return html` <div
class="pf-c-page__main-section pf-m-no-padding-mobile pf-l-grid pf-m-gutter"

View File

@ -34,7 +34,7 @@ export class SourceViewPage extends AKElement {
renderSource(): TemplateResult {
if (!this.source) {
return html`<ak-empty-state loading ?fullHeight=${true}></ak-empty-state>`;
return html`<ak-empty-state ?loading=${true} ?fullHeight=${true}></ak-empty-state>`;
}
switch (this.source?.component) {
case "ak-source-kerberos-form":

View File

@ -5,7 +5,8 @@ import {
GroupMatchingModeToLabel,
UserMatchingModeToLabel,
} from "@goauthentik/admin/sources/oauth/utils";
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { ServerContext } from "@goauthentik/common/server-context.js";
import "@goauthentik/components/ak-switch-input";
import "@goauthentik/components/ak-text-input";
import "@goauthentik/components/ak-textarea-input";
@ -60,8 +61,9 @@ export class KerberosSourceForm extends WithCapabilitiesConfig(BaseSourceForm<Ke
kerberosSourceRequest: data as unknown as KerberosSourceRequest,
});
}
const c = await config();
if (c.capabilities.includes(CapabilitiesEnum.CanSaveMedia)) {
const { capabilities } = ServerContext.config;
if (capabilities.includes(CapabilitiesEnum.CanSaveMedia)) {
const icon = this.getFormFiles()["icon"];
if (icon || this.clearIcon) {
await new SourcesApi(DEFAULT_CONFIG).sourcesAllSetIconCreate({

View File

@ -5,7 +5,8 @@ import {
GroupMatchingModeToLabel,
UserMatchingModeToLabel,
} from "@goauthentik/admin/sources/oauth/utils";
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { ServerContext } from "@goauthentik/common/server-context.js";
import "@goauthentik/components/ak-radio-input";
import "@goauthentik/elements/CodeMirror";
import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";
@ -85,8 +86,9 @@ export class OAuthSourceForm extends WithCapabilitiesConfig(BaseSourceForm<OAuth
oAuthSourceRequest: data as unknown as OAuthSourceRequest,
});
}
const c = await config();
if (c.capabilities.includes(CapabilitiesEnum.CanSaveMedia)) {
const { capabilities } = ServerContext.config;
if (capabilities.includes(CapabilitiesEnum.CanSaveMedia)) {
const icon = this.getFormFiles()["icon"];
if (icon || this.clearIcon) {
await new SourcesApi(DEFAULT_CONFIG).sourcesAllSetIconCreate({

View File

@ -6,7 +6,8 @@ import {
GroupMatchingModeToLabel,
UserMatchingModeToLabel,
} from "@goauthentik/admin/sources/oauth/utils";
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { ServerContext } from "@goauthentik/common/server-context.js";
import {
CapabilitiesEnum,
WithCapabilitiesConfig,
@ -61,8 +62,9 @@ export class SAMLSourceForm extends WithCapabilitiesConfig(BaseSourceForm<SAMLSo
sAMLSourceRequest: data,
});
}
const c = await config();
if (c.capabilities.includes(CapabilitiesEnum.CanSaveMedia)) {
const { capabilities } = ServerContext.config;
if (capabilities.includes(CapabilitiesEnum.CanSaveMedia)) {
const icon = this.getFormFiles()["icon"];
if (icon || this.clearIcon) {
await new SourcesApi(DEFAULT_CONFIG).sourcesAllSetIconCreate({

View File

@ -1,5 +1,5 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { globalAK } from "@goauthentik/common/global";
import { ServerContext } from "@goauthentik/common/server-context";
import "@goauthentik/components/ak-text-input";
import { Form } from "@goauthentik/elements/forms/Form";
@ -21,7 +21,7 @@ export class UserImpersonateForm extends Form<ImpersonationRequest> {
impersonationRequest: data,
})
.then(() => {
window.location.href = globalAK().api.base;
window.location.href = ServerContext.baseURL;
});
}

View File

@ -5,22 +5,14 @@ import {
LoggingMiddleware,
} from "@goauthentik/common/api/middleware";
import { EVENT_LOCALE_REQUEST, VERSION } from "@goauthentik/common/constants";
import { globalAK } from "@goauthentik/common/global";
import { ServerContext } from "@goauthentik/common/server-context";
import { Config, Configuration, CoreApi, CurrentBrand, RootApi } from "@goauthentik/api";
import { Configuration, CurrentBrand } from "@goauthentik/api";
// HACK: Workaround for ESBuild not being able to hoist import statement across entrypoints.
// This can be removed after ESBuild uses a single build context for all entrypoints.
export { CSRFHeaderName };
let globalConfigPromise: Promise<Config> | undefined = Promise.resolve(globalAK().config);
export function config(): Promise<Config> {
if (!globalConfigPromise) {
globalConfigPromise = new RootApi(DEFAULT_CONFIG).rootConfigRetrieve();
}
return globalConfigPromise;
}
export function brandSetFavicon(brand: CurrentBrand) {
/**
* <link rel="icon" href="/static/dist/assets/icons/icon.png">
@ -52,35 +44,22 @@ export function brandSetLocale(brand: CurrentBrand) {
);
}
let globalBrandPromise: Promise<CurrentBrand> | undefined = Promise.resolve(globalAK().brand);
export function brand(): Promise<CurrentBrand> {
if (!globalBrandPromise) {
globalBrandPromise = new CoreApi(DEFAULT_CONFIG)
.coreBrandsCurrentRetrieve()
.then((brand) => {
brandSetFavicon(brand);
brandSetLocale(brand);
return brand;
});
}
return globalBrandPromise;
}
export function getMetaContent(key: string): string {
const metaEl = document.querySelector<HTMLMetaElement>(`meta[name=${key}]`);
if (!metaEl) return "";
return metaEl.content;
}
export const DEFAULT_CONFIG = new Configuration({
basePath: `${globalAK().api.base}api/v3`,
basePath: `${ServerContext.baseURL}api/v3`,
headers: {
"sentry-trace": getMetaContent("sentry-trace"),
"sentry-trace": ServerContext.sentryTrace,
},
middleware: [
new CSRFMiddleware(),
new EventMiddleware(),
new LoggingMiddleware(globalAK().brand),
new LoggingMiddleware(ServerContext.brand),
],
});

View File

@ -1,59 +0,0 @@
import { Config, ConfigFromJSON, CurrentBrand, CurrentBrandFromJSON } from "@goauthentik/api";
export interface GlobalAuthentik {
_converted?: boolean;
locale?: string;
flow?: {
layout: string;
};
config: Config;
brand: CurrentBrand;
versionFamily: string;
versionSubdomain: string;
build: string;
api: {
base: string;
relBase: string;
};
}
export interface AuthentikWindow {
authentik: GlobalAuthentik;
}
export function globalAK(): GlobalAuthentik {
const ak = (window as unknown as AuthentikWindow).authentik;
if (ak && !ak._converted) {
ak._converted = true;
ak.brand = CurrentBrandFromJSON(ak.brand);
ak.config = ConfigFromJSON(ak.config);
}
const apiBase = new URL(process.env.AK_API_BASE_PATH || window.location.origin);
if (!ak) {
return {
config: ConfigFromJSON({
capabilities: [],
}),
brand: CurrentBrandFromJSON({
ui_footer_links: [],
}),
versionFamily: "",
versionSubdomain: "",
build: "",
api: {
base: apiBase.toString(),
relBase: apiBase.pathname,
},
};
}
return ak;
}
export function docLink(path: string): string {
const ak = globalAK();
// Default case or beta build which should always point to latest
if (!ak || ak.build !== "") {
return `https://goauthentik.io${path}`;
}
return `https://${ak.versionSubdomain}.goauthentik.io${path}`;
}

View File

@ -1,32 +1,34 @@
import { config } from "@goauthentik/common/api/config";
import { VERSION } from "@goauthentik/common/constants";
import { me } from "@goauthentik/common/users";
import { readInterfaceRouteParam } from "@goauthentik/elements/router/utils";
import {
ErrorEvent,
EventHint,
browserTracingIntegration,
init,
setTag,
setUser,
} from "@sentry/browser";
import { RouteInterfaceName, readInterfaceRouteParam } from "@goauthentik/elements/router/utils";
import { BrowserOptions, browserTracingIntegration, init, setTag, setUser } from "@sentry/browser";
import { CapabilitiesEnum, Config, ResponseError } from "@goauthentik/api";
import { CapabilitiesEnum, Config, ResponseError, UserSelf } from "@goauthentik/api";
/**
* A generic error that can be thrown without triggering Sentry's reporting.
*
* @category Sentry
*/
export class SentryIgnoredError extends Error {}
export const TAG_SENTRY_COMPONENT = "authentik.component";
export const TAG_SENTRY_CAPABILITIES = "authentik.capabilities";
/**
* Attempt initializing Spotlight.
*
* @see {@link https://spotlightjs.com/ Spotlight}
* @category Sentry
*/
export async function tryInitializingSpotlight() {
return import("@spotlightjs/spotlight").then((Spotlight) =>
Spotlight.init({ injectImmediately: true }),
);
}
export async function configureSentry(canDoPpi = false): Promise<Config> {
const cfg = await config();
if (cfg.errorReporting.enabled) {
init({
dsn: cfg.errorReporting.sentryDsn,
/**
* Default Sentry options for the browser.
*
* @category Sentry
*/
const DEFAULT_SENTRY_BROWSER_OPTIONS = {
ignoreErrors: [
/network/gi,
/fetch/gi,
@ -46,12 +48,7 @@ export async function configureSentry(canDoPpi = false): Promise<Config> {
},
}),
],
tracesSampleRate: cfg.errorReporting.tracesSampleRate,
environment: cfg.errorReporting.environment,
beforeSend: (
event: ErrorEvent,
hint: EventHint,
): ErrorEvent | PromiseLike<ErrorEvent | null> | null => {
beforeSend: (event, hint) => {
if (!hint) {
return event;
}
@ -66,24 +63,69 @@ export async function configureSentry(canDoPpi = false): Promise<Config> {
}
return event;
},
});
setTag(TAG_SENTRY_CAPABILITIES, cfg.capabilities.join(","));
if (window.location.pathname.includes("if/")) {
setTag(TAG_SENTRY_COMPONENT, `web/${readInterfaceRouteParam()}`);
}
if (cfg.capabilities.includes(CapabilitiesEnum.CanDebug)) {
const Spotlight = await import("@spotlightjs/spotlight");
} as const satisfies BrowserOptions;
Spotlight.init({ injectImmediately: true });
/**
* Include the given user in Sentry events.
*
* @category Sentry
*/
export function setSentryPII(user: UserSelf): void {
console.debug("authentik/sentry: PII enabled.");
setUser({ email: user.email });
}
if (cfg.errorReporting.sendPii && canDoPpi) {
me().then((user) => {
setUser({ email: user.user.email });
console.debug("authentik/config: Sentry with PII enabled.");
/**
* Include the given capabilities in Sentry events.
*
* @category Sentry
*/
export function setSentryCapabilities(capabilities: CapabilitiesEnum[]): void {
setTag("authentik.capabilities", capabilities.join(","));
}
/**
* Include the given route interface in Sentry events.
*
* @category Sentry
*/
export function setSentryInterface(interfaceName: RouteInterfaceName) {
setTag("authentik.component", `web/${interfaceName}}`);
}
/**
* Attempt to initialize Sentry with the given configuration.
*
* @see {@linkcode setSentryPII}
* @see {@linkcode setSentryCapabilities}
* @see {@linkcode setSentryInterface}
* @category Sentry
*/
export function tryInitializeSentry({ errorReporting, capabilities }: Config): void {
if (!errorReporting.enabled) return;
init({
...DEFAULT_SENTRY_BROWSER_OPTIONS,
dsn: errorReporting.sentryDsn,
tracesSampleRate: errorReporting.tracesSampleRate,
environment: errorReporting.environment,
enabled: process.env.NODE_ENV !== "development",
});
setSentryCapabilities(capabilities);
setSentryInterface(readInterfaceRouteParam());
if (
process.env.NODE_ENV === "development" &&
capabilities.includes(CapabilitiesEnum.CanDebug)
) {
tryInitializingSpotlight()
.then(() => {
console.debug("authentik/sentry: Sentry with Spotlight enabled.");
})
.catch((err) => {
console.warn("authentik/sentry: Failed to load Spotlight", err);
});
} else {
console.debug("authentik/config: Sentry enabled.");
}
}
return cfg;
}

View File

@ -0,0 +1,116 @@
/**
* @file Server context singleton.
*/
import { Config, ConfigFromJSON, CurrentBrand, CurrentBrandFromJSON } from "@goauthentik/api";
function readMetaElement(key: string, fallback: string = ""): string {
const metaElement = document.querySelector<HTMLMetaElement>(`meta[name="${key}"]`);
const value = metaElement?.getAttribute("content") || fallback;
return value;
}
interface ServerContextValue {
/**
* Server-injected authentik configuration.
*/
config: Readonly<Config>;
/**
* Brand information used to customize the UI.
*/
brand: Readonly<CurrentBrand>;
/**
* A semantic versioning string representing the current version of authentik.
*/
versionFamily: string;
/**
* A subdomain-compatible version string representing the current version of authentik.
*/
versionSubdomain: string;
/**
* A build hash string representing the current build of authentik.
*/
build: string;
/**
* The base URL of the authentik instance.
*/
baseURL: string;
/**
* The relative base URL of the authentik instance.
*/
baseURLRelative: string;
/**
* The layout of the flow, if any.
*/
flowLayout: string;
/**
* The Sentry trace ID for the current request.
*/
sentryTrace: string;
}
/**
* Reads the server context from the DOM.
*/
export function refreshServerContext(): Readonly<ServerContextValue> {
const configElement = document.getElementById(":ak-config:");
const config = configElement?.textContent
? ConfigFromJSON(JSON.parse(configElement.textContent))
: ConfigFromJSON({
capabilities: [],
});
const brandElement = document.getElementById(":ak-brand:");
const brand = brandElement?.textContent
? CurrentBrandFromJSON(JSON.parse(brandElement.textContent))
: CurrentBrandFromJSON({
ui_footer_links: [],
});
const apiBaseURL = new URL(process.env.AK_API_BASE_PATH || window.location.origin);
const value: ServerContextValue = {
sentryTrace: readMetaElement("sentry-trace"),
baseURL: readMetaElement("ak-base-url") || apiBaseURL.toString(),
baseURLRelative: readMetaElement("ak-base-url-rel"),
versionFamily: readMetaElement("ak-version-family"),
versionSubdomain: readMetaElement("ak-version-subdomain"),
build: readMetaElement("ak-build"),
flowLayout: readMetaElement("ak-flow-layout"),
config,
brand,
};
return value;
}
/**
* Server injected values used to configure application.
*
* @singleton
*/
export const ServerContext = refreshServerContext();
export function docLink(path: string): string {
const { build, versionSubdomain } = ServerContext;
// Default case or beta build which should always point to latest
if (build) {
return new URL(path, "https://goauthentik.io").toString();
}
return new URL(path, `https://${versionSubdomain}.goauthentik.io`).toString();
}

View File

@ -1,6 +1,6 @@
import { EVENT_MESSAGE, EVENT_WS_MESSAGE } from "@goauthentik/common/constants";
import { globalAK } from "@goauthentik/common/global";
import { MessageLevel } from "@goauthentik/common/messages";
import { ServerContext } from "@goauthentik/common/server-context";
import { msg } from "@lit/localize";
@ -22,11 +22,14 @@ export class WebsocketClient {
connect(): void {
if (navigator.webdriver) return;
const apiURL = new URL(globalAK().api.base);
const wsUrl = `${window.location.protocol.replace("http", "ws")}//${apiURL.host}${apiURL.pathname}ws/client/`;
this.messageSocket = new WebSocket(wsUrl);
const apiURL = new URL(ServerContext.baseURL);
const wsURL = `${window.location.protocol.replace("http", "ws")}//${apiURL.host}${apiURL.pathname}ws/client/`;
this.messageSocket = new WebSocket(wsURL);
this.messageSocket.addEventListener("open", () => {
console.debug(`authentik/ws: connected to ${wsUrl}`);
console.debug(`authentik/ws: connected to ${wsURL}`);
this.retryDelay = 200;
});
this.messageSocket.addEventListener("close", (e) => {

View File

@ -3,7 +3,7 @@ import {
EVENT_API_DRAWER_TOGGLE,
EVENT_NOTIFICATION_DRAWER_TOGGLE,
} from "@goauthentik/common/constants";
import { globalAK } from "@goauthentik/common/global";
import { ServerContext } from "@goauthentik/common/server-context";
import { UIConfig, UserDisplay, uiConfig } from "@goauthentik/common/ui/config";
import { me } from "@goauthentik/common/users";
import { AKElement } from "@goauthentik/elements/Base";
@ -146,7 +146,7 @@ export class NavigationButtons extends AKElement {
<a
class="pf-c-button pf-m-plain"
type="button"
href="${globalAK().api.base}if/user/#/settings"
href="${ServerContext.baseURL}if/user/#/settings"
>
<pf-tooltip position="top" content=${msg("Settings")}>
<i class="fas fa-cog" aria-hidden="true"></i>
@ -200,7 +200,7 @@ export class NavigationButtons extends AKElement {
${this.renderSettings()}
<div class="pf-c-page__header-tools-item">
<a
href="${globalAK().api.base}flows/-/default/invalidation/"
href="${ServerContext.baseURL}flows/-/default/invalidation/"
class="pf-c-button pf-m-plain"
>
<pf-tooltip position="top" content=${msg("Sign out")}>

View File

@ -1,4 +1,4 @@
import { globalAK } from "@goauthentik/common/global";
import { ServerContext } from "@goauthentik/common/server-context";
import {
StyleSheetInit,
StyleSheetParent,
@ -82,7 +82,7 @@ export class AKElement extends LitElement implements ThemedElement {
constructor() {
super();
const { brand } = globalAK();
const { brand } = ServerContext;
this.#preferredColorScheme = formatColorScheme(brand.uiTheme);
this.activeTheme = resolveUITheme(brand?.uiTheme);

View File

@ -83,7 +83,7 @@ export class Diagram extends AKElement {
}
});
if (!this.diagram) {
return html`<ak-empty-state loading></ak-empty-state>`;
return html`<ak-empty-state ?loading=${true}></ak-empty-state>`;
}
return html`${until(
mermaid.render("graph", this.diagram).then((r) => {

View File

@ -1,6 +1,6 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EVENT_REFRESH } from "@goauthentik/common/constants";
import { globalAK } from "@goauthentik/common/global";
import { ServerContext } from "@goauthentik/common/server-context";
import { ThemedElement } from "@goauthentik/common/theme";
import { authentikConfigContext } from "@goauthentik/elements/AuthentikContexts";
import type { ReactiveElementHost } from "@goauthentik/elements/types.js";
@ -23,8 +23,8 @@ export class ConfigContextController implements ReactiveController {
initialValue: undefined,
});
// Pre-hydrate from template-embedded config
this.context.setValue(globalAK().config);
this.host.config = globalAK().config;
this.context.setValue(ServerContext.config);
this.host.config = ServerContext.config;
this.fetch = this.fetch.bind(this);
this.fetch();
}

View File

@ -3,9 +3,10 @@ import {
EVENT_WS_MESSAGE,
TITLE_DEFAULT,
} from "@goauthentik/common/constants";
import { globalAK } from "@goauthentik/common/global";
import { UIConfig, UserDisplay, getConfigForUser } from "@goauthentik/common/ui/config";
import { ServerContext } from "@goauthentik/common/server-context";
import { getConfigForUser } from "@goauthentik/common/ui/config";
import { DefaultBrand } from "@goauthentik/common/ui/config";
import { UIConfig, UserDisplay } from "@goauthentik/common/ui/config";
import { me } from "@goauthentik/common/users";
import "@goauthentik/components/ak-nav-buttons";
import { AKElement } from "@goauthentik/elements/Base";
@ -404,7 +405,7 @@ export class AKPageNavbar extends WithBrandConfig(AKElement) implements PageNavb
<ak-nav-buttons .uiConfig=${this.uiConfig} .me=${this.session}>
<a
class="pf-c-button pf-m-secondary pf-m-small pf-u-display-none pf-u-display-block-on-md"
href="${globalAK().api.base}if/user/"
href="${ServerContext.baseURL}if/user/"
slot="extra"
>
${msg("User interface")}

View File

@ -1,5 +1,3 @@
import { globalAK } from "@goauthentik/common/global";
import { LOCALES as RAW_LOCALES, enLocale } from "./definitions";
import { AkLocale } from "./types";
@ -51,7 +49,7 @@ export function autoDetectLanguage(userReq = TOMBSTONE, brandReq = TOMBSTONE): s
userReq,
window.navigator?.language ?? TOMBSTONE,
brandReq,
globalAK()?.locale ?? TOMBSTONE,
document.documentElement.getAttribute("lang") ?? TOMBSTONE,
DEFAULT_LOCALE,
].filter(isLocaleCandidate);

View File

@ -1,4 +1,4 @@
import { globalAK } from "@goauthentik/common/global";
import { ServerContext } from "@goauthentik/common/server-context";
import { AKElement } from "@goauthentik/elements/Base";
import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider";
@ -76,7 +76,7 @@ export class EnterpriseStatusBanner extends WithLicenseSummary(AKElement) {
: "pf-m-gold"}"
>
${message}
<a href="${globalAK().api.base}if/admin/#/enterprise/licenses"
<a href="${ServerContext.baseURL}if/admin/#/enterprise/licenses"
>${msg("Click here for more info.")}</a
>
</div>`;

View File

@ -230,7 +230,9 @@ export abstract class AKChart<T> extends AKElement {
<p slot="body">${pluckErrorDetail(this.error)}</p>
</ak-empty-state>
`
: html`${this.chart ? html`` : html`<ak-empty-state loading></ak-empty-state>`}`}
: html`${this.chart
? html``
: html`<ak-empty-state ?loading="${true}"></ak-empty-state>`}`}
${this.centerText ? html` <span>${this.centerText}</span> ` : html``}
<canvas style="${this.chart === undefined ? "display: none;" : ""}"></canvas>
</div>

View File

@ -71,7 +71,7 @@ export abstract class ModelForm<T, PKT extends string | number> extends Form<T>
renderVisible(): TemplateResult {
if ((this._instancePk && !this.instance) || !this._initialDataLoad) {
return html`<ak-empty-state loading></ak-empty-state>`;
return html`<ak-empty-state ?loading=${true}></ak-empty-state>`;
}
return super.renderVisible();
}

View File

@ -1,6 +1,6 @@
import { RequestInfo } from "@goauthentik/common/api/middleware";
import { EVENT_API_DRAWER_TOGGLE, EVENT_REQUEST_POST } from "@goauthentik/common/constants";
import { globalAK } from "@goauthentik/common/global";
import { ServerContext } from "@goauthentik/common/server-context";
import { formatElapsedTime } from "@goauthentik/common/temporal";
import { AKElement } from "@goauthentik/elements/Base";
@ -92,7 +92,7 @@ export class APIDrawer extends AKElement {
<h1 class="pf-c-notification-drawer__header-title">
${msg("API Requests")}
</h1>
<a href="${globalAK().api.base}api/v3/" target="_blank"
<a href="${ServerContext.baseURL}api/v3/" target="_blank"
>${msg("Open API Browser")}</a
>
</div>

View File

@ -1,8 +1,8 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EVENT_NOTIFICATION_DRAWER_TOGGLE, EVENT_REFRESH } from "@goauthentik/common/constants";
import { globalAK } from "@goauthentik/common/global";
import { actionToLabel } from "@goauthentik/common/labels";
import { MessageLevel } from "@goauthentik/common/messages";
import { ServerContext } from "@goauthentik/common/server-context";
import { formatElapsedTime } from "@goauthentik/common/temporal";
import { me } from "@goauthentik/common/users";
import { AKElement } from "@goauthentik/elements/Base";
@ -99,7 +99,7 @@ export class NotificationDrawer extends AKElement {
html`
<a
class="pf-c-dropdown__toggle pf-m-plain"
href="${globalAK().api.base}if/admin/#/events/log/${item.event?.pk}"
href="${ServerContext.baseURL}if/admin/#/events/log/${item.event?.pk}"
>
<pf-tooltip position="top" content=${msg("Show details")}>
<i class="fas fa-share-square"></i>

View File

@ -51,7 +51,7 @@ export class Route {
if (this.callback) {
return html`${until(
this.callback(args),
html`<ak-empty-state loading></ak-empty-state>`,
html`<ak-empty-state ?loading=${true}></ak-empty-state>`,
)}`;
}
if (this.element) {

View File

@ -1,5 +1,5 @@
import type { AdminInterface } from "@goauthentik/admin/AdminInterface/index.entrypoint.js";
import { globalAK } from "@goauthentik/common/global";
import { ServerContext } from "@goauthentik/common/server-context";
import { DefaultBrand } from "@goauthentik/common/ui/config";
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider";
@ -45,7 +45,7 @@ export class SidebarVersion extends WithLicenseSummary(WithVersion(AKElement)) {
if (!this.version || !this.licenseSummary) {
return nothing;
}
let product = globalAK().brand.brandingTitle || DefaultBrand.brandingTitle;
let product = ServerContext.brand.brandingTitle || DefaultBrand.brandingTitle;
if (this.licenseSummary.status != LicenseSummaryStatusEnum.Unlicensed) {
product += ` ${msg("Enterprise")}`;
}

View File

@ -121,7 +121,7 @@ export class SyncStatusCard extends AKElement {
renderSyncStatus(): TemplateResult {
if (this.loading) {
return html`<ak-empty-state loading></ak-empty-state>`;
return html`<ak-empty-state ?loading=${true}></ak-empty-state>`;
}
if (!this.syncState) {
return html`${msg("No sync status.")}`;

View File

@ -19,7 +19,7 @@ describe("ak-empty-state", () => {
});
it("should render the default loader", async () => {
render(html`<ak-empty-state loading header=${msg("Loading")}> </ak-empty-state>`);
render(html`<ak-empty-state ?loading=${true} header=${msg("Loading")}> </ak-empty-state>`);
const empty = await $("ak-empty-state").$(">>>.pf-c-empty-state__icon");
await expect(empty).toExist();

View File

@ -139,7 +139,8 @@ export class UserSourceSettingsPage extends AKElement {
})}
`}
`
: html`<ak-empty-state loading header=${msg("Loading")}> </ak-empty-state>`}
: html`<ak-empty-state ?loading="${true}" header=${msg("Loading")}>
</ak-empty-state>`}
</ul>`;
}
}

View File

@ -4,8 +4,8 @@ import {
EVENT_FLOW_INSPECTOR_TOGGLE,
TITLE_DEFAULT,
} from "@goauthentik/common/constants";
import { globalAK } from "@goauthentik/common/global";
import { configureSentry } from "@goauthentik/common/sentry";
import { tryInitializeSentry } from "@goauthentik/common/sentry";
import { ServerContext } from "@goauthentik/common/server-context";
import { DefaultBrand } from "@goauthentik/common/ui/config";
import { WebsocketClient } from "@goauthentik/common/ws";
import { Interface } from "@goauthentik/elements/Interface";
@ -237,10 +237,12 @@ export class FlowExecutor extends Interface implements StageHost {
}
async firstUpdated(): Promise<void> {
configureSentry();
tryInitializeSentry(ServerContext.config);
if (this.config?.capabilities.includes(CapabilitiesEnum.CanDebug)) {
this.inspectorAvailable = true;
}
this.loading = true;
try {
const challenge = await new FlowsApi(DEFAULT_CONFIG).flowsExecutorGet({
@ -481,7 +483,8 @@ export class FlowExecutor extends Interface implements StageHost {
}
getLayout(): string {
const prefilledFlow = globalAK()?.flow?.layout || FlowLayoutEnum.Stacked;
const prefilledFlow = ServerContext.flowLayout || FlowLayoutEnum.Stacked;
if (this.challenge) {
return this.challenge?.flowInfo?.layout || prefilledFlow;
}
@ -521,7 +524,7 @@ export class FlowExecutor extends Interface implements StageHost {
<img
src="${themeImage(
this.brand?.brandingLogo ??
globalAK()?.brand.brandingLogo ??
ServerContext.brand.brandingLogo ??
DefaultBrand.brandingLogo,
)}"
alt="${msg("authentik Logo")}"

View File

@ -1,4 +1,4 @@
import { globalAK } from "@goauthentik/common/global";
import { ServerContext } from "@goauthentik/common/server-context";
import "@goauthentik/flow/FormStatic";
import { BaseStage } from "@goauthentik/flow/stages/base";
@ -24,7 +24,8 @@ export class SessionEnd extends BaseStage<SessionEndChallenge, unknown> {
render(): TemplateResult {
if (!this.challenge) {
return html`<ak-empty-state loading header=${msg("Loading")}> </ak-empty-state>`;
return html`<ak-empty-state ?loading="${true}" header=${msg("Loading")}>
</ak-empty-state>`;
}
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo?.title}</h1>
@ -47,7 +48,7 @@ export class SessionEnd extends BaseStage<SessionEndChallenge, unknown> {
str`You've logged out of ${this.challenge.applicationName}. You can go back to the overview to launch another application, or log out of your authentik account.`,
)}
</p>
<a href="${globalAK().api.base}" class="pf-c-button pf-m-primary">
<a href="${ServerContext.baseURL}" class="pf-c-button pf-m-primary">
${msg("Go back to overview")}
</a>
${this.challenge.invalidationFlowUrl

View File

@ -1,5 +1,5 @@
import { PFSize } from "@goauthentik/common/enums.js";
import { globalAK } from "@goauthentik/common/global";
import { ServerContext } from "@goauthentik/common/server-context";
import { truncateWords } from "@goauthentik/common/utils";
import "@goauthentik/elements/AppIcon";
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
@ -82,8 +82,7 @@ export class LibraryApplication extends AKElement {
? html`
<a
class="pf-c-button pf-m-control pf-m-small pf-m-block"
href="${globalAK().api
.base}if/admin/#/core/applications/${application?.slug}"
href="${ServerContext.baseURL}if/admin/#/core/applications/${application?.slug}"
>
<i class="fas fa-edit"></i>&nbsp;${msg("Edit")}
</a>

View File

@ -1,4 +1,4 @@
import { docLink, globalAK } from "@goauthentik/common/global";
import { ServerContext, docLink } from "@goauthentik/common/server-context";
import { AKElement } from "@goauthentik/elements/Base";
import { paramURL } from "@goauthentik/elements/router/RouterOutlet";
@ -49,7 +49,7 @@ export class LibraryPageApplicationEmptyList extends AKElement {
<a
aria-disabled="false"
class="cta pf-c-button pf-m-secondary"
href="${globalAK().api.base}if/admin/${href}"
href="${ServerContext.baseURL}if/admin/${href}"
>${msg("Create a new application")}</a
>
</div>

View File

@ -102,7 +102,7 @@ export class LibraryPage extends AKElement {
}
loading() {
return html`<ak-empty-state loading header=${msg("Loading")}> </ak-empty-state>`;
return html`<ak-empty-state ?loading="${true}" header=${msg("Loading")}> </ak-empty-state>`;
}
running() {

View File

@ -4,8 +4,8 @@ import {
EVENT_NOTIFICATION_DRAWER_TOGGLE,
EVENT_WS_MESSAGE,
} from "@goauthentik/common/constants";
import { globalAK } from "@goauthentik/common/global";
import { configureSentry } from "@goauthentik/common/sentry";
import { setSentryPII, tryInitializeSentry } from "@goauthentik/common/sentry";
import { ServerContext } from "@goauthentik/common/server-context";
import { UIConfig, getConfigForUser } from "@goauthentik/common/ui/config";
import { DefaultBrand } from "@goauthentik/common/ui/config";
import { me } from "@goauthentik/common/users";
@ -170,14 +170,14 @@ class UserInterfacePresentation extends AKElement {
return html`<a
class="pf-c-button pf-m-secondary pf-m-small pf-u-display-none pf-u-display-block-on-md"
href="${globalAK().api.base}if/admin/"
href="${ServerContext.baseURL}if/admin/"
slot="extra"
>
${msg("Admin interface")}
</a>
<a
class="pf-c-button pf-m-secondary pf-m-small pf-u-display-none-on-md pf-u-display-block"
href="${globalAK().api.base}if/admin/"
href="${ServerContext.baseURL}if/admin/"
slot="extra"
>
${msg("Admin")}
@ -284,7 +284,9 @@ export class UserInterface extends AuthenticatedInterface {
super();
this.ws = new WebsocketClient();
this.fetchConfigurationDetails();
configureSentry(true);
tryInitializeSentry(ServerContext.config);
this.toggleNotificationDrawer = this.toggleNotificationDrawer.bind(this);
this.toggleApiDrawer = this.toggleApiDrawer.bind(this);
this.fetchConfigurationDetails = this.fetchConfigurationDetails.bind(this);
@ -325,6 +327,8 @@ export class UserInterface extends AuthenticatedInterface {
this.me = session;
this.uiConfig = getConfigForUser(session.user);
setSentryPII(session.user);
new EventsApi(DEFAULT_CONFIG)
.eventsNotificationsList({
seen: false,

View File

@ -1,5 +1,5 @@
import { AndNext } from "@goauthentik/common/api/config";
import { globalAK } from "@goauthentik/common/global";
import { ServerContext } from "@goauthentik/common/server-context";
import { AKElement } from "@goauthentik/elements/Base";
import { msg } from "@lit/localize";
@ -32,7 +32,7 @@ export class UserSettingsPassword extends AKElement {
<div class="pf-c-card__body">
<a
href="${ifDefined(this.configureUrl)}${AndNext(
`${globalAK().api.relBase}if/user/#/settings;${JSON.stringify({ page: "page-details" })}`,
`${ServerContext.baseURL}if/user/#/settings;${JSON.stringify({ page: "page-details" })}`,
)}"
class="pf-c-button pf-m-primary"
>

View File

@ -5,8 +5,8 @@ import {
parseAPIResponseError,
pluckErrorDetail,
} from "@goauthentik/common/errors/network";
import { globalAK } from "@goauthentik/common/global";
import { MessageLevel } from "@goauthentik/common/messages";
import { ServerContext } from "@goauthentik/common/server-context";
import { refreshMe } from "@goauthentik/common/users";
import { AKElement } from "@goauthentik/elements/Base";
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
@ -173,14 +173,15 @@ export class UserSettingsFlowExecutor
level: MessageLevel.success,
message: msg("Successfully updated details"),
});
return html`<ak-empty-state loading header=${msg("Loading")}> </ak-empty-state>`;
return html`<ak-empty-state ?loading=${true} header=${msg("Loading")}>
</ak-empty-state>`;
default:
console.debug(
`authentik/user/flows: unsupported stage type ${this.challenge.component}`,
);
return html`
<a
href="${globalAK().api.base}if/flow/${this.flowSlug}/"
href="${ServerContext.baseURL}if/flow/${this.flowSlug}/"
class="pf-c-button pf-m-primary"
>
${msg("Open settings")}
@ -194,7 +195,8 @@ export class UserSettingsFlowExecutor
return html`<p>${msg("No settings flow configured.")}</p> `;
}
if (!this.challenge || this.loading) {
return html`<ak-empty-state loading header=${msg("Loading")}> </ak-empty-state>`;
return html`<ak-empty-state ?loading=${true} header=${msg("Loading")}>
</ak-empty-state>`;
}
return html` ${this.renderChallenge()} `;
}

View File

@ -1,4 +1,4 @@
import { globalAK } from "@goauthentik/common/global";
import { ServerContext } from "@goauthentik/common/server-context";
import "@goauthentik/elements/forms/HorizontalFormElement";
import { PromptStage } from "@goauthentik/flow/stages/prompt/PromptStage";
@ -51,7 +51,7 @@ export class UserSettingsPromptStage extends PromptStage {
${this.host.brand?.flowUnenrollment
? html` <a
class="pf-c-button pf-m-danger"
href="${globalAK().api.base}if/flow/${this.host.brand
href="${ServerContext.baseURL}if/flow/${this.host.brand
.flowUnenrollment}/"
>
${msg("Delete account")}
@ -64,7 +64,8 @@ export class UserSettingsPromptStage extends PromptStage {
render(): TemplateResult {
if (!this.challenge) {
return html`<ak-empty-state loading header=${msg("Loading")}> </ak-empty-state>`;
return html`<ak-empty-state ?loading="${true}" header=${msg("Loading")}>
</ak-empty-state>`;
}
return html`<div class="pf-c-login__main-body">
<form

View File

@ -1,7 +1,7 @@
import { AndNext, DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { globalAK } from "@goauthentik/common/global";
import { deviceTypeName } from "@goauthentik/common/labels";
import { SentryIgnoredError } from "@goauthentik/common/sentry";
import { ServerContext } from "@goauthentik/common/server-context";
import { formatElapsedTime } from "@goauthentik/common/temporal";
import "@goauthentik/elements/buttons/Dropdown";
import "@goauthentik/elements/buttons/ModalButton";
@ -74,7 +74,7 @@ export class MFADevicesPage extends Table<Device> {
return html`<li>
<a
href="${ifDefined(stage.configureUrl)}${AndNext(
`${globalAK().api.relBase}if/user/#/settings;${JSON.stringify({
`${ServerContext.baseURL}if/user/#/settings;${JSON.stringify({
page: "page-mfa",
})}`,
)}"

View File

@ -612,7 +612,7 @@
</trans-unit>
<trans-unit id="s6dfd15978586d05f">
<source>Welcome, <x id="0" equiv-text="${username || &quot;&quot;}"/>.</source>
<source>Welcome, <x id="0" equiv-text="${name || &quot;&quot;}"/>.</source>
<target>Willkommen,
<x id="0" equiv-text="${name}"/>.</target>
@ -9171,9 +9171,6 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s79b3fcd40dd63921">
<source>Number of previous passwords to check</source>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
</trans-unit>
</body>
</file>

View File

@ -493,7 +493,7 @@
<target>General system status</target>
</trans-unit>
<trans-unit id="s6dfd15978586d05f">
<source>Welcome, <x id="0" equiv-text="${username || &quot;&quot;}"/>.</source>
<source>Welcome, <x id="0" equiv-text="${name || &quot;&quot;}"/>.</source>
<target>Welcome,
<x id="0" equiv-text="${name}"/>.</target>
</trans-unit>
@ -7694,9 +7694,6 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s79b3fcd40dd63921">
<source>Number of previous passwords to check</source>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
</trans-unit>
</body>
</file>

View File

@ -612,7 +612,7 @@
</trans-unit>
<trans-unit id="s6dfd15978586d05f">
<source>Welcome, <x id="0" equiv-text="${username || &quot;&quot;}"/>.</source>
<source>Welcome, <x id="0" equiv-text="${name || &quot;&quot;}"/>.</source>
<target>Bienvenido,
<x id="0" equiv-text="${name}"/>.</target>
@ -9253,9 +9253,6 @@ Las vinculaciones a grupos o usuarios se comparan con el usuario del evento.</ta
</trans-unit>
<trans-unit id="s79b3fcd40dd63921">
<source>Number of previous passwords to check</source>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
</trans-unit>
</body>
</file>

View File

@ -612,7 +612,7 @@
</trans-unit>
<trans-unit id="s6dfd15978586d05f">
<source>Welcome, <x id="0" equiv-text="${username || &quot;&quot;}"/>.</source>
<source>Welcome, <x id="0" equiv-text="${name || &quot;&quot;}"/>.</source>
<target>Bienvenue,
<x id="0" equiv-text="${name}"/>.</target>
@ -9805,9 +9805,6 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
<trans-unit id="s79b3fcd40dd63921">
<source>Number of previous passwords to check</source>
<target>Nombre d'anciens mots de passe à vérifier</target>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
</trans-unit>
</body>
</file>

View File

@ -612,7 +612,7 @@
</trans-unit>
<trans-unit id="s6dfd15978586d05f">
<source>Welcome, <x id="0" equiv-text="${username || &quot;&quot;}"/>.</source>
<source>Welcome, <x id="0" equiv-text="${name || &quot;&quot;}"/>.</source>
<target>Benvenuto,
<x id="0" equiv-text="${name}"/>.</target>
@ -9779,9 +9779,6 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s79b3fcd40dd63921">
<source>Number of previous passwords to check</source>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
</trans-unit>
</body>
</file>

View File

@ -597,7 +597,7 @@
</trans-unit>
<trans-unit id="s6dfd15978586d05f">
<source>Welcome, <x id="0" equiv-text="${username || &quot;&quot;}"/>.</source>
<source>Welcome, <x id="0" equiv-text="${name || &quot;&quot;}"/>.</source>
</trans-unit>
<trans-unit id="sc381422c585b867f">
@ -9161,9 +9161,6 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s79b3fcd40dd63921">
<source>Number of previous passwords to check</source>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
</trans-unit>
</body>
</file>

View File

@ -605,7 +605,7 @@
</trans-unit>
<trans-unit id="s6dfd15978586d05f">
<source>Welcome, <x id="0" equiv-text="${username || &quot;&quot;}"/>.</source>
<source>Welcome, <x id="0" equiv-text="${name || &quot;&quot;}"/>.</source>
<target>Welkom,
<x id="0" equiv-text="${name}"/>.</target>
@ -9063,9 +9063,6 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
</trans-unit>
<trans-unit id="s79b3fcd40dd63921">
<source>Number of previous passwords to check</source>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
</trans-unit>
</body>
</file>

View File

@ -612,7 +612,7 @@
</trans-unit>
<trans-unit id="s6dfd15978586d05f">
<source>Welcome, <x id="0" equiv-text="${username || &quot;&quot;}"/>.</source>
<source>Welcome, <x id="0" equiv-text="${name || &quot;&quot;}"/>.</source>
<target>Witaj,
<x id="0" equiv-text="${name}"/>.</target>
@ -9488,9 +9488,6 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
</trans-unit>
<trans-unit id="s79b3fcd40dd63921">
<source>Number of previous passwords to check</source>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
</trans-unit>
</body>
</file>

View File

@ -603,7 +603,7 @@
</trans-unit>
<trans-unit id="s6dfd15978586d05f">
<source>Welcome, <x id="0" equiv-text="${username || &quot;&quot;}"/>.</source>
<source>Welcome, <x id="0" equiv-text="${name || &quot;&quot;}"/>.</source>
<target>Ŵēĺćōḿē, <x id="0" equiv-text="${name || &quot;&quot;}"/>.</target>
</trans-unit>
@ -9496,7 +9496,4 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s79b3fcd40dd63921">
<source>Number of previous passwords to check</source>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
</trans-unit>
</body></file></xliff>

View File

@ -612,7 +612,7 @@
</trans-unit>
<trans-unit id="s6dfd15978586d05f">
<source>Welcome, <x id="0" equiv-text="${username || &quot;&quot;}"/>.</source>
<source>Welcome, <x id="0" equiv-text="${name || &quot;&quot;}"/>.</source>
<target>Добро пожаловать,
<x id="0" equiv-text="${name}"/>.</target>
@ -9581,9 +9581,6 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s79b3fcd40dd63921">
<source>Number of previous passwords to check</source>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
</trans-unit>
</body>
</file>

View File

@ -602,7 +602,7 @@
</trans-unit>
<trans-unit id="s6dfd15978586d05f">
<source>Welcome, <x id="0" equiv-text="${username || &quot;&quot;}"/>.</source>
<source>Welcome, <x id="0" equiv-text="${name || &quot;&quot;}"/>.</source>
<target>Hoş geldiniz, <x id="0" equiv-text="${name || &quot;&quot;}"/>.</target>
</trans-unit>
@ -9551,9 +9551,6 @@ Gruplara/kullanıcılara yapılan bağlamalar, etkinliğin kullanıcısına kar
</trans-unit>
<trans-unit id="s79b3fcd40dd63921">
<source>Number of previous passwords to check</source>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
</trans-unit>
</body>
</file>

View File

@ -399,7 +399,7 @@
<source>General system status</source>
</trans-unit>
<trans-unit id="s6dfd15978586d05f">
<source>Welcome, <x id="0" equiv-text="${username || &quot;&quot;}"/>.</source>
<source>Welcome, <x id="0" equiv-text="${name || &quot;&quot;}"/>.</source>
</trans-unit>
<trans-unit id="sc381422c585b867f">
<source>Quick actions</source>
@ -6302,9 +6302,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s79b3fcd40dd63921">
<source>Number of previous passwords to check</source>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -612,7 +612,7 @@
</trans-unit>
<trans-unit id="s6dfd15978586d05f">
<source>Welcome, <x id="0" equiv-text="${username || &quot;&quot;}"/>.</source>
<source>Welcome, <x id="0" equiv-text="${name || &quot;&quot;}"/>.</source>
<target>欢迎,
<x id="0" equiv-text="${name}"/>。</target>
@ -9806,10 +9806,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s79b3fcd40dd63921">
<source>Number of previous passwords to check</source>
<target>检查历史密码数量</target>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
<target>切换侧边栏</target>
</trans-unit>
</body>
</file>

View File

@ -485,7 +485,7 @@
<target>常规系统状态</target>
</trans-unit>
<trans-unit id="s6dfd15978586d05f">
<source>Welcome, <x id="0" equiv-text="${username || &quot;&quot;}"/>.</source>
<source>Welcome, <x id="0" equiv-text="${name || &quot;&quot;}"/>.</source>
<target>欢迎,
<x id="0" equiv-text="${name}"/>。</target>
</trans-unit>
@ -7394,9 +7394,6 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s79b3fcd40dd63921">
<source>Number of previous passwords to check</source>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
</trans-unit>
</body>
</file>

View File

@ -612,7 +612,7 @@
</trans-unit>
<trans-unit id="s6dfd15978586d05f">
<source>Welcome, <x id="0" equiv-text="${username || &quot;&quot;}"/>.</source>
<source>Welcome, <x id="0" equiv-text="${name || &quot;&quot;}"/>.</source>
<target>欢迎,
<x id="0" equiv-text="${name}"/>。</target>
@ -9806,10 +9806,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s79b3fcd40dd63921">
<source>Number of previous passwords to check</source>
<target>检查历史密码数量</target>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
<target>切换侧边栏</target>
</trans-unit>
</body>
</file>

View File

@ -596,7 +596,7 @@
</trans-unit>
<trans-unit id="s6dfd15978586d05f">
<source>Welcome, <x id="0" equiv-text="${username || &quot;&quot;}"/>.</source>
<source>Welcome, <x id="0" equiv-text="${name || &quot;&quot;}"/>.</source>
</trans-unit>
<trans-unit id="sc381422c585b867f">
@ -9138,9 +9138,6 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s79b3fcd40dd63921">
<source>Number of previous passwords to check</source>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
</trans-unit>
</body>
</file>

2
website/.gitignore vendored
View File

@ -3,7 +3,6 @@
# Production
/build
/out
/help
# Generated files
@ -26,5 +25,4 @@ yarn-error.log*
static/docker-compose.yml
static/schema.yml
static/releases.gen.json
docs/developer-docs/api/reference/**

View File

@ -1,6 +1,5 @@
# Ignore artifacts:
build
out
coverage
.docusaurus
node_modules

1
website/.prettierrc.json Normal file
View File

@ -0,0 +1 @@
{}

View File

@ -1,8 +1,9 @@
---
title: Email Authenticator Setup stage
authentik_version: "2025.2"
---
<span class="badge badge--version">authentik 2025.2+</span>
This stage configures an email-based authenticator that sends a one-time code to a user's email address for authentication.
When a user goes through a flow that includes this stage, they are prompted for their email address (if not already set). The user then receives an email with a one-time code, which they enter into the authentik Login panel.

View File

@ -2,6 +2,8 @@
title: WebAuthn / Passkeys Authenticator setup stage
---
<span class="badge badge--version">authentik 2021.3.1+</span>
This stage configures a WebAuthn-based Authenticator. This can either be a browser, biometrics or a Security stick like a YubiKey.
### Options

View File

@ -29,8 +29,8 @@ You can also use custom email templates, to use your own design or layout.
Starting with authentik 2024.2, it is possible to create `.txt` files with the same name as the `.html` template. If a matching `.txt` file exists, the email sent will be a multipart email with both the text and HTML template.
:::
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
<Tabs
defaultValue="docker-compose"

View File

@ -2,9 +2,8 @@
title: Caddy
---
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import Placeholders from "./__placeholders.md";
import CaddyStandalone from "./_caddy_standalone.md";

View File

@ -2,12 +2,13 @@
title: Envoy
---
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import Placeholders from "./__placeholders.md";
import EnvoyIstio from "./_envoy_istio.md";
# Envoy
The configuration template shown below apply to both single-application and domain-level forward auth.
:::info

View File

@ -1,5 +1,5 @@
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
# nginx

View File

@ -1,5 +1,5 @@
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
# Traefik

View File

@ -1,82 +0,0 @@
import styles from "./styles.module.css";
const RADIUSProtocols = [
"PAP",
"CHAP",
"Digest",
"MS-CHAP",
"PEAP",
"MS-CHAPv2",
"Cisco LEAP",
"EAP-GTC",
"EAP-MD5",
"EAP-PWD",
] as const satisfies string[];
type RADIUSProtocol = (typeof RADIUSProtocols)[number];
const HashKinds = [
"Cleartext",
"NT",
"MD5",
"Salted MD5",
"SHA1",
"Salted SHA1",
"Unix Crypt",
] as const satisfies string[];
type HashKind = (typeof HashKinds)[number];
const supportMatrix: Record<HashKind, RADIUSProtocol[]> = {
"Cleartext": [
"PAP",
"CHAP",
"Digest",
"MS-CHAP",
"PEAP",
"MS-CHAPv2",
"Cisco LEAP",
"EAP-GTC",
"EAP-MD5",
"EAP-PWD",
],
"NT": ["PAP", "MS-CHAP", "PEAP", "MS-CHAPv2", "Cisco LEAP", "EAP-GTC"],
"MD5": ["PAP", "EAP-GTC"],
"Salted MD5": ["PAP", "EAP-GTC"],
"SHA1": ["PAP", "EAP-GTC"],
"Salted SHA1": ["PAP", "EAP-GTC", "EAP-PWD"],
"Unix Crypt": ["PAP", "EAP-GTC", "EAP-PWD"],
};
export const HashSupport: React.FC = () => {
return (
<table className={styles.table}>
<thead>
<tr>
<th></th>
{HashKinds.map((hashKind, i) => (
<th key={i}>{hashKind}</th>
))}
</tr>
</thead>
<tbody>
{RADIUSProtocols.map((radiusProtocol, i) => (
<tr key={i}>
<td>{radiusProtocol}</td>
{HashKinds.map((hashKind) => {
const protocols = supportMatrix[hashKind];
const supported = protocols.includes(radiusProtocol);
return (
<td data-supported={supported} key={hashKind}>
{supported ? "✓" : "✗"}
</td>
);
})}
</tr>
))}
</tbody>
</table>
);
};

View File

@ -2,7 +2,7 @@
title: RADIUS Provider
---
import { HashSupport } from "./HashSupport";
import { Check, X, AlertTriangle } from "react-feather";
You can configure a Radius provider for applications that don't support any other protocols or that require Radius.
@ -56,4 +56,15 @@ After creation, make sure to select the RADIUS property mapping in the RADIUS pr
The RADIUS provider only supports the [PAP](https://en.wikipedia.org/wiki/Password_Authentication_Protocol) (Password Authentication Protocol) protocol:
<HashSupport />
| | Clear-text | NT hash | MD5 hash | Salted MD5 hash | SHA1 hash | Salted SHA1 hash | Unix Crypt |
| ------------ | --------------- | --------------- | --------------- | --------------- | --------------- | ---------------- | --------------- |
| PAP | <Check></Check> | <Check></Check> | <Check></Check> | <Check></Check> | <Check></Check> | <Check></Check> | <Check></Check> |
| CHAP | <Check></Check> | <X></X> | <X></X> | <X></X> | <X></X> | <X></X> | <X></X> |
| Digest | <Check></Check> | <X></X> | <X></X> | <X></X> | <X></X> | <X></X> | <X></X> |
| MS-CHAP | <Check></Check> | <Check></Check> | <X></X> | <X></X> | <X></X> | <X></X> | <X></X> |
| PEAP | <Check></Check> | <Check></Check> | <X></X> | <X></X> | <X></X> | <X></X> | <X></X> |
| EAP-MSCHAPv2 | <Check></Check> | <Check></Check> | <X></X> | <X></X> | <X></X> | <X></X> | <X></X> |
| Cisco LEAP | <Check></Check> | <Check></Check> | <X></X> | <X></X> | <X></X> | <X></X> | <X></X> |
| EAP-GTC | <Check></Check> | <Check></Check> | <Check></Check> | <Check></Check> | <Check></Check> | <Check></Check> | <Check></Check> |
| EAP-MD5 | <Check></Check> | <X></X> | <X></X> | <X></X> | <X></X> | <X></X> | <X></X> |
| EAP-PWD | <Check></Check> | <X></X> | <X></X> | <X></X> | <X></X> | <Check></Check> | <Check></Check> |

View File

@ -1,20 +0,0 @@
.table td {
text-align: center;
font-weight: bold;
&:first-child {
text-align: right;
width: 13ch;
}
&:not(:first-child) {
width: 10ch;
}
&[data-supported="true"] {
color: var(--ifm-color-success-dark);
}
&[data-supported="false"] {
color: var(--ifm-color-danger-dark);
}
}

View File

@ -4,7 +4,6 @@ title: Example
This is one of the default packaged blueprints to create the default authentication flow.
<!-- prettier-ignore-start -->
```yaml
version: 1
metadata:
@ -65,4 +64,3 @@ entries:
target: !KeyOf flow
model: authentik_flows.flowstagebinding
```
<!-- prettier-ignore-end -->

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