enable assets subdomain with cookies
This commit is contained in:
		
							parent
							
								
									babfb6b38b
								
							
						
					
					
						commit
						a429e48977
					
				
							
								
								
									
										1265
									
								
								lib/apis.js
									
									
									
									
									
								
							
							
						
						
									
										1265
									
								
								lib/apis.js
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										26
									
								
								lib/main.js
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								lib/main.js
									
									
									
									
									
								
							@ -1,6 +1,6 @@
 | 
				
			|||||||
'use strict';
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports.create = function (app, xconfx, apiFactories, apiDeps, errorIfApi) {
 | 
					module.exports.create = function (app, xconfx, apiFactories, apiDeps, errorIfApi, errorIfAssets) {
 | 
				
			||||||
  var PromiseA = require('bluebird');
 | 
					  var PromiseA = require('bluebird');
 | 
				
			||||||
  var path = require('path');
 | 
					  var path = require('path');
 | 
				
			||||||
  var fs = PromiseA.promisifyAll(require('fs'));
 | 
					  var fs = PromiseA.promisifyAll(require('fs'));
 | 
				
			||||||
@ -293,10 +293,27 @@ module.exports.create = function (app, xconfx, apiFactories, apiDeps, errorIfApi
 | 
				
			|||||||
  // TODO handle assets.example.com/sub/assets/com.example.xyz/
 | 
					  // TODO handle assets.example.com/sub/assets/com.example.xyz/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  app.use('/api', require('connect-send-error').error());
 | 
					  app.use('/api', require('connect-send-error').error());
 | 
				
			||||||
 | 
					  app.use('/assets', require('connect-send-error').error());
 | 
				
			||||||
  app.use('/', function (req, res, next) {
 | 
					  app.use('/', function (req, res, next) {
 | 
				
			||||||
    // If this doesn't look like an API we can move along
 | 
					    // If this doesn't look like an API or assets we can move along
 | 
				
			||||||
    if (!/\/api(\/|$)/.test(req.url)) {
 | 
					
 | 
				
			||||||
      // /^api\./.test(req.hostname) &&
 | 
					    /*
 | 
				
			||||||
 | 
					    console.log('.');
 | 
				
			||||||
 | 
					    console.log('[main.js] req.url, req.hostname');
 | 
				
			||||||
 | 
					    console.log(req.url);
 | 
				
			||||||
 | 
					    console.log(req.hostname);
 | 
				
			||||||
 | 
					    console.log('.');
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!/\/(api|assets)(\/|$)/.test(req.url)) {
 | 
				
			||||||
 | 
					      //console.log('[main.js] api|assets');
 | 
				
			||||||
 | 
					      next();
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // keep https://assets.example.com/assets but skip https://example.com/assets
 | 
				
			||||||
 | 
					    if (/\/assets(\/|$)/.test(req.url) && !/(^|\.)(api|assets)(\.)/.test(req.hostname) && !/^[0-9\.]+$/.test(req.hostname)) {
 | 
				
			||||||
 | 
					      //console.log('[main.js] skip');
 | 
				
			||||||
      next();
 | 
					      next();
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -325,6 +342,7 @@ module.exports.create = function (app, xconfx, apiFactories, apiDeps, errorIfApi
 | 
				
			|||||||
    return;
 | 
					    return;
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
  app.use('/', errorIfApi);
 | 
					  app.use('/', errorIfApi);
 | 
				
			||||||
 | 
					  app.use('/', errorIfAssets);
 | 
				
			||||||
  app.use('/', serveStatic);
 | 
					  app.use('/', serveStatic);
 | 
				
			||||||
  app.use('/', serveApps);
 | 
					  app.use('/', serveApps);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -181,6 +181,50 @@ function deepFreeze(obj) {
 | 
				
			|||||||
  Object.freeze(obj);
 | 
					  Object.freeze(obj);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function cookieOauth3(req, res, next) {
 | 
				
			||||||
 | 
					  req.oauth3 = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  var token = req.cookies.jwt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  req.oauth3.encodedToken = token;
 | 
				
			||||||
 | 
					  req.oauth3.verifyAsync = function (jwt) {
 | 
				
			||||||
 | 
					    return verifyToken(jwt || token);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return verifyToken(token).then(function  (decoded) {
 | 
				
			||||||
 | 
					    req.oauth3.token = decoded;
 | 
				
			||||||
 | 
					    if (!decoded) {
 | 
				
			||||||
 | 
					      return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var ppid = decoded.sub || decoded.ppid || decoded.appScopedId;
 | 
				
			||||||
 | 
					    req.oauth3.ppid = ppid;
 | 
				
			||||||
 | 
					    req.oauth3.accountIdx = ppid+'@'+decoded.iss;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var hash = require('crypto').createHash('sha256').update(req.oauth3.accountIdx).digest('base64');
 | 
				
			||||||
 | 
					    hash = hash.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+/g, '');
 | 
				
			||||||
 | 
					    req.oauth3.accountHash = hash;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    req.oauth3.rescope = function (sub) {
 | 
				
			||||||
 | 
					      // TODO: this function is supposed to convert PPIDs of different parties to some account
 | 
				
			||||||
 | 
					      // ID that allows application to keep track of permisions and what-not.
 | 
				
			||||||
 | 
					      return PromiseA.resolve(sub || hash);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }).then(function () {
 | 
				
			||||||
 | 
					    deepFreeze(req.oauth3);
 | 
				
			||||||
 | 
					    //Object.defineProperty(req, 'oauth3', {configurable: false, writable: false});
 | 
				
			||||||
 | 
					    next();
 | 
				
			||||||
 | 
					  }, function (err) {
 | 
				
			||||||
 | 
					    if ('E_NO_TOKEN' === err.code) {
 | 
				
			||||||
 | 
					      next();
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    console.error('[walnut] cookie lib/oauth3 error:');
 | 
				
			||||||
 | 
					    console.error(err);
 | 
				
			||||||
 | 
					    res.send(err);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function attachOauth3(req, res, next) {
 | 
					function attachOauth3(req, res, next) {
 | 
				
			||||||
  req.oauth3 = {};
 | 
					  req.oauth3 = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -215,14 +259,15 @@ function attachOauth3(req, res, next) {
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
  }).then(function () {
 | 
					  }).then(function () {
 | 
				
			||||||
    deepFreeze(req.oauth3);
 | 
					    deepFreeze(req.oauth3);
 | 
				
			||||||
    Object.defineProperty(req, 'oauth3', {configurable: false, writable: false});
 | 
					    //Object.defineProperty(req, 'oauth3', {configurable: false, writable: false});
 | 
				
			||||||
    next();
 | 
					    next();
 | 
				
			||||||
  }, function (err) {
 | 
					  }, function (err) {
 | 
				
			||||||
    console.error('[walnut] lib/oauth3 error:');
 | 
					    console.error('[walnut] JWT lib/oauth3 error:');
 | 
				
			||||||
    console.error(err);
 | 
					    console.error(err);
 | 
				
			||||||
    res.send(err);
 | 
					    res.send(err);
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports.attachOauth3 = attachOauth3;
 | 
					module.exports.attachOauth3 = attachOauth3;
 | 
				
			||||||
 | 
					module.exports.cookieOauth3 = cookieOauth3;
 | 
				
			||||||
module.exports.verifyToken = verifyToken;
 | 
					module.exports.verifyToken = verifyToken;
 | 
				
			||||||
 | 
				
			|||||||
@ -150,6 +150,10 @@ module.exports.create = function (webserver, xconfx, state) {
 | 
				
			|||||||
            models: models
 | 
					            models: models
 | 
				
			||||||
            // TODO don't let packages use this directly
 | 
					            // TODO don't let packages use this directly
 | 
				
			||||||
          , Promise: PromiseA
 | 
					          , Promise: PromiseA
 | 
				
			||||||
 | 
					          , dns: PromiseA.promisifyAll(require('dns'))
 | 
				
			||||||
 | 
					          , crypto: PromiseA.promisifyAll(require('crypto'))
 | 
				
			||||||
 | 
					          , fs: PromiseA.promisifyAll(require('fs'))
 | 
				
			||||||
 | 
					          , path: require('path')
 | 
				
			||||||
          };
 | 
					          };
 | 
				
			||||||
          var apiFactories = {
 | 
					          var apiFactories = {
 | 
				
			||||||
            memstoreFactory: { create: scopeMemstore }
 | 
					            memstoreFactory: { create: scopeMemstore }
 | 
				
			||||||
@ -180,7 +184,7 @@ module.exports.create = function (webserver, xconfx, state) {
 | 
				
			|||||||
          function setupMain() {
 | 
					          function setupMain() {
 | 
				
			||||||
            if (xconfx.debug) { console.log('[main] setup'); }
 | 
					            if (xconfx.debug) { console.log('[main] setup'); }
 | 
				
			||||||
            mainApp = express();
 | 
					            mainApp = express();
 | 
				
			||||||
            require('./main').create(mainApp, xconfx, apiFactories, apiDeps, errorIfApi).then(function () {
 | 
					            require('./main').create(mainApp, xconfx, apiFactories, apiDeps, errorIfApi, errorIfAssets).then(function () {
 | 
				
			||||||
              if (xconfx.debug) { console.log('[main] ready'); }
 | 
					              if (xconfx.debug) { console.log('[main] ready'); }
 | 
				
			||||||
              // TODO process.send({});
 | 
					              // TODO process.send({});
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
@ -225,6 +229,24 @@ module.exports.create = function (webserver, xconfx, state) {
 | 
				
			|||||||
            next();
 | 
					            next();
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          function errorIfNotAssets(req, res, next) {
 | 
				
			||||||
 | 
					            var hostname = req.hostname || req.headers.host;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!/^assets\.[a-z0-9\-]+/.test(hostname)) {
 | 
				
			||||||
 | 
					              res.send({ error:
 | 
				
			||||||
 | 
					               { message: "['" + hostname + req.url + "'] protected asset access is restricted to proper 'asset'-prefixed lowercase subdomains."
 | 
				
			||||||
 | 
					                   + " The HTTP 'Host' header must exist and must begin with 'assets.' as in 'assets.example.com'."
 | 
				
			||||||
 | 
					                   + " For development you may test with assets.localhost.daplie.me (or any domain by modifying your /etc/hosts)"
 | 
				
			||||||
 | 
					               , code: 'E_NOT_API'
 | 
				
			||||||
 | 
					               , _hostname: hostname
 | 
				
			||||||
 | 
					               }
 | 
				
			||||||
 | 
					              });
 | 
				
			||||||
 | 
					              return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            next();
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          function errorIfApi(req, res, next) {
 | 
					          function errorIfApi(req, res, next) {
 | 
				
			||||||
            if (!/^api\./.test(req.headers.host)) {
 | 
					            if (!/^api\./.test(req.headers.host)) {
 | 
				
			||||||
              next();
 | 
					              next();
 | 
				
			||||||
@ -240,7 +262,25 @@ module.exports.create = function (webserver, xconfx, state) {
 | 
				
			|||||||
              return;
 | 
					              return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            res.send({ error: { code: 'E_NO_IMPL', message: "not implemented" } });
 | 
					            res.send({ error: { code: 'E_NO_IMPL', message: "API not implemented" } });
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          function errorIfAssets(req, res, next) {
 | 
				
			||||||
 | 
					            if (!/^assets\./.test(req.headers.host)) {
 | 
				
			||||||
 | 
					              next();
 | 
				
			||||||
 | 
					              return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // has api. hostname prefix
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // doesn't have /api url prefix
 | 
				
			||||||
 | 
					            if (!/^\/assets\//.test(req.url)) {
 | 
				
			||||||
 | 
					              console.log('[walnut/worker assets] req.url', req.url);
 | 
				
			||||||
 | 
					              res.send({ error: { message: "missing /assets/ url prefix" } });
 | 
				
			||||||
 | 
					              return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            res.send({ error: { code: 'E_NO_IMPL', message: "assets handler not implemented" } });
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          app.disable('x-powered-by');
 | 
					          app.disable('x-powered-by');
 | 
				
			||||||
@ -258,8 +298,11 @@ module.exports.create = function (webserver, xconfx, state) {
 | 
				
			|||||||
          }));
 | 
					          }));
 | 
				
			||||||
          app.use('/api', recase);
 | 
					          app.use('/api', recase);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          var cookieParser = require('cookie-parser'); // signing is done in JWT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          app.set('trust proxy', ['loopback', 'linklocal', 'uniquelocal']);
 | 
					          app.set('trust proxy', ['loopback', 'linklocal', 'uniquelocal']);
 | 
				
			||||||
          app.use('/api', errorIfNotApi);
 | 
					          app.use('/api', errorIfNotApi);
 | 
				
			||||||
 | 
					          app.use('/assets', /*errorIfNotAssets,*/ cookieParser()); // serializer { path: '/assets', httpOnly: true, sameSite: true/*, domain: assets.example.com*/ }
 | 
				
			||||||
          app.use('/', function (req, res) {
 | 
					          app.use('/', function (req, res) {
 | 
				
			||||||
            if (!(req.encrypted || req.secure)) {
 | 
					            if (!(req.encrypted || req.secure)) {
 | 
				
			||||||
              // did not come from https
 | 
					              // did not come from https
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user