mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-26 13:48:33 +02:00
chore: Minor code cleanups
This commit is contained in:
parent
26db43b248
commit
b56ed05db1
@ -11,6 +11,10 @@ import {
|
|||||||
import { IPermission } from 'lib/types/model';
|
import { IPermission } from 'lib/types/model';
|
||||||
import { roundToNearestMinutesWithOptions } from 'date-fns/fp';
|
import { roundToNearestMinutesWithOptions } from 'date-fns/fp';
|
||||||
import NotFoundError from '../error/notfound-error';
|
import NotFoundError from '../error/notfound-error';
|
||||||
|
import {
|
||||||
|
ENVIRONMENT_PERMISSION_TYPE,
|
||||||
|
ROOT_PERMISSION_TYPE,
|
||||||
|
} from 'lib/util/constants';
|
||||||
|
|
||||||
const T = {
|
const T = {
|
||||||
ROLE_USER: 'role_user',
|
ROLE_USER: 'role_user',
|
||||||
@ -126,14 +130,17 @@ export class AccessStore implements IAccessStore {
|
|||||||
// Since the editor should have access to the default project,
|
// Since the editor should have access to the default project,
|
||||||
// we map the project to the project and environment specific
|
// we map the project to the project and environment specific
|
||||||
// permissions that are connected to the editor role.
|
// permissions that are connected to the editor role.
|
||||||
if (row.role_id === EDITOR_ID && row.type !== 'root') {
|
if (row.role_id === EDITOR_ID && row.type !== ROOT_PERMISSION_TYPE) {
|
||||||
project = 'default';
|
project = 'default';
|
||||||
} else if (row.type !== 'root') {
|
} else if (row.type !== ROOT_PERMISSION_TYPE) {
|
||||||
project = row.project ? row.project : undefined;
|
project = row.project ? row.project : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const environment =
|
const environment =
|
||||||
row.type === 'environment' ? row.environment : undefined;
|
row.type === ENVIRONMENT_PERMISSION_TYPE
|
||||||
|
? row.environment
|
||||||
|
: undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
project,
|
project,
|
||||||
environment,
|
environment,
|
||||||
@ -170,11 +177,11 @@ export class AccessStore implements IAccessStore {
|
|||||||
role_id: number,
|
role_id: number,
|
||||||
permissions: IPermission[],
|
permissions: IPermission[],
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const rows = permissions.map((x) => {
|
const rows = permissions.map((permission) => {
|
||||||
return {
|
return {
|
||||||
role_id,
|
role_id,
|
||||||
permission_id: x.id,
|
permission_id: permission.id,
|
||||||
environment: x.environment,
|
environment: permission.environment,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
this.db.batchInsert(T.ROLE_PERMISSION, rows);
|
this.db.batchInsert(T.ROLE_PERMISSION, rows);
|
||||||
@ -217,7 +224,7 @@ export class AccessStore implements IAccessStore {
|
|||||||
return rows.map((r) => r.user_id);
|
return rows.map((r) => r.user_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
async addUserToRole(
|
async addUserToProjectRole(
|
||||||
userId: number,
|
userId: number,
|
||||||
roleId: number,
|
roleId: number,
|
||||||
projecId: string,
|
projecId: string,
|
||||||
@ -229,7 +236,7 @@ export class AccessStore implements IAccessStore {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeUserFromRole(
|
async removeUserFromProjectRole(
|
||||||
userId: number,
|
userId: number,
|
||||||
roleId: number,
|
roleId: number,
|
||||||
projectId: string,
|
projectId: string,
|
||||||
|
@ -101,15 +101,6 @@ export default class RoleStore implements IRoleStore {
|
|||||||
return result.length > 0;
|
return result.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
async roleExists(name: string): Promise<boolean> {
|
|
||||||
const result = await this.db.raw(
|
|
||||||
`SELECT EXISTS (SELECT 1 FROM ${T.ROLES} WHERE name = ?) AS present`,
|
|
||||||
[name],
|
|
||||||
);
|
|
||||||
const { present } = result.rows[0];
|
|
||||||
return present;
|
|
||||||
}
|
|
||||||
|
|
||||||
async deleteAll(): Promise<void> {
|
async deleteAll(): Promise<void> {
|
||||||
return this.db(T.ROLES).del();
|
return this.db(T.ROLES).del();
|
||||||
}
|
}
|
||||||
@ -181,8 +172,8 @@ export default class RoleStore implements IRoleStore {
|
|||||||
.where('r.type', '=', 'root');
|
.where('r.type', '=', 'root');
|
||||||
|
|
||||||
return rows.map((row) => ({
|
return rows.map((row) => ({
|
||||||
roleId: +row.id,
|
roleId: Number(row.id),
|
||||||
userId: +row.user_id,
|
userId: Number(row.user_id),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,8 +158,8 @@ export class AccessService {
|
|||||||
const allEnvironmentPermissions = environments.map((env) => {
|
const allEnvironmentPermissions = environments.map((env) => {
|
||||||
return {
|
return {
|
||||||
name: env.name,
|
name: env.name,
|
||||||
permissions: environmentPermissions.map((p) => {
|
permissions: environmentPermissions.map((permission) => {
|
||||||
return { environment: env.name, ...p };
|
return { environment: env.name, ...permission };
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -170,12 +170,12 @@ export class AccessService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async addUserToRole(
|
async addUserToProjectRole(
|
||||||
userId: number,
|
userId: number,
|
||||||
roleId: number,
|
roleId: number,
|
||||||
projectId: string,
|
projectId: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
return this.store.addUserToRole(userId, roleId, projectId);
|
return this.store.addUserToProjectRole(userId, roleId, projectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getRoleByName(roleName: string): Promise<IRole> {
|
async getRoleByName(roleName: string): Promise<IRole> {
|
||||||
@ -194,7 +194,7 @@ export class AccessService {
|
|||||||
RoleType.ROOT,
|
RoleType.ROOT,
|
||||||
);
|
);
|
||||||
|
|
||||||
await this.store.addUserToRole(
|
await this.store.addUserToProjectRole(
|
||||||
userId,
|
userId,
|
||||||
newRootRole.id,
|
newRootRole.id,
|
||||||
ALL_PROJECTS,
|
ALL_PROJECTS,
|
||||||
@ -214,12 +214,12 @@ export class AccessService {
|
|||||||
return userRoles.filter((r) => r.type === RoleType.ROOT);
|
return userRoles.filter((r) => r.type === RoleType.ROOT);
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeUserFromRole(
|
async removeUserFromProjectRole(
|
||||||
userId: number,
|
userId: number,
|
||||||
roleId: number,
|
roleId: number,
|
||||||
projectId: string,
|
projectId: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
return this.store.removeUserFromRole(userId, roleId, projectId);
|
return this.store.removeUserFromProjectRole(userId, roleId, projectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async addPermissionToRole(
|
async addPermissionToRole(
|
||||||
@ -242,9 +242,9 @@ export class AccessService {
|
|||||||
async removePermissionFromRole(
|
async removePermissionFromRole(
|
||||||
roleId: number,
|
roleId: number,
|
||||||
permission: string,
|
permission: string,
|
||||||
projectId?: string,
|
environment?: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (isProjectPermission(permission) && !projectId) {
|
if (isProjectPermission(permission) && !environment) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`ProjectId cannot be empty for permission=${permission}`,
|
`ProjectId cannot be empty for permission=${permission}`,
|
||||||
);
|
);
|
||||||
@ -252,7 +252,7 @@ export class AccessService {
|
|||||||
return this.store.removePermissionFromRole(
|
return this.store.removePermissionFromRole(
|
||||||
roleId,
|
roleId,
|
||||||
permission,
|
permission,
|
||||||
projectId,
|
environment,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,7 +349,11 @@ export class AccessService {
|
|||||||
this.logger.info(
|
this.logger.info(
|
||||||
`Making ${owner.id} admin of ${projectId} via roleId=${ownerRole.id}`,
|
`Making ${owner.id} admin of ${projectId} via roleId=${ownerRole.id}`,
|
||||||
);
|
);
|
||||||
await this.store.addUserToRole(owner.id, ownerRole.id, projectId);
|
await this.store.addUserToProjectRole(
|
||||||
|
owner.id,
|
||||||
|
ownerRole.id,
|
||||||
|
projectId,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,7 +300,11 @@ export default class ProjectService {
|
|||||||
throw new Error(`User already has access to project=${projectId}`);
|
throw new Error(`User already has access to project=${projectId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.accessService.addUserToRole(userId, role.id, projectId);
|
await this.accessService.addUserToProjectRole(
|
||||||
|
userId,
|
||||||
|
role.id,
|
||||||
|
projectId,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: should be an event too
|
// TODO: should be an event too
|
||||||
@ -324,7 +328,11 @@ export default class ProjectService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.accessService.removeUserFromRole(userId, role.id, projectId);
|
await this.accessService.removeUserFromProjectRole(
|
||||||
|
userId,
|
||||||
|
role.id,
|
||||||
|
projectId,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getMembers(projectId: string): Promise<number> {
|
async getMembers(projectId: string): Promise<number> {
|
||||||
|
@ -41,12 +41,12 @@ export interface IAccessStore extends Store<IRole, number> {
|
|||||||
role_id: number,
|
role_id: number,
|
||||||
permissions: IPermission[],
|
permissions: IPermission[],
|
||||||
): Promise<void>;
|
): Promise<void>;
|
||||||
addUserToRole(
|
addUserToProjectRole(
|
||||||
userId: number,
|
userId: number,
|
||||||
roleId: number,
|
roleId: number,
|
||||||
projectId: string,
|
projectId: string,
|
||||||
): Promise<void>;
|
): Promise<void>;
|
||||||
removeUserFromRole(
|
removeUserFromProjectRole(
|
||||||
userId: number,
|
userId: number,
|
||||||
roleId: number,
|
roleId: number,
|
||||||
projectId: string,
|
projectId: string,
|
||||||
|
@ -1 +1,5 @@
|
|||||||
export const DEFAULT_ENV = 'default';
|
export const DEFAULT_ENV = 'default';
|
||||||
|
|
||||||
|
export const ROOT_PERMISSION_TYPE = 'root';
|
||||||
|
export const ENVIRONMENT_PERMISSION_TYPE = 'environment';
|
||||||
|
export const PROJECT_PERMISSION_TYPE = 'project';
|
||||||
|
@ -28,14 +28,18 @@ let readRole;
|
|||||||
const createUserEditorAccess = async (name, email) => {
|
const createUserEditorAccess = async (name, email) => {
|
||||||
const { userStore } = stores;
|
const { userStore } = stores;
|
||||||
const user = await userStore.insert({ name, email });
|
const user = await userStore.insert({ name, email });
|
||||||
await accessService.addUserToRole(user.id, editorRole.id, 'default');
|
await accessService.addUserToProjectRole(user.id, editorRole.id, 'default');
|
||||||
return user;
|
return user;
|
||||||
};
|
};
|
||||||
|
|
||||||
const createUserViewerAccess = async (name, email) => {
|
const createUserViewerAccess = async (name, email) => {
|
||||||
const { userStore } = stores;
|
const { userStore } = stores;
|
||||||
const user = await userStore.insert({ name, email });
|
const user = await userStore.insert({ name, email });
|
||||||
await accessService.addUserToRole(user.id, readRole.id, ALL_PROJECTS);
|
await accessService.addUserToProjectRole(
|
||||||
|
user.id,
|
||||||
|
readRole.id,
|
||||||
|
ALL_PROJECTS,
|
||||||
|
);
|
||||||
return user;
|
return user;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -178,7 +182,11 @@ const createSuperUser = async () => {
|
|||||||
name: 'Alice Admin',
|
name: 'Alice Admin',
|
||||||
email: 'admin@getunleash.io',
|
email: 'admin@getunleash.io',
|
||||||
});
|
});
|
||||||
await accessService.addUserToRole(user.id, adminRole.id, ALL_PROJECTS);
|
await accessService.addUserToProjectRole(
|
||||||
|
user.id,
|
||||||
|
adminRole.id,
|
||||||
|
ALL_PROJECTS,
|
||||||
|
);
|
||||||
return user;
|
return user;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -372,7 +380,7 @@ test('should grant user access to project', async () => {
|
|||||||
await accessService.createDefaultProjectRoles(user, project);
|
await accessService.createDefaultProjectRoles(user, project);
|
||||||
|
|
||||||
const projectRole = await accessService.getRoleByName(RoleName.MEMBER);
|
const projectRole = await accessService.getRoleByName(RoleName.MEMBER);
|
||||||
await accessService.addUserToRole(sUser.id, projectRole.id, project);
|
await accessService.addUserToProjectRole(sUser.id, projectRole.id, project);
|
||||||
|
|
||||||
// // Should be able to update feature toggles inside the project
|
// // Should be able to update feature toggles inside the project
|
||||||
hasCommonProjectAccess(sUser, project, true);
|
hasCommonProjectAccess(sUser, project, true);
|
||||||
@ -397,7 +405,7 @@ test('should not get access if not specifying project', async () => {
|
|||||||
|
|
||||||
const projectRole = await accessService.getRoleByName(RoleName.MEMBER);
|
const projectRole = await accessService.getRoleByName(RoleName.MEMBER);
|
||||||
|
|
||||||
await accessService.addUserToRole(sUser.id, projectRole.id, project);
|
await accessService.addUserToProjectRole(sUser.id, projectRole.id, project);
|
||||||
|
|
||||||
// Should not be able to update feature toggles outside project
|
// Should not be able to update feature toggles outside project
|
||||||
hasCommonProjectAccess(sUser, undefined, false);
|
hasCommonProjectAccess(sUser, undefined, false);
|
||||||
@ -410,14 +418,18 @@ test('should remove user from role', async () => {
|
|||||||
email: 'random123@getunleash.io',
|
email: 'random123@getunleash.io',
|
||||||
});
|
});
|
||||||
|
|
||||||
await accessService.addUserToRole(user.id, editorRole.id, 'default');
|
await accessService.addUserToProjectRole(user.id, editorRole.id, 'default');
|
||||||
|
|
||||||
// check user has one role
|
// check user has one role
|
||||||
const userRoles = await accessService.getRolesForUser(user.id);
|
const userRoles = await accessService.getRolesForUser(user.id);
|
||||||
expect(userRoles.length).toBe(1);
|
expect(userRoles.length).toBe(1);
|
||||||
expect(userRoles[0].name).toBe(RoleName.EDITOR);
|
expect(userRoles[0].name).toBe(RoleName.EDITOR);
|
||||||
|
|
||||||
await accessService.removeUserFromRole(user.id, editorRole.id, 'default');
|
await accessService.removeUserFromProjectRole(
|
||||||
|
user.id,
|
||||||
|
editorRole.id,
|
||||||
|
'default',
|
||||||
|
);
|
||||||
const userRolesAfterRemove = await accessService.getRolesForUser(user.id);
|
const userRolesAfterRemove = await accessService.getRolesForUser(user.id);
|
||||||
expect(userRolesAfterRemove.length).toBe(0);
|
expect(userRolesAfterRemove.length).toBe(0);
|
||||||
});
|
});
|
||||||
@ -429,7 +441,7 @@ test('should return role with users', async () => {
|
|||||||
email: 'random2223@getunleash.io',
|
email: 'random2223@getunleash.io',
|
||||||
});
|
});
|
||||||
|
|
||||||
await accessService.addUserToRole(user.id, editorRole.id, 'default');
|
await accessService.addUserToProjectRole(user.id, editorRole.id, 'default');
|
||||||
|
|
||||||
const roleWithUsers = await accessService.getRoleData(editorRole.id);
|
const roleWithUsers = await accessService.getRoleData(editorRole.id);
|
||||||
expect(roleWithUsers.role.name).toBe(RoleName.EDITOR);
|
expect(roleWithUsers.role.name).toBe(RoleName.EDITOR);
|
||||||
@ -447,7 +459,7 @@ test('should return role with permissions and users', async () => {
|
|||||||
email: 'random2244@getunleash.io',
|
email: 'random2244@getunleash.io',
|
||||||
});
|
});
|
||||||
|
|
||||||
await accessService.addUserToRole(user.id, editorRole.id, 'default');
|
await accessService.addUserToProjectRole(user.id, editorRole.id, 'default');
|
||||||
|
|
||||||
const roleWithPermission = await accessService.getRoleData(editorRole.id);
|
const roleWithPermission = await accessService.getRoleData(editorRole.id);
|
||||||
|
|
||||||
@ -536,7 +548,11 @@ test('should support permission with "ALL" environment requirement', async () =>
|
|||||||
[CREATE_FEATURE_STRATEGY],
|
[CREATE_FEATURE_STRATEGY],
|
||||||
'production',
|
'production',
|
||||||
);
|
);
|
||||||
await accessStore.addUserToRole(user.id, customRole.id, ALL_PROJECTS);
|
await accessStore.addUserToProjectRole(
|
||||||
|
user.id,
|
||||||
|
customRole.id,
|
||||||
|
ALL_PROJECTS,
|
||||||
|
);
|
||||||
|
|
||||||
const hasAccess = await accessService.hasPermission(
|
const hasAccess = await accessService.hasPermission(
|
||||||
user,
|
user,
|
||||||
@ -667,3 +683,17 @@ test('Should be denied access to delete a role that is in use', async () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Should be given full access to project created by user', async () => {
|
||||||
|
const user = editorUser;
|
||||||
|
const newProjectName = 'AWholeNewProject';
|
||||||
|
|
||||||
|
const project = {
|
||||||
|
id: newProjectName,
|
||||||
|
name: newProjectName,
|
||||||
|
description: 'Blah',
|
||||||
|
};
|
||||||
|
await projectService.createProject(project, user.id);
|
||||||
|
|
||||||
|
hasFullProjectAccess(user, newProjectName, true);
|
||||||
|
});
|
||||||
|
6
src/test/fixtures/access-service-mock.ts
vendored
6
src/test/fixtures/access-service-mock.ts
vendored
@ -35,7 +35,7 @@ class AccessServiceMock extends AccessService {
|
|||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
|
|
||||||
addUserToRole(userId: number, roleId: number): Promise<void> {
|
addUserToProjectRole(userId: number, roleId: number): Promise<void> {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,10 +43,6 @@ class AccessServiceMock extends AccessService {
|
|||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
removeUserFromRole(userId: number, roleId: number): Promise<void> {
|
|
||||||
throw new Error('Method not implemented.');
|
|
||||||
}
|
|
||||||
|
|
||||||
addPermissionToRole(
|
addPermissionToRole(
|
||||||
roleId: number,
|
roleId: number,
|
||||||
permission: string,
|
permission: string,
|
||||||
|
14
src/test/fixtures/fake-access-store.ts
vendored
14
src/test/fixtures/fake-access-store.ts
vendored
@ -9,6 +9,14 @@ import {
|
|||||||
import { IAvailablePermissions, IPermission } from 'lib/types/model';
|
import { IAvailablePermissions, IPermission } from 'lib/types/model';
|
||||||
|
|
||||||
class AccessStoreMock implements IAccessStore {
|
class AccessStoreMock implements IAccessStore {
|
||||||
|
removeUserFromProjectRole(
|
||||||
|
userId: number,
|
||||||
|
roleId: number,
|
||||||
|
projectId: string,
|
||||||
|
): Promise<void> {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
wipePermissionsFromRole(role_id: number): Promise<void> {
|
wipePermissionsFromRole(role_id: number): Promise<void> {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
@ -79,11 +87,7 @@ class AccessStoreMock implements IAccessStore {
|
|||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
|
|
||||||
addUserToRole(userId: number, roleId: number): Promise<void> {
|
addUserToProjectRole(userId: number, roleId: number): Promise<void> {
|
||||||
throw new Error('Method not implemented.');
|
|
||||||
}
|
|
||||||
|
|
||||||
removeUserFromRole(userId: number, roleId: number): Promise<void> {
|
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user