Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions src/controller/org.controller/org.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ async function updateOrg (req, res, next) {

const session = await mongoose.startSession({ causalConsistency: false })
let responseMessage
let payload
// Get the query parameters as JSON
// These are validated by the middleware in org/index.js
const queryParametersJson = req.ctx.query
Expand Down Expand Up @@ -402,10 +403,14 @@ async function updateOrg (req, res, next) {
const updatedOrg = await orgRepository.updateOrg(shortNameUrlParameter, queryParametersJson, { session }, !req.useRegistry, requestingUserUUID, isAdmin, isSecretariat)

responseMessage = { message: `${updatedOrg.short_name} organization was successfully updated.`, updated: updatedOrg } // Clarify message
const payload = { action: 'update_org', change: `${updatedOrg.short_name} organization was successfully updated.`, org: updatedOrg }
payload.user_UUID = await userRepo.getUserUUID(req.ctx.user, updatedOrg.UUID)
payload.org_UUID = updatedOrg.UUID
payload.req_UUID = req.ctx.uuid
payload = {
action: 'update_org',
change: `${updatedOrg.short_name} organization was successfully updated.`,
req_UUID: req.ctx.uuid,
org_UUID: updatedOrg.UUID,
user_UUID: requestingUserUUID,
org: updatedOrg
}
await session.commitTransaction()
} catch (error) {
await session.abortTransaction()
Expand All @@ -414,6 +419,7 @@ async function updateOrg (req, res, next) {
await session.endSession()
}

logger.info(JSON.stringify(payload))
return res.status(200).json(responseMessage)
} catch (err) {
next(err)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ async function updateOrg (req, res, next) {
action: 'update_registry_org',
change: body?.short_name + 'organization was successfully updated, but joint approval is required for some fields. Check the ReviewObject for your org to check for a reply from the Secretariat about Joint Approval items.',
req_UUID: req.ctx.uuid,
org_UUID: await repo.getOrgUUID(req.ctx.org),
org_UUID: updatedOrg.UUID,
org: updatedOrg
}

Expand All @@ -438,7 +438,7 @@ async function updateOrg (req, res, next) {
action: 'update_registry_org',
change: body?.short_name + ' was successfully updated.',
req_UUID: req.ctx.uuid,
org_UUID: await repo.getOrgUUID(req.ctx.org),
org_UUID: updatedOrg.UUID,
org: updatedOrg
}
logger.info(JSON.stringify(payload))
Expand Down Expand Up @@ -466,6 +466,7 @@ async function deleteOrg (req, res, next) {
const session = await mongoose.startSession({ causalConsistency: false })
const repo = req.ctx.repositories.getBaseOrgRepository()
const shortName = req.ctx.params.identifier
let targetOrgUUID

try {
session.startTransaction()
Expand All @@ -476,6 +477,7 @@ async function deleteOrg (req, res, next) {
return res.status(404).json(error.orgDnePathParam(shortName))
}

targetOrgUUID = org.UUID
await repo.deleteOrg(shortName, { session })
await session.commitTransaction()
} catch (deleteErr) {
Expand All @@ -493,7 +495,7 @@ async function deleteOrg (req, res, next) {
action: 'delete_registry_org',
change: shortName + ' was successfully deleted.',
req_UUID: req.ctx.uuid,
org_UUID: await repo.getOrgUUID(req.ctx.org)
org_UUID: targetOrgUUID
}
logger.info(JSON.stringify(payload))
return res.status(200).json(responseMessage)
Expand Down
51 changes: 51 additions & 0 deletions test/unit-tests/org/orgUpdateTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const error = new errors.OrgControllerError()
const orgFixtures = require('./mockObjects.org')
const orgController = require('../../../src/controller/org.controller/org.controller')
const orgParams = require('../../../src/controller/org.controller/org.middleware')
const logger = require('../../../src/middleware/logger')

class NullUserRepo {
async getUserUUID () {
Expand Down Expand Up @@ -229,6 +230,56 @@ describe('Testing the PUT /org/:shortname endpoint in Org Controller', () => {
})
})

it('Org update logs the audit payload with the requesting user UUID', async () => {
const requesterUserUUID = 'b48b4f8e-b82a-45ff-9e3b-3bf1534ad663'
const logStub = sinon.stub(logger, 'info')
const getUserUUID = sinon.stub().callsFake((username, orgShortName) => {
if (orgShortName === orgFixtures.owningOrg.UUID) {
throw new Error('Requester user UUID should not be looked up by the updated org UUID')
}
return requesterUserUUID
})
const userRepo = {
getUserUUID,
isAdmin: sinon.stub().resolves(false)
}

app.route('/org-updated-logs-payload/:shortname')
.put((req, res, next) => {
const factory = {
getBaseOrgRepository: () => { return new OrgUpdatedAddingRole() },
getBaseUserRepository: () => { return userRepo }
}
req.ctx.repositories = factory
next()
}, orgParams.parsePostParams, orgController.ORG_UPDATE_SINGLE)

const res = await chai.request(app)
.put(`/org-updated-logs-payload/${orgFixtures.owningOrg.short_name}?active_roles.add=ROOT`)
.set(orgFixtures.secretariatHeader)

const updateLogCall = logStub.getCalls().find(call => {
try {
return JSON.parse(call.args[0]).action === 'update_org'
} catch (err) {
return false
}
})

expect(res).to.have.status(200)
expect(getUserUUID.calledOnce).to.equal(true)
expect(getUserUUID.firstCall.args[0]).to.equal(orgFixtures.secretariatHeader['CVE-API-USER'])
expect(getUserUUID.firstCall.args[1]).to.equal(orgFixtures.secretariatHeader['CVE-API-ORG'])
expect(updateLogCall).to.not.equal(undefined)
const payload = JSON.parse(updateLogCall.args[0])
expect(payload.action).to.equal('update_org')
expect(payload.change).to.equal(`${orgFixtures.owningOrg.short_name} organization was successfully updated.`)
expect(payload.org_UUID).to.equal(orgFixtures.owningOrg.UUID)
expect(payload.user_UUID).to.equal(requesterUserUUID)
expect(payload.req_UUID).to.be.a('string')
expect(payload.org.short_name).to.equal(orgFixtures.owningOrg.short_name)
})

it('Org is unchanged: Adding a role that the org already have', (done) => {
const CONSTANTS = getConstants()

Expand Down
167 changes: 167 additions & 0 deletions test/unit-tests/registry-org/registryOrgPayloadTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
const chai = require('chai')
const sinon = require('sinon')
const mongoose = require('mongoose')
const expect = chai.expect

const registryOrgController = require('../../../src/controller/registry-org.controller/registry-org.controller')
const logger = require('../../../src/middleware/logger')

describe('Registry org payload logging', () => {
let mockSession

beforeEach(() => {
mockSession = {
startTransaction: sinon.stub(),
commitTransaction: sinon.stub().resolves(),
abortTransaction: sinon.stub().resolves(),
endSession: sinon.stub().resolves()
}
sinon.stub(mongoose, 'startSession').resolves(mockSession)
sinon.stub(logger, 'info')
})

afterEach(() => {
sinon.restore()
})

function createResponse () {
return {
status: sinon.stub().returnsThis(),
json: sinon.stub().returnsThis()
}
}

function getLoggedPayload (action) {
const logCall = logger.info.getCalls().find(call => {
try {
return JSON.parse(call.args[0]).action === action
} catch (err) {
return false
}
})

expect(logCall).to.not.equal(undefined)
return JSON.parse(logCall.args[0])
}

async function updateRegistryOrg (updatedOrg) {
const callerOrgUUID = '54f5fa83-86f6-44fd-b45a-290087680ac0'
const org = {
UUID: updatedOrg.UUID,
short_name: updatedOrg.short_name,
toObject: () => ({ ...updatedOrg })
}
const repo = {
isSecretariatByShortName: sinon.stub().resolves(true),
findOneByShortName: sinon.stub().resolves(org),
validateOrg: sinon.stub().returns({ isValid: true }),
checkAliasCollisions: sinon.stub().resolves(null),
getOrgUUID: sinon.stub().callsFake(shortName => {
if (shortName === updatedOrg.short_name) return updatedOrg.UUID
if (shortName === 'mitre') return callerOrgUUID
return null
}),
updateOrgFull: sinon.stub().resolves(updatedOrg)
}
const userRepo = {
isAdmin: sinon.stub().resolves(false),
findOneByUsernameAndOrgShortname: sinon.stub().resolves({ UUID: 'requesting-user-uuid' })
}
const conversationRepo = {
getAllByTargetUUID: sinon.stub().resolves([])
}
const reviewRepo = {
getOrgReviewObjectByOrgShortname: sinon.stub().resolves(null)
}
const req = {
ctx: {
uuid: 'request-uuid',
org: 'mitre',
user: 'test_secretariat_0@mitre.org',
params: { shortname: updatedOrg.short_name },
body: {
short_name: updatedOrg.short_name,
long_name: 'Target Org',
authority: ['CNA']
},
repositories: {
getBaseOrgRepository: () => repo,
getBaseUserRepository: () => userRepo,
getConversationRepository: () => conversationRepo,
getReviewObjectRepository: () => reviewRepo
}
}
}
const res = createResponse()
const next = sinon.stub()

await registryOrgController.UPDATE_ORG(req, res, next)

return { payload: getLoggedPayload('update_registry_org'), res, next, callerOrgUUID }
}

it('logs the updated registry org UUID for update_registry_org', async () => {
const updatedOrg = {
UUID: 'cc5f9414-acf2-4e43-8071-67e8ea810113',
short_name: 'target_org',
authority: ['CNA'],
joint_approval_required: false
}

const { payload, res, next, callerOrgUUID } = await updateRegistryOrg(updatedOrg)
expect(res.status.calledWith(200)).to.equal(true)
expect(next.notCalled).to.equal(true)
expect(payload.org_UUID).to.equal(updatedOrg.UUID)
expect(payload.org_UUID).to.not.equal(callerOrgUUID)
expect(payload.org.UUID).to.equal(updatedOrg.UUID)
})

it('logs the updated registry org UUID for update_registry_org when joint approval is required', async () => {
const updatedOrg = {
UUID: '895cdd1b-9825-4f10-b031-bffad6650c26',
short_name: 'target_org',
authority: ['CNA'],
joint_approval_required: true
}

const { payload, res, next, callerOrgUUID } = await updateRegistryOrg(updatedOrg)
expect(res.status.calledWith(200)).to.equal(true)
expect(next.notCalled).to.equal(true)
expect(payload.org_UUID).to.equal(updatedOrg.UUID)
expect(payload.org_UUID).to.not.equal(callerOrgUUID)
expect(payload.org.UUID).to.equal(updatedOrg.UUID)
})

it('logs the deleted registry org UUID for delete_registry_org', async () => {
const targetOrg = {
UUID: '8db8e7ed-a43f-40b8-9ebb-d68aa8dc2216',
short_name: 'target_org'
}
const repo = {
findOneByShortName: sinon.stub().resolves(targetOrg),
deleteOrg: sinon.stub().resolves(),
getOrgUUID: sinon.stub().throws(new Error('delete payload should not look up the caller org UUID'))
}
const req = {
ctx: {
uuid: 'request-uuid',
org: 'mitre',
params: { identifier: targetOrg.short_name },
repositories: {
getBaseOrgRepository: () => repo
}
}
}
const res = createResponse()
const next = sinon.stub()

await registryOrgController.DELETE_ORG(req, res, next)

const payload = getLoggedPayload('delete_registry_org')
expect(res.status.calledWith(200)).to.equal(true)
expect(next.notCalled).to.equal(true)
expect(repo.deleteOrg.calledWith(targetOrg.short_name)).to.equal(true)
expect(repo.getOrgUUID.notCalled).to.equal(true)
expect(payload.org_UUID).to.equal(targetOrg.UUID)
})
})
Loading