support init(deps)

This commit is contained in:
AJ ONeal 2019-06-15 14:31:03 -06:00
parent 90477942d1
commit 0599acab6d
2 changed files with 226 additions and 192 deletions

416
node.js
View File

@ -464,24 +464,48 @@ ACME._chooseChallenge = function(options, results) {
return challenge; return challenge;
}; };
ACME._depInit = function(me, options) {
if ('function' !== typeof options.init) {
options.init = function() {
return Promise.resolve(null);
};
}
// back/forwards-compat
return ACME._wrapCb(
me,
options,
'init',
{ type: '*', request: me._request },
'null'
);
};
ACME._getZones = function(me, options, dnsHosts) { ACME._getZones = function(me, options, dnsHosts) {
if ('function' !== typeof options.getZones) { if ('function' !== typeof options.getZones) {
options.getZones = function() { options.getZones = function() {
return Promise.resolve([]); return Promise.resolve([]);
}; };
} }
var challenge = { type: 'dns-01', dnsHosts: dnsHosts, request: me._request };
// back/forwards-compat
challenge.challenge = challenge;
return ACME._wrapCb(
me,
options,
'getZones',
challenge,
'an array of zone names'
);
};
ACME._wrapCb = function(me, options, _name, stuff, _desc) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
var challenge = { type: 'dns-01', dnsHosts: dnsHosts };
// back/forwards-compat
challenge.challenge = challenge;
try { try {
if (options.getZones.length <= 1) { if (options[_name].length <= 1) {
options return Promise.resolve(options[_name](stuff))
.getZones(challenge)
.then(resolve) .then(resolve)
.catch(reject); .catch(reject);
} else if (2 === options.getZones.length) { } else if (2 === options[_name].length) {
options.getZones(challenge, function(err, zonenames) { options[_name](stuff, function(err, zonenames) {
if (err) { if (err) {
reject(err); reject(err);
} else { } else {
@ -490,7 +514,7 @@ ACME._getZones = function(me, options, dnsHosts) {
}); });
} else { } else {
throw new Error( throw new Error(
'options.getZones should accept opts and Promise an array of zone names' 'options.' + _name + ' should accept opts and Promise ' + _desc
); );
} }
} catch (e) { } catch (e) {
@ -498,6 +522,7 @@ ACME._getZones = function(me, options, dnsHosts) {
} }
}); });
}; };
function newZoneRegExp(zonename) { function newZoneRegExp(zonename) {
// (^|\.)example\.com$ // (^|\.)example\.com$
// which matches: // which matches:
@ -577,8 +602,9 @@ ACME._challengeToAuth = function(me, options, request, challenge, dryrun) {
.replace(/\.$/, ''); .replace(/\.$/, '');
} }
// for backwards compat // for backwards/forwards compat
auth.challenge = auth; auth.challenge = auth;
auth.request = me._request;
return auth; return auth;
}; };
@ -1071,196 +1097,204 @@ ACME._getCertificate = function(me, options) {
.toString('hex') + d .toString('hex') + d
); );
}); });
return ACME._getZones(me, options, dnsHosts).then(function(zonenames) { return ACME._depInit(me, options, dnsHosts).then(function(zonenames) {
options.zonenames = zonenames; return ACME._getZones(me, options, dnsHosts).then(function(zonenames) {
// Do a little dry-run / self-test options.zonenames = zonenames;
return ACME._testChallenges(me, options).then(function() { // Do a little dry-run / self-test
if (me.debug) { return ACME._testChallenges(me, options).then(function() {
console.debug('[acme-v2] certificates.create');
}
return ACME._getNonce(me).then(function() {
var body = {
// raw wildcard syntax MUST be used here
identifiers: options.domains
.sort(function(a, b) {
// the first in the list will be the subject of the certificate, I believe (and hope)
if (!options.subject) {
return 0;
}
if (options.subject === a) {
return -1;
}
if (options.subject === b) {
return 1;
}
return 0;
})
.map(function(hostname) {
return { type: 'dns', value: hostname };
})
//, "notBefore": "2016-01-01T00:00:00Z"
//, "notAfter": "2016-01-08T00:00:00Z"
};
var payload = JSON.stringify(body);
// determine the signing algorithm to use in protected header // TODO isn't that handled by the signer?
me._kty =
(options.accountKeypair.privateKeyJwk &&
options.accountKeypair.privateKeyJwk.kty) ||
'RSA';
me._alg = 'EC' === me._kty ? 'ES256' : 'RS256'; // TODO vary with bitwidth of key (if not handled)
var jws = me.RSA.signJws(
options.accountKeypair,
undefined,
{
nonce: me._nonce,
alg: me._alg,
url: me._directoryUrls.newOrder,
kid: me._kid
},
Buffer.from(payload, 'utf8')
);
if (me.debug) { if (me.debug) {
console.debug('\n[DEBUG] newOrder\n'); console.debug('[acme-v2] certificates.create');
} }
me._nonce = null; return ACME._getNonce(me).then(function() {
return me var body = {
._request({ // raw wildcard syntax MUST be used here
method: 'POST', identifiers: options.domains
url: me._directoryUrls.newOrder, .sort(function(a, b) {
headers: { 'Content-Type': 'application/jose+json' }, // the first in the list will be the subject of the certificate, I believe (and hope)
json: jws if (!options.subject) {
}) return 0;
.then(function(resp) {
me._nonce = resp.toJSON().headers['replay-nonce'];
var location = resp.toJSON().headers.location;
var setAuths;
var auths = [];
if (me.debug) {
console.debug(location);
} // the account id url
if (me.debug) {
console.debug(resp.toJSON());
}
me._authorizations = resp.body.authorizations;
me._order = location;
me._finalize = resp.body.finalize;
//if (me.debug) console.debug('[DEBUG] finalize:', me._finalize); return;
if (!me._authorizations) {
return Promise.reject(
new Error(
"[acme-v2.js] authorizations were not fetched for '" +
options.domains.join() +
"':\n" +
JSON.stringify(resp.body)
)
);
}
if (me.debug) {
console.debug('[acme-v2] POST newOrder has authorizations');
}
setAuths = me._authorizations.slice(0);
function setNext() {
var authUrl = setAuths.shift();
if (!authUrl) {
return;
}
return ACME._getChallenges(me, options, authUrl).then(function(
results
) {
// var domain = options.domains[i]; // results.identifier.value
// If it's already valid, we're golden it regardless
if (
results.challenges.some(function(ch) {
return 'valid' === ch.status;
})
) {
return setNext();
} }
if (options.subject === a) {
var challenge = ACME._chooseChallenge(options, results); return -1;
if (!challenge) {
// For example, wildcards require dns-01 and, if we don't have that, we have to bail
return Promise.reject(
new Error(
"Server didn't offer any challenge we can handle for '" +
options.domains.join() +
"'."
)
);
} }
if (options.subject === b) {
var auth = ACME._challengeToAuth( return 1;
me,
options,
results,
challenge
);
auths.push(auth);
return ACME._setChallenge(me, options, auth).then(setNext);
});
}
function challengeNext() {
var auth = auths.shift();
if (!auth) {
return;
}
return ACME._postChallenge(me, options, auth).then(challengeNext);
}
// First we set every challenge
// Then we ask for each challenge to be checked
// Doing otherwise would potentially cause us to poison our own DNS cache with misses
return setNext()
.then(challengeNext)
.then(function() {
if (me.debug) {
console.debug('[getCertificate] next.then');
} }
var validatedDomains = body.identifiers.map(function(ident) { return 0;
return ident.value;
});
return ACME._finalizeOrder(me, options, validatedDomains);
}) })
.then(function(order) { .map(function(hostname) {
if (me.debug) { return { type: 'dns', value: hostname };
console.debug('acme-v2: order was finalized'); })
//, "notBefore": "2016-01-01T00:00:00Z"
//, "notAfter": "2016-01-08T00:00:00Z"
};
var payload = JSON.stringify(body);
// determine the signing algorithm to use in protected header // TODO isn't that handled by the signer?
me._kty =
(options.accountKeypair.privateKeyJwk &&
options.accountKeypair.privateKeyJwk.kty) ||
'RSA';
me._alg = 'EC' === me._kty ? 'ES256' : 'RS256'; // TODO vary with bitwidth of key (if not handled)
var jws = me.RSA.signJws(
options.accountKeypair,
undefined,
{
nonce: me._nonce,
alg: me._alg,
url: me._directoryUrls.newOrder,
kid: me._kid
},
Buffer.from(payload, 'utf8')
);
if (me.debug) {
console.debug('\n[DEBUG] newOrder\n');
}
me._nonce = null;
return me
._request({
method: 'POST',
url: me._directoryUrls.newOrder,
headers: { 'Content-Type': 'application/jose+json' },
json: jws
})
.then(function(resp) {
me._nonce = resp.toJSON().headers['replay-nonce'];
var location = resp.toJSON().headers.location;
var setAuths;
var auths = [];
if (me.debug) {
console.debug(location);
} // the account id url
if (me.debug) {
console.debug(resp.toJSON());
}
me._authorizations = resp.body.authorizations;
me._order = location;
me._finalize = resp.body.finalize;
//if (me.debug) console.debug('[DEBUG] finalize:', me._finalize); return;
if (!me._authorizations) {
return Promise.reject(
new Error(
"[acme-v2.js] authorizations were not fetched for '" +
options.domains.join() +
"':\n" +
JSON.stringify(resp.body)
)
);
}
if (me.debug) {
console.debug('[acme-v2] POST newOrder has authorizations');
}
setAuths = me._authorizations.slice(0);
function setNext() {
var authUrl = setAuths.shift();
if (!authUrl) {
return;
} }
return me
._request({ method: 'GET', url: me._certificate, json: true }) return ACME._getChallenges(me, options, authUrl).then(function(
.then(function(resp) { results
if (me.debug) { ) {
console.debug( // var domain = options.domains[i]; // results.identifier.value
'acme-v2: csr submitted and cert received:'
); // If it's already valid, we're golden it regardless
} if (
// https://github.com/certbot/certbot/issues/5721 results.challenges.some(function(ch) {
var certsarr = ACME.splitPemChain( return 'valid' === ch.status;
ACME.formatPemChain(resp.body || '') })
) {
return setNext();
}
var challenge = ACME._chooseChallenge(options, results);
if (!challenge) {
// For example, wildcards require dns-01 and, if we don't have that, we have to bail
return Promise.reject(
new Error(
"Server didn't offer any challenge we can handle for '" +
options.domains.join() +
"'."
)
); );
// cert, chain, fullchain, privkey, /*TODO, subject, altnames, issuedAt, expiresAt */ }
var certs = {
expires: order.expires, var auth = ACME._challengeToAuth(
identifiers: order.identifiers, me,
//, authorizations: order.authorizations options,
cert: certsarr.shift(), results,
//, privkey: privkeyPem challenge
chain: certsarr.join('\n') );
}; auths.push(auth);
if (me.debug) { return ACME._setChallenge(me, options, auth).then(setNext);
console.debug(certs); });
} }
return certs;
function challengeNext() {
var auth = auths.shift();
if (!auth) {
return;
}
return ACME._postChallenge(me, options, auth).then(
challengeNext
);
}
// First we set every challenge
// Then we ask for each challenge to be checked
// Doing otherwise would potentially cause us to poison our own DNS cache with misses
return setNext()
.then(challengeNext)
.then(function() {
if (me.debug) {
console.debug('[getCertificate] next.then');
}
var validatedDomains = body.identifiers.map(function(ident) {
return ident.value;
}); });
});
}); return ACME._finalizeOrder(me, options, validatedDomains);
})
.then(function(order) {
if (me.debug) {
console.debug('acme-v2: order was finalized');
}
return me
._request({
method: 'GET',
url: me._certificate,
json: true
})
.then(function(resp) {
if (me.debug) {
console.debug(
'acme-v2: csr submitted and cert received:'
);
}
// https://github.com/certbot/certbot/issues/5721
var certsarr = ACME.splitPemChain(
ACME.formatPemChain(resp.body || '')
);
// cert, chain, fullchain, privkey, /*TODO, subject, altnames, issuedAt, expiresAt */
var certs = {
expires: order.expires,
identifiers: order.identifiers,
//, authorizations: order.authorizations
cert: certsarr.shift(),
//, privkey: privkeyPem
chain: certsarr.join('\n')
};
if (me.debug) {
console.debug(certs);
}
return certs;
});
});
});
});
}); });
}); });
}); });

View File

@ -1,6 +1,6 @@
{ {
"name": "acme-v2", "name": "acme-v2",
"version": "1.8.1", "version": "1.8.2",
"description": "A lightweight library for getting Free SSL certifications through Let's Encrypt, using the ACME protocol.", "description": "A lightweight library for getting Free SSL certifications through Let's Encrypt, using the ACME protocol.",
"homepage": "https://git.coolaj86.com/coolaj86/acme-v2.js", "homepage": "https://git.coolaj86.com/coolaj86/acme-v2.js",
"main": "node.js", "main": "node.js",