migrate config away from localStorage; ensure config is properly loaded on access token auth

This commit is contained in:
Aine 2024-11-21 23:31:07 +02:00
parent eae00a6ccd
commit cfd8238edc
No known key found for this signature in database
GPG Key ID: 34969C908CCA2804
5 changed files with 68 additions and 112 deletions

View File

@ -2,7 +2,7 @@ import { CheckForApplicationUpdate, AppBar, TitlePortal, InspectorButton, Confir
import { LoginMethod } from "../pages/LoginPage";
import { useEffect, useState, Suspense } from "react";
import { Icons, DefaultIcon } from "./icons";
import { ClearConfig } from "./config";
import { MenuItem, GetConfig, ClearConfig } from "./config";
import Footer from "./Footer";
const AdminUserMenu = () => {
@ -53,18 +53,8 @@ const AdminAppBar = () => {
};
const AdminMenu = (props) => {
const [menu, setMenu] = useState([]);
useEffect(() => {
const menuConfig = localStorage.getItem('menu');
if (menuConfig) {
try {
setMenu(JSON.parse(menuConfig));
} catch (e) {
console.error('Error parsing menu configuration', e);
}
}
}, []);
const [menu, setMenu] = useState([] as MenuItem[]);
useEffect(() => setMenu(GetConfig().menu), []);
return (
<Menu {...props}>
@ -105,4 +95,4 @@ export const AdminLayout = ({ children }) => {
</Layout>
<Footer />
</>
};
};

View File

@ -2,8 +2,7 @@ import storage from "../storage";
export interface Config {
restrictBaseUrl: string | string[];
asManagedUsers: string[];
supportURL: string;
asManagedUsers: RegExp[];
menu: MenuItem[];
}
@ -15,73 +14,71 @@ export interface MenuItem {
export const WellKnownKey = "cc.etke.synapse-admin";
export const LoadConfig = (context: Config): Config => {
if (context.restrictBaseUrl) {
storage.setItem("restrict_base_url", JSON.stringify(context.restrictBaseUrl));
// current configuration
let config: Config = {
restrictBaseUrl: "",
asManagedUsers: [],
menu: [],
};
export const FetchConfig = async () => {
try {
const resp = await fetch("config.json");
const configJSON = await resp.json();
console.log("Loaded config.json", configJSON);
LoadConfig(configJSON);
} catch (e) {
console.error(e);
}
if (context.asManagedUsers) {
storage.setItem("as_managed_users", JSON.stringify(context.asManagedUsers));
// if home_server is set, try to load https://home_server/.well-known/matrix/client
const homeserver = storage.getItem("home_server");
if (homeserver) {
try {
const resp = await fetch(`https://${homeserver}/.well-known/matrix/client`);
const configWK = await resp.json();
if (!configWK[WellKnownKey]) {
console.log(`Loaded https://${homeserver}.well-known/matrix/client, but it doesn't contain ${WellKnownKey} key, skipping`, configWK);
} else {
console.log(`Loaded https://${homeserver}.well-known/matrix/client`, configWK);
LoadConfig(configWK[WellKnownKey]);
}
} catch (e) {
console.log(`https://${homeserver}/.well-known/matrix/client not found, skipping`, e);
}
}
}
// load config from context
export const LoadConfig = (context: any) => {
if (context?.restrictBaseUrl) {
config.restrictBaseUrl = context.restrictBaseUrl as string | string[];
}
if (context?.asManagedUsers) {
config.asManagedUsers = context.asManagedUsers.map((regex: string) => new RegExp(regex));
}
let menu: MenuItem[] = [];
if (context.menu) {
menu = context.menu;
}
if (context.supportURL) {
const migratedSupportURL = {
label: "Contact support",
icon: "SupportAgent",
url: context.supportURL,
};
console.warn("supportURL config option is deprecated. Please, use the menu option instead. Automatically migrated to the new menu option:", migratedSupportURL);
menu.push(migratedSupportURL as MenuItem);
if (context?.menu) {
menu = context.menu as MenuItem[];
}
if (menu.length > 0) {
storage.setItem("menu", JSON.stringify(menu));
config.menu = menu;
}
}
// below we try to calculate "final" config, which will contain values from context and already set values in storage
// because LoadConfig could be called multiple times to get config from different sources
let finalRestrictBaseUrl: string | string[] = "";
try {
finalRestrictBaseUrl = JSON.parse(storage.getItem("restrict_base_url") || "");
if (Array.isArray(finalRestrictBaseUrl) && finalRestrictBaseUrl.length == 1) {
finalRestrictBaseUrl = finalRestrictBaseUrl[0];
}
} catch (e) {}
let finalAsManagedUsers: string[] = [];
try {
finalAsManagedUsers = JSON.parse(storage.getItem("as_managed_users") || "");
} catch (e) {}
let finalMenu: MenuItem[] = [];
try {
finalMenu = JSON.parse(storage.getItem("menu") || "");
} catch (e) {}
return {
restrictBaseUrl: finalRestrictBaseUrl,
asManagedUsers: finalAsManagedUsers,
supportURL: storage.getItem("support_url") || "",
menu: finalMenu,
} as Config;
// get config
export const GetConfig = (): Config => {
return config;
}
// clear config
export const ClearConfig = () => {
// config.json
storage.removeItem("restrict_base_url");
storage.removeItem("as_managed_users");
storage.removeItem("support_url");
storage.removeItem("menu");
config = {} as Config;
// session
storage.removeItem("home_server");
storage.removeItem("base_url");
storage.removeItem("user_id");
storage.removeItem("device_id");
storage.removeItem("access_token");
storage.removeItem("login_type");
storage.clear();
}

View File

@ -1,4 +1,5 @@
import { Identifier } from "ra-core";
import { GetConfig } from "./config";
/**
* Check if a user is managed by an application service
@ -6,11 +7,5 @@ import { Identifier } from "ra-core";
* @returns Whether the user is managed by an application service
*/
export const isASManaged = (id: string | Identifier): boolean => {
const managedUsersString = localStorage.getItem("as_managed_users") || '';
try {
const asManagedUsers = JSON.parse(managedUsersString).map(regex => new RegExp(regex));
return asManagedUsers.some(regex => regex.test(id));
} catch (e) {
return false;
}
return GetConfig().asManagedUsers.some(regex => regex.test(id as string));
};

View File

@ -3,46 +3,15 @@ import React from "react";
import { createRoot } from "react-dom/client";
import App from "./App";
import { Config, WellKnownKey, LoadConfig } from "./components/config";
import { FetchConfig, GetConfig } from "./components/config";
import { AppContext } from "./AppContext";
import storage from "./storage";
// load config.json
let props: Config = {
restrictBaseUrl: [],
asManagedUsers: [],
supportURL: "",
menu: [],
};
try {
const resp = await fetch("config.json");
const configJSON = await resp.json();
console.log("Loaded config.json", configJSON);
props = LoadConfig(configJSON as Config);
} catch (e) {
console.error(e);
}
// if home_server is set, try to load https://home_server/.well-known/matrix/client
const homeserver = storage.getItem("home_server");
if (homeserver) {
try {
const resp = await fetch(`https://${homeserver}/.well-known/matrix/client`);
const configWK = await resp.json();
if (!configWK[WellKnownKey]) {
console.log(`Loaded https://${homeserver}.well-known/matrix/client, but it doesn't contain ${WellKnownKey} key, skipping`, configWK);
} else {
console.log(`Loaded https://${homeserver}.well-known/matrix/client`, configWK);
props = LoadConfig(configWK[WellKnownKey] as Config);
}
} catch (e) {
console.log(`https://${homeserver}/.well-known/matrix/client not found, skipping`, e);
}
}
await FetchConfig();
createRoot(document.getElementById("root")).render(
<React.StrictMode>
<AppContext.Provider value={props}>
<AppContext.Provider value={GetConfig()}>
<App />
</AppContext.Provider>
</React.StrictMode>

View File

@ -3,7 +3,7 @@ import { AuthProvider, HttpError, Options, fetchUtils } from "react-admin";
import storage from "../storage";
import { MatrixError, displayError } from "../components/error";
import { fetchAuthenticatedMedia } from "../utils/fetchMedia";
import { ClearConfig } from "../components/config";
import { FetchConfig, ClearConfig } from "../components/config";
const authProvider: AuthProvider = {
// called when the user attempts to log in
@ -82,6 +82,11 @@ const authProvider: AuthProvider = {
storage.setItem("device_id", json.device_id);
storage.setItem("login_type", accessToken ? "accessToken" : "credentials");
// when doing access token auth, config is not fetched, so we need to do it here
if (accessToken) {
await FetchConfig();
}
return Promise.resolve({redirectTo: "/"});
} catch(err) {
const error = err as HttpError;