Skip to content

Commit e770e25

Browse files
committed
- change maskInviteEmails to postProcessInvites which will be post-processing on invite(s) with following constraints:
1. email field will be omitted from invite if the invite has defined userId 2. email field (if existed) will be masked UNLESS current user has admin permissions OR current user created this invite - also apply this post-processing when creating invite.
1 parent 73a7773 commit e770e25

File tree

13 files changed

+235
-35
lines changed

13 files changed

+235
-35
lines changed

src/routes/projectMemberInvites/create.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -395,9 +395,9 @@ module.exports = [
395395
})
396396
))
397397
.then((values) => {
398-
const response = _.assign({}, { success: values });
398+
const response = _.assign({}, { success: util.postProcessInvites('$[*]', values, req) });
399399
if (failed.length) {
400-
res.status(403).json(_.assign({}, response, { failed }));
400+
res.status(403).json(_.assign({}, response, { failed: util.postProcessInvites('$[*]', failed, req) }));
401401
} else {
402402
res.status(201).json(response);
403403
}

src/routes/projectMemberInvites/create.spec.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ describe('Project Member Invite create', () => {
393393
should.exist(resJson);
394394
resJson.role.should.equal('customer');
395395
resJson.projectId.should.equal(project2.id);
396-
resJson.email.should.equal('hello@world.com');
396+
resJson.email.should.equal('h***o@w***d.com');
397397
resJson.hashEmail.should.equal(md5('hello@world.com'));
398398
server.services.pubsub.publish.calledWith('project.member.invite.created').should.be.true;
399399
done();
@@ -446,8 +446,8 @@ describe('Project Member Invite create', () => {
446446
resJson.role.should.equal('customer');
447447
resJson.projectId.should.equal(project2.id);
448448
resJson.userId.should.equal(12345);
449-
resJson.email.should.equal('hello@world.com');
450-
resJson.hashEmail.should.equal(md5('hello@world.com'));
449+
should.not.exist(resJson.email);
450+
should.not.exist(resJson.hashEmail);
451451
server.services.pubsub.publish.calledWith('project.member.invite.created').should.be.true;
452452
done();
453453
}
@@ -563,7 +563,7 @@ describe('Project Member Invite create', () => {
563563
} else {
564564
const resJson = res.body.failed;
565565
should.exist(resJson);
566-
resJson[0].email.should.equal('romit.choudhary@rivigo.com');
566+
resJson[0].email.should.equal('r***y@r***o.com');
567567
resJson[0].message.should.equal('User with such email is already a member of the team.');
568568
resJson.length.should.equal(1);
569569
server.services.pubsub.publish.neverCalledWith('project.member.invite.created').should.be.true;
@@ -802,7 +802,7 @@ describe('Project Member Invite create', () => {
802802
} else {
803803
const resJson = res.body.failed;
804804
should.exist(resJson);
805-
resJson[0].email.should.equal('duplicate_lowercase@test.com');
805+
resJson[0].email.should.equal('d***e@t***t.com');
806806
resJson[0].message.should.equal('User with such email is already invited to this project.');
807807
resJson.length.should.equal(1);
808808
done();
@@ -828,7 +828,7 @@ describe('Project Member Invite create', () => {
828828
} else {
829829
const resJson = res.body.failed;
830830
should.exist(resJson);
831-
resJson[0].email.should.equal('DUPLICATE_UPPERCASE@test.com'); // email is masked
831+
resJson[0].email.should.equal('D***E@t***t.com'); // email is masked
832832
resJson[0].message.should.equal('User with such email is already invited to this project.');
833833
resJson.length.should.equal(1);
834834
done();

src/routes/projectMemberInvites/get.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ module.exports = [
114114
return invite;
115115
})
116116
))
117-
.then(invite => res.json(util.maskInviteEmails('$[*].email', invite, req)))
117+
.then(invite => res.json(util.postProcessInvites('$.email', invite, req)))
118118
.catch(next);
119119
},
120120
];

src/routes/projectMemberInvites/get.spec.js

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ describe('GET Project Member Invite', () => {
5555
const invite2 = models.ProjectMemberInvite.create({
5656
id: 2,
5757
userId: testUtil.userIds.copilot,
58-
email: null,
58+
email: 'test@topcoder.com',
5959
projectId: project1.id,
6060
role: 'copilot',
6161
createdBy: 1,
@@ -80,6 +80,15 @@ describe('GET Project Member Invite', () => {
8080
}).then((p) => {
8181
project2 = p;
8282

83+
// create members
84+
const pm2 = models.ProjectMember.create({
85+
userId: testUtil.userIds.romit,
86+
projectId: project2.id,
87+
role: 'copilot',
88+
isPrimary: true,
89+
createdBy: 1,
90+
updatedBy: 1,
91+
});
8392
// create invite 3
8493
const invite3 = models.ProjectMemberInvite.create({
8594
id: 3,
@@ -104,7 +113,7 @@ describe('GET Project Member Invite', () => {
104113
status: INVITE_STATUS.ACCEPTED,
105114
});
106115

107-
return Promise.all([invite3, invite4]);
116+
return Promise.all([pm2, invite3, invite4]);
108117
});
109118
return Promise.all([p1, p2])
110119
.then(() => done());
@@ -206,6 +215,7 @@ describe('GET Project Member Invite', () => {
206215
const resJson = res.body;
207216
should.exist(resJson);
208217
should.exist(resJson.projectId);
218+
should.not.exist(resJson.email);
209219
resJson.id.should.be.eql(2);
210220
resJson.userId.should.be.eql(testUtil.userIds.copilot);
211221
resJson.status.should.be.eql(INVITE_STATUS.PENDING);
@@ -237,5 +247,28 @@ describe('GET Project Member Invite', () => {
237247
}
238248
});
239249
});
250+
251+
it('should return the invite with masked email if user get not his/her own invitation by email', (done) => {
252+
request(server)
253+
.get(`/v5/projects/${project2.id}/invites/3`)
254+
.set({
255+
Authorization: `Bearer ${testUtil.jwts.romit}`,
256+
})
257+
.expect('Content-Type', /json/)
258+
.expect(200)
259+
.end((err, res) => {
260+
if (err) {
261+
done(err);
262+
} else {
263+
const resJson = res.body;
264+
should.exist(resJson);
265+
should.exist(resJson.projectId);
266+
resJson.id.should.be.eql(3);
267+
resJson.email.should.be.eql('t***t@t***r.com');
268+
resJson.status.should.be.eql(INVITE_STATUS.PENDING);
269+
done();
270+
}
271+
});
272+
});
240273
});
241274
});

src/routes/projectMemberInvites/list.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ module.exports = [
103103
return invites;
104104
})
105105
))
106-
.then(invites => res.json(util.maskInviteEmails('$[*].email', invites, req)))
106+
.then(invites => res.json(util.postProcessInvites('$[*]', invites, req)))
107107
.catch(next);
108108
},
109109
];

src/routes/projectMemberInvites/list.spec.js

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,16 @@ describe('GET Project Member Invites', () => {
8181
}).then((p) => {
8282
project2 = p;
8383

84+
// create members
85+
const pm2 = models.ProjectMember.create({
86+
userId: testUtil.userIds.romit,
87+
projectId: project2.id,
88+
role: 'copilot',
89+
isPrimary: true,
90+
createdBy: 1,
91+
updatedBy: 1,
92+
});
93+
8494
// create invite 3
8595
const invite3 = models.ProjectMemberInvite.create({
8696
id: 3,
@@ -105,7 +115,7 @@ describe('GET Project Member Invites', () => {
105115
status: INVITE_STATUS.ACCEPTED,
106116
});
107117

108-
return Promise.all([invite3, invite4]);
118+
return Promise.all([pm2, invite3, invite4]);
109119
});
110120
return Promise.all([p1, p2])
111121
.then(() => done());
@@ -209,6 +219,7 @@ describe('GET Project Member Invites', () => {
209219
resJson.length.should.be.eql(1);
210220
// check invitations
211221
_.filter(resJson, inv => inv.id === 2).length.should.be.eql(1);
222+
should.not.exist(resJson[0].email);
212223
done();
213224
}
214225
});
@@ -253,6 +264,31 @@ describe('GET Project Member Invites', () => {
253264
resJson.length.should.be.eql(1);
254265
// check invitations
255266
_.filter(resJson, inv => inv.id === 3).length.should.be.eql(1);
267+
resJson[0].email.should.be.eql('test@topcoder.com');
268+
done();
269+
}
270+
});
271+
});
272+
273+
it('should return the invite with masked email if user get not his/her own invitation by email', (done) => {
274+
request(server)
275+
.get(`/v5/projects/${project2.id}/invites`)
276+
.set({
277+
Authorization: `Bearer ${testUtil.jwts.romit}`,
278+
})
279+
.expect('Content-Type', /json/)
280+
.expect(200)
281+
.end((err, res) => {
282+
if (err) {
283+
done(err);
284+
} else {
285+
const resJson = res.body;
286+
should.exist(resJson);
287+
resJson.should.be.an('array');
288+
resJson.length.should.be.eql(1);
289+
// check invitations
290+
_.filter(resJson, inv => inv.id === 3).length.should.be.eql(1);
291+
resJson[0].email.should.be.eql('t***t@t***r.com');
256292
done();
257293
}
258294
});

src/routes/projectMemberInvites/update.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,11 @@ module.exports = [
122122
};
123123
return util
124124
.addUserToProject(req, member)
125-
.then(() => res.json(util.maskInviteEmails('$.email', updatedInvite, req)))
125+
.then(() => res.json(util.postProcessInvites('$.email', updatedInvite, req)))
126126
.catch(err => next(err));
127127
});
128128
}
129-
return res.json(util.maskInviteEmails('$.email', updatedInvite, req));
129+
return res.json(util.postProcessInvites('$.email', updatedInvite, req));
130130
});
131131
})
132132
.catch(next);

