make Prettier

This commit is contained in:
AJ ONeal 2019-11-02 12:38:12 -06:00
parent de0f4d25b4
commit e0a9fff07d
10 changed files with 501 additions and 470 deletions

7
.prettierrc Normal file
View File

@ -0,0 +1,7 @@
{
"bracketSpacing": true,
"printWidth": 80,
"tabWidth": 4,
"trailingComma": "none",
"useTabs": false
}

View File

@ -4,10 +4,11 @@ A keypair and certificate storage strategy for Greenlock v2.7+ (and v3).
The (much simpler) successor to le-store-certbot. The (much simpler) successor to le-store-certbot.
Works with all ACME (Let's Encrypt) SSL certificate sytles: Works with all ACME (Let's Encrypt) SSL certificate sytles:
* [x] single domains
* [x] multiple domains (SANs, AltNames) - [x] single domains
* [x] wildcards - [x] multiple domains (SANs, AltNames)
* [x] private / localhost domains - [x] wildcards
- [x] private / localhost domains
# Usage # Usage
@ -68,13 +69,21 @@ function approveDomains(opts) {
// Allow only example.com and *.example.com (such as foo.example.com) // Allow only example.com and *.example.com (such as foo.example.com)
// foo.example.com => *.example.com // foo.example.com => *.example.com
var wild = '*.' + opts.domain.split('.').slice(1).join('.'); var wild =
"*." +
opts.domain
.split(".")
.slice(1)
.join(".");
if ('example.com' !== opts.domain && '*.example.com' !== wild) { if ("example.com" !== opts.domain && "*.example.com" !== wild) {
cb(new Error(opts.domain + " is not allowed")); cb(new Error(opts.domain + " is not allowed"));
} }
var result = { subject: 'example.com', altnames: [ 'example.com', '*.example.com' ] }; var result = {
subject: "example.com",
altnames: ["example.com", "*.example.com"]
};
return Promise.resolve(result); return Promise.resolve(result);
} }
``` ```
@ -84,7 +93,9 @@ function approveDomains(opts) {
```js ```js
function approveDomains(opts, certs, cb) { function approveDomains(opts, certs, cb) {
var related = getRelated(opts.domain); var related = getRelated(opts.domain);
if (!related) { cb(new Error(opts.domain + " is not allowed")); }; if (!related) {
cb(new Error(opts.domain + " is not allowed"));
}
opts.subject = related.subject; opts.subject = related.subject;
opts.domains = related.domains; opts.domains = related.domains;
@ -96,15 +107,22 @@ function approveDomains(opts, certs, cb) {
```js ```js
function getRelated(domain) { function getRelated(domain) {
var related; var related;
var wild = '*.' + domain.split('.').slice(1).join('.'); var wild =
if (Object.keys(allAllowedDomains).some(function (k) { "*." +
domain
.split(".")
.slice(1)
.join(".");
if (
Object.keys(allAllowedDomains).some(function(k) {
return allAllowedDomains[k].some(function(name) { return allAllowedDomains[k].some(function(name) {
if (domain === name || wild === name) { if (domain === name || wild === name) {
related = { subject: k, altnames: allAllowedDomains[k] }; related = { subject: k, altnames: allAllowedDomains[k] };
return true; return true;
} }
}); });
})) { })
) {
return related; return related;
} }
} }
@ -112,7 +130,7 @@ function getRelated(domain) {
```js ```js
var allAllowedDomains = { var allAllowedDomains = {
'example.com': ['example.com', '*.example.com'] "example.com": ["example.com", "*.example.com"],
, 'example.net': ['example.net', '*.example.net'] "example.net": ["example.net", "*.example.net"]
} };
``` ```

View File

@ -1,15 +1,15 @@
'use strict'; "use strict";
var accounts = module.exports; var accounts = module.exports;
var store = accounts; var store = accounts;
var U = require('./utils.js'); var U = require("./utils.js");
var fs = require('fs'); var fs = require("fs");
var path = require('path'); var path = require("path");
var PromiseA = require('./promise.js'); var PromiseA = require("./promise.js");
var readFileAsync = PromiseA.promisify(fs.readFile); var readFileAsync = PromiseA.promisify(fs.readFile);
var writeFileAsync = PromiseA.promisify(fs.writeFile); var writeFileAsync = PromiseA.promisify(fs.writeFile);
var mkdirpAsync = PromiseA.promisify(require('@root/mkdirp')); var mkdirpAsync = PromiseA.promisify(require("@root/mkdirp"));
// Implement if you need the ACME account metadata elsewhere in the chain of events // Implement if you need the ACME account metadata elsewhere in the chain of events
//store.accounts.check = function (opts) { //store.accounts.check = function (opts) {
@ -25,14 +25,14 @@ accounts.checkKeypair = function(opts) {
var id = var id =
(opts.account && opts.account.id) || (opts.account && opts.account.id) ||
(opts.subscriberEmail || opts.email) || (opts.subscriberEmail || opts.email) ||
'single-user'; "single-user";
//console.log('accounts.checkKeypair for', id); //console.log('accounts.checkKeypair for', id);
var pathname = path.join( var pathname = path.join(
accountsDir(store, opts), accountsDir(store, opts),
sanitizeFilename(id) + '.json' sanitizeFilename(id) + ".json"
); );
return readFileAsync(U._tameWild(pathname, opts.subject), 'utf8') return readFileAsync(U._tameWild(pathname, opts.subject), "utf8")
.then(function(blob) { .then(function(blob) {
// keypair can treated as an opaque object and just passed along, // keypair can treated as an opaque object and just passed along,
// but just to show you what it is... // but just to show you what it is...
@ -48,7 +48,7 @@ accounts.checkKeypair = function(opts) {
*/ */
}) })
.catch(function(err) { .catch(function(err) {
if ('ENOENT' === err.code) { if ("ENOENT" === err.code) {
return null; return null;
} }
throw err; throw err;
@ -61,7 +61,7 @@ accounts.checkKeypair = function(opts) {
// Return null (not undefined) on success, or throw on error // Return null (not undefined) on success, or throw on error
accounts.setKeypair = function(opts) { accounts.setKeypair = function(opts) {
//console.log('accounts.setKeypair for', opts.account, opts.email, opts.keypair); //console.log('accounts.setKeypair for', opts.account, opts.email, opts.keypair);
var id = opts.account.id || opts.email || 'single-user'; var id = opts.account.id || opts.email || "single-user";
// you can just treat the keypair as opaque and save and retrieve it as JSON // you can just treat the keypair as opaque and save and retrieve it as JSON
var keyblob = JSON.stringify(opts.keypair); var keyblob = JSON.stringify(opts.keypair);
@ -79,12 +79,12 @@ accounts.setKeypair = function(opts) {
.then(function() { .then(function() {
var pathname = path.join( var pathname = path.join(
accountsDir(store, opts), accountsDir(store, opts),
sanitizeFilename(id) + '.json' sanitizeFilename(id) + ".json"
); );
return writeFileAsync( return writeFileAsync(
U._tameWild(pathname, opts.subject), U._tameWild(pathname, opts.subject),
keyblob, keyblob,
'utf8' "utf8"
); );
}) })
.then(function() { .then(function() {
@ -100,7 +100,7 @@ accounts.setKeypair = function(opts) {
//}; //};
function sanitizeFilename(id) { function sanitizeFilename(id) {
return id.replace(/(\.\.)|\\|\//g, '_').replace(/[^!-~]/g, '_'); return id.replace(/(\.\.)|\\|\//g, "_").replace(/[^!-~]/g, "_");
} }
function accountsDir(store, opts) { function accountsDir(store, opts) {
@ -109,5 +109,5 @@ function accountsDir(store, opts) {
opts, opts,
opts.accountsDir || store.options.accountsDir opts.accountsDir || store.options.accountsDir
); );
return U._tameWild(dir, opts.subject || ''); return U._tameWild(dir, opts.subject || "");
} }

View File

@ -1,16 +1,16 @@
'use strict'; "use strict";
var certificates = module.exports; var certificates = module.exports;
var store = certificates; var store = certificates;
var U = require('./utils.js'); var U = require("./utils.js");
var fs = require('fs'); var fs = require("fs");
var path = require('path'); var path = require("path");
var PromiseA = require('./promise.js'); var PromiseA = require("./promise.js");
var sfs = require('safe-replace'); var sfs = require("safe-replace");
var readFileAsync = PromiseA.promisify(fs.readFile); var readFileAsync = PromiseA.promisify(fs.readFile);
var writeFileAsync = PromiseA.promisify(fs.writeFile); var writeFileAsync = PromiseA.promisify(fs.writeFile);
var mkdirpAsync = PromiseA.promisify(require('@root/mkdirp')); var mkdirpAsync = PromiseA.promisify(require("@root/mkdirp"));
// Certificates.check // Certificates.check
// //
@ -25,7 +25,7 @@ certificates.check = function(opts) {
// This just goes to show that any options set in approveDomains() will be available here // This just goes to show that any options set in approveDomains() will be available here
// (the same is true for all of the hooks in this file) // (the same is true for all of the hooks in this file)
if (opts.exampleThrowError) { if (opts.exampleThrowError) {
return Promise.reject(new Error('You want an error? You got it!')); return Promise.reject(new Error("You want an error? You got it!"));
} }
if (opts.exampleReturnNull) { if (opts.exampleReturnNull) {
return Promise.resolve(null); return Promise.resolve(null);
@ -35,9 +35,9 @@ certificates.check = function(opts) {
} }
return Promise.all([ return Promise.all([
readFileAsync(U._tameWild(privkeyPath(store, opts), id), 'ascii'), // 0 // all other PEM types are just readFileAsync(U._tameWild(privkeyPath(store, opts), id), "ascii"), // 0 // all other PEM types are just
readFileAsync(U._tameWild(certPath(store, opts), id), 'ascii'), // 1 // some arrangement of these 3 readFileAsync(U._tameWild(certPath(store, opts), id), "ascii"), // 1 // some arrangement of these 3
readFileAsync(U._tameWild(chainPath(store, opts), id), 'ascii') // 2 // (bundle, combined, fullchain, etc) readFileAsync(U._tameWild(chainPath(store, opts), id), "ascii") // 2 // (bundle, combined, fullchain, etc)
]) ])
.then(function(all) { .then(function(all) {
//////////////////////// ////////////////////////
@ -60,7 +60,7 @@ certificates.check = function(opts) {
}) })
.catch(function(err) { .catch(function(err) {
// Treat non-exceptional failures as null returns (not undefined) // Treat non-exceptional failures as null returns (not undefined)
if ('ENOENT' === err.code) { if ("ENOENT" === err.code) {
return null; return null;
} }
throw err; // True exceptions should be thrown throw err; // True exceptions should be thrown
@ -76,7 +76,7 @@ certificates.checkKeypair = function(opts) {
return readFileAsync( return readFileAsync(
U._tameWild(privkeyPath(store, opts), opts.subject), U._tameWild(privkeyPath(store, opts), opts.subject),
'ascii' "ascii"
) )
.then(function(key) { .then(function(key) {
//////////////////////// ////////////////////////
@ -88,7 +88,7 @@ certificates.checkKeypair = function(opts) {
}; };
}) })
.catch(function(err) { .catch(function(err) {
if ('ENOENT' === err.code) { if ("ENOENT" === err.code) {
return null; return null;
} }
throw err; throw err;
@ -111,7 +111,7 @@ certificates.setKeypair = function(opts) {
return writeFileAsync( return writeFileAsync(
U._tameWild(privkeyPath(store, opts), opts.subject), U._tameWild(privkeyPath(store, opts), opts.subject),
keypair.privateKeyPem, keypair.privateKeyPem,
'ascii' "ascii"
).then(function() { ).then(function() {
return null; return null;
}); });
@ -148,14 +148,14 @@ certificates.set = function(opts) {
) )
).then(function() { ).then(function() {
var fullchainPem = [ var fullchainPem = [
pems.cert.trim() + '\n', pems.cert.trim() + "\n",
pems.chain.trim() + '\n' pems.chain.trim() + "\n"
].join('\n'); // for Apache, Nginx, etc ].join("\n"); // for Apache, Nginx, etc
var bundlePem = [ var bundlePem = [
pems.privkey, pems.privkey,
pems.cert, pems.cert,
pems.chain pems.chain
].join('\n'); // for HAProxy ].join("\n"); // for HAProxy
return PromiseA.all([ return PromiseA.all([
sfs.writeFileAsync( sfs.writeFileAsync(
U._tameWild( U._tameWild(
@ -163,7 +163,7 @@ certificates.set = function(opts) {
opts.subject opts.subject
), ),
pems.cert, pems.cert,
'ascii' "ascii"
), ),
sfs.writeFileAsync( sfs.writeFileAsync(
U._tameWild( U._tameWild(
@ -171,7 +171,7 @@ certificates.set = function(opts) {
opts.subject opts.subject
), ),
pems.chain, pems.chain,
'ascii' "ascii"
), ),
// Most web servers need these two // Most web servers need these two
sfs.writeFileAsync( sfs.writeFileAsync(
@ -180,7 +180,7 @@ certificates.set = function(opts) {
opts.subject opts.subject
), ),
fullchainPem, fullchainPem,
'ascii' "ascii"
), ),
// HAProxy needs "bundle.pem" aka "combined.pem" // HAProxy needs "bundle.pem" aka "combined.pem"
sfs.writeFileAsync( sfs.writeFileAsync(
@ -189,7 +189,7 @@ certificates.set = function(opts) {
opts.subject opts.subject
), ),
bundlePem, bundlePem,
'ascii' "ascii"
) )
]); ]);
}); });
@ -203,7 +203,7 @@ certificates.set = function(opts) {
}; };
function liveDir(store, opts) { function liveDir(store, opts) {
return opts.liveDir || path.join(opts.configDir, 'live', opts.subject); return opts.liveDir || path.join(opts.configDir, "live", opts.subject);
} }
function privkeyPath(store, opts) { function privkeyPath(store, opts) {
@ -216,19 +216,19 @@ function privkeyPath(store, opts) {
store.options.serverKeyPath || store.options.serverKeyPath ||
store.options.privkeyPath || store.options.privkeyPath ||
store.options.domainKeyPath || store.options.domainKeyPath ||
path.join(liveDir(), 'privkey.pem') path.join(liveDir(), "privkey.pem")
); );
return U._tameWild(dir, opts.subject || ''); return U._tameWild(dir, opts.subject || "");
} }
function certPath(store, opts) { function certPath(store, opts) {
var pathname = var pathname =
opts.certPath || opts.certPath ||
store.options.certPath || store.options.certPath ||
path.join(liveDir(), 'cert.pem'); path.join(liveDir(), "cert.pem");
var dir = U._tpl(store, opts, pathname); var dir = U._tpl(store, opts, pathname);
return U._tameWild(dir, opts.subject || ''); return U._tameWild(dir, opts.subject || "");
} }
function fullchainPath(store, opts) { function fullchainPath(store, opts) {
@ -237,9 +237,9 @@ function fullchainPath(store, opts) {
opts, opts,
opts.fullchainPath || opts.fullchainPath ||
store.options.fullchainPath || store.options.fullchainPath ||
path.join(liveDir(), 'fullchain.pem') path.join(liveDir(), "fullchain.pem")
); );
return U._tameWild(dir, opts.subject || ''); return U._tameWild(dir, opts.subject || "");
} }
function chainPath(store, opts) { function chainPath(store, opts) {
@ -248,9 +248,9 @@ function chainPath(store, opts) {
opts, opts,
opts.chainPath || opts.chainPath ||
store.options.chainPath || store.options.chainPath ||
path.join(liveDir(), 'chain.pem') path.join(liveDir(), "chain.pem")
); );
return U._tameWild(dir, opts.subject || ''); return U._tameWild(dir, opts.subject || "");
} }
function bundlePath(store, opts) { function bundlePath(store, opts) {
@ -259,7 +259,7 @@ function bundlePath(store, opts) {
opts, opts,
opts.bundlePath || opts.bundlePath ||
store.options.bundlePath || store.options.bundlePath ||
path.join(liveDir(), 'bundle.pem') path.join(liveDir(), "bundle.pem")
); );
return U._tameWild(dir, opts.subject || ''); return U._tameWild(dir, opts.subject || "");
} }

View File

@ -1,7 +1,7 @@
'use strict'; "use strict";
var os = require('os'); var os = require("os");
var path = require('path'); var path = require("path");
// How Storage Works in Greenlock: High-Level Call Stack // How Storage Works in Greenlock: High-Level Call Stack
// //
@ -56,8 +56,8 @@ module.exports.create = function(config) {
// basic setup // basic setup
var store = { var store = {
accounts: require('./accounts.js'), accounts: require("./accounts.js"),
certificates: require('./certificates.js') certificates: require("./certificates.js")
}; };
// For you store.options should probably start empty and get a minimal set of options copied from `config` above. // For you store.options should probably start empty and get a minimal set of options copied from `config` above.
@ -72,7 +72,7 @@ module.exports.create = function(config) {
store.certificates.options = store.options; store.certificates.options = store.options;
if (!config.basePath && !config.configDir) { if (!config.basePath && !config.configDir) {
console.info('Greenlock Store FS Path:', store.options.configDir); console.info("Greenlock Store FS Path:", store.options.configDir);
} }
return store; return store;
@ -84,20 +84,20 @@ module.exports.create = function(config) {
// //
// Everything below this line is just implementation specific // Everything below this line is just implementation specific
var defaults = { var defaults = {
basePath: path.join(os.homedir(), '.config', 'greenlock'), basePath: path.join(os.homedir(), ".config", "greenlock"),
accountsDir: path.join(':basePath', 'accounts', ':directoryUrl'), accountsDir: path.join(":basePath", "accounts", ":directoryUrl"),
serverDirGet: function(copy) { serverDirGet: function(copy) {
return (copy.directoryUrl || copy.server || '') return (copy.directoryUrl || copy.server || "")
.replace('https://', '') .replace("https://", "")
.replace(/(\/)$/, '') .replace(/(\/)$/, "")
.replace(/\//g, path.sep); .replace(/\//g, path.sep);
}, },
privkeyPath: path.join(':basePath', ':env', ':subject', 'privkey.pem'), privkeyPath: path.join(":basePath", ":env", ":subject", "privkey.pem"),
fullchainPath: path.join(':basePath', ':env', ':subject', 'fullchain.pem'), fullchainPath: path.join(":basePath", ":env", ":subject", "fullchain.pem"),
certPath: path.join(':basePath', ':env', ':subject', 'cert.pem'), certPath: path.join(":basePath", ":env", ":subject", "cert.pem"),
chainPath: path.join(':basePath', ':env', ':subject', 'chain.pem'), chainPath: path.join(":basePath", ":env", ":subject", "chain.pem"),
bundlePath: path.join(':basePath', ':env', ':subject', 'bundle.pem') bundlePath: path.join(":basePath", ":env", ":subject", "bundle.pem")
}; };
defaults.configDir = defaults.basePath; defaults.configDir = defaults.basePath;

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "greenlock-store-fs", "name": "greenlock-store-fs",
"version": "3.0.1", "version": "3.2.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@ -1,6 +1,6 @@
{ {
"name": "greenlock-store-fs", "name": "greenlock-store-fs",
"version": "3.2.0", "version": "3.2.1",
"description": "A file-based certificate store for greenlock that supports wildcards.", "description": "A file-based certificate store for greenlock that supports wildcards.",
"homepage": "https://git.rootprojects.org/root/greenlock-store-fs.js", "homepage": "https://git.rootprojects.org/root/greenlock-store-fs.js",
"main": "index.js", "main": "index.js",

View File

@ -1,17 +1,17 @@
'use strict'; "use strict";
function getPromise() { function getPromise() {
var util = require('util'); var util = require("util");
var PromiseA; var PromiseA;
if (util.promisify && global.Promise) { if (util.promisify && global.Promise) {
PromiseA = global.Promise; PromiseA = global.Promise;
PromiseA.promisify = util.promisify; PromiseA.promisify = util.promisify;
} else { } else {
try { try {
PromiseA = require('bluebird'); PromiseA = require("bluebird");
} catch (e) { } catch (e) {
console.error( console.error(
'Your version of node is missing Promise. Please run `npm install --save bluebird` in your project to fix' "Your version of node is missing Promise. Please run `npm install --save bluebird` in your project to fix"
); );
process.exit(10); process.exit(10);
} }

42
test.js
View File

@ -1,26 +1,32 @@
'use strict'; "use strict";
var tester = require('greenlock-store-test'); var tester = require("greenlock-store-test");
var crypto = require('crypto'); var crypto = require("crypto");
var os = require('os'); var os = require("os");
var path = require('path'); var path = require("path");
var basedir = path.join(os.tmpdir(), 'greenlock-store-fs-test-' + crypto.randomBytes(4).toString('hex')); var basedir = path.join(
var domain = '*.example.com'; os.tmpdir(),
var store = require('./').create({ "greenlock-store-fs-test-" + crypto.randomBytes(4).toString("hex")
configDir: basedir );
, accountsDir: path.join(basedir, 'accounts') var domain = "*.example.com";
, privkeyPath: path.join(basedir, 'live', domain, 'privkey.pem') var store = require("./").create({
, fullchainPath: path.join(basedir, 'live', domain, 'fullchain.pem') configDir: basedir,
, certPath: path.join(basedir, 'live', domain, 'cert.pem') accountsDir: path.join(basedir, "accounts"),
, chainPath: path.join(basedir, 'live', domain, 'chain.pem') privkeyPath: path.join(basedir, "live", domain, "privkey.pem"),
, bundlePath: path.join(basedir, 'live', domain, 'bundle.pem') fullchainPath: path.join(basedir, "live", domain, "fullchain.pem"),
certPath: path.join(basedir, "live", domain, "cert.pem"),
chainPath: path.join(basedir, "live", domain, "chain.pem"),
bundlePath: path.join(basedir, "live", domain, "bundle.pem")
}); });
console.info('Test Dir:', basedir); console.info("Test Dir:", basedir);
tester.test(store).then(function () { tester
.test(store)
.then(function() {
console.info("PASS"); console.info("PASS");
}).catch(function (err) { })
.catch(function(err) {
console.error("FAIL"); console.error("FAIL");
console.error(err); console.error(err);
process.exit(20); process.exit(20);

View File

@ -1,4 +1,4 @@
'use strict'; "use strict";
var U = module.exports; var U = module.exports;
@ -7,43 +7,43 @@ U._tameWild = function tameWild(pathname, wild) {
if (!wild) { if (!wild) {
return pathname; return pathname;
} }
var tame = wild.replace(/\*/g, '_'); var tame = wild.replace(/\*/g, "_");
return pathname.replace(wild, tame); return pathname.replace(wild, tame);
}; };
U._tpl = function tpl(store, opts, str) { U._tpl = function tpl(store, opts, str) {
var server = ['directoryUrl', 'serverDir', 'server']; var server = ["directoryUrl", "serverDir", "server"];
var env = ['env', 'directoryUrl']; var env = ["env", "directoryUrl"];
[ [
['basePath', 'configDir'], ["basePath", "configDir"],
server, server,
['subject', 'hostname', 'domain'], ["subject", "hostname", "domain"],
env env
].forEach(function(group) { ].forEach(function(group) {
group.forEach(function(tmpl) { group.forEach(function(tmpl) {
group.forEach(function(key) { group.forEach(function(key) {
var item = opts[key] || store.options[key]; var item = opts[key] || store.options[key];
if ('string' !== typeof item) { if ("string" !== typeof item) {
return; return;
} }
if ('directoryUrl' === key) { if ("directoryUrl" === key) {
item = item.replace(/^https?:\/\//i, ''); item = item.replace(/^https?:\/\//i, "");
} }
if ('env' === tmpl) { if ("env" === tmpl) {
if (/staging/.test(item)) { if (/staging/.test(item)) {
item = 'staging'; item = "staging";
} else if (/acme-v02/.test(item)) { } else if (/acme-v02/.test(item)) {
item = 'live'; item = "live";
} else { } else {
// item = item; // item = item;
} }
} }
if (-1 === str.indexOf(':' + tmpl)) { if (-1 === str.indexOf(":" + tmpl)) {
return; return;
} }
str = str.replace(':' + tmpl, item); str = str.replace(":" + tmpl, item);
}); });
}); });
}); });