diff --git a/modules/project/pages.js b/modules/project/pages.js index 529d634..92a7fe0 100644 --- a/modules/project/pages.js +++ b/modules/project/pages.js @@ -30,7 +30,11 @@ var pages = function(dbot) { /* TODO: merge back into github module */ var milestones; request({"url":"https://api.github.com/repos/" + dbot.config.modules.github.defaultrepo + "/milestones?state=open","headers":{"User-Agent":"reality/depressionbot (project module)"}}, function(error, response, body){ - milestones = JSON.parse(body); + try { + milestones = JSON.parse(body); + } catch(err) { + milestones = {}; + } }); diff --git a/modules/report/config.json b/modules/report/config.json index 975bd13..3e32ba1 100644 --- a/modules/report/config.json +++ b/modules/report/config.json @@ -2,5 +2,7 @@ "ignorable": true, "notifyVoice": false, "dependencies": [ "users" ], - "dbType": "redis" + "dbType": "redis", + "requireWebLogin": true, + "webAccess": "power_users" } diff --git a/modules/report/pages.js b/modules/report/pages.js index e8bbc89..c199048 100644 --- a/modules/report/pages.js +++ b/modules/report/pages.js @@ -2,14 +2,14 @@ var _ = require('underscore')._; var pages = function(dbot) { return { - '/notify': function(req, res) { + '/report': function(req, res) { res.render('servers', { 'name': dbot.config.name, 'servers': _.keys(dbot.config.servers) }); }, - '/notify/:server': function(req, res) { + '/report/:server': function(req, res) { var server = req.params.server; res.render('channels', { 'name': dbot.config.name, @@ -18,7 +18,7 @@ var pages = function(dbot) { }); }, - '/notify/:server/:channel': function(req, res) { + '/report/:server/:channel': function(req, res) { var server = req.params.server, channel = req.params.channel, notifies = []; diff --git a/modules/report/report.js b/modules/report/report.js index 2c71fd5..4f43e93 100644 --- a/modules/report/report.js +++ b/modules/report/report.js @@ -50,10 +50,21 @@ var report = function(dbot) { this.listener = function(event) { if(_.has(this.pending, event.rUser.id)) { - _.each(this.pending[event.rUser.id], function(message) { - dbot.say(event.server, event.rUser.currentNick, message); - }); - delete this.pending[event.rUser.id]; + var i=0, + pending = this.pending[event.rUser.id]; + + var notifyUser = function(pending) { + if(i >= msg.length) { + delete this.pending[event.rUser.id]; + return; + } + dbot.say(event.server, pending[i], message); + setTimeout(function() { + i++; notifyUser(pending); + }, 1000); + }.bind(this); + + notifyUser(pending); } }.bind(this); this.on = 'JOIN'; diff --git a/modules/warning/config.json b/modules/warning/config.json index 9fb677f..8f816a0 100644 --- a/modules/warning/config.json +++ b/modules/warning/config.json @@ -1,4 +1,6 @@ { "dependencies": [ "report", "users", "web" ], - "dbType": "redis" + "dbType": "redis", + "requireWebLogin": true, + "webAccess": "power_users" } diff --git a/modules/warning/pages.js b/modules/warning/pages.js index 72c5129..0c4cf08 100644 --- a/modules/warning/pages.js +++ b/modules/warning/pages.js @@ -35,9 +35,9 @@ var pages = function(dbot) { }); }, - '/warning/:server/:user': function(req, res) { + '/warning/:server/:uid': function(req, res) { var server = req.params.server, - user = req.params.user; + user = req.params.uid; dbot.api.users.resolveUser(server, user, function(user) { var warnings = []; @@ -47,16 +47,18 @@ var pages = function(dbot) { }, function(warning) { warnings.push(warning); }, function(err) { + console.log(warnings); async.eachSeries(warnings, function(warning, callback) { dbot.api.users.getUser(warning.warner, function(user) { warning.warner = user.primaryNick; callback(false); }); }, function(err) { + console.log(warnings); res.render('warnings', { 'name': dbot.config.name, 'server': server, - 'warnings': warnings + 'warns': warnings }); }); }); diff --git a/modules/web/config.json b/modules/web/config.json index a92a66f..1015dd8 100644 --- a/modules/web/config.json +++ b/modules/web/config.json @@ -1,5 +1,6 @@ { "webHost": "nourishedcloud.com", "webPort": 8080, - "externalPath": false + "externalPath": false, + "dbType": "redis" } diff --git a/modules/web/strings.json b/modules/web/strings.json new file mode 100644 index 0000000..9cf6415 --- /dev/null +++ b/modules/web/strings.json @@ -0,0 +1,5 @@ +{ + "web_pass_set": { + "en": "Congratulations, your account is now set up to log into the web interface!" + } +} diff --git a/modules/web/web.js b/modules/web/web.js index 75d3c08..14f5c93 100644 --- a/modules/web/web.js +++ b/modules/web/web.js @@ -1,6 +1,10 @@ var express = require('express'), + passport = require('passport'), + passHash = require('password-hash'), + flash = require('connect-flash'), _ = require('underscore')._, - fs = require('fs'); + fs = require('fs'), + LocalStrategy = require('passport-local').Strategy; var webInterface = function(dbot) { this.config = dbot.config.modules.web; @@ -9,6 +13,52 @@ var webInterface = function(dbot) { this.app.use(express.static(this.pub)); this.app.set('view engine', 'jade'); + this.app.use(express.cookieParser()); + this.app.use(express.bodyParser()); + this.app.use(express.methodOverride()); + this.app.use(express.session({ 'secret': 'wat' })); + this.app.use(flash()); + + this.app.use(passport.initialize()); + this.app.use(passport.session()); + this.app.use(this.app.router); + + passport.serializeUser(function(user, done) { + console.log('serialising ' + user); + done(null, user.id); + }); + + passport.deserializeUser(function(id, done) { + dbot.api.users.getUser(id, function(user) { + console.log(id); + console.log(user); + done(null, user); + }); + }); + + passport.use(new LocalStrategy(function(username, password, callback) { + var splitUser = username.split('@'), + server = splitUser[1], + username = splitUser[0]; + + dbot.api.users.resolveUser(server, username, function(user) { + if(user) { + this.api.getWebUser(user.id, function(webUser) { + if(webUser) { + if(passHash.verify(password, webUser.password)) { + return callback(null, user); + } else { + return callback(null, false, { 'message': 'Incorrect password.' }); + } + } else { + return callback(null, false, { 'message': 'Use ~setwebpass to set up your account for web login.' }); + } + }); + } else { + return callback(null, false, { 'message': 'Unknown user' }); + } + }.bind(this)); + }.bind(this))); var server = this.app.listen(this.config.webPort); @@ -18,11 +68,15 @@ var webInterface = function(dbot) { if(_.has(pages, p)) { var func = pages[p]; var mod = func.module; - this.app.get(p, (function(req, resp) { + this.app.get(p, this.api.hasAccess, (function(req, resp) { // Crazy shim to seperate module views. var shim = Object.create(resp); shim.render = (function(view, one, two) { // Render with express.js + _.extend(one, { + 'name': dbot.config.name, + 'user': req.user + }); resp.render(this.module + '/' + view, one, two); }).bind(this); shim.render_core = resp.render; @@ -37,6 +91,7 @@ var webInterface = function(dbot) { var moduleNames = _.keys(dbot.modules); var indexModules = []; + // fix the thingy _.each(moduleNames, function(moduleName) { var modulePath = '/' + moduleName; if(_.include(routes, modulePath)) { @@ -44,15 +99,36 @@ var webInterface = function(dbot) { } }); - console.log(indexModules); - - // TODO: get list of loaded modules this.app.get('/', function(req, res) { res.render('index', { 'name': dbot.config.name, + 'user': req.user, 'routes': indexModules }); }); + + this.app.get('/login', function(req, res) { + res.render('login', { + 'user': req.user, + 'message': req.flash('error') + }); + }); + + this.app.post('/login', passport.authenticate('local', { + 'failureRedirect': '/login', + 'failureFlash': true + }), function(req, res) { + if(req.body.redirect) { + res.redirect(req.body.redirect); + } else { + res.redirect('/'); + } + }); + + this.app.get('/logout', function(req, res) { + req.logout(); + res.redirect('/'); + }); }.bind(this); this.onDestroy = function() { @@ -67,8 +143,70 @@ var webInterface = function(dbot) { } else { return 'http://' + this.config.webHost + ':' + this.config.webPort + '/' + path; } + }, + + 'getWebUser': function(id, callback) { + this.db.read('web_users', id, function(err, webUser) { + callback(webUser); + }); + }, + + 'hasAccess': function(req, res, next) { + var module = req.route.path.split('/')[1]; + module = dbot.modules[module]; + + if(module.config.requireWebLogin == true) { + if(req.isAuthenticated()) { + if(!_.isUndefined(module.config.webAccess)) { + var allowedUsers = dbot.config.admins; + if(module.config.webAccess == 'moderators') { + allowedUsers = _.union(allowedUsers, dbot.config.moderators); + } + if(module.config.webAccess == 'power_users') { + allowedUsers = _.union(allowedUsers, dbot.config.moderators); + allowedUsers = _.union(allowedUsers, dbot.config.power_users); + } + + if(_.include(allowedUsers, req.user.primaryNick)) { + return next(); + } else { + res.redirect('/'); + } + } else { + return next(); + } + } else { + res.render('login', { + 'message': 'You need to log in to access this module.', + 'redirect': module + }); + } + } else { + return next(); + } } }; + + this.commands = { + '~setwebpass': function(event) { + var newPass = event.input[1]; + console.log(newPass); + this.api.getWebUser(event.rUser.id, function(webUser) { + if(!webUser) { + webUser = { + 'id': event.rUser.id, + 'password': false + } + } + webUser.password = passHash.generate(newPass); + + this.db.save('web_users', webUser.id, webUser, function(result) { + event.reply(dbot.t('web_pass_set')); + }); + }.bind(this)); + } + }; + this.commands['~setwebpass'].regex = [/^~setwebpass ([^ ]+)$/, 2] }; exports.fetch = function(dbot) { diff --git a/public/styles.css b/public/styles.css index 8e8dfaa..2dc68a7 100644 --- a/public/styles.css +++ b/public/styles.css @@ -230,8 +230,10 @@ div.imgwrap > img { margin-right:10px; margin-left:10px; } -#footer a { +#bread { float:right; + margin-left: 2px; + margin-right: 2px; padding-left:2px; padding-right:2px; background-color:#f5f5f5; diff --git a/views/layout.jade b/views/layout.jade index c7b9b65..c78c770 100644 --- a/views/layout.jade +++ b/views/layout.jade @@ -16,7 +16,12 @@ html(lang='en') div.container#main block content div#footer - a#project(href='/project') About + p#project + a#bread(href='/project') About + if user + span#bread Logged in as #{user.primaryNick} + else + a#bread(href='/login') Login script(type="text/javascript", src="/bootstrap/js/bootstrap.min.js") script(type="text/javascript", src="/d3/d3.v3.min.js") diff --git a/views/login.jade b/views/login.jade new file mode 100644 index 0000000..940f4b4 --- /dev/null +++ b/views/login.jade @@ -0,0 +1,20 @@ +extends layout + +block content + div#backlink + a(href='/') « Home + br + if message + p #{message} + div#login + form(action='/login', method='post') + input(type="hidden", name="redirect", value=redirect) + p + key Username + input(type="text", name="username") + p + key Password + input(type="password", name="password") + p + input(type="submit", value="Log In") + diff --git a/views/report/channels.jade b/views/report/channels.jade index b2a0b7e..50a74c0 100644 --- a/views/report/channels.jade +++ b/views/report/channels.jade @@ -3,8 +3,8 @@ extends ../layout block content h3 Channels on #{server} div#backlink - a(href='/warning') « Server List + a(href='/report') « Server List ul#quotelist -each channel in channels - a(href='/notify/'+server+'/'+encodeURIComponent(channel)) + a(href='/report/'+server+'/'+encodeURIComponent(channel)) li.quotes #{channel} diff --git a/views/report/notifies.jade b/views/report/notifies.jade index 1b4c04d..c076e4c 100644 --- a/views/report/notifies.jade +++ b/views/report/notifies.jade @@ -2,7 +2,7 @@ extends ../layout block content div#backlink - a(href='/notify/'+server) « Server Channels + a(href='/report/'+server) « Server Channels p div#profile_datatable table.table.table-hover.data diff --git a/views/report/servers.jade b/views/report/servers.jade index 924eb19..3401db0 100644 --- a/views/report/servers.jade +++ b/views/report/servers.jade @@ -6,5 +6,5 @@ block content a(href='/') « Home ul#quotelist -each server in servers - a(href='/notify/'+server) + a(href='/report/'+server) li.quotes #{server} diff --git a/views/warning/warnings.jade b/views/warning/warnings.jade index 18bfc70..a9c8ab6 100644 --- a/views/warning/warnings.jade +++ b/views/warning/warnings.jade @@ -12,7 +12,7 @@ block content th Warner th Reason tbody - for warning, key in warnings + each warning, key in warns tr td #{new Date(warning.time)} td #{warning.warner}