Skip to content

Commit cf0c088

Browse files
author
vikasrohit
authored
Merge pull request #374 from topcoder-platform/feature/permission-endpoints
Feature/permission endpoints
2 parents 7a858cb + f74d2f5 commit cf0c088

File tree

16 files changed

+2633
-470
lines changed

16 files changed

+2633
-470
lines changed

postman.json

Lines changed: 635 additions & 470 deletions
Large diffs are not rendered by default.

src/permissions/index.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,4 +117,12 @@ module.exports = () => {
117117
Authorizer.setPolicy('workItem.edit', workManagementPermissions('workItem.edit'));
118118
Authorizer.setPolicy('workItem.delete', workManagementPermissions('workItem.delete'));
119119
Authorizer.setPolicy('workItem.view', projectView);
120+
121+
// Work management permission
122+
Authorizer.setPolicy('workManagementPermission.create', projectAdmin);
123+
Authorizer.setPolicy('workManagementPermission.edit', projectAdmin);
124+
Authorizer.setPolicy('workManagementPermission.delete', projectAdmin);
125+
Authorizer.setPolicy('workManagementPermission.view', projectAdmin);
126+
127+
Authorizer.setPolicy('permissions.view', projectView);
120128
};

src/routes/index.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ router.route('/v4/projects/metadata/productCategories')
6565
router.route('/v4/projects/metadata/productCategories/:key')
6666
.get(require('./productCategories/get'));
6767

68+
router.route('/v4/projects/metadata/workManagementPermission')
69+
.get(require('./workManagementPermissions/list'));
70+
router.route('/v4/projects/metadata/workManagementPermission/:id')
71+
.get(require('./workManagementPermissions/get'));
6872

6973
router.use('/v4/projects/metadata', compression());
7074
router.route('/v4/projects/metadata')
@@ -109,6 +113,10 @@ router.route('/v4/projects/:projectId(\\d+)/members/:id(\\d+)')
109113
.delete(require('./projectMembers/delete'))
110114
.patch(require('./projectMembers/update'));
111115

116+
// Permissions
117+
router.route('/v4/projects/:projectId(\\d+)/permissions')
118+
.get(require('./permissions/get'));
119+
112120
router.route('/v4/projects/:projectId(\\d+)/attachments')
113121
.post(require('./attachments/create'));
114122

@@ -165,6 +173,13 @@ router.route('/v4/projects/metadata/productCategories/:key')
165173
.patch(require('./productCategories/update'))
166174
.delete(require('./productCategories/delete'));
167175

176+
router.route('/v4/projects/metadata/workManagementPermission')
177+
.post(require('./workManagementPermissions/create'));
178+
179+
router.route('/v4/projects/metadata/workManagementPermission/:id')
180+
.patch(require('./workManagementPermissions/update'))
181+
.delete(require('./workManagementPermissions/delete'));
182+
168183
router.route('/v4/projects/metadata/projectTypes')
169184
.post(require('./projectTypes/create'));
170185

src/routes/permissions/get.js

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/**
2+
* API to get project permissions
3+
*/
4+
import validate from 'express-validation';
5+
import Joi from 'joi';
6+
import { middleware as tcMiddleware } from 'tc-core-library-js';
7+
import util from '../../util';
8+
import models from '../../models';
9+
10+
const permissions = tcMiddleware.permissions;
11+
12+
const schema = {
13+
params: {
14+
projectId: Joi.number().integer().positive().required(),
15+
},
16+
};
17+
18+
module.exports = [
19+
validate(schema),
20+
permissions('permissions.view'),
21+
(req, res, next) => {
22+
const projectId = req.params.projectId;
23+
return models.Project.findOne({
24+
where: {
25+
id: projectId,
26+
},
27+
})
28+
.then((project) => {
29+
if (!project) {
30+
const apiErr = new Error(`Project not found for id ${projectId}`);
31+
apiErr.status = 404;
32+
return Promise.reject(apiErr);
33+
}
34+
35+
if (!project.templateId) {
36+
return Promise.resolve([]);
37+
}
38+
39+
return models.WorkManagementPermission.findAll({
40+
where: {
41+
projectTemplateId: project.templateId,
42+
},
43+
});
44+
})
45+
.then((workManagementPermissions) => {
46+
const allowPermissions = {};
47+
48+
// find all allowed permissions
49+
workManagementPermissions.forEach((workManagementPermission) => {
50+
const isAllowed = util.hasPermission(
51+
workManagementPermission.permission,
52+
req.authUser,
53+
req.context.currentProjectMembers,
54+
);
55+
56+
if (isAllowed) {
57+
allowPermissions[workManagementPermission.policy] = true;
58+
}
59+
});
60+
61+
res.json(util.wrapResponse(req.id, allowPermissions));
62+
})
63+
.catch(next);
64+
},
65+
];

