| 
									
										
										
										
											2015-12-15 03:37:39 -08:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-29 02:51:35 -06:00
										 |  |  | var PromiseA; | 
					
						
							|  |  |  | try { | 
					
						
							|  |  |  |   PromiseA = require('bluebird'); | 
					
						
							|  |  |  | } catch(e) { | 
					
						
							|  |  |  |   PromiseA = global.Promise; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-07-04 02:13:11 -06:00
										 |  |  | var util = require('util'); | 
					
						
							|  |  |  | function promisifyAll(obj) { | 
					
						
							|  |  |  |   var aobj = {}; | 
					
						
							|  |  |  |   Object.keys(obj).forEach(function (key) { | 
					
						
							| 
									
										
										
										
											2018-07-04 02:17:18 -06:00
										 |  |  |     if ('function' === typeof obj[key]) { | 
					
						
							| 
									
										
										
										
											2018-07-04 02:21:07 -06:00
										 |  |  |       aobj[key] = obj[key]; | 
					
						
							| 
									
										
										
										
											2018-07-04 02:17:18 -06:00
										 |  |  |       aobj[key + 'Async'] = util.promisify(obj[key]); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-07-04 02:13:11 -06:00
										 |  |  |   }); | 
					
						
							|  |  |  |   return aobj; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-06-29 02:51:35 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-25 22:02:57 +02:00
										 |  |  | function _log(debug) { | 
					
						
							| 
									
										
										
										
											2016-08-09 15:02:10 -04:00
										 |  |  |   if (debug) { | 
					
						
							|  |  |  |     var args = Array.prototype.slice.call(arguments); | 
					
						
							|  |  |  |     args.shift(); | 
					
						
							| 
									
										
										
										
											2018-05-15 16:01:09 -06:00
										 |  |  |     args.unshift("[greenlock/lib/core.js]"); | 
					
						
							| 
									
										
										
										
											2016-08-09 15:02:10 -04:00
										 |  |  |     console.log.apply(console, args); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-15 16:01:09 -06:00
										 |  |  | module.exports.create = function (gl) { | 
					
						
							| 
									
										
										
										
											2016-08-08 19:14:53 -04:00
										 |  |  |   var utils = require('./utils'); | 
					
						
							| 
									
										
										
										
											2018-07-04 02:13:11 -06:00
										 |  |  |   var RSA = promisifyAll(require('rsa-compat').RSA); | 
					
						
							| 
									
										
										
										
											2018-05-15 16:01:09 -06:00
										 |  |  |   var log = gl.log || _log; // allow custom log
 | 
					
						
							| 
									
										
										
										
											2017-04-06 14:21:12 -06:00
										 |  |  |   var pendingRegistrations = {}; | 
					
						
							| 
									
										
										
										
											2016-08-04 18:49:35 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-06 02:05:04 -04:00
										 |  |  |   var core = { | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  |     // Helpers
 | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  |     getAcmeUrlsAsync: function (args) { | 
					
						
							|  |  |  |       var now = Date.now(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // TODO check response header on request for cache time
 | 
					
						
							| 
									
										
										
										
											2018-05-15 16:01:09 -06:00
										 |  |  |       if ((now - gl._ipc.acmeUrlsUpdatedAt) < 10 * 60 * 1000) { | 
					
						
							|  |  |  |         return PromiseA.resolve(gl._ipc.acmeUrls); | 
					
						
							| 
									
										
										
										
											2016-08-06 02:05:04 -04:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2015-12-15 03:37:39 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |       // TODO acme-v2/nocompat
 | 
					
						
							| 
									
										
										
										
											2018-05-15 16:01:09 -06:00
										 |  |  |       return gl.acme.getAcmeUrlsAsync(args.server).then(function (data) { | 
					
						
							|  |  |  |         gl._ipc.acmeUrlsUpdatedAt = Date.now(); | 
					
						
							|  |  |  |         gl._ipc.acmeUrls = data; | 
					
						
							| 
									
										
										
										
											2015-12-15 03:37:39 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-15 16:01:09 -06:00
										 |  |  |         return gl._ipc.acmeUrls; | 
					
						
							| 
									
										
										
										
											2016-08-06 02:05:04 -04:00
										 |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-06 02:05:04 -04:00
										 |  |  |     //
 | 
					
						
							|  |  |  |     // The Main Enchilada
 | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  |     // Accounts
 | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  |   , accounts: { | 
					
						
							| 
									
										
										
										
											2016-08-08 18:11:25 -04:00
										 |  |  |       // Accounts
 | 
					
						
							| 
									
										
										
										
											2016-08-06 02:05:04 -04:00
										 |  |  |       registerAsync: function (args) { | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  |         var err; | 
					
						
							| 
									
										
										
										
											2018-05-15 16:01:09 -06:00
										 |  |  |         var copy = utils.merge(args, gl); | 
					
						
							| 
									
										
										
										
											2016-08-08 19:14:53 -04:00
										 |  |  |         var disagreeTos; | 
					
						
							| 
									
										
										
										
											2016-08-08 18:11:25 -04:00
										 |  |  |         args = utils.tplCopy(copy); | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |         if (!args.account) { args.account = {}; } | 
					
						
							|  |  |  |         if ('object' === typeof args.account && !args.account.id) { args.account.id = args.accountId || args.email || ''; } | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-08 19:14:53 -04:00
										 |  |  |         disagreeTos = (!args.agreeTos && 'undefined' !== typeof args.agreeTos); | 
					
						
							|  |  |  |         if (!args.email || disagreeTos || (parseInt(args.rsaKeySize, 10) < 2048)) { | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  |           err = new Error( | 
					
						
							|  |  |  |             "In order to register an account both 'email' and 'agreeTos' must be present" | 
					
						
							|  |  |  |               + " and 'rsaKeySize' must be 2048 or greater." | 
					
						
							|  |  |  |           ); | 
					
						
							|  |  |  |           err.code = 'E_ARGS'; | 
					
						
							|  |  |  |           return PromiseA.reject(err); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return utils.testEmail(args.email).then(function () { | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |           if (args.account && args.account.privkey && (args.account.privkey.jwk || args.account.privkey.pem)) { | 
					
						
							|  |  |  |             // TODO import jwk or pem and return it here
 | 
					
						
							|  |  |  |             console.warn("TODO: implement accounts.checkKeypairAsync skipping"); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           var accountKeypair; | 
					
						
							| 
									
										
										
										
											2019-04-05 02:29:21 -06:00
										 |  |  |           var newAccountKeypair = true; | 
					
						
							| 
									
										
										
										
											2018-05-15 16:01:09 -06:00
										 |  |  |           var promise = gl.store.accounts.checkKeypairAsync(args).then(function (keypair) { | 
					
						
							| 
									
										
										
										
											2016-08-09 15:02:10 -04:00
										 |  |  |             if (keypair) { | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |               // TODO keypairs
 | 
					
						
							| 
									
										
										
										
											2019-04-05 02:29:21 -06:00
										 |  |  |               newAccountKeypair = false; | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |               accountKeypair = RSA.import(keypair); | 
					
						
							|  |  |  |               return; | 
					
						
							| 
									
										
										
										
											2016-08-09 15:02:10 -04:00
										 |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-08 19:43:31 -04:00
										 |  |  |             if (args.accountKeypair) { | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |               // TODO keypairs
 | 
					
						
							|  |  |  |               accountKeypair = RSA.import(args.accountKeypair); | 
					
						
							|  |  |  |               return; | 
					
						
							| 
									
										
										
										
											2016-08-08 19:43:31 -04:00
										 |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-13 04:20:57 -06:00
										 |  |  |             var keypairOpts = { bitlen: args.rsaKeySize, exp: 65537, public: true, pem: true }; | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |             // TODO keypairs
 | 
					
						
							|  |  |  |             return (args.generateKeypair||RSA.generateKeypairAsync)(keypairOpts).then(function (keypair) { | 
					
						
							| 
									
										
										
										
											2016-08-08 18:11:25 -04:00
										 |  |  |               keypair.privateKeyPem = RSA.exportPrivatePem(keypair); | 
					
						
							| 
									
										
										
										
											2016-08-08 19:14:53 -04:00
										 |  |  |               keypair.publicKeyPem = RSA.exportPublicPem(keypair); | 
					
						
							| 
									
										
										
										
											2016-08-08 18:11:25 -04:00
										 |  |  |               keypair.privateKeyJwk = RSA.exportPrivateJwk(keypair); | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |               accountKeypair = keypair; | 
					
						
							| 
									
										
										
										
											2016-08-08 18:11:25 -04:00
										 |  |  |             }); | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |           }).then(function () { | 
					
						
							|  |  |  |             return accountKeypair; | 
					
						
							|  |  |  |          }); | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-08 18:11:25 -04:00
										 |  |  |           return promise.then(function (keypair) { | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  |             // Note: the ACME urls are always fetched fresh on purpose
 | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |             // TODO acme-v2/nocompat
 | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  |             return core.getAcmeUrlsAsync(args).then(function (urls) { | 
					
						
							|  |  |  |               args._acmeUrls = urls; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |               // TODO acme-v2/nocompat
 | 
					
						
							| 
									
										
										
										
											2018-05-15 16:01:09 -06:00
										 |  |  |               return gl.acme.registerNewAccountAsync({ | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  |                 email: args.email | 
					
						
							|  |  |  |               , newRegUrl: args._acmeUrls.newReg | 
					
						
							| 
									
										
										
										
											2018-05-21 07:20:31 -06:00
										 |  |  |               , newAuthzUrl: args._acmeUrls.newAuthz | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  |               , agreeToTerms: function (tosUrl, agreeCb) { | 
					
						
							| 
									
										
										
										
											2018-05-15 16:01:09 -06:00
										 |  |  |                   if (true === args.agreeTos || tosUrl === args.agreeTos || tosUrl === gl.agreeToTerms) { | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  |                     agreeCb(null, tosUrl); | 
					
						
							|  |  |  |                     return; | 
					
						
							|  |  |  |                   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                   // args.email = email;      // already there
 | 
					
						
							|  |  |  |                   // args.domains = domains   // already there
 | 
					
						
							|  |  |  |                   args.tosUrl = tosUrl; | 
					
						
							| 
									
										
										
										
											2018-05-15 16:01:09 -06:00
										 |  |  |                   gl.agreeToTerms(args, agreeCb); | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  |                 } | 
					
						
							|  |  |  |               , accountKeypair: keypair | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-15 16:01:09 -06:00
										 |  |  |               , debug: gl.debug || args.debug | 
					
						
							| 
									
										
										
										
											2016-08-08 19:14:53 -04:00
										 |  |  |               }).then(function (receipt) { | 
					
						
							|  |  |  |                 var reg = { | 
					
						
							|  |  |  |                   keypair: keypair | 
					
						
							|  |  |  |                 , receipt: receipt | 
					
						
							| 
									
										
										
										
											2019-04-05 02:29:21 -06:00
										 |  |  |                 , kid: receipt && receipt.key && (receipt.key.kid || receipt.kid) | 
					
						
							| 
									
										
										
										
											2016-08-08 19:14:53 -04:00
										 |  |  |                 , email: args.email | 
					
						
							| 
									
										
										
										
											2018-05-21 07:20:31 -06:00
										 |  |  |                 , newRegUrl: args._acmeUrls.newReg | 
					
						
							|  |  |  |                 , newAuthzUrl: args._acmeUrls.newAuthz | 
					
						
							| 
									
										
										
										
											2016-08-08 19:14:53 -04:00
										 |  |  |                 }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-05 02:29:21 -06:00
										 |  |  |                 var accountKeypairPromise; | 
					
						
							|  |  |  |                 args.keypair = keypair; | 
					
						
							| 
									
										
										
										
											2019-04-07 21:35:51 -06:00
										 |  |  |                 args.receipt = receipt; | 
					
						
							| 
									
										
										
										
											2019-04-05 02:29:21 -06:00
										 |  |  |                 if (newAccountKeypair) { | 
					
						
							|  |  |  |                   accountKeypairPromise = gl.store.accounts.setKeypairAsync(args, keypair); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 return PromiseA.resolve(accountKeypairPromise).then(function () { | 
					
						
							| 
									
										
										
										
											2016-08-08 19:14:53 -04:00
										 |  |  |                 // TODO move templating of arguments to right here?
 | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |                 if (!gl.store.accounts.setAsync) { return PromiseA.resolve({ keypair: keypair }); } | 
					
						
							|  |  |  |                   return gl.store.accounts.setAsync(args, reg).then(function (account) { | 
					
						
							|  |  |  |                    if (account && 'object' !== typeof account) { | 
					
						
							|  |  |  |                       throw new Error("store.accounts.setAsync should either return 'null' or an object with at least a string 'id'"); | 
					
						
							|  |  |  |                    } | 
					
						
							|  |  |  |                     if (!account) { account = {}; } | 
					
						
							|  |  |  |                     account.keypair = keypair; | 
					
						
							|  |  |  |                     return account; | 
					
						
							|  |  |  |                   }); | 
					
						
							|  |  |  |                }); | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  |               }); | 
					
						
							| 
									
										
										
										
											2016-08-06 02:05:04 -04:00
										 |  |  |             }); | 
					
						
							|  |  |  |           }); | 
					
						
							| 
									
										
										
										
											2016-08-04 18:49:35 -04:00
										 |  |  |         }); | 
					
						
							| 
									
										
										
										
											2016-08-06 02:05:04 -04:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-08 18:11:25 -04:00
										 |  |  |       // Accounts
 | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |       // (only used for keypair)
 | 
					
						
							| 
									
										
										
										
											2016-08-06 02:05:04 -04:00
										 |  |  |     , getAsync: function (args) { | 
					
						
							| 
									
										
										
										
											2019-04-05 02:29:21 -06:00
										 |  |  |         var accountPromise = null; | 
					
						
							|  |  |  |         if (gl.store.accounts.checkAsync) { | 
					
						
							|  |  |  |           accountPromise = core.accounts.checkAsync(args); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return PromiseA.resolve(accountPromise).then(function (account) { | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |           if (!account) { return core.accounts.registerAsync(args); } | 
					
						
							|  |  |  |           if (account.keypair) { return account; } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if (!args.account) { args.account = {}; } | 
					
						
							|  |  |  |           if ('object' === typeof args.account && !args.account.id) { args.account.id = args.accountId || args.email || ''; } | 
					
						
							|  |  |  |           var copy = utils.merge(args, gl); | 
					
						
							|  |  |  |           args = utils.tplCopy(copy); | 
					
						
							|  |  |  |           return gl.store.accounts.checkKeypairAsync(args).then(function (keypair) { | 
					
						
							|  |  |  |             if (keypair) { return { keypair: keypair }; } | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  |             return core.accounts.registerAsync(args); | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |           }); | 
					
						
							| 
									
										
										
										
											2016-08-06 02:05:04 -04:00
										 |  |  |         }); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-08 18:11:25 -04:00
										 |  |  |       // Accounts
 | 
					
						
							| 
									
										
										
										
											2016-08-06 02:05:04 -04:00
										 |  |  |     , checkAsync: function (args) { | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  |         var requiredArgs = ['accountId', 'email', 'domains', 'domain']; | 
					
						
							| 
									
										
										
										
											2019-04-05 02:29:21 -06:00
										 |  |  |         if (!(args.account && (args.account.id || args.account.kid)) | 
					
						
							|  |  |  |           && !requiredArgs.some(function (key) { return -1 !== Object.keys(args).indexOf(key); })) { | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  |           return PromiseA.reject(new Error( | 
					
						
							|  |  |  |             "In order to register or retrieve an account one of '" + requiredArgs.join("', '") + "' must be present" | 
					
						
							|  |  |  |           )); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2015-12-20 02:01:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-15 16:01:09 -06:00
										 |  |  |         var copy = utils.merge(args, gl); | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  |         args = utils.tplCopy(copy); | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |         if (!args.account) { args.account = {}; } | 
					
						
							|  |  |  |         if ('object' === typeof args.account && !args.account.id) { args.account.id = args.accountId || args.email || ''; } | 
					
						
							| 
									
										
										
										
											2015-12-20 03:31:05 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |         // 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
 | 
					
						
							| 
									
										
										
										
											2019-04-05 02:29:21 -06:00
										 |  |  |         if (!gl.store.accounts.checkAsync) { return PromiseA.resolve(null); } | 
					
						
							| 
									
										
										
										
											2018-05-15 16:01:09 -06:00
										 |  |  |         return gl.store.accounts.checkAsync(args).then(function (account) { | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  |           if (!account) { | 
					
						
							|  |  |  |             return null; | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  |           args.account = account; | 
					
						
							|  |  |  |           args.accountId = account.id; | 
					
						
							| 
									
										
										
										
											2016-08-05 04:14:40 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  |           return account; | 
					
						
							| 
									
										
										
										
											2016-08-06 02:05:04 -04:00
										 |  |  |         }); | 
					
						
							| 
									
										
										
										
											2016-08-04 18:49:35 -04:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2016-08-04 14:26:49 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-06 02:05:04 -04:00
										 |  |  |   , certificates: { | 
					
						
							| 
									
										
										
										
											2016-08-08 18:11:25 -04:00
										 |  |  |       // Certificates
 | 
					
						
							| 
									
										
										
										
											2016-08-06 02:05:04 -04:00
										 |  |  |       registerAsync: function (args) { | 
					
						
							| 
									
										
										
										
											2016-08-07 02:02:02 -04:00
										 |  |  |         var err; | 
					
						
							| 
									
										
										
										
											2018-05-15 16:01:09 -06:00
										 |  |  |         var challengeDefaults = gl['_challengeOpts_' + (args.challengeType || gl.challengeType)] || {}; | 
					
						
							| 
									
										
										
										
											2016-09-13 18:27:24 -06:00
										 |  |  |         var copy = utils.merge(args, challengeDefaults || {}); | 
					
						
							| 
									
										
										
										
											2018-05-15 16:01:09 -06:00
										 |  |  |         copy = utils.merge(copy, gl); | 
					
						
							| 
									
										
										
										
											2019-04-01 23:11:56 -06:00
										 |  |  |         if (!copy.subject) { copy.subject = copy.domains[0]; } | 
					
						
							|  |  |  |         if (!copy.domain) { copy.domain = copy.domains[0]; } | 
					
						
							| 
									
										
										
										
											2016-08-07 02:02:02 -04:00
										 |  |  |         args = utils.tplCopy(copy); | 
					
						
							| 
									
										
										
										
											2016-08-05 04:14:40 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-07 02:02:02 -04:00
										 |  |  |         if (!Array.isArray(args.domains)) { | 
					
						
							|  |  |  |           return PromiseA.reject(new Error('args.domains should be an array of domains')); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-04-01 23:11:56 -06:00
										 |  |  |         //if (-1 === args.domains.indexOf(args.subject)) // TODO relax the constraint once acme-v2 handles subject?
 | 
					
						
							|  |  |  |         if (args.subject !== args.domains[0]) { | 
					
						
							|  |  |  |           console.warn("The certificate's subject (primary domain) should be first in the list of opts.domains"); | 
					
						
							|  |  |  |           console.warn('\topts.subject: (set by you approveDomains(), falling back to opts.domain) ' + args.subject); | 
					
						
							|  |  |  |           console.warn('\topts.domain: (set by SNICallback()) ' + args.domain); | 
					
						
							|  |  |  |           console.warn('\topts.domains: (set by you in approveDomains()) ' + args.domains.join(',')); | 
					
						
							|  |  |  |           console.warn("Updating your code will prevent weird, random, hard-to-repro bugs during renewals"); | 
					
						
							|  |  |  |           console.warn("(also this will be required in the next major version of greenlock)"); | 
					
						
							|  |  |  |           //return PromiseA.reject(new Error('certificate subject (primary domain) must be the first in opts.domains'));
 | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2016-08-07 02:02:02 -04:00
										 |  |  |         if (!(args.domains.length && args.domains.every(utils.isValidDomain))) { | 
					
						
							|  |  |  |           // NOTE: this library can't assume to handle the http loopback
 | 
					
						
							|  |  |  |           // (or dns-01 validation may be used)
 | 
					
						
							|  |  |  |           // so we do not check dns records or attempt a loopback here
 | 
					
						
							| 
									
										
										
										
											2019-04-01 23:11:56 -06:00
										 |  |  |           err = new Error("invalid domain name(s): '(" + args.subject + ') ' + args.domains.join(',') + "'"); | 
					
						
							| 
									
										
										
										
											2016-08-07 02:02:02 -04:00
										 |  |  |           err.code = "INVALID_DOMAIN"; | 
					
						
							|  |  |  |           return PromiseA.reject(err); | 
					
						
							| 
									
										
										
										
											2015-12-17 04:44:28 +00:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2016-08-04 14:26:49 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-06 14:21:12 -06:00
										 |  |  |         // If a previous request to (re)register a certificate is already underway we need
 | 
					
						
							|  |  |  |         // to return the same promise created before rather than registering things twice.
 | 
					
						
							|  |  |  |         // I'm not 100% sure how to properly handle the case where someone registers domain
 | 
					
						
							|  |  |  |         // lists with some but not all elements common, nor am I sure that's even a case that
 | 
					
						
							|  |  |  |         // is allowed to happen anyway. But for now we act like the list is completely the
 | 
					
						
							|  |  |  |         // same if any elements are the same.
 | 
					
						
							|  |  |  |         var promise; | 
					
						
							|  |  |  |         args.domains.some(function (name) { | 
					
						
							|  |  |  |           if (pendingRegistrations.hasOwnProperty(name)) { | 
					
						
							|  |  |  |             promise = pendingRegistrations[name]; | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |         if (promise) { | 
					
						
							|  |  |  |           return promise; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         promise = core.certificates._runRegistration(args); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Now that the registration is actually underway we need to make sure any subsequent
 | 
					
						
							|  |  |  |         // registration attempts return the same promise until it is completed (but not after
 | 
					
						
							|  |  |  |         // it is completed).
 | 
					
						
							|  |  |  |         args.domains.forEach(function (name) { | 
					
						
							|  |  |  |           pendingRegistrations[name] = promise; | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |         function clearPending() { | 
					
						
							|  |  |  |           args.domains.forEach(function (name) { | 
					
						
							|  |  |  |             delete pendingRegistrations[name]; | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         promise.then(clearPending, clearPending); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return promise; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     , _runRegistration: function (args) { | 
					
						
							| 
									
										
										
										
											2016-08-09 15:02:10 -04:00
										 |  |  |         // TODO renewal cb
 | 
					
						
							|  |  |  |         // accountId and or email
 | 
					
						
							| 
									
										
										
										
											2017-04-06 14:21:12 -06:00
										 |  |  |         return core.accounts.getAsync(args).then(function (account) { | 
					
						
							|  |  |  |           args.account = account; | 
					
						
							| 
									
										
										
										
											2016-08-07 02:02:02 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |           if (args.certificate && args.certificate.privkey && (args.certificate.privkey.jwk || args.certificate.privkey.pem)) { | 
					
						
							|  |  |  |             // TODO import jwk or pem and return it here
 | 
					
						
							|  |  |  |             console.warn("TODO: implement certificates.checkKeypairAsync skipping"); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           var domainKeypair; | 
					
						
							| 
									
										
										
										
											2019-04-05 02:29:21 -06:00
										 |  |  |           var newDomainKeypair = true; | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |           // 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];
 | 
					
						
							| 
									
										
										
										
											2018-05-15 16:01:09 -06:00
										 |  |  |           var promise = gl.store.certificates.checkKeypairAsync(args).then(function (keypair) { | 
					
						
							| 
									
										
										
										
											2016-08-09 15:02:10 -04:00
										 |  |  |             if (keypair) { | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |               domainKeypair = RSA.import(keypair); | 
					
						
							| 
									
										
										
										
											2019-04-05 02:29:21 -06:00
										 |  |  |               newDomainKeypair = false; | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |               return; | 
					
						
							| 
									
										
										
										
											2016-08-09 15:02:10 -04:00
										 |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-08 19:43:31 -04:00
										 |  |  |             if (args.domainKeypair) { | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |               domainKeypair = RSA.import(args.domainKeypair); | 
					
						
							|  |  |  |               return; | 
					
						
							| 
									
										
										
										
											2016-08-08 19:43:31 -04:00
										 |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-13 04:20:57 -06:00
										 |  |  |             var keypairOpts = { bitlen: args.rsaKeySize, exp: 65537, public: true, pem: true }; | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |             return (args.generateKeypair||RSA.generateKeypairAsync)(keypairOpts).then(function (keypair) { | 
					
						
							| 
									
										
										
										
											2016-08-07 02:02:02 -04:00
										 |  |  |               keypair.privateKeyPem = RSA.exportPrivatePem(keypair); | 
					
						
							| 
									
										
										
										
											2016-08-08 19:14:53 -04:00
										 |  |  |               keypair.publicKeyPem = RSA.exportPublicPem(keypair); | 
					
						
							| 
									
										
										
										
											2016-08-07 02:02:02 -04:00
										 |  |  |               keypair.privateKeyJwk = RSA.exportPrivateJwk(keypair); | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |               domainKeypair = keypair; | 
					
						
							| 
									
										
										
										
											2016-08-07 02:02:02 -04:00
										 |  |  |             }); | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |           }).then(function () { | 
					
						
							|  |  |  |             return domainKeypair; | 
					
						
							| 
									
										
										
										
											2016-08-06 02:05:04 -04:00
										 |  |  |           }); | 
					
						
							| 
									
										
										
										
											2016-08-04 14:26:49 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-07 02:02:02 -04:00
										 |  |  |           return promise.then(function (domainKeypair) { | 
					
						
							|  |  |  |             args.domainKeypair = domainKeypair; | 
					
						
							|  |  |  |             //args.registration = domainKey;
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  |             // Note: the ACME urls are always fetched fresh on purpose
 | 
					
						
							|  |  |  |             // TODO is this the right place for this?
 | 
					
						
							|  |  |  |             return core.getAcmeUrlsAsync(args).then(function (urls) { | 
					
						
							|  |  |  |               args._acmeUrls = urls; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-09 15:02:10 -04:00
										 |  |  |               var certReq = { | 
					
						
							| 
									
										
										
										
											2018-05-15 16:01:09 -06:00
										 |  |  |                 debug: args.debug || gl.debug | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |               , newAuthzUrl: args._acmeUrls.newAuthz | 
					
						
							|  |  |  |               , newCertUrl: args._acmeUrls.newCert | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               , accountKeypair: RSA.import(account.keypair) | 
					
						
							|  |  |  |               , domainKeypair: domainKeypair | 
					
						
							| 
									
										
										
										
											2019-04-01 23:11:56 -06:00
										 |  |  |               , subject: args.subject // TODO handle this in acme-v2
 | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  |               , domains: args.domains | 
					
						
							| 
									
										
										
										
											2019-04-02 21:23:31 -06:00
										 |  |  |               , challengeTypes: Object.keys(args.challenges) | 
					
						
							| 
									
										
										
										
											2016-08-09 15:02:10 -04:00
										 |  |  |               }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               //
 | 
					
						
							|  |  |  |               // IMPORTANT
 | 
					
						
							|  |  |  |               //
 | 
					
						
							|  |  |  |               // setChallenge and removeChallenge are handed defaults
 | 
					
						
							|  |  |  |               // instead of args because getChallenge does not have
 | 
					
						
							|  |  |  |               // access to args
 | 
					
						
							|  |  |  |               // (args is per-request, defaults is per instance)
 | 
					
						
							|  |  |  |               //
 | 
					
						
							|  |  |  |               // Each of these fires individually for each domain,
 | 
					
						
							|  |  |  |               // even though the certificate on the whole may have many domains
 | 
					
						
							|  |  |  |               //
 | 
					
						
							| 
									
										
										
										
											2019-04-02 21:23:31 -06:00
										 |  |  |               certReq.setChallenge = function (challenge, done) { | 
					
						
							|  |  |  |                 log(args.debug, "setChallenge called for '" + challenge.altname + "'"); | 
					
						
							|  |  |  |                 var copy = utils.merge({ domains: [challenge.altname] }, args); | 
					
						
							| 
									
										
										
										
											2018-05-15 16:01:09 -06:00
										 |  |  |                 copy = utils.merge(copy, gl); | 
					
						
							| 
									
										
										
										
											2016-08-09 15:02:10 -04:00
										 |  |  |                 utils.tplCopy(copy); | 
					
						
							| 
									
										
										
										
											2019-04-02 21:23:31 -06:00
										 |  |  |                 copy.challenge = challenge; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (1 === gl.challenges[challenge.type].set.length) { | 
					
						
							|  |  |  |                   gl.challenges[challenge.type].set(copy).then(function (result) { | 
					
						
							|  |  |  |                     done(null, result); | 
					
						
							|  |  |  |                   }).catch(done); | 
					
						
							|  |  |  |                 } else if (2 === gl.challenges[challenge.type].set.length) { | 
					
						
							|  |  |  |                   gl.challenges[challenge.type].set(copy, done); | 
					
						
							|  |  |  |                 } else { | 
					
						
							|  |  |  |                   Object.keys(challenge).forEach(function (key) { | 
					
						
							|  |  |  |                     done[key] = challenge[key]; | 
					
						
							|  |  |  |                   }); | 
					
						
							|  |  |  |                   gl.challenges[challenge.type].set(copy, challenge.altname, challenge.token, challenge.keyAuthorization, done); | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2016-08-09 15:02:10 -04:00
										 |  |  |               }; | 
					
						
							| 
									
										
										
										
											2019-04-02 21:23:31 -06:00
										 |  |  |               certReq.removeChallenge = function (challenge, done) { | 
					
						
							|  |  |  |                 log(args.debug, "removeChallenge called for '" + challenge.altname + "'"); | 
					
						
							|  |  |  |                 var copy = utils.merge({ domains: [challenge.altname] }, gl); | 
					
						
							| 
									
										
										
										
											2016-08-09 15:02:10 -04:00
										 |  |  |                 utils.tplCopy(copy); | 
					
						
							| 
									
										
										
										
											2019-04-02 21:23:31 -06:00
										 |  |  |                 copy.challenge = challenge; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (1 === gl.challenges[challenge.type].remove.length) { | 
					
						
							|  |  |  |                   gl.challenges[challenge.type].remove(copy).then(function (result) { | 
					
						
							|  |  |  |                     done(null, result); | 
					
						
							|  |  |  |                   }).catch(done); | 
					
						
							|  |  |  |                 } else if (2 === gl.challenges[challenge.type].remove.length) { | 
					
						
							|  |  |  |                   gl.challenges[challenge.type].remove(copy, done); | 
					
						
							|  |  |  |                 } else { | 
					
						
							|  |  |  |                   Object.keys(challenge).forEach(function (key) { | 
					
						
							|  |  |  |                     done[key] = challenge[key]; | 
					
						
							|  |  |  |                   }); | 
					
						
							| 
									
										
										
										
											2019-04-07 09:22:38 -06:00
										 |  |  |                   gl.challenges[challenge.type].remove(copy, challenge.altname, challenge.token, done); | 
					
						
							| 
									
										
										
										
											2019-04-02 21:23:31 -06:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2016-08-09 15:02:10 -04:00
										 |  |  |               }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-01 23:11:56 -06:00
										 |  |  |               log(args.debug, 'calling greenlock.acme.getCertificateAsync', certReq.subject, certReq.domains); | 
					
						
							| 
									
										
										
										
											2016-08-09 15:10:44 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |               // TODO acme-v2/nocompat
 | 
					
						
							| 
									
										
										
										
											2018-05-15 16:01:09 -06:00
										 |  |  |               return gl.acme.getCertificateAsync(certReq).then(utils.attachCertInfo); | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  |             }); | 
					
						
							| 
									
										
										
										
											2016-08-07 02:02:02 -04:00
										 |  |  |           }).then(function (results) { | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |             //var requested = {};
 | 
					
						
							|  |  |  |             //var issued = {};
 | 
					
						
							| 
									
										
										
										
											2016-08-13 14:35:19 -06:00
										 |  |  |             // { cert, chain, privkey /*TODO, subject, altnames, issuedAt, expiresAt */ }
 | 
					
						
							| 
									
										
										
										
											2016-08-06 02:05:04 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-11 22:33:44 -06:00
										 |  |  |             // args.certs.privkey = RSA.exportPrivatePem(options.domainKeypair);
 | 
					
						
							| 
									
										
										
										
											2016-08-13 14:35:19 -06:00
										 |  |  |             args.certs = results; | 
					
						
							|  |  |  |             // args.pems is deprecated
 | 
					
						
							| 
									
										
										
										
											2016-08-07 02:02:02 -04:00
										 |  |  |             args.pems = results; | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |             // 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];
 | 
					
						
							| 
									
										
										
										
											2019-04-05 02:29:21 -06:00
										 |  |  |             var promise; | 
					
						
							|  |  |  |             if (newDomainKeypair) { | 
					
						
							|  |  |  |               args.keypair = domainKeypair; | 
					
						
							|  |  |  |               promise = gl.store.certificates.setKeypairAsync(args, domainKeypair); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             return PromiseA.resolve(promise).then(function () { | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |               return gl.store.certificates.setAsync(args).then(function () { | 
					
						
							|  |  |  |                 return results; | 
					
						
							|  |  |  |               }); | 
					
						
							| 
									
										
										
										
											2016-08-07 02:02:02 -04:00
										 |  |  |             }); | 
					
						
							|  |  |  |           }); | 
					
						
							| 
									
										
										
										
											2016-08-06 02:05:04 -04:00
										 |  |  |         }); | 
					
						
							| 
									
										
										
										
											2016-08-04 18:49:35 -04:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2016-08-08 20:26:46 -04:00
										 |  |  |       // Certificates
 | 
					
						
							| 
									
										
										
										
											2016-08-09 15:38:18 -04:00
										 |  |  |     , renewAsync: function (args, certs) { | 
					
						
							|  |  |  |         var renewableAt = core.certificates._getRenewableAt(args, certs); | 
					
						
							| 
									
										
										
										
											2016-08-09 15:51:42 -04:00
										 |  |  |         var err; | 
					
						
							| 
									
										
										
										
											2016-08-09 15:38:18 -04:00
										 |  |  |         //var halfLife = (certs.expiresAt - certs.issuedAt) / 2;
 | 
					
						
							|  |  |  |         //var renewable = (Date.now() - certs.issuedAt) > halfLife;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         log(args.debug, "(Renew) Expires At", new Date(certs.expiresAt).toISOString()); | 
					
						
							|  |  |  |         log(args.debug, "(Renew) Renewable At", new Date(renewableAt).toISOString()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!args.duplicate && Date.now() < renewableAt) { | 
					
						
							| 
									
										
										
										
											2016-08-09 15:51:42 -04:00
										 |  |  |           err = new Error( | 
					
						
							| 
									
										
										
										
											2016-08-09 15:38:18 -04:00
										 |  |  |               "[ERROR] Certificate issued at '" | 
					
						
							|  |  |  |             + new Date(certs.issuedAt).toISOString() + "' and expires at '" | 
					
						
							|  |  |  |             + new Date(certs.expiresAt).toISOString() + "'. Ignoring renewal attempt until '" | 
					
						
							|  |  |  |             + new Date(renewableAt).toISOString() + "'. Set { duplicate: true } to force." | 
					
						
							| 
									
										
										
										
											2016-08-09 15:51:42 -04:00
										 |  |  |           ); | 
					
						
							|  |  |  |           err.code = 'E_NOT_RENEWABLE'; | 
					
						
							|  |  |  |           return PromiseA.reject(err); | 
					
						
							| 
									
										
										
										
											2016-08-09 15:38:18 -04:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Either the cert has entered its renewal period
 | 
					
						
							|  |  |  |         // or we're forcing a refresh via 'dupliate: true'
 | 
					
						
							|  |  |  |         log(args.debug, "Renewing!"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-05 11:10:22 -07:00
										 |  |  |         if (!args.domains || !args.domains.length) { | 
					
						
							|  |  |  |           args.domains = args.servernames || [certs.subject].concat(certs.altnames); | 
					
						
							| 
									
										
										
										
											2016-08-09 15:38:18 -04:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  |         return core.certificates.registerAsync(args); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2016-08-08 20:26:46 -04:00
										 |  |  |       // Certificates
 | 
					
						
							| 
									
										
										
										
											2016-08-09 15:38:18 -04:00
										 |  |  |     , _isRenewable: function (args, certs) { | 
					
						
							|  |  |  |         var renewableAt = core.certificates._getRenewableAt(args, certs); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         log(args.debug, "Check Expires At", new Date(certs.expiresAt).toISOString()); | 
					
						
							|  |  |  |         log(args.debug, "Check Renewable At", new Date(renewableAt).toISOString()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (args.duplicate || Date.now() >= renewableAt) { | 
					
						
							| 
									
										
										
										
											2016-08-16 13:02:14 -04:00
										 |  |  |           log(args.debug, "certificates are renewable"); | 
					
						
							| 
									
										
										
										
											2016-08-09 15:38:18 -04:00
										 |  |  |           return true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     , _getRenewableAt: function (args, certs) { | 
					
						
							| 
									
										
										
										
											2018-05-15 16:01:09 -06:00
										 |  |  |         return certs.expiresAt - (args.renewWithin || gl.renewWithin); | 
					
						
							| 
									
										
										
										
											2016-08-09 15:38:18 -04:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2016-08-06 02:05:04 -04:00
										 |  |  |     , checkAsync: function (args) { | 
					
						
							| 
									
										
										
										
											2018-05-15 16:01:09 -06:00
										 |  |  |         var copy = utils.merge(args, gl); | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |         // if approveDomains doesn't set subject, we set it here
 | 
					
						
							| 
									
										
										
										
											2019-04-01 14:18:33 -06:00
										 |  |  |         if (!(copy.domains && copy.domains.length)) { copy.domains = [ copy.subject || copy.domain ].filter(Boolean); } | 
					
						
							|  |  |  |         if (!copy.subject) { copy.subject = copy.domains[0]; } | 
					
						
							|  |  |  |         if (!copy.domain) { copy.domain = copy.domains[0]; } | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |         args = utils.tplCopy(copy); | 
					
						
							| 
									
										
										
										
											2015-12-15 15:21:27 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-07 02:02:02 -04:00
										 |  |  |         // returns pems
 | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |         return gl.store.certificates.checkAsync(args).then(function (cert) { | 
					
						
							| 
									
										
										
										
											2019-04-05 18:24:34 -06:00
										 |  |  |           if (!cert) { log(args.debug, 'checkAsync failed to find certificates'); return null; } | 
					
						
							| 
									
										
										
										
											2019-04-05 02:29:21 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-05 18:24:34 -06:00
										 |  |  |           cert = utils.attachCertInfo(cert); | 
					
						
							|  |  |  |           if (utils.certHasDomain(cert, args.domain)) { | 
					
						
							|  |  |  |             log(args.debug, 'checkAsync found existing certificates'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (cert.privkey) { | 
					
						
							|  |  |  |               return cert; | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |               return gl.store.certificates.checkKeypairAsync(args).then(function (keypair) { | 
					
						
							|  |  |  |                 cert.privkey = RSA.exportPrivatePem(keypair).privateKeyPem; | 
					
						
							| 
									
										
										
										
											2019-04-05 02:29:21 -06:00
										 |  |  |                 return cert; | 
					
						
							| 
									
										
										
										
											2019-04-05 18:24:34 -06:00
										 |  |  |               }); | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2016-08-08 20:26:46 -04:00
										 |  |  |           } | 
					
						
							| 
									
										
										
										
											2019-04-05 18:24:34 -06:00
										 |  |  |           log(args.debug, 'checkAsync found mismatched / incomplete certificates'); | 
					
						
							| 
									
										
										
										
											2016-08-08 20:26:46 -04:00
										 |  |  |         }); | 
					
						
							| 
									
										
										
										
											2016-08-04 18:49:35 -04:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2016-08-08 20:26:46 -04:00
										 |  |  |       // Certificates
 | 
					
						
							| 
									
										
										
										
											2016-08-06 02:05:04 -04:00
										 |  |  |     , getAsync: function (args) { | 
					
						
							| 
									
										
										
										
											2018-05-15 16:01:09 -06:00
										 |  |  |         var copy = utils.merge(args, gl); | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |         // if approveDomains doesn't set subject, we set it here
 | 
					
						
							| 
									
										
										
										
											2019-04-01 14:18:33 -06:00
										 |  |  |         if (!(copy.domains && copy.domains.length)) { copy.domains = [ copy.subject || copy.domain ].filter(Boolean); } | 
					
						
							|  |  |  |         if (!copy.subject) { copy.subject = copy.domains[0]; } | 
					
						
							|  |  |  |         if (!copy.domain) { copy.domain = copy.domains[0]; } | 
					
						
							| 
									
										
										
										
											2016-08-07 02:02:02 -04:00
										 |  |  |         args = utils.tplCopy(copy); | 
					
						
							| 
									
										
										
										
											2016-08-06 02:05:04 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |         if (args.certificate && args.certificate.privkey && args.certificate.cert && args.certificate.chain) { | 
					
						
							|  |  |  |           // TODO skip fetching a certificate if it's fetched during approveDomains
 | 
					
						
							|  |  |  |           console.warn("TODO: implement certificates.checkAsync skipping"); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2016-08-06 02:05:04 -04:00
										 |  |  |         return core.certificates.checkAsync(args).then(function (certs) { | 
					
						
							| 
									
										
										
										
											2019-04-01 00:36:59 -06:00
										 |  |  |           if (certs) { certs = utils.attachCertInfo(certs); } | 
					
						
							|  |  |  |           if (!certs || !utils.certHasDomain(certs, args.domain)) { | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  |             // There is no cert available
 | 
					
						
							| 
									
										
										
										
											2018-09-17 01:16:27 -06:00
										 |  |  |             if (false !== args.securityUpdates && !args._communityMemberAdded) { | 
					
						
							| 
									
										
										
										
											2018-05-10 02:31:24 -06:00
										 |  |  |               try { | 
					
						
							| 
									
										
										
										
											2018-09-17 01:16:27 -06:00
										 |  |  |                 // We will notify all greenlock users of mandatory and security updates
 | 
					
						
							| 
									
										
										
										
											2018-11-05 11:10:22 -07:00
										 |  |  |                 // We'll keep track of versions and os so we can make sure things work well
 | 
					
						
							|  |  |  |                 // { name, version, email, domains, action, communityMember, telemetry }
 | 
					
						
							|  |  |  |                 require('./community').add({ | 
					
						
							|  |  |  |                   name: args._communityPackage | 
					
						
							|  |  |  |                 , version: args._communityPackageVersion | 
					
						
							|  |  |  |                 , email: args.email | 
					
						
							|  |  |  |                 , domains: args.domains || args.servernames | 
					
						
							|  |  |  |                 , action: 'reg' | 
					
						
							|  |  |  |                 , communityMember: args.communityMember | 
					
						
							|  |  |  |                 , telemetry: args.telemetry | 
					
						
							|  |  |  |                 }); | 
					
						
							| 
									
										
										
										
											2018-05-10 02:31:24 -06:00
										 |  |  |               } catch(e) { /* ignore */ } | 
					
						
							|  |  |  |               args._communityMemberAdded = true; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  |             return core.certificates.registerAsync(args); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-09 15:38:18 -04:00
										 |  |  |           if (core.certificates._isRenewable(args, certs)) { | 
					
						
							| 
									
										
										
										
											2017-05-05 17:17:28 -06:00
										 |  |  |             // it's time to renew the available cert
 | 
					
						
							| 
									
										
										
										
											2018-09-17 01:16:27 -06:00
										 |  |  |             if (false !== args.securityUpdates && !args._communityMemberAdded) { | 
					
						
							| 
									
										
										
										
											2018-05-10 02:31:24 -06:00
										 |  |  |               try { | 
					
						
							| 
									
										
										
										
											2018-09-17 01:16:27 -06:00
										 |  |  |                 // We will notify all greenlock users of mandatory and security updates
 | 
					
						
							| 
									
										
										
										
											2018-11-05 11:10:22 -07:00
										 |  |  |                 // We'll keep track of versions and os so we can make sure things work well
 | 
					
						
							|  |  |  |                 // { name, version, email, domains, action, communityMember, telemetry }
 | 
					
						
							|  |  |  |                 require('./community').add({ | 
					
						
							|  |  |  |                   name: args._communityPackage | 
					
						
							|  |  |  |                 , version: args._communityPackageVersion | 
					
						
							|  |  |  |                 , email: args.email | 
					
						
							|  |  |  |                 , domains: args.domains || args.servernames | 
					
						
							|  |  |  |                 , action: 'renew' | 
					
						
							|  |  |  |                 , communityMember: args.communityMember | 
					
						
							|  |  |  |                 , telemetry: args.telemetry | 
					
						
							|  |  |  |                 }); | 
					
						
							| 
									
										
										
										
											2018-05-10 02:31:24 -06:00
										 |  |  |               } catch(e) { /* ignore */ } | 
					
						
							|  |  |  |               args._communityMemberAdded = true; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2017-08-25 16:48:24 +10:00
										 |  |  |             certs.renewing = core.certificates.renewAsync(args, certs); | 
					
						
							| 
									
										
										
										
											2017-05-05 17:17:28 -06:00
										 |  |  |             if (args.waitForRenewal) { | 
					
						
							| 
									
										
										
										
											2017-08-25 16:48:24 +10:00
										 |  |  |               return certs.renewing; | 
					
						
							| 
									
										
										
										
											2017-05-05 17:17:28 -06:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2016-08-06 02:05:04 -04:00
										 |  |  |           } | 
					
						
							| 
									
										
										
										
											2016-08-04 14:26:49 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-05 17:17:28 -06:00
										 |  |  |           // return existing unexpired (although potentially stale) certificates when available
 | 
					
						
							| 
									
										
										
										
											2017-08-25 16:48:24 +10:00
										 |  |  |           // there will be an additional .renewing property if the certs are being asynchronously renewed
 | 
					
						
							| 
									
										
										
										
											2016-08-09 15:38:18 -04:00
										 |  |  |           return certs; | 
					
						
							| 
									
										
										
										
											2016-08-07 02:02:02 -04:00
										 |  |  |         }).then(function (results) { | 
					
						
							|  |  |  |           // returns pems
 | 
					
						
							|  |  |  |           return results; | 
					
						
							| 
									
										
										
										
											2016-08-06 02:05:04 -04:00
										 |  |  |         }); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-12-15 15:21:27 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-15 03:37:39 -08:00
										 |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-06 02:05:04 -04:00
										 |  |  |   return core; | 
					
						
							| 
									
										
										
										
											2015-12-15 03:37:39 -08:00
										 |  |  | }; |