handle remote push of domains

This commit is contained in:
AJ ONeal 2018-08-21 02:53:38 +00:00
parent 739f86f1cc
commit 7d11f15ebe
1 changed files with 125 additions and 30 deletions

View File

@ -20,6 +20,16 @@ var mkdirpAsync = util.promisify(require('mkdirp'));
var TRUSTED_ISSUERS = [ 'oauth3.org' ]; var TRUSTED_ISSUERS = [ 'oauth3.org' ];
var DB = require('./db.js'); var DB = require('./db.js');
var Devices = require('../device-tracker'); var Devices = require('../device-tracker');
var Server = require('../server');
var OAUTH3 = require('oauth3.js').create({ pathname: process.cwd() });
/*
// TODO all of the above should be replace with the official lib
return OAUTH3.jwk.verifyToken(req.auth.jwt).then(function (token) {
}).catch(function (err) {
});
*/
var Claims = {}; var Claims = {};
Claims.publicize = function publicizeClaim(claim) { Claims.publicize = function publicizeClaim(claim) {
if (!claim) { if (!claim) {
@ -206,7 +216,7 @@ Accounts.getOrCreate = function (req) {
function sendMail(state, auth) { function sendMail(state, auth) {
console.log('[DEBUG] ext auth', auth); //console.log('[DEBUG] ext auth', auth);
/* /*
curl -s --user 'api:YOUR_API_KEY' \ curl -s --user 'api:YOUR_API_KEY' \
https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages \ https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages \
@ -267,9 +277,7 @@ function sendMail(state, auth) {
} }
}); });
// anything in the 200 range // anything in the 200 range
if (2 === Math.floor(resp.statusCode / 100)) { if (2 !== Math.floor(resp.statusCode / 100)) {
console.log("[DEBUG] email was sent, or so they say");
} else {
console.error("[Error] email failed to send, or so they say:"); console.error("[Error] email failed to send, or so they say:");
console.error(resp.headers); console.error(resp.headers);
console.error(resp.statusCode, resp.body); console.error(resp.statusCode, resp.body);
@ -402,7 +410,7 @@ function oauth3Auth(req, res, next) {
, json: true , json: true
}).then(function (resp) { }).then(function (resp) {
var jwk = resp.body; var jwk = resp.body;
console.log('Retrieved token\'s JWK: ', resp.body); //console.log('Retrieved token\'s JWK: ', resp.body);
if (200 !== resp.statusCode || 'object' !== typeof resp.body) { if (200 !== resp.statusCode || 'object' !== typeof resp.body) {
//headers.authorization //headers.authorization
res.send({ res.send({
@ -452,16 +460,9 @@ function oauth3Auth(req, res, next) {
}); });
}); });
} }
var OAUTH3 = require('oauth3.js').create({ pathname: process.cwd() });
/*
// TODO all of the above should be replace with the official lib
return OAUTH3.jwk.verifyToken(req.auth.jwt).then(function (token) {
}).catch(function (err) {
});
*/
module.exports.pairRequest = function (opts) { module.exports.pairRequest = function (opts) {
console.log("It's auth'n time!"); //console.log("It's auth'n time!");
var state = opts.state; var state = opts.state;
var authReq = opts.auth; var authReq = opts.auth;
var jwt = require('jsonwebtoken'); var jwt = require('jsonwebtoken');
@ -635,23 +636,26 @@ module.exports.pairPin = function (opts) {
// From a WS connection // From a WS connection
module.exports.authHelper = function (meta) { module.exports.authHelper = function (meta) {
console.log('[authHelper] 1'); //console.log('[authHelper] 1');
var state = meta.state; var state = meta.state;
console.log('[authHelper] 2'); //console.log('[authHelper] 2');
return state.Promise.resolve().then(function () { return state.Promise.resolve().then(function () {
console.log('[authHelper] 3'); //console.log('[authHelper] 3');
var auth = meta.session; var auth = meta.session;
console.log('[authHelper] 4', auth); //console.log('[authHelper] 4', auth);
if (!auth || 'string' !== typeof auth.authz || 'object' !== typeof auth.authzData) { if (!auth || 'string' !== typeof auth.authz || 'object' !== typeof auth.authzData) {
console.log('[authHelper] 5'); //console.log('[authHelper] 5');
console.error("[SANITY FAIL] should not complete auth without authz data and access_token"); console.error("[SANITY FAIL] should not complete auth without authz data and access_token");
console.error(auth); console.error(auth);
return; return;
} }
console.log("[authHelper] passing authzData right along", auth.authzData); //console.log("[authHelper] passing authzData right along", auth.authzData);
// validatedTokenData
return auth.authzData; return auth.authzData;
}); });
}; };
// Comes in from ../../index.js
// opts = { state: state, auth: auth_request OR access_token } // opts = { state: state, auth: auth_request OR access_token }
module.exports.authenticate = function (opts) { module.exports.authenticate = function (opts) {
var jwt = require('jsonwebtoken'); var jwt = require('jsonwebtoken');
@ -764,8 +768,7 @@ app.use('/api', CORS({
})); }));
app.use('/api', bodyParser.json()); app.use('/api', bodyParser.json());
app.use('/api/telebit.cloud/account', oauth3Auth); function getAccountData(req) {
app.get('/api/telebit.cloud/account', function (req, res) {
return Accounts.getOrCreate(req).then(function (acc) { return Accounts.getOrCreate(req).then(function (acc) {
var hasEmail = acc.nodes.some(function (node) { var hasEmail = acc.nodes.some(function (node) {
return 'email' === node.type; return 'email' === node.type;
@ -811,9 +814,10 @@ app.get('/api/telebit.cloud/account', function (req, res) {
// TODO assign devices by public key, not domain name // TODO assign devices by public key, not domain name
result.domains.map(function (domain) { result.domains.map(function (domain) {
console.log("[debug] Domain", domain); console.log("[debug] Domain", domain.name);
var devs = Devices.list(req._state.deviceLists, domain.name); var devs = Devices.list(req._state.deviceLists, domain.name);
console.log("[debug] Devs", devs.map(function (d) { return d.socketId; })); console.log("[debug] Devs", devs);
//console.log("[debug] Devs", devs.map(function (d) { return d.socketId; }));
if (!devs.length) { return null; } if (!devs.length) { return null; }
devs.forEach(function (dev) { devs.forEach(function (dev) {
// eventually we should implement so that a new connection // eventually we should implement so that a new connection
@ -824,10 +828,23 @@ app.get('/api/telebit.cloud/account', function (req, res) {
id: dev.id id: dev.id
, socketId: dev.socketId , socketId: dev.socketId
, active: true , active: true
, names: [] , names: [domain.name]
, ports: []
, hostname: dev.hostname
}; };
} }
if (-1 === devsMap[dev.socketId].names.indexOf(domain.name)) {
devsMap[dev.socketId].names.push(domain.name); devsMap[dev.socketId].names.push(domain.name);
}
// copy over ports too
Object.keys(dev.grants).forEach(function (k) {
var grant = dev.grants[k];
grant.ports.forEach(function (p) {
if (-1 === devsMap[dev.socketId].ports.indexOf(p)) {
devsMap[dev.socketId].ports.push(p);
}
});
});
return devsMap[dev.socketId]; return devsMap[dev.socketId];
}); });
}).filter(Boolean); }).filter(Boolean);
@ -855,7 +872,14 @@ app.get('/api/telebit.cloud/account', function (req, res) {
} else { } else {
return getAllGrants(); return getAllGrants();
} }
}).then(function (result) { });
}
app.use('/api/telebit.cloud/account', oauth3Auth);
app.use('/api/telebit.cloud/devices', oauth3Auth);
app.use('/api/telebit.cloud/domains', oauth3Auth);
app.use('/api/telebit.cloud/ports', oauth3Auth);
app.get('/api/telebit.cloud/account', function (req, res) {
getAccountData(req).then(function (result) {
res.send(result); res.send(result);
}).catch(function (err) { }).catch(function (err) {
return res.send({ return res.send({
@ -867,6 +891,77 @@ app.get('/api/telebit.cloud/account', function (req, res) {
}); });
}); });
}); });
app.post('/api/telebit.cloud/devices/:devId/:name', function (req, res) {
var name = req.params.name;
var devId = req.params.devId;
return getAccountData(req).then(function (grants) {
var err;
var dev;
// Test if we have permission to both the device and the domain
if (!(grants.domains.some(function (d) {
return d.name === name;
}) && grants.devices.some(function (d) {
if ((d.id && d.id === devId) || (d.socketId === devId)) {
dev = d;
return dev;
}
}))) {
// Missing permission to one or the other
err = new Error("You must have authorizations for both device '" + devId + "' and domain '" + name + "'... which you do not.");
err.code = "E_PERM";
return PromiseA.reject(err);
}
console.log("dev", dev);
// Test if the specified device already uses the specified domain
if (dev.names.some(function (n) {
if (n === name) { return true; }
})) {
// Yep, already there. No mods needed.
res.send({ success: true, modified: false });
return;
}
var jti = crypto.randomBytes(12).toString('hex');
var domainsMap = {};
var portsMap = {};
var authzData = {
id: jti
, domains: [ name ]
, ports: []
, aud: req._state.config.webminDomain
, iat: Math.round(Date.now() / 1000)
// of the client's computer
, hostname: dev.hostname
};
dev.names.forEach(function (name) {
if (!domainsMap[name]) {
domainsMap[name] = true;
authzData.domains.push(name);
}
});
dev.ports.forEach(function (num) {
if (!portsMap[num]) {
portsMap[num] = true;
authzData.ports.push(num);
}
});
var authz = jwt.sign(authzData, req._state.secret);
authzData.jwt = authz;
// TODO we need to force a grant
Server.onAuth(req._state, Devices.bySocket(req._state.deviceLists, dev.socketId), authz, authzData, true);
res.send({ success: true, data: authzData });
}).catch(function (err) {
return res.send({
error: {
code: err.code || "E_GENERIC"
, message: err.toString()
, _stack: err.stack
}
});
});
});
app.post('/api/telebit.cloud/account', function (req, res) { app.post('/api/telebit.cloud/account', function (req, res) {
return Accounts.create(req).then(function (acc) { return Accounts.create(req).then(function (acc) {
res.send({ res.send({
@ -968,7 +1063,7 @@ app.post('/api/telebit.cloud/account/authorizations/new/:value/:challenge?', fun
if (!records.some(function (txts) { if (!records.some(function (txts) {
return txts.some(function (txt) { return txts.some(function (txt) {
console.log('TXT', txt); //console.log('TXT', txt);
return claim.challenge === txt; return claim.challenge === txt;
}); });
})) { })) {
@ -984,7 +1079,7 @@ app.post('/api/telebit.cloud/account/authorizations/new/:value/:challenge?', fun
}); });
} }
console.log('claim', claim); //console.log('claim', claim);
if ('dns' === claim.type) { if ('dns' === claim.type) {
checkDns(); checkDns();
@ -1043,8 +1138,8 @@ app.get('/api/telebit.cloud/pair_request/:secret', function (req, res) {
// From User (which has entered pin) // From User (which has entered pin)
function pairCode(req, res) { function pairCode(req, res) {
console.log("DEBUG telebit.cloud magic"); //console.log("DEBUG telebit.cloud magic");
console.log(req.body || req.params); //console.log(req.body || req.params);
var magic; var magic;
var pin; var pin;