v2.6.10: updates for simpler store plugin
This commit is contained in:
parent
eb86d4444b
commit
4960604440
16
index.js
16
index.js
|
@ -14,7 +14,7 @@ var util = require('util');
|
|||
function promisifyAllSelf(obj) {
|
||||
if (obj.__promisified) { return obj; }
|
||||
Object.keys(obj).forEach(function (key) {
|
||||
if ('function' === typeof obj[key]) {
|
||||
if ('function' === typeof obj[key] && !/Async$/.test(key)) {
|
||||
obj[key + 'Async'] = util.promisify(obj[key]);
|
||||
}
|
||||
});
|
||||
|
@ -271,13 +271,19 @@ Greenlock.create = function (gl) {
|
|||
}
|
||||
});
|
||||
|
||||
if (gl.store.create) {
|
||||
gl.store = gl.store.create(gl);
|
||||
}
|
||||
try {
|
||||
if (gl.store.create) { gl.store = gl.store.create(gl); }
|
||||
gl.store = promisifyAllSelf(gl.store);
|
||||
gl.store.accounts = promisifyAllSelf(gl.store.accounts);
|
||||
gl.store.certificates = promisifyAllSelf(gl.store.certificates);
|
||||
gl._storeOpts = gl.store.getOptions();
|
||||
gl._storeOpts = gl.store.options || gl.store.getOptions();
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
console.error("\nPROBABLE CAUSE:\n"
|
||||
+ "\tYour le-store module should have a create function and return { options, accounts, certificates }\n");
|
||||
process.exit(18);
|
||||
return;
|
||||
}
|
||||
Object.keys(gl._storeOpts).forEach(function (key) {
|
||||
if (!(key in gl)) {
|
||||
gl[key] = gl._storeOpts[key];
|
||||
|
|
109
lib/core.js
109
lib/core.js
|
@ -45,6 +45,7 @@ module.exports.create = function (gl) {
|
|||
return PromiseA.resolve(gl._ipc.acmeUrls);
|
||||
}
|
||||
|
||||
// TODO acme-v2/nocompat
|
||||
return gl.acme.getAcmeUrlsAsync(args.server).then(function (data) {
|
||||
gl._ipc.acmeUrlsUpdatedAt = Date.now();
|
||||
gl._ipc.acmeUrls = data;
|
||||
|
@ -68,6 +69,8 @@ module.exports.create = function (gl) {
|
|||
var copy = utils.merge(args, gl);
|
||||
var disagreeTos;
|
||||
args = utils.tplCopy(copy);
|
||||
if (!args.account) { args.account = {}; }
|
||||
if ('object' === typeof args.account && !args.account.id) { args.account.id = args.accountId || args.email || ''; }
|
||||
|
||||
disagreeTos = (!args.agreeTos && 'undefined' !== typeof args.agreeTos);
|
||||
if (!args.email || disagreeTos || (parseInt(args.rsaKeySize, 10) < 2048)) {
|
||||
|
@ -80,30 +83,45 @@ module.exports.create = function (gl) {
|
|||
}
|
||||
|
||||
return utils.testEmail(args.email).then(function () {
|
||||
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 newKeypair = true;
|
||||
var accountKeypair;
|
||||
var promise = gl.store.accounts.checkKeypairAsync(args).then(function (keypair) {
|
||||
if (keypair) {
|
||||
return RSA.import(keypair);
|
||||
// TODO keypairs
|
||||
newKeypair = false;
|
||||
accountKeypair = RSA.import(keypair);
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.accountKeypair) {
|
||||
return gl.store.accounts.setKeypairAsync(args, RSA.import(args.accountKeypair));
|
||||
// TODO keypairs
|
||||
accountKeypair = RSA.import(args.accountKeypair);
|
||||
return;
|
||||
}
|
||||
|
||||
var keypairOpts = { bitlen: args.rsaKeySize, exp: 65537, public: true, pem: true };
|
||||
return RSA.generateKeypairAsync(keypairOpts).then(function (keypair) {
|
||||
// TODO keypairs
|
||||
return (args.generateKeypair||RSA.generateKeypairAsync)(keypairOpts).then(function (keypair) {
|
||||
keypair.privateKeyPem = RSA.exportPrivatePem(keypair);
|
||||
keypair.publicKeyPem = RSA.exportPublicPem(keypair);
|
||||
keypair.privateKeyJwk = RSA.exportPrivateJwk(keypair);
|
||||
return gl.store.accounts.setKeypairAsync(args, keypair);
|
||||
accountKeypair = keypair;
|
||||
});
|
||||
}).then(function () {
|
||||
return accountKeypair;
|
||||
});
|
||||
|
||||
return promise.then(function (keypair) {
|
||||
// Note: the ACME urls are always fetched fresh on purpose
|
||||
// TODO is this the right place for this?
|
||||
// TODO acme-v2/nocompat
|
||||
return core.getAcmeUrlsAsync(args).then(function (urls) {
|
||||
args._acmeUrls = urls;
|
||||
|
||||
// TODO acme-v2/nocompat
|
||||
return gl.acme.registerNewAccountAsync({
|
||||
email: args.email
|
||||
, newRegUrl: args._acmeUrls.newReg
|
||||
|
@ -131,27 +149,39 @@ module.exports.create = function (gl) {
|
|||
, newAuthzUrl: args._acmeUrls.newAuthz
|
||||
};
|
||||
|
||||
return gl.store.accounts.setKeypairAsync(args, keypair).then(function () {
|
||||
// TODO move templating of arguments to right here?
|
||||
if (!gl.store.accounts.setAsync) { return PromiseA.resolve({ keypair: keypair }); }
|
||||
return gl.store.accounts.setAsync(args, reg).then(function (account) {
|
||||
// should now have account.id and account.accountId
|
||||
args.account = account;
|
||||
args.accountId = account.id;
|
||||
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;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Accounts
|
||||
// (only used for keypair)
|
||||
, getAsync: function (args) {
|
||||
return core.accounts.checkAsync(args).then(function (account) {
|
||||
if (account) {
|
||||
return account;
|
||||
} else {
|
||||
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 }; }
|
||||
return core.accounts.registerAsync(args);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -166,7 +196,12 @@ module.exports.create = function (gl) {
|
|||
|
||||
var copy = utils.merge(args, gl);
|
||||
args = utils.tplCopy(copy);
|
||||
if (!args.account) { args.account = {}; }
|
||||
if ('object' === typeof args.account && !args.account.id) { args.account.id = args.accountId || args.email || ''; }
|
||||
|
||||
// we can re-register the same account until we're blue in the face and it's all the same
|
||||
// of course, we can also skip the lookup if we do store the account, but whatever
|
||||
if (!gl.store.accounts.checkAsync) { return null; }
|
||||
return gl.store.accounts.checkAsync(args).then(function (account) {
|
||||
|
||||
if (!account) {
|
||||
|
@ -243,22 +278,35 @@ module.exports.create = function (gl) {
|
|||
return core.accounts.getAsync(args).then(function (account) {
|
||||
args.account = account;
|
||||
|
||||
|
||||
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;
|
||||
// This has been done in the getAsync already, so we skip it here
|
||||
// if approveDomains doesn't set subject, we set it here
|
||||
//args.subject = args.subject || args.domains[0];
|
||||
var promise = gl.store.certificates.checkKeypairAsync(args).then(function (keypair) {
|
||||
if (keypair) {
|
||||
return RSA.import(keypair);
|
||||
domainKeypair = RSA.import(keypair);
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.domainKeypair) {
|
||||
return gl.store.certificates.setKeypairAsync(args, RSA.import(args.domainKeypair));
|
||||
domainKeypair = RSA.import(args.domainKeypair);
|
||||
return;
|
||||
}
|
||||
|
||||
var keypairOpts = { bitlen: args.rsaKeySize, exp: 65537, public: true, pem: true };
|
||||
return RSA.generateKeypairAsync(keypairOpts).then(function (keypair) {
|
||||
return (args.generateKeypair||RSA.generateKeypairAsync)(keypairOpts).then(function (keypair) {
|
||||
keypair.privateKeyPem = RSA.exportPrivatePem(keypair);
|
||||
keypair.publicKeyPem = RSA.exportPublicPem(keypair);
|
||||
keypair.privateKeyJwk = RSA.exportPrivateJwk(keypair);
|
||||
return gl.store.certificates.setKeypairAsync(args, keypair);
|
||||
domainKeypair = keypair;
|
||||
});
|
||||
}).then(function () {
|
||||
return domainKeypair;
|
||||
});
|
||||
|
||||
return promise.then(function (domainKeypair) {
|
||||
|
@ -312,20 +360,28 @@ module.exports.create = function (gl) {
|
|||
|
||||
log(args.debug, 'calling greenlock.acme.getCertificateAsync', certReq.domains);
|
||||
|
||||
// TODO acme-v2/nocompat
|
||||
return gl.acme.getCertificateAsync(certReq).then(utils.attachCertInfo);
|
||||
});
|
||||
}).then(function (results) {
|
||||
//var requested = {};
|
||||
//var issued = {};
|
||||
// { cert, chain, privkey /*TODO, subject, altnames, issuedAt, expiresAt */ }
|
||||
|
||||
// args.certs.privkey = RSA.exportPrivatePem(options.domainKeypair);
|
||||
args.certs = results;
|
||||
// args.pems is deprecated
|
||||
args.pems = results;
|
||||
// This has been done in the getAsync already, so we skip it here
|
||||
// if approveDomains doesn't set subject, we set it here
|
||||
//args.subject = args.subject || args.domains[0];
|
||||
return gl.store.certificates.setKeypairAsync(args, domainKeypair).then(function () {
|
||||
return gl.store.certificates.setAsync(args).then(function () {
|
||||
return results;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
// Certificates
|
||||
, renewAsync: function (args, certs) {
|
||||
|
@ -377,13 +433,19 @@ module.exports.create = function (gl) {
|
|||
}
|
||||
, checkAsync: function (args) {
|
||||
var copy = utils.merge(args, gl);
|
||||
utils.tplCopy(copy);
|
||||
// if approveDomains doesn't set subject, we set it here
|
||||
copy.subject = copy.subject || copy.domains[0];
|
||||
args = utils.tplCopy(copy);
|
||||
|
||||
// returns pems
|
||||
return gl.store.certificates.checkAsync(copy).then(function (cert) {
|
||||
return gl.store.certificates.checkAsync(args).then(function (cert) {
|
||||
if (cert) {
|
||||
cert = utils.attachCertInfo(cert);
|
||||
if (utils.certHasDomain(cert, args.domain)) {
|
||||
log(args.debug, 'checkAsync found existing certificates');
|
||||
return utils.attachCertInfo(cert);
|
||||
return cert;
|
||||
}
|
||||
log(args.debug, 'checkAsync found mismatched / incomplete certificates');
|
||||
}
|
||||
|
||||
log(args.debug, 'checkAsync failed to find certificates');
|
||||
|
@ -393,10 +455,17 @@ module.exports.create = function (gl) {
|
|||
// Certificates
|
||||
, getAsync: function (args) {
|
||||
var copy = utils.merge(args, gl);
|
||||
// if approveDomains doesn't set subject, we set it here
|
||||
copy.subject = copy.subject || copy.domains[0];
|
||||
args = utils.tplCopy(copy);
|
||||
|
||||
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");
|
||||
}
|
||||
return core.certificates.checkAsync(args).then(function (certs) {
|
||||
if (!certs) {
|
||||
if (certs) { certs = utils.attachCertInfo(certs); }
|
||||
if (!certs || !utils.certHasDomain(certs, args.domain)) {
|
||||
// There is no cert available
|
||||
if (false !== args.securityUpdates && !args._communityMemberAdded) {
|
||||
try {
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
'use strict';
|
||||
|
||||
var utils = require('./utils.js')
|
||||
var cert = { subject: 'example.com', altnames: ['*.bar.com','foo.net'] };
|
||||
if (utils.certHasDomain(cert, 'bad.com')) {
|
||||
throw new Error("allowed bad domain");
|
||||
}
|
||||
if (!utils.certHasDomain(cert, 'example.com')) {
|
||||
throw new Error("missed subject");
|
||||
}
|
||||
if (utils.certHasDomain(cert, 'bar.com')) {
|
||||
throw new Error("allowed bad (missing) sub");
|
||||
}
|
||||
if (!utils.certHasDomain(cert, 'foo.bar.com')) {
|
||||
throw new Error("didn't allow valid wildcarded-domain");
|
||||
}
|
||||
if (utils.certHasDomain(cert, 'dub.foo.bar.com')) {
|
||||
throw new Error("allowed sub-sub domain");
|
||||
}
|
||||
if (!utils.certHasDomain(cert, 'foo.net')) {
|
||||
throw new Error("missed altname");
|
||||
}
|
||||
|
||||
console.info("PASSED");
|
20
lib/utils.js
20
lib/utils.js
|
@ -9,9 +9,7 @@ var promisify = (require('util').promisify || require('bluebird').promisify);
|
|||
var dnsResolveMxAsync = promisify(require('dns').resolveMx);
|
||||
|
||||
module.exports.attachCertInfo = function (results) {
|
||||
// XXX Note: Parsing the certificate info comes at a great cost (~500kb)
|
||||
var getCertInfo = require('cert-info').info;
|
||||
var certInfo = getCertInfo(results.cert);
|
||||
var certInfo = require('cert-info').info(results.cert);
|
||||
|
||||
// subject, altnames, issuedAt, expiresAt
|
||||
Object.keys(certInfo).forEach(function (key) {
|
||||
|
@ -21,6 +19,20 @@ module.exports.attachCertInfo = function (results) {
|
|||
return results;
|
||||
};
|
||||
|
||||
module.exports.certHasDomain = function (certInfo, _domain) {
|
||||
var names = (certInfo.altnames || []).slice(0);
|
||||
names.push(certInfo.subject);
|
||||
return names.some(function (name) {
|
||||
var domain = _domain.toLowerCase();
|
||||
name = name.toLowerCase();
|
||||
if ('*.' === name.substr(0, 2)) {
|
||||
name = name.substr(2);
|
||||
domain = domain.split('.').slice(1).join('.');
|
||||
}
|
||||
return name === domain;
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.isValidDomain = function (domain) {
|
||||
if (re.test(domain)) {
|
||||
return domain;
|
||||
|
@ -58,7 +70,7 @@ module.exports.tplCopy = function (copy) {
|
|||
var tplKeys;
|
||||
|
||||
copy.hostnameGet = function (copy) {
|
||||
return (copy.domains || [])[0] || copy.domain;
|
||||
return copy.subject || (copy.domains || [])[0] || copy.domain;
|
||||
};
|
||||
|
||||
Object.keys(copy).forEach(function (key) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "greenlock",
|
||||
"version": "2.6.9",
|
||||
"version": "2.6.10",
|
||||
"description": "Let's Encrypt for node.js on npm",
|
||||
"main": "index.js",
|
||||
"files": [
|
||||
|
|
Loading…
Reference in New Issue