Skip to content

Commit

Permalink
fix(core): Admin can only read Roles at or below their permission level
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelbromley committed Jan 12, 2024
1 parent 65aced6 commit fc5d981
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 5 deletions.
18 changes: 18 additions & 0 deletions packages/core/e2e/role.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,24 @@ describe('Role resolver', () => {
await adminClient.asUserWithCredentials(limitedAdmin.emailAddress, 'test');
});

it('limited admin cannot view Roles which require permissions they do not have', async () => {
const result = await adminClient.query<Codegen.GetRolesQuery, Codegen.GetRolesQueryVariables>(
GET_ROLES,
);

const roleCodes = result.roles.items.map(r => r.code);
expect(roleCodes).toEqual(['second-channel-admin-manager']);
});

it('limited admin cannot view Role which requires permissions they do not have', async () => {
const result = await adminClient.query<Codegen.GetRoleQuery, Codegen.GetRoleQueryVariables>(
GET_ROLE,
{ id: orderReaderRole.id },
);

expect(result.role).toBeNull();
});

it(
'limited admin cannot create Role with SuperAdmin permission',
assertThrowsWithMessage(async () => {
Expand Down
38 changes: 33 additions & 5 deletions packages/core/src/service/services/role.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,19 @@ export class RoleService {
return this.listQueryBuilder
.build(Role, options, { relations: relations ?? ['channels'], ctx })
.getManyAndCount()
.then(([items, totalItems]) => ({
items,
totalItems,
}));
.then(async ([items, totalItems]) => {
const visibleRoles: Role[] = [];
for (const item of items) {
const canRead = await this.activeUserCanReadRole(ctx, item);
if (canRead) {
visibleRoles.push(item);
}
}
return {
items: visibleRoles,
totalItems,
};
});
}

findOne(ctx: RequestContext, roleId: ID, relations?: RelationPaths<Role>): Promise<Role | undefined> {
Expand All @@ -85,7 +94,11 @@ export class RoleService {
where: { id: roleId },
relations: relations ?? ['channels'],
})
.then(result => result ?? undefined);
.then(async result => {
if (result && (await this.activeUserCanReadRole(ctx, result))) {
return result;
}
});
}

getChannelsForRole(ctx: RequestContext, roleId: ID): Promise<Channel[]> {
Expand Down Expand Up @@ -156,6 +169,21 @@ export class RoleService {
return false;
}

private async activeUserCanReadRole(ctx: RequestContext, role: Role): Promise<boolean> {
const permissionsRequired = getChannelPermissions([role]);
for (const channelPermissions of permissionsRequired) {
const activeUserHasRequiredPermissions = await this.userHasAllPermissionsOnChannel(
ctx,
channelPermissions.id,
channelPermissions.permissions,
);
if (!activeUserHasRequiredPermissions) {
return false;
}
}
return true;
}

/**
* @description
* Returns true if the User has all the specified permissions on that Channel
Expand Down

0 comments on commit fc5d981

Please sign in to comment.