2021-02-06 04:58:25 +13:00
|
|
|
const {
|
2021-02-13 09:34:54 +13:00
|
|
|
getBuiltinPermissions,
|
2021-02-06 04:58:25 +13:00
|
|
|
PermissionLevels,
|
2021-02-12 02:29:15 +13:00
|
|
|
isPermissionLevelHigherThanRead,
|
2021-02-13 01:02:07 +13:00
|
|
|
higherPermission,
|
2021-02-06 04:58:25 +13:00
|
|
|
} = require("../../utilities/security/permissions")
|
2021-02-10 02:01:45 +13:00
|
|
|
const {
|
|
|
|
isBuiltin,
|
|
|
|
getDBRoleID,
|
|
|
|
getExternalRoleID,
|
2021-02-13 09:34:54 +13:00
|
|
|
getBuiltinRoles,
|
2021-02-10 02:01:45 +13:00
|
|
|
} = require("../../utilities/security/roles")
|
2021-02-13 01:02:07 +13:00
|
|
|
const { getRoleParams } = require("../../db/utils")
|
2021-02-06 07:46:15 +13:00
|
|
|
const CouchDB = require("../../db")
|
2021-02-13 01:02:07 +13:00
|
|
|
const {
|
|
|
|
CURRENTLY_SUPPORTED_LEVELS,
|
|
|
|
getBasePermissions,
|
|
|
|
} = require("../../utilities/security/utilities")
|
2020-12-03 06:08:25 +13:00
|
|
|
|
2021-02-09 06:22:07 +13:00
|
|
|
const PermissionUpdateType = {
|
|
|
|
REMOVE: "remove",
|
|
|
|
ADD: "add",
|
|
|
|
}
|
|
|
|
|
2021-02-13 01:02:07 +13:00
|
|
|
const SUPPORTED_LEVELS = CURRENTLY_SUPPORTED_LEVELS
|
2021-02-12 02:29:15 +13:00
|
|
|
|
2021-02-13 01:02:07 +13:00
|
|
|
// quick function to perform a bit of weird logic, make sure fetch calls
|
|
|
|
// always say a write role also has read permission
|
|
|
|
function fetchLevelPerms(permissions, level, roleId) {
|
|
|
|
if (!permissions) {
|
|
|
|
permissions = {}
|
2021-02-11 23:24:37 +13:00
|
|
|
}
|
2021-02-13 01:02:07 +13:00
|
|
|
permissions[level] = roleId
|
|
|
|
if (
|
|
|
|
isPermissionLevelHigherThanRead(level) &&
|
|
|
|
!permissions[PermissionLevels.READ]
|
|
|
|
) {
|
|
|
|
permissions[PermissionLevels.READ] = roleId
|
2021-02-11 23:24:37 +13:00
|
|
|
}
|
2021-02-12 02:29:15 +13:00
|
|
|
return permissions
|
2021-02-11 23:24:37 +13:00
|
|
|
}
|
|
|
|
|
2021-02-10 05:01:02 +13:00
|
|
|
// utility function to stop this repetition - permissions always stored under roles
|
|
|
|
async function getAllDBRoles(db) {
|
|
|
|
const body = await db.allDocs(
|
|
|
|
getRoleParams(null, {
|
|
|
|
include_docs: true,
|
|
|
|
})
|
|
|
|
)
|
2021-05-04 22:32:22 +12:00
|
|
|
return body.rows.map(row => row.doc)
|
2021-02-10 05:01:02 +13:00
|
|
|
}
|
|
|
|
|
2021-02-06 07:46:15 +13:00
|
|
|
async function updatePermissionOnRole(
|
|
|
|
appId,
|
2021-02-09 06:22:07 +13:00
|
|
|
{ roleId, resourceId, level },
|
|
|
|
updateType
|
2021-02-06 07:46:15 +13:00
|
|
|
) {
|
|
|
|
const db = new CouchDB(appId)
|
2021-02-09 06:22:07 +13:00
|
|
|
const remove = updateType === PermissionUpdateType.REMOVE
|
2021-02-10 02:01:45 +13:00
|
|
|
const isABuiltin = isBuiltin(roleId)
|
|
|
|
const dbRoleId = getDBRoleID(roleId)
|
2021-02-10 05:01:02 +13:00
|
|
|
const dbRoles = await getAllDBRoles(db)
|
2021-02-06 07:46:15 +13:00
|
|
|
const docUpdates = []
|
2021-02-06 04:58:25 +13:00
|
|
|
|
2021-02-10 02:01:45 +13:00
|
|
|
// the permission is for a built in, make sure it exists
|
2021-05-04 22:32:22 +12:00
|
|
|
if (isABuiltin && !dbRoles.some(role => role._id === dbRoleId)) {
|
2021-02-13 09:34:54 +13:00
|
|
|
const builtin = getBuiltinRoles()[roleId]
|
2021-02-10 02:01:45 +13:00
|
|
|
builtin._id = getDBRoleID(builtin._id)
|
|
|
|
dbRoles.push(builtin)
|
|
|
|
}
|
2021-02-09 07:30:30 +13:00
|
|
|
|
2021-02-06 07:46:15 +13:00
|
|
|
// now try to find any roles which need updated, e.g. removing the
|
|
|
|
// resource from another role and then adding to the new role
|
|
|
|
for (let role of dbRoles) {
|
2021-02-09 06:22:07 +13:00
|
|
|
let updated = false
|
|
|
|
const rolePermissions = role.permissions ? role.permissions : {}
|
|
|
|
// handle the removal/updating the role which has this permission first
|
2021-02-10 02:01:45 +13:00
|
|
|
// the updating (role._id !== dbRoleId) is required because a resource/level can
|
2021-02-09 06:22:07 +13:00
|
|
|
// only be permitted in a single role (this reduces hierarchy confusion and simplifies
|
|
|
|
// the general UI for this, rather than needing to show everywhere it is used)
|
|
|
|
if (
|
2021-02-10 02:01:45 +13:00
|
|
|
(role._id !== dbRoleId || remove) &&
|
2021-02-09 06:22:07 +13:00
|
|
|
rolePermissions[resourceId] === level
|
|
|
|
) {
|
|
|
|
delete rolePermissions[resourceId]
|
|
|
|
updated = true
|
|
|
|
}
|
|
|
|
// handle the adding, we're on the correct role, at it to this
|
2021-02-10 02:01:45 +13:00
|
|
|
if (!remove && role._id === dbRoleId) {
|
2021-02-13 01:02:07 +13:00
|
|
|
rolePermissions[resourceId] = higherPermission(
|
|
|
|
rolePermissions[resourceId],
|
|
|
|
level
|
|
|
|
)
|
2021-02-09 06:22:07 +13:00
|
|
|
updated = true
|
|
|
|
}
|
|
|
|
// handle the update, add it to bulk docs to perform at end
|
|
|
|
if (updated) {
|
|
|
|
role.permissions = rolePermissions
|
|
|
|
docUpdates.push(role)
|
2021-02-06 07:46:15 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-10 02:01:45 +13:00
|
|
|
const response = await db.bulkDocs(docUpdates)
|
2021-05-04 22:32:22 +12:00
|
|
|
return response.map(resp => {
|
2021-02-10 02:14:23 +13:00
|
|
|
resp._id = getExternalRoleID(resp.id)
|
|
|
|
delete resp.id
|
|
|
|
return resp
|
|
|
|
})
|
2021-02-06 04:58:25 +13:00
|
|
|
}
|
|
|
|
|
2021-05-03 19:31:09 +12:00
|
|
|
exports.fetchBuiltin = function (ctx) {
|
2021-02-13 09:34:54 +13:00
|
|
|
ctx.body = Object.values(getBuiltinPermissions())
|
2020-12-03 06:08:25 +13:00
|
|
|
}
|
2021-02-06 04:58:25 +13:00
|
|
|
|
2021-05-03 19:31:09 +12:00
|
|
|
exports.fetchLevels = function (ctx) {
|
2021-02-09 06:22:07 +13:00
|
|
|
// for now only provide the read/write perms externally
|
2021-02-12 02:29:15 +13:00
|
|
|
ctx.body = SUPPORTED_LEVELS
|
2021-02-09 06:22:07 +13:00
|
|
|
}
|
|
|
|
|
2021-05-03 19:31:09 +12:00
|
|
|
exports.fetch = async function (ctx) {
|
2021-02-10 05:01:02 +13:00
|
|
|
const db = new CouchDB(ctx.appId)
|
|
|
|
const roles = await getAllDBRoles(db)
|
|
|
|
let permissions = {}
|
|
|
|
// create an object with structure role ID -> resource ID -> level
|
|
|
|
for (let role of roles) {
|
2021-02-12 07:13:09 +13:00
|
|
|
if (!role.permissions) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
const roleId = getExternalRoleID(role._id)
|
|
|
|
for (let [resource, level] of Object.entries(role.permissions)) {
|
2021-02-13 01:02:07 +13:00
|
|
|
permissions[resource] = fetchLevelPerms(
|
|
|
|
permissions[resource],
|
|
|
|
level,
|
|
|
|
roleId
|
|
|
|
)
|
2021-02-10 05:01:02 +13:00
|
|
|
}
|
|
|
|
}
|
2021-02-13 01:02:07 +13:00
|
|
|
// apply the base permissions
|
|
|
|
const finalPermissions = {}
|
|
|
|
for (let [resource, permission] of Object.entries(permissions)) {
|
|
|
|
const basePerms = getBasePermissions(resource)
|
|
|
|
finalPermissions[resource] = Object.assign(basePerms, permission)
|
|
|
|
}
|
|
|
|
ctx.body = finalPermissions
|
2021-02-10 05:01:02 +13:00
|
|
|
}
|
|
|
|
|
2021-05-03 19:31:09 +12:00
|
|
|
exports.getResourcePerms = async function (ctx) {
|
2021-02-09 06:22:07 +13:00
|
|
|
const resourceId = ctx.params.resourceId
|
|
|
|
const db = new CouchDB(ctx.appId)
|
|
|
|
const body = await db.allDocs(
|
|
|
|
getRoleParams(null, {
|
|
|
|
include_docs: true,
|
|
|
|
})
|
|
|
|
)
|
2021-05-04 22:32:22 +12:00
|
|
|
const roles = body.rows.map(row => row.doc)
|
2021-02-13 01:02:07 +13:00
|
|
|
let permissions = {}
|
2021-02-12 02:29:15 +13:00
|
|
|
for (let level of SUPPORTED_LEVELS) {
|
2021-02-09 06:22:07 +13:00
|
|
|
// update the various roleIds in the resource permissions
|
2021-02-12 02:38:07 +13:00
|
|
|
for (let role of roles) {
|
2021-02-12 07:13:09 +13:00
|
|
|
if (role.permissions && role.permissions[resourceId] === level) {
|
2021-02-13 01:02:07 +13:00
|
|
|
permissions = fetchLevelPerms(
|
|
|
|
permissions,
|
|
|
|
level,
|
|
|
|
getExternalRoleID(role._id)
|
|
|
|
)
|
2021-02-12 02:38:07 +13:00
|
|
|
}
|
2021-02-09 06:22:07 +13:00
|
|
|
}
|
|
|
|
}
|
2021-02-13 01:02:07 +13:00
|
|
|
ctx.body = Object.assign(getBasePermissions(resourceId), permissions)
|
2021-02-06 04:58:25 +13:00
|
|
|
}
|
|
|
|
|
2021-05-03 19:31:09 +12:00
|
|
|
exports.addPermission = async function (ctx) {
|
2021-02-09 06:22:07 +13:00
|
|
|
ctx.body = await updatePermissionOnRole(
|
|
|
|
ctx.appId,
|
|
|
|
ctx.params,
|
|
|
|
PermissionUpdateType.ADD
|
|
|
|
)
|
2021-02-06 04:58:25 +13:00
|
|
|
}
|
|
|
|
|
2021-05-03 19:31:09 +12:00
|
|
|
exports.removePermission = async function (ctx) {
|
2021-02-09 06:22:07 +13:00
|
|
|
ctx.body = await updatePermissionOnRole(
|
|
|
|
ctx.appId,
|
|
|
|
ctx.params,
|
|
|
|
PermissionUpdateType.REMOVE
|
|
|
|
)
|
2021-02-06 04:58:25 +13:00
|
|
|
}
|