Utility for detecting loops in a list of roles.
This commit is contained in:
parent
10e187014a
commit
fa9bb030c9
|
@ -0,0 +1,47 @@
|
|||
import { Role } from "@budibase/types"
|
||||
|
||||
// Function to detect loops in roles
|
||||
export function checkForRoleInheritanceLoops(roles: Role[]): boolean {
|
||||
const roleMap = new Map<string, Role>()
|
||||
roles.forEach(role => {
|
||||
roleMap.set(role._id!, role)
|
||||
})
|
||||
|
||||
const checked = new Set<string>()
|
||||
const checking = new Set<string>()
|
||||
|
||||
function hasLoop(roleId: string): boolean {
|
||||
if (checking.has(roleId)) {
|
||||
return true
|
||||
}
|
||||
if (checked.has(roleId)) {
|
||||
return false
|
||||
}
|
||||
|
||||
checking.add(roleId)
|
||||
|
||||
const role = roleMap.get(roleId)
|
||||
if (!role) {
|
||||
// role not found - ignore
|
||||
checking.delete(roleId)
|
||||
return false
|
||||
}
|
||||
|
||||
const inherits = Array.isArray(role.inherits)
|
||||
? role.inherits
|
||||
: [role.inherits]
|
||||
for (const inheritedId of inherits) {
|
||||
if (inheritedId && hasLoop(inheritedId)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// mark this role has been fully checked
|
||||
checking.delete(roleId)
|
||||
checked.add(roleId)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return !!roles.find(role => hasLoop(role._id!))
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
import { checkForRoleInheritanceLoops } from "../roles"
|
||||
import { Role } from "@budibase/types"
|
||||
|
||||
/**
|
||||
* This unit test exists as this utility will be used in the frontend and backend, confirmation
|
||||
* of its API and expected results is useful since the backend tests won't confirm it works
|
||||
* exactly as the frontend needs it to - easy to add specific test cases here that the frontend
|
||||
* might need to check/cover.
|
||||
*/
|
||||
|
||||
interface TestRole extends Omit<Role, "_id"> {
|
||||
_id: string
|
||||
}
|
||||
|
||||
let allRoles: TestRole[] = []
|
||||
|
||||
function role(id: string, inherits: string | string[]): TestRole {
|
||||
const role = {
|
||||
_id: id,
|
||||
inherits: inherits,
|
||||
name: "ROLE",
|
||||
permissionId: "PERMISSION",
|
||||
permissions: {}, // not needed for this test
|
||||
}
|
||||
allRoles.push(role)
|
||||
return role
|
||||
}
|
||||
|
||||
describe("role utilities", () => {
|
||||
let role1: TestRole, role2: TestRole
|
||||
|
||||
beforeEach(() => {
|
||||
role1 = role("role_1", [])
|
||||
role2 = role("role_2", [role1._id])
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
allRoles = []
|
||||
})
|
||||
|
||||
function check(hasLoop: boolean) {
|
||||
const result = checkForRoleInheritanceLoops(allRoles)
|
||||
expect(result).toBe(hasLoop)
|
||||
}
|
||||
|
||||
describe("checkForRoleInheritanceLoops", () => {
|
||||
it("should confirm no loops", () => {
|
||||
check(false)
|
||||
})
|
||||
|
||||
it("should confirm there is a loop", () => {
|
||||
const role3 = role("role_3", [role2._id])
|
||||
const role4 = role("role_4", [role3._id, role2._id, role1._id])
|
||||
role3.inherits = [
|
||||
...(Array.isArray(role3.inherits) ? role3.inherits : []),
|
||||
role4._id,
|
||||
]
|
||||
check(true)
|
||||
})
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue