goldilocks.js/lib/loopback.js

96 lines
2.9 KiB
JavaScript

'use strict';
module.exports.create = function (deps) {
var PromiseA = require('bluebird');
var request = PromiseA.promisify(require('request'));
var pending = {};
function checkPublicAddr(host) {
return request({
method: 'GET'
, url: host+'/api/org.oauth3.tunnel/checkip'
, json: true
}).then(function (result) {
if (!result.body) {
return PromiseA.reject(new Error('No response body in request for public address'));
}
if (result.body.error) {
var err = new Error(result.body.error.message);
return PromiseA.reject(Object.assign(err, result.body.error));
}
return result.body.address;
});
}
function checkSinglePort(host, address, port) {
var crypto = require('crypto');
var token = crypto.randomBytes(8).toString('hex');
var keyAuth = crypto.randomBytes(32).toString('hex');
pending[token] = keyAuth;
var opts = {
address: address
, port: port
, token: token
, keyAuthorization: keyAuth
, iat: Date.now()
};
return request({
method: 'POST'
, url: host+'/api/org.oauth3.tunnel/loopback'
, json: opts
})
.then(function (result) {
delete pending[token];
if (!result.body) {
return PromiseA.reject(new Error('No response body in loopback request for port '+port));
}
// If the loopback requests don't go to us then there are all kinds of ways it could
// error, but none of them really provide much extra information so we don't do
// anything that will break the PromiseA.all out and mask the other results.
if (result.body.error) {
console.log('error on remote side of port '+port+' loopback', result.body.error);
}
return !!result.body.success;
}, function (err) {
delete pending[token];
throw err;
});
}
function loopback(provider) {
return deps.OAUTH3.discover(provider).then(function (directives) {
return checkPublicAddr(directives.api).then(function (address) {
console.log('checking to see if', address, 'gets back to us');
var ports = require('./servers').listeners.tcp.list();
return PromiseA.all(ports.map(function (port) {
return checkSinglePort(directives.api, address, port);
}))
.then(function (values) {
console.log(pending);
var result = {error: null, address: address};
ports.forEach(function (port, ind) {
result[port] = values[ind];
});
return result;
});
});
});
}
loopback.server = require('http').createServer(function (req, res) {
var parsed = require('url').parse(req.url);
var token = parsed.pathname.replace('/.well-known/cloud-challenge/', '');
if (pending[token]) {
res.setHeader('Content-Type', 'text/plain');
res.end(pending[token]);
} else {
res.statusCode = 404;
res.end();
}
});
return loopback;
};