diff --git a/apps/client/src/common/context/AppContext.tsx b/apps/client/src/common/context/AppContext.tsx index 8a9e082c6..684aafd9d 100644 --- a/apps/client/src/common/context/AppContext.tsx +++ b/apps/client/src/common/context/AppContext.tsx @@ -26,7 +26,6 @@ export const AppContextProvider = ({ children }: PropsWithChildren) => { useEffect(() => { if (status === 'pending') return; - if (!data) return; const previousEditor = sessionStorage.getItem(storageKeys.editor); if (previousEditor && previousEditor === data.editorKey) { @@ -53,10 +52,6 @@ export const AppContextProvider = ({ children }: PropsWithChildren) => { return savedPin == null || savedPin === '' || pin === savedPin; } - if (!data) { - return false; - } - if (permission === 'editor') { const correct = isValid(pin, data.editorKey); if (correct) { diff --git a/apps/client/src/common/hooks-query/useSettings.ts b/apps/client/src/common/hooks-query/useSettings.ts index 330305112..c53b21e06 100644 --- a/apps/client/src/common/hooks-query/useSettings.ts +++ b/apps/client/src/common/hooks-query/useSettings.ts @@ -1,4 +1,5 @@ import { useQuery } from '@tanstack/react-query'; +import { unobfuscate } from 'ontime-utils'; import { queryRefetchIntervalSlow } from '../../ontimeConfig'; import { APP_SETTINGS } from '../api/constants'; @@ -14,7 +15,17 @@ export default function useSettings() { retryDelay: (attempt) => attempt * 2500, refetchInterval: queryRefetchIntervalSlow, networkMode: 'always', + select: (data) => { + const unobfuscated = { ...data }; + if (data.editorKey) { + unobfuscated.editorKey = unobfuscate(data.editorKey); + } + if (data.operatorKey) { + unobfuscated.operatorKey = unobfuscate(data.operatorKey); + } + return unobfuscated; + }, }); - return { data, status, isFetching, isError, refetch }; + return { data: data ?? ontimePlaceholderSettings, status, isFetching, isError, refetch }; } diff --git a/apps/server/src/api-data/settings/settings.controller.ts b/apps/server/src/api-data/settings/settings.controller.ts index 690cd4359..6dcc103ee 100644 --- a/apps/server/src/api-data/settings/settings.controller.ts +++ b/apps/server/src/api-data/settings/settings.controller.ts @@ -6,10 +6,20 @@ import { DataProvider } from '../../classes/data-provider/DataProvider.js'; import { failEmptyObjects } from '../../utils/routerUtils.js'; import { extractPin } from '../../services/project-service/ProjectService.js'; import { isDocker } from '../../setup/index.js'; +import { obfuscate } from 'ontime-utils'; export async function getSettings(_req: Request, res: Response) { const settings = DataProvider.getSettings(); - res.status(200).send(settings); + const obfuscatedSettings = { ...settings }; + if (settings.editorKey) { + obfuscatedSettings.editorKey = obfuscate(settings.editorKey); + } + + if (settings.operatorKey) { + obfuscatedSettings.editorKey = obfuscate(settings.editorKey); + } + + res.status(200).send(obfuscatedSettings); } export async function postSettings(req: Request, res: Response) { diff --git a/apps/server/src/classes/data-provider/DataProvider.ts b/apps/server/src/classes/data-provider/DataProvider.ts index 27e7f11a4..06a00f02f 100644 --- a/apps/server/src/classes/data-provider/DataProvider.ts +++ b/apps/server/src/classes/data-provider/DataProvider.ts @@ -48,7 +48,7 @@ export class DataProvider { await this.persist(); } - static getSettings(): Settings { + static getSettings(): Readonly { return data.settings; } diff --git a/packages/utils/index.ts b/packages/utils/index.ts index 5535b3939..d71d619fd 100644 --- a/packages/utils/index.ts +++ b/packages/utils/index.ts @@ -56,6 +56,7 @@ export { deleteAtIndex, insertAtIndex, reorderArray, sortArrayByProperty } from // generic utilities export { unpackError } from './src/generic/generic.js'; +export { obfuscate, unobfuscate } from './src/generic/generic.js'; export { isNumeric } from './src/types/types.js'; // model validation diff --git a/packages/utils/src/generic/__tests__/generic.test.ts b/packages/utils/src/generic/__tests__/generic.test.ts new file mode 100644 index 000000000..8c4ea0211 --- /dev/null +++ b/packages/utils/src/generic/__tests__/generic.test.ts @@ -0,0 +1,18 @@ +import { obfuscate, unobfuscate } from '../generic.js'; + +describe('obfuscate and unobfuscate', () => { + it('should return the obfuscated string', () => { + const str = 'abc123'; + const obfuscated = obfuscate(str); + expect(obfuscated).not.toBe(str); + expect(obfuscated.startsWith('_')).toBe(true); + }); + + it('should return the original string after obfuscating and unobfuscating', () => { + const str = 'abc123'; + const obfuscated = obfuscate(str); + const unobfuscated = unobfuscate(obfuscated); + expect(unobfuscated).toBe(str); + expect(unobfuscated.startsWith('_')).toBe(false); + }); +}); diff --git a/packages/utils/src/generic/generic.ts b/packages/utils/src/generic/generic.ts index ae378d4ba..e48032663 100644 --- a/packages/utils/src/generic/generic.ts +++ b/packages/utils/src/generic/generic.ts @@ -4,3 +4,39 @@ export function unpackError(error: unknown): string { } return String(error); } + +/** + * Obfuscate a string + * Uses a variation of ROT13 that handles numeric values + * @param str + * @returns + */ +export function obfuscate(str: string): string { + const obfuscated = str.replace(/[a-zA-Z0-9]/g, (c) => { + if (/[a-zA-Z]/.test(c)) { + // @ts-expect-error -- we use some javascript magic here + return String.fromCharCode((c <= 'Z' ? 90 : 122) >= (c = c.charCodeAt(0) + 13) ? c : c - 26); + } else { + // @ts-expect-error -- we use some javascript magic here + + return String.fromCharCode((c <= '4' ? 57 : 48) >= (c = c.charCodeAt(0) + 5) ? c : c - 10); + } + }); + if (str.startsWith('_')) { + return obfuscated.replace('_', ''); + } + return `_${obfuscated}`; +} + +/** + * Unobfoscate a string + * Uses a variation of ROT13 that handles numeric values + * @param str + * @returns + */ +export function unobfuscate(str: string): string { + if (str.startsWith('_')) { + return obfuscate(str); + } + return str; +}