1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

fix: group and user has at least 1 project owner counter respects multirole project groups (#8453)

This commit is contained in:
David Leek 2024-10-16 12:47:00 +02:00 committed by GitHub
parent b9ea6641ff
commit bb800e3537
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 52 additions and 12 deletions

View File

@ -2,6 +2,7 @@ import { createTestConfig } from '../../../test/config/test-config';
import { BadDataError } from '../../error';
import { type IBaseEvent, RoleName, TEST_AUDIT_USER } from '../../types';
import { createFakeProjectService } from './createProjectService';
import ProjectService from './project-service';
describe('enterprise extension: enable change requests', () => {
const createService = () => {
@ -300,4 +301,44 @@ describe('enterprise extension: enable change requests', () => {
),
).resolves.toBeTruthy();
});
test('has at least one owner after deletion when group has the role and a project user for role exists', async () => {
const config = createTestConfig();
const projectId = 'fake-project-id';
const service = new ProjectService(
{
projectStore: {} as any,
projectOwnersReadModel: {} as any,
projectFlagCreatorsReadModel: {} as any,
eventStore: {} as any,
featureToggleStore: {} as any,
environmentStore: {} as any,
featureEnvironmentStore: {} as any,
accountStore: {} as any,
projectStatsStore: {} as any,
projectReadModel: {} as any,
onboardingReadModel: {} as any,
},
config,
{
getProjectUsersForRole: async () =>
Promise.resolve([{ id: 1 } as any]),
} as any,
{} as any,
{
getProjectGroups: async () =>
Promise.resolve([{ roles: [2, 5] } as any]),
} as any,
{} as any,
{} as any,
{} as any,
{} as any,
);
await service.validateAtLeastOneOwner(projectId, {
id: 5,
name: 'Owner',
type: 'Owner',
});
});
});

View File

@ -1146,8 +1146,8 @@ export default class ProjectService {
projectId,
);
const groups = await this.groupService.getProjectGroups(projectId);
const roleGroups = groups.filter(
(g) => g.roleId === currentRole.id,
const roleGroups = groups.filter((g) =>
g.roles?.includes(currentRole.id),
);
if (users.length + roleGroups.length < 2) {
throw new ProjectWithoutOwnerError();
@ -1264,11 +1264,11 @@ export default class ProjectService {
auditUser: IAuditUser,
): Promise<void> {
const usersWithRoles = await this.getAccessToProject(projectId);
const user = usersWithRoles.groups.find((u) => u.id === userId);
if (!user)
const userGroup = usersWithRoles.groups.find((u) => u.id === userId);
if (!userGroup)
throw new ValidationError('Unexpected empty user', [], undefined);
const currentRole = usersWithRoles.roles.find(
(r) => r.id === user.roleId,
const currentRole = usersWithRoles.roles.find((r) =>
userGroup.roles?.includes(r.id),
);
if (!currentRole)
throw new ValidationError(

View File

@ -2,7 +2,6 @@ import type {
ICreateGroupModel,
IGroup,
IGroupModel,
IGroupModelWithProjectRole,
IGroupProject,
IGroupRole,
IGroupUser,
@ -29,6 +28,7 @@ import type { IAccountStore } from '../types/stores/account-store';
import type { IUser } from '../types/user';
import type EventService from '../features/events/event-service';
import { SSO_SYNC_USER } from '../db/group-store';
import type { IGroupWithProjectRoles } from '../types/stores/access-store';
const setsAreEqual = (firstSet, secondSet) =>
firstSet.size === secondSet.size &&
@ -179,7 +179,7 @@ export class GroupService {
async getProjectGroups(
projectId: string,
): Promise<IGroupModelWithProjectRole[]> {
): Promise<IGroupWithProjectRoles[]> {
const projectGroups = await this.groupStore.getProjectGroups(projectId);
if (projectGroups.length > 0) {

View File

@ -53,8 +53,7 @@ export interface ICreateGroupUserModel {
user: Pick<IUser, 'id'>;
}
export interface IGroupModelWithProjectRole extends IGroupModel {
roleId: number;
export interface IGroupModelWithAddedAt extends IGroupModel {
addedAt: Date;
}

View File

@ -1,5 +1,5 @@
import type { PermissionRef } from '../../services/access-service';
import type { IGroupModelWithProjectRole } from '../group';
import type { IGroupModelWithAddedAt } from '../group';
import type { IPermission, IUserAccessOverview, IUserWithRole } from '../model';
import type { Store } from './store';
@ -62,7 +62,7 @@ export interface IUserWithProjectRoles
extends IUserWithRole,
IEntityWithProjectRoles {}
export interface IGroupWithProjectRoles
extends IGroupModelWithProjectRole,
extends IGroupModelWithAddedAt,
IEntityWithProjectRoles {}
export interface IAccessStore extends Store<IRole, number> {