merge with letsencrypt branch
This commit is contained in:
		
						commit
						28db03ae23
					
				
							
								
								
									
										65
									
								
								bin/walnut
									
									
									
									
									
								
							
							
						
						
									
										65
									
								
								bin/walnut
									
									
									
									
									
								
							@ -1,65 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env node
 | 
					 | 
				
			||||||
'use strict';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
require('../walnut.js');
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
var c = require('console-plus');
 | 
					 | 
				
			||||||
console.log = c.log;
 | 
					 | 
				
			||||||
console.error = c.error;
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function eagerLoad() {
 | 
					 | 
				
			||||||
  var PromiseA = require('bluebird').Promise;
 | 
					 | 
				
			||||||
  var promise = PromiseA.resolve();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ 'express'
 | 
					 | 
				
			||||||
  , 'request'
 | 
					 | 
				
			||||||
  , 'sqlite3'
 | 
					 | 
				
			||||||
  , 'body-parser'
 | 
					 | 
				
			||||||
  , 'urlrouter'
 | 
					 | 
				
			||||||
  , 'express-lazy'
 | 
					 | 
				
			||||||
  , 'connect-send-error'
 | 
					 | 
				
			||||||
  , 'underscore.string'
 | 
					 | 
				
			||||||
  , 'secret-utils'
 | 
					 | 
				
			||||||
  , 'connect-cors'
 | 
					 | 
				
			||||||
  , 'uuid'
 | 
					 | 
				
			||||||
  , 'connect-recase'
 | 
					 | 
				
			||||||
  , 'escape-string-regexp'
 | 
					 | 
				
			||||||
  , 'connect-query'
 | 
					 | 
				
			||||||
  , 'recase'
 | 
					 | 
				
			||||||
  ].forEach(function (name/*, i*/) {
 | 
					 | 
				
			||||||
    promise = promise.then(function () {
 | 
					 | 
				
			||||||
      return new PromiseA(function (resolve/*, reject*/) {
 | 
					 | 
				
			||||||
        setTimeout(function () {
 | 
					 | 
				
			||||||
          require(name);
 | 
					 | 
				
			||||||
          resolve();
 | 
					 | 
				
			||||||
        }, 4);
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ function () {
 | 
					 | 
				
			||||||
      require('body-parser').json();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    /*
 | 
					 | 
				
			||||||
    // do not use urlencoded as it enables csrf
 | 
					 | 
				
			||||||
  , function () {
 | 
					 | 
				
			||||||
      require('body-parser').urlencoded();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    */
 | 
					 | 
				
			||||||
  ].forEach(function (fn) {
 | 
					 | 
				
			||||||
    promise = promise.then(function (thing) {
 | 
					 | 
				
			||||||
      return new PromiseA(function (resolve) {
 | 
					 | 
				
			||||||
        setTimeout(function () {
 | 
					 | 
				
			||||||
         resolve(fn(thing));
 | 
					 | 
				
			||||||
        }, 4);
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  promise.then(function () {
 | 
					 | 
				
			||||||
    console.log('Eager Loading Complete');
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
setTimeout(eagerLoad, 100);
 | 
					 | 
				
			||||||
							
								
								
									
										1
									
								
								bin/walnut
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								bin/walnut
									
									
									
									
									
										Symbolic link
									
								
							@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					walnut.js
 | 
				
			||||||
@ -1 +0,0 @@
 | 
				
			|||||||
walnut
 | 
					 | 
				
			||||||
							
								
								
									
										65
									
								
								bin/walnut.js
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										65
									
								
								bin/walnut.js
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,65 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env node
 | 
				
			||||||
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require('../walnut.js');
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					var c = require('console-plus');
 | 
				
			||||||
 | 
					console.log = c.log;
 | 
				
			||||||
 | 
					console.error = c.error;
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function eagerLoad() {
 | 
				
			||||||
 | 
					  var PromiseA = require('bluebird').Promise;
 | 
				
			||||||
 | 
					  var promise = PromiseA.resolve();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [ 'express'
 | 
				
			||||||
 | 
					  , 'request'
 | 
				
			||||||
 | 
					  , 'sqlite3'
 | 
				
			||||||
 | 
					  , 'body-parser'
 | 
				
			||||||
 | 
					  , 'urlrouter'
 | 
				
			||||||
 | 
					  , 'express-lazy'
 | 
				
			||||||
 | 
					  , 'connect-send-error'
 | 
				
			||||||
 | 
					  , 'underscore.string'
 | 
				
			||||||
 | 
					  , 'secret-utils'
 | 
				
			||||||
 | 
					  , 'connect-cors'
 | 
				
			||||||
 | 
					  , 'uuid'
 | 
				
			||||||
 | 
					  , 'connect-recase'
 | 
				
			||||||
 | 
					  , 'escape-string-regexp'
 | 
				
			||||||
 | 
					  , 'connect-query'
 | 
				
			||||||
 | 
					  , 'recase'
 | 
				
			||||||
 | 
					  ].forEach(function (name/*, i*/) {
 | 
				
			||||||
 | 
					    promise = promise.then(function () {
 | 
				
			||||||
 | 
					      return new PromiseA(function (resolve/*, reject*/) {
 | 
				
			||||||
 | 
					        setTimeout(function () {
 | 
				
			||||||
 | 
					          require(name);
 | 
				
			||||||
 | 
					          resolve();
 | 
				
			||||||
 | 
					        }, 4);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [ function () {
 | 
				
			||||||
 | 
					      require('body-parser').json();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					    // do not use urlencoded as it enables csrf
 | 
				
			||||||
 | 
					  , function () {
 | 
				
			||||||
 | 
					      require('body-parser').urlencoded();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					  ].forEach(function (fn) {
 | 
				
			||||||
 | 
					    promise = promise.then(function (thing) {
 | 
				
			||||||
 | 
					      return new PromiseA(function (resolve) {
 | 
				
			||||||
 | 
					        setTimeout(function () {
 | 
				
			||||||
 | 
					         resolve(fn(thing));
 | 
				
			||||||
 | 
					        }, 4);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  promise.then(function () {
 | 
				
			||||||
 | 
					    console.log('Eager Loading Complete');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					setTimeout(eagerLoad, 100);
 | 
				
			||||||
							
								
								
									
										100
									
								
								boot/master.js
									
									
									
									
									
								
							
							
						
						
									
										100
									
								
								boot/master.js
									
									
									
									
									
								
							@ -9,12 +9,46 @@ console.info('arch:', process.arch);
 | 
				
			|||||||
console.info('platform:', process.platform);
 | 
					console.info('platform:', process.platform);
 | 
				
			||||||
console.info('\n\n\n[MASTER] Welcome to WALNUT!');
 | 
					console.info('\n\n\n[MASTER] Welcome to WALNUT!');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function tryConf(pathname, def) {
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    return require(pathname);
 | 
				
			||||||
 | 
					  } catch(e) {
 | 
				
			||||||
 | 
					    return def;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var path = require('path');
 | 
					var path = require('path');
 | 
				
			||||||
var cluster = require('cluster');
 | 
					var cluster = require('cluster');
 | 
				
			||||||
//var minWorkers = 2;
 | 
					//var minWorkers = 2;
 | 
				
			||||||
var numCores = 2; // Math.max(minWorkers, require('os').cpus().length);
 | 
					var numCores = 2; // Math.max(minWorkers, require('os').cpus().length);
 | 
				
			||||||
var workers = [];
 | 
					var workers = [];
 | 
				
			||||||
var state = { firstRun: true };
 | 
					var state = { firstRun: true };
 | 
				
			||||||
 | 
					// TODO Should these be configurable? If so, where?
 | 
				
			||||||
 | 
					// TODO communicate config with environment vars?
 | 
				
			||||||
 | 
					var caddy = tryConf(
 | 
				
			||||||
 | 
					  path.join('..', '..', 'config.caddy.json')
 | 
				
			||||||
 | 
					, { conf: null        // __dirname + '/Caddyfile'
 | 
				
			||||||
 | 
					  , bin: null         // '/usr/local/bin/caddy'
 | 
				
			||||||
 | 
					  , sitespath: null   // path.join(__dirname, 'sites-enabled')
 | 
				
			||||||
 | 
					  , locked: false     // true
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					var useCaddy = require('fs').existsSync(caddy.bin);
 | 
				
			||||||
 | 
					var info = {
 | 
				
			||||||
 | 
					  type: 'walnut.init'
 | 
				
			||||||
 | 
					, conf: {
 | 
				
			||||||
 | 
					    protocol: useCaddy ? 'http' : 'https'
 | 
				
			||||||
 | 
					  , externalPort: 443
 | 
				
			||||||
 | 
					  , externalPortInsecure: 80 // TODO externalInsecurePort
 | 
				
			||||||
 | 
					  , localPort: process.argv[2] || (useCaddy ? 4080 : 443) // system / local network
 | 
				
			||||||
 | 
					  , insecurePort: process.argv[3] || (useCaddy ? 80 : 80) // meh
 | 
				
			||||||
 | 
					  , certPaths: useCaddy ? null : [
 | 
				
			||||||
 | 
					      path.join(__dirname, '..', '..', 'certs', 'live')
 | 
				
			||||||
 | 
					    , path.join(__dirname, '..', '..', 'letsencrypt', 'live')
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					  , trustProxy: useCaddy ? true : false
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function fork() {
 | 
					function fork() {
 | 
				
			||||||
  if (workers.length < numCores) {
 | 
					  if (workers.length < numCores) {
 | 
				
			||||||
@ -26,53 +60,15 @@ cluster.on('online', function (worker) {
 | 
				
			|||||||
  console.info('[MASTER] Worker ' + worker.process.pid + ' is online');
 | 
					  console.info('[MASTER] Worker ' + worker.process.pid + ' is online');
 | 
				
			||||||
  fork();
 | 
					  fork();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  var config = {
 | 
					 | 
				
			||||||
    externalPort: 443                                       // world accessible
 | 
					 | 
				
			||||||
  , externalPortInsecure: 80                                // world accessible
 | 
					 | 
				
			||||||
  // TODO externalInsecurePort?
 | 
					 | 
				
			||||||
  , locked: false // TODO XXX
 | 
					 | 
				
			||||||
    // XXX
 | 
					 | 
				
			||||||
    // TODO needs mappings from db
 | 
					 | 
				
			||||||
    // TODO autoconfig Caddy caddy
 | 
					 | 
				
			||||||
    // XXX
 | 
					 | 
				
			||||||
  , caddy: {
 | 
					 | 
				
			||||||
      conf: __dirname + '/Caddyfile'
 | 
					 | 
				
			||||||
    , bin: '/usr/local/bin/caddy'
 | 
					 | 
				
			||||||
    , sitespath: path.join(__dirname, 'sites-enabled')
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  var useCaddy = require('fs').existsSync(config.caddy.bin);
 | 
					 | 
				
			||||||
  var caddy;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  config.localPort = process.argv[2] || (useCaddy ? 4080 : 443);   // system / local network
 | 
					 | 
				
			||||||
  config.insecurePort = process.argv[3] || (useCaddy ? 80 : 80);   // meh
 | 
					 | 
				
			||||||
  if (state.firstRun) {
 | 
					  if (state.firstRun) {
 | 
				
			||||||
    state.firstRun = false;
 | 
					    state.firstRun = false;
 | 
				
			||||||
    if (useCaddy) {
 | 
					    if (useCaddy) {
 | 
				
			||||||
      caddy = require('../lib/spawn-caddy').create(config);
 | 
					      caddy = require('../lib/spawn-caddy').create(caddy);
 | 
				
			||||||
      // relies on { localPort, locked }
 | 
					      // relies on { localPort, locked }
 | 
				
			||||||
      caddy.spawn(config);
 | 
					      caddy.spawn(caddy);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // TODO XXX Should these be configurable? If so, where?
 | 
					 | 
				
			||||||
  var certPaths = [
 | 
					 | 
				
			||||||
    path.join(__dirname, '..', '..', 'certs', 'live')
 | 
					 | 
				
			||||||
  , path.join(__dirname, '..', '..', 'letsencrypt', 'live')
 | 
					 | 
				
			||||||
  ];
 | 
					 | 
				
			||||||
  // TODO communicate config with environment vars?
 | 
					 | 
				
			||||||
  var info = {
 | 
					 | 
				
			||||||
    type: 'walnut.init'
 | 
					 | 
				
			||||||
  , conf: {
 | 
					 | 
				
			||||||
      protocol: useCaddy ? 'http' : 'https'
 | 
					 | 
				
			||||||
    , externalPort: config.externalPort
 | 
					 | 
				
			||||||
    , localPort: config.localPort
 | 
					 | 
				
			||||||
    , insecurePort: config.insecurePort
 | 
					 | 
				
			||||||
    , certPaths: useCaddy ? null : certPaths
 | 
					 | 
				
			||||||
    , trustProxy: useCaddy ? true : false
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  function touchMaster(msg) {
 | 
					  function touchMaster(msg) {
 | 
				
			||||||
    if ('walnut.webserver.listening' !== msg.type) {
 | 
					    if ('walnut.webserver.listening' !== msg.type) {
 | 
				
			||||||
      console.warn('[MASTER] received unexpected message from worker');
 | 
					      console.warn('[MASTER] received unexpected message from worker');
 | 
				
			||||||
@ -83,19 +79,19 @@ cluster.on('online', function (worker) {
 | 
				
			|||||||
    // calls init if init has not been called
 | 
					    // calls init if init has not been called
 | 
				
			||||||
    state.caddy = caddy;
 | 
					    state.caddy = caddy;
 | 
				
			||||||
    state.workers = workers;
 | 
					    state.workers = workers;
 | 
				
			||||||
    require('../lib/master').touch(config, state).then(function (results) {
 | 
					    require('../lib/master').touch(info.conf, state).then(function (results) {
 | 
				
			||||||
      //var memstore = results.memstore;
 | 
					      //var memstore = results.memstore;
 | 
				
			||||||
      var sqlstore = results.sqlstore;
 | 
					      var sqlstore = results.sqlstore;
 | 
				
			||||||
      info.type = 'walnut.webserver.onrequest';
 | 
					      info.type = 'walnut.webserver.onrequest';
 | 
				
			||||||
      // TODO let this load after server is listening
 | 
					      // TODO let this load after server is listening
 | 
				
			||||||
      info.conf['org.oauth3.consumer'] = config['org.oauth3.consumer'];
 | 
					      info.conf['org.oauth3.consumer'] = results['org.oauth3.consumer'];
 | 
				
			||||||
      info.conf['org.oauth3.provider'] = config['org.oauth3.provider'];
 | 
					      info.conf['org.oauth3.provider'] = results['org.oauth3.provider'];
 | 
				
			||||||
      info.conf.keys = config.keys;
 | 
					      info.conf.keys = results.keys;
 | 
				
			||||||
      info.conf.memstoreSock = config.memstoreSock;
 | 
					      //info.conf.memstoreSock = config.memstoreSock;
 | 
				
			||||||
      info.conf.sqlite3Sock = config.sqlite3Sock;
 | 
					      //info.conf.sqlite3Sock = config.sqlite3Sock;
 | 
				
			||||||
      // TODO get this from db config instead
 | 
					      // TODO get this from db config instead
 | 
				
			||||||
      info.conf.privkey = config.privkey;
 | 
					      //info.conf.privkey = config.privkey;
 | 
				
			||||||
      info.conf.pubkey = config.pubkey;
 | 
					      //info.conf.pubkey = config.pubkey;
 | 
				
			||||||
      info.conf.redirects = [
 | 
					      info.conf.redirects = [
 | 
				
			||||||
        { "ip": false, "id": "*", "value": false } // default no-www
 | 
					        { "ip": false, "id": "*", "value": false } // default no-www
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -116,10 +112,10 @@ cluster.on('online', function (worker) {
 | 
				
			|||||||
      // TODO use sqlite3 or autogenerate ?
 | 
					      // TODO use sqlite3 or autogenerate ?
 | 
				
			||||||
      info.conf.privkey = require('fs').readFileSync(__dirname + '/../../' + '/nsx.redirect-www.org.key.pem', 'ascii');
 | 
					      info.conf.privkey = require('fs').readFileSync(__dirname + '/../../' + '/nsx.redirect-www.org.key.pem', 'ascii');
 | 
				
			||||||
      info.conf.pubkey = require('fs').readFileSync(__dirname + '/../../' + '/nsx.redirect-www.org.key.pem.pub', 'ascii');
 | 
					      info.conf.pubkey = require('fs').readFileSync(__dirname + '/../../' + '/nsx.redirect-www.org.key.pem.pub', 'ascii');
 | 
				
			||||||
  // keys
 | 
					      // keys
 | 
				
			||||||
  // letsencrypt
 | 
					      // letsencrypt
 | 
				
			||||||
  // com.example.provider
 | 
					      // com.example.provider
 | 
				
			||||||
  // com.example.consumer
 | 
					      // com.example.consumer
 | 
				
			||||||
      worker.send(info);
 | 
					      worker.send(info);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -4,9 +4,9 @@ module.exports.create = function (opts) {
 | 
				
			|||||||
  var id = '0';
 | 
					  var id = '0';
 | 
				
			||||||
  var promiseApp;
 | 
					  var promiseApp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  function createAndBindInsecure(message, cb) {
 | 
					  function createAndBindInsecure(lex, message, cb) {
 | 
				
			||||||
    // TODO conditional if 80 is being served by caddy
 | 
					    // TODO conditional if 80 is being served by caddy
 | 
				
			||||||
    require('../lib/insecure-server').create(message.conf.externalPort, message.conf.insecurePort, message, function (err, webserver) {
 | 
					    require('../lib/insecure-server').create(lex, message.conf.externalPort, message.conf.insecurePort, message, function (err, webserver) {
 | 
				
			||||||
      console.info("#" + id + " Listening on http://" + webserver.address().address + ":" + webserver.address().port, '\n');
 | 
					      console.info("#" + id + " Listening on http://" + webserver.address().address + ":" + webserver.address().port, '\n');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // we are returning the promise result to the caller
 | 
					      // we are returning the promise result to the caller
 | 
				
			||||||
@ -14,9 +14,48 @@ module.exports.create = function (opts) {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function createLe(conf) {
 | 
				
			||||||
 | 
					    var LEX = require('letsencrypt-express');
 | 
				
			||||||
 | 
					    var lex = LEX.create({
 | 
				
			||||||
 | 
					      configDir: conf.letsencrypt.configDir // i.e. __dirname + '/letsencrypt.config'
 | 
				
			||||||
 | 
					    , approveRegistration: function (hostname, cb) {
 | 
				
			||||||
 | 
					        cb(null, {
 | 
				
			||||||
 | 
					          domains: [hostname]                 // TODO handle www and bare on the same cert
 | 
				
			||||||
 | 
					        , email: conf.letsencrypt.email
 | 
				
			||||||
 | 
					        , agreeTos: conf.letsencrypt.agreeTos
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        /*
 | 
				
			||||||
 | 
					        letsencrypt.getConfig({ domains: [domain] }, function (err, config) {
 | 
				
			||||||
 | 
					          if (!(config && config.checkpoints >= 0)) {
 | 
				
			||||||
 | 
					            cb(err, null);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          cb(null, {
 | 
				
			||||||
 | 
					            email: config.email
 | 
				
			||||||
 | 
					                // can't remember which it is, but the pyconf is different that the regular variable
 | 
				
			||||||
 | 
					          , agreeTos: config.tos || config.agree || config.agreeTos
 | 
				
			||||||
 | 
					          , server: config.server || LE.productionServerUrl
 | 
				
			||||||
 | 
					          , domains: config.domains || [domain]
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        */
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    //var letsencrypt = lex.letsencrypt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return lex;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  function createAndBindServers(message, cb) {
 | 
					  function createAndBindServers(message, cb) {
 | 
				
			||||||
 | 
					    var lex;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (message.conf.letsencrypt) {
 | 
				
			||||||
 | 
					      lex = createLe(message.conf);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // NOTE that message.conf[x] will be overwritten when the next message comes in
 | 
					    // NOTE that message.conf[x] will be overwritten when the next message comes in
 | 
				
			||||||
    require('../lib/local-server').create(message.conf.certPaths, message.conf.localPort, message, function (err, webserver) {
 | 
					    require('../lib/local-server').create(lex, message.conf.certPaths, message.conf.localPort, message, function (err, webserver) {
 | 
				
			||||||
      if (err) {
 | 
					      if (err) {
 | 
				
			||||||
        console.error('[ERROR] worker.js');
 | 
					        console.error('[ERROR] worker.js');
 | 
				
			||||||
        console.error(err.stack);
 | 
					        console.error(err.stack);
 | 
				
			||||||
@ -27,7 +66,7 @@ module.exports.create = function (opts) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      // we don't need time to pass, just to be able to return
 | 
					      // we don't need time to pass, just to be able to return
 | 
				
			||||||
      process.nextTick(function () {
 | 
					      process.nextTick(function () {
 | 
				
			||||||
        createAndBindInsecure(message, cb);
 | 
					        createAndBindInsecure(lex, message, cb);
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // we are returning the promise result to the caller
 | 
					      // we are returning the promise result to the caller
 | 
				
			||||||
@ -67,7 +106,7 @@ module.exports.create = function (opts) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
          process.removeListener('message', initWebServer);
 | 
					          process.removeListener('message', initWebServer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          resolve(require('../lib/worker').create(webserver, srvmsg));
 | 
					          resolve(require('../lib/worker').create(webserver, srvmsg.conf));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        process.send({ type: 'walnut.webserver.listening' });
 | 
					        process.send({ type: 'walnut.webserver.listening' });
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
'use strict';
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports.create = function (securePort, insecurePort, info, serverCallback) {
 | 
					module.exports.create = function (lex, securePort, insecurePort, info, serverCallback) {
 | 
				
			||||||
  var PromiseA = require('bluebird').Promise;
 | 
					  var PromiseA = require('bluebird').Promise;
 | 
				
			||||||
  var appPromise;
 | 
					  var appPromise;
 | 
				
			||||||
  //var app;
 | 
					  //var app;
 | 
				
			||||||
@ -101,7 +101,13 @@ module.exports.create = function (securePort, insecurePort, info, serverCallback
 | 
				
			|||||||
      appPromise = serverCallback(null, insecureServer);
 | 
					      appPromise = serverCallback(null, insecureServer);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
  insecureServer.on('request', redirectHttps);
 | 
					
 | 
				
			||||||
 | 
					  if (lex) {
 | 
				
			||||||
 | 
					    var LEX = require('letsencrypt-express');
 | 
				
			||||||
 | 
					    insecureServer.on('request', LEX.createAcmeResponder(lex, redirectHttps));
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    insecureServer.on('request', redirectHttps);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return PromiseA.resolve(insecureServer);
 | 
					  return PromiseA.resolve(insecureServer);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Note the odd use of callbacks (instead of promises) here
 | 
					// Note the odd use of callbacks (instead of promises) here
 | 
				
			||||||
// It's to avoid loading bluebird yet (see sni-server.js for explanation)
 | 
					// It's to avoid loading bluebird yet (see sni-server.js for explanation)
 | 
				
			||||||
module.exports.create = function (certPaths, port, info, serverCallback) {
 | 
					module.exports.create = function (lex, certPaths, port, info, serverCallback) {
 | 
				
			||||||
  function initServer(err, server) {
 | 
					  function initServer(err, server) {
 | 
				
			||||||
    var app;
 | 
					    var app;
 | 
				
			||||||
    var promiseApp;
 | 
					    var promiseApp;
 | 
				
			||||||
@ -29,7 +29,7 @@ module.exports.create = function (certPaths, port, info, serverCallback) {
 | 
				
			|||||||
    */
 | 
					    */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Get up and listening as absolutely quickly as possible
 | 
					    // Get up and listening as absolutely quickly as possible
 | 
				
			||||||
    server.on('request', function (req, res) {
 | 
					    function onRequest(req, res) {
 | 
				
			||||||
      // this is a hot piece of code, so we cache the result
 | 
					      // this is a hot piece of code, so we cache the result
 | 
				
			||||||
      if (app) {
 | 
					      if (app) {
 | 
				
			||||||
        app(req, res);
 | 
					        app(req, res);
 | 
				
			||||||
@ -41,11 +41,18 @@ module.exports.create = function (certPaths, port, info, serverCallback) {
 | 
				
			|||||||
        app = _app;
 | 
					        app = _app;
 | 
				
			||||||
        app(req, res);
 | 
					        app(req, res);
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (lex) {
 | 
				
			||||||
 | 
					      var LEX = require('letsencrypt-express');
 | 
				
			||||||
 | 
					      server.on('request', LEX.createAcmeResponder(lex, onRequest));
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      server.on('request', onRequest);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (certPaths) {
 | 
					  if (certPaths) {
 | 
				
			||||||
    require('./sni-server').create(certPaths, initServer);
 | 
					    require('./sni-server').create(lex, certPaths, initServer);
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    initServer(null, require('http').createServer());
 | 
					    initServer(null, require('http').createServer());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,407 +0,0 @@
 | 
				
			|||||||
'use strict';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var PromiseA = require('bluebird');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
module.exports.inject = function (conf, app, pkgConf, pkgDeps) {
 | 
					 | 
				
			||||||
  var scoper = require('app-scoped-ids');
 | 
					 | 
				
			||||||
  var inProcessCache = {};
 | 
					 | 
				
			||||||
  var createClientFactory = require('sqlite3-cluster/client').createClientFactory;
 | 
					 | 
				
			||||||
  var dir = [
 | 
					 | 
				
			||||||
    { tablename: 'codes'
 | 
					 | 
				
			||||||
    , idname: 'uuid'
 | 
					 | 
				
			||||||
    , indices: ['createdAt']
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  , { tablename: 'logins' // coolaj86, coolaj86@gmail.com, +1-317-426-6525
 | 
					 | 
				
			||||||
    , idname: 'hashId'
 | 
					 | 
				
			||||||
    //, relations: [{ tablename: 'secrets', id: 'hashid', fk: 'loginId' }]
 | 
					 | 
				
			||||||
    , indices: ['createdAt', 'type', 'node']
 | 
					 | 
				
			||||||
    //, immutable: false
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  , { tablename: 'verifications'
 | 
					 | 
				
			||||||
    , idname: 'hashId' // hash(date + node)
 | 
					 | 
				
			||||||
    //, relations: [{ tablename: 'secrets', id: 'hashid', fk: 'loginId' }]
 | 
					 | 
				
			||||||
    , indices: ['createdAt', 'nodeId']
 | 
					 | 
				
			||||||
    //, immutable: true
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  , { tablename: 'secrets'
 | 
					 | 
				
			||||||
    , idname: 'hashId' // hash(node + secret)
 | 
					 | 
				
			||||||
    , indices: ['createdAt']
 | 
					 | 
				
			||||||
    //, immutable: true
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  , { tablename: 'recoveryNodes' // just for 1st-party logins
 | 
					 | 
				
			||||||
    , idname: 'hashId' //
 | 
					 | 
				
			||||||
      // TODO how transmit that something should be deleted / disabled?
 | 
					 | 
				
			||||||
    , indices: ['createdAt', 'updatedAt', 'loginHash', 'recoveryNode', 'deleted']
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    //
 | 
					 | 
				
			||||||
    // Accounts
 | 
					 | 
				
			||||||
    //
 | 
					 | 
				
			||||||
  , { tablename: 'accounts_logins'
 | 
					 | 
				
			||||||
    , idname: 'id' // hash(accountId + loginId)
 | 
					 | 
				
			||||||
    , indices: ['createdAt', 'revokedAt', 'loginId', 'accountId']
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  , { tablename: 'accounts'
 | 
					 | 
				
			||||||
    , idname: 'id' // crypto random id? or hash(name) ?
 | 
					 | 
				
			||||||
    , unique: ['name']
 | 
					 | 
				
			||||||
    , indices: ['createdAt', 'updatedAt', 'deletedAt', 'name', 'displayName']
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    //
 | 
					 | 
				
			||||||
    // OAuth3
 | 
					 | 
				
			||||||
    //
 | 
					 | 
				
			||||||
  , { tablename: 'private_key'
 | 
					 | 
				
			||||||
    , idname: 'id'
 | 
					 | 
				
			||||||
    , indices: ['createdAt']
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  , { tablename: 'oauth_clients'
 | 
					 | 
				
			||||||
    , idname: 'id'
 | 
					 | 
				
			||||||
    , indices: ['createdAt', 'updatedAt', 'accountId']
 | 
					 | 
				
			||||||
    , hasMany: ['apiKeys'] // TODO
 | 
					 | 
				
			||||||
    , belongsTo: ['account']
 | 
					 | 
				
			||||||
    , schema: function () {
 | 
					 | 
				
			||||||
        return {
 | 
					 | 
				
			||||||
          test: true
 | 
					 | 
				
			||||||
        , insecure: true
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  , { tablename: 'api_keys'
 | 
					 | 
				
			||||||
    , idname: 'id'
 | 
					 | 
				
			||||||
    , indices: ['createdAt', 'updatedAt', 'oauthClientId']
 | 
					 | 
				
			||||||
    , belongsTo: ['oauthClient'] // TODO pluralization
 | 
					 | 
				
			||||||
    , schema: function () {
 | 
					 | 
				
			||||||
        return {
 | 
					 | 
				
			||||||
          test: true
 | 
					 | 
				
			||||||
        , insecure: true
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  , { tablename: 'tokens' // note that a token functions as a session
 | 
					 | 
				
			||||||
    , idname: 'id'
 | 
					 | 
				
			||||||
    , indices: ['createdAt', 'updatedAt', 'expiresAt', 'revokedAt', 'oauthClientId', 'loginId', 'accountId']
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  , { tablename: 'grants'
 | 
					 | 
				
			||||||
    , idname: 'id' // sha256(scope + oauthClientId + (accountId || loginId))
 | 
					 | 
				
			||||||
    , indices: ['createdAt', 'updatedAt', 'oauthClientId', 'loginId', 'accountId']
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  ];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  function getAppScopedControllers(experienceId) {
 | 
					 | 
				
			||||||
    if (inProcessCache[experienceId]) {
 | 
					 | 
				
			||||||
      return PromiseA.resolve(inProcessCache[experienceId]);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    var mq = require('masterquest');
 | 
					 | 
				
			||||||
    var path = require('path');
 | 
					 | 
				
			||||||
    // TODO how can we encrypt this?
 | 
					 | 
				
			||||||
    var systemFactory = createClientFactory({
 | 
					 | 
				
			||||||
      // TODO only complain if the values are different
 | 
					 | 
				
			||||||
        algorithm: 'aes'
 | 
					 | 
				
			||||||
      , bits: 128
 | 
					 | 
				
			||||||
      , mode: 'cbc'
 | 
					 | 
				
			||||||
      , dirname: path.join(__dirname, '..', '..', 'var') // TODO info.conf
 | 
					 | 
				
			||||||
      //, prefix: appname.replace(/\//g, ':') // 'com.example.'
 | 
					 | 
				
			||||||
      //, dbname: 'cluster'
 | 
					 | 
				
			||||||
      , suffix: ''
 | 
					 | 
				
			||||||
      , ext: '.sqlcipher'
 | 
					 | 
				
			||||||
      , sock: conf.sqlite3Sock
 | 
					 | 
				
			||||||
      , ipcKey: conf.ipcKey
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    var clientFactory = createClientFactory({
 | 
					 | 
				
			||||||
    // TODO only complain if the values are different
 | 
					 | 
				
			||||||
      dirname: path.join(__dirname, '..', '..', 'var') // TODO info.conf
 | 
					 | 
				
			||||||
    , prefix: 'com.oauth3' // 'com.example.'
 | 
					 | 
				
			||||||
    //, dbname: 'config'
 | 
					 | 
				
			||||||
    , suffix: ''
 | 
					 | 
				
			||||||
    , ext: '.sqlite3'
 | 
					 | 
				
			||||||
    , sock: conf.sqlite3Sock
 | 
					 | 
				
			||||||
    , ipcKey: conf.ipcKey
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    inProcessCache[experienceId] = systemFactory.create({
 | 
					 | 
				
			||||||
      init: true
 | 
					 | 
				
			||||||
    //, key: '00000000000000000000000000000000'
 | 
					 | 
				
			||||||
    , dbname: experienceId // 'com.example.'
 | 
					 | 
				
			||||||
    }).then(function (sqlStore) {
 | 
					 | 
				
			||||||
      //var db = factory.
 | 
					 | 
				
			||||||
      return mq.wrap(sqlStore, dir).then(function (models) {
 | 
					 | 
				
			||||||
        return require('./oauthclient-microservice/lib/sign-token').create(models.PrivateKey).init().then(function (signer) {
 | 
					 | 
				
			||||||
          var CodesCtrl = require('authcodes').create(models.Codes);
 | 
					 | 
				
			||||||
          /* models = { Logins, Verifications } */
 | 
					 | 
				
			||||||
          var LoginsCtrl = require('./authentication-microservice/lib/logins').create({}, CodesCtrl, models);
 | 
					 | 
				
			||||||
          /* models = { ApiKeys, OauthClients } */
 | 
					 | 
				
			||||||
          var ClientsCtrl = require('./oauthclient-microservice/lib/oauthclients').createController({}, models, signer);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          return {
 | 
					 | 
				
			||||||
            Codes: CodesCtrl
 | 
					 | 
				
			||||||
          , Logins: LoginsCtrl
 | 
					 | 
				
			||||||
          , Clients: ClientsCtrl
 | 
					 | 
				
			||||||
          , SqlFactory: clientFactory
 | 
					 | 
				
			||||||
          , models: models
 | 
					 | 
				
			||||||
          };
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    }).then(function (ctrls) {
 | 
					 | 
				
			||||||
      inProcessCache[experienceId] = ctrls;
 | 
					 | 
				
			||||||
      return ctrls;
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return inProcessCache[experienceId];
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  //var jwsUtils = require('./lib/jws-utils').create(signer);
 | 
					 | 
				
			||||||
  var CORS = require('connect-cors');
 | 
					 | 
				
			||||||
  var cors = CORS({ credentials: true, headers: [
 | 
					 | 
				
			||||||
    'X-Requested-With'
 | 
					 | 
				
			||||||
  , 'X-HTTP-Method-Override'
 | 
					 | 
				
			||||||
  , 'Content-Type'
 | 
					 | 
				
			||||||
  , 'Accept'
 | 
					 | 
				
			||||||
  , 'Authorization'
 | 
					 | 
				
			||||||
  ], methods: [ "GET", "POST", "PATCH", "PUT", "DELETE" ] });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Allows CORS access to API with ?access_token=
 | 
					 | 
				
			||||||
  // TODO Access-Control-Max-Age: 600
 | 
					 | 
				
			||||||
  // TODO How can we help apps handle this? token?
 | 
					 | 
				
			||||||
  // TODO allow apps to configure trustedDomains, auth, etc
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  //function weakDecipher(secret, val) { return require('./weak-crypt').weakDecipher(val, secret); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // Generic Session / Login / Account Routes
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  function parseAccessToken(req, opts) {
 | 
					 | 
				
			||||||
    var token;
 | 
					 | 
				
			||||||
    var parts;
 | 
					 | 
				
			||||||
    var scheme;
 | 
					 | 
				
			||||||
    var credentials;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (req.headers && req.headers.authorization) {
 | 
					 | 
				
			||||||
      parts = req.headers.authorization.split(' ');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (parts.length !== 2) {
 | 
					 | 
				
			||||||
        return PromiseA.reject(new Error("malformed Authorization header"));
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      scheme = parts[0];
 | 
					 | 
				
			||||||
      credentials = parts[1];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (-1 !== (opts && opts.schemes || ['token', 'bearer']).indexOf(scheme.toLowerCase())) {
 | 
					 | 
				
			||||||
        token = credentials;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (req.body && req.body.access_token) {
 | 
					 | 
				
			||||||
      if (token) { PromiseA.reject(new Error("token exists in header and body")); }
 | 
					 | 
				
			||||||
      token = req.body.access_token;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // TODO disallow query with req.method === 'GET'
 | 
					 | 
				
			||||||
    // (cookies should be used for protected static assets)
 | 
					 | 
				
			||||||
    if (req.query && req.query.access_token) {
 | 
					 | 
				
			||||||
      if (token) { PromiseA.reject(new Error("token already exists in either header or body and also in query")); }
 | 
					 | 
				
			||||||
      token = req.query.access_token;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /*
 | 
					 | 
				
			||||||
    err = new Error(challenge());
 | 
					 | 
				
			||||||
    err.code = 'E_BEARER_REALM';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!token) { return PromiseA.reject(err); }
 | 
					 | 
				
			||||||
    */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return PromiseA.resolve(token);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  function getClient(req, token, priv, Controllers) {
 | 
					 | 
				
			||||||
    if (!token) {
 | 
					 | 
				
			||||||
      token = req.oauth3.token;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    var cacheId = '_' + token.k + 'Client';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (priv[cacheId]) {
 | 
					 | 
				
			||||||
      return PromiseA.resolve(priv[cacheId]);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // TODO could get client directly by token.app (id of client)
 | 
					 | 
				
			||||||
    priv[cacheId] = Controllers.Clients.login(null, token.k).then(function (apiKey) {
 | 
					 | 
				
			||||||
      if (!apiKey) {
 | 
					 | 
				
			||||||
        return PromiseA.reject(new Error("Client no longer valid"));
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      priv[cacheId + 'Key'] = apiKey;
 | 
					 | 
				
			||||||
      priv[cacheId] = apiKey.oauthClient;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return apiKey.oauthClient;
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return priv[cacheId];
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  function getAccountsByLogin(req, token, priv, Controllers, loginId, decrypt) {
 | 
					 | 
				
			||||||
    return getClient(req, req.oauth.token, priv).then(function (oauthClient) {
 | 
					 | 
				
			||||||
      if (decrypt) {
 | 
					 | 
				
			||||||
        loginId = scoper.unscope(loginId, oauthClient.secret);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return Controllers.models.AccountsLogins.find({ loginId: loginId }).then(function (accounts) {
 | 
					 | 
				
			||||||
        return PromiseA.all(accounts.map(function (obj) {
 | 
					 | 
				
			||||||
          return Controllers.models.Accounts.get(obj.accountId)/*.then(function (account) {
 | 
					 | 
				
			||||||
            account.appScopedId = weakCipher(oauthClient.secret, account.id);
 | 
					 | 
				
			||||||
            return account;
 | 
					 | 
				
			||||||
          })*/;
 | 
					 | 
				
			||||||
        }));
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  function getAccountsByArray(req, Controllers, arr) {
 | 
					 | 
				
			||||||
    return PromiseA.all(arr.map(function (accountId) {
 | 
					 | 
				
			||||||
      return Controllers.models.Accounts.get(accountId.id || accountId);
 | 
					 | 
				
			||||||
    }));
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  function getAccounts(req, token, priv, Controllers) {
 | 
					 | 
				
			||||||
    if (!token) {
 | 
					 | 
				
			||||||
      token = req.oauth3.token;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    var err;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (priv._accounts) {
 | 
					 | 
				
			||||||
      return PromiseA.resolve(priv._accounts);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if ((req.oauth3.token.idx || req.oauth3.token.usr) && ('password' === req.oauth3.token.grt || 'login' === req.oauth3.token.as)) {
 | 
					 | 
				
			||||||
      priv._accounts = getAccountsByLogin(req, req.oauth3.token, priv, Controllers, (req.oauth3.token.idx || req.oauth3.token.usr), !!req.oauth3.token.idx);
 | 
					 | 
				
			||||||
    } else if (req.oauth3.token.axs && req.oauth3.token.axs.length || req.oauth3.token.acx) {
 | 
					 | 
				
			||||||
      req.oauth3._accounts = getAccountsByArray(req, Controllers, req.oauth3.token.axs && req.oauth3.token.axs.length && req.oauth3.token.axs || [req.oauth3.token.acx]);
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      err = new Error("neither login nor accounts were specified");
 | 
					 | 
				
			||||||
      err.code = "E_NO_AUTHZ";
 | 
					 | 
				
			||||||
      req.oauth3._accounts = PromiseA.reject(err);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    req.oauth3._accounts.then(function (accounts) {
 | 
					 | 
				
			||||||
      req.oauth3._accounts = accounts;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return accounts;
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return req.oauth3._accounts;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  function getLoginId(req, token, priv/*, Controllers*/) {
 | 
					 | 
				
			||||||
    if (!token) {
 | 
					 | 
				
			||||||
      token = req.oauth3.token;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    var cacheId = '_' + token.idx + 'LoginId';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (priv[cacheId]) {
 | 
					 | 
				
			||||||
      return PromiseA.resolve(priv[cacheId]);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // TODO
 | 
					 | 
				
			||||||
    // this ends up defeating part of the purpose of JWT (few database calls)
 | 
					 | 
				
			||||||
    // perhaps the oauthClient secret should be sent, encrypted with a master key,
 | 
					 | 
				
			||||||
    // with the request? Or just mash the oauthClient secret with the loginId
 | 
					 | 
				
			||||||
    // and encrypt with the master key?
 | 
					 | 
				
			||||||
    priv._loginId = getClient(req, token, priv).then(function (oauthClient) {
 | 
					 | 
				
			||||||
      var loginId;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (token.idx) {
 | 
					 | 
				
			||||||
        loginId = scoper.unscope(token.idx, oauthClient.secret);
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        loginId = token.usr;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      priv[cacheId] = loginId;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return loginId;
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return priv[cacheId];
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  function getLogin(req, token, priv, Controllers) {
 | 
					 | 
				
			||||||
    if (!token) {
 | 
					 | 
				
			||||||
      token = req.oauth3.token;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    var cacheId = '_' + token.idx + 'Login';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (priv[cacheId]) {
 | 
					 | 
				
			||||||
      return PromiseA.resolve(priv[cacheId]);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    priv[cacheId] = getLoginId(req, token, priv).then(function (loginId) {
 | 
					 | 
				
			||||||
      // DB.Logins.get(hashId)
 | 
					 | 
				
			||||||
      return Controllers.Logins.rawGet(loginId).then(function (login) {
 | 
					 | 
				
			||||||
        priv[cacheId] = login;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return login;
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return priv[cacheId];
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  function attachOauth3(req, res, next) {
 | 
					 | 
				
			||||||
    var privs = {};
 | 
					 | 
				
			||||||
    req.oauth3 = {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    getAppScopedControllers(req.experienceId).then(function (Controllers) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return parseAccessToken(req).then(function (token) {
 | 
					 | 
				
			||||||
        if (!token) {
 | 
					 | 
				
			||||||
          next();
 | 
					 | 
				
			||||||
          return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        var jwt = require('jsonwebtoken');
 | 
					 | 
				
			||||||
        var data = jwt.decode(token);
 | 
					 | 
				
			||||||
        var err;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (!data) {
 | 
					 | 
				
			||||||
          err = new Error('not a json web token');
 | 
					 | 
				
			||||||
          err.code = 'E_NOT_JWT';
 | 
					 | 
				
			||||||
          res.send({
 | 
					 | 
				
			||||||
            error: err.code
 | 
					 | 
				
			||||||
          , error_description: err.message
 | 
					 | 
				
			||||||
          , error_url: 'https://oauth3.org/docs/errors#' + (err.code || 'E_UNKNOWN_EXCEPTION')
 | 
					 | 
				
			||||||
          });
 | 
					 | 
				
			||||||
          // PromiseA.reject(err);
 | 
					 | 
				
			||||||
          return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        req.oauth3.token = token;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        req.oauth3.getLoginId = function (token) {
 | 
					 | 
				
			||||||
          getLoginId(req, token || req.oauth3.token, privs, Controllers);
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        req.oauth3.getLogin = function (token) {
 | 
					 | 
				
			||||||
          getLogin(req, token || req.oauth3.token, privs, Controllers);
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // TODO modify prototypes?
 | 
					 | 
				
			||||||
        req.oauth3.getClient = function (token) {
 | 
					 | 
				
			||||||
          getClient(req, token || req.oauth3.token, privs, Controllers);
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // TODO req.oauth3.getAccountIds
 | 
					 | 
				
			||||||
        req.oauth3.getAccounts = function (token) {
 | 
					 | 
				
			||||||
          getAccounts(req, token || req.oauth3.token, privs, Controllers);
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        next();
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  app.use('/', cors);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  app.use('/', attachOauth3);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@ -110,7 +110,7 @@ function loadPages(pkgConf, packagedPage, req, res, next) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getApi(conf, pkgConf, pkgDeps, packagedApi) {
 | 
					function getApi(conf, pkgConf, pkgDeps, packagedApi) {
 | 
				
			||||||
  var PromiseA = require('bluebird');
 | 
					  var PromiseA = pkgDeps.Promise;
 | 
				
			||||||
  var path = require('path');
 | 
					  var path = require('path');
 | 
				
			||||||
  var pkgpath = path.join(pkgConf.apipath, packagedApi.id/*, (packagedApi.api.version || '')*/);
 | 
					  var pkgpath = path.join(pkgConf.apipath, packagedApi.id/*, (packagedApi.api.version || '')*/);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -162,6 +162,9 @@ function getApi(conf, pkgConf, pkgDeps, packagedApi) {
 | 
				
			|||||||
      packagedApi._api_app = myApp;
 | 
					      packagedApi._api_app = myApp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      //require('./oauth3-auth').inject(conf, packagedApi._api, pkgConf, pkgDeps);
 | 
					      //require('./oauth3-auth').inject(conf, packagedApi._api, pkgConf, pkgDeps);
 | 
				
			||||||
 | 
					      pkgDeps.getOauth3Controllers =
 | 
				
			||||||
 | 
					      packagedApi._getOauth3Controllers = require('oauthcommon/example-oauthmodels').create(conf).getControllers;
 | 
				
			||||||
 | 
					      require('oauthcommon').inject(packagedApi._getOauth3Controllers, packagedApi._api, pkgConf, pkgDeps);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // DEBUG
 | 
					      // DEBUG
 | 
				
			||||||
      //
 | 
					      //
 | 
				
			||||||
@ -251,16 +254,18 @@ function layerItUp(pkgConf, router, req, res, next) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function runApi(opts, router, req, res, next) {
 | 
					function runApi(opts, router, req, res, next) {
 | 
				
			||||||
 | 
					  var path = require('path');
 | 
				
			||||||
  var pkgConf = opts.config;
 | 
					  var pkgConf = opts.config;
 | 
				
			||||||
  var pkgDeps = opts.deps;
 | 
					  var pkgDeps = opts.deps;
 | 
				
			||||||
  //var Services = opts.Services;
 | 
					  //var Services = opts.Services;
 | 
				
			||||||
  var packagedApi;
 | 
					  var packagedApi;
 | 
				
			||||||
 | 
					  var pathname;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // TODO compile packagesMap
 | 
					  // TODO compile packagesMap
 | 
				
			||||||
  // TODO people may want to use the framework in a non-framework way (i.e. to conceal the module name)
 | 
					  // TODO people may want to use the framework in a non-framework way (i.e. to conceal the module name)
 | 
				
			||||||
  router.packagedApis.some(function (_packagedApi) {
 | 
					  router.packagedApis.some(function (_packagedApi) {
 | 
				
			||||||
    // console.log('[DEBUG _packagedApi.id]', _packagedApi.id);
 | 
					    // console.log('[DEBUG _packagedApi.id]', _packagedApi.id);
 | 
				
			||||||
    var pathname = router.pathname;
 | 
					    pathname = router.pathname;
 | 
				
			||||||
    if ('/' === pathname) {
 | 
					    if ('/' === pathname) {
 | 
				
			||||||
      pathname = '';
 | 
					      pathname = '';
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -290,7 +295,7 @@ function runApi(opts, router, req, res, next) {
 | 
				
			|||||||
    // TODO this identifier may need to be non-deterministic as to transfer if a domain name changes but is still the "same" app
 | 
					    // TODO this identifier may need to be non-deterministic as to transfer if a domain name changes but is still the "same" app
 | 
				
			||||||
    // (i.e. a company name change. maybe auto vs manual register - just like oauth3?)
 | 
					    // (i.e. a company name change. maybe auto vs manual register - just like oauth3?)
 | 
				
			||||||
    // NOTE: probably best to alias the name logically
 | 
					    // NOTE: probably best to alias the name logically
 | 
				
			||||||
  , value: (req.hostname + req.pathname).replace(/\/$/, '')
 | 
					  , value: (path.join(req.hostname, pathname || '')).replace(/\/$/, '')
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
  Object.defineProperty(req, 'escapedExperienceId', {
 | 
					  Object.defineProperty(req, 'escapedExperienceId', {
 | 
				
			||||||
    enumerable: true
 | 
					    enumerable: true
 | 
				
			||||||
 | 
				
			|||||||
@ -5,9 +5,10 @@
 | 
				
			|||||||
// require everything as lazily as possible until our server
 | 
					// require everything as lazily as possible until our server
 | 
				
			||||||
// is actually listening on the socket. Bluebird is heavy.
 | 
					// is actually listening on the socket. Bluebird is heavy.
 | 
				
			||||||
// Even the built-in modules can take dozens of milliseconds to require
 | 
					// Even the built-in modules can take dozens of milliseconds to require
 | 
				
			||||||
module.exports.create = function (certPaths, serverCallback) {
 | 
					module.exports.create = function (lex, certPaths, serverCallback) {
 | 
				
			||||||
  // Recognize that this secureContexts cache is local to this CPU core
 | 
					  // Recognize that this secureContexts cache is local to this CPU core
 | 
				
			||||||
  var secureContexts = {};
 | 
					  var secureContexts = {};
 | 
				
			||||||
 | 
					  var ciphers = 'ECDH+AESGCM:DH+AESGCM:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS:!AES256';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  function createSecureServer() {
 | 
					  function createSecureServer() {
 | 
				
			||||||
    var domainname = 'www.example.com';
 | 
					    var domainname = 'www.example.com';
 | 
				
			||||||
@ -21,7 +22,7 @@ module.exports.create = function (certPaths, serverCallback) {
 | 
				
			|||||||
      // https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
 | 
					      // https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
 | 
				
			||||||
      // https://nodejs.org/api/tls.html
 | 
					      // https://nodejs.org/api/tls.html
 | 
				
			||||||
      // removed :ECDH+AES256:DH+AES256 and added :!AES256 because AES-256 wastes CPU
 | 
					      // removed :ECDH+AES256:DH+AES256 and added :!AES256 because AES-256 wastes CPU
 | 
				
			||||||
    , ciphers: 'ECDH+AESGCM:DH+AESGCM:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS:!AES256'
 | 
					    , ciphers: ciphers
 | 
				
			||||||
    , honorCipherOrder: true
 | 
					    , honorCipherOrder: true
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -43,5 +44,15 @@ module.exports.create = function (certPaths, serverCallback) {
 | 
				
			|||||||
    serverCallback(null, require('https').createServer(secureOpts));
 | 
					    serverCallback(null, require('https').createServer(secureOpts));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  createSecureServer();
 | 
					  function createLeServer() {
 | 
				
			||||||
 | 
					    lex.httpsOptions.ciphers = ciphers;
 | 
				
			||||||
 | 
					    lex.httpsOptions.honorCipherOrder = true;
 | 
				
			||||||
 | 
					    serverCallback(null, require('https').createServer(lex.httpsOptions));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (lex) {
 | 
				
			||||||
 | 
					    createLeServer();
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    createSecureServer();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -1,28 +1,28 @@
 | 
				
			|||||||
'use strict';
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function tplCaddyfile(conf) {
 | 
					function tplCaddyfile(caddyConf) {
 | 
				
			||||||
  var contents = [];
 | 
					  var contents = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  conf.caddy.domains.forEach(function (hostname) {
 | 
					  caddyConf.domains.forEach(function (hostname) {
 | 
				
			||||||
    var content = "";
 | 
					    var content = "";
 | 
				
			||||||
    var pagesname = hostname;
 | 
					    var pagesname = hostname;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // TODO prefix
 | 
					    // TODO prefix
 | 
				
			||||||
    content+= "https://" + hostname + " {\n"
 | 
					    content += "https://" + hostname + " {\n"
 | 
				
			||||||
      + "  gzip\n"
 | 
					      + "  gzip\n"
 | 
				
			||||||
      + "  tls "
 | 
					      + "  tls "
 | 
				
			||||||
          + "/srv/walnut/certs/live/" + hostname + "/fullchain.pem "
 | 
					          + "/srv/walnut/certs/live/" + hostname + "/fullchain.pem "
 | 
				
			||||||
          + "/srv/walnut/certs/live/" + hostname + "/privkey.pem\n"
 | 
					          + "/srv/walnut/certs/live/" + hostname + "/privkey.pem\n"
 | 
				
			||||||
    ;
 | 
					    ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (conf.locked) {
 | 
					    if (caddyConf.locked) {
 | 
				
			||||||
      content += "  root /srv/walnut/init.public/\n";
 | 
					      content += "  root /srv/walnut/init.public/\n";
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      content += "  root " + conf.caddy.sitespath + "/" + pagesname + "/\n";
 | 
					      content += "  root " + caddyConf.sitespath + "/" + pagesname + "/\n";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    content +=
 | 
					    content +=
 | 
				
			||||||
      "  proxy /api http://localhost:" + conf.localPort.toString() + " {\n"
 | 
					      "  proxy /api http://localhost:" + caddyConf.localPort.toString() + " {\n"
 | 
				
			||||||
    + "    proxy_header Host {host}\n"
 | 
					    + "    proxy_header Host {host}\n"
 | 
				
			||||||
    + "    proxy_header X-Forwarded-Host {host}\n"
 | 
					    + "    proxy_header X-Forwarded-Host {host}\n"
 | 
				
			||||||
    + "    proxy_header X-Forwarded-Proto {scheme}\n"
 | 
					    + "    proxy_header X-Forwarded-Proto {scheme}\n"
 | 
				
			||||||
@ -37,10 +37,10 @@ function tplCaddyfile(conf) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports.tplCaddyfile = tplCaddyfile;
 | 
					module.exports.tplCaddyfile = tplCaddyfile;
 | 
				
			||||||
module.exports.create = function (config) {
 | 
					module.exports.create = function (caddyConf) {
 | 
				
			||||||
  var spawn = require('child_process').spawn;
 | 
					  var spawn = require('child_process').spawn;
 | 
				
			||||||
  var caddyBin = config.caddy.bin;
 | 
					  var caddyBin = caddyConf.bin;
 | 
				
			||||||
  var caddyConf = config.caddy.conf;
 | 
					  var caddyfilePath = caddyConf.conf;
 | 
				
			||||||
  // TODO put up a booting / lock screen on boot
 | 
					  // TODO put up a booting / lock screen on boot
 | 
				
			||||||
  // and wait for all to be grabbed from db
 | 
					  // and wait for all to be grabbed from db
 | 
				
			||||||
  // NOTE caddy cannot yet support multiple roots
 | 
					  // NOTE caddy cannot yet support multiple roots
 | 
				
			||||||
@ -49,8 +49,8 @@ module.exports.create = function (config) {
 | 
				
			|||||||
  var fs = require('fs');
 | 
					  var fs = require('fs');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // TODO this should be expanded to include proxies a la proxydyn
 | 
					  // TODO this should be expanded to include proxies a la proxydyn
 | 
				
			||||||
  function writeCaddyfile(conf, cb) {
 | 
					  function writeCaddyfile(caddyConf, cb) {
 | 
				
			||||||
    fs.readdir(config.caddy.sitespath, function (err, nodes) {
 | 
					    fs.readdir(caddyConf.sitespath, function (err, nodes) {
 | 
				
			||||||
      if (err) {
 | 
					      if (err) {
 | 
				
			||||||
        if (cb) {
 | 
					        if (cb) {
 | 
				
			||||||
          cb(err);
 | 
					          cb(err);
 | 
				
			||||||
@ -61,12 +61,12 @@ module.exports.create = function (config) {
 | 
				
			|||||||
        throw err;
 | 
					        throw err;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      conf.caddy.domains = nodes.filter(function (node) {
 | 
					      caddyConf.domains = nodes.filter(function (node) {
 | 
				
			||||||
        return /\./.test(node) && !/(^\.)|([\/\:\\])/.test(node);
 | 
					        return /\./.test(node) && !/(^\.)|([\/\:\\])/.test(node);
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      var contents = tplCaddyfile(conf);
 | 
					      var contents = tplCaddyfile(caddyConf);
 | 
				
			||||||
      fs.writeFile(caddyConf, contents, 'utf8', function (err) {
 | 
					      fs.writeFile(caddyfilePath, contents, 'utf8', function (err) {
 | 
				
			||||||
        if (err) {
 | 
					        if (err) {
 | 
				
			||||||
          if (cb) {
 | 
					          if (cb) {
 | 
				
			||||||
            cb(err);
 | 
					            cb(err);
 | 
				
			||||||
@ -82,9 +82,9 @@ module.exports.create = function (config) {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  function spawnCaddy(conf, cb) {
 | 
					  function spawnCaddy(caddyConf, cb) {
 | 
				
			||||||
    console.log('[CADDY] start');
 | 
					    console.log('[CADDY] start');
 | 
				
			||||||
    writeCaddyfile(conf, function (err) {
 | 
					    writeCaddyfile(caddyfilePath, function (err) {
 | 
				
			||||||
      if (err) {
 | 
					      if (err) {
 | 
				
			||||||
        console.error('[writeCaddyfile]');
 | 
					        console.error('[writeCaddyfile]');
 | 
				
			||||||
        console.error(err.stack);
 | 
					        console.error(err.stack);
 | 
				
			||||||
@ -108,7 +108,7 @@ module.exports.create = function (config) {
 | 
				
			|||||||
        // Command failed: killall caddy
 | 
					        // Command failed: killall caddy
 | 
				
			||||||
        // caddy: no process found
 | 
					        // caddy: no process found
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      caddy = spawn(caddyBin, ['-conf', caddyConf],  { stdio: ['ignore', 'pipe', 'pipe'] });
 | 
					      caddy = spawn(caddyBin, ['-conf', caddyfilePath],  { stdio: ['ignore', 'pipe', 'pipe'] });
 | 
				
			||||||
      caddy.stdout.on('data', function (str) {
 | 
					      caddy.stdout.on('data', function (str) {
 | 
				
			||||||
        console.error('[Caddy]', str.toString('utf8'));
 | 
					        console.error('[Caddy]', str.toString('utf8'));
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
@ -123,7 +123,7 @@ module.exports.create = function (config) {
 | 
				
			|||||||
        console.log(code, signal);
 | 
					        console.log(code, signal);
 | 
				
			||||||
        caddy = null;
 | 
					        caddy = null;
 | 
				
			||||||
        setTimeout(function () {
 | 
					        setTimeout(function () {
 | 
				
			||||||
          spawnCaddy(conf);
 | 
					          spawnCaddy(caddyConf);
 | 
				
			||||||
        }, 1 * 1000);
 | 
					        }, 1 * 1000);
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -151,8 +151,8 @@ module.exports.create = function (config) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
    spawn: spawnCaddy
 | 
					    spawn: spawnCaddy
 | 
				
			||||||
  , update: function (conf) {
 | 
					  , update: function (caddyConf) {
 | 
				
			||||||
      return writeCaddyfile(conf, sighup);
 | 
					      return writeCaddyfile(caddyConf, sighup);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  , sighup: sighup
 | 
					  , sighup: sighup
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
'use strict';
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports.create = function (webserver, info, state) {
 | 
					module.exports.create = function (webserver, conf, state) {
 | 
				
			||||||
  if (!state) {
 | 
					  if (!state) {
 | 
				
			||||||
    state = {};
 | 
					    state = {};
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -14,31 +14,31 @@ module.exports.create = function (webserver, info, state) {
 | 
				
			|||||||
  var sqlstores = {};
 | 
					  var sqlstores = {};
 | 
				
			||||||
  var models = {};
 | 
					  var models = {};
 | 
				
			||||||
  var systemFactory = require('sqlite3-cluster/client').createClientFactory({
 | 
					  var systemFactory = require('sqlite3-cluster/client').createClientFactory({
 | 
				
			||||||
      dirname: path.join(__dirname, '..', '..', 'var') // TODO info.conf
 | 
					      dirname: path.join(__dirname, '..', '..', 'var') // TODO conf
 | 
				
			||||||
    , prefix: 'com.example.'
 | 
					    , prefix: 'com.example.'
 | 
				
			||||||
    //, dbname: 'config'
 | 
					    //, dbname: 'config'
 | 
				
			||||||
    , suffix: ''
 | 
					    , suffix: ''
 | 
				
			||||||
    , ext: '.sqlite3'
 | 
					    , ext: '.sqlite3'
 | 
				
			||||||
    , sock: info.conf.sqlite3Sock
 | 
					    , sock: conf.sqlite3Sock
 | 
				
			||||||
    , ipcKey: info.conf.ipcKey
 | 
					    , ipcKey: conf.ipcKey
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
  var clientFactory = require('sqlite3-cluster/client').createClientFactory({
 | 
					  var clientFactory = require('sqlite3-cluster/client').createClientFactory({
 | 
				
			||||||
      algorithm: 'aes'
 | 
					      algorithm: 'aes'
 | 
				
			||||||
    , bits: 128
 | 
					    , bits: 128
 | 
				
			||||||
    , mode: 'cbc'
 | 
					    , mode: 'cbc'
 | 
				
			||||||
    , dirname: path.join(__dirname, '..', '..', 'var') // TODO info.conf
 | 
					    , dirname: path.join(__dirname, '..', '..', 'var') // TODO conf
 | 
				
			||||||
    , prefix: 'com.example.'
 | 
					    , prefix: 'com.example.'
 | 
				
			||||||
    //, dbname: 'cluster'
 | 
					    //, dbname: 'cluster'
 | 
				
			||||||
    , suffix: ''
 | 
					    , suffix: ''
 | 
				
			||||||
    , ext: '.sqlcipher'
 | 
					    , ext: '.sqlcipher'
 | 
				
			||||||
    , sock: info.conf.sqlite3Sock
 | 
					    , sock: conf.sqlite3Sock
 | 
				
			||||||
    , ipcKey: info.conf.ipcKey
 | 
					    , ipcKey: conf.ipcKey
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
  var cstore = require('cluster-store');
 | 
					  var cstore = require('cluster-store');
 | 
				
			||||||
  var redirectives;
 | 
					  var redirectives;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  app.disable('x-powered-by');
 | 
					  app.disable('x-powered-by');
 | 
				
			||||||
  if (info.conf.trustProxy) {
 | 
					  if (conf.trustProxy) {
 | 
				
			||||||
    console.info('[Trust Proxy]');
 | 
					    console.info('[Trust Proxy]');
 | 
				
			||||||
    app.set('trust proxy', ['loopback']);
 | 
					    app.set('trust proxy', ['loopback']);
 | 
				
			||||||
    //app.set('trust proxy', function (ip) { console.log('[ip]', ip); return true; });
 | 
					    //app.set('trust proxy', function (ip) { console.log('[ip]', ip); return true; });
 | 
				
			||||||
@ -87,8 +87,8 @@ module.exports.create = function (webserver, info, state) {
 | 
				
			|||||||
    host = host.toLowerCase();
 | 
					    host = host.toLowerCase();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // TODO this should be hot loadable / changeable
 | 
					    // TODO this should be hot loadable / changeable
 | 
				
			||||||
    if (!redirectives && info.conf.redirects) {
 | 
					    if (!redirectives && conf.redirects) {
 | 
				
			||||||
      redirectives = require('./hostname-redirects').compile(info.conf.redirects);
 | 
					      redirectives = require('./hostname-redirects').compile(conf.redirects);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!/^www\./.test(host) && !redirectives) {
 | 
					    if (!/^www\./.test(host) && !redirectives) {
 | 
				
			||||||
@ -126,10 +126,10 @@ module.exports.create = function (webserver, info, state) {
 | 
				
			|||||||
    // TODO security on memstore
 | 
					    // TODO security on memstore
 | 
				
			||||||
    // TODO memstoreFactory.create
 | 
					    // TODO memstoreFactory.create
 | 
				
			||||||
    cstore.create({
 | 
					    cstore.create({
 | 
				
			||||||
      sock: info.conf.memstoreSock
 | 
					      sock: conf.memstoreSock
 | 
				
			||||||
    , connect: info.conf.memstoreSock
 | 
					    , connect: conf.memstoreSock
 | 
				
			||||||
      // TODO implement
 | 
					      // TODO implement
 | 
				
			||||||
    , key: info.conf.ipcKey
 | 
					    , key: conf.ipcKey
 | 
				
			||||||
    }).then(function (_memstore) {
 | 
					    }).then(function (_memstore) {
 | 
				
			||||||
      memstore = _memstore;
 | 
					      memstore = _memstore;
 | 
				
			||||||
      return memstore;
 | 
					      return memstore;
 | 
				
			||||||
@ -171,14 +171,14 @@ module.exports.create = function (webserver, info, state) {
 | 
				
			|||||||
        , vhostsMap: vhostsMap
 | 
					        , vhostsMap: vhostsMap
 | 
				
			||||||
        , vhostPatterns: null
 | 
					        , vhostPatterns: null
 | 
				
			||||||
        , server: webserver
 | 
					        , server: webserver
 | 
				
			||||||
        , externalPort: info.conf.externalPort
 | 
					        , externalPort: conf.externalPort
 | 
				
			||||||
        , privkey: info.conf.privkey
 | 
					        , privkey: conf.privkey
 | 
				
			||||||
        , pubkey: info.conf.pubkey
 | 
					        , pubkey: conf.pubkey
 | 
				
			||||||
        , redirects: info.conf.redirects
 | 
					        , redirects: conf.redirects
 | 
				
			||||||
        , apiPrefix: '/api'
 | 
					        , apiPrefix: '/api'
 | 
				
			||||||
        , 'org.oauth3.consumer': info.conf['org.oauth3.consumer']
 | 
					        , 'org.oauth3.consumer': conf['org.oauth3.consumer']
 | 
				
			||||||
        , 'org.oauth3.provider': info.conf['org.oauth3.provider']
 | 
					        , 'org.oauth3.provider': conf['org.oauth3.provider']
 | 
				
			||||||
        , keys: info.conf.keys
 | 
					        , keys: conf.keys
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        var pkgDeps = {
 | 
					        var pkgDeps = {
 | 
				
			||||||
          memstore: memstore
 | 
					          memstore: memstore
 | 
				
			||||||
@ -187,10 +187,11 @@ module.exports.create = function (webserver, info, state) {
 | 
				
			|||||||
        , systemSqlFactory: systemFactory
 | 
					        , systemSqlFactory: systemFactory
 | 
				
			||||||
        //, handlePromise: require('./lib/common').promisableRequest;
 | 
					        //, handlePromise: require('./lib/common').promisableRequest;
 | 
				
			||||||
        //, handleRejection: require('./lib/common').rejectableRequest;
 | 
					        //, handleRejection: require('./lib/common').rejectableRequest;
 | 
				
			||||||
        //, localPort: info.conf.localPort
 | 
					        //, localPort: conf.localPort
 | 
				
			||||||
        , Promise: PromiseA
 | 
					        , Promise: PromiseA
 | 
				
			||||||
        , express: express
 | 
					        , express: express
 | 
				
			||||||
        , app: app
 | 
					        , app: app
 | 
				
			||||||
 | 
					        //, oauthmodels: require('oauthcommon/example-oauthmodels').create(conf)
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        var Services = require('./services-loader').create(pkgConf, {
 | 
					        var Services = require('./services-loader').create(pkgConf, {
 | 
				
			||||||
          memstore: memstore
 | 
					          memstore: memstore
 | 
				
			||||||
@ -223,7 +224,7 @@ module.exports.create = function (webserver, info, state) {
 | 
				
			|||||||
            config: pkgConf
 | 
					            config: pkgConf
 | 
				
			||||||
          , deps: pkgDeps
 | 
					          , deps: pkgDeps
 | 
				
			||||||
          , services: Services
 | 
					          , services: Services
 | 
				
			||||||
          , conf: info.conf
 | 
					          , conf: conf
 | 
				
			||||||
          }, req, res, next);
 | 
					          }, req, res, next);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -58,6 +58,7 @@
 | 
				
			|||||||
    "cookie-session": "1.x",
 | 
					    "cookie-session": "1.x",
 | 
				
			||||||
    "cookie-signature": "^1.0.6",
 | 
					    "cookie-signature": "^1.0.6",
 | 
				
			||||||
    "crc": "^3.2.1",
 | 
					    "crc": "^3.2.1",
 | 
				
			||||||
 | 
					    "ddns-cli": "^1.2.1",
 | 
				
			||||||
    "debug": "^2.1.3",
 | 
					    "debug": "^2.1.3",
 | 
				
			||||||
    "depd": "^1.0.0",
 | 
					    "depd": "^1.0.0",
 | 
				
			||||||
    "destroy": "^1.0.3",
 | 
					    "destroy": "^1.0.3",
 | 
				
			||||||
@ -81,6 +82,7 @@
 | 
				
			|||||||
    "json-storage": "2.x",
 | 
					    "json-storage": "2.x",
 | 
				
			||||||
    "jsonwebtoken": "^5.4.0",
 | 
					    "jsonwebtoken": "^5.4.0",
 | 
				
			||||||
    "lodash": "2.x",
 | 
					    "lodash": "2.x",
 | 
				
			||||||
 | 
					    "letsencrypt-express": "1.1.x",
 | 
				
			||||||
    "masterquest-sqlite3": "git://github.com/coolaj86/node-masterquest-sqlite3.git",
 | 
					    "masterquest-sqlite3": "git://github.com/coolaj86/node-masterquest-sqlite3.git",
 | 
				
			||||||
    "media-typer": "^0.3.0",
 | 
					    "media-typer": "^0.3.0",
 | 
				
			||||||
    "methods": "^1.1.1",
 | 
					    "methods": "^1.1.1",
 | 
				
			||||||
@ -105,7 +107,7 @@
 | 
				
			|||||||
    "request": "2.44.0",
 | 
					    "request": "2.44.0",
 | 
				
			||||||
    "request-ip": "^1.1.1",
 | 
					    "request-ip": "^1.1.1",
 | 
				
			||||||
    "scmp": "1.x",
 | 
					    "scmp": "1.x",
 | 
				
			||||||
    "secret-utils": "1.x",
 | 
					    "secret-utils": "^2.0.0",
 | 
				
			||||||
    "semver": "^4.3.1",
 | 
					    "semver": "^4.3.1",
 | 
				
			||||||
    "send": "^0.12.2",
 | 
					    "send": "^0.12.2",
 | 
				
			||||||
    "serve-favicon": "2.x",
 | 
					    "serve-favicon": "2.x",
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user