multiple bugfixes and enhancements for accounts
This commit is contained in:
parent
3a1f66f9e2
commit
dad8249d5f
12
index.js
12
index.js
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
85
lib/core.js
85
lib/core.js
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue