Skip to content

Commit 907ccbe

Browse files
authored
Merge branch 'dev' into emathew/fix-org-get-single-test
2 parents 8c64041 + 5ba8610 commit 907ccbe

File tree

13 files changed

+695
-341
lines changed

13 files changed

+695
-341
lines changed

src/controller/org.controller/index.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,18 @@ router.post('/registry/org/:shortname/user',
7979
mw.validateUser,
8080
mw.onlySecretariatOrAdmin,
8181
mw.onlyOrgWithPartnerRole,
82+
param(['shortname']).isString().trim().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }),
83+
body(['org_uuid']).optional().isString().trim(),
84+
body(['uuid']).optional().isString().trim(),
85+
body(['name.first']).optional().isString().trim().isLength({ max: CONSTANTS.MAX_FIRSTNAME_LENGTH }).withMessage(errorMsgs.FIRSTNAME_LENGTH),
86+
body(['name.last']).optional().isString().trim().isLength({ max: CONSTANTS.MAX_LASTNAME_LENGTH }).withMessage(errorMsgs.LASTNAME_LENGTH),
87+
body(['name.middle']).optional().isString().trim().isLength({ max: CONSTANTS.MAX_MIDDLENAME_LENGTH }).withMessage(errorMsgs.MIDDLENAME_LENGTH),
88+
body(['name.suffix']).optional().isString().trim().isLength({ max: CONSTANTS.MAX_SUFFIX_LENGTH }).withMessage(errorMsgs.SUFFIX_LENGTH),
89+
body(['authority.active_roles']).optional()
90+
.custom(mw.isFlatStringArray)
91+
.bail()
92+
.customSanitizer(toUpperCaseArray)
93+
.custom(isUserRole),
8294
parseError,
8395
parsePostParams,
8496
controller.USER_CREATE_SINGLE

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

