From 068bd67ab7947667f74e4ac8a2c47c89c0502b3e Mon Sep 17 00:00:00 2001 From: Jure Triglav <juretriglav@gmail.com> Date: Tue, 29 Sep 2020 01:03:21 +0200 Subject: [PATCH] feat: improve permissions to support multiple manuscript versions --- config/default.js | 41 +++++------ config/permissions.js | 159 +++++++++++++++++++++++++++--------------- 2 files changed, 121 insertions(+), 79 deletions(-) diff --git a/config/default.js b/config/default.js index 3986e83430..62ecdfb4fd 100644 --- a/config/default.js +++ b/config/default.js @@ -3,30 +3,25 @@ const components = require('./components.json') const logger = require('winston') module.exports = { + teams: { + seniorEditor: { + name: 'Senior Editor', + }, + handlingEditor: { + name: 'Handling Editor', + }, + managingEditor: { + name: 'Managing Editor', + }, + reviewer: { + name: 'Reviewer', + }, + author: { + name: 'Author', + }, + }, authsome: { mode: path.resolve(__dirname, 'authsome.js'), - teams: { - seniorEditor: { - name: 'Senior Editors', - permissions: '', - }, - handlingEditor: { - name: 'Handling Editors', - permissions: '', - }, - managingEditor: { - name: 'Managing Editors', - permissions: '', - }, - reviewer: { - name: 'Reviewer', - permissions: '', - }, - author: { - name: 'Authors', - permissions: '', - }, - }, }, validations: path.resolve(__dirname, 'validations.js'), pubsweet: { @@ -149,13 +144,13 @@ module.exports = { }, publicKeys: [ 'pubsweet-client', - 'authsome', 'validations', 'pubsweet-component-xpub-dashboard', 'pubsweet-component-xpub-formbuilder', 'pubsweet', 'detectionMethodCorrelations', 'journal', + 'teams', ], schema: {}, journal: require('./journal'), diff --git a/config/permissions.js b/config/permissions.js index 583bf92a18..236ab7d55f 100644 --- a/config/permissions.js +++ b/config/permissions.js @@ -106,16 +106,22 @@ const userIsAllowedToChat = rule({ cache: 'strict' })( const user_is_review_author_and_review_is_not_completed = rule({ cache: 'strict', })(async (parent, args, ctx, info) => { - const review = await ctx.models.Review.query().findById(args.id) - const manuscript = await ctx.models.Manuscript.query().findById( - review.manuscriptId, - ) + let manuscriptId + if (args.id) { + ;({ manuscriptId } = await ctx.models.Review.query().findById(args.id)) + } else { + ;({ manuscriptId } = args.input) + } + + const manuscript = await ctx.models.Manuscript.query().findById(manuscriptId) const team = await ctx.models.Team.query() .where({ manuscriptId: manuscript.id, role: 'reviewer', }) .first() + if (!team) return false + const members = await team .$relatedQuery('members') .where('userId', ctx.user.id) @@ -127,6 +133,19 @@ const user_is_review_author_and_review_is_not_completed = rule({ return false }) +const user_is_editor_of_the_manuscript_of_the_review = rule({ + cache: 'strict', +})(async (parent, args, ctx, info) => { + let manuscriptId + if (args.id) { + ;({ manuscriptId } = await ctx.models.Review.query().findById(args.id)) + } else { + ;({ manuscriptId } = args.input) + } + + return _userIsEditor(ctx.user, manuscriptId) +}) + const user_is_invited_reviewer = rule({ cache: 'strict' })( async (parent, args, ctx, info) => { const team = await ctx.models.Team.query().findById(args.teamId) @@ -187,62 +206,90 @@ const current_user_is_the_reviewer_of_the_manuscript_of_the_file_and_review_not_ return false }) -const permissions = shield( - { - Query: { - currentUser: isAuthenticated, - paginatedManuscripts: userIsAdmin, - detailsForURL: allow, - publishedManuscripts: allow, - manuscripts: allow, - manuscript: allow, - messages: allow, - getFile: allow, // this is a query that gets the form - }, - Mutation: { - createManuscript: isAuthenticated, - updateManuscript: user_is_author, - createMessage: userIsAllowedToChat, - updateReview: user_is_review_author_and_review_is_not_completed, - reviewerResponse: user_is_invited_reviewer, - completeReview: user_is_review_author_and_review_is_not_completed, - }, - Subscription: { - messageCreated: userIsAllowedToChat, - }, - CurrentRole: allow, - Team: allow, - TeamMember: allow, - URLMetadata: allow, - User: allow, - PaginatedManuscripts: allow, - Manuscript: allow, - File: or( - parent_manuscript_is_published, - or( - current_user_is_the_reviewer_of_the_manuscript_of_the_file_and_review_not_complete, - userIsEditor, - userIsAdmin, - ), +const permissions = { + Query: { + currentUser: isAuthenticated, + paginatedManuscripts: userIsAdmin, + detailsForURL: allow, + publishedManuscripts: allow, + manuscripts: allow, + manuscript: allow, + messages: allow, + getFile: allow, // this is a query that gets the form + user: allow, + }, + Mutation: { + createManuscript: isAuthenticated, + updateManuscript: user_is_author, + submitManuscript: user_is_author, + createMessage: userIsAllowedToChat, + updateReview: or( + user_is_review_author_and_review_is_not_completed, + user_is_editor_of_the_manuscript_of_the_review, + ), + reviewerResponse: user_is_invited_reviewer, + completeReview: or( + user_is_review_author_and_review_is_not_completed, + user_is_editor_of_the_manuscript_of_the_review, ), - Review: or(parent_manuscript_is_published, review_is_by_current_user), - ReviewComment: allow, - Channel: allow, - Message: allow, - MessagesRelay: allow, - PageInfo: allow, - ManuscriptMeta: allow, - Note: allow, - Identity: allow, + createNewVersion: allow, }, - { - allowExternalErrors: false, - debug: true, - fallbackRule: or(userIsAdmin, userIsEditor), + Subscription: { + messageCreated: userIsAllowedToChat, }, -) + CurrentRole: allow, + Team: allow, + TeamMember: allow, + URLMetadata: allow, + User: allow, + PaginatedManuscripts: allow, + Manuscript: allow, + ManuscriptVersion: allow, + File: or( + parent_manuscript_is_published, + or( + current_user_is_the_reviewer_of_the_manuscript_of_the_file_and_review_not_complete, + userIsEditor, + userIsAdmin, + ), + ), + Review: or(parent_manuscript_is_published, review_is_by_current_user), + ReviewComment: allow, + Channel: allow, + Message: allow, + MessagesRelay: allow, + PageInfo: allow, + ManuscriptMeta: allow, + Note: allow, + Identity: allow, +} + +const fallbackRule = or(userIsAdmin, userIsEditor) + +// We only ever need to go two levels down, so no need for recursion +const addOverrideRule = permissions => { + const adaptedPermissions = {} + Object.keys(permissions).forEach(key1 => { + const value = permissions[key1] + if (value.constructor.name !== 'Object') { + adaptedPermissions[key1] = or(fallbackRule, value) + } else { + adaptedPermissions[key1] = value + Object.keys(value).forEach(key2 => { + adaptedPermissions[key1][key2] = or(fallbackRule, value[key2]) + }) + } + }) + return adaptedPermissions +} + +const shieldWithPermissions = shield(addOverrideRule(permissions), { + allowExternalErrors: false, + debug: true, + fallbackRule, +}) -module.exports = permissions +module.exports = shieldWithPermissions // const userIsEditorOfManuscript = rule({ // cache: 'strict', -- GitLab