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

View File

@ -13,7 +13,7 @@ function createAccount(args, handlers) {
// TODO support ECDSA
// 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 } */
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
// just one email address, that could get crazy.
// We should have a folder per email and list
// each account as a file in the folder
// TODO
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.createAccount = createAccount;

View File

@ -45,12 +45,23 @@ module.exports.merge = function merge(defaults, args) {
return copy;
};
module.exports.tplHostname = function merge(hostname, copy) {
module.exports.tplCopy = function merge(copy) {
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) {
if ('string' === typeof copy[key]) {
copy[key] = copy[key].replace(':hostname', hostname).replace(':host', hostname);
Object.keys(tpls).sort(function (a, b) {
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 merge = require('./common').merge;
var tplHostname = require('./common').tplHostname;
var tplCopy = require('./common').tplCopy;
var fetchFromConfigLiveDir = require('./common').fetchFromDisk;
var ipc = {}; // in-process cache
@ -34,7 +34,7 @@ function getAcmeUrls(args) {
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) {
console.log("get certificate");
}
@ -58,7 +58,7 @@ function getCertificateAsync(account, args, defaults, handlers) {
//
, setChallenge: function (domain, key, value, done) {
var copy = merge(defaults, { domains: [domain] });
tplHostname(domain, copy);
tplCopy(copy);
args.domains = [domain];
args.webrootPath = args.webrootPath;
@ -74,7 +74,7 @@ function getCertificateAsync(account, args, defaults, handlers) {
}
, removeChallenge: function (domain, key, done) {
var copy = merge(defaults, { domains: [domain] });
tplHostname(domain, copy);
tplCopy(copy);
if (3 === handlers.removeChallenge.length) {
handlers.removeChallenge(copy, key, done);
@ -204,46 +204,7 @@ function getCertificateAsync(account, args, defaults, handlers) {
});
}
function registerWithAcme(args, defaults, handlers) {
var pyconf = PromiseA.promisifyAll(require('pyconf'));
var server = args.server;
var acmeHostname = require('url').parse(server).hostname;
var configDir = args.configDir;
args.renewalPath = args.renewalPath || path.join(configDir, 'renewal', args.domains[0] + '.conf');
args.accountsDir = args.accountsDir || path.join(configDir, 'accounts', acmeHostname, 'directory');
return pyconf.readFileAsync(args.renewalPath).then(function (renewal) {
var accountId = renewal.account;
renewal = renewal.account;
return accountId;
}, function (err) {
if ("ENOENT" === err.code) {
return Accounts.getAccountByEmail(args, handlers);
}
return PromiseA.reject(err);
}).then(function (accountId) {
// Note: the ACME urls are always fetched fresh on purpose
return getAcmeUrls(args).then(function (urls) {
args._acmeUrls = urls;
if (accountId) {
return Accounts.getAccount(accountId, args, handlers);
} else {
return Accounts.createAccount(args, handlers);
}
});
}).then(function (account) {
/*
if (renewal.account !== account) {
// the account has become corrupt, re-register
return;
}
*/
//console.log(account);
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)
@ -269,6 +230,56 @@ function registerWithAcme(args, defaults, handlers) {
return certs;
}
});
};
function getOrCreateAcmeAccount(args, defaults, handlers) {
var pyconf = PromiseA.promisifyAll(require('pyconf'));
var server = args.server;
var acmeHostname = require('url').parse(server).hostname;
var configDir = args.configDir;
args.renewalPath = args.renewalPath || path.join(configDir, 'renewal', args.domains[0] + '.conf');
args.accountsDir = args.accountsDir || path.join(configDir, 'accounts', acmeHostname, 'directory');
return pyconf.readFileAsync(args.renewalPath).then(function (renewal) {
var accountId = renewal.account;
renewal = renewal.account;
return accountId;
}, function (err) {
if ("ENOENT" === err.code) {
if (args.debug) {
console.log("[LE] try email");
}
return Accounts.getAccountIdByEmail(args, handlers);
}
return PromiseA.reject(err);
}).then(function (accountId) {
// Note: the ACME urls are always fetched fresh on purpose
return getAcmeUrls(args).then(function (urls) {
args._acmeUrls = urls;
if (accountId) {
if (args.debug) {
console.log('[LE] use account');
}
return Accounts.getAccount(accountId, args, handlers);
} else {
if (args.debug) {
console.log('[LE] create account');
}
return Accounts.createAccount(args, handlers);
}
});
}).then(function (account) {
/*
if (renewal.account !== account) {
// the account has become corrupt, re-register
return;
}
*/
});
/*
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:
args.accountsDir = args.accountsDir || ':config/accounts/:server';
copy = merge(args, defaults);
tplHostname(args.domains[0], copy);
tplCopy(copy);
if (args.debug) {
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) {
var copy = merge(args, defaults);
tplHostname(args.domains[0], copy);
tplCopy(copy);
if (args.debug) {
console.log('[LE DEBUG] fetch domains', copy);