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
5 changes: 5 additions & 0 deletions workspaces/rbac/.changeset/violet-walls-sort.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@backstage-community/plugin-rbac-backend': minor
---

Add support for group reference in superUsers list, using direct membership only
3 changes: 3 additions & 0 deletions workspaces/rbac/plugins/rbac-backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,11 @@ permission:
superUsers:
- name: user:default/alice
- name: user:default/mike
- name: group:default/admins
```

> **Note:** **Transient memberships are not supported for `superUsers`.** Meaning, when a group is specified as a super user, only direct group memberships are taken into account. Users who belong to a sub-group of a configured super user group will not be granted super user access.

For more information on the available API endpoints accessible to the policy administrators, refer to the [API documentation](./docs/apis.md).

### Configure plugins with permission
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1036,6 +1036,72 @@ describe('RBACPermissionPolicy Tests', () => {
);
});

it('should allow access to a user who is a member of a group configured as super user', async () => {
const superUsersConfig = new Array<{ name: string }>();
superUsersConfig.push({ name: 'group:default/super_users_group' });

const config = newConfig(csvPermFile, admins, superUsersConfig);
const adapter = await newAdapter(config);
const enfDelegateForTest = await newEnforcerDelegate(adapter, config);
const policyForTest = await newPermissionPolicy(
config,
enfDelegateForTest,
roleMetadataStorageTest,
);

const decision = await policyForTest.handle(
newPolicyQueryWithResourcePermission(
'catalog.entity.delete',
'catalog-entity',
'delete',
),
newPolicyQueryUser('user:default/some_user', [
'group:default/super_users_group',
]),
);
expect(decision.result).toBe(AuthorizeResult.ALLOW);
expectAuditorLogForPermission(
'user:default/some_user',
'catalog.entity.delete',
'catalog-entity',
'delete',
AuthorizeResult.ALLOW,
);
});

it('should deny access to a user who is not a member of a group configured as super user', async () => {
const superUsersConfig = new Array<{ name: string }>();
superUsersConfig.push({ name: 'group:default/super_users_group' });

const config = newConfig(csvPermFile, admins, superUsersConfig);
const adapter = await newAdapter(config);
const enfDelegateForTest = await newEnforcerDelegate(adapter, config);
const policyForTest = await newPermissionPolicy(
config,
enfDelegateForTest,
roleMetadataStorageTest,
);

const decision = await policyForTest.handle(
newPolicyQueryWithResourcePermission(
'catalog.entity.delete',
'catalog-entity',
'delete',
),
newPolicyQueryUser('user:default/some_user', [
'group:default/other_group',
]),
);
expect(decision.result).toBe(AuthorizeResult.DENY);
expectAuditorLogForPermission(
'user:default/some_user',
'catalog.entity.delete',
'catalog-entity',
'delete',
AuthorizeResult.DENY,
);
});

it('should remove users that are no longer in the config file', async () => {
const enfRole = await enfDelegate.getFilteredGroupingPolicy(1, adminRole);
const enfPermission = await enfDelegate.getFilteredPolicy(0, adminRole);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,12 @@ export class RBACPermissionPolicy implements PermissionPolicy {
return { result: AuthorizeResult.DENY };
}

if (this.superUserList!.includes(userEntityRef)) {
if (
this.superUserList!.includes(userEntityRef) ||
user.info.ownershipEntityRefs.some(ref =>
this.superUserList!.includes(ref),
)
) {
await auditorEvent.success({
meta: { result: AuthorizeResult.ALLOW },
});
Expand Down