1
0
Fork 0
mirror of synced 2024-06-28 02:50:50 +12:00
budibase/packages/server/src/api/controllers/role.js

134 lines
3.6 KiB
JavaScript

const CouchDB = require("../../db")
const {
getBuiltinRoles,
BUILTIN_ROLE_IDS,
Role,
getRole,
isBuiltin,
getExternalRoleID,
} = require("../../utilities/security/roles")
const {
generateRoleID,
getRoleParams,
getUserParams,
ViewNames,
} = require("../../db/utils")
const UpdateRolesOptions = {
CREATED: "created",
REMOVED: "removed",
}
// exclude internal roles like builder
const EXTERNAL_BUILTIN_ROLE_IDS = [
BUILTIN_ROLE_IDS.ADMIN,
BUILTIN_ROLE_IDS.POWER,
BUILTIN_ROLE_IDS.BASIC,
BUILTIN_ROLE_IDS.PUBLIC,
]
async function updateRolesOnUserTable(db, roleId, updateOption) {
const table = await db.get(ViewNames.USERS)
const schema = table.schema
const remove = updateOption === UpdateRolesOptions.REMOVED
let updated = false
for (let prop of Object.keys(schema)) {
if (prop === "roleId") {
updated = true
const constraints = schema[prop].constraints
const indexOf = constraints.inclusion.indexOf(roleId)
if (remove && indexOf !== -1) {
constraints.inclusion.splice(indexOf, 1)
} else if (!remove && indexOf === -1) {
constraints.inclusion.push(roleId)
}
break
}
}
if (updated) {
await db.put(table)
}
}
exports.fetch = async function(ctx) {
const db = new CouchDB(ctx.user.appId)
const body = await db.allDocs(
getRoleParams(null, {
include_docs: true,
})
)
let roles = body.rows.map(row => row.doc)
const builtinRoles = getBuiltinRoles()
// need to combine builtin with any DB record of them (for sake of permissions)
for (let builtinRoleId of EXTERNAL_BUILTIN_ROLE_IDS) {
const builtinRole = builtinRoles[builtinRoleId]
const dbBuiltin = roles.filter(
dbRole => getExternalRoleID(dbRole._id) === builtinRoleId
)[0]
if (dbBuiltin == null) {
roles.push(builtinRole)
} else {
// remove role and all back after combining with the builtin
roles = roles.filter(role => role._id !== dbBuiltin._id)
dbBuiltin._id = getExternalRoleID(dbBuiltin._id)
roles.push(Object.assign(builtinRole, dbBuiltin))
}
}
ctx.body = roles
}
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, name, inherits, permissionId } = ctx.request.body
if (!_id) {
_id = generateRoleID()
} else if (isBuiltin(_id)) {
ctx.throw(400, "Cannot update builtin roles.")
}
const role = new Role(_id, name)
.addPermission(permissionId)
.addInheritance(inherits)
if (ctx.request.body._rev) {
role._rev = ctx.request.body._rev
}
const result = await db.put(role)
await updateRolesOnUserTable(db, _id, UpdateRolesOptions.CREATED)
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)
const roleId = ctx.params.roleId
if (isBuiltin(roleId)) {
ctx.throw(400, "Cannot delete builtin role.")
}
// first check no users actively attached to role
const users = (
await db.allDocs(
getUserParams(null, {
include_docs: true,
})
)
).rows.map(row => row.doc)
const usersWithRole = users.filter(user => user.roleId === roleId)
if (usersWithRole.length !== 0) {
ctx.throw(400, "Cannot delete role when it is in use.")
}
await db.remove(roleId, ctx.params.rev)
await updateRolesOnUserTable(
db,
ctx.params.roleId,
UpdateRolesOptions.REMOVED
)
ctx.message = `Role ${ctx.params.roleId} deleted successfully`
ctx.status = 200
}