Skip to content

Commit

Permalink
Merge branch 'dev' into api-docs-redirect-correction
Browse files Browse the repository at this point in the history
  • Loading branch information
jdaigneau5 committed Aug 26, 2024
2 parents 20c1596 + 3300b06 commit 59e72b7
Show file tree
Hide file tree
Showing 12 changed files with 649 additions and 44 deletions.
16 changes: 8 additions & 8 deletions .github/workflows/test-http.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,30 @@ 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"
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:
name: test-results
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' }}
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/test-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
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
11 changes: 9 additions & 2 deletions api-docs/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -2099,7 +2099,7 @@
"Organization"
],
"summary": "Updates information about the organization specified by short name (accessible to Secretariat)",
"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>",
"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>",
"operationId": "orgUpdateSingle",
"parameters": [
{
Expand Down Expand Up @@ -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"
}
]
}
}
}
Expand Down
20 changes: 20 additions & 0 deletions schemas/org/am-i-alive-response.json
Original file line number Diff line number Diff line change
@@ -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."
}
}
}
}
}
16 changes: 11 additions & 5 deletions src/controller/org.controller/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,9 +245,10 @@ router.put('/org/:shortname',
#swagger.summary = "Updates information about the organization specified by short name (accessible to Secretariat)"
#swagger.description = "
<h2>Access Control</h2>
<p>User must belong to an organization with the <b>Secretariat</b> role</p>
<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>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>"
#swagger.parameters['shortname'] = { description: 'The shortname of the organization' }
#swagger.parameters['$ref'] = [
'#/components/parameters/id_quota',
Expand All @@ -263,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' }
]
}
}
}
}
Expand Down Expand Up @@ -309,10 +315,10 @@ router.put('/org/:shortname',
}
*/
mw.validateUser,
mw.onlySecretariat,
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(),
Expand Down
65 changes: 41 additions & 24 deletions src/controller/org.controller/org.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ 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 })

// org doesn't exist
Expand All @@ -337,30 +338,38 @@ async function updateOrg (req, res, next) {
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)
})
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)
})
}
}
}
})
})
}

if (shortName === orgMakingChanges) {
newOrg.last_active = Date.now()
}

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

if (!isSec) {
if (!result || !result.last_active) {
return res.status(500).json(error.serverError())
}
result = { last_active: result.last_active }
}

const responseMessage = {
message: shortName + ' organization was successfully updated.',
updated: result
Expand Down Expand Up @@ -819,7 +835,8 @@ function setAggregateOrgObj (query) {
name: true,
'authority.active_roles': true,
'policies.id_quota': true,
time: true
time: true,
last_active: true
}
}
]
Expand Down
27 changes: 27 additions & 0 deletions src/middleware/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,32 @@ async function validateUser (req, res, next) {
}
}

async function validateOrg (req, res, next) {
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) {
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(403).json(error.secretariatOnly())
} else if (Object.keys(req.query).length > 0) {
return res.status(403).json(error.secretariatOnly())
}
}

logger.info({ uuid: req.ctx.uuid, message: 'Confirmed ' + org + ' has the authority to make changes to ' + reqOrg })
next()
} catch (err) {
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
Expand Down Expand Up @@ -483,6 +509,7 @@ module.exports = {
setCacheControl,
optionallyValidateUser,
validateUser,
validateOrg,
onlySecretariat,
onlySecretariatOrBulkDownload,
onlySecretariatOrAdmin,
Expand Down
3 changes: 2 additions & 1 deletion src/model/org.js
Original file line number Diff line number Diff line change
Expand Up @@ -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' } })
Expand Down
Loading

0 comments on commit 59e72b7

Please sign in to comment.