multiple bugfixes and enhancements for accounts

This commit is contained in:
AJ ONeal 2015-12-20 00:27:48 +00:00
parent 3a1f66f9e2
commit dad8249d5f
4 changed files with 129 additions and 47 deletions

View File

@ -5,7 +5,7 @@
var PromiseA = require('bluebird'); var PromiseA = require('bluebird');
var leCore = require('letiny-core'); var leCore = require('letiny-core');
var merge = require('./lib/common').merge; var merge = require('./lib/common').merge;
var tplHostname = require('./lib/common').tplHostname; var tplCopy = require('./lib/common').tplCopy;
var LE = module.exports; var LE = module.exports;
LE.productionServerUrl = leCore.productionServerUrl; LE.productionServerUrl = leCore.productionServerUrl;
@ -58,7 +58,7 @@ LE.create = function (defaults, handlers, backend) {
var getChallenge = require('./lib/default-handlers').getChallenge; var getChallenge = require('./lib/default-handlers').getChallenge;
var copy = merge(defaults, { domains: [hostname] }); var copy = merge(defaults, { domains: [hostname] });
tplHostname(hostname, copy); tplCopy(copy);
defaultos.domains = [hostname]; defaultos.domains = [hostname];
if (3 === getChallenge.length) { if (3 === getChallenge.length) {
@ -158,9 +158,13 @@ LE.create = function (defaults, handlers, backend) {
return; return;
} }
//console.log("[NLE]: begin registration"); if (args.debug) {
console.log("[NLE]: begin registration");
}
return backend.registerAsync(copy).then(function (pems) { return backend.registerAsync(copy).then(function (pems) {
//console.log("[NLE]: end registration"); if (args.debug) {
console.log("[NLE]: end registration");
}
cb(null, pems); cb(null, pems);
//return le.fetch(args, cb); //return le.fetch(args, cb);
}, cb); }, cb);

View File

