Skip to content
Snippets Groups Projects
Commit 44acae40 authored by Jure's avatar Jure
Browse files

feat: additional authorization/permission rules

parent aa3ce80c
No related branches found
No related tags found
No related merge requests found
// eslint-disable-next-line no-unused-vars
const { rule, shield, and, or, not, allow, deny } = require('graphql-shield')
const isAdmin = rule({ cache: 'contextual' })(
async (parent, args, ctx, info) => ctx.user.admin,
const _userIsEditor = async (user, manuscriptId) => {
if (!user) {
return false
}
let query = user
.$relatedQuery('teams')
.where(builder =>
builder
.where({ role: 'seniorEditor' })
.orWhere({ role: 'handlingEditor' }),
)
// Manuscript is optional...
if (manuscriptId) {
query = query.where({ manuscriptId })
}
const rows = await query.resultSize()
return !!rows
}
const userIsEditor = rule({
cache: 'contextual',
})(async (parent, args, ctx, info) => _userIsEditor(ctx.user))
const _userIsMemberOfTeamWithRole = async (user, manuscriptId, role) => {
if (!user) {
return false
}
const query = user
.$relatedQuery('teams')
.where({ role })
.andWhere({ manuscriptId })
const rows = await query.resultSize()
return !!rows
}
const userIsAdmin = rule({ cache: 'contextual' })(
async (parent, args, ctx, info) => ctx.user && ctx.user.admin,
)
const isEditor = rule({ cache: 'contextual' })(
const parentManuscriptIsPublished = rule({ cache: 'contextual' })(
async (parent, args, ctx, info) => {
const rows = ctx.user
.$relatedQuery('teams')
.where({ role: 'seniorEditor' })
.orWhere({ role: 'handlingEditor' })
.resultSize()
return rows !== 0
const manuscript = await ctx.models.Manuscript.query().findById(
parent.manuscriptId,
)
return !!manuscript.published
},
)
const reviewIsByCurrentUser = rule({ cache: 'contextual' })(
async (parent, args, ctx, info) => {
const rows =
ctx.user &&
ctx.user
.$relatedQuery('teams')
.where({ role: 'reviewer' })
.resultSize()
return !!rows
},
)
const isAuthenticated = rule({ cache: 'contextual' })(
async (parent, args, ctx, info) => !!ctx.user,
)
// Who can send a message to a channel?
// Configured like so now:
// if the channel is for 'all', then all associated with the manuscript can create messages
// if the channel is for 'editorial', only editors and admins can chat there
const userIsAllowedToChat = rule({ cache: 'strict' })(
async (parent, args, ctx, info) => {
if (ctx.user && ctx.user.admin) {
return true
}
const channel = await ctx.models.Channel.query().findById(args.channelId)
const manuscript = await ctx.models.Manuscript.query().findById(
channel.manuscriptId,
)
const isAuthor = await _userIsMemberOfTeamWithRole(
ctx.user,
manuscript.id,
'author',
)
const isReviewer = await _userIsMemberOfTeamWithRole(
ctx.user,
manuscript.id,
'reviewer',
)
const isEditor = await _userIsEditor(ctx.user, manuscript.id)
if (channel.type === 'all') {
return isAuthor || isReviewer || isEditor
} else if (channel.type === 'editorial') {
return isReviewer || isEditor
}
},
)
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,
)
const team = await ctx.models.Team.query()
.where({
manuscriptId: manuscript.id,
role: 'reviewer',
})
.first()
const members = await team
.$relatedQuery('members')
.where('userId', ctx.user.id)
if (members && members[0] && members[0].status !== 'completed') {
return true
}
return false
})
const user_is_invited_reviewer = rule({ cache: 'strict' })(
async (parent, args, ctx, info) => {
const team = await ctx.models.Team.query().findById(args.teamId)
const member = await team
.$relatedQuery('members')
.where({ userId: ctx.user.id, status: 'invited' })
.first()
return !!member
},
)
const user_is_author = rule({ cache: 'strict' })(
async (parent, args, ctx, info) => {
const team = await ctx.models.Team.query()
.where({
manuscriptId: args.id,
role: 'author',
})
.first()
const author = team
.$relatedQuery('members')
.where({ userId: ctx.user.id })
.first()
return !!author
},
)
const permissions = shield(
{
Query: {
paginatedManuscripts: isAdmin,
currentUser: isAuthenticated,
paginatedManuscripts: userIsAdmin,
detailsForURL: allow,
publishedManuscripts: allow,
manuscripts: allow,
manuscript: allow,
messages: allow,
getFile: allow,
},
Mutation: {
createManuscript: isAuthenticated,
updateManuscript: user_is_author,
createMessage: userIsAllowedToChat,
updateReview: user_is_review_author_and_review_is_not_completed,
reviewerResponse: user_is_invited_reviewer,
},
Subscription: {
messageCreated: userIsAllowedToChat,
},
CurrentRole: allow,
Team: allow,
TeamMember: allow,
URLMetadata: allow,
// Fruit: isAuthenticated,
// Customer: isAdmin,
User: allow,
PaginatedManuscripts: allow,
Manuscript: allow,
Review: or(parentManuscriptIsPublished, reviewIsByCurrentUser),
ReviewComment: allow,
Channel: allow,
Message: allow,
MessagesRelay: allow,
PageInfo: allow,
ManuscriptMeta: allow,
Note: allow,
Identity: allow,
},
{
allowExternalErrors: true,
fallbackRule: or(isAdmin, isEditor),
allowExternalErrors: false,
debug: true,
fallbackRule: or(userIsAdmin, userIsEditor),
},
)
module.exports = permissions
// const userIsEditorOfManuscript = rule({
// cache: 'strict',
// })(async (parent, args, ctx, info) =>
// _userIsEditor(ctx.user, parent.manuscriptId),
// )
// const userIsAuthorOfManuscript = rule({
// cache: 'strict',
// })(async (parent, args, ctx, info) =>
// _userIsMemberOfTeamWithRole(ctx.user, parent.manuscriptId, 'author'),
// )
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment