diff --git a/packages/server/src/api/controllers/permission.js b/packages/server/src/api/controllers/permission.js index 372e804958..1e6bd1869c 100644 --- a/packages/server/src/api/controllers/permission.js +++ b/packages/server/src/api/controllers/permission.js @@ -18,6 +18,16 @@ const PermissionUpdateType = { ADD: "add", } +// utility function to stop this repetition - permissions always stored under roles +async function getAllDBRoles(db) { + const body = await db.allDocs( + getRoleParams(null, { + include_docs: true, + }) + ) + return body.rows.map(row => row.doc) +} + async function updatePermissionOnRole( appId, { roleId, resourceId, level }, @@ -27,12 +37,7 @@ async function updatePermissionOnRole( const remove = updateType === PermissionUpdateType.REMOVE const isABuiltin = isBuiltin(roleId) const dbRoleId = getDBRoleID(roleId) - const body = await db.allDocs( - getRoleParams(null, { - include_docs: true, - }) - ) - const dbRoles = body.rows.map(row => row.doc) + const dbRoles = await getAllDBRoles(db) const docUpdates = [] // the permission is for a built in, make sure it exists @@ -87,6 +92,28 @@ exports.fetchLevels = function(ctx) { ctx.body = [PermissionLevels.WRITE, PermissionLevels.READ] } +exports.fetch = async function(ctx) { + const db = new CouchDB(ctx.appId) + const roles = await getAllDBRoles(db) + let permissions = {} + // create an object with structure role ID -> resource ID -> level + for (let role of roles) { + if (role.permissions) { + const roleId = getExternalRoleID(role._id) + if (permissions[roleId] == null) { + permissions[roleId] = {} + } + for (let [resource, level] of Object.entries(role.permissions)) { + permissions[roleId][resource] = higherPermission( + permissions[roleId][resource], + level + ) + } + } + } + ctx.body = permissions +} + exports.getResourcePerms = async function(ctx) { const resourceId = ctx.params.resourceId const db = new CouchDB(ctx.appId) diff --git a/packages/server/src/api/routes/permission.js b/packages/server/src/api/routes/permission.js index 1d7c042f99..9cf5ae287e 100644 --- a/packages/server/src/api/routes/permission.js +++ b/packages/server/src/api/routes/permission.js @@ -23,6 +23,7 @@ function generateValidator() { router .get("/api/permission/builtin", authorized(BUILDER), controller.fetchBuiltin) .get("/api/permission/levels", authorized(BUILDER), controller.fetchLevels) + .get("/api/permission", authorized(BUILDER), controller.fetch) .get( "/api/permission/:resourceId", authorized(BUILDER), diff --git a/packages/server/src/api/routes/tests/couchTestUtils.js b/packages/server/src/api/routes/tests/couchTestUtils.js index 3d99e16873..0bc7f90998 100644 --- a/packages/server/src/api/routes/tests/couchTestUtils.js +++ b/packages/server/src/api/routes/tests/couchTestUtils.js @@ -73,6 +73,22 @@ exports.createTable = async (request, appId, table, removeId = true) => { return res.body } +exports.createRow = async (request, appId, tableId, row = null) => { + row = row || { + name: "Test Contact", + description: "original description", + status: "new", + tableId: tableId, + } + const res = await request + .post(`/api/${tableId}/rows`) + .send(row) + .set(exports.defaultHeaders(appId)) + .expect("Content-Type", /json/) + .expect(200) + return res.body +} + exports.createRole = async (request, appId) => { const roleBody = { name: "NewRole", diff --git a/packages/server/src/api/routes/tests/permissions.spec.js b/packages/server/src/api/routes/tests/permissions.spec.js index fc5ce23f96..e1322b7b13 100644 --- a/packages/server/src/api/routes/tests/permissions.spec.js +++ b/packages/server/src/api/routes/tests/permissions.spec.js @@ -1,12 +1,14 @@ const { createApplication, createTable, + createRow, supertest, defaultHeaders, addPermission, } = require("./couchTestUtils") const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles") +const HIGHER_ROLE_ID = BUILTIN_ROLE_IDS.BASIC const STD_ROLE_ID = BUILTIN_ROLE_IDS.PUBLIC describe("/permission", () => { @@ -15,6 +17,7 @@ describe("/permission", () => { let appId let table let perms + let row beforeAll(async () => { ;({ request, server } = await supertest()) @@ -29,8 +32,17 @@ describe("/permission", () => { appId = app.instance._id table = await createTable(request, appId) perms = await addPermission(request, appId, STD_ROLE_ID, table._id) + row = await createRow(request, appId, table._id) }) + async function getTablePermissions() { + return request + .get(`/api/permission/${table._id}`) + .set(defaultHeaders(appId)) + .expect("Content-Type", /json/) + .expect(200) + } + describe("levels", () => { it("should be able to get levels", async () => { const res = await request @@ -45,7 +57,7 @@ describe("/permission", () => { }) }) - describe("test", () => { + describe("add", () => { it("should be able to add permission to a role for the table", async () => { expect(perms.length).toEqual(1) expect(perms[0]._id).toEqual(`${STD_ROLE_ID}`) @@ -57,6 +69,40 @@ describe("/permission", () => { .set(defaultHeaders(appId)) .expect("Content-Type", /json/) .expect(200) + expect(res.body[STD_ROLE_ID]).toEqual("read") + }) + + it("should get resource permissions with multiple roles", async () => { + perms = await addPermission(request, appId, HIGHER_ROLE_ID, table._id, "write") + const res = await getTablePermissions() + expect(res.body[HIGHER_ROLE_ID]).toEqual("write") + expect(res.body[STD_ROLE_ID]).toEqual("read") + const allRes = await request + .get(`/api/permission`) + .set(defaultHeaders(appId)) + .expect("Content-Type", /json/) + .expect(200) + expect(allRes.body[HIGHER_ROLE_ID][table._id]).toEqual("write") + expect(allRes.body[STD_ROLE_ID][table._id]).toEqual("read") + }) + }) + + describe("remove", () => { + it("should be able to remove the permission", async () => { + const res = await request + .delete(`/api/permission/${STD_ROLE_ID}/${table._id}/read`) + .set(defaultHeaders(appId)) + .expect("Content-Type", /json/) + .expect(200) + expect(res.body[0]._id).toEqual(STD_ROLE_ID) + const permsRes = await getTablePermissions() + expect(permsRes.body[STD_ROLE_ID]).toBeUndefined() + }) + }) + + describe("check public user allowed", () => { + it("should be able to read the row", async () => { + // TODO }) }) }) diff --git a/packages/server/src/middleware/authorized.js b/packages/server/src/middleware/authorized.js index 82ca7f98f2..b838ef4e29 100644 --- a/packages/server/src/middleware/authorized.js +++ b/packages/server/src/middleware/authorized.js @@ -64,12 +64,12 @@ module.exports = (permType, permLevel = null) => async (ctx, next) => { ctx.throw(403, "Not Authorized") } - // if ( - // hasResource(ctx) && - // doesHaveResourcePermission(permissions, permLevel, ctx) - // ) { - // return next() - // } + if ( + hasResource(ctx) && + doesHaveResourcePermission(permissions, permLevel, ctx) + ) { + return next() + } if (!doesHaveBasePermission(permType, permLevel, basePermissions)) { ctx.throw(403, "User does not have permission")