diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..fe82f3b --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "bracketSpacing": true, + "printWidth": 80, + "tabWidth": 4, + "trailingComma": "none", + "useTabs": false +} diff --git a/README.md b/README.md index df55229..0ae2cdf 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,11 @@ A keypair and certificate storage strategy for Greenlock v2.7+ (and v3). The (much simpler) successor to le-store-certbot. Works with all ACME (Let's Encrypt) SSL certificate sytles: -* [x] single domains -* [x] multiple domains (SANs, AltNames) -* [x] wildcards -* [x] private / localhost domains + +- [x] single domains +- [x] multiple domains (SANs, AltNames) +- [x] wildcards +- [x] private / localhost domains # Usage @@ -65,17 +66,25 @@ The subject and the first altname must be an exact match: `subject === altnames[ ```js 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 - var wild = '*.' + opts.domain.split('.').slice(1).join('.'); + // foo.example.com => *.example.com + var wild = + "*." + + opts.domain + .split(".") + .slice(1) + .join("."); - if ('example.com' !== opts.domain && '*.example.com' !== wild) { - cb(new Error(opts.domain + " is not allowed")); - } + if ("example.com" !== opts.domain && "*.example.com" !== wild) { + cb(new Error(opts.domain + " is not allowed")); + } - var result = { subject: 'example.com', altnames: [ 'example.com', '*.example.com' ] }; - return Promise.resolve(result); + var result = { + subject: "example.com", + altnames: ["example.com", "*.example.com"] + }; + return Promise.resolve(result); } ``` @@ -83,36 +92,45 @@ function approveDomains(opts) { ```js function approveDomains(opts, certs, cb) { - var related = getRelated(opts.domain); - if (!related) { cb(new Error(opts.domain + " is not allowed")); }; + var related = getRelated(opts.domain); + if (!related) { + cb(new Error(opts.domain + " is not allowed")); + } - opts.subject = related.subject; - opts.domains = related.domains; + opts.subject = related.subject; + opts.domains = related.domains; - cb({ options: opts, certs: certs }); + cb({ options: opts, certs: certs }); } ``` ```js function getRelated(domain) { - var related; - var wild = '*.' + domain.split('.').slice(1).join('.'); - if (Object.keys(allAllowedDomains).some(function (k) { - return allAllowedDomains[k].some(function (name) { - if (domain === name || wild === name) { - related = { subject: k, altnames: allAllowedDomains[k] }; - return true; - } - }); - })) { - return related; - } + var related; + var wild = + "*." + + domain + .split(".") + .slice(1) + .join("."); + if ( + Object.keys(allAllowedDomains).some(function(k) { + return allAllowedDomains[k].some(function(name) { + if (domain === name || wild === name) { + related = { subject: k, altnames: allAllowedDomains[k] }; + return true; + } + }); + }) + ) { + return related; + } } ``` ```js var allAllowedDomains = { - 'example.com': ['example.com', '*.example.com'] -, 'example.net': ['example.net', '*.example.net'] -} + "example.com": ["example.com", "*.example.com"], + "example.net": ["example.net", "*.example.net"] +}; ``` diff --git a/accounts.js b/accounts.js index aee2d68..cdd7d76 100644 --- a/accounts.js +++ b/accounts.js @@ -1,15 +1,15 @@ -'use strict'; +"use strict"; var accounts = module.exports; var store = accounts; -var U = require('./utils.js'); +var U = require("./utils.js"); -var fs = require('fs'); -var path = require('path'); -var PromiseA = require('./promise.js'); +var fs = require("fs"); +var path = require("path"); +var PromiseA = require("./promise.js"); var readFileAsync = PromiseA.promisify(fs.readFile); 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 //store.accounts.check = function (opts) { @@ -22,23 +22,23 @@ var mkdirpAsync = PromiseA.promisify(require('@root/mkdirp')); // Use account.id, or email, if id hasn't been set, to find an account keypair. // Return an object with string privateKeyPem and/or object privateKeyJwk (or null, not undefined) accounts.checkKeypair = function(opts) { - var id = - (opts.account && opts.account.id) || - (opts.subscriberEmail || opts.email) || - 'single-user'; - //console.log('accounts.checkKeypair for', id); + var id = + (opts.account && opts.account.id) || + (opts.subscriberEmail || opts.email) || + "single-user"; + //console.log('accounts.checkKeypair for', id); - var pathname = path.join( - accountsDir(store, opts), - sanitizeFilename(id) + '.json' - ); - return readFileAsync(U._tameWild(pathname, opts.subject), 'utf8') - .then(function(blob) { - // keypair can treated as an opaque object and just passed along, - // but just to show you what it is... - var keypair = JSON.parse(blob); - return keypair; - /* + var pathname = path.join( + accountsDir(store, opts), + sanitizeFilename(id) + ".json" + ); + return readFileAsync(U._tameWild(pathname, opts.subject), "utf8") + .then(function(blob) { + // keypair can treated as an opaque object and just passed along, + // but just to show you what it is... + var keypair = JSON.parse(blob); + return keypair; + /* { privateKeyPem: keypair.privateKeyPem, // string PEM private key privateKeyJwk: keypair.privateKeyJwk, // object JWK private key @@ -46,13 +46,13 @@ accounts.checkKeypair = function(opts) { public: keypair.public }; */ - }) - .catch(function(err) { - if ('ENOENT' === err.code) { - return null; - } - throw err; - }); + }) + .catch(function(err) { + if ("ENOENT" === err.code) { + return null; + } + throw err; + }); }; // Accounts.setKeypair({ account, email, keypair, ... }): @@ -60,12 +60,12 @@ accounts.checkKeypair = function(opts) { // Use account.id (or email if no id is present) to save an account keypair // Return null (not undefined) on success, or throw on error accounts.setKeypair = function(opts) { - //console.log('accounts.setKeypair for', opts.account, opts.email, opts.keypair); - var id = opts.account.id || opts.email || 'single-user'; + //console.log('accounts.setKeypair for', opts.account, opts.email, opts.keypair); + var id = opts.account.id || opts.email || "single-user"; - // you can just treat the keypair as opaque and save and retrieve it as JSON - var keyblob = JSON.stringify(opts.keypair); - /* + // 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({ privateKeyPem: opts.keypair.privateKeyPem, // string PEM privateKeyJwk: opts.keypair.privateKeyJwk, // object JWK @@ -73,24 +73,24 @@ accounts.setKeypair = function(opts) { }); */ - // Ignore. - // Just implementation specific details here. - return mkdirpAsync(accountsDir(store, opts)) - .then(function() { - var pathname = path.join( - accountsDir(store, opts), - sanitizeFilename(id) + '.json' - ); - return writeFileAsync( - U._tameWild(pathname, opts.subject), - keyblob, - 'utf8' - ); - }) - .then(function() { - // This is your job: return null, not undefined - return null; - }); + // Ignore. + // Just implementation specific details here. + return mkdirpAsync(accountsDir(store, opts)) + .then(function() { + var pathname = path.join( + accountsDir(store, opts), + sanitizeFilename(id) + ".json" + ); + return writeFileAsync( + U._tameWild(pathname, opts.subject), + keyblob, + "utf8" + ); + }) + .then(function() { + // This is your job: return null, not undefined + return null; + }); }; // Implement if you need the ACME account metadata elsewhere in the chain of events @@ -100,14 +100,14 @@ accounts.setKeypair = function(opts) { //}; function sanitizeFilename(id) { - return id.replace(/(\.\.)|\\|\//g, '_').replace(/[^!-~]/g, '_'); + return id.replace(/(\.\.)|\\|\//g, "_").replace(/[^!-~]/g, "_"); } function accountsDir(store, opts) { - var dir = U._tpl( - store, - opts, - opts.accountsDir || store.options.accountsDir - ); - return U._tameWild(dir, opts.subject || ''); + var dir = U._tpl( + store, + opts, + opts.accountsDir || store.options.accountsDir + ); + return U._tameWild(dir, opts.subject || ""); } diff --git a/certificates.js b/certificates.js index 89f8d76..a4a5bbf 100644 --- a/certificates.js +++ b/certificates.js @@ -1,70 +1,70 @@ -'use strict'; +"use strict"; var certificates = module.exports; var store = certificates; -var U = require('./utils.js'); +var U = require("./utils.js"); -var fs = require('fs'); -var path = require('path'); -var PromiseA = require('./promise.js'); -var sfs = require('safe-replace'); +var fs = require("fs"); +var path = require("path"); +var PromiseA = require("./promise.js"); +var sfs = require("safe-replace"); var readFileAsync = PromiseA.promisify(fs.readFile); var writeFileAsync = PromiseA.promisify(fs.writeFile); -var mkdirpAsync = PromiseA.promisify(require('@root/mkdirp')); +var mkdirpAsync = PromiseA.promisify(require("@root/mkdirp")); // Certificates.check // // Use certificate.id, or subject, if id hasn't been set, to find a certificate. // Return an object with string PEMs for cert and chain (or null, not undefined) certificates.check = function(opts) { - // { certificate.id, subject, ... } - var id = (opts.certificate && opts.certificate.id) || opts.subject; - //console.log('certificates.check for', opts); + // { certificate.id, subject, ... } + var id = (opts.certificate && opts.certificate.id) || opts.subject; + //console.log('certificates.check for', opts); - // For advanced use cases: - // 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) - if (opts.exampleThrowError) { - return Promise.reject(new Error('You want an error? You got it!')); - } - if (opts.exampleReturnNull) { - return Promise.resolve(null); - } - if (opts.exampleReturnCerts) { - return Promise.resolve(opts.exampleReturnCerts); - } + // For advanced use cases: + // 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) + if (opts.exampleThrowError) { + return Promise.reject(new Error("You want an error? You got it!")); + } + if (opts.exampleReturnNull) { + return Promise.resolve(null); + } + if (opts.exampleReturnCerts) { + return Promise.resolve(opts.exampleReturnCerts); + } - return Promise.all([ - 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(chainPath(store, opts), id), 'ascii') // 2 // (bundle, combined, fullchain, etc) - ]) - .then(function(all) { - //////////////////////// - // PAY ATTENTION HERE // - //////////////////////// - // This is all you have to return: cert, chain - return { - cert: all[1], // string PEM. the bare cert, half of the concatonated fullchain.pem you need - chain: all[2], // string PEM. the bare chain, the second half of the fullchain.pem - privkey: all[0] // string PEM. optional, allows checkKeypair to be skipped + return Promise.all([ + 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(chainPath(store, opts), id), "ascii") // 2 // (bundle, combined, fullchain, etc) + ]) + .then(function(all) { + //////////////////////// + // PAY ATTENTION HERE // + //////////////////////// + // This is all you have to return: cert, chain + return { + cert: all[1], // string PEM. the bare cert, half of the concatonated fullchain.pem you need + chain: all[2], // string PEM. the bare chain, the second half of the fullchain.pem + privkey: all[0] // string PEM. optional, allows checkKeypair to be skipped - // These can be useful to store in your database, - // but otherwise they're easy to derive from the cert. - // (when not available they'll be generated from cert-info) - //, subject: certinfo.subject // string domain name - //, altnames: certinfo.altnames // array of domain name strings - //, issuedAt: certinfo.issuedAt // number in ms (a.k.a. NotBefore) - //, expiresAt: certinfo.expiresAt // number in ms (a.k.a. NotAfter) - }; - }) - .catch(function(err) { - // Treat non-exceptional failures as null returns (not undefined) - if ('ENOENT' === err.code) { - return null; - } - throw err; // True exceptions should be thrown - }); + // These can be useful to store in your database, + // but otherwise they're easy to derive from the cert. + // (when not available they'll be generated from cert-info) + //, subject: certinfo.subject // string domain name + //, altnames: certinfo.altnames // array of domain name strings + //, issuedAt: certinfo.issuedAt // number in ms (a.k.a. NotBefore) + //, expiresAt: certinfo.expiresAt // number in ms (a.k.a. NotAfter) + }; + }) + .catch(function(err) { + // Treat non-exceptional failures as null returns (not undefined) + if ("ENOENT" === err.code) { + return null; + } + throw err; // True exceptions should be thrown + }); }; // Certificates.checkKeypair @@ -72,27 +72,27 @@ certificates.check = function(opts) { // Use certificate.kid, certificate.id, or subject to find a certificate keypair // Return an object with string privateKeyPem and/or object privateKeyJwk (or null, not undefined) certificates.checkKeypair = function(opts) { - //console.log('certificates.checkKeypair:', opts); + //console.log('certificates.checkKeypair:', opts); - return readFileAsync( - U._tameWild(privkeyPath(store, opts), opts.subject), - 'ascii' - ) - .then(function(key) { - //////////////////////// - // PAY ATTENTION HERE // - //////////////////////// - return { - privateKeyPem: key // In this case we only saved privateKeyPem, so we only return it - //privateKeyJwk: null // (but it's fine, just different encodings of the same thing) - }; - }) - .catch(function(err) { - if ('ENOENT' === err.code) { - return null; - } - throw err; - }); + return readFileAsync( + U._tameWild(privkeyPath(store, opts), opts.subject), + "ascii" + ) + .then(function(key) { + //////////////////////// + // PAY ATTENTION HERE // + //////////////////////// + return { + privateKeyPem: key // In this case we only saved privateKeyPem, so we only return it + //privateKeyJwk: null // (but it's fine, just different encodings of the same thing) + }; + }) + .catch(function(err) { + if ("ENOENT" === err.code) { + return null; + } + throw err; + }); }; // Certificates.setKeypair({ certificate, subject, keypair, ... }): @@ -100,22 +100,22 @@ certificates.checkKeypair = function(opts) { // Use certificate.kid (or certificate.id or subject if no kid is present) to find a certificate keypair // Return null (not undefined) on success, or throw on error certificates.setKeypair = function(opts) { - var keypair = opts.keypair || keypair; + var keypair = opts.keypair || keypair; - // Ignore. - // Just specific implementation details. - return mkdirpAsync( - U._tameWild(path.dirname(privkeyPath(store, opts)), opts.subject) - ).then(function() { - // keypair is normally an opaque object, but here it's a PEM for the FS (for things like Apache and Nginx) - return writeFileAsync( - U._tameWild(privkeyPath(store, opts), opts.subject), - keypair.privateKeyPem, - 'ascii' - ).then(function() { - return null; - }); - }); + // Ignore. + // Just specific implementation details. + return mkdirpAsync( + U._tameWild(path.dirname(privkeyPath(store, opts)), opts.subject) + ).then(function() { + // keypair is normally an opaque object, but here it's a PEM for the FS (for things like Apache and Nginx) + return writeFileAsync( + U._tameWild(privkeyPath(store, opts), opts.subject), + keypair.privateKeyPem, + "ascii" + ).then(function() { + return null; + }); + }); }; // Certificates.set({ subject, pems, ... }): @@ -123,143 +123,143 @@ certificates.setKeypair = function(opts) { // Use certificate.id (or subject if no ki is present) to save a certificate // Return null (not undefined) on success, or throw on error certificates.set = function(opts) { - //console.log('certificates.set:', opts); - var pems = { - cert: opts.pems.cert, // string PEM the first half of the concatonated fullchain.pem cert - chain: opts.pems.chain, // string PEM the second half (yes, you need this too) - privkey: opts.pems.privkey // Ignore. string PEM, useful if you have to create bundle.pem - }; + //console.log('certificates.set:', opts); + var pems = { + cert: opts.pems.cert, // string PEM the first half of the concatonated fullchain.pem cert + chain: opts.pems.chain, // string PEM the second half (yes, you need this too) + privkey: opts.pems.privkey // Ignore. string PEM, useful if you have to create bundle.pem + }; - // Ignore - // Just implementation specific details (writing lots of combinatons of files) - return mkdirpAsync(path.dirname(certPath(store, opts))) - .then(function() { - return mkdirpAsync( - path.dirname(U._tameWild(chainPath(store, opts), opts.subject)) - ).then(function() { - return mkdirpAsync( - path.dirname( - U._tameWild(fullchainPath(store, opts), opts.subject) - ) - ).then(function() { - return mkdirpAsync( - path.dirname( - U._tameWild(bundlePath(store, opts), opts.subject) - ) - ).then(function() { - var fullchainPem = [ - pems.cert.trim() + '\n', - pems.chain.trim() + '\n' - ].join('\n'); // for Apache, Nginx, etc - var bundlePem = [ - pems.privkey, - pems.cert, - pems.chain - ].join('\n'); // for HAProxy - return PromiseA.all([ - sfs.writeFileAsync( - U._tameWild( - certPath(store, opts), - opts.subject - ), - pems.cert, - 'ascii' - ), - sfs.writeFileAsync( - U._tameWild( - chainPath(store, opts), - opts.subject - ), - pems.chain, - 'ascii' - ), - // Most web servers need these two - sfs.writeFileAsync( - U._tameWild( - fullchainPath(store, opts), - opts.subject - ), - fullchainPem, - 'ascii' - ), - // HAProxy needs "bundle.pem" aka "combined.pem" - sfs.writeFileAsync( - U._tameWild( - bundlePath(store, opts), - opts.subject - ), - bundlePem, - 'ascii' - ) - ]); - }); - }); - }); - }) - .then(function() { - // That's your job: return null - return null; - }); + // Ignore + // Just implementation specific details (writing lots of combinatons of files) + return mkdirpAsync(path.dirname(certPath(store, opts))) + .then(function() { + return mkdirpAsync( + path.dirname(U._tameWild(chainPath(store, opts), opts.subject)) + ).then(function() { + return mkdirpAsync( + path.dirname( + U._tameWild(fullchainPath(store, opts), opts.subject) + ) + ).then(function() { + return mkdirpAsync( + path.dirname( + U._tameWild(bundlePath(store, opts), opts.subject) + ) + ).then(function() { + var fullchainPem = [ + pems.cert.trim() + "\n", + pems.chain.trim() + "\n" + ].join("\n"); // for Apache, Nginx, etc + var bundlePem = [ + pems.privkey, + pems.cert, + pems.chain + ].join("\n"); // for HAProxy + return PromiseA.all([ + sfs.writeFileAsync( + U._tameWild( + certPath(store, opts), + opts.subject + ), + pems.cert, + "ascii" + ), + sfs.writeFileAsync( + U._tameWild( + chainPath(store, opts), + opts.subject + ), + pems.chain, + "ascii" + ), + // Most web servers need these two + sfs.writeFileAsync( + U._tameWild( + fullchainPath(store, opts), + opts.subject + ), + fullchainPem, + "ascii" + ), + // HAProxy needs "bundle.pem" aka "combined.pem" + sfs.writeFileAsync( + U._tameWild( + bundlePath(store, opts), + opts.subject + ), + bundlePem, + "ascii" + ) + ]); + }); + }); + }); + }) + .then(function() { + // That's your job: return null + return null; + }); }; 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) { - var dir = U._tpl( - store, - opts, - opts.serverKeyPath || - opts.privkeyPath || - opts.domainKeyPath || - store.options.serverKeyPath || - store.options.privkeyPath || - store.options.domainKeyPath || - path.join(liveDir(), 'privkey.pem') - ); - return U._tameWild(dir, opts.subject || ''); + var dir = U._tpl( + store, + opts, + opts.serverKeyPath || + opts.privkeyPath || + opts.domainKeyPath || + store.options.serverKeyPath || + store.options.privkeyPath || + store.options.domainKeyPath || + path.join(liveDir(), "privkey.pem") + ); + return U._tameWild(dir, opts.subject || ""); } function certPath(store, opts) { - var pathname = - opts.certPath || - store.options.certPath || - path.join(liveDir(), 'cert.pem'); + var pathname = + opts.certPath || + store.options.certPath || + path.join(liveDir(), "cert.pem"); - var dir = U._tpl(store, opts, pathname); - return U._tameWild(dir, opts.subject || ''); + var dir = U._tpl(store, opts, pathname); + return U._tameWild(dir, opts.subject || ""); } function fullchainPath(store, opts) { - var dir = U._tpl( - store, - opts, - opts.fullchainPath || - store.options.fullchainPath || - path.join(liveDir(), 'fullchain.pem') - ); - return U._tameWild(dir, opts.subject || ''); + var dir = U._tpl( + store, + opts, + opts.fullchainPath || + store.options.fullchainPath || + path.join(liveDir(), "fullchain.pem") + ); + return U._tameWild(dir, opts.subject || ""); } function chainPath(store, opts) { - var dir = U._tpl( - store, - opts, - opts.chainPath || - store.options.chainPath || - path.join(liveDir(), 'chain.pem') - ); - return U._tameWild(dir, opts.subject || ''); + var dir = U._tpl( + store, + opts, + opts.chainPath || + store.options.chainPath || + path.join(liveDir(), "chain.pem") + ); + return U._tameWild(dir, opts.subject || ""); } function bundlePath(store, opts) { - var dir = U._tpl( - store, - opts, - opts.bundlePath || - store.options.bundlePath || - path.join(liveDir(), 'bundle.pem') - ); - return U._tameWild(dir, opts.subject || ''); + var dir = U._tpl( + store, + opts, + opts.bundlePath || + store.options.bundlePath || + path.join(liveDir(), "bundle.pem") + ); + return U._tameWild(dir, opts.subject || ""); } diff --git a/index.js b/index.js index 6354396..5be6bcf 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,7 @@ -'use strict'; +"use strict"; -var os = require('os'); -var path = require('path'); +var os = require("os"); +var path = require("path"); // How Storage Works in Greenlock: High-Level Call Stack // @@ -50,32 +50,32 @@ var path = require('path'); // Either your user calls create with specific options, or greenlock calls it for you with a big options blob module.exports.create = function(config) { - // Bear in mind that the only time any of this gets called is on first access after startup, new registration, and - // renewal - so none of this needs to be particularly fast. It may need to be memory efficient, however - if you have - // more than 10,000 domains, for example. + // Bear in mind that the only time any of this gets called is on first access after startup, new registration, and + // renewal - so none of this needs to be particularly fast. It may need to be memory efficient, however - if you have + // more than 10,000 domains, for example. - // basic setup - var store = { - accounts: require('./accounts.js'), - certificates: require('./certificates.js') - }; + // basic setup + var store = { + accounts: require("./accounts.js"), + certificates: require("./certificates.js") + }; - // For you store.options should probably start empty and get a minimal set of options copied from `config` above. - // Example: - //store.options = {}; - //store.options.databaseUrl = config.databaseUrl; + // For you store.options should probably start empty and get a minimal set of options copied from `config` above. + // Example: + //store.options = {}; + //store.options.databaseUrl = config.databaseUrl; - // In the case of greenlock-store-fs there's a bunch of legacy stuff that goes on, so we just clobber it all on. - // Don't be like greenlock-store-fs (see note above). - store.options = mergeOptions(config); - store.accounts.options = store.options; - store.certificates.options = store.options; + // In the case of greenlock-store-fs there's a bunch of legacy stuff that goes on, so we just clobber it all on. + // Don't be like greenlock-store-fs (see note above). + store.options = mergeOptions(config); + store.accounts.options = store.options; + store.certificates.options = store.options; - if (!config.basePath && !config.configDir) { - console.info('Greenlock Store FS Path:', store.options.configDir); - } + if (!config.basePath && !config.configDir) { + console.info("Greenlock Store FS Path:", store.options.configDir); + } - return store; + return store; }; /////////////////////////////////////////////////////////////////////////////// @@ -84,36 +84,36 @@ module.exports.create = function(config) { // // Everything below this line is just implementation specific var defaults = { - basePath: path.join(os.homedir(), '.config', 'greenlock'), + basePath: path.join(os.homedir(), ".config", "greenlock"), - accountsDir: path.join(':basePath', 'accounts', ':directoryUrl'), - serverDirGet: function(copy) { - return (copy.directoryUrl || copy.server || '') - .replace('https://', '') - .replace(/(\/)$/, '') - .replace(/\//g, path.sep); - }, - privkeyPath: path.join(':basePath', ':env', ':subject', 'privkey.pem'), - fullchainPath: path.join(':basePath', ':env', ':subject', 'fullchain.pem'), - certPath: path.join(':basePath', ':env', ':subject', 'cert.pem'), - chainPath: path.join(':basePath', ':env', ':subject', 'chain.pem'), - bundlePath: path.join(':basePath', ':env', ':subject', 'bundle.pem') + accountsDir: path.join(":basePath", "accounts", ":directoryUrl"), + serverDirGet: function(copy) { + return (copy.directoryUrl || copy.server || "") + .replace("https://", "") + .replace(/(\/)$/, "") + .replace(/\//g, path.sep); + }, + privkeyPath: path.join(":basePath", ":env", ":subject", "privkey.pem"), + fullchainPath: path.join(":basePath", ":env", ":subject", "fullchain.pem"), + certPath: path.join(":basePath", ":env", ":subject", "cert.pem"), + chainPath: path.join(":basePath", ":env", ":subject", "chain.pem"), + bundlePath: path.join(":basePath", ":env", ":subject", "bundle.pem") }; defaults.configDir = defaults.basePath; function mergeOptions(configs) { - if (!configs.serverKeyPath) { - configs.serverKeyPath = - configs.domainKeyPath || - configs.privkeyPath || - defaults.privkeyPath; - } + if (!configs.serverKeyPath) { + configs.serverKeyPath = + configs.domainKeyPath || + configs.privkeyPath || + defaults.privkeyPath; + } - Object.keys(defaults).forEach(function(key) { - if (!configs[key]) { - configs[key] = defaults[key]; - } - }); + Object.keys(defaults).forEach(function(key) { + if (!configs[key]) { + configs[key] = defaults[key]; + } + }); - return configs; + return configs; } diff --git a/package-lock.json b/package-lock.json index 833099c..8fa7f64 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,18 @@ { - "name": "greenlock-store-fs", - "version": "3.0.1", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@root/mkdirp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@root/mkdirp/-/mkdirp-1.0.0.tgz", - "integrity": "sha512-hxGAYUx5029VggfG+U9naAhQkoMSXtOeXtbql97m3Hi6/sQSRL/4khKZPyOF6w11glyCOU38WCNLu9nUcSjOfA==" - }, - "safe-replace": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-replace/-/safe-replace-1.1.0.tgz", - "integrity": "sha512-9/V2E0CDsKs9DWOOwJH7jYpSl9S3N05uyevNjvsnDauBqRowBPOyot1fIvV5N2IuZAbYyvrTXrYFVG0RZInfFw==" + "name": "greenlock-store-fs", + "version": "3.2.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@root/mkdirp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@root/mkdirp/-/mkdirp-1.0.0.tgz", + "integrity": "sha512-hxGAYUx5029VggfG+U9naAhQkoMSXtOeXtbql97m3Hi6/sQSRL/4khKZPyOF6w11glyCOU38WCNLu9nUcSjOfA==" + }, + "safe-replace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-replace/-/safe-replace-1.1.0.tgz", + "integrity": "sha512-9/V2E0CDsKs9DWOOwJH7jYpSl9S3N05uyevNjvsnDauBqRowBPOyot1fIvV5N2IuZAbYyvrTXrYFVG0RZInfFw==" + } } - } } diff --git a/package.json b/package.json index fb47af3..52fd16e 100644 --- a/package.json +++ b/package.json @@ -1,31 +1,31 @@ { - "name": "greenlock-store-fs", - "version": "3.2.0", - "description": "A file-based certificate store for greenlock that supports wildcards.", - "homepage": "https://git.rootprojects.org/root/greenlock-store-fs.js", - "main": "index.js", - "directories": { - "test": "tests" - }, - "scripts": { - "test": "node tests" - }, - "repository": { - "type": "git", - "url": "https://git.rootprojects.org/root/greenlock-store-fs.js.git" - }, - "keywords": [ - "greenlock", - "json", - "keypairs", - "certificates", - "store", - "database" - ], - "author": "AJ ONeal (https://solderjs.com/)", - "license": "MPL-2.0", - "dependencies": { - "@root/mkdirp": "^1.0.0", - "safe-replace": "^1.1.0" - } + "name": "greenlock-store-fs", + "version": "3.2.1", + "description": "A file-based certificate store for greenlock that supports wildcards.", + "homepage": "https://git.rootprojects.org/root/greenlock-store-fs.js", + "main": "index.js", + "directories": { + "test": "tests" + }, + "scripts": { + "test": "node tests" + }, + "repository": { + "type": "git", + "url": "https://git.rootprojects.org/root/greenlock-store-fs.js.git" + }, + "keywords": [ + "greenlock", + "json", + "keypairs", + "certificates", + "store", + "database" + ], + "author": "AJ ONeal (https://solderjs.com/)", + "license": "MPL-2.0", + "dependencies": { + "@root/mkdirp": "^1.0.0", + "safe-replace": "^1.1.0" + } } diff --git a/promise.js b/promise.js index b351781..7db13a8 100644 --- a/promise.js +++ b/promise.js @@ -1,22 +1,22 @@ -'use strict'; +"use strict"; function getPromise() { - var util = require('util'); - var PromiseA; - if (util.promisify && global.Promise) { - PromiseA = global.Promise; - PromiseA.promisify = util.promisify; - } else { - try { - PromiseA = require('bluebird'); - } catch (e) { - console.error( - 'Your version of node is missing Promise. Please run `npm install --save bluebird` in your project to fix' - ); - process.exit(10); - } - } - return PromiseA; + var util = require("util"); + var PromiseA; + if (util.promisify && global.Promise) { + PromiseA = global.Promise; + PromiseA.promisify = util.promisify; + } else { + try { + PromiseA = require("bluebird"); + } catch (e) { + console.error( + "Your version of node is missing Promise. Please run `npm install --save bluebird` in your project to fix" + ); + process.exit(10); + } + } + return PromiseA; } module.exports = getPromise(); diff --git a/test.js b/test.js index c70566b..61c326a 100644 --- a/test.js +++ b/test.js @@ -1,27 +1,33 @@ -'use strict'; +"use strict"; -var tester = require('greenlock-store-test'); +var tester = require("greenlock-store-test"); -var crypto = require('crypto'); -var os = require('os'); -var path = require('path'); -var basedir = path.join(os.tmpdir(), 'greenlock-store-fs-test-' + crypto.randomBytes(4).toString('hex')); -var domain = '*.example.com'; -var store = require('./').create({ - configDir: basedir -, accountsDir: path.join(basedir, 'accounts') -, privkeyPath: path.join(basedir, 'live', domain, 'privkey.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') +var crypto = require("crypto"); +var os = require("os"); +var path = require("path"); +var basedir = path.join( + os.tmpdir(), + "greenlock-store-fs-test-" + crypto.randomBytes(4).toString("hex") +); +var domain = "*.example.com"; +var store = require("./").create({ + configDir: basedir, + accountsDir: path.join(basedir, "accounts"), + privkeyPath: path.join(basedir, "live", domain, "privkey.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 () { - console.info("PASS"); -}).catch(function (err) { - console.error("FAIL"); - console.error(err); - process.exit(20); -}); +tester + .test(store) + .then(function() { + console.info("PASS"); + }) + .catch(function(err) { + console.error("FAIL"); + console.error(err); + process.exit(20); + }); diff --git a/utils.js b/utils.js index 6286a98..147bd87 100644 --- a/utils.js +++ b/utils.js @@ -1,51 +1,51 @@ -'use strict'; +"use strict"; var U = module.exports; // because not all file systems like '*' in a name (and they're scary) U._tameWild = function tameWild(pathname, wild) { - if (!wild) { - return pathname; - } - var tame = wild.replace(/\*/g, '_'); - return pathname.replace(wild, tame); + if (!wild) { + return pathname; + } + var tame = wild.replace(/\*/g, "_"); + return pathname.replace(wild, tame); }; U._tpl = function tpl(store, opts, str) { - var server = ['directoryUrl', 'serverDir', 'server']; - var env = ['env', 'directoryUrl']; - [ - ['basePath', 'configDir'], - server, - ['subject', 'hostname', 'domain'], - env - ].forEach(function(group) { - group.forEach(function(tmpl) { - group.forEach(function(key) { - var item = opts[key] || store.options[key]; - if ('string' !== typeof item) { - return; - } + var server = ["directoryUrl", "serverDir", "server"]; + var env = ["env", "directoryUrl"]; + [ + ["basePath", "configDir"], + server, + ["subject", "hostname", "domain"], + env + ].forEach(function(group) { + group.forEach(function(tmpl) { + group.forEach(function(key) { + var item = opts[key] || store.options[key]; + if ("string" !== typeof item) { + return; + } - if ('directoryUrl' === key) { - item = item.replace(/^https?:\/\//i, ''); - } - if ('env' === tmpl) { - if (/staging/.test(item)) { - item = 'staging'; - } else if (/acme-v02/.test(item)) { - item = 'live'; - } else { - // item = item; - } - } + if ("directoryUrl" === key) { + item = item.replace(/^https?:\/\//i, ""); + } + if ("env" === tmpl) { + if (/staging/.test(item)) { + item = "staging"; + } else if (/acme-v02/.test(item)) { + item = "live"; + } else { + // item = item; + } + } - if (-1 === str.indexOf(':' + tmpl)) { - return; - } - str = str.replace(':' + tmpl, item); - }); - }); - }); - return str; + if (-1 === str.indexOf(":" + tmpl)) { + return; + } + str = str.replace(":" + tmpl, item); + }); + }); + }); + return str; };