Skip to content

Commit 8b51873

Browse files
committed
Only return members with project object in these GET /projects and GET /projects/{id} endpoints if user has permission READ_PROJECT_MEMBER.
1 parent 2cd7ea6 commit 8b51873

File tree

4 files changed

+40
-9
lines changed

4 files changed

+40
-9
lines changed

src/routes/projects/get.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import config from 'config';
33
import { middleware as tcMiddleware } from 'tc-core-library-js';
44
import models from '../../models';
55
import util from '../../util';
6+
import { PERMISSION } from '../../permissions/constants';
67

78
const ES_PROJECT_INDEX = config.get('elasticsearchConfig.indexName');
89
const ES_PROJECT_TYPE = config.get('elasticsearchConfig.docType');
@@ -103,7 +104,8 @@ const retrieveProjectFromES = (projectId, req) => {
103104
fields = fields ? fields.split(',') : [];
104105
fields = util.parseFields(fields, {
105106
projects: PROJECT_ATTRIBUTES,
106-
project_members: util.addUserDetailsFieldsIfAllowed(PROJECT_MEMBER_ATTRIBUTES_ES, req),
107+
project_members: util.hasPermissionByReq(PERMISSION.READ_PROJECT_MEMBER, req)
108+
? util.addUserDetailsFieldsIfAllowed(PROJECT_MEMBER_ATTRIBUTES_ES, req) : null,
107109
project_member_invites: PROJECT_MEMBER_INVITE_ATTRIBUTES,
108110
project_phases: PROJECT_PHASE_ATTRIBUTES,
109111
project_phases_products: PROJECT_PHASE_PRODUCTS_ATTRIBUTES,
@@ -143,7 +145,9 @@ const retrieveProjectFromDB = (projectId, req) => {
143145
return Promise.reject(apiErr);
144146
}
145147
// check context for project members
146-
project.members = _.map(req.context.currentProjectMembers, m => _.pick(m, fields.project_members));
148+
if (util.hasPermissionByReq(PERMISSION.READ_PROJECT_MEMBER, req)) {
149+
project.members = _.map(req.context.currentProjectMembers, m => _.pick(m, fields.project_members));
150+
}
147151
// check if attachments field was requested
148152
if (!req.query.fields || _.indexOf(req.query.fields, 'attachments') > -1) {
149153
return util.getProjectAttachments(req, project.id);

src/routes/projects/get.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ describe('GET Project', () => {
268268
should.not.exist(resJson.billingAccountId);
269269
should.exist(resJson.name);
270270
resJson.status.should.be.eql('draft');
271-
resJson.members.should.have.lengthOf(2);
271+
should.not.exist(resJson.members);
272272
done();
273273
}
274274
});

src/routes/projects/list.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,8 @@ const retrieveProjectsFromDB = (req, criteria, sort, ffields) => {
493493
// make sure project.id is part of fields
494494
if (_.indexOf(fields.projects, 'id') < 0) fields.projects.push('id');
495495
const retrieveAttachments = !req.query.fields || req.query.fields.indexOf('attachments') > -1;
496-
const retrieveMembers = !req.query.fields || !!fields.project_members.length;
496+
const retrieveMembers = (!req.query.fields || !!fields.project_members.length)
497+
&& util.hasPermissionByReq(PERMISSION.READ_PROJECT_MEMBER, req);
497498

498499
return models.Project.searchText({
499500
filters: criteria.filters,
@@ -551,7 +552,8 @@ const retrieveProjects = (req, criteria, sort, ffields) => {
551552
// parse the fields string to determine what fields are to be returned
552553
fields = util.parseFields(fields, {
553554
projects: PROJECT_ATTRIBUTES,
554-
project_members: util.addUserDetailsFieldsIfAllowed(PROJECT_MEMBER_ATTRIBUTES_ES, req),
555+
project_members: util.hasPermissionByReq(PERMISSION.READ_PROJECT_MEMBER, req) ?
556+
util.addUserDetailsFieldsIfAllowed(PROJECT_MEMBER_ATTRIBUTES_ES, req) : null,
555557
project_member_invites: PROJECT_MEMBER_INVITE_ATTRIBUTES,
556558
project_phases: PROJECT_PHASE_ATTRIBUTES,
557559
project_phases_products: PROJECT_PHASE_PRODUCTS_ATTRIBUTES,

src/routes/projects/list.spec.js

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,29 @@ describe('LIST Project', () => {
405405
});
406406
});
407407

408+
it('should not include the project members using M2M token without "read:project-members" scope', (done) => {
409+
request(server)
410+
.get('/v5/projects')
411+
.set({
412+
Authorization: `Bearer ${testUtil.m2m['read:projects']}`,
413+
})
414+
.expect('Content-Type', /json/)
415+
.expect(200)
416+
.end((err, res) => {
417+
if (err) {
418+
done(err);
419+
} else {
420+
const resJson = res.body;
421+
should.exist(resJson);
422+
resJson.should.have.lengthOf(3);
423+
resJson.forEach((project) => {
424+
should.not.exist(project.members);
425+
});
426+
done();
427+
}
428+
});
429+
});
430+
408431
it('should return the project when project that is in reviewed state in which the copilot is its member or has been invited', (done) => {
409432
request(server)
410433
.get('/v5/projects')
@@ -1159,7 +1182,8 @@ describe('LIST Project', () => {
11591182
});
11601183

11611184
describe('URL Query fields', () => {
1162-
it('should not return "email" for project members when "fields" query param is not defined (to non-admin users)', (done) => {
1185+
it(`should not include project members when "fields" query param is not defined (to non-admin users)
1186+
without READ_PROJECT_MEMBER permission`, (done) => {
11631187
request(server)
11641188
.get('/v5/projects/')
11651189
.set({
@@ -1174,14 +1198,15 @@ describe('LIST Project', () => {
11741198
const resJson = res.body;
11751199
should.exist(resJson);
11761200
resJson.should.have.lengthOf(1);
1177-
resJson[0].members[0].should.not.have.property('email');
1201+
should.not.exist(resJson[0].members);
11781202
done();
11791203
}
11801204
});
11811205
});
11821206

11831207

1184-
it('should not return "email" for project members even if it\'s listed in "fields" query param (to non-admin users)', (done) => {
1208+
it(`should not include project members even if it's listed in "fields" query param (to non-admin users)
1209+
without READ_PROJECT_MEMBER permission`, (done) => {
11851210
request(server)
11861211
.get('/v5/projects/?fields=members.email,members.id')
11871212
.set({
@@ -1196,7 +1221,7 @@ describe('LIST Project', () => {
11961221
const resJson = res.body;
11971222
should.exist(resJson);
11981223
resJson.should.have.lengthOf(1);
1199-
resJson[0].members[0].should.not.have.property('email');
1224+
should.not.exist(resJson[0].members);
12001225
done();
12011226
}
12021227
});

0 commit comments

Comments
 (0)