Skip to content

Commit 3300b06

Browse files
authored
Merge pull request #1262 from CVEProject/jf-1258
Resolved issue #1258, adds feature to PUT /org/{shortname} to update new `last_active` field.
2 parents 8fc52ad + 85a4076 commit 3300b06

File tree

10 files changed

+637
-32
lines changed

10 files changed

+637
-32
lines changed

api-docs/openapi.json

+9-2
Original file line numberDiff line numberDiff line change
@@ -2099,7 +2099,7 @@
20992099
"Organization"
21002100
],
21012101
"summary": "Updates information about the organization specified by short name (accessible to Secretariat)",
2102-
"description": " <h2>Access Control</h2> <p>User must belong to an organization with the <b>Secretariat</b> role</p> <h2>Expected Behavior</h2> <p><b>Secretariat:</b> Updates any organization's information</p>",
2102+
"description": " <h2>Access Control</h2> <p>User must belong to an organization with the <b>Secretariat</b> role, or user must belong to the organization specified by short name</p> <h2>Expected Behavior</h2> <p><b>Secretariat:</b> Updates any organization's information</p> <p><b>Non-secretariat:</b> Updates 'last_active' timestamp to show that an org is still active</p>",
21032103
"operationId": "orgUpdateSingle",
21042104
"parameters": [
21052105
{
@@ -2142,7 +2142,14 @@
21422142
"content": {
21432143
"application/json": {
21442144
"schema": {
2145-
"$ref": "../schemas/org/update-org-response.json"
2145+
"oneOf": [
2146+
{
2147+
"$ref": "../schemas/org/update-org-response.json"
2148+
},
2149+
{
2150+
"$ref": "../schemas/org/am-i-alive-response.json"
2151+
}
2152+
]
21462153
}
21472154
}
21482155
}

schemas/org/am-i-alive-response.json

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-04/schema",
3+
"type": "object",
4+
"properties": {
5+
"message": {
6+
"type": "string",
7+
"description": "Success description"
8+
},
9+
"updated": {
10+
"type": "object",
11+
"properties": {
12+
"last_active": {
13+
"type": "string",
14+
"format": "date-time",
15+
"description": "The time the organization was last active."
16+
}
17+
}
18+
}
19+
}
20+
}

src/controller/org.controller/index.js

+11-5
Original file line numberDiff line numberDiff line change
@@ -245,9 +245,10 @@ router.put('/org/:shortname',
245245
#swagger.summary = "Updates information about the organization specified by short name (accessible to Secretariat)"
246246
#swagger.description = "
247247
<h2>Access Control</h2>
248-
<p>User must belong to an organization with the <b>Secretariat</b> role</p>
248+
<p>User must belong to an organization with the <b>Secretariat</b> role, or user must belong to the organization specified by short name</p>
249249
<h2>Expected Behavior</h2>
250-
<p><b>Secretariat:</b> Updates any organization's information</p>"
250+
<p><b>Secretariat:</b> Updates any organization's information</p>
251+
<p><b>Non-secretariat:</b> Updates 'last_active' timestamp to show that an org is still active</p>"
251252
#swagger.parameters['shortname'] = { description: 'The shortname of the organization' }
252253
#swagger.parameters['$ref'] = [
253254
'#/components/parameters/id_quota',
@@ -263,7 +264,12 @@ router.put('/org/:shortname',
263264
description: 'Returns information about the organization updated',
264265
content: {
265266
"application/json": {
266-
schema: { $ref: '../schemas/org/update-org-response.json' }
267+
schema: {
268+
oneOf: [
269+
{ $ref: '../schemas/org/update-org-response.json' },
270+
{ $ref: '../schemas/org/am-i-alive-response.json' }
271+
]
272+
}
267273
}
268274
}
269275
}
@@ -309,10 +315,10 @@ router.put('/org/:shortname',
309315
}
310316
*/
311317
mw.validateUser,
312-
mw.onlySecretariat,
318+
param(['shortname']).isString().trim().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }),
319+
mw.validateOrg,
313320
query().custom((query) => { return mw.validateQueryParameterNames(query, ['new_short_name', 'id_quota', 'name', 'active_roles.add', 'active_roles.remove']) }),
314321
query(['new_short_name', 'id_quota', 'name', 'active_roles.add', 'active_roles.remove']).custom((val) => { return mw.containsNoInvalidCharacters(val) }),
315-
param(['shortname']).isString().trim().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }),
316322
query(['new_short_name']).optional().isString().trim().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }),
317323
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),
318324
query(['name']).optional().isString().trim().notEmpty(),

src/controller/org.controller/org.controller.js

