diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..ab561f3c56 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000000..9b4e6f9a52 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "env": { + "browser": true, + "commonjs": true, + "es2020": true + }, + "parserOptions": { + "ecmaVersion": 11 + }, + "rules": { + "arrow-spacing": [ 2, { "before": true, "after": true } ], + "array-bracket-spacing": [ 2, "always" ], + "block-spacing": [ 2, "always" ], + "camelcase": [ 1, { "properties": "always" } ], + "space-in-parens": [ 2, "always" ], + "keyword-spacing": 2 + } +} diff --git a/modules/core/graph/resolvers/branches.js b/modules/core/graph/resolvers/branches.js index 3fdcf87acb..4b2f20c206 100644 --- a/modules/core/graph/resolvers/branches.js +++ b/modules/core/graph/resolvers/branches.js @@ -1,7 +1,7 @@ 'use strict' const appRoot = require( 'app-root-path' ) -const { AuthorizationError, ApolloError } = require( 'apollo-server-express' ) +const { ForbiddenError, ApolloError } = require( 'apollo-server-express' ) const { validateServerRole, validateScopes, authorizeResolver } = require( `${appRoot}/modules/shared` ) const { @@ -25,10 +25,10 @@ module.exports = { Query: {}, Stream: { async branches( parent, args, context, info ) { - throw new ApolloError('not implemented') + throw new ApolloError( 'not implemented' ) }, - async branch( parent, args, context, info ) { - throw new ApolloError('not implemented') + async branch( parent, args, context, info) { + throw new ApolloError( 'not implemented' ) }, }, Branch: { @@ -40,26 +40,34 @@ module.exports = { async branchCreate( parent, args, context, info ) { await validateServerRole( context, 'server:user' ) await validateScopes( context.scopes, 'streams:write' ) - await authorizeResolver( context.userId, args.streamId, 'stream:contributor' ) + await authorizeResolver( context.userId, args.branch.streamId, 'stream:contributor' ) - let id = await createBranch( args.branch, args.streamId, context.userId ) + let id = await createBranch( { ...args.branch, authorId: context.userId } ) return id }, + async branchUpdate( parent, args, context, info ) { await validateServerRole( context, 'server:user' ) await validateScopes( context.scopes, 'streams:write' ) - await authorizeResolver( context.userId, args.streamId, 'stream:contributor' ) + await authorizeResolver( context.userId, args.branch.streamId, 'stream:contributor' ) - await updateBranch( args.branch ) - return true + return await updateBranch( { ...args.branch } ) }, + async branchDelete( parent, args, context, info ) { await validateServerRole( context, 'server:user' ) await validateScopes( context.scopes, 'streams:write' ) - await authorizeResolver( context.userId, args.streamId, 'stream:contributor' ) + let role = await authorizeResolver( context.userId, args.branch.streamId, 'stream:contributor' ) + + let branch = await getBranchById( { id: args.branch.id } ) + if ( !branch ) { + throw new ApolloError( 'Branch not found.' ) + } + + if ( branch.authorId !== context.userId && role !== 'stream:owner' ) + throw new ForbiddenError( 'Only the branch creator or stream owners are allowed to delete branches.' ) - await deleteBranchById( args.branchId ) - return true + return await deleteBranchById( { id: args.branch.id } ) } } -} \ No newline at end of file +} diff --git a/modules/core/graph/resolvers/streams.js b/modules/core/graph/resolvers/streams.js index 0d0e26ebea..b3a672e948 100644 --- a/modules/core/graph/resolvers/streams.js +++ b/modules/core/graph/resolvers/streams.js @@ -50,7 +50,7 @@ module.exports = { await validateScopes( context.scopes, 'streams:write' ) await authorizeResolver( context.userId, args.id, 'stream:owner' ) - await deleteStream( args.id ) + await deleteStream( { streamId: args.id } ) return true }, async streamGrantPermission( parent, args, context, info ) { diff --git a/modules/core/services/branches.js b/modules/core/services/branches.js index 91422b878d..042032389f 100644 --- a/modules/core/services/branches.js +++ b/modules/core/services/branches.js @@ -38,4 +38,4 @@ module.exports = { async deleteBranchById( { id } ) { return await Branches( ).where( { id: id } ).del( ) }, -} \ No newline at end of file +} diff --git a/modules/core/services/commits.js b/modules/core/services/commits.js index 9739d22b60..4a7cdfa85f 100644 --- a/modules/core/services/commits.js +++ b/modules/core/services/commits.js @@ -46,6 +46,7 @@ module.exports = { }, async createCommitByBranchName( { streamId, branchName, objectId, authorId, message, previousCommitIds } ) { + branchName = branchName.toLowerCase() let branches = await getBranchesByStreamId( { streamId: streamId } ) let myBranch = branches.find( b => b.name === branchName ) @@ -74,6 +75,7 @@ module.exports = { }, async getCommitsTotalCountByBranchName( { streamId, branchName } ) { + branchName = branchName.toLowerCase() let branches = await getBranchesByStreamId( { streamId: streamId } ) let myBranch = branches.find( b => b.name === branchName ) @@ -102,6 +104,7 @@ module.exports = { }, async getCommitsByBranchName( { streamId, branchName, limit, cursor } ) { + branchName = branchName.toLowerCase() let branches = await getBranchesByStreamId( { streamId: streamId } ) let myBranch = branches.find( b => b.name === branchName ) diff --git a/modules/core/tests/branches.spec.js b/modules/core/tests/branches.spec.js index d76a99ea60..972e4ad83c 100644 --- a/modules/core/tests/branches.spec.js +++ b/modules/core/tests/branches.spec.js @@ -97,4 +97,4 @@ describe( 'Branches', ( ) => { expect( branches ).to.have.lengthOf( 3 ) } ) -} ) \ No newline at end of file +} ) diff --git a/modules/core/tests/graph.spec.js b/modules/core/tests/graph.spec.js index c01fd21346..57056538f6 100644 --- a/modules/core/tests/graph.spec.js +++ b/modules/core/tests/graph.spec.js @@ -33,11 +33,11 @@ describe( 'GraphQL API Core', ( ) => { 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' ] ))}` + 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' ] ) )}` userB.id = await createUser( userB ) - userB.token = `Bearer ${(await createPersonalAccessToken( userB.id, 'test token user B', [ 'streams:read', 'streams:write', 'users:read', 'users:email', 'tokens:write', 'tokens:read', 'profile:read', 'profile:email' ] ))}` + userB.token = `Bearer ${( await createPersonalAccessToken( userB.id, 'test token user B', [ 'streams:read', 'streams:write', 'users:read', 'users:email', 'tokens:write', 'tokens:read', 'profile:read', 'profile:email' ] ) )}` userC.id = await createUser( userC ) - userC.token = `Bearer ${(await createPersonalAccessToken( userC.id, 'test token user B', [ 'streams:read', 'streams:write', 'users:read', 'users:email', 'tokens:write', 'tokens:read', 'profile:read', 'profile:email' ] ))}` + userC.token = `Bearer ${( await createPersonalAccessToken( userC.id, 'test token user B', [ 'streams:read', 'streams:write', 'users:read', 'users:email', 'tokens:write', 'tokens:read', 'profile:read', 'profile:email' ] ) )}` addr = `http://localhost:${process.env.PORT || 3000}` } ) @@ -66,9 +66,9 @@ describe( 'GraphQL API Core', ( ) => { let c2 = {} // some branches - let b1 = { name: 'branch 1', description: 'test branch' } - let b2 = { name: 'master', description: 'master branch' } - let b3 = { name: 'branch 3', description: 'wow' } + let b1 = {} + let b2 = {} + let b3 = {} describe( 'Mutations', ( ) => { @@ -154,7 +154,12 @@ describe( 'GraphQL API Core', ( ) => { expect( res.body.data.streamGrantPermission ).to.equal( true ) const res2 = await sendRequest( userB.token, { query: `mutation{ streamGrantPermission( streamId: "${ts5}", userId: "${userA.id}" role: "stream:owner") }` } ) + expect( res2 ).to.be.json + expect( res2.body.errors ).to.not.exist + const res3 = await sendRequest( userB.token, { query: `mutation{ streamGrantPermission( streamId: "${ts3}", userId: "${userC.id}" role: "stream:owner") }` } ) + expect( res3 ).to.be.json + expect( res3.body.errors ).to.not.exist } ) it( 'Should fail to grant permissions if not owner', async ( ) => { @@ -172,7 +177,7 @@ describe( 'GraphQL API Core', ( ) => { } ) it( 'Should update permissions', async ( ) => { - const res = await sendRequest( userA.token, { query: `mutation{ streamGrantPermission( streamId: "${ts1}", userId: "${userB.id}" role: "stream:reviewer") }` } ) + const res = await sendRequest( userA.token, { query: `mutation{ streamGrantPermission( streamId: "${ts1}", userId: "${userB.id}" role: "stream:contributor") }` } ) expect( res ).to.be.json expect( res.body.errors ).to.not.exist expect( res.body.data.streamGrantPermission ).to.equal( true ) @@ -218,6 +223,7 @@ describe( 'GraphQL API Core', ( ) => { it( 'Should delete a stream', async ( ) => { const res = await sendRequest( userB.token, { query: `mutation { streamDelete( id:"${ts4}")}` } ) + expect( res ).to.be.json expect( res.body.errors ).to.not.exist expect( res.body.data ).to.have.property( 'streamDelete' ) @@ -305,22 +311,36 @@ describe( 'GraphQL API Core', ( ) => { } ) it( 'Should create several branches', async ( ) => { - const res1 = await sendRequest( userA.token, { query: `mutation($branch:BranchCreateInput!) { branchCreate(streamId:"${ts1}", branch:$branch) }`, variables: { branch: b1 } } ) + b1 = { streamId: ts1, name: 'dim/dev', description: 'dimitries development branch' } + + const res1 = await sendRequest( userA.token, { query: `mutation( $branch:BranchCreateInput! ) { branchCreate( branch:$branch ) }`, variables: { branch: b1 } } ) expect( res1 ).to.be.json expect( res1.body.errors ).to.not.exist expect( res1.body.data ).to.have.property( 'branchCreate' ) expect( res1.body.data.branchCreate ).to.be.a( 'string' ) b1.id = res1.body.data.branchCreate - const res2 = await sendRequest( userB.token, { query: `mutation($branch:BranchCreateInput!) { branchCreate(streamId:"${ts1}", branch:$branch) }`, variables: { branch: b2 } } ) + b2 = { streamId: ts1, name: 'dim/dev/api-surgery', description: 'another branch' } + + const res2 = await sendRequest( userB.token, { query: `mutation( $branch:BranchCreateInput! ) { branchCreate( branch:$branch ) }`, variables: { branch: b2 } } ) + expect( res2.body.errors ).to.not.exist b2.id = res2.body.data.branchCreate - const res3 = await sendRequest( userB.token, { query: `mutation($branch:BranchCreateInput!) { branchCreate(streamId:"${ts1}", branch:$branch) }`, variables: { branch: b3 } } ) + b3 = { streamId: ts1, name: 'userB/dev/api', description: 'more branches branch' } + const res3 = await sendRequest( userB.token, { query: `mutation( $branch:BranchCreateInput! ) { branchCreate( branch:$branch ) }`, variables: { branch: b3 } } ) + expect( res3.body.errors ).to.not.exist b3.id = res3.body.data.branchCreate } ) it( 'Should update a branch', async ( ) => { - const res1 = await sendRequest( userA.token, { query: `mutation($branch:BranchUpdateInput!) { branchUpdate(streamId:"${ts1}", branch:$branch) }`, variables: { branch: { id: b1.id, commits: [ c1.id ] } } } ) + + let payload = { + streamId: ts1, + id: b2.id, + name: 'userb/whatever/whatever' + } + + const res1 = await sendRequest( userA.token, { query: `mutation( $branch:BranchUpdateInput! ) { branchUpdate( branch:$branch ) }`, variables: { branch: payload } } ) expect( res1 ).to.be.json expect( res1.body.errors ).to.not.exist expect( res1.body.data ).to.have.property( 'branchUpdate' ) @@ -328,11 +348,50 @@ describe( 'GraphQL API Core', ( ) => { } ) it( 'Should delete a branch', async ( ) => { - const res = await sendRequest( userA.token, { query: `mutation { branchDelete(streamId:"${ts1}", branchId:"${b2.id}")}` } ) + + // give C some access permissions + const perms = await sendRequest( userA.token, { query: `mutation{ streamGrantPermission( streamId: "${ts1}", userId: "${userC.id}" role: "stream:contributor") }` } ) + + let payload = { + streamId: ts1, + id: b2.id + } + + let badPayload = { + streamId: ts1, + id: 'APRIL FOOOLS!' + } + + const res = await sendRequest( userC.token, { query: `mutation( $branch:BranchDeleteInput! ) { branchDelete( branch: $branch ) }`, variables: { branch: badPayload } } ) + expect( res ).to.be.json + expect( res.body.errors ).to.exist + expect( res.body.errors[ 0 ].message ).to.equal( 'Branch not found.' ) + + const res1 = await sendRequest( userC.token, { query: `mutation( $branch:BranchDeleteInput! ) { branchDelete( branch: $branch ) }`, variables: { branch: payload } } ) + expect( res1 ).to.be.json + expect( res1.body.errors ).to.exist + expect( res1.body.errors[ 0 ].message ).to.equal( 'Only the branch creator or stream owners are allowed to delete branches.' ) + + const res2 = await sendRequest( userA.token, { query: `mutation( $branch:BranchDeleteInput! ) { branchDelete( branch: $branch ) }`, variables: { branch: payload } } ) + expect( res2 ).to.be.json + expect( res2.body.errors ).to.not.exist + + // revoke perms for c back (dont' wanna mess up our integration-unit tests below) + await sendRequest( userA.token, { query: `mutation{ streamRevokePermission( streamId: "${ts1}", userId: "${userC.id}" ) }` } ) + } ) + + it( 'Should commit to a non-master branch as well...', async ( ) => { + let cc = {} + cc.message = 'what a message for a second commit' + cc.streamId = ts1 + cc.objectId = objIds[ 3 ] + cc.branchName = 'userB/dev/api' + + let res = await sendRequest( userB.token, { query: `mutation( $myCommit: CommitCreateInput! ) { commitCreate( commit: $myCommit ) }`, variables: { myCommit: cc } } ) expect( res ).to.be.json expect( res.body.errors ).to.not.exist - expect( res.body.data ).to.have.property( 'branchDelete' ) - expect( res.body.data.branchDelete ).to.equal( true ) + expect( res.body.data ).to.have.property( 'commitCreate' ) + expect( res.body.data.commitCreate ).to.be.a( 'string' ) } ) } ) @@ -341,61 +400,73 @@ describe( 'GraphQL API Core', ( ) => { describe( 'Queries', ( ) => { - it( 'Should retrieve my profile', async ( ) => { - const res = await sendRequest( userA.token, { query: `{ user { id name email role apiTokens { id name } } }` } ) - expect( res ).to.be.json - expect( res.body.errors ).to.not.exist - expect( res.body.data ).to.have.property( 'user' ) - expect( res.body.data.user.name ).to.equal( 'MiticÄ' ) - expect( res.body.data.user.email ).to.equal( 'd.1@speckle.systems' ) - expect( res.body.data.user.role ).to.equal( 'server:admin' ) - } ) + describe( 'My Profile', ( ) => { - it( 'Should retrieve a different profile profile', async ( ) => { - const res = await sendRequest( userA.token, { query: ` { user(id:"${userB.id}") { id name email } }` } ) + it( 'Should retrieve my profile', async ( ) => { + const res = await sendRequest( userA.token, { query: `{ user { id name email role apiTokens { id name } } }` } ) + expect( res ).to.be.json + expect( res.body.errors ).to.not.exist + expect( res.body.data ).to.have.property( 'user' ) + expect( res.body.data.user.name ).to.equal( 'MiticÄ' ) + expect( res.body.data.user.email ).to.equal( 'd.1@speckle.systems' ) + expect( res.body.data.user.role ).to.equal( 'server:admin' ) + } ) - expect( res ).to.be.json - expect( res.body.errors ).to.not.exist - expect( res.body.data ).to.have.property( 'user' ) - expect( res.body.data.user.name ).to.equal( 'd2' ) - expect( res.body.data.user.email ).to.equal( 'd.2@speckle.systems' ) - } ) - it( 'Should not retrieve a profile if no auth', async ( ) => { - const res = await sendRequest( null, { query: `{ user { id name email } }` } ) - expect( res ).to.be.json - expect( res.body.errors ).to.exist - } ) + it( 'Should retrieve my streams', async ( ) => { + const res = await sendRequest( userA.token, { query: `{ user { streamCollection { totalCount streams { id name role } } } }` } ) + expect( res ).to.be.json + expect( res.body.errors ).to.not.exist + expect( res.body.data.user.streamCollection.totalCount ).to.equal( 3 ) + + let streams = res.body.data.user.streamCollection.streams + let s1 = streams.find( s => s.name === 'TS1 (u A) Private UPDATED' ) + expect( s1 ).to.exist + } ) - it( 'Should not retrieve user email field if out of scope', async ( ) => { - // token1 has only users:read scope - const res = await sendRequest( token1, { query: ` { user(id:"${userB.id}") { id name email } }` } ) - expect( res ).to.be.json - expect( res.body.errors ).to.not.exist - expect( res.body.data.user.email ).to.be.null } ) - it( 'Should retrieve my streams', async ( ) => { - const res = await sendRequest( userA.token, { query: `{ user { streamCollection { totalCount streams { id name role } } } }` } ) - expect( res ).to.be.json - expect( res.body.errors ).to.not.exist - expect( res.body.data.user.streamCollection.totalCount ).to.equal( 3 ) + describe( 'Different Users` Profile', ( ) => { - let streams = res.body.data.user.streamCollection.streams - let s1 = streams.find( s => s.name === 'TS1 (u A) Private UPDATED' ) - expect( s1 ).to.exist - } ) + it( 'Should retrieve a different profile profile', async ( ) => { + const res = await sendRequest( userA.token, { query: ` { user(id:"${userB.id}") { id name email } }` } ) + + expect( res ).to.be.json + expect( res.body.errors ).to.not.exist + expect( res.body.data ).to.have.property( 'user' ) + expect( res.body.data.user.name ).to.equal( 'd2' ) + expect( res.body.data.user.email ).to.equal( 'd.2@speckle.systems' ) + } ) + + it( 'Should not retrieve a profile if no auth', async ( ) => { + const res = await sendRequest( null, { query: `{ user { id name email } }` } ) + expect( res ).to.be.json + expect( res.body.errors ).to.exist + } ) + + it( 'Should not retrieve user email field if out of scope', async ( ) => { + // token1 has only users:read scope + const res = await sendRequest( token1, { query: ` { user(id:"${userB.id}") { id name email } }` } ) + expect( res ).to.be.json + expect( res.body.errors ).to.not.exist + expect( res.body.data.user.email ).to.be.null + } ) + + it( 'Should only retrieve public streams from a different user profile ', async ( ) => { + const res = await sendRequest( token1, { query: `query { user(id:"${userB.id}") { streamCollection { totalCount streams { id name isPublic role }} } }` } ) + expect( res ).to.be.json + expect( res.body.errors ).to.not.exist + expect( res.body.data.user.streamCollection.totalCount ).to.equal( 1 ) + } ) - it( 'Should only retrieve public streams from a different user profile ', async ( ) => { - const res = await sendRequest( token1, { query: `query { user(id:"${userB.id}") { streamCollection { totalCount streams { id name isPublic role }} } }` } ) - expect( res ).to.be.json - expect( res.body.errors ).to.not.exist - expect( res.body.data.user.streamCollection.totalCount ).to.equal( 1 ) } ) - let retrievedStream - it( 'Should fully retrieve a stream', async ( ) => { - const res = await sendRequest( userA.token, { query: `query { + describe( 'Streams', ( ) => { + + let retrievedStream + + it( 'Should fully retrieve a stream', async ( ) => { + const res = await sendRequest( userA.token, { query: `query { stream(id:"${ts1}") { id name @@ -437,39 +508,39 @@ describe( 'GraphQL API Core', ( ) => { } }` } ) - expect( res ).to.be.json - expect( res.body.errors ).to.not.exist + expect( res ).to.be.json + expect( res.body.errors ).to.not.exist - let stream = res.body.data.stream - retrievedStream = stream + let stream = res.body.data.stream + retrievedStream = stream - expect( stream.name ).to.equal( 'TS1 (u A) Private UPDATED' ) - expect( stream.tags.totalCount ).to.equal( 2 ) - expect( stream.branches.totalCount ).to.equal( 1 ) - expect( stream.commits.totalCount ).to.equal( 2 ) - expect( stream.users ).to.have.lengthOf( 2 ) - } ) + expect( stream.name ).to.equal( 'TS1 (u A) Private UPDATED' ) + expect( stream.tags.totalCount ).to.equal( 2 ) + expect( stream.branches.totalCount ).to.equal( 1 ) + expect( stream.commits.totalCount ).to.equal( 2 ) + expect( stream.users ).to.have.lengthOf( 2 ) + } ) - it( 'should retrieve a stream branch', async ( ) => { - // note: adding another commit for the sake of it - const res1 = await sendRequest( userA.token, { query: `mutation($branch:BranchUpdateInput!) { branchUpdate(streamId:"${ts1}", branch:$branch) }`, variables: { branch: { id: b1.id, commits: [ c2.id ] } } } ) - const res = await sendRequest( userA.token, { query: `query { stream(id:"${ts1}") { branch(id:"${retrievedStream.branches.branches[0].id}") { name description commits { totalCount } } } } ` } ) + it( 'should retrieve a stream branch', async ( ) => { + // note: adding another commit for the sake of it + const res1 = await sendRequest( userA.token, { query: `mutation($branch:BranchUpdateInput!) { branchUpdate(streamId:"${ts1}", branch:$branch) }`, variables: { branch: { id: b1.id, commits: [ c2.id ] } } } ) + const res = await sendRequest( userA.token, { query: `query { stream(id:"${ts1}") { branch(id:"${retrievedStream.branches.branches[0].id}") { name description commits { totalCount } } } } ` } ) - expect( res ).to.be.json - expect( res.body.errors ).to.not.exist - expect( res.body.data.stream.branch.name ).to.equal( 'branch 1' ) - expect( res.body.data.stream.branch.commits.totalCount ).to.equal( 2 ) + expect( res ).to.be.json + expect( res.body.errors ).to.not.exist + expect( res.body.data.stream.branch.name ).to.equal( 'branch 1' ) + expect( res.body.data.stream.branch.commits.totalCount ).to.equal( 2 ) - } ) + } ) - it( 'should retrieve a stream commit', async ( ) => { - const res = await sendRequest( userA.token, { query: `query { stream(id:"${ts1}") { commit(id:"${c2.id}") { id description data } } }` } ) - expect( res ).to.be.json - expect( res.body.errors ).to.not.exist - expect( res.body.data.stream.commit.description ).to.equal( 'test second commit' ) + it( 'should retrieve a stream commit', async ( ) => { + const res = await sendRequest( userA.token, { query: `query { stream(id:"${ts1}") { commit(id:"${c2.id}") { id description data } } }` } ) + expect( res ).to.be.json + expect( res.body.errors ).to.not.exist + expect( res.body.data.stream.commit.description ).to.equal( 'test second commit' ) + } ) } ) - describe( 'Objects', ( ) => { let myCommit let myObjs @@ -681,4 +752,4 @@ function createManyObjects( shitTon, noise ) { function getAFuckingId( obj ) { obj.id = obj.id || crypto.createHash( 'md5' ).update( JSON.stringify( obj ) ).digest( 'hex' ) -} \ No newline at end of file +} diff --git a/modules/shared/index.js b/modules/shared/index.js index 64c7b15f4d..4b019f0534 100644 --- a/modules/shared/index.js +++ b/modules/shared/index.js @@ -37,7 +37,7 @@ async function contextApiTokenHelper( { req, res } ) { async function contextMiddleware( req, res, next ) { let result = await contextApiTokenHelper( { req, res } ) req.context = result - next() + next( ) } /* @@ -107,14 +107,14 @@ async function authorizeResolver( userId, resourceId, requiredRole ) { throw new ApolloError( `Resource of type ${role.resourceTarget} with ${resourceId} not found` ) } - let entry = await knex( role.aclTableName ).select( '*' ).where( { resourceId: resourceId, userId: userId } ).first( ) + let userAclEntry = await knex( role.aclTableName ).select( '*' ).where( { resourceId: resourceId, userId: userId } ).first( ) - if ( !entry ) throw new ForbiddenError( 'You are not authorized' ) + if ( !userAclEntry ) throw new ForbiddenError( 'You are not authorized' ) - entry.role = roles.find( r => r.name === entry.role ) + userAclEntry.role = roles.find( r => r.name === userAclEntry.role ) - if ( entry.role.weight >= role.weight ) - return true + if ( userAclEntry.role.weight >= role.weight ) + return userAclEntry.role.name else throw new ForbiddenError( 'You are not authorized' ) diff --git a/package-lock.json b/package-lock.json index bc18499365..69a3974c40 100644 --- a/package-lock.json +++ b/package-lock.json @@ -643,6 +643,18 @@ "negotiator": "0.6.2" } }, + "acorn": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", + "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", + "dev": true + }, + "acorn-jsx": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", + "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", + "dev": true + }, "aggregate-error": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", @@ -653,6 +665,18 @@ "indent-string": "^4.0.0" } }, + "ajv": { + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", + "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, "ansi-colors": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", @@ -977,6 +1001,12 @@ "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, "async-limiter": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", @@ -1263,8 +1293,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "optional": true + "dev": true }, "camelcase": { "version": "5.3.1", @@ -1881,6 +1910,12 @@ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, "default-require-extensions": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", @@ -1995,6 +2030,15 @@ "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "dev": true }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, "dotenv": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", @@ -2016,6 +2060,23 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + }, + "dependencies": { + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + } + } + }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -2070,17 +2131,229 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, + "eslint": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.5.0.tgz", + "integrity": "sha512-vlUP10xse9sWt9SGRtcr1LAC67BENcQMFeV+w5EvLEoFe3xJ8cF1Skd0msziRx/VMC+72B4DxreCE+OR12OA6Q==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "eslint-scope": "^5.1.0", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^1.3.0", + "espree": "^7.2.0", + "esquery": "^1.2.0", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash": "^4.17.19", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "lodash": { + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", + "dev": true + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "eslint-scope": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz", + "integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + }, "esm": { "version": "3.2.25", "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==" }, + "espree": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.2.0.tgz", + "integrity": "sha512-H+cQ3+3JYRMEIOl87e7QdHX70ocly5iW4+dttuR8iYSPr/hXKFb+7dBsZ7+u1adC4VrnPlTkv0+OwuPnDop19g==", + "dev": true, + "requires": { + "acorn": "^7.3.1", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.3.0" + } + }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, + "esquery": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", + "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.1.0.tgz", + "integrity": "sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -2330,11 +2603,23 @@ } } }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, "figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", @@ -2344,6 +2629,15 @@ "escape-string-regexp": "^1.0.5" } }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -2475,6 +2769,34 @@ } } }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + }, + "dependencies": { + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, "follow-redirects": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.11.0.tgz", @@ -2600,6 +2922,12 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, "gauge": { "version": "2.7.4", "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", @@ -3019,6 +3347,12 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, "ignore-walk": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", @@ -3032,7 +3366,6 @@ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", "dev": true, - "optional": true, "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -3042,8 +3375,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "optional": true + "dev": true } } }, @@ -3525,6 +3857,18 @@ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, "json5": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", @@ -3590,6 +3934,16 @@ } } }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, "liftoff": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz", @@ -3978,6 +4332,12 @@ "to-regex": "^3.0.1" } }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, "needle": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/needle/-/needle-2.3.3.tgz", @@ -4476,6 +4836,20 @@ "mimic-fn": "^1.0.0" } }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, "os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", @@ -4550,7 +4924,6 @@ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, - "optional": true, "requires": { "callsites": "^3.0.0" } @@ -4851,6 +5224,12 @@ "xtend": "^4.0.0" } }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -4865,6 +5244,12 @@ "fromentries": "^1.2.0" } }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, "proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", @@ -4874,6 +5259,12 @@ "ipaddr.js": "1.9.1" } }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, "qs": { "version": "6.7.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", @@ -4998,6 +5389,12 @@ "safe-regex": "^1.1.0" } }, + "regexpp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "dev": true + }, "release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", @@ -5285,6 +5682,17 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + } + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -5669,6 +6077,46 @@ "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, "tar": { "version": "4.4.13", "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", @@ -5715,6 +6163,12 @@ } } }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -5811,6 +6265,15 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==" }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, "type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -5916,6 +6379,15 @@ } } }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, "urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", @@ -5952,6 +6424,12 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.2.tgz", "integrity": "sha512-vy9V/+pKG+5ZTYKf+VcphF5Oc6EFiu3W8Nv3P3zIh0EqVI80ZxOzuPfe9EHjkFNvf8+xuTHVeei4Drydlx4zjw==" }, + "v8-compile-cache": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", + "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", + "dev": true + }, "v8flags": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.3.tgz", @@ -6047,6 +6525,15 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, "write-file-atomic": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", diff --git a/package.json b/package.json index 61fcc272ce..229ba6fb15 100644 --- a/package.json +++ b/package.json @@ -63,6 +63,7 @@ "chai-http": "^4.3.0", "concurrently": "^5.2.0", "cz-conventional-changelog": "^3.1.0", + "eslint": "^7.5.0", "http-proxy-middleware": "^1.0.4", "mocha": "^7.1.1", "nyc": "^15.0.1",