merge
This commit is contained in:
commit
894eeb8757
|
@ -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
|
// Create User
|
||||||
cy.contains("Users").click()
|
cy.contains("Users").click()
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ Cypress.Commands.add("createUser", (username, password, accessLevel) => {
|
||||||
.type(username)
|
.type(username)
|
||||||
cy.get("select")
|
cy.get("select")
|
||||||
.first()
|
.first()
|
||||||
.select(accessLevel)
|
.select(role)
|
||||||
|
|
||||||
// Save
|
// Save
|
||||||
cy.get(".buttons")
|
cy.get(".buttons")
|
||||||
|
|
|
@ -18,7 +18,7 @@ export class Screen extends BaseStructure {
|
||||||
},
|
},
|
||||||
routing: {
|
routing: {
|
||||||
route: "",
|
route: "",
|
||||||
accessLevelId: "",
|
roleId: "",
|
||||||
},
|
},
|
||||||
name: "screen-id",
|
name: "screen-id",
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,9 +56,7 @@
|
||||||
password: string().required(
|
password: string().required(
|
||||||
"Please enter a password for your first user."
|
"Please enter a password for your first user."
|
||||||
),
|
),
|
||||||
accessLevelId: string().required(
|
roleId: string().required("You need to select a role for your user."),
|
||||||
"You need to select an access level for your user."
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -79,9 +77,7 @@
|
||||||
|
|
||||||
if (hasKey) {
|
if (hasKey) {
|
||||||
validationSchemas.shift()
|
validationSchemas.shift()
|
||||||
validationSchemas = validationSchemas
|
|
||||||
steps.shift()
|
steps.shift()
|
||||||
steps = steps
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handles form navigation
|
// Handles form navigation
|
||||||
|
@ -166,7 +162,7 @@
|
||||||
name: $createAppStore.values.username,
|
name: $createAppStore.values.username,
|
||||||
username: $createAppStore.values.username,
|
username: $createAppStore.values.username,
|
||||||
password: $createAppStore.values.password,
|
password: $createAppStore.values.password,
|
||||||
accessLevelId: $createAppStore.values.accessLevelId,
|
roleId: $createAppStore.values.roleId,
|
||||||
}
|
}
|
||||||
const userResp = await api.post(`/api/users`, user)
|
const userResp = await api.post(`/api/users`, user)
|
||||||
const json = await userResp.json()
|
const json = await userResp.json()
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
placeholder="Password"
|
placeholder="Password"
|
||||||
type="password"
|
type="password"
|
||||||
error={blurred.password && validationErrors.password} />
|
error={blurred.password && validationErrors.password} />
|
||||||
<Select label="Access Level" secondary name="accessLevelId">
|
<Select label="Role" secondary name="roleId">
|
||||||
<option value="ADMIN">Admin</option>
|
<option value="ADMIN">Admin</option>
|
||||||
<option value="POWER_USER">Power User</option>
|
<option value="POWER_USER">Power User</option>
|
||||||
</Select>
|
</Select>
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="root">
|
<div class="root">
|
||||||
{#if $store.currentFrontEndType === "layout" || $allScreens.length}
|
{#if $store.currentFrontEndType === 'layout' || $allScreens.length}
|
||||||
<div class="switcher">
|
<div class="switcher">
|
||||||
<button
|
<button
|
||||||
class:selected={selected === COMPONENT_SELECTION_TAB}
|
class:selected={selected === COMPONENT_SELECTION_TAB}
|
||||||
|
|
|
@ -55,7 +55,6 @@
|
||||||
{:else if tab === 'layouts'}
|
{:else if tab === 'layouts'}
|
||||||
<Layout />
|
<Layout />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
</Switcher>
|
</Switcher>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -67,8 +66,8 @@
|
||||||
<!-- <Modal bind:this={modal}> -->
|
<!-- <Modal bind:this={modal}> -->
|
||||||
<!-- <NewScreenModal /> -->
|
<!-- <NewScreenModal /> -->
|
||||||
<!-- </Modal> -->
|
<!-- </Modal> -->
|
||||||
<!-- {/if} -->
|
|
||||||
|
|
||||||
|
<!-- {/if} -->
|
||||||
<style>
|
<style>
|
||||||
.title {
|
.title {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -70,7 +70,7 @@
|
||||||
draftScreen.props._instanceName = name
|
draftScreen.props._instanceName = name
|
||||||
draftScreen.props._component = baseComponent
|
draftScreen.props._component = baseComponent
|
||||||
// TODO: need to fix this up correctly
|
// TODO: need to fix this up correctly
|
||||||
draftScreen.routing = { route, accessLevelId: "ADMIN" }
|
draftScreen.routing = { route, roleId: "ADMIN" }
|
||||||
|
|
||||||
await store.actions.screens.create(draftScreen)
|
await store.actions.screens.create(draftScreen)
|
||||||
if (createLink) {
|
if (createLink) {
|
||||||
|
|
|
@ -10,7 +10,7 @@ export const FrontendTypes = {
|
||||||
}
|
}
|
||||||
|
|
||||||
// fields on the user table that cannot be edited
|
// 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_LAYOUTS = {
|
export const DEFAULT_LAYOUTS = {
|
||||||
main: {
|
main: {
|
||||||
|
|
|
@ -52,9 +52,9 @@
|
||||||
{
|
{
|
||||||
"type": "node",
|
"type": "node",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"name": "Jest - Access Levels",
|
"name": "Jest - Roles",
|
||||||
"program": "${workspaceFolder}/node_modules/.bin/jest",
|
"program": "${workspaceFolder}/node_modules/.bin/jest",
|
||||||
"args": ["accesslevel.spec", "--runInBand"],
|
"args": ["role.spec", "--runInBand"],
|
||||||
"console": "integratedTerminal",
|
"console": "integratedTerminal",
|
||||||
"internalConsoleOptions": "neverOpen",
|
"internalConsoleOptions": "neverOpen",
|
||||||
"disableOptimisticBPs": true,
|
"disableOptimisticBPs": true,
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -20,9 +20,9 @@ const {
|
||||||
generateScreenID,
|
generateScreenID,
|
||||||
} = require("../../db/utils")
|
} = require("../../db/utils")
|
||||||
const {
|
const {
|
||||||
BUILTIN_LEVEL_IDS,
|
BUILTIN_ROLE_IDS,
|
||||||
AccessController,
|
AccessController,
|
||||||
} = require("../../utilities/security/accessLevels")
|
} = require("../../utilities/security/roles")
|
||||||
const {
|
const {
|
||||||
downloadExtractComponentLibraries,
|
downloadExtractComponentLibraries,
|
||||||
} = require("../../utilities/createAppPackage")
|
} = require("../../utilities/createAppPackage")
|
||||||
|
@ -56,10 +56,10 @@ async function getScreens(db) {
|
||||||
).rows.map(row => row.doc)
|
).rows.map(row => row.doc)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUserAccessLevelId(ctx) {
|
function getUserRoleId(ctx) {
|
||||||
return !ctx.user.accessLevel || !ctx.user.accessLevel._id
|
return !ctx.user.role || !ctx.user.role._id
|
||||||
? BUILTIN_LEVEL_IDS.PUBLIC
|
? BUILTIN_ROLE_IDS.PUBLIC
|
||||||
: ctx.user.accessLevel._id
|
: ctx.user.role._id
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createInstance(template) {
|
async function createInstance(template) {
|
||||||
|
@ -111,11 +111,11 @@ exports.fetch = async function(ctx) {
|
||||||
exports.fetchAppDefinition = async function(ctx) {
|
exports.fetchAppDefinition = async function(ctx) {
|
||||||
const db = new CouchDB(ctx.params.appId)
|
const db = new CouchDB(ctx.params.appId)
|
||||||
const layouts = await getLayouts(db)
|
const layouts = await getLayouts(db)
|
||||||
const userAccessLevelId = getUserAccessLevelId(ctx)
|
const userRoleId = getUserRoleId(ctx)
|
||||||
const accessController = new AccessController(ctx.params.appId)
|
const accessController = new AccessController(ctx.params.appId)
|
||||||
const screens = accessController.checkScreensAccess(
|
const screens = accessController.checkScreensAccess(
|
||||||
await getScreens(db),
|
await getScreens(db),
|
||||||
userAccessLevelId
|
userRoleId
|
||||||
)
|
)
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
layouts,
|
layouts,
|
||||||
|
|
|
@ -32,7 +32,7 @@ exports.authenticate = async ctx => {
|
||||||
if (await bcrypt.compare(password, dbUser.password)) {
|
if (await bcrypt.compare(password, dbUser.password)) {
|
||||||
const payload = {
|
const payload = {
|
||||||
userId: dbUser._id,
|
userId: dbUser._id,
|
||||||
accessLevelId: dbUser.accessLevelId,
|
roleId: dbUser.roleId,
|
||||||
version: app.version,
|
version: app.version,
|
||||||
permissions: dbUser.permissions || [],
|
permissions: dbUser.permissions || [],
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
const { getRoutingInfo } = require("../../utilities/routing")
|
const { getRoutingInfo } = require("../../utilities/routing")
|
||||||
const {
|
const {
|
||||||
getUserAccessLevelHierarchy,
|
getUserRoleHierarchy,
|
||||||
BUILTIN_LEVEL_IDS,
|
BUILTIN_ROLE_IDS,
|
||||||
} = require("../../utilities/security/accessLevels")
|
} = require("../../utilities/security/roles")
|
||||||
|
|
||||||
const URL_SEPARATOR = "/"
|
const URL_SEPARATOR = "/"
|
||||||
|
|
||||||
|
@ -33,15 +33,15 @@ Routing.prototype.getScreensProp = function(fullpath) {
|
||||||
return this.json[topLevel].subpaths[fullpath].screens
|
return this.json[topLevel].subpaths[fullpath].screens
|
||||||
}
|
}
|
||||||
|
|
||||||
Routing.prototype.addScreenId = function(fullpath, accessLevel, screenId) {
|
Routing.prototype.addScreenId = function(fullpath, roleId, screenId) {
|
||||||
this.getScreensProp(fullpath)[accessLevel] = screenId
|
this.getScreensProp(fullpath)[roleId] = screenId
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the full routing structure by querying the routing view and processing the result into the tree.
|
* 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.
|
* @param {string} appId The application to produce the routing structure for.
|
||||||
* @returns {Promise<object>} The routing structure, this is the full structure designed for use in the builder,
|
* @returns {Promise<object>} 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) {
|
async function getRoutingStructure(appId) {
|
||||||
const screenRoutes = await getRoutingInfo(appId)
|
const screenRoutes = await getRoutingInfo(appId)
|
||||||
|
@ -49,8 +49,8 @@ async function getRoutingStructure(appId) {
|
||||||
|
|
||||||
for (let screenRoute of screenRoutes) {
|
for (let screenRoute of screenRoutes) {
|
||||||
let fullpath = screenRoute.routing.route
|
let fullpath = screenRoute.routing.route
|
||||||
const accessLevel = screenRoute.routing.accessLevelId
|
const roleId = screenRoute.routing.roleId
|
||||||
routing.addScreenId(fullpath, accessLevel, screenRoute.id)
|
routing.addScreenId(fullpath, roleId, screenRoute.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
return { routes: routing.json }
|
return { routes: routing.json }
|
||||||
|
@ -62,29 +62,26 @@ exports.fetch = async ctx => {
|
||||||
|
|
||||||
exports.clientFetch = async ctx => {
|
exports.clientFetch = async ctx => {
|
||||||
const routing = await getRoutingStructure(ctx.appId)
|
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
|
// builder is a special case, always return the full routing structure
|
||||||
if (accessLevelId === BUILTIN_LEVEL_IDS.BUILDER) {
|
if (roleId === BUILTIN_ROLE_IDS.BUILDER) {
|
||||||
accessLevelId = BUILTIN_LEVEL_IDS.ADMIN
|
roleId = BUILTIN_ROLE_IDS.ADMIN
|
||||||
}
|
}
|
||||||
const accessLevelIds = await getUserAccessLevelHierarchy(
|
const roleIds = await getUserRoleHierarchy(ctx.appId, roleId)
|
||||||
ctx.appId,
|
|
||||||
accessLevelId
|
|
||||||
)
|
|
||||||
for (let topLevel of Object.values(routing.routes)) {
|
for (let topLevel of Object.values(routing.routes)) {
|
||||||
for (let subpathKey of Object.keys(topLevel.subpaths)) {
|
for (let subpathKey of Object.keys(topLevel.subpaths)) {
|
||||||
let found = false
|
let found = false
|
||||||
const subpath = topLevel.subpaths[subpathKey]
|
const subpath = topLevel.subpaths[subpathKey]
|
||||||
const accessLevelOptions = Object.keys(subpath.screens)
|
const roleOptions = Object.keys(subpath.screens)
|
||||||
if (accessLevelOptions.length === 1 && !accessLevelOptions[0]) {
|
if (roleOptions.length === 1 && !roleOptions[0]) {
|
||||||
subpath.screenId = subpath.screens[accessLevelOptions[0]]
|
subpath.screenId = subpath.screens[roleOptions[0]]
|
||||||
subpath.accessLevelId = BUILTIN_LEVEL_IDS.BASIC
|
subpath.roleId = BUILTIN_ROLE_IDS.BASIC
|
||||||
found = true
|
found = true
|
||||||
} else {
|
} else {
|
||||||
for (let levelId of accessLevelIds) {
|
for (let roleId of roleIds) {
|
||||||
if (accessLevelOptions.indexOf(levelId) !== -1) {
|
if (roleOptions.indexOf(roleId) !== -1) {
|
||||||
subpath.screenId = subpath.screens[levelId]
|
subpath.screenId = subpath.screens[roleId]
|
||||||
subpath.accessLevelId = levelId
|
subpath.roleId = roleId
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const CouchDB = require("../../db")
|
const CouchDB = require("../../db")
|
||||||
const { getScreenParams, generateScreenID } = require("../../db/utils")
|
const { getScreenParams, generateScreenID } = require("../../db/utils")
|
||||||
const { AccessController } = require("../../utilities/security/accessLevels")
|
const { AccessController } = require("../../utilities/security/roles")
|
||||||
|
|
||||||
exports.fetch = async ctx => {
|
exports.fetch = async ctx => {
|
||||||
const appId = ctx.user.appId
|
const appId = ctx.user.appId
|
||||||
|
@ -16,7 +16,7 @@ exports.fetch = async ctx => {
|
||||||
|
|
||||||
ctx.body = await new AccessController(appId).checkScreensAccess(
|
ctx.body = await new AccessController(appId).checkScreensAccess(
|
||||||
screens,
|
screens,
|
||||||
ctx.user.accessLevel._id
|
ctx.user.role._id
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
const CouchDB = require("../../db")
|
const CouchDB = require("../../db")
|
||||||
const bcrypt = require("../../utilities/bcrypt")
|
const bcrypt = require("../../utilities/bcrypt")
|
||||||
const { generateUserID, getUserParams, ViewNames } = require("../../db/utils")
|
const { generateUserID, getUserParams, ViewNames } = require("../../db/utils")
|
||||||
const {
|
const { BUILTIN_ROLE_ID_ARRAY } = require("../../utilities/security/roles")
|
||||||
BUILTIN_LEVEL_ID_ARRAY,
|
|
||||||
} = require("../../utilities/security/accessLevels")
|
|
||||||
const {
|
const {
|
||||||
BUILTIN_PERMISSION_NAMES,
|
BUILTIN_PERMISSION_NAMES,
|
||||||
} = require("../../utilities/security/permissions")
|
} = require("../../utilities/security/permissions")
|
||||||
|
@ -20,21 +18,15 @@ exports.fetch = async function(ctx) {
|
||||||
|
|
||||||
exports.create = async function(ctx) {
|
exports.create = async function(ctx) {
|
||||||
const db = new CouchDB(ctx.user.appId)
|
const db = new CouchDB(ctx.user.appId)
|
||||||
const {
|
const { username, password, name, roleId, permissions } = ctx.request.body
|
||||||
username,
|
|
||||||
password,
|
|
||||||
name,
|
|
||||||
accessLevelId,
|
|
||||||
permissions,
|
|
||||||
} = ctx.request.body
|
|
||||||
|
|
||||||
if (!username || !password) {
|
if (!username || !password) {
|
||||||
ctx.throw(400, "Username and Password Required.")
|
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 = {
|
const user = {
|
||||||
_id: generateUserID(username),
|
_id: generateUserID(username),
|
||||||
|
@ -42,7 +34,7 @@ exports.create = async function(ctx) {
|
||||||
password: await bcrypt.hash(password),
|
password: await bcrypt.hash(password),
|
||||||
name: name || username,
|
name: name || username,
|
||||||
type: "user",
|
type: "user",
|
||||||
accessLevelId,
|
roleId,
|
||||||
permissions: permissions || [BUILTIN_PERMISSION_NAMES.POWER],
|
permissions: permissions || [BUILTIN_PERMISSION_NAMES.POWER],
|
||||||
tableId: ViewNames.USERS,
|
tableId: ViewNames.USERS,
|
||||||
}
|
}
|
||||||
|
@ -97,14 +89,14 @@ exports.find = async function(ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkAccessLevel = async (db, accessLevelId) => {
|
const checkRole = async (db, roleId) => {
|
||||||
if (!accessLevelId) return
|
if (!roleId) return
|
||||||
if (BUILTIN_LEVEL_ID_ARRAY.indexOf(accessLevelId) !== -1) {
|
if (BUILTIN_ROLE_ID_ARRAY.indexOf(roleId) !== -1) {
|
||||||
return {
|
return {
|
||||||
_id: accessLevelId,
|
_id: roleId,
|
||||||
name: accessLevelId,
|
name: roleId,
|
||||||
permissions: [],
|
permissions: [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return await db.get(accessLevelId)
|
return await db.get(roleId)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
|
@ -10,7 +10,7 @@ const staticRoutes = require("./static")
|
||||||
const componentRoutes = require("./component")
|
const componentRoutes = require("./component")
|
||||||
const automationRoutes = require("./automation")
|
const automationRoutes = require("./automation")
|
||||||
const webhookRoutes = require("./webhook")
|
const webhookRoutes = require("./webhook")
|
||||||
const accesslevelRoutes = require("./accesslevel")
|
const roleRoutes = require("./role")
|
||||||
const deployRoutes = require("./deploy")
|
const deployRoutes = require("./deploy")
|
||||||
const apiKeysRoutes = require("./apikeys")
|
const apiKeysRoutes = require("./apikeys")
|
||||||
const templatesRoutes = require("./templates")
|
const templatesRoutes = require("./templates")
|
||||||
|
@ -26,7 +26,7 @@ exports.mainRoutes = [
|
||||||
automationRoutes,
|
automationRoutes,
|
||||||
viewRoutes,
|
viewRoutes,
|
||||||
componentRoutes,
|
componentRoutes,
|
||||||
accesslevelRoutes,
|
roleRoutes,
|
||||||
apiKeysRoutes,
|
apiKeysRoutes,
|
||||||
templatesRoutes,
|
templatesRoutes,
|
||||||
analyticsRoutes,
|
analyticsRoutes,
|
||||||
|
|
|
@ -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
|
|
@ -5,9 +5,10 @@ const controller = require("../controllers/routing")
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
// gets the full structure, not just the correct screen ID for your access level
|
|
||||||
router
|
router
|
||||||
|
// gets correct structure for user role
|
||||||
.get("/api/routing/client", controller.clientFetch)
|
.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)
|
.get("/api/routing", authorized(BUILDER), controller.fetch)
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
||||||
|
|
|
@ -14,7 +14,7 @@ function generateSaveValidation() {
|
||||||
name: Joi.string().required(),
|
name: Joi.string().required(),
|
||||||
routing: Joi.object({
|
routing: Joi.object({
|
||||||
route: Joi.string().required(),
|
route: Joi.string().required(),
|
||||||
accessLevelId: Joi.string().required().allow(""),
|
roleId: Joi.string().required().allow(""),
|
||||||
}).required().unknown(true),
|
}).required().unknown(true),
|
||||||
props: Joi.object({
|
props: Joi.object({
|
||||||
_id: Joi.string().required(),
|
_id: Joi.string().required(),
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
const CouchDB = require("../../../db")
|
const CouchDB = require("../../../db")
|
||||||
const supertest = require("supertest")
|
const supertest = require("supertest")
|
||||||
const {
|
const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles")
|
||||||
BUILTIN_LEVEL_IDS,
|
|
||||||
} = require("../../../utilities/security/accessLevels")
|
|
||||||
const {
|
const {
|
||||||
BUILTIN_PERMISSION_NAMES,
|
BUILTIN_PERMISSION_NAMES,
|
||||||
} = require("../../../utilities/security/permissions")
|
} = require("../../../utilities/security/permissions")
|
||||||
|
@ -26,7 +24,7 @@ exports.supertest = async () => {
|
||||||
exports.defaultHeaders = appId => {
|
exports.defaultHeaders = appId => {
|
||||||
const builderUser = {
|
const builderUser = {
|
||||||
userId: "BUILDER",
|
userId: "BUILDER",
|
||||||
accessLevelId: BUILTIN_LEVEL_IDS.BUILDER,
|
roleId: BUILTIN_ROLE_IDS.BUILDER,
|
||||||
}
|
}
|
||||||
|
|
||||||
const builderToken = jwt.sign(builderUser, env.JWT_SECRET)
|
const builderToken = jwt.sign(builderUser, env.JWT_SECRET)
|
||||||
|
@ -128,7 +126,7 @@ exports.createUser = async (
|
||||||
name: "Bill",
|
name: "Bill",
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
accessLevelId: BUILTIN_LEVEL_IDS.POWER,
|
roleId: BUILTIN_ROLE_IDS.POWER,
|
||||||
})
|
})
|
||||||
return res.body
|
return res.body
|
||||||
}
|
}
|
||||||
|
@ -184,13 +182,13 @@ const createUserWithPermissions = async (
|
||||||
name: username,
|
name: username,
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
accessLevelId: BUILTIN_LEVEL_IDS.POWER,
|
roleId: BUILTIN_ROLE_IDS.POWER,
|
||||||
permissions,
|
permissions,
|
||||||
})
|
})
|
||||||
|
|
||||||
const anonUser = {
|
const anonUser = {
|
||||||
userId: "ANON",
|
userId: "ANON",
|
||||||
accessLevelId: BUILTIN_LEVEL_IDS.PUBLIC,
|
roleId: BUILTIN_ROLE_IDS.PUBLIC,
|
||||||
appId: appId,
|
appId: appId,
|
||||||
version: packageJson.version,
|
version: packageJson.version,
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,12 @@ const {
|
||||||
defaultHeaders
|
defaultHeaders
|
||||||
} = require("./couchTestUtils")
|
} = require("./couchTestUtils")
|
||||||
const {
|
const {
|
||||||
BUILTIN_LEVEL_IDS,
|
BUILTIN_ROLE_IDS,
|
||||||
} = require("../../../utilities/security/accessLevels")
|
} = 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 server
|
||||||
let request
|
let request
|
||||||
let appId
|
let appId
|
||||||
|
@ -35,15 +35,15 @@ describe("/accesslevels", () => {
|
||||||
|
|
||||||
describe("create", () => {
|
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
|
const res = await request
|
||||||
.post(`/api/accesslevels`)
|
.post(`/api/roles`)
|
||||||
.send(accessLevelBody)
|
.send(roleBody)
|
||||||
.set(defaultHeaders(appId))
|
.set(defaultHeaders(appId))
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.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._id).toBeDefined()
|
||||||
expect(res.body._rev).toBeDefined()
|
expect(res.body._rev).toBeDefined()
|
||||||
})
|
})
|
||||||
|
@ -52,57 +52,57 @@ describe("/accesslevels", () => {
|
||||||
|
|
||||||
describe("fetch", () => {
|
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
|
const createRes = await request
|
||||||
.post(`/api/accesslevels`)
|
.post(`/api/roles`)
|
||||||
.send(accessLevelBody)
|
.send(roleBody)
|
||||||
.set(defaultHeaders(appId))
|
.set(defaultHeaders(appId))
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
const customLevel = createRes.body
|
const customRole = createRes.body
|
||||||
|
|
||||||
const res = await request
|
const res = await request
|
||||||
.get(`/api/accesslevels`)
|
.get(`/api/roles`)
|
||||||
.set(defaultHeaders(appId))
|
.set(defaultHeaders(appId))
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.body.length).toBe(3)
|
expect(res.body.length).toBe(3)
|
||||||
|
|
||||||
const adminLevel = res.body.find(r => r._id === BUILTIN_LEVEL_IDS.ADMIN)
|
const adminRole = res.body.find(r => r._id === BUILTIN_ROLE_IDS.ADMIN)
|
||||||
expect(adminLevel.inherits).toEqual(BUILTIN_LEVEL_IDS.POWER)
|
expect(adminRole.inherits).toEqual(BUILTIN_ROLE_IDS.POWER)
|
||||||
expect(adminLevel).toBeDefined()
|
expect(adminRole).toBeDefined()
|
||||||
|
|
||||||
const powerUserLevel = res.body.find(r => r._id === BUILTIN_LEVEL_IDS.POWER)
|
const powerUserRole = res.body.find(r => r._id === BUILTIN_ROLE_IDS.POWER)
|
||||||
expect(powerUserLevel.inherits).toEqual(BUILTIN_LEVEL_IDS.BASIC)
|
expect(powerUserRole.inherits).toEqual(BUILTIN_ROLE_IDS.BASIC)
|
||||||
expect(powerUserLevel).toBeDefined()
|
expect(powerUserRole).toBeDefined()
|
||||||
|
|
||||||
const customLevelFetched = res.body.find(r => r._id === customLevel._id)
|
const customRoleFetched = res.body.find(r => r._id === customRole._id)
|
||||||
expect(customLevelFetched.inherits).toEqual(BUILTIN_LEVEL_IDS.BASIC)
|
expect(customRoleFetched.inherits).toEqual(BUILTIN_ROLE_IDS.BASIC)
|
||||||
expect(customLevelFetched).toBeDefined()
|
expect(customRoleFetched).toBeDefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("destroy", () => {
|
describe("destroy", () => {
|
||||||
it("should delete custom access level", async () => {
|
it("should delete custom roles", async () => {
|
||||||
const createRes = await request
|
const createRes = await request
|
||||||
.post(`/api/accesslevels`)
|
.post(`/api/roles`)
|
||||||
.send({ name: "user" })
|
.send({ name: "user" })
|
||||||
.set(defaultHeaders(appId))
|
.set(defaultHeaders(appId))
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
const customLevel = createRes.body
|
const customRole = createRes.body
|
||||||
|
|
||||||
await request
|
await request
|
||||||
.delete(`/api/accesslevels/${customLevel._id}/${customLevel._rev}`)
|
.delete(`/api/roles/${customRole._id}/${customRole._rev}`)
|
||||||
.set(defaultHeaders(appId))
|
.set(defaultHeaders(appId))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
await request
|
await request
|
||||||
.get(`/api/accesslevels/${customLevel._id}`)
|
.get(`/api/roles/${customRole._id}`)
|
||||||
.set(defaultHeaders(appId))
|
.set(defaultHeaders(appId))
|
||||||
.expect(404)
|
.expect(404)
|
||||||
})
|
})
|
|
@ -9,8 +9,8 @@ const {
|
||||||
BUILTIN_PERMISSION_NAMES,
|
BUILTIN_PERMISSION_NAMES,
|
||||||
} = require("../../../utilities/security/permissions")
|
} = require("../../../utilities/security/permissions")
|
||||||
const {
|
const {
|
||||||
BUILTIN_LEVEL_IDS,
|
BUILTIN_ROLE_IDS,
|
||||||
} = require("../../../utilities/security/accessLevels")
|
} = require("../../../utilities/security/roles")
|
||||||
|
|
||||||
describe("/users", () => {
|
describe("/users", () => {
|
||||||
let request
|
let request
|
||||||
|
@ -67,7 +67,7 @@ describe("/users", () => {
|
||||||
const res = await request
|
const res = await request
|
||||||
.post(`/api/users`)
|
.post(`/api/users`)
|
||||||
.set(defaultHeaders(appId))
|
.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(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ describe("/users", () => {
|
||||||
await testPermissionsForEndpoint({
|
await testPermissionsForEndpoint({
|
||||||
request,
|
request,
|
||||||
method: "POST",
|
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`,
|
url: `/api/users`,
|
||||||
appId: appId,
|
appId: appId,
|
||||||
permName1: BUILTIN_PERMISSION_NAMES.ADMIN,
|
permName1: BUILTIN_PERMISSION_NAMES.ADMIN,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const accessLevels = require("../../utilities/security/accessLevels")
|
const roles = require("../../utilities/security/roles")
|
||||||
const userController = require("../../api/controllers/user")
|
const userController = require("../../api/controllers/user")
|
||||||
const env = require("../../environment")
|
const env = require("../../environment")
|
||||||
const usage = require("../../utilities/usageQuota")
|
const usage = require("../../utilities/usageQuota")
|
||||||
|
@ -11,7 +11,7 @@ module.exports.definition = {
|
||||||
type: "ACTION",
|
type: "ACTION",
|
||||||
stepId: "CREATE_USER",
|
stepId: "CREATE_USER",
|
||||||
inputs: {
|
inputs: {
|
||||||
accessLevelId: accessLevels.BUILTIN_LEVEL_IDS.POWER,
|
roleId: roles.BUILTIN_ROLE_IDS.POWER,
|
||||||
},
|
},
|
||||||
schema: {
|
schema: {
|
||||||
inputs: {
|
inputs: {
|
||||||
|
@ -25,14 +25,14 @@ module.exports.definition = {
|
||||||
customType: "password",
|
customType: "password",
|
||||||
title: "Password",
|
title: "Password",
|
||||||
},
|
},
|
||||||
accessLevelId: {
|
roleId: {
|
||||||
type: "string",
|
type: "string",
|
||||||
title: "Access Level",
|
title: "Role",
|
||||||
enum: accessLevels.BUILTIN_LEVEL_ID_ARRAY,
|
enum: roles.BUILTIN_ROLE_ID_ARRAY,
|
||||||
pretty: accessLevels.BUILTIN_LEVEL_NAME_ARRAY,
|
pretty: roles.BUILTIN_ROLE_NAME_ARRAY,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
required: ["username", "password", "accessLevelId"],
|
required: ["username", "password", "roleId"],
|
||||||
},
|
},
|
||||||
outputs: {
|
outputs: {
|
||||||
properties: {
|
properties: {
|
||||||
|
@ -59,13 +59,13 @@ module.exports.definition = {
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.run = async function({ inputs, appId, apiKey }) {
|
module.exports.run = async function({ inputs, appId, apiKey }) {
|
||||||
const { username, password, accessLevelId } = inputs
|
const { username, password, roleId } = inputs
|
||||||
const ctx = {
|
const ctx = {
|
||||||
user: {
|
user: {
|
||||||
appId: appId,
|
appId: appId,
|
||||||
},
|
},
|
||||||
request: {
|
request: {
|
||||||
body: { username, password, accessLevelId },
|
body: { username, password, roleId },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const { BUILTIN_LEVEL_IDS } = require("../utilities/security/accessLevels")
|
const { BUILTIN_ROLE_IDS } = require("../utilities/security/roles")
|
||||||
|
|
||||||
const AuthTypes = {
|
const AuthTypes = {
|
||||||
APP: "app",
|
APP: "app",
|
||||||
|
@ -24,14 +24,14 @@ const USERS_TABLE_SCHEMA = {
|
||||||
fieldName: "username",
|
fieldName: "username",
|
||||||
name: "username",
|
name: "username",
|
||||||
},
|
},
|
||||||
accessLevelId: {
|
roleId: {
|
||||||
fieldName: "accessLevelId",
|
fieldName: "roleId",
|
||||||
name: "accessLevelId",
|
name: "roleId",
|
||||||
type: "options",
|
type: "options",
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "string",
|
type: "string",
|
||||||
presence: false,
|
presence: false,
|
||||||
inclusion: Object.keys(BUILTIN_LEVEL_IDS),
|
inclusion: Object.keys(BUILTIN_ROLE_IDS),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const { BUILTIN_LEVEL_IDS } = require("../utilities/security/accessLevels")
|
const { BUILTIN_ROLE_IDS } = require("../utilities/security/roles")
|
||||||
|
|
||||||
exports.HOME_SCREEN = {
|
exports.HOME_SCREEN = {
|
||||||
description: "",
|
description: "",
|
||||||
|
@ -97,7 +97,7 @@ exports.HOME_SCREEN = {
|
||||||
},
|
},
|
||||||
routing: {
|
routing: {
|
||||||
route: "/",
|
route: "/",
|
||||||
accessLevelId: BUILTIN_LEVEL_IDS.BASIC,
|
roleId: BUILTIN_ROLE_IDS.BASIC,
|
||||||
},
|
},
|
||||||
name: "home-screen",
|
name: "home-screen",
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ const DocumentTypes = {
|
||||||
AUTOMATION: "au",
|
AUTOMATION: "au",
|
||||||
LINK: "li",
|
LINK: "li",
|
||||||
APP: "app",
|
APP: "app",
|
||||||
ACCESS_LEVEL: "ac",
|
ROLE: "role",
|
||||||
WEBHOOK: "wh",
|
WEBHOOK: "wh",
|
||||||
INSTANCE: "inst",
|
INSTANCE: "inst",
|
||||||
LAYOUT: "layout",
|
LAYOUT: "layout",
|
||||||
|
@ -169,18 +169,18 @@ exports.getAppParams = (appId = null, otherProps = {}) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a new access level ID.
|
* Generates a new role ID.
|
||||||
* @returns {string} The new access level ID which the access level doc can be stored under.
|
* @returns {string} The new role ID which the role doc can be stored under.
|
||||||
*/
|
*/
|
||||||
exports.generateAccessLevelID = () => {
|
exports.generateRoleID = () => {
|
||||||
return `${DocumentTypes.ACCESS_LEVEL}${SEPARATOR}${newid()}`
|
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 = {}) => {
|
exports.getRoleParams = (roleId = null, otherProps = {}) => {
|
||||||
return getDocParams(DocumentTypes.ACCESS_LEVEL, accessLevelId, otherProps)
|
return getDocParams(DocumentTypes.ROLE, roleId, otherProps)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
class Integration {
|
class Integration {
|
||||||
constructor() {
|
constructor() {}
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,9 +1,6 @@
|
||||||
const jwt = require("jsonwebtoken")
|
const jwt = require("jsonwebtoken")
|
||||||
const STATUS_CODES = require("../utilities/statusCodes")
|
const STATUS_CODES = require("../utilities/statusCodes")
|
||||||
const {
|
const { getRole, BUILTIN_ROLES } = require("../utilities/security/roles")
|
||||||
getAccessLevel,
|
|
||||||
BUILTIN_LEVELS,
|
|
||||||
} = require("../utilities/security/accessLevels")
|
|
||||||
const { AuthTypes } = require("../constants")
|
const { AuthTypes } = require("../constants")
|
||||||
const { getAppId, getCookieName, setCookie, isClient } = require("../utilities")
|
const { getAppId, getCookieName, setCookie, isClient } = require("../utilities")
|
||||||
|
|
||||||
|
@ -35,7 +32,7 @@ module.exports = async (ctx, next) => {
|
||||||
ctx.appId = appId
|
ctx.appId = appId
|
||||||
ctx.user = {
|
ctx.user = {
|
||||||
appId,
|
appId,
|
||||||
accessLevel: BUILTIN_LEVELS.PUBLIC,
|
role: BUILTIN_ROLES.PUBLIC,
|
||||||
}
|
}
|
||||||
await next()
|
await next()
|
||||||
return
|
return
|
||||||
|
@ -49,7 +46,7 @@ module.exports = async (ctx, next) => {
|
||||||
ctx.user = {
|
ctx.user = {
|
||||||
...jwtPayload,
|
...jwtPayload,
|
||||||
appId: appId,
|
appId: appId,
|
||||||
accessLevel: await getAccessLevel(appId, jwtPayload.accessLevelId),
|
role: await getRole(appId, jwtPayload.roleId),
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
ctx.throw(err.status || STATUS_CODES.FORBIDDEN, err.text)
|
ctx.throw(err.status || STATUS_CODES.FORBIDDEN, err.text)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const { BUILTIN_LEVEL_IDS } = require("../utilities/security/accessLevels")
|
const { BUILTIN_ROLE_IDS } = require("../utilities/security/roles")
|
||||||
const {
|
const {
|
||||||
PermissionTypes,
|
PermissionTypes,
|
||||||
doesHavePermission,
|
doesHavePermission,
|
||||||
|
@ -7,7 +7,7 @@ const env = require("../environment")
|
||||||
const { apiKeyTable } = require("../db/dynamoClient")
|
const { apiKeyTable } = require("../db/dynamoClient")
|
||||||
const { AuthTypes } = require("../constants")
|
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("|"))
|
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")
|
ctx.throw(403, "User not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
const accessLevel = ctx.user.accessLevel
|
const role = ctx.user.role
|
||||||
const permissions = ctx.user.permissions
|
const permissions = ctx.user.permissions
|
||||||
if (ADMIN_ACCESS.indexOf(accessLevel._id) !== -1) {
|
if (ADMIN_ROLES.indexOf(role._id) !== -1) {
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 { BUILTIN_PERMISSION_NAMES } = require("../security/permissions")
|
||||||
const env = require("../../environment")
|
const env = require("../../environment")
|
||||||
const CouchDB = require("../../db")
|
const CouchDB = require("../../db")
|
||||||
|
@ -10,7 +10,7 @@ const APP_PREFIX = DocumentTypes.APP + SEPARATOR
|
||||||
module.exports = async (ctx, appId, version) => {
|
module.exports = async (ctx, appId, version) => {
|
||||||
const builderUser = {
|
const builderUser = {
|
||||||
userId: "BUILDER",
|
userId: "BUILDER",
|
||||||
accessLevelId: BUILTIN_LEVEL_IDS.BUILDER,
|
roleId: BUILTIN_ROLE_IDS.BUILDER,
|
||||||
permissions: [BUILTIN_PERMISSION_NAMES.ADMIN],
|
permissions: [BUILTIN_PERMISSION_NAMES.ADMIN],
|
||||||
version,
|
version,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<AccessLevel|object|null>} 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<string[]>} 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
|
|
|
@ -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<Role|object|null>} 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<string[]>} 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
|
Loading…
Reference in New Issue