@ -13,7 +13,7 @@ function createAccount(args, handlers) {
// TODO support ECDSA // TODO support ECDSA
// arg.rsaBitLength args.rsaExponent // arg.rsaBitLength args.rsaExponent
return leCrypto.generateRsaKeypairAsync(args.rsaBitLength, args.rsaExponent).then(function (pems) { return leCrypto.generateRsaKeypairAsync(args.rsaKeySize, 65537).then(function (pems) {
/* pems = { privateKeyPem, privateKeyJwk, publicKeyPem, publicKeyMd5, publicKeySha256 } */ /* pems = { privateKeyPem, privateKeyJwk, publicKeyPem, publicKeyMd5, publicKeySha256 } */
return LeCore.registerNewAccountAsync({ return LeCore.registerNewAccountAsync({
@ -117,15 +117,69 @@ function getAccount(accountId, args, handlers) {
}); });
} }
function getAccountByEmail(/*args*/) { function getAccountIdByEmail(args, handlers) {
// If we read 10,000 account directories looking for // If we read 10,000 account directories looking for
// just one email address, that could get crazy. // just one email address, that could get crazy.
// We should have a folder per email and list // We should have a folder per email and list
// each account as a file in the folder // each account as a file in the folder
// TODO // TODO
return PromiseA.resolve(null); var email = args.email;
if ('string' !== typeof email) {
if (args.debug) {
console.log("[LE] No email given");
}
return PromiseA.resolve(null);
}
return fs.readdirAsync(args.accountsDir).then(function (nodes) {
if (args.debug) {
console.log("[LE] arg.accountsDir success");
}
return PromiseA.all(nodes.map(function (node) {
return fs.readFileAsync(path.join(args.accountsDir, node, 'regr.json'), 'utf8').then(function (text) {
var regr = JSON.parse(text);
regr.__accountId = node;
return regr;
});
})).then(function (regrs) {
var accountId;
/*
if (args.debug) {
console.log('read many regrs');
console.log('regrs', regrs);
}
*/
regrs.some(function (regr) {
return regr.body.contact.some(function (contact) {
var match = contact.toLowerCase() === 'mailto:' + email.toLowerCase();
if (match) {
accountId = regr.__accountId;
return true;
}
});
});
if (!accountId) {
return null;
}
return accountId;
});
}).then(function (accountId) {
return accountId;
}, function (err) {
if ('ENOENT' == err.code) {
// ignore error
return null;
}
return PromiseA.reject(err);
});
} }
module.exports.getAccountByEmail = getAccountByEmail; module.exports.getAccountIdByEmail = getAccountIdByEmail;
module.exports.getAccount = getAccount; module.exports.getAccount = getAccount;
module.exports.createAccount = createAccount; module.exports.createAccount = createAccount;

View File

@ -45,12 +45,23 @@ module.exports.merge = function merge(defaults, args) {
return copy; return copy;
}; };
module.exports.tplHostname = function merge(hostname, copy) { module.exports.tplCopy = function merge(copy) {
var homedir = require('homedir')(); var homedir = require('homedir')();
var tpls = {
hostname: (copy.domains || [])[0]
, server: (copy.server || '').replace('https://', '').replace(/(\/)$/, '')
, conf: copy.configDir
, config: copy.configDir
};
Object.keys(copy).forEach(function (key) { Object.keys(copy).forEach(function (key) {
if ('string' === typeof copy[key]) { if ('string' === typeof copy[key]) {
copy[key] = copy[key].replace(':hostname', hostname).replace(':host', hostname); Object.keys(tpls).sort(function (a, b) {
copy[key] = copy[key].replace(homeRe, homedir + path.sep); return b.length - a.length;
}).forEach(function (tplname) {
copy[key] = copy[key].replace(':' + tplname, tpls[tplname]);
copy[key] = copy[key].replace(homeRe, homedir + path.sep);
});
} }
}); });

View File

@ -10,7 +10,7 @@ var leCrypto = PromiseA.promisifyAll(LeCore.leCrypto);
var Accounts = require('./accounts'); var Accounts = require('./accounts');
var merge = require('./common').merge; var merge = require('./common').merge;
var tplHostname = require('./common').tplHostname; var tplCopy = require('./common').tplCopy;
var fetchFromConfigLiveDir = require('./common').fetchFromDisk; var fetchFromConfigLiveDir = require('./common').fetchFromDisk;
var ipc = {}; // in-process cache var ipc = {}; // in-process cache
@ -34,7 +34,7 @@ function getAcmeUrls(args) {
function getCertificateAsync(account, args, defaults, handlers) { function getCertificateAsync(account, args, defaults, handlers) {
return leCrypto.generateRsaKeypairAsync(args.rsaKeySize, args.rsaExponent).then(function (domainKey) { return leCrypto.generateRsaKeypairAsync(args.rsaKeySize, 65537).then(function (domainKey) {
if (args.debug) { if (args.debug) {
console.log("get certificate"); console.log("get certificate");
} }
@ -58,7 +58,7 @@ function getCertificateAsync(account, args, defaults, handlers) {
// //
, setChallenge: function (domain, key, value, done) { , setChallenge: function (domain, key, value, done) {
var copy = merge(defaults, { domains: [domain] }); var copy = merge(defaults, { domains: [domain] });
tplHostname(domain, copy); tplCopy(copy);
args.domains = [domain]; args.domains = [domain];
args.webrootPath = args.webrootPath; args.webrootPath = args.webrootPath;
@ -74,7 +74,7 @@ function getCertificateAsync(account, args, defaults, handlers) {
} }
, removeChallenge: function (domain, key, done) { , removeChallenge: function (domain, key, done) {
var copy = merge(defaults, { domains: [domain] }); var copy = merge(defaults, { domains: [domain] });
tplHostname(domain, copy); tplCopy(copy);
if (3 === handlers.removeChallenge.length) { if (3 === handlers.removeChallenge.length) {
handlers.removeChallenge(copy, key, done); handlers.removeChallenge(copy, key, done);
@ -204,7 +204,35 @@ function getCertificateAsync(account, args, defaults, handlers) {
}); });
} }
function registerWithAcme(args, defaults, handlers) { function getOrCreateDomainCertificate(account, args, defaults, hanlers) {
return fetchFromConfigLiveDir(args).then(function (certs) {
// if nothing, register and save
// if something, check date (don't register unless 30+ days)
// if good, don't bother registering
// (but if we get to the point that we're actually calling
// this function, that shouldn't be the case, right?)
//console.log(certs);
if (!certs) {
// no certs, seems like a good time to get some
return getCertificateAsync(account, args, defaults, handlers);
}
else if (certs.issuedAt > (27 * 24 * 60 * 60 * 1000)) {
// cert is at least 27 days old we can renew that
return getCertificateAsync(account, args, defaults, handlers);
}
else if (args.duplicate) {
// YOLO! I be gettin' fresh certs 'erday! Yo!
return getCertificateAsync(account, args, defaults, handlers);
}
else {
console.warn('[WARN] Ignoring renewal attempt for certificate less than 27 days old. Use args.duplicate to force.');
// We're happy with what we have
return certs;
}
});
};
function getOrCreateAcmeAccount(args, defaults, handlers) {
var pyconf = PromiseA.promisifyAll(require('pyconf')); var pyconf = PromiseA.promisifyAll(require('pyconf'));
var server = args.server; var server = args.server;
var acmeHostname = require('url').parse(server).hostname; var acmeHostname = require('url').parse(server).hostname;
@ -220,18 +248,28 @@ function registerWithAcme(args, defaults, handlers) {
return accountId; return accountId;
}, function (err) { }, function (err) {
if ("ENOENT" === err.code) { if ("ENOENT" === err.code) {
return Accounts.getAccountByEmail(args, handlers); if (args.debug) {
console.log("[LE] try email");
}
return Accounts.getAccountIdByEmail(args, handlers);
} }
return PromiseA.reject(err); return PromiseA.reject(err);
}).then(function (accountId) { }).then(function (accountId) {
// Note: the ACME urls are always fetched fresh on purpose // Note: the ACME urls are always fetched fresh on purpose
return getAcmeUrls(args).then(function (urls) { return getAcmeUrls(args).then(function (urls) {
args._acmeUrls = urls; args._acmeUrls = urls;
if (accountId) { if (accountId) {
if (args.debug) {
console.log('[LE] use account');
}
return Accounts.getAccount(accountId, args, handlers); return Accounts.getAccount(accountId, args, handlers);
} else { } else {
if (args.debug) {
console.log('[LE] create account');
}
return Accounts.createAccount(args, handlers); return Accounts.createAccount(args, handlers);
} }
}); });
@ -242,33 +280,6 @@ function registerWithAcme(args, defaults, handlers) {
return; return;
} }
*/ */
//console.log(account);
return fetchFromConfigLiveDir(args).then(function (certs) {
// if nothing, register and save
// if something, check date (don't register unless 30+ days)
// if good, don't bother registering
// (but if we get to the point that we're actually calling
// this function, that shouldn't be the case, right?)
//console.log(certs);
if (!certs) {
// no certs, seems like a good time to get some
return getCertificateAsync(account, args, defaults, handlers);
}
else if (certs.issuedAt > (27 * 24 * 60 * 60 * 1000)) {
// cert is at least 27 days old we can renew that
return getCertificateAsync(account, args, defaults, handlers);
}
else if (args.duplicate) {
// YOLO! I be gettin' fresh certs 'erday! Yo!
return getCertificateAsync(account, args, defaults, handlers);
}
else {
console.warn('[WARN] Ignoring renewal attempt for certificate less than 27 days old. Use args.duplicate to force.');
// We're happy with what we have
return certs;
}
});
}); });
/* /*
return fs.readdirAsync(accountsDir, function (nodes) { return fs.readdirAsync(accountsDir, function (nodes) {
@ -293,16 +304,18 @@ module.exports.create = function (defaults, handlers) {
// So :config/accounts/:server/directory is *incorrect*, but the following *is* correct: // So :config/accounts/:server/directory is *incorrect*, but the following *is* correct:
args.accountsDir = args.accountsDir || ':config/accounts/:server'; args.accountsDir = args.accountsDir || ':config/accounts/:server';
copy = merge(args, defaults); copy = merge(args, defaults);
tplHostname(args.domains[0], copy); tplCopy(copy);
if (args.debug) { if (args.debug) {
console.log('[LE DEBUG] reg domains', args.domains); console.log('[LE DEBUG] reg domains', args.domains);
} }
return registerWithAcme(copy, defaults, handlers); return getOrCreateAcmeAccount(copy, defaults, handlers).then(function (account) {
return getOrCreateDomainCertificate(account, copy, defaults, handlers);
});
} }
, fetchAsync: function (args) { , fetchAsync: function (args) {
var copy = merge(args, defaults); var copy = merge(args, defaults);
tplHostname(args.domains[0], copy); tplCopy(copy);
if (args.debug) { if (args.debug) {
console.log('[LE DEBUG] fetch domains', copy); console.log('[LE DEBUG] fetch domains', copy);