mirror of
https://github.com/Unleash/unleash.git
synced 2024-12-22 19:07:54 +01:00
fix: setRolesForUser and setRolesForGroup role check (#6380)
In order to stop privilege escalation via `/api/admin/projects/:project/users/:userId/roles` and `/api/admin/projects/:project/groups/:groupId/roles` this PR adds the same check we added to setAccess methods to the methods updating access for these two methods. Also adds tests that verify that we throw an exception if you try to assign roles you do not have. Thank you @nunogois for spotting this during testing.
This commit is contained in:
parent
80d89ab260
commit
0887999dd0
@ -728,8 +728,8 @@ export default class ProjectService {
|
|||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return rolesBeingAdded.every((role) =>
|
return rolesBeingAdded.every((roleId) =>
|
||||||
userRoles.some((userRole) => userRole.id === role),
|
userRoles.some((userRole) => userRole.id === roleId),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
async addAccess(
|
async addAccess(
|
||||||
@ -785,7 +785,6 @@ export default class ProjectService {
|
|||||||
projectId,
|
projectId,
|
||||||
userId,
|
userId,
|
||||||
);
|
);
|
||||||
|
|
||||||
const ownerRole = await this.accessService.getRoleByName(
|
const ownerRole = await this.accessService.getRoleByName(
|
||||||
RoleName.OWNER,
|
RoleName.OWNER,
|
||||||
);
|
);
|
||||||
@ -795,7 +794,12 @@ export default class ProjectService {
|
|||||||
if (hasOwnerRole && isRemovingOwnerRole) {
|
if (hasOwnerRole && isRemovingOwnerRole) {
|
||||||
await this.validateAtLeastOneOwner(projectId, ownerRole);
|
await this.validateAtLeastOneOwner(projectId, ownerRole);
|
||||||
}
|
}
|
||||||
|
const isAllowedToAssignRoles = await this.isAllowedToAddAccess(
|
||||||
|
createdByUserId,
|
||||||
|
projectId,
|
||||||
|
newRoles,
|
||||||
|
);
|
||||||
|
if (isAllowedToAssignRoles) {
|
||||||
await this.accessService.setProjectRolesForUser(
|
await this.accessService.setProjectRolesForUser(
|
||||||
projectId,
|
projectId,
|
||||||
userId,
|
userId,
|
||||||
@ -816,6 +820,11 @@ export default class ProjectService {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
throw new InvalidOperationError(
|
||||||
|
'User tried to assign a role they did not have access to',
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async setRolesForGroup(
|
async setRolesForGroup(
|
||||||
@ -838,7 +847,12 @@ export default class ProjectService {
|
|||||||
if (hasOwnerRole && isRemovingOwnerRole) {
|
if (hasOwnerRole && isRemovingOwnerRole) {
|
||||||
await this.validateAtLeastOneOwner(projectId, ownerRole);
|
await this.validateAtLeastOneOwner(projectId, ownerRole);
|
||||||
}
|
}
|
||||||
|
const isAllowedToAssignRoles = await this.isAllowedToAddAccess(
|
||||||
|
createdByUserId,
|
||||||
|
projectId,
|
||||||
|
newRoles,
|
||||||
|
);
|
||||||
|
if (isAllowedToAssignRoles) {
|
||||||
await this.accessService.setProjectRolesForGroup(
|
await this.accessService.setProjectRolesForGroup(
|
||||||
projectId,
|
projectId,
|
||||||
groupId,
|
groupId,
|
||||||
@ -860,6 +874,11 @@ export default class ProjectService {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
throw new InvalidOperationError(
|
||||||
|
'User tried to assign a role they did not have access to',
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async findProjectGroupRole(
|
async findProjectGroupRole(
|
||||||
|
@ -25,6 +25,7 @@ import {
|
|||||||
SYSTEM_USER_ID,
|
SYSTEM_USER_ID,
|
||||||
} from '../../../lib/types';
|
} from '../../../lib/types';
|
||||||
import { User } from '../../../lib/server-impl';
|
import { User } from '../../../lib/server-impl';
|
||||||
|
import { InvalidOperationError } from '../../../lib/error';
|
||||||
|
|
||||||
let stores: IUnleashStores;
|
let stores: IUnleashStores;
|
||||||
let db: ITestDb;
|
let db: ITestDb;
|
||||||
@ -619,7 +620,80 @@ describe('Managing Project access', () => {
|
|||||||
projectUser.username,
|
projectUser.username,
|
||||||
projectUser.id,
|
projectUser.id,
|
||||||
),
|
),
|
||||||
).resolves.not.toThrow();
|
).resolves.not.toThrow(
|
||||||
|
new InvalidOperationError(
|
||||||
|
'User tried to assign a role they did not have access to',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test('Users can not assign roles they do not have to a user through explicit roles endpoint', async () => {
|
||||||
|
const project = {
|
||||||
|
id: 'user_fail_assign_to_user',
|
||||||
|
name: 'user_fail_assign_to_user',
|
||||||
|
description: '',
|
||||||
|
mode: 'open' as const,
|
||||||
|
defaultStickiness: 'clientId',
|
||||||
|
};
|
||||||
|
await projectService.createProject(project, user);
|
||||||
|
const projectUser = await stores.userStore.insert({
|
||||||
|
name: 'Some project user',
|
||||||
|
email: 'fail_assign_role_to_user@example.com',
|
||||||
|
});
|
||||||
|
const secondUser = await stores.userStore.insert({
|
||||||
|
name: 'Some other user',
|
||||||
|
email: 'otheruser_no_roles@example.com',
|
||||||
|
});
|
||||||
|
const customRole = await stores.roleStore.create({
|
||||||
|
name: 'role_that_noone_has',
|
||||||
|
roleType: 'custom',
|
||||||
|
description:
|
||||||
|
'Used to prove that you can not assign a role you do not have via setRolesForUser',
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
projectService.setRolesForUser(
|
||||||
|
project.id,
|
||||||
|
secondUser.id,
|
||||||
|
[customRole.id],
|
||||||
|
projectUser.username,
|
||||||
|
projectUser.id,
|
||||||
|
),
|
||||||
|
).rejects.toThrow();
|
||||||
|
});
|
||||||
|
test('Users can not assign roles they do not have to a group through explicit roles endpoint', async () => {
|
||||||
|
const project = {
|
||||||
|
id: 'user_fail_assign_to_group',
|
||||||
|
name: 'user_fail_assign_to_group',
|
||||||
|
description: '',
|
||||||
|
mode: 'open' as const,
|
||||||
|
defaultStickiness: 'clientId',
|
||||||
|
};
|
||||||
|
await projectService.createProject(project, user);
|
||||||
|
const projectUser = await stores.userStore.insert({
|
||||||
|
name: 'Some project user',
|
||||||
|
email: 'fail_assign_role_to_group@example.com',
|
||||||
|
});
|
||||||
|
const group = await stores.groupStore.create({
|
||||||
|
name: 'Some group_awaiting_role',
|
||||||
|
});
|
||||||
|
const customRole = await stores.roleStore.create({
|
||||||
|
name: 'role_that_noone_has_fail_assign_group',
|
||||||
|
roleType: 'custom',
|
||||||
|
description:
|
||||||
|
'Used to prove that you can not assign a role you do not have via setRolesForGroup',
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
projectService.setRolesForGroup(
|
||||||
|
project.id,
|
||||||
|
group.id,
|
||||||
|
[customRole.id],
|
||||||
|
projectUser.username,
|
||||||
|
projectUser.id,
|
||||||
|
),
|
||||||
|
).rejects.toThrow(
|
||||||
|
new InvalidOperationError(
|
||||||
|
'User tried to assign a role they did not have access to',
|
||||||
|
),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user