Skip to content

Commit

Permalink
fix: Make permissions actually work with new environments
Browse files Browse the repository at this point in the history
  • Loading branch information
sighphyre authored and ivarconr committed Jan 11, 2022
1 parent ba40099 commit 0634758
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 82 deletions.
65 changes: 53 additions & 12 deletions src/lib/db/access-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const T = {
ROLES: 'roles',
ROLE_PERMISSION: 'role_permission',
PERMISSIONS: 'permissions',
PERMISSION_TYPES: 'permission_types',
};

interface IPermissionRow {
Expand Down Expand Up @@ -60,6 +61,10 @@ export class AccessStore implements IAccessStore {
await this.db.batchInsert(T.PERMISSIONS, rows);
}

async getRoleByName(name: string): Promise<IRole> {
return this.db(T.ROLES).where({ name }).first();
}

async delete(key: number): Promise<void> {
await this.db(T.ROLES).where({ id: key }).del();
}
Expand Down Expand Up @@ -93,8 +98,20 @@ export class AccessStore implements IAccessStore {

async getAvailablePermissions(): Promise<IAvailablePermissions> {
const rows = await this.db
.select(['id', 'permission', 'environment', 'display_name'])
.from(T.PERMISSIONS);
.select([
'p.id',
'p.permission',
'p.environment',
'pt.display_name',
])
.join(
`${T.PERMISSION_TYPES} AS pt`,
'pt.permission',
'p.permission',
)
.where('pt.type', 'project')
.orWhere('pt.type', 'environment')
.from(`${T.PERMISSIONS} as p`);

let projectPermissions: IPermission[] = [];
let rawEnvironments = new Map<string, IPermissionRow[]>();
Expand Down Expand Up @@ -174,15 +191,24 @@ export class AccessStore implements IAccessStore {
return this.db
.select(['id', 'name', 'type', 'description'])
.from<IRole>(T.ROLES)
.andWhere('type', 'project');
.where('type', 'custom')
.orWhere('type', 'project');
}

async getRolesForProject(projectId: string): Promise<IRole[]> {
return this.db
.select(['id', 'name', 'type', 'project', 'description'])
.from<IRole>(T.ROLES)
.where('project', projectId)
.andWhere('type', 'project');
.select(['r.id', 'r.name', 'r.type', 'ru.project', 'r.description'])
.from<IRole>(`${T.ROLE_USER} as ru`)
.innerJoin(`${T.ROLES} as r`, 'ru.role_id', 'r.id')
.where('project', projectId);
}

async unlinkUserRoles(userId: number): Promise<void> {
return this.db(T.ROLE_USER)
.where({
user_id: userId,
})
.delete();
}

async getRootRoles(): Promise<IRole[]> {
Expand All @@ -208,15 +234,25 @@ export class AccessStore implements IAccessStore {
.where('ru.user_id', '=', userId);
}

async getUserIdsForRole(
async getUserIdsForRole(roleId: number): Promise<number[]> {
const rows = await this.db
.select(['user_id'])
.from<IRole>(T.ROLE_USER)
.where('role_id', roleId);
return rows.map((r) => r.user_id);
}

async getProjectUserIdsForRole(
roleId: number,
projectId?: string,
): Promise<number[]> {
console.log('Checking for', roleId, projectId);
const rows = await this.db
.select(['user_id'])
.from<IRole>(T.ROLE_USER)
.where('role_id', roleId)
.andWhere('project', projectId);
.from<IRole>(`${T.ROLE_USER} AS ru`)
.join(`${T.ROLES} as r`, 'ru.role_id', 'id')
.where('r.id', roleId)
.andWhere('ru.project', projectId);
return rows.map((r) => r.user_id);
}

Expand All @@ -232,11 +268,16 @@ export class AccessStore implements IAccessStore {
});
}

async removeUserFromRole(userId: number, roleId: number): Promise<void> {
async removeUserFromRole(
userId: number,
roleId: number,
projectId: string,
): Promise<void> {
return this.db(T.ROLE_USER)
.where({
user_id: userId,
role_id: roleId,
project: projectId,
})
.delete();
}
Expand Down
47 changes: 31 additions & 16 deletions src/lib/services/access-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ export class AccessService {
permission: p,
}));
}
console.log('Checking perms for user id', user.id);
return this.store.getPermissionsForUser(user.id);
}

Expand Down Expand Up @@ -171,8 +172,12 @@ export class AccessService {
return userRoles.filter((r) => r.type === RoleType.ROOT);
}

