| 
									
										
										
										
											2015-12-15 03:37:39 -08:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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(); | 
					
						
							|  |  |  |     args.unshift("[le/lib/core.js]"); | 
					
						
							|  |  |  |     console.log.apply(console, args); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-05 18:54:28 -04:00
										 |  |  | module.exports.create = function (le) { | 
					
						
							| 
									
										
										
										
											2016-08-04 18:49:35 -04:00
										 |  |  |   var PromiseA = require('bluebird'); | 
					
						
							| 
									
										
										
										
											2016-08-08 19:14:53 -04:00
										 |  |  |   var utils = require('./utils'); | 
					
						
							| 
									
										
										
										
											2016-08-04 18:49:35 -04:00
										 |  |  |   var RSA = PromiseA.promisifyAll(require('rsa-compat').RSA); | 
					
						
							| 
									
										
										
										
											2016-08-25 22:02:57 +02:00
										 |  |  |   var log = le.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
 | 
					
						
							|  |  |  |       if ((now - le._ipc.acmeUrlsUpdatedAt) < 10 * 60 * 1000) { | 
					
						
							|  |  |  |         return PromiseA.resolve(le._ipc.acmeUrls); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2015-12-15 03:37:39 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-08 11:21:33 -04:00
										 |  |  |       return le.acme.getAcmeUrlsAsync(args.server).then(function (data) { | 
					
						
							| 
									
										
										
										
											2016-08-06 02:05:04 -04:00
										 |  |  |         le._ipc.acmeUrlsUpdatedAt = Date.now(); | 
					
						
							|  |  |  |         le._ipc.acmeUrls = data; | 
					
						
							| 
									
										
										
										
											2015-12-15 03:37:39 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-06 02:05:04 -04:00
										 |  |  |         return le._ipc.acmeUrls; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											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; | 
					
						
							| 
									
										
										
										
											2016-08-08 18:11:25 -04:00
										 |  |  |         var copy = utils.merge(args, le); | 
					
						
							| 
									
										
										
										
											2016-08-08 19:14:53 -04:00
										 |  |  |         var disagreeTos; | 
					
						
							| 
									
										
										
										
											2016-08-08 18:11:25 -04:00
										 |  |  |         args = utils.tplCopy(copy); | 
					
						
							| 
									
										
										
										
											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 () { | 
					
						
							| 
									
										
										
										
											2016-08-08 18:11:25 -04:00
										 |  |  |           var keypairOpts = { public: true, pem: true }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           var promise = le.store.accounts.checkKeypairAsync(args).then(function (keypair) { | 
					
						
							| 
									
										
										
										
											2016-08-09 15:02:10 -04:00
										 |  |  |             if (keypair) { | 
					
						
							|  |  |  |               return RSA.import(keypair); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-08 19:43:31 -04:00
										 |  |  |             if (args.accountKeypair) { | 
					
						
							|  |  |  |               return le.store.accounts.setKeypairAsync(args, RSA.import(args.accountKeypair)); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-08 18:11:25 -04:00
										 |  |  |             return RSA.generateKeypairAsync(args.rsaKeySize, 65537, keypairOpts).then(function (keypair) { | 
					
						
							|  |  |  |               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); | 
					
						
							|  |  |  |               return le.store.accounts.setKeypairAsync(args, keypair); | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |           }); | 
					
						
							| 
									
										
										
										
											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
 | 
					
						
							|  |  |  |             // TODO is this the right place for this?
 | 
					
						
							|  |  |  |             return core.getAcmeUrlsAsync(args).then(function (urls) { | 
					
						
							|  |  |  |               args._acmeUrls = urls; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               return le.acme.registerNewAccountAsync({ | 
					
						
							|  |  |  |                 email: args.email | 
					
						
							|  |  |  |               , newRegUrl: args._acmeUrls.newReg | 
					
						
							|  |  |  |               , agreeToTerms: function (tosUrl, agreeCb) { | 
					
						
							|  |  |  |                   if (true === args.agreeTos || tosUrl === args.agreeTos || tosUrl === le.agreeToTerms) { | 
					
						
							|  |  |  |                     agreeCb(null, tosUrl); | 
					
						
							|  |  |  |                     return; | 
					
						
							|  |  |  |                   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                   // args.email = email;      // already there
 | 
					
						
							|  |  |  |                   // args.domains = domains   // already there
 | 
					
						
							|  |  |  |                   args.tosUrl = tosUrl; | 
					
						
							|  |  |  |                   le.agreeToTerms(args, agreeCb); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |               , accountKeypair: keypair | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               , debug: le.debug || args.debug | 
					
						
							| 
									
										
										
										
											2016-08-08 19:14:53 -04:00
										 |  |  |               }).then(function (receipt) { | 
					
						
							|  |  |  |                 var reg = { | 
					
						
							|  |  |  |                   keypair: keypair | 
					
						
							|  |  |  |                 , receipt: receipt | 
					
						
							|  |  |  |                 , email: args.email | 
					
						
							|  |  |  |                 }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // TODO move templating of arguments to right here?
 | 
					
						
							|  |  |  |                 return le.store.accounts.setAsync(args, reg).then(function (account) { | 
					
						
							|  |  |  |                   // should now have account.id and account.accountId
 | 
					
						
							|  |  |  |                   args.account = account; | 
					
						
							|  |  |  |                   args.accountId = account.id; | 
					
						
							| 
									
										
										
										
											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-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
										 |  |  |     , getAsync: function (args) { | 
					
						
							|  |  |  |         return core.accounts.checkAsync(args).then(function (account) { | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  |           if (account) { | 
					
						
							|  |  |  |             return account; | 
					
						
							|  |  |  |           } else { | 
					
						
							|  |  |  |             return core.accounts.registerAsync(args); | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											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']; | 
					
						
							| 
									
										
										
										
											2016-08-08 18:11:25 -04:00
										 |  |  |         if (!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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  |         var copy = utils.merge(args, le); | 
					
						
							|  |  |  |         args = utils.tplCopy(copy); | 
					
						
							| 
									
										
										
										
											2015-12-20 03:31:05 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  |         return le.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; | 
					
						
							| 
									
										
										
										
											2016-09-13 18:27:24 -06:00
										 |  |  |         var challengeDefaults = le['_challengeOpts_' + (args.challengeType || le.challengeType)] || {}; | 
					
						
							|  |  |  |         var copy = utils.merge(args, challengeDefaults || {}); | 
					
						
							|  |  |  |         copy = utils.merge(copy, le); | 
					
						
							| 
									
										
										
										
											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')); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         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
 | 
					
						
							|  |  |  |           err = new Error("invalid domain name(s): '" + args.domains + "'"); | 
					
						
							|  |  |  |           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
										 |  |  | 
 | 
					
						
							|  |  |  |           var promise = le.store.certificates.checkKeypairAsync(args).then(function (keypair) { | 
					
						
							| 
									
										
										
										
											2016-08-09 15:02:10 -04:00
										 |  |  |             if (keypair) { | 
					
						
							|  |  |  |               return RSA.import(keypair); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-08 19:43:31 -04:00
										 |  |  |             if (args.domainKeypair) { | 
					
						
							|  |  |  |               return le.store.certificates.setKeypairAsync(args, RSA.import(args.domainKeypair)); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-06 14:21:12 -06:00
										 |  |  |             var keypairOpts = { public: true, pem: true }; | 
					
						
							| 
									
										
										
										
											2016-08-07 02:02:02 -04:00
										 |  |  |             return RSA.generateKeypairAsync(args.rsaKeySize, 65537, keypairOpts).then(function (keypair) { | 
					
						
							|  |  |  |               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); | 
					
						
							|  |  |  |               return le.store.certificates.setKeypairAsync(args, keypair); | 
					
						
							|  |  |  |             }); | 
					
						
							| 
									
										
										
										
											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 = { | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  |                 debug: args.debug || le.debug | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               , newAuthzUrl: args._acmeUrls.newAuthz | 
					
						
							|  |  |  |               , newCertUrl: args._acmeUrls.newCert | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               , accountKeypair: RSA.import(account.keypair) | 
					
						
							|  |  |  |               , domainKeypair: domainKeypair | 
					
						
							|  |  |  |               , domains: args.domains | 
					
						
							|  |  |  |               , challengeType: args.challengeType | 
					
						
							| 
									
										
										
										
											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
 | 
					
						
							|  |  |  |               //
 | 
					
						
							|  |  |  |               certReq.setChallenge = function (domain, key, value, done) { | 
					
						
							|  |  |  |                 log(args.debug, "setChallenge called for '" + domain + "'"); | 
					
						
							| 
									
										
										
										
											2016-09-15 00:40:57 -06:00
										 |  |  |                 var copy = utils.merge({ domains: [domain] }, args); | 
					
						
							|  |  |  |                 copy = utils.merge(copy, le); | 
					
						
							| 
									
										
										
										
											2016-08-09 15:02:10 -04:00
										 |  |  |                 utils.tplCopy(copy); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-13 18:08:13 -06:00
										 |  |  |                 // TODO need to save challengeType
 | 
					
						
							|  |  |  |                 le.challenges[args.challengeType].set(copy, domain, key, value, done); | 
					
						
							| 
									
										
										
										
											2016-08-09 15:02:10 -04:00
										 |  |  |               }; | 
					
						
							|  |  |  |               certReq.removeChallenge = function (domain, key, done) { | 
					
						
							| 
									
										
										
										
											2016-09-15 00:40:57 -06:00
										 |  |  |                 log(args.debug, "removeChallenge called for '" + domain + "'"); | 
					
						
							| 
									
										
										
										
											2016-08-09 15:02:10 -04:00
										 |  |  |                 var copy = utils.merge({ domains: [domain] }, le); | 
					
						
							|  |  |  |                 utils.tplCopy(copy); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-13 18:08:13 -06:00
										 |  |  |                 le.challenges[args.challengeType].remove(copy, domain, key, done); | 
					
						
							| 
									
										
										
										
											2016-08-09 15:02:10 -04:00
										 |  |  |               }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-16 13:02:14 -04:00
										 |  |  |               log(args.debug, 'calling le.acme.getCertificateAsync', certReq.domains); | 
					
						
							| 
									
										
										
										
											2016-08-09 15:10:44 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-09 15:02:10 -04:00
										 |  |  |               return le.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) { | 
					
						
							| 
									
										
										
										
											2016-08-13 14:35:19 -06:00
										 |  |  |             // { cert, chain, privkey /*TODO, subject, altnames, issuedAt, expiresAt */ }
 | 
					
						
							| 
									
										
										
										
											2016-08-06 02:05:04 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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; | 
					
						
							|  |  |  |             return le.store.certificates.setAsync(args).then(function () { | 
					
						
							|  |  |  |               return results; | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |           }); | 
					
						
							| 
									
										
										
										
											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!"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // TODO fetch email address / accountId (accountBydomain) if not present
 | 
					
						
							| 
									
										
										
										
											2016-08-09 15:02:10 -04:00
										 |  |  |         // store.config.getAsync(args.domains).then(function (config) { /*...*/ });
 | 
					
						
							| 
									
										
										
										
											2016-08-09 15:38:18 -04:00
										 |  |  |         if (!args.domains || (args.domains.length || 0) <= 2) { | 
					
						
							|  |  |  |           // this is a renewal, therefore we should renewal ALL of the domains
 | 
					
						
							|  |  |  |           // associated with this certificate, unless args.domains is a list larger
 | 
					
						
							|  |  |  |           // than example.com,www.example.com
 | 
					
						
							|  |  |  |           // TODO check www. prefix
 | 
					
						
							|  |  |  |           args.domains = certs.altnames; | 
					
						
							|  |  |  |           if (Array.isArray(certs.domains) && certs.domains.length) { | 
					
						
							|  |  |  |             args.domains = certs.domains; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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) { | 
					
						
							| 
									
										
										
										
											2016-08-09 15:51:42 -04:00
										 |  |  |         return certs.expiresAt - (args.renewWithin || le.renewWithin); | 
					
						
							| 
									
										
										
										
											2016-08-09 15:38:18 -04:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2016-08-06 02:05:04 -04:00
										 |  |  |     , checkAsync: function (args) { | 
					
						
							|  |  |  |         var copy = utils.merge(args, le); | 
					
						
							|  |  |  |         utils.tplCopy(copy); | 
					
						
							| 
									
										
										
										
											2015-12-15 15:21:27 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-07 02:02:02 -04:00
										 |  |  |         // returns pems
 | 
					
						
							| 
									
										
										
										
											2016-08-08 20:26:46 -04:00
										 |  |  |         return le.store.certificates.checkAsync(copy).then(function (cert) { | 
					
						
							|  |  |  |           if (cert) { | 
					
						
							| 
									
										
										
										
											2016-08-16 13:02:14 -04:00
										 |  |  |             log(args.debug, 'checkAsync found existing certificates'); | 
					
						
							| 
									
										
										
										
											2016-08-08 20:26:46 -04:00
										 |  |  |             return utils.attachCertInfo(cert); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-16 13:02:14 -04:00
										 |  |  |           log(args.debug, 'checkAsync failed to find certificates'); | 
					
						
							| 
									
										
										
										
											2016-08-08 20:26:46 -04:00
										 |  |  |           return null; | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											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) { | 
					
						
							|  |  |  |         var copy = utils.merge(args, le); | 
					
						
							| 
									
										
										
										
											2016-08-07 02:02:02 -04:00
										 |  |  |         args = utils.tplCopy(copy); | 
					
						
							| 
									
										
										
										
											2016-08-06 02:05:04 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return core.certificates.checkAsync(args).then(function (certs) { | 
					
						
							| 
									
										
										
										
											2016-08-08 15:17:09 -04:00
										 |  |  |           if (!certs) { | 
					
						
							|  |  |  |             // There is no cert available
 | 
					
						
							|  |  |  |             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
 | 
					
						
							| 
									
										
										
										
											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
										 |  |  | }; |