core: pre-hydrate config into templates to directly load correct assets

closes #3228

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2022-07-29 00:02:48 +02:00
parent d4af47f576
commit cada292e00
8 changed files with 66 additions and 18 deletions

View File

@ -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)

View File

@ -7,6 +7,12 @@
<script src="{% static 'dist/admin/AdminInterface.js' %}" type="module"></script>
<meta name="theme-color" content="#18191a" media="(prefers-color-scheme: dark)">
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
<script>
window.authentik = {};
window.authentik.locale = "{{ tenant.default_locale }}";
window.authentik.config = JSON.parse('{{ config_json|safe }}');
window.authentik.tenant = JSON.parse('{{ tenant_json|safe }}');
</script>
{% endblock %}
{% block body %}

View File

@ -10,9 +10,10 @@
<script>ShadyDOM = { force: !navigator.webdriver };</script>
{% endif %}
<script>
window.authentik = {
"locale": "{{ tenant.default_locale }}",
};
window.authentik = {};
window.authentik.locale = "{{ tenant.default_locale }}";
window.authentik.config = JSON.parse( '{{ config_json|safe }}');
window.authentik.tenant = JSON.parse('{{ tenant_json|safe }}');
window.authentik.flow = {
"layout": "{{ flow.layout }}",
};

View File

@ -7,6 +7,12 @@
<script src="{% static 'dist/user/UserInterface.js' %}" type="module"></script>
<meta name="theme-color" content="#151515" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#151515" media="(prefers-color-scheme: dark)">
<script>
window.authentik = {};
window.authentik.locale = "{{ tenant.default_locale }}";
window.authentik.config = JSON.parse('{{ config_json|safe }}');
window.authentik.tenant = JSON.parse('{{ tenant_json|safe }}');
</script>
{% endblock %}
{% block body %}

View File

@ -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/<uuid:pk>/", TemplateView.as_view(template_name="if/admin.html")),
path("ws/outpost/<uuid:pk>/", 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"),
),
]

View File

@ -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"

View File

@ -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<Config>;
let globalConfigPromise: Promise<Config> | undefined = Promise.resolve(
ConfigFromJSON(globalAK()?.config),
);
export function config(): Promise<Config> {
if (!globalConfigPromise) {
globalConfigPromise = new RootApi(DEFAULT_CONFIG).rootConfigRetrieve();
@ -60,7 +65,9 @@ export function tenantSetLocale(tenant: CurrentTenant) {
activateLocale(tenant.defaultLocale);
}
let globalTenantPromise: Promise<CurrentTenant>;
let globalTenantPromise: Promise<CurrentTenant> | undefined = Promise.resolve(
CurrentTenantFromJSON(globalAK()?.tenant),
);
export function tenant(): Promise<CurrentTenant> {
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}`);

View File

@ -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 {