openhim-core
Version:
The OpenHIM core application that provides logging and routing of http requests
265 lines (218 loc) • 9.51 kB
text/coffeescript
Channel = require('../model/channels').Channel
Client = require('../model/clients').Client
logger = require 'winston'
authorisation = require './authorisation'
utils = require '../utils'
###
# 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.
###
filterRolesFromChannels = (channels, clients) ->
rolesMap = {} # K: permission, V: channels, clients that share permission
for ch in channels
for permission in ch.allow
isClient = false
for cl in clients
if cl.clientID is permission
isClient = true
if not isClient
if not rolesMap[permission]
rolesMap[permission] =
channels: []
clients: []
rolesMap[permission].channels.push _id: ch._id, name: ch.name
for cl in clients
for permission in cl.roles
if not rolesMap[permission]
rolesMap[permission] =
channels: []
clients: []
rolesMap[permission].clients.push _id: cl._id, clientID: cl.clientID
rolesArray = []
for role of rolesMap
rolesArray.push
name: role
channels: rolesMap[role].channels
clients: rolesMap[role].clients
return rolesArray
exports.getRoles = ->
# Test if the user is authorised
if not authorisation.inGroup 'admin', this.authenticated
return utils.logAndSetResponse this, 403, "User #{this.authenticated.email} is not an admin, API access to getRoles denied.", 'info'
try
channels = yield Channel.find({}, {name: 1, allow: 1 }).exec()
clients = yield Client.find({}, {clientID: 1, roles: 1 }).exec()
this.body = filterRolesFromChannels channels, clients
catch e
logger.error "Could not fetch roles via the API: #{e.message}"
this.message = e.message
this.status = 500
exports.getRole = (name) ->
# Test if the user is authorised
if not authorisation.inGroup 'admin', this.authenticated
return utils.logAndSetResponse this, 403, "User #{this.authenticated.email} is not an admin, API access to getRole denied.", 'info'
try
channels = yield Channel.find({allow: {$in: [name]}}, {name: 1 }).exec()
clients = yield Client.find({ roles: $in: [name]}, {clientID: 1 }).exec()
if (channels is null or channels.length is 0) and (clients is null or clients.length is 0)
utils.logAndSetResponse this, 404, "Role with name '#{name}' could not be found.", 'info'
else
this.body =
name: name
channels: channels.map (r) -> _id: r._id, name: r.name
clients: clients.map (c) -> _id: c._id, clientID: c.clientID
catch e
logger.error "Could not find role with name '#{name}' via the API: #{e.message}"
this.body = e.message
this.status = 500
buildFindChannelByIdOrNameCriteria = (ctx, role) ->
criteria = {}
ids = []
names = []
for ch in 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 and 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
buildFindClientByIdOrClientIDCriteria = (ctx, role) ->
criteria = {}
ids = []
clientIDs = []
for ch in 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 and 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
exports.addRole = ->
# Test if the user is authorised
if not authorisation.inGroup 'admin', this.authenticated
return utils.logAndSetResponse this, 403, "User #{this.authenticated.email} is not an admin, API access to addRole denied.", 'info'
role = this.request.body
if not role.name
return utils.logAndSetResponse this, 400, 'Must specify a role name', 'info'
if role.channels?.length is 0 and role.clients?.length is 0
return utils.logAndSetResponse this, 400, 'Must specify at least one channel or client to link the role to', 'info'
try
chResult = yield Channel.find({allow: {$in: [role.name]}}, {name: 1 }).exec()
clResult = yield Client.find({roles: {$in: [role.name]}}, {clientID: 1 }).exec()
if chResult?.length > 0 or clResults?.length > 0
return utils.logAndSetResponse this, 400, "Role with name '#{role.name}' already exists.", 'info'
if role.channels
chCriteria = buildFindChannelByIdOrNameCriteria this, role
return if not chCriteria
if role.clients
clCriteria = buildFindClientByIdOrClientIDCriteria this, role
return if not clCriteria
if role.channels
yield Channel.update(chCriteria, { $push: allow: role.name }, { multi: true }).exec()
if role.clients
yield Client.update(clCriteria, { $push: roles: role.name }, { multi: true }).exec()
logger.info "User #{this.authenticated.email} setup role '#{role.name}'"
this.body = 'Role successfully created'
this.status = 201
catch e
logger.error "Could not add a role via the API: #{e.message}"
this.body = e.message
this.status = 400
exports.updateRole = (name) ->
# Test if the user is authorised
if not authorisation.inGroup 'admin', this.authenticated
return utils.logAndSetResponse this, 403, "User #{this.authenticated.email} is not an admin, API access to updateRole denied.", 'info'
role = this.request.body
try
# request validity checks
chResult = yield Channel.find({allow: {$in: [name]}}, {name: 1 }).exec()
clResult = yield Client.find({roles: {$in: [name]}}, {clientID: 1 }).exec()
if (chResult is null or chResult.length is 0) and (clResult is null or clResult.length is 0)
return utils.logAndSetResponse this, 404, "Role with name '#{name}' could not be found.", 'info'
if role.name
# do check here but only perform rename updates later after channel/client updates
channels = yield Channel.find({allow: {$in: [role.name]}}, {name: 1 }).exec()
clients = yield Client.find({roles: {$in: [role.name]}}, {name: 1 }).exec()
if channels?.length > 0 or clients?.length > 0
return utils.logAndSetResponse this, 400, "Role with name '#{role.name}' already exists.", 'info'
if role.channels
chCriteria = buildFindChannelByIdOrNameCriteria this, role
return if not chCriteria
if role.clients
clCriteria = buildFindClientByIdOrClientIDCriteria this, role
return if not clCriteria
# update channels
if role.channels
# clear role from existing
yield Channel.update({}, { $pull: allow: name }, { multi: true }).exec()
# set role on channels
if role.channels.length > 0
yield Channel.update(chCriteria, { $push: allow: name }, { multi: true }).exec()
# update clients
if role.clients
# clear role from existing
yield Client.update({}, { $pull: roles: name }, { multi: true }).exec()
# set role on clients
if role.clients?.length > 0
yield Client.update(clCriteria, { $push: roles: name }, { multi: true }).exec()
# rename role
if role.name
yield Channel.update({ allow: $in: [name] }, { $push: allow: role.name }, { multi: true }).exec()
yield Channel.update({ allow: $in: [name] }, { $pull: allow: name }, { multi: true }).exec()
yield Client.update({ roles: $in: [name] }, { $push: roles: role.name }, { multi: true }).exec()
yield Client.update({ roles: $in: [name] }, { $pull: roles: name }, { multi: true }).exec()
logger.info "User #{this.authenticated.email} updated role with name '#{name}'"
this.body = 'Successfully updated role'
this.status = 200
catch e
logger.error "Could not update role with name '#{name}' via the API: #{e.message}"
this.body = e.message
this.status = 500
exports.deleteRole = (name) ->
# Test if the user is authorised
if not authorisation.inGroup 'admin', this.authenticated
return utils.logAndSetResponse this, 403, "User #{this.authenticated.email} is not an admin, API access to updateRole denied.", 'info'
try
channels = yield Channel.find({allow: {$in: [name]}}, {name: 1 }).exec()
clients = yield Client.find({ roles: $in: [name]}, {clientID: 1 }).exec()
if (channels is null or channels.length is 0) and (clients is null or clients.length is 0)
return utils.logAndSetResponse this, 404, "Role with name '#{name}' could not be found.", 'info'
yield Channel.update({}, { $pull: allow: name }, { multi: true }).exec()
yield Client.update({}, { $pull: roles: name }, { multi: true }).exec()
logger.info "User #{this.authenticated.email} deleted role with name '#{name}'"
this.body = 'Successfully deleted role'
catch e
logger.error "Could not update role with name '#{name}' via the API: #{e.message}"
this.body = e.message
this.status = 500