From 976761b6e048a5d9f0fbc7e887c7481440063f9e Mon Sep 17 00:00:00 2001 From: aj Date: Tue, 12 Sep 2017 22:32:33 +0000 Subject: [PATCH] [WIP] retrieve sub from db --- lib/apis.js | 75 ++++++++++++++++++++++++++++++++++++++++++++------- lib/oauth3.js | 65 +++++++++++++++++++++++++++++++++----------- lib/worker.js | 11 ++++++++ 3 files changed, 126 insertions(+), 25 deletions(-) diff --git a/lib/apis.js b/lib/apis.js index 2737ce9..76aba6e 100644 --- a/lib/apis.js +++ b/lib/apis.js @@ -161,7 +161,7 @@ module.exports.create = function (xconfx, apiFactories, apiDeps) { function accountRequired(req, res, next) { // if this already has auth, great - if (req.oauth3.ppid) { + if (req.oauth3.ppid && req.oauth3.accountIdx) { next(); return; } @@ -305,7 +305,27 @@ module.exports.create = function (xconfx, apiFactories, apiDeps) { myApp.handleRejection = rejectableRequest; myApp.grantsRequired = grantsRequired; - myApp.use('/', require('./oauth3').attachOauth3); + function getSitePackageStoreProp(otherPkgId) { + var restPath = path.join(myConf.restPath, otherPkgId); + var apiPath = path.join(myConf.apiPath, otherPkgId); + var dir; + + // TODO usage package.json as a falback if the standard location is not used + try { + dir = require(path.join(apiPath, 'models.js')); + } catch(e) { + dir = require(path.join(restPath, 'models.js')); + } + + return getSiteStore(clientUrih, otherPkgId, dir); + } + + function attachOauth3(req, res, next) { + return getSitePackageStoreProp('issuer@oauth3.org').then(function (Models) { + return require('./oauth3').attachOauth3(Models, req, res, next); + }); + } + myApp.use('/', attachOauth3); // TODO delete these caches when config changes var _stripe; @@ -318,7 +338,9 @@ module.exports.create = function (xconfx, apiFactories, apiDeps) { //if (xconfx.debug) { console.log('[api.js] loading handler prereqs'); } return getSiteConfig(clientUrih).then(function (siteConfig) { //if (xconfx.debug) { console.log('[api.js] loaded handler site config'); } - Object.defineProperty(req, 'getSiteMailer', { + + // Use getSiteCapability('email@daplie.com') instead + Object.defineProperty(req, 'getSiteMailer' /*deprecated*/, { enumerable: true , configurable: false , writable: false @@ -355,6 +377,13 @@ module.exports.create = function (xconfx, apiFactories, apiDeps) { } }); + Object.defineProperty(req, 'getSitePackageStore', { + enumerable: true + , configurable: false + , writable: false + , value: getSitePackageStoreProp + }); + Object.defineProperty(req, 'getSiteStore', { enumerable: true , configurable: false @@ -820,14 +849,37 @@ module.exports.create = function (xconfx, apiFactories, apiDeps) { myApp.handleRejection = rejectableRequest; myApp.grantsRequired = grantsRequired; - myApp.use('/', require('./oauth3').cookieOauth3); + function otherGetSitePackageStoreProp(otherPkgId) { + var restPath = path.join(myConf.restPath, otherPkgId); + var apiPath = path.join(myConf.apiPath, otherPkgId); + var dir; + + // TODO usage package.json as a falback if the standard location is not used + try { + dir = require(path.join(apiPath, 'models.js')); + } catch(e) { + dir = require(path.join(restPath, 'models.js')); + } + + return getSiteStore(clientUrih, otherPkgId, dir); + } + myApp.use('/', function cookieAttachOauth3(req, res, next) { + return otherGetSitePackageStoreProp('issuer@oauth3.org').then(function (Models) { + return require('./oauth3').cookieOauth3(Models, req, res, next); + }); + }); myApp.use('/', function (req, res, next) { console.log('########################################### session ###############################'); console.log('req.url', req.url); console.log('req.oauth3', req.oauth3); next(); }); - myApp.post('/assets/issuer@oauth3.org/session', require('./oauth3').attachOauth3, function (req, res) { + function otherAttachOauth3(req, res, next) { + return otherGetSitePackageStoreProp('issuer@oauth3.org').then(function (Models) { + return require('./oauth3').attachOauth3(Models, req, res, next); + }); + } + myApp.post('/assets/issuer@oauth3.org/session', otherAttachOauth3, function (req, res) { console.log('get the session'); console.log(req.url); console.log("req.cookies:"); @@ -1004,7 +1056,8 @@ module.exports.create = function (xconfx, apiFactories, apiDeps) { // sub.example.com/api should resolve to sub.example.com // example.com/subapp/api should resolve to example.com#subapp // sub.example.com/subapp/api should resolve to sub.example.com#subapp - var clientUrih = req.hostname.replace(/^(api|assets)\./, '') + req.url.replace(/\/(api|assets)\/.*/, '/').replace(/\/+/g, '#').replace(/#$/, ''); + var appUri = req.hostname.replace(/^(api|assets)\./, '') + req.url.replace(/\/(api|assets)\/.*/, '/').replace(/\/$/, ''); + var clientUrih = appUri.replace(/\/+/g, '#').replace(/#$/, ''); var clientApiUri = req.hostname.replace(/^(api|assets)\./, 'api.') + req.url.replace(/\/(api|assets)\/.*/, '/').replace(/\/$/, ''); var clientAssetsUri = req.hostname.replace(/^(api|assets)\./, 'assets.') + req.url.replace(/\/(api|assets)\/.*/, '/').replace(/\/$/, ''); //var clientAssetsUri = req.hostname.replace(/^(api|assets)\./, 'api.') + req.url.replace(/\/(api|assets)\/.*/, '/').replace(/\/$/, ''); @@ -1016,7 +1069,12 @@ module.exports.create = function (xconfx, apiFactories, apiDeps) { var now = Date.now(); var hasBeenHandled = false; - // Existing (Deprecated) + Object.defineProperty(req, 'clientUrl', { + enumerable: true + , configurable: false + , writable: false + , value: (req.headers.referer || ('https://' + appUri)).replace(/\/$/, '').replace(/\?.*/, '') + }); Object.defineProperty(req, 'apiUrlPrefix', { enumerable: true , configurable: false @@ -1029,7 +1087,7 @@ module.exports.create = function (xconfx, apiFactories, apiDeps) { , writable: false , value: 'https://' + clientAssetsUri + '/assets/' + pkgId }); - Object.defineProperty(req, 'experienceId', { + Object.defineProperty(req, 'experienceId' /*deprecated*/, { enumerable: true , configurable: false , writable: false @@ -1054,7 +1112,6 @@ module.exports.create = function (xconfx, apiFactories, apiDeps) { , value: pkgId }); - // New Object.defineProperty(req, 'clientUrih', { enumerable: true , configurable: false diff --git a/lib/oauth3.js b/lib/oauth3.js index c89df8d..63f1427 100644 --- a/lib/oauth3.js +++ b/lib/oauth3.js @@ -2,6 +2,42 @@ var PromiseA = require('bluebird'); +function generateRescope(req, Models, decoded, fullPpid, ppid) { + return 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. + console.log('[rescope] Attempting ', fullPpid); + return Models.IssuerOauth3OrgGrants.find({ azpSub: fullPpid }).then(function (results) { + if (results[0]) { + console.log('[rescope] lukcy duck: got it on the 1st try'); + return PromiseA.resolve(results); + } + + // XXX BUG XXX + // should be able to distinguish between own ids and 3rd party via @whatever.com + return Models.IssuerOauth3OrgGrants.find({ azpSub: ppid }); + }).then(function (results) { + var result = results[0]; + + if (!result || !result.sub || !decoded.iss) { + // XXX BUG XXX TODO swap this external ppid for an internal (and ask user to link with existing profile) + //req.oauth3.accountIdx = fullPpid; + throw new Error("internal / external ID swapping not yet implemented. TODO: " + + "No profile found with that credential. Would you like to create a new profile or link to an existing profile?"); + } + + // XXX BUG XXX need to pass own url in to use as issuer for own tokens + req.oauth3.accountIdx = result.sub + '@' + decoded.iss; + + console.log('[rescope] result:'); + console.log(results); + console.log(req.oauth3.accountIdx); + + return PromiseA.resolve(req.oauth3.accountIdx); + }); + }; +} + function extractAccessToken(req) { var token = null; var parts; @@ -181,7 +217,7 @@ function deepFreeze(obj) { Object.freeze(obj); } -function cookieOauth3(req, res, next) { +function cookieOauth3(Models, req, res, next) { req.oauth3 = {}; var token = req.cookies.jwt; @@ -205,11 +241,7 @@ function cookieOauth3(req, res, next) { 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); - }; + req.oauth3.rescope = generateRescope(req, Models, decoded, fullPpid, ppid); }).then(function () { deepFreeze(req.oauth3); //Object.defineProperty(req, 'oauth3', {configurable: false, writable: false}); @@ -225,7 +257,7 @@ function cookieOauth3(req, res, next) { }); } -function attachOauth3(req, res, next) { +function attachOauth3(Models, req, res, next) { req.oauth3 = {}; extractAccessToken(req).then(function (token) { @@ -245,20 +277,21 @@ function attachOauth3(req, res, next) { } var ppid = decoded.sub || decoded.ppid || decoded.appScopedId; + var fullPpid = ppid+'@'+decoded.iss; 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, ''); + // TODO we can anonymize the relationship between our user as the other service's user + // in our own database by hashing the remote service's ppid and using that as the lookup + var hash = require('crypto').createHash('sha256').update(fullPpid).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); - }; + req.oauth3.rescope = generateRescope(req, Models, decoded, fullPpid, ppid); + + console.log('############### assigned req.oauth3:'); + console.log(req.oauth3); }).then(function () { - deepFreeze(req.oauth3); + //deepFreeze(req.oauth3); //Object.defineProperty(req, 'oauth3', {configurable: false, writable: false}); next(); }, function (err) { diff --git a/lib/worker.js b/lib/worker.js index 313c6d0..abe224a 100644 --- a/lib/worker.js +++ b/lib/worker.js @@ -154,6 +154,17 @@ module.exports.create = function (webserver, xconfx, state) { , crypto: PromiseA.promisifyAll(require('crypto')) , fs: PromiseA.promisifyAll(require('fs')) , path: require('path') + , validate: { + isEmail: function (email) { + return /@/.test(email) && !/\s+/.test(email); + } + , email: function (email) { + if (apiDeps.validate.isEmail(email)) { + return null; + } + return new Error('invalid email address'); + } + } }; var apiFactories = { memstoreFactory: { create: scopeMemstore }