handle remote push of domains
This commit is contained in:
parent
739f86f1cc
commit
7d11f15ebe
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue