feat(graphql): scaffoled permission mutations on streams
Also, intentionally broke a test on how permissions are handled.
This commit is contained in:
@@ -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 )
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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?' )
|
||||
},
|
||||
|
||||
@@ -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 )
|
||||
|
||||
|
||||
Reference in New Issue
Block a user