diff --git a/lib/apis.js b/lib/apis.js index 5f57b35..c417132 100644 --- a/lib/apis.js +++ b/lib/apis.js @@ -8,6 +8,9 @@ module.exports.create = function (xconfx, apiFactories, apiDeps) { var fs = PromiseA.promisifyAll(require('fs')); var path = require('path'); var localCache = { rests: {}, pkgs: {} }; + var promisableRequest = require('./common').promisableRequest; + var rejectableRequest = require('./common').rejectableRequest; + var crypto = require('crypto'); // TODO xconfx.apispath xconfx.restPath = path.join(__dirname, '..', '..', 'packages', 'rest'); @@ -103,6 +106,107 @@ module.exports.create = function (xconfx, apiFactories, apiDeps) { }); } + function accountRequiredById(req, res, next) { + var promise = req.oauth3.verifyAsync().then(function (/*result*/) { + var tok = req.oauth3.token; + var accountId = req.params.accountId || '__NO_ID_GIVEN__'; + var ppid; + + if (tok.sub && tok.sub.split(/,/g).filter(function (ppid) { + return ppid === accountId; + }).length) { + ppid = accountId; + } + + if (tok.axs && tok.axs.filter(function (acc) { + return acc.id === accountId || acc.appScopedId === accountId; + }).length) { + ppid = accountId; + } + + if (tok.acx && accountId === (tok.acx.appScopedId || tok.acx.id || tok.acx)) { + ppid = accountId; + } + + if (!ppid) { + return PromiseA.reject(new Error("missing accountId '" + accountId + "' in access token")); + } + + return req.oauth3.rescope(ppid).then(function (accountIdx) { + req.oauth3.accountIdx = accountIdx; + req.oauth3.ppid = ppid; + req.oauth3.accountHash = crypto.createHash('sha1').update(accountIdx).digest('hex'); + + next(); + }); + }); + + rejectableRequest(req, res, promise, "[com.daplie.walnut] attach account by id"); + } + + function accountRequired(req, res, next) { + // if this already has auth, great + if (req.oauth3.ppid) { + next(); + return; + } + + // being public does not disallow authentication + if (req.isPublic && !req.oauth3.encodedToken) { + next(); + return; + } + + if (!req.oauth3.encodedToken) { + rejectableRequest( + req + , res + , PromiseA.reject(new Error("this secure resource requires an access token")) + , "[com.daplie.walnut] required account (not /public)" + ); + return; + } + + // verify the auth if it's here + var promise = req.oauth3.verifyAsync().then(function (/*result*/) { + var tok = req.oauth3.token; + var ppid; + var err; + + if (tok.sub) { + if (tok.sub.split(/,/g).length > 1) { + err = new Error("more than one 'sub' specified in token"); + return PromiseA.reject(err); + } + ppid = tok.sub; + } + else if (tok.axs && tok.axs.length) { + if (tok.axs.length > 1) { + err = new Error("more than one 'axs' specified in token (also, update to using 'sub' instead)"); + return PromiseA.reject(err); + } + ppid = tok.axs[0].appScopedId || tok.axs[0].id; + } + else if (tok.acx) { + ppid = tok.acx.appScopedId || tok.acx.id || tok.acx; + } + + if (!ppid) { + return PromiseA.reject(new Error("could not determine accountId from access token")); + } + + return req.oauth3.rescope(ppid).then(function (accountIdx) { + req.oauth3.accountIdx = accountIdx; + req.oauth3.ppid = ppid; + req.oauth3.accountHash = crypto.createHash('sha1').update(accountIdx).digest('hex'); + + next(); + }); + }); + + rejectableRequest(req, res, promise, "[com.daplie.walnut] required account (not /public)"); + } + function loadRestHelper(myConf, clientUrih, pkgId) { var pkgPath = path.join(myConf.restPath, pkgId); var pkgLinks = []; @@ -151,8 +255,8 @@ module.exports.create = function (xconfx, apiFactories, apiDeps) { console.log('DEBUG pkgPath', pkgPath); myApp = express(); - myApp.handlePromise = require('./common').promisableRequest; - myApp.handleRejection = require('./common').rejectableRequest; + myApp.handlePromise = promisableRequest; + myApp.handleRejection = rejectableRequest; myApp.grantsRequired = function (grants) { if (!Array.isArray(grants)) { throw new Error("Usage: app.grantsRequired([ 'name|altname|altname2', 'othergrant' ])"); @@ -198,11 +302,6 @@ module.exports.create = function (xconfx, apiFactories, apiDeps) { //require('oauthcommon').inject(packagedApi._getOauth3Controllers, packagedApi._api, pkgConf, pkgDeps); require('oauthcommon').inject(_getOauth3Controllers, myApp/*, pkgConf, pkgDeps*/); - myApp.use('/public', function preHandler(req, res, next) { - // TODO authenticate or use guest user - next(); - }); - // TODO delete these caches when config changes var _stripe; var _stripe_test; @@ -325,6 +424,13 @@ module.exports.create = function (xconfx, apiFactories, apiDeps) { next(); }); }); + myApp.use('/public', function preHandler(req, res, next) { + // TODO authenticate or use guest user + req.isPublic = true; + next(); + }); + myApp.use('/accounts/:accountId', accountRequiredById); + myApp.use('/acl', accountRequired); // // TODO handle /accounts/:accountId