Compare commits
	
		
			2 Commits
		
	
	
		
			website/in
			...
			server-con
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 29a3117a94 | |||
| 66e40720c5 | 
							
								
								
									
										2
									
								
								.github/workflows/ci-outpost.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/ci-outpost.yml
									
									
									
									
										vendored
									
									
								
							| @ -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 | ||||
|  | ||||
| @ -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" \ | ||||
|  | ||||
| @ -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). | ||||
|  | ||||
| @ -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> | ||||
|  | ||||
| @ -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( | ||||
|  | ||||
| @ -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 = { | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
|  | ||||
|  | ||||
| @ -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() | ||||
|  | ||||
| @ -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: | ||||
|  | ||||
| @ -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
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								go.mod
									
									
									
									
									
								
							| @ -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
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								go.sum
									
									
									
									
									
								
							| @ -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
											
										
									
								
							
							
								
								
									
										4
									
								
								packages/docusaurus-config/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								packages/docusaurus-config/package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -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", | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@goauthentik/docusaurus-config", | ||||
|     "version": "1.0.6", | ||||
|     "version": "1.0.5", | ||||
|     "description": "authentik's Docusaurus config", | ||||
|     "license": "MIT", | ||||
|     "scripts": { | ||||
|  | ||||
| @ -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] | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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::") | ||||
|  | ||||
							
								
								
									
										9
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										9
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -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", | ||||
|  | ||||
| @ -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) => { | ||||
|  | ||||
| @ -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/"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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"; | ||||
|  | ||||
| @ -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"; | ||||
|  | ||||
|  | ||||
| @ -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"; | ||||
|  | ||||
| @ -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"; | ||||
|  | ||||
| @ -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"; | ||||
|  | ||||
|  | ||||
| @ -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"; | ||||
|  | ||||
| @ -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": | ||||
|  | ||||
| @ -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>`; | ||||
|  | ||||
| @ -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" | ||||
|  | ||||
| @ -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": | ||||
|  | ||||
| @ -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({ | ||||
|  | ||||
| @ -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({ | ||||
|  | ||||
| @ -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({ | ||||
|  | ||||
| @ -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; | ||||
|             }); | ||||
|     } | ||||
|  | ||||
|  | ||||
| @ -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), | ||||
|     ], | ||||
| }); | ||||
|  | ||||
|  | ||||
| @ -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}`; | ||||
| } | ||||
| @ -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 }); | ||||
|         } | ||||
|         if (cfg.errorReporting.sendPii && canDoPpi) { | ||||
|             me().then((user) => { | ||||
|                 setUser({ email: user.user.email }); | ||||
|                 console.debug("authentik/config: Sentry with PII enabled."); | ||||
|             }); | ||||
|         } else { | ||||
|             console.debug("authentik/config: Sentry enabled."); | ||||
|         } | ||||
|     } | ||||
|     return cfg; | ||||
| /** | ||||
|  * 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 }); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * 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); | ||||
|             }); | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										116
									
								
								web/src/common/server-context.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								web/src/common/server-context.ts
									
									
									
									
									
										Normal 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(); | ||||
| } | ||||
| @ -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) => { | ||||
|  | ||||
| @ -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")}> | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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) => { | ||||
|  | ||||
| @ -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(); | ||||
|     } | ||||
|  | ||||
| @ -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")} | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
|  | ||||
| @ -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>`; | ||||
|  | ||||
| @ -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> | ||||
|  | ||||
| @ -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(); | ||||
|     } | ||||
|  | ||||
| @ -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> | ||||
|  | ||||
| @ -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> | ||||
|  | ||||
| @ -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) { | ||||
|  | ||||
| @ -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")}`; | ||||
|         } | ||||
|  | ||||
| @ -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.")}`; | ||||
|  | ||||
| @ -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(); | ||||
|  | ||||
| @ -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>`; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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")}" | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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> ${msg("Edit")} | ||||
|                       </a> | ||||
|  | ||||
| @ -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> | ||||
|  | ||||
| @ -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() { | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -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" | ||||
|                 > | ||||
|  | ||||
| @ -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()} `; | ||||
|     } | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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", | ||||
|                                     })}`, | ||||
|                                 )}" | ||||
|  | ||||
| @ -612,7 +612,7 @@ | ||||
|          | ||||
|       </trans-unit> | ||||
|       <trans-unit id="s6dfd15978586d05f"> | ||||
|         <source>Welcome, <x id="0" equiv-text="${username || ""}"/>.</source> | ||||
|         <source>Welcome, <x id="0" equiv-text="${name || ""}"/>.</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> | ||||
|  | ||||
| @ -493,7 +493,7 @@ | ||||
|         <target>General system status</target> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="s6dfd15978586d05f"> | ||||
|         <source>Welcome, <x id="0" equiv-text="${username || ""}"/>.</source> | ||||
|         <source>Welcome, <x id="0" equiv-text="${name || ""}"/>.</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> | ||||
|  | ||||
| @ -612,7 +612,7 @@ | ||||
|          | ||||
|       </trans-unit> | ||||
|       <trans-unit id="s6dfd15978586d05f"> | ||||
|         <source>Welcome, <x id="0" equiv-text="${username || ""}"/>.</source> | ||||
|         <source>Welcome, <x id="0" equiv-text="${name || ""}"/>.</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> | ||||
|  | ||||
| @ -612,7 +612,7 @@ | ||||
|          | ||||
|       </trans-unit> | ||||
|       <trans-unit id="s6dfd15978586d05f"> | ||||
|         <source>Welcome, <x id="0" equiv-text="${username || ""}"/>.</source> | ||||
|         <source>Welcome, <x id="0" equiv-text="${name || ""}"/>.</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> | ||||
|  | ||||
| @ -612,7 +612,7 @@ | ||||
|          | ||||
|       </trans-unit> | ||||
|       <trans-unit id="s6dfd15978586d05f"> | ||||
|         <source>Welcome, <x id="0" equiv-text="${username || ""}"/>.</source> | ||||
|         <source>Welcome, <x id="0" equiv-text="${name || ""}"/>.</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> | ||||
|  | ||||
| @ -597,7 +597,7 @@ | ||||
|          | ||||
|       </trans-unit> | ||||
|       <trans-unit id="s6dfd15978586d05f"> | ||||
|         <source>Welcome, <x id="0" equiv-text="${username || ""}"/>.</source> | ||||
|         <source>Welcome, <x id="0" equiv-text="${name || ""}"/>.</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> | ||||
|  | ||||
| @ -605,7 +605,7 @@ | ||||
|          | ||||
|       </trans-unit> | ||||
|       <trans-unit id="s6dfd15978586d05f"> | ||||
|         <source>Welcome, <x id="0" equiv-text="${username || ""}"/>.</source> | ||||
|         <source>Welcome, <x id="0" equiv-text="${name || ""}"/>.</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> | ||||
|  | ||||
| @ -612,7 +612,7 @@ | ||||
|          | ||||
|       </trans-unit> | ||||
|       <trans-unit id="s6dfd15978586d05f"> | ||||
|         <source>Welcome, <x id="0" equiv-text="${username || ""}"/>.</source> | ||||
|         <source>Welcome, <x id="0" equiv-text="${name || ""}"/>.</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> | ||||
|  | ||||
| @ -603,7 +603,7 @@ | ||||
|  | ||||
|       </trans-unit> | ||||
|       <trans-unit id="s6dfd15978586d05f"> | ||||
|         <source>Welcome, <x id="0" equiv-text="${username || ""}"/>.</source> | ||||
|         <source>Welcome, <x id="0" equiv-text="${name || ""}"/>.</source> | ||||
|   <target>Ŵēĺćōḿē, <x id="0" equiv-text="${name || ""}"/>.</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> | ||||
|  | ||||
| @ -612,7 +612,7 @@ | ||||
|          | ||||
|       </trans-unit> | ||||
|       <trans-unit id="s6dfd15978586d05f"> | ||||
|         <source>Welcome, <x id="0" equiv-text="${username || ""}"/>.</source> | ||||
|         <source>Welcome, <x id="0" equiv-text="${name || ""}"/>.</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> | ||||
|  | ||||
| @ -602,7 +602,7 @@ | ||||
|          | ||||
|       </trans-unit> | ||||
|       <trans-unit id="s6dfd15978586d05f"> | ||||
|         <source>Welcome, <x id="0" equiv-text="${username || ""}"/>.</source> | ||||
|         <source>Welcome, <x id="0" equiv-text="${name || ""}"/>.</source> | ||||
|         <target>Hoş geldiniz, <x id="0" equiv-text="${name || ""}"/>.</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> | ||||
|  | ||||
| @ -399,7 +399,7 @@ | ||||
|   <source>General system status</source> | ||||
| </trans-unit> | ||||
| <trans-unit id="s6dfd15978586d05f"> | ||||
|   <source>Welcome, <x id="0" equiv-text="${username || ""}"/>.</source> | ||||
|   <source>Welcome, <x id="0" equiv-text="${name || ""}"/>.</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> | ||||
|  | ||||
| @ -612,7 +612,7 @@ | ||||
|          | ||||
|       </trans-unit> | ||||
|       <trans-unit id="s6dfd15978586d05f"> | ||||
|         <source>Welcome, <x id="0" equiv-text="${username || ""}"/>.</source> | ||||
|         <source>Welcome, <x id="0" equiv-text="${name || ""}"/>.</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> | ||||
|  | ||||
| @ -485,7 +485,7 @@ | ||||
|         <target>常规系统状态</target> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="s6dfd15978586d05f"> | ||||
|         <source>Welcome, <x id="0" equiv-text="${username || ""}"/>.</source> | ||||
|         <source>Welcome, <x id="0" equiv-text="${name || ""}"/>.</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> | ||||
|  | ||||
| @ -612,7 +612,7 @@ | ||||
|          | ||||
|       </trans-unit> | ||||
|       <trans-unit id="s6dfd15978586d05f"> | ||||
|         <source>Welcome, <x id="0" equiv-text="${username || ""}"/>.</source> | ||||
|         <source>Welcome, <x id="0" equiv-text="${name || ""}"/>.</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> | ||||
|  | ||||
| @ -596,7 +596,7 @@ | ||||
|          | ||||
|       </trans-unit> | ||||
|       <trans-unit id="s6dfd15978586d05f"> | ||||
|         <source>Welcome, <x id="0" equiv-text="${username || ""}"/>.</source> | ||||
|         <source>Welcome, <x id="0" equiv-text="${name || ""}"/>.</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
									
									
								
							
							
						
						
									
										2
									
								
								website/.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -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/** | ||||
|  | ||||
| @ -1,6 +1,5 @@ | ||||
| # Ignore artifacts: | ||||
| build | ||||
| out | ||||
| coverage | ||||
| .docusaurus | ||||
| node_modules | ||||
|  | ||||
							
								
								
									
										1
									
								
								website/.prettierrc.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								website/.prettierrc.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| {} | ||||
| @ -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. | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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" | ||||
|  | ||||
| @ -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"; | ||||
|  | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| import TabItem from "@theme/TabItem"; | ||||
| import Tabs from "@theme/Tabs"; | ||||
| import TabItem from "@theme/TabItem"; | ||||
|  | ||||
| # nginx | ||||
|  | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| import TabItem from "@theme/TabItem"; | ||||
| import Tabs from "@theme/Tabs"; | ||||
| import TabItem from "@theme/TabItem"; | ||||
|  | ||||
| # Traefik | ||||
|  | ||||
|  | ||||
| @ -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> | ||||
|     ); | ||||
| }; | ||||
| @ -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> | | ||||
|  | ||||
| @ -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); | ||||
|     } | ||||
| } | ||||
| @ -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
		Reference in New Issue
	
	Block a user
	