more network handling stuff

This commit is contained in:
AJ ONeal 2017-03-18 14:48:49 -06:00
parent 3b5d7a49d4
commit 54a8bc15d9
4 changed files with 146 additions and 46 deletions

View File

@ -12,6 +12,12 @@
Server Name: '<span ng-bind="vm.config.device.hostname"></span>' running from
<br/>
<code><span ng-bind="vm.config.cwd"></span>/</code>
<br/>
iface: <span ng-bind="vm.admin.network.iface"></span>
<br/>
ipv4: <span ng-bind="vm.admin.network.ipv4"></span>
<br/>
ipv6: <span ng-bind="vm.admin.network.ipv6"></span>
</span>
<br/>
<br/>
@ -20,7 +26,7 @@
<div class="container">
<div class="row">
<div class="col-md-4">
<div class="col-sm-3">
<ul class="nav nav-pills nav-stacked" ng-init="vm.admin.page = 'authn'">
<li
role="presentation"
@ -104,7 +110,7 @@
</div>
</div>
<div class="col-md-8">
<div class="col-sm-9">
<div ng-if="!vm.config || 'authn' === vm.admin.page">
<p>In order to administer this server you must authenticate.</p>
@ -155,6 +161,7 @@
/>
<button
type="button"
class="btn btn-primary"
ng-click="vm.admin.setDeviceName(vm.admin.servername)"
ng-disabled="vm.config.device.hostname === vm.admin.servername"
>Save</button>
@ -214,6 +221,11 @@
<div ng-if="'oauth3-tunnel' === vm.admin.network.iface">
<div ng-if="vm.admin.tunnel.advanced">
<h2>OAuth3 Tunnel Options</h2>
<label><input
type="checkbox"
ng-model="vm.admin.tunnel.optimistic"
ng-change="vm.setTunnel()"> Prefer local network when available</label>
<br/>
<label>URL</label> <input
class"form-input"
type="url"

View File

@ -19,7 +19,9 @@ angular.module('com.daplie.cloud', [ 'org.oauth3' ])
var vm = this;
var OAUTH3 = window.OAUTH3;
vm.hello = 'Hello!';
vm.admin = { network: { iface: null, ipv4: null, ipv6: null } };
vm.admin.ipify = 'https://api.ipify.org?format=json';
vm.clientUri = OAUTH3.clientUri(window.location);
vm.setSimple = function () {
@ -178,10 +180,52 @@ angular.module('com.daplie.cloud', [ 'org.oauth3' ])
}).then(function (configResp) {
console.log('config', configResp.data);
vm.config = configResp.data;
vm.config.addresses.forEach(function (addr) {
if (/unicast/.test(addr.range) || /internet/i.test(addr.range)) {
// TODO
// there could be more than one iface (ipv4 on one and ipv6 on the other)
// there could also be more than one valid public ip
if ("IPv4" === addr.family) {
vm.admin.network.iface = addr.iface;
vm.admin.network.ipv4 = addr.address;
}
else if ("IPv6" === addr.family) {
// IPv6 gives valid internet address even when behind a firewall
// IPv6 requires a loopback test
// TODO always do the loopback / firewall test anyway
vm.admin.network.ipv6 = addr.address;
}
}
});
vm.admin.servername = vm.config.device.hostname;
//vm.config.ifaces
//vm.config.addresses = [];
vm.viewDomains(vm.config, domains, dns);
return resp;
if (vm.admin.network.iface) {
return resp;
}
// TODO try UPnP IGD / NAT-PMP / DNS-SD / PCP (ipv6) here
// TODO two requests - one to {{ipv4.ipify.org}}, the other to {{ipv6.ipify.org}}
vm.admin.network.iface = 'gateway';
return OAUTH3.request({
method: 'POST'
, url: 'https://' + vm.clientUri + '/api/com.daplie.caddy/request'
, session: session
, data: {
method: 'GET'
, url: 'https://api.ipify.org?format=json'
}
}).then(function (ipResp) {
console.log('ip-response', ipResp);
vm.admin.network.ipv4 = ipResp.data.body.ipv4 || ipResp.data.body.ip;
vm.admin.network.ipv6 = ipResp.data.body.ipv6;
return resp;
});
});
}, function (err) {
console.error(err);

View File

@ -75,6 +75,7 @@ module.exports = function (opts) {
}
}
, recase: require('recase').create({})
, request: PromiseA.promisify(require('request'))
, options: opts
});
}
@ -163,6 +164,10 @@ module.exports = function (opts) {
serveInit.config(req, res);
return;
}
if ('/api/com.daplie.caddy/request' === req.url) {
serveInit.request(req, res);
return;
}
if (content && '/' === req.url) {
// res.setHeader('Content-Type', 'application/octet-stream');

View File

@ -48,54 +48,65 @@ module.exports.create = function (deps) {
init: function (req, res) {
jsonParser(req, res, 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;
return deps.PromiseA.resolve().then(function () {
// 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');
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;
console.log('ids', id, tid, rid);
return deps.storage.owners.all().then(function (results) {
console.log('results', results);
var err;
// 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');
// 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");
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, { token: token, refresh: refresh });
}
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('no owner, creating');
return deps.storage.owners.set(id, { token: token, refresh: refresh });
}
console.log('has results');
console.log('has correct owner');
// 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, { token: token, refresh: refresh });
}
}).then(function () {
res.setHeader('Content-Type', 'application/json;');
res.end(JSON.stringify({ success: true }));
// 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, { token: token, refresh: refresh });
}
}).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 } }));
@ -120,5 +131,33 @@ module.exports.create = function (deps) {
});
});
}
, 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);
});
});
});
}
};
};