getting closer to accounts via api

This commit is contained in:
AJ ONeal 2018-06-20 09:07:35 +00:00
parent 58ed4baff1
commit e4640a8a00
4 changed files with 138 additions and 36 deletions

View File

@ -7,6 +7,8 @@ var pkg = require('../package.json');
var url = require('url'); var url = require('url');
var path = require('path'); var path = require('path');
var os = require('os'); var os = require('os');
var fs = require('fs');
var urequest = require('@coolaj86/urequest');
var common = require('../lib/cli-common.js'); var common = require('../lib/cli-common.js');
var http = require('http'); var http = require('http');
var YAML = require('js-yaml'); var YAML = require('js-yaml');
@ -397,7 +399,6 @@ function serveControlsHelper() {
res.end('{"error":{"message":"unrecognized rpc"}}'); res.end('{"error":{"message":"unrecognized rpc"}}');
}); });
var fs = require('fs');
if (fs.existsSync(state._ipc.path)) { if (fs.existsSync(state._ipc.path)) {
fs.unlinkSync(state._ipc.path); fs.unlinkSync(state._ipc.path);
} }
@ -494,15 +495,7 @@ function connectTunnel() {
controlServer.close(); controlServer.close();
} }
// reverse 2FA otp // reverse 2FA otp
function leftpad(i, n, c) {
while (i.toString().length < (n || 4)) {
i = (c || '0') + i;
}
return i;
}
function getOtp() {
return leftpad(Math.round(Math.random() * 9999), 4, '0');
}
process.on('SIGINT', sigHandler); process.on('SIGINT', sigHandler);
state.net = state.net || { state.net = state.net || {
createConnection: function (info, cb) { createConnection: function (info, cb) {
@ -515,7 +508,6 @@ function connectTunnel() {
} }
}; };
state.otp = getOtp();
state.greenlockConf = state.config.greenlock || {}; state.greenlockConf = state.config.greenlock || {};
state.sortingHat = state.config.sortingHat || path.resolve(__dirname, '..', 'lib/sorting-hat.js'); state.sortingHat = state.config.sortingHat || path.resolve(__dirname, '..', 'lib/sorting-hat.js');
@ -653,39 +645,79 @@ function rawTunnel(cb) {
state.relayUrl = common.parseUrl(state.relay); state.relayUrl = common.parseUrl(state.relay);
state.relayHostname = common.parseHostname(state.relay); state.relayHostname = common.parseHostname(state.relay);
if (!state.config.token && state.config.secret) { urequest({ url: state.relayUrl + common.apiDirectory, json: true }, function (err, resp, body) {
var jwt = require('jsonwebtoken'); state._apiDirectory = body;
var tokenData = { state.wss = body.tunnel.method + '://' + body.api_host.replace(/:hostname/g, state.relayHostname) + body.tunnel.pathname;
domains: Object.keys(state.config.servernames || {}).filter(function (name) {
return /\./.test(name); console.log('api dir:');
}) console.log(body);
, ports: Object.keys(state.config.ports || {}).filter(function (port) {
port = parseInt(port, 10); console.log('state.wss:');
return port > 0 && port <= 65535; console.log(state.wss);
})
, aud: aud if (!state.config.token && state.config.secret) {
, iss: Math.round(Date.now() / 1000) var jwt = require('jsonwebtoken');
var tokenData = {
domains: Object.keys(state.config.servernames || {}).filter(function (name) {
return /\./.test(name);
})
, ports: Object.keys(state.config.ports || {}).filter(function (port) {
port = parseInt(port, 10);
return port > 0 && port <= 65535;
})
, aud: state.relayUrl
, iss: Math.round(Date.now() / 1000)
};
state.token = jwt.sign(tokenData, state.config.secret);
}
state.token = state.token || state.config.token;
if (state.token) { cb(null, connectTunnel()); return; }
if (!state.config.email) {
cb(new Error("No email... how did that happen?"));
return;
}
// TODO sign token with own private key, including public key and thumbprint
// (much like ACME JOSE account)
state.otp = common.otp();
state._auth = {
subject: state.config.email
, subject_scheme: 'mailto'
// TODO create domains list earlier
, scope: Object.keys(state.config.servernames || {}).join(',')
, otp: state.otp
, hostname: os.hostname()
// Used for User-Agent
, os_type: os.type()
, os_platform: os.platform()
, os_release: os.release()
, os_arch: os.arch()
}; };
state.token = jwt.sign(tokenData, state.config.secret); if (err || !body || !body.pair_request) {
}
state.token = state.token || state.config.token;
common.urequest({ url: state.relayUrl + common.apiDirectory, json: true }, function (err, resp, body) {
state._apiDirectory = body;
state.wss = body.tunnel.method + '://' + body.api_host.replace(/:hostname/g, state.relayHostname) + body.tunnel.pathname
if (token) {
cb(null, connectTunnel()); cb(null, connectTunnel());
return; return;
} }
// TODO sign token with own private key, including public key and thumbprint
// (much like ACME JOSE account)
// TODO do auth stuff // TODO do auth stuff
var pairRequest = url.resolve('https://' + body.api_host.replace(/:hostname/g, state.relayHostname), body.pair_request.pathname);
var req = {
url: pairRequest
, method: body.pair_request.method
, json: state._auth
};
console.log('[telebitd.js] req');
console.log(req);
urequest(req, function (err, resp, body) {
if (err) { console.error('[telebitd.js] pair request', err); }
cb(null, connectTunnel()); console.log(body);
// TODO poll for token
//cb(null, connectTunnel());
}
);
}); });
} }

