Skip to content

Commit

Permalink
feat(permissions): list body members with permission
Browse files Browse the repository at this point in the history
  • Loading branch information
serge1peshcoff committed Mar 31, 2020
1 parent cbd2eb1 commit 4988337
Show file tree
Hide file tree
Showing 2 changed files with 221 additions and 0 deletions.
53 changes: 53 additions & 0 deletions middlewares/memberships.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
const {
Permission,
BodyMembership,
CircleMembership,
JoinRequest,
User
} = require('../models');
const helpers = require('../lib/helpers');
const { Sequelize } = require('../lib/sequelize');
const constants = require('../lib/constants');
const errors = require('../lib/errors');

exports.listAllMemberships = async (req, res) => {
if (req.query.holds_permission) {
return exports.listAllMembershipsWithPermission(req, res);
}

if (!req.permissions.hasPermission('view_member:body')) {
return errors.makeForbiddenError(res, 'Permission view_member:body is required, but not present.');
}
Expand All @@ -29,6 +36,52 @@ exports.listAllMemberships = async (req, res) => {
});
};

exports.listAllMembershipsWithPermission = async (req, res) => {
if (!req.permissions.hasPermission('view_member:body')) {
return errors.makeForbiddenError(res, 'Permission view_member:body is required, but not present.');
}

if (!req.query.holds_permission.action || !req.query.holds_permission.object) {
return errors.makeBadRequestError(res, 'Action or object is not specified.');
}

const permission = await Permission.findOne({
where: {
scope: 'local',
action: req.query.holds_permission.action,
object: req.query.holds_permission.object
}
});

if (!permission) {
return errors.makeNotFoundError(res, 'Permission is not found.');
}

const circles = await req.permissions.fetchPermissionCircles(permission);
const circleIds = circles.map((circle) => circle.id);

const result = await BodyMembership.findAndCountAll({
where: {
body_id: req.currentBody.id,
'$user->circle_memberships.circle_id$': { [Sequelize.Op.in]: circleIds },
...helpers.filterBy(req.query.query, constants.FIELDS_TO_QUERY.BODY_MEMBERSHIP)
},
...helpers.getPagination(req.query),
order: helpers.getSorting(req.query),
include: [{
model: User,
include: [CircleMembership]
}]
});

return res.json({
success: true,
data: result.rows,
meta: { count: result.count }
});
};


