diff --git a/packages/server/src/permission/permission.service.ts b/packages/server/src/permission/permission.service.ts index 0df5c274..3e55d9f0 100644 --- a/packages/server/src/permission/permission.service.ts +++ b/packages/server/src/permission/permission.service.ts @@ -114,7 +114,7 @@ export class PermissionService { async grantStudyAdmin(study: Study, user: string, isAdmin: boolean, requestingUser: TokenPayload): Promise { // 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'); } @@ -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; @@ -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; diff --git a/packages/server/src/permission/permissions/project.ts b/packages/server/src/permission/permissions/project.ts index b4f56cd0..1521e086 100644 --- a/packages/server/src/permission/permissions/project.ts +++ b/packages/server/src/permission/permissions/project.ts @@ -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 ]; diff --git a/packages/server/src/permission/permissions/roles.ts b/packages/server/src/permission/permissions/roles.ts index 10586534..40b56a5c 100644 --- a/packages/server/src/permission/permissions/roles.ts +++ b/packages/server/src/permission/permissions/roles.ts @@ -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' @@ -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] ]; diff --git a/packages/server/src/project/project.resolver.ts b/packages/server/src/project/project.resolver.ts index 1bce80af..76903aac 100644 --- a/packages/server/src/project/project.resolver.ts +++ b/packages/server/src/project/project.resolver.ts @@ -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 { - 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); } } diff --git a/packages/server/src/project/project.service.ts b/packages/server/src/project/project.service.ts index a0e60a10..6531707a 100644 --- a/packages/server/src/project/project.service.ts +++ b/packages/server/src/project/project.service.ts @@ -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 { @@ -42,4 +45,16 @@ export class ProjectService { async delete(project: Project): Promise { await this.projectModel.deleteOne({ _id: project._id }); } + + async findAllForUser(user: TokenPayload, organization: string): Promise { + 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; + } }