feat(resolvers): added serverInfo route, bumped test coverage

This commit is contained in:
Dimitrie Stefanescu
2020-05-21 10:24:02 +01:00
parent bd5d7436f6
commit 59e03f80ee
8 changed files with 191 additions and 22 deletions
+29
View File
@@ -0,0 +1,29 @@
'use strict'
const root = require( 'app-root-path' )
const { AuthenticationError, UserInputError } = require( 'apollo-server-express' )
const { getAvailableScopes, getAvailableRoles, getServerName, getServerDescription, getAdminContact, getTOS } = require( '../../services/generic' )
module.exports = {
Query: {
async serverInfo( parent, args, context, info ) {
let si = {
name: await getServerName( ),
description: await getServerDescription( ),
adminContact: await getAdminContact( ),
tos: await getTOS( )
}
return si
}
},
ServerInfo: {
async roles( parent, args, context, info ) {
return await getAvailableRoles( )
},
async scopes( parent, args, context, info ) {
return await getAvailableScopes( )
}
},
Mutation: {}
}
-6
View File
@@ -34,12 +34,6 @@ module.exports = {
}
},
Mutation: {
// NOTE: this mutation will not exist, or will be enabled only if local user creation is enabled
async userCreate( parent, args, context, info ) {
let userId = await createUser( args.user )
let token = await createToken( userId, "Default Token", [ 'streams:read', 'streams:write' ] )
return token
},
async userEdit( parent, args, context, info ) {
await updateUser( context.userId, args.user )
return true
+32
View File
@@ -0,0 +1,32 @@
extend type Query {
serverInfo: ServerInfo!
}
"""
Information about this server.
"""
type ServerInfo {
name: String!
description: String!
adminContact: String!
tos: String!
roles: [Role]
scopes: [Scope]
}
"""
Available roles.
"""
type Role {
name: String!
description: String!
resourceTarget: String!
}
"""
Available scopes.
"""
type Scope {
name: String!
description: String!
}
+19 -7
View File
@@ -8,14 +8,26 @@ const Scopes = ( ) => knex( 'app_scopes' )
module.exports = {
async getAvailableScopes( ) {
return await Roles( ).select( '*' )
},
async getAvailableRoles( ) {
return await Scopes( ).select( '*' )
},
async getServerInfo( ) {
return { todo: true }
}
async getAvailableRoles( ) {
return await Roles( ).select( '*' )
},
async getServerName( ) {
return `TODO: True`
},
async getServerDescription( ) {
return `TODO: True`
},
async getAdminContact( ) {
return `TODO: True`
},
async getTOS( ) {
return `TODO: True`
},
}
+61
View File
@@ -0,0 +1,61 @@
const chai = require( 'chai' )
const assert = require( 'assert' )
const root = require( 'app-root-path' )
const { init } = require( `${root}/app` )
const knex = require( `${root}/db/knex` )
const expect = chai.expect
const { contextApiTokenHelper, validateScopes, authorizeResolver } = require( '../../shared' )
describe( 'Generic AuthN & AuthZ controller tests', ( ) => {
it( 'Validate scopes', async ( ) => {
try {
await validateScopes( )
assert.fail( 'Should have thrown an error with invalid input' )
} catch ( e ) {
//
}
try {
await validateScopes( [ 'a' ], 'b' )
assert.fail( 'Should have thrown an error' )
} catch ( e ) {
//
}
await validateScopes( [ 'a', 'b' ], 'b' ) // should pass
} )
it( 'Should create proper context', async ( ) => {
let res = await contextApiTokenHelper( { req: { headers: { authorization: 'Bearer BS' } } } )
expect( res.auth ).to.equal( false )
let res2 = await contextApiTokenHelper( { req: { headers: { authorization: null } } } )
expect( res2.auth ).to.equal( false )
let res3 = await contextApiTokenHelper( { req: { headers: { authorization: undefined } } } )
expect( res3.auth ).to.equal( false )
} )
it( 'Resolver Authorization Should fail nicely when roles & resources are wanky', async ( ) => {
try {
let res = await authorizeResolver( null, 'foo', 'bar' )
assert.fail( 'resolver authorization should have thrown' )
} catch ( e ) {
}
try {
let res = await authorizeResolver( 'foo', 'bar', 'streams:read' )
assert.fail( 'resolver authorization should have thrown' )
} catch ( e ) {
}
} )
} )
+38 -2
View File
@@ -150,7 +150,7 @@ describe( 'GraphQL API Core', ( ) => {
it( 'Should grant some permissions', async ( ) => {
const res = await sendRequest( userA.token, { query: `mutation{ streamGrantPermission( streamId: "${ts1}", userId: "${userB.id}" role: "stream:owner") }` } )
expect( res ).to.be.json
expect( res.body.errors ).to.not.exist
expect( res.body.data.streamGrantPermission ).to.equal( true )
@@ -195,7 +195,7 @@ describe( 'GraphQL API Core', ( ) => {
const resNotAuth = await sendRequest( userC.token, { query: `query { stream(id:"${ts3}") { id name role } }` } )
expect( resNotAuth ).to.be.json
expect( resNotAuth.body.errors ).to.exist
} )
it( 'Should fail to edit/write on a public stream if no access is provided', async ( ) => {
@@ -600,7 +600,43 @@ describe( 'GraphQL API Core', ( ) => {
} )
} )
} )
describe( 'Server Info', ( ) => {
it( 'Should return a valid server information object', async ( ) => {
let q = `
query{
serverInfo{
name
adminContact
tos
description
roles{
name
description
resourceTarget
}
scopes{
name
description
}
}
}`
let res = await sendRequest( null, { query: q } )
expect( res ).to.be.json
expect( res.body.errors ).to.not.exist
expect( res.body.data.serverInfo ).to.be.an( 'object' )
let si = res.body.data.serverInfo
expect( si.name ).to.be.a( 'string' )
expect( si.adminContact ).to.be.a( 'string' )
expect( si.tos ).to.be.a( 'string' )
expect( si.description ).to.be.a( 'string' )
expect( si.roles ).to.be.a( 'array' )
expect( si.scopes ).to.be.a( 'array' )
} )
} )
} )
+11 -7
View File
@@ -13,18 +13,22 @@ const { validateToken } = require( `${root}/modules/core/services/tokens` )
async function contextApiTokenHelper( { req, res } ) {
// TODO: Cache results for 5 minutes
if ( req.headers.authorization ) {
if ( req.headers.authorization != null ) {
try {
let token = req.headers.authorization.split( ' ' )[ 1 ]
let token = req.headers.authorization.split( ' ' )[ 1 ]
let { valid, scopes, userId } = await validateToken( token )
let { valid, scopes, userId } = await validateToken( token )
if ( !valid ) {
return { auth: false }
}
if ( !valid ) {
return { auth: false }
return { auth: true, userId, token, scopes }
} catch ( e ) {
return { auth: false, err: e }
}
return { auth: true, userId, token, scopes }
}
return { auth: false }
}
/*
+1
View File
@@ -8,6 +8,7 @@
"test": "PORT=3001 DEBUG=speckle:test,speckle:errors NODE_ENV=test nyc nyc --reporter=html mocha -s 0 --exit",
"test-watch": "PORT=3001 DEBUG=speckle:test,speckle:errors NODE_ENV=test mocha --watch -s 0 --exit",
"test-graph": "PORT=3001 DEBUG=speckle:test,speckle:errors NODE_ENV=test mocha ./modules/core/tests/graph.spec.js --watch -s 0 --exit --no-config",
"test-generic": "PORT=3001 DEBUG=speckle:test,speckle:errors NODE_ENV=test mocha ./modules/core/tests/generic.spec.js --watch -s 0 --exit --no-config",
"test-objects": "PORT=3001 DEBUG=speckle:test,speckle:errors NODE_ENV=test mocha ./modules/core/tests/objects.spec.js --watch -s 0 --exit --no-config",
"test-streams": "PORT=3001 DEBUG=speckle:test,speckle:errors NODE_ENV=test mocha ./modules/core/tests/streams.spec.js --watch -s 0 --exit --no-config",
"test-references": "PORT=3001 DEBUG=speckle:test,speckle:errors NODE_ENV=test mocha ./modules/core/tests/references.spec.js --watch -s 0 --exit --no-config",