src/routes/projects/get.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ module.exports = [
186186
req.log.debug('Project found in ES');
187187
return result;
188188
}).then((project) => {
189-
res.status(200).json(util.maskInviteEmails('$.invites[?(@.email)]', project, req));
189+
res.status(200).json(util.postProcessInvites('$.invites[?(@.email)]', project, req));
190190
})
191191
.catch(err => next(err));
192192
},

src/routes/projects/get.spec.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,28 @@ describe('GET Project', () => {
526526
});
527527
});
528528

529+
it('should not return "email" for any invite which has userId field', (done) => {
530+
request(server)
531+
.get(`/v5/projects/${project1.id}`)
532+
.set({
533+
Authorization: `Bearer ${testUtil.jwts.member}`,
534+
})
535+
.expect('Content-Type', /json/)
536+
.expect(200)
537+
.end((err, res) => {
538+
if (err) {
539+
done(err);
540+
} else {
541+
const resJson = res.body;
542+
should.exist(resJson);
543+
resJson.invites.length.should.be.eql(1);
544+
resJson.invites[0].should.have.property('userId');
545+
should.not.exist(resJson.invites[0].email);
546+
done();
547+
}
548+
});
549+
});
550+
529551
it('should only return "members.role" field, when it\'s the only field listed in "fields" query param', (done) => {
530552
request(server)
531553
.get(`/v5/projects/${project1.id}?fields=members.role`)

src/routes/projects/list.js

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -629,15 +629,18 @@ module.exports = [
629629
// so we don't want DB to return unrelated data, ref issue #450
630630
if (_.intersection(_.keys(filters), SUPPORTED_FILTERS).length > 0) {
631631
req.log.debug('Don\'t fallback to DB because some filters are defined.');
632-
return util.setPaginationHeaders(req, res, util.maskInviteEmails('$[*].invites[?(@.email)]', result, req));
632+
return util.setPaginationHeaders(req, res,
633+
util.postProcessInvites('$.rows[*].invites[?(@.email)]', result, req));
633634
}
634635

635636
return retrieveProjectsFromDB(req, criteria, sort, req.query.fields)
636-
.then(r => util.setPaginationHeaders(req, res, util.maskInviteEmails('$[*].invites[?(@.email)]', r, req)));
637+
.then(r => util.setPaginationHeaders(req, res,
638+
util.postProcessInvites('$.rows[*].invites[?(@.email)]', r, req)));
637639
}
638640
req.log.debug('Projects found in ES');
639641
// set header
640-
return util.setPaginationHeaders(req, res, util.maskInviteEmails('$[*].invites[?(@.email)]', result, req));
642+
return util.setPaginationHeaders(req, res,
643+
util.postProcessInvites('$.rows[*].invites[?(@.email)]', result, req));
641644
})
642645
.catch(err => next(err));
643646
}
@@ -655,14 +658,17 @@ module.exports = [
655658
// so we don't want DB to return unrelated data, ref issue #450
656659
if (_.intersection(_.keys(filters), SUPPORTED_FILTERS).length > 0) {
657660
req.log.debug('Don\'t fallback to DB because some filters are defined.');
658-
return util.setPaginationHeaders(req, res, util.maskInviteEmails('$[*].invites[?(@.email)]', result, req));
661+
return util.setPaginationHeaders(req, res,
662+
util.postProcessInvites('$.rows[*].invites[?(@.email)]', result, req));
659663
}
660664

661665
return retrieveProjectsFromDB(req, criteria, sort, req.query.fields)
662-
.then(r => util.setPaginationHeaders(req, res, util.maskInviteEmails('$[*].invites[?(@.email)]', r, req)));
666+
.then(r => util.setPaginationHeaders(req, res,
667+
util.postProcessInvites('$.rows[*].invites[?(@.email)]', r, req)));
663668
}
664669
req.log.debug('Projects found in ES');
665-
return util.setPaginationHeaders(req, res, util.maskInviteEmails('$[*].invites[?(@.email)]', result, req));
670+
return util.setPaginationHeaders(req, res,
671+
util.postProcessInvites('$.rows[*].invites[?(@.email)]', result, req));
666672
})
667673
.catch(err => next(err));
668674
},

