Skip to content

Commit d5d69da

Browse files
author
vikasrohit
authored
Merge pull request #347 from topcoder-platform/dev-next
Dev next
2 parents 7060d3f + 5eaf7b8 commit d5d69da

File tree

6 files changed

+508
-148
lines changed

6 files changed

+508
-148
lines changed

src/permissions/constants.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* Definitions of permissions which could be used with util methods
3+
* `util.hasPermission` or `util.hasPermissionForProject`.
4+
*
5+
* We can define permission using two logics:
6+
* 1. **WHAT** can be done with such a permission. Such constants may have names like:
7+
* - `VIEW_PROJECT`
8+
* - `EDIT_MILESTONE`
9+
* - `DELETE_WORK`
10+
* and os on.
11+
* 2. **WHO** can do actions with such a permission. Such constants **MUST** start from the prefix `ROLES_`, examples:
12+
* - `ROLES_COPILOT_AND_ABOVE`
13+
* - `ROLES_PROJECT_MEMBERS`
14+
* - `ROLES_ADMINS`
15+
*/
16+
import {
17+
PROJECT_MEMBER_ROLE,
18+
ADMIN_ROLES,
19+
} from '../constants';
20+
21+
export const PERMISSION = { // eslint-disable-line import/prefer-default-export
22+
/**
23+
* Permissions defined by logic: **WHO** can do actions with such a permission.
24+
*/
25+
ROLES_COPILOT_AND_ABOVE: {
26+
topcoderRoles: ADMIN_ROLES,
27+
projectRoles: [
28+
PROJECT_MEMBER_ROLE.MANAGER,
29+
PROJECT_MEMBER_ROLE.COPILOT,
30+
],
31+
},
32+
/**
33+
* Permissions defined by logic: **WHAT** can be done with such a permission.
34+
*/
35+
};
36+

src/permissions/copilotAndAbove.js

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
import _ from 'lodash';
22
import util from '../util';
3-
import {
4-
PROJECT_MEMBER_ROLE,
5-
ADMIN_ROLES,
6-
} from '../constants';
73
import models from '../models';
8-
4+
import { PERMISSION } from './constants';
95

