mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-21 13:47:39 +02:00
feat: Add api endpoints for roles and permissions list
This commit is contained in:
parent
817f960765
commit
cb02ae9c92
@ -9,13 +9,26 @@ import {
|
||||
IUserPermission,
|
||||
IUserRole,
|
||||
} from '../types/stores/access-store';
|
||||
import {
|
||||
IAvailablePermissions,
|
||||
IEnvironmentPermission,
|
||||
IPermission,
|
||||
} from 'lib/types/model';
|
||||
|
||||
const T = {
|
||||
ROLE_USER: 'role_user',
|
||||
ROLES: 'role_project',
|
||||
ROLES: 'roles',
|
||||
ROLE_PERMISSION: 'role_permission',
|
||||
PERMISSIONS: 'permissions',
|
||||
};
|
||||
|
||||
interface IPermissionRow {
|
||||
id: number;
|
||||
permission: string;
|
||||
display_name: string;
|
||||
environment: string;
|
||||
}
|
||||
|
||||
export class AccessStore implements IAccessStore {
|
||||
private logger: Logger;
|
||||
|
||||
@ -33,6 +46,20 @@ export class AccessStore implements IAccessStore {
|
||||
});
|
||||
}
|
||||
|
||||
async setupPermissionsForEnvironment(
|
||||
environmentName: string,
|
||||
permissions: string[],
|
||||
): Promise<void> {
|
||||
const rows = permissions.map((permission) => {
|
||||
return {
|
||||
permission: permission,
|
||||
display_name: '',
|
||||
environment: environmentName,
|
||||
};
|
||||
});
|
||||
await this.db.batchInsert(T.PERMISSIONS, rows);
|
||||
}
|
||||
|
||||
async delete(key: number): Promise<void> {
|
||||
await this.db(T.ROLES).where({ id: key }).del();
|
||||
}
|
||||
@ -64,12 +91,56 @@ export class AccessStore implements IAccessStore {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
async getAvailablePermissions(): Promise<IAvailablePermissions> {
|
||||
const rows = await this.db
|
||||
.select(['id', 'permission', 'environment', 'display_name'])
|
||||
.from(T.PERMISSIONS);
|
||||
|
||||
let projectPermissions: IPermission[] = [];
|
||||
let rawEnvironments = new Map<string, IPermissionRow[]>();
|
||||
|
||||
for (let permission of rows) {
|
||||
if (!permission.environment) {
|
||||
projectPermissions.push(this.mapPermission(permission));
|
||||
} else {
|
||||
if (!rawEnvironments.get(permission.environment)) {
|
||||
rawEnvironments.set(permission.environment, []);
|
||||
}
|
||||
rawEnvironments.get(permission.environment).push(permission);
|
||||
}
|
||||
}
|
||||
let allEnvironmentPermissions: Array<IEnvironmentPermission> =
|
||||
Array.from(rawEnvironments).map(
|
||||
([environmentName, environmentPermissions]) => {
|
||||
return {
|
||||
environmentName: environmentName,
|
||||
permissions: environmentPermissions.map(
|
||||
this.mapPermission,
|
||||
),
|
||||
};
|
||||
},
|
||||
);
|
||||
return {
|
||||
project: projectPermissions,
|
||||
environments: allEnvironmentPermissions,
|
||||
};
|
||||
}
|
||||
|
||||
mapPermission(permission: IPermissionRow): IPermission {
|
||||
return {
|
||||
id: permission.id,
|
||||
name: permission.permission,
|
||||
displayName: permission.display_name,
|
||||
};
|
||||
}
|
||||
|
||||
async getPermissionsForUser(userId: number): Promise<IUserPermission[]> {
|
||||
const stopTimer = this.timer('getPermissionsForUser');
|
||||
const rows = await this.db
|
||||
.select('project', 'permission', 'environment')
|
||||
.from<IUserPermission>(`${T.ROLE_PERMISSION} AS rp`)
|
||||
.leftJoin(`${T.ROLE_USER} AS ur`, 'ur.role_id', 'rp.role_id')
|
||||
.join(`${T.ROLE_USER} AS ur`, 'ur.role_id', 'rp.role_id')
|
||||
.join(`${T.PERMISSIONS} AS p`, 'p.id', 'rp.permission_id')
|
||||
.where('ur.user_id', '=', userId);
|
||||
stopTimer();
|
||||
return rows;
|
||||
@ -109,7 +180,7 @@ export class AccessStore implements IAccessStore {
|
||||
|
||||
async getRootRoles(): Promise<IRole[]> {
|
||||
return this.db
|
||||
.select(['id', 'name', 'type', 'project', 'description'])
|
||||
.select(['id', 'name', 'type', 'description'])
|
||||
.from<IRole>(T.ROLES)
|
||||
.andWhere('type', 'root');
|
||||
}
|
||||
@ -138,10 +209,15 @@ export class AccessStore implements IAccessStore {
|
||||
return rows.map((r) => r.user_id);
|
||||
}
|
||||
|
||||
async addUserToRole(userId: number, roleId: number): Promise<void> {
|
||||
async addUserToRole(
|
||||
userId: number,
|
||||
roleId: number,
|
||||
projecId: string,
|
||||
): Promise<void> {
|
||||
return this.db(T.ROLE_USER).insert({
|
||||
user_id: userId,
|
||||
role_id: roleId,
|
||||
project: projecId,
|
||||
});
|
||||
}
|
||||
|
||||
@ -171,7 +247,6 @@ export class AccessStore implements IAccessStore {
|
||||
async createRole(
|
||||
name: string,
|
||||
type: string,
|
||||
project?: string,
|
||||
description?: string,
|
||||
): Promise<IRole> {
|
||||
const [id] = await this.db(T.ROLES)
|
||||
@ -179,7 +254,6 @@ export class AccessStore implements IAccessStore {
|
||||
name,
|
||||
description,
|
||||
type,
|
||||
project,
|
||||
})
|
||||
.returning('id');
|
||||
return {
|
||||
@ -187,22 +261,25 @@ export class AccessStore implements IAccessStore {
|
||||
name,
|
||||
description,
|
||||
type,
|
||||
project,
|
||||
};
|
||||
}
|
||||
|
||||
async addPermissionsToRole(
|
||||
role_id: number,
|
||||
permissions: string[],
|
||||
projectId?: string,
|
||||
environment?: string,
|
||||
): Promise<void> {
|
||||
const rows = permissions.map((permission) => ({
|
||||
const result = await this.db.raw(
|
||||
`SELECT id FROM ${T.PERMISSIONS} where environment = ? and permission = ANY(?)`,
|
||||
[environment, permissions],
|
||||
);
|
||||
const ids = result.rows.map((x) => x.id);
|
||||
|
||||
const rows = ids.map((permission_id) => ({
|
||||
role_id,
|
||||
project: projectId,
|
||||
permission,
|
||||
environment,
|
||||
permission_id,
|
||||
}));
|
||||
|
||||
return this.db.batchInsert(T.ROLE_PERMISSION, rows);
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,13 @@ export default class RoleStore {
|
||||
}
|
||||
|
||||
async create(role: ICustomRoleInsert): Promise<ICustomRole> {
|
||||
const row = await this.db(TABLE).insert(role).returning('*');
|
||||
const row = await this.db(TABLE)
|
||||
.insert({
|
||||
name: role.name,
|
||||
description: role.description,
|
||||
type: role.roleType,
|
||||
})
|
||||
.returning('*');
|
||||
return this.mapRow(row[0]);
|
||||
}
|
||||
|
||||
@ -47,12 +53,7 @@ export default class RoleStore {
|
||||
}
|
||||
|
||||
async get(id: number): Promise<ICustomRole> {
|
||||
const rows = await this.db
|
||||
.select(COLUMNS)
|
||||
.from(TABLE)
|
||||
.where({ id })
|
||||
.orderBy('name', 'asc');
|
||||
|
||||
const rows = await this.db.select(COLUMNS).from(TABLE).where({ id });
|
||||
return this.mapRow(rows[0]);
|
||||
}
|
||||
|
||||
|
@ -10,10 +10,10 @@ import { IUserStore } from '../types/stores/user-store';
|
||||
import { Logger } from '../logger';
|
||||
import { IUnleashStores } from '../types/stores';
|
||||
import {
|
||||
IAvailablePermissions,
|
||||
IPermission,
|
||||
IRoleData,
|
||||
IUserWithRole,
|
||||
PermissionType,
|
||||
RoleName,
|
||||
RoleType,
|
||||
} from '../types/model';
|
||||
@ -63,12 +63,12 @@ export class AccessService {
|
||||
this.store = accessStore;
|
||||
this.userStore = userStore;
|
||||
this.logger = getLogger('/services/access-service.ts');
|
||||
this.permissions = Object.values(permissions).map((p) => ({
|
||||
name: p,
|
||||
type: isProjectPermission(p)
|
||||
? PermissionType.project
|
||||
: PermissionType.root,
|
||||
}));
|
||||
// this.permissions = Object.values(permissions).map((p) => ({
|
||||
// name: p,
|
||||
// type: isProjectPermission(p)
|
||||
// ? PermissionType.project
|
||||
// : PermissionType.root,
|
||||
// }));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -90,6 +90,8 @@ export class AccessService {
|
||||
|
||||
try {
|
||||
const userP = await this.getPermissionsForUser(user);
|
||||
console.log('My checks are', permission, projectId, environment);
|
||||
console.log('My permissions for user are', userP);
|
||||
|
||||
return userP
|
||||
.filter(
|
||||
@ -126,12 +128,16 @@ export class AccessService {
|
||||
return this.store.getPermissionsForUser(user.id);
|
||||
}
|
||||
|
||||
getPermissions(): IPermission[] {
|
||||
return this.permissions;
|
||||
async getPermissions(): Promise<IAvailablePermissions> {
|
||||
return this.store.getAvailablePermissions();
|
||||
}
|
||||
|
||||
async addUserToRole(userId: number, roleId: number): Promise<void> {
|
||||
return this.store.addUserToRole(userId, roleId);
|
||||
async addUserToRole(
|
||||
userId: number,
|
||||
roleId: number,
|
||||
projectId: string,
|
||||
): Promise<void> {
|
||||
return this.store.addUserToRole(userId, roleId, projectId);
|
||||
}
|
||||
|
||||
async setUserRootRole(
|
||||
@ -139,14 +145,17 @@ export class AccessService {
|
||||
role: number | RoleName,
|
||||
): Promise<void> {
|
||||
const newRootRole = await this.resolveRootRole(role);
|
||||
|
||||
if (newRootRole) {
|
||||
try {
|
||||
await this.store.removeRolesOfTypeForUser(
|
||||
userId,
|
||||
RoleType.ROOT,
|
||||
);
|
||||
await this.store.addUserToRole(userId, newRootRole.id);
|
||||
await this.store.addUserToRole(
|
||||
userId,
|
||||
newRootRole.id,
|
||||
ALL_PROJECTS,
|
||||
);
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Could not add role=${newRootRole.name} to userId=${userId}`,
|
||||
@ -251,7 +260,6 @@ export class AccessService {
|
||||
const ownerRole = await this.store.createRole(
|
||||
RoleName.OWNER,
|
||||
RoleType.PROJECT,
|
||||
projectId,
|
||||
PROJECT_DESCRIPTION.OWNER,
|
||||
);
|
||||
await this.store.addPermissionsToRole(
|
||||
@ -265,7 +273,7 @@ export class AccessService {
|
||||
this.logger.info(
|
||||
`Making ${owner.id} admin of ${projectId} via roleId=${ownerRole.id}`,
|
||||
);
|
||||
await this.store.addUserToRole(owner.id, ownerRole.id);
|
||||
await this.store.addUserToRole(owner.id, ownerRole.id, projectId);
|
||||
}
|
||||
const memberRole = await this.store.createRole(
|
||||
RoleName.MEMBER,
|
||||
|
@ -300,7 +300,7 @@ export default class ProjectService {
|
||||
throw new Error(`User already has access to project=${projectId}`);
|
||||
}
|
||||
|
||||
await this.accessService.addUserToRole(userId, role.id);
|
||||
await this.accessService.addUserToRole(userId, role.id, projectId);
|
||||
}
|
||||
|
||||
// TODO: should be an event too
|
||||
|
@ -21,6 +21,10 @@ export default class RoleService {
|
||||
return this.store.getAll();
|
||||
}
|
||||
|
||||
async get(id: number): Promise<ICustomRole> {
|
||||
return this.store.get(id);
|
||||
}
|
||||
|
||||
async create(role: ICustomRoleInsert): Promise<ICustomRole> {
|
||||
return this.store.create(role);
|
||||
}
|
||||
|
@ -133,14 +133,19 @@ class UserService {
|
||||
const user = await this.store.insert({
|
||||
username: 'admin',
|
||||
});
|
||||
this.logger.info(`Did the thing"`);
|
||||
const passwordHash = await bcrypt.hash(pwd, saltRounds);
|
||||
this.logger.info(`Bcrypted that beast"`);
|
||||
await this.store.setPasswordHash(user.id, passwordHash);
|
||||
|
||||
this.logger.info(`Set the hash"`);
|
||||
await this.accessService.setUserRootRole(
|
||||
user.id,
|
||||
RoleName.ADMIN,
|
||||
);
|
||||
} catch (e) {
|
||||
console.log(`My error ${e}`);
|
||||
console.log(e);
|
||||
this.logger.error(e);
|
||||
this.logger.error('Unable to create default user "admin"');
|
||||
}
|
||||
}
|
||||
|
@ -215,9 +215,20 @@ export interface IRoleData {
|
||||
permissions: IUserPermission[];
|
||||
}
|
||||
|
||||
export interface IAvailablePermissions {
|
||||
project: IPermission[];
|
||||
environments: IEnvironmentPermission[];
|
||||
}
|
||||
|
||||
export interface IPermission {
|
||||
id: number;
|
||||
name: string;
|
||||
type: PermissionType;
|
||||
displayName: string;
|
||||
}
|
||||
|
||||
export interface IEnvironmentPermission {
|
||||
environmentName: string;
|
||||
permissions: IPermission[];
|
||||
}
|
||||
|
||||
export enum PermissionType {
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { IAvailablePermissions } from '../model';
|
||||
import { Store } from './store';
|
||||
|
||||
export interface IUserPermission {
|
||||
@ -11,7 +12,6 @@ export interface IRole {
|
||||
name: string;
|
||||
description?: string;
|
||||
type: string;
|
||||
project?: string;
|
||||
}
|
||||
|
||||
export interface IUserRole {
|
||||
@ -19,6 +19,7 @@ export interface IUserRole {
|
||||
userId: number;
|
||||
}
|
||||
export interface IAccessStore extends Store<IRole, number> {
|
||||
getAvailablePermissions(): Promise<IAvailablePermissions>;
|
||||
getPermissionsForUser(userId: number): Promise<IUserPermission[]>;
|
||||
getPermissionsForRole(roleId: number): Promise<IUserPermission[]>;
|
||||
getRoles(): Promise<IRole[]>;
|
||||
@ -27,7 +28,15 @@ export interface IAccessStore extends Store<IRole, number> {
|
||||
removeRolesForProject(projectId: string): Promise<void>;
|
||||
getRolesForUserId(userId: number): Promise<IRole[]>;
|
||||
getUserIdsForRole(roleId: number): Promise<number[]>;
|
||||
addUserToRole(userId: number, roleId: number): Promise<void>;
|
||||
setupPermissionsForEnvironment(
|
||||
environmentName: string,
|
||||
permissions: string[],
|
||||
): Promise<void>;
|
||||
addUserToRole(
|
||||
userId: number,
|
||||
roleId: number,
|
||||
projectId: string,
|
||||
): Promise<void>;
|
||||
removeUserFromRole(userId: number, roleId: number): Promise<void>;
|
||||
removeRolesOfTypeForUser(userId: number, roleType: string): Promise<void>;
|
||||
createRole(
|
||||
@ -39,7 +48,6 @@ export interface IAccessStore extends Store<IRole, number> {
|
||||
addPermissionsToRole(
|
||||
role_id: number,
|
||||
permissions: string[],
|
||||
projectId?: string,
|
||||
environment?: string,
|
||||
): Promise<void>;
|
||||
removePermissionFromRole(
|
||||
|
@ -4,6 +4,7 @@ import { Store } from './store';
|
||||
export interface ICustomRoleInsert {
|
||||
name: string;
|
||||
description: string;
|
||||
roleType: string;
|
||||
}
|
||||
|
||||
export interface IRoleStore extends Store<ICustomRole, number> {
|
||||
|
@ -1,13 +1,48 @@
|
||||
exports.up = function (db, cb) {
|
||||
db.runSql(
|
||||
`
|
||||
ALTER TABLE roles RENAME TO role_project;
|
||||
CREATE TABLE roles (
|
||||
id integer PRIMARY KEY NOT NULL,
|
||||
name varchar(255),
|
||||
description text
|
||||
);
|
||||
`,
|
||||
CREATE TABLE IF NOT EXISTS permissions
|
||||
( id SERIAL PRIMARY KEY,
|
||||
permission VARCHAR(255) NOT NULL,
|
||||
environment VARCHAR(255),
|
||||
display_name TEXT,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT now()
|
||||
);
|
||||
|
||||
INSERT INTO permissions (permission, environment, display_name) (SELECT DISTINCT permission, environment, '' from role_permission);
|
||||
|
||||
ALTER TABLE role_user ADD COLUMN
|
||||
project VARCHAR(255);
|
||||
|
||||
UPDATE role_user
|
||||
SET project = roles.project
|
||||
FROM roles
|
||||
WHERE role_user.role_id = roles.id;
|
||||
|
||||
ALTER TABLE roles DROP COLUMN project;
|
||||
|
||||
ALTER TABLE roles
|
||||
ADD COLUMN
|
||||
updated_at TIMESTAMP WITH TIME ZONE;
|
||||
|
||||
ALTER TABLE role_permission
|
||||
ADD COLUMN
|
||||
permission_id INTEGER;
|
||||
|
||||
UPDATE role_permission
|
||||
SET permission_id = permissions.id
|
||||
FROM permissions
|
||||
WHERE
|
||||
(role_permission.environment = permissions.environment
|
||||
OR (role_permission.environment IS NULL AND permissions.environment IS NULL))
|
||||
AND
|
||||
role_permission.permission = permissions.permission;
|
||||
|
||||
ALTER TABLE role_permission
|
||||
DROP COLUMN project,
|
||||
DROP COLUMN permission,
|
||||
DROP COLUMN environment
|
||||
`,
|
||||
cb,
|
||||
);
|
||||
};
|
||||
@ -15,8 +50,6 @@ exports.up = function (db, cb) {
|
||||
exports.down = function (db, cb) {
|
||||
db.runSql(
|
||||
`
|
||||
DROP TABLE roles;
|
||||
ALTER TABLE role_project RENAME TO roles;
|
||||
`,
|
||||
cb,
|
||||
);
|
||||
|
@ -24,7 +24,7 @@ let readRole;
|
||||
const createUserEditorAccess = async (name, email) => {
|
||||
const { userStore } = stores;
|
||||
const user = await userStore.insert({ name, email });
|
||||
await accessService.addUserToRole(user.id, editorRole.id);
|
||||
await accessService.addUserToRole(user.id, editorRole.id, ALL_PROJECTS);
|
||||
return user;
|
||||
};
|
||||
|
||||
@ -34,7 +34,7 @@ const createSuperUser = async () => {
|
||||
name: 'Alice Admin',
|
||||
email: 'admin@getunleash.io',
|
||||
});
|
||||
await accessService.addUserToRole(user.id, adminRole.id);
|
||||
await accessService.addUserToRole(user.id, adminRole.id, ALL_PROJECTS);
|
||||
return user;
|
||||
};
|
||||
|
||||
@ -384,27 +384,6 @@ test('should return role with permissions and users', async () => {
|
||||
expect(roleWithPermission.users.length > 2).toBe(true);
|
||||
});
|
||||
|
||||
test('should return list of permissions', async () => {
|
||||
const p = await accessService.getPermissions();
|
||||
|
||||
const findPerm = (perm) => p.find((_) => _.name === perm);
|
||||
|
||||
const {
|
||||
DELETE_FEATURE,
|
||||
UPDATE_FEATURE,
|
||||
CREATE_FEATURE,
|
||||
UPDATE_PROJECT,
|
||||
CREATE_PROJECT,
|
||||
} = permissions;
|
||||
|
||||
expect(p.length > 2).toBe(true);
|
||||
expect(findPerm(CREATE_PROJECT).type).toBe('root');
|
||||
expect(findPerm(UPDATE_PROJECT).type).toBe('project');
|
||||
expect(findPerm(CREATE_FEATURE).type).toBe('project');
|
||||
expect(findPerm(UPDATE_FEATURE).type).toBe('project');
|
||||
expect(findPerm(DELETE_FEATURE).type).toBe('project');
|
||||
});
|
||||
|
||||
test('should set root role for user', async () => {
|
||||
const { userStore } = stores;
|
||||
const user = await userStore.insert({
|
||||
@ -468,20 +447,16 @@ test('should support permission with "ALL" environment requirement', async () =>
|
||||
const customRole = await accessStore.createRole(
|
||||
'Power user',
|
||||
'custom',
|
||||
'default',
|
||||
'Grants access to modify all environments',
|
||||
);
|
||||
|
||||
const { CREATE_FEATURE_STRATEGY } = permissions;
|
||||
|
||||
await accessStore.addPermissionsToRole(
|
||||
customRole.id,
|
||||
[CREATE_FEATURE_STRATEGY],
|
||||
'default',
|
||||
ALL_PROJECTS,
|
||||
'production',
|
||||
);
|
||||
|
||||
await accessStore.addUserToRole(user.id, customRole.id);
|
||||
await accessStore.addUserToRole(user.id, customRole.id, ALL_PROJECTS);
|
||||
|
||||
const hasAccess = await accessService.hasPermission(
|
||||
user,
|
||||
@ -498,7 +473,7 @@ test('should support permission with "ALL" environment requirement', async () =>
|
||||
'default',
|
||||
'development',
|
||||
);
|
||||
expect(hasNotAccess).toBe(true);
|
||||
expect(hasNotAccess).toBe(false);
|
||||
});
|
||||
|
||||
test('Should have access to create a strategy in an environment', async () => {
|
||||
|
8
src/test/fixtures/access-service-mock.ts
vendored
8
src/test/fixtures/access-service-mock.ts
vendored
@ -4,7 +4,11 @@ import { AccessService } from '../../lib/services/access-service';
|
||||
import User from '../../lib/types/user';
|
||||
import noLoggerProvider from './no-logger';
|
||||
import { IRole } from '../../lib/types/stores/access-store';
|
||||
import { IPermission, IRoleData, IUserWithRole } from '../../lib/types/model';
|
||||
import {
|
||||
IAvailablePermissions,
|
||||
IRoleData,
|
||||
IUserWithRole,
|
||||
} from '../../lib/types/model';
|
||||
|
||||
class AccessServiceMock extends AccessService {
|
||||
constructor() {
|
||||
@ -22,7 +26,7 @@ class AccessServiceMock extends AccessService {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
getPermissions(): IPermission[] {
|
||||
getPermissions(): Promise<IAvailablePermissions> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
|
12
src/test/fixtures/fake-access-store.ts
vendored
12
src/test/fixtures/fake-access-store.ts
vendored
@ -6,12 +6,24 @@ import {
|
||||
IUserPermission,
|
||||
IUserRole,
|
||||
} from '../../lib/types/stores/access-store';
|
||||
import { IAvailablePermissions, IPermission } from 'lib/types/model';
|
||||
|
||||
class AccessStoreMock implements IAccessStore {
|
||||
setupPermissionsForEnvironment(
|
||||
environmentName: string,
|
||||
permissions: string[],
|
||||
): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
userPermissions: IUserPermission[] = [];
|
||||
|
||||
roles: IRole[] = [];
|
||||
|
||||
getAvailablePermissions(): Promise<IAvailablePermissions> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
getPermissionsForUser(userId: Number): Promise<IUserPermission[]> {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
1
src/test/fixtures/fake-role-store.ts
vendored
1
src/test/fixtures/fake-role-store.ts
vendored
@ -15,6 +15,7 @@ export default class FakeRoleStore implements IRoleStore {
|
||||
createdAt: new Date(),
|
||||
});
|
||||
}
|
||||
|
||||
async getAll(): Promise<ICustomRole[]> {
|
||||
return Promise.resolve([
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user