1
0
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:
Gastón Fournier 2023-11-24 14:22:31 +01:00 committed by GitHub
parent 295b0c073e
commit d680e50055
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 197 additions and 53 deletions

View File

@ -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,
};
}; };

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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 {

View File

@ -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(

View File

@ -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',

View File

@ -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,

View File

@ -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)

View File

@ -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,

View File

@ -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,

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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,
); );
} }

View File

@ -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);
} }

View File

@ -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);