Skip to content

Commit 89a8ad4

Browse files
authored
Merge pull request #1441 from CVEProject/emathew/1400-registryOrg-createUserByOrg
Resolves issue 1400, Add createUserByOrg registryOrg/ endpoint
2 parents 2488b5f + 15de2d4 commit 89a8ad4

4 files changed

Lines changed: 254 additions & 6 deletions

File tree

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

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -605,9 +605,30 @@ router.post('/registryOrg/:shortname/user',
605605
// // mw.onlyOrgWithPartnerRole,
606606
mw.onlySecretariat,
607607
param(['shortname']).isString().trim().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }),
608-
body(['cve_program_org_membership'])
608+
// body(['cve_program_org_membership']).optional().custom(isFlatStringArray), // TO-DO: implement custom(mw.isCveProgramOrgMembershipObject),
609+
body(
610+
[
611+
'user_id',
612+
'name.first',
613+
'name.last'
614+
])
615+
.isString(),
616+
body(
617+
[
618+
'name.middle',
619+
'name.suffix',
620+
'contact_info.phone',
621+
'contact_info.email' // // TO-DO: this needs to be required only when contact-info is present?
622+
])
623+
.default('')
624+
.isString(),
625+
body(['org_affiliations']) // TO-DO
626+
.optional(),
627+
body(['authority.active_roles'])
609628
.optional()
610-
.custom(mw.isCveProgramOrgMembershipObject),
629+
.custom(isFlatStringArray)
630+
.customSanitizer(toUpperCaseArray)
631+
.custom(isOrgRole), // TO-DO: this needs to be required only when authority is present?
611632
parseError,
612633
parsePostParams,
613634
controller.USER_CREATE_SINGLE)

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

Lines changed: 127 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
const argon2 = require('argon2')
12
const uuid = require('uuid')
23
const logger = require('../../middleware/logger')
34
const { getConstants } = require('../../constants')
45
const RegistryOrg = require('../../model/registry-org')
56
const RegistryUser = require('../../model/registry-user')
67
const errors = require('./error')
8+
const cryptoRandomString = require('crypto-random-string')
79
const error = new errors.RegistryOrgControllerError()
810
const validateUUID = require('uuid').validate
911

@@ -202,7 +204,6 @@ async function updateOrg (req, res, next) {
202204
const shortName = req.ctx.params.shortname
203205
const userRepo = req.ctx.repositories.getRegistryUserRepository()
204206
const registryOrgRepo = req.ctx.repositories.getRegistryOrgRepository()
205-
// const shortName = req.ctx.params.shortname
206207

207208
const org = await registryOrgRepo.findOneByShortName(shortName)
208209
if (!org) {
@@ -380,8 +381,131 @@ async function getUsers (req, res, next) {
380381
}
381382
}
382383

383-
function createUserByOrg (req, res, next) {
384-
console.log('HERE')
384+
async function createUserByOrg (req, res, next) {
385+
try {
386+
const requesterUsername = req.ctx.user
387+
const requesterShortName = req.ctx.org
388+
const shortName = req.ctx.params.shortname
389+
390+
const registryUserRepo = req.ctx.repositories.getRegistryUserRepository()
391+
const registryOrgRepo = req.ctx.repositories.getRegistryOrgRepository()
392+
const orgUUID = await registryOrgRepo.getOrgUUID(shortName)
393+
const requesterOrgUUID = await registryOrgRepo.getOrgUUID(requesterShortName)
394+
const body = req.ctx.body
395+
396+
const isSecretariat = await registryOrgRepo.isSecretariat(requesterShortName)
397+
const isAdmin = await registryUserRepo.isAdmin(requesterUsername, requesterShortName)
398+
399+
if (!isSecretariat && !isAdmin) { // may be redundant after validation check is implemented
400+
return res.status(403).json(error.notOrgAdminOrSecretariat()) // User must be secretariat or an admin
401+
}
402+
if (!orgUUID) {
403+
return res.status(404).json(error.orgDnePathParam(shortName)) // Org must exist
404+
}
405+
if (!isSecretariat) { // Admins can only create user within the same org
406+
if (orgUUID !== requesterOrgUUID) {
407+
return res.status(403).json(error.notOrgAdminOrSecretariat()) // The Admin user must belong to the new user's organization
408+
}
409+
}
410+
411+
const username = body.user_id || body.username
412+
if (!username) {
413+
return res.status(400).json({ message: 'user_id is required' })
414+
}
415+
const existingUser = await registryUserRepo.findOneByUserNameAndOrgUUID(username, orgUUID)
416+
if (existingUser) {
417+
return res.status(400).json(error.userExists(username))
418+
}
419+
420+
const bodyKeys = Object.keys(body).map(k => k.toLowerCase())
421+
if (bodyKeys.includes('uuid')) {
422+
return res.status(400).json(error.uuidProvided('user'))
423+
}
424+
425+
// Creating a new user under specific org
426+
const newUser = new RegistryUser()
427+
bodyKeys.forEach(k => {
428+
if (k === 'user_id' || k === 'username') {
429+
newUser.user_id = body[k]
430+
} else if (k === 'name') {
431+
newUser.name = {
432+
first: '',
433+
last: '',
434+
middle: '',
435+
suffix: '',
436+
...body.name
437+
}
438+
} else if (k === 'org_affiliations') {
439+
newUser.org_affiliations = body[k].map(item => {
440+
const {
441+
orgId = '',
442+
email = '',
443+
phone = '',
444+
...rest
445+
} = item
446+
447+
return {
448+
org_id: orgId,
449+
email,
450+
phone,
451+
...rest
452+
}
453+
})
454+
} else if (k === 'cve_program_org_membership') {
455+
newUser.cve_program_org_membership = body[k].map(item => {
456+
const {
457+
programOrg = '',
458+
roles = [],
459+
460+
status = false,
461+
...rest
462+
} = item
463+
464+
return {
465+
program_org: programOrg,
466+
roles,
467+
status,
468+
...rest
469+
}
470+
})
471+
}
472+
})
473+
474+
newUser.UUID = uuid.v4()
475+
476+
const randomKey = cryptoRandomString({ length: getConstants().CRYPTO_RANDOM_STRING_LENGTH })
477+
newUser.secret = await argon2.hash(randomKey)
478+
newUser.last_active = null
479+
newUser.deactivation_date = null
480+
481+
await registryUserRepo.updateByUserNameAndOrgUUID(newUser.user_id, orgUUID, newUser, { upsert: true })
482+
await registryUserRepo.addOrgToUserAffiliation(newUser.UUID, orgUUID)
483+
await registryOrgRepo.addUserToOrgList(orgUUID, newUser.UUID, body.authority?.active_roles ? [...new Set(body.authority.active_roles)].includes('ADMIN') : false, { upsert: true })
484+
485+
const agt = setAggregateUserObj({ UUID: newUser.UUID })
486+
let result = await registryUserRepo.aggregate(agt)
487+
result = result.length > 0 ? result[0] : null
488+
489+
const payload = {
490+
action: 'create_registry_user',
491+
change: result.user_id + ' was successfully created.',
492+
req_UUID: req.ctx.uuid,
493+
org_UUID: orgUUID,
494+
user: result
495+
}
496+
payload.user_UUID = await registryUserRepo.getUserUUID(req.ctx.user, orgUUID)
497+
logger.info(JSON.stringify(payload))
498+
499+
result.secret = randomKey
500+
const responseMessage = {
501+
message: result.user_id + ' was successfully created.',
502+
created: result
503+
}
504+
505+
return res.status(200).json(responseMessage)
506+
} catch (err) {
507+
next(err)
508+
}
385509
}
386510

387511
function setAggregateUserObj (query) {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const error = new errors.RegistryOrgControllerError()
66

77
function parsePostParams (req, res, next) {
88
utils.reqCtxMapping(req, 'body', [])
9-
utils.reqCtxMapping(req, 'params', ['identifier, shortname'])
9+
utils.reqCtxMapping(req, 'params', ['identifier', 'shortname'])
1010
utils.reqCtxMapping(req, 'query', [
1111
'long_name', 'short_name', 'aliases',
1212
'cve_program_org_function', 'authority.active_roles',
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/* eslint-disable no-unused-expressions */
2+
const chai = require('chai')
3+
chai.use(require('chai-http'))
4+
const expect = chai.expect
5+
6+
const constants = require('../constants.js')
7+
const app = require('../../../src/index.js')
8+
9+
describe('Testing user can be created by org with /registryOrg endpoint', () => {
10+
context('Positive Tests', () => {
11+
it('Allows creation of user by org', async () => {
12+
const payload = {
13+
user_id: 'testUser',
14+
name: {
15+
first: 'TestFirst',
16+
last: 'FakeLastName',
17+
middle: 'Cool',
18+
suffix: 'Mr.'
19+
},
20+
authority: {
21+
active_roles: ['CNA']
22+
}
23+
}
24+
25+
// Create a new user by org
26+
const res = await chai
27+
.request(app)
28+
.post('/api/registryOrg/win_5/user')
29+
.set(constants.headers)
30+
.send(payload)
31+
32+
expect(res).to.have.status(200)
33+
expect(res.body).to.have.property('message', `${payload.user_id} was successfully created.`)
34+
expect(res.body).to.have.property('created').that.is.an('object')
35+
36+
const created = res.body.created
37+
expect(created).to.include({
38+
user_id: payload.user_id
39+
})
40+
expect(created.name).to.deep.include({
41+
first: payload.name.first,
42+
last: payload.name.last,
43+
middle: payload.name.middle,
44+
suffix: payload.name.suffix
45+
})
46+
47+
expect(created).to.have.property('UUID').that.is.a('string')
48+
expect(created).to.have.property('last_active', null)
49+
expect(created).to.have.property('deactivation_date', null)
50+
expect(created).to.have.property('org_affiliations')
51+
expect(created.org_affiliations[0]).to.have.nested.property('org_id')
52+
})
53+
})
54+
context('Negative Tests', () => {
55+
it('Fails creation of user for nonSecretariat role', async () => {
56+
const payload = {
57+
user_id: 'fakeregistryuser999',
58+
name: {
59+
first: 'TestFirstName',
60+
last: 'FakeLastName',
61+
middle: 'Cool',
62+
suffix: 'Mr.'
63+
},
64+
authority: {
65+
active_roles: ['CNA']
66+
}
67+
}
68+
await chai
69+
.request(app)
70+
.post('/api/registryOrg/win_5/user')
71+
.set(constants.nonSecretariatUserHeaders)
72+
.send(payload)
73+
.then((res, err) => {
74+
expect(res).to.have.status(403)
75+
expect(res.body.error).to.equal('SECRETARIAT_ONLY')
76+
// expect(res.body.error).to.equal('NOT_ORG_ADMIN_OR_SECRETARIAT') // Will be this error when validation is fixed
77+
})
78+
})
79+
it('Fails creation of user if they exist', async () => {
80+
const payload = {
81+
user_id: 'fakeregistryuser999',
82+
name: {
83+
first: 'TestFirstName',
84+
last: 'FakeLastName',
85+
middle: 'Cool',
86+
suffix: 'Mr.'
87+
},
88+
authority: {
89+
active_roles: ['CNA']
90+
}
91+
}
92+
await chai
93+
.request(app)
94+
.post('/api/registryOrg/win_5/user')
95+
.set(constants.headers)
96+
.send(payload)
97+
.then((res, err) => {
98+
expect(res).to.have.status(400)
99+
expect(res.body.error).to.equal('USER_EXISTS')
100+
})
101+
})
102+
})
103+
})

0 commit comments

Comments
 (0)