mirror of
https://github.com/Hopiu/webapi-eca.git
synced 2026-03-16 22:10:31 +00:00
Login functionality successfully implemented,
switched crypto to crypto-js from google. login.html form loads crypto-js directly from google codebase in order to hash passwords before sending them to the server
This commit is contained in:
parent
8192eee64d
commit
32f5553471
21 changed files with 767 additions and 446 deletions
27
README.md
27
README.md
|
|
@ -1,27 +1,28 @@
|
|||
README: webapi-eca
|
||||
==================
|
||||
|
||||
# TODO Remake
|
||||
|
||||
>A Modular ECA Engine Server which acts as a middleware between WebAPI's.
|
||||
>This folder continues examples of an ECA engine and how certain use cases could be implemented together with a rules language.
|
||||
>Be sure the user which runs the server doesn't have ANY write rights on the server!
|
||||
>Malicious modules could capture or destroy your server!
|
||||
>
|
||||
>
|
||||
>The server is started through the [rules_server.js](rules_server.html) module by calling `node rule_server.js`.
|
||||
>The server is started through the [server.js](server.html) module by calling `node rule_server.js`.
|
||||
|
||||
|
||||
Getting started
|
||||
---------------
|
||||
Prerequisites:
|
||||
|
||||
- node.js & npm (find it [here](http://nodejs.org/))
|
||||
- *(optional) coffee, if you want to compile from coffee sources:*
|
||||
**Prerequisites:**
|
||||
|
||||
- node.js (find it [here](http://nodejs.org/))
|
||||
- *(optional) [CoffeeScript](http://coffeescript.org/), if you want to compile from coffee sources:*
|
||||
|
||||
sudo npm -g install coffee-script
|
||||
sudo npm -g install coffee-script
|
||||
|
||||
Clone project:
|
||||
|
||||
git clone https://github.com/dominicbosch/webapi-eca.git
|
||||
git clone https://github.com/dominicbosch/webapi-eca.git
|
||||
|
||||
Download and install dependencies:
|
||||
|
||||
|
|
@ -33,7 +34,7 @@ Get your [redis](http://redis.io/) instance up and running (and find the port fo
|
|||
Edit the configuration file:
|
||||
|
||||
vi config/config.json
|
||||
|
||||
|
||||
Apply your settings, for example:
|
||||
|
||||
{
|
||||
|
|
@ -49,15 +50,17 @@ Start the server:
|
|||
|
||||
*Congratulations, your own WebAPI based ECA engine server is now up and running!*
|
||||
|
||||
|
||||
Optional command line tools:
|
||||
----------------------------
|
||||
Run test suite:
|
||||
|
||||
node run_tests
|
||||
|
||||
Create the doc *(to be accessed via the webserver, e.g.: localhost:8125/doc/)*:
|
||||
|
||||
node create_doc
|
||||
|
||||
Run test suite:
|
||||
|
||||
node run_tests
|
||||
|
||||
_
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,20 @@
|
|||
###
|
||||
|
||||
Config
|
||||
======
|
||||
Configuration
|
||||
=============
|
||||
> Loads the configuration file and acts as an interface to it.
|
||||
|
||||
###
|
||||
|
||||
# **Requires:**
|
||||
|
||||
# - [Logging](logging.html)
|
||||
log = require './logging'
|
||||
|
||||
# - Node.js Modules: [fs](http://nodejs.org/api/fs.html) and
|
||||
# [path](http://nodejs.org/api/path.html)
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
# Requires:
|
||||
|
||||
# - The [Logging](logging.html) module
|
||||
log = require './logging'
|
||||
|
||||
###
|
||||
##Module call
|
||||
|
|
@ -24,7 +27,7 @@ exports = module.exports = ( args ) ->
|
|||
args = args ? {}
|
||||
log args
|
||||
if typeof args.relPath is 'string'
|
||||
loadConfigFiles args.relPath
|
||||
loadConfigFile args.relPath
|
||||
module.exports
|
||||
|
||||
###
|
||||
|
|
@ -61,35 +64,35 @@ Fetch a property from the configuration
|
|||
fetchProp = ( prop ) => @config?[prop]
|
||||
|
||||
###
|
||||
Answer true if the config file is ready, else false
|
||||
***Returns*** true if the config file is ready, else false
|
||||
|
||||
@public isReady()
|
||||
###
|
||||
exports.isReady = => @config?
|
||||
|
||||
###
|
||||
Returns the HTTP port
|
||||
***Returns*** the HTTP port
|
||||
|
||||
@public getHttpPort()
|
||||
###
|
||||
exports.getHttpPort = -> fetchProp 'http_port'
|
||||
|
||||
###
|
||||
Returns the DB port
|
||||
***Returns*** the DB port*
|
||||
|
||||
@public getDBPort()
|
||||
###
|
||||
exports.getDBPort = -> fetchProp 'db_port'
|
||||
|
||||
###
|
||||
Returns the crypto key
|
||||
***Returns*** the crypto key
|
||||
|
||||
@public getCryptoKey()
|
||||
###
|
||||
exports.getCryptoKey = -> fetchProp 'crypto_key'
|
||||
|
||||
###
|
||||
Returns the session secret
|
||||
***Returns*** the session secret
|
||||
|
||||
@public getSessionSecret()
|
||||
###
|
||||
|
|
|
|||
|
|
@ -19,14 +19,15 @@ DB Interface
|
|||
|
||||
###
|
||||
|
||||
redis = require 'redis'
|
||||
crypto = require 'crypto' # TODO change to Google's "crypto-js""
|
||||
# **Requires:**
|
||||
|
||||
# Requires:
|
||||
|
||||
# - The [Logging](logging.html) module
|
||||
# - [Logging](logging.html)
|
||||
log = require './logging'
|
||||
|
||||
# - External Modules: [crypto-js](https://github.com/evanvosberg/crypto-js) and
|
||||
# [redis](https://github.com/mranney/node_redis)
|
||||
crypto = require 'crypto-js'
|
||||
redis = require 'redis'
|
||||
|
||||
###
|
||||
Module call
|
||||
|
|
@ -54,7 +55,7 @@ ten attempts within five seconds, or nothing on success to the callback(err).
|
|||
@public isConnected( *cb* )
|
||||
@param {function} cb
|
||||
###
|
||||
#}TODO check if timeout works with func in func
|
||||
#TODO check if timeout works with func in func
|
||||
exports.isConnected = ( cb ) =>
|
||||
if @db.connected then cb()
|
||||
else
|
||||
|
|
@ -72,6 +73,22 @@ exports.isConnected = ( cb ) =>
|
|||
setTimeout fCheckConnection, 500
|
||||
|
||||
|
||||
###
|
||||
Hashes a string based on SHA-3-512.
|
||||
|
||||
@private hash( *plainText* )
|
||||
@param {String} plainText
|
||||
###
|
||||
hash = ( plainText ) =>
|
||||
if !plainText? then return null
|
||||
try
|
||||
(crypto.SHA3 plainText, { outputLength: 512 }).toString()
|
||||
catch err
|
||||
err.addInfo = 'during hashing'
|
||||
log.error 'DB', err
|
||||
null
|
||||
|
||||
|
||||
###
|
||||
Encrypts a string using the crypto key from the config file, based on aes-256-cbc.
|
||||
|
||||
|
|
@ -140,7 +157,7 @@ getSetRecords = ( set, fSingle, cb ) =>
|
|||
else
|
||||
semaphore = arrReply.length
|
||||
objReplies = {}
|
||||
# } TODO What if the DB needs longer than two seconds to respond?...
|
||||
#TODO What if the DB needs longer than two seconds to respond?...
|
||||
setTimeout ->
|
||||
if semaphore > 0
|
||||
cb new Error('Timeout fetching ' + set)
|
||||
|
|
@ -206,7 +223,7 @@ Store a string representation of the authentication parameters for an action mod
|
|||
###
|
||||
exports.storeActionAuth = ( userId, moduleId, data ) =>
|
||||
log.print 'DB', 'storeActionAuth: ' + userId + ':' + moduleId
|
||||
@db.set 'action-auth:' + userId + ':' + moduleId, encrypt(data),
|
||||
@db.set 'action-auth:' + userId + ':' + moduleId, hash(data),
|
||||
replyHandler 'storing action auth ' + userId + ':' + moduleId
|
||||
|
||||
###
|
||||
|
|
@ -270,7 +287,7 @@ Store a string representation of he authentication parameters for an event modul
|
|||
###
|
||||
exports.storeEventAuth = ( userId, moduleId, data ) =>
|
||||
log.print 'DB', 'storeEventAuth: ' + userId + ':' + moduleId
|
||||
@db.set 'event-auth:' + userId + ':' + moduleId, encrypt(data),
|
||||
@db.set 'event-auth:' + userId + ':' + moduleId, hash(data),
|
||||
replyHandler 'storing event auth ' + userId + ':' + moduleId
|
||||
|
||||
###
|
||||
|
|
@ -331,12 +348,12 @@ Store a user object (needs to be a flat structure).
|
|||
@param {Object} objUser
|
||||
###
|
||||
exports.storeUser = ( objUser ) =>
|
||||
# TODO Only store user if not already existing, or at least only then add a private key
|
||||
# for his encryption. we would want to have one private key per user, right?
|
||||
#TODO Only store user if not already existing, or at least only then add a private key
|
||||
#for his encryption. we would want to have one private key per user, right?
|
||||
log.print 'DB', 'storeUser: ' + objUser.username
|
||||
if objUser and objUser.username and objUser.password
|
||||
@db.sadd 'users', objUser.username, replyHandler 'storing user key ' + objUser.username
|
||||
objUser.password = encrypt objUser.password
|
||||
objUser.password = hash objUser.password
|
||||
@db.hmset 'user:' + objUser.username, objUser, replyHandler 'storing user properties ' + objUser.username
|
||||
else
|
||||
log.error 'DB', new Error 'username or password was missing'
|
||||
|
|
@ -374,14 +391,17 @@ exports.getRoleUsers = ( role ) =>
|
|||
@db.get 'role-users:' + role, cb
|
||||
|
||||
###
|
||||
Checks the credentials and on success returns the user object to the callback(err, obj) function.
|
||||
Checks the credentials and on success returns the user object to the
|
||||
callback(err, obj) function. The password has to be hashed (SHA-3-512)
|
||||
beforehand by the instance closest to the user that enters the password,
|
||||
because we only store hashes of passwords for safety reasons.
|
||||
|
||||
@public loginUser( *username, password, cb* )
|
||||
@param {String} username
|
||||
@param {String} password
|
||||
@param {function} cb
|
||||
###
|
||||
# TODO verify and test whole function
|
||||
#TODO verify and test whole function
|
||||
exports.loginUser = ( username, password, cb ) =>
|
||||
log.print 'DB', 'User "' + username + '" tries to log in'
|
||||
fCheck = ( pw ) ->
|
||||
|
|
@ -389,16 +409,16 @@ exports.loginUser = ( username, password, cb ) =>
|
|||
if err
|
||||
cb err
|
||||
else if obj and obj.password
|
||||
if encrypt(pw) == obj.password
|
||||
if pw == obj.password
|
||||
log.print 'DB', 'User "' + obj.username + '" logged in!'
|
||||
cb null, obj
|
||||
else
|
||||
cb new Error 'Wrong credentials!'
|
||||
else
|
||||
cb new Error 'Empty arguments!'
|
||||
cb new Error 'User not found!'
|
||||
@db.hgetall 'user:' + username, fCheck password
|
||||
|
||||
# TODO implement functions required for user sessions and the rule activation
|
||||
#TODO implement functions required for user sessions and the rule activation
|
||||
|
||||
###
|
||||
Shuts down the db link.
|
||||
|
|
|
|||
|
|
@ -2,54 +2,78 @@
|
|||
|
||||
HTTP Listener
|
||||
=============
|
||||
> Handles the HTTP requests to the server at the port specified by the
|
||||
> [config](config.html) file.
|
||||
> Receives the HTTP requests to the server at the port specified by the
|
||||
> [config](config.html) file. These requests (bound to a method) are then
|
||||
> redirected to the appropriate handler which then takes care of the request.
|
||||
|
||||
###
|
||||
|
||||
# **Requires:**
|
||||
|
||||
# - [Logging](logging.html)
|
||||
log = require './logging'
|
||||
|
||||
# - [Config](config.html)
|
||||
config = require './config'
|
||||
|
||||
# - [User Handler](user_handler.html)
|
||||
requestHandler = require './request_handler'
|
||||
|
||||
# - Node.js Modules: [path](http://nodejs.org/api/path.html) and
|
||||
# [querystring](http://nodejs.org/api/querystring.html)
|
||||
path = require 'path'
|
||||
express = require 'express'
|
||||
app = express()
|
||||
# } RedisStore = require('connect-redis')(express), # TODO use RedisStore for persistent sessions
|
||||
qs = require 'querystring'
|
||||
|
||||
# Requires:
|
||||
# - External Modules: [express](http://expressjs.com/api.html)
|
||||
express = require 'express'
|
||||
app = express()
|
||||
|
||||
# - The [Logging](logging.html) module
|
||||
log = require './logging'
|
||||
# - The [Config](config.html) module
|
||||
config = require './config'
|
||||
# - The [User Handler](user_handler.html) module
|
||||
userHandler = require './user_handler'
|
||||
#RedisStore = require('connect-redis')(express), # TODO use RedisStore for persistent sessions
|
||||
|
||||
# Just to have at least something. I know all of you know it now ;-P
|
||||
sess_sec = '#C[>;j`@".TXm2TA;A2Tg)'
|
||||
|
||||
|
||||
# The module needs to be called as a function to initialize it.
|
||||
# After that it fetches the http\_port, db\_port & sess\_sec properties
|
||||
# from the configuration file.
|
||||
###
|
||||
Module call
|
||||
-----------
|
||||
Initializes the HTTP Listener and its child modules Logging,
|
||||
Configuration and Request Handler, then tries to fetch the session
|
||||
key from the configuration.
|
||||
|
||||
@param {Object} args
|
||||
###
|
||||
exports = module.exports = ( args ) ->
|
||||
args = args ? {}
|
||||
log args
|
||||
config args
|
||||
userHandler args
|
||||
# TODO check whether this really does what it's supposed to do (fetch wrong sess property)
|
||||
requestHandler args
|
||||
#TODO check whether this really does what it's supposed to do (fetch wrong sess property)
|
||||
sess_sec = config.getSessionSecret() || sess_sec
|
||||
module.exports
|
||||
|
||||
exports.addHandlers = ( fEvtHandler, fShutDown ) =>
|
||||
userHandler.addShutdownHandler fShutDown
|
||||
@eventHandler = fEvtHandler
|
||||
exports.addHandlers = ( fEvtHandler, fShutDown ) ->
|
||||
requestHandler.addHandlers fEvtHandler, fShutDown
|
||||
# Add cookie support for session handling.
|
||||
app.use express.cookieParser()
|
||||
app.use express.session { secret: sess_sec }
|
||||
# At the moment there's no redis session backbone (didn't work straight away)
|
||||
log.print 'HL', 'no session backbone'
|
||||
|
||||
# **Accepted requests to paths:**
|
||||
|
||||
# Redirect the requests to the appropriate handler.
|
||||
app.use '/', express.static path.resolve __dirname, '..', 'webpages'
|
||||
app.get '/rulesforge', userHandler.handleRequest
|
||||
app.get '/admin', userHandler.handleRequest
|
||||
app.post '/login', userHandler.handleLogin
|
||||
app.post '/push_event', onPushEvent
|
||||
# - **`GET` to _"/"_:** Static redirect to the _"webpages/public"_ directory
|
||||
app.use '/', express.static path.resolve __dirname, '..', 'webpages', 'public'
|
||||
# - **`POST` to _"/event"_:** Events coming from remote systems are passed to the engine
|
||||
app.post '/event', requestHandler.handleEvent
|
||||
# - **`GET` to _"/user"_:** User requests are possible for all users with an account
|
||||
app.get '/user', requestHandler.handleUser
|
||||
# - **`GET` to _"/admin"_:** Only admins can issue requests to this handler
|
||||
app.get '/admin', requestHandler.handleAdmin
|
||||
# - **`POST` to _"/login"_:** Credentials will be verified
|
||||
app.post '/login', requestHandler.handleLogin
|
||||
# - **`POST` to _"/logout"_:** User will be logged out
|
||||
app.post '/logout', requestHandler.handleLogout
|
||||
try
|
||||
http_port = config.getHttpPort()
|
||||
if http_port
|
||||
|
|
@ -60,25 +84,6 @@ exports.addHandlers = ( fEvtHandler, fShutDown ) =>
|
|||
e.addInfo = 'opening port'
|
||||
log.error e
|
||||
|
||||
#
|
||||
# If a post request reaches the server, this function handles it and treats the request as a possible event.
|
||||
#
|
||||
onPushEvent = ( req, resp ) =>
|
||||
body = ''
|
||||
req.on 'data', ( data ) ->
|
||||
body += data
|
||||
req.on 'end', =>
|
||||
obj = qs.parse body
|
||||
# If required event properties are present we process the event #
|
||||
if obj and obj.event and obj.eventid
|
||||
resp.write 'Thank you for the event (' + obj.event + '[' + obj.eventid + '])!'
|
||||
@eventHandler obj
|
||||
else
|
||||
resp.writeHead 400, { "Content-Type": "text/plain" }
|
||||
resp.write 'Your event was missing important parameters!'
|
||||
resp.end()
|
||||
|
||||
|
||||
exports.shutDown = () ->
|
||||
log.print 'HL', 'Shutting down HTTP listener'
|
||||
process.exit() # This is a bit brute force...
|
||||
|
|
|
|||
|
|
@ -1,25 +1,35 @@
|
|||
###
|
||||
|
||||
User Handler
|
||||
Request Handler
|
||||
============
|
||||
> TODO Add documentation
|
||||
|
||||
###
|
||||
|
||||
# **Requires:**
|
||||
|
||||
# - [Logging](logging.html)
|
||||
log = require './logging'
|
||||
|
||||
# - [DB Interface](db_interface.html)
|
||||
db = require './db_interface'
|
||||
|
||||
# - [Module Manager](module_manager.html)
|
||||
mm = require './module_manager'
|
||||
|
||||
# - 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'
|
||||
|
||||
# Requires:
|
||||
# - 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'
|
||||
|
||||
# - The [Logging](logging.html) module
|
||||
log = require './logging'
|
||||
# - The [DB Interface](db_interface.html) module
|
||||
db = require './db_interface'
|
||||
# - The [Module Manager](module_manager.html) module
|
||||
mm = require './module_manager'
|
||||
|
||||
### Prepare the admin command handlers that are issued via HTTP requests. ###
|
||||
# Prepare the admin command handlers that are invoked via HTTP requests.
|
||||
objAdminCmds =
|
||||
'loadrules': mm.loadRulesFromFS,
|
||||
'loadaction': mm.loadActionModuleFromFS,
|
||||
|
|
@ -38,69 +48,149 @@ exports = module.exports = ( args ) ->
|
|||
db.storeUser user for user in users
|
||||
module.exports
|
||||
|
||||
###
|
||||
This allows the parent to add handlers. The event handler will receive
|
||||
the events that were received. The shutdown function will be called if the
|
||||
admin command shutdown is issued.
|
||||
|
||||
exports.addShutdownHandler = ( fShutdown ) ->
|
||||
@public addHandlers( *fEvtHandler, fShutdown* )
|
||||
@param {function} fEvtHandler
|
||||
@param {function} fShutdown
|
||||
###
|
||||
exports.addHandlers = ( fEvtHandler, fShutdown ) =>
|
||||
@eventHanlder = fEvtHandler
|
||||
objAdminCmds.shutdown = fShutdown
|
||||
|
||||
|
||||
exports.handleRequest = ( req, resp ) ->
|
||||
req.on 'end', -> resp.end()
|
||||
if req.session and req.session.user
|
||||
resp.send 'You\'re logged in'
|
||||
else
|
||||
resp.sendfile path.resolve __dirname, '..', 'webpages', 'handlers', 'login.html'
|
||||
req.session.lastPage = req.originalUrl
|
||||
###
|
||||
|
||||
*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* )
|
||||
###
|
||||
exports.handleEvent = ( req, resp ) =>
|
||||
body = ''
|
||||
req.on 'data', ( data ) ->
|
||||
body += data
|
||||
req.on 'end', =>
|
||||
obj = qs.parse body
|
||||
# If required event properties are present we process the event #
|
||||
if obj and obj.event and obj.eventid
|
||||
resp.send 'Thank you for the event (' + obj.event + '[' + obj.eventid + '])!'
|
||||
@eventHandler obj
|
||||
else
|
||||
resp.writeHead 400, { "Content-Type": "text/plain" }
|
||||
resp.send 'Your event was missing important parameters!'
|
||||
|
||||
|
||||
###
|
||||
|
||||
*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 handleLogin( *req, resp* )
|
||||
###
|
||||
exports.handleLogin = ( req, resp ) ->
|
||||
body = ''
|
||||
req.on 'data', ( data ) -> body += data
|
||||
req.on 'end', ->
|
||||
if not req.session or not req.session.user
|
||||
obj = qs.parse body
|
||||
db.loginUser obj.username, obj.password, ( err, obj ) ->
|
||||
if not err
|
||||
req.session.user = obj
|
||||
if req.session.user
|
||||
resp.write 'Welcome ' + req.session.user.name + '!'
|
||||
db.loginUser obj.username, obj.password, ( err, usr ) ->
|
||||
if(err)
|
||||
# Tapping on fingers, at least in log...
|
||||
log.print 'RH', "AUTH-UH-OH (#{obj.username}): " + err.message
|
||||
else
|
||||
resp.writeHead 401, { "Content-Type": "text/plain" }
|
||||
resp.write 'Login failed!'
|
||||
resp.end()
|
||||
# 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!'
|
||||
else
|
||||
resp.write 'Welcome ' + req.session.user.name + '!'
|
||||
resp.end()
|
||||
resp.send 'Welcome ' + req.session.user.name + '!'
|
||||
|
||||
###
|
||||
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 ) ->
|
||||
if req.session
|
||||
req.session.user = null
|
||||
resp.send 'Bye!'
|
||||
|
||||
|
||||
answerHandler = ( resp ) ->
|
||||
hasBeenAnswered = false
|
||||
postAnswer( msg ) ->
|
||||
if not hasBeenAnswered
|
||||
resp.write msg
|
||||
resp.end()
|
||||
hasBeenAnswered = true
|
||||
{
|
||||
answerSuccess: ( msg ) ->
|
||||
if not hasBeenAnswered
|
||||
postAnswer msg,
|
||||
answerError: ( msg ) ->
|
||||
if not hasBeenAnswered
|
||||
resp.writeHead 400, { "Content-Type": "text/plain" }
|
||||
postAnswer msg,
|
||||
isAnswered: -> hasBeenAnswered
|
||||
}
|
||||
getHandlerPath = (name) ->
|
||||
path.resolve __dirname, '..', 'webpages', 'handlers', name + '.html'
|
||||
|
||||
|
||||
getHandlerFileAsString = (name) ->
|
||||
fs.readFileSync getHandlerPath( name ), 'utf8'
|
||||
###
|
||||
|
||||
*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* )
|
||||
###
|
||||
exports.handleUser = ( req, resp ) ->
|
||||
if req.session and req.session.user
|
||||
welcome = getHandlerFileAsString 'welcome'
|
||||
menubar = getHandlerFileAsString 'menubar'
|
||||
view = {
|
||||
user: req.session.user,
|
||||
div_menubar: menubar
|
||||
}
|
||||
resp.send mustache.render welcome, view
|
||||
else
|
||||
resp.sendfile getHandlerPath 'login'
|
||||
|
||||
###
|
||||
|
||||
*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 handleAdmin( *req, resp* )
|
||||
###
|
||||
exports.handleAdmin = ( req, resp ) ->
|
||||
if req.session and req.session.user
|
||||
if req.session.user.isAdmin is "true"
|
||||
welcome = getHandlerFileAsString 'welcome'
|
||||
menubar = getHandlerFileAsString 'menubar'
|
||||
view =
|
||||
user: req.session.user,
|
||||
div_menubar: menubar
|
||||
resp.send mustache.render welcome, view
|
||||
else
|
||||
unauthorized = getHandlerFileAsString 'unauthorized'
|
||||
menubar = getHandlerFileAsString 'menubar'
|
||||
view =
|
||||
user: req.session.user,
|
||||
div_menubar: menubar
|
||||
resp.send mustache.render unauthorized, view
|
||||
else
|
||||
resp.sendfile getHandlerPath 'login'
|
||||
|
||||
# TODO add loadUsers as directive to admin commands
|
||||
# exports.loadUsers = ->
|
||||
# var users = JSON.parse(fs.readFileSync(path.resolve(__dirname, '..', 'users.json')));
|
||||
# for(var name in users) {
|
||||
# db.storeUser(users[name]);
|
||||
# }
|
||||
# };
|
||||
|
||||
onAdminCommand = ( req, response ) ->
|
||||
q = req.query;
|
||||
log.print 'HL', 'Received admin request: ' + req.originalUrl
|
||||
q = req.query
|
||||
log.print 'RH', 'Received admin request: ' + q
|
||||
if q.cmd
|
||||
fAdminCommands q, answerHandler response
|
||||
#answerSuccess(response, 'Thank you, we try our best!');
|
||||
|
|
@ -117,7 +207,7 @@ fAdminCommands = ( args, answHandler ) ->
|
|||
if args and args.cmd
|
||||
adminCmds[args.cmd]? args, answHandler
|
||||
else
|
||||
log.print 'RS', 'No command in request'
|
||||
log.print 'RH', 'No command in request'
|
||||
|
||||
###
|
||||
The fAnsw function receives an answerHandler object as an argument when called
|
||||
|
|
|
|||
|
|
@ -20,18 +20,23 @@ Rules Server
|
|||
|
||||
###
|
||||
|
||||
# Requires:
|
||||
# **Requires:**
|
||||
|
||||
# - The [Logging](logging.html) module
|
||||
# - [Logging](logging.html)
|
||||
log = require './logging'
|
||||
# - The [Config](config.html) module
|
||||
|
||||
# - [Configuration](config.html)
|
||||
conf = require './config'
|
||||
# - The [DB Interface](db_interface.html) module
|
||||
|
||||
# - [DB Interface](db_interface.html)
|
||||
db = require './db_interface'
|
||||
# - The [Engine](engine.html) module
|
||||
|
||||
# - [Engine](engine.html)
|
||||
engine = require './engine'
|
||||
# - The [HTTP Listener](http_listener.html) module
|
||||
|
||||
# - [HTTP Listener](http_listener.html)
|
||||
http_listener = require './http_listener'
|
||||
|
||||
args = {}
|
||||
procCmds = {}
|
||||
|
||||
|
|
@ -48,22 +53,20 @@ process.on 'uncaughtException', ( err ) ->
|
|||
log.error 'RS', err
|
||||
shutDown()
|
||||
else throw err
|
||||
|
||||
###
|
||||
This function is invoked right after the module is loaded and starts the server.
|
||||
|
||||
@private init()
|
||||
###
|
||||
|
||||
init = ->
|
||||
log.print 'RS', 'STARTING SERVER'
|
||||
|
||||
### Check whether the config file is ready, which is required to start the server. ###
|
||||
# > Check whether the config file is ready, which is required to start the server.
|
||||
if !conf.isReady()
|
||||
log.error 'RS', 'Config file not ready!'
|
||||
process.exit()
|
||||
|
||||
### Fetch the `log_type` argument and post a log about which log type is used.###
|
||||
# > Fetch the `log_type` argument and post a log about which log type is used.
|
||||
if process.argv.length > 2
|
||||
args.logType = parseInt(process.argv[2]) || 0
|
||||
switch args.logType
|
||||
|
|
@ -75,31 +78,31 @@ init = ->
|
|||
else
|
||||
log.print 'RS', 'No log method argument provided, using standard I/O'
|
||||
|
||||
### Fetch the `http_port` argument ###
|
||||
# > Fetch the `http_port` argument
|
||||
if process.argv.length > 3 then args.http_port = parseInt process.argv[3]
|
||||
else log.print 'RS', 'No HTTP port passed, using standard port from config file'
|
||||
|
||||
log.print 'RS', 'Initialzing DB'
|
||||
db args
|
||||
### We only proceed with the initialization if the DB is ready ###
|
||||
# > We only proceed with the initialization if the DB is ready
|
||||
db.isConnected ( err, result ) ->
|
||||
if !err
|
||||
|
||||
### Initialize all required modules with the args object.###
|
||||
# > Initialize all required modules with the args object.
|
||||
log.print 'RS', 'Initialzing engine'
|
||||
engine args
|
||||
log.print 'RS', 'Initialzing http listener'
|
||||
http_listener args
|
||||
|
||||
### Distribute handlers between modules to link the application. ###
|
||||
# > Distribute handlers between modules to link the application.
|
||||
log.print 'RS', 'Passing handlers to engine'
|
||||
engine.addDBLinkAndLoadActionsAndRules db
|
||||
log.print 'RS', 'Passing handlers to http listener'
|
||||
# TODO engine pushEvent needs to go into redis queue
|
||||
#TODO engine pushEvent needs to go into redis queue
|
||||
http_listener.addHandlers db, engine.pushEvent, shutDown
|
||||
# log.print 'RS', 'Passing handlers to module manager'
|
||||
# TODO loadAction and addRule will be removed
|
||||
# mm.addHandlers db, engine.loadActionModule, engine.addRule
|
||||
#log.print 'RS', 'Passing handlers to module manager'
|
||||
#TODO loadAction and addRule will be removed
|
||||
#mm.addHandlers db, engine.loadActionModule, engine.addRule
|
||||
|
||||
###
|
||||
Shuts down the server.
|
||||
|
|
@ -117,17 +120,10 @@ shutDown = ->
|
|||
When the server is run as a child process, this function handles messages
|
||||
from the parent process (e.g. the testing suite)
|
||||
###
|
||||
|
||||
process.on 'message', ( cmd ) -> procCmds[cmd]?()
|
||||
|
||||
###
|
||||
The die command redirects to the shutDown function.
|
||||
###
|
||||
|
||||
# The die command redirects to the shutDown function.
|
||||
procCmds.die = shutDown
|
||||
|
||||
###
|
||||
*Start initialization*
|
||||
###
|
||||
|
||||
# *Start initialization*
|
||||
init()
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ require('groc').CLI(
|
|||
"coffee/*.coffee",
|
||||
"mod_actions/**/*.js",
|
||||
"mod_events/**/*.js",
|
||||
"-o./webpages/doc"
|
||||
"-o./webpages/public/doc"
|
||||
],
|
||||
function(err) {
|
||||
if (err) console.error(err);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// Generated by CoffeeScript 1.6.3
|
||||
/*
|
||||
|
||||
Config
|
||||
======
|
||||
Configuration
|
||||
=============
|
||||
> Loads the configuration file and acts as an interface to it.
|
||||
*/
|
||||
|
||||
|
|
@ -11,12 +11,12 @@ Config
|
|||
var exports, fetchProp, fs, loadConfigFile, log, path,
|
||||
_this = this;
|
||||
|
||||
log = require('./logging');
|
||||
|
||||
fs = require('fs');
|
||||
|
||||
path = require('path');
|
||||
|
||||
log = require('./logging');
|
||||
|
||||
/*
|
||||
##Module call
|
||||
|
||||
|
|
@ -30,7 +30,7 @@ Config
|
|||
args = args != null ? args : {};
|
||||
log(args);
|
||||
if (typeof args.relPath === 'string') {
|
||||
loadConfigFiles(args.relPath);
|
||||
loadConfigFile(args.relPath);
|
||||
}
|
||||
return module.exports;
|
||||
};
|
||||
|
|
@ -76,7 +76,7 @@ Config
|
|||
};
|
||||
|
||||
/*
|
||||
Answer true if the config file is ready, else false
|
||||
***Returns*** true if the config file is ready, else false
|
||||
|
||||
@public isReady()
|
||||
*/
|
||||
|
|
@ -87,7 +87,7 @@ Config
|
|||
};
|
||||
|
||||
/*
|
||||
Returns the HTTP port
|
||||
***Returns*** the HTTP port
|
||||
|
||||
@public getHttpPort()
|
||||
*/
|
||||
|
|
@ -98,7 +98,7 @@ Config
|
|||
};
|
||||
|
||||
/*
|
||||
Returns the DB port
|
||||
***Returns*** the DB port*
|
||||
|
||||
@public getDBPort()
|
||||
*/
|
||||
|
|
@ -109,7 +109,7 @@ Config
|
|||
};
|
||||
|
||||
/*
|
||||
Returns the crypto key
|
||||
***Returns*** the crypto key
|
||||
|
||||
@public getCryptoKey()
|
||||
*/
|
||||
|
|
@ -120,7 +120,7 @@ Config
|
|||
};
|
||||
|
||||
/*
|
||||
Returns the session secret
|
||||
***Returns*** the session secret
|
||||
|
||||
@public getSessionSecret()
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -21,15 +21,15 @@ DB Interface
|
|||
|
||||
|
||||
(function() {
|
||||
var crypto, decrypt, encrypt, exports, getSetRecords, log, redis, replyHandler,
|
||||
var crypto, decrypt, encrypt, exports, getSetRecords, hash, log, redis, replyHandler,
|
||||
_this = this;
|
||||
|
||||
redis = require('redis');
|
||||
|
||||
crypto = require('crypto');
|
||||
|
||||
log = require('./logging');
|
||||
|
||||
crypto = require('crypto-js');
|
||||
|
||||
redis = require('redis');
|
||||
|
||||
/*
|
||||
Module call
|
||||
-----------
|
||||
|
|
@ -88,6 +88,31 @@ DB Interface
|
|||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Hashes a string based on SHA-3-512.
|
||||
|
||||
@private hash( *plainText* )
|
||||
@param {String} plainText
|
||||
*/
|
||||
|
||||
|
||||
hash = function(plainText) {
|
||||
var err;
|
||||
if (plainText == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return (crypto.SHA3(plainText, {
|
||||
outputLength: 512
|
||||
})).toString();
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
err.addInfo = 'during hashing';
|
||||
log.error('DB', err);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Encrypts a string using the crypto key from the config file, based on aes-256-cbc.
|
||||
|
||||
|
|
@ -269,7 +294,7 @@ DB Interface
|
|||
|
||||
exports.storeActionAuth = function(userId, moduleId, data) {
|
||||
log.print('DB', 'storeActionAuth: ' + userId + ':' + moduleId);
|
||||
return _this.db.set('action-auth:' + userId + ':' + moduleId, encrypt(data), replyHandler('storing action auth ' + userId + ':' + moduleId));
|
||||
return _this.db.set('action-auth:' + userId + ':' + moduleId, hash(data), replyHandler('storing action auth ' + userId + ':' + moduleId));
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -348,7 +373,7 @@ DB Interface
|
|||
|
||||
exports.storeEventAuth = function(userId, moduleId, data) {
|
||||
log.print('DB', 'storeEventAuth: ' + userId + ':' + moduleId);
|
||||
return _this.db.set('event-auth:' + userId + ':' + moduleId, encrypt(data), replyHandler('storing event auth ' + userId + ':' + moduleId));
|
||||
return _this.db.set('event-auth:' + userId + ':' + moduleId, hash(data), replyHandler('storing event auth ' + userId + ':' + moduleId));
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -427,7 +452,7 @@ DB Interface
|
|||
log.print('DB', 'storeUser: ' + objUser.username);
|
||||
if (objUser && objUser.username && objUser.password) {
|
||||
_this.db.sadd('users', objUser.username, replyHandler('storing user key ' + objUser.username));
|
||||
objUser.password = encrypt(objUser.password);
|
||||
objUser.password = hash(objUser.password);
|
||||
return _this.db.hmset('user:' + objUser.username, objUser, replyHandler('storing user properties ' + objUser.username));
|
||||
} else {
|
||||
return log.error('DB', new Error('username or password was missing'));
|
||||
|
|
@ -476,7 +501,10 @@ DB Interface
|
|||
};
|
||||
|
||||
/*
|
||||
Checks the credentials and on success returns the user object to the callback(err, obj) function.
|
||||
Checks the credentials and on success returns the user object to the
|
||||
callback(err, obj) function. The password has to be hashed (SHA-3-512)
|
||||
beforehand by the instance closest to the user that enters the password,
|
||||
because we only store hashes of passwords for safety reasons.
|
||||
|
||||
@public loginUser( *username, password, cb* )
|
||||
@param {String} username
|
||||
|
|
@ -493,14 +521,14 @@ DB Interface
|
|||
if (err) {
|
||||
return cb(err);
|
||||
} else if (obj && obj.password) {
|
||||
if (encrypt(pw) === obj.password) {
|
||||
if (pw === obj.password) {
|
||||
log.print('DB', 'User "' + obj.username + '" logged in!');
|
||||
return cb(null, obj);
|
||||
} else {
|
||||
return cb(new Error('Wrong credentials!'));
|
||||
}
|
||||
} else {
|
||||
return cb(new Error('Empty arguments!'));
|
||||
return cb(new Error('User not found!'));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,54 +3,65 @@
|
|||
|
||||
HTTP Listener
|
||||
=============
|
||||
> Handles the HTTP requests to the server at the port specified by the
|
||||
> [config](config.html) file.
|
||||
> Receives the HTTP requests to the server at the port specified by the
|
||||
> [config](config.html) file. These requests (bound to a method) are then
|
||||
> redirected to the appropriate handler which then takes care of the request.
|
||||
*/
|
||||
|
||||
|
||||
(function() {
|
||||
var app, config, exports, express, log, onPushEvent, path, qs, sess_sec, userHandler,
|
||||
_this = this;
|
||||
|
||||
path = require('path');
|
||||
|
||||
express = require('express');
|
||||
|
||||
app = express();
|
||||
|
||||
qs = require('querystring');
|
||||
var app, config, exports, express, log, path, qs, requestHandler, sess_sec;
|
||||
|
||||
log = require('./logging');
|
||||
|
||||
config = require('./config');
|
||||
|
||||
userHandler = require('./user_handler');
|
||||
requestHandler = require('./request_handler');
|
||||
|
||||
path = require('path');
|
||||
|
||||
qs = require('querystring');
|
||||
|
||||
express = require('express');
|
||||
|
||||
app = express();
|
||||
|
||||
sess_sec = '#C[>;j`@".TXm2TA;A2Tg)';
|
||||
|
||||
/*
|
||||
Module call
|
||||
-----------
|
||||
Initializes the HTTP Listener and its child modules Logging,
|
||||
Configuration and Request Handler, then tries to fetch the session
|
||||
key from the configuration.
|
||||
|
||||
@param {Object} args
|
||||
*/
|
||||
|
||||
|
||||
exports = module.exports = function(args) {
|
||||
args = args != null ? args : {};
|
||||
log(args);
|
||||
config(args);
|
||||
userHandler(args);
|
||||
requestHandler(args);
|
||||
sess_sec = config.getSessionSecret() || sess_sec;
|
||||
return module.exports;
|
||||
};
|
||||
|
||||
exports.addHandlers = function(fEvtHandler, fShutDown) {
|
||||
var e, http_port;
|
||||
userHandler.addShutdownHandler(fShutDown);
|
||||
_this.eventHandler = fEvtHandler;
|
||||
requestHandler.addHandlers(fEvtHandler, fShutDown);
|
||||
app.use(express.cookieParser());
|
||||
app.use(express.session({
|
||||
secret: sess_sec
|
||||
}));
|
||||
log.print('HL', 'no session backbone');
|
||||
app.use('/', express["static"](path.resolve(__dirname, '..', 'webpages')));
|
||||
app.get('/rulesforge', userHandler.handleRequest);
|
||||
app.get('/admin', userHandler.handleRequest);
|
||||
app.post('/login', userHandler.handleLogin);
|
||||
app.post('/push_event', onPushEvent);
|
||||
app.use('/', express["static"](path.resolve(__dirname, '..', 'webpages', 'public')));
|
||||
app.post('/event', requestHandler.handleEvent);
|
||||
app.get('/user', requestHandler.handleUser);
|
||||
app.get('/admin', requestHandler.handleAdmin);
|
||||
app.post('/login', requestHandler.handleLogin);
|
||||
app.post('/logout', requestHandler.handleLogout);
|
||||
try {
|
||||
http_port = config.getHttpPort();
|
||||
if (http_port) {
|
||||
|
|
@ -65,28 +76,6 @@ HTTP Listener
|
|||
}
|
||||
};
|
||||
|
||||
onPushEvent = function(req, resp) {
|
||||
var body;
|
||||
body = '';
|
||||
req.on('data', function(data) {
|
||||
return body += data;
|
||||
});
|
||||
return req.on('end', function() {
|
||||
var obj;
|
||||
obj = qs.parse(body);
|
||||
if (obj && obj.event && obj.eventid) {
|
||||
resp.write('Thank you for the event (' + obj.event + '[' + obj.eventid + '])!');
|
||||
_this.eventHandler(obj);
|
||||
} else {
|
||||
resp.writeHead(400, {
|
||||
"Content-Type": "text/plain"
|
||||
});
|
||||
resp.write('Your event was missing important parameters!');
|
||||
}
|
||||
return resp.end();
|
||||
});
|
||||
};
|
||||
|
||||
exports.shutDown = function() {
|
||||
log.print('HL', 'Shutting down HTTP listener');
|
||||
return process.exit();
|
||||
|
|
|
|||
282
js-coffee/request_handler.js
Normal file
282
js-coffee/request_handler.js
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
// Generated by CoffeeScript 1.6.3
|
||||
/*
|
||||
|
||||
Request Handler
|
||||
============
|
||||
> TODO Add documentation
|
||||
*/
|
||||
|
||||
|
||||
(function() {
|
||||
var crypto, db, exports, fAdminCommands, fs, getHandlerFileAsString, getHandlerPath, log, mm, mustache, objAdminCmds, onAdminCommand, path, qs,
|
||||
_this = this;
|
||||
|
||||
log = require('./logging');
|
||||
|
||||
db = require('./db_interface');
|
||||
|
||||
mm = require('./module_manager');
|
||||
|
||||
fs = require('fs');
|
||||
|
||||
path = require('path');
|
||||
|
||||
qs = require('querystring');
|
||||
|
||||
mustache = require('mustache');
|
||||
|
||||
crypto = require('crypto-js');
|
||||
|
||||
objAdminCmds = {
|
||||
'loadrules': mm.loadRulesFromFS,
|
||||
'loadaction': mm.loadActionModuleFromFS,
|
||||
'loadactions': mm.loadActionModulesFromFS,
|
||||
'loadevent': mm.loadEventModuleFromFS,
|
||||
'loadevents': mm.loadEventModulesFromFS
|
||||
};
|
||||
|
||||
exports = module.exports = function(args) {
|
||||
var user, users, _i, _len;
|
||||
args = args != null ? args : {};
|
||||
log(args);
|
||||
db(args);
|
||||
mm(args);
|
||||
mm.addDBLink(db);
|
||||
users = JSON.parse(fs.readFileSync(path.resolve(__dirname, '..', 'config', 'users.json')));
|
||||
for (_i = 0, _len = users.length; _i < _len; _i++) {
|
||||
user = users[_i];
|
||||
db.storeUser(user);
|
||||
}
|
||||
return module.exports;
|
||||
};
|
||||
|
||||
/*
|
||||
This allows the parent to add handlers. The event handler will receive
|
||||
the events that were received. The shutdown function will be called if the
|
||||
admin command shutdown is issued.
|
||||
|
||||
@public addHandlers( *fEvtHandler, fShutdown* )
|
||||
@param {function} fEvtHandler
|
||||
@param {function} fShutdown
|
||||
*/
|
||||
|
||||
|
||||
exports.addHandlers = function(fEvtHandler, fShutdown) {
|
||||
_this.eventHanlder = fEvtHandler;
|
||||
return objAdminCmds.shutdown = fShutdown;
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
*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* )
|
||||
*/
|
||||
|
||||
|
||||
exports.handleEvent = function(req, resp) {
|
||||
var body;
|
||||
body = '';
|
||||
req.on('data', function(data) {
|
||||
return body += data;
|
||||
});
|
||||
return req.on('end', function() {
|
||||
var obj;
|
||||
obj = qs.parse(body);
|
||||
if (obj && obj.event && obj.eventid) {
|
||||
resp.send('Thank you for the event (' + obj.event + '[' + obj.eventid + '])!');
|
||||
return _this.eventHandler(obj);
|
||||
} else {
|
||||
resp.writeHead(400, {
|
||||
"Content-Type": "text/plain"
|
||||
});
|
||||
return resp.send('Your event was missing important parameters!');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
*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 handleLogin( *req, resp* )
|
||||
*/
|
||||
|
||||
|
||||
exports.handleLogin = function(req, resp) {
|
||||
var body;
|
||||
body = '';
|
||||
req.on('data', function(data) {
|
||||
return body += data;
|
||||
});
|
||||
return req.on('end', function() {
|
||||
var obj;
|
||||
if (!req.session || !req.session.user) {
|
||||
obj = qs.parse(body);
|
||||
return db.loginUser(obj.username, obj.password, function(err, usr) {
|
||||
if (err) {
|
||||
log.print('RH', ("AUTH-UH-OH (" + obj.username + "): ") + err.message);
|
||||
} else {
|
||||
req.session.user = usr;
|
||||
}
|
||||
if (req.session.user) {
|
||||
return resp.send('OK!');
|
||||
} else {
|
||||
return resp.send(401, 'NO!');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return resp.send('Welcome ' + req.session.user.name + '!');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
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 = function(req, resp) {
|
||||
if (req.session) {
|
||||
req.session.user = null;
|
||||
return resp.send('Bye!');
|
||||
}
|
||||
};
|
||||
|
||||
getHandlerPath = function(name) {
|
||||
return path.resolve(__dirname, '..', 'webpages', 'handlers', name + '.html');
|
||||
};
|
||||
|
||||
getHandlerFileAsString = function(name) {
|
||||
return fs.readFileSync(getHandlerPath(name), 'utf8');
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
*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* )
|
||||
*/
|
||||
|
||||
|
||||
exports.handleUser = function(req, resp) {
|
||||
var menubar, view, welcome;
|
||||
if (req.session && req.session.user) {
|
||||
welcome = getHandlerFileAsString('welcome');
|
||||
menubar = getHandlerFileAsString('menubar');
|
||||
view = {
|
||||
user: req.session.user,
|
||||
div_menubar: menubar
|
||||
};
|
||||
return resp.send(mustache.render(welcome, view));
|
||||
} else {
|
||||
return resp.sendfile(getHandlerPath('login'));
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
*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 handleAdmin( *req, resp* )
|
||||
*/
|
||||
|
||||
|
||||
exports.handleAdmin = function(req, resp) {
|
||||
var menubar, unauthorized, view, welcome;
|
||||
if (req.session && req.session.user) {
|
||||
if (req.session.user.isAdmin === "true") {
|
||||
welcome = getHandlerFileAsString('welcome');
|
||||
menubar = getHandlerFileAsString('menubar');
|
||||
view = {
|
||||
user: req.session.user,
|
||||
div_menubar: menubar
|
||||
};
|
||||
return resp.send(mustache.render(welcome, view));
|
||||
} else {
|
||||
unauthorized = getHandlerFileAsString('unauthorized');
|
||||
menubar = getHandlerFileAsString('menubar');
|
||||
view = {
|
||||
user: req.session.user,
|
||||
div_menubar: menubar
|
||||
};
|
||||
return resp.send(mustache.render(unauthorized, view));
|
||||
}
|
||||
} else {
|
||||
return resp.sendfile(getHandlerPath('login'));
|
||||
}
|
||||
};
|
||||
|
||||
onAdminCommand = function(req, response) {
|
||||
var q;
|
||||
q = req.query;
|
||||
log.print('RH', 'Received admin request: ' + q);
|
||||
if (q.cmd) {
|
||||
return fAdminCommands(q, answerHandler(response));
|
||||
} else {
|
||||
return answerError(response, 'I\'m not sure about what you want from me...');
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
admin commands handler receives all command arguments and an answerHandler
|
||||
object that eases response handling to the HTTP request issuer.
|
||||
|
||||
@private fAdminCommands( *args, answHandler* )
|
||||
*/
|
||||
|
||||
|
||||
fAdminCommands = function(args, answHandler) {
|
||||
var fAnsw, _name;
|
||||
if (args && args.cmd) {
|
||||
if (typeof adminCmds[_name = args.cmd] === "function") {
|
||||
adminCmds[_name](args, answHandler);
|
||||
}
|
||||
} else {
|
||||
log.print('RH', 'No command in request');
|
||||
}
|
||||
/*
|
||||
The fAnsw function receives an answerHandler object as an argument when called
|
||||
and returns an anonymous function
|
||||
*/
|
||||
|
||||
fAnsw = function(ah) {
|
||||
/*
|
||||
The anonymous function checks whether the answerHandler was already used to
|
||||
issue an answer, if no answer was provided we answer with an error message
|
||||
*/
|
||||
|
||||
return function() {
|
||||
if (!ah.isAnswered()) {
|
||||
return ah.answerError('Not handled...');
|
||||
}
|
||||
};
|
||||
};
|
||||
/*
|
||||
Delayed function call of the anonymous function that checks the answer handler
|
||||
*/
|
||||
|
||||
return setTimeout(fAnsw(answHandler), 2000);
|
||||
};
|
||||
|
||||
}).call(this);
|
||||
|
|
@ -65,14 +65,10 @@ Rules Server
|
|||
|
||||
init = function() {
|
||||
log.print('RS', 'STARTING SERVER');
|
||||
/* Check whether the config file is ready, which is required to start the server.*/
|
||||
|
||||
if (!conf.isReady()) {
|
||||
log.error('RS', 'Config file not ready!');
|
||||
process.exit();
|
||||
}
|
||||
/* Fetch the `log_type` argument and post a log about which log type is used.*/
|
||||
|
||||
if (process.argv.length > 2) {
|
||||
args.logType = parseInt(process.argv[2]) || 0;
|
||||
switch (args.logType) {
|
||||
|
|
@ -92,8 +88,6 @@ Rules Server
|
|||
} else {
|
||||
log.print('RS', 'No log method argument provided, using standard I/O');
|
||||
}
|
||||
/* Fetch the `http_port` argument*/
|
||||
|
||||
if (process.argv.length > 3) {
|
||||
args.http_port = parseInt(process.argv[3]);
|
||||
} else {
|
||||
|
|
@ -101,18 +95,12 @@ Rules Server
|
|||
}
|
||||
log.print('RS', 'Initialzing DB');
|
||||
db(args);
|
||||
/* We only proceed with the initialization if the DB is ready*/
|
||||
|
||||
return db.isConnected(function(err, result) {
|
||||
if (!err) {
|
||||
/* Initialize all required modules with the args object.*/
|
||||
|
||||
log.print('RS', 'Initialzing engine');
|
||||
engine(args);
|
||||
log.print('RS', 'Initialzing http listener');
|
||||
http_listener(args);
|
||||
/* Distribute handlers between modules to link the application.*/
|
||||
|
||||
log.print('RS', 'Passing handlers to engine');
|
||||
engine.addDBLinkAndLoadActionsAndRules(db);
|
||||
log.print('RS', 'Passing handlers to http listener');
|
||||
|
|
@ -148,18 +136,8 @@ Rules Server
|
|||
return typeof procCmds[cmd] === "function" ? procCmds[cmd]() : void 0;
|
||||
});
|
||||
|
||||
/*
|
||||
The die command redirects to the shutDown function.
|
||||
*/
|
||||
|
||||
|
||||
procCmds.die = shutDown;
|
||||
|
||||
/*
|
||||
*Start initialization*
|
||||
*/
|
||||
|
||||
|
||||
init();
|
||||
|
||||
}).call(this);
|
||||
|
|
|
|||
|
|
@ -1,180 +0,0 @@
|
|||
// Generated by CoffeeScript 1.6.3
|
||||
/*
|
||||
|
||||
User Handler
|
||||
============
|
||||
> TODO Add documentation
|
||||
*/
|
||||
|
||||
|
||||
(function() {
|
||||
var answerHandler, db, exports, fAdminCommands, fs, log, mm, objAdminCmds, onAdminCommand, path, qs;
|
||||
|
||||
fs = require('fs');
|
||||
|
||||
path = require('path');
|
||||
|
||||
qs = require('querystring');
|
||||
|
||||
log = require('./logging');
|
||||
|
||||
db = require('./db_interface');
|
||||
|
||||
mm = require('./module_manager');
|
||||
|
||||
/* Prepare the admin command handlers that are issued via HTTP requests.*/
|
||||
|
||||
|
||||
objAdminCmds = {
|
||||
'loadrules': mm.loadRulesFromFS,
|
||||
'loadaction': mm.loadActionModuleFromFS,
|
||||
'loadactions': mm.loadActionModulesFromFS,
|
||||
'loadevent': mm.loadEventModuleFromFS,
|
||||
'loadevents': mm.loadEventModulesFromFS
|
||||
};
|
||||
|
||||
exports = module.exports = function(args) {
|
||||
var user, users, _i, _len;
|
||||
args = args != null ? args : {};
|
||||
log(args);
|
||||
db(args);
|
||||
mm(args);
|
||||
mm.addDBLink(db);
|
||||
users = JSON.parse(fs.readFileSync(path.resolve(__dirname, '..', 'config', 'users.json')));
|
||||
for (_i = 0, _len = users.length; _i < _len; _i++) {
|
||||
user = users[_i];
|
||||
db.storeUser(user);
|
||||
}
|
||||
return module.exports;
|
||||
};
|
||||
|
||||
exports.addShutdownHandler = function(fShutdown) {
|
||||
return objAdminCmds.shutdown = fShutdown;
|
||||
};
|
||||
|
||||
exports.handleRequest = function(req, resp) {
|
||||
req.on('end', function() {
|
||||
return resp.end();
|
||||
});
|
||||
if (req.session && req.session.user) {
|
||||
resp.send('You\'re logged in');
|
||||
} else {
|
||||
resp.sendfile(path.resolve(__dirname, '..', 'webpages', 'handlers', 'login.html'));
|
||||
}
|
||||
return req.session.lastPage = req.originalUrl;
|
||||
};
|
||||
|
||||
exports.handleLogin = function(req, resp) {
|
||||
var body;
|
||||
body = '';
|
||||
req.on('data', function(data) {
|
||||
return body += data;
|
||||
});
|
||||
return req.on('end', function() {
|
||||
var obj;
|
||||
if (!req.session || !req.session.user) {
|
||||
obj = qs.parse(body);
|
||||
return db.loginUser(obj.username, obj.password, function(err, obj) {
|
||||
if (!err) {
|
||||
req.session.user = obj;
|
||||
}
|
||||
if (req.session.user) {
|
||||
resp.write('Welcome ' + req.session.user.name + '!');
|
||||
} else {
|
||||
resp.writeHead(401, {
|
||||
"Content-Type": "text/plain"
|
||||
});
|
||||
resp.write('Login failed!');
|
||||
}
|
||||
return resp.end();
|
||||
});
|
||||
} else {
|
||||
resp.write('Welcome ' + req.session.user.name + '!');
|
||||
return resp.end();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
answerHandler = function(resp) {
|
||||
var hasBeenAnswered;
|
||||
hasBeenAnswered = false;
|
||||
postAnswer(msg)(function() {
|
||||
if (!hasBeenAnswered) {
|
||||
resp.write(msg);
|
||||
resp.end();
|
||||
return hasBeenAnswered = true;
|
||||
}
|
||||
});
|
||||
return {
|
||||
answerSuccess: function(msg) {
|
||||
if (!hasBeenAnswered) {
|
||||
return postAnswer(msg);
|
||||
}
|
||||
},
|
||||
answerError: function(msg) {
|
||||
if (!hasBeenAnswered) {
|
||||
resp.writeHead(400, {
|
||||
"Content-Type": "text/plain"
|
||||
});
|
||||
}
|
||||
return postAnswer(msg);
|
||||
},
|
||||
isAnswered: function() {
|
||||
return hasBeenAnswered;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
onAdminCommand = function(req, response) {
|
||||
var q;
|
||||
q = req.query;
|
||||
log.print('HL', 'Received admin request: ' + req.originalUrl);
|
||||
if (q.cmd) {
|
||||
return fAdminCommands(q, answerHandler(response));
|
||||
} else {
|
||||
return answerError(response, 'I\'m not sure about what you want from me...');
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
admin commands handler receives all command arguments and an answerHandler
|
||||
object that eases response handling to the HTTP request issuer.
|
||||
|
||||
@private fAdminCommands( *args, answHandler* )
|
||||
*/
|
||||
|
||||
|
||||
fAdminCommands = function(args, answHandler) {
|
||||
var fAnsw, _name;
|
||||
if (args && args.cmd) {
|
||||
if (typeof adminCmds[_name = args.cmd] === "function") {
|
||||
adminCmds[_name](args, answHandler);
|
||||
}
|
||||
} else {
|
||||
log.print('RS', 'No command in request');
|
||||
}
|
||||
/*
|
||||
The fAnsw function receives an answerHandler object as an argument when called
|
||||
and returns an anonymous function
|
||||
*/
|
||||
|
||||
fAnsw = function(ah) {
|
||||
/*
|
||||
The anonymous function checks whether the answerHandler was already used to
|
||||
issue an answer, if no answer was provided we answer with an error message
|
||||
*/
|
||||
|
||||
return function() {
|
||||
if (!ah.isAnswered()) {
|
||||
return ah.answerError('Not handled...');
|
||||
}
|
||||
};
|
||||
};
|
||||
/*
|
||||
Delayed function call of the anonymous function that checks the answer handler
|
||||
*/
|
||||
|
||||
return setTimeout(fAnsw(answHandler), 2000);
|
||||
};
|
||||
|
||||
}).call(this);
|
||||
|
|
@ -10,8 +10,10 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"connect-redis": "1.4.6",
|
||||
"crypto-js": "3.1.2",
|
||||
"express": "3.4.0",
|
||||
"groc": "0.6.1",
|
||||
"mustache": "0.7.3",
|
||||
"needle": "0.6.1",
|
||||
"nodeunit": "0.8.2",
|
||||
"redis": "0.9.0",
|
||||
|
|
|
|||
18
webpages/handlers/command_answer.html
Normal file
18
webpages/handlers/command_answer.html
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Command "{{command}}" Result</title>
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
<script src='//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js' type='text/javascript'></script>
|
||||
</head>
|
||||
<body>
|
||||
{{{div_menubar}}}
|
||||
<div id="mainbody">
|
||||
<div id="pagetitle">{{user.username}} unauthorized!</div>
|
||||
<p>
|
||||
Sorry this roles is missing for you.<br />
|
||||
You only have these privileges: {{user.roles}}
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -4,17 +4,21 @@
|
|||
<title>Login</title>
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
<script src='//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js' type='text/javascript'></script>
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/sha3.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Login</h1>
|
||||
<table>
|
||||
<tr><td>username: </td><td><input type="text" id="username" /></td></tr>
|
||||
<tr><td>password: </td><td><input type="password" id="password" /></td></tr>
|
||||
</table>
|
||||
<button id="but_submit">login</button>
|
||||
<div id="mainbody">
|
||||
<div id="pagetitle">Login</div>
|
||||
<table>
|
||||
<tr><td>username: </td><td><input type="text" id="username" /></td></tr>
|
||||
<tr><td>password: </td><td><input type="password" id="password" /></td></tr>
|
||||
</table>
|
||||
<button id="but_submit">login</button>
|
||||
</div>
|
||||
<script>
|
||||
$('#but_submit').click(function() {
|
||||
$.post('../login', { username: $('#username').val(), password: $('#password').val() })
|
||||
var hashedPassword = (CryptoJS.SHA3($('#password').val(), { outputLength: 512 })).toString();
|
||||
$.post('../login', { username: $('#username').val(), password: hashedPassword })
|
||||
.done(function(data) {
|
||||
window.location.href = document.URL;
|
||||
})
|
||||
|
|
|
|||
11
webpages/handlers/menubar.html
Normal file
11
webpages/handlers/menubar.html
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<div id="menubar">
|
||||
<div id="menubar_menu">menu</div>
|
||||
<div id="menubar_logout">logout</div>
|
||||
<script>
|
||||
$('#menubar_logout').click(function() {
|
||||
$.post('../logout').done(function() {
|
||||
window.location.href = document.URL;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
18
webpages/handlers/unauthorized.html
Normal file
18
webpages/handlers/unauthorized.html
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Unauthorized {{user.username}}</title>
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
<script src='//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js' type='text/javascript'></script>
|
||||
</head>
|
||||
<body>
|
||||
{{{div_menubar}}}
|
||||
<div id="mainbody">
|
||||
<div id="pagetitle">{{user.username}} unauthorized!</div>
|
||||
<p>
|
||||
Sorry this roles is missing for you.<br />
|
||||
You only have these privileges: {{user.roles}}
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
18
webpages/handlers/welcome.html
Normal file
18
webpages/handlers/welcome.html
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Welcome {{user.username}}</title>
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
<script src='//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js' type='text/javascript'></script>
|
||||
</head>
|
||||
<body>
|
||||
{{{div_menubar}}}
|
||||
<div id="mainbody">
|
||||
<div id="pagetitle">Welcome {{user.username}}</div>
|
||||
<p>
|
||||
We're glad you're back!<br />
|
||||
Your roles are: {{user.roles}}
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
40
webpages/public/style.css
Normal file
40
webpages/public/style.css
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
|
||||
body {
|
||||
font-family: sans-serif, "Times New Roman", Georgia, Serif;
|
||||
font-size: 80%;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
#menubar {
|
||||
font-size: 0.75em;
|
||||
width: 100%;
|
||||
padding: 2px;
|
||||
height: 1em;
|
||||
background-color: #DDD;
|
||||
}
|
||||
|
||||
#menubar_menu {
|
||||
float: left;
|
||||
margin:0 auto;
|
||||
}
|
||||
|
||||
#menubar_logout {
|
||||
height: 100%;
|
||||
float: right;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#menubar_logout:hover {
|
||||
background-color: #AAA;
|
||||
}
|
||||
|
||||
#mainbody {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
#pagetitle {
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
|
||||
body {
|
||||
font-family: sans-serif, "Times New Roman", Georgia, Serif;
|
||||
}
|
||||
Loading…
Reference in a new issue