Skip to content

Commit 5fcbad4

Browse files
authored
Merge pull request #1711 from CVEProject/dev
Updating Test from Dev
2 parents 0e528de + 20538fc commit 5fcbad4

29 files changed

+2645
-10457
lines changed

api-docs/openapi.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"openapi": "3.0.2",
3-
"info": {
4-
"version": "2.7.0",
3+
"info": {
4+
"version": "2.7.1",
55
"title": "CVE Services API",
66
"description": "The CVE Services API supports automation tooling for the CVE Program. Credentials are required for most service endpoints. Representatives of <a href='https://www.cve.org/ProgramOrganization/CNAs'>CVE Numbering Authorities (CNAs)</a> should use one of the methods below to obtain credentials: <ul><li>If your organization already has an Organizational Administrator (OA) account for the CVE Services, ask your admin for credentials</li> <li>Contact your Root (<a href='https://www.cve.org/PartnerInformation/ListofPartners/partner/Google'>Google</a>, <a href='https://www.cve.org/PartnerInformation/ListofPartners/partner/INCIBE'>INCIBE</a>, <a href='https://www.cve.org/PartnerInformation/ListofPartners/partner/jpcert'>JPCERT/CC</a>, or <a href='https://www.cve.org/PartnerInformation/ListofPartners/partner/redhat'>Red Hat</a>) or Top-Level Root (<a href='https://www.cve.org/PartnerInformation/ListofPartners/partner/icscert'>CISA ICS</a> or <a href='https://www.cve.org/PartnerInformation/ListofPartners/partner/mitre'>MITRE</a>) to request credentials </ul> <p>CVE data is to be in the JSON 5.2 CVE Record format. Details of the JSON 5.2 schema are located <a href='https://github.com/CVEProject/cve-schema/releases/tag/v5.2.0' target='_blank'>here</a>.</p> <a href='https://cveform.mitre.org/' class='link' target='_blank'>Contact the CVE Services team</a>",
77
"contact": {
@@ -2605,7 +2605,7 @@
26052605
"Registry Organization"
26062606
],
26072607
"summary": "Updates information about the organization specified by short name (accessible Temporarily to Secretariat only)",
2608-
"description": " <h2>Access Control</h2> <p>User must belong to an organization with the <b>Secretariat</b> role temporarily.</p> <p>In the future, only the organization's admin will be able to request changes to its information.</p> <p>With Joint Approval required for the following fields:</p> <h2>Expected Behavior</h2> <b>This endpoint expects a full organization object in the request body.</b> <p><b>Secretariat:</b> Updates any organization's information</p> <p><b>Organization Admin:</b> Requests changes to its organization's information</p> <ul> <li>short_name</li> <li>long_name</li> <li>authority</li> <li>aliases</li> <li>oversees</li> <li>root_or_tlr</li> <li>charter_or</li> <li>product_list</li> <li>disclosure_policy</li> <li>contact_info.poc</li> <li>contact_info.poc_email</li> <li>contact_info.poc_phone</li> <li>contact_info.org_email</li> <li>cna_role_type</li> <li>cna_country</li> <li>vulnerability_advisory_locations</li> <li>advisory_location_require_credentials</li> <li>industry</li> <li>tl_root_start_date</li> <li>is_cna_discussion_list</li> </ul>",
2608+
"description": " <h2>Access Control</h2> <p>User must belong to an organization with the <b>Secretariat</b> role temporarily.</p> <p>In the future, only the organization's admin will be able to request changes to its information.</p> <p>With Joint Approval required for the following fields:</p> <h2>Expected Behavior</h2> <b>This endpoint expects a full organization object in the request body.</b> <p><b>Secretariat:</b> Updates any organization's information</p> <p><b>Organization Admin:</b> Requests changes to its organization's information</p> <ul> <li>short_name</li> <li>long_name</li> <li>authority</li> <li>aliases</li> <li>oversees</li> <li>root_or_tlr</li> <li>charter_or_scope</li> <li>product_list</li> <li>disclosure_policy</li> <li>contact_info.poc</li> <li>contact_info.poc_email</li> <li>contact_info.poc_phone</li> <li>contact_info.org_email</li> <li>cna_role_type</li> <li>cna_country</li> <li>vulnerability_advisory_locations</li> <li>advisory_location_require_credentials</li> <li>industry</li> <li>tl_root_start_date</li> <li>is_cna_discussion_list</li> </ul>",
26092609
"operationId": "orgUpdateSingle",
26102610
"parameters": [
26112611
{

package-lock.json

Lines changed: 1795 additions & 10301 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
{
22
"name": "cve-services",
33
"author": "Automation Working Group",
4-
"version": "2.7.0",
4+
"version": "2.7.1",
55
"license": "(CC0)",
66
"devDependencies": {
77
"@faker-js/faker": "^7.6.0",
8-
"apidoc": "^0.53.1",
98
"chai": "^4.2.0",
109
"chai-arrays": "^2.0.0",
1110
"chai-http": "^4.3.0",
@@ -21,7 +20,7 @@
2120
"eslint-plugin-node": "^11.0.0",
2221
"eslint-plugin-promise": "^4.2.1",
2322
"eslint-plugin-standard": "^4.0.1",
24-
"mocha": "^10.1.0",
23+
"mocha": "^10.8.2",
2524
"nyc": "^15.1.0",
2625
"sinon": "^15.0.4",
2726
"standard": "^16.0.3"
@@ -64,13 +63,8 @@
6463
"overrides": {
6564
"mongo-cursor-pagination": {
6665
"bson": "^6.10.1"
67-
}
68-
},
69-
"apidoc": {
70-
"name": "CVE-Services",
71-
"version": "0.0.0",
72-
"description": "Some Future Description",
73-
"title": "CVE API Services"
66+
},
67+
"serialize-javascript": "^7.0.3"
7468
},
7569
"nyc": {
7670
"exclude": "test-utils/"

schemas/conversation/conversation.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,15 @@
4646
"format": "date-time",
4747
"description": "Timestamp when the message was posted"
4848
},
49+
"edited_at": {
50+
"type": "string",
51+
"format": "date-time",
52+
"description": "Timestamp when the message was last edited"
53+
},
54+
"editor_id": {
55+
"type": "string",
56+
"description": "UUID of the user who last edited the message"
57+
},
4958
"last_updated": {
5059
"type": "string",
5160
"format": "date-time",
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"$id": "https://cve.mitre.org/schema/conversation/update-conversation-response.json",
4+
"type": "object",
5+
"title": "Update Conversation Response",
6+
"description": "JSON Schema for the response when updating a conversation",
7+
"properties": {
8+
"message": {
9+
"type": "string",
10+
"description": "Response message"
11+
},
12+
"conversation": {
13+
"$ref": "conversation.json",
14+
"description": "The updated conversation message"
15+
}
16+
}
17+
}

src/constants/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ function getConstants () {
4444
USER_ROLES: [
4545
'ADMIN'
4646
],
47-
JOINT_APPROVAL_FIELDS: ['short_name', 'long_name', 'authority', 'aliases', 'oversees', 'root_or_tlr', 'charter_or', 'product_list', 'disclosure_policy', 'contact_info.poc', 'contact_info.poc_email', 'contact_info.poc_phone', 'contact_info.org_email', 'cna_role_type', 'cna_country', 'vulnerability_advisory_locations', 'advisory_location_require_credentials', 'industry', 'tl_root_start_date', 'is_cna_discussion_list'],
48-
JOINT_APPROVAL_FIELDS_LEGACY: ['short_name', 'name', 'authority.active_roles'],
47+
JOINT_APPROVAL_FIELDS: ['short_name', 'long_name', 'authority', 'aliases', 'oversees', 'root_or_tlr', 'charter_or_scope', 'product_list', 'disclosure_policy', 'contact_info.poc', 'contact_info.poc_email', 'contact_info.poc_phone', 'contact_info.org_email', 'cna_role_type', 'cna_country', 'vulnerability_advisory_locations', 'advisory_location_require_credentials', 'industry', 'tl_root_start_date', 'is_cna_discussion_list', 'hard_quota'],
48+
JOINT_APPROVAL_FIELDS_LEGACY: ['short_name', 'name', 'authority.active_roles', 'policies.id_quota'],
4949
USER_ROLE_ENUM: {
5050
ADMIN: 'ADMIN'
5151
},

src/controller/org.controller/index.js

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,7 @@ router.put('/registry/org/:shortname',
549549
<li>aliases</li>
550550
<li>oversees</li>
551551
<li>root_or_tlr</li>
552-
<li>charter_or</li>
552+
<li>charter_or_scope</li>
553553
<li>product_list</li>
554554
<li>disclosure_policy</li>
555555
<li>contact_info.poc</li>
@@ -1079,6 +1079,81 @@ router.post('/registry/org/:shortname/user/:username/revoke-role',
10791079
registryUserController.REVOKE_ROLE
10801080
)
10811081

1082+
router.put('/registry/org/:shortname/conversation/:index',
1083+
/*
1084+
#swagger.tags = ['Registry Organization']
1085+
#swagger.operationId = 'registryUserUpdateConversation'
1086+
#swagger.summary = "Update the conversation at the given index for the given organization (accessible to Secretariat or Org Admin)"
1087+
#swagger.description = "
1088+
<h2>Access Control</h2>
1089+
<p>User must belong to an organization with the <b>Secretariat</b> role or be an <b>Admin</b> of the organization</p>
1090+
<h2>Expected Behavior</h2>
1091+
<p><b>Admin User:</b> Allowed to update only the message body of any conversation posted by them</p>
1092+
<p><b>Secretariat:</b> Allowed to update the message body and/or visibility of any conversation</p>"
1093+
#swagger.parameters['shortname'] = { description: 'The shortname of the organization' }
1094+
#swagger.parameters['index'] = { description: 'The index of the conversation to update' }
1095+
#swagger.parameters['$ref'] = [
1096+
'#/components/parameters/apiEntityHeader',
1097+
'#/components/parameters/apiUserHeader',
1098+
'#/components/parameters/apiSecretHeader'
1099+
]
1100+
#swagger.responses[200] = {
1101+
description: 'Returns the updated conversation',
1102+
content: {
1103+
"application/json": {
1104+
schema: { $ref: '../schemas/conversation/update-conversation-response.json' }
1105+
}
1106+
}
1107+
}
1108+
#swagger.responses[400] = {
1109+
description: 'Bad Request',
1110+
content: {
1111+
"application/json": {
1112+
schema: { $ref: '../schemas/errors/bad-request.json' }
1113+
}
1114+
}
1115+
}
1116+
#swagger.responses[401] = {
1117+
description: 'Not Authenticated',
1118+
content: {
1119+
"application/json": {
1120+
schema: { $ref: '../schemas/errors/generic.json' }
1121+
}
1122+
}
1123+
}
1124+
#swagger.responses[403] = {
1125+
description: 'Forbidden',
1126+
content: {
1127+
"application/json": {
1128+
schema: { $ref: '../schemas/errors/generic.json' }
1129+
}
1130+
}
1131+
}
1132+
#swagger.responses[404] = {
1133+
description: 'Not Found',
1134+
content: {
1135+
"application/json": {
1136+
schema: { $ref: '../schemas/errors/generic.json' }
1137+
}
1138+
}
1139+
}
1140+
#swagger.responses[500] = {
1141+
description: 'Internal Server Error',
1142+
content: {
1143+
"application/json": {
1144+
schema: { $ref: '../schemas/errors/generic.json' }
1145+
}
1146+
}
1147+
}
1148+
*/
1149+
mw.useRegistry(),
1150+
mw.validateUser,
1151+
mw.onlyOrgWithPartnerRole,
1152+
parseError,
1153+
parsePostParams,
1154+
registryOrgController.EDIT_CONVERSATION
1155+
)
1156+
10821157
router.get('/org',
10831158
/*
10841159
#swagger.tags = ['Organization']

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

Lines changed: 15 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -199,36 +199,27 @@ async function getUser (req, res, next) {
199199
*/
200200
async function getOrgIdQuota (req, res, next) {
201201
try {
202-
const session = await mongoose.startSession()
203202
const orgRepo = req.ctx.repositories.getBaseOrgRepository()
204203
const requesterOrgShortName = req.ctx.org
205204
const shortName = req.ctx.params.shortname
206205

207-
try {
208-
session.startTransaction()
209-
const requesterOrg = await orgRepo.findOneByShortName(requesterOrgShortName, { session })
210-
const isSecretariat = await orgRepo.isSecretariat(requesterOrg, !req.useRegistry)
211-
212-
if (requesterOrgShortName !== shortName && !isSecretariat) {
213-
logger.info({ uuid: req.ctx.uuid, message: shortName + ' organization id quota can only be viewed by the users of the same organization or the Secretariat.' })
214-
return res.status(403).json(error.notSameOrgOrSecretariat())
215-
}
206+
const requesterOrg = await orgRepo.findOneByShortName(requesterOrgShortName)
207+
const isSecretariat = await orgRepo.isSecretariat(requesterOrg, !req.useRegistry)
216208

217-
const org = await orgRepo.getOrg(shortName, false, { session }, !req.useRegistry)
218-
if (!org) { // a null org can only happen if the requestor is the Secretariat
219-
logger.info({ uuid: req.ctx.uuid, message: shortName + ' organization does not exist.' })
220-
return res.status(404).json(error.orgDnePathParam(shortName))
221-
}
209+
if (requesterOrgShortName !== shortName && !isSecretariat) {
210+
logger.info({ uuid: req.ctx.uuid, message: shortName + ' organization id quota can only be viewed by the users of the same organization or the Secretariat.' })
211+
return res.status(403).json(error.notSameOrgOrSecretariat())
212+
}
222213

223-
const returnPayload = await orgRepo.getOrgIdQuota(org, !req.useRegistry)
224-
logger.info({ uuid: req.ctx.uuid, message: 'The organization\'s id quota was returned to the user.', details: returnPayload })
225-
return res.status(200).json(returnPayload)
226-
} catch (error) {
227-
await session.abortTransaction()
228-
throw error
229-
} finally {
230-
await session.endSession()
214+
const org = await orgRepo.getOrg(shortName, false, {}, !req.useRegistry)
215+
if (!org) { // a null org can only happen if the requestor is the Secretariat
216+
logger.info({ uuid: req.ctx.uuid, message: shortName + ' organization does not exist.' })
217+
return res.status(404).json(error.orgDnePathParam(shortName))
231218
}
219+
220+
const returnPayload = await orgRepo.getOrgIdQuota(org, !req.useRegistry)
221+
logger.info({ uuid: req.ctx.uuid, message: 'The organization\'s id quota was returned to the user.', details: returnPayload })
222+
return res.status(200).json(returnPayload)
232223
} catch (err) {
233224
next(err)
234225
}
@@ -301,8 +292,6 @@ async function createOrg (req, res, next) {
301292
org: returnValue
302293
}
303294

304-
// const userRepo = req.ctx.repositories.getUserRepository()
305-
// payload.user_UUID = await userRepo.getUserUUID(req.ctx.user, payload.org_UUID)
306295
logger.info(JSON.stringify(payload))
307296
return res.status(200).json(responseMessage)
308297
} catch (err) {
@@ -664,7 +653,7 @@ async function updateUser (req, res, next) {
664653
*/
665654
async function resetSecret (req, res, next) {
666655
try {
667-
const session = await mongoose.startSession()
656+
const session = await mongoose.startSession({ causalConsistency: false })
668657
const requesterOrgShortName = req.ctx.org
669658
const requesterUsername = req.ctx.user
670659
const targetOrgShortName = req.ctx.params.shortname

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,6 @@ const QUERY_PARAMETERS = {
333333
}
334334

335335
function parsePutParams (req, res, next) {
336-
console.log('DEBUG: parsePutParams req.body:', JSON.stringify(req.body))
337336
req.body = utils.deepRemoveEmpty(req.body)
338337
utils.reqCtxMapping(req, 'body', [])
339338
// Extract all possible query parameters

src/controller/registry-org.controller/error.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,34 @@ class RegistryOrgControllerError extends idrErr.IDRError {
9191
err.message = 'The requested user can not be created and added to the organization because the organization has hit its limit of 100 users. Contact the Secretariat.'
9292
return err
9393
}
94+
95+
conversationDne (shortname, index) {
96+
const err = {}
97+
err.error = 'CONVERSATION_DNE'
98+
err.message = `The conversation at index ${index} does not exist for the ${shortname} organization.`
99+
return err
100+
}
101+
102+
notAllowedToEditConversation () {
103+
const err = {}
104+
err.error = 'NOT_ALLOWED_TO_EDIT_CONVERSATION'
105+
err.message = 'You must be the original author or Secretariat to edit this conversation.'
106+
return err
107+
}
108+
109+
notAllowedToChangeConversationVisibility () {
110+
const err = {}
111+
err.error = 'NOT_ALLOWED_TO_CHANGE_CONVERSATION_VISIBILITY'
112+
err.message = 'Only the Secretariat is allowed to change the visibility of a conversation.'
113+
return err
114+
}
115+
116+
invalidConversationObject () {
117+
const err = {}
118+
err.error = 'BAD_INPUT'
119+
err.message = 'Parameters were invalid: conversation must be an object with a body.'
120+
return err
121+
}
94122
}
95123

96124
module.exports = {

0 commit comments

Comments
 (0)