migrate config away from localStorage; ensure config is properly loaded on access token auth
This commit is contained in:
parent
eae00a6ccd
commit
cfd8238edc
@ -2,7 +2,7 @@ import { CheckForApplicationUpdate, AppBar, TitlePortal, InspectorButton, Confir
|
|||||||
import { LoginMethod } from "../pages/LoginPage";
|
import { LoginMethod } from "../pages/LoginPage";
|
||||||
import { useEffect, useState, Suspense } from "react";
|
import { useEffect, useState, Suspense } from "react";
|
||||||
import { Icons, DefaultIcon } from "./icons";
|
import { Icons, DefaultIcon } from "./icons";
|
||||||
import { ClearConfig } from "./config";
|
import { MenuItem, GetConfig, ClearConfig } from "./config";
|
||||||
import Footer from "./Footer";
|
import Footer from "./Footer";
|
||||||
|
|
||||||
const AdminUserMenu = () => {
|
const AdminUserMenu = () => {
|
||||||
@ -53,18 +53,8 @@ const AdminAppBar = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const AdminMenu = (props) => {
|
const AdminMenu = (props) => {
|
||||||
const [menu, setMenu] = useState([]);
|
const [menu, setMenu] = useState([] as MenuItem[]);
|
||||||
|
useEffect(() => setMenu(GetConfig().menu), []);
|
||||||
useEffect(() => {
|
|
||||||
const menuConfig = localStorage.getItem('menu');
|
|
||||||
if (menuConfig) {
|
|
||||||
try {
|
|
||||||
setMenu(JSON.parse(menuConfig));
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Error parsing menu configuration', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu {...props}>
|
<Menu {...props}>
|
||||||
@ -105,4 +95,4 @@ export const AdminLayout = ({ children }) => {
|
|||||||
</Layout>
|
</Layout>
|
||||||
<Footer />
|
<Footer />
|
||||||
</>
|
</>
|
||||||
};
|
};
|
||||||
|
@ -2,8 +2,7 @@ import storage from "../storage";
|
|||||||
|
|
||||||
export interface Config {
|
export interface Config {
|
||||||
restrictBaseUrl: string | string[];
|
restrictBaseUrl: string | string[];
|
||||||
asManagedUsers: string[];
|
asManagedUsers: RegExp[];
|
||||||
supportURL: string;
|
|
||||||
menu: MenuItem[];
|
menu: MenuItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -15,73 +14,71 @@ export interface MenuItem {
|
|||||||
|
|
||||||
export const WellKnownKey = "cc.etke.synapse-admin";
|
export const WellKnownKey = "cc.etke.synapse-admin";
|
||||||
|
|
||||||
export const LoadConfig = (context: Config): Config => {
|
// current configuration
|
||||||
if (context.restrictBaseUrl) {
|
let config: Config = {
|
||||||
storage.setItem("restrict_base_url", JSON.stringify(context.restrictBaseUrl));
|
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) {
|
// if home_server is set, try to load https://home_server/.well-known/matrix/client
|
||||||
storage.setItem("as_managed_users", JSON.stringify(context.asManagedUsers));
|
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[] = [];
|
let menu: MenuItem[] = [];
|
||||||
if (context.menu) {
|
if (context?.menu) {
|
||||||
menu = context.menu;
|
menu = context.menu as MenuItem[];
|
||||||
}
|
|
||||||
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 (menu.length > 0) {
|
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
|
// get config
|
||||||
// because LoadConfig could be called multiple times to get config from different sources
|
export const GetConfig = (): Config => {
|
||||||
let finalRestrictBaseUrl: string | string[] = "";
|
return config;
|
||||||
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;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// clear config
|
||||||
export const ClearConfig = () => {
|
export const ClearConfig = () => {
|
||||||
// config.json
|
// config.json
|
||||||
storage.removeItem("restrict_base_url");
|
config = {} as Config;
|
||||||
storage.removeItem("as_managed_users");
|
|
||||||
storage.removeItem("support_url");
|
|
||||||
storage.removeItem("menu");
|
|
||||||
|
|
||||||
// session
|
// session
|
||||||
storage.removeItem("home_server");
|
storage.clear();
|
||||||
storage.removeItem("base_url");
|
|
||||||
storage.removeItem("user_id");
|
|
||||||
storage.removeItem("device_id");
|
|
||||||
storage.removeItem("access_token");
|
|
||||||
storage.removeItem("login_type");
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Identifier } from "ra-core";
|
import { Identifier } from "ra-core";
|
||||||
|
import { GetConfig } from "./config";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a user is managed by an application service
|
* 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
|
* @returns Whether the user is managed by an application service
|
||||||
*/
|
*/
|
||||||
export const isASManaged = (id: string | Identifier): boolean => {
|
export const isASManaged = (id: string | Identifier): boolean => {
|
||||||
const managedUsersString = localStorage.getItem("as_managed_users") || '';
|
return GetConfig().asManagedUsers.some(regex => regex.test(id as string));
|
||||||
try {
|
|
||||||
const asManagedUsers = JSON.parse(managedUsersString).map(regex => new RegExp(regex));
|
|
||||||
return asManagedUsers.some(regex => regex.test(id));
|
|
||||||
} catch (e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
@ -3,46 +3,15 @@ import React from "react";
|
|||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
|
|
||||||
import App from "./App";
|
import App from "./App";
|
||||||
import { Config, WellKnownKey, LoadConfig } from "./components/config";
|
import { FetchConfig, GetConfig } from "./components/config";
|
||||||
import { AppContext } from "./AppContext";
|
import { AppContext } from "./AppContext";
|
||||||
import storage from "./storage";
|
import storage from "./storage";
|
||||||
|
|
||||||
// load config.json
|
await FetchConfig();
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
createRoot(document.getElementById("root")).render(
|
createRoot(document.getElementById("root")).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<AppContext.Provider value={props}>
|
<AppContext.Provider value={GetConfig()}>
|
||||||
<App />
|
<App />
|
||||||
</AppContext.Provider>
|
</AppContext.Provider>
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
|
@ -3,7 +3,7 @@ import { AuthProvider, HttpError, Options, fetchUtils } from "react-admin";
|
|||||||
import storage from "../storage";
|
import storage from "../storage";
|
||||||
import { MatrixError, displayError } from "../components/error";
|
import { MatrixError, displayError } from "../components/error";
|
||||||
import { fetchAuthenticatedMedia } from "../utils/fetchMedia";
|
import { fetchAuthenticatedMedia } from "../utils/fetchMedia";
|
||||||
import { ClearConfig } from "../components/config";
|
import { FetchConfig, ClearConfig } from "../components/config";
|
||||||
|
|
||||||
const authProvider: AuthProvider = {
|
const authProvider: AuthProvider = {
|
||||||
// called when the user attempts to log in
|
// called when the user attempts to log in
|
||||||
@ -82,6 +82,11 @@ const authProvider: AuthProvider = {
|
|||||||
storage.setItem("device_id", json.device_id);
|
storage.setItem("device_id", json.device_id);
|
||||||
storage.setItem("login_type", accessToken ? "accessToken" : "credentials");
|
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: "/"});
|
return Promise.resolve({redirectTo: "/"});
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
const error = err as HttpError;
|
const error = err as HttpError;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user