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:
mike12345567 2021-02-11 18:13:09 +00:00
parent 60b7d68c2e
commit 3fd557bf08
4 changed files with 55 additions and 36 deletions

View File

@ -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)
} }
} }

View File

@ -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)
}) })
}) })

View File

@ -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",

View File

@ -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
} }
/** /**