openhim-core
Version:
The OpenHIM core application that provides logging and routing of http requests
567 lines (480 loc) • 15 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getRoles = getRoles;
exports.getRole = getRole;
exports.addRole = addRole;
exports.updateRole = updateRole;
exports.deleteRole = deleteRole;
var _winston = _interopRequireDefault(require("winston"));
var _channels = require("../model/channels");
var _clients = require("../model/clients");
var authorisation = _interopRequireWildcard(require("./authorisation"));
var utils = _interopRequireWildcard(require("../utils"));
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/*
* Roles is a virtual API; virtual in the sense that it is not linked
* to a concrete roles collection.
*
* Rather it an abstraction of the 'allow' field on Channels and 'roles' on Clients,
* providing a mechanism for setting up allowed permissions.
*/
function filterRolesFromChannels(channels, clients) {
let cl;
let permission;
const rolesMap = {}; // K: permission, V: channels, clients that share permission
for (const ch of Array.from(channels)) {
for (permission of Array.from(ch.allow)) {
let isClient = false;
for (cl of Array.from(clients)) {
if (cl.clientID === permission) {
isClient = true;
}
}
if (!isClient) {
if (!rolesMap[permission]) {
rolesMap[permission] = {
channels: [],
clients: []
};
}
rolesMap[permission].channels.push({
_id: ch._id,
name: ch.name
});
}
}
}
for (cl of Array.from(clients)) {
for (permission of Array.from(cl.roles)) {
if (!rolesMap[permission]) {
rolesMap[permission] = {
channels: [],
clients: []
};
}
rolesMap[permission].clients.push({
_id: cl._id,
clientID: cl.clientID
});
}
}
const rolesArray = [];
for (const role in rolesMap) {
rolesArray.push({
name: role,
channels: rolesMap[role].channels,
clients: rolesMap[role].clients
});
}
return rolesArray;
}
async function getRoles(ctx) {
// Test if the user is authorised
if (!authorisation.inGroup('admin', ctx.authenticated)) {
return utils.logAndSetResponse(ctx, 403, `User ${ctx.authenticated.email} is not an admin, API access to getRoles denied.`, 'info');
}
try {
const channels = await _channels.ChannelModelAPI.find({}, {
name: 1,
allow: 1
});
const clients = await _clients.ClientModelAPI.find({}, {
clientID: 1,
roles: 1
});
ctx.body = filterRolesFromChannels(channels, clients);
} catch (e) {
_winston.default.error(`Could not fetch roles via the API: ${e.message}`);
ctx.message = e.message;
ctx.status = 500;
}
}
async function getRole(ctx, name) {
// Test if the user is authorised
if (!authorisation.inGroup('admin', ctx.authenticated)) {
return utils.logAndSetResponse(ctx, 403, `User ${ctx.authenticated.email} is not an admin, API access to getRole denied.`, 'info');
}
try {
const channels = await _channels.ChannelModelAPI.find({
allow: {
$in: [name]
}
}, {
name: 1
});
const clients = await _clients.ClientModelAPI.find({
roles: {
$in: [name]
}
}, {
clientID: 1
});
if ((channels === null || channels.length === 0) && (clients === null || clients.length === 0)) {
utils.logAndSetResponse(ctx, 404, `Role with name '${name}' could not be found.`, 'info');
} else {
ctx.body = {
name,
channels: channels.map(r => ({
_id: r._id,
name: r.name
})),
clients: clients.map(c => ({
_id: c._id,
clientID: c.clientID
}))
};
}
} catch (e) {
_winston.default.error(`Could not find role with name '${name}' via the API: ${e.message}`);
ctx.body = e.message;
ctx.status = 500;
}
}
function buildFindChannelByIdOrNameCriteria(ctx, role) {
let criteria = {};
const ids = [];
const names = [];
for (const ch of Array.from(role.channels)) {
if (ch._id) {
ids.push(ch._id);
} else if (ch.name) {
names.push(ch.name);
} else {
utils.logAndSetResponse(ctx, 400, '_id and/or name must be specified for a channel', 'info');
return null;
}
}
if (ids.length > 0 && names.length > 0) {
criteria = {
$or: [{
_id: {
$in: ids
}
}, {
name: {
$in: names
}
}]
};
} else {
if (ids.length > 0) {
criteria._id = {
$in: ids
};
}
if (names.length > 0) {
criteria.name = {
$in: names
};
}
}
return criteria;
}
function buildFindClientByIdOrClientIDCriteria(ctx, role) {
let criteria = {};
const ids = [];
const clientIDs = [];
for (const ch of Array.from(role.clients)) {
if (ch._id) {
ids.push(ch._id);
} else if (ch.clientID) {
clientIDs.push(ch.clientID);
} else {
utils.logAndSetResponse(ctx, 400, '_id and/or clientID must be specified for a client', 'info');
return null;
}
}
if (ids.length > 0 && clientIDs.length > 0) {
criteria = {
$or: [{
_id: {
$in: ids
}
}, {
clientID: {
$in: clientIDs
}
}]
};
} else {
if (ids.length > 0) {
criteria._id = {
$in: ids
};
}
if (clientIDs.length > 0) {
criteria.clientID = {
$in: clientIDs
};
}
}
return criteria;
}
async function addRole(ctx) {
// Test if the user is authorised
if (!authorisation.inGroup('admin', ctx.authenticated)) {
return utils.logAndSetResponse(ctx, 403, `User ${ctx.authenticated.email} is not an admin, API access to addRole denied.`, 'info');
}
const role = ctx.request.body;
if (!role.name) {
return utils.logAndSetResponse(ctx, 400, 'Must specify a role name', 'info');
}
const {
clients = [],
channels = []
} = role || {};
if (clients.length === 0 && channels.length === 0) {
return utils.logAndSetResponse(ctx, 400, 'Must specify at least one channel or client to link the role to', 'info');
}
try {
const chResult = await _channels.ChannelModelAPI.find({
allow: {
$in: [role.name]
}
}, {
name: 1
});
const clResult = await _clients.ClientModelAPI.find({
roles: {
$in: [role.name]
}
}, {
clientID: 1
});
if ((chResult != null ? chResult.length : undefined) > 0 || (clResult != null ? clResult.length : undefined) > 0) {
return utils.logAndSetResponse(ctx, 400, `Role with name '${role.name}' already exists.`, 'info');
}
const clientConflict = await _clients.ClientModelAPI.find({
clientID: role.name
}, {
clientID: 1
});
if ((clientConflict != null ? clientConflict.length : undefined) > 0) {
return utils.logAndSetResponse(ctx, 409, `A clientID conflicts with role name '${role.name}'. A role name cannot be the same as a clientID.`, 'info');
}
if (channels.length > 0) {
const chCriteria = buildFindChannelByIdOrNameCriteria(ctx, role);
if (!chCriteria) {
return;
}
await _channels.ChannelModelAPI.updateMany(chCriteria, {
$push: {
allow: role.name
}
});
}
if (clients.length > 0) {
const clCriteria = buildFindClientByIdOrClientIDCriteria(ctx, role);
if (!clCriteria) {
return;
}
await _clients.ClientModelAPI.updateMany(clCriteria, {
$push: {
roles: role.name
}
});
}
_winston.default.info(`User ${ctx.authenticated.email} setup role '${role.name}'`);
ctx.body = 'Role successfully created';
ctx.status = 201;
} catch (e) {
_winston.default.error(`Could not add a role via the API: ${e.message}`);
ctx.body = e.message;
ctx.status = 400;
}
}
async function updateRole(ctx, name) {
// Test if the user is authorised
if (!authorisation.inGroup('admin', ctx.authenticated)) {
return utils.logAndSetResponse(ctx, 403, `User ${ctx.authenticated.email} is not an admin, API access to updateRole denied.`, 'info');
}
const role = ctx.request.body;
const {
channels,
clients
} = role || {};
try {
// request validity checks
const chResult = await _channels.ChannelModelAPI.find({
allow: {
$in: [name]
}
}, {
name: 1
});
const clResult = await _clients.ClientModelAPI.find({
roles: {
$in: [name]
}
}, {
clientID: 1
});
if (chResult.length === 0 && clResult.length === 0) {
return utils.logAndSetResponse(ctx, 404, `Role with name '${name}' could not be found.`, 'info');
}
if (channels != null && channels.length === 0 && clients != null && clients.length === 0) {
return utils.logAndSetResponse(ctx, 400, `Can't have set role '${name}' to have no channels and clients `, 'info');
}
if (clResult.length === 0 && channels != null && channels.length === 0) {
return utils.logAndSetResponse(ctx, 400, `Can't clear channels on '${name}' if it has no clients set`, 'info');
}
if (chResult.length === 0 && clients != null && clients.length === 0) {
return utils.logAndSetResponse(ctx, 400, `Can't clear clients on '${name}' if it has no channels set`, 'info');
}
if (role.name) {
// do check here but only perform rename updates later after channel/client updates
const foundChannels = await _channels.ChannelModelAPI.find({
allow: {
$in: [role.name]
}
}, {
name: 1
});
const foundClients = await _clients.ClientModelAPI.find({
roles: {
$in: [role.name]
}
}, {
name: 1
});
if ((foundChannels != null ? foundChannels.length : undefined) > 0 || (foundClients != null ? foundClients.length : undefined > 0)) {
return utils.logAndSetResponse(ctx, 400, `Role with name '${role.name}' already exists.`, 'info');
}
const clientConflict = await _clients.ClientModelAPI.find({
clientID: role.name
}, {
clientID: 1
});
if (clientConflict != null ? clientConflict.length : undefined > 0) {
return utils.logAndSetResponse(ctx, 409, `A clientID conflicts with role name '${role.name}'. A role name cannot be the same as a clientID.`, 'info');
}
} // TODO : refactor this
if (channels != null) {
const chCriteria = buildFindChannelByIdOrNameCriteria(ctx, role);
if (!chCriteria) {
return;
}
await _channels.ChannelModelAPI.updateMany({}, {
$pull: {
allow: name
}
}); // set role on channels
if (role.channels.length > 0) {
await _channels.ChannelModelAPI.updateMany(chCriteria, {
$push: {
allow: name
}
});
}
}
if (clients) {
const clCriteria = buildFindClientByIdOrClientIDCriteria(ctx, role);
if (!clCriteria) {
return;
} // clear role from existing
await _clients.ClientModelAPI.updateMany({}, {
$pull: {
roles: name
}
}); // set role on clients
if ((role.clients != null ? role.clients.length : undefined) > 0) {
await _clients.ClientModelAPI.updateMany(clCriteria, {
$push: {
roles: name
}
});
}
} // rename role
if (role.name) {
await _channels.ChannelModelAPI.updateMany({
allow: {
$in: [name]
}
}, {
$push: {
allow: role.name
}
});
await _channels.ChannelModelAPI.updateMany({
allow: {
$in: [name]
}
}, {
$pull: {
allow: name
}
});
await _clients.ClientModelAPI.updateMany({
roles: {
$in: [name]
}
}, {
$push: {
roles: role.name
}
});
await _clients.ClientModelAPI.updateMany({
roles: {
$in: [name]
}
}, {
$pull: {
roles: name
}
});
}
_winston.default.info(`User ${ctx.authenticated.email} updated role with name '${name}'`);
ctx.body = 'Successfully updated role';
ctx.status = 200;
} catch (e) {
_winston.default.error(`Could not update role with name '${name}' via the API: ${e.message}`);
ctx.body = e.message;
ctx.status = 500;
}
}
async function deleteRole(ctx, name) {
// Test if the user is authorised
if (!authorisation.inGroup('admin', ctx.authenticated)) {
return utils.logAndSetResponse(ctx, 403, `User ${ctx.authenticated.email} is not an admin, API access to updateRole denied.`, 'info');
}
try {
const channels = await _channels.ChannelModelAPI.find({
allow: {
$in: [name]
}
}, {
name: 1
});
const clients = await _clients.ClientModelAPI.find({
roles: {
$in: [name]
}
}, {
clientID: 1
});
if ((channels === null || channels.length === 0) && (clients === null || clients.length === 0)) {
return utils.logAndSetResponse(ctx, 404, `Role with name '${name}' could not be found.`, 'info');
}
await _channels.ChannelModelAPI.updateMany({}, {
$pull: {
allow: name
}
});
await _clients.ClientModelAPI.updateMany({}, {
$pull: {
roles: name
}
});
_winston.default.info(`User ${ctx.authenticated.email} deleted role with name '${name}'`);
ctx.body = 'Successfully deleted role';
} catch (e) {
_winston.default.error(`Could not update role with name '${name}' via the API: ${e.message}`);
ctx.body = e.message;
ctx.status = 500;
}
}
//# sourceMappingURL=roles.js.map