feat(graphql): scaffoled permission mutations on streams

Also, intentionally broke a test on how permissions are handled.
This commit is contained in:
Dimitrie Stefanescu
2020-04-21 00:11:15 +01:00
parent 200331aacb
commit 0225fcedc0
4 changed files with 42 additions and 14 deletions
+23 -2
View File
@@ -1,4 +1,5 @@
'use strict'
const { AuthorizationError, ApolloError } = require( 'apollo-server-express' )
const root = require( 'app-root-path' )
const { createStream, getStream, updateStream, deleteStream, getUserStreams, getStreamUsers, grantPermissionsStream, revokePermissionsStream } = require( '../../streams/services' )
const { validateScopes, authorizeResolver } = require( `${root}/modules/shared` )
@@ -23,8 +24,8 @@ module.exports = {
async streamCollection( parent, args, context, info ) {
// TODO: Return only the user's public streams if parent.id !== context.userId
let streams = await getUserStreams( parent.id )
// TODO: Implement offsets in service
return { totalCount: streams.length, streams: streams.slice( args.offset, args.offset + args.limit ) }
// TODO: Implement offsets in service, not in friggin array slice
return { totalCount: streams.length, streams: streams.slice( args.offset, args.offset + args.limit ) }
}
},
Mutation: {
@@ -35,10 +36,30 @@ module.exports = {
return id
},
async streamUpdate( parent, args, context, info ) {
await validateScopes( context.scopes, 'streams:write' )
await authorizeResolver( context.userId, args.stream.id, 'stream_acl', 'streams', 'owner' )
return await updateStream( args.stream )
},
async streamDelete( parent, args, context, info ) {
await validateScopes( context.scopes, 'streams:write' )
await authorizeResolver( context.userId, args.id, 'stream_acl', 'streams', 'owner' )
return await deleteStream( args.id )
},
async streamClone( parent, args, context, info ) {
// TODO
},
async streamGrantPermission( parent, args, context, info ) {
await validateScopes( context.scopes, 'streams:write' )
await authorizeResolver( context.userId, args.streamId, 'stream_acl', 'streams', 'owner' )
if ( context.userId === args.userId ) throw new AuthorizationError( 'You cannot set roles for yourself.' )
return await grantPermissionsStream( args.streamId, args.userId, args.role.toLowerCase( ) || 'read' )
},
async streamRevokePermission( parent, args, context, info ) {
await validateScopes( context.scopes, 'streams:write' )
await authorizeResolver( context.userId, args.streamId, 'stream_acl', 'streams', 'owner' )
return await revokePermissionsStream( args.streamId, context.userId )
}
}
}
+5 -5
View File
@@ -38,11 +38,11 @@ enum StreamUserRole {
"""
Contributors can read and write.
"""
CONTRIBUTOR,
WRITE, # CONTRIBUTOR
"""
Reviewers can view and pull.
"""
REVIEWER
READ # REVIEWER
}
extend type Mutation {
@@ -56,17 +56,17 @@ extend type Mutation {
"""
Grants permissions to an user on a given stream.
"""
streamGrantPermissions( streamId: String!, userId: String!, role: StreamUserRole! ): Boolean
streamGrantPermission( streamId: String!, userId: String!, role: StreamUserRole! ): Boolean
"""
Revokes the permissions of an user on a given stream.
"""
streamRevokePermissions( streamId: String!, userId: String! ): Boolean
streamRevokePermission( streamId: String!, userId: String! ): Boolean
}
input StreamInput {
id: String
name: String!
name: String
description: String
isPublic: Boolean
}
+12 -5
View File
@@ -37,10 +37,12 @@ module.exports = {
},
async grantPermissionsStream( streamId, userId, role ) {
if ( role === 'owner' ) {
let [ ownerAcl ] = await Acl( ).where( { resourceId: streamId, role: 'owner' } ).returning( '*' ).del( )
await Acl( ).insert( { resourceId: streamId, userId: ownerAcl.userId, role: 'write' } )
}
// NOTE: allow streams to have more than one owner.
// That's why the code below is commented out.
// if ( role === 'owner' ) {
// let [ ownerAcl ] = await Acl( ).where( { resourceId: streamId, role: 'owner' } ).returning( '*' ).del( )
// await Acl( ).insert( { resourceId: streamId, userId: ownerAcl.userId, role: 'write' } )
// }
// upsert
let query = Acl( ).insert( { userId: userId, resourceId: streamId, role: role } ).toString( ) + ` on conflict on constraint stream_acl_pkey do update set role=excluded.role`
@@ -49,8 +51,13 @@ module.exports = {
},
async revokePermissionsStream( streamId, userId ) {
let streamAclEntries = Acl( ).where( { resourceId: streamId } ).select( '*' )
let streamAclEntriesCount = Acl( ).count( { resourceId: streamId } )
// TODO: check if streamAclEntriesCount === 1 then throw big boo-boo (can't delete last ownership link)
// TODO: below behaviour not correct. Flow:
// Count owners
// If owner count > 1, then proceed to delete, otherwise throw an error (can't delete last owner - delete stream)
let delCount = await Acl( ).where( { resourceId: streamId, userId: userId } ).whereNot( { role: 'owner' } ).del( )
if ( delCount === 0 )
throw new Error( 'Could not revoke permissions for user. Is he an owner?' )
},
+2 -2
View File
@@ -1,5 +1,5 @@
'use strict'
const { ForbiddenError } = require( 'apollo-server-express' )
const { ForbiddenError, ApolloError } = require( 'apollo-server-express' )
const debug = require( 'debug' )( 'speckle:middleware' )
const root = require( 'app-root-path' )
const knex = require( `${root}/db/knex` )
@@ -126,7 +126,7 @@ function authorize( aclTable, resourceTable, requiredRole ) {
*/
async function authorizeResolver( userId, resourceId, aclTable, resourceTable, role ) {
async function authorizeResolver( userId, resourceId, aclTable, resourceTable, requiredRole ) {
let ACL = ( ) => knex( aclTable )
let Resource = ( ) => knex( resourceTable )