UNPKG

openhim-core

Version:

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

360 lines (279 loc) 12.3 kB
User = require('../model/users').User Q = require 'q' logger = require 'winston' authorisation = require './authorisation' moment = require 'moment' randtoken = require 'rand-token' contact = require '../contact' config = require "../config/config" config.newUserExpiry = config.get('newUserExpiry') config.userPasswordResetExpiry = config.get('userPasswordResetExpiry') config.alerts = config.get('alerts') utils = require "../utils" atna = require 'atna-audit' os = require 'os' auditing = require '../auditing' himSourceID = config.get('auditing').auditEvents.auditSourceID ### # Get authentication details ### exports.authenticate = (email) -> email = unescape email try user = yield User.findOne(email: email).exec() if not user utils.logAndSetResponse this, 404, "Could not find user by email #{email}", 'info' # Audit unknown user requested audit = atna.userLoginAudit atna.OUTCOME_SERIOUS_FAILURE, himSourceID, os.hostname(), email audit = atna.wrapInSyslog audit auditing.sendAuditEvent audit, -> logger.debug 'Processed internal audit' else this.body = salt: user.passwordSalt ts: new Date() catch e utils.logAndSetResponse this, 500, "Error during authentication #{e}", 'error' ################################# ### Reset password Functions ### ################################# passwordResetPlainMessageTemplate = (firstname, setPasswordLink) -> """ <---------- Existing User - Reset Password ----------> Hi #{firstname}, A request has been made to reset your password on the OpenHIM instance running on #{config.alerts.himInstance} Follow the below link to reset your password and log into OpenHIM Console #{setPasswordLink} <---------- Existing User - Reset Password ----------> """ passwordResetHtmlMessageTemplate = (firstname, setPasswordLink) -> """ <h1>Reset OpenHIM Password</h1> <p>Hi #{firstname},<br/><br/>A request has been made to reset your password on the OpenHIM instance running on #{config.alerts.himInstance}</p> <p>Follow the below link to set your password and log into OpenHIM Console</p> <p>#{setPasswordLink}</p> """ exports.generateRandomToken = () -> return randtoken.generate 32 ### # update user token/expiry and send new password email ### exports.userPasswordResetRequest = (email) -> email = unescape email if email is 'root@openhim.org' this.body = "Cannot request password reset for 'root@openhim.org'" this.status = 403 return # Generate the new user token here # set expiry date = true token = exports.generateRandomToken() duration = config.userPasswordResetExpiry.duration durationType = config.userPasswordResetExpiry.durationType expiry = moment().add(duration, durationType).utc().format() updateUserTokenExpiry = token: token tokenType: 'existingUser' expiry: expiry try user = yield User.findOneAndUpdate(email: email, updateUserTokenExpiry).exec() if not user this.body = "Tried to request password reset for invalid email address: #{email}" this.status = 404 logger.info "Tried to request password reset for invalid email address: #{email}" return consoleURL = config.alerts.consoleURL setPasswordLink = "#{consoleURL}/#/set-password/#{token}" # Send email to user to reset password plainMessage = passwordResetPlainMessageTemplate user.firstname, setPasswordLink htmlMessage = passwordResetHtmlMessageTemplate user.firstname, setPasswordLink sendEmail = Q.denodeify contact.contactUser sendEmailError = yield sendEmail 'email', email, 'OpenHIM Console Password Reset', plainMessage, htmlMessage if sendEmailError utils.logAndSetResponse this, 500, "Could not send email to user via the API #{e}", 'error' logger.info 'The email has been sent to the user' this.body = "Successfully set user token/expiry for password reset." this.status = 201 logger.info "User updated token/expiry for password reset #{email}" catch e utils.logAndSetResponse this, 500, "Could not update user with email #{email} via the API #{e}", 'error' ####################################### ### New User Set Password Functions ### ####################################### # get the new user details exports.getUserByToken = (token) -> token = unescape token try projectionRestriction = "email": 1, "firstname": 1, "surname": 1, "msisdn": 1, "token": 1, "tokenType": 1, "locked": 1, "expiry": 1, "_id": 0 result = yield User.findOne(token: token, projectionRestriction).exec() if not result this.body = "User with token #{token} could not be found." this.status = 404 else # if expiry date has past if moment(result.expiry).isBefore(moment()) # user- set password - expired this.body = "Token #{token} has expired" this.status = 410 else this.body = result catch e utils.logAndSetResponse this, 500, "Could not find user with token #{token} via the API #{e}", 'error' # update the password/details for the new user exports.updateUserByToken = (token) -> token = unescape token userData = this.request.body try # first try get new user details to check expiry date userDataExpiry = yield User.findOne(token: token).exec() if not userDataExpiry this.body = "User with token #{token} could not be found." this.status = 404 return else # if expiry date has past if moment(userDataExpiry.expiry).isBefore(moment()) # new user- set password - expired this.body = "User with token #{token} has expired to set their password." this.status = 410 return catch e utils.logAndSetResponse this, 500, "Could not find user with token #{token} via the API #{e}", 'error' return # check to make sure 'msisdn' isnt 'undefined' when saving if userData.msisdn then msisdn = userData.msisdn else msisdn = null # construct user object to prevent other properties from being updated userUpdateObj = token: null tokenType: null expiry: null passwordAlgorithm: userData.passwordAlgorithm passwordSalt: userData.passwordSalt passwordHash: userData.passwordHash if userDataExpiry.tokenType is 'newUser' userUpdateObj.firstname = userData.firstname userUpdateObj.surname = userData.surname userUpdateObj.locked = false userUpdateObj.msisdn = msisdn try yield User.findOneAndUpdate(token: token, userUpdateObj).exec() this.body = "Successfully set new user password." logger.info "User updated by token #{token}" catch e utils.logAndSetResponse this, 500, "Could not update user with token #{token} via the API #{e}", 'error' ####################################### ### New User Set Password Functions ### ####################################### plainMessageTemplate = (firstname, setPasswordLink) -> """ <---------- New User - Set Password ----------> Hi #{firstname}, A profile has been created for you on the OpenHIM instance running on #{config.alerts.himInstance} Follow the below link to set your password and log into OpenHIM Console #{setPasswordLink} <---------- New User - Set Password ----------> """ htmlMessageTemplate = (firstname, setPasswordLink) -> """ <h1>New OpenHIM Profile</h1> <p>Hi #{firstname},<br/><br/>A profile has been created for you on the OpenHIM instance running on #{config.alerts.himInstance}</p> <p>Follow the below link to set your password and log into OpenHIM Console</p> <p>#{setPasswordLink}</p> """ ### # Adds a user ### exports.addUser = -> # Test if the user is authorised if not authorisation.inGroup 'admin', this.authenticated utils.logAndSetResponse this, 403, "User #{this.authenticated.email} is not an admin, API access to addUser denied.", 'info' return userData = this.request.body # Generate the new user token here # set locked = true # set expiry date = true token = randtoken.generate 32 userData.token = token userData.tokenType = 'newUser' userData.locked = true duration = config.newUserExpiry.duration durationType = config.newUserExpiry.durationType userData.expiry = moment().add(duration, durationType).utc().format() consoleURL = config.alerts.consoleURL setPasswordLink = "#{consoleURL}/#/set-password/#{token}" try user = new User userData result = yield Q.ninvoke user, 'save' # Send email to new user to set password plainMessage = plainMessageTemplate userData.firstname, setPasswordLink htmlMessage = htmlMessageTemplate userData.firstname, setPasswordLink contact.contactUser 'email', userData.email, 'OpenHIM Console Profile', plainMessage, htmlMessage, (err) -> if err logger.error "The email could not be sent to the user via the API #{err}" else logger.info 'The email has been sent to the new user' this.body = 'User successfully created' this.status = 201 logger.info "User #{this.authenticated.email} created user #{userData.email}" catch e utils.logAndSetResponse this, 500, "Could not add user via the API #{e}", 'error' ### # Retrieves the details of a specific user ### exports.getUser = (email) -> email = unescape email # Test if the user is authorised, allow a user to fetch their own details if not authorisation.inGroup('admin', this.authenticated) and this.authenticated.email isnt email utils.logAndSetResponse this, 403, "User #{this.authenticated.email} is not an admin, API access to getUser denied.", 'info' return try result = yield User.findOne(email: email).exec() if not result this.body = "User with email #{email} could not be found." this.status = 404 else this.body = result catch e utils.logAndSetResponse this, 500, "Could not get user via the API #{e}", 'error' exports.updateUser = (email) -> email = unescape email # Test if the user is authorised, allow a user to update their own details if not authorisation.inGroup('admin', this.authenticated) and this.authenticated.email isnt email utils.logAndSetResponse this, 403, "User #{this.authenticated.email} is not an admin, API access to updateUser denied.", 'info' return userData = this.request.body # reset token/locked/expiry when user is updated and password supplied if userData.passwordAlgorithm and userData.passwordHash and userData.passwordSalt userData.token = null userData.tokenType = null userData.locked = false userData.expiry = null # Don't allow a non-admin user to change their groups if this.authenticated.email is email and not authorisation.inGroup 'admin', this.authenticated then delete userData.groups #Ignore _id if it exists (update is by email) if userData._id then delete userData._id try yield User.findOneAndUpdate(email: email, userData).exec() this.body = "Successfully updated user." logger.info "User #{this.authenticated.email} updated user #{userData.email}" catch e utils.logAndSetResponse this, 500, "Could not update user #{email} via the API #{e}", 'error' exports.removeUser = (email) -> # Test if the user is authorised if not authorisation.inGroup 'admin', this.authenticated utils.logAndSetResponse this, 403, "User #{this.authenticated.email} is not an admin, API access to removeUser denied.", 'info' return email = unescape email # Test if the user is root@openhim.org if email is 'root@openhim.org' utils.logAndSetResponse this, 403, "User root@openhim.org is OpenHIM root, User cannot be deleted through the API", 'info' return try yield User.findOneAndRemove(email: email).exec() this.body = "Successfully removed user with email #{email}" logger.info "User #{this.authenticated.email} removed user #{email}" catch e utils.logAndSetResponse this, 500, "Could not remove user #{email} via the API #{e}", 'error' exports.getUsers = -> # Test if the user is authorised if not authorisation.inGroup 'admin', this.authenticated utils.logAndSetResponse this, 403, "User #{this.authenticated.email} is not an admin, API access to getUsers denied.", 'info' return try this.body = yield User.find().exec() catch e utils.logAndSetResponse this, 500, "Could not fetch all users via the API #{e}", 'error'