Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions examples/rbac_with_role_hierarchy_domains_model.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[request_definition]
r = sub, dom, obj, act

[policy_definition]
p = sub, dom, obj, act

[role_definition]
g = _, _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = (g(r.sub, p.sub, r.dom) || g(r.sub, p.sub, '*')) && (p.dom == '*' || r.dom == p.dom) && r.obj == p.obj && r.act == p.act
25 changes: 25 additions & 0 deletions examples/rbac_with_role_hierarchy_domains_policy.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
p, abstract_role1, *, devis, read
p, abstract_role1, *, devis, create

p, abstract_role2, *, devis, read
p, abstract_role2, *, organization, read
p, abstract_role2, *, organization, write

g, role1, abstract_role1, tenant1
g, role1, abstract_role1, tenant2
g, role1, abstract_role1, tenant3

g, role2, abstract_role2, tenant1
g, role2, abstract_role2, tenant2
g, role2, abstract_role2, tenant3

g, super_user, abstract_role2, *

g, michael, role1, tenant1
g, antoine, role1, tenant2
g, kevin, role1, tenant3

g, thomas, role2, tenant1
g, lucie, role2, tenant3

g, theo, super_user, *
23 changes: 23 additions & 0 deletions src/enforcer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,13 +305,27 @@ export class Enforcer extends ManagementEnforcer {
let n: string | undefined;
while ((n = q.shift()) !== undefined) {
for (const rm of this.rmMap.values()) {
// Get roles for the specific domain
const role = await rm.getRoles(n, ...domain);
role.forEach((r) => {
if (!res.has(r)) {
res.add(r);
q.push(r);
}
});

// Also get roles with wildcard domain '*' if domain is provided and not already '*'
if (domain && domain.length > 0 && domain[0] !== '*') {
const wildcardDomain = [...domain];
wildcardDomain[0] = '*';
const wildcardRole = await rm.getRoles(n, ...wildcardDomain);
wildcardRole.forEach((r) => {
if (!res.has(r)) {
res.add(r);
q.push(r);
}
});
}
}
}

Expand All @@ -337,8 +351,17 @@ export class Enforcer extends ManagementEnforcer {

for (const n of roles) {
if (withDomain) {
// Get policies matching the specific domain
const p = await this.getFilteredPolicy(0, n, ...domain);
res.push(...p);

// Also get policies with wildcard domain '*' if the domain is not already '*'
if (domain[0] !== '*') {
const wildcardDomain = [...domain];
wildcardDomain[0] = '*';
const wildcardP = await this.getFilteredPolicy(0, n, ...wildcardDomain);
res.push(...wildcardP);
}
} else {
const p = await this.getPermissionsForUser(n);
res.push(...p);
Expand Down
62 changes: 62 additions & 0 deletions test/rbacRoleHierarchyDomains.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { newEnforcer } from '../src';

describe('Test Role Hierarchy with Domains and Wildcards', () => {
test('getImplicitPermissionsForUser should handle wildcard domains', async () => {
const e = await newEnforcer(
'examples/rbac_with_role_hierarchy_domains_model.conf',
'examples/rbac_with_role_hierarchy_domains_policy.csv'
);

// Test michael in tenant1 - should get permissions from abstract_role1 with domain *
const michaelPerms = await e.getImplicitPermissionsForUser('michael', 'tenant1');

// Michael should have:
// - abstract_role1 permissions (devis read/create) with domain *
expect(michaelPerms).toContainEqual(['abstract_role1', '*', 'devis', 'read']);
expect(michaelPerms).toContainEqual(['abstract_role1', '*', 'devis', 'create']);

// Test thomas in tenant1 - should get permissions from abstract_role2 with domain *
const thomasPerms = await e.getImplicitPermissionsForUser('thomas', 'tenant1');

// Thomas should have:
// - abstract_role2 permissions (devis read, organization read/write) with domain *
expect(thomasPerms).toContainEqual(['abstract_role2', '*', 'devis', 'read']);
expect(thomasPerms).toContainEqual(['abstract_role2', '*', 'organization', 'read']);
expect(thomasPerms).toContainEqual(['abstract_role2', '*', 'organization', 'write']);

// Test theo with super_user - should get permissions from abstract_role2 with domain *
const theoPerms = await e.getImplicitPermissionsForUser('theo', 'tenant1');

// Theo should have:
// - abstract_role2 permissions (devis read, organization read/write) with domain *
expect(theoPerms).toContainEqual(['abstract_role2', '*', 'devis', 'read']);
expect(theoPerms).toContainEqual(['abstract_role2', '*', 'organization', 'read']);
expect(theoPerms).toContainEqual(['abstract_role2', '*', 'organization', 'write']);
});

test('enforce should work with wildcard domains in role hierarchy', async () => {
const e = await newEnforcer(
'examples/rbac_with_role_hierarchy_domains_model.conf',
'examples/rbac_with_role_hierarchy_domains_policy.csv'
);

// Michael in tenant1 should be able to read devis
expect(await e.enforce('michael', 'tenant1', 'devis', 'read')).toBe(true);
expect(await e.enforce('michael', 'tenant1', 'devis', 'create')).toBe(true);

// Michael in tenant2 should NOT have access (not assigned to tenant2)
expect(await e.enforce('michael', 'tenant2', 'devis', 'read')).toBe(false);

// Antoine in tenant2 should be able to read devis
expect(await e.enforce('antoine', 'tenant2', 'devis', 'read')).toBe(true);

// Thomas in tenant1 should have organization permissions
expect(await e.enforce('thomas', 'tenant1', 'organization', 'read')).toBe(true);
expect(await e.enforce('thomas', 'tenant1', 'organization', 'write')).toBe(true);

// Theo with super_user should have access to any tenant
expect(await e.enforce('theo', 'tenant1', 'organization', 'read')).toBe(true);
expect(await e.enforce('theo', 'tenant2', 'organization', 'read')).toBe(true);
expect(await e.enforce('theo', 'tenant3', 'organization', 'read')).toBe(true);
});
});