From 51ef9be5173f05039ffb5e8cea6f3d90dfc229d1 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Fri, 5 Apr 2019 02:29:21 -0600 Subject: [PATCH] WIP more v3 compatibility work --- index.js | 44 ++++++++++++++++++++++++++++++++++---------- lib/core.js | 42 ++++++++++++++++++++++++++++++++++-------- package.json | 2 +- 3 files changed, 69 insertions(+), 19 deletions(-) diff --git a/index.js b/index.js index 757fffe..b8c117f 100644 --- a/index.js +++ b/index.js @@ -21,6 +21,25 @@ function promisifyAllSelf(obj) { obj.__promisified = true; return obj; } +function promisifyAllStore(obj) { + Object.keys(obj).forEach(function (key) { + if ('function' !== typeof obj[key] || /Async$/.test(key)) { return; } + + var p; + if (1 === obj[key].length) { + // wrap just in case it's synchronous (or improperly throws) + p = function (opts) { + return PromiseA.resolve(obj[key](opts)); + }; + } else { + p = util.promisify(obj[key]); + } + // internal backwards compat + obj[key + 'Async'] = util.promisify(obj[key]); + }); + obj.__promisified = true; + return obj; +} var Greenlock = module.exports; Greenlock.Greenlock = Greenlock; @@ -88,12 +107,17 @@ Greenlock._undefine = function (gl) { return gl; }; Greenlock.create = function (gl) { - gl.store = gl.store || require('le-store-certbot').create({ - debug: gl.debug - , configDir: gl.configDir - , logsDir: gl.logsDir - , webrootPath: gl.webrootPath - }); + if (!gl.store) { + console.warn("Deprecation Notice: You're haven't chosen a storage strategy." + + " The old default is 'le-store-certbot', but the new default will be 'le-store-fs'." + + " Please explicitly set `{ store: require('le-store-fs') }`."); + gl.store = require('le-store-certbot').create({ + debug: gl.debug + , configDir: gl.configDir + , logsDir: gl.logsDir + , webrootPath: gl.webrootPath + }); + } gl.core = require('./lib/core'); var log = gl.log || _log; @@ -264,7 +288,7 @@ Greenlock.create = function (gl) { gl.acme = gl.acme.create(gl); } gl.acme = promisifyAllSelf(gl.acme); - gl._acmeOpts = gl.acme.options || gl.acme.getOptions(); + gl._acmeOpts = gl.acme.getOptions && gl.acme.getOptions() || gl.acme.options || {}; Object.keys(gl._acmeOpts).forEach(function (key) { if (!(key in gl)) { gl[key] = gl._acmeOpts[key]; @@ -274,8 +298,8 @@ Greenlock.create = function (gl) { try { if (gl.store.create) { gl.store = gl.store.create(gl); } gl.store = promisifyAllSelf(gl.store); - gl.store.accounts = promisifyAllSelf(gl.store.accounts); - gl.store.certificates = promisifyAllSelf(gl.store.certificates); + gl.store.accounts = promisifyAllStore(gl.store.accounts); + gl.store.certificates = promisifyAllStore(gl.store.certificates); gl._storeOpts = gl.store.options || gl.store.getOptions(); } catch(e) { console.error(e); @@ -491,7 +515,7 @@ Greenlock.create = function (gl) { try { if (1 === gl.approveDomains.length) { - gl.approveDomains(opts).then(cb2).catch(eb2); + PromiseA.resolve(gl.approveDomains(opts)).then(cb2).catch(eb2); } else if (2 === gl.approveDomains.length) { gl.approveDomains(opts, mb2); } else { diff --git a/lib/core.js b/lib/core.js index 00f4f55..69f073d 100644 --- a/lib/core.js +++ b/lib/core.js @@ -87,12 +87,12 @@ module.exports.create = function (gl) { // TODO import jwk or pem and return it here console.warn("TODO: implement accounts.checkKeypairAsync skipping"); } - var newKeypair = true; var accountKeypair; + var newAccountKeypair = true; var promise = gl.store.accounts.checkKeypairAsync(args).then(function (keypair) { if (keypair) { // TODO keypairs - newKeypair = false; + newAccountKeypair = false; accountKeypair = RSA.import(keypair); return; } @@ -144,12 +144,18 @@ module.exports.create = function (gl) { var reg = { keypair: keypair , receipt: receipt + , kid: receipt && receipt.key && (receipt.key.kid || receipt.kid) , email: args.email , newRegUrl: args._acmeUrls.newReg , newAuthzUrl: args._acmeUrls.newAuthz }; - return gl.store.accounts.setKeypairAsync(args, keypair).then(function () { + var accountKeypairPromise; + args.keypair = keypair; + if (newAccountKeypair) { + accountKeypairPromise = gl.store.accounts.setKeypairAsync(args, keypair); + } + return PromiseA.resolve(accountKeypairPromise).then(function () { // TODO move templating of arguments to right here? if (!gl.store.accounts.setAsync) { return PromiseA.resolve({ keypair: keypair }); } return gl.store.accounts.setAsync(args, reg).then(function (account) { @@ -170,7 +176,11 @@ module.exports.create = function (gl) { // Accounts // (only used for keypair) , getAsync: function (args) { - return core.accounts.checkAsync(args).then(function (account) { + var accountPromise = null; + if (gl.store.accounts.checkAsync) { + accountPromise = core.accounts.checkAsync(args); + } + return PromiseA.resolve(accountPromise).then(function (account) { if (!account) { return core.accounts.registerAsync(args); } if (account.keypair) { return account; } @@ -188,7 +198,8 @@ module.exports.create = function (gl) { // Accounts , checkAsync: function (args) { var requiredArgs = ['accountId', 'email', 'domains', 'domain']; - if (!requiredArgs.some(function (key) { return -1 !== Object.keys(args).indexOf(key); })) { + if (!(args.account && (args.account.id || args.account.kid)) + && !requiredArgs.some(function (key) { return -1 !== Object.keys(args).indexOf(key); })) { return PromiseA.reject(new Error( "In order to register or retrieve an account one of '" + requiredArgs.join("', '") + "' must be present" )); @@ -201,7 +212,7 @@ module.exports.create = function (gl) { // we can re-register the same account until we're blue in the face and it's all the same // of course, we can also skip the lookup if we do store the account, but whatever - if (!gl.store.accounts.checkAsync) { return null; } + if (!gl.store.accounts.checkAsync) { return PromiseA.resolve(null); } return gl.store.accounts.checkAsync(args).then(function (account) { if (!account) { @@ -295,12 +306,14 @@ module.exports.create = function (gl) { console.warn("TODO: implement certificates.checkKeypairAsync skipping"); } var domainKeypair; + var newDomainKeypair = true; // This has been done in the getAsync already, so we skip it here // if approveDomains doesn't set subject, we set it here //args.subject = args.subject || args.domains[0]; var promise = gl.store.certificates.checkKeypairAsync(args).then(function (keypair) { if (keypair) { domainKeypair = RSA.import(keypair); + newDomainKeypair = false; return; } @@ -410,7 +423,12 @@ module.exports.create = function (gl) { // This has been done in the getAsync already, so we skip it here // if approveDomains doesn't set subject, we set it here //args.subject = args.subject || args.domains[0]; - return gl.store.certificates.setKeypairAsync(args, domainKeypair).then(function () { + var promise; + if (newDomainKeypair) { + args.keypair = domainKeypair; + promise = gl.store.certificates.setKeypairAsync(args, domainKeypair); + } + return PromiseA.resolve(promise).then(function () { return gl.store.certificates.setAsync(args).then(function () { return results; }); @@ -480,7 +498,15 @@ module.exports.create = function (gl) { cert = utils.attachCertInfo(cert); if (utils.certHasDomain(cert, args.domain)) { log(args.debug, 'checkAsync found existing certificates'); - return cert; + + if (cert.privkey) { + return cert; + } else { + return gl.store.certificates.checkKeypairAsync(args).then(function (keypair) { + cert.privkey = RSA.exportPrivatePem(keypair).privateKeyPem; + return cert; + }); + } } log(args.debug, 'checkAsync found mismatched / incomplete certificates'); } diff --git a/package.json b/package.json index ac4bfb7..cb8c43a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "greenlock", - "version": "2.7.8", + "version": "2.7.9-rc1", "description": "Let's Encrypt for node.js on npm", "main": "index.js", "files": [