async removeUserFromRole(userId: number, roleId: number): Promise<void> {
return this.store.removeUserFromRole(userId, roleId);
async removeUserFromRole(
userId: number,
roleId: number,
projectId: string,
): Promise<void> {
return this.store.removeUserFromRole(userId, roleId, projectId);
}

async addPermissionToRole(
Expand Down Expand Up @@ -230,11 +235,23 @@ export class AccessService {
return this.store.getRolesForUserId(userId);
}

async getUsersForRole(
async unlinkUserRoles(userId: number): Promise<void> {
return this.store.unlinkUserRoles(userId);
}

async getUsersForRole(roleId: number): Promise<IUser[]> {
const userIdList = await this.store.getUserIdsForRole(roleId);
if (userIdList.length > 0) {
return this.userStore.getAllWithId(userIdList);
}
return [];
}

async getProjectUsersForRole(
roleId: number,
projectId?: string,
): Promise<IUser[]> {
const userIdList = await this.store.getUserIdsForRole(
const userIdList = await this.store.getProjectUserIdsForRole(
roleId,
projectId,
);
Expand All @@ -250,9 +267,13 @@ export class AccessService {
): Promise<[IRole[], IUserWithRole[]]> {
const roles = await this.store.getProjectRoles();

console.log('GOt the following roles bacl', roles);
const users = await Promise.all(
roles.map(async (role) => {
const usrs = await this.getUsersForRole(role.id, projectId);
const usrs = await this.getProjectUsersForRole(
role.id,
projectId,
);
return usrs.map((u) => ({ ...u, roleId: role.id }));
}),
);
Expand All @@ -267,11 +288,8 @@ export class AccessService {
throw new Error('ProjectId cannot be empty');
}

const ownerRole = await this.store.createRole(
RoleName.OWNER,
RoleType.PROJECT,
PROJECT_DESCRIPTION.OWNER,
);
const ownerRole = await this.store.getRoleByName(RoleName.OWNER);

await this.store.addPermissionsToRole(
ownerRole.id,
PROJECT_ADMIN,
Expand All @@ -285,12 +303,9 @@ export class AccessService {
);
await this.store.addUserToRole(owner.id, ownerRole.id, projectId);
}
const memberRole = await this.store.createRole(
RoleName.MEMBER,
RoleType.PROJECT,
projectId,
PROJECT_DESCRIPTION.MEMBER,
);

const memberRole = await this.store.getRoleByName(RoleName.MEMBER);

await this.store.addPermissionsToRole(
memberRole.id,
PROJECT_REGULAR,
Expand Down
7 changes: 4 additions & 3 deletions src/lib/services/project-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { IFeatureTypeStore } from '../types/stores/feature-type-store';
import { IFeatureToggleStore } from '../types/stores/feature-toggle-store';
import { IFeatureEnvironmentStore } from '../types/stores/feature-environment-store';
import { IProjectQuery, IProjectStore } from '../types/stores/project-store';
import { IRole } from '../types/stores/access-store';
import { IRoleDescriptor } from '../types/stores/access-store';
import { IEventStore } from '../types/stores/event-store';
import FeatureToggleService from './feature-toggle-service';
import { CREATE_FEATURE, UPDATE_FEATURE } from '../types/permissions';
Expand All @@ -38,7 +38,7 @@ const DEFAULT_PROJECT = 'default';

export interface UsersWithRoles {
users: IUserWithRole[];
roles: IRole[];
roles: IRoleDescriptor[];
}

export default class ProjectService {
Expand Down Expand Up @@ -271,6 +271,7 @@ export default class ProjectService {
const [roles, users] = await this.accessService.getProjectRoleUsers(
projectId,
);
console.log('Got the following response', roles, users);

return {
roles,
Expand Down Expand Up @@ -324,7 +325,7 @@ export default class ProjectService {
}
}

await this.accessService.removeUserFromRole(userId, role.id);
await this.accessService.removeUserFromRole(userId, role.id, projectId);
}

async getMembers(projectId: string): Promise<number> {
Expand Down
7 changes: 1 addition & 6 deletions src/lib/services/user-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,12 +262,7 @@ class UserService {

async deleteUser(userId: number, updatedBy?: User): Promise<void> {
const user = await this.store.get(userId);
const roles = await this.accessService.getRolesForUser(userId);
await Promise.all(
roles.map((role) =>
this.accessService.removeUserFromRole(userId, role.id),
),
);
await this.accessService.unlinkUserRoles(userId);
await this.sessionService.deleteSessionsForUser(userId);

await this.store.delete(userId);
Expand Down
15 changes: 14 additions & 1 deletion src/lib/types/stores/access-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,29 @@ export interface IRole {
type: string;
}

export interface IRoleDescriptor {
name: string;
description?: string;
type: string;
}

export interface IUserRole {
roleId: number;
userId: number;
}
export interface IAccessStore extends Store<IRole, number> {
getRoleByName(name: string): Promise<IRole>;
getAvailablePermissions(): Promise<IAvailablePermissions>;
getPermissionsForUser(userId: number): Promise<IUserPermission[]>;
getPermissionsForRole(roleId: number): Promise<IUserPermission[]>;
getRoles(): Promise<IRole[]>;
getRolesForProject(projectId: string): Promise<IRole[]>;
unlinkUserRoles(userId: number): Promise<void>;
getProjectRoles(): Promise<IRole[]>;
getRootRoles(): Promise<IRole[]>;
removeRolesForProject(projectId: string): Promise<void>;
getRolesForUserId(userId: number): Promise<IRole[]>;
getProjectUserIdsForRole(roleId: number, projectId?: string);
getUserIdsForRole(roleId: number, projectId?: string): Promise<number[]>;
addEnvironmentPermissionsToRole(
role_id: number,
Expand All @@ -42,7 +51,11 @@ export interface IAccessStore extends Store<IRole, number> {
roleId: number,
projectId: string,
): Promise<void>;
removeUserFromRole(userId: number, roleId: number): Promise<void>;
removeUserFromRole(
userId: number,
roleId: number,
projectId: string,
): Promise<void>;
removeRolesOfTypeForUser(userId: number, roleType: string): Promise<void>;
createRole(
name: string,
Expand Down
61 changes: 35 additions & 26 deletions src/migrations/20211202120808-add-custom-roles.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@ exports.up = function (db, cb) {
( 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);
CREATE TABLE IF NOT EXISTS permission_types (
permission VARCHAR(255),
display_name TEXT,
type VARCHAR(255)
);
INSERT INTO permissions (permission, environment) (select distinct permission,environment from role_permission);
ALTER TABLE role_user ADD COLUMN
project VARCHAR(255);
Expand All @@ -19,6 +24,9 @@ exports.up = function (db, cb) {
FROM roles
WHERE role_user.role_id = roles.id;
ALTER TABLE role_user DROP CONSTRAINT role_user_pkey;
ALTER TABLE role_user ADD PRIMARY KEY (role_id, user_id, project);
ALTER TABLE roles DROP COLUMN project;
ALTER TABLE roles
Expand All @@ -29,7 +37,7 @@ exports.up = function (db, cb) {
ADD COLUMN
permission_id INTEGER;
UPDATE role_permission
UPDATE role_permission
SET permission_id = permissions.id
FROM permissions
WHERE
Expand All @@ -43,29 +51,30 @@ exports.up = function (db, cb) {
DROP COLUMN permission,
DROP COLUMN environment;
UPDATE permissions SET display_name = 'Admin' where permission = 'ADMIN';
UPDATE permissions SET display_name = 'Create Strategies' where permission = 'CREATE_STRATEGY';
UPDATE permissions SET display_name = 'Create Addons' where permission = 'CREATE_ADDON';
UPDATE permissions SET display_name = 'Delete Addons' where permission = 'DELETE_ADDON';
UPDATE permissions SET display_name = 'Update Addons' where permission = 'UPDATE_ADDON';
UPDATE permissions SET display_name = 'Create Feature Toggles' where permission = 'CREATE_FEATURE';
UPDATE permissions SET display_name = 'Update Feature Toggles' where permission = 'UPDATE_FEATURE';
UPDATE permissions SET display_name = 'Delete Feature Toggles' where permission = 'DELETE_FEATURE';
UPDATE permissions SET display_name = 'Update Applications' where permission = 'UPDATE_APPLICATION';
UPDATE permissions SET display_name = 'Update Tag Types' where permission = 'UPDATE_TAG_TYPE';
UPDATE permissions SET display_name = 'Delete Tag Types' where permission = 'DELETE_TAG_TYPE';
UPDATE permissions SET display_name = 'Create Projects' where permission = 'CREATE_PROJECT';
UPDATE permissions SET display_name = 'Update Projects' where permission = 'UPDATE_PROJECT';
UPDATE permissions SET display_name = 'Delete Projects' where permission = 'DELETE_PROJECT';
UPDATE permissions SET display_name = 'Update Strategies on Toggles' where permission = 'UPDATE_FEATURE_STRATEGY';
UPDATE permissions SET display_name = 'Add Strategies to Toggles' where permission = 'CREATE_FEATURE_STRATEGY';
UPDATE permissions SET display_name = 'Remove Strategies from Toggles' where permission = 'DELETE_FEATURE_STRATEGY';
UPDATE permissions SET display_name = 'Update Strategies' where permission = 'UPDATE_STRATEGY';
UPDATE permissions SET display_name = 'Delete Strategies' where permission = 'DELETE_STRATEGY';
UPDATE permissions SET display_name = 'Enable/Disable Toggles for Environments' where permission = 'UPDATE_FEATURE_ENVIRONMENT';
UPDATE permissions SET display_name = 'Update Context Fields' where permission = 'UPDATE_CONTEXT_FIELD';
UPDATE permissions SET display_name = 'Create Context Fields' where permission = 'CREATE_CONTEXT_FIELD';
UPDATE permissions SET display_name = 'Delete Context Fields' where permission = 'DELETE_CONTEXT_FIELD';
INSERT INTO permission_types (permission, display_name, type) VALUES ('ADMIN', 'Admin', 'root');
INSERT INTO permission_types (permission, display_name, type) VALUES ('CLIENT', 'Client', 'root');
INSERT INTO permission_types (permission, display_name, type) VALUES ('CREATE_STRATEGY','Create Strategies', 'root');
INSERT INTO permission_types (permission, display_name, type) VALUES ('CREATE_ADDON', 'Create Addons', 'root');
INSERT INTO permission_types (permission, display_name, type) VALUES ('DELETE_ADDON', 'Delete Addons', 'root');
INSERT INTO permission_types (permission, display_name, type) VALUES ('UPDATE_ADDON', 'Update Addons', 'root');
INSERT INTO permission_types (permission, display_name, type) VALUES ('CREATE_FEATURE', 'Create Feature Toggles', 'project');
INSERT INTO permission_types (permission, display_name, type) VALUES ('UPDATE_FEATURE', 'Update Feature Toggles', 'project');
INSERT INTO permission_types (permission, display_name, type) VALUES ('DELETE_FEATURE', 'Delete Feature Toggles', 'project');
INSERT INTO permission_types (permission, display_name, type) VALUES ('UPDATE_APPLICATION', 'Update Applications', 'root');
INSERT INTO permission_types (permission, display_name, type) VALUES ('UPDATE_TAG_TYPE', 'Update Tag Types', 'root');
INSERT INTO permission_types (permission, display_name, type) VALUES ('DELETE_TAG_TYPE', 'Delete Tag Types', 'root');
INSERT INTO permission_types (permission, display_name, type) VALUES ('CREATE_PROJECT', 'Create Projects', 'root');
INSERT INTO permission_types (permission, display_name, type) VALUES ('UPDATE_PROJECT', 'Update Projects', 'project');
INSERT INTO permission_types (permission, display_name, type) VALUES ('DELETE_PROJECT', 'Delete Projects', 'project');
INSERT INTO permission_types (permission, display_name, type) VALUES ('UPDATE_FEATURE_STRATEGY', 'Update Strategies on Toggles', 'environment');
INSERT INTO permission_types (permission, display_name, type) VALUES ('CREATE_FEATURE_STRATEGY', 'Add Strategies to Toggles', 'environment');
INSERT INTO permission_types (permission, display_name, type) VALUES ('DELETE_FEATURE_STRATEGY', 'Remove Strategies from Toggles', 'environment');
INSERT INTO permission_types (permission, display_name, type) VALUES ('UPDATE_STRATEGY', 'Update Strategies', 'root');
INSERT INTO permission_types (permission, display_name, type) VALUES ('DELETE_STRATEGY', 'Delete Strategies', 'root');
INSERT INTO permission_types (permission, display_name, type) VALUES ('UPDATE_FEATURE_ENVIRONMENT', 'Enable/Disable Toggles for Environments', 'environment');
INSERT INTO permission_types (permission, display_name, type) VALUES ('UPDATE_CONTEXT_FIELD', 'Update Context Fields', 'root');
INSERT INTO permission_types (permission, display_name, type) VALUES ('CREATE_CONTEXT_FIELD', 'Create Context Fields', 'root');
INSERT INTO permission_types (permission, display_name, type) VALUES ('DELETE_CONTEXT_FIELD', 'Delete Context Fields', 'root');
`,
cb,
);
Expand Down

0 comments on commit 0634758

Please sign in to comment.