Flipping RBAC implementation to use levels -> role for resource perms API and resource -> level -> role for full fetch (please note full fetch will only work for resources that have a custom permission in the system somewhere, everything else simply defaults to standard.
This commit is contained in:
parent
60b7d68c2e
commit
3fd557bf08
|
@ -25,7 +25,7 @@ const PermissionUpdateType = {
|
||||||
const SUPPORTED_LEVELS = [PermissionLevels.WRITE, PermissionLevels.READ]
|
const SUPPORTED_LEVELS = [PermissionLevels.WRITE, PermissionLevels.READ]
|
||||||
|
|
||||||
function getPermissionType(resourceId) {
|
function getPermissionType(resourceId) {
|
||||||
const docType = DocumentTypes.filter(docType =>
|
const docType = Object.values(DocumentTypes).filter(docType =>
|
||||||
resourceId.startsWith(docType)
|
resourceId.startsWith(docType)
|
||||||
)[0]
|
)[0]
|
||||||
switch (docType) {
|
switch (docType) {
|
||||||
|
@ -54,7 +54,10 @@ function getBasePermissions(resourceId) {
|
||||||
}
|
}
|
||||||
const perms = getBuiltinPermissionByID(role.permissionId)
|
const perms = getBuiltinPermissionByID(role.permissionId)
|
||||||
const typedPermission = perms.permissions.find(perm => perm.type === type)
|
const typedPermission = perms.permissions.find(perm => perm.type === type)
|
||||||
if (typedPermission) {
|
if (
|
||||||
|
typedPermission &&
|
||||||
|
SUPPORTED_LEVELS.indexOf(typedPermission.level) !== -1
|
||||||
|
) {
|
||||||
const level = typedPermission.level
|
const level = typedPermission.level
|
||||||
permissions[level] = lowerBuiltinRoleID(permissions[level], roleId)
|
permissions[level] = lowerBuiltinRoleID(permissions[level], roleId)
|
||||||
if (isPermissionLevelHigherThanRead(level)) {
|
if (isPermissionLevelHigherThanRead(level)) {
|
||||||
|
@ -148,18 +151,15 @@ exports.fetch = async function(ctx) {
|
||||||
let permissions = {}
|
let permissions = {}
|
||||||
// create an object with structure role ID -> resource ID -> level
|
// create an object with structure role ID -> resource ID -> level
|
||||||
for (let role of roles) {
|
for (let role of roles) {
|
||||||
if (role.permissions) {
|
if (!role.permissions) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
const roleId = getExternalRoleID(role._id)
|
const roleId = getExternalRoleID(role._id)
|
||||||
if (permissions[roleId] == null) {
|
|
||||||
permissions[roleId] = {}
|
|
||||||
}
|
|
||||||
// TODO: need to work this out
|
|
||||||
for (let [resource, level] of Object.entries(role.permissions)) {
|
for (let [resource, level] of Object.entries(role.permissions)) {
|
||||||
permissions[roleId][resource] = higherPermission(
|
if (permissions[resource] == null) {
|
||||||
permissions[roleId][resource],
|
permissions[resource] = getBasePermissions(resource)
|
||||||
level
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
permissions[resource][level] = roleId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.body = permissions
|
ctx.body = permissions
|
||||||
|
@ -178,7 +178,7 @@ exports.getResourcePerms = async function(ctx) {
|
||||||
for (let level of SUPPORTED_LEVELS) {
|
for (let level of SUPPORTED_LEVELS) {
|
||||||
// update the various roleIds in the resource permissions
|
// update the various roleIds in the resource permissions
|
||||||
for (let role of roles) {
|
for (let role of roles) {
|
||||||
if (role.permissions && role.permissions[resourceId]) {
|
if (role.permissions && role.permissions[resourceId] === level) {
|
||||||
resourcePerms[level] = getExternalRoleID(role._id)
|
resourcePerms[level] = getExternalRoleID(role._id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,21 +71,22 @@ describe("/permission", () => {
|
||||||
.set(defaultHeaders(appId))
|
.set(defaultHeaders(appId))
|
||||||
.expect("Content-Type", /json/)
|
.expect("Content-Type", /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
expect(res.body[STD_ROLE_ID]).toEqual("read")
|
expect(res.body["read"]).toEqual(STD_ROLE_ID)
|
||||||
|
expect(res.body["write"]).toEqual(HIGHER_ROLE_ID)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should get resource permissions with multiple roles", async () => {
|
it("should get resource permissions with multiple roles", async () => {
|
||||||
perms = await addPermission(request, appId, HIGHER_ROLE_ID, table._id, "write")
|
perms = await addPermission(request, appId, HIGHER_ROLE_ID, table._id, "write")
|
||||||
const res = await getTablePermissions()
|
const res = await getTablePermissions()
|
||||||
expect(res.body[HIGHER_ROLE_ID]).toEqual("write")
|
expect(res.body["read"]).toEqual(STD_ROLE_ID)
|
||||||
expect(res.body[STD_ROLE_ID]).toEqual("read")
|
expect(res.body["write"]).toEqual(HIGHER_ROLE_ID)
|
||||||
const allRes = await request
|
const allRes = await request
|
||||||
.get(`/api/permission`)
|
.get(`/api/permission`)
|
||||||
.set(defaultHeaders(appId))
|
.set(defaultHeaders(appId))
|
||||||
.expect("Content-Type", /json/)
|
.expect("Content-Type", /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
expect(allRes.body[HIGHER_ROLE_ID][table._id]).toEqual("write")
|
expect(allRes.body[table._id]["write"]).toEqual(HIGHER_ROLE_ID)
|
||||||
expect(allRes.body[STD_ROLE_ID][table._id]).toEqual("read")
|
expect(allRes.body[table._id]["read"]).toEqual(STD_ROLE_ID)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,7 @@ function getAllowedLevels(userPermLevel) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.BUILTIN_PERMISSION_IDS = {
|
exports.BUILTIN_PERMISSION_IDS = {
|
||||||
|
PUBLIC: "public",
|
||||||
READ_ONLY: "read_only",
|
READ_ONLY: "read_only",
|
||||||
WRITE: "write",
|
WRITE: "write",
|
||||||
ADMIN: "admin",
|
ADMIN: "admin",
|
||||||
|
@ -70,6 +71,13 @@ exports.BUILTIN_PERMISSION_IDS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.BUILTIN_PERMISSIONS = {
|
exports.BUILTIN_PERMISSIONS = {
|
||||||
|
PUBLIC: {
|
||||||
|
_id: exports.BUILTIN_PERMISSION_IDS.PUBLIC,
|
||||||
|
name: "Public",
|
||||||
|
permissions: [
|
||||||
|
new Permission(PermissionTypes.WEBHOOK, PermissionLevels.EXECUTE)
|
||||||
|
],
|
||||||
|
},
|
||||||
READ_ONLY: {
|
READ_ONLY: {
|
||||||
_id: exports.BUILTIN_PERMISSION_IDS.READ_ONLY,
|
_id: exports.BUILTIN_PERMISSION_IDS.READ_ONLY,
|
||||||
name: "Read only",
|
name: "Read only",
|
||||||
|
|
|
@ -37,7 +37,7 @@ exports.BUILTIN_ROLES = {
|
||||||
.addPermission(BUILTIN_PERMISSION_IDS.WRITE)
|
.addPermission(BUILTIN_PERMISSION_IDS.WRITE)
|
||||||
.addInheritance(BUILTIN_IDS.PUBLIC),
|
.addInheritance(BUILTIN_IDS.PUBLIC),
|
||||||
PUBLIC: new Role(BUILTIN_IDS.PUBLIC, "Public").addPermission(
|
PUBLIC: new Role(BUILTIN_IDS.PUBLIC, "Public").addPermission(
|
||||||
BUILTIN_PERMISSION_IDS.READ_ONLY
|
BUILTIN_PERMISSION_IDS.PUBLIC
|
||||||
),
|
),
|
||||||
BUILDER: new Role(BUILTIN_IDS.BUILDER, "Builder").addPermission(
|
BUILDER: new Role(BUILTIN_IDS.BUILDER, "Builder").addPermission(
|
||||||
BUILTIN_PERMISSION_IDS.ADMIN
|
BUILTIN_PERMISSION_IDS.ADMIN
|
||||||
|
@ -57,11 +57,10 @@ function isBuiltin(role) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whichever builtin roleID is lower.
|
* Works through the inheritance ranks to see how far up the builtin stack this ID is.
|
||||||
*/
|
*/
|
||||||
exports.lowerBuiltinRoleID = (roleId1, roleId2) => {
|
function builtinRoleToNumber(id) {
|
||||||
const MAX = Object.values(BUILTIN_IDS).length + 1
|
const MAX = Object.values(BUILTIN_IDS).length + 1
|
||||||
function toNum(id) {
|
|
||||||
if (id === BUILTIN_IDS.ADMIN || id === BUILTIN_IDS.BUILDER) {
|
if (id === BUILTIN_IDS.ADMIN || id === BUILTIN_IDS.BUILDER) {
|
||||||
return MAX
|
return MAX
|
||||||
}
|
}
|
||||||
|
@ -75,8 +74,19 @@ exports.lowerBuiltinRoleID = (roleId1, roleId2) => {
|
||||||
count++
|
count++
|
||||||
} while (role !== null)
|
} while (role !== null)
|
||||||
return count
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whichever builtin roleID is lower.
|
||||||
|
*/
|
||||||
|
exports.lowerBuiltinRoleID = (roleId1, roleId2) => {
|
||||||
|
if (!roleId1) {
|
||||||
|
return roleId2
|
||||||
}
|
}
|
||||||
return toNum(roleId1) > toNum(roleId2) ? roleId2 : roleId1
|
if (!roleId2) {
|
||||||
|
return roleId1
|
||||||
|
}
|
||||||
|
return builtinRoleToNumber(roleId1) > builtinRoleToNumber(roleId2) ? roleId2 : roleId1
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue