Repository: CVEProject/cve-services
Pull request #251 (2021-01-13 18:07:19)

Opening the GET single user endpoint for users under a same organization
This change allows the secretariat and users under a same organization to access non sensitive information about a user in that organization.



Patch:
diff --git a/src/controller/org.controller/index.js b/src/controller/org.controller/index.js
index 3ec84421..6e7fa8b3 100644
--- a/src/controller/org.controller/index.js
+++ b/src/controller/org.controller/index.js
@@ -57,7 +57,6 @@ router.post('/org/:shortname/user',
   parsePostParams,
   controller.USER_CREATE_SINGLE)
 router.get('/org/:shortname/user/:username',
-  mw.onlySecretariat,
   mw.validateUser,
   param(['shortname']).isString().trim().escape().notEmpty(),
   param(['username']).isString().trim().escape().notEmpty(),
diff --git a/src/controller/org.controller/org.controller.js b/src/controller/org.controller/org.controller.js
index 994f06d9..0208d8a4 100644
--- a/src/controller/org.controller/org.controller.js
+++ b/src/controller/org.controller/org.controller.js
@@ -72,12 +72,21 @@ async function getCna (req, res, next) {
 // Get the details of a single user for the specified username
 async function getUser (req, res, next) {
   try {
+    const shortName = req.ctx.org
     const username = req.ctx.params.username
     const cnaShortName = req.ctx.params.shortname
     let returned = false
     const orgRepo = req.ctx.repositories.getOrgRepository()
+    const isSecretariat = await orgRepo.isSecretariat(shortName)
+
+    if (cnaShortName !== shortName && !isSecretariat) {
+      returned = true
+      logger.info({ message: shortName + ' organization can only be viewed by that organization\'s users or the Secretariat.' })
+      return res.status(403).json(error.notOwnerOrSecretariat())
+    }
+
     const orgUUID = await orgRepo.getOrgUUID(cnaShortName)
-    if (!orgUUID) {
+    if (!orgUUID && !returned) {
       returned = true
       logger.info(cnaShortName + ' CNA does not exist.')
       return res.status(404).json(error.cnaDneParam(cnaShortName))


Test patch:
diff --git a/test-utils/repositories.js b/test-utils/repositories.js
index fcf5d444..977478ce 100644
--- a/test-utils/repositories.js
+++ b/test-utils/repositories.js
@@ -307,6 +307,10 @@ class OrgGetUserCveIdUpdated {
     cveIdMockObj.owningOrgUser.org_UUID = cveIdMockObj.org.UUID
   }
 
+  async isSecretariat (shortname) {
+    return true
+  }
+
   async getOrgUUID (shortname) {
     expect(shortname).to.be.a('string').and.to.equal(cveIdMockObj.org.short_name)
     return cveIdMockObj.org.UUID
@@ -334,15 +338,30 @@ class OrgUserSecretReset {
 }
 
 class OrgGetUserOrgDoesntExist {
+  async isSecretariat (shortname) {
+    return true
+  }
+
   async getOrgUUID (shortname) {
     return null
   }
 }
 
 class OrgGetUser {
+  async isSecretariat (shortname) {
+    return shortname === orgMockObj.existentOrg.short_name
+  }
+
   async getOrgUUID (shortname) {
-    expect(shortname).to.be.a('string').and.to.equal(orgMockObj.existentOrg.short_name)
-    return orgMockObj.existentOrg.UUID
+    expect(shortname).to.be.a('string')
+
+    if (shortname === orgMockObj.existentOrg.short_name) {
+      return orgMockObj.existentOrg.UUID
+    } else if (shortname === orgMockObj.owningOrg.short_name) {
+      return orgMockObj.owningOrg.UUID
+    }
+
+    return null
   }
 }
 
@@ -1692,7 +1711,16 @@ class UserSecretReset {
 class UserGetUser {
   async aggregate (aggregation) {
     expect(aggregation[0]).to.have.property('$match')
-    return [orgMockObj.existentUser]
+
+    if (aggregation[0].$match.username === orgMockObj.existentUser.username &&
+      aggregation[0].$match.org_UUID === orgMockObj.existentUser.org_UUID) {
+      return [orgMockObj.existentUser]
+    } else if (aggregation[0].$match.username === orgMockObj.existentUserDummy.username &&
+      aggregation[0].$match.org_UUID === orgMockObj.existentUserDummy.org_UUID) {
+      return [orgMockObj.existentUserDummy]
+    }
+
+    return []
   }
 }
 
diff --git a/test/unit-tests/cve-id/cveIdTest.js b/test/unit-tests/cve-id/cveIdTest.js
index 1156e5f4..c4a77db0 100644
--- a/test/unit-tests/cve-id/cveIdTest.js
+++ b/test/unit-tests/cve-id/cveIdTest.js
@@ -259,11 +259,12 @@ describe('Test ID Reservator Endpoints', () => {
 
           chai.request(server)
             .get(`/cve-id-filtered-update-user-org-request-cve-id-2/${org.short_name}/${owningOrgUser.username}`)
-            .set(owningOrgHeader) // the owning cna
+            .set(secretariatHeader) // the owning cna
             .end((err, res) => {
               if (err) {
                 done(err)
               }
+
               // assert expected response
               expect(res).to.have.status(200)
               expect(res).to.have.property('body').and.to.be.a('object')
diff --git a/test/unit-tests/org/userTest.js b/test/unit-tests/org/userTest.js
index abef3f82..e0a2f038 100644
--- a/test/unit-tests/org/userTest.js
+++ b/test/unit-tests/org/userTest.js
@@ -2,26 +2,19 @@ const server = require('../../../test-utils/index')
 const chai = require('chai')
 const expect = chai.expect
 chai.use(require('chai-http'))
-const mongoose = require('mongoose')
 const secretariatHeader = require('./mockObjects.org').secretariatHeader
+const owningOrgHeader = require('./mockObjects.org').owningOrgHeader
+const orgHeader = require('./mockObjects.org').orgHeader
 const existentUser = require('./mockObjects.org').existentUser
 const nonExistentUser = require('./mockObjects.org').nonExistentUser
 const existentOrg = require('./mockObjects.org').existentOrg
 const owningOrg = require('./mockObjects.org').owningOrg
 const existentUserDummy = require('./mockObjects.org').existentUserDummy
-const existentOrgDummy2 = require('./mockObjects.org').existentOrgDummy2
 const nonExistentOrg = require('./mockObjects.org').nonExistentOrg
 const errors = require('../../../src/controller/org.controller/error')
 const error = new errors.OrgControllerError()
-const ReposFactory = require('../../../src/repositories/repositoryFactory')
-const repositories = new ReposFactory()
-const userRepo = repositories.getUserRepository()
-const orgRepo = repositories.getOrgRepository()
 
 describe('Test user functions in Org Controller', () => {
-  before(async () => {
-  })
-
   context('Creating a user', () => {
     it('User is not created because org does not exist', (done) => {
       // perform the request to the api
@@ -390,6 +383,7 @@ describe('Test user functions in Org Controller', () => {
           if (err) {
             done(err)
           }
+
           // assert expected response
           expect(res).to.have.status(404)
           expect(res).to.have.property('body').and.to.be.a('object')
@@ -400,7 +394,7 @@ describe('Test user functions in Org Controller', () => {
         })
     })
 
-    it('User exists', (done) => {
+    it('User exists and the requester is the secretariat', (done) => {
       // perform the request to the api
       chai.request(server)
         .get(`/user-get-user/${existentOrg.short_name}/${existentUser.username}`)
@@ -417,11 +411,44 @@ describe('Test user functions in Org Controller', () => {
           done()
         })
     })
-  })
 
-  after(async () => {
-    // The db connection must be closed after all tests are run. The userTest.js is the last test file to run because it is
-    // the last file in the unit test folder (tests are run sequentially). Therefore, this test closes the db connection.
-    mongoose.connection.close()
+    it('User exists and the requester belongs to the user\'s org', (done) => {
+      // perform the request to the api
+      chai.request(server)
+        .get(`/user-get-user/${owningOrg.short_name}/${existentUserDummy.username}`)
+        .set(owningOrgHeader)
+        .end((err, res) => {
+          if (err) {
+            done(err)
+          }
+
+          // assert expected response
+          expect(res).to.have.status(200)
+          expect(res).to.have.property('body').and.to.be.a('object')
+          expect(res.body).to.have.property('username').and.to.equal(existentUserDummy.username)
+          expect(res.body).to.have.property('org_UUID').and.to.equal(existentUserDummy.org_UUID)
+          done()
+        })
+    })
+
+    it('User exists and the requester does not belong to the user\'s org', (done) => {
+      // perform the request to the api
+      chai.request(server)
+        .get(`/user-get-user/${owningOrg.short_name}/${existentUserDummy.username}`)
+        .set(orgHeader)
+        .end((err, res) => {
+          if (err) {
+            done(err)
+          }
+
+          // assert expected response
+          expect(res).to.have.status(403)
+          expect(res).to.have.property('body').and.to.be.a('object')
+          const errObj = error.notOwnerOrSecretariat()
+          expect(res.body.error).to.equal(errObj.error)
+          expect(res.body.message).to.equal(errObj.message)
+          done()
+        })
+    })
   })
 })