Skip to content

Permission endpoints #370

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,105 changes: 635 additions & 470 deletions postman.json

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions src/permissions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,12 @@ module.exports = () => {
Authorizer.setPolicy('workItem.edit', workManagementPermissions('workItem.edit'));
Authorizer.setPolicy('workItem.delete', workManagementPermissions('workItem.delete'));
Authorizer.setPolicy('workItem.view', projectView);

// Work management permission
Authorizer.setPolicy('workManagementPermission.create', projectAdmin);
Authorizer.setPolicy('workManagementPermission.edit', projectAdmin);
Authorizer.setPolicy('workManagementPermission.delete', projectAdmin);
Authorizer.setPolicy('workManagementPermission.view', projectAdmin);

Authorizer.setPolicy('permissions.view', projectView);
};
15 changes: 15 additions & 0 deletions src/routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ router.route('/v4/projects/metadata/productCategories')
router.route('/v4/projects/metadata/productCategories/:key')
.get(require('./productCategories/get'));

router.route('/v4/projects/metadata/workManagementPermission')
.get(require('./workManagementPermissions/list'));
router.route('/v4/projects/metadata/workManagementPermission/:id')
.get(require('./workManagementPermissions/get'));

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

// Permissions
router.route('/v4/projects/:projectId(\\d+)/permissions')
.get(require('./permissions/get'));

router.route('/v4/projects/:projectId(\\d+)/attachments')
.post(require('./attachments/create'));

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

router.route('/v4/projects/metadata/workManagementPermission')
.post(require('./workManagementPermissions/create'));

router.route('/v4/projects/metadata/workManagementPermission/:id')
.patch(require('./workManagementPermissions/update'))
.delete(require('./workManagementPermissions/delete'));

router.route('/v4/projects/metadata/projectTypes')
.post(require('./projectTypes/create'));

Expand Down
64 changes: 64 additions & 0 deletions src/routes/permissions/get.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* API to get project permissions
*/
import _ from 'lodash';
import validate from 'express-validation';
import Joi from 'joi';
import { middleware as tcMiddleware } from 'tc-core-library-js';
import util from '../../util';
import models from '../../models';

const permissions = tcMiddleware.permissions;

const schema = {
params: {
projectId: Joi.number().integer().positive().required(),
},
};

module.exports = [
validate(schema),
permissions('permissions.view'),
(req, res, next) => {
const projectId = req.params.projectId;
let workManagementPermissions;
return models.Project.findOne({
where: {
id: projectId,
},
})
.then((project) => {
if (!project) {
const apiErr = new Error(`Project not found for id ${projectId}`);
apiErr.status = 404;
return Promise.reject(apiErr);
}

if (!project.templateId) {
return Promise.resolve(true);
}

return models.WorkManagementPermission.findAll({
where: {
projectTemplateId: project.templateId,
},
});
})
.then((allPermissions) => {
workManagementPermissions = allPermissions;
return Promise.all(_.map(workManagementPermissions, workManagementPermission =>
util.hasPermissionForProject(workManagementPermission.permission, req.authUser, projectId)),
);
})
.then((accesses) => {
const allAccess = {};
_.each(workManagementPermissions, (p, ind) => {
if (accesses[ind]) {
allAccess[`${p.policy}`] = accesses[ind];
}
});
res.json(util.wrapResponse(req.id, allAccess));
})
.catch(next);
},
];
231 changes: 231 additions & 0 deletions src/routes/permissions/get.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
/* eslint-disable no-unused-expressions */
/**
* Tests for get.js
*/
import _ from 'lodash';
import chai from 'chai';
import request from 'supertest';

import models from '../../models';
import server from '../../app';
import testUtil from '../../tests/util';

chai.should();

