Skip to content
Closed
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
118 changes: 76 additions & 42 deletions src/controller/audit.controller/audit.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const validateUUID = require('uuid').validate
* Create a new audit document
* Called by POST /api/audit/org/
*/
async function createAuditDocument (req, res, next) {
async function createAuditDocumentForOrg (req, res, next) {
try {
const session = await mongoose.startSession()
const repo = req.ctx.repositories.getAuditRepository()
Expand Down Expand Up @@ -72,9 +72,25 @@ async function createAuditDocument (req, res, next) {
await session.abortTransaction()
return res.status(400).json(error.missingRequiredField('change_author'))
}

// Process entry immediately after validation
returnValue = await repo.appendToAuditHistoryForOrg(
body.target_uuid,
entry.audit_object,
entry.change_author,
{ session }
)
}
} else {
// Create audit document with initial empty entry or default entry
returnValue = await repo.appendToAuditHistoryForOrg(
body.target_uuid,
body.audit_object || {},
body.change_author || req.ctx.org,
{ session }
)
}
returnValue = await repo.createAuditDocument(body, { session })

await session.commitTransaction()

logger.info({
Expand All @@ -100,7 +116,7 @@ async function createAuditDocument (req, res, next) {
* Called by PUT /api/audit/org/
* Allows for multiple appends in a single request
*/
async function appendToAuditHistory (req, res, next) {
async function appendToAuditHistoryForOrg (req, res, next) {
try {
const session = await mongoose.startSession()
const repo = req.ctx.repositories.getAuditRepository()
Expand Down Expand Up @@ -149,7 +165,7 @@ async function appendToAuditHistory (req, res, next) {
}

// Append this history entry
returnValue = await repo.appendToAuditHistory(
returnValue = await repo.appendToAuditHistoryForOrg(
body.target_uuid,
entry.audit_object,
entry.change_author,
Expand Down Expand Up @@ -190,7 +206,7 @@ async function appendToAuditHistory (req, res, next) {
* Get all audit documents
* Called by GET /api/audit/org/
*/
async function getAllAuditDocuments (req, res, next) {
async function getAllOrgAuditDocuments (req, res, next) {
try {
const session = await mongoose.startSession()
const repo = req.ctx.repositories.getAuditRepository()
Expand All @@ -213,7 +229,7 @@ async function getAllAuditDocuments (req, res, next) {
* Get audit document by its document UUID
* Called by GET /api/audit/org/document/:document_uuid
*/
async function getAuditByDocumentUUID (req, res, next) {
async function getOrgAuditByDocumentUUID (req, res, next) {
try {
const session = await mongoose.startSession()
const repo = req.ctx.repositories.getAuditRepository()
Expand Down Expand Up @@ -247,48 +263,53 @@ async function getAuditByDocumentUUID (req, res, next) {
next(err)
}
}

/**
* Get audit history by target UUID
* Called by GET /api/audit/org/:target_uuid
* TODO: remove comment-> I changed parameter name from org_identifier to target_uuid to be more generic.
* Get audit history by target identifier (shortname or UUID)
* Called by GET /api/audit/org/:identifier
*/
async function getAuditByTargetUUID (req, res, next) {
async function getOrgAuditByOrgIdentifier (req, res, next) {
try {
const session = await mongoose.startSession()
const repo = req.ctx.repositories.getAuditRepository()
const orgRepo = req.ctx.repositories.getBaseOrgRepository()
const targetUUID = req.ctx.params.target_uuid
const identifier = req.ctx.params.org_identifier
const identifierIsUUID = validateUUID(identifier)
let returnValue

if (!targetUUID) {
logger.info({ uuid: req.ctx.uuid, message: 'Missing target_uuid parameter' })
return res.status(400).json(error.missingRequiredField('target_uuid'))
}

if (!validateUUID(targetUUID)) {
logger.info({ uuid: req.ctx.uuid, message: 'Invalid target_uuid format' })
return res.status(400).json(error.invalidUUID('target_uuid'))
if (!identifier) {
return res.status(400).json(error.missingRequiredField('identifier'))
}

try {
session.startTransaction()

// Find the target organization
const targetOrg = await orgRepo.findOneByUUID(targetUUID, { session })
// Find the target organization by either UUID or shortname
const targetOrg = identifierIsUUID
? await orgRepo.findOneByUUID(identifier, { session })
: await orgRepo.findOneByShortName(identifier, { session })

if (!targetOrg) {
logger.info({ uuid: req.ctx.uuid, message: `No organization found with UUID ${targetUUID}` })
logger.info({
uuid: req.ctx.uuid,
message: `No organization found with ${identifierIsUUID ? 'UUID' : 'shortname'} ${identifier}`
})
await session.abortTransaction()
return res.status(404).json(error.orgDne(targetUUID))
return res.status(404).json(error.orgDne(identifier))
}

// TODO: confirm middleware is checking admin and secretariat permissions properly
// Get the org's UUID for audit lookup
const targetUUID = targetOrg.UUID

returnValue = await repo.findOneByTargetUUID(targetUUID, { session })

if (!returnValue) {
logger.info({ uuid: req.ctx.uuid, message: `No audit history found for target UUID ${targetUUID}` })
logger.info({
uuid: req.ctx.uuid,
message: `No audit history found for organization ${identifier} (UUID: ${targetUUID})`
})
await session.abortTransaction()
return res.status(404).json(error.auditDneByTarget(targetUUID))
return res.status(404).json(error.auditDneByTarget(identifier))
}

await session.commitTransaction()
Expand All @@ -301,7 +322,7 @@ async function getAuditByTargetUUID (req, res, next) {

logger.info({
uuid: req.ctx.uuid,
message: `Audit history for target UUID ${targetUUID} sent to user ${req.ctx.user}`
message: `Audit history for ${identifierIsUUID ? 'UUID' : 'shortname'} ${identifier} sent to user ${req.ctx.user}`
})
return res.status(200).json(returnValue)
} catch (err) {
Expand All @@ -317,18 +338,14 @@ async function getLastXChanges (req, res, next) {
try {
const session = await mongoose.startSession()
const repo = req.ctx.repositories.getAuditRepository()
const targetUUID = req.ctx.params.target_uuid
const orgRepo = req.ctx.repositories.getBaseOrgRepository()
const identifier = req.ctx.params.org_identifier
const identifierIsUUID = validateUUID(identifier)
const numberOfChanges = parseInt(req.ctx.params.number_of_changes)
let returnValue

if (!targetUUID) {
logger.info({ uuid: req.ctx.uuid, message: 'Missing org_identifier parameter' })
return res.status(400).json(error.missingRequiredField('org_identifier'))
}

if (!validateUUID(targetUUID)) {
logger.info({ uuid: req.ctx.uuid, message: 'Invalid target_uuid format' })
return res.status(400).json(error.invalidUUID('target_uuid'))
if (!identifier) {
return res.status(400).json(error.missingRequiredField('identifier'))
}

if (isNaN(numberOfChanges) || numberOfChanges < 1) {
Expand All @@ -339,6 +356,23 @@ async function getLastXChanges (req, res, next) {
try {
session.startTransaction()

// Find the target organization by either UUID or shortname
const targetOrg = identifierIsUUID
? await orgRepo.findOneByUUID(identifier, { session })
: await orgRepo.findOneByShortName(identifier, { session })

if (!targetOrg) {
logger.info({
uuid: req.ctx.uuid,
message: `No organization found with ${identifierIsUUID ? 'UUID' : 'shortname'} ${identifier}`
})
await session.abortTransaction()
return res.status(404).json(error.orgDne(identifier))
}

// Get the org's UUID for audit lookup
const targetUUID = targetOrg.UUID

const lastChanges = await repo.getLastXChanges(targetUUID, numberOfChanges, { session })

if (!lastChanges || lastChanges.length === 0) {
Expand All @@ -362,7 +396,7 @@ async function getLastXChanges (req, res, next) {

logger.info({
uuid: req.ctx.uuid,
message: `Last ${numberOfChanges} changes for ${targetUUID} sent to user ${req.ctx.user}`
message: `Last ${numberOfChanges} changes for ${identifier} sent to user ${req.ctx.user}`
})
return res.status(200).json(returnValue)
} catch (err) {
Expand All @@ -371,10 +405,10 @@ async function getLastXChanges (req, res, next) {
}

module.exports = {
AUDIT_CREATE_SINGLE: createAuditDocument,
AUDIT_UPDATE: appendToAuditHistory,
AUDIT_GET_ALL: getAllAuditDocuments,
AUDIT_GET_BY_UUID: getAuditByDocumentUUID,
AUDIT_GET_BY_TARGET_UUID: getAuditByTargetUUID,
AUDIT_CREATE_SINGLE: createAuditDocumentForOrg,
AUDIT_UPDATE: appendToAuditHistoryForOrg,
AUDIT_GET_ALL: getAllOrgAuditDocuments,
AUDIT_GET_BY_UUID: getOrgAuditByDocumentUUID,
AUDIT_GET_BY_ORG_IDENTIFIER: getOrgAuditByOrgIdentifier,
AUDIT_GET_LAST: getLastXChanges
}
6 changes: 3 additions & 3 deletions src/controller/audit.controller/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ router.get('/audit/org/document/:document_uuid',
)

// Get audit by org identifier (Secretariat or Admin)
router.get('/audit/org/:target_uuid',
router.get('/audit/org/:org_identifier',
mw.validateUser,
mw.onlySecretariatOrAdmin,
auditMw.parseGetParams,
controller.AUDIT_GET_BY_TARGET_UUID
controller.AUDIT_GET_BY_ORG_IDENTIFIER
)

// Get last X changes (Secretariat or Org Admin)
router.get('/audit/org/:target_uuid/:number_of_changes',
router.get('/audit/org/:org_identifier/:number_of_changes',
mw.onlySecretariatOrAdmin,
mw.validateUser,
auditMw.parseGetParams,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@
const agt = setAggregateUserObj({})
const pg = await repo.aggregatePaginate(agt, options)

await RegistryOrg.populateOrgAffiliations(pg.itemsList)
await RegistryOrg.populateCVEProgramOrgMembership(pg.itemsList)

const payload = { users: pg.itemsList }

if (pg.itemCount >= CONSTANTS.PAGINATOR_OPTIONS.limit) {
Expand Down Expand Up @@ -77,7 +74,7 @@

// TODO: check if affiliated orgs and program orgs exist, and if their membership limit is reached

const newUser = new RegistryUser()

Check failure on line 77 in src/controller/registry-user.controller/registry-user.controller.js

View workflow job for this annotation

GitHub Actions / lint-src (16.x)

'RegistryUser' is not defined
Object.keys(body).map(k => k.toLowerCase()).forEach(k => {
if (k === 'user_id' || k === 'username') {
newUser.user_id = body[k]
Expand Down Expand Up @@ -143,7 +140,7 @@
// Check if requester is Admin of the designated user's org

const user = await registryUserRepo.findOneByUUID(userUUID)
const newUser = new RegistryUser()

Check failure on line 143 in src/controller/registry-user.controller/registry-user.controller.js

View workflow job for this annotation

GitHub Actions / lint-src (16.x)

'RegistryUser' is not defined

// Sets the name values to what currently exists in the database, this ensures data is retained during partial name updates
newUser.name.first = user.name.first
Expand Down
61 changes: 33 additions & 28 deletions src/repositories/auditRepository.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const Audit = require('../model/audit')
const BaseRepository = require('./baseRepository')
const BaseOrgRepository = require('./baseOrgRepository')
const uuid = require('uuid')

class AuditRepository extends BaseRepository {
Expand All @@ -13,57 +14,61 @@ class AuditRepository extends BaseRepository {
return validateObject
}

/**
* Create a new audit document
*/
async createAuditDocument (data, options = {}) {
const auditData = {
uuid: uuid.v4(),
target_uuid: data.target_uuid,
history: data.history || []
}

const audit = new Audit(auditData)
const result = await audit.save(options)
return result.toObject()
}

/**
* Append a new entry to the audit history
* Creates document if it doesn't exist
*/
async appendToAuditHistory (targetUUID, auditObject, changeAuthor, options = {}) {
async appendToAuditHistoryForOrg (targetUUID, auditObject, changeAuthor, options = {}) {
const historyEntry = {
timestamp: new Date(),
audit_object: auditObject,
change_author: changeAuthor
}

try {
// Try to find existing document
let audit = await Audit.findOne({ target_uuid: targetUUID })
let audit = await this.findOneByTargetUUID(targetUUID, options)

if (!audit) {
if (!audit) {
// Create new document if doesn't exist
audit = new Audit({
uuid: uuid.v4(),
target_uuid: targetUUID,
history: [historyEntry]
})
} else {
// Assuming 'uuid' is available for generating a new UUID
audit = new Audit({
uuid: uuid.v4(),
target_uuid: targetUUID,
history: [historyEntry]
})
} else {
// Append to existing history
audit.history.push(historyEntry)
audit.history.push(historyEntry)
}

const result = await audit.save(options)
return result.toObject()
} catch (error) {
throw new Error('Failed to save audit history entry.')
}
}

const result = await audit.save(options)
return result.toObject()
/**
* Find audit document by target UUID
*/
async findOneByOrgShortname (orgShortName, options = {}) {
const baseOrgRepository = new BaseOrgRepository()
const org = await baseOrgRepository.findOneByShortName(orgShortName)
if (!org) {
return null
}
const query = { target_uuid: org.UUID }
return this.collection.findOne(query, null, options)
}

/**
* Find audit document by target UUID
*/
async findOneByTargetUUID (targetUUID, options = {}) {
const query = { target_uuid: targetUUID }
return this.collection.findOne(query, null, options)
const auditObject = await Audit.findOne(query, null, options)
return auditObject
}

/**
Expand Down
Loading
Loading