diff --git a/packages/server/src/api/controllers/permission.ts b/packages/server/src/api/controllers/permission.ts index c7afb6a351..668501b3f3 100644 --- a/packages/server/src/api/controllers/permission.ts +++ b/packages/server/src/api/controllers/permission.ts @@ -9,6 +9,7 @@ import { AddPermissionRequest, RemovePermissionRequest, RemovePermissionResponse, + FetchResourcePermissionInfoResponse, } from "@budibase/types" import { CURRENTLY_SUPPORTED_LEVELS, @@ -28,7 +29,9 @@ export function fetchLevels(ctx: UserCtx) { ctx.body = SUPPORTED_LEVELS } -export async function fetch(ctx: UserCtx) { +export async function fetch( + ctx: UserCtx +) { const db = context.getAppDB() const dbRoles: Role[] = await sdk.permissions.getAllDBRoles(db) let permissions: any = {} @@ -49,7 +52,7 @@ export async function fetch(ctx: UserCtx) { } } // apply the base permissions - const finalPermissions: Record> = {} + const finalPermissions: FetchResourcePermissionInfoResponse = {} for (let [resource, permission] of Object.entries(permissions)) { const basePerms = getBasePermissions(resource) finalPermissions[resource] = Object.assign(basePerms, permission) diff --git a/packages/server/src/api/routes/tests/permissions.spec.ts b/packages/server/src/api/routes/tests/permissions.spec.ts index b148d6fde1..f8974f44aa 100644 --- a/packages/server/src/api/routes/tests/permissions.spec.ts +++ b/packages/server/src/api/routes/tests/permissions.spec.ts @@ -12,7 +12,7 @@ const STD_ROLE_ID = BUILTIN_ROLE_IDS.PUBLIC describe("/permission", () => { let request = setup.getRequest() let config = setup.getConfig() - let table: Table & { _id: string } + let table: Table let perms: Document[] let row: Row let view: ViewV2 @@ -26,15 +26,33 @@ describe("/permission", () => { beforeEach(async () => { mocks.licenses.useCloudFree() - table = (await config.createTable()) as typeof table + table = await config.createTable() + + await config.api.permission.revoke({ + roleId: roles.BUILTIN_ROLE_IDS.ADMIN, + resourceId: table._id!, + level: PermissionLevel.READ, + }) + await config.api.permission.revoke({ + roleId: roles.BUILTIN_ROLE_IDS.ADMIN, + resourceId: table._id!, + level: PermissionLevel.WRITE, + }) + await config.api.permission.revoke({ + roleId: roles.BUILTIN_ROLE_IDS.ADMIN, + resourceId: table._id!, + level: PermissionLevel.EXECUTE, + }) + row = await config.createRow() view = await config.api.viewV2.create({ tableId: table._id!, name: generator.guid(), }) + perms = await config.api.permission.add({ roleId: STD_ROLE_ID, - resourceId: table._id, + resourceId: table._id!, level: PermissionLevel.READ, }) }) @@ -74,27 +92,22 @@ describe("/permission", () => { }) }) - it("should get resource permissions with multiple roles", async () => { + it.only("should get resource permissions with multiple roles", async () => { perms = await config.api.permission.add({ roleId: HIGHER_ROLE_ID, - resourceId: table._id, + resourceId: table._id!, level: PermissionLevel.WRITE, }) - const res = await config.api.permission.get(table._id) - expect(res).toEqual({ - permissions: { - read: { permissionType: "EXPLICIT", role: STD_ROLE_ID }, - write: { permissionType: "EXPLICIT", role: HIGHER_ROLE_ID }, - }, + const { permissions } = await config.api.permission.get(table._id!) + expect(permissions).toEqual({ + read: { permissionType: "EXPLICIT", role: STD_ROLE_ID }, + write: { permissionType: "EXPLICIT", role: HIGHER_ROLE_ID }, + execute: { permissionType: "BASE", role: "BASIC" }, }) - const allRes = await request - .get(`/api/permission`) - .set(config.defaultHeaders()) - .expect("Content-Type", /json/) - .expect(200) - expect(allRes.body[table._id]["read"]).toEqual(STD_ROLE_ID) - expect(allRes.body[table._id]["write"]).toEqual(HIGHER_ROLE_ID) + const all = await config.api.permission.fetch() + expect(all[table._id!]["read"]).toEqual(STD_ROLE_ID) + expect(all[table._id!]["write"]).toEqual(HIGHER_ROLE_ID) }) }) @@ -102,11 +115,11 @@ describe("/permission", () => { it("should be able to remove the permission", async () => { const res = await config.api.permission.revoke({ roleId: STD_ROLE_ID, - resourceId: table._id, + resourceId: table._id!, level: PermissionLevel.READ, }) expect(res[0]._id).toEqual(STD_ROLE_ID) - const permsRes = await config.api.permission.get(table._id) + const permsRes = await config.api.permission.get(table._id!) expect(permsRes.permissions[STD_ROLE_ID]).toBeUndefined() }) }) @@ -142,7 +155,7 @@ describe("/permission", () => { it("should not be able to access the view data when the table is not public and there are no view permissions overrides", async () => { await config.api.permission.revoke({ roleId: STD_ROLE_ID, - resourceId: table._id, + resourceId: table._id!, level: PermissionLevel.READ, }) @@ -167,7 +180,7 @@ describe("/permission", () => { }) await config.api.permission.revoke({ roleId: STD_ROLE_ID, - resourceId: table._id, + resourceId: table._id!, level: PermissionLevel.READ, }) // replicate changes before checking permissions @@ -179,8 +192,8 @@ describe("/permission", () => { it("shouldn't allow writing from a public user", async () => { const res = await request - .post(`/api/${table._id}/rows`) - .send(basicRow(table._id)) + .post(`/api/${table._id!}/rows`) + .send(basicRow(table._id!)) .set(config.publicHeaders()) .expect("Content-Type", /json/) .expect(401) diff --git a/packages/server/src/api/routes/tests/table.spec.ts b/packages/server/src/api/routes/tests/table.spec.ts index f82476d412..60e8142522 100644 --- a/packages/server/src/api/routes/tests/table.spec.ts +++ b/packages/server/src/api/routes/tests/table.spec.ts @@ -21,6 +21,7 @@ import { ViewCalculation, ViewV2Enriched, RowExportFormat, + PermissionSource, } from "@budibase/types" import { checkBuilderEndpoint } from "./utilities/TestFunctions" import * as setup from "./utilities" @@ -193,8 +194,20 @@ describe.each([ it("should create tables with ADMIN read and write permissions", async () => { const table = await config.api.table.save(tableForDatasource(datasource)) const { permissions } = await config.api.permission.get(table._id!) - expect(permissions.read.role).toEqual(roles.BUILTIN_ROLE_IDS.ADMIN) - expect(permissions.write.role).toEqual(roles.BUILTIN_ROLE_IDS.ADMIN) + expect(permissions).toEqual({ + read: { + permissionType: PermissionSource.EXPLICIT, + role: roles.BUILTIN_ROLE_IDS.ADMIN, + }, + write: { + permissionType: PermissionSource.EXPLICIT, + role: roles.BUILTIN_ROLE_IDS.ADMIN, + }, + execute: { + permissionType: PermissionSource.EXPLICIT, + role: roles.BUILTIN_ROLE_IDS.ADMIN, + }, + }) }) }) diff --git a/packages/server/src/tests/utilities/api/permission.ts b/packages/server/src/tests/utilities/api/permission.ts index 986796d9a1..b4e641a1be 100644 --- a/packages/server/src/tests/utilities/api/permission.ts +++ b/packages/server/src/tests/utilities/api/permission.ts @@ -1,6 +1,7 @@ import { AddPermissionRequest, AddPermissionResponse, + FetchResourcePermissionInfoResponse, GetResourcePermsResponse, RemovePermissionRequest, RemovePermissionResponse, @@ -26,6 +27,15 @@ export class PermissionAPI extends TestAPI { ) } + fetch = async ( + expectations?: Expectations + ): Promise => { + return await this._get( + `/api/permission`, + { expectations } + ) + } + revoke = async ( request: RemovePermissionRequest, expectations?: Expectations diff --git a/packages/server/src/utilities/security.ts b/packages/server/src/utilities/security.ts index 40dba465f1..a3353f6184 100644 --- a/packages/server/src/utilities/security.ts +++ b/packages/server/src/utilities/security.ts @@ -35,9 +35,9 @@ export function getPermissionType(resourceId: string) { /** * works out the basic permissions based on builtin roles for a resource, using its ID */ -export function getBasePermissions(resourceId: string) { +export function getBasePermissions(resourceId: string): Record { const type = getPermissionType(resourceId) - const basePermissions: { [key: string]: string } = {} + const basePermissions: Record = {} for (let [roleId, role] of Object.entries(roles.getBuiltinRoles())) { if (!role.permissionId) { continue diff --git a/packages/types/src/api/web/app/permission.ts b/packages/types/src/api/web/app/permission.ts index bead2a4279..a5c4df5733 100644 --- a/packages/types/src/api/web/app/permission.ts +++ b/packages/types/src/api/web/app/permission.ts @@ -1,5 +1,9 @@ import { PermissionLevel } from "../../../sdk" +export interface FetchResourcePermissionInfoResponse { + [key: string]: Record +} + export interface ResourcePermissionInfo { role: string permissionType: string