src/routes/projects/list.spec.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,8 @@ describe('LIST Project', () => {
173173
let project1;
174174
let project2;
175175
let project3;
176-
before(function inner(done) {
177-
this.timeout(10000);
176+
before((done) => {
177+
// this.timeout(10000);
178178
testUtil.clearDb()
179179
.then(() => testUtil.clearES())
180180
.then(() => {
@@ -376,6 +376,10 @@ describe('LIST Project', () => {
376376
const resJson = res.body;
377377
should.exist(resJson);
378378
resJson.should.have.lengthOf(2);
379+
resJson[0].invites[0].should.have.property('userId');
380+
should.not.exist(resJson[0].invites[0].email);
381+
resJson[1].invites[0].should.have.property('userId');
382+
should.not.exist(resJson[1].invites[0].email);
379383
done();
380384
}
381385
});
@@ -1070,6 +1074,9 @@ describe('LIST Project', () => {
10701074
should.exist(resJson);
10711075
resJson.should.have.lengthOf(1);
10721076
resJson[0].name.should.equal('test1');
1077+
resJson[0].invites.should.have.lengthOf(1);
1078+
resJson[0].invites[0].should.have.property('userId');
1079+
should.not.exist(resJson[0].invites[0].email);
10731080
done();
10741081
}
10751082
});

0 commit comments

Comments
 (0)