From 664aea56a604b242e68a67257c3b3959d6f9cd6b Mon Sep 17 00:00:00 2001 From: Jack Flores Date: Wed, 24 Jul 2024 15:58:19 -0400 Subject: [PATCH 01/21] 1258 first draft, one test failing --- src/controller/org.controller/index.js | 2 +- .../org.controller/org.controller.js | 105 ++++++++++-------- src/middleware/middleware.js | 32 ++++++ src/model/org.js | 3 +- test/integration-tests/org/putOrgTest.js | 58 ++++++++++ 5 files changed, 152 insertions(+), 48 deletions(-) create mode 100644 test/integration-tests/org/putOrgTest.js diff --git a/src/controller/org.controller/index.js b/src/controller/org.controller/index.js index 15a160a6..abbac143 100644 --- a/src/controller/org.controller/index.js +++ b/src/controller/org.controller/index.js @@ -309,7 +309,7 @@ router.put('/org/:shortname', } */ mw.validateUser, - mw.onlySecretariat, + mw.validateOrg, query().custom((query) => { return mw.validateQueryParameterNames(query, ['new_short_name', 'id_quota', 'name', 'active_roles.add', 'active_roles.remove']) }), query(['new_short_name', 'id_quota', 'name', 'active_roles.add', 'active_roles.remove']).custom((val) => { return mw.containsNoInvalidCharacters(val) }), param(['shortname']).isString().trim().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), diff --git a/src/controller/org.controller/org.controller.js b/src/controller/org.controller/org.controller.js index d1a094b5..fd87893c 100644 --- a/src/controller/org.controller/org.controller.js +++ b/src/controller/org.controller/org.controller.js @@ -322,6 +322,7 @@ async function createOrg (req, res, next) { * Called by PUT /api/org/{shortname} **/ async function updateOrg (req, res, next) { + console.log("in controller") // todo: delete try { const shortName = req.ctx.params.shortname const newOrg = new Org() @@ -329,67 +330,77 @@ async function updateOrg (req, res, next) { const addRoles = [] const orgRepo = req.ctx.repositories.getOrgRepository() const org = await orgRepo.findOneByShortName(shortName) + const orgMakingChanges = req.ctx.org let agt = setAggregateOrgObj({ short_name: shortName }) + logger.info({uuid: req.ctx.uuid, message: 'UPDATING AN ORG'}) + + // org doesn't exist if (!org) { logger.info({ uuid: req.ctx.uuid, message: shortName + ' organization could not be updated in MongoDB because it does not exist.' }) return res.status(404).json(error.orgDnePathParam(shortName)) } - Object.keys(req.ctx.query).forEach(k => { - const key = k.toLowerCase() - - if (key === 'new_short_name') { - newOrg.short_name = req.ctx.query.new_short_name - agt = setAggregateOrgObj({ short_name: newOrg.short_name }) - } else if (key === 'name') { - newOrg.name = req.ctx.query.name - } else if (key === 'id_quota') { - newOrg.policies.id_quota = req.ctx.query.id_quota - } else if (key === 'active_roles.add') { - if (Array.isArray(req.ctx.query['active_roles.add'])) { - req.ctx.query['active_roles.add'].forEach(r => { - addRoles.push(r) - }) - } - } else if (key === 'active_roles.remove') { - if (Array.isArray(req.ctx.query['active_roles.remove'])) { - req.ctx.query['active_roles.remove'].forEach(r => { - removeRoles.push(r) - }) + newOrg.last_active = Date.now() + + const isSec = await orgRepo.isSecretariat(orgMakingChanges) + + if (isSec) { + Object.keys(req.ctx.query).forEach(k => { + const key = k.toLowerCase() + + if (key === 'new_short_name') { + newOrg.short_name = req.ctx.query.new_short_name + agt = setAggregateOrgObj({ short_name: newOrg.short_name }) + } else if (key === 'name') { + newOrg.name = req.ctx.query.name + } else if (key === 'id_quota') { + newOrg.policies.id_quota = req.ctx.query.id_quota + } else if (key === 'active_roles.add') { + if (Array.isArray(req.ctx.query['active_roles.add'])) { + req.ctx.query['active_roles.add'].forEach(r => { + addRoles.push(r) + }) + } + } else if (key === 'active_roles.remove') { + if (Array.isArray(req.ctx.query['active_roles.remove'])) { + req.ctx.query['active_roles.remove'].forEach(r => { + removeRoles.push(r) + }) + } } - } - }) + }) - // updating the org's roles - if (org) { - const roles = org.authority.active_roles + // updating the org's roles + if (org) { + const roles = org.authority.active_roles - // adding roles - addRoles.forEach(role => { - if (!roles.includes(role)) { - roles.push(role) - } - }) + // adding roles + addRoles.forEach(role => { + if (!roles.includes(role)) { + roles.push(role) + } + }) - // removing roles - removeRoles.forEach(role => { - const index = roles.indexOf(role) + // removing roles + removeRoles.forEach(role => { + const index = roles.indexOf(role) - if (index > -1) { - roles.splice(index, 1) - } - }) + if (index > -1) { + roles.splice(index, 1) + } + }) - newOrg.authority.active_roles = roles - } + newOrg.authority.active_roles = roles + } - if (newOrg.short_name) { - const result = await orgRepo.findOneByShortName(newOrg.short_name) + if (newOrg.short_name) { + const result = await orgRepo.findOneByShortName(newOrg.short_name) - if (result) { - return res.status(403).json(error.duplicateShortname(newOrg.short_name)) + if (result) { + return res.status(403).json(error.duplicateShortname(newOrg.short_name)) + } } } @@ -420,6 +431,7 @@ async function updateOrg (req, res, next) { logger.info(JSON.stringify(payload)) return res.status(200).json(responseMessage) } catch (err) { + console.log("err in congtroller: " + err) // todo: delete next(err) } } @@ -819,7 +831,8 @@ function setAggregateOrgObj (query) { name: true, 'authority.active_roles': true, 'policies.id_quota': true, - time: true + time: true, + last_active: true } } ] diff --git a/src/middleware/middleware.js b/src/middleware/middleware.js index ed497219..d83f1992 100644 --- a/src/middleware/middleware.js +++ b/src/middleware/middleware.js @@ -107,23 +107,27 @@ async function validateUser (req, res, next) { logger.info({ uuid: req.ctx.uuid, message: 'Authenticating user: ' + user }) // userUUID may be null if user does not exist const orgUUID = await orgRepo.getOrgUUID(org) if (!orgUUID) { + console.log("401 1: " + org + " is was not in db ") // todo: delete logger.info({ uuid: req.ctx.uuid, message: org + ' organization does not exist. User authentication FAILED for ' + user }) return res.status(401).json(error.unauthorized()) } const result = await userRepo.findOneByUserNameAndOrgUUID(user, orgUUID) if (!result) { + console.log("401 2") // todo: delete logger.warn(JSON.stringify({ uuid: req.ctx.uuid, message: 'User not found. User authentication FAILED for ' + user })) return res.status(401).json(error.unauthorized()) } if (!result.active) { + console.log("401 3") // todo: delete logger.warn(JSON.stringify({ uuid: req.ctx.uuid, message: 'User deactivated. Authentication failed for ' + user })) return res.status(401).json(error.unauthorized()) } const isPwd = await argon2.verify(result.secret, key) if (!isPwd) { + console.log("401 4") // todo: delete logger.warn(JSON.stringify({ uuid: req.ctx.uuid, message: 'Incorrect apikey. User authentication FAILED for ' + user })) return res.status(401).json(error.unauthorized()) } @@ -135,6 +139,33 @@ async function validateUser (req, res, next) { } } +async function validateOrg (req, res, next) { + console.log("in middleware") // todo: delete + const org = req.ctx.org + const reqOrg = req.params.shortname + const orgRepo = req.ctx.repositories.getOrgRepository() + const CONSTANTS = getConstants() + + try { + logger.info({ uuid: req.ctx.uuid, message: 'Authenticating org: ' + org }) + + const isSec = await orgRepo.isSecretariat(org) + if (!isSec) { + if (!(org == reqOrg)) { + console.log(org + " is not a sec and is not equal to " + reqOrg) // todo: delete + logger.info({ uuid: req.ctx.uuid, message: org + ' is not a ' + CONSTANTS.AUTH_ROLE_ENUM.SECRETARIAT + ' or the same as ' + reqOrg + ' and is not allowed to make these changes.' }) + return res.status(401).json(error.unauthorized()) + } + } + + logger.info({ uuid: req.ctx.uuid, message: 'Confirmed ' + org + ' has the authority to make changes to ' + reqOrg }) + next() + } catch (err) { + console.log("err in middle: " + err) // todo: delete + next(err) + } +} + // Checks that the requester belongs to an org that has the 'BULK_DOWNLOAD' role async function onlySecretariatOrBulkDownload (req, res, next) { const org = req.ctx.org @@ -483,6 +514,7 @@ module.exports = { setCacheControl, optionallyValidateUser, validateUser, + validateOrg, onlySecretariat, onlySecretariatOrBulkDownload, onlySecretariatOrAdmin, diff --git a/src/model/org.js b/src/model/org.js index 48f3b226..2c0964dc 100644 --- a/src/model/org.js +++ b/src/model/org.js @@ -24,7 +24,8 @@ const schema = { created: Date, modified: Date }, - inUse: Boolean + inUse: Boolean, + last_active: Date } const OrgSchema = new mongoose.Schema(schema, { collection: 'Org', timestamps: { createdAt: 'time.created', updatedAt: 'time.modified' } }) diff --git a/test/integration-tests/org/putOrgTest.js b/test/integration-tests/org/putOrgTest.js new file mode 100644 index 00000000..6924eeb6 --- /dev/null +++ b/test/integration-tests/org/putOrgTest.js @@ -0,0 +1,58 @@ +const chai = require('chai') +chai.use(require('chai-http')) +const expect = chai.expect + +const constants = require('../constants.js') +const app = require('../../../src/index.js') + +const params = {new_short_name: 'test_org', name: 'Test Organization', id_quota: '100000'} + +describe('Testing org put endpoint', () => { + context('Positive Tests', () => { + it('Allows update made by a secretariat to itself', async () => { + await chai.request(app) + .put('/api/org/mitre') + .set({ ...constants.headers }) + .query(params) + .send() + .then((res, err) => { + console.log(res.text) + expect(res).to.have.status(200) + expect(res.body.updated.short_name).to.equal(constants.testOrg.short_name) + expect(err).to.be.undefined + }) + }) + it('Allows update made by a secretariat to another org', async () => { + await chai.request(app) + .put('/api/org/cause_8') + .set({ ...constants.headers }) + .send() + .then((res, err) => { + expect(res).to.have.status(200) + expect(err).to.be.undefined + }) + }) + it('Allows update made by non secretariat org to itself', async () => { + await chai.request(app) + .put('/api/org/win_5') + .set({ ...constants.nonSecretariatUserHeaders }) + .send() + .then((res, err) => { + expect(res).to.have.status(200) + expect(err).to.be.undefined + }) + }) + }) + context('Negative Tests', () => { + it('Fails update made by a non-secretariat org to a different org', async () => { + await chai.request(app) + .put('/api/org/cause_8') + .set({ ...constants.nonSecretariatUserHeaders }) + .send() + .then((res, err) => { + expect(res).to.have.status(401) + expect(err).to.be.undefined + }) + }) + }) +}) \ No newline at end of file From a85cbb9e5826b1e674fdbf00ff9e13c886613ee3 Mon Sep 17 00:00:00 2001 From: jack-flores Date: Fri, 26 Jul 2024 14:33:33 -0400 Subject: [PATCH 02/21] all tests passing, more tests to write --- package-lock.json | 46 ++++++++++++------------ package.json | 1 + src/middleware/middleware.js | 5 --- test/integration-tests/org/putOrgTest.js | 6 ++-- 4 files changed, 27 insertions(+), 31 deletions(-) diff --git a/package-lock.json b/package-lock.json index ac792daf..0aa40813 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "cve-services", - "version": "2.3.3", + "version": "2.3.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "cve-services", - "version": "2.3.3", + "version": "2.3.1", "license": "(CC0)", "dependencies": { "ajv": "^8.6.2", @@ -2023,12 +2023,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -3882,9 +3882,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -4212,9 +4212,9 @@ } }, "node_modules/get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true, "engines": { "node": "*" @@ -11906,12 +11906,12 @@ } }, "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "requires": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" } }, "browser-stdout": { @@ -13288,9 +13288,9 @@ } }, "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "requires": { "to-regex-range": "^5.0.1" @@ -13534,9 +13534,9 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true }, "get-intrinsic": { @@ -18194,4 +18194,4 @@ "dev": true } } -} \ No newline at end of file +} diff --git a/package.json b/package.json index 2bc2cb1c..e733d223 100644 --- a/package.json +++ b/package.json @@ -96,6 +96,7 @@ "swagger-autogen": "node src/swagger.js", "test": "NODE_ENV=test mocha --recursive --exit || true", "test:integration": "NODE_ENV=test node-dev src/scripts/populate.js y; NODE_ENV=test mocha test/integration-tests --recursive --exit", + "test:put-org": "NODE_ENV=test node-dev src/scripts/populate.js y; NODE_ENV=test mocha test/integration-tests/org/putOrgTest.js --recursive --exit", "test:unit-tests": "NODE_ENV=test mocha test/unit-tests --recursive --exit || true", "test:coverage": "NODE_ENV=test nyc --reporter=text mocha src/* --recursive --exit || true", "test:coverage-html": "NODE_ENV=test nyc --reporter=html mocha src/* --recursive --exit || true", diff --git a/src/middleware/middleware.js b/src/middleware/middleware.js index d83f1992..8b0d67da 100644 --- a/src/middleware/middleware.js +++ b/src/middleware/middleware.js @@ -114,20 +114,17 @@ async function validateUser (req, res, next) { const result = await userRepo.findOneByUserNameAndOrgUUID(user, orgUUID) if (!result) { - console.log("401 2") // todo: delete logger.warn(JSON.stringify({ uuid: req.ctx.uuid, message: 'User not found. User authentication FAILED for ' + user })) return res.status(401).json(error.unauthorized()) } if (!result.active) { - console.log("401 3") // todo: delete logger.warn(JSON.stringify({ uuid: req.ctx.uuid, message: 'User deactivated. Authentication failed for ' + user })) return res.status(401).json(error.unauthorized()) } const isPwd = await argon2.verify(result.secret, key) if (!isPwd) { - console.log("401 4") // todo: delete logger.warn(JSON.stringify({ uuid: req.ctx.uuid, message: 'Incorrect apikey. User authentication FAILED for ' + user })) return res.status(401).json(error.unauthorized()) } @@ -140,7 +137,6 @@ async function validateUser (req, res, next) { } async function validateOrg (req, res, next) { - console.log("in middleware") // todo: delete const org = req.ctx.org const reqOrg = req.params.shortname const orgRepo = req.ctx.repositories.getOrgRepository() @@ -161,7 +157,6 @@ async function validateOrg (req, res, next) { logger.info({ uuid: req.ctx.uuid, message: 'Confirmed ' + org + ' has the authority to make changes to ' + reqOrg }) next() } catch (err) { - console.log("err in middle: " + err) // todo: delete next(err) } } diff --git a/test/integration-tests/org/putOrgTest.js b/test/integration-tests/org/putOrgTest.js index 6924eeb6..d865d231 100644 --- a/test/integration-tests/org/putOrgTest.js +++ b/test/integration-tests/org/putOrgTest.js @@ -5,7 +5,7 @@ const expect = chai.expect const constants = require('../constants.js') const app = require('../../../src/index.js') -const params = {new_short_name: 'test_org', name: 'Test Organization', id_quota: '100000'} +const params = {new_short_name: 'test_org', name: 'Test Organization', id_quota: 100000} describe('Testing org put endpoint', () => { context('Positive Tests', () => { @@ -13,12 +13,12 @@ describe('Testing org put endpoint', () => { await chai.request(app) .put('/api/org/mitre') .set({ ...constants.headers }) - .query(params) + .query({id_quota: '100000'}) .send() .then((res, err) => { console.log(res.text) expect(res).to.have.status(200) - expect(res.body.updated.short_name).to.equal(constants.testOrg.short_name) + expect(res.body.updated.policies.id_quota).to.equal(params.id_quota) expect(err).to.be.undefined }) }) From f967daa4570567bd77c1718640041252f1f6d3c4 Mon Sep 17 00:00:00 2001 From: jack-flores Date: Mon, 29 Jul 2024 14:29:15 -0400 Subject: [PATCH 03/21] #1258 all tests passing --- .../org.controller/org.controller.js | 2 - src/middleware/middleware.js | 2 - test/integration-tests/org/putOrgTest.js | 57 +++++++++++++++++-- 3 files changed, 52 insertions(+), 9 deletions(-) diff --git a/src/controller/org.controller/org.controller.js b/src/controller/org.controller/org.controller.js index fd87893c..69e7a1ed 100644 --- a/src/controller/org.controller/org.controller.js +++ b/src/controller/org.controller/org.controller.js @@ -322,7 +322,6 @@ async function createOrg (req, res, next) { * Called by PUT /api/org/{shortname} **/ async function updateOrg (req, res, next) { - console.log("in controller") // todo: delete try { const shortName = req.ctx.params.shortname const newOrg = new Org() @@ -431,7 +430,6 @@ async function updateOrg (req, res, next) { logger.info(JSON.stringify(payload)) return res.status(200).json(responseMessage) } catch (err) { - console.log("err in congtroller: " + err) // todo: delete next(err) } } diff --git a/src/middleware/middleware.js b/src/middleware/middleware.js index 8b0d67da..dcbd6b6f 100644 --- a/src/middleware/middleware.js +++ b/src/middleware/middleware.js @@ -107,7 +107,6 @@ async function validateUser (req, res, next) { logger.info({ uuid: req.ctx.uuid, message: 'Authenticating user: ' + user }) // userUUID may be null if user does not exist const orgUUID = await orgRepo.getOrgUUID(org) if (!orgUUID) { - console.log("401 1: " + org + " is was not in db ") // todo: delete logger.info({ uuid: req.ctx.uuid, message: org + ' organization does not exist. User authentication FAILED for ' + user }) return res.status(401).json(error.unauthorized()) } @@ -148,7 +147,6 @@ async function validateOrg (req, res, next) { const isSec = await orgRepo.isSecretariat(org) if (!isSec) { if (!(org == reqOrg)) { - console.log(org + " is not a sec and is not equal to " + reqOrg) // todo: delete logger.info({ uuid: req.ctx.uuid, message: org + ' is not a ' + CONSTANTS.AUTH_ROLE_ENUM.SECRETARIAT + ' or the same as ' + reqOrg + ' and is not allowed to make these changes.' }) return res.status(401).json(error.unauthorized()) } diff --git a/test/integration-tests/org/putOrgTest.js b/test/integration-tests/org/putOrgTest.js index d865d231..a9ef2c3a 100644 --- a/test/integration-tests/org/putOrgTest.js +++ b/test/integration-tests/org/putOrgTest.js @@ -5,7 +5,9 @@ const expect = chai.expect const constants = require('../constants.js') const app = require('../../../src/index.js') -const params = {new_short_name: 'test_org', name: 'Test Organization', id_quota: 100000} +const params = { name: 'Test Organization', id_quota: 100 } +const secretariat_params = { name: 'MITRE Corporation', id_quota: 100000 } +const cna_params = { name: 'Adams, Nielsen and Hensley', id_quota: 1309 } describe('Testing org put endpoint', () => { context('Positive Tests', () => { @@ -13,32 +15,67 @@ describe('Testing org put endpoint', () => { await chai.request(app) .put('/api/org/mitre') .set({ ...constants.headers }) - .query({id_quota: '100000'}) + .query(params) .send() .then((res, err) => { - console.log(res.text) expect(res).to.have.status(200) + expect(res.body.updated.name).to.equal(params.name) expect(res.body.updated.policies.id_quota).to.equal(params.id_quota) expect(err).to.be.undefined }) + await chai.request(app) + .put(`/api/org/mitre`) + .set({ ...constants.headers }) + .query(secretariat_params) + .send() + .then((res, err) => { + expect(res).to.have.status(200) + expect(res.body.updated.name).to.equal(secretariat_params.name) + expect(res.body.updated.policies.id_quota).to.equal(secretariat_params.id_quota) + expect(err).to.be.undefined + }) }) it('Allows update made by a secretariat to another org', async () => { await chai.request(app) - .put('/api/org/cause_8') + .put('/api/org/win_5') .set({ ...constants.headers }) + .query(params) .send() .then((res, err) => { expect(res).to.have.status(200) + expect(res.body.updated.name).to.equal(params.name) + expect(res.body.updated.policies.id_quota).to.equal(params.id_quota) + expect(err).to.be.undefined + }) + await chai.request(app) + .put('/api/org/win_5') + .set({ ...constants.headers }) + .query(cna_params) + .send() + .then((res, err) => { + expect(res).to.have.status(200) + expect(res.body.updated.name).to.equal(cna_params.name) + expect(res.body.updated.policies.id_quota).to.equal(cna_params.id_quota) expect(err).to.be.undefined }) }) - it('Allows update made by non secretariat org to itself', async () => { + it('Update made by non secretariat org to itself ONLY updates last_active field', async () => { + let now = Date.now() await chai.request(app) .put('/api/org/win_5') .set({ ...constants.nonSecretariatUserHeaders }) + .query(params) .send() .then((res, err) => { + // Assert that that the last_active field was updated under 2 seconds ago + let last_active = Date.parse(res.body.updated.last_active) + let diff = Math.abs(now - last_active) + let within_two_seconds = diff < 2000 + expect(within_two_seconds).to.be.true + // Assert no other fields were changed expect(res).to.have.status(200) + expect(res.body.updated.name).to.equal(cna_params.name) + expect(res.body.updated.policies.id_quota).to.equal(cna_params.id_quota) expect(err).to.be.undefined }) }) @@ -54,5 +91,15 @@ describe('Testing org put endpoint', () => { expect(err).to.be.undefined }) }) + it('Fails update made by a non-secretariat org to a secretariat', async () => { + await chai.request(app) + .put('/api/org/mitre') + .set({ ...constants.nonSecretariatUserHeaders }) + .send() + .then((res, err) => { + expect(res).to.have.status(401) + expect(err).to.be.undefined + }) + }) }) }) \ No newline at end of file From f98b83d730ed9a6d056fb63b3e42d0296db8b082 Mon Sep 17 00:00:00 2001 From: jack-flores Date: Mon, 29 Jul 2024 14:38:42 -0400 Subject: [PATCH 04/21] #1258 all tests passing --- package.json | 1 - src/controller/org.controller/org.controller.js | 3 --- 2 files changed, 4 deletions(-) diff --git a/package.json b/package.json index e733d223..2bc2cb1c 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,6 @@ "swagger-autogen": "node src/swagger.js", "test": "NODE_ENV=test mocha --recursive --exit || true", "test:integration": "NODE_ENV=test node-dev src/scripts/populate.js y; NODE_ENV=test mocha test/integration-tests --recursive --exit", - "test:put-org": "NODE_ENV=test node-dev src/scripts/populate.js y; NODE_ENV=test mocha test/integration-tests/org/putOrgTest.js --recursive --exit", "test:unit-tests": "NODE_ENV=test mocha test/unit-tests --recursive --exit || true", "test:coverage": "NODE_ENV=test nyc --reporter=text mocha src/* --recursive --exit || true", "test:coverage-html": "NODE_ENV=test nyc --reporter=html mocha src/* --recursive --exit || true", diff --git a/src/controller/org.controller/org.controller.js b/src/controller/org.controller/org.controller.js index 69e7a1ed..b31c673b 100644 --- a/src/controller/org.controller/org.controller.js +++ b/src/controller/org.controller/org.controller.js @@ -331,9 +331,6 @@ async function updateOrg (req, res, next) { const org = await orgRepo.findOneByShortName(shortName) const orgMakingChanges = req.ctx.org let agt = setAggregateOrgObj({ short_name: shortName }) - - logger.info({uuid: req.ctx.uuid, message: 'UPDATING AN ORG'}) - // org doesn't exist if (!org) { From a660d9719eaf9ed76bcecce9494a169a64931187 Mon Sep 17 00:00:00 2001 From: jack-flores Date: Mon, 29 Jul 2024 16:25:44 -0400 Subject: [PATCH 05/21] #1258 small changes to negative tests --- test/integration-tests/org/putOrgTest.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/integration-tests/org/putOrgTest.js b/test/integration-tests/org/putOrgTest.js index a9ef2c3a..006335c0 100644 --- a/test/integration-tests/org/putOrgTest.js +++ b/test/integration-tests/org/putOrgTest.js @@ -89,6 +89,8 @@ describe('Testing org put endpoint', () => { .then((res, err) => { expect(res).to.have.status(401) expect(err).to.be.undefined + expect(res.body).to.haveOwnProperty('error') + expect(res.body.error).to.equal('UNAUTHORIZED') }) }) it('Fails update made by a non-secretariat org to a secretariat', async () => { @@ -99,6 +101,8 @@ describe('Testing org put endpoint', () => { .then((res, err) => { expect(res).to.have.status(401) expect(err).to.be.undefined + expect(res.body).to.haveOwnProperty('error') + expect(res.body.error).to.equal('UNAUTHORIZED') }) }) }) From b94e2c251694dad5b8037b4a54fc0b2223e2e9df Mon Sep 17 00:00:00 2001 From: jack-flores Date: Wed, 31 Jul 2024 12:01:20 -0400 Subject: [PATCH 06/21] #1258 fixes for pr pipeline --- .../org.controller/org.controller.js | 49 +++-- src/middleware/middleware.js | 4 +- test/integration-tests/org/putOrgTest.js | 184 +++++++++--------- test/unit-tests/org/orgUpdateTest.js | 19 +- 4 files changed, 134 insertions(+), 122 deletions(-) diff --git a/src/controller/org.controller/org.controller.js b/src/controller/org.controller/org.controller.js index b31c673b..929c4853 100644 --- a/src/controller/org.controller/org.controller.js +++ b/src/controller/org.controller/org.controller.js @@ -331,7 +331,7 @@ async function updateOrg (req, res, next) { const org = await orgRepo.findOneByShortName(shortName) const orgMakingChanges = req.ctx.org let agt = setAggregateOrgObj({ short_name: shortName }) - + // org doesn't exist if (!org) { logger.info({ uuid: req.ctx.uuid, message: shortName + ' organization could not be updated in MongoDB because it does not exist.' }) @@ -339,7 +339,6 @@ async function updateOrg (req, res, next) { } newOrg.last_active = Date.now() - const isSec = await orgRepo.isSecretariat(orgMakingChanges) if (isSec) { @@ -367,36 +366,36 @@ async function updateOrg (req, res, next) { } } }) + } - // updating the org's roles - if (org) { - const roles = org.authority.active_roles + // updating the org's roles + if (org) { + const roles = org.authority.active_roles - // adding roles - addRoles.forEach(role => { - if (!roles.includes(role)) { - roles.push(role) - } - }) + // adding roles + addRoles.forEach(role => { + if (!roles.includes(role)) { + roles.push(role) + } + }) - // removing roles - removeRoles.forEach(role => { - const index = roles.indexOf(role) + // removing roles + removeRoles.forEach(role => { + const index = roles.indexOf(role) - if (index > -1) { - roles.splice(index, 1) - } - }) + if (index > -1) { + roles.splice(index, 1) + } + }) - newOrg.authority.active_roles = roles - } + newOrg.authority.active_roles = roles + } - if (newOrg.short_name) { - const result = await orgRepo.findOneByShortName(newOrg.short_name) + if (newOrg.short_name) { + const result = await orgRepo.findOneByShortName(newOrg.short_name) - if (result) { - return res.status(403).json(error.duplicateShortname(newOrg.short_name)) - } + if (result) { + return res.status(403).json(error.duplicateShortname(newOrg.short_name)) } } diff --git a/src/middleware/middleware.js b/src/middleware/middleware.js index dcbd6b6f..829a90d2 100644 --- a/src/middleware/middleware.js +++ b/src/middleware/middleware.js @@ -142,11 +142,11 @@ async function validateOrg (req, res, next) { const CONSTANTS = getConstants() try { - logger.info({ uuid: req.ctx.uuid, message: 'Authenticating org: ' + org }) + logger.info({ uuid: req.ctx.uuid, message: 'Authenticating org: ' + org }) const isSec = await orgRepo.isSecretariat(org) if (!isSec) { - if (!(org == reqOrg)) { + if (!(org === reqOrg)) { logger.info({ uuid: req.ctx.uuid, message: org + ' is not a ' + CONSTANTS.AUTH_ROLE_ENUM.SECRETARIAT + ' or the same as ' + reqOrg + ' and is not allowed to make these changes.' }) return res.status(401).json(error.unauthorized()) } diff --git a/test/integration-tests/org/putOrgTest.js b/test/integration-tests/org/putOrgTest.js index 006335c0..8b9dde5c 100644 --- a/test/integration-tests/org/putOrgTest.js +++ b/test/integration-tests/org/putOrgTest.js @@ -6,104 +6,104 @@ const constants = require('../constants.js') const app = require('../../../src/index.js') const params = { name: 'Test Organization', id_quota: 100 } -const secretariat_params = { name: 'MITRE Corporation', id_quota: 100000 } -const cna_params = { name: 'Adams, Nielsen and Hensley', id_quota: 1309 } +const secretariatParams = { name: 'MITRE Corporation', id_quota: 100000 } +const cnaParams = { name: 'Adams, Nielsen and Hensley', id_quota: 1309 } describe('Testing org put endpoint', () => { - context('Positive Tests', () => { - it('Allows update made by a secretariat to itself', async () => { - await chai.request(app) - .put('/api/org/mitre') - .set({ ...constants.headers }) - .query(params) - .send() - .then((res, err) => { - expect(res).to.have.status(200) - expect(res.body.updated.name).to.equal(params.name) - expect(res.body.updated.policies.id_quota).to.equal(params.id_quota) - expect(err).to.be.undefined - }) - await chai.request(app) - .put(`/api/org/mitre`) - .set({ ...constants.headers }) - .query(secretariat_params) - .send() - .then((res, err) => { - expect(res).to.have.status(200) - expect(res.body.updated.name).to.equal(secretariat_params.name) - expect(res.body.updated.policies.id_quota).to.equal(secretariat_params.id_quota) - expect(err).to.be.undefined - }) + context('Positive Tests', () => { + it('Allows update made by a secretariat to itself', async () => { + await chai.request(app) + .put('/api/org/mitre') + .set({ ...constants.headers }) + .query(params) + .send() + .then((res, err) => { + expect(res).to.have.status(200) + expect(res.body.updated.name).to.equal(params.name) + expect(res.body.updated.policies.id_quota).to.equal(params.id_quota) + expect(err).to.be.undefined }) - it('Allows update made by a secretariat to another org', async () => { - await chai.request(app) - .put('/api/org/win_5') - .set({ ...constants.headers }) - .query(params) - .send() - .then((res, err) => { - expect(res).to.have.status(200) - expect(res.body.updated.name).to.equal(params.name) - expect(res.body.updated.policies.id_quota).to.equal(params.id_quota) - expect(err).to.be.undefined - }) - await chai.request(app) - .put('/api/org/win_5') - .set({ ...constants.headers }) - .query(cna_params) - .send() - .then((res, err) => { - expect(res).to.have.status(200) - expect(res.body.updated.name).to.equal(cna_params.name) - expect(res.body.updated.policies.id_quota).to.equal(cna_params.id_quota) - expect(err).to.be.undefined - }) + await chai.request(app) + .put('/api/org/mitre') + .set({ ...constants.headers }) + .query(secretariatParams) + .send() + .then((res, err) => { + expect(res).to.have.status(200) + expect(res.body.updated.name).to.equal(secretariatParams.name) + expect(res.body.updated.policies.id_quota).to.equal(secretariatParams.id_quota) + expect(err).to.be.undefined }) - it('Update made by non secretariat org to itself ONLY updates last_active field', async () => { - let now = Date.now() - await chai.request(app) - .put('/api/org/win_5') - .set({ ...constants.nonSecretariatUserHeaders }) - .query(params) - .send() - .then((res, err) => { - // Assert that that the last_active field was updated under 2 seconds ago - let last_active = Date.parse(res.body.updated.last_active) - let diff = Math.abs(now - last_active) - let within_two_seconds = diff < 2000 - expect(within_two_seconds).to.be.true - // Assert no other fields were changed - expect(res).to.have.status(200) - expect(res.body.updated.name).to.equal(cna_params.name) - expect(res.body.updated.policies.id_quota).to.equal(cna_params.id_quota) - expect(err).to.be.undefined - }) + }) + it('Allows update made by a secretariat to another org', async () => { + await chai.request(app) + .put('/api/org/win_5') + .set({ ...constants.headers }) + .query(params) + .send() + .then((res, err) => { + expect(res).to.have.status(200) + expect(res.body.updated.name).to.equal(params.name) + expect(res.body.updated.policies.id_quota).to.equal(params.id_quota) + expect(err).to.be.undefined + }) + await chai.request(app) + .put('/api/org/win_5') + .set({ ...constants.headers }) + .query(cnaParams) + .send() + .then((res, err) => { + expect(res).to.have.status(200) + expect(res.body.updated.name).to.equal(cnaParams.name) + expect(res.body.updated.policies.id_quota).to.equal(cnaParams.id_quota) + expect(err).to.be.undefined }) }) - context('Negative Tests', () => { - it('Fails update made by a non-secretariat org to a different org', async () => { - await chai.request(app) - .put('/api/org/cause_8') - .set({ ...constants.nonSecretariatUserHeaders }) - .send() - .then((res, err) => { - expect(res).to.have.status(401) - expect(err).to.be.undefined - expect(res.body).to.haveOwnProperty('error') - expect(res.body.error).to.equal('UNAUTHORIZED') - }) + it('Update made by non secretariat org to itself ONLY updates last_active field', async () => { + const now = Date.now() + await chai.request(app) + .put('/api/org/win_5') + .set({ ...constants.nonSecretariatUserHeaders }) + .query(params) + .send() + .then((res, err) => { + // Assert that that the last_active field was updated under 2 seconds ago + const lastActive = Date.parse(res.body.updated.last_active) + const diff = Math.abs(now - lastActive) + const withinTwoSeconds = diff < 2000 + expect(withinTwoSeconds).to.be.true + // Assert no other fields were changed + expect(res).to.have.status(200) + expect(res.body.updated.name).to.equal(cnaParams.name) + expect(res.body.updated.policies.id_quota).to.equal(cnaParams.id_quota) + expect(err).to.be.undefined }) - it('Fails update made by a non-secretariat org to a secretariat', async () => { - await chai.request(app) - .put('/api/org/mitre') - .set({ ...constants.nonSecretariatUserHeaders }) - .send() - .then((res, err) => { - expect(res).to.have.status(401) - expect(err).to.be.undefined - expect(res.body).to.haveOwnProperty('error') - expect(res.body.error).to.equal('UNAUTHORIZED') - }) + }) + }) + context('Negative Tests', () => { + it('Fails update made by a non-secretariat org to a different org', async () => { + await chai.request(app) + .put('/api/org/cause_8') + .set({ ...constants.nonSecretariatUserHeaders }) + .send() + .then((res, err) => { + expect(res).to.have.status(401) + expect(err).to.be.undefined + expect(res.body).to.haveOwnProperty('error') + expect(res.body.error).to.equal('UNAUTHORIZED') + }) + }) + it('Fails update made by a non-secretariat org to a secretariat', async () => { + await chai.request(app) + .put('/api/org/mitre') + .set({ ...constants.nonSecretariatUserHeaders }) + .send() + .then((res, err) => { + expect(res).to.have.status(401) + expect(err).to.be.undefined + expect(res.body).to.haveOwnProperty('error') + expect(res.body.error).to.equal('UNAUTHORIZED') }) }) -}) \ No newline at end of file + }) +}) diff --git a/test/unit-tests/org/orgUpdateTest.js b/test/unit-tests/org/orgUpdateTest.js index f978d6f9..05e696f7 100644 --- a/test/unit-tests/org/orgUpdateTest.js +++ b/test/unit-tests/org/orgUpdateTest.js @@ -48,6 +48,10 @@ class OrgUpdatedAddingRole { async getOrgUUID () { return null } + + async isSecretariat () { + return true + } } class OrgUpdatedRemovingRole { @@ -66,6 +70,10 @@ class OrgUpdatedRemovingRole { async getOrgUUID () { return null } + + async isSecretariat () { + return true + } } describe('Testing the PUT /org/:shortname endpoint in Org Controller', () => { @@ -102,8 +110,11 @@ describe('Testing the PUT /org/:shortname endpoint in Org Controller', () => { async findOneByShortName () { return orgFixtures.existentOrg } - } + async isSecretariat () { + return true + } + } app.route('/org-not-updated-shortname-exists/:shortname') .put((req, res, next) => { const factory = { @@ -112,7 +123,6 @@ describe('Testing the PUT /org/:shortname endpoint in Org Controller', () => { req.ctx.repositories = factory next() }, orgParams.parsePostParams, orgController.ORG_UPDATE_SINGLE) - chai.request(app) .put(`/org-not-updated-shortname-exists/${orgFixtures.existentOrg.short_name}?new_short_name=cisco`) .set(orgFixtures.secretariatHeader) @@ -120,7 +130,6 @@ describe('Testing the PUT /org/:shortname endpoint in Org Controller', () => { if (err) { done(err) } - expect(res).to.have.status(403) expect(res).to.have.property('body').and.to.be.a('object') const errObj = error.duplicateShortname('cisco') @@ -288,6 +297,10 @@ describe('Testing the PUT /org/:shortname endpoint in Org Controller', () => { async aggregate () { return [orgFixtures.existentOrg] } + + async isSecretariat () { + return true + } } app.route('/org-not-updated-no-query-parameters/:shortname') From 53c9fb17a136f84dbe2bfaa4e3b298ea77b39b3b Mon Sep 17 00:00:00 2001 From: jack-flores Date: Wed, 31 Jul 2024 12:21:50 -0400 Subject: [PATCH 07/21] quieting bad eslint error --- test/integration-tests/org/putOrgTest.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/integration-tests/org/putOrgTest.js b/test/integration-tests/org/putOrgTest.js index 8b9dde5c..0b47f864 100644 --- a/test/integration-tests/org/putOrgTest.js +++ b/test/integration-tests/org/putOrgTest.js @@ -1,3 +1,4 @@ +/* eslint-disable no-unused-expressions */ const chai = require('chai') chai.use(require('chai-http')) const expect = chai.expect From 403b3bf417cbad7f950e7860b0c4ee659b5c8284 Mon Sep 17 00:00:00 2001 From: jack-flores Date: Wed, 31 Jul 2024 15:53:52 -0400 Subject: [PATCH 08/21] #1258 now rejects non-sec requests with params --- .../org.controller/org.controller.js | 4 +++ src/middleware/middleware.js | 6 +++-- test/integration-tests/org/putOrgTest.js | 27 ++++++++++++++----- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/controller/org.controller/org.controller.js b/src/controller/org.controller/org.controller.js index 929c4853..b4e1873f 100644 --- a/src/controller/org.controller/org.controller.js +++ b/src/controller/org.controller/org.controller.js @@ -409,6 +409,10 @@ async function updateOrg (req, res, next) { result = await orgRepo.aggregate(agt) result = result.length > 0 ? result[0] : null + if (!isSec) { + result = { last_active: result.last_active } + } + const responseMessage = { message: shortName + ' organization was successfully updated.', updated: result diff --git a/src/middleware/middleware.js b/src/middleware/middleware.js index 829a90d2..35e50510 100644 --- a/src/middleware/middleware.js +++ b/src/middleware/middleware.js @@ -146,9 +146,11 @@ async function validateOrg (req, res, next) { const isSec = await orgRepo.isSecretariat(org) if (!isSec) { - if (!(org === reqOrg)) { + if (org !== reqOrg) { logger.info({ uuid: req.ctx.uuid, message: org + ' is not a ' + CONSTANTS.AUTH_ROLE_ENUM.SECRETARIAT + ' or the same as ' + reqOrg + ' and is not allowed to make these changes.' }) - return res.status(401).json(error.unauthorized()) + return res.status(403).json(error.secretariatOnly()) + } else if (Object.keys(req.query).length > 0) { + return res.status(403).json(error.secretariatOnly()) } } diff --git a/test/integration-tests/org/putOrgTest.js b/test/integration-tests/org/putOrgTest.js index 0b47f864..dec0a2b0 100644 --- a/test/integration-tests/org/putOrgTest.js +++ b/test/integration-tests/org/putOrgTest.js @@ -65,7 +65,6 @@ describe('Testing org put endpoint', () => { await chai.request(app) .put('/api/org/win_5') .set({ ...constants.nonSecretariatUserHeaders }) - .query(params) .send() .then((res, err) => { // Assert that that the last_active field was updated under 2 seconds ago @@ -75,8 +74,9 @@ describe('Testing org put endpoint', () => { expect(withinTwoSeconds).to.be.true // Assert no other fields were changed expect(res).to.have.status(200) - expect(res.body.updated.name).to.equal(cnaParams.name) - expect(res.body.updated.policies.id_quota).to.equal(cnaParams.id_quota) + expect(res.body.updated.active_roles).to.be.undefined + expect(res.body.updated.name).to.be.undefined + expect(res.body.updated.policies).to.be.undefined expect(err).to.be.undefined }) }) @@ -88,10 +88,23 @@ describe('Testing org put endpoint', () => { .set({ ...constants.nonSecretariatUserHeaders }) .send() .then((res, err) => { - expect(res).to.have.status(401) + expect(res).to.have.status(403) + expect(err).to.be.undefined + expect(res.body).to.haveOwnProperty('error') + expect(res.body.error).to.equal('SECRETARIAT_ONLY') + }) + }) + it('Fails update to fields made by a non-secretariat org to itself', async () => { + await chai.request(app) + .put('/api/org/win_5') + .set({ ...constants.nonSecretariatUserHeaders }) + .query(params) + .send() + .then((res, err) => { + expect(res).to.have.status(403) expect(err).to.be.undefined expect(res.body).to.haveOwnProperty('error') - expect(res.body.error).to.equal('UNAUTHORIZED') + expect(res.body.error).to.equal('SECRETARIAT_ONLY') }) }) it('Fails update made by a non-secretariat org to a secretariat', async () => { @@ -100,10 +113,10 @@ describe('Testing org put endpoint', () => { .set({ ...constants.nonSecretariatUserHeaders }) .send() .then((res, err) => { - expect(res).to.have.status(401) + expect(res).to.have.status(403) expect(err).to.be.undefined expect(res.body).to.haveOwnProperty('error') - expect(res.body.error).to.equal('UNAUTHORIZED') + expect(res.body.error).to.equal('SECRETARIAT_ONLY') }) }) }) From 03cc6185f85439c34b50b735dd8938f71c5883f6 Mon Sep 17 00:00:00 2001 From: jack-flores Date: Fri, 2 Aug 2024 15:11:45 -0400 Subject: [PATCH 09/21] #1258 updated swagger documentation --- src/controller/org.controller/index.js | 1 + .../org.controller/org.controller.js | 3 +- test/unit-tests/org/orgUpdateTest.js | 96 ++++++++++++++++++- 3 files changed, 96 insertions(+), 4 deletions(-) diff --git a/src/controller/org.controller/index.js b/src/controller/org.controller/index.js index abbac143..598cb055 100644 --- a/src/controller/org.controller/index.js +++ b/src/controller/org.controller/index.js @@ -247,6 +247,7 @@ router.put('/org/:shortname',

Access Control

User must belong to an organization with the Secretariat role

Expected Behavior

+

CNA: Updates 'last_active' timestamp to show that a CNA is still active

Secretariat: Updates any organization's information

" #swagger.parameters['shortname'] = { description: 'The shortname of the organization' } #swagger.parameters['$ref'] = [ diff --git a/src/controller/org.controller/org.controller.js b/src/controller/org.controller/org.controller.js index b4e1873f..0e82f640 100644 --- a/src/controller/org.controller/org.controller.js +++ b/src/controller/org.controller/org.controller.js @@ -338,7 +338,6 @@ async function updateOrg (req, res, next) { return res.status(404).json(error.orgDnePathParam(shortName)) } - newOrg.last_active = Date.now() const isSec = await orgRepo.isSecretariat(orgMakingChanges) if (isSec) { @@ -399,6 +398,8 @@ async function updateOrg (req, res, next) { } } + newOrg.last_active = Date.now() + // update org let result = await orgRepo.updateByOrgUUID(org.UUID, newOrg) if (result.n === 0) { diff --git a/test/unit-tests/org/orgUpdateTest.js b/test/unit-tests/org/orgUpdateTest.js index 05e696f7..61a4502a 100644 --- a/test/unit-tests/org/orgUpdateTest.js +++ b/test/unit-tests/org/orgUpdateTest.js @@ -1,3 +1,4 @@ +/* eslint-disable no-unused-expressions */ const express = require('express') const app = express() const chai = require('chai') @@ -38,7 +39,9 @@ class OrgUpdatedAddingRole { } async aggregate () { - return [orgFixtures.owningOrg] + const org = orgFixtures.owningOrg + org.last_active = Date.now() + return [org] } async updateByOrgUUID () { @@ -60,7 +63,9 @@ class OrgUpdatedRemovingRole { } async aggregate () { - return [orgFixtures.owningOrg] + const org = orgFixtures.owningOrg + org.last_active = Date.now() + return [org] } async updateByOrgUUID () { @@ -172,6 +177,11 @@ describe('Testing the PUT /org/:shortname endpoint in Org Controller', () => { expect(res.body.updated.name).to.equal(orgFixtures.owningOrg.name) expect(res.body.updated.UUID).to.equal(orgFixtures.owningOrg.UUID) expect(res.body.updated.policies.id_quota).to.equal(orgFixtures.owningOrg.policies.id_quota) + const now = Date.now() + const lastActive = res.body.updated.last_active + const diff = Math.abs(now - lastActive) + const withinTwoSeconds = diff < 500 + expect(withinTwoSeconds).to.be.true done() }) }) @@ -207,6 +217,11 @@ describe('Testing the PUT /org/:shortname endpoint in Org Controller', () => { expect(res.body.updated.name).to.equal(orgFixtures.owningOrg.name) expect(res.body.updated.UUID).to.equal(orgFixtures.owningOrg.UUID) expect(res.body.updated.policies.id_quota).to.equal(orgFixtures.owningOrg.policies.id_quota) + const now = Date.now() + const lastActive = res.body.updated.last_active + const diff = Math.abs(now - lastActive) + const withinTwoSeconds = diff < 500 + expect(withinTwoSeconds).to.be.true done() }) }) @@ -241,6 +256,11 @@ describe('Testing the PUT /org/:shortname endpoint in Org Controller', () => { expect(res.body.updated.name).to.equal(orgFixtures.owningOrg.name) expect(res.body.updated.UUID).to.equal(orgFixtures.owningOrg.UUID) expect(res.body.updated.policies.id_quota).to.equal(orgFixtures.owningOrg.policies.id_quota) + const now = Date.now() + const lastActive = res.body.updated.last_active + const diff = Math.abs(now - lastActive) + const withinTwoSeconds = diff < 500 + expect(withinTwoSeconds).to.be.true done() }) }) @@ -275,6 +295,11 @@ describe('Testing the PUT /org/:shortname endpoint in Org Controller', () => { expect(res.body.updated.name).to.equal(orgFixtures.owningOrg.name) expect(res.body.updated.UUID).to.equal(orgFixtures.owningOrg.UUID) expect(res.body.updated.policies.id_quota).to.equal(orgFixtures.owningOrg.policies.id_quota) + const now = Date.now() + const lastActive = res.body.updated.last_active + const diff = Math.abs(now - lastActive) + const withinTwoSeconds = diff < 500 + expect(withinTwoSeconds).to.be.true done() }) }) @@ -295,7 +320,9 @@ describe('Testing the PUT /org/:shortname endpoint in Org Controller', () => { } async aggregate () { - return [orgFixtures.existentOrg] + const org = orgFixtures.existentOrg + org.last_active = Date.now() + return [org] } async isSecretariat () { @@ -328,6 +355,69 @@ describe('Testing the PUT /org/:shortname endpoint in Org Controller', () => { expect(res.body.updated.name).to.equal(orgFixtures.existentOrg.name) expect(res.body.updated.short_name).to.equal(orgFixtures.existentOrg.short_name) expect(res.body.updated.UUID).to.equal(orgFixtures.existentOrg.UUID) + const now = Date.now() + const lastActive = res.body.updated.last_active + const diff = Math.abs(now - lastActive) + const withinTwoSeconds = diff < 500 + expect(withinTwoSeconds).to.be.true + }) + + it('Non-secretariat only last_active field updated', (done) => { + class NonSecretariat { + async findOneByShortName () { + return orgFixtures.owningOrg + } + + async aggregate () { + const org = orgFixtures.owningOrg + org.last_active = Date.now() + return [org] + } + + async updateByOrgUUID () { + return { n: 1 } + } + + async getOrgUUID () { + return null + } + + async isSecretariat () { + return false + } + } + app.route('/org-non-secretariat-updates-last-active-field/:shortname') + .put((req, res, next) => { + const factory = { + getOrgRepository: () => { return new NonSecretariat() }, + getUserRepository: () => { return new NullUserRepo() } + } + req.ctx.repositories = factory + next() + }, orgParams.parsePostParams, orgController.ORG_UPDATE_SINGLE) + + chai.request(app) + .put(`/org-non-secretariat-updates-last-active-field/${orgFixtures.owningOrg.short_name}?name=TestOrg`) + .set(orgFixtures.owningOrgHeader) + .end((err, res) => { + if (err) { + done(err) + } + expect(res).to.have.status(200) + expect(res).to.have.property('body').and.to.be.a('object') + expect(res.body).to.have.property('updated').and.to.be.a('object') + // Assert that that the last_active field was updated under 0.5 seconds ago + const now = Date.now() + const lastActive = res.body.updated.last_active + const diff = Math.abs(now - lastActive) + const withinTwoSeconds = diff < 500 + expect(withinTwoSeconds).to.be.true + // Assert no other fields were changed + expect(res.body.updated.active_roles).to.be.undefined + expect(res.body.updated.name).to.be.undefined + expect(res.body.updated.policies).to.be.undefined + done() + }) }) }) }) From c082fb52ac9ee90c4fbd0fe5d5839f05d4ccaa22 Mon Sep 17 00:00:00 2001 From: jack-flores Date: Fri, 2 Aug 2024 15:39:39 -0400 Subject: [PATCH 10/21] test to see if my username shows up --- test/unit-tests/org/orgUpdateTest.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/unit-tests/org/orgUpdateTest.js b/test/unit-tests/org/orgUpdateTest.js index 61a4502a..312cf0b9 100644 --- a/test/unit-tests/org/orgUpdateTest.js +++ b/test/unit-tests/org/orgUpdateTest.js @@ -386,6 +386,7 @@ describe('Testing the PUT /org/:shortname endpoint in Org Controller', () => { return false } } + app.route('/org-non-secretariat-updates-last-active-field/:shortname') .put((req, res, next) => { const factory = { From cfb89119a2c744a610b351337528daf619b0fba9 Mon Sep 17 00:00:00 2001 From: jack-flores Date: Mon, 5 Aug 2024 09:40:41 -0400 Subject: [PATCH 11/21] #1258 added schema for am-i-alive --- api-docs/openapi.json | 2 +- schemas/org/am-i-alive-response.json | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 schemas/org/am-i-alive-response.json diff --git a/api-docs/openapi.json b/api-docs/openapi.json index 1a7ebad4..f6974fb0 100644 --- a/api-docs/openapi.json +++ b/api-docs/openapi.json @@ -2099,7 +2099,7 @@ "Organization" ], "summary": "Updates information about the organization specified by short name (accessible to Secretariat)", - "description": "

Access Control

User must belong to an organization with the Secretariat role

Expected Behavior

Secretariat: Updates any organization's information

", + "description": "

Access Control

User must belong to an organization with the Secretariat role

Expected Behavior

CNA: Updates 'last_active' timestamp to show that a CNA is still active

Secretariat: Updates any organization's information

", "operationId": "orgUpdateSingle", "parameters": [ { diff --git a/schemas/org/am-i-alive-response.json b/schemas/org/am-i-alive-response.json new file mode 100644 index 00000000..b9b3d55b --- /dev/null +++ b/schemas/org/am-i-alive-response.json @@ -0,0 +1,20 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema", + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Success description" + }, + "updated": { + "type": "object", + "properties": { + "last_active": { + "type": "string", + "format": "date-time", + "description": "The time the organization was last active." + } + } + } + } + } \ No newline at end of file From fcc7fc48901581242b535dd3bff32da74d02aa93 Mon Sep 17 00:00:00 2001 From: "Daigneau, Jeremy T" Date: Mon, 5 Aug 2024 10:59:55 -0400 Subject: [PATCH 12/21] Updated github actions to use docker compose instead of docker-compose --- .github/workflows/test-http.yml | 14 +++++++------- .github/workflows/test-integration.yml | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test-http.yml b/.github/workflows/test-http.yml index 1a3d6678..0ced6fc4 100644 --- a/.github/workflows/test-http.yml +++ b/.github/workflows/test-http.yml @@ -14,22 +14,22 @@ jobs: - name: Build and Run Services and Mongo Containers run: | cp docker/.docker-env.example docker/.docker-env - docker-compose --file docker/docker-compose.yml build - docker-compose --file docker/docker-compose.yml up -d + docker compose --file docker/docker-compose.yml build + docker compose --file docker/docker-compose.yml up -d - name: Build Test Container run: | cp test-http/docker/.docker-env.example test-http/docker/.docker-env - docker-compose --file test-http/docker/docker-compose.yml build + docker compose --file test-http/docker/docker-compose.yml build - name: Run Test Container run: | - docker-compose --file test-http/docker/docker-compose.yml up -d + docker compose --file test-http/docker/docker-compose.yml up -d - name: Sleep run: bash -c "while ! docker-compose --file docker/docker-compose.yml logs --tail=10 cveawg | grep -q 'Serving on port'; do sleep 1; done" - name: Load Data into MongoDb - run: docker-compose -f docker/docker-compose.yml exec -T cveawg npm run populate:dev y + run: docker compose -f docker/docker-compose.yml exec -T cveawg npm run populate:dev y - name: Run Black Box Tests run: | - docker-compose --file test-http/docker/docker-compose.yml exec -T demon pytest src/ | tee test-http/src/testOutput.txt + docker compose --file test-http/docker/docker-compose.yml exec -T demon pytest src/ | tee test-http/src/testOutput.txt - name: Archive Test Results uses: actions/upload-artifact@v2 with: @@ -37,7 +37,7 @@ jobs: path: test-http/src/testOutput.txt retention-days: 1 - name: Extract Tests Results - run: docker-compose --file test-http/docker/docker-compose.yml exec -T demon /bin/bash src/parse_tests_output.sh | (read foo; echo "##[set-output name=result;]$(echo $foo)") + run: docker compose --file test-http/docker/docker-compose.yml exec -T demon /bin/bash src/parse_tests_output.sh | (read foo; echo "##[set-output name=result;]$(echo $foo)") id: tests_result - name: Evaluate Tests Results if: ${{ steps.tests_result.outputs.result == 'Tests failed' }} diff --git a/.github/workflows/test-integration.yml b/.github/workflows/test-integration.yml index b7569a79..48f54d04 100644 --- a/.github/workflows/test-integration.yml +++ b/.github/workflows/test-integration.yml @@ -14,10 +14,10 @@ jobs: - name: Build and Run Services and Mongo Containers run: | cp docker/.docker-env.test-example docker/.docker-env - docker-compose --file docker/docker-compose.yml build - docker-compose --file docker/docker-compose.yml up -d + docker compose --file docker/docker-compose.yml build + docker compose --file docker/docker-compose.yml up -d - name: Sleep run: bash -c "while ! docker-compose --file docker/docker-compose.yml logs --tail=10 cveawg | grep -q 'Serving on port'; do sleep 1; done" - name: Run Tests - run: docker-compose -f docker/docker-compose.yml exec -T cveawg npm run test:integration + run: docker compose -f docker/docker-compose.yml exec -T cveawg npm run test:integration continue-on-error: false \ No newline at end of file From 45db2a8eed1ba2d8eb353a15f279ea5f63d73ec2 Mon Sep 17 00:00:00 2001 From: "Daigneau, Jeremy T" Date: Mon, 5 Aug 2024 11:22:01 -0400 Subject: [PATCH 13/21] More docker-compose fixes --- .github/workflows/test-http.yml | 2 +- .github/workflows/test-integration.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-http.yml b/.github/workflows/test-http.yml index 0ced6fc4..5f56c1b0 100644 --- a/.github/workflows/test-http.yml +++ b/.github/workflows/test-http.yml @@ -24,7 +24,7 @@ jobs: run: | docker compose --file test-http/docker/docker-compose.yml up -d - name: Sleep - run: bash -c "while ! docker-compose --file docker/docker-compose.yml logs --tail=10 cveawg | grep -q 'Serving on port'; do sleep 1; done" + run: bash -c "while ! docker compose --file docker/docker-compose.yml logs --tail=10 cveawg | grep -q 'Serving on port'; do sleep 1; done" - name: Load Data into MongoDb run: docker compose -f docker/docker-compose.yml exec -T cveawg npm run populate:dev y - name: Run Black Box Tests diff --git a/.github/workflows/test-integration.yml b/.github/workflows/test-integration.yml index 48f54d04..1f5efbd6 100644 --- a/.github/workflows/test-integration.yml +++ b/.github/workflows/test-integration.yml @@ -17,7 +17,7 @@ jobs: docker compose --file docker/docker-compose.yml build docker compose --file docker/docker-compose.yml up -d - name: Sleep - run: bash -c "while ! docker-compose --file docker/docker-compose.yml logs --tail=10 cveawg | grep -q 'Serving on port'; do sleep 1; done" + run: bash -c "while ! docker compose --file docker/docker-compose.yml logs --tail=10 cveawg | grep -q 'Serving on port'; do sleep 1; done" - name: Run Tests run: docker compose -f docker/docker-compose.yml exec -T cveawg npm run test:integration continue-on-error: false \ No newline at end of file From a45d63a543ee465967211cba85124a5a8f8be6c9 Mon Sep 17 00:00:00 2001 From: jack-flores Date: Thu, 8 Aug 2024 15:20:04 -0400 Subject: [PATCH 14/21] #1258 more unit tests --- test/unit-tests/middleware/validateOrgTest.js | 172 ++++++++++++++++++ .../unit-tests/org/orgUpdateLastActiveTest.js | 136 ++++++++++++++ test/unit-tests/org/orgUpdateTest.js | 100 +--------- 3 files changed, 314 insertions(+), 94 deletions(-) create mode 100644 test/unit-tests/middleware/validateOrgTest.js create mode 100644 test/unit-tests/org/orgUpdateLastActiveTest.js diff --git a/test/unit-tests/middleware/validateOrgTest.js b/test/unit-tests/middleware/validateOrgTest.js new file mode 100644 index 00000000..af239880 --- /dev/null +++ b/test/unit-tests/middleware/validateOrgTest.js @@ -0,0 +1,172 @@ +/* eslint-disable no-unused-expressions */ +const chai = require('chai') +const sinon = require('sinon') +const { validateOrg } = require('../../../src/middleware/middleware.js') +const OrgRepository = require('../../../src/repositories/orgRepository.js') +const expect = chai.expect + +const secretariat = { + short_name: 'mitre', + name: 'MITRE Corporation', + authority: { + active_roles: [ + 'SECRETARIAT', + 'CNA' + ] + }, + policies: { + id_quota: 1248 + } +} + +const nonSecretariat = { + short_name: 'win_5', + name: 'test_org', + authority: { + active_roles: [ + 'CNA' + ] + }, + policies: { + id_quota: 200 + } +} + +const nonSecretariat2 = { + short_name: 'cause_8', + name: 'test_org2', + authority: { + active_roles: [ + 'CNA' + ] + }, + policies: { + id_quota: 888 + } +} + +describe('Testing the validateOrg function', () => { + let status, json, res, next, getOrgRepository, orgRepo + beforeEach(() => { + status = sinon.stub() + json = sinon.spy() + res = { json, status } + next = sinon.stub() + status.returns(res) + + orgRepo = new OrgRepository() + getOrgRepository = sinon.stub() + getOrgRepository.returns(orgRepo) + }) + context('Positive Tests', () => { + it('Secretariat can update itself', async () => { + sinon.stub(orgRepo, 'isSecretariat').returns(true) + + const req = { + ctx: { + org: secretariat.short_name, + repositories: { + getOrgRepository + } + }, + params: { + shortname: secretariat.short_name + }, + query: { + id_quota: 111 + } + } + await validateOrg(req, res, next) + + expect(next.calledOnce).to.be.true + expect(next.firstCall.args).to.be.empty + }) + it('Secretariat can update another org', async () => { + sinon.stub(orgRepo, 'isSecretariat').returns(true) + + const req = { + ctx: { + org: secretariat.short_name, + repositories: { + getOrgRepository + } + }, + params: { + shortname: nonSecretariat.short_name + }, + query: { + id_quota: 999 + } + } + await validateOrg(req, res, next) + + expect(next.calledOnce).to.be.true + expect(next.firstCall.args).to.be.empty + }) + it('Non-secretariat can update itself', async () => { + sinon.stub(orgRepo, 'isSecretariat').returns(true) + + const req = { + ctx: { + org: nonSecretariat.short_name, + repositories: { + getOrgRepository + } + }, + params: { + shortname: nonSecretariat.short_name + } + } + await validateOrg(req, res, next) + + expect(next.calledOnce).to.be.true + expect(next.firstCall.args).to.be.empty + }) + }) + context('Negative Tests', () => { + it('Non-secretariat cannot update its fields other than last_active', async () => { + sinon.stub(orgRepo, 'isSecretariat').returns(false) + + const req = { + ctx: { + org: nonSecretariat.short_name, + repositories: { + getOrgRepository + } + }, + params: { + shortname: nonSecretariat.short_name + }, + query: { + id_quota: 999 + } + } + await validateOrg(req, res, next) + + expect(status.calledWith(403)).to.be.true + expect(next.calledOnce).to.be.false + }) + it('Non-secretariat cannot update another org', async () => { + sinon.stub(orgRepo, 'isSecretariat').returns(false) + + const req = { + ctx: { + org: nonSecretariat.short_name, + repositories: { + getOrgRepository + } + }, + params: { + shortname: nonSecretariat2.short_name + }, + query: { + id_quota: 999 + } + } + await validateOrg(req, res, next) + + expect(status.calledWith(403)).to.be.true + expect(next.calledOnce).to.be.false + }) + }) +}) diff --git a/test/unit-tests/org/orgUpdateLastActiveTest.js b/test/unit-tests/org/orgUpdateLastActiveTest.js new file mode 100644 index 00000000..1bffe773 --- /dev/null +++ b/test/unit-tests/org/orgUpdateLastActiveTest.js @@ -0,0 +1,136 @@ +/* eslint-disable no-unused-expressions */ +const chai = require('chai') +const sinon = require('sinon') +const { ORG_UPDATE_SINGLE } = require('../../../src/controller/org.controller/org.controller.js') +const OrgRepository = require('../../../src/repositories/orgRepository.js') +const UserRepository = require('../../../src/repositories/userRepository.js') +const expect = chai.expect + +const secretariat = { + short_name: 'mitre', + name: 'MITRE Corporation', + authority: { + active_roles: [ + 'SECRETARIAT', + 'CNA' + ] + }, + policies: { + id_quota: 1248 + } +} + +const nonSecretariat = { + short_name: 'win_5', + name: 'test_org', + authority: { + active_roles: [ + 'CNA' + ] + }, + policies: { + id_quota: 200 + } +} + +describe('Testing the updateOrg function', () => { + let status, json, res, next, getOrgRepository, orgRepo, getUserRepository, + userRepo, updateOrg + 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) + + userRepo = new UserRepository() + getUserRepository = sinon.stub() + getUserRepository.returns(userRepo) + + updateOrg = sinon.stub(orgRepo, 'updateByOrgUUID').returns(true) + sinon.stub(orgRepo, 'getOrgUUID').returns(true) + sinon.stub(userRepo, 'getUserUUID').returns(true) + }) + context('Positive Tests', () => { + it('Secretariat updates itself', async () => { + sinon.stub(orgRepo, 'isSecretariat').returns(true) + sinon.stub(orgRepo, 'findOneByShortName').returns(secretariat) + sinon.stub(orgRepo, 'aggregate').returns([secretariat]) + + const req = { + ctx: { + org: secretariat.short_name, + repositories: { + getOrgRepository, + getUserRepository + }, + params: { + shortname: secretariat.short_name + }, + query: { + id_quota: 111 + } + } + } + await ORG_UPDATE_SINGLE(req, res, next) + + expect(status.args[0][0]).to.equal(200) + expect(updateOrg.args[0][1].policies.id_quota).to.equal(req.ctx.query.id_quota) + }) + it('Secretariat updates a different org', async () => { + sinon.stub(orgRepo, 'isSecretariat').returns(true) + sinon.stub(orgRepo, 'findOneByShortName').returns(nonSecretariat) + sinon.stub(orgRepo, 'aggregate').returns([nonSecretariat]) + + const req = { + ctx: { + org: secretariat.short_name, + repositories: { + getOrgRepository, + getUserRepository + }, + params: { + shortname: nonSecretariat.short_name + }, + query: { + id_quota: 999 + } + } + } + await ORG_UPDATE_SINGLE(req, res, next) + + expect(status.args[0][0]).to.equal(200) + expect(updateOrg.args[0][1].policies.id_quota).to.equal(req.ctx.query.id_quota) + }) + it('Non-secretariat no params only updates last_active field', async () => { + sinon.stub(orgRepo, 'isSecretariat').returns(false) + sinon.stub(orgRepo, 'findOneByShortName').returns(nonSecretariat) + sinon.stub(orgRepo, 'aggregate').returns([nonSecretariat]) + + const req = { + ctx: { + org: nonSecretariat.short_name, + repositories: { + getOrgRepository, + getUserRepository + }, + params: { + shortname: nonSecretariat.short_name + } + } + } + await ORG_UPDATE_SINGLE(req, res, next) + + expect(status.args[0][0]).to.equal(200) + const now = Date.now() + const lastActive = updateOrg.args[0][1].last_active + const diff = Math.abs(now - lastActive) + const withinHalfASecond = diff < 500 + expect(withinHalfASecond).to.be.true + }) + }) +}) diff --git a/test/unit-tests/org/orgUpdateTest.js b/test/unit-tests/org/orgUpdateTest.js index 312cf0b9..4e5f00b7 100644 --- a/test/unit-tests/org/orgUpdateTest.js +++ b/test/unit-tests/org/orgUpdateTest.js @@ -1,4 +1,3 @@ -/* eslint-disable no-unused-expressions */ const express = require('express') const app = express() const chai = require('chai') @@ -39,9 +38,7 @@ class OrgUpdatedAddingRole { } async aggregate () { - const org = orgFixtures.owningOrg - org.last_active = Date.now() - return [org] + return [orgFixtures.owningOrg] } async updateByOrgUUID () { @@ -63,9 +60,7 @@ class OrgUpdatedRemovingRole { } async aggregate () { - const org = orgFixtures.owningOrg - org.last_active = Date.now() - return [org] + return [orgFixtures.owningOrg] } async updateByOrgUUID () { @@ -120,6 +115,7 @@ describe('Testing the PUT /org/:shortname endpoint in Org Controller', () => { return true } } + app.route('/org-not-updated-shortname-exists/:shortname') .put((req, res, next) => { const factory = { @@ -128,6 +124,7 @@ describe('Testing the PUT /org/:shortname endpoint in Org Controller', () => { req.ctx.repositories = factory next() }, orgParams.parsePostParams, orgController.ORG_UPDATE_SINGLE) + chai.request(app) .put(`/org-not-updated-shortname-exists/${orgFixtures.existentOrg.short_name}?new_short_name=cisco`) .set(orgFixtures.secretariatHeader) @@ -135,6 +132,7 @@ describe('Testing the PUT /org/:shortname endpoint in Org Controller', () => { if (err) { done(err) } + expect(res).to.have.status(403) expect(res).to.have.property('body').and.to.be.a('object') const errObj = error.duplicateShortname('cisco') @@ -177,11 +175,6 @@ describe('Testing the PUT /org/:shortname endpoint in Org Controller', () => { expect(res.body.updated.name).to.equal(orgFixtures.owningOrg.name) expect(res.body.updated.UUID).to.equal(orgFixtures.owningOrg.UUID) expect(res.body.updated.policies.id_quota).to.equal(orgFixtures.owningOrg.policies.id_quota) - const now = Date.now() - const lastActive = res.body.updated.last_active - const diff = Math.abs(now - lastActive) - const withinTwoSeconds = diff < 500 - expect(withinTwoSeconds).to.be.true done() }) }) @@ -217,11 +210,6 @@ describe('Testing the PUT /org/:shortname endpoint in Org Controller', () => { expect(res.body.updated.name).to.equal(orgFixtures.owningOrg.name) expect(res.body.updated.UUID).to.equal(orgFixtures.owningOrg.UUID) expect(res.body.updated.policies.id_quota).to.equal(orgFixtures.owningOrg.policies.id_quota) - const now = Date.now() - const lastActive = res.body.updated.last_active - const diff = Math.abs(now - lastActive) - const withinTwoSeconds = diff < 500 - expect(withinTwoSeconds).to.be.true done() }) }) @@ -256,11 +244,6 @@ describe('Testing the PUT /org/:shortname endpoint in Org Controller', () => { expect(res.body.updated.name).to.equal(orgFixtures.owningOrg.name) expect(res.body.updated.UUID).to.equal(orgFixtures.owningOrg.UUID) expect(res.body.updated.policies.id_quota).to.equal(orgFixtures.owningOrg.policies.id_quota) - const now = Date.now() - const lastActive = res.body.updated.last_active - const diff = Math.abs(now - lastActive) - const withinTwoSeconds = diff < 500 - expect(withinTwoSeconds).to.be.true done() }) }) @@ -295,11 +278,6 @@ describe('Testing the PUT /org/:shortname endpoint in Org Controller', () => { expect(res.body.updated.name).to.equal(orgFixtures.owningOrg.name) expect(res.body.updated.UUID).to.equal(orgFixtures.owningOrg.UUID) expect(res.body.updated.policies.id_quota).to.equal(orgFixtures.owningOrg.policies.id_quota) - const now = Date.now() - const lastActive = res.body.updated.last_active - const diff = Math.abs(now - lastActive) - const withinTwoSeconds = diff < 500 - expect(withinTwoSeconds).to.be.true done() }) }) @@ -320,9 +298,7 @@ describe('Testing the PUT /org/:shortname endpoint in Org Controller', () => { } async aggregate () { - const org = orgFixtures.existentOrg - org.last_active = Date.now() - return [org] + return [orgFixtures.existentOrg] } async isSecretariat () { @@ -355,70 +331,6 @@ describe('Testing the PUT /org/:shortname endpoint in Org Controller', () => { expect(res.body.updated.name).to.equal(orgFixtures.existentOrg.name) expect(res.body.updated.short_name).to.equal(orgFixtures.existentOrg.short_name) expect(res.body.updated.UUID).to.equal(orgFixtures.existentOrg.UUID) - const now = Date.now() - const lastActive = res.body.updated.last_active - const diff = Math.abs(now - lastActive) - const withinTwoSeconds = diff < 500 - expect(withinTwoSeconds).to.be.true - }) - - it('Non-secretariat only last_active field updated', (done) => { - class NonSecretariat { - async findOneByShortName () { - return orgFixtures.owningOrg - } - - async aggregate () { - const org = orgFixtures.owningOrg - org.last_active = Date.now() - return [org] - } - - async updateByOrgUUID () { - return { n: 1 } - } - - async getOrgUUID () { - return null - } - - async isSecretariat () { - return false - } - } - - app.route('/org-non-secretariat-updates-last-active-field/:shortname') - .put((req, res, next) => { - const factory = { - getOrgRepository: () => { return new NonSecretariat() }, - getUserRepository: () => { return new NullUserRepo() } - } - req.ctx.repositories = factory - next() - }, orgParams.parsePostParams, orgController.ORG_UPDATE_SINGLE) - - chai.request(app) - .put(`/org-non-secretariat-updates-last-active-field/${orgFixtures.owningOrg.short_name}?name=TestOrg`) - .set(orgFixtures.owningOrgHeader) - .end((err, res) => { - if (err) { - done(err) - } - expect(res).to.have.status(200) - expect(res).to.have.property('body').and.to.be.a('object') - expect(res.body).to.have.property('updated').and.to.be.a('object') - // Assert that that the last_active field was updated under 0.5 seconds ago - const now = Date.now() - const lastActive = res.body.updated.last_active - const diff = Math.abs(now - lastActive) - const withinTwoSeconds = diff < 500 - expect(withinTwoSeconds).to.be.true - // Assert no other fields were changed - expect(res.body.updated.active_roles).to.be.undefined - expect(res.body.updated.name).to.be.undefined - expect(res.body.updated.policies).to.be.undefined - done() - }) }) }) }) From 278065c94a3aeb500232fa1b1028b18666bd1335 Mon Sep 17 00:00:00 2001 From: jack-flores Date: Thu, 15 Aug 2024 11:41:43 -0400 Subject: [PATCH 15/21] #1258 secretariat update to another org no longer updates last_active --- .../org.controller/org.controller.js | 10 ++- test/integration-tests/org/putOrgTest.js | 80 ++++++++++++++++++- .../unit-tests/org/orgUpdateLastActiveTest.js | 4 +- 3 files changed, 90 insertions(+), 4 deletions(-) diff --git a/src/controller/org.controller/org.controller.js b/src/controller/org.controller/org.controller.js index 0e82f640..5b94b2d2 100644 --- a/src/controller/org.controller/org.controller.js +++ b/src/controller/org.controller/org.controller.js @@ -364,7 +364,12 @@ async function updateOrg (req, res, next) { }) } } + if (shortName === orgMakingChanges) { + newOrg.last_active = Date.now() + } }) + } else { + newOrg.last_active = Date.now() } // updating the org's roles @@ -398,8 +403,6 @@ async function updateOrg (req, res, next) { } } - newOrg.last_active = Date.now() - // update org let result = await orgRepo.updateByOrgUUID(org.UUID, newOrg) if (result.n === 0) { @@ -411,6 +414,9 @@ async function updateOrg (req, res, next) { result = result.length > 0 ? result[0] : null if (!isSec) { + if (!result.last_active) { + return res.status(500).json(error.serverError()) + } result = { last_active: result.last_active } } diff --git a/test/integration-tests/org/putOrgTest.js b/test/integration-tests/org/putOrgTest.js index dec0a2b0..28c3a062 100644 --- a/test/integration-tests/org/putOrgTest.js +++ b/test/integration-tests/org/putOrgTest.js @@ -60,7 +60,37 @@ describe('Testing org put endpoint', () => { expect(err).to.be.undefined }) }) - it('Update made by non secretariat org to itself ONLY updates last_active field', async () => { + it('Update made by a secretariat to another org does NOT update last_active field', async () => { + await chai.request(app) + .put('/api/org/win_5') + .set({ ...constants.headers }) + .query(params) + .send() + .then((res, err) => { + expect(res.body.updated.last_active).to.be.undefined + expect(res).to.have.status(200) + expect(err).to.be.undefined + }) + }) + it('Update made by a secretariat to itself DOES update last_active field', async () => { + const now = Date.now() + await chai.request(app) + .put('/api/org/mitre') + .set({ ...constants.headers }) + .query(params) + .send() + .then((res, err) => { + expect(res.body.updated.last_active).to.not.be.null + // Assert that that the last_active field was updated under 2 seconds ago + const lastActive = Date.parse(res.body.updated.last_active) + const diff = Math.abs(now - lastActive) + const withinTwoSeconds = diff < 2000 + expect(withinTwoSeconds).to.be.true + expect(res).to.have.status(200) + expect(err).to.be.undefined + }) + }) + it('Update made by non-secretariat org to itself ONLY updates last_active field', async () => { const now = Date.now() await chai.request(app) .put('/api/org/win_5') @@ -80,6 +110,54 @@ describe('Testing org put endpoint', () => { expect(err).to.be.undefined }) }) + it('Request body ignored in update made by non-secretariat org to itself', async () => { + const requestBody = { + key1: 'value1', + key2: 'value2', + key3: 'value3', + key4: 'value4', + key5: 'value5', + key6: 'value6', + key7: 'value7', + key8: 'value8' + } + await chai.request(app) + .put('/api/org/win_5') + .set({ ...constants.nonSecretariatUserHeaders }) + .send(requestBody) + .then((res, err) => { + expect(res).to.have.status(200) + expect(res.body.updated.last_active).to.not.be.null + expect(res.body.updated.active_roles).to.be.undefined + expect(res.body.updated.name).to.be.undefined + expect(res.body.updated.policies).to.be.undefined + expect(err).to.be.undefined + }) + }) + it('Request body ignored in update made by secretariat to itself', async () => { + const requestBody = { + key1: 'value1', + key2: 'value2', + key3: 'value3', + key4: 'value4', + key5: 'value5', + key6: 'value6', + key7: 'value7', + key8: 'value8' + } + await chai.request(app) + .put('/api/org/mitre') + .set({ ...constants.headers }) + .query(params) + .send(requestBody) + .then((res, err) => { + expect(res).to.have.status(200) + expect(res.body.updated.last_active).to.not.be.null + expect(res.body.updated.name).to.equal(params.name) + expect(res.body.updated.policies.id_quota).to.equal(params.id_quota) + expect(err).to.be.undefined + }) + }) }) context('Negative Tests', () => { it('Fails update made by a non-secretariat org to a different org', async () => { diff --git a/test/unit-tests/org/orgUpdateLastActiveTest.js b/test/unit-tests/org/orgUpdateLastActiveTest.js index 1bffe773..33fa5bc0 100644 --- a/test/unit-tests/org/orgUpdateLastActiveTest.js +++ b/test/unit-tests/org/orgUpdateLastActiveTest.js @@ -109,7 +109,9 @@ describe('Testing the updateOrg function', () => { it('Non-secretariat no params only updates last_active field', async () => { sinon.stub(orgRepo, 'isSecretariat').returns(false) sinon.stub(orgRepo, 'findOneByShortName').returns(nonSecretariat) - sinon.stub(orgRepo, 'aggregate').returns([nonSecretariat]) + const nonSecretariatAgt = nonSecretariat + nonSecretariatAgt.last_active = Date.now() + sinon.stub(orgRepo, 'aggregate').returns([nonSecretariatAgt]) const req = { ctx: { From 20c159611b10bb7afcbbb50105315b37b98348b9 Mon Sep 17 00:00:00 2001 From: Noah Jaffe Date: Fri, 16 Aug 2024 08:46:35 -0400 Subject: [PATCH 16/21] Update swagger.js to reflect changes from https://github.com/CVEProject/cve-schema/commit/b83c668. Fixes #1246 --- src/swagger.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/swagger.js b/src/swagger.js index 4f77c388..0dc9cc1f 100644 --- a/src/swagger.js +++ b/src/swagger.js @@ -34,7 +34,7 @@ const doc = { or MITRE) to request credentials \ \

CVE data is to be in the JSON 5.1 CVE Record format. Details of the JSON 5.1 schema are \ - located here.

\ + located here.

\ Contact the CVE Services team", contact: { name: 'CVE Services Overview', From 0efc8aa8e5cbee6831e691afedabb22605bbb3f7 Mon Sep 17 00:00:00 2001 From: jack-flores Date: Mon, 19 Aug 2024 15:27:16 -0400 Subject: [PATCH 17/21] #1258 addressing comments from team --- api-docs/openapi.json | 2 +- package-lock.json | 46 +++++++++---------- src/controller/org.controller/index.js | 2 +- .../org.controller/org.controller.js | 6 ++- 4 files changed, 29 insertions(+), 27 deletions(-) diff --git a/api-docs/openapi.json b/api-docs/openapi.json index f6974fb0..c01ef610 100644 --- a/api-docs/openapi.json +++ b/api-docs/openapi.json @@ -2099,7 +2099,7 @@ "Organization" ], "summary": "Updates information about the organization specified by short name (accessible to Secretariat)", - "description": "

Access Control

User must belong to an organization with the Secretariat role

Expected Behavior

CNA: Updates 'last_active' timestamp to show that a CNA is still active

Secretariat: Updates any organization's information

", + "description": "

Access Control

User must belong to an organization with the Secretariat or CNA role

Expected Behavior

CNA: Updates 'last_active' timestamp to show that a CNA is still active

Secretariat: Updates any organization's information

", "operationId": "orgUpdateSingle", "parameters": [ { diff --git a/package-lock.json b/package-lock.json index 0aa40813..ac792daf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "cve-services", - "version": "2.3.1", + "version": "2.3.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "cve-services", - "version": "2.3.1", + "version": "2.3.3", "license": "(CC0)", "dependencies": { "ajv": "^8.6.2", @@ -2023,12 +2023,12 @@ } }, "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "dependencies": { - "fill-range": "^7.1.1" + "fill-range": "^7.0.1" }, "engines": { "node": ">=8" @@ -3882,9 +3882,9 @@ } }, "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -4212,9 +4212,9 @@ } }, "node_modules/get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", "dev": true, "engines": { "node": "*" @@ -11906,12 +11906,12 @@ } }, "braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "requires": { - "fill-range": "^7.1.1" + "fill-range": "^7.0.1" } }, "browser-stdout": { @@ -13288,9 +13288,9 @@ } }, "fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "requires": { "to-regex-range": "^5.0.1" @@ -13534,9 +13534,9 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, "get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", "dev": true }, "get-intrinsic": { @@ -18194,4 +18194,4 @@ "dev": true } } -} +} \ No newline at end of file diff --git a/src/controller/org.controller/index.js b/src/controller/org.controller/index.js index 598cb055..ab3b76b6 100644 --- a/src/controller/org.controller/index.js +++ b/src/controller/org.controller/index.js @@ -310,10 +310,10 @@ router.put('/org/:shortname', } */ mw.validateUser, + param(['shortname']).isString().trim().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), mw.validateOrg, query().custom((query) => { return mw.validateQueryParameterNames(query, ['new_short_name', 'id_quota', 'name', 'active_roles.add', 'active_roles.remove']) }), query(['new_short_name', 'id_quota', 'name', 'active_roles.add', 'active_roles.remove']).custom((val) => { return mw.containsNoInvalidCharacters(val) }), - param(['shortname']).isString().trim().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), query(['new_short_name']).optional().isString().trim().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), query(['id_quota']).optional().not().isArray().isInt({ min: CONSTANTS.MONGOOSE_VALIDATION.Org_policies_id_quota_min, max: CONSTANTS.MONGOOSE_VALIDATION.Org_policies_id_quota_max }).withMessage(errorMsgs.ID_QUOTA), query(['name']).optional().isString().trim().notEmpty(), diff --git a/src/controller/org.controller/org.controller.js b/src/controller/org.controller/org.controller.js index 5b94b2d2..56678126 100644 --- a/src/controller/org.controller/org.controller.js +++ b/src/controller/org.controller/org.controller.js @@ -368,7 +368,9 @@ async function updateOrg (req, res, next) { newOrg.last_active = Date.now() } }) - } else { + } + + if (shortName === orgMakingChanges) { newOrg.last_active = Date.now() } @@ -414,7 +416,7 @@ async function updateOrg (req, res, next) { result = result.length > 0 ? result[0] : null if (!isSec) { - if (!result.last_active) { + if (!result || !result.last_active) { return res.status(500).json(error.serverError()) } result = { last_active: result.last_active } From 9d1e931dd084ed9d220e5bf33b3927cc463ee032 Mon Sep 17 00:00:00 2001 From: jack-flores Date: Mon, 19 Aug 2024 17:21:03 -0400 Subject: [PATCH 18/21] #1258 update openapi.json --- api-docs/openapi.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/api-docs/openapi.json b/api-docs/openapi.json index c01ef610..7eed055b 100644 --- a/api-docs/openapi.json +++ b/api-docs/openapi.json @@ -2142,7 +2142,14 @@ "content": { "application/json": { "schema": { - "$ref": "../schemas/org/update-org-response.json" + "oneOf": [ + { + "$ref": "../schemas/org/update-org-response.json" + }, + { + "$ref": "../schemas/org/update-org-response.json" + } + ] } } } From 13cc2b2be0d769c8c7a943d4ea490fa4e6194675 Mon Sep 17 00:00:00 2001 From: jack-flores Date: Tue, 20 Aug 2024 11:36:51 -0400 Subject: [PATCH 19/21] reverting openapi.json to most recent correct ver --- api-docs/openapi.json | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/api-docs/openapi.json b/api-docs/openapi.json index 7eed055b..1a7ebad4 100644 --- a/api-docs/openapi.json +++ b/api-docs/openapi.json @@ -2099,7 +2099,7 @@ "Organization" ], "summary": "Updates information about the organization specified by short name (accessible to Secretariat)", - "description": "

Access Control

User must belong to an organization with the Secretariat or CNA role

Expected Behavior

CNA: Updates 'last_active' timestamp to show that a CNA is still active

Secretariat: Updates any organization's information

", + "description": "

Access Control

User must belong to an organization with the Secretariat role

Expected Behavior

Secretariat: Updates any organization's information

", "operationId": "orgUpdateSingle", "parameters": [ { @@ -2142,14 +2142,7 @@ "content": { "application/json": { "schema": { - "oneOf": [ - { - "$ref": "../schemas/org/update-org-response.json" - }, - { - "$ref": "../schemas/org/update-org-response.json" - } - ] + "$ref": "../schemas/org/update-org-response.json" } } } From 85a407610a274975c9d8cce3e7969abbfd396d2d Mon Sep 17 00:00:00 2001 From: jack-flores Date: Tue, 20 Aug 2024 11:43:52 -0400 Subject: [PATCH 20/21] #1258 addressing comments from team --- api-docs/openapi.json | 11 +++++++++-- src/controller/org.controller/index.js | 13 +++++++++---- src/controller/org.controller/org.controller.js | 3 --- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/api-docs/openapi.json b/api-docs/openapi.json index 1a7ebad4..34fb3ced 100644 --- a/api-docs/openapi.json +++ b/api-docs/openapi.json @@ -2099,7 +2099,7 @@ "Organization" ], "summary": "Updates information about the organization specified by short name (accessible to Secretariat)", - "description": "

Access Control

User must belong to an organization with the Secretariat role

Expected Behavior

Secretariat: Updates any organization's information

", + "description": "

Access Control

User must belong to an organization with the Secretariat role, or user must belong to the organization specified by short name

Expected Behavior

Secretariat: Updates any organization's information

Non-secretariat: Updates 'last_active' timestamp to show that an org is still active

", "operationId": "orgUpdateSingle", "parameters": [ { @@ -2142,7 +2142,14 @@ "content": { "application/json": { "schema": { - "$ref": "../schemas/org/update-org-response.json" + "oneOf": [ + { + "$ref": "../schemas/org/update-org-response.json" + }, + { + "$ref": "../schemas/org/am-i-alive-response.json" + } + ] } } } diff --git a/src/controller/org.controller/index.js b/src/controller/org.controller/index.js index ab3b76b6..2c927983 100644 --- a/src/controller/org.controller/index.js +++ b/src/controller/org.controller/index.js @@ -245,10 +245,10 @@ router.put('/org/:shortname', #swagger.summary = "Updates information about the organization specified by short name (accessible to Secretariat)" #swagger.description = "

Access Control

-

User must belong to an organization with the Secretariat role

+

User must belong to an organization with the Secretariat role, or user must belong to the organization specified by short name

Expected Behavior

-

CNA: Updates 'last_active' timestamp to show that a CNA is still active

-

Secretariat: Updates any organization's information

" +

Secretariat: Updates any organization's information

+

Non-secretariat: Updates 'last_active' timestamp to show that an org is still active

" #swagger.parameters['shortname'] = { description: 'The shortname of the organization' } #swagger.parameters['$ref'] = [ '#/components/parameters/id_quota', @@ -264,7 +264,12 @@ router.put('/org/:shortname', description: 'Returns information about the organization updated', content: { "application/json": { - schema: { $ref: '../schemas/org/update-org-response.json' } + schema: { + oneOf: [ + { $ref: '../schemas/org/update-org-response.json' }, + { $ref: '../schemas/org/am-i-alive-response.json' } + ] + } } } } diff --git a/src/controller/org.controller/org.controller.js b/src/controller/org.controller/org.controller.js index 56678126..7803da44 100644 --- a/src/controller/org.controller/org.controller.js +++ b/src/controller/org.controller/org.controller.js @@ -364,9 +364,6 @@ async function updateOrg (req, res, next) { }) } } - if (shortName === orgMakingChanges) { - newOrg.last_active = Date.now() - } }) } From f4a125bc57b4c4af05d75ca1b7f12958e531e809 Mon Sep 17 00:00:00 2001 From: "Daigneau, Jeremy T" Date: Mon, 26 Aug 2024 11:01:02 -0400 Subject: [PATCH 21/21] updated version number to 2.4.0 --- api-docs/openapi.json | 4 ++-- package-lock.json | 4 ++-- src/swagger.js | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/api-docs/openapi.json b/api-docs/openapi.json index 34fb3ced..1fb6cb8f 100644 --- a/api-docs/openapi.json +++ b/api-docs/openapi.json @@ -1,9 +1,9 @@ { "openapi": "3.0.2", "info": { - "version": "2.3.3", + "version": "2.4.0", "title": "CVE Services API", - "description": "The CVE Services API supports automation tooling for the CVE Program. Credentials are required for most service endpoints. Representatives of CVE Numbering Authorities (CNAs) should use one of the methods below to obtain credentials:
  • If your organization already has an Organizational Administrator (OA) account for the CVE Services, ask your admin for credentials
  • Contact your Root (Google, INCIBE, JPCERT/CC, or Red Hat) or Top-Level Root (CISA ICS or MITRE) to request credentials

CVE data is to be in the JSON 5.1 CVE Record format. Details of the JSON 5.1 schema are located here.

Contact the CVE Services team", + "description": "The CVE Services API supports automation tooling for the CVE Program. Credentials are required for most service endpoints. Representatives of CVE Numbering Authorities (CNAs) should use one of the methods below to obtain credentials:
  • If your organization already has an Organizational Administrator (OA) account for the CVE Services, ask your admin for credentials
  • Contact your Root (Google, INCIBE, JPCERT/CC, or Red Hat) or Top-Level Root (CISA ICS or MITRE) to request credentials

CVE data is to be in the JSON 5.1 CVE Record format. Details of the JSON 5.1 schema are located here.

Contact the CVE Services team", "contact": { "name": "CVE Services Overview", "url": "https://cveproject.github.io/automation-cve-services#services-overview" diff --git a/package-lock.json b/package-lock.json index ac792daf..f061baab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "cve-services", - "version": "2.3.3", + "version": "2.4.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "cve-services", - "version": "2.3.3", + "version": "2.4.0", "license": "(CC0)", "dependencies": { "ajv": "^8.6.2", diff --git a/src/swagger.js b/src/swagger.js index 0dc9cc1f..c9ea656a 100644 --- a/src/swagger.js +++ b/src/swagger.js @@ -18,7 +18,7 @@ const fullCnaContainerRequest = require('../schemas/cve/create-cve-record-cna-re /* eslint-disable no-multi-str */ const doc = { info: { - version: '2.3.3', + version: '2.4.0', title: 'CVE Services API', description: "The CVE Services API supports automation tooling for the CVE Program. Credentials are \ required for most service endpoints. Representatives of \