From 302dfe050071a8b8d8c8d1538ead55cef4bcb51e Mon Sep 17 00:00:00 2001 From: Dimitrie Stefanescu Date: Tue, 14 Jul 2020 20:02:33 +0100 Subject: [PATCH] test(upload/download): implemented some of the up/down tests --- modules/core/rest/download.js | 20 +++++- modules/core/rest/upload.js | 23 +++++-- modules/core/tests/rest.spec.js | 118 +++++++++++++++++++++++++------- package-lock.json | 10 +++ package.json | 3 +- 5 files changed, 141 insertions(+), 33 deletions(-) diff --git a/modules/core/rest/download.js b/modules/core/rest/download.js index 31f97a27d..69bdf73ea 100644 --- a/modules/core/rest/download.js +++ b/modules/core/rest/download.js @@ -1,17 +1,31 @@ 'use strict' const zlib = require( 'zlib' ) const Busboy = require( 'busboy' ) -let debug = require( 'debug' ) +const debug = require( 'debug' ) const appRoot = require( 'app-root-path' ) -const { contextMiddleware } = require( `${appRoot}/modules/shared` ) +const { contextMiddleware, validateScopes, authorizeResolver } = require( `${appRoot}/modules/shared` ) const { getObject, getObjectChildrenStream } = require( '../services/objects' ) module.exports = ( app ) => { app.get( '/objects/:streamId/:objectId', contextMiddleware, async ( req, res ) => { - // TODO: authN & authZ checks + if ( !req.context || !req.context.auth ) { + return res.status( 401 ).end( ) + } + + try { + await validateScopes( req.context.scopes, 'streams:read' ) + } catch ( err ) { + return res.status( 401 ).end( ) + } + + try { + await authorizeResolver( req.context.userId, req.params.streamId, 'stream:reviewer' ) + } catch ( err ) { + return res.status( 401 ).end( ) + } let simpleText = req.headers.accept === 'text/plain' diff --git a/modules/core/rest/upload.js b/modules/core/rest/upload.js index fdba88692..6006f998d 100644 --- a/modules/core/rest/upload.js +++ b/modules/core/rest/upload.js @@ -1,19 +1,32 @@ 'use strict' const zlib = require( 'zlib' ) const Busboy = require( 'busboy' ) -let debug = require( 'debug' ) +const debug = require( 'debug' ) +const appRoot = require( 'app-root-path' ) + +const { contextMiddleware, validateScopes, authorizeResolver } = require( `${appRoot}/modules/shared` ) const { createObjects, createObjectsBatched } = require( '../services/objects' ) module.exports = ( app ) => { - app.post( '/objects/:streamId', async ( req, res ) => { + app.post( '/objects/:streamId', contextMiddleware, async ( req, res ) => { - if ( !req.context.auth ) { + if ( !req.context || !req.context.auth ) { return res.status( 401 ).end( ) } - // TODO: authN & authZ checks -> can this user write to this stream? + try { + await validateScopes( req.context.scopes, 'streams:write' ) + } catch ( err ) { + return res.status( 401 ).end( ) + } + + try { + await authorizeResolver( req.context.userId, req.params.streamId, 'stream:contributor' ) + } catch ( err ) { + return res.status( 401 ).end( ) + } let busboy = new Busboy( { headers: req.headers } ) let totalProcessed = 0 @@ -35,7 +48,7 @@ module.exports = ( app ) => { } ) busboy.on( 'finish', ( ) => { - console.log( 'Done parsing ' + totalProcessed + ' objs ' + process.memoryUsage( ).heapUsed / 1024 / 1024 + ' mb mem' ) + debug( 'speckle:upload-endpoint' )( 'Done parsing ' + totalProcessed + ' objs ' + process.memoryUsage( ).heapUsed / 1024 / 1024 + ' mb mem' ) res.writeHead( 303, { Connection: 'close', Location: '/' } ) res.end( ) } ) diff --git a/modules/core/tests/rest.spec.js b/modules/core/tests/rest.spec.js index e54ebbfd8..c66ac6820 100644 --- a/modules/core/tests/rest.spec.js +++ b/modules/core/tests/rest.spec.js @@ -1,6 +1,9 @@ const chai = require( 'chai' ) const chaiHttp = require( 'chai-http' ) +const request = require( 'supertest' ) + const assert = require( 'assert' ) +const crypto = require( 'crypto' ) const appRoot = require( 'app-root-path' ) const { init, startHttp } = require( `${appRoot}/app` ) @@ -10,11 +13,18 @@ chai.use( chaiHttp ) const knex = require( `${appRoot}/db/knex` ) +const { createUser } = require( '../services/users' ) +const { createPersonalAccessToken } = require( '../services/tokens' ) +const { createStream } = require( '../services/streams' ) + describe( `Upload/Download Routes`, ( ) => { let userA = { name: 'd1', username: 'd1', email: 'd.1@speckle.systems', password: 'wow' } + let testStream = { + name: 'Test Stream 01', + description: 'wonderful test stream' + } - // let testServer let expressApp before( async ( ) => { await knex.migrate.rollback( ) @@ -22,42 +32,102 @@ describe( `Upload/Download Routes`, ( ) => { let { app } = await init( ) expressApp = app - // let { server } = await startHttp( app ) - // testServer = server + + userA.id = await createUser( userA ) + userA.token = `Bearer ${(await createPersonalAccessToken( userA.id, 'test token user A', [ 'streams:read', 'streams:write', 'users:read', 'users:email', 'tokens:write', 'tokens:read', 'profile:read', 'profile:email' ] ))}` + + testStream.id = await createStream( testStream, userA.id ) } ) - after( async ( ) => { + after( async ( ) => {} ) + + it( 'Should not allow upload requests without an authorization token or valid streamId', async ( ) => { + + // invalid token and streamId + let res = await chai.request( expressApp ).get( `/objects/wow_hack/null` ).set( 'Authorization', 'this is a hoax' ) + expect( res ).to.have.status( 401 ) + + // invalid token + res = await chai.request( expressApp ).get( `/objects/${testStream.id}/null` ).set( 'Authorization', 'this is a hoax' ) + expect( res ).to.have.status( 401 ) + + // invalid streamid + res = await chai.request( expressApp ).get( `/objects/${'thisDoesNotExist'}/null` ).set( 'Authorization', userA.token ) + expect( res ).to.have.status( 401 ) } ) - it( 'Should not allow upload requests without an authorization token', async ( ) => { - let res = await chai.request( expressApp ).post(`/objects/${streamId}`).set('Authorization', myToken ) - console.log( res ) + it( 'Should not allow download requests without an authorization token or valid streamId', async ( ) => { + // invalid token and streamId + let res = await chai.request( expressApp ).post( `/objects/wow_hack` ).set( 'Authorization', 'this is a hoax' ) + expect( res ).to.have.status( 401 ) + + // invalid token + res = await chai.request( expressApp ).post( `/objects/${testStream.id}` ).set( 'Authorization', 'this is a hoax' ) + expect( res ).to.have.status( 401 ) + + // invalid streamid + res = await chai.request( expressApp ).post( `/objects/${'thisDoesNotExist'}` ).set( 'Authorization', userA.token ) + expect( res ).to.have.status( 401 ) } ) - it( 'Should not allow download requests without an authorization token', async ( ) => { - assert.fail() - } ) - - it( 'Should not allow upload requests with a bogus stream id', async ( ) => { - assert.fail() - } ) - - it( 'Should not allow download requests with a bogus stream id', async ( ) => { - assert.fail() - } ) + let parentId it( 'Should properly upload a bunch of objects', async ( ) => { - // this is gonna be a difficult test to write... - assert.fail() + let objBatches = [ createManyObjects( 3000 ), createManyObjects( 3000 ), createManyObjects( 3000 ) ] + parentId = objBatches[0][0].id + + let res = + await request( expressApp ) + .post( `/objects/${testStream.id}` ) + .set( 'Authorization', userA.token ) + .attach( 'batch1', Buffer.from( JSON.stringify( objBatches[ 0 ] ), 'utf8' ) ) + .attach( 'batch2', Buffer.from( JSON.stringify( objBatches[ 1 ] ), 'utf8' ) ) + .attach( 'batch3', Buffer.from( JSON.stringify( objBatches[ 2 ] ), 'utf8' ) ) + + expect( res ).to.have.status( 303 ) } ) it( 'Should properly download an object, with all its children', async ( ) => { - // this is gonna be a difficult test to write... - assert.fail() + console.log( parentId ) + let res = await request( expressApp ) + .get(`/objects/${testStream.id}/${parentId}` ) + .set( 'Authorization', userA.token ) + + console.log( res.status) + assert.fail( ) } ) } ) -function sendRequest( auth, path, obj ) { - return chai.request( expressApp ).post( '/' ).set( 'Authorization', auth ).send( obj ) +function createManyObjects( amount, noise ) { + amount = amount || 10000 + noise = noise || Math.random( ) * 100 + + let objs = [ ] + + let base = { name: 'base bastard 2', noise: noise, __closure: {} } + objs.push( base ) + let k = 0 + + for ( let i = 0; i < amount; i++ ) { + let baby = { + name: `mr. ${i}`, + nest: { duck: i % 2 === 0, mallard: 'falsey', arr: [ i + 42, i, i ] }, + test: { value: i, secondValue: 'mallard ' + i % 10 }, + similar: k, + } + + if ( i % 3 === 0 ) k++ + getId( baby ) + + base.__closure[ baby.id ] = 1 + objs.push( baby ) + } + + getId( base ) + return objs +} + +function getId( obj ) { + obj.id = obj.id || crypto.createHash( 'md5' ).update( JSON.stringify( obj ) ).digest( 'hex' ) } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 09ed48c10..bc1849936 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5645,6 +5645,16 @@ } } }, + "supertest": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-4.0.2.tgz", + "integrity": "sha512-1BAbvrOZsGA3YTCWqbmh14L0YEq0EGICX/nBnfkfVJn7SrxQV1I3pMYjSzG9y/7ZU2V9dWqyqk2POwxlb09duQ==", + "dev": true, + "requires": { + "methods": "^1.1.2", + "superagent": "^3.8.3" + } + }, "supports-color": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", diff --git a/package.json b/package.json index e0568f36d..2e8ef3de1 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,8 @@ "cz-conventional-changelog": "^3.1.0", "http-proxy-middleware": "^1.0.4", "mocha": "^7.1.1", - "nyc": "^15.0.1" + "nyc": "^15.0.1", + "supertest": "^4.0.2" }, "config": { "commitizen": {