From 649a8532d9e5062cfc97ed3a6b37d9cb6ef54aac Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Thu, 13 Jun 2019 01:36:25 -0600 Subject: [PATCH] from tabs back to spaces --- README.md | 34 ++-- example.js | 18 +- index.js | 586 ++++++++++++++++++++++++++--------------------------- 3 files changed, 319 insertions(+), 319 deletions(-) diff --git a/README.md b/README.md index b4058e6..ab3b87a 100644 --- a/README.md +++ b/README.md @@ -48,23 +48,23 @@ var tester = require('acme-challenge-test'); var domain = 'example.com'; tester - .testRecord('http-01', domain, { - set: function(opts) { - console.log('set opts:', opts); - throw new Error('set not implemented'); - }, + .testRecord('http-01', domain, { + set: function(opts) { + console.log('set opts:', opts); + throw new Error('set not implemented'); + }, - remove: function(opts) { - console.log('remove opts:', opts); - throw new Error('remove not implemented'); - }, + remove: function(opts) { + console.log('remove opts:', opts); + throw new Error('remove not implemented'); + }, - get: function(opts) { - console.log('get opts:', opts); - throw new Error('get not implemented'); - } - }) - .then(function() { - console.info('PASS'); - }); + get: function(opts) { + console.log('get opts:', opts); + throw new Error('get not implemented'); + } + }) + .then(function() { + console.info('PASS'); + }); ``` diff --git a/example.js b/example.js index 7106618..85c27bd 100644 --- a/example.js +++ b/example.js @@ -15,12 +15,12 @@ var challenger = require('acme-dns-01-cli').create({}); var zone = 'example.com'; tester - .test(type, zone, challenger) - .then(function() { - console.info('ALL PASSED'); - }) - .catch(function(err) { - console.error('FAIL'); - console.error(err); - process.exit(20); - }); + .test(type, zone, challenger) + .then(function() { + console.info('ALL PASSED'); + }) + .catch(function(err) { + console.error('FAIL'); + console.error(err); + process.exit(20); + }); diff --git a/index.js b/index.js index e5c90e4..8d6ef24 100644 --- a/index.js +++ b/index.js @@ -3,336 +3,336 @@ var crypto = require('crypto'); module.exports.create = function() { - throw new Error( - 'acme-challenge-test is a test fixture for acme-challenge-* plugins, not a plugin itself' - ); + throw new Error( + 'acme-challenge-test is a test fixture for acme-challenge-* plugins, not a plugin itself' + ); }; // ignore all of this, it's just to normalize Promise vs node-style callback thunk vs synchronous function promiseCheckAndCatch(obj, name) { - var promisify = require('util').promisify; - // don't loose this-ness, just in case that's important - var fn = obj[name].bind(obj); - var promiser; + var promisify = require('util').promisify; + // don't loose this-ness, just in case that's important + var fn = obj[name].bind(obj); + var promiser; - // function signature must match, or an error will be thrown - if (1 === fn.length) { - // wrap so that synchronous errors are caught (alsa handles synchronous results) - promiser = function(opts) { - return Promise.resolve().then(function() { - return fn(opts); - }); - }; - } else if (2 === fn.length) { - // wrap as a promise - promiser = promisify(fn); - } else { - return Promise.reject( - new Error( - "'challenge." + - name + - "' should accept either one argument, the options," + - ' and return a Promise or accept two arguments, the options and a node-style callback thunk' - ) - ); - } + // function signature must match, or an error will be thrown + if (1 === fn.length) { + // wrap so that synchronous errors are caught (alsa handles synchronous results) + promiser = function(opts) { + return Promise.resolve().then(function() { + return fn(opts); + }); + }; + } else if (2 === fn.length) { + // wrap as a promise + promiser = promisify(fn); + } else { + return Promise.reject( + new Error( + "'challenge." + + name + + "' should accept either one argument, the options," + + ' and return a Promise or accept two arguments, the options and a node-style callback thunk' + ) + ); + } - function shouldntBeUndefined(result) { - if ('undefined' === typeof result) { - throw new Error( - "'challenge.'" + - name + - "' should never return `undefined`. Please explicitly return null" + - " (or fix the place where a value should have been returned but wasn't)." - ); - } - return result; - } + function shouldntBeUndefined(result) { + if ('undefined' === typeof result) { + throw new Error( + "'challenge.'" + + name + + "' should never return `undefined`. Please explicitly return null" + + " (or fix the place where a value should have been returned but wasn't)." + ); + } + return result; + } - return function(opts) { - return promiser(opts).then(shouldntBeUndefined); - }; + return function(opts) { + return promiser(opts).then(shouldntBeUndefined); + }; } function mapAsync(els, doer) { - els = els.slice(0); - var results = []; - function next() { - var el = els.shift(); - if (!el) { - return results; - } - return doer(el).then(function(result) { - results.push(result); - return next(); - }); - } - return next(); + els = els.slice(0); + var results = []; + function next() { + var el = els.shift(); + if (!el) { + return results; + } + return doer(el).then(function(result) { + results.push(result); + return next(); + }); + } + return next(); } function newZoneRegExp(zonename) { - // (^|\.)example\.com$ - // which matches: - // foo.example.com - // example.com - // but not: - // fooexample.com - return new RegExp('(^|\\.)' + zonename.replace(/\./g, '\\.') + '$'); + // (^|\.)example\.com$ + // which matches: + // foo.example.com + // example.com + // but not: + // fooexample.com + return new RegExp('(^|\\.)' + zonename.replace(/\./g, '\\.') + '$'); } function pluckZone(zones, dnsHost) { - return zones - .filter(function(zonename) { - // the only character that needs to be escaped for regex - // and is allowed in a domain name is '.' - return newZoneRegExp(zonename).test(dnsHost); - }) - .sort(function(a, b) { - // longest match first - return b.length - a.length; - })[0]; + return zones + .filter(function(zonename) { + // the only character that needs to be escaped for regex + // and is allowed in a domain name is '.' + return newZoneRegExp(zonename).test(dnsHost); + }) + .sort(function(a, b) { + // longest match first + return b.length - a.length; + })[0]; } // Here's the meat, where the tests are happening: function testEach(type, domains, challenger) { - var chr = wrapChallenger(type, challenger); - var all = domains.map(function(d) { - return { domain: d }; - }); - var rnd = crypto.randomBytes(2).toString('hex'); + var chr = wrapChallenger(type, challenger); + var all = domains.map(function(d) { + return { domain: d }; + }); + var rnd = crypto.randomBytes(2).toString('hex'); - return chr.zones({ dnsHosts: domains }).then(function(zones) { - return mapAsync(all, function(opts) { - console.info("TEST '%s'", opts.domain); - var zone = pluckZone(zones, opts.domain); - opts.challenge = fakeChallenge(type, zone, opts.domain, rnd); - var ch = opts.challenge; - if ('http-01' === ch.type && ch.wildname) { - throw new Error('http-01 cannot be used for wildcard domains'); - } + return chr.zones({ dnsHosts: domains }).then(function(zones) { + return mapAsync(all, function(opts) { + console.info("TEST '%s'", opts.domain); + var zone = pluckZone(zones, opts.domain); + opts.challenge = fakeChallenge(type, zone, opts.domain, rnd); + var ch = opts.challenge; + if ('http-01' === ch.type && ch.wildname) { + throw new Error('http-01 cannot be used for wildcard domains'); + } - // The first time we just check it against itself - // this will cause the prompt to appear - return chr.set(opts).then(function() { - // this will cause the final completion message to appear - // _test is used by the manual cli reference implementations - var query = { type: ch.type, /*debug*/ status: ch.status, _test: true }; - if ('http-01' === ch.type) { - query.identifier = ch.identifier; - query.token = ch.token; - // For testing only - query.url = ch.challengeUrl; - } else if ('dns-01' === ch.type) { - query.identifier = { type: 'dns', value: ch.dnsHost }; - // For testing only - query.altname = ch.altname; - // there should only be two possible TXT records per challenge domain: - // one for the bare domain, and the other if and only if there's a wildcard - query.wildcard = ch.wildcard; - query.dnsAuthorization = ch.dnsAuthorization; - } else { - query = JSON.parse(JSON.stringify(ch)); - query.comment = 'unknown challenge type, supplying everything'; - } - opts.query = query; - return opts; - }); - }) - .then(function(all) { - return mapAsync(all, function(opts) { - var ch = opts.challenge; - return chr.get({ challenge: opts.query }).then(function(secret) { - if ('string' === typeof secret) { - console.info( - 'secret was passed as a string, which works historically, but should be an object instead:' - ); - console.info('{ "keyAuthorization": "' + secret + '" }'); - console.info('or'); - // TODO this should be "keyAuthorizationDigest" - console.info('{ "dnsAuthorization": "' + secret + '" }'); - console.info( - 'This is to help keep acme / greenlock (and associated plugins) future-proof for new challenge types' - ); - } - // historically 'secret' has been a string, but I'd like it to transition to be an object. - // to make it backwards compatible in v2.7 to change it, - // so I'm not sure that we really need to. - if ('http-01' === ch.type) { - secret = secret.keyAuthorization || secret; - if (ch.keyAuthorization !== secret) { - throw new Error( - "http-01 challenge.get() returned '" + - secret + - "', which does not match the keyAuthorization" + - " saved with challenge.set(), which was '" + - ch.keyAuthorization + - "'" - ); - } - } else if ('dns-01' === ch.type) { - secret = secret.dnsAuthorization || secret; - if (ch.dnsAuthorization !== secret) { - throw new Error( - "dns-01 challenge.get() returned '" + - secret + - "', which does not match the dnsAuthorization" + - " (keyAuthDigest) saved with challenge.set(), which was '" + - ch.dnsAuthorization + - "'" - ); - } - } else { - if ('tls-alpn-01' === ch.type) { - console.warn( - "'tls-alpn-01' support is in development" + - " (or developed and we haven't update this yet). Please contact us." - ); - } else { - console.warn( - "We don't know how to test '" + - ch.type + - "'... are you sure that's a thing?" - ); - } - secret = secret.keyAuthorization || secret; - if (ch.keyAuthorization !== secret) { - console.warn( - "The returned value doesn't match keyAuthorization", - ch.keyAuthorization, - secret - ); - } - } - }); - }); - }) - .then(function() { - return mapAsync(all, function(opts) { - return chr.remove(opts).then(function() { - return chr.get(opts).then(function(result) { - if (result) { - throw new Error( - 'challenge.remove() should have made it not possible for challenge.get() to return a value' - ); - } - if (null !== result) { - throw new Error( - 'challenge.get() should return null when the value is not set' - ); - } - console.info("PASS '%s'", opts.domain); - }); - }); - }); - }) - .then(function() { - console.info('All soft tests: PASS'); - console.warn( - 'Hard tests (actually checking http URLs and dns records) is implemented in acme-v2.' - ); - console.warn( - "We'll copy them over here as well, but that's a TODO for next week." - ); - }); - }); + // The first time we just check it against itself + // this will cause the prompt to appear + return chr.set(opts).then(function() { + // this will cause the final completion message to appear + // _test is used by the manual cli reference implementations + var query = { type: ch.type, /*debug*/ status: ch.status, _test: true }; + if ('http-01' === ch.type) { + query.identifier = ch.identifier; + query.token = ch.token; + // For testing only + query.url = ch.challengeUrl; + } else if ('dns-01' === ch.type) { + query.identifier = { type: 'dns', value: ch.dnsHost }; + // For testing only + query.altname = ch.altname; + // there should only be two possible TXT records per challenge domain: + // one for the bare domain, and the other if and only if there's a wildcard + query.wildcard = ch.wildcard; + query.dnsAuthorization = ch.dnsAuthorization; + } else { + query = JSON.parse(JSON.stringify(ch)); + query.comment = 'unknown challenge type, supplying everything'; + } + opts.query = query; + return opts; + }); + }) + .then(function(all) { + return mapAsync(all, function(opts) { + var ch = opts.challenge; + return chr.get({ challenge: opts.query }).then(function(secret) { + if ('string' === typeof secret) { + console.info( + 'secret was passed as a string, which works historically, but should be an object instead:' + ); + console.info('{ "keyAuthorization": "' + secret + '" }'); + console.info('or'); + // TODO this should be "keyAuthorizationDigest" + console.info('{ "dnsAuthorization": "' + secret + '" }'); + console.info( + 'This is to help keep acme / greenlock (and associated plugins) future-proof for new challenge types' + ); + } + // historically 'secret' has been a string, but I'd like it to transition to be an object. + // to make it backwards compatible in v2.7 to change it, + // so I'm not sure that we really need to. + if ('http-01' === ch.type) { + secret = secret.keyAuthorization || secret; + if (ch.keyAuthorization !== secret) { + throw new Error( + "http-01 challenge.get() returned '" + + secret + + "', which does not match the keyAuthorization" + + " saved with challenge.set(), which was '" + + ch.keyAuthorization + + "'" + ); + } + } else if ('dns-01' === ch.type) { + secret = secret.dnsAuthorization || secret; + if (ch.dnsAuthorization !== secret) { + throw new Error( + "dns-01 challenge.get() returned '" + + secret + + "', which does not match the dnsAuthorization" + + " (keyAuthDigest) saved with challenge.set(), which was '" + + ch.dnsAuthorization + + "'" + ); + } + } else { + if ('tls-alpn-01' === ch.type) { + console.warn( + "'tls-alpn-01' support is in development" + + " (or developed and we haven't update this yet). Please contact us." + ); + } else { + console.warn( + "We don't know how to test '" + + ch.type + + "'... are you sure that's a thing?" + ); + } + secret = secret.keyAuthorization || secret; + if (ch.keyAuthorization !== secret) { + console.warn( + "The returned value doesn't match keyAuthorization", + ch.keyAuthorization, + secret + ); + } + } + }); + }); + }) + .then(function() { + return mapAsync(all, function(opts) { + return chr.remove(opts).then(function() { + return chr.get(opts).then(function(result) { + if (result) { + throw new Error( + 'challenge.remove() should have made it not possible for challenge.get() to return a value' + ); + } + if (null !== result) { + throw new Error( + 'challenge.get() should return null when the value is not set' + ); + } + console.info("PASS '%s'", opts.domain); + }); + }); + }); + }) + .then(function() { + console.info('All soft tests: PASS'); + console.warn( + 'Hard tests (actually checking http URLs and dns records) is implemented in acme-v2.' + ); + console.warn( + "We'll copy them over here as well, but that's a TODO for next week." + ); + }); + }); } function testZone(type, zone, challenger) { - var domains = [zone, 'foo.' + zone]; - if ('dns-01' === type) { - domains.push('*.foo.' + zone); - } - return testEach(type, domains, challenger); + var domains = [zone, 'foo.' + zone]; + if ('dns-01' === type) { + domains.push('*.foo.' + zone); + } + return testEach(type, domains, challenger); } function wrapChallenger(type, challenger) { - var zones; - if ('dns-01' === type) { - if ('function' !== typeof challenger.zones) { - console.error( - 'You must implement `zones` to return an array of strings.' + - " If you're testing a special type of service that doesn't support" + - ' domain zone listing (as opposed to domain record listing),' + - ' such as DuckDNS, return an empty array.' - ); - process.exit(28); - return; - } - zones = promiseCheckAndCatch(challenger, 'zones'); - } else { - zones = function() { - return Promise.resolve([]); - }; - } + var zones; + if ('dns-01' === type) { + if ('function' !== typeof challenger.zones) { + console.error( + 'You must implement `zones` to return an array of strings.' + + " If you're testing a special type of service that doesn't support" + + ' domain zone listing (as opposed to domain record listing),' + + ' such as DuckDNS, return an empty array.' + ); + process.exit(28); + return; + } + zones = promiseCheckAndCatch(challenger, 'zones'); + } else { + zones = function() { + return Promise.resolve([]); + }; + } - if ('function' !== typeof challenger.get) { - console.error( - "'challenge.get' should be implemented for the sake of testing." + - ' It should be implemented as the internal method for fetching the challenge' + - ' (i.e. reading from a database, file system or API, not return internal),' + - ' not the external check (the http call, dns query, etc),' + - ' which will already be done as part of this test.' - ); - process.exit(29); - return; - } + if ('function' !== typeof challenger.get) { + console.error( + "'challenge.get' should be implemented for the sake of testing." + + ' It should be implemented as the internal method for fetching the challenge' + + ' (i.e. reading from a database, file system or API, not return internal),' + + ' not the external check (the http call, dns query, etc),' + + ' which will already be done as part of this test.' + ); + process.exit(29); + return; + } - return { - zones: zones, - set: promiseCheckAndCatch(challenger, 'set'), - get: promiseCheckAndCatch(challenger, 'get'), - remove: promiseCheckAndCatch(challenger, 'remove') - }; + return { + zones: zones, + set: promiseCheckAndCatch(challenger, 'set'), + get: promiseCheckAndCatch(challenger, 'get'), + remove: promiseCheckAndCatch(challenger, 'remove') + }; } function fakeChallenge(type, zone, altname, rnd) { - var expires = new Date(Date.now() + 10 * 60 * 1000).toISOString(); - var token = crypto.randomBytes(8).toString('hex'); - var thumb = crypto.randomBytes(16).toString('hex'); - var keyAuth = token + '.' + crypto.randomBytes(16).toString('hex'); - var dnsAuth = crypto - .createHash('sha256') - .update(keyAuth) - .digest('base64') - .replace(/\+/g, '-') - .replace(/\//g, '_') - .replace(/=/g, ''); + var expires = new Date(Date.now() + 10 * 60 * 1000).toISOString(); + var token = crypto.randomBytes(8).toString('hex'); + var thumb = crypto.randomBytes(16).toString('hex'); + var keyAuth = token + '.' + crypto.randomBytes(16).toString('hex'); + var dnsAuth = crypto + .createHash('sha256') + .update(keyAuth) + .digest('base64') + .replace(/\+/g, '-') + .replace(/\//g, '_') + .replace(/=/g, ''); - var challenge = { - type: type, - identifier: { type: 'dns', value: null }, // completed below - wildcard: false, // completed below - status: 'pending', - expires: expires, - token: token, - thumbprint: thumb, - keyAuthorization: keyAuth, - url: null, // completed below - dnsZone: zone, - dnsHost: '_' + rnd.slice(0, 2) + '-acme-challenge-' + rnd.slice(2) + '.', // completed below - dnsAuthorization: dnsAuth, - altname: altname, - _test: true // used by CLI referenced implementations - }; - if (zone) { - challenge.dnsZone = zone; - challenge.dnsPrefix = challenge.dnsHost - .replace(newZoneRegExp(zone), '') - .replace(/\.$/, ''); - } - if ('*.' === altname.slice(0, 2)) { - challenge.wildcard = true; - altname = altname.slice(2); - } - challenge.identifier.value = altname; - challenge.url = - 'http://' + altname + '/.well-known/acme-challenge/' + challenge.token; - challenge.dnsHost += altname; + var challenge = { + type: type, + identifier: { type: 'dns', value: null }, // completed below + wildcard: false, // completed below + status: 'pending', + expires: expires, + token: token, + thumbprint: thumb, + keyAuthorization: keyAuth, + url: null, // completed below + dnsZone: zone, + dnsHost: '_' + rnd.slice(0, 2) + '-acme-challenge-' + rnd.slice(2) + '.', // completed below + dnsAuthorization: dnsAuth, + altname: altname, + _test: true // used by CLI referenced implementations + }; + if (zone) { + challenge.dnsZone = zone; + challenge.dnsPrefix = challenge.dnsHost + .replace(newZoneRegExp(zone), '') + .replace(/\.$/, ''); + } + if ('*.' === altname.slice(0, 2)) { + challenge.wildcard = true; + altname = altname.slice(2); + } + challenge.identifier.value = altname; + challenge.url = + 'http://' + altname + '/.well-known/acme-challenge/' + challenge.token; + challenge.dnsHost += altname; - return challenge; + return challenge; } function testRecord(type, altname, challenger) { - return testEach(type, [altname], challenger); + return testEach(type, [altname], challenger); } module.exports.testRecord = testRecord;