src/routes/permissions/get.spec.js

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
/* eslint-disable no-unused-expressions */
2+
/**
3+
* Tests for get.js
4+
*/
5+
import _ from 'lodash';
6+
import chai from 'chai';
7+
import request from 'supertest';
8+
9+
import models from '../../models';
10+
import server from '../../app';
11+
import testUtil from '../../tests/util';
12+
13+
chai.should();
14+
15+
describe('GET permissions', () => {
16+
let projectId;
17+
let templateId;
18+
19+
const memberUser = {
20+
handle: testUtil.getDecodedToken(testUtil.jwts.member).handle,
21+
userId: testUtil.getDecodedToken(testUtil.jwts.member).userId,
22+
firstName: 'fname',
23+
lastName: 'lName',
24+
email: 'some@abc.com',
25+
};
26+
const copilotUser = {
27+
handle: testUtil.getDecodedToken(testUtil.jwts.copilot).handle,
28+
userId: testUtil.getDecodedToken(testUtil.jwts.copilot).userId,
29+
firstName: 'fname',
30+
lastName: 'lName',
31+
email: 'some@abc.com',
32+
};
33+
34+
const permissions = [
35+
{
36+
policy: 'work.create',
37+
permission: {
38+
allowRule: {
39+
projectRoles: ['customer', 'copilot'],
40+
topcoderRoles: ['Connect Manager', 'administrator'],
41+
},
42+
denyRule: { projectRoles: ['copilot'] },
43+
},
44+
createdBy: 1,
45+
updatedBy: 1,
46+
},
47+
{
48+
policy: 'work.edit',
49+
permission: {
50+
allowRule: {
51+
projectRoles: ['copilot'],
52+
topcoderRoles: ['Connect Manager'],
53+
},
54+
denyRule: { topcoderRoles: ['Connect Admin'] },
55+
},
56+
createdBy: 1,
57+
updatedBy: 1,
58+
},
59+
];
60+
61+
beforeEach((done) => {
62+
testUtil.clearDb()
63+
.then(() => {
64+
models.ProjectTemplate.create({
65+
name: 'template 2',
66+
key: 'key 2',
67+
category: 'category 2',
68+
icon: 'http://example.com/icon1.ico',
69+
question: 'question 2',
70+
info: 'info 2',
71+
aliases: ['key-2', 'key_2'],
72+
scope: {},
73+
phases: {},
74+
createdBy: 1,
75+
updatedBy: 2,
76+
})
77+
.then((t) => {
78+
templateId = t.id;
79+
// Create projects
80+
models.Project.create({
81+
type: 'generic',
82+
billingAccountId: 1,
83+
name: 'test1',
84+
description: 'test project1',
85+
status: 'draft',
86+
templateId,
87+
details: {},
88+
createdBy: 1,
89+
updatedBy: 1,
90+
lastActivityAt: 1,
91+
lastActivityUserId: '1',
92+
})
93+
.then((project) => {
94+
projectId = project.id;
95+
models.ProjectMember.bulkCreate([{
96+
id: 1,
97+
userId: copilotUser.userId,
98+
projectId,
99+
role: 'copilot',
100+
isPrimary: false,
101+
createdBy: 1,
102+
updatedBy: 1,
103+
}, {
104+
id: 2,
105+
userId: memberUser.userId,
106+
projectId,
107+
role: 'customer',
108+
isPrimary: true,
109+
createdBy: 1,
110+
updatedBy: 1,
111+
}]).then(() => {
112+
const newPermissions = _.map(permissions, p => _.assign({}, p, { projectTemplateId: templateId }));
113+
models.WorkManagementPermission.bulkCreate(newPermissions, { returning: true })
114+
.then(() => done());
115+
});
116+
});
117+
});
118+
});
119+
});
120+
121+
after(testUtil.clearDb);
122+
123+
describe('GET /projects/{projectId}/permissions', () => {
124+
it('should return 403 if user is not authenticated', (done) => {
125+
request(server)
126+
.get(`/v4/projects/${projectId}/permissions`)
127+
.expect(403, done);
128+
});
129+
130+
it('should return 403 for non-member', (done) => {
131+
request(server)
132+
.get(`/v4/projects/${projectId}/permissions`)
133+
.set({
134+
Authorization: `Bearer ${testUtil.jwts.member2}`,
135+
})
136+
.expect(403, done);
137+
});
138+
139+
it('should return 404 for non-existed project', (done) => {
140+
request(server)
141+
.get('/v4/projects/9999/permissions')
142+
.set({
143+
Authorization: `Bearer ${testUtil.jwts.admin}`,
144+
})
145+
.expect(404, done);
146+
});
147+
148+
it('should return 200 for project with no template', (done) => {
149+
models.Project.create({
150+
type: 'generic',
151+
name: 'test1',
152+
status: 'draft',
153+
createdBy: 1,
154+
updatedBy: 1,
155+
lastActivityAt: 1,
156+
lastActivityUserId: '1',
157+
})
158+
.then((p) => {
159+
request(server)
160+
.get(`/v4/projects/${p.id}/permissions`)
161+
.set({
162+
Authorization: `Bearer ${testUtil.jwts.admin}`,
163+
})
164+
.expect(200)
165+
.end((err, res) => {
166+
const resJson = res.body.result.content;
167+
resJson.should.be.empty;
168+
done();
169+
});
170+
});
171+
});
172+
173+
it('should return 200 for connect admin - no permission', (done) => {
174+
request(server)
175+
.get(`/v4/projects/${projectId}/permissions`)
176+
.set({
177+
Authorization: `Bearer ${testUtil.jwts.connectAdmin}`,
178+
})
179+
.expect(200)
180+
.end((err, res) => {
181+
const resJson = res.body.result.content;
182+
resJson.should.not.have.all.keys(permissions[0].policy, permissions[1].policy);
183+
done();
184+
});
185+
});
186+
187+
it('should return 200 for copilot - has both no-permission and permission', (done) => {
188+
request(server)
189+
.get(`/v4/projects/${projectId}/permissions`)
190+
.set({
191+
Authorization: `Bearer ${testUtil.jwts.copilot}`,
192+
})
193+
.expect(200)
194+
.end((err, res) => {
195+
const resJson = res.body.result.content;
196+
resJson.should.have.all.keys(permissions[1].policy);
197+
resJson.should.not.have.all.keys(permissions[0].policy);
198+
done();
199+
});
200+
});
201+
202+
it('should return 200 for admin - has both permission and no-permission', (done) => {
203+
request(server)
204+
.get(`/v4/projects/${projectId}/permissions`)
205+
.set({
206+
Authorization: `Bearer ${testUtil.jwts.admin}`,
207+
})
208+
.expect(200)
209+
.end((err, res) => {
210+
const resJson = res.body.result.content;
211+
resJson.should.have.all.keys(permissions[0].policy);
212+
resJson.should.not.have.all.keys(permissions[1].policy);
213+
done();
214+
});
215+
});
216+
217+
it('should return 200 for manager - has permissions', (done) => {
218+
request(server)
219+
.get(`/v4/projects/${projectId}/permissions`)
220+
.set({
221+
Authorization: `Bearer ${testUtil.jwts.manager}`,
222+
})
223+
.expect(200)
224+
.end((err, res) => {
225+
const resJson = res.body.result.content;
226+
resJson.should.have.all.keys(permissions[0].policy, permissions[1].policy);
227+
done();
228+
});
229+
});
230+
});
231+
});

0 commit comments

Comments
 (0)