From cada292e00c46bee56f7806035d3ccb68c3e5f64 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Fri, 29 Jul 2022 00:02:48 +0200 Subject: [PATCH] core: pre-hydrate config into templates to directly load correct assets closes #3228 Signed-off-by: Jens Langhammer --- authentik/api/v3/config.py | 13 ++++++++----- authentik/core/templates/if/admin.html | 6 ++++++ authentik/core/templates/if/flow.html | 7 ++++--- authentik/core/templates/if/user.html | 6 ++++++ authentik/core/urls.py | 11 +++++------ authentik/core/views/interface.py | 15 ++++++++++++++- web/src/api/Config.ts | 22 +++++++++++++++++++--- web/src/api/Global.ts | 4 ++++ 8 files changed, 66 insertions(+), 18 deletions(-) diff --git a/authentik/api/v3/config.py b/authentik/api/v3/config.py index 4e0414b6a..7207b2abb 100644 --- a/authentik/api/v3/config.py +++ b/authentik/api/v3/config.py @@ -68,10 +68,9 @@ class ConfigView(APIView): caps.append(Capabilities.CAN_IMPERSONATE) return caps - @extend_schema(responses={200: ConfigSerializer(many=False)}) - def get(self, request: Request) -> Response: - """Retrieve public configuration options""" - config = ConfigSerializer( + def get_config(self) -> ConfigSerializer: + """Get Config""" + return ConfigSerializer( { "error_reporting": { "enabled": CONFIG.y("error_reporting.enabled"), @@ -86,4 +85,8 @@ class ConfigView(APIView): "cache_timeout_reputation": int(CONFIG.y("redis.cache_timeout_reputation")), } ) - return Response(config.data) + + @extend_schema(responses={200: ConfigSerializer(many=False)}) + def get(self, request: Request) -> Response: + """Retrieve public configuration options""" + return Response(self.get_config().data) diff --git a/authentik/core/templates/if/admin.html b/authentik/core/templates/if/admin.html index dc3e6dc82..33c980b7e 100644 --- a/authentik/core/templates/if/admin.html +++ b/authentik/core/templates/if/admin.html @@ -7,6 +7,12 @@ + {% endblock %} {% block body %} diff --git a/authentik/core/templates/if/flow.html b/authentik/core/templates/if/flow.html index cf93d4a3d..6183417bf 100644 --- a/authentik/core/templates/if/flow.html +++ b/authentik/core/templates/if/flow.html @@ -10,9 +10,10 @@ {% endif %} + {% endblock %} {% block body %} diff --git a/authentik/core/urls.py b/authentik/core/urls.py index 7d8bedee9..2252674c6 100644 --- a/authentik/core/urls.py +++ b/authentik/core/urls.py @@ -4,11 +4,10 @@ from django.contrib.auth.decorators import login_required from django.urls import path from django.views.decorators.csrf import ensure_csrf_cookie from django.views.generic import RedirectView -from django.views.generic.base import TemplateView from authentik.core.views import apps, impersonate from authentik.core.views.debug import AccessDeniedView -from authentik.core.views.interface import FlowInterfaceView +from authentik.core.views.interface import FlowInterfaceView, InterfaceView from authentik.core.views.session import EndSessionView urlpatterns = [ @@ -39,12 +38,12 @@ urlpatterns = [ # Interfaces path( "if/admin/", - ensure_csrf_cookie(TemplateView.as_view(template_name="if/admin.html")), + ensure_csrf_cookie(InterfaceView.as_view(template_name="if/admin.html")), name="if-admin", ), path( "if/user/", - ensure_csrf_cookie(TemplateView.as_view(template_name="if/user.html")), + ensure_csrf_cookie(InterfaceView.as_view(template_name="if/user.html")), name="if-user", ), path( @@ -58,10 +57,10 @@ urlpatterns = [ name="if-session-end", ), # Fallback for WS - path("ws/outpost//", TemplateView.as_view(template_name="if/admin.html")), + path("ws/outpost//", InterfaceView.as_view(template_name="if/admin.html")), path( "ws/client/", - TemplateView.as_view(template_name="if/admin.html"), + InterfaceView.as_view(template_name="if/admin.html"), ), ] diff --git a/authentik/core/views/interface.py b/authentik/core/views/interface.py index d851c79e5..42e6b29b4 100644 --- a/authentik/core/views/interface.py +++ b/authentik/core/views/interface.py @@ -1,13 +1,26 @@ """Interface views""" from typing import Any +from json import dumps from django.shortcuts import get_object_or_404 from django.views.generic.base import TemplateView +from rest_framework.request import Request +from authentik.api.v3.config import ConfigView from authentik.flows.models import Flow +from authentik.tenants.api import CurrentTenantSerializer -class FlowInterfaceView(TemplateView): +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["tenant_json"] = dumps(CurrentTenantSerializer(self.request.tenant).data) + return super().get_context_data(**kwargs) + + +class FlowInterfaceView(InterfaceView): """Flow interface""" template_name = "if/flow.html" diff --git a/web/src/api/Config.ts b/web/src/api/Config.ts index 4e7f6eda6..b61f26228 100644 --- a/web/src/api/Config.ts +++ b/web/src/api/Config.ts @@ -1,4 +1,5 @@ -import { VERSION } from "@goauthentik/web/constants"; +import { globalAK } from "@goauthentik/web/api/Global"; +import { EVENT_REFRESH, VERSION } from "@goauthentik/web/constants"; import { MessageMiddleware } from "@goauthentik/web/elements/messages/Middleware"; import { APIMiddleware } from "@goauthentik/web/elements/notifications/APIDrawer"; import { activateLocale } from "@goauthentik/web/interfaces/locale"; @@ -6,9 +7,11 @@ import { getCookie } from "@goauthentik/web/utils"; import { Config, + ConfigFromJSON, Configuration, CoreApi, CurrentTenant, + CurrentTenantFromJSON, FetchParams, Middleware, RequestContext, @@ -27,7 +30,9 @@ export class LoggingMiddleware implements Middleware { } } -let globalConfigPromise: Promise; +let globalConfigPromise: Promise | undefined = Promise.resolve( + ConfigFromJSON(globalAK()?.config), +); export function config(): Promise { if (!globalConfigPromise) { globalConfigPromise = new RootApi(DEFAULT_CONFIG).rootConfigRetrieve(); @@ -60,7 +65,9 @@ export function tenantSetLocale(tenant: CurrentTenant) { activateLocale(tenant.defaultLocale); } -let globalTenantPromise: Promise; +let globalTenantPromise: Promise | undefined = Promise.resolve( + CurrentTenantFromJSON(globalAK()?.tenant), +); export function tenant(): Promise { if (!globalTenantPromise) { globalTenantPromise = new CoreApi(DEFAULT_CONFIG) @@ -108,4 +115,13 @@ export function AndNext(url: string): string { return `?next=${encodeURIComponent(url)}`; } +window.addEventListener(EVENT_REFRESH, () => { + // Upon global refresh, disregard whatever was pre-hydrated and + // actually load info from API + globalConfigPromise = undefined; + globalTenantPromise = undefined; + config(); + tenant(); +}); + console.debug(`authentik(early): version ${VERSION}, apiBase ${DEFAULT_CONFIG.basePath}`); diff --git a/web/src/api/Global.ts b/web/src/api/Global.ts index bbec287a5..855db7d46 100644 --- a/web/src/api/Global.ts +++ b/web/src/api/Global.ts @@ -1,8 +1,12 @@ +import { Config, CurrentTenant } from "@goauthentik/api"; + export interface GlobalAuthentik { locale?: string; flow?: { layout: string; }; + config: Config; + tenant: CurrentTenant; } export interface AuthentikWindow {