exports.updateMembership = async (req, res) => {
if (!req.permissions.hasPermission('update_member:body')) {
return errors.makeForbiddenError(res, 'Permission update_member:body is required, but not present.');
Expand Down
168 changes: 168 additions & 0 deletions test/api/memberships-listing-with-permission.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
const { startServer, stopServer } = require('../../lib/server.js');
const { request } = require('../scripts/helpers');
const generator = require('../scripts/generator');

describe('Memberships list wth permission', () => {
beforeAll(async () => {
await startServer();
});

afterAll(async () => {
await stopServer();
});

afterEach(async () => {
await generator.clearAll();
});

test('should fail if no view:member permission', async () => {
const user = await generator.createUser();
const token = await generator.createAccessToken({}, user);

const body = await generator.createBody();
await generator.createBodyMembership(body, user);

const res = await request({
uri: '/bodies/' + body.id + '/members',
method: 'GET',
qs: { holds_permission: { action: 'action', object: 'object' } },
headers: { 'X-Auth-Token': token.value }
});

expect(res.statusCode).toEqual(403);
expect(res.body.success).toEqual(false);
expect(res.body).toHaveProperty('message');
expect(res.body).not.toHaveProperty('data');
});

test('should complain if no action is specified', async () => {
const user = await generator.createUser({ superadmin: true });
const token = await generator.createAccessToken({}, user);

const body = await generator.createBody();
await generator.createBodyMembership(body, user);

await generator.createPermission({ scope: 'global', action: 'view_member', object: 'body' });

const res = await request({
uri: '/bodies/' + body.id + '/members',
method: 'GET',
qs: { holds_permission: { object: 'object' } },
headers: { 'X-Auth-Token': token.value }
});

expect(res.statusCode).toEqual(400);
expect(res.body.success).toEqual(false);
expect(res.body).toHaveProperty('message');
expect(res.body).not.toHaveProperty('data');
});

test('should complain if no object is specified', async () => {
const user = await generator.createUser({ superadmin: true });
const token = await generator.createAccessToken({}, user);

const body = await generator.createBody();
await generator.createBodyMembership(body, user);

await generator.createPermission({ scope: 'global', action: 'view_member', object: 'body' });

const res = await request({
uri: '/bodies/' + body.id + '/members',
method: 'GET',
qs: { holds_permission: { action: 'action' } },
headers: { 'X-Auth-Token': token.value }
});

expect(res.statusCode).toEqual(400);
expect(res.body.success).toEqual(false);
expect(res.body).toHaveProperty('message');
expect(res.body).not.toHaveProperty('data');
});

test('should fail if no permission found', async () => {
const user = await generator.createUser({ superadmin: true });
const token = await generator.createAccessToken({}, user);

const body = await generator.createBody();
await generator.createBodyMembership(body, user);

await generator.createPermission({ scope: 'global', action: 'view_member', object: 'body' });

const res = await request({
uri: '/bodies/' + body.id + '/members',
method: 'GET',
qs: { holds_permission: { action: 'action', object: 'object' } },
headers: { 'X-Auth-Token': token.value }
});

expect(res.statusCode).toEqual(404);
expect(res.body.success).toEqual(false);
expect(res.body).toHaveProperty('message');
expect(res.body).not.toHaveProperty('data');
});

test('should list member who got this permission from a bound circle directly', async () => {
const user = await generator.createUser({ superadmin: true });
const token = await generator.createAccessToken({}, user);

await generator.createPermission({ scope: 'global', action: 'view_member', object: 'body' });

const body = await generator.createBody();
const permission = await generator.createPermission({ scope: 'local', action: 'action', object: 'object' });
const circle = await generator.createCircle({ body_id: body.id });
const otherUser = await generator.createUser();
await generator.createBodyMembership(body, otherUser);
await generator.createCircleMembership(circle, otherUser);
await generator.createCirclePermission(circle, permission);

const res = await request({
uri: '/bodies/' + body.id + '/members',
method: 'GET',
qs: { holds_permission: { action: 'action', object: 'object' } },
headers: { 'X-Auth-Token': token.value }
});

expect(res.statusCode).toEqual(200);
expect(res.body.success).toEqual(true);
expect(res.body).toHaveProperty('data');
expect(res.body).not.toHaveProperty('errors');

expect(res.body.data.length).toEqual(1);
expect(res.body.data[0].user.id).toEqual(otherUser.id);
});

test('should list member who got this permission from a bound circle indirectly', async () => {
const user = await generator.createUser({ superadmin: true });
const token = await generator.createAccessToken({}, user);

await generator.createPermission({ scope: 'global', action: 'view_member', object: 'body' });

const body = await generator.createBody();
const permission = await generator.createPermission({ scope: 'local', action: 'action', object: 'object' });


const firstCircle = await generator.createCircle();
const secondCircle = await generator.createCircle({ parent_circle_id: firstCircle.id });
const thirdCircle = await generator.createCircle({ parent_circle_id: secondCircle.id, body_id: body.id });

const otherUser = await generator.createUser();
await generator.createBodyMembership(body, otherUser);
await generator.createCircleMembership(thirdCircle, otherUser);
await generator.createCirclePermission(firstCircle, permission);

const res = await request({
uri: '/bodies/' + body.id + '/members',
method: 'GET',
qs: { holds_permission: { action: 'action', object: 'object' } },
headers: { 'X-Auth-Token': token.value }
});

expect(res.statusCode).toEqual(200);
expect(res.body.success).toEqual(true);
expect(res.body).toHaveProperty('data');
expect(res.body).not.toHaveProperty('errors');

expect(res.body.data.length).toEqual(1);
expect(res.body.data[0].user.id).toEqual(otherUser.id);
});
});

0 comments on commit 4988337

Please sign in to comment.