WIP de-nest and restructure
This commit is contained in:
parent
2a28dca257
commit
2301966f9f
File diff suppressed because it is too large
Load Diff
|
@ -525,6 +525,27 @@ controllers.newAccount = function (req, res) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
controllers.acmeAccounts = function (req, res) {
|
||||||
|
if (!req.jws || !req.jws.verified) {
|
||||||
|
res.statusCode = 400;
|
||||||
|
res.send({"error":{"message": "this type of requests must be encoded as a jws payload"
|
||||||
|
+ " and signed by a known account holder"}});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var account;
|
||||||
|
var accountId = req.params[0];
|
||||||
|
DB.accounts.some(function (acc) {
|
||||||
|
// TODO calculate thumbprint from jwk
|
||||||
|
// find a key with matching jwk
|
||||||
|
if (acc._id === accountId) {
|
||||||
|
account = acc;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// TODO check that the JWS matches the accountI
|
||||||
|
console.warn("[warn] account ID still acts as secret, should use JWS kid for verification");
|
||||||
|
res.send(account);
|
||||||
|
};
|
||||||
|
|
||||||
function jsonEggspress(req, res, next) {
|
function jsonEggspress(req, res, next) {
|
||||||
/*
|
/*
|
||||||
|
@ -1064,6 +1085,10 @@ function handleApi() {
|
||||||
|
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
// TODO convert /acme/accounts/:account_id into a regex
|
||||||
|
app.get(/^\/acme\/accounts\/([\w]+)/, controllers.acmeAccounts);
|
||||||
|
// POST-as-GET
|
||||||
|
app.post(/^\/acme\/accounts\/([\w]+)/, controllers.acmeAccounts);
|
||||||
app.use(/\b(relay)\b/, mustTrust, controllers.relay);
|
app.use(/\b(relay)\b/, mustTrust, controllers.relay);
|
||||||
app.get(/\b(config)\b/, mustTrust, getConfigOnly);
|
app.get(/\b(config)\b/, mustTrust, getConfigOnly);
|
||||||
app.use(/\b(init|config)\b/, mustTrust, initOrConfig);
|
app.use(/\b(init|config)\b/, mustTrust, initOrConfig);
|
||||||
|
|
|
@ -186,29 +186,31 @@ var telebitState = {};
|
||||||
var appMethods = {
|
var appMethods = {
|
||||||
initialize: function () {
|
initialize: function () {
|
||||||
console.log("call initialize");
|
console.log("call initialize");
|
||||||
if (!appData.init.relay) {
|
return requestAccountHelper().then(function (/*key*/) {
|
||||||
appData.init.relay = DEFAULT_RELAY;
|
if (!appData.init.relay) {
|
||||||
}
|
appData.init.relay = DEFAULT_RELAY;
|
||||||
appData.init.relay = appData.init.relay.toLowerCase();
|
|
||||||
telebitState = { relay: appData.init.relay };
|
|
||||||
|
|
||||||
return Telebit.api.directory(telebitState).then(function (dir) {
|
|
||||||
if (!dir.api_host) {
|
|
||||||
window.alert("Error: '" + telebitState.relay + "' does not appear to be a valid telebit service");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
appData.init.relay = appData.init.relay.toLowerCase();
|
||||||
|
telebitState = { relay: appData.init.relay };
|
||||||
|
|
||||||
telebitState.dir = dir;
|
return Telebit.api.directory(telebitState).then(function (dir) {
|
||||||
|
if (!dir.api_host) {
|
||||||
|
window.alert("Error: '" + telebitState.relay + "' does not appear to be a valid telebit service");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// If it's one of the well-known relays
|
telebitState.dir = dir;
|
||||||
if (-1 !== TELEBIT_RELAYS.indexOf(appData.init.relay)) {
|
|
||||||
return doConfigure();
|
// If it's one of the well-known relays
|
||||||
} else {
|
if (-1 !== TELEBIT_RELAYS.indexOf(appData.init.relay)) {
|
||||||
changeState('advanced');
|
return doConfigure();
|
||||||
}
|
} else {
|
||||||
}).catch(function (err) {
|
changeState('advanced');
|
||||||
console.error(err);
|
}
|
||||||
window.alert("Error: [initialize] " + (err.message || JSON.stringify(err, null, 2)));
|
}).catch(function (err) {
|
||||||
|
console.error(err);
|
||||||
|
window.alert("Error: [initialize] " + (err.message || JSON.stringify(err, null, 2)));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
, advance: function () {
|
, advance: function () {
|
||||||
|
@ -473,54 +475,84 @@ new Vue({
|
||||||
, methods: appMethods
|
, methods: appMethods
|
||||||
});
|
});
|
||||||
|
|
||||||
function run(key) {
|
function requestAccountHelper() {
|
||||||
api._key = key;
|
function reset() {
|
||||||
// 😁 1. Get ACME directory
|
changeState('setup');
|
||||||
// 😁 2. Fetch ACME account
|
setState();
|
||||||
// 3. Test if account has access
|
}
|
||||||
// 4. Show command line auth instructions to auth
|
return new Promise(function (resolve) {
|
||||||
// 5. Sign requests / use JWT
|
appData.init.email = localStorage.getItem('email');
|
||||||
// 6. Enforce token required for config, status, etc
|
if (!appData.init.email) {
|
||||||
// 7. Move admin interface to standard ports (admin.foo-bar-123.telebit.xyz)
|
// don't resolve
|
||||||
api.config().then(function (config) {
|
reset();
|
||||||
telebitState.config = config;
|
|
||||||
if (config.greenlock) {
|
|
||||||
appData.init.acmeServer = config.greenlock.server;
|
|
||||||
}
|
|
||||||
if (config.relay) {
|
|
||||||
appData.init.relay = config.relay;
|
|
||||||
}
|
|
||||||
if (config.email) {
|
|
||||||
appData.init.email = config.email;
|
|
||||||
}
|
|
||||||
if (config.agreeTos) {
|
|
||||||
appData.init.letos = config.agreeTos;
|
|
||||||
appData.init.teletos = config.agreeTos;
|
|
||||||
}
|
|
||||||
if (config._otp) {
|
|
||||||
appData.init.otp = config._otp;
|
|
||||||
}
|
|
||||||
|
|
||||||
telebitState.pollUrl = config._pollUrl || localStorage.getItem('poll_url');
|
|
||||||
|
|
||||||
if ((!config.token && !config._otp) || !config.relay || !config.email || !config.agreeTos) {
|
|
||||||
changeState('setup');
|
|
||||||
setState();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!config.token && config._otp) {
|
return requestAccount(appData.init.email).then(function (key) {
|
||||||
changeState('otp');
|
if (!key) { throw new Error("[SANITY] Error: completed without key"); }
|
||||||
setState();
|
resolve(key);
|
||||||
// this will skip ahead as necessary
|
}).catch(function (err) {
|
||||||
return Telebit.authorize(telebitState, showOtp).then(function () {
|
appData.init.email = "";
|
||||||
return changeState('status');
|
localStorage.removeItem('email');
|
||||||
});
|
console.error(err);
|
||||||
}
|
window.alert("something went wrong");
|
||||||
|
// don't resolve
|
||||||
|
reset();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// TODO handle default state
|
function run() {
|
||||||
changeState('status');
|
return requestAccountHelper().then(function (key) {
|
||||||
}).catch(function (err) {
|
api._key = key;
|
||||||
appData.views.flash.error = err.message || JSON.stringify(err, null, 2);
|
// TODO create session instance of Telebit
|
||||||
|
Telebit._key = key;
|
||||||
|
// 😁 1. Get ACME directory
|
||||||
|
// 😁 2. Fetch ACME account
|
||||||
|
// 3. Test if account has access
|
||||||
|
// 4. Show command line auth instructions to auth
|
||||||
|
// 😁 5. Sign requests / use JWT
|
||||||
|
// 😁 6. Enforce token required for config, status, etc
|
||||||
|
// 7. Move admin interface to standard ports (admin.foo-bar-123.telebit.xyz)
|
||||||
|
api.config().then(function (config) {
|
||||||
|
telebitState.config = config;
|
||||||
|
if (config.greenlock) {
|
||||||
|
appData.init.acmeServer = config.greenlock.server;
|
||||||
|
}
|
||||||
|
if (config.relay) {
|
||||||
|
appData.init.relay = config.relay;
|
||||||
|
}
|
||||||
|
if (config.email) {
|
||||||
|
appData.init.email = config.email;
|
||||||
|
}
|
||||||
|
if (config.agreeTos) {
|
||||||
|
appData.init.letos = config.agreeTos;
|
||||||
|
appData.init.teletos = config.agreeTos;
|
||||||
|
}
|
||||||
|
if (config._otp) {
|
||||||
|
appData.init.otp = config._otp;
|
||||||
|
}
|
||||||
|
|
||||||
|
telebitState.pollUrl = config._pollUrl || localStorage.getItem('poll_url');
|
||||||
|
|
||||||
|
if ((!config.token && !config._otp) || !config.relay || !config.email || !config.agreeTos) {
|
||||||
|
changeState('setup');
|
||||||
|
setState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!config.token && config._otp) {
|
||||||
|
changeState('otp');
|
||||||
|
setState();
|
||||||
|
// this will skip ahead as necessary
|
||||||
|
return Telebit.authorize(telebitState, showOtp).then(function () {
|
||||||
|
return changeState('status');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO handle default state
|
||||||
|
changeState('status');
|
||||||
|
}).catch(function (err) {
|
||||||
|
appData.views.flash.error = err.message || JSON.stringify(err, null, 2);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -543,46 +575,34 @@ function getKey() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getEmail() {
|
function requestAccount(email) {
|
||||||
return Promise.resolve().then(function () {
|
|
||||||
var email = localStorage.getItem('email');
|
|
||||||
if (email) { return email; }
|
|
||||||
while (!email) {
|
|
||||||
email = window.prompt("Email address (device owner)?");
|
|
||||||
}
|
|
||||||
return email;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function requestAccount() {
|
|
||||||
return getKey().then(function (jwk) {
|
return getKey().then(function (jwk) {
|
||||||
return getEmail().then(function(email) {
|
// creates new or returns existing
|
||||||
// creates new or returns existing
|
var acme = ACME.create({});
|
||||||
var acme = ACME.create({});
|
var url = window.location.protocol + '//' + window.location.host + '/acme/directory';
|
||||||
var url = window.location.protocol + '//' + window.location.host + '/acme/directory';
|
return acme.init(url).then(function () {
|
||||||
return acme.init(url).then(function () {
|
return acme.accounts.create({
|
||||||
return acme.accounts.create({
|
agreeToTerms: function (tos) { return tos; }
|
||||||
agreeToTerms: function (tos) { return tos; }
|
, accountKeypair: { privateKeyJwk: jwk }
|
||||||
, accountKeypair: { privateKeyJwk: jwk }
|
, email: email
|
||||||
, email: email
|
}).then(function (account) {
|
||||||
}).then(function (account) {
|
console.log('account:');
|
||||||
console.log('account:');
|
console.log(account);
|
||||||
console.log(account);
|
if (account.id) {
|
||||||
if (account.id) {
|
localStorage.setItem('email', email);
|
||||||
localStorage.setItem('email', email);
|
}
|
||||||
}
|
return jwk;
|
||||||
return jwk;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
window.api = api;
|
window.api = api;
|
||||||
requestAccount().then(function (jwk) {
|
run();
|
||||||
run(jwk);
|
setTimeout(function () {
|
||||||
setTimeout(function () {
|
document.body.hidden = false;
|
||||||
document.body.hidden = false;
|
}, 50);
|
||||||
}, 50);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
// Debug
|
||||||
|
window.changeState = changeState;
|
||||||
}());
|
}());
|
||||||
|
|
|
@ -34,11 +34,12 @@ module.exports = function eggspress() {
|
||||||
}
|
}
|
||||||
|
|
||||||
var urlstr = (req.url.replace(/\/$/, '') + '/');
|
var urlstr = (req.url.replace(/\/$/, '') + '/');
|
||||||
if (!urlstr.match(todo[0])) {
|
var match = urlstr.match(todo[0]);
|
||||||
|
if (!match) {
|
||||||
//console.log("[eggspress] pattern doesn't match", todo[0], req.url);
|
//console.log("[eggspress] pattern doesn't match", todo[0], req.url);
|
||||||
next();
|
next();
|
||||||
return;
|
return;
|
||||||
} else if ('string' === typeof todo[0] && 0 !== urlstr.match(todo[0]).index) {
|
} else if ('string' === typeof todo[0] && 0 !== match.index) {
|
||||||
//console.log("[eggspress] string pattern is not the start", todo[0], req.url);
|
//console.log("[eggspress] string pattern is not the start", todo[0], req.url);
|
||||||
next();
|
next();
|
||||||
return;
|
return;
|
||||||
|
@ -58,6 +59,7 @@ module.exports = function eggspress() {
|
||||||
}
|
}
|
||||||
|
|
||||||
var fns = todo[1].slice(0);
|
var fns = todo[1].slice(0);
|
||||||
|
req.params = match.slice(1);
|
||||||
|
|
||||||
function nextTodo(err) {
|
function nextTodo(err) {
|
||||||
if (err) { fail(err); return; }
|
if (err) { fail(err); return; }
|
||||||
|
|
|
@ -93,26 +93,45 @@ module.exports.create = function (state) {
|
||||||
}
|
}
|
||||||
return reqOpts;
|
return reqOpts;
|
||||||
};
|
};
|
||||||
RC.createErrorHandler = function (replay, opts, cb) {
|
RC.createRelauncher = function (replay, opts, cb) {
|
||||||
return function (err) {
|
return function (err) {
|
||||||
// ENOENT - never started, cleanly exited last start, or creating socket at a different path
|
/*global Promise*/
|
||||||
// ECONNREFUSED - leftover socket just needs to be restarted
|
var p = new Promise(function (resolve, reject) {
|
||||||
if ('ENOENT' === err.code || 'ECONNREFUSED' === err.code) {
|
// ENOENT - never started, cleanly exited last start, or creating socket at a different path
|
||||||
if (opts._taketwo) {
|
// ECONNREFUSED - leftover socket just needs to be restarted
|
||||||
cb(err);
|
if ('ENOENT' !== err.code && 'ECONNREFUSED' !== err.code) {
|
||||||
|
reject(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// retried and failed again: quit
|
||||||
|
if (opts._taketwo) {
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
require('../../usr/share/install-launcher.js').install({ env: process.env }, function (err) {
|
require('../../usr/share/install-launcher.js').install({ env: process.env }, function (err) {
|
||||||
if (err) { cb(err); return; }
|
if (err) { reject(err); return; }
|
||||||
opts._taketwo = true;
|
opts._taketwo = true;
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
replay(opts, cb);
|
if (replay.length <= 1) {
|
||||||
|
replay(opts).then(resolve).catch(reject);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
replay(opts, function (err, res) {
|
||||||
|
if (err) { reject(err); }
|
||||||
|
else { resolve(res); }
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
}, 2500);
|
}, 2500);
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
});
|
||||||
|
if (cb) {
|
||||||
|
p.then(function () { cb(null); }).catch(function (err) { cb(err); });
|
||||||
}
|
}
|
||||||
|
return p;
|
||||||
cb(err);
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
RC.request = function request(opts, fn) {
|
RC.request = function request(opts, fn) {
|
||||||
|
@ -141,7 +160,8 @@ module.exports.create = function (state) {
|
||||||
makeResponder(service, resp, fn);
|
makeResponder(service, resp, fn);
|
||||||
});
|
});
|
||||||
|
|
||||||
req.on('error', RC.createErrorHandler(RC.request, opts, fn));
|
var errHandler = RC.createRelauncher(RC.request, opts, fn);
|
||||||
|
req.on('error', errHandler);
|
||||||
|
|
||||||
// Simple GET
|
// Simple GET
|
||||||
if ('POST' !== method || !opts.data) {
|
if ('POST' !== method || !opts.data) {
|
||||||
|
|
Loading…
Reference in New Issue