From 94f995f2b3f76bd227fa868ce38bbb6aec76f871 Mon Sep 17 00:00:00 2001 From: alexm Date: Fri, 9 May 2025 14:26:39 +0200 Subject: [PATCH] feat: refs #7822 implement ACL directive and enhance ACL handling in useAcl --- src/boot/checkAcl.js | 14 ++++++++++++++ src/boot/quasar.js | 2 ++ src/components/VnTable/VnTable.vue | 5 +++++ src/composables/useAcl.js | 27 ++++++++++++++++----------- 4 files changed, 37 insertions(+), 11 deletions(-) create mode 100644 src/boot/checkAcl.js diff --git a/src/boot/checkAcl.js b/src/boot/checkAcl.js new file mode 100644 index 000000000..2d2804d02 --- /dev/null +++ b/src/boot/checkAcl.js @@ -0,0 +1,14 @@ +import { useAcl } from 'src/composables/useAcl'; +export default { + mounted(el, binding) { + if (!binding.value) return; + + const allowed = useAcl().hasAcl( + binding.value.model, + binding.value.prop, + binding.value.accessType || 'READ', + ); + + el.disabled = !allowed; + }, +}; diff --git a/src/boot/quasar.js b/src/boot/quasar.js index a4f2ba0fe..d99bea1f8 100644 --- a/src/boot/quasar.js +++ b/src/boot/quasar.js @@ -2,6 +2,7 @@ import axios from 'axios'; import { boot } from 'quasar/wrappers'; import qFormMixin from './qformMixin'; import keyShortcut from './keyShortcut'; +import checkAcl from './checkAcl'; import { QForm } from 'quasar'; import { QLayout } from 'quasar'; import mainShortcutMixin from './mainShortcutMixin'; @@ -12,6 +13,7 @@ export default boot(({ app }) => { QLayout.mixins = [mainShortcutMixin]; app.directive('shortcut', keyShortcut); + app.directive('acl', checkAcl); app.config.errorHandler = async (error) => { let message; const response = error.response; diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue index 9327dcd39..183446af0 100644 --- a/src/components/VnTable/VnTable.vue +++ b/src/components/VnTable/VnTable.vue @@ -150,6 +150,10 @@ const $props = defineProps({ type: String, default: 'vnTable', }, + checkCreateAcl: { + type: Object, + default: undefined, + }, }); const { t } = useI18n(); @@ -1084,6 +1088,7 @@ const handleHeaderSelection = (evt, data) => { fab icon="add" v-shortcut="'+'" + v-acl="checkCreateAcl" data-cy="vnTableCreateBtn" /> diff --git a/src/composables/useAcl.js b/src/composables/useAcl.js index 52704fee9..f97fc05de 100644 --- a/src/composables/useAcl.js +++ b/src/composables/useAcl.js @@ -8,32 +8,37 @@ export function useAcl() { const { data } = await axios.get('VnUsers/acls'); const acls = {}; data.forEach((acl) => { - acls[acl.model] = acls[acl.model] || {}; - acls[acl.model][acl.property] = acls[acl.model][acl.property] || {}; - acls[acl.model][acl.property][acl.accessType] = true; + const model = acl.model?.toLowerCase(); + const property = acl.property?.toLowerCase(); + acls[model] = acls[model] || {}; + acls[model][property] = acls[model][property] || {}; + acls[model][property][acl.accessType?.toLowerCase()] = true; }); state.setAcls(acls); } function hasAny(acls) { + let result = false; for (const acl of acls) { let { model, props, accessType } = acl; - const modelAcls = state.getAcls().value[model]; + const modelAcls = state.getAcls().value[model.toLowerCase()]; Array.isArray(props) || (props = [props]); - if (modelAcls) - return ['*', ...props].some((key) => { - const acl = modelAcls[key]; + if (modelAcls) { + result = ['*', ...props].some((key) => { + const acl = modelAcls[key.toLowerCase()]; return acl && (acl['*'] || acl[accessType]); }); + } + if (result) return result; } - return false; + return result; } function hasAcl(model, prop, accessType) { - const modelAcl = state.getAcls().value[model]; - const propAcl = modelAcl?.[prop] || modelAcl?.['*']; - return !!(propAcl?.[accessType] || propAcl?.['*']); + const modelAcl = state.getAcls().value[model?.toLowerCase()]; + const propAcl = modelAcl?.[prop?.toLowerCase()] || modelAcl?.['*']; + return !!(propAcl?.[accessType?.toLowerCase()] || propAcl?.['*']); } return {