From d7c09e2e2e8d56aa04da2fc0d4f40ff8dedffebb Mon Sep 17 00:00:00 2001 From: Dominic Bosch Date: Mon, 21 Apr 2014 14:42:26 +0200 Subject: [PATCH] admin command newuser added --- coffee/components-manager.coffee | 8 +-- coffee/engine.coffee | 9 +-- coffee/persistence.coffee | 9 ++- coffee/request-handler.coffee | 72 ++++++++++++++++--- config/users.json | 8 ++- js/components-manager.js | 34 +++++---- js/engine.js | 16 +++-- js/persistence.js | 12 +++- js/request-handler.js | 79 ++++++++++++++++++--- testing/files/testObjects.json | 9 +-- webpages/handlers/coffee/admin.coffee | 10 +++ webpages/handlers/coffee/forge_rule.coffee | 2 +- webpages/handlers/js/admin.js | 13 +++- webpages/handlers/js/forge_rule.js | 2 +- webpages/handlers/remote-scripts/admin.html | 1 + webpages/handlers/templates/admin.html | 6 +- 16 files changed, 221 insertions(+), 69 deletions(-) create mode 100644 webpages/handlers/remote-scripts/admin.html diff --git a/coffee/components-manager.coffee b/coffee/components-manager.coffee index 612c730..384bc69 100644 --- a/coffee/components-manager.coffee +++ b/coffee/components-manager.coffee @@ -50,14 +50,14 @@ exports.addRuleListener = ( eh ) => eventEmitter.addListener 'rule', eh # Fetch all active rules per user - db.getAllActivatedRuleIdsPerUser ( err, objUsers ) -> + db.getAllActivatedRuleIdsPerUser ( err, objUsers ) => # Go through all rules of each user - fGoThroughUsers = ( user, rules ) -> + fGoThroughUsers = ( user, rules ) => # Fetch the rules object for each rule in each user - fFetchRule = ( userName ) -> - ( rule ) -> + fFetchRule = ( userName ) => + ( rule ) => db.getRule rule, ( err, strRule ) => try oRule = JSON.parse strRule diff --git a/coffee/engine.coffee b/coffee/engine.coffee index 9342284..61bcef6 100644 --- a/coffee/engine.coffee +++ b/coffee/engine.coffee @@ -124,13 +124,14 @@ updateActionModules = ( updatedRuleId ) => fRequired = ( actionName ) -> for action in oUser[updatedRuleId].rule.actions # Since the event is in the format 'module -> function' we need to split the string - if (action.split ' -> ')[0] is actionName + if (action.split ' -> ')[ 0 ] is actionName return true false # Go thorugh all loaded action modules and check whether the action is still required - for action of oUser[updatedRuleId].rule.actions - delete oUser[updatedRuleId].actions[action] if not fRequired action + if oUser[updatedRuleId] + for action of oUser[updatedRuleId].rule.actions + delete oUser[updatedRuleId].actions[action] if not fRequired action fRemoveNotRequired oUser for name, oUser of listUserRules @@ -142,7 +143,7 @@ updateActionModules = ( updatedRuleId ) => # Load the action invoker module if it was part of the updated rule or if it's new fAddIfNewOrNotExisting = ( actionName ) => - moduleName = (actionName.split ' -> ')[0] + moduleName = (actionName.split ' -> ')[ 0 ] if not oMyRule.actions[moduleName] or oMyRule.rule.id is updatedRuleId db.actionInvokers.getModule moduleName, ( err, obj ) => if obj diff --git a/coffee/persistence.coffee b/coffee/persistence.coffee index 9ad5015..613d7c2 100644 --- a/coffee/persistence.coffee +++ b/coffee/persistence.coffee @@ -624,9 +624,10 @@ exports.storeUser = ( objUser ) => if objUser and objUser.username and objUser.password @db.sadd 'users', objUser.username, replyHandler "sadd 'users' -> '#{ objUser.username }'" - objUser.password = objUser.password @db.hmset "user:#{ objUser.username }", objUser, replyHandler "hmset 'user:#{ objUser.username }' -> [objUser]" + @db.hset "user:#{ objUser.username }", "roles", JSON.stringify( objUser.roles ), + replyHandler "hset 'user:#{ objUser.username }' field 'roles' -> [objUser]" else @log.warn new Error 'DB | username or password was missing' @@ -649,7 +650,10 @@ Fetch a user by id and pass it to cb(err, obj). ### exports.getUser = ( userId, cb ) => @log.info "DB | getUser: '#{ userId }'" - @db.hgetall "user:#{ userId }", cb + @db.hgetall "user:#{ userId }", ( err, obj ) => + try + obj.roles = JSON.parse obj.roles + cb err, obj ### Deletes a user and all his associated linked and active rules. @@ -708,6 +712,7 @@ exports.loginUser = ( userId, password, cb ) => else if obj and obj.password if pw is obj.password @log.info "DB | User '#{ obj.username }' logged in!" + obj.roles = JSON.parse obj.roles cb null, obj else cb (new Error 'Wrong credentials!'), null diff --git a/coffee/request-handler.coffee b/coffee/request-handler.coffee index 3f6a39c..748d76f 100644 --- a/coffee/request-handler.coffee +++ b/coffee/request-handler.coffee @@ -25,7 +25,9 @@ qs = require 'querystring' # [crypto-js](https://github.com/evanvosberg/crypto-js) mustache = require 'mustache' crypto = require 'crypto-js' - + +pathUsers = path.resolve __dirname, '..', 'config', 'users.json' + # Prepare the user command handlers which are invoked via HTTP requests. dirHandlers = path.resolve __dirname, '..', 'webpages', 'handlers' exports = module.exports = ( args ) => @@ -42,10 +44,53 @@ exports = module.exports = ( args ) => message: 'Shutting down... BYE!' setTimeout args[ 'shutdown-function' ], 500 cb null, data + + 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 + db args # Load the standard users from the user config file - users = JSON.parse fs.readFileSync path.resolve __dirname, '..', 'config', 'users.json' + users = JSON.parse fs.readFileSync pathUsers, 'utf8' fStoreUser = ( username, oUser ) -> oUser.username = username db.storeUser oUser @@ -280,9 +325,9 @@ exports.handleAdmin = ( req, resp ) -> if not req.session.user page = 'login' #TODO isAdmin should come from the db role - else if req.session.user.isAdmin isnt "true" + else if req.session.user.roles.indexOf( "admin" ) is -1 page = 'login' - msg = 'You need to be admin!' + msg = 'You need to be admin for this page!' else page = 'admin' renderPage page, req, resp, msg @@ -299,19 +344,26 @@ objects.* ### exports.handleAdminCommand = ( req, resp ) => if req.session and - req.session.user and - req.session.user.isAdmin is "true" + req.session.user and + req.session.user.roles.indexOf( "admin" ) > -1 body = '' req.on 'data', ( data ) -> body += data req.on 'end', => obj = qs.parse body @log.info 'RH | Received admin request: ' + obj.command - if obj.command and @objAdminCmds[obj.command] - @objAdminCmds[obj.command] obj, ( err, obj ) -> - resp.send obj.code, obj - else + arrCmd = obj.command.split( ' ' ) + if not arrCmd[ 0 ] or not @objAdminCmds[ arrCmd[ 0 ] ] resp.send 404, 'Command unknown!' + 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 else resp.send 401, 'You need to be logged in as admin!' diff --git a/config/users.json b/config/users.json index 12616d9..2646e5d 100644 --- a/config/users.json +++ b/config/users.json @@ -1,10 +1,12 @@ { "admin": { "password": "7407946a7a90b037ba5e825040f184a142161e4c61d81feb83ec8c7f011a99b0d77f39c9170c3231e1003c5cf859c69bd93043b095feff5cce6f6d45ec513764", - "roles": [ "admin" ] + "roles": [ + "admin" + ] }, "dominic": { "password": "2d51496fbe5b6d3e98e22d68140609eaedd64de457b2f75c346a4a98f87928eac11ea2be747709ae7a2f5b177af09a60a8dbf14bf703e0cb9b147fc0a3e3a064", - "roles": [ "admin" ] + "roles": [] } -} +} \ No newline at end of file diff --git a/js/components-manager.js b/js/components-manager.js index b5daeca..b24f8cf 100644 --- a/js/components-manager.js +++ b/js/components-manager.js @@ -61,24 +61,22 @@ Components Manager var fFetchRule, rule, _i, _len, _results; fFetchRule = function(userName) { return function(rule) { - return db.getRule(rule, (function(_this) { - return function(err, strRule) { - var oRule; - try { - oRule = JSON.parse(strRule); - db.resetLog(userName, oRule.id); - db.appendLog(userName, oRule.id, "INIT", "Rule '" + oRule.id + "' initialized. Interval set to " + oRule.event_interval + " minutes"); - return eventEmitter.emit('rule', { - event: 'init', - user: userName, - rule: oRule - }); - } catch (_error) { - err = _error; - return _this.log.warn("CM | There's an invalid rule in the system: " + strRule); - } - }; - })(this)); + return db.getRule(rule, function(err, strRule) { + var oRule; + try { + oRule = JSON.parse(strRule); + db.resetLog(userName, oRule.id); + db.appendLog(userName, oRule.id, "INIT", "Rule '" + oRule.id + "' initialized. Interval set to " + oRule.event_interval + " minutes"); + return eventEmitter.emit('rule', { + event: 'init', + user: userName, + rule: oRule + }); + } catch (_error) { + err = _error; + return _this.log.warn("CM | There's an invalid rule in the system: " + strRule); + } + }); }; }; _results = []; diff --git a/js/engine.js b/js/engine.js index 4366ed1..fccb8d6 100644 --- a/js/engine.js +++ b/js/engine.js @@ -142,15 +142,17 @@ Engine } return false; }; - _results = []; - for (action in oUser[updatedRuleId].rule.actions) { - if (!fRequired(action)) { - _results.push(delete oUser[updatedRuleId].actions[action]); - } else { - _results.push(void 0); + if (oUser[updatedRuleId]) { + _results = []; + for (action in oUser[updatedRuleId].rule.actions) { + if (!fRequired(action)) { + _results.push(delete oUser[updatedRuleId].actions[action]); + } else { + _results.push(void 0); + } } + return _results; } - return _results; }; for (name in listUserRules) { oUser = listUserRules[name]; diff --git a/js/persistence.js b/js/persistence.js index c0e2dbb..2ff4e38 100644 --- a/js/persistence.js +++ b/js/persistence.js @@ -839,8 +839,8 @@ Persistence _this.log.info("DB | storeUser: '" + objUser.username + "'"); if (objUser && objUser.username && objUser.password) { _this.db.sadd('users', objUser.username, replyHandler("sadd 'users' -> '" + objUser.username + "'")); - objUser.password = objUser.password; - return _this.db.hmset("user:" + objUser.username, objUser, replyHandler("hmset 'user:" + objUser.username + "' -> [objUser]")); + _this.db.hmset("user:" + objUser.username, objUser, replyHandler("hmset 'user:" + objUser.username + "' -> [objUser]")); + return _this.db.hset("user:" + objUser.username, "roles", JSON.stringify(objUser.roles), replyHandler("hset 'user:" + objUser.username + "' field 'roles' -> [objUser]")); } else { return _this.log.warn(new Error('DB | username or password was missing')); } @@ -874,7 +874,12 @@ Persistence exports.getUser = (function(_this) { return function(userId, cb) { _this.log.info("DB | getUser: '" + userId + "'"); - return _this.db.hgetall("user:" + userId, cb); + return _this.db.hgetall("user:" + userId, function(err, obj) { + try { + obj.roles = JSON.parse(obj.roles); + } catch (_error) {} + return cb(err, obj); + }); }; })(this); @@ -957,6 +962,7 @@ Persistence } else if (obj && obj.password) { if (pw === obj.password) { _this.log.info("DB | User '" + obj.username + "' logged in!"); + obj.roles = JSON.parse(obj.roles); return cb(null, obj); } else { return cb(new Error('Wrong credentials!'), null); diff --git a/js/request-handler.js b/js/request-handler.js index d46dd29..cac23d3 100644 --- a/js/request-handler.js +++ b/js/request-handler.js @@ -11,7 +11,7 @@ Request Handler */ (function() { - var crypto, db, dirHandlers, exports, fs, getHandlerPath, getRemoteScripts, getScript, getTemplate, mustache, path, qs, renderPage; + var crypto, db, dirHandlers, exports, fs, getHandlerPath, getRemoteScripts, getScript, getTemplate, mustache, path, pathUsers, qs, renderPage; db = require('./persistence'); @@ -25,6 +25,8 @@ Request Handler crypto = require('crypto-js'); + pathUsers = path.resolve(__dirname, '..', 'config', 'users.json'); + dirHandlers = path.resolve(__dirname, '..', 'webpages', 'handlers'); exports = module.exports = (function(_this) { @@ -41,10 +43,57 @@ Request Handler }; setTimeout(args['shutdown-function'], 500); return cb(null, data); + }, + newuser: function(obj, cb) { + var data, err, fPersistNewUser, oUser, roles; + data = { + code: 200, + message: 'User stored thank you!' + }; + if (obj.username && obj.password) { + if (obj.roles) { + try { + roles = JSON.parse(obj.roles); + } catch (_error) { + err = _error; + this.log('RH | error parsing newuser roles: ' + err.message); + roles = []; + } + } else { + roles = []; + } + oUser = { + username: obj.username, + password: obj.password, + roles: roles + }; + db.storeUser(oUser); + fPersistNewUser = function(username, password, roles) { + return function(err, data) { + var users; + users = JSON.parse(data); + users[username] = { + password: password, + roles: roles + }; + return fs.writeFile(pathUsers, JSON.stringify(users, void 0, 2), 'utf8', function(err) { + if (err) { + this.log.error("RH | Unable to write new user file! "); + return this.log.error(err); + } + }); + }; + }; + fs.readFile(pathUsers, 'utf8', fPersistNewUser(obj.username, obj.password, roles)); + } else { + data.code = 401; + data.message = 'Missing parameter for this command'; + } + return cb(null, data); } }; db(args); - users = JSON.parse(fs.readFileSync(path.resolve(__dirname, '..', 'config', 'users.json'))); + users = JSON.parse(fs.readFileSync(pathUsers, 'utf8')); fStoreUser = function(username, oUser) { oUser.username = username; return db.storeUser(oUser); @@ -329,9 +378,9 @@ Request Handler var msg, page; if (!req.session.user) { page = 'login'; - } else if (req.session.user.isAdmin !== "true") { + } else if (req.session.user.roles.indexOf("admin") === -1) { page = 'login'; - msg = 'You need to be admin!'; + msg = 'You need to be admin for this page!'; } else { page = 'admin'; } @@ -353,21 +402,31 @@ Request Handler exports.handleAdminCommand = (function(_this) { return function(req, resp) { var body; - if (req.session && req.session.user && req.session.user.isAdmin === "true") { + if (req.session && req.session.user && req.session.user.roles.indexOf("admin") > -1) { body = ''; req.on('data', function(data) { return body += data; }); return req.on('end', function() { - var obj; + var arrCmd, arrKV, arrParams, keyVal, oParams, obj, _i, _len; obj = qs.parse(body); _this.log.info('RH | Received admin request: ' + obj.command); - if (obj.command && _this.objAdminCmds[obj.command]) { - return _this.objAdminCmds[obj.command](obj, function(err, obj) { + arrCmd = obj.command.split(' '); + if (!arrCmd[0] || !_this.objAdminCmds[arrCmd[0]]) { + return resp.send(404, 'Command unknown!'); + } else { + arrParams = arrCmd.slice(1); + oParams = {}; + for (_i = 0, _len = arrParams.length; _i < _len; _i++) { + keyVal = arrParams[_i]; + arrKV = keyVal.split(":"); + if (arrKV.length === 2) { + oParams[arrKV[0]] = arrKV[1]; + } + } + return _this.objAdminCmds[arrCmd[0]](oParams, function(err, obj) { return resp.send(obj.code, obj); }); - } else { - return resp.send(404, 'Command unknown!'); } }); } else { diff --git a/testing/files/testObjects.json b/testing/files/testObjects.json index 5bb808f..3f75f77 100644 --- a/testing/files/testObjects.json +++ b/testing/files/testObjects.json @@ -111,17 +111,18 @@ "users": { "userOne": { "username": "tester-1", - "password": "password-1" + "password": "password-1", + "roles": [] }, "userTwo": { "username": "tester-2", - "password": "password-2" + "password": "password-2", + "roles": [] }, "userAdmin": { "username": "tester-admin", "password": "password-admin", - "roles": [ "admin" ], - "isAdmin": true + "roles": [ "admin" ] } } diff --git a/webpages/handlers/coffee/admin.coffee b/webpages/handlers/coffee/admin.coffee index a7d29b7..916838c 100644 --- a/webpages/handlers/coffee/admin.coffee +++ b/webpages/handlers/coffee/admin.coffee @@ -2,6 +2,11 @@ fOnLoad = () -> document.title = 'Administrate' $( '#pagetitle' ).text 'Hi {{{user.username}}}, issue your commands please:' + + if not window.CryptoJS + $( '#info' ).attr 'class', 'error' + $( '#info' ).text 'CryptoJS library missing! Are you connected to the internet?' + $( '#but_submit' ).click () -> data = command: $( '#inp_command' ).val() @@ -19,4 +24,9 @@ fOnLoad = () -> window.location.href = 'admin' setTimeout fDelayed, 500 + $( '#inp_password' ).keyup () -> + hp = CryptoJS.SHA3 $( this ).val(), + outputLength: 512 + $( '#display_hash' ).text hp.toString() + window.addEventListener 'load', fOnLoad, true diff --git a/webpages/handlers/coffee/forge_rule.coffee b/webpages/handlers/coffee/forge_rule.coffee index 6d0eb93..a4b6801 100644 --- a/webpages/handlers/coffee/forge_rule.coffee +++ b/webpages/handlers/coffee/forge_rule.coffee @@ -514,7 +514,7 @@ fOnLoad = () -> $( '#event_interval' ).val oRule.event_interval # Conditions - editor.setValue JSON.stringify oRule.conditions + editor.setValue JSON.stringify oRule.conditions, undefined, 2 # Actions for action in oRule.actions diff --git a/webpages/handlers/js/admin.js b/webpages/handlers/js/admin.js index bb5fcec..12abbb9 100644 --- a/webpages/handlers/js/admin.js +++ b/webpages/handlers/js/admin.js @@ -5,7 +5,11 @@ fOnLoad = function() { document.title = 'Administrate'; $('#pagetitle').text('Hi {{{user.username}}}, issue your commands please:'); - return $('#but_submit').click(function() { + if (!window.CryptoJS) { + $('#info').attr('class', 'error'); + $('#info').text('CryptoJS library missing! Are you connected to the internet?'); + } + $('#but_submit').click(function() { var data; data = { command: $('#inp_command').val() @@ -28,6 +32,13 @@ return setTimeout(fDelayed, 500); }); }); + return $('#inp_password').keyup(function() { + var hp; + hp = CryptoJS.SHA3($(this).val(), { + outputLength: 512 + }); + return $('#display_hash').text(hp.toString()); + }); }; window.addEventListener('load', fOnLoad, true); diff --git a/webpages/handlers/js/forge_rule.js b/webpages/handlers/js/forge_rule.js index f55b2d1..8bb5c39 100644 --- a/webpages/handlers/js/forge_rule.js +++ b/webpages/handlers/js/forge_rule.js @@ -666,7 +666,7 @@ } $('#input_event').val(oRule.event); $('#event_interval').val(oRule.event_interval); - editor.setValue(JSON.stringify(oRule.conditions)); + editor.setValue(JSON.stringify(oRule.conditions, void 0, 2)); _ref = oRule.actions; _results = []; for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) { diff --git a/webpages/handlers/remote-scripts/admin.html b/webpages/handlers/remote-scripts/admin.html new file mode 100644 index 0000000..bd7e515 --- /dev/null +++ b/webpages/handlers/remote-scripts/admin.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/webpages/handlers/templates/admin.html b/webpages/handlers/templates/admin.html index 5f2b539..d2107fd 100644 --- a/webpages/handlers/templates/admin.html +++ b/webpages/handlers/templates/admin.html @@ -1,2 +1,6 @@ - \ No newline at end of file + +

+

Get your password hashed:

+ +