//set some vars var express = require('express'); var cookieParser = require('cookie-parser'); var bodyParser = require('body-parser'); var session = require('cookie-session'); var methodOverride = require('method-override'); var passport = require('passport'); var util = require('util'); // var appName = require('../../package.json').name; var appName = require('./package.json').name || require('../../package.json').name; var ports = require('ports'); var port = ports.getPort(appName + '-hoodie-plugin-social'); var compareVersion = require('compare-version'); // var hoodieServerVer = require('../hoodie-server/package.json').version; var hoodieServerVer = require('hoodie-server/package.json').version || require('../hoodie-server/package.json').version; var facebookStrategy = require('passport-facebook').Strategy; var twitterStrategy = require('passport-twitter').Strategy; var googleStrategy = require('passport-google-oauth').OAuth2Strategy; //from git://github.com/z0mt3c/passport-google-oauth.git var authServer = express(); var auths = {}; var host = null; var socialApi = require('./social_api.js'); var moment = require('moment'); var socialTasks = []; //keeps track of social active tasks
module.exports = function (hoodie, cb) { //check for plugin config items and set if not there if (!hoodie.config.get('port')) hoodie.config.set('port', ports.getPort(appName+'-hoodie-plugin-social')); if (!hoodie.config.get('facebook_config')) hoodie.config.set('facebook_config', {"enabled":false,"settings":{"clientID":"","clientSecret":""}}); if (!hoodie.config.get('twitter_config')) hoodie.config.set('twitter_config', {"enabled":false,"settings":{"consumerKey":"","consumerSecret":""}}); if (!hoodie.config.get('google_config')) hoodie.config.set('google_config', {"enabled":false,"settings":{}}); //get the CouchDB config then setup proxy hoodie.request('get', '_config', {}, function(err, data){ var port = hoodie.config.get('port'); if (!data.httpd_global_handlers._auth || (data.httpd_global_handlers._auth.indexOf(port) == -1)) { var value = '{couch_httpd_proxy, handle_proxy_req, <<"http://0.0.0.0:'+port+'">>}'; hoodie.request('PUT', '_config/httpd_global_handlers/_auth/', {data:JSON.stringify(value)},function(err, data){ if (err) console.log(err); }); } }); //setup base route for front end status calls authServer.get('/', function(req, res) { //set the host directly from front end query parameter //work around until https://github.com/hoodiehq/hoodie-server/issues/183 is resolved if (req.query.uri) host = req.query.uri; //check if we intend to destroy the current auth object if (req.query.destroy == 'true' && req.session.ref) { delete auths[req.session.ref]; req.session.destroy(); res.redirect(host+'/'); return false; } //either send the current auth object or create one if ((req.session.ref != undefined) && (auths[req.session.ref] != undefined)) { res.send(auths[req.session.ref]); } else { setAuthObj(function(ref) { req.session.ref = ref; res.send(auths[req.session.ref]); }); } }); //setup generic authenticate route (redirect destination from specific provider routes) authServer.get('/authenticate', function(req, res) { if (passport._strategies[req.query.provider] == undefined) { invokeStrategy(req.query.provider, res); } else { if (req.query.provider == 'facebook') { passport.authenticate(req.query.provider, { display: 'touch' })(req, res); } else { passport.authenticate(req.query.provider)(req, res); } } }); //setup facebook specific authenicate and callback routes authServer.get('/authenticate/facebook', function(req, res) { res.redirect(host+'/authenticate?provider=facebook'); }); authServer.get('/facebook/callback', passport.authenticate('facebook'), function(req, res) {res.redirect(host+'/callback?provider=facebook');}); //setup twitter specific authenicate and callback routes authServer.get('/authenticate/twitter', function(req, res) { res.redirect(host+'/authenticate?provider=twitter'); }); authServer.get('/twitter/callback', passport.authenticate('twitter'), function(req, res) {res.redirect(host+'/callback?provider=twitter');}); //setup google specific authenicate and callback routes authServer.get('/authenticate/google', function(req, res) { res.redirect(host+'/authenticate?provider=google'); }); authServer.get('/google/callback', passport.authenticate('google'), function(req, res) {res.redirect(host+'/callback?provider=google');}); //setup generic callback route (redirect destination from specific provider routes) authServer.get('/callback', function(req, res) { //generate a auth obj if we need one if (req.session.ref == undefined || (auths[req.session.ref] == undefined)) { setAuthObj(function(ref){ req.session.ref = ref; }); } //if there's no email provided by the provider (like twitter), we will create our own id var id = (req.user.emails == undefined) ? req.user.displayName.replace(' ','_').toLowerCase()+'_'+req.user.id : req.user.emails[0].value; //update the auth object values auths[req.session.ref]['authenticated'] = true; auths[req.session.ref]['provider'] = req.query.provider; auths[req.session.ref]['id'] = id; auths[req.session.ref]['full_profile'] = req.user; delete auths[req.session.ref]['auth_urls']; //check if we have a couch user and act accordingly hoodie.account.find('user', id, function(err, data){ if (!err) { //set the auth time value (used for cleanup) auths[req.session.ref]['auth_time'] = new Date().getTime(); //temporarily change the users password - this is where the magic happens! auths[req.session.ref]['temp_pass'] = Math.random().toString(36).slice(2,11); hoodie.account.update('user', id, {password:auths[req.session.ref]['temp_pass']}, function(err, data){ console.log(data); }); //give the user some visual feedback res.send('<html><head><script src="http://fgnass.github.io/spin.js/dist/spin.min.js"></script></head><body onload="/*self.close();*/" style="margin:0; padding:0; width:100%; height: 100%; display: table;"><div style="display:table-cell; text-align:center; vertical-align: middle;"><div id="spin" style="display:inline-block;"></div></div><script>var spinner=new Spinner().spin(); document.getElementById("spin").appendChild(spinner.el);</script></body></html>'); } else { //assume the error is because the couch user is not there and just create one var uuid = Math.random().toString(36).slice(2,9); var timeStamp = new Date(); var userdoc = { id: id, password: Math.random().toString(36).slice(2,11), ownerHash: uuid, createdAt: timeStamp, updatedAt: timeStamp, signedUpAt: timeStamp, database: 'user/'+uuid, name: 'user/'+id }; hoodie.account.add('user', userdoc, function(err, data){ //cycle back through so we can catch the fully created user if (!err) res.redirect(host+'/'+req.query.provider+'/callback'); }); } }); }); //No need to keep this stuff around, so lets clean up after ourselves var cleanupInterval = setInterval(function() {cleanupAuths();},15000); function cleanupAuths() { var now = new Date().getTime(); for(var i in auths) { if (now - auths[i].auth_time >= 30000) { delete auths[i]; } } } //function to invoke a strategy function invokeStrategy(provider, res) { config = hoodie.config.get(provider+'_config'); if (config.enabled) { settings = config.settings; settings['failureRedirect'] = '/fail'; //todo - set this route up if (provider == 'facebook') { settings['callbackURL'] = host+'/facebook/callback'; var providerStrategy = facebookStrategy; var verify = function(accessToken,refreshToken,profile,done){process.nextTick(function(){return done(null,profile);});} } else if (provider == 'twitter') { settings['callbackURL'] = host+'/twitter/callback'; var providerStrategy = twitterStrategy; var verify = function(accessToken,refreshToken,profile,done){process.nextTick(function(){return done(null,profile);});} } else if (provider == 'google') { settings['returnURL'] = host+'/google/callback'; settings['realm'] = host; var providerStrategy = googleStrategy; var verify = function(identifier,profile,done){process.nextTick(function(){return done(null,profile);});} } passport.use(new providerStrategy(settings,verify)); res.redirect(host+'/authenticate/'+provider); } else { res.send('Provider not configured'); return false; } } //function to assign a an auth object function setAuthObj(callback) { //generate random reference ID var ref = Math.random().toString(36).slice(2); //set a new request object for tracking progress auths[ref] = { "requested": new Date().getTime(), "authenticated":false, "auth_urls": { "facebook":host+"/authenticate/facebook", "twitter":host+"/authenticate/twitter", "google":host+"/authenticate/google" } }; callback(ref); } //start the server on load var port = hoodie.config.get('port'); authServer.listen(port); console.log('Hoodie Social Plugin: Listening on port '+port); //Hoodie Callback cb(); }
exports.getConfig = function (platform, env, project_dir, argv) { // location of project's package.json var pkgfile = path.resolve(project_dir, 'package.json'); // default platform-agnostic config var cfg = { project_dir: project_dir, host: process.env.HOODIE_BIND_ADDRESS || '127.0.0.1', app: JSON.parse(fs.readFileSync(pkgfile)), domain: 'dev', local_tld: false, platform: platform, open_browser: true }; // set the Hoodie instance ID, this is used to check // against the user roles when doing a test for Hoodie admin cfg.id = cfg.app.name; cfg.www_port = ports.getPort(cfg.id + '-www'); cfg.admin_port = ports.getPort(cfg.id + '-admin'); // add CouchDB paths to config cfg = _.extend(cfg, exports.getCouch(env)); // add Hoodie paths to config cfg.hoodie = { app_path: path.resolve(cfg.project_dir, 'data') }; // do magic firewall stuff for .dev domains on mac if (platform === 'darwin') { // only if it is installed try { require('local-tld'); cfg.local_tld = true; } catch(e) { // no local-tld, it’s fine, carry on. } } // option to turn on/off local-tld on command-line if (argv.hasOwnProperty('local-tld')) { cfg.local_tld = argv['local-tld']; } if (exports.isNodejitsu(env)) { // Nodejitsu config cfg.host = '0.0.0.0'; cfg.domain = 'jit.su'; cfg.couch.run = false; // move the www and admin ports and run a nodejitsu server // to proxy requests to them based on subdomains (since we don't // run local-tld on nodejitsu) cfg.www_port = 8180; cfg.admin_port = 8190; cfg.run_nodejitsu_server = true; cfg.open_browser = false; // turn off fancy terminal stuff where possible cfg.boring = true; if (!env.COUCH_URL) { throw new Error('COUCH_URL environment variable not set'); } if (!env.HOODIE_ADMIN_USER) { throw new Error('HOODIE_ADMIN_USER environment variable not set'); } if (!env.HOODIE_ADMIN_PASS) { throw new Error('HOODIE_ADMIN_PASS environment variable not set'); } } if(!process.env.COUCH_URL) { cfg.couch.port = ports.getPort(cfg.id + '-couch'); } // option to turn on/off open browser on command-line if (argv.hasOwnProperty('open-browser')) { cfg.open_browser = argv['open-browser']; } if (process.env.CI) { cfg.open_browser = false; } if (!cfg.couch.url) { cfg.couch.url = 'http://' + cfg.host + ':' + cfg.couch.port; } // get the host for couchb url var parsed = url.parse(cfg.couch.url); cfg.couch.host = parsed.hostname; cfg.couch.port = parsed.port || 80; return cfg; };