Skip to content

Commit

Permalink
feat(general): my global permissions and testing
Browse files Browse the repository at this point in the history
  • Loading branch information
serge1peshcoff committed Feb 9, 2020
1 parent 4944a85 commit 2fa1577
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 4 deletions.
27 changes: 26 additions & 1 deletion lib/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,30 @@ function filterFields(body, fieldsToFilter) {
return unflattenObject(flatten);
}

// A helper to traverse indirect circles (so if a person is a member
// of a circle which is a child circle, it should return both of the circles.)
// Required for recursive circles schema.
// Accepts 2 params, all circles map (with key as ID and value as circle)
// and circlesIds (array of ids), returns array of ids to be consumed
// by Sequelize.Op.in
function traverseIndirectCircles(allCirclesMap, circlesIds) {
const indirectCircles = {};
const traverseIndirectCirclesRecursive = (circleId) => {
indirectCircles[circleId] = true;
if (!allCirclesMap[circleId].parent_circle_id) {
return;
}

return traverseIndirectCirclesRecursive(allCirclesMap[circleId].parent_circle_id);
};

for (const id of circlesIds) {
traverseIndirectCirclesRecursive(id);
}

return Object.keys(indirectCircles);
}

// Figure out if the value is a number or a string containing only numbers
function isNumber(value) {
/* istanbul ignore if */
Expand All @@ -59,5 +83,6 @@ module.exports = {
filterFields,
flattenObject,
unflattenObject,
isNumber
isNumber,
traverseIndirectCircles
};
34 changes: 31 additions & 3 deletions middlewares/generic.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const moment = require('moment');
const _ = require('lodash');

const errors = require('../lib/errors');
const logger = require('../lib/logger');
Expand All @@ -7,6 +8,8 @@ const {
User,
Body,
Circle,
CircleMembership,
CirclePermission,
Permission,
AccessToken
} = require('../models');
Expand Down Expand Up @@ -37,6 +40,33 @@ exports.maybeAuthorize = async (req, res, next) => {

req.user = accessToken.user;

// Fetching permissions.
// 1) get the list of the circles user's in.
const directCircleMemberships = await CircleMembership.findAll({
where: { user_id: req.user.id }
});

// 2) get the list of all circles with only id and parent_circle_id
// and converting it to a map to not look over the whole
// array each time.
req.allCircles = await Circle.findAll({ fields: ['id', 'parent_circle_id'] });
req.allCirclesMap = _.keyBy(req.allCircles, 'id');

// 3) fetch all the permissions
const indirectCirclesArray = helpers.traverseIndirectCircles(req.allCirclesMap, directCircleMemberships.map((membership) => membership.circle_id));
req.permissions = await Permission.findAll({
where: {
'$circle_permissions.circle_id$': { [Sequelize.Op.in]: indirectCirclesArray },
scope: 'global'
},
include: [CirclePermission]
});

req.permissionsMap = _(req.permissions)
.map((elt) => [elt, 1])
.unzipWith()
.value();

return next();
};

Expand All @@ -51,12 +81,10 @@ exports.ensureAuthorized = async (req, res, next) => {
return next();
};

/* istanbul ignore next */
exports.getMyGlobalPermissions = async (req, res) => {
// TODO: return real permissions.
return res.json({
success: true,
data: []
data: req.permissions
});
};

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"express": "^4.17.1",
"express-promise-router": "^3.0.3",
"express-query-boolean": "^2.0.0",
"lodash": "^4.17.15",
"moment": "^2.24.0",
"morgan": "^1.9.1",
"node-cron": "^2.0.3",
Expand Down
109 changes: 109 additions & 0 deletions test/api/my-permissions-global.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
const { startServer, stopServer } = require('../../lib/server.js');
const { request } = require('../scripts/helpers');
const generator = require('../scripts/generator');

describe('My permissions global', () => {
beforeAll(async () => {
await startServer();
});

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

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

test('should list the global permission that is assigned to a circle user is member of', async () => {
const user = await generator.createUser();
const token = await generator.createAccessToken({}, user);

const permission = await generator.createPermission({ scope: 'global', action: 'action', object: 'object' });
const circle = await generator.createCircle();
await generator.createCirclePermission(circle, permission);
await generator.createCircleMembership(circle, user);

const res = await request({
uri: '/my_permissions',
method: 'GET',
headers: { 'X-Auth-Token': token.value }
});

expect(res.statusCode).toEqual(200);
expect(res.body.success).toEqual(true);
expect(res.body).not.toHaveProperty('errors');
expect(res.body).toHaveProperty('data');
expect(res.body.data.length).toEqual(1);
expect(res.body.data[0].combined).toEqual('global:action:object');
});

test('should not list the local permissions that is assigned to a circle user is member of', async () => {
const user = await generator.createUser();
const token = await generator.createAccessToken({}, user);

const permission = await generator.createPermission({ scope: 'local', action: 'action', object: 'object' });
const circle = await generator.createCircle();
await generator.createCirclePermission(circle, permission);
await generator.createCircleMembership(circle, user);

const res = await request({
uri: '/my_permissions',
method: 'GET',
headers: { 'X-Auth-Token': token.value }
});

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

test('should list the global permission that is assigned to a circle user is indirectly a member of', async () => {
const user = await generator.createUser();
const token = await generator.createAccessToken({}, user);

const permission = await generator.createPermission({ scope: 'global', 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 });

await generator.createCirclePermission(firstCircle, permission);
await generator.createCircleMembership(thirdCircle, user);

const res = await request({
uri: '/my_permissions',
method: 'GET',
headers: { 'X-Auth-Token': token.value }
});

expect(res.statusCode).toEqual(200);
expect(res.body.success).toEqual(true);
expect(res.body).not.toHaveProperty('errors');
expect(res.body).toHaveProperty('data');
expect(res.body.data.length).toEqual(1);
expect(res.body.data[0].combined).toEqual('global:action:object');
});

test('should not list the global permission that is assigned to a circle user is not a member of', async () => {
const user = await generator.createUser();
const token = await generator.createAccessToken({}, user);

const permission = await generator.createPermission({ scope: 'global', action: 'action', object: 'object' });
const circle = await generator.createCircle();
await generator.createCirclePermission(circle, permission);

const res = await request({
uri: '/my_permissions',
method: 'GET',
headers: { 'X-Auth-Token': token.value }
});

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

0 comments on commit 2fa1577

Please sign in to comment.