From d36c7d744fd0294a9012c6c0569d5dde1777d615 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 31 Aug 2023 10:24:52 +0200 Subject: [PATCH 01/41] Rename --- .../backend/DataTable/buttons/ManageAccessButton.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/buttons/ManageAccessButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/ManageAccessButton.svelte index f6a74784fa..5a7e274eb8 100644 --- a/packages/builder/src/components/backend/DataTable/buttons/ManageAccessButton.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/ManageAccessButton.svelte @@ -10,13 +10,13 @@ let modal let resourcePermissions - async function openDropdown() { + async function openModal() { resourcePermissions = await permissions.forResource(resourceId) modal.show() } - + Access From dcd8c3b2896ed5da567c64d305e2b62df8357f33 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 31 Aug 2023 10:36:17 +0200 Subject: [PATCH 02/41] Move permissions code to sdk --- .../server/src/api/controllers/permission.ts | 26 +------------ .../server/src/sdk/app/permissions/index.ts | 38 ++++++++++++++++++- 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/packages/server/src/api/controllers/permission.ts b/packages/server/src/api/controllers/permission.ts index 8314f29398..ef138ee5c2 100644 --- a/packages/server/src/api/controllers/permission.ts +++ b/packages/server/src/api/controllers/permission.ts @@ -147,31 +147,7 @@ export async function fetch(ctx: UserCtx) { export async function getResourcePerms(ctx: UserCtx) { const resourceId = ctx.params.resourceId - const db = context.getAppDB() - const body = await db.allDocs( - getRoleParams(null, { - include_docs: true, - }) - ) - const rolesList = body.rows.map(row => row.doc) - let permissions: Record = {} - for (let level of SUPPORTED_LEVELS) { - // update the various roleIds in the resource permissions - for (let role of rolesList) { - const rolePerms = roles.checkForRoleResourceArray( - role.permissions, - resourceId - ) - if ( - rolePerms && - rolePerms[resourceId] && - rolePerms[resourceId].indexOf(level) !== -1 - ) { - permissions[level] = roles.getExternalRoleID(role._id, role.version)! - } - } - } - ctx.body = Object.assign(getBasePermissions(resourceId), permissions) + ctx.body = await sdk.permissions.getResourcePerms(resourceId) } export async function addPermission(ctx: UserCtx) { diff --git a/packages/server/src/sdk/app/permissions/index.ts b/packages/server/src/sdk/app/permissions/index.ts index 2219120db6..144b4fab2b 100644 --- a/packages/server/src/sdk/app/permissions/index.ts +++ b/packages/server/src/sdk/app/permissions/index.ts @@ -1,10 +1,15 @@ +import { context, roles } from "@budibase/backend-core" +import { features } from "@budibase/pro" import { DocumentType, PermissionLevel, VirtualDocumentType, } from "@budibase/types" -import { isViewID } from "../../../db/utils" -import { features } from "@budibase/pro" +import { getRoleParams, isViewID } from "../../../db/utils" +import { + CURRENTLY_SUPPORTED_LEVELS, + getBasePermissions, +} from "../../../utilities/security" type ResourceActionAllowedResult = | { allowed: true } @@ -35,3 +40,32 @@ export async function resourceActionAllowed({ resourceType: VirtualDocumentType.VIEW, } } + +export async function getResourcePerms(resourceId: string) { + const db = context.getAppDB() + const body = await db.allDocs( + getRoleParams(null, { + include_docs: true, + }) + ) + const rolesList = body.rows.map(row => row.doc) + let permissions: Record = {} + for (let level of CURRENTLY_SUPPORTED_LEVELS) { + // update the various roleIds in the resource permissions + for (let role of rolesList) { + const rolePerms = roles.checkForRoleResourceArray( + role.permissions, + resourceId + ) + if ( + rolePerms && + rolePerms[resourceId] && + rolePerms[resourceId].indexOf(level) !== -1 + ) { + permissions[level] = roles.getExternalRoleID(role._id, role.version)! + } + } + } + + return Object.assign(getBasePermissions(resourceId), permissions) +} From 566f9ecd22fc6caaaf0b9cb0ac332148db53607e Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 31 Aug 2023 10:53:25 +0200 Subject: [PATCH 03/41] Create remove permission js api --- packages/builder/src/stores/backend/permissions.js | 7 +++++++ packages/frontend-core/src/api/permissions.js | 13 +++++++++++++ 2 files changed, 20 insertions(+) diff --git a/packages/builder/src/stores/backend/permissions.js b/packages/builder/src/stores/backend/permissions.js index aaab406bc9..ea7bb347e2 100644 --- a/packages/builder/src/stores/backend/permissions.js +++ b/packages/builder/src/stores/backend/permissions.js @@ -13,6 +13,13 @@ export function createPermissionStore() { level, }) }, + remove: async ({ level, role, resource }) => { + return await API.removePermissionFromResource({ + resourceId: resource, + roleId: role, + level, + }) + }, forResource: async resourceId => { return await API.getPermissionForResource(resourceId) }, diff --git a/packages/frontend-core/src/api/permissions.js b/packages/frontend-core/src/api/permissions.js index 5407cb3ce5..9ba0be23cd 100644 --- a/packages/frontend-core/src/api/permissions.js +++ b/packages/frontend-core/src/api/permissions.js @@ -21,4 +21,17 @@ export const buildPermissionsEndpoints = API => ({ url: `/api/permission/${roleId}/${resourceId}/${level}`, }) }, + + /** + * Remove the the permissions for a certain resource + * @param resourceId the ID of the resource to update + * @param roleId the ID of the role to update the permissions of + * @param level the level to remove the role for this resource + * @return {Promise<*>} + */ + removePermissionFromResource: async ({ resourceId, roleId, level }) => { + return await API.delete({ + url: `/api/permission/${roleId}/${resourceId}/${level}`, + }) + }, }) From bbc484e2c454eb6fa083c2e89730e9f7021dbaba Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 31 Aug 2023 13:00:51 +0200 Subject: [PATCH 04/41] Handle frontend levels --- packages/builder/src/stores/backend/permissions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/stores/backend/permissions.js b/packages/builder/src/stores/backend/permissions.js index ea7bb347e2..0807c1cf10 100644 --- a/packages/builder/src/stores/backend/permissions.js +++ b/packages/builder/src/stores/backend/permissions.js @@ -21,7 +21,7 @@ export function createPermissionStore() { }) }, forResource: async resourceId => { - return await API.getPermissionForResource(resourceId) + return (await API.getPermissionForResource(resourceId)).permissions }, } } From a56712f4d7c79f2baea12277ad04109eca958f12 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 31 Aug 2023 13:01:17 +0200 Subject: [PATCH 05/41] Change shape --- .../server/src/api/controllers/permission.ts | 4 +- .../server/src/sdk/app/permissions/index.ts | 54 +++++++++++++++---- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/packages/server/src/api/controllers/permission.ts b/packages/server/src/api/controllers/permission.ts index ef138ee5c2..ea9b9a4596 100644 --- a/packages/server/src/api/controllers/permission.ts +++ b/packages/server/src/api/controllers/permission.ts @@ -147,7 +147,9 @@ export async function fetch(ctx: UserCtx) { export async function getResourcePerms(ctx: UserCtx) { const resourceId = ctx.params.resourceId - ctx.body = await sdk.permissions.getResourcePerms(resourceId) + ctx.body = { + permissions: await sdk.permissions.getResourcePerms(resourceId), + } } export async function addPermission(ctx: UserCtx) { diff --git a/packages/server/src/sdk/app/permissions/index.ts b/packages/server/src/sdk/app/permissions/index.ts index 144b4fab2b..cb4ce93250 100644 --- a/packages/server/src/sdk/app/permissions/index.ts +++ b/packages/server/src/sdk/app/permissions/index.ts @@ -3,9 +3,14 @@ import { features } from "@budibase/pro" import { DocumentType, PermissionLevel, + Role, VirtualDocumentType, } from "@budibase/types" -import { getRoleParams, isViewID } from "../../../db/utils" +import { + extractViewInfoFromID, + getRoleParams, + isViewID, +} from "../../../db/utils" import { CURRENTLY_SUPPORTED_LEVELS, getBasePermissions, @@ -41,15 +46,31 @@ export async function resourceActionAllowed({ } } -export async function getResourcePerms(resourceId: string) { +type ResourcePermissions = Record< + string, + { + role: string + inherited?: boolean | undefined + } +> + +export async function getResourcePerms( + resourceId: string +): Promise { const db = context.getAppDB() const body = await db.allDocs( getRoleParams(null, { include_docs: true, }) ) - const rolesList = body.rows.map(row => row.doc) - let permissions: Record = {} + const rolesList = body.rows.map(row => row.doc) + let permissions: Record = {} + + let parentResourceToCheck + if (isViewID(resourceId) && (await features.isViewPermissionEnabled())) { + parentResourceToCheck = extractViewInfoFromID(resourceId).tableId + } + for (let level of CURRENTLY_SUPPORTED_LEVELS) { // update the various roleIds in the resource permissions for (let role of rolesList) { @@ -57,15 +78,28 @@ export async function getResourcePerms(resourceId: string) { role.permissions, resourceId ) - if ( - rolePerms && - rolePerms[resourceId] && - rolePerms[resourceId].indexOf(level) !== -1 + if (rolePerms[resourceId]?.indexOf(level) > -1) { + permissions[level] = { + role: roles.getExternalRoleID(role._id!, role.version), + } + } else if ( + parentResourceToCheck && + rolePerms[parentResourceToCheck]?.indexOf(level) > -1 ) { - permissions[level] = roles.getExternalRoleID(role._id, role.version)! + permissions[level] = { + role: roles.getExternalRoleID(role._id!, role.version), + inherited: true, + } } } } - return Object.assign(getBasePermissions(resourceId), permissions) + const basePermissions = Object.entries( + getBasePermissions(resourceId) + ).reduce((p, [level, role]) => { + p[level] = { role } + return p + }, {}) + const result = Object.assign(basePermissions, permissions) + return result } From 774ff745b6a640f7b7e5ff0cf9904a04146ce02d Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 1 Sep 2023 09:40:29 +0200 Subject: [PATCH 06/41] Return role origin --- .../server/src/api/controllers/permission.ts | 29 +++++++++++++++-- .../server/src/sdk/app/permissions/index.ts | 31 ++++++++++--------- packages/types/src/api/web/app/index.ts | 1 + packages/types/src/api/web/app/permission.ts | 4 +++ 4 files changed, 48 insertions(+), 17 deletions(-) create mode 100644 packages/types/src/api/web/app/permission.ts diff --git a/packages/server/src/api/controllers/permission.ts b/packages/server/src/api/controllers/permission.ts index ea9b9a4596..8cc5bcec74 100644 --- a/packages/server/src/api/controllers/permission.ts +++ b/packages/server/src/api/controllers/permission.ts @@ -1,5 +1,11 @@ import { permissions, roles, context, HTTPError } from "@budibase/backend-core" -import { UserCtx, Database, Role, PermissionLevel } from "@budibase/types" +import { + UserCtx, + Database, + Role, + PermissionLevel, + GetResourcePermsResponse, +} from "@budibase/types" import { getRoleParams } from "../../db/utils" import { CURRENTLY_SUPPORTED_LEVELS, @@ -145,10 +151,27 @@ export async function fetch(ctx: UserCtx) { ctx.body = finalPermissions } -export async function getResourcePerms(ctx: UserCtx) { +export async function getResourcePerms( + ctx: UserCtx +) { const resourceId = ctx.params.resourceId + const resourcePermissions = await sdk.permissions.getResourcePerms(resourceId) + ctx.body = { - permissions: await sdk.permissions.getResourcePerms(resourceId), + permissions: Object.entries(resourcePermissions).reduce( + (p, [level, role]) => { + p[level] = role.role + return p + }, + {} as Record + ), + permissionType: Object.entries(resourcePermissions).reduce( + (p, [level, role]) => { + p[level] = role.type + return p + }, + {} as Record + ), } } diff --git a/packages/server/src/sdk/app/permissions/index.ts b/packages/server/src/sdk/app/permissions/index.ts index cb4ce93250..b1ebbd2ac8 100644 --- a/packages/server/src/sdk/app/permissions/index.ts +++ b/packages/server/src/sdk/app/permissions/index.ts @@ -46,12 +46,15 @@ export async function resourceActionAllowed({ } } +enum PermissionType { + EXPLICIT = "explicit", + INHERITED = "inherited", + BASE = "base", +} + type ResourcePermissions = Record< string, - { - role: string - inherited?: boolean | undefined - } + { role: string; type: PermissionType } > export async function getResourcePerms( @@ -64,11 +67,13 @@ export async function getResourcePerms( }) ) const rolesList = body.rows.map(row => row.doc) - let permissions: Record = {} + let permissions: ResourcePermissions = {} - let parentResourceToCheck + let permsToInherit: ResourcePermissions | undefined if (isViewID(resourceId) && (await features.isViewPermissionEnabled())) { - parentResourceToCheck = extractViewInfoFromID(resourceId).tableId + permsToInherit = await getResourcePerms( + extractViewInfoFromID(resourceId).tableId + ) } for (let level of CURRENTLY_SUPPORTED_LEVELS) { @@ -81,14 +86,12 @@ export async function getResourcePerms( if (rolePerms[resourceId]?.indexOf(level) > -1) { permissions[level] = { role: roles.getExternalRoleID(role._id!, role.version), + type: PermissionType.EXPLICIT, } - } else if ( - parentResourceToCheck && - rolePerms[parentResourceToCheck]?.indexOf(level) > -1 - ) { + } else if (permsToInherit && permsToInherit[level]) { permissions[level] = { - role: roles.getExternalRoleID(role._id!, role.version), - inherited: true, + role: permsToInherit[level].role, + type: PermissionType.INHERITED, } } } @@ -97,7 +100,7 @@ export async function getResourcePerms( const basePermissions = Object.entries( getBasePermissions(resourceId) ).reduce((p, [level, role]) => { - p[level] = { role } + p[level] = { role, type: PermissionType.BASE } return p }, {}) const result = Object.assign(basePermissions, permissions) diff --git a/packages/types/src/api/web/app/index.ts b/packages/types/src/api/web/app/index.ts index e7b4b87aa9..276d7fa7c1 100644 --- a/packages/types/src/api/web/app/index.ts +++ b/packages/types/src/api/web/app/index.ts @@ -4,3 +4,4 @@ export * from "./row" export * from "./view" export * from "./rows" export * from "./table" +export * from "./permission" diff --git a/packages/types/src/api/web/app/permission.ts b/packages/types/src/api/web/app/permission.ts new file mode 100644 index 0000000000..52f6bdcabb --- /dev/null +++ b/packages/types/src/api/web/app/permission.ts @@ -0,0 +1,4 @@ +export interface GetResourcePermsResponse { + permissions: Record + permissionType: Record +} From 5d870fb41a2dd1920419d37ea6259451a2cdb655 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 1 Sep 2023 09:50:55 +0200 Subject: [PATCH 07/41] Rename --- .../server/src/api/controllers/permission.ts | 8 +++++ .../server/src/sdk/app/permissions/index.ts | 31 ++++++++++--------- packages/types/src/api/web/app/permission.ts | 1 + 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/packages/server/src/api/controllers/permission.ts b/packages/server/src/api/controllers/permission.ts index 8cc5bcec74..6702c24450 100644 --- a/packages/server/src/api/controllers/permission.ts +++ b/packages/server/src/api/controllers/permission.ts @@ -156,6 +156,8 @@ export async function getResourcePerms( ) { const resourceId = ctx.params.resourceId const resourcePermissions = await sdk.permissions.getResourcePerms(resourceId) + const inheritablePermissions = + await sdk.permissions.getInheritablePermissions(resourceId) ctx.body = { permissions: Object.entries(resourcePermissions).reduce( @@ -172,6 +174,12 @@ export async function getResourcePerms( }, {} as Record ), + inheritablePermissions: + inheritablePermissions && + Object.entries(inheritablePermissions).reduce((p, [level, role]) => { + p[level] = role.role + return p + }, {} as Record), } } diff --git a/packages/server/src/sdk/app/permissions/index.ts b/packages/server/src/sdk/app/permissions/index.ts index b1ebbd2ac8..ce6ac8c98a 100644 --- a/packages/server/src/sdk/app/permissions/index.ts +++ b/packages/server/src/sdk/app/permissions/index.ts @@ -46,17 +46,25 @@ export async function resourceActionAllowed({ } } -enum PermissionType { - EXPLICIT = "explicit", - INHERITED = "inherited", - BASE = "base", +enum PermissionSource { + EXPLICIT = "EXPLICIT", + INHERITED = "INHERITED", + BASE = "BASE", } type ResourcePermissions = Record< string, - { role: string; type: PermissionType } + { role: string; type: PermissionSource } > +export async function getInheritablePermissions( + resourceId: string +): Promise { + if (isViewID(resourceId) && (await features.isViewPermissionEnabled())) { + return await getResourcePerms(extractViewInfoFromID(resourceId).tableId) + } +} + export async function getResourcePerms( resourceId: string ): Promise { @@ -69,12 +77,7 @@ export async function getResourcePerms( const rolesList = body.rows.map(row => row.doc) let permissions: ResourcePermissions = {} - let permsToInherit: ResourcePermissions | undefined - if (isViewID(resourceId) && (await features.isViewPermissionEnabled())) { - permsToInherit = await getResourcePerms( - extractViewInfoFromID(resourceId).tableId - ) - } + const permsToInherit = await getInheritablePermissions(resourceId) for (let level of CURRENTLY_SUPPORTED_LEVELS) { // update the various roleIds in the resource permissions @@ -86,12 +89,12 @@ export async function getResourcePerms( if (rolePerms[resourceId]?.indexOf(level) > -1) { permissions[level] = { role: roles.getExternalRoleID(role._id!, role.version), - type: PermissionType.EXPLICIT, + type: PermissionSource.EXPLICIT, } } else if (permsToInherit && permsToInherit[level]) { permissions[level] = { role: permsToInherit[level].role, - type: PermissionType.INHERITED, + type: PermissionSource.INHERITED, } } } @@ -100,7 +103,7 @@ export async function getResourcePerms( const basePermissions = Object.entries( getBasePermissions(resourceId) ).reduce((p, [level, role]) => { - p[level] = { role, type: PermissionType.BASE } + p[level] = { role, type: PermissionSource.BASE } return p }, {}) const result = Object.assign(basePermissions, permissions) diff --git a/packages/types/src/api/web/app/permission.ts b/packages/types/src/api/web/app/permission.ts index 52f6bdcabb..73ad47d83f 100644 --- a/packages/types/src/api/web/app/permission.ts +++ b/packages/types/src/api/web/app/permission.ts @@ -1,4 +1,5 @@ export interface GetResourcePermsResponse { permissions: Record permissionType: Record + inheritablePermissions?: Record } From 3192bbe4ddc865959c05ae80325b0fe967123443 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 1 Sep 2023 09:56:12 +0200 Subject: [PATCH 08/41] Clean --- .../backend/DataTable/buttons/ManageAccessButton.svelte | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/builder/src/components/backend/DataTable/buttons/ManageAccessButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/ManageAccessButton.svelte index 5a7e274eb8..1de31bf83b 100644 --- a/packages/builder/src/components/backend/DataTable/buttons/ManageAccessButton.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/ManageAccessButton.svelte @@ -23,7 +23,6 @@ From bdaf179f20e2681db2033e5cc078242ed4c6b006 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 1 Sep 2023 10:16:49 +0200 Subject: [PATCH 09/41] Remove placeholder --- .../backend/DataTable/modals/ManageAccessModal.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/components/backend/DataTable/modals/ManageAccessModal.svelte b/packages/builder/src/components/backend/DataTable/modals/ManageAccessModal.svelte index 678fb3b1c5..ff2db2088e 100644 --- a/packages/builder/src/components/backend/DataTable/modals/ManageAccessModal.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/ManageAccessModal.svelte @@ -54,7 +54,7 @@ {#each Object.keys(permissions) as level} - +