From 65302e1dd9d6d6a57d89ba3657a507fcf86de35d Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 2 Dec 2020 17:08:25 +0000 Subject: [PATCH] Changing the role system to have permissions integrated rather than the permissions being per user. --- packages/server/src/api/controllers/auth.js | 1 - .../server/src/api/controllers/permission.js | 6 ++ packages/server/src/api/controllers/role.js | 10 ++- packages/server/src/api/controllers/user.js | 22 +---- packages/server/src/api/routes/index.js | 2 + packages/server/src/api/routes/permission.js | 10 +++ packages/server/src/api/routes/role.js | 18 +++- .../src/api/routes/tests/couchTestUtils.js | 85 ++++++------------ .../server/src/api/routes/tests/role.spec.js | 16 ++-- .../server/src/api/routes/tests/user.spec.js | 36 ++++---- packages/server/src/middleware/authorized.js | 7 +- .../src/utilities/builder/setBuilderToken.js | 2 - .../src/utilities/security/permissions.js | 18 ++-- .../server/src/utilities/security/roles.js | 90 +++++++++++++------ 14 files changed, 182 insertions(+), 141 deletions(-) create mode 100644 packages/server/src/api/controllers/permission.js create mode 100644 packages/server/src/api/routes/permission.js diff --git a/packages/server/src/api/controllers/auth.js b/packages/server/src/api/controllers/auth.js index de872ebfc4..43cc4317f4 100644 --- a/packages/server/src/api/controllers/auth.js +++ b/packages/server/src/api/controllers/auth.js @@ -34,7 +34,6 @@ exports.authenticate = async ctx => { userId: dbUser._id, roleId: dbUser.roleId, version: app.version, - permissions: dbUser.permissions || [], } // if in cloud add the user api key if (env.CLOUD) { diff --git a/packages/server/src/api/controllers/permission.js b/packages/server/src/api/controllers/permission.js new file mode 100644 index 0000000000..a2715a5363 --- /dev/null +++ b/packages/server/src/api/controllers/permission.js @@ -0,0 +1,6 @@ +const { BUILTIN_PERMISSIONS } = require("../../utilities/security/permissions") + +exports.fetch = async function(ctx) { + // TODO: need to build out custom permissions + ctx.body = Object.values(BUILTIN_PERMISSIONS) +} diff --git a/packages/server/src/api/controllers/role.js b/packages/server/src/api/controllers/role.js index ff34a4e954..7889233026 100644 --- a/packages/server/src/api/controllers/role.js +++ b/packages/server/src/api/controllers/role.js @@ -25,9 +25,13 @@ exports.find = async function(ctx) { exports.save = async function(ctx) { const db = new CouchDB(ctx.user.appId) - - let id = ctx.request.body._id || generateRoleID() - const role = new Role(id, ctx.request.body.name, ctx.request.body.inherits) + let { _id, name, inherits, permissionId } = ctx.request.body + if (!_id) { + _id = generateRoleID() + } + const role = new Role(_id, name) + .addPermission(permissionId) + .addInheritance(inherits) if (ctx.request.body._rev) { role._rev = ctx.request.body._rev } diff --git a/packages/server/src/api/controllers/user.js b/packages/server/src/api/controllers/user.js index f20ccd3d7e..bea62ecbd5 100644 --- a/packages/server/src/api/controllers/user.js +++ b/packages/server/src/api/controllers/user.js @@ -1,10 +1,7 @@ const CouchDB = require("../../db") const bcrypt = require("../../utilities/bcrypt") const { generateUserID, getUserParams, ViewNames } = require("../../db/utils") -const { BUILTIN_ROLE_ID_ARRAY } = require("../../utilities/security/roles") -const { - BUILTIN_PERMISSION_NAMES, -} = require("../../utilities/security/permissions") +const { getRole } = require("../../utilities/security/roles") exports.fetch = async function(ctx) { const database = new CouchDB(ctx.user.appId) @@ -18,13 +15,13 @@ exports.fetch = async function(ctx) { exports.create = async function(ctx) { const db = new CouchDB(ctx.user.appId) - const { username, password, name, roleId, permissions } = ctx.request.body + const { username, password, name, roleId } = ctx.request.body if (!username || !password) { ctx.throw(400, "Username and Password Required.") } - const role = await checkRole(db, roleId) + const role = await getRole(ctx.user.appId, roleId) if (!role) ctx.throw(400, "Invalid Role") @@ -35,7 +32,6 @@ exports.create = async function(ctx) { name: name || username, type: "user", roleId, - permissions: permissions || [BUILTIN_PERMISSION_NAMES.POWER], tableId: ViewNames.USERS, } @@ -88,15 +84,3 @@ exports.find = async function(ctx) { _rev: user._rev, } } - -const checkRole = async (db, roleId) => { - if (!roleId) return - if (BUILTIN_ROLE_ID_ARRAY.indexOf(roleId) !== -1) { - return { - _id: roleId, - name: roleId, - permissions: [], - } - } - return await db.get(roleId) -} diff --git a/packages/server/src/api/routes/index.js b/packages/server/src/api/routes/index.js index 81c26b366d..c663accea0 100644 --- a/packages/server/src/api/routes/index.js +++ b/packages/server/src/api/routes/index.js @@ -16,6 +16,7 @@ const apiKeysRoutes = require("./apikeys") const templatesRoutes = require("./templates") const analyticsRoutes = require("./analytics") const routingRoutes = require("./routing") +const permissionRoutes = require("./permission") exports.mainRoutes = [ deployRoutes, @@ -32,6 +33,7 @@ exports.mainRoutes = [ analyticsRoutes, webhookRoutes, routingRoutes, + permissionRoutes, // these need to be handled last as they still use /api/:tableId // this could be breaking as koa may recognise other routes as this tableRoutes, diff --git a/packages/server/src/api/routes/permission.js b/packages/server/src/api/routes/permission.js new file mode 100644 index 0000000000..9dcec253b3 --- /dev/null +++ b/packages/server/src/api/routes/permission.js @@ -0,0 +1,10 @@ +const Router = require("@koa/router") +const controller = require("../controllers/permission") +const authorized = require("../../middleware/authorized") +const { BUILDER } = require("../../utilities/security/permissions") + +const router = Router() + +router.get("/api/permissions", authorized(BUILDER), controller.fetch) + +module.exports = router diff --git a/packages/server/src/api/routes/role.js b/packages/server/src/api/routes/role.js index e0a157d8b8..98ac333e17 100644 --- a/packages/server/src/api/routes/role.js +++ b/packages/server/src/api/routes/role.js @@ -2,11 +2,27 @@ const Router = require("@koa/router") const controller = require("../controllers/role") const authorized = require("../../middleware/authorized") const { BUILDER } = require("../../utilities/security/permissions") +const Joi = require("joi") +const joiValidator = require("../../middleware/joi-validator") +const { + BUILTIN_PERMISSION_IDS, +} = require("../../utilities/security/permissions") const router = Router() +function generateValidator() { + // prettier-ignore + return joiValidator.body(Joi.object({ + _id: Joi.string().optional(), + _rev: Joi.string().optional(), + name: Joi.string().required(), + permissionId: Joi.string().valid(...Object.values(BUILTIN_PERMISSION_IDS)).required(), + inherits: Joi.string().optional(), + }).unknown(true)) +} + router - .post("/api/roles", authorized(BUILDER), controller.save) + .post("/api/roles", authorized(BUILDER), generateValidator(), controller.save) .get("/api/roles", authorized(BUILDER), controller.fetch) .get("/api/roles/:roleId", authorized(BUILDER), controller.find) .delete("/api/roles/:roleId/:rev", authorized(BUILDER), controller.destroy) diff --git a/packages/server/src/api/routes/tests/couchTestUtils.js b/packages/server/src/api/routes/tests/couchTestUtils.js index c7846f85b8..e3a366ac78 100644 --- a/packages/server/src/api/routes/tests/couchTestUtils.js +++ b/packages/server/src/api/routes/tests/couchTestUtils.js @@ -1,9 +1,6 @@ const CouchDB = require("../../../db") const supertest = require("supertest") const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles") -const { - BUILTIN_PERMISSION_NAMES, -} = require("../../../utilities/security/permissions") const packageJson = require("../../../../package") const jwt = require("jsonwebtoken") const env = require("../../../environment") @@ -131,49 +128,7 @@ exports.createUser = async ( return res.body } -const createUserWithOnePermission = async (request, appId, permName) => { - let permissions = [permName] - - return await createUserWithPermissions( - request, - appId, - permissions, - "onePermOnlyUser" - ) -} - -const createUserWithAdminPermissions = async (request, appId) => { - let permissions = [BUILTIN_PERMISSION_NAMES.ADMIN] - - return await createUserWithPermissions( - request, - appId, - permissions, - "adminUser" - ) -} - -const createUserWithAllPermissionExceptOne = async ( - request, - appId, - permName -) => { - let permissions = [permName] - - return await createUserWithPermissions( - request, - appId, - permissions, - "allPermsExceptOneUser" - ) -} - -const createUserWithPermissions = async ( - request, - appId, - permissions, - username -) => { +const createUserWithRole = async (request, appId, roleId, username) => { const password = `password_${username}` await request .post(`/api/users`) @@ -182,8 +137,7 @@ const createUserWithPermissions = async ( name: username, username, password, - roleId: BUILTIN_ROLE_IDS.POWER, - permissions, + roleId, }) const anonUser = { @@ -216,23 +170,29 @@ exports.testPermissionsForEndpoint = async ({ url, body, appId, - permName1, - permName2, + passRole, + failRole, }) => { - const headers = await createUserWithOnePermission(request, appId, permName1) - - await createRequest(request, method, url, body) - .set(headers) - .expect(200) - - const noPermsHeaders = await createUserWithAllPermissionExceptOne( + const passHeader = await createUserWithRole( request, appId, - permName2 + passRole, + "passUser" ) await createRequest(request, method, url, body) - .set(noPermsHeaders) + .set(passHeader) + .expect(200) + + const failHeader = await createUserWithRole( + request, + appId, + failRole, + "failUser" + ) + + await createRequest(request, method, url, body) + .set(failHeader) .expect(403) } @@ -243,7 +203,12 @@ exports.builderEndpointShouldBlockNormalUsers = async ({ body, appId, }) => { - const headers = await createUserWithAdminPermissions(request, appId) + const headers = await createUserWithRole( + request, + appId, + BUILTIN_ROLE_IDS.BASIC, + "basicUser" + ) await createRequest(request, method, url, body) .set(headers) diff --git a/packages/server/src/api/routes/tests/role.spec.js b/packages/server/src/api/routes/tests/role.spec.js index ca451c85c2..5503b7bf5d 100644 --- a/packages/server/src/api/routes/tests/role.spec.js +++ b/packages/server/src/api/routes/tests/role.spec.js @@ -8,8 +8,9 @@ const { const { BUILTIN_ROLE_IDS, } = require("../../../utilities/security/roles") +const { BUILTIN_PERMISSION_IDS } = require("../../../utilities/security/permissions") -const roleBody = { name: "user", inherits: BUILTIN_ROLE_IDS.BASIC } +const roleBody = { name: "NewRole", inherits: BUILTIN_ROLE_IDS.BASIC, permissionId: BUILTIN_PERMISSION_IDS.READ_ONLY } describe("/roles", () => { let server @@ -43,7 +44,7 @@ describe("/roles", () => { .expect('Content-Type', /json/) .expect(200) - expect(res.res.statusMessage).toEqual("Role 'user' created successfully.") + expect(res.res.statusMessage).toEqual("Role 'NewRole' created successfully.") expect(res.body._id).toBeDefined() expect(res.body._rev).toBeDefined() }) @@ -71,16 +72,19 @@ describe("/roles", () => { expect(res.body.length).toBe(3) const adminRole = res.body.find(r => r._id === BUILTIN_ROLE_IDS.ADMIN) - expect(adminRole.inherits).toEqual(BUILTIN_ROLE_IDS.POWER) expect(adminRole).toBeDefined() + expect(adminRole.inherits).toEqual(BUILTIN_ROLE_IDS.POWER) + expect(adminRole.permissionId).toEqual(BUILTIN_PERMISSION_IDS.ADMIN) const powerUserRole = res.body.find(r => r._id === BUILTIN_ROLE_IDS.POWER) - expect(powerUserRole.inherits).toEqual(BUILTIN_ROLE_IDS.BASIC) expect(powerUserRole).toBeDefined() + expect(powerUserRole.inherits).toEqual(BUILTIN_ROLE_IDS.BASIC) + expect(powerUserRole.permissionId).toEqual(BUILTIN_PERMISSION_IDS.POWER) const customRoleFetched = res.body.find(r => r._id === customRole._id) - expect(customRoleFetched.inherits).toEqual(BUILTIN_ROLE_IDS.BASIC) expect(customRoleFetched).toBeDefined() + expect(customRoleFetched.inherits).toEqual(BUILTIN_ROLE_IDS.BASIC) + expect(customRoleFetched.permissionId).toEqual(BUILTIN_PERMISSION_IDS.READ_ONLY) }) }); @@ -89,7 +93,7 @@ describe("/roles", () => { it("should delete custom roles", async () => { const createRes = await request .post(`/api/roles`) - .send({ name: "user" }) + .send({ name: "user", permissionId: BUILTIN_PERMISSION_IDS.READ_ONLY }) .set(defaultHeaders(appId)) .expect('Content-Type', /json/) .expect(200) diff --git a/packages/server/src/api/routes/tests/user.spec.js b/packages/server/src/api/routes/tests/user.spec.js index 96e5923d1d..deac296567 100644 --- a/packages/server/src/api/routes/tests/user.spec.js +++ b/packages/server/src/api/routes/tests/user.spec.js @@ -5,12 +5,16 @@ const { createUser, testPermissionsForEndpoint, } = require("./couchTestUtils") -const { - BUILTIN_PERMISSION_NAMES, -} = require("../../../utilities/security/permissions") const { BUILTIN_ROLE_IDS, } = require("../../../utilities/security/roles") +const { cloneDeep } = require("lodash/fp") + +const baseBody = { + name: "brandNewUser", + password: "yeeooo", + roleId: BUILTIN_ROLE_IDS.POWER +} describe("/users", () => { let request @@ -20,12 +24,12 @@ describe("/users", () => { beforeAll(async () => { ({ request, server } = await supertest(server)) - }); + }) beforeEach(async () => { app = await createApplication(request) appId = app.instance._id - }); + }) afterAll(() => { server.close() @@ -54,38 +58,40 @@ describe("/users", () => { method: "GET", url: `/api/users`, appId: appId, - permName1: BUILTIN_PERMISSION_NAMES.POWER, - permName2: BUILTIN_PERMISSION_NAMES.WRITE, + passRole: BUILTIN_ROLE_IDS.ADMIN, + failRole: BUILTIN_ROLE_IDS.PUBLIC, }) }) }) describe("create", () => { - it("returns a success message when a user is successfully created", async () => { + const body = cloneDeep(baseBody) + body.username = "bill" const res = await request .post(`/api/users`) .set(defaultHeaders(appId)) - .send({ name: "Bill", username: "bill", password: "bills_password", roleId: BUILTIN_ROLE_IDS.POWER }) + .send(body) .expect(200) .expect('Content-Type', /json/) - expect(res.res.statusMessage).toEqual("User created successfully."); + expect(res.res.statusMessage).toEqual("User created successfully.") expect(res.body._id).toBeUndefined() }) it("should apply authorization to endpoint", async () => { + const body = cloneDeep(baseBody) + body.username = "brandNewUser" await testPermissionsForEndpoint({ request, method: "POST", - body: { name: "brandNewUser", username: "brandNewUser", password: "yeeooo", roleId: BUILTIN_ROLE_IDS.POWER }, + body, url: `/api/users`, appId: appId, - permName1: BUILTIN_PERMISSION_NAMES.ADMIN, - permName2: BUILTIN_PERMISSION_NAMES.POWER, + passRole: BUILTIN_ROLE_IDS.ADMIN, + failRole: BUILTIN_ROLE_IDS.PUBLIC, }) }) - - }); + }) }) diff --git a/packages/server/src/middleware/authorized.js b/packages/server/src/middleware/authorized.js index f18cf3b5c8..e959e05f9d 100644 --- a/packages/server/src/middleware/authorized.js +++ b/packages/server/src/middleware/authorized.js @@ -1,4 +1,7 @@ -const { BUILTIN_ROLE_IDS } = require("../utilities/security/roles") +const { + BUILTIN_ROLE_IDS, + getUserPermissionIds, +} = require("../utilities/security/roles") const { PermissionTypes, doesHavePermission, @@ -48,7 +51,7 @@ module.exports = (permType, permLevel = null) => async (ctx, next) => { } const role = ctx.user.role - const permissions = ctx.user.permissions + const permissions = await getUserPermissionIds(ctx.appId, role._id) if (ADMIN_ROLES.indexOf(role._id) !== -1) { return next() } diff --git a/packages/server/src/utilities/builder/setBuilderToken.js b/packages/server/src/utilities/builder/setBuilderToken.js index ed374a1d8e..93863cee63 100644 --- a/packages/server/src/utilities/builder/setBuilderToken.js +++ b/packages/server/src/utilities/builder/setBuilderToken.js @@ -1,5 +1,4 @@ const { BUILTIN_ROLE_IDS } = require("../security/roles") -const { BUILTIN_PERMISSION_NAMES } = require("../security/permissions") const env = require("../../environment") const CouchDB = require("../../db") const jwt = require("jsonwebtoken") @@ -11,7 +10,6 @@ module.exports = async (ctx, appId, version) => { const builderUser = { userId: "BUILDER", roleId: BUILTIN_ROLE_IDS.BUILDER, - permissions: [BUILTIN_PERMISSION_NAMES.ADMIN], version, } if (env.BUDIBASE_API_KEY) { diff --git a/packages/server/src/utilities/security/permissions.js b/packages/server/src/utilities/security/permissions.js index d19f31e393..0836b29412 100644 --- a/packages/server/src/utilities/security/permissions.js +++ b/packages/server/src/utilities/security/permissions.js @@ -45,7 +45,7 @@ function getAllowedLevels(userPermLevel) { } } -exports.BUILTIN_PERMISSION_NAMES = { +exports.BUILTIN_PERMISSION_IDS = { READ_ONLY: "read_only", WRITE: "write", ADMIN: "admin", @@ -54,21 +54,24 @@ exports.BUILTIN_PERMISSION_NAMES = { exports.BUILTIN_PERMISSIONS = { READ_ONLY: { - name: exports.BUILTIN_PERMISSION_NAMES.READ_ONLY, + _id: exports.BUILTIN_PERMISSION_IDS.READ_ONLY, + name: "Read only", permissions: [ new Permission(PermissionTypes.TABLE, PermissionLevels.READ), new Permission(PermissionTypes.VIEW, PermissionLevels.READ), ], }, WRITE: { - name: exports.BUILTIN_PERMISSION_NAMES.WRITE, + _id: exports.BUILTIN_PERMISSION_IDS.WRITE, + name: "Read/Write", permissions: [ new Permission(PermissionTypes.TABLE, PermissionLevels.WRITE), new Permission(PermissionTypes.VIEW, PermissionLevels.READ), ], }, POWER: { - name: exports.BUILTIN_PERMISSION_NAMES.POWER, + _id: exports.BUILTIN_PERMISSION_IDS.POWER, + name: "Power", permissions: [ new Permission(PermissionTypes.TABLE, PermissionLevels.WRITE), new Permission(PermissionTypes.USER, PermissionLevels.READ), @@ -78,7 +81,8 @@ exports.BUILTIN_PERMISSIONS = { ], }, ADMIN: { - name: exports.BUILTIN_PERMISSION_NAMES.ADMIN, + _id: exports.BUILTIN_PERMISSION_IDS.ADMIN, + name: "Admin", permissions: [ new Permission(PermissionTypes.TABLE, PermissionLevels.ADMIN), new Permission(PermissionTypes.USER, PermissionLevels.ADMIN), @@ -89,11 +93,11 @@ exports.BUILTIN_PERMISSIONS = { }, } -exports.doesHavePermission = (permType, permLevel, userPermissionNames) => { +exports.doesHavePermission = (permType, permLevel, permissionIds) => { const builtins = Object.values(exports.BUILTIN_PERMISSIONS) let permissions = flatten( builtins - .filter(builtin => userPermissionNames.indexOf(builtin.name) !== -1) + .filter(builtin => permissionIds.indexOf(builtin._id) !== -1) .map(builtin => builtin.permissions) ) for (let permission of permissions) { diff --git a/packages/server/src/utilities/security/roles.js b/packages/server/src/utilities/security/roles.js index 09ca2c23fd..e971217d17 100644 --- a/packages/server/src/utilities/security/roles.js +++ b/packages/server/src/utilities/security/roles.js @@ -1,5 +1,6 @@ const CouchDB = require("../../db") const { cloneDeep } = require("lodash/fp") +const { BUILTIN_PERMISSION_IDS } = require("./permissions") const BUILTIN_IDS = { ADMIN: "ADMIN", @@ -9,20 +10,37 @@ const BUILTIN_IDS = { BUILDER: "BUILDER", } -function Role(id, name, inherits) { +function Role(id, name) { this._id = id this.name = name - if (inherits) { - this.inherits = inherits - } +} + +Role.prototype.addPermission = function(permissionId) { + this.permissionId = permissionId + return this +} + +Role.prototype.addInheritance = function(inherits) { + this.inherits = inherits + return this } exports.BUILTIN_ROLES = { - ADMIN: new Role(BUILTIN_IDS.ADMIN, "Admin", BUILTIN_IDS.POWER), - POWER: new Role(BUILTIN_IDS.POWER, "Power", BUILTIN_IDS.BASIC), - BASIC: new Role(BUILTIN_IDS.BASIC, "Basic", BUILTIN_IDS.PUBLIC), - PUBLIC: new Role(BUILTIN_IDS.PUBLIC, "Public"), - BUILDER: new Role(BUILTIN_IDS.BUILDER, "Builder"), + ADMIN: new Role(BUILTIN_IDS.ADMIN, "Admin") + .addPermission(BUILTIN_PERMISSION_IDS.ADMIN) + .addInheritance(BUILTIN_IDS.POWER), + POWER: new Role(BUILTIN_IDS.POWER, "Power") + .addPermission(BUILTIN_PERMISSION_IDS.POWER) + .addInheritance(BUILTIN_IDS.BASIC), + BASIC: new Role(BUILTIN_IDS.BASIC, "Basic") + .addPermission(BUILTIN_PERMISSION_IDS.WRITE) + .addInheritance(BUILTIN_IDS.PUBLIC), + PUBLIC: new Role(BUILTIN_IDS.PUBLIC, "Public").addPermission( + BUILTIN_PERMISSION_IDS.READ_ONLY + ), + BUILDER: new Role(BUILTIN_IDS.BUILDER, "Builder").addPermission( + BUILTIN_PERMISSION_IDS.ADMIN + ), } exports.BUILTIN_ROLE_ID_ARRAY = Object.values(exports.BUILTIN_ROLES).map( @@ -60,6 +78,29 @@ exports.getRole = async (appId, roleId) => { return role } +/** + * Simple function to get all the roles based on the top level user role ID. + */ +async function getAllUserRoles(appId, userRoleId) { + if (!userRoleId) { + return [BUILTIN_IDS.PUBLIC] + } + let currentRole = await exports.getRole(appId, userRoleId) + let roles = currentRole ? [currentRole] : [] + let roleIds = [userRoleId] + // get all the inherited roles + while ( + currentRole && + currentRole.inherits && + roleIds.indexOf(currentRole.inherits) === -1 + ) { + roleIds.push(currentRole.inherits) + currentRole = await exports.getRole(appId, currentRole.inherits) + roles.push(currentRole) + } + return roles +} + /** * Returns an ordered array of the user's inherited role IDs, this can be used * to determine if a user can access something that requires a specific role. @@ -70,22 +111,21 @@ exports.getRole = async (appId, roleId) => { */ exports.getUserRoleHierarchy = async (appId, userRoleId) => { // special case, if they don't have a role then they are a public user - if (!userRoleId) { - return [BUILTIN_IDS.PUBLIC] - } - let roleIds = [userRoleId] - let userRole = await exports.getRole(appId, userRoleId) - // check if inherited makes it possible - while ( - userRole && - userRole.inherits && - roleIds.indexOf(userRole.inherits) === -1 - ) { - roleIds.push(userRole.inherits) - // go to get the inherited incase it inherits anything - userRole = await exports.getRole(appId, userRole.inherits) - } - return roleIds + return (await getAllUserRoles(appId, userRoleId)).map(role => role._id) +} + +/** + * Get all of the user permissions which could be found across the role hierarchy + * @param appId The ID of the application from which roles should be obtained. + * @param userRoleId The user's role ID, this can be found in their access token. + * @returns {Promise} A list of permission IDs these should all be unique. + */ +exports.getUserPermissionIds = async (appId, userRoleId) => { + return [ + ...new Set( + (await getAllUserRoles(appId, userRoleId)).map(role => role.permissionId) + ), + ] } class AccessController {