'use strict'; /*global Promise*/ var Challenge = module.exports; Challenge.create = function (config) { // If your implementation needs config options, set them. Otherwise, don't bother (duh). var http01 = require('le-challenge-http').create(config); var dns01 = require('le-challenge-dns').create(config); var challenger = {}; // Note: normally you'd implement these right here, but for the sake of // documentation I've abstracted them out "Table of Contents"-style. // call out to set the challenge, wherever challenger.set = function (opts, cb) { // Note: this can be defined as a thunk (like this) or a Promise var ch = opts.challenge; if ('http-01' === ch.type) { return http01.set(opts, cb); } else if ('dns-01' === ch.type) { return dns01.set(opts, cb); } else { return Challenge._setAny(opts, cb); } }; // call out to remove the challenge, wherever challenger.remove = function (opts) { // Note: this can be defined synchronously (like this) or as a Promise, or a thunk var ch = opts.challenge; if ('http-01' === ch.type) { return http01.remove(opts); } else if ('dns-01' === ch.type) { return dns01.remove(opts); } else { return Challenge._removeAny(opts); } }; // only really useful for http // (and tls-alpn-01, which isn't implemented yet) challenger.get = function (opts) { // Note: this can be defined as a Promise (like this) or synchronously, or a thunk var ch = opts.challenge; if ('http-01' === ch.type) { return http01.get(opts); } else if ('dns-01' === ch.type) { return dns01.get(opts); } else { return Challenge._get(opts); } }; // Whatever you set to 'options' will be merged into 'opts' just before each call // (for convenience, so you don't have to merge it yourself). challenger.options = { debug: config.debug }; return challenger; }; Challenge._setAny = function (args, cb) { var ch = args.challenge; console.info("[ACME " + ch.type + " '" + ch.altname + "' CHALLENGE]"); console.info("Your mission (since you chose to accept it):"); console.info("You must, by whatever means necessary, use the following information" + " to make a device or service ready to respond to a '" + ch.type + "' request."); console.info(""); console.info(JSON.stringify(ch, null, 2).replace(/^/gm, '\t')); console.info(""); console.info("Press the any key once the response is ready to continue with the '" + ch.type + "' challenge process"); console.info("[Press the ANY key to continue...]"); process.stdin.resume(); process.stdin.once('data', function () { process.stdin.pause(); cb(null, null); }); }; Challenge._removeAny = function (args) { var ch = args.challenge; console.info(""); console.info("[ACME " + ch.type + " '" + ch.altname + "' COMPLETE]: " + ch.status); console.info("You may now undo whatever you did to create and ready the response."); console.info(""); return null; }; // This can be used for http-01 and tls-alpn-01 (when it's available), but not dns-01. // And not all http-01 or tls-alpn-01 strategies will need to implement this. Challenge._get = function (args) { var ch = args.challenge; if (!Challenge._getCache[ch.altname + ':' + ch.token]) { Challenge._getCache[ch.altname + ':' + ch.token] = true; console.info(""); console.info("[ACME " + ch.type + " '" + ch.altname + "' REQUEST]: " + ch.status); console.info("The '" + ch.type + "' challenge request has arrived!"); console.info("It's now time to painstakingly type out the expected response object with your bear hands."); console.log("Yes. Your bear hands."); console.log('ex: { "keyAuthorization": "xxxxxxxx.yyyyyyyy" }'); process.stdout.write("> "); } return new Promise(function (resolve, reject) { process.stdin.resume(); process.stdin.once('error', reject); process.stdin.once('data', function (chunk) { process.stdin.pause(); var result = chunk.toString(); try { result = JSON.parse(result); } catch(e) { args.challenge.keyAuthorization = result; result = args.challenge; } resolve(result); }); }); }; // Because the ACME server will hammer us with requests, and that's confusing during a manual test: Challenge._getCache = {};