diff --git a/backend/global_settings/urls.py b/backend/global_settings/urls.py index 71a7515a6..9cf8da8f2 100644 --- a/backend/global_settings/urls.py +++ b/backend/global_settings/urls.py @@ -3,7 +3,12 @@ from iam.sso.views import SSOSettingsViewSet -from .views import GlobalSettingsViewSet, get_sso_info, update_general_settings +from .views import ( + GlobalSettingsViewSet, + get_sso_info, + update_general_settings, + get_general_settings, +) from .routers import DefaultSettingsRouter @@ -21,6 +26,7 @@ # This route should ideally be placed under the routes of the routers, but the DefaultRouter usage overwrite the route and makes it inaccessible. # Could we use DefaultSettingsRouter to register the "global" route to fix that ? path(r"general/update/", update_general_settings, name="update_general_settings"), + path(r"general/info/", get_general_settings, name="get_general_settings"), path(r"", include(router.urls)), path(r"", include(settings_router.urls)), path(r"sso/info/", get_sso_info, name="get_sso_info"), diff --git a/backend/global_settings/views.py b/backend/global_settings/views.py index e6645a937..adedb51c4 100644 --- a/backend/global_settings/views.py +++ b/backend/global_settings/views.py @@ -37,6 +37,25 @@ def update(self, request, *args, **kwargs): UPDATABLE_GENERAL_SETTINGS = frozenset( ["lang"] ) # This represents the list of "general" GlobalSettings an admin has the right to change. +PUBLIC_GENERAL_SETTINGS = [ + "lang" +] # List of general settings accessible by anyone (non-sensitive general settings). + + +@api_view(["GET"]) +@permission_classes([permissions.AllowAny]) +def get_general_settings(request): + """ + API endpoint to get the general settings. + """ + general_settings = GlobalSettings.objects.filter(name="general").first() + if general_settings is None: + return {} + + public_settings = { + key: general_settings.value.get(key) for key in PUBLIC_GENERAL_SETTINGS + } + return Response(public_settings) @api_view(["PATCH"]) diff --git a/frontend/src/hooks.server.ts b/frontend/src/hooks.server.ts index a63710bcf..169adb675 100644 --- a/frontend/src/hooks.server.ts +++ b/frontend/src/hooks.server.ts @@ -7,6 +7,8 @@ import { languageTag, setLanguageTag } from '$paraglide/runtime'; import { loadFeatureFlags } from '$lib/feature-flags'; +let generalSettings = {}; + async function ensureCsrfToken(event: RequestEvent): Promise { let csrfToken = event.cookies.get('csrftoken') || ''; if (!csrfToken) { @@ -28,23 +30,43 @@ async function ensureCsrfToken(event: RequestEvent): Promise { async function validateUserSession(event: RequestEvent): Promise { const token = event.cookies.get('token'); - if (!token) return null; + const requestList = [fetch(`${BASE_API_URL}/settings/general/info/`)]; + if (token) { + requestList.push( + fetch(`${BASE_API_URL}/iam/current-user/`, { + credentials: 'include', + headers: { + 'content-type': 'application/json', + Authorization: `Token ${token}` + } + }) + ); + } - const res = await fetch(`${BASE_API_URL}/iam/current-user/`, { - credentials: 'include', - headers: { - 'content-type': 'application/json', - Authorization: `Token ${token}` - } - }); + const responseList = await Promise.all(requestList); + const settingsRes = responseList[0]; + const newGeneralSettings = await settingsRes.json(); + generalSettings = newGeneralSettings; + + if (!event.cookies.get('ciso_lang')) { + event.cookies.set('ciso_lang', generalSettings.lang || 'en', { + httpOnly: false, + sameSite: 'lax', + path: '/', + secure: true + }); + } + + if (!token) return null; - if (!res.ok) { + const userRes = responseList[1]; + if (!userRes.ok) { event.cookies.delete('token', { path: '/' }); redirect(302, `/login?next=${event.url.pathname}`); } - return res.json(); + return userRes.json(); } export const handle: Handle = async ({ event, resolve }) => { @@ -56,7 +78,7 @@ export const handle: Handle = async ({ event, resolve }) => { const errorId = new URL(event.request.url).searchParams.get('error'); if (errorId) { - setLanguageTag(event.cookies.get('ciso_lang') || 'en'); + setLanguageTag(event.cookies.get('ciso_lang') || generalSettings.lang || 'en'); setFlash({ type: 'error', message: safeTranslate(errorId) }, event); redirect(302, '/login'); } diff --git a/frontend/src/lib/components/Forms/ModelForm/GeneralSettingForm.svelte b/frontend/src/lib/components/Forms/ModelForm/GeneralSettingForm.svelte index d90bcfd72..48f37f81f 100644 --- a/frontend/src/lib/components/Forms/ModelForm/GeneralSettingForm.svelte +++ b/frontend/src/lib/components/Forms/ModelForm/GeneralSettingForm.svelte @@ -2,9 +2,10 @@ import Select from '../Select.svelte'; import type { SuperValidated } from 'sveltekit-superforms'; import type { ModelInfo, CacheLock } from '$lib/utils/types'; - import { availableLanguageTags, languageTag, setLanguageTag } from '$paraglide/runtime'; + import { availableLanguageTags } from '$paraglide/runtime'; import { LOCALE_DISPLAY_MAP } from '$lib/utils/constants'; import * as m from '$paraglide/messages.js'; + export let form: SuperValidated; export let model: ModelInfo; export let cacheLocks: Record = {}; diff --git a/frontend/src/lib/components/Forms/Select.svelte b/frontend/src/lib/components/Forms/Select.svelte index 412381c3a..d376016d8 100644 --- a/frontend/src/lib/components/Forms/Select.svelte +++ b/frontend/src/lib/components/Forms/Select.svelte @@ -76,7 +76,6 @@ {@const defaultValue = blank ? '' : null} {/if} - {JSON.stringify(options)} {#each options as option}