mirror of
https://github.com/Unleash/unleash.git
synced 2025-06-27 01:19:00 +02:00
feat: audit roles (#5408)
## About the changes Audit changes to roles both root and project roles.
This commit is contained in:
parent
295b0c073e
commit
d680e50055
@ -14,6 +14,7 @@ import FakeEnvironmentStore from '../../../test/fixtures/fake-environment-store'
|
|||||||
import FakeAccessStore from '../../../test/fixtures/fake-access-store';
|
import FakeAccessStore from '../../../test/fixtures/fake-access-store';
|
||||||
import FeatureTagStore from '../../db/feature-tag-store';
|
import FeatureTagStore from '../../db/feature-tag-store';
|
||||||
import FakeFeatureTagStore from '../../../test/fixtures/fake-feature-tag-store';
|
import FakeFeatureTagStore from '../../../test/fixtures/fake-feature-tag-store';
|
||||||
|
import { IEventStore } from '../../types';
|
||||||
|
|
||||||
export const createAccessService = (
|
export const createAccessService = (
|
||||||
db: Db,
|
db: Db,
|
||||||
@ -38,15 +39,16 @@ export const createAccessService = (
|
|||||||
);
|
);
|
||||||
|
|
||||||
return new AccessService(
|
return new AccessService(
|
||||||
{ accessStore, accountStore, roleStore, environmentStore, groupStore },
|
{ accessStore, accountStore, roleStore, environmentStore },
|
||||||
{ getLogger, flagResolver },
|
{ getLogger, flagResolver },
|
||||||
groupService,
|
groupService,
|
||||||
|
eventService,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createFakeAccessService = (
|
export const createFakeAccessService = (
|
||||||
config: IUnleashConfig,
|
config: IUnleashConfig,
|
||||||
): AccessService => {
|
): { accessService: AccessService; eventStore: IEventStore } => {
|
||||||
const { getLogger, flagResolver } = config;
|
const { getLogger, flagResolver } = config;
|
||||||
const eventStore = new FakeEventStore();
|
const eventStore = new FakeEventStore();
|
||||||
const groupStore = new FakeGroupStore();
|
const groupStore = new FakeGroupStore();
|
||||||
@ -65,9 +67,15 @@ export const createFakeAccessService = (
|
|||||||
eventService,
|
eventService,
|
||||||
);
|
);
|
||||||
|
|
||||||
return new AccessService(
|
const accessService = new AccessService(
|
||||||
{ accessStore, accountStore, roleStore, environmentStore, groupStore },
|
{ accessStore, accountStore, roleStore, environmentStore, groupStore },
|
||||||
{ getLogger, flagResolver },
|
{ getLogger, flagResolver },
|
||||||
groupService,
|
groupService,
|
||||||
|
eventService,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
accessService,
|
||||||
|
eventStore,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
@ -71,7 +71,7 @@ export const createFakeExportImportTogglesService = (
|
|||||||
const eventStore = new FakeEventStore();
|
const eventStore = new FakeEventStore();
|
||||||
const featureStrategiesStore = new FakeFeatureStrategiesStore();
|
const featureStrategiesStore = new FakeFeatureStrategiesStore();
|
||||||
const featureEnvironmentStore = new FakeFeatureEnvironmentStore();
|
const featureEnvironmentStore = new FakeFeatureEnvironmentStore();
|
||||||
const accessService = createFakeAccessService(config);
|
const { accessService } = createFakeAccessService(config);
|
||||||
const featureToggleService = createFakeFeatureToggleService(config);
|
const featureToggleService = createFakeFeatureToggleService(config);
|
||||||
const privateProjectChecker = createFakePrivateProjectChecker();
|
const privateProjectChecker = createFakePrivateProjectChecker();
|
||||||
|
|
||||||
|
@ -110,9 +110,10 @@ export const createFeatureToggleService = (
|
|||||||
eventService,
|
eventService,
|
||||||
);
|
);
|
||||||
const accessService = new AccessService(
|
const accessService = new AccessService(
|
||||||
{ accessStore, accountStore, roleStore, environmentStore, groupStore },
|
{ accessStore, accountStore, roleStore, environmentStore },
|
||||||
{ getLogger, flagResolver },
|
{ getLogger, flagResolver },
|
||||||
groupService,
|
groupService,
|
||||||
|
eventService,
|
||||||
);
|
);
|
||||||
const segmentService = createSegmentService(db, config);
|
const segmentService = createSegmentService(db, config);
|
||||||
const changeRequestAccessReadModel = createChangeRequestAccessReadModel(
|
const changeRequestAccessReadModel = createChangeRequestAccessReadModel(
|
||||||
@ -180,6 +181,7 @@ export const createFakeFeatureToggleService = (
|
|||||||
{ accessStore, accountStore, roleStore, environmentStore, groupStore },
|
{ accessStore, accountStore, roleStore, environmentStore, groupStore },
|
||||||
{ getLogger, flagResolver },
|
{ getLogger, flagResolver },
|
||||||
groupService,
|
groupService,
|
||||||
|
eventService,
|
||||||
);
|
);
|
||||||
const segmentService = createFakeSegmentService(config);
|
const segmentService = createFakeSegmentService(config);
|
||||||
const changeRequestAccessReadModel = createFakeChangeRequestAccessService();
|
const changeRequestAccessReadModel = createFakeChangeRequestAccessService();
|
||||||
|
@ -134,7 +134,7 @@ export const createFakeProjectService = (
|
|||||||
const environmentStore = new FakeEnvironmentStore();
|
const environmentStore = new FakeEnvironmentStore();
|
||||||
const featureEnvironmentStore = new FakeFeatureEnvironmentStore();
|
const featureEnvironmentStore = new FakeFeatureEnvironmentStore();
|
||||||
const projectStatsStore = new FakeProjectStatsStore();
|
const projectStatsStore = new FakeProjectStatsStore();
|
||||||
const accessService = createFakeAccessService(config);
|
const { accessService } = createFakeAccessService(config);
|
||||||
const featureToggleService = createFakeFeatureToggleService(config);
|
const featureToggleService = createFakeFeatureToggleService(config);
|
||||||
const favoriteFeaturesStore = new FakeFavoriteFeaturesStore();
|
const favoriteFeaturesStore = new FakeFavoriteFeaturesStore();
|
||||||
const favoriteProjectsStore = new FakeFavoriteProjectsStore();
|
const favoriteProjectsStore = new FakeFavoriteProjectsStore();
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
import NameExistsError from '../error/name-exists-error';
|
import NameExistsError from '../error/name-exists-error';
|
||||||
import getLogger from '../../test/fixtures/no-logger';
|
import getLogger from '../../test/fixtures/no-logger';
|
||||||
import { createFakeAccessService } from '../features/access/createAccessService';
|
import { createFakeAccessService } from '../features/access/createAccessService';
|
||||||
import { AccessService, IRoleValidation } from './access-service';
|
import {
|
||||||
|
AccessService,
|
||||||
|
IRoleCreation,
|
||||||
|
IRoleValidation,
|
||||||
|
} from './access-service';
|
||||||
import { createTestConfig } from '../../test/config/test-config';
|
import { createTestConfig } from '../../test/config/test-config';
|
||||||
import { CUSTOM_ROOT_ROLE_TYPE } from '../util/constants';
|
import { CUSTOM_ROOT_ROLE_TYPE } from '../util/constants';
|
||||||
import FakeGroupStore from '../../test/fixtures/fake-group-store';
|
import FakeGroupStore from '../../test/fixtures/fake-group-store';
|
||||||
@ -11,8 +15,8 @@ import FakeEnvironmentStore from '../../test/fixtures/fake-environment-store';
|
|||||||
import AccessStoreMock from '../../test/fixtures/fake-access-store';
|
import AccessStoreMock from '../../test/fixtures/fake-access-store';
|
||||||
import { GroupService } from '../services/group-service';
|
import { GroupService } from '../services/group-service';
|
||||||
import FakeEventStore from '../../test/fixtures/fake-event-store';
|
import FakeEventStore from '../../test/fixtures/fake-event-store';
|
||||||
import { IRole } from 'lib/types/stores/access-store';
|
import { IRole } from '../../lib/types/stores/access-store';
|
||||||
import { IGroup } from 'lib/types';
|
import { IGroup, ROLE_CREATED } from '../../lib/types';
|
||||||
import EventService from './event-service';
|
import EventService from './event-service';
|
||||||
import FakeFeatureTagStore from '../../test/fixtures/fake-feature-tag-store';
|
import FakeFeatureTagStore from '../../test/fixtures/fake-feature-tag-store';
|
||||||
|
|
||||||
@ -26,9 +30,7 @@ function getSetup(customRootRolesKillSwitch: boolean = true) {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return createFakeAccessService(config);
|
||||||
accessService: createFakeAccessService(config),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test('should fail when name exists', async () => {
|
test('should fail when name exists', async () => {
|
||||||
@ -164,13 +166,24 @@ test('should be able to validate and cleanup with additional properties', async
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('user with custom root role should get a user root role', async () => {
|
test('user with custom root role should get a user root role', async () => {
|
||||||
const { accessService } = getSetup(false);
|
const { accessService, eventStore } = getSetup(false);
|
||||||
const customRootRole = await accessService.createRole({
|
const createRoleInput: IRoleCreation = {
|
||||||
name: 'custom-root-role',
|
name: 'custom-root-role',
|
||||||
description: 'test custom root role',
|
description: 'test custom root role',
|
||||||
type: CUSTOM_ROOT_ROLE_TYPE,
|
type: CUSTOM_ROOT_ROLE_TYPE,
|
||||||
permissions: [],
|
permissions: [
|
||||||
});
|
{
|
||||||
|
id: 1,
|
||||||
|
environment: 'development',
|
||||||
|
name: 'fake',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'root-fake-permission',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const customRootRole = await accessService.createRole(createRoleInput);
|
||||||
const user = {
|
const user = {
|
||||||
id: 1,
|
id: 1,
|
||||||
rootRole: customRootRole.id,
|
rootRole: customRootRole.id,
|
||||||
@ -180,6 +193,23 @@ test('user with custom root role should get a user root role', async () => {
|
|||||||
const roles = await accessService.getUserRootRoles(user.id);
|
const roles = await accessService.getUserRootRoles(user.id);
|
||||||
expect(roles).toHaveLength(1);
|
expect(roles).toHaveLength(1);
|
||||||
expect(roles[0].name).toBe('custom-root-role');
|
expect(roles[0].name).toBe('custom-root-role');
|
||||||
|
const events = await eventStore.getEvents();
|
||||||
|
expect(events).toHaveLength(1);
|
||||||
|
expect(events[0]).toEqual({
|
||||||
|
type: ROLE_CREATED,
|
||||||
|
createdBy: 'unknown',
|
||||||
|
data: {
|
||||||
|
id: 0,
|
||||||
|
name: 'custom-root-role',
|
||||||
|
description: 'test custom root role',
|
||||||
|
type: CUSTOM_ROOT_ROLE_TYPE,
|
||||||
|
// make sure we have a cleaned up version of permissions in the event
|
||||||
|
permissions: [
|
||||||
|
{ environment: 'development', name: 'fake' },
|
||||||
|
{ name: 'root-fake-permission' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('throws error when trying to delete a project role in use by group', async () => {
|
test('throws error when trying to delete a project role in use by group', async () => {
|
||||||
@ -222,10 +252,10 @@ test('throws error when trying to delete a project role in use by group', async
|
|||||||
accountStore,
|
accountStore,
|
||||||
roleStore,
|
roleStore,
|
||||||
environmentStore,
|
environmentStore,
|
||||||
groupStore,
|
|
||||||
},
|
},
|
||||||
config,
|
config,
|
||||||
groupService,
|
groupService,
|
||||||
|
eventService,
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import * as permissions from '../types/permissions';
|
import * as permissions from '../types/permissions';
|
||||||
import User, { IUser } from '../types/user';
|
import { IUser } from '../types/user';
|
||||||
import {
|
import {
|
||||||
IAccessInfo,
|
IAccessInfo,
|
||||||
IAccessStore,
|
IAccessStore,
|
||||||
@ -14,7 +14,7 @@ import {
|
|||||||
IUserWithProjectRoles,
|
IUserWithProjectRoles,
|
||||||
} from '../types/stores/access-store';
|
} from '../types/stores/access-store';
|
||||||
import { Logger } from '../logger';
|
import { Logger } from '../logger';
|
||||||
import { IAccountStore, IGroupStore, IUnleashStores } from '../types/stores';
|
import { IAccountStore, IUnleashStores } from '../types/stores';
|
||||||
import {
|
import {
|
||||||
IAvailablePermissions,
|
IAvailablePermissions,
|
||||||
ICustomRole,
|
ICustomRole,
|
||||||
@ -23,9 +23,9 @@ import {
|
|||||||
IUserWithRole,
|
IUserWithRole,
|
||||||
RoleName,
|
RoleName,
|
||||||
} from '../types/model';
|
} from '../types/model';
|
||||||
import { IRoleStore } from 'lib/types/stores/role-store';
|
import { IRoleStore } from '../types/stores/role-store';
|
||||||
import NameExistsError from '../error/name-exists-error';
|
import NameExistsError from '../error/name-exists-error';
|
||||||
import { IEnvironmentStore } from 'lib/types/stores/environment-store';
|
import { IEnvironmentStore } from '../types/stores/environment-store';
|
||||||
import RoleInUseError from '../error/role-in-use-error';
|
import RoleInUseError from '../error/role-in-use-error';
|
||||||
import { roleSchema } from '../schema/role-schema';
|
import { roleSchema } from '../schema/role-schema';
|
||||||
import {
|
import {
|
||||||
@ -40,7 +40,15 @@ import InvalidOperationError from '../error/invalid-operation-error';
|
|||||||
import BadDataError from '../error/bad-data-error';
|
import BadDataError from '../error/bad-data-error';
|
||||||
import { IGroup } from '../types/group';
|
import { IGroup } from '../types/group';
|
||||||
import { GroupService } from './group-service';
|
import { GroupService } from './group-service';
|
||||||
import { IFlagResolver, IUnleashConfig, IUserAccessOverview } from 'lib/types';
|
import {
|
||||||
|
IFlagResolver,
|
||||||
|
IUnleashConfig,
|
||||||
|
IUserAccessOverview,
|
||||||
|
ROLE_CREATED,
|
||||||
|
ROLE_DELETED,
|
||||||
|
ROLE_UPDATED,
|
||||||
|
} from '../types';
|
||||||
|
import EventService from './event-service';
|
||||||
|
|
||||||
const { ADMIN } = permissions;
|
const { ADMIN } = permissions;
|
||||||
|
|
||||||
@ -57,11 +65,12 @@ export type IdPermissionRef = Pick<IPermission, 'id' | 'environment'>;
|
|||||||
export type NamePermissionRef = Pick<IPermission, 'name' | 'environment'>;
|
export type NamePermissionRef = Pick<IPermission, 'name' | 'environment'>;
|
||||||
export type PermissionRef = IdPermissionRef | NamePermissionRef;
|
export type PermissionRef = IdPermissionRef | NamePermissionRef;
|
||||||
|
|
||||||
interface IRoleCreation {
|
export interface IRoleCreation {
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
type?: 'root-custom' | 'custom';
|
type?: 'root-custom' | 'custom';
|
||||||
permissions?: PermissionRef[];
|
permissions?: PermissionRef[];
|
||||||
|
createdBy?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IRoleValidation {
|
export interface IRoleValidation {
|
||||||
@ -76,6 +85,7 @@ export interface IRoleUpdate {
|
|||||||
description: string;
|
description: string;
|
||||||
type?: 'root-custom' | 'custom';
|
type?: 'root-custom' | 'custom';
|
||||||
permissions?: PermissionRef[];
|
permissions?: PermissionRef[];
|
||||||
|
createdBy?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AccessWithRoles {
|
export interface AccessWithRoles {
|
||||||
@ -95,34 +105,30 @@ export class AccessService {
|
|||||||
|
|
||||||
private groupService: GroupService;
|
private groupService: GroupService;
|
||||||
|
|
||||||
private groupStore: IGroupStore;
|
|
||||||
|
|
||||||
private environmentStore: IEnvironmentStore;
|
private environmentStore: IEnvironmentStore;
|
||||||
|
|
||||||
private logger: Logger;
|
private logger: Logger;
|
||||||
|
|
||||||
private flagResolver: IFlagResolver;
|
private flagResolver: IFlagResolver;
|
||||||
|
|
||||||
|
private eventService: EventService;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
{
|
{
|
||||||
accessStore,
|
accessStore,
|
||||||
accountStore,
|
accountStore,
|
||||||
roleStore,
|
roleStore,
|
||||||
environmentStore,
|
environmentStore,
|
||||||
groupStore,
|
|
||||||
}: Pick<
|
}: Pick<
|
||||||
IUnleashStores,
|
IUnleashStores,
|
||||||
| 'accessStore'
|
'accessStore' | 'accountStore' | 'roleStore' | 'environmentStore'
|
||||||
| 'accountStore'
|
> & { groupStore?: any }, // TODO remove groupStore later, kept for backward compatibility with enterprise
|
||||||
| 'roleStore'
|
|
||||||
| 'environmentStore'
|
|
||||||
| 'groupStore'
|
|
||||||
>,
|
|
||||||
{
|
{
|
||||||
getLogger,
|
getLogger,
|
||||||
flagResolver,
|
flagResolver,
|
||||||
}: Pick<IUnleashConfig, 'getLogger' | 'flagResolver'>,
|
}: Pick<IUnleashConfig, 'getLogger' | 'flagResolver'>,
|
||||||
groupService: GroupService,
|
groupService: GroupService,
|
||||||
|
eventService: EventService,
|
||||||
) {
|
) {
|
||||||
this.store = accessStore;
|
this.store = accessStore;
|
||||||
this.accountStore = accountStore;
|
this.accountStore = accountStore;
|
||||||
@ -131,7 +137,7 @@ export class AccessService {
|
|||||||
this.environmentStore = environmentStore;
|
this.environmentStore = environmentStore;
|
||||||
this.logger = getLogger('/services/access-service.ts');
|
this.logger = getLogger('/services/access-service.ts');
|
||||||
this.flagResolver = flagResolver;
|
this.flagResolver = flagResolver;
|
||||||
this.groupStore = groupStore;
|
this.eventService = eventService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -483,7 +489,7 @@ export class AccessService {
|
|||||||
async getGroupsForRole(roleId: number): Promise<IGroup[]> {
|
async getGroupsForRole(roleId: number): Promise<IGroup[]> {
|
||||||
const groupdIdList = await this.store.getGroupIdsForRole(roleId);
|
const groupdIdList = await this.store.getGroupIdsForRole(roleId);
|
||||||
if (groupdIdList.length > 0) {
|
if (groupdIdList.length > 0) {
|
||||||
return this.groupStore.getAllWithId(groupdIdList);
|
return this.groupService.getAllWithId(groupdIdList);
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@ -643,6 +649,17 @@ export class AccessService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const addedPermissions = await this.store.getPermissionsForRole(
|
||||||
|
newRole.id,
|
||||||
|
);
|
||||||
|
this.eventService.storeEvent({
|
||||||
|
type: ROLE_CREATED,
|
||||||
|
createdBy: role.createdBy || 'unknown',
|
||||||
|
data: {
|
||||||
|
...newRole,
|
||||||
|
permissions: this.sanitizePermissions(addedPermissions),
|
||||||
|
},
|
||||||
|
});
|
||||||
return newRole;
|
return newRole;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -662,6 +679,7 @@ export class AccessService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await this.validateRole(role, role.id);
|
await this.validateRole(role, role.id);
|
||||||
|
const existingRole = await this.roleStore.get(role.id);
|
||||||
const baseRole = {
|
const baseRole = {
|
||||||
id: role.id,
|
id: role.id,
|
||||||
name: role.name,
|
name: role.name,
|
||||||
@ -669,25 +687,55 @@ export class AccessService {
|
|||||||
roleType,
|
roleType,
|
||||||
};
|
};
|
||||||
const rolePermissions = role.permissions;
|
const rolePermissions = role.permissions;
|
||||||
const newRole = await this.roleStore.update(baseRole);
|
const updatedRole = await this.roleStore.update(baseRole);
|
||||||
|
const existingPermissions = await this.store.getPermissionsForRole(
|
||||||
|
role.id,
|
||||||
|
);
|
||||||
if (rolePermissions) {
|
if (rolePermissions) {
|
||||||
await this.store.wipePermissionsFromRole(newRole.id);
|
await this.store.wipePermissionsFromRole(updatedRole.id);
|
||||||
if (roleType === CUSTOM_ROOT_ROLE_TYPE) {
|
if (roleType === CUSTOM_ROOT_ROLE_TYPE) {
|
||||||
await this.store.addPermissionsToRole(
|
await this.store.addPermissionsToRole(
|
||||||
newRole.id,
|
updatedRole.id,
|
||||||
rolePermissions,
|
rolePermissions,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
await this.store.addEnvironmentPermissionsToRole(
|
await this.store.addEnvironmentPermissionsToRole(
|
||||||
newRole.id,
|
updatedRole.id,
|
||||||
rolePermissions,
|
rolePermissions,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return newRole;
|
const updatedPermissions = await this.store.getPermissionsForRole(
|
||||||
|
role.id,
|
||||||
|
);
|
||||||
|
this.eventService.storeEvent({
|
||||||
|
type: ROLE_UPDATED,
|
||||||
|
createdBy: role.createdBy || 'unknown',
|
||||||
|
data: {
|
||||||
|
...updatedRole,
|
||||||
|
permissions: this.sanitizePermissions(updatedPermissions),
|
||||||
|
},
|
||||||
|
preData: {
|
||||||
|
...existingRole,
|
||||||
|
permissions: this.sanitizePermissions(existingPermissions),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return updatedRole;
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteRole(id: number): Promise<void> {
|
sanitizePermissions(
|
||||||
|
permissions: IPermission[],
|
||||||
|
): { name: string; environment?: string }[] {
|
||||||
|
return permissions.map(({ name, environment }) => {
|
||||||
|
const sanitizedEnvironment =
|
||||||
|
environment && environment !== null && environment !== ''
|
||||||
|
? environment
|
||||||
|
: undefined;
|
||||||
|
return { name, environment: sanitizedEnvironment };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteRole(id: number, deletedBy = 'unknown'): Promise<void> {
|
||||||
await this.validateRoleIsNotBuiltIn(id);
|
await this.validateRoleIsNotBuiltIn(id);
|
||||||
|
|
||||||
const roleUsers = await this.getUsersForRole(id);
|
const roleUsers = await this.getUsersForRole(id);
|
||||||
@ -699,7 +747,18 @@ export class AccessService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.roleStore.delete(id);
|
const existingRole = await this.roleStore.get(id);
|
||||||
|
const existingPermissions = await this.store.getPermissionsForRole(id);
|
||||||
|
await this.roleStore.delete(id);
|
||||||
|
this.eventService.storeEvent({
|
||||||
|
type: ROLE_DELETED,
|
||||||
|
createdBy: deletedBy,
|
||||||
|
preData: {
|
||||||
|
...existingRole,
|
||||||
|
permissions: this.sanitizePermissions(existingPermissions),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
async validateRoleIsUnique(
|
async validateRoleIsUnique(
|
||||||
|
@ -41,7 +41,7 @@ export class EmailService {
|
|||||||
|
|
||||||
private readonly sender: string;
|
private readonly sender: string;
|
||||||
|
|
||||||
constructor(email: IEmailOption, getLogger: LogProvider) {
|
constructor(email: IEmailOption | undefined, getLogger: LogProvider) {
|
||||||
this.logger = getLogger('services/email-service.ts');
|
this.logger = getLogger('services/email-service.ts');
|
||||||
if (email?.host) {
|
if (email?.host) {
|
||||||
this.sender = email.sender;
|
this.sender = email.sender;
|
||||||
@ -101,7 +101,7 @@ export class EmailService {
|
|||||||
text: bodyText,
|
text: bodyText,
|
||||||
};
|
};
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
this.mailer.sendMail(email).then(
|
this.mailer!.sendMail(email).then(
|
||||||
() =>
|
() =>
|
||||||
this.logger.info(
|
this.logger.info(
|
||||||
'Successfully sent reset-password email',
|
'Successfully sent reset-password email',
|
||||||
@ -162,7 +162,7 @@ export class EmailService {
|
|||||||
text: bodyText,
|
text: bodyText,
|
||||||
};
|
};
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
this.mailer.sendMail(email).then(
|
this.mailer!.sendMail(email).then(
|
||||||
() =>
|
() =>
|
||||||
this.logger.info(
|
this.logger.info(
|
||||||
'Successfully sent getting started email',
|
'Successfully sent getting started email',
|
||||||
|
@ -59,6 +59,10 @@ export class GroupService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getAllWithId(ids: number[]) {
|
||||||
|
return this.groupStore.getAllWithId(ids);
|
||||||
|
}
|
||||||
|
|
||||||
mapGroupWithProjects(
|
mapGroupWithProjects(
|
||||||
groupProjects: IGroupProject[],
|
groupProjects: IGroupProject[],
|
||||||
group: IGroupModel,
|
group: IGroupModel,
|
||||||
|
@ -106,7 +106,12 @@ export const createServices = (
|
|||||||
): IUnleashServices => {
|
): IUnleashServices => {
|
||||||
const eventService = new EventService(stores, config);
|
const eventService = new EventService(stores, config);
|
||||||
const groupService = new GroupService(stores, config, eventService);
|
const groupService = new GroupService(stores, config, eventService);
|
||||||
const accessService = new AccessService(stores, config, groupService);
|
const accessService = new AccessService(
|
||||||
|
stores,
|
||||||
|
config,
|
||||||
|
groupService,
|
||||||
|
eventService,
|
||||||
|
);
|
||||||
const apiTokenService = new ApiTokenService(stores, config, eventService);
|
const apiTokenService = new ApiTokenService(stores, config, eventService);
|
||||||
const lastSeenService = db
|
const lastSeenService = db
|
||||||
? createLastSeenService(db, config)
|
? createLastSeenService(db, config)
|
||||||
|
@ -62,6 +62,10 @@ export const PROJECT_ACCESS_USER_ROLES_DELETED =
|
|||||||
export const PROJECT_ACCESS_GROUP_ROLES_DELETED =
|
export const PROJECT_ACCESS_GROUP_ROLES_DELETED =
|
||||||
'project-access-group-roles-deleted';
|
'project-access-group-roles-deleted';
|
||||||
|
|
||||||
|
export const ROLE_CREATED = 'role-created';
|
||||||
|
export const ROLE_UPDATED = 'role-updated';
|
||||||
|
export const ROLE_DELETED = 'role-deleted';
|
||||||
|
|
||||||
export const PROJECT_CREATED = 'project-created' as const;
|
export const PROJECT_CREATED = 'project-created' as const;
|
||||||
export const PROJECT_UPDATED = 'project-updated' as const;
|
export const PROJECT_UPDATED = 'project-updated' as const;
|
||||||
export const PROJECT_DELETED = 'project-deleted' as const;
|
export const PROJECT_DELETED = 'project-deleted' as const;
|
||||||
@ -208,6 +212,9 @@ export const IEventTypes = [
|
|||||||
PROJECT_GROUP_ROLE_CHANGED,
|
PROJECT_GROUP_ROLE_CHANGED,
|
||||||
PROJECT_GROUP_ADDED,
|
PROJECT_GROUP_ADDED,
|
||||||
PROJECT_GROUP_REMOVED,
|
PROJECT_GROUP_REMOVED,
|
||||||
|
ROLE_CREATED,
|
||||||
|
ROLE_UPDATED,
|
||||||
|
ROLE_DELETED,
|
||||||
DROP_PROJECTS,
|
DROP_PROJECTS,
|
||||||
TAG_CREATED,
|
TAG_CREATED,
|
||||||
TAG_DELETED,
|
TAG_DELETED,
|
||||||
|
@ -51,7 +51,12 @@ beforeAll(async () => {
|
|||||||
app = await setupApp(stores);
|
app = await setupApp(stores);
|
||||||
const eventService = new EventService(stores, config);
|
const eventService = new EventService(stores, config);
|
||||||
const groupService = new GroupService(stores, config, eventService);
|
const groupService = new GroupService(stores, config, eventService);
|
||||||
accessService = new AccessService(stores, config, groupService);
|
accessService = new AccessService(
|
||||||
|
stores,
|
||||||
|
config,
|
||||||
|
groupService,
|
||||||
|
eventService,
|
||||||
|
);
|
||||||
const emailService = new EmailService(config.email, config.getLogger);
|
const emailService = new EmailService(config.email, config.getLogger);
|
||||||
const sessionStore = new SessionStore(
|
const sessionStore = new SessionStore(
|
||||||
db,
|
db,
|
||||||
|
@ -37,9 +37,13 @@ beforeAll(async () => {
|
|||||||
app = await setupApp(stores);
|
app = await setupApp(stores);
|
||||||
const eventService = new EventService(stores, config);
|
const eventService = new EventService(stores, config);
|
||||||
const groupService = new GroupService(stores, config, eventService);
|
const groupService = new GroupService(stores, config, eventService);
|
||||||
const accessService = new AccessService(stores, config, groupService);
|
const accessService = new AccessService(
|
||||||
|
stores,
|
||||||
|
config,
|
||||||
|
groupService,
|
||||||
|
eventService,
|
||||||
|
);
|
||||||
const resetTokenService = new ResetTokenService(stores, config);
|
const resetTokenService = new ResetTokenService(stores, config);
|
||||||
// @ts-ignore
|
|
||||||
const emailService = new EmailService(undefined, config.getLogger);
|
const emailService = new EmailService(undefined, config.getLogger);
|
||||||
const sessionService = new SessionService(stores, config);
|
const sessionService = new SessionService(stores, config);
|
||||||
const settingService = new SettingService(stores, config, eventService);
|
const settingService = new SettingService(stores, config, eventService);
|
||||||
|
@ -30,7 +30,12 @@ beforeAll(async () => {
|
|||||||
stores = db.stores;
|
stores = db.stores;
|
||||||
const eventService = new EventService(stores, config);
|
const eventService = new EventService(stores, config);
|
||||||
const groupService = new GroupService(stores, config, eventService);
|
const groupService = new GroupService(stores, config, eventService);
|
||||||
accessService = new AccessService(stores, config, groupService);
|
accessService = new AccessService(
|
||||||
|
stores,
|
||||||
|
config,
|
||||||
|
groupService,
|
||||||
|
eventService,
|
||||||
|
);
|
||||||
resetTokenService = new ResetTokenService(stores, config);
|
resetTokenService = new ResetTokenService(stores, config);
|
||||||
sessionService = new SessionService(stores, config);
|
sessionService = new SessionService(stores, config);
|
||||||
const emailService = new EmailService(undefined, config.getLogger);
|
const emailService = new EmailService(undefined, config.getLogger);
|
||||||
|
@ -34,7 +34,12 @@ beforeAll(async () => {
|
|||||||
const config = createTestConfig();
|
const config = createTestConfig();
|
||||||
const eventService = new EventService(stores, config);
|
const eventService = new EventService(stores, config);
|
||||||
const groupService = new GroupService(stores, config, eventService);
|
const groupService = new GroupService(stores, config, eventService);
|
||||||
const accessService = new AccessService(stores, config, groupService);
|
const accessService = new AccessService(
|
||||||
|
stores,
|
||||||
|
config,
|
||||||
|
groupService,
|
||||||
|
eventService,
|
||||||
|
);
|
||||||
const resetTokenService = new ResetTokenService(stores, config);
|
const resetTokenService = new ResetTokenService(stores, config);
|
||||||
const emailService = new EmailService(undefined, config.getLogger);
|
const emailService = new EmailService(undefined, config.getLogger);
|
||||||
sessionService = new SessionService(stores, config);
|
sessionService = new SessionService(stores, config);
|
||||||
|
2
src/test/fixtures/access-service-mock.ts
vendored
2
src/test/fixtures/access-service-mock.ts
vendored
@ -17,10 +17,10 @@ class AccessServiceMock extends AccessService {
|
|||||||
accountStore: undefined,
|
accountStore: undefined,
|
||||||
roleStore: undefined,
|
roleStore: undefined,
|
||||||
environmentStore: undefined,
|
environmentStore: undefined,
|
||||||
groupStore: undefined,
|
|
||||||
},
|
},
|
||||||
{ getLogger: noLoggerProvider, flagResolver: undefined },
|
{ getLogger: noLoggerProvider, flagResolver: undefined },
|
||||||
undefined,
|
undefined,
|
||||||
|
undefined,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
13
src/test/fixtures/fake-access-store.ts
vendored
13
src/test/fixtures/fake-access-store.ts
vendored
@ -13,12 +13,15 @@ import { IPermission } from 'lib/types/model';
|
|||||||
import { IRoleStore, IUserAccessOverview } from 'lib/types';
|
import { IRoleStore, IUserAccessOverview } from 'lib/types';
|
||||||
import FakeRoleStore from './fake-role-store';
|
import FakeRoleStore from './fake-role-store';
|
||||||
import { PermissionRef } from 'lib/services/access-service';
|
import { PermissionRef } from 'lib/services/access-service';
|
||||||
|
import { P } from 'ts-toolbelt/out/Object/_api';
|
||||||
|
|
||||||
class AccessStoreMock implements IAccessStore {
|
class AccessStoreMock implements IAccessStore {
|
||||||
fakeRolesStore: IRoleStore;
|
fakeRolesStore: IRoleStore;
|
||||||
|
|
||||||
userToRoleMap: Map<number, number> = new Map();
|
userToRoleMap: Map<number, number> = new Map();
|
||||||
|
|
||||||
|
rolePermissions: Map<number, IPermission[]> = new Map();
|
||||||
|
|
||||||
constructor(roleStore?: IRoleStore) {
|
constructor(roleStore?: IRoleStore) {
|
||||||
this.fakeRolesStore = roleStore ?? new FakeRoleStore();
|
this.fakeRolesStore = roleStore ?? new FakeRoleStore();
|
||||||
}
|
}
|
||||||
@ -133,7 +136,8 @@ class AccessStoreMock implements IAccessStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getPermissionsForRole(roleId: number): Promise<IPermission[]> {
|
getPermissionsForRole(roleId: number): Promise<IPermission[]> {
|
||||||
throw new Error('Method not implemented.');
|
const found = this.rolePermissions.get(roleId) ?? [];
|
||||||
|
return Promise.resolve(found);
|
||||||
}
|
}
|
||||||
|
|
||||||
getRoles(): Promise<IRole[]> {
|
getRoles(): Promise<IRole[]> {
|
||||||
@ -183,7 +187,12 @@ class AccessStoreMock implements IAccessStore {
|
|||||||
permissions: PermissionRef[],
|
permissions: PermissionRef[],
|
||||||
environment?: string,
|
environment?: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// do nothing for now
|
this.rolePermissions.set(
|
||||||
|
role_id,
|
||||||
|
(environment
|
||||||
|
? permissions.map((p) => ({ ...p, environment }))
|
||||||
|
: permissions) as IPermission[],
|
||||||
|
);
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1
src/test/fixtures/fake-role-store.ts
vendored
1
src/test/fixtures/fake-role-store.ts
vendored
@ -42,6 +42,7 @@ export default class FakeRoleStore implements IRoleStore {
|
|||||||
...role,
|
...role,
|
||||||
type: role.roleType,
|
type: role.roleType,
|
||||||
id: this.roles.length,
|
id: this.roles.length,
|
||||||
|
roleType: undefined, // roleType is not part of ICustomRole and simulates what the DB responds
|
||||||
};
|
};
|
||||||
this.roles.push(roleCreated);
|
this.roles.push(roleCreated);
|
||||||
return Promise.resolve(roleCreated);
|
return Promise.resolve(roleCreated);
|
||||||
|
Loading…
Reference in New Issue
Block a user