Browse Source

fix: refactor isUIAllowed and move to useRoles composable

pull/6399/head
mertmit 1 year ago
parent
commit
b2eadae74f
  1. 4
      packages/nc-gui/components/cell/MultiSelect.vue
  2. 4
      packages/nc-gui/components/cell/SingleSelect.vue
  3. 4
      packages/nc-gui/components/cell/TextArea.vue
  4. 9
      packages/nc-gui/components/cell/attachment/Modal.vue
  5. 16
      packages/nc-gui/components/dashboard/TreeView/AddNewTableNode.vue
  6. 14
      packages/nc-gui/components/dashboard/TreeView/BaseOptions.vue
  7. 20
      packages/nc-gui/components/dashboard/TreeView/ProjectNode.vue
  8. 16
      packages/nc-gui/components/dashboard/TreeView/TableNode.vue
  9. 4
      packages/nc-gui/components/dashboard/TreeView/index.vue
  10. 2
      packages/nc-gui/components/general/AddBaseButton.vue
  11. 4
      packages/nc-gui/components/general/ShareBaseButton.vue
  12. 4
      packages/nc-gui/components/general/ShareProject.vue
  13. 7
      packages/nc-gui/components/project/AllTables.vue
  14. 2
      packages/nc-gui/components/project/View.vue
  15. 6
      packages/nc-gui/components/smartsheet/Cell.vue
  16. 2
      packages/nc-gui/components/smartsheet/Details.vue
  17. 4
      packages/nc-gui/components/smartsheet/Form.vue
  18. 2
      packages/nc-gui/components/smartsheet/Gallery.vue
  19. 2
      packages/nc-gui/components/smartsheet/Kanban.vue
  20. 4
      packages/nc-gui/components/smartsheet/Toolbar.vue
  21. 15
      packages/nc-gui/components/smartsheet/expanded-form/Comments.vue
  22. 4
      packages/nc-gui/components/smartsheet/expanded-form/Header.vue
  23. 3
      packages/nc-gui/components/smartsheet/expanded-form/index.vue
  24. 4
      packages/nc-gui/components/smartsheet/grid/Table.vue
  25. 4
      packages/nc-gui/components/smartsheet/header/Cell.vue
  26. 4
      packages/nc-gui/components/smartsheet/header/VirtualCell.vue
  27. 4
      packages/nc-gui/components/smartsheet/sidebar/RenameableMenuItem.vue
  28. 4
      packages/nc-gui/components/smartsheet/sidebar/index.vue
  29. 2
      packages/nc-gui/components/smartsheet/sidebar/toolbar/Developer.vue
  30. 4
      packages/nc-gui/components/smartsheet/toolbar/KanbanStackEditOrAdd.vue
  31. 4
      packages/nc-gui/components/smartsheet/toolbar/MoreActions.vue
  32. 4
      packages/nc-gui/components/smartsheet/toolbar/ShareView.vue
  33. 4
      packages/nc-gui/components/smartsheet/toolbar/ViewActions.vue
  34. 4
      packages/nc-gui/components/tabs/Auth.vue
  35. 4
      packages/nc-gui/components/tabs/Smartsheet.vue
  36. 4
      packages/nc-gui/components/tabs/auth/UserManagement.vue
  37. 4
      packages/nc-gui/components/virtual-cell/BelongsTo.vue
  38. 4
      packages/nc-gui/components/virtual-cell/HasMany.vue
  39. 2
      packages/nc-gui/components/virtual-cell/Links.vue
  40. 4
      packages/nc-gui/components/virtual-cell/ManyToMany.vue
  41. 2
      packages/nc-gui/components/virtual-cell/components/ItemChip.vue
  42. 4
      packages/nc-gui/components/workspace/CreateProjectBtn.vue
  43. 10
      packages/nc-gui/components/workspace/ProjectList.vue
  44. 2
      packages/nc-gui/composables/useExpandedFormStore.ts
  45. 15
      packages/nc-gui/composables/useGridViewColumn.ts
  46. 4
      packages/nc-gui/composables/useKanbanViewStore.ts
  47. 2
      packages/nc-gui/composables/useMapViewDataStore.ts
  48. 45
      packages/nc-gui/composables/useRoles/index.ts
  49. 51
      packages/nc-gui/composables/useUIPermission/index.ts
  50. 0
      packages/nc-gui/composables/useUIPermission/rolePermissions.ts
  51. 15
      packages/nc-gui/composables/useViewColumns.ts
  52. 4
      packages/nc-gui/composables/useViewData.ts
  53. 4
      packages/nc-gui/composables/useViewFilters.ts
  54. 2
      packages/nc-gui/composables/useViewGroupBy.ts
  55. 4
      packages/nc-gui/composables/useViewSorts.ts
  56. 148
      packages/nc-gui/lib/acl.ts
  57. 149
      packages/nc-gui/lib/constants.ts
  58. 1
      packages/nc-gui/lib/index.ts
  59. 4
      packages/nc-gui/pages/account/index.vue
  60. 4
      packages/nc-gui/pages/account/index/users/[[nestedPage]].vue
  61. 2
      packages/nc-gui/store/projectsShortcuts.ts
  62. 4
      packages/nocodb-sdk/src/lib/helperFunctions.ts

4
packages/nc-gui/components/cell/MultiSelect.vue

@ -26,8 +26,8 @@ import {
useEventListener,
useMetas,
useProject,
useRoles,
useSelectedCellKeyupListener,
useUIPermission,
watch,
} from '#imports'
import MdiCloseCircle from '~icons/mdi/close-circle'
@ -79,7 +79,7 @@ const { $api } = useNuxtApp()
const { getMeta } = useMetas()
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const { isPg, isMysql } = useProject()

4
packages/nc-gui/components/cell/SingleSelect.vue

@ -22,8 +22,8 @@ import {
ref,
useEventListener,
useProject,
useRoles,
useSelectedCellKeyupListener,
useUIPermission,
watch,
} from '#imports'
@ -69,7 +69,7 @@ const searchVal = ref()
const { getMeta } = useMetas()
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const { isPg, isMysql } = useProject()

4
packages/nc-gui/components/cell/TextArea.vue

