diff --git a/workspaces/rbac/.changeset/violet-walls-sort.md b/workspaces/rbac/.changeset/violet-walls-sort.md new file mode 100644 index 00000000000..3476f88fe97 --- /dev/null +++ b/workspaces/rbac/.changeset/violet-walls-sort.md @@ -0,0 +1,5 @@ +--- +'@backstage-community/plugin-rbac-backend': minor +--- + +Add support for group reference in superUsers list, using direct membership only diff --git a/workspaces/rbac/plugins/rbac-backend/README.md b/workspaces/rbac/plugins/rbac-backend/README.md index 754885c5453..9930236b15d 100644 --- a/workspaces/rbac/plugins/rbac-backend/README.md +++ b/workspaces/rbac/plugins/rbac-backend/README.md @@ -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 diff --git a/workspaces/rbac/plugins/rbac-backend/src/policies/permission-policy.test.ts b/workspaces/rbac/plugins/rbac-backend/src/policies/permission-policy.test.ts index 6673dc0bb5b..b65f92d2924 100644 --- a/workspaces/rbac/plugins/rbac-backend/src/policies/permission-policy.test.ts +++ b/workspaces/rbac/plugins/rbac-backend/src/policies/permission-policy.test.ts @@ -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); diff --git a/workspaces/rbac/plugins/rbac-backend/src/policies/permission-policy.ts b/workspaces/rbac/plugins/rbac-backend/src/policies/permission-policy.ts index c113abf8378..c03b391336e 100644 --- a/workspaces/rbac/plugins/rbac-backend/src/policies/permission-policy.ts +++ b/workspaces/rbac/plugins/rbac-backend/src/policies/permission-policy.ts @@ -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 }, });