UNPKG

openhim-core

Version:

The OpenHIM core application that provides logging and routing of http requests

567 lines (480 loc) 15 kB
"use strict"; 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