+41-24
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ async function updateOrg (req, res, next) {
329329
const addRoles = []
330330
const orgRepo = req.ctx.repositories.getOrgRepository()
331331
const org = await orgRepo.findOneByShortName(shortName)
332+
const orgMakingChanges = req.ctx.org
332333
let agt = setAggregateOrgObj({ short_name: shortName })
333334

334335
// org doesn't exist
@@ -337,30 +338,38 @@ async function updateOrg (req, res, next) {
337338
return res.status(404).json(error.orgDnePathParam(shortName))
338339
}
339340

340-
Object.keys(req.ctx.query).forEach(k => {
341-
const key = k.toLowerCase()
342-
343-
if (key === 'new_short_name') {
344-
newOrg.short_name = req.ctx.query.new_short_name
345-
agt = setAggregateOrgObj({ short_name: newOrg.short_name })
346-
} else if (key === 'name') {
347-
newOrg.name = req.ctx.query.name
348-
} else if (key === 'id_quota') {
349-
newOrg.policies.id_quota = req.ctx.query.id_quota
350-
} else if (key === 'active_roles.add') {
351-
if (Array.isArray(req.ctx.query['active_roles.add'])) {
352-
req.ctx.query['active_roles.add'].forEach(r => {
353-
addRoles.push(r)
354-
})
355-
}
356-
} else if (key === 'active_roles.remove') {
357-
if (Array.isArray(req.ctx.query['active_roles.remove'])) {
358-
req.ctx.query['active_roles.remove'].forEach(r => {
359-
removeRoles.push(r)
360-
})
341+
const isSec = await orgRepo.isSecretariat(orgMakingChanges)
342+
343+
if (isSec) {
344+
Object.keys(req.ctx.query).forEach(k => {
345+
const key = k.toLowerCase()
346+
347+
if (key === 'new_short_name') {
348+
newOrg.short_name = req.ctx.query.new_short_name
349+
agt = setAggregateOrgObj({ short_name: newOrg.short_name })
350+
} else if (key === 'name') {
351+
newOrg.name = req.ctx.query.name
352+
} else if (key === 'id_quota') {
353+
newOrg.policies.id_quota = req.ctx.query.id_quota
354+
} else if (key === 'active_roles.add') {
355+
if (Array.isArray(req.ctx.query['active_roles.add'])) {
356+
req.ctx.query['active_roles.add'].forEach(r => {
357+
addRoles.push(r)
358+
})
359+
}
360+
} else if (key === 'active_roles.remove') {
361+
if (Array.isArray(req.ctx.query['active_roles.remove'])) {
362+
req.ctx.query['active_roles.remove'].forEach(r => {
363+
removeRoles.push(r)
364+
})
365+
}
361366
}
362-
}
363-
})
367+
})
368+
}
369+
370+
if (shortName === orgMakingChanges) {
371+
newOrg.last_active = Date.now()
372+
}
364373

365374
// updating the org's roles
366375
if (org) {
@@ -403,6 +412,13 @@ async function updateOrg (req, res, next) {
403412
result = await orgRepo.aggregate(agt)
404413
result = result.length > 0 ? result[0] : null
405414

415+
if (!isSec) {
416+
if (!result || !result.last_active) {
417+
return res.status(500).json(error.serverError())
418+
}
419+
result = { last_active: result.last_active }
420+
}
421+
406422
const responseMessage = {
407423
message: shortName + ' organization was successfully updated.',
408424
updated: result
@@ -819,7 +835,8 @@ function setAggregateOrgObj (query) {
819835
name: true,
820836
'authority.active_roles': true,
821837
'policies.id_quota': true,
822-
time: true
838+
time: true,
839+
last_active: true
823840
}
824841
}
825842
]

src/middleware/middleware.js

+27
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,32 @@ async function validateUser (req, res, next) {
135135
}
136136
}
137137

138+
async function validateOrg (req, res, next) {
139+
const org = req.ctx.org
140+
const reqOrg = req.params.shortname
141+
const orgRepo = req.ctx.repositories.getOrgRepository()
142+
const CONSTANTS = getConstants()
143+
144+
try {
145+
logger.info({ uuid: req.ctx.uuid, message: 'Authenticating org: ' + org })
146+
147+
const isSec = await orgRepo.isSecretariat(org)
148+
if (!isSec) {
149+
if (org !== reqOrg) {
150+
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.' })
151+
return res.status(403).json(error.secretariatOnly())
152+
} else if (Object.keys(req.query).length > 0) {
153+
return res.status(403).json(error.secretariatOnly())
154+
}
155+
}
156+
157+
logger.info({ uuid: req.ctx.uuid, message: 'Confirmed ' + org + ' has the authority to make changes to ' + reqOrg })
158+
next()
159+
} catch (err) {
160+
next(err)
161+
}
162+
}
163+
138164
// Checks that the requester belongs to an org that has the 'BULK_DOWNLOAD' role
139165
async function onlySecretariatOrBulkDownload (req, res, next) {
140166
const org = req.ctx.org
@@ -483,6 +509,7 @@ module.exports = {
483509
setCacheControl,
484510
optionallyValidateUser,
485511
validateUser,
512+
validateOrg,
486513
onlySecretariat,
487514
onlySecretariatOrBulkDownload,
488515
onlySecretariatOrAdmin,

src/model/org.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ const schema = {
2424
created: Date,
2525
modified: Date
2626
},
27-
inUse: Boolean
27+
inUse: Boolean,
28+
last_active: Date
2829
}
2930

3031
const OrgSchema = new mongoose.Schema(schema, { collection: 'Org', timestamps: { createdAt: 'time.created', updatedAt: 'time.modified' } })

0 commit comments

Comments
 (0)