106
/**
117
* Permission to allow copilot and above roles to perform certain operations
@@ -16,27 +12,17 @@ import models from '../models';
1612
*/
1713
module.exports = req => new Promise((resolve, reject) => {
1814
const projectId = _.parseInt(req.params.projectId);
19-
const isAdmin = util.hasRoles(req, ADMIN_ROLES);
20-
21-
if (isAdmin) {
22-
return resolve(true);
23-
}
2415

2516
return models.ProjectMember.getActiveProjectMembers(projectId)
2617
.then((members) => {
18+
const hasPermission = util.hasPermission(PERMISSION.ROLES_COPILOT_AND_ABOVE, req.authUser, members);
19+
20+
// TODO should we really do this?
21+
// if no, we can replace `getActiveProjectMembers + util.hasPermission` with one `util.hasPermissionForProject`
2722
req.context = req.context || {};
2823
req.context.currentProjectMembers = members;
29-
const validMemberProjectRoles = [
30-
PROJECT_MEMBER_ROLE.MANAGER,
31-
PROJECT_MEMBER_ROLE.COPILOT,
32-
];
33-
// check if the copilot or manager has access to this project
34-
const isMember = _.some(
35-
members,
36-
m => m.userId === req.authUser.userId && validMemberProjectRoles.includes(m.role),
37-
);
3824

39-
if (!isMember) {
25+
if (!hasPermission) {
4026
// the copilot or manager is not a registered project member
4127
return reject(new Error('You do not have permissions to perform this action'));
4228
}

src/routes/projectMemberInvites/create.js

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,25 @@ const compareEmail = (email1, email2, options = { UNIQUE_GMAIL_VALIDATION: false
6060
* @param {Array} invites existent invites from DB
6161
* @param {Object} data template for new invites to be put in DB
6262
* @param {Array} failed failed invites error message
63+
* @param {Array} members already members of the project
6364
*
6465
* @returns {Promise<Promise[]>} list of promises
6566
*/
66-
const buildCreateInvitePromises = (req, invite, invites, data, failed) => {
67+
const buildCreateInvitePromises = (req, invite, invites, data, failed, members) => {
6768
const invitePromises = [];
6869
if (invite.userIds) {
6970
// remove invites for users that are invited already
70-
_.remove(invite.userIds, u => _.some(invites, i => i.userId === u));
71+
const errMessageForAlreadyInvitedUsers = 'User with such handle is already invited to this project.';
72+
_.remove(invite.userIds, u => _.some(invites, (i) => {
73+
const isPresent = i.userId === u;
74+
if (isPresent) {
75+
failed.push(_.assign({}, {
76+
userId: u,
77+
message: errMessageForAlreadyInvitedUsers,
78+
}));
79+
}
80+
return isPresent;
81+
}));
7182
invite.userIds.forEach((userId) => {
7283
const dataNew = _.clone(data);
7384

@@ -96,8 +107,34 @@ const buildCreateInvitePromises = (req, invite, invites, data, failed) => {
96107
compareEmail(existentUser.email, inviteEmail, { UNIQUE_GMAIL_VALIDATION: false })),
97108
);
98109

110+
// remove users that are already member of the team
111+
const errMessageForAlreadyMemberUsers = 'User with such email is already a member of the team.';
112+
113+
_.remove(existentUsersWithNumberId, user => _.some(members, (m) => {
114+
const isPresent = (m.userId === user.id);
115+
if (isPresent) {
116+
failed.push(_.assign({}, {
117+
email: user.email,
118+
message: errMessageForAlreadyMemberUsers,
119+
}));
120+
}
121+
return isPresent;
122+
}));
123+
99124
// remove invites for users that are invited already
100-
_.remove(existentUsersWithNumberId, user => _.some(invites, i => i.userId === user.id));
125+
const errMessageForAlreadyInvitedUsers = 'User with such email is already invited to this project.';
126+
127+
_.remove(existentUsersWithNumberId, user => _.some(invites, (i) => {
128+
const isPresent = (i.userId === user.id);
129+
if (isPresent) {
130+
failed.push(_.assign({}, {
131+
email: i.email,
132+
message: errMessageForAlreadyInvitedUsers,
133+
}));
134+
}
135+
return isPresent;
136+
}));
137+
101138
existentUsersWithNumberId.forEach((user) => {
102139
const dataNew = _.clone(data);
103140

@@ -109,8 +146,19 @@ const buildCreateInvitePromises = (req, invite, invites, data, failed) => {
109146

110147
// remove invites for users that are invited already
111148
_.remove(nonExistentUserEmails, email =>
112-
_.some(invites, i =>
113-
compareEmail(i.email, email, { UNIQUE_GMAIL_VALIDATION: config.get('UNIQUE_GMAIL_VALIDATION') })));
149+
_.some(invites, (i) => {
150+
const areEmailsSame = compareEmail(i.email, email, {
151+
UNIQUE_GMAIL_VALIDATION: config.get('UNIQUE_GMAIL_VALIDATION'),
152+
});
153+
if (areEmailsSame) {
154+
failed.push(_.assign({}, {
155+
email: i.email,
156+
message: errMessageForAlreadyInvitedUsers,
157+
}));
158+
}
159+
return areEmailsSame;
160+
}),
161+
);
114162
nonExistentUserEmails.forEach((email) => {
115163
const dataNew = _.clone(data);
116164

@@ -204,9 +252,19 @@ module.exports = [
204252
const projectId = _.parseInt(req.params.projectId);
205253

206254
const promises = [];
255+
const errorMessageForAlreadyMemberUser = 'User with such handle is already a member of the team.';
207256
if (invite.userIds) {
208257
// remove members already in the team
209-
_.remove(invite.userIds, u => _.some(members, m => m.userId === u));
258+
_.remove(invite.userIds, u => _.some(members, (m) => {
259+
const isPresent = m.userId === u;
260+
if (isPresent) {
261+
failed.push(_.assign({}, {
262+
userId: m.userId,
263+
message: errorMessageForAlreadyMemberUser,
264+
}));
265+
}
266+
return isPresent;
267+
}));
210268
// permission:
211269
// user has to have constants.MANAGER_ROLES role
212270
// to be invited as PROJECT_MEMBER_ROLE.MANAGER
@@ -262,7 +320,7 @@ module.exports = [
262320
};
263321

264322
req.log.debug('Creating invites');
265-
return models.sequelize.Promise.all(buildCreateInvitePromises(req, invite, invites, data, failed))
323+
return models.sequelize.Promise.all(buildCreateInvitePromises(req, invite, invites, data, failed, members))
266324
.then((values) => {
267325
values.forEach((v) => {
268326
req.app.emit(EVENT.ROUTING_KEY.PROJECT_MEMBER_INVITE_CREATED, {

0 commit comments

Comments
 (0)