mirror of
				https://git.coolaj86.com/coolaj86/greenlock-store-memory.js.git
				synced 2025-11-04 02:52:47 +00:00 
			
		
		
		
	v1.0.0: a stupid-simple reference implementation for storage strategies
This commit is contained in:
		
						commit
						cbe20c4d00
					
				
							
								
								
									
										61
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,61 @@
 | 
				
			|||||||
 | 
					# ---> Node
 | 
				
			||||||
 | 
					# Logs
 | 
				
			||||||
 | 
					logs
 | 
				
			||||||
 | 
					*.log
 | 
				
			||||||
 | 
					npm-debug.log*
 | 
				
			||||||
 | 
					yarn-debug.log*
 | 
				
			||||||
 | 
					yarn-error.log*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Runtime data
 | 
				
			||||||
 | 
					pids
 | 
				
			||||||
 | 
					*.pid
 | 
				
			||||||
 | 
					*.seed
 | 
				
			||||||
 | 
					*.pid.lock
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Directory for instrumented libs generated by jscoverage/JSCover
 | 
				
			||||||
 | 
					lib-cov
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Coverage directory used by tools like istanbul
 | 
				
			||||||
 | 
					coverage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# nyc test coverage
 | 
				
			||||||
 | 
					.nyc_output
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
 | 
				
			||||||
 | 
					.grunt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Bower dependency directory (https://bower.io/)
 | 
				
			||||||
 | 
					bower_components
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# node-waf configuration
 | 
				
			||||||
 | 
					.lock-wscript
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Compiled binary addons (http://nodejs.org/api/addons.html)
 | 
				
			||||||
 | 
					build/Release
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Dependency directories
 | 
				
			||||||
 | 
					node_modules/
 | 
				
			||||||
 | 
					jspm_packages/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Typescript v1 declaration files
 | 
				
			||||||
 | 
					typings/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Optional npm cache directory
 | 
				
			||||||
 | 
					.npm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Optional eslint cache
 | 
				
			||||||
 | 
					.eslintcache
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Optional REPL history
 | 
				
			||||||
 | 
					.node_repl_history
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Output of 'npm pack'
 | 
				
			||||||
 | 
					*.tgz
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Yarn Integrity file
 | 
				
			||||||
 | 
					.yarn-integrity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# dotenv environment variables file
 | 
				
			||||||
 | 
					.env
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										41
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,41 @@
 | 
				
			|||||||
 | 
					Copyright 2019 AJ ONeal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This is open source software; you can redistribute it and/or modify it under the
 | 
				
			||||||
 | 
					terms of either:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   a) the "MIT License"
 | 
				
			||||||
 | 
					   b) the "Apache-2.0 License"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MIT License
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
				
			||||||
 | 
					   of this software and associated documentation files (the "Software"), to deal
 | 
				
			||||||
 | 
					   in the Software without restriction, including without limitation the rights
 | 
				
			||||||
 | 
					   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
				
			||||||
 | 
					   copies of the Software, and to permit persons to whom the Software is
 | 
				
			||||||
 | 
					   furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   The above copyright notice and this permission notice shall be included in all
 | 
				
			||||||
 | 
					   copies or substantial portions of the Software.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
				
			||||||
 | 
					   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
				
			||||||
 | 
					   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
				
			||||||
 | 
					   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			||||||
 | 
					   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
				
			||||||
 | 
					   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
				
			||||||
 | 
					   SOFTWARE.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Apache-2.0 License Summary
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					   you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					   You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					   distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					   See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					   limitations under the License.
 | 
				
			||||||
							
								
								
									
										245
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										245
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,245 @@
 | 
				
			|||||||
 | 
					# le-store-memory
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					An in-memory reference implementation of a Certificate and Keypair storage strategy for Greenlock v2.7+ (and v3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					var greenlock = require('greenlock');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This in-memory plugin has only one option: 'cache'.
 | 
				
			||||||
 | 
					// We could have It's used so that we can peek and poke at the store.
 | 
				
			||||||
 | 
					var cache = {};
 | 
				
			||||||
 | 
					var gl = greenlock.create({
 | 
				
			||||||
 | 
					  store: require('le-store-memory').create({ cache: cache })
 | 
				
			||||||
 | 
					, approveDomains: approveDomains
 | 
				
			||||||
 | 
					  ...
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# How to build your own module:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**TL;DR**: Just take a look the code here, and don't over think it.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Also, you have the flexibility to get really fancy. _Don't!_
 | 
				
			||||||
 | 
					You probably don't need to (unless you already know that you do).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In most cases you're just implementing dumb storage.
 | 
				
			||||||
 | 
					If all you do is `JSON.stringify()` on `set` (save) and `JSON.parse()` after `check` (get)
 | 
				
			||||||
 | 
					and just treat it as a blob with an ID, you'll do just fine. You can always optimize later.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Promises** vs **Thunks** ("node callbacks") vs **Synchronous** returns:
 | 
				
			||||||
 | 
					You can use whatever style you like best. Everything is promisified under the hood.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Whenever you have neither a result, nor an error, you must **always return null** (instead of 'undefined').
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### storage strategy vs approveDomains()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The _most_ important thing to keep in mind: `approveDomains()` is where all of the implementation-specific logic goes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If you're writing a storage strategy (presumably why you're here), it's because you have logic in `approveDomains()`
 | 
				
			||||||
 | 
					that isn't supported by existing strategies. That makes it tempting to start thinking about things backwards, letting
 | 
				
			||||||
 | 
					your implementation-specific logic creep into your storage strategy. DON'T DO IT.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Keep in mind that, ultimately, **it takes human decision** / interaction / configuration to add, remove, or
 | 
				
			||||||
 | 
					**modify the collection of domains** that are allowed, and how many / which domains are listed on each certificate -
 | 
				
			||||||
 | 
					all of which is a _completely_ separate process that lives outside of Greenlock (i.e uploading a site to a new folder).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The coupling between the method chosen for storage and the method chosen for approval is inherint, but keep it loose.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Lastly, it would be appropriate to include an example `approveDomains()` with your implementation for reference.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 0. approveDomains() is the kick off
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					`approveDomains()` is called only when there is no certificate for a given domain in Greenlock's internal cache
 | 
				
			||||||
 | 
					and when that certificate is "renewable" (typically 15 days before expiration, which is configurable).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The user (perhaps you) will have checked in their database (or config file or file system) and retrieved relevant
 | 
				
			||||||
 | 
					details (email associated with the domain, related domains that belong as altnames on the certificate, etc).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Those options will be available to _all_ storage and challenge strategies. In fact, they can even change which
 | 
				
			||||||
 | 
					strategy is used (i.e. some users using a Digital Ocean strategy for DNS challenges, others using Route53).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					function approveDomains(opts) {
 | 
				
			||||||
 | 
					  var info = userDb.getInfo(opts.domain);
 | 
				
			||||||
 | 
					  if (!info) { throw new Error("ignoring junk request, bad domain"); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  opts.email = info.certificateOwner;
 | 
				
			||||||
 | 
					  opts.subject = info.certificateSubject
 | 
				
			||||||
 | 
					  opts.domains = info.certificateAltnames;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return opts; // or Promise.resolve(opts);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 1. Implement `accounts.setKeypair`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					First, you should implement `accounts.setKeypair()`. Just treat it like dumb storage.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This only gets called after a new account has already been created successfully.
 | 
				
			||||||
 | 
					That will only happen when a completely new certificate is going to be issued (not renewal),
 | 
				
			||||||
 | 
					and there's no user account already associate with that set of domains.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					store.accounts.setKeypair = function (opts) {
 | 
				
			||||||
 | 
					  console.log('accounts.setKeypair:', opts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  var id = opts.account.id || opts.email || 'default';
 | 
				
			||||||
 | 
					  var keypair = opts.keypair;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  cache.accountKeypairs[id] = JSON.stringify({
 | 
				
			||||||
 | 
					    privateKeyPem: keypair.privateKeyPem
 | 
				
			||||||
 | 
					  , privateKeyJwk: keypair.privateKeyJwk
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return null; // or Promise.resolve(null);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 2. Implement `accounts.checkKeypair`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Whatever you did above, you just do the opposite instead. Tada!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					store.accounts.checkKeypair = function (opts) {
 | 
				
			||||||
 | 
					  console.log('accounts.checkKeypair:', opts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  var id = opts.account.id || opts.email || 'default';
 | 
				
			||||||
 | 
					  var keyblob = cache.accountKeypairs[id];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!keyblob) { return null; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return JSON.parse(keyblob);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 3. (and 4.) Optionally save ACME account metadata
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You should probably skip this and not worry about it.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					However, if you have a special need for it, or if you want to shave off an ACME API call,
 | 
				
			||||||
 | 
					you can save the account `kid` (a misnomer intended to mean "key id", but actually refers
 | 
				
			||||||
 | 
					to an arbitrary ACME URL, used to identify the account).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					store.accounts.set = function (opts) {
 | 
				
			||||||
 | 
					  console.log('accounts.set:', opts);
 | 
				
			||||||
 | 
					  return null;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					store.accounts.check = function (opts) {
 | 
				
			||||||
 | 
					  var id = opts.account.id || opts.email || 'default';
 | 
				
			||||||
 | 
					  console.log('accounts.check:', opts);
 | 
				
			||||||
 | 
					  return null;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If you don't implement these the account key will be used to "recover" the `kid` as necessary.
 | 
				
			||||||
 | 
					You don't have to worry though, it doesn't create a duplicate accounts or have any other negative
 | 
				
			||||||
 | 
					side affects other than an additional API call as needed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 5. Implement a method to save certificate keypairs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Each certificate is supposed to have a unique keypair, which **must not** be the same as the account keypair.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Again, just treat it like a blob in dumb storage and you'll be fine.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This is the same as `accounts.setKeypair()`, but using a different idea.
 | 
				
			||||||
 | 
					You could even use the same data store in most cases because the IDs aren't likely to clash.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					store.certificates.setKeypair = function (opts) {
 | 
				
			||||||
 | 
					  console.log('certificates.setKeypair:', opts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  var id = opts.certificate.kid || opts.certificate.id || opts.subject;
 | 
				
			||||||
 | 
					  var keypair = opts.keypair;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  cache.certificateKeypairs[id] = JSON.stringify({
 | 
				
			||||||
 | 
					    privateKeyPem: keypair.privateKeyPem
 | 
				
			||||||
 | 
					  , privateKeyJwk: keypair.privateKeyJwk
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return null;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 6. Implement a method to get certificate keypairs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You know the drill. Same as `accounts.checkKeypair()`, but a different ID.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This isn't called until after the certificate retrieval is successful.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Note: Every account **must have** a unique account key and account keys are
 | 
				
			||||||
 | 
					**not allowed** to be used as certificate keys. However, you could use the
 | 
				
			||||||
 | 
					same certificate key for all domains on a device (i.e. a server) or an account.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					store.certificates.checkKeypair = function (opts) {
 | 
				
			||||||
 | 
					  console.log('certificates.checkKeypair:', opts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  var id = opts.certificate.kid || opts.certificate.id || opts.subject;
 | 
				
			||||||
 | 
					  var keyblob = cache.certificateKeypairs[id];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!keyblob) { return null; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return JSON.parse(keyblob);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 7. Implement a method to save certificates
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Whenever the ACME process completes successfully, you get a shiny new certificate with all of the domains you requested.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It's a good idea to save them - otherwise you run the risk of running up your rate limit and getting blocked
 | 
				
			||||||
 | 
					as your server restarts, respawns, auto-scales, etc.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					store.certificates.set = function (opts) {
 | 
				
			||||||
 | 
					  console.log('certificates.set:', opts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  var id = opts.certificate.id || opts.subject;
 | 
				
			||||||
 | 
					  var pems = opts.pems;
 | 
				
			||||||
 | 
					  cache.certificates[id] = JSON.stringify({
 | 
				
			||||||
 | 
					    cert: pems.cert
 | 
				
			||||||
 | 
					  , chain: pems.chain
 | 
				
			||||||
 | 
					  , subject: pems.subject
 | 
				
			||||||
 | 
					  , altnames: pems.altnames
 | 
				
			||||||
 | 
					  , issuedAt: pems.issuedAt   // a.k.a. NotBefore
 | 
				
			||||||
 | 
					  , expiresAt: pems.expiresAt // a.k.a. NotAfter
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return null;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Note that `chain` is likely to be the same for all certificates issued by a service,
 | 
				
			||||||
 | 
					but there's no guarantee. The service may rotate which keys do the signing, for example.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 8. Implement a method to get certificates
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Lastly, you just need a way to fetch the result of all the work you've done.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					store.certificates.check = function (opts) {
 | 
				
			||||||
 | 
					  console.log('certificates.check:', opts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  var id = opts.certificate.id || opts.subject;
 | 
				
			||||||
 | 
					  var certblob = cache.certificates[id];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!certblob) { return null; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return JSON.parse(certblob);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Huzzah!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					There you go - you basically just have 8 setter and getter functions that usually act as dumb storage,
 | 
				
			||||||
 | 
					but that you can tweak with custom options if you need to.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Remember: Keep It Stupid-Simple
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					:D
 | 
				
			||||||
							
								
								
									
										144
									
								
								index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								index.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,144 @@
 | 
				
			|||||||
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports.create = function (opts) {
 | 
				
			||||||
 | 
					  // pass in database url, connection string, filepath,
 | 
				
			||||||
 | 
					  // or whatever it is you need to get your job done well
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // This is our in-memory storage.
 | 
				
			||||||
 | 
					  // We take it from the outside to make testing the dummy module easier.
 | 
				
			||||||
 | 
					  var cache = opts.cache || {};
 | 
				
			||||||
 | 
					  if (!cache.accounts) { cache.accounts = {}; }
 | 
				
			||||||
 | 
					  if (!cache.certificates) { cache.certificates = {}; }
 | 
				
			||||||
 | 
					  // Although we could have two collections of keypairs,
 | 
				
			||||||
 | 
					  // it's also fine to store both types together.
 | 
				
			||||||
 | 
					  if (!cache.keypairs) { cache.keypairs = {}; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  var store = {};
 | 
				
			||||||
 | 
					  // any options you need per instance
 | 
				
			||||||
 | 
					  // (probably okay to leave empty)
 | 
				
			||||||
 | 
					  store.options = {};
 | 
				
			||||||
 | 
					  store.accounts = {};
 | 
				
			||||||
 | 
					  store.certificates = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Whenever a new keypair is used to successfully create an account, we need to save its keypair
 | 
				
			||||||
 | 
					  store.accounts.setKeypair = function (opts) {
 | 
				
			||||||
 | 
					    console.log('accounts.setKeypair:', opts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var id = opts.account.id || opts.email || 'default';
 | 
				
			||||||
 | 
					    var keypair = opts.keypair;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cache.keypairs[id] = JSON.stringify({
 | 
				
			||||||
 | 
					      privateKeyPem: keypair.privateKeyPem
 | 
				
			||||||
 | 
					    , privateKeyJwk: keypair.privateKeyJwk
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return null; // or Promise.resolve(null);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // We need a way to retrieve a prior account's keypair for renewals and additional ACME certificate "orders"
 | 
				
			||||||
 | 
					  store.accounts.checkKeypair = function (opts) {
 | 
				
			||||||
 | 
					    console.log('accounts.checkKeypair:', opts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var id = opts.account.id || opts.email || 'default';
 | 
				
			||||||
 | 
					    var keyblob = cache.keypairs[id];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!keyblob) { return null; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return JSON.parse(keyblob);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // We can optionally implement ACME account storage and retrieval
 | 
				
			||||||
 | 
					  // (to reduce API calls), but it's really not necessary.
 | 
				
			||||||
 | 
					  /*
 | 
				
			||||||
 | 
					    store.accounts.set = function (opts) {
 | 
				
			||||||
 | 
					      console.log('accounts.set:', opts);
 | 
				
			||||||
 | 
					      return null;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    store.accounts.check = function (opts) {
 | 
				
			||||||
 | 
					      var id = opts.account.id || opts.email || 'default';
 | 
				
			||||||
 | 
					      console.log('accounts.check:', opts);
 | 
				
			||||||
 | 
					      return null;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // The certificate keypairs must not be the same as any account keypair
 | 
				
			||||||
 | 
					  store.certificates.setKeypair = function (opts) {
 | 
				
			||||||
 | 
					    console.log('certificates.setKeypair:', opts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var id = opts.certificate.kid || opts.certificate.id || opts.subject;
 | 
				
			||||||
 | 
					    var keypair = opts.keypair;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cache.keypairs[id] = JSON.stringify({
 | 
				
			||||||
 | 
					      privateKeyPem: keypair.privateKeyPem
 | 
				
			||||||
 | 
					    , privateKeyJwk: keypair.privateKeyJwk
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    // Note: you can use the "keypairs" package to convert between
 | 
				
			||||||
 | 
					    // public and private for jwk and pem, as well as convert JWK <-> PEM
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // You won't be able to use a certificate without it's private key, gotta save it
 | 
				
			||||||
 | 
					  store.certificates.checkKeypair = function (opts) {
 | 
				
			||||||
 | 
					    console.log('certificates.checkKeypair:', opts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var id = opts.certificate.kid || opts.certificate.id || opts.subject;
 | 
				
			||||||
 | 
					    var keyblob = cache.keypairs[id];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!keyblob) { return null; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return JSON.parse(keyblob);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // And you'll also need to save certificates. You may find the metadata useful to save
 | 
				
			||||||
 | 
					  // (perhaps to delete expired keys), but the same information can also be redireved from
 | 
				
			||||||
 | 
					  // the key using the "cert-info" package.
 | 
				
			||||||
 | 
					  store.certificates.set = function (opts) {
 | 
				
			||||||
 | 
					    console.log('certificates.set:', opts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var id = opts.certificate.id || opts.subject;
 | 
				
			||||||
 | 
					    var pems = opts.pems;
 | 
				
			||||||
 | 
					    cache.certificates[id] = JSON.stringify({
 | 
				
			||||||
 | 
					      cert: pems.cert
 | 
				
			||||||
 | 
					    , chain: pems.chain
 | 
				
			||||||
 | 
					    , subject: pems.subject
 | 
				
			||||||
 | 
					    , altnames: pems.altnames
 | 
				
			||||||
 | 
					    , issuedAt: pems.issuedAt   // a.k.a. NotBefore
 | 
				
			||||||
 | 
					    , expiresAt: pems.expiresAt // a.k.a. NotAfter
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // This is actually the first thing to be called after approveDomins(),
 | 
				
			||||||
 | 
					  // but it's easiest to implement last since it's not useful until there
 | 
				
			||||||
 | 
					  // are certs that can actually be loaded from storage.
 | 
				
			||||||
 | 
					  store.certificates.check = function (opts) {
 | 
				
			||||||
 | 
					    console.log('certificates.check:', opts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var id = opts.certificate.id || opts.subject;
 | 
				
			||||||
 | 
					    var certblob = cache.certificates[id];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!certblob) { return null; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return JSON.parse(certblob);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										5
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "name": "le-store-memory",
 | 
				
			||||||
 | 
					  "version": "1.0.0",
 | 
				
			||||||
 | 
					  "lockfileVersion": 1
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										29
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "name": "le-store-memory",
 | 
				
			||||||
 | 
					  "version": "1.0.0",
 | 
				
			||||||
 | 
					  "description": "An in-memory reference implementation for account, certificate, and keypair storage strategies in Greenlock",
 | 
				
			||||||
 | 
					  "homepage": "https://git.coolaj86.com/coolaj86/le-store-memory.js",
 | 
				
			||||||
 | 
					  "main": "index.js",
 | 
				
			||||||
 | 
					  "directories": {
 | 
				
			||||||
 | 
					    "test": "tests"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "scripts": {
 | 
				
			||||||
 | 
					    "test": "node tests"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "repository": {
 | 
				
			||||||
 | 
					    "type": "git",
 | 
				
			||||||
 | 
					    "url": "https://git.coolaj86.com/coolaj86/le-store-memory.js.git"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "keywords": [
 | 
				
			||||||
 | 
					    "greenlock",
 | 
				
			||||||
 | 
					    "acme",
 | 
				
			||||||
 | 
					    "json",
 | 
				
			||||||
 | 
					    "keypairs",
 | 
				
			||||||
 | 
					    "certificates",
 | 
				
			||||||
 | 
					    "store",
 | 
				
			||||||
 | 
					    "database"
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
 | 
				
			||||||
 | 
					  "license": "MPL-2.0",
 | 
				
			||||||
 | 
					  "dependencies": {}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user