Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion packages/server/src/permission/permission.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export class PermissionService {

async grantStudyAdmin(study: Study, user: string, isAdmin: boolean, requestingUser: TokenPayload): Promise<boolean> {
// Make sure the target user is not a project admin
const isProjectAdmin = await this.enforcer.enforce(user, Roles.PROJECT_ADMIN, study._id);
const isProjectAdmin = await this.enforcer.enforce(user, Roles.PROJECT_ADMIN, study._id.toString());
if (isProjectAdmin) {
throw new UnauthorizedException('Target user is an owner');
}
Expand All @@ -127,8 +127,13 @@ export class PermissionService {
// Otherwise grant the permissions
if (isAdmin) {
await this.enforcer.addPolicy(user, Roles.STUDY_ADMIN, study._id.toString());
await this.enforcer.addPolicy(user, Roles.PROJECT_VIEWER, study.project);
} else {
await this.enforcer.removePolicy(user, Roles.STUDY_ADMIN, study._id.toString());
// If the user isn't a contributor, then also remove project PROJECT_VIEWER
if (!(await this.enforcer.enforce(user, Roles.CONTRIBUTOR, study._id.toString()))) {
await this.enforcer.removePolicy(user, Roles.PROJECT_VIEWER, study.project);
}
}

return true;
Expand All @@ -154,8 +159,10 @@ export class PermissionService {
// Otherwise grant the permissions
if (isContributor) {
await this.enforcer.addPolicy(user, Roles.CONTRIBUTOR, study._id.toString());
await this.enforcer.addPolicy(user, Roles.PROJECT_VIEWER, study.project);
} else {
await this.enforcer.removePolicy(user, Roles.CONTRIBUTOR, study._id.toString());
await this.enforcer.removePolicy(user, Roles.PROJECT_VIEWER, study.project);
}

return true;
Expand Down
4 changes: 3 additions & 1 deletion packages/server/src/permission/permissions/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ export const roleToProjectPermissions: string[][] = [
[Roles.PROJECT_ADMIN, ProjectPermissions.UPDATE],
[Roles.OWNER, ProjectPermissions.GRANT_ADMIN],

// PROJECT_VIEWER permissions
[Roles.PROJECT_VIEWER, ProjectPermissions.READ]

// STUDY_ADMIN permissions

// CONTRIBUTOR permissions
[Roles.CONTRIBUTOR, ProjectPermissions.READ]

// TRAINED_CONTRIBUTOR permissions
];
2 changes: 2 additions & 0 deletions packages/server/src/permission/permissions/roles.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export enum Roles {
OWNER = 'owner',
PROJECT_ADMIN = 'project_admin',
PROJECT_VIEWER = 'project_viewer',
STUDY_ADMIN = 'study_admin',
TRAINED_CONTRIBUTOR = 'trained_contributor',
CONTRIBUTOR = 'contributor'
Expand All @@ -13,5 +14,6 @@ export enum Roles {
export const roleHierarchy: string[][] = [
[Roles.OWNER, Roles.PROJECT_ADMIN],
[Roles.PROJECT_ADMIN, Roles.STUDY_ADMIN],
[Roles.PROJECT_ADMIN, Roles.PROJECT_VIEWER],
[Roles.STUDY_ADMIN, Roles.CONTRIBUTOR]
];
6 changes: 1 addition & 5 deletions packages/server/src/project/project.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,11 @@ export class ProjectResolver {
return true;
}

// TODO: Handle the ability to get project based on user access
@Query(() => [Project])
async getProjects(
@OrganizationContext() organization: Organization,
@TokenContext() user: TokenPayload
): Promise<Project[]> {
if (!(await this.enforcer.enforce(user.user_id, ProjectPermissions.READ, organization._id))) {
throw new UnauthorizedException('User does not have permission to read projects');
}
return this.projectService.findAll(organization._id);
return this.projectService.findAllForUser(user, organization._id);
}
}
15 changes: 15 additions & 0 deletions packages/server/src/project/project.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import { Project, ProjectDocument } from './project.model';
import { ProjectCreate } from './dtos/create.dto';
import { CASBIN_PROVIDER } from '../permission/casbin.provider';
import * as casbin from 'casbin';
import { TokenPayload } from 'src/jwt/token.dto';
import { ProjectPermissions } from 'src/permission/permissions/project';
import { Roles } from 'src/permission/permissions/roles';

@Injectable()
export class ProjectService {
Expand Down Expand Up @@ -42,4 +45,16 @@ export class ProjectService {
async delete(project: Project): Promise<void> {
await this.projectModel.deleteOne({ _id: project._id });
}

async findAllForUser(user: TokenPayload, organization: string): Promise<Project[]> {
const projects = await this.findAll(organization);
const allowedProjects: Project[] = [];
for (const project of projects) {
const hasAccess = await this.enforcer.enforce(user.user_id, ProjectPermissions.READ, project._id.toString());
if (hasAccess) {
allowedProjects.push(project);
}
}
return allowedProjects;
}
}