View File

@ -37,8 +37,30 @@ common.parseUrl = function (hostname) {
hostname = location.protocol.replace(/https?/, 'https') + '//' + hostname + location.pathname; hostname = location.protocol.replace(/https?/, 'https') + '//' + hostname + location.pathname;
return hostname; return hostname;
}; };
common.parseHostname = function (hostname) {
var url = require('url');
var location = url.parse(hostname);
if (!location.protocol || /\./.test(location.protocol)) {
hostname = 'https://' + hostname;
location = url.parse(hostname);
}
//hostname = location.hostname + (location.port ? ':' + location.port : '');
//hostname = location.protocol.replace(/https?/, 'https') + '//' + hostname + location.pathname;
return location.hostname;
};
common.apiDirectory = '_apis/telebit.cloud/index.json'; common.apiDirectory = '_apis/telebit.cloud/index.json';
function leftpad(i, n, c) {
while (i.toString().length < (n || 4)) {
i = (c || '0') + i;
}
return i;
}
common.otp = function getOtp() {
return leftpad(Math.round(Math.random() * 9999), 4, '0');
};
common.urequest = function (opts, cb) { common.urequest = function (opts, cb) {
var https = require('https'); var https = require('https');
// request.js behavior: // request.js behavior:

30
tests/pair-request.js Normal file
View File

@ -0,0 +1,30 @@
'use strict';
var email = 'jon@example.com';
var urequest = require('@coolaj86/urequest');
var req = {
url: 'https://api.telebit.ppl.family/api/telebit.cloud/pair_request'
, method: 'POST'
, headers: { 'cOntEnt-tYpE': 'application/json;charset=utf-8' }
, json: {
subject: email
, subject_scheme: 'mailto'
, scope: ''
, otp: '321654'
, hostname: "Jon's Macbook Pro"
, os_type: 'Linux'
, os_platform: 'linux'
, os_release: '4.4.0-116-generic'
, os_arch: 'x64'
}
};
urequest(req, function (err, resp, body) {
if (err) {
console.error(err);
return;
}
console.log('Location:', resp.headers.location);
console.log('Body:');
console.log(body);
});

18
tests/pair-state.js Normal file
View File

@ -0,0 +1,18 @@
'use strict';
var stateUrl = 'https://api.telebit.ppl.family/api/telebit.cloud/pair_state/bca27428719e9c67805359f1';
var urequest = require('@coolaj86/urequest');
var req = {
url: stateUrl
, method: 'GET'
, json: true
};
urequest(req, function (err, resp, body) {
if (err) {
console.error(err);
return;
}
console.log('Done:');
console.log(body);
});