From 54b230f0202b40518cf3893fb4a7e9d9f92caf77 Mon Sep 17 00:00:00 2001 From: "Daigneau, Jeremy T" Date: Tue, 18 Jul 2023 12:51:51 -0400 Subject: [PATCH] #1013 updated onlyOrgWithRole middleware to onlyOrgWithPartnerRole. Added relevant tests --- src/controller/org.controller/index.js | 6 +- src/middleware/error.js | 7 + src/middleware/middleware.js | 9 +- .../middleware/onlyOrgWithPartnerRoleTest.js | 189 ++++++++++++++++++ 4 files changed, 205 insertions(+), 6 deletions(-) create mode 100644 test/unit-tests/middleware/onlyOrgWithPartnerRoleTest.js diff --git a/src/controller/org.controller/index.js b/src/controller/org.controller/index.js index 2be6777a..b2b5c128 100644 --- a/src/controller/org.controller/index.js +++ b/src/controller/org.controller/index.js @@ -545,7 +545,7 @@ router.post('/org/:shortname/user', */ mw.validateUser, mw.onlySecretariatOrAdmin, - mw.onlyOrgWithRole, + mw.onlyOrgWithPartnerRole, param(['shortname']).isString().trim().escape().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), body(['username']).isString().trim().escape().notEmpty().custom(isValidUsername), body(['org_uuid']).optional().isString().trim().escape(), @@ -712,7 +712,7 @@ router.put('/org/:shortname/user/:username', } */ mw.validateUser, - mw.onlyOrgWithRole, + mw.onlyOrgWithPartnerRole, query().custom((query) => { return mw.validateQueryParameterNames(query, ['active', 'new_username', 'org_short_name', 'name.first', 'name.last', 'name.middle', 'name.suffix', 'active_roles.add', 'active_roles.remove']) @@ -806,7 +806,7 @@ router.put('/org/:shortname/user/:username/reset_secret', } */ mw.validateUser, - mw.onlyOrgWithRole, + mw.onlyOrgWithPartnerRole, param(['shortname']).isString().trim().escape().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), param(['username']).isString().trim().escape().notEmpty().custom(isValidUsername), parseError, diff --git a/src/middleware/error.js b/src/middleware/error.js index 5d667328..afc658a1 100644 --- a/src/middleware/error.js +++ b/src/middleware/error.js @@ -63,6 +63,13 @@ class MiddlewareError extends idrErr.IDRError { return err } + orgHasNoPartnerRole (shortname) { // mw + const err = {} + err.error = 'ORG_HAS_NO_PARTNER_ROLE' + err.message = `The '${shortname}' organization designated by the shortname parameter does not have any partner roles.` + return err + } + orgDoesNotOwnId (org, id) { const err = { error: 'ORG_DOES_NOT_OWN_ID', diff --git a/src/middleware/middleware.js b/src/middleware/middleware.js index babe8376..8e1ff53f 100644 --- a/src/middleware/middleware.js +++ b/src/middleware/middleware.js @@ -255,7 +255,7 @@ async function onlyAdps (req, res, next) { } } // Checks that an org has a role or any sort -async function onlyOrgWithRole (req, res, next) { +async function onlyOrgWithPartnerRole (req, res, next) { const shortName = req.ctx.org const orgRepo = req.ctx.repositories.getOrgRepository() @@ -264,12 +264,15 @@ async function onlyOrgWithRole (req, res, next) { if (org === null) { logger.info({ uuid: req.ctx.uuid, message: shortName + ' does NOT exist ' }) return res.status(404).json(error.orgDoesNotExist(shortName)) + } else if (org.authority.active_roles.length === 1 && org.authority.active_roles[0] === 'BULK_DOWNLOAD') { + logger.info({ uuid: req.ctx.uuid, message: org.short_name + 'only has BULK_DOWNLOAD role ' }) + return res.status(403).json(error.orgHasNoPartnerRole(shortName)) } else if (org.authority.active_roles.length > 0) { logger.info({ uuid: req.ctx.uuid, message: org.short_name + ' has a role ' }) next() } else { logger.info({ uuid: req.ctx.uuid, message: org.short_name + ' does NOT have a role ' }) - return res.status(403).json(error.orgHasNoRole(shortName)) + return res.status(403).json(error.orgHasNoPartnerRole(shortName)) } } catch (err) { next(err) @@ -445,7 +448,7 @@ module.exports = { onlySecretariatOrAdmin, onlyCnas, onlyAdps, - onlyOrgWithRole, + onlyOrgWithPartnerRole, validateQueryParameterNames, cnaMustOwnID, createCtxAndReqUUID, diff --git a/test/unit-tests/middleware/onlyOrgWithPartnerRoleTest.js b/test/unit-tests/middleware/onlyOrgWithPartnerRoleTest.js new file mode 100644 index 00000000..789588a3 --- /dev/null +++ b/test/unit-tests/middleware/onlyOrgWithPartnerRoleTest.js @@ -0,0 +1,189 @@ +/* eslint-disable no-unused-expressions */ + +const chai = require('chai') +const sinon = require('sinon') +const { faker } = require('@faker-js/faker') +const expect = chai.expect + +const OrgRepository = require('../../../src/repositories/orgRepository.js') +const { onlyOrgWithPartnerRole } = require('../../../src/middleware/middleware.js') +const errors = require('../../../src/middleware/error.js') +const error = new errors.MiddlewareError() + +const stubAdpOrg = { + short_name: 'adpOrg', + name: 'test_adp', + UUID: faker.datatype.uuid(), + authority: { + active_roles: [ + 'ADP' + ] + } +} + +const stubCnaOrg = { + short_name: 'cnaOrg', + name: 'test_cna', + UUID: faker.datatype.uuid(), + authority: { + active_roles: [ + 'CNA' + ] + } +} + +const stubBulkDownloadOrg = { + short_name: 'bdOrg', + name: 'test_bd', + UUID: faker.datatype.uuid(), + authority: { + active_roles: [ + 'BULK_DOWNLOAD' + ] + } +} + +const stubOrgNoRole = { + short_name: 'NoRole', + name: 'test_org', + UUID: faker.datatype.uuid(), + authority: { + active_roles: [] + } +} + +const stubSecretariat = { + short_name: 'secOrg', + name: 'test_sec', + UUID: faker.datatype.uuid(), + authority: { + active_roles: [ + 'SECRETARIAT' + ] + } +} + +describe('Testing onlyOrgWithPartnerRole middleware', () => { + let status, json, res, next, getOrgRepository, orgRepo + beforeEach(() => { + status = sinon.stub() + json = sinon.spy() + res = { json, status } + next = sinon.spy() + status.returns(res) + orgRepo = new OrgRepository() + getOrgRepository = sinon.stub() + getOrgRepository.returns(orgRepo) + }) + context('Negative Tests', () => { + it('Should return 403 for users from orgs without a partner role ', async () => { + const req = { + ctx: { + org: stubBulkDownloadOrg.short_name, + uuid: stubBulkDownloadOrg.UUID, + repositories: { + getOrgRepository + } + } + } + const stub = sinon.stub(orgRepo, 'findOneByShortName').returns(stubBulkDownloadOrg) + + await onlyOrgWithPartnerRole(req, res, next) + expect(stub.calledOnce).to.be.true + expect(status.calledOnce).to.be.true + expect(status.args[0][0]).to.equal(403) + expect(res.json.args[0][0].error).to.equal(error.orgHasNoPartnerRole().error) + }) + it('Should return 403 for users from orgs without a role ', async () => { + const req = { + ctx: { + org: stubOrgNoRole.short_name, + uuid: stubOrgNoRole.UUID, + repositories: { + getOrgRepository + } + } + } + const stub = sinon.stub(orgRepo, 'findOneByShortName').returns(stubOrgNoRole) + + await onlyOrgWithPartnerRole(req, res, next) + expect(stub.calledOnce).to.be.true + expect(status.calledOnce).to.be.true + expect(status.args[0][0]).to.equal(403) + expect(res.json.args[0][0].error).to.equal(error.orgHasNoPartnerRole().error) + }) + + it('Should return 404 if the requester org does not exist', async () => { + const req = { + ctx: { + org: stubCnaOrg.short_name, + uuid: stubCnaOrg.UUID, + repositories: { + getOrgRepository + } + } + } + const stub = sinon.stub(orgRepo, 'findOneByShortName').returns(null) + + await onlyOrgWithPartnerRole(req, res, next) + expect(stub.calledOnce).to.be.true + expect(status.calledOnce).to.be.true + expect(status.args[0][0]).to.equal(404) + expect(res.json.args[0][0].error).to.equal(error.orgDoesNotExist(stubCnaOrg.short_name).error) + }) + }) + + context('Positive Tests', () => { + it('Should allow orgs with ADP partner role through by calling next() ', async () => { + const req = { + ctx: { + org: stubAdpOrg.short_name, + uuid: stubAdpOrg.UUID, + repositories: { + getOrgRepository + } + } + } + const stub = sinon.stub(orgRepo, 'findOneByShortName').returns(stubAdpOrg) + + await onlyOrgWithPartnerRole(req, res, next) + expect(stub.calledOnce).to.be.true + expect(status.calledOnce).to.be.false + expect(next.calledOnce).to.be.true + }) + it('Should allow orgs with CNA partner role through by calling next() ', async () => { + const req = { + ctx: { + org: stubCnaOrg.short_name, + uuid: stubCnaOrg.UUID, + repositories: { + getOrgRepository + } + } + } + const stub = sinon.stub(orgRepo, 'findOneByShortName').returns(stubCnaOrg) + + await onlyOrgWithPartnerRole(req, res, next) + expect(stub.calledOnce).to.be.true + expect(status.calledOnce).to.be.false + expect(next.calledOnce).to.be.true + }) + it('Should allow orgs with Secretariat role through by calling next() ', async () => { + const req = { + ctx: { + org: stubSecretariat.short_name, + uuid: stubSecretariat.UUID, + repositories: { + getOrgRepository + } + } + } + const stub = sinon.stub(orgRepo, 'findOneByShortName').returns(stubSecretariat) + + await onlyOrgWithPartnerRole(req, res, next) + expect(stub.calledOnce).to.be.true + expect(status.calledOnce).to.be.false + expect(next.calledOnce).to.be.true + }) + }) +})