'use strict'; module.exports.dependencies = [ 'OAUTH3', 'storage.owners', 'options.device' ]; module.exports.api = { tunnel: function (deps, session) { var OAUTH3 = deps.OAUTH3; var url = require('url'); var providerUri = session.token.aud; var urlObj = url.parse(OAUTH3.url.normalize(session.token.azp)); var oauth3 = OAUTH3.create(urlObj, { providerUri: providerUri , session: session }); //var crypto = require('crypto'); //var id = crypto.createHash('sha256').update(session.token.sub).digest('hex'); return oauth3.setProvider(providerUri).then(function () { return oauth3.api('domains.list').then(function (domains) { var domainsMap = {}; domains.forEach(function (d) { if (!d.device) { return; } if (d.device !== deps.options.device.hostname) { return; } domainsMap[d.name] = true; }); console.log('domains matching hostname', Object.keys(domainsMap)); console.log('device', deps.options.device); return oauth3.api('tunnel.token', { data: { // filter to all domains that are on this device domains: Object.keys(domainsMap) , device: { hostname: deps.options.device.hostname , id: deps.options.device.uid || deps.options.device.id } } }).then(function (result) { console.log(result); }); }); }); //, { token: token, refresh: refresh }); } }; module.exports.create = function (deps) { var scmp = require('scmp'); var crypto = require('crypto'); var jwt = require('jsonwebtoken'); var bodyParser = require('body-parser'); var jsonParser = bodyParser.json({ inflate: true, limit: '100kb', reviver: null, strict: true /* type, verify */ }); var api = module.exports.api; /* var owners; deps.storage.owners.on('set', function (_owners) { owners = _owners; }); */ deps.storage.owners.exists = function (id) { return deps.storage.owners.all().then(function (owners) { return owners.some(function (owner) { return scmp(id, owner.id); }); }); }; function isAuthorized(req, res, fn) { var auth = jwt.decode((req.headers.authorization||'').replace(/^bearer\s+/i, '')); if (!auth) { res.setHeader('Content-Type', 'application/json;'); res.end(JSON.stringify({ error: { message: "no token", code: 'E_NO_TOKEN', uri: undefined } })); return; } var id = crypto.createHash('sha256').update(auth.sub).digest('hex'); return deps.storage.owners.exists(id).then(function (exists) { if (!exists) { res.setHeader('Content-Type', 'application/json;'); res.end(JSON.stringify({ error: { message: "not authorized", code: 'E_NO_AUTHZ', uri: undefined } })); return; } req.userId = id; fn(); }); } return { init: function (req, res) { jsonParser(req, res, function () { return deps.PromiseA.resolve().then(function () { console.log('req.body', req.body); var auth = jwt.decode((req.headers.authorization||'').replace(/^bearer\s+/i, '')); var token = jwt.decode(req.body.access_token); var refresh = jwt.decode(req.body.refresh_token); auth.sub = auth.sub || auth.acx.id; token.sub = token.sub || token.acx.id; refresh.sub = refresh.sub || refresh.acx.id; // TODO validate token with issuer, but as-is the sub is already a secret var id = crypto.createHash('sha256').update(auth.sub).digest('hex'); var tid = crypto.createHash('sha256').update(token.sub).digest('hex'); var rid = crypto.createHash('sha256').update(refresh.sub).digest('hex'); var session = { access_token: req.body.access_token , token: token , refresh_token: req.body.refresh_token , refresh: refresh }; console.log('ids', id, tid, rid); if (req.body.ip_url) { // TODO set options / GunDB deps.options.ip_url = req.body.ip_url; } return deps.storage.owners.all().then(function (results) { console.log('results', results); var err; // There is no owner yet. First come, first serve. if (!results || !results.length) { if (tid !== id || rid !== id) { err = new Error( "When creating an owner the Authorization Bearer and Token and Refresh must all match" ); return deps.PromiseA.reject(err); } console.log('no owner, creating'); return deps.storage.owners.set(id, session); } console.log('has results'); // There are onwers. Is this one of them? if (!results.some(function (token) { return scmp(id, token.id); })) { err = new Error("Authorization token does not belong to an existing owner."); return deps.PromiseA.reject(err); } console.log('has correct owner'); // We're adding an owner, unless it already exists if (!results.some(function (token) { return scmp(tid, token.id); })) { console.log('adds new owner with existing owner'); return deps.storage.owners.set(id, session); } }).then(function () { res.setHeader('Content-Type', 'application/json;'); res.end(JSON.stringify({ success: true })); }); }, function (err) { res.setHeader('Content-Type', 'application/json;'); res.end(JSON.stringify({ error: { message: err.message, code: err.code, uri: err.uri } })); }); }); } , tunnel: function (req, res) { isAuthorized(req, res, function () { jsonParser(req, res, function () { console.log('req.body', req.body); return deps.storage.owners.get(req.userId).then(function (session) { session.token.id = req.userId; return api.tunnel(deps, session); }); }); }); } , config: function (req, res) { isAuthorized(req, res, function () { if ('POST' !== req.method) { res.setHeader('Content-Type', 'application/json;'); res.end(JSON.stringify(deps.recase.snakeCopy(deps.options))); return; } jsonParser(req, res, function () { console.log('req.body', req.body); deps.storage.config.merge(req.body); deps.storage.config.save(); }); }); } , request: function (req, res) { jsonParser(req, res, function () { isAuthorized(req, res, function () { deps.request({ method: req.body.method || 'GET' , url: req.body.url , headers: req.body.headers , body: req.body.data }).then(function (resp) { if (resp.body instanceof Buffer || 'string' === typeof resp.body) { resp.body = JSON.parse(resp.body); } return { statusCode: resp.statusCode , status: resp.status , headers: resp.headers , body: resp.body , data: resp.data }; }).then(function (result) { res.send(result); }); }); }); } , _api: api }; };