"use strict"; var os = require("os"); var path = require("path"); // How Storage Works in Greenlock: High-Level Call Stack // // nested === skipped if parent succeeds (or has cached result) // // tls.SNICallback() // TLS connection with SNI kicks of the request // // greenlock.approveDomains(opts) // Greenlokc does some housekeeping, checks for a cert in // // an internal cache, and only asks you to approve new // // certificate // registration if it doesn't find anything. // // In `opts` you'll receive `domain` and a few other things. // // You should return { subject: '...', altnames: ['...'] } // // Anything returned by approveDomains() will be received // // by all plugins at all stages // // greenlock.store.certificates.check() // Certificate checking happens after approval for several // // reasons, including preventing duplicate registrations // // but most importantly because you can dynamically swap the // // storage plugin right from approveDomains(). // greenlock.store.certificates.checkKeypair() // Check for a keypair associated with the domain // // greenlock.store.accounts.check() // Optional. If you need it, look at other Greenlock docs // // greenlock.store.accounts.checkKeypair() // Check storage for registered account key // (opts.generateKeypair||RSA.generateKeypair)() // Generates a new keypair // greenlock.core.accounts.register() // Registers the keypair as an ACME account // greenlock.store.accounts.setKeypair() // Saves the keypair of the registered account // greenlock.store.accounts.set() // Optional. Saves superfluous ACME account metadata // // greenlock.core.certificates.register() // Begin certificate registration process & housekeeping // (opts.generateKeypair||RSA.generateKeypair)() // Generates a new certificate keypair // greenlock.acme.certificates.register() // Performs the ACME challenge processes // greenlock.store.certificates.setKeypair() // Saves the keypair for the valid certificate // greenlock.store.certificates.set() // Saves the valid certificate //////////////////////////////////////////// // Recap of the high-level overview above // //////////////////////////////////////////// // // None of this ever gets called except if there's not a cert already cached. // That only happens on service boot, and about every 75 days for each cert's renewal. // // Therefore, none of this needs to be fast, fancy, or clever // // For any type of customization, whatever is set in `approveDomains()` is available everywhere else. // 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. // 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; // 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); } return store; }; /////////////////////////////////////////////////////////////////////////////// // Ignore // /////////////////////////////////////////////////////////////////////////////// // // Everything below this line is just implementation specific var defaults = { 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") }; defaults.configDir = defaults.basePath; function mergeOptions(configs) { if (!configs.serverKeyPath) { configs.serverKeyPath = configs.domainKeyPath || configs.privkeyPath || defaults.privkeyPath; } Object.keys(defaults).forEach(function(key) { if (!configs[key]) { configs[key] = defaults[key]; } }); return configs; }