From 05225f77878c570f1629c165b3ce680dc87e84d8 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 2 Dec 2020 13:20:56 +0000 Subject: [PATCH] Changing the naming of access levels to be roles. --- packages/builder/cypress/support/commands.js | 4 +- .../store/screenTemplates/utils/Screen.js | 2 +- .../components/start/CreateAppModal.svelte | 8 +- .../src/components/start/Steps/User.svelte | 2 +- .../userInterface/NewScreenModal.svelte | 2 +- packages/builder/src/constants/index.js | 2 +- packages/server/.vscode/launch.json | 4 +- .../server/src/api/controllers/accesslevel.js | 52 ------ .../server/src/api/controllers/application.js | 14 +- packages/server/src/api/controllers/auth.js | 2 +- packages/server/src/api/controllers/role.js | 45 ++++++ .../server/src/api/controllers/routing.js | 43 +++-- packages/server/src/api/controllers/screen.js | 6 +- packages/server/src/api/controllers/user.js | 30 ++-- packages/server/src/api/routes/accesslevel.js | 18 --- packages/server/src/api/routes/index.js | 4 +- packages/server/src/api/routes/role.js | 14 ++ packages/server/src/api/routes/routing.js | 3 +- packages/server/src/api/routes/screen.js | 2 +- .../src/api/routes/tests/couchTestUtils.js | 12 +- .../{accesslevel.spec.js => role.spec.js} | 54 +++---- .../server/src/api/routes/tests/user.spec.js | 8 +- .../src/automations/steps/createUser.js | 18 +-- packages/server/src/constants/index.js | 10 +- packages/server/src/constants/screens.js | 4 +- packages/server/src/db/utils.js | 16 +- .../server/src/middleware/authenticated.js | 9 +- packages/server/src/middleware/authorized.js | 8 +- .../src/utilities/builder/setBuilderToken.js | 4 +- .../src/utilities/security/accessLevels.js | 150 ------------------ .../server/src/utilities/security/roles.js | 143 +++++++++++++++++ 31 files changed, 330 insertions(+), 363 deletions(-) delete mode 100644 packages/server/src/api/controllers/accesslevel.js create mode 100644 packages/server/src/api/controllers/role.js delete mode 100644 packages/server/src/api/routes/accesslevel.js create mode 100644 packages/server/src/api/routes/role.js rename packages/server/src/api/routes/tests/{accesslevel.spec.js => role.spec.js} (50%) delete mode 100644 packages/server/src/utilities/security/accessLevels.js create mode 100644 packages/server/src/utilities/security/roles.js diff --git a/packages/builder/cypress/support/commands.js b/packages/builder/cypress/support/commands.js index 76f3417eac..ecb4eac6ce 100644 --- a/packages/builder/cypress/support/commands.js +++ b/packages/builder/cypress/support/commands.js @@ -111,7 +111,7 @@ Cypress.Commands.add("addRow", values => { }) }) -Cypress.Commands.add("createUser", (username, password, accessLevel) => { +Cypress.Commands.add("createUser", (username, password, role) => { // Create User cy.contains("Users").click() @@ -126,7 +126,7 @@ Cypress.Commands.add("createUser", (username, password, accessLevel) => { .type(username) cy.get("select") .first() - .select(accessLevel) + .select(role) // Save cy.get(".buttons") diff --git a/packages/builder/src/builderStore/store/screenTemplates/utils/Screen.js b/packages/builder/src/builderStore/store/screenTemplates/utils/Screen.js index 523296e959..76df96ae0c 100644 --- a/packages/builder/src/builderStore/store/screenTemplates/utils/Screen.js +++ b/packages/builder/src/builderStore/store/screenTemplates/utils/Screen.js @@ -18,7 +18,7 @@ export class Screen extends BaseStructure { }, routing: { route: "", - accessLevelId: "", + roleId: "", }, name: "screen-id", } diff --git a/packages/builder/src/components/start/CreateAppModal.svelte b/packages/builder/src/components/start/CreateAppModal.svelte index 049b21c995..818a527de8 100644 --- a/packages/builder/src/components/start/CreateAppModal.svelte +++ b/packages/builder/src/components/start/CreateAppModal.svelte @@ -56,8 +56,8 @@ password: string().required( "Please enter a password for your first user." ), - accessLevelId: string().required( - "You need to select an access level for your user." + roleId: string().required( + "You need to select a role for your user." ), }, ] @@ -79,9 +79,7 @@ if (hasKey) { validationSchemas.shift() - validationSchemas = validationSchemas steps.shift() - steps = steps } // Handles form navigation @@ -167,7 +165,7 @@ name: $createAppStore.values.username, username: $createAppStore.values.username, password: $createAppStore.values.password, - accessLevelId: $createAppStore.values.accessLevelId, + roleId: $createAppStore.values.roleId, } const userResp = await api.post(`/api/users`, user) const json = await userResp.json() diff --git a/packages/builder/src/components/start/Steps/User.svelte b/packages/builder/src/components/start/Steps/User.svelte index edc1fdf40b..0bf9ce19cb 100644 --- a/packages/builder/src/components/start/Steps/User.svelte +++ b/packages/builder/src/components/start/Steps/User.svelte @@ -21,7 +21,7 @@ placeholder="Password" type="password" error={blurred.password && validationErrors.password} /> - diff --git a/packages/builder/src/components/userInterface/NewScreenModal.svelte b/packages/builder/src/components/userInterface/NewScreenModal.svelte index 293f623fe7..724fcba6f8 100644 --- a/packages/builder/src/components/userInterface/NewScreenModal.svelte +++ b/packages/builder/src/components/userInterface/NewScreenModal.svelte @@ -70,7 +70,7 @@ draftScreen.props._instanceName = name draftScreen.props._component = baseComponent // TODO: need to fix this up correctly - draftScreen.routing = { route, accessLevelId: "ADMIN" } + draftScreen.routing = { route, roleId: "ADMIN" } await store.actions.screens.create(draftScreen) if (createLink) { diff --git a/packages/builder/src/constants/index.js b/packages/builder/src/constants/index.js index ae88d81bde..0f89fa57c0 100644 --- a/packages/builder/src/constants/index.js +++ b/packages/builder/src/constants/index.js @@ -3,7 +3,7 @@ export const TableNames = { } // fields on the user table that cannot be edited -export const UNEDITABLE_USER_FIELDS = ["username", "password", "accessLevelId"] +export const UNEDITABLE_USER_FIELDS = ["username", "password", "roleId"] export const DEFAULT_PAGES_OBJECT = { main: { diff --git a/packages/server/.vscode/launch.json b/packages/server/.vscode/launch.json index 7279e560d8..7417938376 100644 --- a/packages/server/.vscode/launch.json +++ b/packages/server/.vscode/launch.json @@ -52,9 +52,9 @@ { "type": "node", "request": "launch", - "name": "Jest - Access Levels", + "name": "Jest - Roles", "program": "${workspaceFolder}/node_modules/.bin/jest", - "args": ["accesslevel.spec", "--runInBand"], + "args": ["role.spec", "--runInBand"], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "disableOptimisticBPs": true, diff --git a/packages/server/src/api/controllers/accesslevel.js b/packages/server/src/api/controllers/accesslevel.js deleted file mode 100644 index b2985e2953..0000000000 --- a/packages/server/src/api/controllers/accesslevel.js +++ /dev/null @@ -1,52 +0,0 @@ -const CouchDB = require("../../db") -const { - BUILTIN_LEVELS, - AccessLevel, - getAccessLevel, -} = require("../../utilities/security/accessLevels") -const { - generateAccessLevelID, - getAccessLevelParams, -} = require("../../db/utils") - -exports.fetch = async function(ctx) { - const db = new CouchDB(ctx.user.appId) - const body = await db.allDocs( - getAccessLevelParams(null, { - include_docs: true, - }) - ) - const customAccessLevels = body.rows.map(row => row.doc) - - const staticAccessLevels = [BUILTIN_LEVELS.ADMIN, BUILTIN_LEVELS.POWER] - ctx.body = [...staticAccessLevels, ...customAccessLevels] -} - -exports.find = async function(ctx) { - ctx.body = await getAccessLevel(ctx.user.appId, ctx.params.levelId) -} - -exports.save = async function(ctx) { - const db = new CouchDB(ctx.user.appId) - - let id = ctx.request.body._id || generateAccessLevelID() - const level = new AccessLevel( - id, - ctx.request.body.name, - ctx.request.body.inherits - ) - if (ctx.request.body._rev) { - level._rev = ctx.request.body._rev - } - const result = await db.put(level) - level._rev = result.rev - ctx.body = level - ctx.message = `Access Level '${level.name}' created successfully.` -} - -exports.destroy = async function(ctx) { - const db = new CouchDB(ctx.user.appId) - await db.remove(ctx.params.levelId, ctx.params.rev) - ctx.message = `Access Level ${ctx.params.id} deleted successfully` - ctx.status = 200 -} diff --git a/packages/server/src/api/controllers/application.js b/packages/server/src/api/controllers/application.js index 2cbb84d240..6b1cf2b53e 100644 --- a/packages/server/src/api/controllers/application.js +++ b/packages/server/src/api/controllers/application.js @@ -19,7 +19,7 @@ const { generatePageID, generateScreenID, } = require("../../db/utils") -const { BUILTIN_LEVEL_IDS } = require("../../utilities/security/accessLevels") +const { BUILTIN_ROLE_IDS } = require("../../utilities/security/roles") const { downloadExtractComponentLibraries, } = require("../../utilities/createAppPackage") @@ -94,12 +94,12 @@ exports.fetchAppDefinition = async function(ctx) { const db = new CouchDB(ctx.params.appId) // TODO: need to get rid of pages here, they shouldn't be needed anymore const { mainPage, unauthPage } = await getMainAndUnauthPage(db) - const userAccessLevelId = - !ctx.user.accessLevel || !ctx.user.accessLevel._id - ? BUILTIN_LEVEL_IDS.PUBLIC - : ctx.user.accessLevel._id + const userRoleId = + !ctx.user.role || !ctx.user.role._id + ? BUILTIN_ROLE_IDS.PUBLIC + : ctx.user.role._id const correctPage = - userAccessLevelId === BUILTIN_LEVEL_IDS.PUBLIC ? unauthPage : mainPage + userRoleId === BUILTIN_ROLE_IDS.PUBLIC ? unauthPage : mainPage const screens = ( await db.allDocs( getScreenParams(correctPage._id, { @@ -107,7 +107,7 @@ exports.fetchAppDefinition = async function(ctx) { }) ) ).rows.map(row => row.doc) - // TODO: need to handle access control here, limit screens to user access level + // TODO: need to handle access control here, limit screens to user role ctx.body = { page: correctPage, screens: screens, diff --git a/packages/server/src/api/controllers/auth.js b/packages/server/src/api/controllers/auth.js index 21136b0214..de872ebfc4 100644 --- a/packages/server/src/api/controllers/auth.js +++ b/packages/server/src/api/controllers/auth.js @@ -32,7 +32,7 @@ exports.authenticate = async ctx => { if (await bcrypt.compare(password, dbUser.password)) { const payload = { userId: dbUser._id, - accessLevelId: dbUser.accessLevelId, + roleId: dbUser.roleId, version: app.version, permissions: dbUser.permissions || [], } diff --git a/packages/server/src/api/controllers/role.js b/packages/server/src/api/controllers/role.js new file mode 100644 index 0000000000..ff34a4e954 --- /dev/null +++ b/packages/server/src/api/controllers/role.js @@ -0,0 +1,45 @@ +const CouchDB = require("../../db") +const { + BUILTIN_ROLES, + Role, + getRole, +} = require("../../utilities/security/roles") +const { generateRoleID, getRoleParams } = require("../../db/utils") + +exports.fetch = async function(ctx) { + const db = new CouchDB(ctx.user.appId) + const body = await db.allDocs( + getRoleParams(null, { + include_docs: true, + }) + ) + const customRoles = body.rows.map(row => row.doc) + + const staticRoles = [BUILTIN_ROLES.ADMIN, BUILTIN_ROLES.POWER] + ctx.body = [...staticRoles, ...customRoles] +} + +exports.find = async function(ctx) { + ctx.body = await getRole(ctx.user.appId, ctx.params.roleId) +} + +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) + if (ctx.request.body._rev) { + role._rev = ctx.request.body._rev + } + const result = await db.put(role) + role._rev = result.rev + ctx.body = role + ctx.message = `Role '${role.name}' created successfully.` +} + +exports.destroy = async function(ctx) { + const db = new CouchDB(ctx.user.appId) + await db.remove(ctx.params.roleId, ctx.params.rev) + ctx.message = `Role ${ctx.params.id} deleted successfully` + ctx.status = 200 +} diff --git a/packages/server/src/api/controllers/routing.js b/packages/server/src/api/controllers/routing.js index f068291ac3..ab1489145b 100644 --- a/packages/server/src/api/controllers/routing.js +++ b/packages/server/src/api/controllers/routing.js @@ -1,8 +1,8 @@ const { getRoutingInfo } = require("../../utilities/routing") const { - getUserAccessLevelHierarchy, - BUILTIN_LEVEL_IDS, -} = require("../../utilities/security/accessLevels") + getUserRoleHierarchy, + BUILTIN_ROLE_IDS, +} = require("../../utilities/security/roles") const URL_SEPARATOR = "/" @@ -33,15 +33,15 @@ Routing.prototype.getScreensProp = function(fullpath) { return this.json[topLevel].subpaths[fullpath].screens } -Routing.prototype.addScreenId = function(fullpath, accessLevel, screenId) { - this.getScreensProp(fullpath)[accessLevel] = screenId +Routing.prototype.addScreenId = function(fullpath, roleId, screenId) { + this.getScreensProp(fullpath)[roleId] = screenId } /** * Gets the full routing structure by querying the routing view and processing the result into the tree. * @param {string} appId The application to produce the routing structure for. * @returns {Promise} The routing structure, this is the full structure designed for use in the builder, - * if the client routing is required then the updateRoutingStructureForUserLevel should be used. + * if the client routing is required then the updateRoutingStructureForUserRole should be used. */ async function getRoutingStructure(appId) { const screenRoutes = await getRoutingInfo(appId) @@ -49,8 +49,8 @@ async function getRoutingStructure(appId) { for (let screenRoute of screenRoutes) { let fullpath = screenRoute.routing.route - const accessLevel = screenRoute.routing.accessLevelId - routing.addScreenId(fullpath, accessLevel, screenRoute.id) + const roleId = screenRoute.routing.roleId + routing.addScreenId(fullpath, roleId, screenRoute.id) } return { routes: routing.json } @@ -62,29 +62,26 @@ exports.fetch = async ctx => { exports.clientFetch = async ctx => { const routing = await getRoutingStructure(ctx.appId) - let accessLevelId = ctx.user.accessLevel._id + let roleId = ctx.user.role._id // builder is a special case, always return the full routing structure - if (accessLevelId === BUILTIN_LEVEL_IDS.BUILDER) { - accessLevelId = BUILTIN_LEVEL_IDS.ADMIN + if (roleId === BUILTIN_ROLE_IDS.BUILDER) { + roleId = BUILTIN_ROLE_IDS.ADMIN } - const accessLevelIds = await getUserAccessLevelHierarchy( - ctx.appId, - accessLevelId - ) + const roleIds = await getUserRoleHierarchy(ctx.appId, roleId) for (let topLevel of Object.values(routing.routes)) { for (let subpathKey of Object.keys(topLevel.subpaths)) { let found = false const subpath = topLevel.subpaths[subpathKey] - const accessLevelOptions = Object.keys(subpath.screens) - if (accessLevelOptions.length === 1 && !accessLevelOptions[0]) { - subpath.screenId = subpath.screens[accessLevelOptions[0]] - subpath.accessLevelId = BUILTIN_LEVEL_IDS.BASIC + const roleOptions = Object.keys(subpath.screens) + if (roleOptions.length === 1 && !roleOptions[0]) { + subpath.screenId = subpath.screens[roleOptions[0]] + subpath.roleId = BUILTIN_ROLE_IDS.BASIC found = true } else { - for (let levelId of accessLevelIds) { - if (accessLevelOptions.indexOf(levelId) !== -1) { - subpath.screenId = subpath.screens[levelId] - subpath.accessLevelId = levelId + for (let roleId of roleIds) { + if (roleOptions.indexOf(roleId) !== -1) { + subpath.screenId = subpath.screens[roleId] + subpath.roleId = roleId found = true break } diff --git a/packages/server/src/api/controllers/screen.js b/packages/server/src/api/controllers/screen.js index 694d171fff..0af37a73da 100644 --- a/packages/server/src/api/controllers/screen.js +++ b/packages/server/src/api/controllers/screen.js @@ -1,6 +1,6 @@ const CouchDB = require("../../db") const { getScreenParams, generateScreenID } = require("../../db/utils") -const { AccessController } = require("../../utilities/security/accessLevels") +const { AccessController } = require("../../utilities/security/roles") exports.fetch = async ctx => { const appId = ctx.user.appId @@ -16,7 +16,7 @@ exports.fetch = async ctx => { ctx.body = await new AccessController(appId).checkScreensAccess( screens, - ctx.user.accessLevel._id + ctx.user.role._id ) } @@ -32,7 +32,7 @@ exports.find = async ctx => { ctx.body = await new AccessController(appId).checkScreensAccess( screens, - ctx.user.accessLevel._id + ctx.user.role._id ) } diff --git a/packages/server/src/api/controllers/user.js b/packages/server/src/api/controllers/user.js index f67ae72a89..f20ccd3d7e 100644 --- a/packages/server/src/api/controllers/user.js +++ b/packages/server/src/api/controllers/user.js @@ -1,9 +1,7 @@ const CouchDB = require("../../db") const bcrypt = require("../../utilities/bcrypt") const { generateUserID, getUserParams, ViewNames } = require("../../db/utils") -const { - BUILTIN_LEVEL_ID_ARRAY, -} = require("../../utilities/security/accessLevels") +const { BUILTIN_ROLE_ID_ARRAY } = require("../../utilities/security/roles") const { BUILTIN_PERMISSION_NAMES, } = require("../../utilities/security/permissions") @@ -20,21 +18,15 @@ exports.fetch = async function(ctx) { exports.create = async function(ctx) { const db = new CouchDB(ctx.user.appId) - const { - username, - password, - name, - accessLevelId, - permissions, - } = ctx.request.body + const { username, password, name, roleId, permissions } = ctx.request.body if (!username || !password) { ctx.throw(400, "Username and Password Required.") } - const accessLevel = await checkAccessLevel(db, accessLevelId) + const role = await checkRole(db, roleId) - if (!accessLevel) ctx.throw(400, "Invalid Access Level") + if (!role) ctx.throw(400, "Invalid Role") const user = { _id: generateUserID(username), @@ -42,7 +34,7 @@ exports.create = async function(ctx) { password: await bcrypt.hash(password), name: name || username, type: "user", - accessLevelId, + roleId, permissions: permissions || [BUILTIN_PERMISSION_NAMES.POWER], tableId: ViewNames.USERS, } @@ -97,14 +89,14 @@ exports.find = async function(ctx) { } } -const checkAccessLevel = async (db, accessLevelId) => { - if (!accessLevelId) return - if (BUILTIN_LEVEL_ID_ARRAY.indexOf(accessLevelId) !== -1) { +const checkRole = async (db, roleId) => { + if (!roleId) return + if (BUILTIN_ROLE_ID_ARRAY.indexOf(roleId) !== -1) { return { - _id: accessLevelId, - name: accessLevelId, + _id: roleId, + name: roleId, permissions: [], } } - return await db.get(accessLevelId) + return await db.get(roleId) } diff --git a/packages/server/src/api/routes/accesslevel.js b/packages/server/src/api/routes/accesslevel.js deleted file mode 100644 index 1f21a1ea29..0000000000 --- a/packages/server/src/api/routes/accesslevel.js +++ /dev/null @@ -1,18 +0,0 @@ -const Router = require("@koa/router") -const controller = require("../controllers/accesslevel") -const authorized = require("../../middleware/authorized") -const { BUILDER } = require("../../utilities/security/permissions") - -const router = Router() - -router - .post("/api/accesslevels", authorized(BUILDER), controller.save) - .get("/api/accesslevels", authorized(BUILDER), controller.fetch) - .get("/api/accesslevels/:levelId", authorized(BUILDER), controller.find) - .delete( - "/api/accesslevels/:levelId/:rev", - authorized(BUILDER), - controller.destroy - ) - -module.exports = router diff --git a/packages/server/src/api/routes/index.js b/packages/server/src/api/routes/index.js index 840f968eb3..81c26b366d 100644 --- a/packages/server/src/api/routes/index.js +++ b/packages/server/src/api/routes/index.js @@ -10,7 +10,7 @@ const staticRoutes = require("./static") const componentRoutes = require("./component") const automationRoutes = require("./automation") const webhookRoutes = require("./webhook") -const accesslevelRoutes = require("./accesslevel") +const roleRoutes = require("./role") const deployRoutes = require("./deploy") const apiKeysRoutes = require("./apikeys") const templatesRoutes = require("./templates") @@ -26,7 +26,7 @@ exports.mainRoutes = [ automationRoutes, viewRoutes, componentRoutes, - accesslevelRoutes, + roleRoutes, apiKeysRoutes, templatesRoutes, analyticsRoutes, diff --git a/packages/server/src/api/routes/role.js b/packages/server/src/api/routes/role.js new file mode 100644 index 0000000000..e0a157d8b8 --- /dev/null +++ b/packages/server/src/api/routes/role.js @@ -0,0 +1,14 @@ +const Router = require("@koa/router") +const controller = require("../controllers/role") +const authorized = require("../../middleware/authorized") +const { BUILDER } = require("../../utilities/security/permissions") + +const router = Router() + +router + .post("/api/roles", authorized(BUILDER), 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) + +module.exports = router diff --git a/packages/server/src/api/routes/routing.js b/packages/server/src/api/routes/routing.js index 32eb14d390..99903db790 100644 --- a/packages/server/src/api/routes/routing.js +++ b/packages/server/src/api/routes/routing.js @@ -5,9 +5,10 @@ const controller = require("../controllers/routing") const router = Router() -// gets the full structure, not just the correct screen ID for your access level router + // gets correct structure for user role .get("/api/routing/client", controller.clientFetch) + // gets the full structure, not just the correct screen ID for user role .get("/api/routing", authorized(BUILDER), controller.fetch) module.exports = router diff --git a/packages/server/src/api/routes/screen.js b/packages/server/src/api/routes/screen.js index ce49f66043..8a15d6afef 100644 --- a/packages/server/src/api/routes/screen.js +++ b/packages/server/src/api/routes/screen.js @@ -14,7 +14,7 @@ function generateSaveValidation() { name: Joi.string().required(), routing: Joi.object({ route: Joi.string().required(), - accessLevelId: Joi.string().required().allow(""), + roleId: Joi.string().required().allow(""), }).required().unknown(true), props: Joi.object({ _id: Joi.string().required(), diff --git a/packages/server/src/api/routes/tests/couchTestUtils.js b/packages/server/src/api/routes/tests/couchTestUtils.js index ee12c1c6d8..61333a59d0 100644 --- a/packages/server/src/api/routes/tests/couchTestUtils.js +++ b/packages/server/src/api/routes/tests/couchTestUtils.js @@ -1,8 +1,8 @@ const CouchDB = require("../../../db") const supertest = require("supertest") const { - BUILTIN_LEVEL_IDS, -} = require("../../../utilities/security/accessLevels") + BUILTIN_ROLE_IDS, +} = require("../../../utilities/security/roles") const { BUILTIN_PERMISSION_NAMES, } = require("../../../utilities/security/permissions") @@ -26,7 +26,7 @@ exports.supertest = async () => { exports.defaultHeaders = appId => { const builderUser = { userId: "BUILDER", - accessLevelId: BUILTIN_LEVEL_IDS.BUILDER, + roleId: BUILTIN_ROLE_IDS.BUILDER, } const builderToken = jwt.sign(builderUser, env.JWT_SECRET) @@ -128,7 +128,7 @@ exports.createUser = async ( name: "Bill", username, password, - accessLevelId: BUILTIN_LEVEL_IDS.POWER, + roleId: BUILTIN_ROLE_IDS.POWER, }) return res.body } @@ -184,13 +184,13 @@ const createUserWithPermissions = async ( name: username, username, password, - accessLevelId: BUILTIN_LEVEL_IDS.POWER, + roleId: BUILTIN_ROLE_IDS.POWER, permissions, }) const anonUser = { userId: "ANON", - accessLevelId: BUILTIN_LEVEL_IDS.PUBLIC, + roleId: BUILTIN_ROLE_IDS.PUBLIC, appId: appId, version: packageJson.version, } diff --git a/packages/server/src/api/routes/tests/accesslevel.spec.js b/packages/server/src/api/routes/tests/role.spec.js similarity index 50% rename from packages/server/src/api/routes/tests/accesslevel.spec.js rename to packages/server/src/api/routes/tests/role.spec.js index eeb786f7d3..ca451c85c2 100644 --- a/packages/server/src/api/routes/tests/accesslevel.spec.js +++ b/packages/server/src/api/routes/tests/role.spec.js @@ -6,12 +6,12 @@ const { defaultHeaders } = require("./couchTestUtils") const { - BUILTIN_LEVEL_IDS, -} = require("../../../utilities/security/accessLevels") + BUILTIN_ROLE_IDS, +} = require("../../../utilities/security/roles") -const accessLevelBody = { name: "user", inherits: BUILTIN_LEVEL_IDS.BASIC } +const roleBody = { name: "user", inherits: BUILTIN_ROLE_IDS.BASIC } -describe("/accesslevels", () => { +describe("/roles", () => { let server let request let appId @@ -35,15 +35,15 @@ describe("/accesslevels", () => { describe("create", () => { - it("returns a success message when level is successfully created", async () => { + it("returns a success message when role is successfully created", async () => { const res = await request - .post(`/api/accesslevels`) - .send(accessLevelBody) + .post(`/api/roles`) + .send(roleBody) .set(defaultHeaders(appId)) .expect('Content-Type', /json/) .expect(200) - expect(res.res.statusMessage).toEqual("Access Level 'user' created successfully.") + expect(res.res.statusMessage).toEqual("Role 'user' created successfully.") expect(res.body._id).toBeDefined() expect(res.body._rev).toBeDefined() }) @@ -52,57 +52,57 @@ describe("/accesslevels", () => { describe("fetch", () => { - it("should list custom levels, plus 2 default levels", async () => { + it("should list custom roles, plus 2 default roles", async () => { const createRes = await request - .post(`/api/accesslevels`) - .send(accessLevelBody) + .post(`/api/roles`) + .send(roleBody) .set(defaultHeaders(appId)) .expect('Content-Type', /json/) .expect(200) - const customLevel = createRes.body + const customRole = createRes.body const res = await request - .get(`/api/accesslevels`) + .get(`/api/roles`) .set(defaultHeaders(appId)) .expect('Content-Type', /json/) .expect(200) expect(res.body.length).toBe(3) - const adminLevel = res.body.find(r => r._id === BUILTIN_LEVEL_IDS.ADMIN) - expect(adminLevel.inherits).toEqual(BUILTIN_LEVEL_IDS.POWER) - expect(adminLevel).toBeDefined() + const adminRole = res.body.find(r => r._id === BUILTIN_ROLE_IDS.ADMIN) + expect(adminRole.inherits).toEqual(BUILTIN_ROLE_IDS.POWER) + expect(adminRole).toBeDefined() - const powerUserLevel = res.body.find(r => r._id === BUILTIN_LEVEL_IDS.POWER) - expect(powerUserLevel.inherits).toEqual(BUILTIN_LEVEL_IDS.BASIC) - expect(powerUserLevel).toBeDefined() + const powerUserRole = res.body.find(r => r._id === BUILTIN_ROLE_IDS.POWER) + expect(powerUserRole.inherits).toEqual(BUILTIN_ROLE_IDS.BASIC) + expect(powerUserRole).toBeDefined() - const customLevelFetched = res.body.find(r => r._id === customLevel._id) - expect(customLevelFetched.inherits).toEqual(BUILTIN_LEVEL_IDS.BASIC) - expect(customLevelFetched).toBeDefined() + const customRoleFetched = res.body.find(r => r._id === customRole._id) + expect(customRoleFetched.inherits).toEqual(BUILTIN_ROLE_IDS.BASIC) + expect(customRoleFetched).toBeDefined() }) }); describe("destroy", () => { - it("should delete custom access level", async () => { + it("should delete custom roles", async () => { const createRes = await request - .post(`/api/accesslevels`) + .post(`/api/roles`) .send({ name: "user" }) .set(defaultHeaders(appId)) .expect('Content-Type', /json/) .expect(200) - const customLevel = createRes.body + const customRole = createRes.body await request - .delete(`/api/accesslevels/${customLevel._id}/${customLevel._rev}`) + .delete(`/api/roles/${customRole._id}/${customRole._rev}`) .set(defaultHeaders(appId)) .expect(200) await request - .get(`/api/accesslevels/${customLevel._id}`) + .get(`/api/roles/${customRole._id}`) .set(defaultHeaders(appId)) .expect(404) }) diff --git a/packages/server/src/api/routes/tests/user.spec.js b/packages/server/src/api/routes/tests/user.spec.js index f9277039ea..96e5923d1d 100644 --- a/packages/server/src/api/routes/tests/user.spec.js +++ b/packages/server/src/api/routes/tests/user.spec.js @@ -9,8 +9,8 @@ const { BUILTIN_PERMISSION_NAMES, } = require("../../../utilities/security/permissions") const { - BUILTIN_LEVEL_IDS, -} = require("../../../utilities/security/accessLevels") + BUILTIN_ROLE_IDS, +} = require("../../../utilities/security/roles") describe("/users", () => { let request @@ -67,7 +67,7 @@ describe("/users", () => { const res = await request .post(`/api/users`) .set(defaultHeaders(appId)) - .send({ name: "Bill", username: "bill", password: "bills_password", accessLevelId: BUILTIN_LEVEL_IDS.POWER }) + .send({ name: "Bill", username: "bill", password: "bills_password", roleId: BUILTIN_ROLE_IDS.POWER }) .expect(200) .expect('Content-Type', /json/) @@ -79,7 +79,7 @@ describe("/users", () => { await testPermissionsForEndpoint({ request, method: "POST", - body: { name: "brandNewUser", username: "brandNewUser", password: "yeeooo", accessLevelId: BUILTIN_LEVEL_IDS.POWER }, + body: { name: "brandNewUser", username: "brandNewUser", password: "yeeooo", roleId: BUILTIN_ROLE_IDS.POWER }, url: `/api/users`, appId: appId, permName1: BUILTIN_PERMISSION_NAMES.ADMIN, diff --git a/packages/server/src/automations/steps/createUser.js b/packages/server/src/automations/steps/createUser.js index b24c2cbe56..2e124b148b 100644 --- a/packages/server/src/automations/steps/createUser.js +++ b/packages/server/src/automations/steps/createUser.js @@ -1,4 +1,4 @@ -const accessLevels = require("../../utilities/security/accessLevels") +const roles = require("../../utilities/security/roles") const userController = require("../../api/controllers/user") const env = require("../../environment") const usage = require("../../utilities/usageQuota") @@ -11,7 +11,7 @@ module.exports.definition = { type: "ACTION", stepId: "CREATE_USER", inputs: { - accessLevelId: accessLevels.BUILTIN_LEVEL_IDS.POWER, + roleId: roles.BUILTIN_ROLE_IDS.POWER, }, schema: { inputs: { @@ -25,14 +25,14 @@ module.exports.definition = { customType: "password", title: "Password", }, - accessLevelId: { + roleId: { type: "string", - title: "Access Level", - enum: accessLevels.BUILTIN_LEVEL_ID_ARRAY, - pretty: accessLevels.BUILTIN_LEVEL_NAME_ARRAY, + title: "Role", + enum: roles.BUILTIN_ROLE_ID_ARRAY, + pretty: roles.BUILTIN_ROLE_NAME_ARRAY, }, }, - required: ["username", "password", "accessLevelId"], + required: ["username", "password", "roleId"], }, outputs: { properties: { @@ -59,13 +59,13 @@ module.exports.definition = { } module.exports.run = async function({ inputs, appId, apiKey }) { - const { username, password, accessLevelId } = inputs + const { username, password, roleId } = inputs const ctx = { user: { appId: appId, }, request: { - body: { username, password, accessLevelId }, + body: { username, password, roleId }, }, } diff --git a/packages/server/src/constants/index.js b/packages/server/src/constants/index.js index 24fe45eb6b..d691bcee55 100644 --- a/packages/server/src/constants/index.js +++ b/packages/server/src/constants/index.js @@ -1,4 +1,4 @@ -const { BUILTIN_LEVEL_IDS } = require("../utilities/security/accessLevels") +const { BUILTIN_ROLE_IDS } = require("../utilities/security/roles") const AuthTypes = { APP: "app", @@ -24,14 +24,14 @@ const USERS_TABLE_SCHEMA = { fieldName: "username", name: "username", }, - accessLevelId: { - fieldName: "accessLevelId", - name: "accessLevelId", + roleId: { + fieldName: "roleId", + name: "roleId", type: "options", constraints: { type: "string", presence: false, - inclusion: Object.keys(BUILTIN_LEVEL_IDS), + inclusion: Object.keys(BUILTIN_ROLE_IDS), }, }, }, diff --git a/packages/server/src/constants/screens.js b/packages/server/src/constants/screens.js index a4e9c031ed..394d60b3f8 100644 --- a/packages/server/src/constants/screens.js +++ b/packages/server/src/constants/screens.js @@ -1,4 +1,4 @@ -const { BUILTIN_LEVEL_IDS } = require("../utilities/security/accessLevels") +const { BUILTIN_ROLE_IDS } = require("../utilities/security/roles") exports.HOME_SCREEN = { description: "", @@ -97,7 +97,7 @@ exports.HOME_SCREEN = { }, routing: { route: "/", - accessLevelId: BUILTIN_LEVEL_IDS.BASIC, + roleId: BUILTIN_ROLE_IDS.BASIC, }, name: "d834fea2-1b3e-4320-ab34-f9009f5ecc59", } diff --git a/packages/server/src/db/utils.js b/packages/server/src/db/utils.js index 96fd92218e..1989633463 100644 --- a/packages/server/src/db/utils.js +++ b/packages/server/src/db/utils.js @@ -10,7 +10,7 @@ const DocumentTypes = { AUTOMATION: "au", LINK: "li", APP: "app", - ACCESS_LEVEL: "ac", + ROLE: "role", WEBHOOK: "wh", INSTANCE: "inst", PAGE: "page", @@ -169,18 +169,18 @@ exports.getAppParams = (appId = null, otherProps = {}) => { } /** - * Generates a new access level ID. - * @returns {string} The new access level ID which the access level doc can be stored under. + * Generates a new role ID. + * @returns {string} The new role ID which the role doc can be stored under. */ -exports.generateAccessLevelID = () => { - return `${DocumentTypes.ACCESS_LEVEL}${SEPARATOR}${newid()}` +exports.generateRoleID = () => { + return `${DocumentTypes.ROLE}${SEPARATOR}${newid()}` } /** - * Gets parameters for retrieving an access level, this is a utility function for the getDocParams function. + * Gets parameters for retrieving a role, this is a utility function for the getDocParams function. */ -exports.getAccessLevelParams = (accessLevelId = null, otherProps = {}) => { - return getDocParams(DocumentTypes.ACCESS_LEVEL, accessLevelId, otherProps) +exports.getRoleParams = (roleId = null, otherProps = {}) => { + return getDocParams(DocumentTypes.ROLE, roleId, otherProps) } /** diff --git a/packages/server/src/middleware/authenticated.js b/packages/server/src/middleware/authenticated.js index 497c24699d..277c2b28db 100644 --- a/packages/server/src/middleware/authenticated.js +++ b/packages/server/src/middleware/authenticated.js @@ -1,9 +1,6 @@ const jwt = require("jsonwebtoken") const STATUS_CODES = require("../utilities/statusCodes") -const { - getAccessLevel, - BUILTIN_LEVELS, -} = require("../utilities/security/accessLevels") +const { getRole, BUILTIN_ROLES } = require("../utilities/security/roles") const { AuthTypes } = require("../constants") const { getAppId, getCookieName, setCookie, isClient } = require("../utilities") @@ -35,7 +32,7 @@ module.exports = async (ctx, next) => { ctx.appId = appId ctx.user = { appId, - accessLevel: BUILTIN_LEVELS.PUBLIC, + role: BUILTIN_ROLES.PUBLIC, } await next() return @@ -49,7 +46,7 @@ module.exports = async (ctx, next) => { ctx.user = { ...jwtPayload, appId: appId, - accessLevel: await getAccessLevel(appId, jwtPayload.accessLevelId), + role: await getRole(appId, jwtPayload.roleId), } } catch (err) { ctx.throw(err.status || STATUS_CODES.FORBIDDEN, err.text) diff --git a/packages/server/src/middleware/authorized.js b/packages/server/src/middleware/authorized.js index 5f4b78b97e..f18cf3b5c8 100644 --- a/packages/server/src/middleware/authorized.js +++ b/packages/server/src/middleware/authorized.js @@ -1,4 +1,4 @@ -const { BUILTIN_LEVEL_IDS } = require("../utilities/security/accessLevels") +const { BUILTIN_ROLE_IDS } = require("../utilities/security/roles") const { PermissionTypes, doesHavePermission, @@ -7,7 +7,7 @@ const env = require("../environment") const { apiKeyTable } = require("../db/dynamoClient") const { AuthTypes } = require("../constants") -const ADMIN_ACCESS = [BUILTIN_LEVEL_IDS.ADMIN, BUILTIN_LEVEL_IDS.BUILDER] +const ADMIN_ROLES = [BUILTIN_ROLE_IDS.ADMIN, BUILTIN_ROLE_IDS.BUILDER] const LOCAL_PASS = new RegExp(["webhooks/trigger", "webhooks/schema"].join("|")) @@ -47,9 +47,9 @@ module.exports = (permType, permLevel = null) => async (ctx, next) => { ctx.throw(403, "User not found") } - const accessLevel = ctx.user.accessLevel + const role = ctx.user.role const permissions = ctx.user.permissions - if (ADMIN_ACCESS.indexOf(accessLevel._id) !== -1) { + 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 f3adf079ad..ed374a1d8e 100644 --- a/packages/server/src/utilities/builder/setBuilderToken.js +++ b/packages/server/src/utilities/builder/setBuilderToken.js @@ -1,4 +1,4 @@ -const { BUILTIN_LEVEL_IDS } = require("../security/accessLevels") +const { BUILTIN_ROLE_IDS } = require("../security/roles") const { BUILTIN_PERMISSION_NAMES } = require("../security/permissions") const env = require("../../environment") const CouchDB = require("../../db") @@ -10,7 +10,7 @@ const APP_PREFIX = DocumentTypes.APP + SEPARATOR module.exports = async (ctx, appId, version) => { const builderUser = { userId: "BUILDER", - accessLevelId: BUILTIN_LEVEL_IDS.BUILDER, + roleId: BUILTIN_ROLE_IDS.BUILDER, permissions: [BUILTIN_PERMISSION_NAMES.ADMIN], version, } diff --git a/packages/server/src/utilities/security/accessLevels.js b/packages/server/src/utilities/security/accessLevels.js deleted file mode 100644 index 344dd20930..0000000000 --- a/packages/server/src/utilities/security/accessLevels.js +++ /dev/null @@ -1,150 +0,0 @@ -const CouchDB = require("../../db") -const { cloneDeep } = require("lodash/fp") - -const BUILTIN_IDS = { - ADMIN: "ADMIN", - POWER: "POWER_USER", - BASIC: "BASIC", - PUBLIC: "PUBLIC", - BUILDER: "BUILDER", -} - -function AccessLevel(id, name, inherits) { - this._id = id - this.name = name - if (inherits) { - this.inherits = inherits - } -} - -exports.BUILTIN_LEVELS = { - ADMIN: new AccessLevel(BUILTIN_IDS.ADMIN, "Admin", BUILTIN_IDS.POWER), - POWER: new AccessLevel(BUILTIN_IDS.POWER, "Power", BUILTIN_IDS.BASIC), - BASIC: new AccessLevel(BUILTIN_IDS.BASIC, "Basic", BUILTIN_IDS.PUBLIC), - PUBLIC: new AccessLevel(BUILTIN_IDS.PUBLIC, "Public"), - BUILDER: new AccessLevel(BUILTIN_IDS.BUILDER, "Builder"), -} - -exports.BUILTIN_LEVEL_ID_ARRAY = Object.values(exports.BUILTIN_LEVELS).map( - level => level._id -) - -exports.BUILTIN_LEVEL_NAME_ARRAY = Object.values(exports.BUILTIN_LEVELS).map( - level => level.name -) - -function isBuiltin(accessLevel) { - return exports.BUILTIN_LEVEL_ID_ARRAY.indexOf(accessLevel) !== -1 -} - -/** - * Gets the access level object, this is mainly useful for two purposes, to check if the level exists and - * to check if the access level inherits any others. - * @param {string} appId The app in which to look for the access level. - * @param {string|null} accessLevelId The level ID to lookup. - * @returns {Promise} The access level object, which may contain an "inherits" property. - */ -exports.getAccessLevel = async (appId, accessLevelId) => { - if (!accessLevelId) { - return null - } - let accessLevel - if (isBuiltin(accessLevelId)) { - accessLevel = cloneDeep( - Object.values(exports.BUILTIN_LEVELS).find( - level => level._id === accessLevelId - ) - ) - } else { - const db = new CouchDB(appId) - accessLevel = await db.get(accessLevelId) - } - return accessLevel -} - -/** - * Returns an ordered array of the user's inherited access level IDs, this can be used - * to determine if a user can access something that requires a specific access level. - * @param {string} appId The ID of the application from which access levels should be obtained. - * @param {string} userAccessLevelId The user's access level, this can be found in their access token. - * @returns {Promise} returns an ordered array of the access levels, with the first being their - * highest level of access and the last being the lowest level. - */ -exports.getUserAccessLevelHierarchy = async (appId, userAccessLevelId) => { - // special case, if they don't have a level then they are a public user - if (!userAccessLevelId) { - return [BUILTIN_IDS.PUBLIC] - } - let accessLevelIds = [userAccessLevelId] - let userAccess = await exports.getAccessLevel(appId, userAccessLevelId) - // check if inherited makes it possible - while ( - userAccess && - userAccess.inherits && - accessLevelIds.indexOf(userAccess.inherits) === -1 - ) { - accessLevelIds.push(userAccess.inherits) - // go to get the inherited incase it inherits anything - userAccess = await exports.getAccessLevel(appId, userAccess.inherits) - } - // add the user's actual level at the end (not at start as that stops iteration - return accessLevelIds -} - -class AccessController { - constructor(appId) { - this.appId = appId - this.userHierarchies = {} - } - - async hasAccess(tryingAccessLevelId, userAccessLevelId) { - // special cases, the screen has no access level, the access levels are the same or the user - // is currently in the builder - if ( - tryingAccessLevelId == null || - tryingAccessLevelId === "" || - tryingAccessLevelId === userAccessLevelId || - userAccessLevelId === BUILTIN_IDS.BUILDER - ) { - return true - } - let accessLevelIds = this.userHierarchies[userAccessLevelId] - if (!accessLevelIds) { - accessLevelIds = await exports.getUserAccessLevelHierarchy( - this.appId, - userAccessLevelId - ) - this.userHierarchies[userAccessLevelId] = userAccessLevelId - } - - return accessLevelIds.indexOf(tryingAccessLevelId) !== -1 - } - - async checkScreensAccess(screens, userAccessLevelId) { - let accessibleScreens = [] - // don't want to handle this with Promise.all as this would mean all custom access levels would be - // retrieved at same time, it is likely a custom levels will be re-used and therefore want - // to work in sync for performance save - for (let screen of screens) { - const accessible = await this.checkScreenAccess(screen, userAccessLevelId) - if (accessible) { - accessibleScreens.push(accessible) - } - } - return accessibleScreens - } - - async checkScreenAccess(screen, userAccessLevelId) { - const accessLevelId = - screen && screen.routing ? screen.routing.accessLevelId : null - if (await this.hasAccess(accessLevelId, userAccessLevelId)) { - return screen - } - return null - } -} - -exports.AccessController = AccessController -exports.BUILTIN_LEVEL_IDS = BUILTIN_IDS -exports.isBuiltin = isBuiltin -exports.AccessLevel = AccessLevel diff --git a/packages/server/src/utilities/security/roles.js b/packages/server/src/utilities/security/roles.js new file mode 100644 index 0000000000..09ca2c23fd --- /dev/null +++ b/packages/server/src/utilities/security/roles.js @@ -0,0 +1,143 @@ +const CouchDB = require("../../db") +const { cloneDeep } = require("lodash/fp") + +const BUILTIN_IDS = { + ADMIN: "ADMIN", + POWER: "POWER_USER", + BASIC: "BASIC", + PUBLIC: "PUBLIC", + BUILDER: "BUILDER", +} + +function Role(id, name, inherits) { + this._id = id + this.name = name + if (inherits) { + this.inherits = inherits + } +} + +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"), +} + +exports.BUILTIN_ROLE_ID_ARRAY = Object.values(exports.BUILTIN_ROLES).map( + level => level._id +) + +exports.BUILTIN_ROLE_NAME_ARRAY = Object.values(exports.BUILTIN_ROLES).map( + level => level.name +) + +function isBuiltin(role) { + return exports.BUILTIN_ROLE_ID_ARRAY.indexOf(role) !== -1 +} + +/** + * Gets the role object, this is mainly useful for two purposes, to check if the level exists and + * to check if the role inherits any others. + * @param {string} appId The app in which to look for the role. + * @param {string|null} roleId The level ID to lookup. + * @returns {Promise} The role object, which may contain an "inherits" property. + */ +exports.getRole = async (appId, roleId) => { + if (!roleId) { + return null + } + let role + if (isBuiltin(roleId)) { + role = cloneDeep( + Object.values(exports.BUILTIN_ROLES).find(role => role._id === roleId) + ) + } else { + const db = new CouchDB(appId) + role = await db.get(roleId) + } + return role +} + +/** + * 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. + * @param {string} appId The ID of the application from which roles should be obtained. + * @param {string} userRoleId The user's role ID, this can be found in their access token. + * @returns {Promise} returns an ordered array of the roles, with the first being their + * highest level of access and the last being the lowest level. + */ +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 +} + +class AccessController { + constructor(appId) { + this.appId = appId + this.userHierarchies = {} + } + + async hasAccess(tryingRoleId, userRoleId) { + // special cases, the screen has no role, the roles are the same or the user + // is currently in the builder + if ( + tryingRoleId == null || + tryingRoleId === "" || + tryingRoleId === userRoleId || + tryingRoleId === BUILTIN_IDS.BUILDER + ) { + return true + } + let roleIds = this.userHierarchies[userRoleId] + if (!roleIds) { + roleIds = await exports.getUserRoleHierarchy(this.appId, userRoleId) + this.userHierarchies[userRoleId] = userRoleId + } + + return roleIds.indexOf(tryingRoleId) !== -1 + } + + async checkScreensAccess(screens, userRoleId) { + let accessibleScreens = [] + // don't want to handle this with Promise.all as this would mean all custom roles would be + // retrieved at same time, it is likely a custom role will be re-used and therefore want + // to work in sync for performance save + for (let screen of screens) { + const accessible = await this.checkScreenAccess(screen, userRoleId) + if (accessible) { + accessibleScreens.push(accessible) + } + } + return accessibleScreens + } + + async checkScreenAccess(screen, userRoleId) { + const roleId = screen && screen.routing ? screen.routing.roleId : null + if (await this.hasAccess(roleId, userRoleId)) { + return screen + } + return null + } +} + +exports.AccessController = AccessController +exports.BUILTIN_ROLE_IDS = BUILTIN_IDS +exports.isBuiltin = isBuiltin +exports.Role = Role