Lines changed: 79 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ async function createOrg (req, res, next) {
281281
const body = req.ctx.body
282282
let returnValue
283283
// Do not allow the user to pass in a UUID
284-
if (body?.UUID ?? null) return res.status(400).json(error.uuidProvided('org'))
284+
if ((body?.UUID ?? null) || (body?.uuid ?? null)) return res.status(400).json(error.uuidProvided('org'))
285285

286286
try {
287287
session.startTransaction()
@@ -441,14 +441,26 @@ async function createUser (req, res, next) {
441441
try {
442442
const body = req.ctx.body
443443
const userRepo = req.ctx.repositories.getBaseUserRepository()
444+
const orgRepo = req.ctx.repositories.getBaseOrgRepository()
444445
const orgShortName = req.ctx.params.shortname
445446
let returnValue
446447

448+
// Check to make sure Org Exists first
449+
const orgUUID = await orgRepo.getOrgUUID(orgShortName)
450+
if (!orgUUID) {
451+
logger.info({ uuid: req.ctx.uuid, message: 'The user could not be created because ' + orgShortName + ' organization does not exist.' })
452+
return res.status(404).json(error.orgDnePathParam(orgShortName))
453+
}
454+
447455
// Do not allow the user to pass in a UUID
448-
if (body?.UUID ?? null) {
456+
if ((body?.UUID ?? null) || (body?.uuid ?? null)) {
449457
return res.status(400).json(error.uuidProvided('user'))
450458
}
451459

460+
if ((body?.org_UUID ?? null) || (body?.org_uuid ?? null)) {
461+
return res.status(400).json(error.uuidProvided('org'))
462+
}
463+
452464
try {
453465
session.startTransaction()
454466
if (req.useRegistry) {
@@ -461,6 +473,10 @@ async function createUser (req, res, next) {
461473
await session.abortTransaction()
462474
return res.status(400).json({ message: 'Parameters were invalid', errors: result.errors })
463475
}
476+
} else {
477+
if (!body?.username || typeof body?.username !== 'string' || !body?.username.length > 0) {
478+
return res.status(400).json({ message: 'Parameters were invalid', details: [{ param: 'username', msg: 'Parameter must be a non empty string' }] })
479+
}
464480
}
465481

466482
// Ask repo if user already exists
@@ -476,7 +492,7 @@ async function createUser (req, res, next) {
476492
}
477493

478494
const users = await userRepo.findUsersByOrgShortname(orgShortName, { session })
479-
if (users.toObject().length >= 100) {
495+
if (users.length >= 100) {
480496
await session.abortTransaction()
481497
return res.status(400).json(error.userLimitReached())
482498
}
@@ -552,6 +568,19 @@ async function updateUser (req, res, next) {
552568
return res.status(404).json(error.orgDnePathParam(shortNameParams))
553569
}
554570

571+
if (shortNameParams !== requesterShortName && !isRequesterSecretariat) {
572+
logger.info({ uuid: req.ctx.uuid, message: `${shortNameParams} organization data can only be modified by users of the same organization or the Secretariat.` })
573+
await session.abortTransaction()
574+
return res.status(403).json(error.notSameOrgOrSecretariat())
575+
}
576+
577+
// Specific check for org_short_name (Secretariat only)
578+
if (queryParametersJson.org_short_name && !isRequesterSecretariat) {
579+
logger.info({ uuid: req.ctx.uuid, message: 'Only Secretariat can reassign user organization.' })
580+
await session.abortTransaction()
581+
return res.status(403).json(error.notAllowedToChangeOrganization())
582+
}
583+
555584
if (!isRequesterSecretariat && !isAdmin) {
556585
if (targetUserUUID !== requesterUUID) {
557586
if (!targetUserUUID) {
@@ -565,29 +594,41 @@ async function updateUser (req, res, next) {
565594
}
566595
}
567596

568-
if (!targetUserUUID) {
569-
logger.info({ uuid: req.ctx.uuid, message: 'User DNE' })
570-
await session.abortTransaction()
571-
return res.status(404).json(error.userDne(usernameParams))
597+
const newOrgShortNameToMoveTo = queryParametersJson.org_short_name
598+
599+
if (newOrgShortNameToMoveTo) {
600+
if (newOrgShortNameToMoveTo === shortNameParams) {
601+
logger.info({ uuid: req.ctx.uuid, message: `User ${usernameParams} is already in organization ${newOrgShortNameToMoveTo}.` })
602+
await session.abortTransaction()
603+
return res.status(403).json(error.alreadyInOrg(newOrgShortNameToMoveTo, usernameParams))
604+
}
605+
606+
const newTargetRegistryOrgUUID = await orgRepo.getOrgUUID(newOrgShortNameToMoveTo, { session })
607+
608+
if (!newTargetRegistryOrgUUID) {
609+
logger.info({ uuid: req.ctx.uuid, message: `New target organization ${newOrgShortNameToMoveTo} does not exist.` })
610+
await session.abortTransaction()
611+
return res.status(404).json(error.orgDne(newOrgShortNameToMoveTo, 'org_short_name', 'query'))
612+
}
572613
}
573614

574-
if (shortNameParams !== requesterShortName && !isRequesterSecretariat) {
575-
logger.info({ uuid: req.ctx.uuid, message: `${shortNameParams} organization data can only be modified by users of the same organization or the Secretariat.` })
576-
await session.abortTransaction()
577-
return res.status(403).json(error.notSameOrgOrSecretariat())
615+
if (queryParametersJson.active) {
616+
if (requesterUUID === targetUserUUID) {
617+
await session.abortTransaction()
618+
return res.status(403).json(error.notOrgAdminOrSecretariatUpdate())
619+
}
578620
}
579621

580-
if (await userRepo.orgHasUser(shortNameParams, targetUserUUID, { session })) {
581-
logger.info({ uuid: req.ctx.uuid, message: `User ${usernameParams} does not exist for ${shortNameParams} organization.` })
622+
if (!targetUserUUID) {
623+
logger.info({ uuid: req.ctx.uuid, message: 'User DNE' })
582624
await session.abortTransaction()
583625
return res.status(404).json(error.userDne(usernameParams))
584626
}
585627

586-
// Specific check for org_short_name (Secretariat only)
587-
if (queryParametersJson.org_short_name && !isRequesterSecretariat) {
588-
logger.info({ uuid: req.ctx.uuid, message: 'Only Secretariat can reassign user organization.' })
628+
if (!await userRepo.orgHasUserByUUID(shortNameParams, targetUserUUID, { session })) {
629+
logger.info({ uuid: req.ctx.uuid, message: `User ${usernameParams} does not exist for ${shortNameParams} organization.` })
589630
await session.abortTransaction()
590-
return res.status(403).json(error.notAllowedToChangeOrganization())
631+
return res.status(404).json(error.userDne(usernameParams))
591632
}
592633

593634
// General permission check for fields requiring admin/secretariat
@@ -609,13 +650,6 @@ async function updateUser (req, res, next) {
609650
}
610651
}
611652

612-
if (queryParametersJson.active) {
613-
if (requesterUUID === targetUserUUID) {
614-
await session.abortTransaction()
615-
return res.status(403).json(error.notOrgAdminOrSecretariatUpdate())
616-
}
617-
}
618-
619653
// This is a special case, and needs to be handled in the controller, and not in the repository
620654
const rolesFromQuery = queryParametersJson['active_roles.remove'] ?? []
621655
const removeRolesCollector = []
@@ -633,25 +667,7 @@ async function updateUser (req, res, next) {
633667
}
634668
}
635669

636-
const newOrgShortNameToMoveTo = queryParametersJson.org_short_name
637-
638-
if (newOrgShortNameToMoveTo) {
639-
if (newOrgShortNameToMoveTo === shortNameParams) {
640-
logger.info({ uuid: req.ctx.uuid, message: `User ${usernameParams} is already in organization ${newOrgShortNameToMoveTo}.` })
641-
await session.abortTransaction()
642-
return res.status(403).json(error.alreadyInOrg(newOrgShortNameToMoveTo, usernameParams))
643-
}
644-
645-
const newTargetRegistryOrgUUID = await orgRepo.getOrgUUID(newOrgShortNameToMoveTo, { session })
646-
647-
if (!newTargetRegistryOrgUUID) {
648-
logger.info({ uuid: req.ctx.uuid, message: `New target organization ${newOrgShortNameToMoveTo} does not exist.` })
649-
await session.abortTransaction()
650-
return res.status(404).json(error.orgDne(newOrgShortNameToMoveTo, 'org_short_name', 'query'))
651-
}
652-
}
653-
654-
const payload = await userRepo.updateUser(usernameParams, shortNameParams, queryParametersJson, { session })
670+
const payload = await userRepo.updateUser(usernameParams, shortNameParams, queryParametersJson, { session }, !req.useRegistry)
655671
await session.commitTransaction()
656672
return res.status(200).json({ message: `${usernameParams} was successfully updated.`, updated: payload })
657673
} catch (err) {
@@ -707,24 +723,29 @@ async function resetSecret (req, res, next) {
707723
const requesterUserUUID = await userRepo.getUserUUID(requesterUsername, requesterOrgShortName, { session }, !req.useRegistry)
708724

709725
const isRequesterSecretariat = await orgRepo.isSecretariatByShortName(requesterOrgShortName, { session })
710-
// const isAdmin = await userRepo.isAdmin(requesterUsername, targetOrgShortName, { session })
711-
if (!isRequesterSecretariat && (requesterOrgShortName !== targetOrgShortName)) {
726+
727+
if (!isRequesterSecretariat) {
728+
// If they are in the same organization, they must be the target user themselves OR an admin of the target org.
729+
730+
// 1. WE are not the same user
712731
if (requesterUserUUID !== targetUserUUID) {
713-
logger.info({ uuid: req.ctx.uuid, message: 'The api secret can only be reset by the Secretariat, an Org admin or if the requester is the user.' })
714-
await session.abortTransaction()
715-
return res.status(403).json(error.notSameUserOrSecretariat())
732+
// Check to see if we are the admin of the target organization
733+
const isAdminOfTargetOrg = await userRepo.isAdmin(requesterUsername, targetOrgShortName, { session })
734+
// The tests say we have to check the org next:
735+
if (requesterOrgShortName !== targetOrgShortName && !isAdminOfTargetOrg) {
736+
logger.info({ uuid: req.ctx.uuid, message: 'The api secret can only be reset by the Secretariat, an Org admin or if the requester is the user.' })
737+
await session.abortTransaction()
738+
return res.status(403).json(error.notSameOrgOrSecretariat())
739+
}
740+
741+
if (!isAdminOfTargetOrg) {
742+
logger.info({ uuid: req.ctx.uuid, message: 'The api secret can only be reset by the Secretariat, an Org admin or if the requester is the user.' })
743+
await session.abortTransaction()
744+
return res.status(403).json(error.notSameUserOrSecretariat())
745+
}
716746
}
717-
logger.info({ uuid: req.ctx.uuid, message: 'The api secret can only be reset by the Secretariat, an Org admin or if the requester is the user.' })
718-
await session.abortTransaction()
719-
return res.status(403).json(error.notSameOrgOrSecretariat())
720-
}
721-
// Check if requester is either admin of target org or secretariat, or is same as target user
722-
const isAdminOrSecretariat = await userRepo.isAdminOrSecretariat(targetOrgShortName, requesterUsername, requesterOrgShortName, { session }, !req.useRegistry)
723-
if (!isAdminOrSecretariat && (requesterUserUUID !== targetUserUUID)) {
724-
logger.info({ uuid: req.ctx.uuid, message: 'The api secret can only be reset by the Secretariat, an Org admin or if the requester is the user.' })
725-
await session.abortTransaction()
726-
return res.status(403).json(error.notSameUserOrSecretariat())
727747
}
748+
728749
const updatedSecret = await userRepo.resetSecret(targetUsername, targetOrgShortName, { session }, !req.useRegistry)
729750

730751
logger.info({ uuid: req.ctx.uuid, message: `The API secret was successfully reset and sent to ${targetUsername}` })

src/middleware/schemas/BulkDownloadOrg.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
{
22
"$schema": "http://json-schema.org/draft-07/schema#",
3-
"$id": "https://cve.org/schemas/org/bulkdownload",
3+
"$id": "BaseOrg",
44
"type": "object",
55
"title": "CVE Bulk Download Organization",
66
"description": "Schema for a CVE Bulk Download Organization",
77
"allOf": [
8-
{ "$ref": "/schemas/org/base" },
8+
{ "$ref": "BaseOrg" },
99
{
1010
"properties": {
1111
"authority": {

src/model/adporg.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,6 @@ ADPSchema.statics.validateOrg = function (record) {
2424
}
2525
return validateObject
2626
}
27-
const CNAOrg = BaseOrg.discriminator('ADPOrg', ADPSchema, options)
27+
const ADPOrg = BaseOrg.discriminator('ADPOrg', ADPSchema, options)
2828

29-
module.exports = CNAOrg
29+
module.exports = ADPOrg

src/model/bulkdownloadorg.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
const mongoose = require('mongoose')
2+
const BaseOrg = require('./baseorg')
3+
const fs = require('fs')
4+
const Ajv = require('ajv')
5+
const addFormats = require('ajv-formats')
6+
const BaseOrgSchema = JSON.parse(fs.readFileSync('src/middleware/schemas/BaseOrg.json'))
7+
const BulkDownloadOrgSchema = JSON.parse(fs.readFileSync('src/middleware/schemas/BulkDownloadOrg.json'))
8+
const ajv = new Ajv({ allErrors: false })
9+
addFormats(ajv)
10+
ajv.addSchema(BaseOrgSchema)
11+
12+
const validate = ajv.compile(BulkDownloadOrgSchema)
13+
14+
const schema = {}
15+
16+
const options = { discriminatorKey: 'kind' }
17+
const BulkDownloadSchema = new mongoose.Schema(schema, options)
18+
BulkDownloadSchema.statics.validateOrg = function (record) {
19+
const validateObject = {}
20+
validateObject.isValid = validate(record)
21+
22+
if (!validateObject.isValid) {
23+
validateObject.errors = validate.errors
24+
}
25+
return validateObject
26+
}
27+
const BulkDownloadOrg = BaseOrg.discriminator('BulkDownloadOrg', BulkDownloadSchema, options)
28+
29+
module.exports = BulkDownloadOrg

src/repositories/baseOrgRepository.js

Lines changed: 14 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const BaseRepository = require('./baseRepository')
22
const BaseOrgModel = require('../model/baseorg')
33
const CNAOrgModel = require('../model/cnaorg')
44
const ADPOrgModel = require('../model/adporg')
5+
const BulkDownloadModel = require('../model/bulkdownloadorg')
56
const SecretariatOrgModel = require('../model/secretariatorg')
67
const CveIdRepository = require('./cveIdRepository')
78
const uuid = require('uuid')
@@ -32,30 +33,6 @@ function setAggregateRegistryOrgObj (query) {
3233
return [
3334
{
3435
$match: query
35-
},
36-
{
37-
$project: {
38-
_id: false,
39-
UUID: true,
40-
long_name: true,
41-
short_name: true,
42-
aliases: true,
43-
authority: true,
44-
reports_to: true,
45-
oversees: true,
46-
root_or_tlr: true,
47-
users: true,
48-
admins: true,
49-
charter_or_scope: true,
50-
disclosure_policy: true,
51-
product_list: true,
52-
soft_quota: true,
53-
hard_quota: true,
54-
contact_info: true,
55-
in_use: true,
56-
created: true,
57-
last_updated: true
58-
}
5936
}
6037
]
6138
}
@@ -113,10 +90,11 @@ class BaseOrgRepository extends BaseRepository {
11390

11491
async getAllOrgs (options = {}, returnLegacyFormat = false) {
11592
const OrgRepository = require('./orgRepository')
93+
const orgRepo = new OrgRepository()
11694
let pg
11795
if (returnLegacyFormat) {
11896
const agt = setAggregateOrgObj({})
119-
pg = await OrgRepository.aggregatePaginate(agt, options)
97+
pg = await orgRepo.aggregatePaginate(agt, options)
12098
} else {
12199
const agt = setAggregateRegistryOrgObj({})
122100
pg = await this.aggregatePaginate(agt, options)
@@ -234,7 +212,7 @@ class BaseOrgRepository extends BaseRepository {
234212
registryObject = await SecretariatObjectToSave.save(options)
235213
} else if (registryObjectRaw.authority.includes('CNA')) {
236214
// A special case, we should make sure we have the default quota if it is not set
237-
if (registryObjectRaw.hard_quota === undefined) {
215+
if (!registryObjectRaw.hard_quota) {
238216
// set to default quota if none is specified
239217
registryObjectRaw.hard_quota = CONSTANTS.DEFAULT_ID_QUOTA
240218
}
@@ -245,6 +223,10 @@ class BaseOrgRepository extends BaseRepository {
245223
registryObjectRaw.hard_quota = 0
246224
const adpObjectToSave = new ADPOrgModel(registryObjectRaw)
247225
registryObject = await adpObjectToSave.save(options)
226+
} else if (registryObjectRaw.authority.includes('BULK_DOWNLOAD')) {
227+
registryObjectRaw.hard_quota = 0
228+
const bulkDownloadObjectToSave = new BulkDownloadModel(registryObjectRaw)
229+
registryObject = await bulkDownloadObjectToSave.save(options)
248230
} else {
249231
// eslint-disable-next-line no-throw-literal
250232
throw 'dave you screwed up'
@@ -255,16 +237,17 @@ class BaseOrgRepository extends BaseRepository {
255237

256238
//* ******* Legacy has some special cases that we have to deal with here.**************
257239
legacyObjectRaw.inUse = false
258-
if (legacyObjectRaw.policies.id_quota === undefined) {
240+
if (!legacyObjectRaw?.policies?.id_quota) {
259241
// set to default quota if none is specified
260-
legacyObjectRaw.policies.id_quota = CONSTANTS.DEFAULT_ID_QUOTA
242+
_.set(legacyObjectRaw, 'policies.id_quota', CONSTANTS.DEFAULT_ID_QUOTA)
261243
}
262244
if (
263-
legacyObjectRaw.authority.active_roles.length === 1 &&
264-
legacyObjectRaw.authority.active_roles[0] === 'ADP'
245+
legacyObjectRaw.authority.active_roles.length === 1 && (
246+
legacyObjectRaw.authority.active_roles[0] === 'ADP' ||
247+
legacyObjectRaw.authority.active_roles[0] === 'BULK_DOWNLOAD')
265248
) {
266249
// ADPs have quota of 0
267-
legacyObjectRaw.policies.id_quota = 0
250+
_.set(legacyObjectRaw, 'policies.id_quota', 0)
268251
}
269252

270253
// The legacy way of doing this, the way this is written under the hood there is no other way

0 commit comments

Comments
 (0)