diff --git a/src/lib/services/project-service.ts b/src/lib/services/project-service.ts index 0683c4883c07..507a8d6932ee 100644 --- a/src/lib/services/project-service.ts +++ b/src/lib/services/project-service.ts @@ -429,7 +429,9 @@ export default class ProjectService { return this.accessService.getProjectRoleAccess(projectId); } - // Deprecated: See addAccess instead. + /** + * @deprecated see addAccess instead. + */ async addUser( projectId: string, roleId: number, @@ -469,6 +471,9 @@ export default class ProjectService { ); } + /** + * @deprecated use removeUserAccess + */ async removeUser( projectId: string, roleId: number, @@ -510,7 +515,10 @@ export default class ProjectService { const ownerRole = await this.accessService.getRoleByName( RoleName.OWNER, ); - await this.validateAtLeastOneOwner(projectId, ownerRole); + + if (existingRoles.includes(ownerRole.id)) { + await this.validateAtLeastOneOwner(projectId, ownerRole); + } await this.accessService.removeUserAccess(projectId, userId); @@ -539,7 +547,10 @@ export default class ProjectService { const ownerRole = await this.accessService.getRoleByName( RoleName.OWNER, ); - await this.validateAtLeastOneOwner(projectId, ownerRole); + + if (existingRoles.includes(ownerRole.id)) { + await this.validateAtLeastOneOwner(projectId, ownerRole); + } await this.accessService.removeGroupAccess(projectId, groupId); @@ -591,6 +602,9 @@ export default class ProjectService { ); } + /** + * @deprecated use removeGroupAccess + */ async removeGroup( projectId: string, roleId: number, @@ -744,7 +758,6 @@ export default class ProjectService { if (hasOwnerRole && isRemovingOwnerRole) { await this.validateAtLeastOneOwner(projectId, ownerRole); } - await this.validateAtLeastOneOwner(projectId, ownerRole); await this.accessService.setProjectRolesForGroup( projectId, @@ -870,7 +883,6 @@ export default class ProjectService { // Nothing to do.... return; } - await this.validateAtLeastOneOwner(projectId, currentRole); await this.accessService.updateUserProjectRole( @@ -924,7 +936,6 @@ export default class ProjectService { // Nothing to do.... return; } - await this.validateAtLeastOneOwner(projectId, currentRole); await this.accessService.updateGroupProjectRole( diff --git a/src/test/e2e/services/project-service.e2e.test.ts b/src/test/e2e/services/project-service.e2e.test.ts index efbd717a996a..5ad23511ce98 100644 --- a/src/test/e2e/services/project-service.e2e.test.ts +++ b/src/test/e2e/services/project-service.e2e.test.ts @@ -1048,6 +1048,44 @@ describe('ensure project has at least one owner', () => { ); }); + test('should be able to remove member user from the project when another is owner', async () => { + const project = { + id: 'remove-users-members-allowed', + name: 'New project', + description: 'Blah', + mode: 'open' as const, + defaultStickiness: 'clientId', + }; + await projectService.createProject(project, user); + + const memberRole = await stores.roleStore.getRoleByName( + RoleName.MEMBER, + ); + + const memberUser = await stores.userStore.insert({ + name: 'Some Name', + email: 'member@getunleash.io', + }); + + await projectService.addAccess( + project.id, + [memberRole.id], + [], + [memberUser.id], + 'test', + ); + + const usersBefore = await projectService.getProjectUsers(project.id); + await projectService.removeUserAccess( + project.id, + memberUser.id, + 'test', + ); + const usersAfter = await projectService.getProjectUsers(project.id); + expect(usersBefore).toHaveLength(2); + expect(usersAfter).toHaveLength(1); + }); + test('should not update role for user on project when she is the owner', async () => { const project = { id: 'update-users-not-allowed',