From dbcb4f92dc1ef88cd2af3b956fbf242fa9c48093 Mon Sep 17 00:00:00 2001 From: Manuel Stahl Date: Fri, 16 Aug 2024 20:58:26 +0200 Subject: [PATCH] Use central defintion of storage system Change-Id: Ibf31c650b08920bf82827607c3421556ac90ae61 --- src/components/media.tsx | 3 ++- src/pages/LoginPage.tsx | 9 ++++---- src/resources/users.tsx | 4 ++-- src/storage.ts | 3 +++ src/synapse/authProvider.test.ts | 29 ++++++++++++------------ src/synapse/authProvider.ts | 22 +++++++++--------- src/synapse/dataProvider.test.ts | 5 +++-- src/synapse/dataProvider.ts | 38 +++++++++++++++++--------------- src/synapse/synapse.ts | 6 +++-- 9 files changed, 66 insertions(+), 53 deletions(-) create mode 100644 src/storage.ts diff --git a/src/components/media.tsx b/src/components/media.tsx index 893b1af7..772d2348 100644 --- a/src/components/media.tsx +++ b/src/components/media.tsx @@ -34,6 +34,7 @@ import { Link } from "react-router-dom"; import { dateParser } from "./date"; import { DeleteMediaParams, SynapseDataProvider } from "../synapse/dataProvider"; import { getMediaUrl } from "../synapse/synapse"; +import storage from "../storage"; const DeleteMediaDialog = ({ open, onClose, onSubmit }) => { const translate = useTranslate(); @@ -339,7 +340,7 @@ export const ViewMediaButton = ({ media_id, label }) => { }; export const MediaIDField = ({ source }) => { - const homeserver = localStorage.getItem("home_server"); + const homeserver = storage.getItem("home_server"); const record = useRecordContext(); if (!record) return null; diff --git a/src/pages/LoginPage.tsx b/src/pages/LoginPage.tsx index dfb93714..04cba250 100644 --- a/src/pages/LoginPage.tsx +++ b/src/pages/LoginPage.tsx @@ -27,6 +27,7 @@ import { isValidBaseUrl, splitMxid, } from "../synapse/synapse"; +import storage from "../storage"; const FormBox = styled(Box)(({ theme }) => ({ display: "flex", @@ -94,7 +95,7 @@ const LoginPage = () => { const [locale, setLocale] = useLocaleState(); const locales = useLocales(); const translate = useTranslate(); - const base_url = allowSingleBaseUrl ? restrictBaseUrl : localStorage.getItem("base_url"); + const base_url = allowSingleBaseUrl ? restrictBaseUrl : storage.getItem("base_url"); const [ssoBaseUrl, setSSOBaseUrl] = useState(""); const loginToken = /\?loginToken=([a-zA-Z0-9_-]+)/.exec(window.location.href); @@ -103,8 +104,8 @@ const LoginPage = () => { console.log("SSO token is", ssoToken); // Prevent further requests window.history.replaceState({}, "", window.location.href.replace(loginToken[0], "#").split("#")[0]); - const baseUrl = localStorage.getItem("sso_base_url"); - localStorage.removeItem("sso_base_url"); + const baseUrl = storage.getItem("sso_base_url"); + storage.removeItem("sso_base_url"); if (baseUrl) { const auth = { base_url: baseUrl, @@ -154,7 +155,7 @@ const LoginPage = () => { }; const handleSSO = () => { - localStorage.setItem("sso_base_url", ssoBaseUrl); + storage.setItem("sso_base_url", ssoBaseUrl); const ssoFullUrl = `${ssoBaseUrl}/_matrix/client/r0/login/sso/redirect?redirectUrl=${encodeURIComponent( window.location.href )}`; diff --git a/src/resources/users.tsx b/src/resources/users.tsx index 10cf9b9e..2e33df44 100644 --- a/src/resources/users.tsx +++ b/src/resources/users.tsx @@ -128,8 +128,8 @@ export const UserList = (props: ListProps) => ( // https://matrix.org/docs/spec/appendices#user-identifiers // here only local part of user_id -// maxLength = 255 - "@" - ":" - localStorage.getItem("home_server").length -// localStorage.getItem("home_server").length is not valid here +// maxLength = 255 - "@" - ":" - storage.getItem("home_server").length +// storage.getItem("home_server").length is not valid here const validateUser = [required(), maxLength(253), regex(/^[a-z0-9._=\-/]+$/, "synapseadmin.users.invalid_user_id")]; const validateAddress = [required(), maxLength(255)]; diff --git a/src/storage.ts b/src/storage.ts new file mode 100644 index 00000000..a6f1adf3 --- /dev/null +++ b/src/storage.ts @@ -0,0 +1,3 @@ +const storage = localStorage; + +export default storage; diff --git a/src/synapse/authProvider.test.ts b/src/synapse/authProvider.test.ts index 45335fc9..bf5103b7 100644 --- a/src/synapse/authProvider.test.ts +++ b/src/synapse/authProvider.test.ts @@ -1,13 +1,14 @@ import fetchMock from "jest-fetch-mock"; import authProvider from "./authProvider"; +import storage from "../storage"; fetchMock.enableMocks(); describe("authProvider", () => { beforeEach(() => { fetchMock.resetMocks(); - localStorage.clear(); + storage.clear(); }); describe("login", () => { @@ -36,10 +37,10 @@ describe("authProvider", () => { }), method: "POST", }); - expect(localStorage.getItem("base_url")).toEqual("http://example.com"); - expect(localStorage.getItem("user_id")).toEqual("@user:example.com"); - expect(localStorage.getItem("access_token")).toEqual("foobar"); - expect(localStorage.getItem("device_id")).toEqual("some_device"); + expect(storage.getItem("base_url")).toEqual("http://example.com"); + expect(storage.getItem("user_id")).toEqual("@user:example.com"); + expect(storage.getItem("access_token")).toEqual("foobar"); + expect(storage.getItem("device_id")).toEqual("some_device"); }); }); @@ -67,16 +68,16 @@ describe("authProvider", () => { }), method: "POST", }); - expect(localStorage.getItem("base_url")).toEqual("https://example.com"); - expect(localStorage.getItem("user_id")).toEqual("@user:example.com"); - expect(localStorage.getItem("access_token")).toEqual("foobar"); - expect(localStorage.getItem("device_id")).toEqual("some_device"); + expect(storage.getItem("base_url")).toEqual("https://example.com"); + expect(storage.getItem("user_id")).toEqual("@user:example.com"); + expect(storage.getItem("access_token")).toEqual("foobar"); + expect(storage.getItem("device_id")).toEqual("some_device"); }); describe("logout", () => { - it("should remove the access_token from localStorage", async () => { - localStorage.setItem("base_url", "example.com"); - localStorage.setItem("access_token", "foo"); + it("should remove the access_token from storage", async () => { + storage.setItem("base_url", "example.com"); + storage.setItem("access_token", "foo"); fetchMock.mockResponse(JSON.stringify({})); await authProvider.logout(null); @@ -89,7 +90,7 @@ describe("authProvider", () => { method: "POST", user: { authenticated: true, token: "Bearer foo" }, }); - expect(localStorage.getItem("access_token")).toBeNull(); + expect(storage.getItem("access_token")).toBeNull(); }); }); @@ -113,7 +114,7 @@ describe("authProvider", () => { }); it("should resolve when logged in", async () => { - localStorage.setItem("access_token", "foobar"); + storage.setItem("access_token", "foobar"); await expect(authProvider.checkAuth({})).resolves.toBeUndefined(); }); diff --git a/src/synapse/authProvider.ts b/src/synapse/authProvider.ts index d84fab1a..5bc7d876 100644 --- a/src/synapse/authProvider.ts +++ b/src/synapse/authProvider.ts @@ -1,5 +1,7 @@ import { AuthProvider, Options, fetchUtils } from "react-admin"; +import storage from "../storage"; + const authProvider: AuthProvider = { // called when the user attempts to log in login: async ({ @@ -19,7 +21,7 @@ const authProvider: AuthProvider = { body: JSON.stringify( Object.assign( { - device_id: localStorage.getItem("device_id"), + device_id: storage.getItem("device_id"), initial_device_display_name: "Synapse Admin", }, loginToken @@ -40,23 +42,23 @@ const authProvider: AuthProvider = { // server, since the admin might want to access the admin API via some // private address base_url = base_url.replace(/\/+$/g, ""); - localStorage.setItem("base_url", base_url); + storage.setItem("base_url", base_url); const decoded_base_url = window.decodeURIComponent(base_url); const login_api_url = decoded_base_url + "/_matrix/client/r0/login"; const { json } = await fetchUtils.fetchJson(login_api_url, options); - localStorage.setItem("home_server", json.home_server); - localStorage.setItem("user_id", json.user_id); - localStorage.setItem("access_token", json.access_token); - localStorage.setItem("device_id", json.device_id); + storage.setItem("home_server", json.home_server); + storage.setItem("user_id", json.user_id); + storage.setItem("access_token", json.access_token); + storage.setItem("device_id", json.device_id); }, // called when the user clicks on the logout button logout: async () => { console.log("logout"); - const logout_api_url = localStorage.getItem("base_url") + "/_matrix/client/r0/logout"; - const access_token = localStorage.getItem("access_token"); + const logout_api_url = storage.getItem("base_url") + "/_matrix/client/r0/logout"; + const access_token = storage.getItem("access_token"); const options: Options = { method: "POST", @@ -68,7 +70,7 @@ const authProvider: AuthProvider = { if (typeof access_token === "string") { await fetchUtils.fetchJson(logout_api_url, options); - localStorage.removeItem("access_token"); + storage.removeItem("access_token"); } }, // called when the API returns an error @@ -81,7 +83,7 @@ const authProvider: AuthProvider = { }, // called when the user navigates to a new location, to check for authentication checkAuth: () => { - const access_token = localStorage.getItem("access_token"); + const access_token = storage.getItem("access_token"); console.log("checkAuth " + access_token); return typeof access_token === "string" ? Promise.resolve() : Promise.reject(); }, diff --git a/src/synapse/dataProvider.test.ts b/src/synapse/dataProvider.test.ts index 64b38db6..a5786746 100644 --- a/src/synapse/dataProvider.test.ts +++ b/src/synapse/dataProvider.test.ts @@ -1,6 +1,7 @@ import fetchMock from "jest-fetch-mock"; import dataProvider from "./dataProvider"; +import storage from "../storage"; fetchMock.enableMocks(); @@ -9,8 +10,8 @@ beforeEach(() => { }); describe("dataProvider", () => { - localStorage.setItem("base_url", "http://localhost"); - localStorage.setItem("access_token", "access_token"); + storage.setItem("base_url", "http://localhost"); + storage.setItem("access_token", "access_token"); it("fetches all users", async () => { fetchMock.mockResponseOnce( diff --git a/src/synapse/dataProvider.ts b/src/synapse/dataProvider.ts index 4bf7450a..d74b206f 100644 --- a/src/synapse/dataProvider.ts +++ b/src/synapse/dataProvider.ts @@ -2,9 +2,11 @@ import { stringify } from "query-string"; import { DataProvider, DeleteParams, Identifier, Options, RaRecord, fetchUtils } from "react-admin"; +import storage from "../storage"; + // Adds the access token to all requests const jsonClient = (url: string, options: Options = {}) => { - const token = localStorage.getItem("access_token"); + const token = storage.getItem("access_token"); console.log("httpClient " + url); if (token != null) { options.user = { @@ -16,7 +18,7 @@ const jsonClient = (url: string, options: Options = {}) => { }; const mxcUrlToHttp = (mxcUrl: string) => { - const homeserver = localStorage.getItem("base_url"); + const homeserver = storage.getItem("base_url"); const re = /^mxc:\/\/([^/]+)\/(\w+)/; const ret = re.exec(mxcUrl); console.log("mxcClient " + ret); @@ -232,7 +234,7 @@ const resourceMap = { data: "users", total: json => json.total, create: (data: RaRecord) => ({ - endpoint: `/_synapse/admin/v2/users/@${encodeURIComponent(data.id)}:${localStorage.getItem("home_server")}`, + endpoint: `/_synapse/admin/v2/users/@${encodeURIComponent(data.id)}:${storage.getItem("home_server")}`, body: data, method: "PUT", }), @@ -341,7 +343,7 @@ const resourceMap = { data: "media", total: json => json.total, delete: (params: DeleteParams) => ({ - endpoint: `/_synapse/admin/v1/media/${localStorage.getItem("home_server")}/${params.id}`, + endpoint: `/_synapse/admin/v1/media/${storage.getItem("home_server")}/${params.id}`, }), }, protect_media: { @@ -358,11 +360,11 @@ const resourceMap = { quarantine_media: { map: (qm: UserMedia) => ({ id: qm.media_id }), create: (params: UserMedia) => ({ - endpoint: `/_synapse/admin/v1/media/quarantine/${localStorage.getItem("home_server")}/${params.media_id}`, + endpoint: `/_synapse/admin/v1/media/quarantine/${storage.getItem("home_server")}/${params.media_id}`, method: "POST", }), delete: (params: DeleteParams) => ({ - endpoint: `/_synapse/admin/v1/media/unquarantine/${localStorage.getItem("home_server")}/${params.id}`, + endpoint: `/_synapse/admin/v1/media/unquarantine/${storage.getItem("home_server")}/${params.id}`, method: "POST", }), }, @@ -506,7 +508,7 @@ const dataProvider: SynapseDataProvider = { order_by: field, dir: getSearchOrder(order), }; - const homeserver = localStorage.getItem("base_url"); + const homeserver = storage.getItem("base_url"); if (!homeserver || !(resource in resourceMap)) throw Error("Homeserver not set"); const res = resourceMap[resource]; @@ -523,7 +525,7 @@ const dataProvider: SynapseDataProvider = { getOne: async (resource, params) => { console.log("getOne " + resource); - const homeserver = localStorage.getItem("base_url"); + const homeserver = storage.getItem("base_url"); if (!homeserver || !(resource in resourceMap)) throw Error("Homeserver not set"); const res = resourceMap[resource]; @@ -535,7 +537,7 @@ const dataProvider: SynapseDataProvider = { getMany: async (resource, params) => { console.log("getMany " + resource); - const homeserver = localStorage.getItem("base_url"); + const homeserver = storage.getItem("base_url"); if (!homeserver || !(resource in resourceMap)) throw Error("Homerserver not set"); const res = resourceMap[resource]; @@ -560,7 +562,7 @@ const dataProvider: SynapseDataProvider = { dir: getSearchOrder(order), }; - const homeserver = localStorage.getItem("base_url"); + const homeserver = storage.getItem("base_url"); if (!homeserver || !(resource in resourceMap)) throw Error("Homeserver not set"); const res = resourceMap[resource]; @@ -577,7 +579,7 @@ const dataProvider: SynapseDataProvider = { update: async (resource, params) => { console.log("update " + resource); - const homeserver = localStorage.getItem("base_url"); + const homeserver = storage.getItem("base_url"); if (!homeserver || !(resource in resourceMap)) throw Error("Homeserver not set"); const res = resourceMap[resource]; @@ -592,7 +594,7 @@ const dataProvider: SynapseDataProvider = { updateMany: async (resource, params) => { console.log("updateMany " + resource); - const homeserver = localStorage.getItem("base_url"); + const homeserver = storage.getItem("base_url"); if (!homeserver || !(resource in resourceMap)) throw Error("Homeserver not set"); const res = resourceMap[resource]; @@ -609,7 +611,7 @@ const dataProvider: SynapseDataProvider = { create: async (resource, params) => { console.log("create " + resource); - const homeserver = localStorage.getItem("base_url"); + const homeserver = storage.getItem("base_url"); if (!homeserver || !(resource in resourceMap)) throw Error("Homeserver not set"); const res = resourceMap[resource]; @@ -626,7 +628,7 @@ const dataProvider: SynapseDataProvider = { createMany: async (resource: string, params: { ids: Identifier[]; data: RaRecord }) => { console.log("createMany " + resource); - const homeserver = localStorage.getItem("base_url"); + const homeserver = storage.getItem("base_url"); if (!homeserver || !(resource in resourceMap)) throw Error("Homeserver not set"); const res = resourceMap[resource]; @@ -648,7 +650,7 @@ const dataProvider: SynapseDataProvider = { delete: async (resource, params) => { console.log("delete " + resource); - const homeserver = localStorage.getItem("base_url"); + const homeserver = storage.getItem("base_url"); if (!homeserver || !(resource in resourceMap)) throw Error("Homeserver not set"); const res = resourceMap[resource]; @@ -673,7 +675,7 @@ const dataProvider: SynapseDataProvider = { deleteMany: async (resource, params) => { console.log("deleteMany " + resource); - const homeserver = localStorage.getItem("base_url"); + const homeserver = storage.getItem("base_url"); if (!homeserver || !(resource in resourceMap)) throw Error("Homeserver not set"); const res = resourceMap[resource]; @@ -719,10 +721,10 @@ const dataProvider: SynapseDataProvider = { * @returns */ deleteMedia: async ({ before_ts, size_gt = 0, keep_profiles = true }) => { - const homeserver = localStorage.getItem("home_server"); // TODO only required for synapse < 1.78.0 + const homeserver = storage.getItem("home_server"); // TODO only required for synapse < 1.78.0 const endpoint = `/_synapse/admin/v1/media/${homeserver}/delete?before_ts=${before_ts}&size_gt=${size_gt}&keep_profiles=${keep_profiles}`; - const base_url = localStorage.getItem("base_url"); + const base_url = storage.getItem("base_url"); const endpoint_url = base_url + endpoint; const { json } = await jsonClient(endpoint_url, { method: "POST" }); return json as DeleteMediaResult; diff --git a/src/synapse/synapse.ts b/src/synapse/synapse.ts index fc8cc0ea..3180fb9b 100644 --- a/src/synapse/synapse.ts +++ b/src/synapse/synapse.ts @@ -1,5 +1,7 @@ import { fetchUtils } from "react-admin"; +import storage from "../storage"; + export const splitMxid = mxid => { const re = /^@(?[a-zA-Z0-9._=\-/]+):(?[a-zA-Z0-9\-.]+\.[a-zA-Z]+)$/; return re.exec(mxid)?.groups; @@ -53,7 +55,7 @@ export const getSupportedLoginFlows = async baseUrl => { }; export const getMediaUrl = media_id => { - const baseUrl = localStorage.getItem("base_url"); + const baseUrl = storage.getItem("base_url"); return `${baseUrl}/_matrix/media/v1/download/${media_id}?allow_redirect=true`; }; @@ -62,7 +64,7 @@ export const getMediaUrl = media_id => { * @returns full MXID as string */ export function generateRandomMxId(): string { - const homeserver = localStorage.getItem("home_server"); + const homeserver = storage.getItem("home_server"); const characters = "0123456789abcdefghijklmnopqrstuvwxyz"; const localpart = Array.from(crypto.getRandomValues(new Uint32Array(8))) .map(x => characters[x % characters.length])