2013-11-26 22:24:15 +00:00
|
|
|
###
|
|
|
|
|
|
2013-11-28 15:05:47 +00:00
|
|
|
Request Handler
|
2013-11-26 22:24:15 +00:00
|
|
|
============
|
2014-02-19 13:14:08 +00:00
|
|
|
> The request handler (surprisingly) handles requests made through HTTP to
|
|
|
|
|
> the [HTTP Listener](http-listener.html). It will handle user requests for
|
|
|
|
|
> pages as well as POST requests such as user login, module storing, event
|
|
|
|
|
> invocation and also admin commands.
|
2013-11-26 22:24:15 +00:00
|
|
|
|
|
|
|
|
###
|
|
|
|
|
|
2014-02-19 13:14:08 +00:00
|
|
|
# **Loads Modules:**
|
2013-11-28 15:05:47 +00:00
|
|
|
|
2014-02-13 17:16:03 +00:00
|
|
|
# - [Persistence](persistence.html)
|
|
|
|
|
db = require './persistence'
|
2013-11-28 15:05:47 +00:00
|
|
|
|
|
|
|
|
# - Node.js Modules: [fs](http://nodejs.org/api/fs.html),
|
|
|
|
|
# [path](http://nodejs.org/api/path.html) and
|
|
|
|
|
# [querystring](http://nodejs.org/api/querystring.html)
|
|
|
|
|
fs = require 'fs'
|
|
|
|
|
path = require 'path'
|
|
|
|
|
qs = require 'querystring'
|
|
|
|
|
|
|
|
|
|
# - External Modules: [mustache](https://github.com/janl/mustache.js) and
|
|
|
|
|
# [crypto-js](https://github.com/evanvosberg/crypto-js)
|
|
|
|
|
mustache = require 'mustache'
|
|
|
|
|
crypto = require 'crypto-js'
|
2014-04-21 12:42:26 +00:00
|
|
|
|
|
|
|
|
pathUsers = path.resolve __dirname, '..', 'config', 'users.json'
|
|
|
|
|
|
2013-11-29 17:41:55 +00:00
|
|
|
# Prepare the user command handlers which are invoked via HTTP requests.
|
2014-02-21 16:18:58 +00:00
|
|
|
dirHandlers = path.resolve __dirname, '..', 'webpages', 'handlers'
|
2014-02-20 16:22:56 +00:00
|
|
|
exports = module.exports = ( args ) =>
|
2014-04-16 15:42:56 +00:00
|
|
|
@log = args.logger
|
|
|
|
|
|
|
|
|
|
# Register the request service
|
|
|
|
|
@userRequestHandler = args[ 'request-service' ]
|
|
|
|
|
|
|
|
|
|
# Register the shutdown handler to the admin command.
|
|
|
|
|
@objAdminCmds =
|
|
|
|
|
shutdown: ( obj, cb ) ->
|
|
|
|
|
data =
|
|
|
|
|
code: 200
|
|
|
|
|
message: 'Shutting down... BYE!'
|
|
|
|
|
setTimeout args[ 'shutdown-function' ], 500
|
|
|
|
|
cb null, data
|
2014-04-21 12:42:26 +00:00
|
|
|
|
|
|
|
|
newuser: ( obj, cb ) ->
|
|
|
|
|
data =
|
|
|
|
|
code: 200
|
|
|
|
|
message: 'User stored thank you!'
|
|
|
|
|
if obj.username and obj.password
|
|
|
|
|
if obj.roles
|
|
|
|
|
try
|
|
|
|
|
roles = JSON.parse obj.roles
|
|
|
|
|
catch err
|
|
|
|
|
@log 'RH | error parsing newuser roles: ' + err.message
|
|
|
|
|
roles = []
|
|
|
|
|
else
|
|
|
|
|
roles = []
|
|
|
|
|
oUser =
|
|
|
|
|
username: obj.username
|
|
|
|
|
password: obj.password
|
|
|
|
|
roles: roles
|
|
|
|
|
db.storeUser oUser
|
|
|
|
|
|
|
|
|
|
fPersistNewUser = ( username, password, roles ) ->
|
|
|
|
|
( err, data ) ->
|
|
|
|
|
users = JSON.parse data
|
|
|
|
|
users[ username ] =
|
|
|
|
|
password: password
|
|
|
|
|
roles: roles
|
|
|
|
|
fs.writeFile pathUsers, JSON.stringify( users, undefined, 2 ), 'utf8', ( err ) ->
|
|
|
|
|
if err
|
|
|
|
|
@log.error "RH | Unable to write new user file! "
|
|
|
|
|
@log.error err
|
|
|
|
|
# {
|
|
|
|
|
# "admin": {
|
|
|
|
|
# "password": "7407946a7a90b037ba5e825040f184a142161e4c61d81feb83ec8c7f011a99b0d77f39c9170c3231e1003c5cf859c69bd93043b095feff5cce6f6d45ec513764",
|
|
|
|
|
# "roles": ["admin"]
|
|
|
|
|
# }
|
|
|
|
|
# }
|
|
|
|
|
# {"admin":{"password":"7407946a7a90b037ba5e825040f184a142161e4c61d81feb83ec8c7f011a99b0d77f39c9170c3231e1003c5cf859c69bd93043b095feff5cce6f6d45ec513764","roles":["admin"]},"dominic":{"password":"2d51496fbe5b6d3e98e22d68140609eaedd64de457b2f75c346a4a98f87928eac11ea2be747709ae7a2f5b177af09a60a8dbf14bf703e0cb9b147fc0a3e3a064","roles":[]}}
|
|
|
|
|
fs.readFile pathUsers, 'utf8', fPersistNewUser obj.username, obj.password, roles
|
|
|
|
|
else
|
|
|
|
|
data.code = 401
|
|
|
|
|
data.message = 'Missing parameter for this command'
|
|
|
|
|
cb null, data
|
|
|
|
|
|
2014-04-16 15:42:56 +00:00
|
|
|
db args
|
|
|
|
|
|
|
|
|
|
# Load the standard users from the user config file
|
2014-04-21 12:42:26 +00:00
|
|
|
users = JSON.parse fs.readFileSync pathUsers, 'utf8'
|
2014-04-16 15:42:56 +00:00
|
|
|
fStoreUser = ( username, oUser ) ->
|
|
|
|
|
oUser.username = username
|
|
|
|
|
db.storeUser oUser
|
|
|
|
|
fStoreUser user, oUser for user, oUser of users
|
|
|
|
|
module.exports
|
2013-11-26 22:24:15 +00:00
|
|
|
|
|
|
|
|
|
2013-11-28 15:05:47 +00:00
|
|
|
###
|
2013-11-28 18:14:05 +00:00
|
|
|
Handles possible events that were posted to this server and pushes them into the
|
|
|
|
|
event queue.
|
2013-11-28 15:05:47 +00:00
|
|
|
|
|
|
|
|
*Requires
|
|
|
|
|
the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest)
|
|
|
|
|
and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse)
|
|
|
|
|
objects.*
|
|
|
|
|
|
|
|
|
|
@public handleEvent( *req, resp* )
|
|
|
|
|
###
|
2014-02-21 16:18:58 +00:00
|
|
|
exports.handleEvent = ( req, resp ) ->
|
2014-04-16 15:42:56 +00:00
|
|
|
body = ''
|
|
|
|
|
req.on 'data', ( data ) ->
|
|
|
|
|
body += data
|
|
|
|
|
|
|
|
|
|
req.on 'end', ->
|
|
|
|
|
#if req.session and req.session.user
|
|
|
|
|
try
|
|
|
|
|
obj = JSON.parse body
|
|
|
|
|
catch err
|
|
|
|
|
resp.send 400, 'Badly formed event!'
|
|
|
|
|
# If required event properties are present we process the event #
|
|
|
|
|
if obj and obj.event and not err
|
2014-04-19 20:35:05 +00:00
|
|
|
timestamp = ( new Date() ).toISOString()
|
2014-04-16 15:42:56 +00:00
|
|
|
rand = ( Math.floor Math.random() * 10e9 ).toString( 16 ).toUpperCase()
|
2014-04-20 23:34:49 +00:00
|
|
|
obj.eventid = "#{ obj.event }_UTC|#{ timestamp }_#{ rand }"
|
2014-04-16 15:42:56 +00:00
|
|
|
answ =
|
|
|
|
|
code: 200
|
|
|
|
|
message: "Thank you for the event: #{ obj.eventid }"
|
|
|
|
|
resp.send answ.code, answ
|
|
|
|
|
db.pushEvent obj
|
|
|
|
|
else
|
|
|
|
|
resp.send 400, 'Your event was missing important parameters!'
|
|
|
|
|
# else
|
|
|
|
|
# resp.send 401, 'Please login!'
|
2013-11-28 15:05:47 +00:00
|
|
|
|
2013-11-26 22:24:15 +00:00
|
|
|
|
2014-04-15 12:28:40 +00:00
|
|
|
|
2013-11-28 15:05:47 +00:00
|
|
|
###
|
2014-02-21 16:18:58 +00:00
|
|
|
Associates the user object with the session if login is successful.
|
2013-11-28 15:05:47 +00:00
|
|
|
|
|
|
|
|
*Requires
|
|
|
|
|
the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest)
|
|
|
|
|
and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse)
|
|
|
|
|
objects.*
|
2013-11-26 22:24:15 +00:00
|
|
|
|
2013-11-28 15:05:47 +00:00
|
|
|
@public handleLogin( *req, resp* )
|
|
|
|
|
###
|
2014-02-21 16:18:58 +00:00
|
|
|
exports.handleLogin = ( req, resp ) =>
|
2014-04-16 15:42:56 +00:00
|
|
|
body = ''
|
|
|
|
|
req.on 'data', ( data ) -> body += data
|
|
|
|
|
req.on 'end', =>
|
|
|
|
|
obj = JSON.parse body
|
|
|
|
|
db.loginUser obj.username, obj.password, ( err, usr ) =>
|
|
|
|
|
if err
|
|
|
|
|
# Tapping on fingers, at least in log...
|
|
|
|
|
@log.warn "RH | AUTH-UH-OH ( #{ obj.username } ): #{ err.message }"
|
|
|
|
|
else
|
|
|
|
|
# no error, so we can associate the user object from the DB to the session
|
|
|
|
|
req.session.user = usr
|
|
|
|
|
if req.session.user
|
|
|
|
|
resp.send 'OK!'
|
|
|
|
|
else
|
|
|
|
|
resp.send 401, 'NO!'
|
2013-11-28 15:05:47 +00:00
|
|
|
|
|
|
|
|
###
|
|
|
|
|
A post request retrieved on this handler causes the user object to be
|
|
|
|
|
purged from the session, thus the user will be logged out.
|
|
|
|
|
|
|
|
|
|
*Requires
|
|
|
|
|
the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest)
|
|
|
|
|
and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse)
|
|
|
|
|
objects.*
|
|
|
|
|
|
|
|
|
|
@public handleLogout( *req, resp* )
|
|
|
|
|
###
|
|
|
|
|
exports.handleLogout = ( req, resp ) ->
|
2014-04-16 15:42:56 +00:00
|
|
|
if req.session
|
|
|
|
|
req.session.user = null
|
|
|
|
|
resp.send 'Bye!'
|
2013-11-28 15:05:47 +00:00
|
|
|
|
|
|
|
|
|
2013-11-28 18:14:05 +00:00
|
|
|
###
|
|
|
|
|
Resolves the path to a handler webpage.
|
|
|
|
|
|
|
|
|
|
@private getHandlerPath( *name* )
|
|
|
|
|
@param {String} name
|
|
|
|
|
###
|
|
|
|
|
getHandlerPath = ( name ) ->
|
2014-04-16 15:42:56 +00:00
|
|
|
path.join dirHandlers, name + '.html'
|
2013-11-28 15:05:47 +00:00
|
|
|
|
|
|
|
|
###
|
2014-02-21 16:18:58 +00:00
|
|
|
Fetches a template.
|
2013-11-28 15:05:47 +00:00
|
|
|
|
2014-02-21 16:18:58 +00:00
|
|
|
@private getTemplate( *name* )
|
2013-11-28 18:14:05 +00:00
|
|
|
@param {String} name
|
|
|
|
|
###
|
2014-02-21 16:18:58 +00:00
|
|
|
getTemplate = ( name ) ->
|
2014-04-16 15:42:56 +00:00
|
|
|
pth = path.join dirHandlers, 'templates', name + '.html'
|
|
|
|
|
fs.readFileSync pth, 'utf8'
|
|
|
|
|
|
2014-02-10 21:28:10 +00:00
|
|
|
###
|
2014-02-21 16:18:58 +00:00
|
|
|
Fetches a script.
|
2014-02-10 21:28:10 +00:00
|
|
|
|
2014-02-21 16:18:58 +00:00
|
|
|
@private getScript( *name* )
|
2014-02-10 21:28:10 +00:00
|
|
|
@param {String} name
|
|
|
|
|
###
|
2014-02-21 16:18:58 +00:00
|
|
|
getScript = ( name ) ->
|
2014-04-16 15:42:56 +00:00
|
|
|
pth = path.join dirHandlers, 'js', name + '.js'
|
|
|
|
|
fs.readFileSync pth, 'utf8'
|
|
|
|
|
|
2013-11-28 18:14:05 +00:00
|
|
|
###
|
2014-02-21 16:18:58 +00:00
|
|
|
Fetches remote scripts snippets.
|
2013-11-29 17:41:55 +00:00
|
|
|
|
2014-02-21 16:18:58 +00:00
|
|
|
@private getRemoteScripts( *name* )
|
2013-11-29 17:41:55 +00:00
|
|
|
@param {String} name
|
|
|
|
|
###
|
2014-02-21 16:18:58 +00:00
|
|
|
getRemoteScripts = ( name ) ->
|
2014-04-16 15:42:56 +00:00
|
|
|
pth = path.join dirHandlers, 'remote-scripts', name + '.html'
|
|
|
|
|
fs.readFileSync pth, 'utf8'
|
|
|
|
|
|
2013-11-29 17:41:55 +00:00
|
|
|
###
|
2014-02-21 16:18:58 +00:00
|
|
|
Renders a page, with helps of mustache, depending on the user session and returns it.
|
2013-11-29 17:41:55 +00:00
|
|
|
|
2014-02-21 16:18:58 +00:00
|
|
|
@private renderPage( *name, sess, msg* )
|
|
|
|
|
@param {String} name
|
|
|
|
|
@param {Object} sess
|
|
|
|
|
@param {Object} msg
|
2013-11-29 17:41:55 +00:00
|
|
|
###
|
2014-02-21 16:18:58 +00:00
|
|
|
renderPage = ( name, req, resp, msg ) ->
|
2014-04-16 15:42:56 +00:00
|
|
|
# Grab the skeleton
|
|
|
|
|
pathSkel = path.join dirHandlers, 'skeleton.html'
|
|
|
|
|
skeleton = fs.readFileSync pathSkel, 'utf8'
|
|
|
|
|
code = 200
|
|
|
|
|
data =
|
|
|
|
|
message: msg
|
|
|
|
|
user: req.session.user
|
|
|
|
|
|
|
|
|
|
# Try to grab the script belonging to this page. But don't bother if it's not existing
|
|
|
|
|
try
|
|
|
|
|
script = getScript name
|
|
|
|
|
# Try to grab the remote scripts belonging to this page. But don't bother if it's not existing
|
|
|
|
|
try
|
|
|
|
|
remote_scripts = getRemoteScripts name
|
|
|
|
|
|
|
|
|
|
# Now try to find the page the user requested.
|
|
|
|
|
try
|
|
|
|
|
content = getTemplate name
|
|
|
|
|
catch err
|
|
|
|
|
# If the page doesn't exist we return the error page, load the error script into it
|
|
|
|
|
# and render the error page with some additional data
|
|
|
|
|
content = getTemplate 'error'
|
|
|
|
|
script = getScript 'error'
|
|
|
|
|
code = 404
|
|
|
|
|
data.message = 'Invalid Page!'
|
|
|
|
|
|
|
|
|
|
if req.session.user
|
|
|
|
|
menubar = getTemplate 'menubar'
|
|
|
|
|
|
|
|
|
|
pageElements =
|
|
|
|
|
content: content
|
|
|
|
|
script: script
|
|
|
|
|
remote_scripts: remote_scripts
|
|
|
|
|
menubar: menubar
|
|
|
|
|
|
|
|
|
|
# First we render the page by including all page elements into the skeleton
|
|
|
|
|
page = mustache.render skeleton, pageElements
|
|
|
|
|
|
|
|
|
|
# Then we complete the rendering by adding the data, and send the result to the user
|
|
|
|
|
resp.send code, mustache.render page, data
|
2013-11-29 17:41:55 +00:00
|
|
|
|
|
|
|
|
###
|
2014-02-21 16:18:58 +00:00
|
|
|
Present the desired forge page to the user.
|
2013-11-29 17:41:55 +00:00
|
|
|
|
|
|
|
|
*Requires
|
|
|
|
|
the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest)
|
|
|
|
|
and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse)
|
|
|
|
|
objects.*
|
|
|
|
|
|
2014-02-21 16:18:58 +00:00
|
|
|
@public handleForge( *req, resp* )
|
2013-11-29 17:41:55 +00:00
|
|
|
###
|
2014-02-21 16:18:58 +00:00
|
|
|
exports.handleForge = ( req, resp ) ->
|
2014-04-16 15:42:56 +00:00
|
|
|
page = req.query.page
|
|
|
|
|
if not req.session.user
|
|
|
|
|
page = 'login'
|
|
|
|
|
renderPage page, req, resp
|
2013-11-29 17:41:55 +00:00
|
|
|
|
2013-12-08 21:59:04 +00:00
|
|
|
###
|
|
|
|
|
Handles the user command requests.
|
|
|
|
|
|
|
|
|
|
*Requires
|
|
|
|
|
the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest)
|
|
|
|
|
and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse)
|
|
|
|
|
objects.*
|
|
|
|
|
|
|
|
|
|
@public handleUser( *req, resp* )
|
|
|
|
|
###
|
2014-02-21 16:18:58 +00:00
|
|
|
exports.handleUserCommand = ( req, resp ) =>
|
2014-04-16 15:42:56 +00:00
|
|
|
if req.session and req.session.user
|
|
|
|
|
body = ''
|
|
|
|
|
#Append data to body while receiving fragments
|
|
|
|
|
req.on 'data', ( data ) ->
|
|
|
|
|
body += data
|
|
|
|
|
req.on 'end', =>
|
|
|
|
|
obj = qs.parse body
|
|
|
|
|
# Let the user request handler service answer the request
|
|
|
|
|
@userRequestHandler req.session.user, obj, ( obj ) ->
|
|
|
|
|
resp.send obj.code, obj
|
|
|
|
|
else
|
|
|
|
|
resp.send 401, 'Login first!'
|
2014-02-23 23:59:49 +00:00
|
|
|
|
2014-02-21 16:18:58 +00:00
|
|
|
|
|
|
|
|
###
|
|
|
|
|
Present the admin console to the user if he's allowed to see it.
|
|
|
|
|
|
|
|
|
|
*Requires
|
|
|
|
|
the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest)
|
|
|
|
|
and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse)
|
|
|
|
|
objects.*
|
|
|
|
|
|
|
|
|
|
@public handleForge( *req, resp* )
|
|
|
|
|
###
|
|
|
|
|
exports.handleAdmin = ( req, resp ) ->
|
2014-04-16 15:42:56 +00:00
|
|
|
if not req.session.user
|
|
|
|
|
page = 'login'
|
|
|
|
|
#TODO isAdmin should come from the db role
|
2014-04-21 12:42:26 +00:00
|
|
|
else if req.session.user.roles.indexOf( "admin" ) is -1
|
2014-04-16 15:42:56 +00:00
|
|
|
page = 'login'
|
2014-04-21 12:42:26 +00:00
|
|
|
msg = 'You need to be admin for this page!'
|
2014-04-16 15:42:56 +00:00
|
|
|
else
|
|
|
|
|
page = 'admin'
|
|
|
|
|
renderPage page, req, resp, msg
|
2013-12-08 21:59:04 +00:00
|
|
|
|
2013-11-28 15:05:47 +00:00
|
|
|
###
|
2013-11-29 17:41:55 +00:00
|
|
|
Handles the admin command requests.
|
2013-11-28 15:05:47 +00:00
|
|
|
|
|
|
|
|
*Requires
|
|
|
|
|
the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest)
|
|
|
|
|
and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse)
|
|
|
|
|
objects.*
|
|
|
|
|
|
2014-02-21 16:18:58 +00:00
|
|
|
@public handleAdminCommand( *req, resp* )
|
2013-11-28 15:05:47 +00:00
|
|
|
###
|
2014-02-21 16:18:58 +00:00
|
|
|
exports.handleAdminCommand = ( req, resp ) =>
|
2014-04-16 15:42:56 +00:00
|
|
|
if req.session and
|
2014-04-21 12:42:26 +00:00
|
|
|
req.session.user and
|
|
|
|
|
req.session.user.roles.indexOf( "admin" ) > -1
|
2014-04-16 15:42:56 +00:00
|
|
|
body = ''
|
|
|
|
|
req.on 'data', ( data ) ->
|
|
|
|
|
body += data
|
|
|
|
|
req.on 'end', =>
|
|
|
|
|
obj = qs.parse body
|
|
|
|
|
@log.info 'RH | Received admin request: ' + obj.command
|
2014-04-21 12:42:26 +00:00
|
|
|
arrCmd = obj.command.split( ' ' )
|
|
|
|
|
if not arrCmd[ 0 ] or not @objAdminCmds[ arrCmd[ 0 ] ]
|
2014-04-16 15:42:56 +00:00
|
|
|
resp.send 404, 'Command unknown!'
|
2014-04-21 12:42:26 +00:00
|
|
|
else
|
|
|
|
|
arrParams = arrCmd.slice 1
|
|
|
|
|
oParams = {}
|
|
|
|
|
for keyVal in arrParams
|
|
|
|
|
arrKV = keyVal.split ":"
|
|
|
|
|
if arrKV.length is 2
|
|
|
|
|
oParams[ arrKV[ 0 ] ] = arrKV[ 1 ]
|
|
|
|
|
@objAdminCmds[ arrCmd[ 0 ] ] oParams, ( err, obj ) ->
|
|
|
|
|
resp.send obj.code, obj
|
2014-04-16 15:42:56 +00:00
|
|
|
else
|
|
|
|
|
resp.send 401, 'You need to be logged in as admin!'
|
|
|
|
|
|