Compare commits
	
		
			2 Commits
		
	
	
		
			workspace-
			...
			server-con
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 29a3117a94 | |||
| 66e40720c5 | 
| @ -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) | ||||
|  | ||||
|  | ||||
|  | ||||
| @ -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/"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -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"; | ||||
|  | ||||
| @ -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,89 +1,131 @@ | ||||
| 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"; | ||||
|  | ||||
| export async function configureSentry(canDoPpi = false): Promise<Config> { | ||||
|     const cfg = await config(); | ||||
|  | ||||
|     if (cfg.errorReporting.enabled) { | ||||
|         init({ | ||||
|             dsn: cfg.errorReporting.sentryDsn, | ||||
|             ignoreErrors: [ | ||||
|                 /network/gi, | ||||
|                 /fetch/gi, | ||||
|                 /module/gi, | ||||
|                 // Error on edge on ios, | ||||
|                 // https://stackoverflow.com/questions/69261499/what-is-instantsearchsdkjsbridgeclearhighlight | ||||
|                 /instantSearchSDKJSBridgeClearHighlight/gi, | ||||
|                 // Seems to be an issue in Safari and Firefox | ||||
|                 /MutationObserver.observe/gi, | ||||
|                 /NS_ERROR_FAILURE/gi, | ||||
|             ], | ||||
|             release: `authentik@${VERSION}`, | ||||
|             integrations: [ | ||||
|                 browserTracingIntegration({ | ||||
|                     shouldCreateSpanForRequest: (url: string) => { | ||||
|                         return url.startsWith(window.location.host); | ||||
|                     }, | ||||
|                 }), | ||||
|             ], | ||||
|             tracesSampleRate: cfg.errorReporting.tracesSampleRate, | ||||
|             environment: cfg.errorReporting.environment, | ||||
|             beforeSend: ( | ||||
|                 event: ErrorEvent, | ||||
|                 hint: EventHint, | ||||
|             ): ErrorEvent | PromiseLike<ErrorEvent | null> | null => { | ||||
|                 if (!hint) { | ||||
|                     return event; | ||||
|                 } | ||||
|                 if (hint.originalException instanceof SentryIgnoredError) { | ||||
|                     return null; | ||||
|                 } | ||||
|                 if ( | ||||
|                     hint.originalException instanceof ResponseError || | ||||
|                     hint.originalException instanceof DOMException | ||||
|                 ) { | ||||
|                     return null; | ||||
|                 } | ||||
|                 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"); | ||||
|  | ||||
|             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; | ||||
| /** | ||||
|  * 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 }), | ||||
|     ); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Default Sentry options for the browser. | ||||
|  * | ||||
|  * @category Sentry | ||||
|  */ | ||||
| const DEFAULT_SENTRY_BROWSER_OPTIONS = { | ||||
|     ignoreErrors: [ | ||||
|         /network/gi, | ||||
|         /fetch/gi, | ||||
|         /module/gi, | ||||
|         // Error on edge on ios, | ||||
|         // https://stackoverflow.com/questions/69261499/what-is-instantsearchsdkjsbridgeclearhighlight | ||||
|         /instantSearchSDKJSBridgeClearHighlight/gi, | ||||
|         // Seems to be an issue in Safari and Firefox | ||||
|         /MutationObserver.observe/gi, | ||||
|         /NS_ERROR_FAILURE/gi, | ||||
|     ], | ||||
|     release: `authentik@${VERSION}`, | ||||
|     integrations: [ | ||||
|         browserTracingIntegration({ | ||||
|             shouldCreateSpanForRequest: (url: string) => { | ||||
|                 return url.startsWith(window.location.host); | ||||
|             }, | ||||
|         }), | ||||
|     ], | ||||
|     beforeSend: (event, hint) => { | ||||
|         if (!hint) { | ||||
|             return event; | ||||
|         } | ||||
|         if (hint.originalException instanceof SentryIgnoredError) { | ||||
|             return null; | ||||
|         } | ||||
|         if ( | ||||
|             hint.originalException instanceof ResponseError || | ||||
|             hint.originalException instanceof DOMException | ||||
|         ) { | ||||
|             return null; | ||||
|         } | ||||
|         return event; | ||||
|     }, | ||||
| } as const satisfies BrowserOptions; | ||||
|  | ||||
| /** | ||||
|  * 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); | ||||
|  | ||||
| @ -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>`; | ||||
|  | ||||
| @ -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> | ||||
|  | ||||
| @ -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")}`; | ||||
|         } | ||||
|  | ||||
| @ -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"; | ||||
|  | ||||
| @ -48,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> | ||||
|  | ||||
| @ -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"; | ||||
| @ -181,7 +181,7 @@ export class UserSettingsFlowExecutor | ||||
|                 ); | ||||
|                 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")} | ||||
|  | ||||
| @ -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")} | ||||
|  | ||||
| @ -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", | ||||
|                                     })}`, | ||||
|                                 )}" | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	