| 
									
										
										
										
											2019-04-05 02:26:42 -06:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-07 11:18:49 -06:00
										 |  |  | // IMPORTANT
 | 
					
						
							|  |  |  | // IMPORTANT
 | 
					
						
							|  |  |  | // IMPORTANT
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Ready? DON'T OVERTHINK IT!!! (Seriously, this is a huge problem)
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // If you get confused, you're probably smart and thinking too deep.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Want an explanation of how and why? Okay...
 | 
					
						
							|  |  |  | // https://coolaj86.com/articles/lets-encrypt-v2-step-by-step/
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // But really, you probably don't want to know how and why (because then you'd be implementing your own from scratch)
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // IMPORTANT
 | 
					
						
							|  |  |  | // IMPORTANT
 | 
					
						
							|  |  |  | // IMPORTANT
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // If you want to create a storage strategy quick-and-easy, treat everything as either dumb strings or JSON blobs
 | 
					
						
							|  |  |  | // (just as is done here), don't try to do clever optimizations, 5th normal form, etc (you ain't gonna need it),
 | 
					
						
							|  |  |  | // but DO use the simple test provided by `greenlock-store-test`.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // IMPORTANT
 | 
					
						
							|  |  |  | // IMPORTANT
 | 
					
						
							|  |  |  | // IMPORTANT
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Don't get fancy. Don't overthink it.
 | 
					
						
							|  |  |  | // If you want to be fancy and clever, do that after you can pass `greenlock-store-test` the dumb way shown here.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Also: please do contribute clarifying comments.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-05 02:26:42 -06:00
										 |  |  | module.exports.create = function (opts) { | 
					
						
							|  |  |  |   // pass in database url, connection string, filepath,
 | 
					
						
							|  |  |  |   // or whatever it is you need to get your job done well
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-07 11:32:57 -06:00
										 |  |  |   // This is our dummy in-memory storage.
 | 
					
						
							|  |  |  |   // (we optionally receive it as an option so that it can be defined outside to make testing easier)
 | 
					
						
							| 
									
										
										
										
											2019-04-05 02:26:42 -06:00
										 |  |  |   var cache = opts.cache || {}; | 
					
						
							|  |  |  |   if (!cache.accounts) { cache.accounts = {}; } | 
					
						
							|  |  |  |   if (!cache.certificates) { cache.certificates = {}; } | 
					
						
							|  |  |  |   // Although we could have two collections of keypairs,
 | 
					
						
							| 
									
										
										
										
											2019-04-07 11:32:57 -06:00
										 |  |  |   // it's also fine to store both types together (their ids will be distinct).
 | 
					
						
							| 
									
										
										
										
											2019-04-05 02:26:42 -06:00
										 |  |  |   if (!cache.keypairs) { cache.keypairs = {}; } | 
					
						
							| 
									
										
										
										
											2019-04-07 11:32:57 -06:00
										 |  |  |   // This is an in-memory store, hence we don't actually save it.
 | 
					
						
							|  |  |  |   function saveCertificate(id, blob) { cache.certificates[id] = blob; return null; } | 
					
						
							|  |  |  |   function getCertificate(id) { return cache.certificates[id]; } | 
					
						
							|  |  |  |   function saveKeypair(id, blob) { cache.keypairs[id] = blob; return null; } | 
					
						
							|  |  |  |   function getKeypair(id) { return cache.keypairs[id]; } | 
					
						
							| 
									
										
										
										
											2019-04-05 02:26:42 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   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) { | 
					
						
							| 
									
										
										
										
											2019-04-05 18:28:27 -06:00
										 |  |  |     console.log('accounts.setKeypair:', opts.account, opts.email, opts.keypair); | 
					
						
							| 
									
										
										
										
											2019-04-05 02:26:42 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     var id = opts.account.id || opts.email || 'default'; | 
					
						
							|  |  |  |     var keypair = opts.keypair; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-07 11:39:49 -06:00
										 |  |  |     return saveKeypair(id, JSON.stringify({ | 
					
						
							| 
									
										
										
										
											2019-04-05 02:26:42 -06:00
										 |  |  |       privateKeyPem: keypair.privateKeyPem | 
					
						
							|  |  |  |     , privateKeyJwk: keypair.privateKeyJwk | 
					
						
							| 
									
										
										
										
											2019-04-07 11:39:49 -06:00
										 |  |  |     })); // Must return or Promise `null` instead of `undefined`
 | 
					
						
							| 
									
										
										
										
											2019-04-05 02:26:42 -06:00
										 |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // We need a way to retrieve a prior account's keypair for renewals and additional ACME certificate "orders"
 | 
					
						
							|  |  |  |   store.accounts.checkKeypair = function (opts) { | 
					
						
							| 
									
										
										
										
											2019-04-05 18:28:27 -06:00
										 |  |  |     console.log('accounts.checkKeypair:', opts.account, opts.email); | 
					
						
							| 
									
										
										
										
											2019-04-05 02:26:42 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     var id = opts.account.id || opts.email || 'default'; | 
					
						
							| 
									
										
										
										
											2019-04-07 11:32:57 -06:00
										 |  |  |     var keyblob = getKeypair(id); | 
					
						
							| 
									
										
										
										
											2019-04-05 02:26:42 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     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; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-07 11:18:49 -06:00
										 |  |  |   // The certificate keypairs (properly named privkey.pem, though sometimes sutpidly called cert.key)
 | 
					
						
							|  |  |  |   // https://community.letsencrypt.org/t/what-are-those-pem-files/18402
 | 
					
						
							|  |  |  |   // Certificate Keypairs must not be used for Accounts and vice-versamust not be the same as any account keypair
 | 
					
						
							|  |  |  |   //
 | 
					
						
							| 
									
										
										
										
											2019-04-05 02:26:42 -06:00
										 |  |  |   store.certificates.setKeypair = function (opts) { | 
					
						
							| 
									
										
										
										
											2019-04-05 18:28:27 -06:00
										 |  |  |     console.log('certificates.setKeypair:', opts.certificate, opts.subject, opts.keypair); | 
					
						
							| 
									
										
										
										
											2019-04-05 02:26:42 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-07 11:18:49 -06:00
										 |  |  |     // The ID is a string that doesn't clash between accounts and certificates.
 | 
					
						
							|  |  |  |     // That's all you need to know... unless you're doing something special (in which case you're on your own).
 | 
					
						
							| 
									
										
										
										
											2019-04-05 02:26:42 -06:00
										 |  |  |     var id = opts.certificate.kid || opts.certificate.id || opts.subject; | 
					
						
							|  |  |  |     var keypair = opts.keypair; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-07 11:39:49 -06:00
										 |  |  |     return saveKeypair(id, JSON.stringify({ | 
					
						
							| 
									
										
										
										
											2019-04-05 02:26:42 -06:00
										 |  |  |       privateKeyPem: keypair.privateKeyPem | 
					
						
							|  |  |  |     , privateKeyJwk: keypair.privateKeyJwk | 
					
						
							| 
									
										
										
										
											2019-04-07 11:39:49 -06:00
										 |  |  |     })); // Must return or Promise `null` instead of `undefined`
 | 
					
						
							| 
									
										
										
										
											2019-04-05 02:26:42 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-07 11:39:49 -06:00
										 |  |  |     // Side Note: you can use the "keypairs" package to convert between
 | 
					
						
							|  |  |  |     // public and private for jwk and pem, as well as convert JWK <-> PEM
 | 
					
						
							| 
									
										
										
										
											2019-04-05 02:26:42 -06:00
										 |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // You won't be able to use a certificate without it's private key, gotta save it
 | 
					
						
							|  |  |  |   store.certificates.checkKeypair = function (opts) { | 
					
						
							| 
									
										
										
										
											2019-04-05 18:28:27 -06:00
										 |  |  |     console.log('certificates.checkKeypair:', opts.certificate, opts.subject); | 
					
						
							| 
									
										
										
										
											2019-04-05 02:26:42 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     var id = opts.certificate.kid || opts.certificate.id || opts.subject; | 
					
						
							| 
									
										
										
										
											2019-04-07 11:32:57 -06:00
										 |  |  |     var keyblob = getKeypair(id); | 
					
						
							| 
									
										
										
										
											2019-04-05 02:26:42 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     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) { | 
					
						
							| 
									
										
										
										
											2019-04-05 18:28:27 -06:00
										 |  |  |     console.log('certificates.set:', opts.certificate, opts.subject); | 
					
						
							| 
									
										
										
										
											2019-04-05 02:26:42 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     var id = opts.certificate.id || opts.subject; | 
					
						
							|  |  |  |     var pems = opts.pems; | 
					
						
							| 
									
										
										
										
											2019-04-07 11:39:49 -06:00
										 |  |  |     return saveCertificate(id, JSON.stringify({ | 
					
						
							| 
									
										
										
										
											2019-04-05 02:26:42 -06:00
										 |  |  |       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
 | 
					
						
							| 
									
										
										
										
											2019-04-07 11:39:49 -06:00
										 |  |  |     })); // Must return or Promise `null` instead of `undefined`
 | 
					
						
							| 
									
										
										
										
											2019-04-05 02:26:42 -06:00
										 |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // 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) { | 
					
						
							| 
									
										
										
										
											2019-04-05 18:28:27 -06:00
										 |  |  |     console.log('certificates.check:', opts.certificate, opts.subject); | 
					
						
							| 
									
										
										
										
											2019-04-05 02:26:42 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     var id = opts.certificate.id || opts.subject; | 
					
						
							| 
									
										
										
										
											2019-04-07 11:32:57 -06:00
										 |  |  |     var certblob = getCertificate(id); | 
					
						
							| 
									
										
										
										
											2019-04-05 02:26:42 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (!certblob) { return null; } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return JSON.parse(certblob); | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2019-04-05 18:28:27 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return store; | 
					
						
							| 
									
										
										
										
											2019-04-05 02:26:42 -06:00
										 |  |  | }; |