describe('GET permissions', () => {
let projectId;
let templateId;

const memberUser = {
handle: testUtil.getDecodedToken(testUtil.jwts.member).handle,
userId: testUtil.getDecodedToken(testUtil.jwts.member).userId,
firstName: 'fname',
lastName: 'lName',
email: 'some@abc.com',
};
const copilotUser = {
handle: testUtil.getDecodedToken(testUtil.jwts.copilot).handle,
userId: testUtil.getDecodedToken(testUtil.jwts.copilot).userId,
firstName: 'fname',
lastName: 'lName',
email: 'some@abc.com',
};

const permissions = [
{
policy: 'work.create',
permission: {
allowRule: {
projectRoles: ['customer', 'copilot'],
topcoderRoles: ['Connect Manager', 'administrator'],
},
denyRule: { projectRoles: ['copilot'] },
},
createdBy: 1,
updatedBy: 1,
},
{
policy: 'work.edit',
permission: {
allowRule: {
projectRoles: ['copilot'],
topcoderRoles: ['Connect Manager'],
},
denyRule: { topcoderRoles: ['Connect Admin'] },
},
createdBy: 1,
updatedBy: 1,
},
];

beforeEach((done) => {
testUtil.clearDb()
.then(() => {
models.ProjectTemplate.create({
name: 'template 2',
key: 'key 2',
category: 'category 2',
icon: 'http://example.com/icon1.ico',
question: 'question 2',
info: 'info 2',
aliases: ['key-2', 'key_2'],
scope: {},
phases: {},
createdBy: 1,
updatedBy: 2,
})
.then((t) => {
templateId = t.id;
// Create projects
models.Project.create({
type: 'generic',
billingAccountId: 1,
name: 'test1',
description: 'test project1',
status: 'draft',
templateId,
details: {},
createdBy: 1,
updatedBy: 1,
lastActivityAt: 1,
lastActivityUserId: '1',
})
.then((project) => {
projectId = project.id;
models.ProjectMember.bulkCreate([{
id: 1,
userId: copilotUser.userId,
projectId,
role: 'copilot',
isPrimary: false,
createdBy: 1,
updatedBy: 1,
}, {
id: 2,
userId: memberUser.userId,
projectId,
role: 'customer',
isPrimary: true,
createdBy: 1,
updatedBy: 1,
}]).then(() => {
const newPermissions = _.map(permissions, p => _.assign({}, p, { projectTemplateId: templateId }));
models.WorkManagementPermission.bulkCreate(newPermissions, { returning: true })
.then(() => done());
});
});
});
});
});

after(testUtil.clearDb);

describe('GET /projects/{projectId}/permissions', () => {
it('should return 403 if user is not authenticated', (done) => {
request(server)
.get(`/v4/projects/${projectId}/permissions`)
.expect(403, done);
});

it('should return 403 for non-member', (done) => {
request(server)
.get(`/v4/projects/${projectId}/permissions`)
.set({
Authorization: `Bearer ${testUtil.jwts.member2}`,
})
.expect(403, done);
});

it('should return 404 for non-existed project', (done) => {
request(server)
.get('/v4/projects/9999/permissions')
.set({
Authorization: `Bearer ${testUtil.jwts.admin}`,
})
.expect(404, done);
});

it('should return 200 for project with no template', (done) => {
models.Project.create({
type: 'generic',
name: 'test1',
status: 'draft',
createdBy: 1,
updatedBy: 1,
lastActivityAt: 1,
lastActivityUserId: '1',
})
.then((p) => {
request(server)
.get(`/v4/projects/${p.id}/permissions`)
.set({
Authorization: `Bearer ${testUtil.jwts.admin}`,
})
.expect(200)
.end((err, res) => {
const resJson = res.body.result.content;
resJson.should.be.empty;
done();
});
});
});

it('should return 200 for connect admin - no permission', (done) => {
request(server)
.get(`/v4/projects/${projectId}/permissions`)
.set({
Authorization: `Bearer ${testUtil.jwts.connectAdmin}`,
})
.expect(200)
.end((err, res) => {
const resJson = res.body.result.content;
resJson.should.not.have.all.keys(permissions[0].policy, permissions[1].policy);
done();
});
});

it('should return 200 for copilot - has both no-permission and permission', (done) => {
request(server)
.get(`/v4/projects/${projectId}/permissions`)
.set({
Authorization: `Bearer ${testUtil.jwts.copilot}`,
})
.expect(200)
.end((err, res) => {
const resJson = res.body.result.content;
resJson.should.have.all.keys(permissions[1].policy);
resJson.should.not.have.all.keys(permissions[0].policy);
done();
});
});

it('should return 200 for admin - has both permission and no-permission', (done) => {
request(server)
.get(`/v4/projects/${projectId}/permissions`)
.set({
Authorization: `Bearer ${testUtil.jwts.admin}`,
})
.expect(200)
.end((err, res) => {
const resJson = res.body.result.content;
resJson.should.have.all.keys(permissions[0].policy);
resJson.should.not.have.all.keys(permissions[1].policy);
done();
});
});

it('should return 200 for manager - has permissions', (done) => {
request(server)
.get(`/v4/projects/${projectId}/permissions`)
.set({
Authorization: `Bearer ${testUtil.jwts.manager}`,
})
.expect(200)
.end((err, res) => {
const resJson = res.body.result.content;
resJson.should.have.all.keys(permissions[0].policy, permissions[1].policy);
done();
});
});
});
});
Loading