From 153bc6dddeffda94d0209845864f2e9b8397e396 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sun, 20 Mar 2022 13:34:38 +0100 Subject: [PATCH] permissions refactor --- packages/api/.env | 1 + packages/api/src/controllers/config.js | 2 +- packages/api/src/controllers/files.js | 11 ++++-- packages/tools/src/testPermission.ts | 34 +++++++++++++++---- .../web/src/widgets/WidgetIconPanel.svelte | 3 +- 5 files changed, 40 insertions(+), 11 deletions(-) diff --git a/packages/api/.env b/packages/api/.env index c7746f94..8bea8426 100644 --- a/packages/api/.env +++ b/packages/api/.env @@ -1,3 +1,4 @@ DEVMODE=1 +# PERMISSIONS=~widgets/app,~widgets/plugins # DISABLE_SHELL=1 # HIDE_APP_EDITOR=1 \ No newline at end of file diff --git a/packages/api/src/controllers/config.js b/packages/api/src/controllers/config.js index 32d385fb..937898a6 100644 --- a/packages/api/src/controllers/config.js +++ b/packages/api/src/controllers/config.js @@ -32,7 +32,7 @@ module.exports = { return { runAsPortal: !!connections.portalConnections, singleDatabase: connections.singleDatabase, - hideAppEditor: !!process.env.HIDE_APP_EDITOR, + // hideAppEditor: !!process.env.HIDE_APP_EDITOR, allowShellConnection: platformInfo.allowShellConnection, allowShellScripting: platformInfo.allowShellConnection, permissions, diff --git a/packages/api/src/controllers/files.js b/packages/api/src/controllers/files.js index 350f7226..4116bd9e 100644 --- a/packages/api/src/controllers/files.js +++ b/packages/api/src/controllers/files.js @@ -46,26 +46,29 @@ module.exports = { delete_meta: true, async delete({ folder, file }) { - if (!hasPermission(`files/${folder}/write`)) return; + if (!hasPermission(`files/${folder}/write`)) return false; await fs.unlink(path.join(filesdir(), folder, file)); socket.emitChanged(`files-changed-${folder}`); socket.emitChanged(`all-files-changed`); + return true; }, rename_meta: true, async rename({ folder, file, newFile }) { - if (!hasPermission(`files/${folder}/write`)) return; + if (!hasPermission(`files/${folder}/write`)) return false; await fs.rename(path.join(filesdir(), folder, file), path.join(filesdir(), folder, newFile)); socket.emitChanged(`files-changed-${folder}`); socket.emitChanged(`all-files-changed`); + return true; }, copy_meta: true, async copy({ folder, file, newFile }) { - if (!hasPermission(`files/${folder}/write`)) return; + if (!hasPermission(`files/${folder}/write`)) return false; await fs.copyFile(path.join(filesdir(), folder, file), path.join(filesdir(), folder, newFile)); socket.emitChanged(`files-changed-${folder}`); socket.emitChanged(`all-files-changed`); + return true; }, load_meta: true, @@ -90,11 +93,13 @@ module.exports = { save_meta: true, async save({ folder, file, data, format }) { if (folder.startsWith('archive:')) { + if (!hasPermission(`archive/write`)) return false; const dir = resolveArchiveFolder(folder.substring('archive:'.length)); await fs.writeFile(path.join(dir, file), serialize(format, data)); socket.emitChanged(`archive-files-changed-${folder.substring('archive:'.length)}`); return true; } else if (folder.startsWith('app:')) { + if (!hasPermission(`apps/write`)) return false; const app = folder.substring('app:'.length); await fs.writeFile(path.join(appdir(), app, file), serialize(format, data)); socket.emitChanged(`app-files-changed-${app}`); diff --git a/packages/tools/src/testPermission.ts b/packages/tools/src/testPermission.ts index 907d7169..322476b3 100644 --- a/packages/tools/src/testPermission.ts +++ b/packages/tools/src/testPermission.ts @@ -1,16 +1,38 @@ import _escapeRegExp from 'lodash/escapeRegExp'; import _isString from 'lodash/isString'; +import _compact from 'lodash/compact'; -export function compilePermissions(permissions: string[] | string) { +interface CompiledPermissions { + revoke: RegExp; + allow: RegExp; +} + +function compileRegexp(permissions) { + if (permissions.length == 0) return null; + return new RegExp(permissions.map(x => '^' + _escapeRegExp(x).replace(/\\\*/g, '.*') + '$').join('|')); +} + +export function compilePermissions(permissions: string[] | string): CompiledPermissions { if (!permissions) return null; if (_isString(permissions)) permissions = permissions.split(','); - return permissions.map(x => new RegExp('^' + _escapeRegExp(x).replace(/\\\*/g, '.*') + '$')); + permissions = _compact(permissions.map(x => x.trim())); + const revoke = permissions.filter(x => x.startsWith('~')).map(x => x.substring(1)); + const allow = permissions.filter(x => !x.startsWith('~')); + return { + revoke: compileRegexp(revoke), + allow: compileRegexp(allow), + }; } -export function testPermission(tested: string, permissions: RegExp[]) { +export function testPermission(tested: string, permissions: CompiledPermissions) { if (!permissions) return true; - for (const permission of permissions) { - if (tested.match(permission)) return true; + if (!permissions.revoke) return true; + + if (tested.match(permissions.revoke)) { + if (!tested.match(permissions.allow)) { + return false; + } } - return false; + + return true; } diff --git a/packages/web/src/widgets/WidgetIconPanel.svelte b/packages/web/src/widgets/WidgetIconPanel.svelte index 0ac958bd..7c0e68a5 100644 --- a/packages/web/src/widgets/WidgetIconPanel.svelte +++ b/packages/web/src/widgets/WidgetIconPanel.svelte @@ -4,6 +4,7 @@ import { currentDropDownMenu, selectedWidget, visibleCommandPalette, visibleHamburgerMenuWidget } from '../stores'; import mainMenuDefinition from '../../../../app/src/mainMenuDefinition'; import { useConfig } from '../utility/metadataLoaders'; + import hasPermission from '../utility/hasPermission'; let domSettings; let domMainMenu; @@ -88,7 +89,7 @@ {/if} - {#each widgets.filter(x => !$config?.hideAppEditor || x.name != 'app') as item} + {#each widgets.filter(x => hasPermission(`widgets/${x.name}`)) as item}
handleChangeWidget(item.name)}>