@ -6,11 +6,11 @@ import {
EditColumnInj,
EditModeInj,
IsExpandedFormOpenInj,
ReadonlyInj,
RowHeightInj,
iconMap,
inject,
useVModel,
ReadonlyInj,
} from '#imports'
const props = defineProps<{
@ -139,9 +139,9 @@ onClickOutside(inputWrapperRef, (e) => {
class="p-1 !pt-1 !pr-3 !border-0 !border-r-0 !focus:outline-transparent nc-scrollbar-md !text-black"
:bordered="false"
:auto-size="{ minRows: 20, maxRows: 20 }"
:disabled="readOnly"
@keydown.stop
@keydown.escape="isVisible = false"
:disabled="readOnly"
/>
</div>
</template>

9
packages/nc-gui/components/cell/attachment/Modal.vue

@ -2,9 +2,9 @@
import { onKeyDown, useEventListener } from '@vueuse/core'
import { useAttachmentCell } from './utils'
import { useSortable } from './sort'
import { iconMap, isImage, ref, useAttachment, useDropZone, useUIPermission, watch } from '#imports'
import { iconMap, isImage, ref, useAttachment, useDropZone, useRoles, watch } from '#imports'
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const {
open,
@ -155,10 +155,7 @@ const handleFileDelete = (i: number) => {
</div>
</a-tooltip>
<a-tooltip
v-if="isSharedForm || (!readOnly && isUIAllowed('dataEdit') && !isPublic && !isLocked)"
placement="bottom"
>
<a-tooltip v-if="isSharedForm || (!readOnly && isUIAllowed('dataEdit') && !isPublic && !isLocked)" placement="bottom">
<template #title> Rename File </template>
<div class="nc-attachment-download group-hover:(opacity-100) mr-[35px]">

16
packages/nc-gui/components/dashboard/TreeView/AddNewTableNode.vue

@ -4,7 +4,7 @@ import { storeToRefs } from 'pinia'
import { toRef } from '@vue/reactivity'
import { resolveComponent } from '@vue/runtime-core'
import { ref } from 'vue'
import { ProjectRoleInj, useDialog, useUIPermission } from '#imports'
import { ProjectRoleInj, useDialog, useRoles } from '#imports'
const props = withDefaults(
defineProps<{
@ -20,7 +20,7 @@ const emit = defineEmits<{
openTableCreateDialog: () => void
}>()
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const project = toRef(props, 'project')
@ -116,7 +116,7 @@ function openTableCreateMagicDialog(baseId?: string) {
<template>
<div
v-if="isUIAllowed('tableCreate', false, projectRole)"
v-if="isUIAllowed('tableCreate', { roles: projectRole })"
class="group flex items-center gap-2 pl-2 pr-4.75 py-1 text-primary/70 hover:(text-primary/100) cursor-pointer select-none"
@click="emit('openTableCreateDialog')"
>
@ -158,7 +158,7 @@ function openTableCreateMagicDialog(baseId?: string) {
<!-- Quick Import From -->
<a-menu-item-group :title="$t('title.quickImportFrom')" class="!px-0 !mx-0">
<a-menu-item
v-if="isUIAllowed('airtableImport', false, projectRole)"
v-if="isUIAllowed('airtableImport', { roles: projectRole })"
key="quick-import-airtable"
@click="openAirtableImportDialog(project.bases[baseIndex].id)"
>
@ -169,7 +169,7 @@ function openTableCreateMagicDialog(baseId?: string) {
</a-menu-item>
<a-menu-item
v-if="isUIAllowed('csvImport', false, projectRole)"
v-if="isUIAllowed('csvImport', { roles: projectRole })"
key="quick-import-csv"
@click="openQuickImportDialog('csv', project.bases[baseIndex].id)"
>
@ -180,7 +180,7 @@ function openTableCreateMagicDialog(baseId?: string) {
</a-menu-item>
<a-menu-item
v-if="isUIAllowed('jsonImport', false, projectRole)"
v-if="isUIAllowed('jsonImport', { roles: projectRole })"
key="quick-import-json"
@click="openQuickImportDialog('json', project.bases[baseIndex].id)"
>
@ -191,7 +191,7 @@ function openTableCreateMagicDialog(baseId?: string) {
</a-menu-item>
<a-menu-item
v-if="isUIAllowed('excelImport', false, projectRole)"
v-if="isUIAllowed('excelImport', { roles: projectRole })"
key="quick-import-excel"
@click="openQuickImportDialog('excel', project.bases[baseIndex].id)"
>
@ -243,7 +243,7 @@ function openTableCreateMagicDialog(baseId?: string) {
<a-menu-divider class="my-0" /> -->
<a-menu-item v-if="isUIAllowed('importRequest', false, projectRole)" key="add-new-table" class="py-1 rounded-b">
<a-menu-item v-if="isUIAllowed('importRequest', { roles: projectRole })" key="add-new-table" class="py-1 rounded-b">
<a
v-e="['e:datasource:import-request']"
href="https://github.com/nocodb/nocodb/issues/2052"

14
packages/nc-gui/components/dashboard/TreeView/BaseOptions.vue

@ -8,7 +8,7 @@ const props = defineProps<{
const base = toRef(props, 'base')
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const projectRole = inject(ProjectRoleInj)
@ -68,7 +68,7 @@ function openQuickImportDialog(type: string) {
<template #expandIcon></template>
<NcMenuItem
v-if="isUIAllowed('airtableImport', false, projectRole)"
v-if="isUIAllowed('airtableImport', { roles: projectRole })"
key="quick-import-airtable"
@click="openAirtableImportDialog(base.id)"
>
@ -76,13 +76,17 @@ function openQuickImportDialog(type: string) {
<div class="ml-0.5">Airtable</div>
</NcMenuItem>
<NcMenuItem v-if="isUIAllowed('csvImport', false, projectRole)" key="quick-import-csv" @click="openQuickImportDialog('csv')">
<NcMenuItem
v-if="isUIAllowed('csvImport', { roles: projectRole })"
key="quick-import-csv"
@click="openQuickImportDialog('csv')"
>
<GeneralIcon icon="csv" class="w-4 group-hover:text-black" />
CSV file
</NcMenuItem>
<NcMenuItem
v-if="isUIAllowed('jsonImport', false, projectRole)"
v-if="isUIAllowed('jsonImport', { roles: projectRole })"
key="quick-import-json"
@click="openQuickImportDialog('json')"
>
@ -91,7 +95,7 @@ function openQuickImportDialog(type: string) {
</NcMenuItem>
<NcMenuItem
v-if="isUIAllowed('excelImport', false, projectRole)"
v-if="isUIAllowed('excelImport', { roles: projectRole })"
key="quick-import-excel"
@click="openQuickImportDialog('excel')"
>

20
packages/nc-gui/components/dashboard/TreeView/ProjectNode.vue

@ -44,7 +44,7 @@ const { activeTable } = storeToRefs(useTablesStore())
const { appInfo, navigateToProject } = useGlobal()
const { orgRoles } = useRoles()
const { orgRoles, isUIAllowed } = useRoles()
useTabs()
@ -56,8 +56,6 @@ const { t } = useI18n()
const input = ref<HTMLInputElement>()
const { isUIAllowed } = useUIPermission()
const projectRole = inject(ProjectRoleInj)
const { activeProjectId } = storeToRefs(useProjects())
@ -441,7 +439,11 @@ const DlgProjectDuplicateOnOk = async (jobData: { id: string; project_id: string
</span>
<div :class="{ 'flex flex-grow h-full': !editMode }" @click="onProjectClick(project)"></div>
<NcDropdown v-if="isUIAllowed('tableCreate', false, projectRole)" v-model:visible="isOptionsOpen" :trigger="['click']">
<NcDropdown
v-if="isUIAllowed('tableCreate', { roles: projectRole })"
v-model:visible="isOptionsOpen"
:trigger="['click']"
>
<NcButton
class="nc-sidebar-node-btn"
:class="{ '!text-black !opacity-100': isOptionsOpen }"
@ -473,7 +475,7 @@ const DlgProjectDuplicateOnOk = async (jobData: { id: string; project_id: string
{{ $t('activity.account.projInfo') }}
</NcMenuItem>
<NcMenuItem
v-if="isUIAllowed('projectDuplicate', true, [stringifyRolesObj(orgRoles), projectRole].join())"
v-if="isUIAllowed('projectDuplicate', { roles: [stringifyRolesObj(orgRoles), projectRole].join() })"
@click="duplicateProject(project)"
>
<GeneralIcon icon="duplicate" class="text-gray-700" />
@ -516,7 +518,7 @@ const DlgProjectDuplicateOnOk = async (jobData: { id: string; project_id: string
</template>
<NcMenuItem
v-if="isUIAllowed('projectDelete', false, projectRole)"
v-if="isUIAllowed('projectDelete', { roles: [stringifyRolesObj(orgRoles), projectRole].join() })"
class="!text-red-500 !hover:bg-red-50"
@click="isProjectDeleteDialogVisible = true"
>
@ -528,7 +530,7 @@ const DlgProjectDuplicateOnOk = async (jobData: { id: string; project_id: string
</NcDropdown>
<NcButton
v-if="isUIAllowed('tableCreate', false, projectRole)"
v-if="isUIAllowed('tableCreate', { roles: projectRole })"
class="nc-sidebar-node-btn"
size="xxsmall"
type="text"
@ -610,7 +612,7 @@ const DlgProjectDuplicateOnOk = async (jobData: { id: string; project_id: string
</a-tooltip>
</div>
<div
v-if="isUIAllowed('tableCreate', false, projectRole)"
v-if="isUIAllowed('tableCreate', { roles: projectRole })"
class="flex flex-row items-center gap-x-0.25 w-12.25"
>
<NcDropdown
@ -648,7 +650,7 @@ const DlgProjectDuplicateOnOk = async (jobData: { id: string; project_id: string
</NcDropdown>
<NcButton
v-if="isUIAllowed('tableCreate', false, projectRole)"
v-if="isUIAllowed('tableCreate', { roles: projectRole })"
type="text"
size="xxsmall"
class="nc-sidebar-node-btn"

16
packages/nc-gui/components/dashboard/TreeView/TableNode.vue

@ -5,7 +5,7 @@ import { message } from 'ant-design-vue'
import { storeToRefs } from 'pinia'
import { useNuxtApp } from '#app'
import { ProjectRoleInj, TreeViewInj, useTabs, useUIPermission } from '#imports'
import { ProjectRoleInj, TreeViewInj, useRoles, useTabs } from '#imports'
const props = withDefaults(
defineProps<{
@ -22,7 +22,7 @@ const baseIndex = toRef(props, 'baseIndex')
const route = useRoute()
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const tabStore = useTabs()
const { updateTab } = tabStore
@ -71,7 +71,7 @@ const { isSharedBase } = useProject()
// const isMultiBase = computed(() => project.bases && project.bases.length > 1)
const canUserEditEmote = computed(() => {
return isUIAllowed('tableIconEdit', false, projectRole?.value)
return isUIAllowed('tableIconEdit', { roles: projectRole?.value })
})
</script>
@ -125,7 +125,7 @@ const canUserEditEmote = computed(() => {
v-if="table.type === 'table'"
class="flex w-5 !text-gray-500 text-sm"
:class="{
'group-hover:text-gray-500': isUIAllowed('tableSort', false, projectRole),
'group-hover:text-gray-500': isUIAllowed('tableSort', { roles: projectRole }),
'!text-black': openedTableId === table.id,
}"
/>
@ -133,7 +133,7 @@ const canUserEditEmote = computed(() => {
v-else
class="flex w-5 !text-gray-500 text-sm"
:class="{
'group-hover:text-gray-500': isUIAllowed('tableSort', false, projectRole),
'group-hover:text-gray-500': isUIAllowed('tableSort', { roles: projectRole }),
'!text-black': openedTableId === table.id,
}"
/>
@ -157,7 +157,7 @@ const canUserEditEmote = computed(() => {
<NcDropdown
v-if="
!isSharedBase && (isUIAllowed('tableRename', false, projectRole) || isUIAllowed('tableDelete', false, projectRole))
!isSharedBase && (isUIAllowed('tableRename', { roles: projectRole }) || isUIAllowed('tableDelete', { roles: projectRole }))
"
:trigger="['click']"
@click.stop
@ -173,7 +173,7 @@ const canUserEditEmote = computed(() => {
<template #overlay>
<NcMenu>
<NcMenuItem
v-if="isUIAllowed('tableRename', false, projectRole)"
v-if="isUIAllowed('tableRename', { roles: projectRole })"
:data-testid="`sidebar-table-rename-${table.title}`"
@click="openRenameTableDialog(table, project.bases[baseIndex].id)"
>
@ -195,7 +195,7 @@ const canUserEditEmote = computed(() => {
</NcMenuItem>
<NcMenuItem
v-if="isUIAllowed('tableDelete', false, projectRole)"
v-if="isUIAllowed('tableDelete', { roles: projectRole })"
:data-testid="`sidebar-table-delete-${table.title}`"
class="!text-red-500 !hover:bg-red-50"
@click="isTableDeleteDialogVisible = true"

4
packages/nc-gui/components/dashboard/TreeView/index.vue

@ -20,14 +20,14 @@ import {
useNuxtApp,
useProject,
useProjects,
useRoles,
useTablesStore,
useTabs,
useUIPermission,
} from '#imports'
import { useRouter } from '#app'
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const { addTab } = useTabs()

2
packages/nc-gui/components/general/AddBaseButton.vue

@ -1,7 +1,7 @@
<script setup lang="ts">
import { iconMap } from '#imports'
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const { t } = useI18n()

4
packages/nc-gui/components/general/ShareBaseButton.vue

@ -1,11 +1,11 @@
<script setup lang="ts">
import { isDrawerOrModalExist, isMac, useNuxtApp, useRoute, useUIPermission } from '#imports'
import { isDrawerOrModalExist, isMac, useNuxtApp, useRoles, useRoute } from '#imports'
const route = useRoute()
const showUserModal = ref(false)
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const { $e } = useNuxtApp()

4
packages/nc-gui/components/general/ShareProject.vue

@ -1,5 +1,5 @@
<script setup lang="ts">
import { isDrawerOrModalExist, isMac, useNuxtApp } from '#imports'
import { isDrawerOrModalExist, isMac, useNuxtApp, useRoles } from '#imports'
interface Props {
disabled?: boolean
@ -15,7 +15,7 @@ const { project } = storeToRefs(useProject())
const { $e } = useNuxtApp()
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
useEventListener(document, 'keydown', async (e: KeyboardEvent) => {
const cmdOrCtrl = isMac() ? e.metaKey : e.ctrlKey

7
packages/nc-gui/components/project/AllTables.vue

@ -1,5 +1,4 @@
<script lang="ts" setup>
import { stringifyRolesObj } from 'nocodb-sdk'
import type { BaseType, TableType } from 'nocodb-sdk'
import dayjs from 'dayjs'
@ -7,9 +6,7 @@ const { activeTables } = storeToRefs(useTablesStore())
const { openTable } = useTablesStore()
const { openedProject } = storeToRefs(useProjects())
const { isUIAllowed } = useUIPermission()
const { allRoles } = useRoles()
const { isUIAllowed } = useRoles()
const { $e } = useNuxtApp()
@ -74,7 +71,7 @@ function openTableCreateDialog(baseIndex?: number | undefined) {
<template>
<div class="nc-all-tables-view">
<div v-if="isUIAllowed('tableCreate', false, stringifyRolesObj(allRoles))" class="flex flex-row gap-x-6 pb-3 pt-6">
<div v-if="isUIAllowed('tableCreate')" class="flex flex-row gap-x-6 pb-3 pt-6">
<div class="nc-project-view-all-table-btn" data-testid="proj-view-btn__add-new-table" @click="openTableCreateDialog()">
<GeneralIcon icon="addOutlineBox" />
<div class="label">{{ $t('general.new') }} {{ $t('objects.table') }}</div>

2
packages/nc-gui/components/project/View.vue

@ -14,7 +14,7 @@ const route = router.currentRoute
return openedProject.value?.bases?.[0]
}) */
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const { isMobileMode } = useGlobal()

6
packages/nc-gui/components/smartsheet/Cell.vue

@ -252,7 +252,11 @@ onUnmounted(() => {
<LazyCellJson v-else-if="isJSON(column)" v-model="vModel" />
<LazyCellText v-else v-model="vModel" />
<div
v-if="(isLocked || (isPublic && readOnly && !isForm) || isSystemColumn(column)) && !isAttachment(column) && !isTextArea(column)"
v-if="
(isLocked || (isPublic && readOnly && !isForm) || isSystemColumn(column)) &&
!isAttachment(column) &&
!isTextArea(column)
"
class="nc-locked-overlay"
/>
</template>

2
packages/nc-gui/components/smartsheet/Details.vue

@ -2,7 +2,7 @@
const { openedViewsTab } = storeToRefs(useViewsStore())
const { onViewsTabChange } = useViewsStore()
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const openedSubTab = computed({
get() {

4
packages/nc-gui/components/smartsheet/Form.vue

@ -22,7 +22,7 @@ import {
useGlobal,
useI18n,
useNuxtApp,
useUIPermission,
useRoles,
useViewColumns,
useViewData,
watch,
@ -42,7 +42,7 @@ const formRef = ref()
const { $api, $e } = useNuxtApp()
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const formState = reactive({})

2
packages/nc-gui/components/smartsheet/Gallery.vue

@ -84,7 +84,7 @@ const isRowEmpty = (record: any, col: any) => {
return Array.isArray(val) && val.length === 0
}
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const hasEditPermission = computed(() => isUIAllowed('dataEdit'))
// TODO: extract this code (which is duplicated in grid and gallery) into a separate component
const _contextMenu = ref(false)

2
packages/nc-gui/components/smartsheet/Kanban.vue

@ -89,7 +89,7 @@ const {
const { isViewDataLoading } = storeToRefs(useViewsStore())
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const { appInfo } = useGlobal()

4
packages/nc-gui/components/smartsheet/Toolbar.vue

@ -1,5 +1,5 @@
<script setup lang="ts">
import { IsPublicInj, inject, ref, useSharedView, useSmartsheetStoreOrThrow, useUIPermission, useViewsStore } from '#imports'
import { IsPublicInj, inject, ref, useRoles, useSharedView, useSmartsheetStoreOrThrow, useViewsStore } from '#imports'
const { isGrid, isGallery, isKanban, isMap } = useSmartsheetStoreOrThrow()
@ -9,7 +9,7 @@ const { isViewsLoading } = storeToRefs(useViewsStore())
const { isMobileMode } = useGlobal()
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const { allowCSVDownload } = useSharedView()

15
packages/nc-gui/components/smartsheet/expanded-form/Comments.vue

@ -1,7 +1,18 @@
<script setup lang="ts">
import type { VNodeRef } from '@vue/runtime-core'
import type { AuditType } from 'nocodb-sdk'
import { enumColor, iconMap, ref, timeAgo, useCopy, useExpandedFormStoreOrThrow, useGlobal, useI18n, watch } from '#imports'
import {
enumColor,
iconMap,
ref,
timeAgo,
useCopy,
useExpandedFormStoreOrThrow,
useGlobal,
useI18n,
useRoles,
watch,
} from '#imports'
const { loadCommentsAndLogs, commentsAndLogs, isCommentsLoading, commentsOnly, saveComment, isYou, comment, updateComment } =
useExpandedFormStoreOrThrow()
@ -18,7 +29,7 @@ const { t } = useI18n()
const { user } = useGlobal()
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const hasEditPermission = computed(() => isUIAllowed('commentEdit'))

4
packages/nc-gui/components/smartsheet/expanded-form/Header.vue

@ -7,9 +7,9 @@ import {
isMac,
useCopy,
useExpandedFormStoreOrThrow,
useRoles,
useSmartsheetRowStoreOrThrow,
useSmartsheetStoreOrThrow,
useUIPermission,
} from '#imports'
const props = defineProps<{ view?: ViewType }>()
@ -24,7 +24,7 @@ const { commentsDrawer, displayValue, primaryKey, save: _save, loadRow, deleteRo
const { isNew, syncLTARRefs, state } = useSmartsheetRowStoreOrThrow()
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const reloadTrigger = inject(ReloadRowDataHookInj, createEventHook())

3
packages/nc-gui/components/smartsheet/expanded-form/index.vue

@ -21,6 +21,7 @@ import {
useActiveKeyupListener,
useProvideExpandedFormStore,
useProvideSmartsheetStore,
useRoles,
useRouter,
useVModel,
watch,
@ -57,7 +58,7 @@ const meta = toRef(props, 'meta')
const router = useRouter()
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
// override cell click hook to avoid unexpected behavior at form fields
provide(CellClickHookInj, undefined)

4
packages/nc-gui/components/smartsheet/grid/Table.vue

@ -30,9 +30,9 @@ import {
useI18n,
useMultiSelect,
useNuxtApp,
useRoles,
useRoute,
useSmartsheetStoreOrThrow,
useUIPermission,
useUndoRedo,
useViewsStore,
watch,
@ -171,7 +171,7 @@ const cellRefs = ref<{ el: HTMLElement }[]>([])
const gridRect = useElementBounding(gridWrapper)
// #Permissions
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const hasEditPermission = computed(() => isUIAllowed('dataEdit'))
const isAddingColumnAllowed = computed(() => !readOnly.value && !isLocked.value && isUIAllowed('fieldAdd') && !isSqlView.value)

4
packages/nc-gui/components/smartsheet/header/Cell.vue

@ -1,6 +1,6 @@
<script setup lang="ts">
import type { ColumnReqType, ColumnType } from 'nocodb-sdk'
import { ColumnInj, IsExpandedFormOpenInj, IsFormInj, IsKanbanInj, inject, provide, ref, toRef, useUIPermission } from '#imports'
import { ColumnInj, IsExpandedFormOpenInj, IsFormInj, IsKanbanInj, inject, provide, ref, toRef, useRoles } from '#imports'
interface Props {
column: ColumnType
@ -23,7 +23,7 @@ const isKanban = inject(IsKanbanInj, ref(false))
const column = toRef(props, 'column')
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
provide(ColumnInj, column)

4
packages/nc-gui/components/smartsheet/header/VirtualCell.vue

@ -19,7 +19,7 @@ import {
toRef,
useI18n,
useMetas,
useUIPermission,
useRoles,
} from '#imports'
const props = defineProps<{ column: ColumnType; hideMenu?: boolean; required?: boolean | number; hideIcon?: boolean }>()
@ -38,7 +38,7 @@ provide(ColumnInj, column)
const { metas } = useMetas()
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const meta = inject(MetaInj, ref())

4
packages/nc-gui/components/smartsheet/sidebar/RenameableMenuItem.vue

@ -2,7 +2,7 @@
import type { VNodeRef } from '@vue/runtime-core'
import type { KanbanType, ViewType, ViewTypes } from 'nocodb-sdk'
import type { WritableComputedRef } from '@vue/reactivity'
import { IsLockedInj, inject, message, onKeyStroke, useDebounceFn, useNuxtApp, useUIPermission, useVModel } from '#imports'
import { IsLockedInj, inject, message, onKeyStroke, useDebounceFn, useNuxtApp, useRoles, useVModel } from '#imports'
interface Props {
view: ViewType
@ -31,7 +31,7 @@ const vModel = useVModel(props, 'view', emits) as WritableComputedRef<ViewType &
const { $e } = useNuxtApp()
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const activeView = inject(ActiveViewInj, ref())

4
packages/nc-gui/components/smartsheet/sidebar/index.vue

@ -10,9 +10,9 @@ import {
useCommandPalette,
useDialog,
useNuxtApp,
useRoles,
useRoute,
useRouter,
useUIPermission,
useViewsStore,
watch,
} from '#imports'
@ -39,7 +39,7 @@ const setLastOpenedViewId = (viewId?: string) => {
}
}
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const router = useRouter()

2
packages/nc-gui/components/smartsheet/sidebar/toolbar/Developer.vue

@ -1,7 +1,7 @@
<script setup lang="ts">
const { isMobileMode } = useGlobal()
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const isPublicView = inject(IsPublicInj, ref(false))

4
packages/nc-gui/components/smartsheet/toolbar/KanbanStackEditOrAdd.vue

@ -9,10 +9,10 @@ import {
ref,
useKanbanViewStoreOrThrow,
useMenuCloseOnEsc,
useUIPermission,
useRoles,
} from '#imports'
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const { groupingFieldColumn } = useKanbanViewStoreOrThrow()

4
packages/nc-gui/components/smartsheet/toolbar/MoreActions.vue

@ -15,9 +15,9 @@ import {
useI18n,
useNuxtApp,
useProject,
useRoles,
useSharedView,
useSmartsheetStoreOrThrow,
useUIPermission,
} from '#imports'
const { t } = useI18n()
@ -48,7 +48,7 @@ const showWebhookDrawer = ref(false)
const quickImportDialog = ref(false)
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const exportFile = async (exportType: ExportTypes) => {
let offset = 0

4
packages/nc-gui/components/smartsheet/toolbar/ShareView.vue

@ -16,8 +16,8 @@ import {
useI18n,
useNuxtApp,
useProject,
useRoles,
useSmartsheetStoreOrThrow,
useUIPermission,
watch,
} from '#imports'
import type { SharedView } from '#imports'
@ -32,7 +32,7 @@ const { $e } = useNuxtApp()
const { dashboardUrl } = useDashboard()
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const { isSharedBase } = storeToRefs(useProject())

4
packages/nc-gui/components/smartsheet/toolbar/ViewActions.vue

@ -17,8 +17,8 @@ import {
useMenuCloseOnEsc,
useNuxtApp,
useProject,
useRoles,
useSmartsheetStoreOrThrow,
useUIPermission,
} from '#imports'
const { t } = useI18n()
@ -56,7 +56,7 @@ const quickImportDialogs: Record<(typeof quickImportDialogTypes)[number], Ref<bo
{},
) as Record<QuickImportDialogType, Ref<boolean>>
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
useProject()

4
packages/nc-gui/components/tabs/Auth.vue

@ -1,6 +1,6 @@
<script setup lang="ts">
import type { ComputedRef } from 'vue'
import { computed, useI18n, useUIPermission } from '#imports'
import { computed, useI18n, useRoles } from '#imports'
interface Tab {
title: string
@ -10,7 +10,7 @@ interface Tab {
const { t } = useI18n()
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const tabsInfo: Tab[] = [
{

4
packages/nc-gui/components/tabs/Smartsheet.vue

@ -21,8 +21,8 @@ import {
useMetas,
useProvideKanbanViewStore,
useProvideSmartsheetStore,
useRoles,
useSqlEditor,
useUIPermission,
} from '#imports'
import type { TabItem } from '#imports'
@ -30,7 +30,7 @@ const props = defineProps<{
activeTab: TabItem
}>()
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const { metas, getMeta } = useMetas()

4
packages/nc-gui/components/tabs/auth/UserManagement.vue

@ -14,7 +14,7 @@ import {
useI18n,
useNuxtApp,
useProject,
useUIPermission,
useRoles,
watchDebounced,
} from '#imports'
import type { User } from '#imports'
@ -29,7 +29,7 @@ const { project } = storeToRefs(useProject())
const { copy } = useCopy()
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const { dashboardUrl } = useDashboard()

4
packages/nc-gui/components/virtual-cell/BelongsTo.vue

@ -16,9 +16,9 @@ import {
inject,
ref,
useProvideLTARStore,
useRoles,
useSelectedCellKeyupListener,
useSmartsheetRowStoreOrThrow,
useUIPermission,
} from '#imports'
const column = inject(ColumnInj)!
@ -39,7 +39,7 @@ const isLocked = inject(IsLockedInj, ref(false))
const isUnderLookup = inject(IsUnderLookupInj, ref(false))
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const listItemsDlg = ref(false)

4
packages/nc-gui/components/virtual-cell/HasMany.vue

@ -14,9 +14,9 @@ import {
inject,
ref,
useProvideLTARStore,
useRoles,
useSelectedCellKeyupListener,
useSmartsheetRowStoreOrThrow,
useUIPermission,
} from '#imports'
const column = inject(ColumnInj)!
@ -39,7 +39,7 @@ const listItemsDlg = ref(false)
const childListDlg = ref(false)
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const { state, isNew, removeLTARRef } = useSmartsheetRowStoreOrThrow()

2
packages/nc-gui/components/virtual-cell/Links.vue

@ -27,7 +27,7 @@ const listItemsDlg = ref(false)
const childListDlg = ref(false)
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const { state, isNew } = useSmartsheetRowStoreOrThrow()

4
packages/nc-gui/components/virtual-cell/ManyToMany.vue

@ -16,9 +16,9 @@ import {
inject,
ref,
useProvideLTARStore,
useRoles,
useSelectedCellKeyupListener,
useSmartsheetRowStoreOrThrow,
useUIPermission,
} from '#imports'
const column = inject(ColumnInj)!
@ -41,7 +41,7 @@ const listItemsDlg = ref(false)
const childListDlg = ref(false)
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const { state, isNew, removeLTARRef } = useSmartsheetRowStoreOrThrow()

2
packages/nc-gui/components/virtual-cell/components/ItemChip.vue

@ -28,7 +28,7 @@ const emit = defineEmits(['unlink'])
const { relatedTableMeta } = useLTARStoreOrThrow()!
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const readOnly = inject(ReadonlyInj, ref(false))

4
packages/nc-gui/components/workspace/CreateProjectBtn.vue

@ -10,7 +10,7 @@ const props = defineProps<{
centered?: boolean
}>()
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const { orgRoles, workspaceRoles } = useRoles()
@ -28,7 +28,7 @@ const centered = computed(() => props.centered ?? true)
<template>
<NcButton
v-if="isUIAllowed('projectCreate', false, workspaceRoles ?? orgRoles) && !isSharedBase"
v-if="isUIAllowed('projectCreate', { roles: workspaceRoles ?? orgRoles }) && !isSharedBase"
type="text"
:size="size"
:centered="centered"

10
packages/nc-gui/components/workspace/ProjectList.vue

@ -20,7 +20,7 @@ const { navigateToProject } = useGlobal()
const { $e, $jobs } = useNuxtApp()
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const { refreshCommandPalette } = useCommandPalette()
@ -326,7 +326,7 @@ const setIcon = async (icon: string, project: ProjectType) => {
<template v-if="column.dataIndex === 'id'">
<a-dropdown
v-if="isUIAllowed('projectActionMenu', true, [record.workspace_role, record.project_role].join())"
v-if="isUIAllowed('projectActionMenu', { roles: [record.workspace_role, record.project_role].join() })"
:trigger="['click']"
>
<div @click.stop>
@ -346,7 +346,7 @@ const setIcon = async (icon: string, project: ProjectType) => {
<a-menu-item
v-if="
record.type === NcProjectType.DB &&
isUIAllowed('projectDuplicate', true, [record.workspace_role, record.project_role].join())
isUIAllowed('projectDuplicate', { roles: [record.workspace_role, record.project_role].join() })
"
@click="duplicateProject(record)"
>
@ -357,7 +357,7 @@ const setIcon = async (icon: string, project: ProjectType) => {
</a-menu-item>
<!--
<a-menu-item
v-if="false && isUIAllowed('projectMove', true, [record.workspace_role, record.project_role].join())"
v-if="false && isUIAllowed('projectMove', { roles: [record.workspace_role, record.project_role].join() })"
@click="moveProject(record)"
>
<div class="nc-menu-item-wrapper">
@ -367,7 +367,7 @@ const setIcon = async (icon: string, project: ProjectType) => {
</a-menu-item>
-->
<a-menu-item
v-if="isUIAllowed('projectDelete', true, [record.workspace_role, record.project_role].join())"
v-if="isUIAllowed('projectDelete', { roles: [record.workspace_role, record.project_role].join() })"
@click="deleteProject(record)"
>
<div class="nc-menu-item-wrapper text-red-500">

2
packages/nc-gui/composables/useExpandedFormStore.ts

@ -57,7 +57,7 @@ const [useProvideExpandedFormStore, useExpandedFormStore] = useInjectionState((m
const reloadTrigger = inject(ReloadRowDataHookInj, createEventHook())
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
// getters
const displayValue = computed(() => {

15
packages/nc-gui/composables/useGridViewColumn.ts

@ -1,23 +1,12 @@
import type { ColumnType, GridColumnReqType, GridColumnType, ViewType } from 'nocodb-sdk'
import type { Ref } from 'vue'
import {
IsPublicInj,
computed,
inject,
ref,
useMetas,
useNuxtApp,
useStyleTag,
useUIPermission,
useUndoRedo,
watch,
} from '#imports'
import { IsPublicInj, computed, inject, ref, useMetas, useNuxtApp, useRoles, useStyleTag, useUndoRedo, watch } from '#imports'
const [useProvideGridViewColumn, useGridViewColumn] = useInjectionState(
(view: Ref<(ViewType & { columns?: GridColumnType[] }) | undefined>, statePublic = false) => {
const { css, load: loadCss, unload: unloadCss } = useStyleTag('')
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const { $api } = useNuxtApp()

4
packages/nc-gui/composables/useKanbanViewStore.ts

@ -21,9 +21,9 @@ import {
useInjectionState,
useNuxtApp,
useProject,
useRoles,
useSharedView,
useSmartsheetStoreOrThrow,
useUIPermission,
useUndoRedo,
} from '#imports'
@ -51,7 +51,7 @@ const [useProvideKanbanViewStore, useKanbanViewStore] = useInjectionState(
const { sharedView, fetchSharedViewData, fetchSharedViewGroupedData } = useSharedView()
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const isPublic = ref(shared) || inject(IsPublicInj, ref(false))

2
packages/nc-gui/composables/useMapViewDataStore.ts

@ -34,7 +34,7 @@ const [useProvideMapViewStore, useMapViewStore] = useInjectionState(
const { $api } = useNuxtApp()
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const isPublic = ref(shared) || inject(IsPublicInj, ref(false))

45
packages/nc-gui/composables/useRoles/index.ts

@ -1,6 +1,22 @@
import type { RolesObj } from 'nocodb-sdk'
import { isString } from '@vue/shared'
import type { Roles, RolesObj, WorkspaceUserRoles } from 'nocodb-sdk'
import { extractRolesObj } from 'nocodb-sdk'
import { computed, createSharedComposable, useApi, useGlobal } from '#imports'
import { computed, createSharedComposable, rolePermissions, useApi, useGlobal } from '#imports'
import type { Permission } from '#imports'
const hasPermission = (role: Exclude<Roles, WorkspaceUserRoles>, hasRole: boolean, permission: Permission | string) => {
const rolePermission = rolePermissions[role]
if (!hasRole || !rolePermission) return false
if (isString(rolePermission) && rolePermission === '*') return true
if ('include' in rolePermission && rolePermission.include) {
return !!rolePermission.include[permission as keyof typeof rolePermission.include]
}
return rolePermission[permission as keyof typeof rolePermission]
}
/**
* Provides the roles a user currently has
@ -74,7 +90,7 @@ export const useRoles = createSharedComposable(() => {
...user.value,
roles: res.roles,
project_roles: res.project_roles,
}
} as typeof User
} else if (options?.isSharedErd) {
const res = await api.auth.me(
{
@ -91,7 +107,7 @@ export const useRoles = createSharedComposable(() => {
...user.value,
roles: res.roles,
project_roles: res.project_roles,
}
} as typeof User
} else if (projectId) {
const res = await api.auth.me({ project_id: projectId })
@ -99,9 +115,28 @@ export const useRoles = createSharedComposable(() => {
...user.value,
roles: res.roles,
project_roles: res.project_roles,
} as typeof User
}
}
const isUIAllowed = (
permission: Permission | string,
args: { roles?: string | Record<string, boolean> | string[] | null } = {},
) => {
const { roles } = args
let checkRoles: Record<string, boolean> = {}
if (!roles) {
if (allRoles.value) checkRoles = allRoles.value
} else {
checkRoles = extractRolesObj(roles)
}
return Object.entries(checkRoles).some(([role, hasRole]) =>
hasPermission(role as Exclude<Roles, WorkspaceUserRoles>, hasRole, permission),
)
}
return { allRoles, orgRoles, workspaceRoles, projectRoles, loadRoles }
return { allRoles, orgRoles, workspaceRoles, projectRoles, loadRoles, isUIAllowed }
})

51
packages/nc-gui/composables/useUIPermission/index.ts

@ -1,51 +0,0 @@
import { isString } from '@vue/shared'
import type { Roles } from 'nocodb-sdk'
import { extractRolesObj } from 'nocodb-sdk'
import { createSharedComposable, rolePermissions, useGlobal, useRoles } from '#imports'
import type { Permission } from '#imports'
const hasPermission = (role: Roles, hasRole: boolean, permission: Permission | string) => {
const rolePermission = rolePermissions[role]
if (!hasRole || !rolePermission) return false
if (isString(rolePermission) && rolePermission === '*') return true
if ('include' in rolePermission && rolePermission.include) {
return !!rolePermission.include[permission as keyof typeof rolePermission.include]
}
return rolePermission[permission as keyof typeof rolePermission]
}
export const useUIPermission = createSharedComposable(() => {
const { previewAs } = useGlobal()
const { allRoles } = useRoles()
const isUIAllowed = (
permission: Permission | string,
skipPreviewAs = false,
userRoles: string | Record<string, boolean> | string[] | null = null,
combineWithStateRoles = false,
) => {
if (previewAs.value && !skipPreviewAs) {
return hasPermission(previewAs.value, true, permission)
}
let roles: Record<string, boolean> = {}
if (!userRoles) {
if (allRoles.value) roles = allRoles.value
} else {
roles = extractRolesObj(userRoles)
}
if (userRoles && combineWithStateRoles) {
roles = { ...roles, ...allRoles.value }
}
return Object.entries(roles).some(([role, hasRole]) => hasPermission(role as Roles, hasRole, permission))
}
return { isUIAllowed }
})

0
packages/nc-gui/composables/useUIPermission/rolePermissions.ts

15
packages/nc-gui/composables/useViewColumns.ts

@ -1,18 +1,7 @@
import { ViewTypes, isSystemColumn } from 'nocodb-sdk'
import type { ColumnType, MapType, TableType, ViewType } from 'nocodb-sdk'
import type { ComputedRef, Ref } from 'vue'
import {
IsPublicInj,
computed,
inject,
ref,
storeToRefs,
useNuxtApp,
useProject,
useUIPermission,
useUndoRedo,
watch,
} from '#imports'
import { IsPublicInj, computed, inject, ref, storeToRefs, useNuxtApp, useProject, useRoles, useUndoRedo, watch } from '#imports'
import type { Field } from '#imports'
export function useViewColumns(
@ -28,7 +17,7 @@ export function useViewColumns(
const { $api, $e } = useNuxtApp()
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const { isSharedBase } = storeToRefs(useProject())

4
packages/nc-gui/composables/useViewData.ts

@ -16,11 +16,11 @@ import {
useI18n,
useNuxtApp,
useProject,
useRoles,
useRouter,
useSharedView,
useSmartsheetStoreOrThrow,
useState,
useUIPermission,
} from '#imports'
import type { Row } from '#imports'
@ -76,7 +76,7 @@ export function useViewData(
const { sorts, nestedFilters } = useSmartsheetStoreOrThrow()
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const routeQuery = computed(() => route.value.query as Record<string, string>)

4
packages/nc-gui/composables/useViewFilters.ts

@ -16,7 +16,7 @@ import {
useMetas,
useNuxtApp,
useProject,
useUIPermission,
useRoles,
watch,
} from '#imports'
import type { Filter, UndoRedoAction } from '#imports'
@ -42,7 +42,7 @@ export function useViewFilters(
const { $api, $e } = useNuxtApp()
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const { metas } = useMetas()

2
packages/nc-gui/composables/useViewGroupBy.ts

@ -34,7 +34,7 @@ export const useViewGroupBy = (view: Ref<ViewType | undefined>, where?: Computed
const isGroupBy = computed(() => !!groupBy.value.length)
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const { sorts, nestedFilters } = useSmartsheetStoreOrThrow()

4
packages/nc-gui/composables/useViewSorts.ts

@ -12,9 +12,9 @@ import {
storeToRefs,
useNuxtApp,
useProject,
useRoles,
useSharedView,
useSmartsheetStoreOrThrow,
useUIPermission,
} from '#imports'
export function useViewSorts(view: Ref<ViewType | undefined>, reloadData?: () => void) {
@ -24,7 +24,7 @@ export function useViewSorts(view: Ref<ViewType | undefined>, reloadData?: () =>
const { $api, $e } = useNuxtApp()
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const { isSharedBase } = storeToRefs(useProject())

148
packages/nc-gui/lib/acl.ts

@ -0,0 +1,148 @@
import { OrgUserRoles, ProjectRoles } from 'nocodb-sdk'
const roleScopes = {
org: [OrgUserRoles.VIEWER, OrgUserRoles.CREATOR],
project: [ProjectRoles.VIEWER, ProjectRoles.COMMENTER, ProjectRoles.EDITOR, ProjectRoles.CREATOR, ProjectRoles.OWNER],
}
interface Perm {
include?: Record<string, boolean>
}
/**
* Each permission value means the following
* `*` - which is wildcard, means all permissions are allowed
* `include` - which is an object, means only the permissions listed in the object are allowed
* `undefined` or `{}` - which is the default value, means no permissions are allowed
* */
const rolePermissions = {
// org level role permissions
[OrgUserRoles.SUPER_ADMIN]: '*',
[OrgUserRoles.CREATOR]: {
include: {
projectCreate: true,
projectMove: true,
projectDelete: true,
projectDuplicate: true,
newUser: true,
},
},
[OrgUserRoles.VIEWER]: {
include: {
importRequest: true,
},
},
// Project role permissions
[ProjectRoles.OWNER]: {
include: {
projectDelete: true,
},
},
[ProjectRoles.CREATOR]: {
include: {
baseCreate: true,
fieldUpdate: true,
hookList: true,
tableCreate: true,
tableRename: true,
tableDelete: true,
tableDuplicate: true,
tableSort: true,
layoutRename: true,
layoutDelete: true,
airtableImport: true,
jsonImport: true,
excelImport: true,
settingsPage: true,
newUser: true,
webhook: true,
fieldEdit: true,
fieldAdd: true,
tableIconEdit: true,
viewCreateOrEdit: true,
viewShare: true,
projectShare: true,
},
},
[ProjectRoles.EDITOR]: {
include: {
dataInsert: true,
dataEdit: true,
sortSync: true,
filterSync: true,
filterChildrenRead: true,
viewFieldEdit: true,
csvImport: true,
apiDocs: true,
},
},
[ProjectRoles.COMMENTER]: {
include: {
commentEdit: true,
commentList: true,
commentCount: true,
},
},
[ProjectRoles.VIEWER]: {
include: {
projectSettings: true,
expandedForm: true,
},
},
[ProjectRoles.NO_ACCESS]: {
include: {},
},
} as Record<OrgUserRoles | ProjectRoles, Perm | '*'>
/*
We inherit include permissions from previous roles in the same scope (role order)
To determine role order, we use `roleScopes` object
So for example ProjectRoles.COMMENTER has `commentEdit` permission,
which means ProjectRoles.EDITOR, ProjectRoles.CREATOR, ProjectRoles.OWNER will also have `commentEdit` permission
where as ProjectRoles.VIEWER, ProjectRoles.NO_ACCESS will not have `commentEdit` permission.
This is why we are validating that there are no duplicate permissions within the same scope
even though it is not required for the code to work. It is to keep the code clean and easy to understand.
*/
// validate no duplicate permissions within same scope
Object.values(roleScopes).forEach((roles) => {
const scopePermissions: Record<string, boolean> = {}
const duplicates: string[] = []
roles.forEach((role) => {
const perms = (rolePermissions[role] as Perm).include || {}
Object.keys(perms).forEach((perm) => {
if (scopePermissions[perm]) {
duplicates.push(perm)
}
scopePermissions[perm] = true
})
})
if (duplicates.length) {
throw new Error(
`Duplicate permissions found in roles ${roles.join(', ')}. Please remove duplicate permissions: ${duplicates.join(', ')}`,
)
}
})
// inherit include permissions within scope (role order)
Object.values(roleScopes).forEach((roles) => {
let roleIndex = 0
for (const role of roles) {
if (roleIndex === 0) {
roleIndex++
continue
}
if (rolePermissions[role] === '*') continue
if ((rolePermissions[role] as Perm).include && (rolePermissions[roles[roleIndex - 1]] as Perm).include) {
Object.assign((rolePermissions[role] as Perm).include!, (rolePermissions[roles[roleIndex - 1]] as Perm).include)
}
roleIndex++
}
})
export { rolePermissions }

149
packages/nc-gui/lib/constants.ts

@ -1,5 +1,3 @@
import { OrgUserRoles, ProjectRoles } from 'nocodb-sdk'
export const NOCO = 'noco'
export const SYSTEM_COLUMNS = ['id', 'title', 'created_at', 'updated_at']
@ -18,150 +16,3 @@ export const GROUP_BY_VARS = {
__nc_false__: 'Unchecked',
} as Record<string, string>,
}
const roleScopes = {
org: [OrgUserRoles.VIEWER, OrgUserRoles.CREATOR],
project: [ProjectRoles.VIEWER, ProjectRoles.COMMENTER, ProjectRoles.EDITOR, ProjectRoles.CREATOR, ProjectRoles.OWNER],
}
interface Perm {
include?: Record<string, boolean>
}
/**
* Each permission value means the following
* `*` - which is wildcard, means all permissions are allowed
* `include` - which is an object, means only the permissions listed in the object are allowed
* `undefined` or `{}` - which is the default value, means no permissions are allowed
* */
const rolePermissions = {
// org level role permissions
[OrgUserRoles.SUPER_ADMIN]: '*',
[OrgUserRoles.CREATOR]: {
include: {
projectCreate: true,
projectMove: true,
projectDelete: true,
projectDuplicate: true,
newUser: true,
},
},
[OrgUserRoles.VIEWER]: {
include: {
importRequest: true,
},
},
// Project role permissions
[ProjectRoles.OWNER]: {
include: {
projectDelete: true,
},
},
[ProjectRoles.CREATOR]: {
include: {
baseCreate: true,
fieldUpdate: true,
hookList: true,
tableCreate: true,
tableRename: true,
tableDelete: true,
tableDuplicate: true,
tableSort: true,
layoutRename: true,
layoutDelete: true,
airtableImport: true,
jsonImport: true,
excelImport: true,
settingsPage: true,
newUser: true,
webhook: true,
fieldEdit: true,
fieldAdd: true,
tableIconEdit: true,
viewCreateOrEdit: true,
viewShare: true,
projectShare: true,
},
},
[ProjectRoles.EDITOR]: {
include: {
dataInsert: true,
dataEdit: true,
sortSync: true,
filterSync: true,
filterChildrenRead: true,
viewFieldEdit: true,
csvImport: true,
apiDocs: true,
},
},
[ProjectRoles.COMMENTER]: {
include: {
commentEdit: true,
commentList: true,
commentCount: true,
},
},
[ProjectRoles.VIEWER]: {
include: {
projectSettings: true,
expandedForm: true,
},
},
[ProjectRoles.NO_ACCESS]: {
include: {},
},
} as Record<OrgUserRoles | ProjectRoles, Perm | '*'>
/*
We inherit include permissions from previous roles in the same scope (role order)
To determine role order, we use `roleScopes` object
So for example ProjectRoles.COMMENTER has `commentEdit` permission,
which means ProjectRoles.EDITOR, ProjectRoles.CREATOR, ProjectRoles.OWNER will also have `commentEdit` permission
where as ProjectRoles.VIEWER, ProjectRoles.NO_ACCESS will not have `commentEdit` permission.
This is why we are validating that there are no duplicate permissions within the same scope
even though it is not required for the code to work. It is to keep the code clean and easy to understand.
*/
// validate no duplicate permissions within same scope
Object.values(roleScopes).forEach((roles) => {
const scopePermissions: Record<string, boolean> = {}
const duplicates: string[] = []
roles.forEach((role) => {
const perms = (rolePermissions[role] as Perm).include || {}
Object.keys(perms).forEach((perm) => {
if (scopePermissions[perm]) {
duplicates.push(perm)
}
scopePermissions[perm] = true
})
})
if (duplicates.length) {
throw new Error(
`Duplicate permissions found in roles ${roles.join(', ')}. Please remove duplicate permissions: ${duplicates.join(', ')}`,
)
}
})
// inherit include permissions within scope (role order)
Object.values(roleScopes).forEach((roles) => {
let roleIndex = 0
for (const role of roles) {
if (roleIndex === 0) {
roleIndex++
continue
}
if (rolePermissions[role] === '*') continue
if ((rolePermissions[role] as Perm).include && (rolePermissions[roles[roleIndex - 1]] as Perm).include) {
Object.assign((rolePermissions[role] as Perm).include!, (rolePermissions[roles[roleIndex - 1]] as Perm).include)
}
roleIndex++
}
})
export { rolePermissions }

1
packages/nc-gui/lib/index.ts

@ -1,3 +1,4 @@
export * from './constants'
export * from './enums'
export * from './types'
export * from './acl'

4
packages/nc-gui/pages/account/index.vue

@ -1,11 +1,11 @@
<script lang="ts" setup>
import { iconMap, navigateTo, useUIPermission } from '#imports'
import { iconMap, navigateTo, useRoles } from '#imports'
definePageMeta({
hideHeader: true,
})
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const $route = useRoute()

4
packages/nc-gui/pages/account/index/users/[[nestedPage]].vue

@ -1,7 +1,7 @@
<script setup lang="ts">
import { useUIPermission } from '#imports'
import { useRoles } from '#imports'
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
</script>
<template>

2
packages/nc-gui/store/projectsShortcuts.ts

@ -3,7 +3,7 @@ import { isDrawerOrModalExist, useEventListener } from '#imports'
export const useProjectsShortcuts = defineStore('projectsShortcutsStore', () => {
const { $e } = useNuxtApp()
const { isUIAllowed } = useUIPermission()
const { isUIAllowed } = useRoles()
const isMounted = ref(false)

4
packages/nocodb-sdk/src/lib/helperFunctions.ts

@ -1,5 +1,5 @@
import UITypes from './UITypes';
import { Roles, RolesObj, RolesType } from './globals';
import { RolesObj, RolesType } from './globals';
// import {RelationTypes} from "./globals";
@ -39,7 +39,7 @@ const extractRolesObj = (roles: RolesType): RolesObj => {
}, {});
};
const stringifyRolesObj = (roles?: Roles | null): string => {
const stringifyRolesObj = (roles?: RolesObj | null): string => {
if (!roles) return '';
const rolesArr = Object.keys(roles).filter((r) => roles[r]);
return rolesArr.join(',');

Loading…
Cancel
Save