From 9441bf7f8ec8e7ffb732929b358dc24e73f91391 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Fri, 29 Jun 2018 04:15:23 -0600 Subject: [PATCH] WIP get preauth clientside and realauth daemonside --- bin/telebit.js | 561 ++++++++++++++++++++---------------- bin/telebitd.js | 23 +- lib/cli-common.js | 49 +++- lib/remote.js | 13 +- package.json | 12 +- usr/share/install_helper.sh | 3 + 6 files changed, 390 insertions(+), 271 deletions(-) diff --git a/bin/telebit.js b/bin/telebit.js index b04bea4..9834e1d 100755 --- a/bin/telebit.js +++ b/bin/telebit.js @@ -35,8 +35,8 @@ if (-1 !== argIndex) { } function help() { - console.info(''); - console.info('Telebit Remote v' + pkg.version); + //console.info(''); + //console.info('Telebit Remote v' + pkg.version); console.info(''); console.info('Usage:'); console.info(''); @@ -90,8 +90,7 @@ if (!confpath || /^--/.test(confpath)) { process.exit(1); } -function askForConfig(answers, mainCb) { - answers = answers || {}; +function askForConfig(state, mainCb) { var fs = require('fs'); var ttyname = '/dev/tty'; var stdin = useTty ? fs.createReadStream(ttyname, { @@ -104,7 +103,7 @@ function askForConfig(answers, mainCb) { // https://github.com/nodejs/node/issues/21319 , terminal: !useTty }); - answers._useTty = useTty; + state._useTty = useTty; // NOTE: Use of setTimeout // We're using setTimeout just to make the user experience a little @@ -115,7 +114,7 @@ function askForConfig(answers, mainCb) { // ~ 150-250ms is the sweet spot for most humans (long enough to notice change and not be jarred, but stay on task) var firstSet = [ function askEmail(cb) { - if (answers.email) { cb(); return; } + if (state.config.email) { cb(); return; } //console.info(""); console.info("Welcome!"); console.info(""); @@ -130,8 +129,8 @@ function askForConfig(answers, mainCb) { rl.question('email: ', function (email) { email = /@/.test(email) && email.trim(); if (!email) { askEmail(cb); return; } - answers.email = email.trim(); - answers.agree_tos = true; + state.config.email = email.trim(); + state.config.agreeTos = true; console.info(""); setTimeout(cb, 250); }); @@ -159,14 +158,14 @@ function askForConfig(answers, mainCb) { console.warn(""); console.warn(body); } else if (body && body.pair_request) { - answers._can_pair = true; + state._can_pair = true; } - answers.relay = relay; + state.config.relay = relay; cb(); }); } - if (answers.relay) { checkRelay(); return; } + if (state.config.relay) { checkRelay(); return; } console.info(""); console.info(""); console.info("What relay will you be using? (press enter for default)"); @@ -175,10 +174,10 @@ function askForConfig(answers, mainCb) { } , function checkRelay(cb) { nextSet = []; - if ('telebit.cloud' !== answers.relay) { + if ('telebit.cloud' !== state.config.relay) { nextSet = nextSet.concat(standardSet); } - if (!answers._can_pair) { + if (!state._can_pair) { nextSet = nextSet.concat(fossSet); } cb(); @@ -188,7 +187,7 @@ function askForConfig(answers, mainCb) { // There are questions that we need to aks in the CLI // if we can't guarantee that they are being asked in the web interface function askAgree(cb) { - if (answers.agree_tos) { cb(); return; } + if (state.config.agreeTos) { cb(); return; } console.info(""); console.info(""); console.info("Do you accept the terms of service for each and all of the following?"); @@ -204,7 +203,7 @@ function askForConfig(answers, mainCb) { if (!/^y(es)?$/i.test(resp) && 'true' !== resp) { throw new Error("You didn't accept the Terms of Service... not sure what to do..."); } - answers.agree_tos = true; + state.config.agreeTos = true; console.info(""); setTimeout(cb, 250); }); @@ -212,35 +211,35 @@ function askForConfig(answers, mainCb) { , function askUpdates(cb) { // required means transactional, security alerts, mandatory updates var options = [ 'newsletter', 'important', 'required' ]; - if (-1 !== options.indexOf(answers.updates)) { cb(); return; } + if (-1 !== options.indexOf(state._updates)) { cb(); return; } console.info(""); console.info(""); console.info("What updates would you like to receive? (" + options.join(',') + ")"); console.info(""); rl.question('messages (default: important): ', function (updates) { - updates = (updates || '').trim().toLowerCase(); - if (!updates) { updates = 'important'; } - if (-1 === options.indexOf(updates)) { askUpdates(cb); return; } + state._updates = (updates || '').trim().toLowerCase(); + if (!state._updates) { state._updates = 'important'; } + if (-1 === options.indexOf(state._updates)) { askUpdates(cb); return; } - if ('newsletter' === updates) { - answers.newsletter = true; - answers.communityMember = true; - } else if ('important' === updates) { - answers.communityMember = true; + if ('newsletter' === state._updates) { + state.config.newsletter = true; + state.config.communityMember = true; + } else if ('important' === state._updates) { + state.config.communityMember = true; } setTimeout(cb, 250); }); } , function askTelemetry(cb) { - if (answers.telemetry) { cb(); return; } + if (state.config.telemetry) { cb(); return; } console.info(""); console.info(""); console.info("Contribute project telemetry data? (press enter for default [yes])"); console.info(""); rl.question('telemetry [Y/n]: ', function (telemetry) { if (!telemetry || /^y(es)?$/i.test(telemetry)) { - answers.telemetry = true; + state.config.telemetry = true; } setTimeout(cb, 250); }); @@ -248,10 +247,11 @@ function askForConfig(answers, mainCb) { ]; var fossSet = [ function askTokenOrSecret(cb) { - if (answers._can_pair || answers.token || answers.secret) { cb(); return; } + if (state._can_pair || state.token || state.config.token + || state.secret || state.config.secret) { cb(); return; } console.info(""); console.info(""); - console.info("What's your authorization for '" + answers.relay + "'?"); + console.info("What's your authorization for '" + state.config.relay + "'?"); console.info(""); // TODO check .well-known to learn supported token types console.info("Currently supported:"); @@ -264,24 +264,17 @@ function askForConfig(answers, mainCb) { var jwt = require('jsonwebtoken'); resp = (resp || '').trim(); try { - answers.token = jwt.decode(resp); + state.config.token = jwt.decode(resp); } catch(e) { // is not jwt - try { - if (JSON.parse(resp).subject) { - answers.token = resp; - } - } catch(e) { - // is not authRequest either - } } - if (!answers.token) { + if (!state.config.token) { resp = resp.toLowerCase(); if (resp === Buffer.from(resp, 'hex').toString('hex')) { - answers.secret = resp; + state.config.secret = resp; } } - if (!answers.token && !answers.secret) { + if (!state.config.token && !state.config.secret) { askTokenOrSecret(cb); return; } @@ -289,7 +282,7 @@ function askForConfig(answers, mainCb) { }); } , function askServernames(cb) { - if (!answers.secret || answers.servernames) { cb(); return; } + if (!state.config.secret || state.config._servernames) { cb(); return; } console.info(""); console.info(""); console.info("What servername(s) will you be relaying here?"); @@ -299,12 +292,12 @@ function askForConfig(answers, mainCb) { resp = (resp || '').trim().split(/,/g); if (!resp.length) { askServernames(); return; } // TODO validate the domains - answers.servernames = resp; + state.config._servernames = resp; setTimeout(cb, 250); }); } , function askPorts(cb) { - if (!answers.secret || answers.ports) { cb(); return; } + if (!state.config.secret || state.config._ports) { cb(); return; } console.info(""); console.info(""); console.info("What tcp port(s) will you be relaying here?"); @@ -314,7 +307,7 @@ function askForConfig(answers, mainCb) { resp = (resp || '').trim().split(/,/g); if (!resp.length) { askPorts(); return; } // TODO validate the domains - answers.ports = resp; + state.config._ports = resp; setTimeout(cb, 250); }); } @@ -328,7 +321,7 @@ function askForConfig(answers, mainCb) { if (useTty) { try { stdin.push(null); } catch(e) { /*ignore*/ } } rl.close(); if (useTty) { try { stdin.close(); } catch(e) { /*ignore*/ } } - mainCb(null, answers); + mainCb(null, state); return; } q(next); @@ -403,6 +396,8 @@ var utils = { }); } , putConfig: function putConfig(service, args, fn) { + //console.log('debug path:'); + //console.log('/rpc/' + service + '?_body=' + JSON.stringify(args)); var req = http.get({ socketPath: state._ipc.path , method: 'POST' @@ -465,53 +460,143 @@ var utils = { } }; -function parseConfig(err, text) { +// Two styles: +// http 3000 +// http modulename +function makeRpc(key) { + if (key !== argv[0]) { + return false; + } + utils.putConfig(argv[0], argv.slice(1)); + return true; +} - console.info(""); - console.info(verstr.join(' ')); +function packConfig(config) { + return Object.keys(config).map(function (key) { + var val = config[key]; + if (val && 'object' === typeof val && !Array.isArray(val)) { + val = JSON.stringify(val); + } + return key + ':' + val; // converts arrays to strings with , + }); +} - try { - state.config = JSON.parse(text || '{}'); - } catch(e1) { - try { - state.config = YAML.safeLoad(text || '{}'); - } catch(e2) { - console.error(e1.message); - console.error(e2.message); - process.exit(1); +function getToken(err, state) { + if (err) { + console.error("Error while initializing config [init]:"); + throw err; + } + + // { _otp, config: {} } + common.api.token(state, { + error: function (err/*, next*/) { + console.error("[Error] common.api.token:"); + console.error(err); return; } - } - - state.config = camelCopy(state.config || {}) || {}; - common._init( - state.config.root || path.join(os.homedir(), '.local/share/telebit') - , (state.config.root && path.join(state.config.root, 'etc')) || path.join(os.homedir(), '.config/telebit') - ); - state._ipc = common.pipename(state.config, true); - - if (!Object.keys(state.config).length) { - console.info('(' + state._ipc.comment + ": " + state._ipc.path + ')'); - } - console.info(""); - - if ((err && 'ENOENT' === err.code) || !Object.keys(state.config).length) { - if (!err || 'ENOENT' === err.code) { - //console.warn("Empty config file. Run 'telebit init' to configure.\n"); - } else { - console.warn("Couldn't load config:\n\n\t" + err.message + "\n"); + , directory: function (dir, next) { + //console.log('Telebit Relay Discovered:'); + state._apiDirectory = dir; + //console.log(dir); + //console.log(); + next(); } - } - - // Two styles: - // http 3000 - // http modulename - function makeRpc(key) { - if (key !== argv[0]) { - return false; + , tunnelUrl: function (tunnelUrl, next) { + //console.log('Telebit Relay Tunnel Socket:', tunnelUrl); + state.wss = tunnelUrl; + next(); } - utils.putConfig(argv[0], argv.slice(1)); - return true; + , requested: function (authReq, next) { + //console.log("Pairing Requested"); + state.config._otp = state.config._otp = authReq.otp; + + if (!state.config.token && state._can_pair) { + console.info(""); + console.info("=============================================="); + console.info(" Hey, Listen! "); + console.info("=============================================="); + console.info(" "); + console.info(" GO CHECK YOUR EMAIL! "); + console.info(" "); + console.info(" DEVICE PAIR CODE: 0000 ".replace(/0000/g, state.config._otp)); + console.info(" "); + console.info("=============================================="); + console.info(""); + } + + next(); + } + , connect: function (pretoken, next) { + //console.log("Enabling Pairing Locally..."); + state.config.pretoken = pretoken; + state._connecting = true; + + // TODO use php-style object querification +// TODO XXX + utils.putConfig('config', packConfig(state.config), function (err/*, body*/) { + if (err) { + state._error = err; + console.error("Error while initializing config [connect]:"); + console.error(err); + return; + } + console.log("waiting..."); + next(); + }); + } + , offer: function (token, next) { + //console.log("Pairing Enabled by Relay"); + state.config.token = token; + if (state._error) { + return; + } + if (state._connecting) { + return; + } + state._connecting = true; + utils.putConfig('config', packConfig(state.config), function (err/*, body*/) { + if (err) { + state._error = err; + console.error("Error while initializing config [offer]:"); + console.error(err); + return; + } + //console.log("Pairing Enabled Locally"); + next(); + }); + } + , granted: function (_, next) { + //console.log("Pairing complete!"); + next(); + } + , end: function () { + utils.putConfig('enable', [], function (err) { + if (err) { console.error(err); return; } + //console.info("Success"); + + // workaround for https://github.com/nodejs/node/issues/21319 + if (state._useTty) { + setTimeout(function () { + console.info(); + console.info("Press any key to continue..."); + console.info(); + process.exit(0); + }, 0.5 * 1000); + return; + } + // end workaround + + parseCli(state); + }); + } + }); +} + +function parseCli(/*state*/) { + if (-1 !== argv.indexOf('init')) { + utils.putConfig('list', []/*, function (err) { + }*/); + return; } if ([ 'ssh', 'http', 'tcp' ].some(function (key) { @@ -522,124 +607,10 @@ function parseConfig(err, text) { utils.putConfig(argv[0], argv.slice(1)); return true; } - help(); return true; })) { - return true; - } - - if (-1 !== argv.indexOf('init')) { - parsers.init(argv, function (err, answers) { - if (err) { - console.error("Error while initializing config [init]:"); - throw err; - } - - common.api.token(state, { - error: function (err/*, next*/) { - console.error("[Error] common.api.token:"); - console.error(err); - return; - } - , directory: function (dir, next) { - //console.log('Telebit Relay Discovered:'); - state._apiDirectory = dir; - //console.log(dir); - //console.log(); - next(); - } - , tunnelUrl: function (tunnelUrl, next) { - //console.log('Telebit Relay Tunnel Socket:', tunnelUrl); - state.wss = tunnelUrl; - next(); - } - , requested: function (authReq, next) { - //console.log("Pairing Requested"); - var pin = authReq.pin || authReq.otp || authReq.pairCode; - state.otp = state._otp = pin; - state.auth = state.authRequest = state._auth = authReq; - - if (!answers.token && answers._can_pair) { - console.info(""); - console.info("=============================================="); - console.info(" Hey, Listen! "); - console.info("=============================================="); - console.info(" "); - console.info(" GO CHECK YOUR EMAIL! "); - console.info(" "); - console.info(" DEVICE PAIR CODE: 0000 ".replace(/0000/g, answers._otp)); - console.info(" "); - console.info("=============================================="); - console.info(""); - } - - next(); - } - , connect: function (pretoken, next) { - //console.log("Enabling Pairing Locally..."); - answers.token = pretoken; - answers._connecting = true; - // TODO use php-style object querification - utils.putConfig('config', Object.keys(answers).map(function (key) { - return key + ':' + answers[key]; - }), function (err/*, body*/) { - if (err) { - answers._error = err; - console.error("Error while initializing config [connect]:"); - console.error(err); - return; - } - //console.log("Pairing Enabled Locally"); - next(); - }); - } - , offer: function (token, next) { - //console.log("Pairing Enabled by Relay"); - answers.token = token; - if (answers._error) { - return; - } - if (answers._connecting) { - return; - } - answers._connecting = true; - utils.putConfig('config', Object.keys(answers).map(function (key) { - return key + ':' + answers[key]; - }), function (err/*, body*/) { - if (err) { - answers._error = err; - console.error("Error while initializing config [offer]:"); - console.error(err); - return; - } - //console.log("Pairing Enabled Locally"); - next(); - }); - } - , granted: function (_, next) { - //console.log("Token has been granted!"); - next(); - } - , end: function () { - utils.putConfig('enable', [], function () { - utils.putConfig('list', [], function (err) { - if (err) { console.error(err); return; } - console.info("Success"); - // workaround for https://github.com/nodejs/node/issues/21319 - if (answers._useTty) { - setTimeout(function () { - console.info(); - console.info("Press any key to continue..."); - console.info(); - process.exit(0); - }, 0.5 * 1000); - } - // end workaround - }); - }); - } - }); - }); + help(); + process.exit(13); return; } @@ -648,59 +619,167 @@ function parseConfig(err, text) { } help(); + process.exit(11); +} + +function handleConfig(err, config) { + //console.log('CONFIG'); + //console.log(config); + state.config = config; + + if (err) { console.error(err); process.exit(101); return; } + + // + // check for init first, before anything else + // because it has arguments that may help in + // the next steps + // + if (-1 !== argv.indexOf('init')) { + parsers.init(argv, getToken); + return; + } + + if (!state.config.relay || !state.config.token) { + if (!state.config.relay) { + state.config.relay = 'telebit.cloud'; + } + + console.log("question the user?", Date.now()); + askForConfig(state, function (err, state) { + // no errors actually get passed, so this is just future-proofing + if (err) { throw err; } + + if (!state.config.token && state._can_pair) { + state.config._otp = common.otp(); + } + + console.log("done questioning:", Date.now()); + state.relay = state.config.relay; + if (!state.token && !state.config.token) { + getToken(err, state); + } else { + parseCli(state); + } + }); + return; + } + + console.log("no questioning:"); + parseCli(state); +} + +function parseConfig(err, text) { + + console.info(""); + console.info(verstr.join(' ')); + + try { + state._clientConfig = JSON.parse(text || '{}'); + } catch(e1) { + try { + state._clientConfig = YAML.safeLoad(text || '{}'); + } catch(e2) { + console.error(e1.message); + console.error(e2.message); + process.exit(1); + return; + } + } + + state._clientConfig = camelCopy(state._clientConfig || {}) || {}; + common._init( + // make a default working dir and log dir + state._clientConfig.root || path.join(os.homedir(), '.local/share/telebit') + , (state._clientConfig.root && path.join(state._clientConfig.root, 'etc')) + || path.resolve(common.DEFAULT_CONFIG_PATH, '..') + ); + state._ipc = common.pipename(state._clientConfig, true); + + if (!Object.keys(state._clientConfig).length) { + console.info('(' + state._ipc.comment + ": " + state._ipc.path + ')'); + console.info(""); + } + + if ((err && 'ENOENT' === err.code) || !Object.keys(state._clientConfig).length) { + if (!err || 'ENOENT' === err.code) { + //console.warn("Empty config file. Run 'telebit init' to configure.\n"); + } else { + console.warn("Couldn't load config:\n\n\t" + err.message + "\n"); + } + } + + utils.request({ service: 'config' }, handleConfig); } var parsers = { - init: function (argv, cb) { + init: function (argv, parseCb) { var answers = {}; - var bool = [ - '--advanced' - ]; + var boolish = [ '--advanced' ]; if ('init' !== argv[0]) { throw new Error("init must be the first argument"); } argv.shift(); - utils.request({ service: 'config' }, function (err/*, body*/) { - if (err) { cb(err); return; } + // init --foo bar + argv.forEach(function (arg, i) { + if (!/^--/.test(arg)) { return; } + if (-1 !== boolish.indexOf(arg)) { + answers['_' + arg.replace(/^--/, '')] = true; + } + if (/^-/.test(argv[i + 1])) { + throw new Error(argv[i + 1] + ' requires an argument'); + } + answers[arg] = argv[i + 1]; + }); - // init --foo bar - argv.forEach(function (arg, i) { - if (!/^--/.test(arg)) { return; } - if (-1 !== bool.indexOf(arg)) { - answers['_' + arg.replace(/^--/, '')] = true; - } - if (/^-/.test(argv[i + 1])) { - throw new Error(argv[i + 1] + ' requires an argument'); - } - answers[arg] = argv[i + 1]; - }); - // init foo:bar - argv.forEach(function (arg) { - if (/^--/.test(arg)) { return; } - var parts = arg.split(/:/g); - if (2 !== parts.length) { - throw new Error("bad option to init: '" + arg + "'"); - } - if (answers[parts[0]]) { - throw new Error("duplicate key to init '" + parts[0] + "'"); - } - answers[parts[0]] = parts[1]; - }); + // init foo:bar + argv.forEach(function (arg) { + if (/^--/.test(arg)) { return; } + var parts = arg.split(/:/g); + if (2 !== parts.length) { + throw new Error("bad option to init: '" + arg + "'"); + } + if (answers[parts[0]]) { + throw new Error("duplicate key to init '" + parts[0] + "'"); + } + answers[parts[0]] = parts[1]; + }); - if (!answers._advanced && !answers.relay) { - answers.relay = 'telebit.cloud'; + // things that aren't straight-forward copy-over + if (!answers.advanced && !answers.relay) { + answers.relay = 'telebit.cloud'; + } + if (Array.isArray(common._NOTIFICATIONS[answers.update])) { + common._NOTIFICATIONS[answers.update].forEach(function (name) { + state.config[name] = true; + }); + } + if (answers.servernames) { + state.config._servernames = answers.servernames; + } + if (answers.ports) { + state.config._ports = answers.ports; + } + + // things that are straight-forward copy-over + common.CONFIG_KEYS.forEach(function (key) { + if ('true' === answers[key]) { answers[key] = true; } + if ('false' === answers[key]) { answers[key] = false; } + if ('null' === answers[key]) { answers[key] = null; } + if ('undefined' === answers[key]) { delete answers[key]; } + if ('undefined' !== typeof answers[key]) { + state.config[key] = answers[key]; + } + }); + + askForConfig(state, function (err, state) { + if (err) { parseCb(err); return; } + + if (!state.config.token && state._can_pair) { + state.config._otp = common.otp(); } - askForConfig(answers, function (err, answers) { - if (err) { cb(err); return; } - - if (!answers.token && answers._can_pair) { - answers._otp = common.otp(); - } - - cb(null, answers); - }); + parseCb(null, state); }); } }; diff --git a/bin/telebitd.js b/bin/telebitd.js index 89de872..85876e6 100755 --- a/bin/telebitd.js +++ b/bin/telebitd.js @@ -168,8 +168,9 @@ function serveControlsHelper() { // TODO camelCase query state.config.email = conf.email || state.config.email || ''; - if ('undefined' !== typeof conf.agree_tos) { - state.config.agreeTos = conf.agree_tos; + if ('undefined' !== typeof conf.agreeTos + || 'undefined' !== typeof conf.agreeTos ) { + state.config.agreeTos = conf.agreeTos || conf.agree_tos; } state.otp = conf._otp || '0000'; // this should only be done on the client side state.config.relay = conf.relay || state.config.relay || ''; @@ -179,24 +180,28 @@ function serveControlsHelper() { if (state.secret) { state.token = common.signToken(state); } + if (!state.token) { + state.token = conf._token; + } if ('undefined' !== typeof conf.newsletter) { state.config.newsletter = conf.newsletter; } - if ('undefined' !== typeof conf.community_member) { - state.config.communityMember = conf.community_member; + if ('undefined' !== typeof conf.communityMember + || 'undefined' !== typeof conf.community_member) { + state.config.communityMember = conf.communityMember || conf.community_member; } if ('undefined' !== typeof conf.telemetry) { state.config.telemetry = conf.telemetry; } - if (conf.servernames) { - (conf.servernames||'').split(/,/g).forEach(function (key) { + if (conf._servernames) { + (conf._servernames||'').split(/,/g).forEach(function (key) { if (!state.config.servernames[key]) { state.config.servernames[key] = {}; } }); } - if (conf.ports) { - (conf.ports||'').split(/,/g).forEach(function (key) { + if (conf._ports) { + (conf._ports||'').split(/,/g).forEach(function (key) { if (!state.config.ports[key]) { state.config.ports[key] = {}; } @@ -475,7 +480,7 @@ function parseConfig(err, text) { state.config = {}; } common._init( - state.config.root || path.join(__dirname, '..') + state.config.root || path.join(os.homedir(), '.local/share/telebit') // || path.join(__dirname, '..') , (state.config.root && path.join(state.config.root, 'etc')) || path.resolve(common.DEFAULT_CONFIG_PATH, '..') ); state._ipc = common.pipename(state.config, true); diff --git a/lib/cli-common.js b/lib/cli-common.js index 1004dc9..758eb06 100644 --- a/lib/cli-common.js +++ b/lib/cli-common.js @@ -9,6 +9,27 @@ var os = require('os'); var homedir = os.homedir(); var urequest = require('@coolaj86/urequest'); +common._NOTIFICATIONS = { + 'newsletter': [ 'newsletter', 'communityMember' ] +, 'important': [ 'communityMember' ] +}; +common.CONFIG_KEYS = [ + 'newsletter' +, 'communityMember' +, 'telemetry' +, 'sshAuto' +, 'email' +, 'agreeTos' +, 'relay' +, 'token' +, 'pretoken' +, 'secret' +]; +//, '_servernames' // list instead of object +//, '_ports' // list instead of object +//, '_otp' // otp should not be saved +//, '_token' // temporary token + common.pipename = function (config, newApi) { var _ipc = { path: (config.sock || common.DEFAULT_SOCK_PATH) @@ -62,7 +83,7 @@ common.signToken = function (state) { port = parseInt(port, 10); return port > 0 && port <= 65535; }) - , aud: state.relayUrl + , aud: state._relayUrl , iss: Math.round(Date.now() / 1000) }; @@ -70,8 +91,8 @@ common.signToken = function (state) { }; common.api = {}; common.api.directory = function (state, next) { - state.relayUrl = common.parseUrl(state.relay); - urequest({ url: state.relayUrl + common.apiDirectory, json: true }, function (err, resp, dir) { + state._relayUrl = common.parseUrl(state.relay); + urequest({ url: state._relayUrl + common.apiDirectory, json: true }, function (err, resp, dir) { if (!dir) { dir = { api_host: ':hostname', tunnel: { method: "wss", pathname: "" } }; } state._apiDirectory = dir; next(err, dir); @@ -81,9 +102,8 @@ common.api._parseWss = function (state, dir) { if (!dir || !dir.api_host) { dir = { api_host: ':hostname', tunnel: { method: "wss", pathname: "" } }; } - state.relayHostname = common.parseHostname(state.relay); - state.wss = dir.tunnel.method + '://' + dir.api_host.replace(/:hostname/g, state.relayHostname) + dir.tunnel.pathname; - return state.wss; + state._relayHostname = common.parseHostname(state.relay); + return dir.tunnel.method + '://' + dir.api_host.replace(/:hostname/g, state._relayHostname) + dir.tunnel.pathname; }; common.api.wss = function (state, cb) { common.api.directory(state, function (err, dir) { @@ -99,12 +119,13 @@ common.api.token = function (state, handlers) { handlers.tunnelUrl(state.wss, function () { //console.log('[debug] after tunnelUrl'); - if (/*!state.config.token &&*/ state.config.secret) { - state.token = common.signToken(state); + if (state.config.secret /* && !state.config.token */) { + state.config._token = common.signToken(state); } - state.token = state.token || state.config.token; + state.token = state.token || state.config.token || state.config._token; if (state.token) { //console.log('[debug] token via token or secret'); + // { token, pretoken } handlers.connect(state.token, function () { handlers.end(null, function () {}); }); @@ -120,13 +141,13 @@ common.api.token = function (state, handlers) { // TODO sign token with own private key, including public key and thumbprint // (much like ACME JOSE account) - var otp = state.otp || state._otp || '0000'; // common.otp(); - var authReq = state.authRequest || state._auth || { + var otp = state.config._otp; // common.otp(); + var authReq = { subject: state.config.email , subject_scheme: 'mailto' // TODO create domains list earlier - , scope: Object.keys(state.config.servernames || {}) - .concat(Object.keys(state.config.ports || {})).join(',') + , scope: (state.config._servernames || Object.keys(state.config.servernames || {})) + .concat(state.config._ports || Object.keys(state.config.ports || {})).join(',') , otp: otp , hostname: os.hostname() // Used for User-Agent @@ -135,7 +156,7 @@ common.api.token = function (state, handlers) { , os_release: os.release() , os_arch: os.arch() }; - var pairRequestUrl = url.resolve('https://' + dir.api_host.replace(/:hostname/g, state.relayHostname), dir.pair_request.pathname); + var pairRequestUrl = url.resolve('https://' + dir.api_host.replace(/:hostname/g, state._relayHostname), dir.pair_request.pathname); var req = { url: pairRequestUrl , method: dir.pair_request.method diff --git a/lib/remote.js b/lib/remote.js index 4a75363..9ac157b 100644 --- a/lib/remote.js +++ b/lib/remote.js @@ -1,8 +1,13 @@ (function () { 'use strict'; +var PromiseA; +try { + PromiseA = require('bluebird'); +} catch(e) { + PromiseA = global.Promise; +} var WebSocket = require('ws'); -var PromiseA = require('bluebird'); var sni = require('sni'); var Packer = require('proxy-packer'); var os = require('os'); @@ -453,11 +458,11 @@ function _connect(state) { if (!authenticated) { if(state.handlers.onError) { - let err = new Error('Failed to connect on first attempt... check authentication'); + var err = new Error('Failed to connect on first attempt... check authentication'); state.handlers.onError(err); } if(state.handlers.onClose) { - state.handlers.onClose() + state.handlers.onClose(); } console.info('[close] failed on first attempt... check authentication.'); timeoutId = null; @@ -470,7 +475,7 @@ function _connect(state) { timeoutId = setTimeout(connect, 5000); } else { if(state.handlers.onClose) { - state.handlers.onClose() + state.handlers.onClose(); } } } diff --git a/package.json b/package.json index 94812b6..5aa953b 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,10 @@ "version": "0.18.9", "description": "Break out of localhost. Connect to any device from anywhere over any tcp port or securely in a browser. A secure tunnel. A poor man's reverse VPN.", "main": "lib/remote.js", + "files": [ + "bin", + "lib" + ], "bin": { "telebit": "bin/telebit.js", "telebitd": "bin/telebitd.js" @@ -48,10 +52,9 @@ }, "homepage": "https://git.coolaj86.com/coolaj86/telebit.js#readme", "dependencies": { - "@coolaj86/urequest": "^1.1.1", - "bluebird": "^3.5.1", + "@coolaj86/urequest": "^1.1.4", "finalhandler": "^1.1.1", - "greenlock": "^2.2.20", + "greenlock": "^2.3.1", "js-yaml": "^3.11.0", "jsonwebtoken": "^7.1.9", "mkdirp": "^0.5.1", @@ -63,6 +66,9 @@ "socket-pair": "^1.0.3", "ws": "^2.2.3" }, + "trulyOptionalDependencies": { + "bluebird": "^3.5.1" + }, "enginesStrict": true, "engines": { "node": "10.2.1 10.4" diff --git a/usr/share/install_helper.sh b/usr/share/install_helper.sh index f295977..c619ae8 100644 --- a/usr/share/install_helper.sh +++ b/usr/share/install_helper.sh @@ -206,6 +206,9 @@ pushd $TELEBIT_TMP >/dev/null echo -n "." fi $tmp_npm install >/dev/null 2>/dev/null & + # ursa is now an entirely optional dependency for key generation + # but very much needed on ARM devices + $tmp_npm install ursa >/dev/null 2>/dev/null & tmp_npm_pid=$! while [ -n "$tmp_npm_pid" ]; do sleep 2