tenants: add default_locale read only field, pre-hydrate in flows and read in autodetect as first choice
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
3344af72c2
commit
0a73e7ac9f
|
@ -24,7 +24,7 @@
|
||||||
"*.akflow": "json"
|
"*.akflow": "json"
|
||||||
},
|
},
|
||||||
"typescript.preferences.importModuleSpecifier": "non-relative",
|
"typescript.preferences.importModuleSpecifier": "non-relative",
|
||||||
"typescript.preferences.importModuleSpecifierEnding": "js",
|
"typescript.preferences.importModuleSpecifierEnding": "index",
|
||||||
"typescript.tsdk": "./web/node_modules/typescript/lib",
|
"typescript.tsdk": "./web/node_modules/typescript/lib",
|
||||||
"typescript.enablePromptUseWorkspaceTsdk": true
|
"typescript.enablePromptUseWorkspaceTsdk": true
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,9 @@
|
||||||
<script>ShadyDOM = { force: !navigator.webdriver };</script>
|
<script>ShadyDOM = { force: !navigator.webdriver };</script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<script>
|
<script>
|
||||||
window.authentik = {};
|
window.authentik = {
|
||||||
|
"locale": "{{ tenant.default_locale }}",
|
||||||
|
};
|
||||||
window.authentik.flow = {
|
window.authentik.flow = {
|
||||||
"layout": "{{ flow.layout }}",
|
"layout": "{{ flow.layout }}",
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,7 +4,7 @@ from typing import Any
|
||||||
from drf_spectacular.utils import extend_schema
|
from drf_spectacular.utils import extend_schema
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
from rest_framework.fields import CharField, ListField
|
from rest_framework.fields import CharField, ListField, ReadOnlyField
|
||||||
from rest_framework.permissions import AllowAny
|
from rest_framework.permissions import AllowAny
|
||||||
from rest_framework.request import Request
|
from rest_framework.request import Request
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
@ -76,6 +76,8 @@ class CurrentTenantSerializer(PassiveSerializer):
|
||||||
flow_unenrollment = CharField(source="flow_unenrollment.slug", required=False)
|
flow_unenrollment = CharField(source="flow_unenrollment.slug", required=False)
|
||||||
flow_user_settings = CharField(source="flow_user_settings.slug", required=False)
|
flow_user_settings = CharField(source="flow_user_settings.slug", required=False)
|
||||||
|
|
||||||
|
default_locale = ReadOnlyField()
|
||||||
|
|
||||||
|
|
||||||
class TenantViewSet(UsedByMixin, ModelViewSet):
|
class TenantViewSet(UsedByMixin, ModelViewSet):
|
||||||
"""Tenant Viewset"""
|
"""Tenant Viewset"""
|
||||||
|
|
|
@ -65,6 +65,11 @@ class Tenant(models.Model):
|
||||||
|
|
||||||
attributes = models.JSONField(default=dict, blank=True)
|
attributes = models.JSONField(default=dict, blank=True)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def default_locale(self) -> str:
|
||||||
|
"""Get default locale"""
|
||||||
|
return self.attributes.get("settings", {}).get("locale", "")
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
if self.default:
|
if self.default:
|
||||||
return "Default tenant"
|
return "Default tenant"
|
||||||
|
|
|
@ -20658,10 +20658,14 @@ components:
|
||||||
type: string
|
type: string
|
||||||
flow_user_settings:
|
flow_user_settings:
|
||||||
type: string
|
type: string
|
||||||
|
default_locale:
|
||||||
|
type: string
|
||||||
|
readOnly: true
|
||||||
required:
|
required:
|
||||||
- branding_favicon
|
- branding_favicon
|
||||||
- branding_logo
|
- branding_logo
|
||||||
- branding_title
|
- branding_title
|
||||||
|
- default_locale
|
||||||
- matched_domain
|
- matched_domain
|
||||||
- ui_footer_links
|
- ui_footer_links
|
||||||
DeniedActionEnum:
|
DeniedActionEnum:
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { VERSION } from "@goauthentik/web/constants";
|
import { VERSION } from "@goauthentik/web/constants";
|
||||||
import { MessageMiddleware } from "@goauthentik/web/elements/messages/Middleware";
|
import { MessageMiddleware } from "@goauthentik/web/elements/messages/Middleware";
|
||||||
import { APIMiddleware } from "@goauthentik/web/elements/notifications/APIDrawer";
|
import { APIMiddleware } from "@goauthentik/web/elements/notifications/APIDrawer";
|
||||||
|
import { activateLocale } from "@goauthentik/web/interfaces/locale";
|
||||||
import { getCookie } from "@goauthentik/web/utils";
|
import { getCookie } from "@goauthentik/web/utils";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -34,28 +35,39 @@ export function config(): Promise<Config> {
|
||||||
return globalConfigPromise;
|
return globalConfigPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function tenantSetFavicon(tenant: CurrentTenant) {
|
||||||
|
/**
|
||||||
|
* <link rel="icon" href="/static/dist/assets/icons/icon.png">
|
||||||
|
* <link rel="shortcut icon" href="/static/dist/assets/icons/icon.png">
|
||||||
|
*/
|
||||||
|
const rels = ["icon", "shortcut icon"];
|
||||||
|
rels.forEach((rel) => {
|
||||||
|
let relIcon = document.head.querySelector<HTMLLinkElement>(`link[rel='${rel}']`);
|
||||||
|
if (!relIcon) {
|
||||||
|
relIcon = document.createElement("link");
|
||||||
|
relIcon.rel = rel;
|
||||||
|
document.getElementsByTagName("head")[0].appendChild(relIcon);
|
||||||
|
}
|
||||||
|
relIcon.href = tenant.brandingFavicon;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function tenantSetLocale(tenant: CurrentTenant) {
|
||||||
|
if (tenant.defaultLocale === "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.debug("authentik/locale: setting locale from tenant default");
|
||||||
|
activateLocale(tenant.defaultLocale);
|
||||||
|
}
|
||||||
|
|
||||||
let globalTenantPromise: Promise<CurrentTenant>;
|
let globalTenantPromise: Promise<CurrentTenant>;
|
||||||
export function tenant(): Promise<CurrentTenant> {
|
export function tenant(): Promise<CurrentTenant> {
|
||||||
if (!globalTenantPromise) {
|
if (!globalTenantPromise) {
|
||||||
globalTenantPromise = new CoreApi(DEFAULT_CONFIG)
|
globalTenantPromise = new CoreApi(DEFAULT_CONFIG)
|
||||||
.coreTenantsCurrentRetrieve()
|
.coreTenantsCurrentRetrieve()
|
||||||
.then((tenant) => {
|
.then((tenant) => {
|
||||||
/**
|
tenantSetFavicon(tenant);
|
||||||
* <link rel="icon" href="/static/dist/assets/icons/icon.png">
|
tenantSetLocale(tenant);
|
||||||
* <link rel="shortcut icon" href="/static/dist/assets/icons/icon.png">
|
|
||||||
*/
|
|
||||||
const rels = ["icon", "shortcut icon"];
|
|
||||||
rels.forEach((rel) => {
|
|
||||||
let relIcon = document.head.querySelector<HTMLLinkElement>(
|
|
||||||
`link[rel='${rel}']`,
|
|
||||||
);
|
|
||||||
if (!relIcon) {
|
|
||||||
relIcon = document.createElement("link");
|
|
||||||
relIcon.rel = rel;
|
|
||||||
document.getElementsByTagName("head")[0].appendChild(relIcon);
|
|
||||||
}
|
|
||||||
relIcon.href = tenant.brandingFavicon;
|
|
||||||
});
|
|
||||||
return tenant;
|
return tenant;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
export interface GlobalAuthentik {
|
||||||
|
locale?: string;
|
||||||
|
flow?: {
|
||||||
|
layout: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AuthentikWindow {
|
||||||
|
authentik: GlobalAuthentik;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function globalAK(): GlobalAuthentik {
|
||||||
|
return (window as unknown as AuthentikWindow).authentik;
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
import { DEFAULT_CONFIG, tenant } from "@goauthentik/web/api/Config";
|
import { DEFAULT_CONFIG, tenant } from "@goauthentik/web/api/Config";
|
||||||
|
import { globalAK } from "@goauthentik/web/api/Global";
|
||||||
import { configureSentry } from "@goauthentik/web/api/Sentry";
|
import { configureSentry } from "@goauthentik/web/api/Sentry";
|
||||||
import { WebsocketClient } from "@goauthentik/web/common/ws";
|
import { WebsocketClient } from "@goauthentik/web/common/ws";
|
||||||
import { EVENT_FLOW_ADVANCE, TITLE_DEFAULT } from "@goauthentik/web/constants";
|
import { EVENT_FLOW_ADVANCE, TITLE_DEFAULT } from "@goauthentik/web/constants";
|
||||||
|
@ -46,14 +47,6 @@ import {
|
||||||
|
|
||||||
import { StageHost } from "./stages/base";
|
import { StageHost } from "./stages/base";
|
||||||
|
|
||||||
export interface FlowWindow extends Window {
|
|
||||||
authentik: {
|
|
||||||
flow: {
|
|
||||||
layout: LayoutEnum;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@customElement("ak-flow-executor")
|
@customElement("ak-flow-executor")
|
||||||
export class FlowExecutor extends LitElement implements StageHost {
|
export class FlowExecutor extends LitElement implements StageHost {
|
||||||
flowSlug?: string;
|
flowSlug?: string;
|
||||||
|
@ -435,7 +428,7 @@ export class FlowExecutor extends LitElement implements StageHost {
|
||||||
}
|
}
|
||||||
|
|
||||||
getLayout(): string {
|
getLayout(): string {
|
||||||
const prefilledFlow = (window as unknown as FlowWindow).authentik.flow.layout;
|
const prefilledFlow = globalAK().flow?.layout || LayoutEnum.Stacked;
|
||||||
if (this.challenge) {
|
if (this.challenge) {
|
||||||
return this.challenge?.flowInfo?.layout || prefilledFlow;
|
return this.challenge?.flowInfo?.layout || prefilledFlow;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { globalAK } from "@goauthentik/web/api/Global";
|
||||||
|
|
||||||
import { Messages, i18n } from "@lingui/core";
|
import { Messages, i18n } from "@lingui/core";
|
||||||
import { detect, fromNavigator, fromUrl } from "@lingui/detect-locale";
|
import { detect, fromNavigator, fromUrl } from "@lingui/detect-locale";
|
||||||
import { t } from "@lingui/macro";
|
import { t } from "@lingui/macro";
|
||||||
|
@ -121,7 +123,14 @@ const DEFAULT_FALLBACK = () => "en";
|
||||||
|
|
||||||
export function autoDetectLanguage() {
|
export function autoDetectLanguage() {
|
||||||
const detected =
|
const detected =
|
||||||
detect(fromUrl("locale"), fromNavigator(), DEFAULT_FALLBACK) || DEFAULT_FALLBACK();
|
detect(
|
||||||
|
() => {
|
||||||
|
return globalAK().locale;
|
||||||
|
},
|
||||||
|
fromUrl("locale"),
|
||||||
|
fromNavigator(),
|
||||||
|
DEFAULT_FALLBACK,
|
||||||
|
) || DEFAULT_FALLBACK();
|
||||||
const locales = [detected];
|
const locales = [detected];
|
||||||
// For now we only care about the first locale part
|
// For now we only care about the first locale part
|
||||||
if (detected.includes("_")) {
|
if (detected.includes("_")) {
|
||||||
|
|
Reference in New Issue