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:
parent
b9ea6641ff
commit
bb800e3537
@ -2,6 +2,7 @@ import { createTestConfig } from '../../../test/config/test-config';
|
|||||||
import { BadDataError } from '../../error';
|
import { BadDataError } from '../../error';
|
||||||
import { type IBaseEvent, RoleName, TEST_AUDIT_USER } from '../../types';
|
import { type IBaseEvent, RoleName, TEST_AUDIT_USER } from '../../types';
|
||||||
import { createFakeProjectService } from './createProjectService';
|
import { createFakeProjectService } from './createProjectService';
|
||||||
|
import ProjectService from './project-service';
|
||||||
|
|
||||||
describe('enterprise extension: enable change requests', () => {
|
describe('enterprise extension: enable change requests', () => {
|
||||||
const createService = () => {
|
const createService = () => {
|
||||||
@ -300,4 +301,44 @@ describe('enterprise extension: enable change requests', () => {
|
|||||||
),
|
),
|
||||||
).resolves.toBeTruthy();
|
).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',
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1146,8 +1146,8 @@ export default class ProjectService {
|
|||||||
projectId,
|
projectId,
|
||||||
);
|
);
|
||||||
const groups = await this.groupService.getProjectGroups(projectId);
|
const groups = await this.groupService.getProjectGroups(projectId);
|
||||||
const roleGroups = groups.filter(
|
const roleGroups = groups.filter((g) =>
|
||||||
(g) => g.roleId === currentRole.id,
|
g.roles?.includes(currentRole.id),
|
||||||
);
|
);
|
||||||
if (users.length + roleGroups.length < 2) {
|
if (users.length + roleGroups.length < 2) {
|
||||||
throw new ProjectWithoutOwnerError();
|
throw new ProjectWithoutOwnerError();
|
||||||
@ -1264,11 +1264,11 @@ export default class ProjectService {
|
|||||||
auditUser: IAuditUser,
|
auditUser: IAuditUser,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const usersWithRoles = await this.getAccessToProject(projectId);
|
const usersWithRoles = await this.getAccessToProject(projectId);
|
||||||
const user = usersWithRoles.groups.find((u) => u.id === userId);
|
const userGroup = usersWithRoles.groups.find((u) => u.id === userId);
|
||||||
if (!user)
|
if (!userGroup)
|
||||||
throw new ValidationError('Unexpected empty user', [], undefined);
|
throw new ValidationError('Unexpected empty user', [], undefined);
|
||||||
const currentRole = usersWithRoles.roles.find(
|
const currentRole = usersWithRoles.roles.find((r) =>
|
||||||
(r) => r.id === user.roleId,
|
userGroup.roles?.includes(r.id),
|
||||||
);
|
);
|
||||||
if (!currentRole)
|
if (!currentRole)
|
||||||
throw new ValidationError(
|
throw new ValidationError(
|
||||||
|
@ -2,7 +2,6 @@ import type {
|
|||||||
ICreateGroupModel,
|
ICreateGroupModel,
|
||||||
IGroup,
|
IGroup,
|
||||||
IGroupModel,
|
IGroupModel,
|
||||||
IGroupModelWithProjectRole,
|
|
||||||
IGroupProject,
|
IGroupProject,
|
||||||
IGroupRole,
|
IGroupRole,
|
||||||
IGroupUser,
|
IGroupUser,
|
||||||
@ -29,6 +28,7 @@ import type { IAccountStore } from '../types/stores/account-store';
|
|||||||
import type { IUser } from '../types/user';
|
import type { IUser } from '../types/user';
|
||||||
import type EventService from '../features/events/event-service';
|
import type EventService from '../features/events/event-service';
|
||||||
import { SSO_SYNC_USER } from '../db/group-store';
|
import { SSO_SYNC_USER } from '../db/group-store';
|
||||||
|
import type { IGroupWithProjectRoles } from '../types/stores/access-store';
|
||||||
|
|
||||||
const setsAreEqual = (firstSet, secondSet) =>
|
const setsAreEqual = (firstSet, secondSet) =>
|
||||||
firstSet.size === secondSet.size &&
|
firstSet.size === secondSet.size &&
|
||||||
@ -179,7 +179,7 @@ export class GroupService {
|
|||||||
|
|
||||||
async getProjectGroups(
|
async getProjectGroups(
|
||||||
projectId: string,
|
projectId: string,
|
||||||
): Promise<IGroupModelWithProjectRole[]> {
|
): Promise<IGroupWithProjectRoles[]> {
|
||||||
const projectGroups = await this.groupStore.getProjectGroups(projectId);
|
const projectGroups = await this.groupStore.getProjectGroups(projectId);
|
||||||
|
|
||||||
if (projectGroups.length > 0) {
|
if (projectGroups.length > 0) {
|
||||||
|
@ -53,8 +53,7 @@ export interface ICreateGroupUserModel {
|
|||||||
user: Pick<IUser, 'id'>;
|
user: Pick<IUser, 'id'>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IGroupModelWithProjectRole extends IGroupModel {
|
export interface IGroupModelWithAddedAt extends IGroupModel {
|
||||||
roleId: number;
|
|
||||||
addedAt: Date;
|
addedAt: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { PermissionRef } from '../../services/access-service';
|
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 { IPermission, IUserAccessOverview, IUserWithRole } from '../model';
|
||||||
import type { Store } from './store';
|
import type { Store } from './store';
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ export interface IUserWithProjectRoles
|
|||||||
extends IUserWithRole,
|
extends IUserWithRole,
|
||||||
IEntityWithProjectRoles {}
|
IEntityWithProjectRoles {}
|
||||||
export interface IGroupWithProjectRoles
|
export interface IGroupWithProjectRoles
|
||||||
extends IGroupModelWithProjectRole,
|
extends IGroupModelWithAddedAt,
|
||||||
IEntityWithProjectRoles {}
|
IEntityWithProjectRoles {}
|
||||||
|
|
||||||
export interface IAccessStore extends Store<IRole, number> {
|
export interface IAccessStore extends Store<IRole, number> {
|
||||||
|
Loading…
Reference in New Issue
Block a user