mirror of
				https://github.com/therootcompany/greenlock.js.git
				synced 2024-11-16 17:29:00 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			255 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			255 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict';
 | |
| 
 | |
| var LE = require('../');
 | |
| var ipc = {}; // in-process cache
 | |
| 
 | |
| module.exports.create = function (defaults, handlers, backend) {
 | |
|   defaults.server = defaults.server || LE.liveServer;
 | |
|   handlers.merge = require('./common').merge;
 | |
|   handlers.tplCopy = require('./common').tplCopy;
 | |
| 
 | |
|   var PromiseA = require('bluebird');
 | |
|   var RSA = PromiseA.promisifyAll(require('rsa-compat').RSA);
 | |
|   var LeCore = PromiseA.promisifyAll(require('letiny-core'));
 | |
|   var crypto = require('crypto');
 | |
| 
 | |
|   function createAccount(args, handlers) {
 | |
|     // arg.rsaBitLength args.rsaExponent
 | |
|     return RSA.generateKeypairAsync(args.rsaKeySize || 2048, 65537, { public: true, pem: true }).then(function (keypair) {
 | |
| 
 | |
|       return LeCore.registerNewAccountAsync({
 | |
|         email: args.email
 | |
|       , newRegUrl: args._acmeUrls.newReg
 | |
|       , agreeToTerms: function (tosUrl, agree) {
 | |
|           // args.email = email; // already there
 | |
|           args.tosUrl = tosUrl;
 | |
|           handlers.agreeToTerms(args, agree);
 | |
|         }
 | |
|       , accountKeypair: keypair
 | |
| 
 | |
|       , debug: defaults.debug || args.debug || handlers.debug
 | |
|       }).then(function (body) {
 | |
|         // TODO XXX use sha256 (the python client uses md5)
 | |
|         // TODO ssh fingerprint (noted on rsa-compat issues page, I believe)
 | |
|         keypair.publicKeyMd5 = crypto.createHash('md5').update(RSA.exportPublicPem(keypair)).digest('hex');
 | |
|         keypair.publicKeySha256 = crypto.createHash('sha256').update(RSA.exportPublicPem(keypair)).digest('hex');
 | |
| 
 | |
|         var accountId = keypair.publicKeyMd5;
 | |
|         var regr = { body: body };
 | |
|         var account = {};
 | |
| 
 | |
|         args.accountId = accountId;
 | |
| 
 | |
|         account.keypair = keypair;
 | |
|         account.regr = regr;
 | |
|         account.accountId = accountId;
 | |
|         account.id = accountId;
 | |
| 
 | |
|         args.account = account;
 | |
| 
 | |
|         return backend.setAccountAsync(args, account).then(function () {
 | |
|           return account;
 | |
|         });
 | |
|       });
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   function getAcmeUrls(args) {
 | |
|     var now = Date.now();
 | |
| 
 | |
|     // TODO check response header on request for cache time
 | |
|     if ((now - ipc.acmeUrlsUpdatedAt) < 10 * 60 * 1000) {
 | |
|       return PromiseA.resolve(ipc.acmeUrls);
 | |
|     }
 | |
| 
 | |
|     return LeCore.getAcmeUrlsAsync(args.server).then(function (data) {
 | |
|       ipc.acmeUrlsUpdatedAt = Date.now();
 | |
|       ipc.acmeUrls = data;
 | |
| 
 | |
|       return ipc.acmeUrls;
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   function getCertificateAsync(args, defaults, handlers) {
 | |
|     function log() {
 | |
|       if (args.debug || defaults.debug) {
 | |
|         console.log.apply(console, arguments);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     var account = args.account;
 | |
|     var promise;
 | |
|     var keypairOpts = { public: true, pem: true };
 | |
| 
 | |
|     promise = backend.getPrivatePem(args).then(function (pem) {
 | |
|       return RSA.import({ privateKeyPem: pem });
 | |
|     }, function (/*err*/) {
 | |
|       return RSA.generateKeypairAsync(args.rsaKeySize, 65537, keypairOpts).then(function (keypair) {
 | |
|         keypair.privateKeyPem = RSA.exportPrivatePem(keypair);
 | |
|         keypair.privateKeyJwk = RSA.exportPrivateJwk(keypair);
 | |
|         return backend.setPrivatePem(args, keypair);
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     return promise.then(function (domainKeypair) {
 | |
|       log("[le/core.js] get certificate");
 | |
| 
 | |
|       args.domainKeypair = domainKeypair;
 | |
|       //args.registration = domainKey;
 | |
| 
 | |
|       return LeCore.getCertificateAsync({
 | |
|         debug: args.debug
 | |
| 
 | |
|       , newAuthzUrl: args._acmeUrls.newAuthz
 | |
|       , newCertUrl: args._acmeUrls.newCert
 | |
| 
 | |
|       , accountKeypair: RSA.import(account.keypair)
 | |
|       , domainKeypair: domainKeypair
 | |
|       , domains: args.domains
 | |
| 
 | |
|         //
 | |
|         // 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)
 | |
|         //
 | |
|       , setChallenge: function (domain, key, value, done) {
 | |
|           var copy = handlers.merge({ domains: [domain] }, defaults);
 | |
|           handlers.tplCopy(copy);
 | |
| 
 | |
|           args.domains = [domain];
 | |
|           //args.domains = args.domains || [domain];
 | |
|           if (4 === handlers.setChallenge.length) {
 | |
|             console.warn('[WARNING] deprecated use. Define setChallenge as function (opts, domain, key, val, cb) { }');
 | |
|             handlers.setChallenge(copy, key, value, done);
 | |
|           }
 | |
|           else if (5 === handlers.setChallenge.length) {
 | |
|             handlers.setChallenge(copy, domain, key, value, done);
 | |
|           }
 | |
|           else {
 | |
|             done(new Error("handlers.setChallenge receives the wrong number of arguments"));
 | |
|           }
 | |
|         }
 | |
|       , removeChallenge: function (domain, key, done) {
 | |
|           var copy = handlers.merge({ domains: [domain] }, defaults);
 | |
|           handlers.tplCopy(copy);
 | |
| 
 | |
|           if (3 === handlers.removeChallenge.length) {
 | |
|             handlers.removeChallenge(copy, key, done);
 | |
|           }
 | |
|           else if (4 === handlers.removeChallenge.length) {
 | |
|             handlers.removeChallenge(copy, domain, key, done);
 | |
|           }
 | |
|           else {
 | |
|             done(new Error("handlers.removeChallenge receives the wrong number of arguments"));
 | |
|           }
 | |
|         }
 | |
|       });
 | |
|     }).then(function (results) {
 | |
|       // { cert, chain, fullchain, privkey }
 | |
|       args.pems = results;
 | |
|       return backend.setRegistration(args, defaults, handlers);
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   function getOrCreateDomainCertificate(args, defaults, handlers) {
 | |
|     if (args.duplicate) {
 | |
|       // we're forcing a refresh via 'dupliate: true'
 | |
|       return getCertificateAsync(args, defaults, handlers);
 | |
|     }
 | |
| 
 | |
|     return backend.getRegistration(args).then(function (certs) {
 | |
|       var halfLife = (certs.expiresAt - certs.issuedAt) / 2;
 | |
| 
 | |
|       if (!certs || (Date.now() - certs.issuedAt) > halfLife) {
 | |
|         // There is no cert available
 | |
|         // Or the cert is more than half-expired
 | |
|         return getCertificateAsync(args, defaults, handlers);
 | |
|       }
 | |
| 
 | |
|       return PromiseA.reject(new Error(
 | |
|           "[ERROR] Certificate issued at '"
 | |
|         + new Date(certs.issuedAt).toISOString() + "' and expires at '"
 | |
|         + new Date(certs.expiresAt).toISOString() + "'. Ignoring renewal attempt until half-life at '"
 | |
|         + new Date(certs.issuedA + halfLife).toISOString() + "'. Set { duplicate: true } to force."
 | |
|       ));
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   // returns 'account' from lib/accounts { meta, regr, keypair, accountId (id) }
 | |
|   function getOrCreateAcmeAccount(args, defaults, handlers) {
 | |
|     function log() {
 | |
|       if (args.debug) {
 | |
|         console.log.apply(console, arguments);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return backend.getAccountId(args).then(function (accountId) {
 | |
| 
 | |
|       // Note: the ACME urls are always fetched fresh on purpose
 | |
|       return getAcmeUrls(args).then(function (urls) {
 | |
|         args._acmeUrls = urls;
 | |
| 
 | |
|         if (accountId) {
 | |
|           log('[le/core.js] use account');
 | |
| 
 | |
|           args.accountId = accountId;
 | |
|           return Accounts.getAccount(args, handlers);
 | |
|         } else {
 | |
|           log('[le/core.js] create account');
 | |
|           return Accounts.createAccount(args, handlers);
 | |
|         }
 | |
|       });
 | |
|     }).then(function (account) {
 | |
|       /*
 | |
|       if (renewal.account !== account) {
 | |
|         // the account has become corrupt, re-register
 | |
|         return;
 | |
|       }
 | |
|       */
 | |
|       log('[le/core.js] created account');
 | |
|       return account;
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   var wrapped = {
 | |
|    registerAsync: function (args) {
 | |
|       var copy = handlers.merge(args, defaults);
 | |
|       handlers.tplCopy(copy);
 | |
| 
 | |
|       return getOrCreateAcmeAccount(copy, defaults, handlers).then(function (account) {
 | |
|         copy.account = account;
 | |
| 
 | |
|         return backend.getOrCreateRenewal(copy).then(function (pyobj) {
 | |
| 
 | |
|           copy.pyobj = pyobj;
 | |
|           return getOrCreateDomainCertificate(copy, defaults, handlers);
 | |
|         });
 | |
|       }).then(function (result) {
 | |
|         return result;
 | |
|       }, function (err) {
 | |
|         return PromiseA.reject(err);
 | |
|       });
 | |
|     }
 | |
|   , getOrCreateAccount: function (args) {
 | |
|       // TODO
 | |
|       keypair.privateKeyPem = RSA.exportPrivatePem(keypair);
 | |
|       keypair.publicKeyPem = RSA.exportPublicPem(keypair);
 | |
|       return createAccount(args, handlers);
 | |
|     }
 | |
|   , configureAsync: function (hargs) {
 | |
|       var copy = merge(hargs, defaults);
 | |
|       tplCopy(copy);
 | |
| 
 | |
|       return getOrCreateAcmeAccount(copy, defaults, handlers).then(function (account) {
 | |
|         copy.account = account;
 | |
|         return backend.getOrCreateRenewal(copy);
 | |
|       });
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   return wrapped;
 | |
| };
 |