203 lines
8.4 KiB
JavaScript
203 lines
8.4 KiB
JavaScript
|
'use strict';
|
||
|
/*global Promise*/
|
||
|
|
||
|
module.exports.create = function () {
|
||
|
throw new Error("greenlock-store-test is a test fixture for greenlock-store-* plugins, not a plugin itself");
|
||
|
};
|
||
|
|
||
|
// ignore all of this, it's just to normalize Promise vs node-style callback thunk vs synchronous
|
||
|
function promiseCheckAndCatch(obj, name, mergables) {
|
||
|
var promisify = require('util').promisify;
|
||
|
// don't loose this-ness, just in case that's important
|
||
|
var fn = obj[name].bind(obj);
|
||
|
var promiser;
|
||
|
|
||
|
// function signature must match, or an error will be thrown
|
||
|
if (1 === fn.length) {
|
||
|
// wrap so that synchronous errors are caught (alsa handles synchronous results)
|
||
|
promiser = function (opts) {
|
||
|
return Promise.resolve().then(function () {
|
||
|
return fn(merge(mergables, opts));
|
||
|
});
|
||
|
};
|
||
|
} else if (2 === fn.length) {
|
||
|
// wrap as a promise
|
||
|
promiser = promisify(function (opts, cb) {
|
||
|
return fn(merge(mergables, opts), cb);
|
||
|
});
|
||
|
} else {
|
||
|
return Promise.reject(new Error("'store." + name + "' should accept either one argument, the options,"
|
||
|
+ " and return a Promise or accept two arguments, the options and a node-style callback thunk"));
|
||
|
}
|
||
|
|
||
|
function shouldntBeNull(result) {
|
||
|
if ('undefined' === typeof result) {
|
||
|
throw new Error("'store.'" + name + "' should never return `undefined`. Please explicitly return null"
|
||
|
+ " (or fix the place where a value should have been returned but wasn't).");
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
return function (opts) {
|
||
|
return promiser(opts).then(shouldntBeNull);
|
||
|
};
|
||
|
}
|
||
|
function merge(one, two) {
|
||
|
var three = {};
|
||
|
Object.keys(one||{}).forEach(function (k) {
|
||
|
if ('undefined' !== typeof one[k]) {
|
||
|
three[k] = one[k];
|
||
|
}
|
||
|
});
|
||
|
Object.keys(two||{}).forEach(function (k) {
|
||
|
if ('undefined' !== typeof two[k]) {
|
||
|
three[k] = two[k];
|
||
|
}
|
||
|
});
|
||
|
return three;
|
||
|
}
|
||
|
|
||
|
// Here's the meat, where the tests are happening:
|
||
|
module.exports.test = function (store) {
|
||
|
var subject = '*.example.com';
|
||
|
var email = 'jon.doe@sbemail.com';
|
||
|
|
||
|
// TODO go just barely beyond dumb storage and provide real keys using 'keypairs.js'
|
||
|
var accountKey = { privateKeyPem: 'AccountKey', privateKeyJwk: { account: true } };
|
||
|
var domainKey = { privateKeyPem: 'DomainKey', privateKeyJwk: { domain: true } };
|
||
|
var domainCert = {
|
||
|
cert: 'DomainCert', chain: 'DomainChain', privkey: 'DomainKey'
|
||
|
, subject: '*.example.com', altnames: ['*.example.com', 'example.com']
|
||
|
, issuedAt: new Date().valueOf(), expiresAt: new Date(Date.now() + 3600000).valueOf()
|
||
|
};
|
||
|
|
||
|
var checkCerts = promiseCheckAndCatch(store.certificates, 'check', store.options);
|
||
|
var checkCertKey = promiseCheckAndCatch(store.certificates, 'checkKeypair', store.options);
|
||
|
var checkAccountKey = promiseCheckAndCatch(store.accounts, 'checkKeypair', store.options);
|
||
|
var setAccountKey = promiseCheckAndCatch(store.accounts, 'setKeypair', store.options);
|
||
|
var setCertKey = promiseCheckAndCatch(store.certificates, 'setKeypair', store.options);
|
||
|
var setCert = promiseCheckAndCatch(store.certificates, 'set', store.options);
|
||
|
|
||
|
return checkCerts({ certificate: {}, subject: subject }).then(function (results) {
|
||
|
if (null !== results) {
|
||
|
throw new Error("should get null when checking certificates for " + subject);
|
||
|
}
|
||
|
}).then(function () {
|
||
|
// TODO check accounts.check
|
||
|
var opts = { account: {}, email: email, subject: subject, certificate: {} };
|
||
|
return checkAccountKey(opts).then(function (results) {
|
||
|
if (null !== results) {
|
||
|
throw new Error("should not have an account keypair for " + subject);
|
||
|
}
|
||
|
});
|
||
|
}).then(function () {
|
||
|
var opts = { account: {}, email: email, keypair: accountKey, subject: subject, certificate: {} };
|
||
|
return setAccountKey(opts).then(function (results) {
|
||
|
if ('undefined' === typeof results) {
|
||
|
throw new Error("should never get undefined as a return value, did you forget to return?");
|
||
|
}
|
||
|
});
|
||
|
}).then(function () {
|
||
|
var opts = { certificate: {}, subject: subject, keypair: domainKey, email: email, account: {} };
|
||
|
return setCertKey(opts).then(function (results) {
|
||
|
if ('undefined' === typeof results) {
|
||
|
throw new Error("should never get undefined as a return value, did you forget to return?");
|
||
|
}
|
||
|
});
|
||
|
}).then(function () {
|
||
|
var opts = { certificate: {}, subject: subject, pems: domainCert, email: email, account: {} };
|
||
|
return setCert(opts).then(function (results) {
|
||
|
if ('undefined' === typeof results) {
|
||
|
throw new Error("should never get undefined as a return value, did you forget to return?");
|
||
|
}
|
||
|
});
|
||
|
}).then(function () {
|
||
|
var opts = { certificate: {}, subject: subject, email: email, account: {} };
|
||
|
return checkCerts(opts).then(function (results) {
|
||
|
if (!results || !results.chain || !results.cert) {
|
||
|
throw new Error("expected to get certificate and chain for " + subject);
|
||
|
}
|
||
|
if (domainCert.cert.replace(/\r\n/g, '\n').trim() !== results.cert.replace(/\r\n/g, '\n').trim()) {
|
||
|
throw new Error("expected to get exactly the same certificate (aside from CRLF and ending newline) as was set");
|
||
|
}
|
||
|
if (domainCert.chain.replace(/\r\n/g, '\n').trim() !== results.chain.replace(/\r\n/g, '\n').trim()) {
|
||
|
throw new Error("expected to get exactly the same chain (aside from CRLF and ending newline) as was set");
|
||
|
}
|
||
|
if (results.privkey) {
|
||
|
if (domainCert.chain.replace(/\r\n/g, '\n').trim() !== results.chain.replace(/\r\n/g, '\n').trim()) {
|
||
|
throw new Error("privkey is optional, but when it's set it should be the same string PEM as was set");
|
||
|
}
|
||
|
}
|
||
|
if (results.altnames) {
|
||
|
if (domainCert.altnames.join(' ') !== results.altnames.join(' ')) {
|
||
|
throw new Error("altnames sholud be the same type and value as was set");
|
||
|
}
|
||
|
}
|
||
|
if (results.subject) {
|
||
|
if (domainCert.subject !== results.subject) {
|
||
|
throw new Error("subject sholud be the same type and value as was set");
|
||
|
}
|
||
|
}
|
||
|
if (results.issuedAt) {
|
||
|
if (domainCert.issuedAt !== results.issuedAt) {
|
||
|
throw new Error("issuedAt sholud be the same type and value as was set");
|
||
|
}
|
||
|
}
|
||
|
if (results.expiresAt) {
|
||
|
if (domainCert.expiresAt !== results.expiresAt) {
|
||
|
throw new Error("expiresAt sholud be the same type and value as was set");
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
}).then(function () {
|
||
|
var opts = { certificate: {}, subject: subject, email: email, account: {} };
|
||
|
return checkCertKey(opts).then(function (results) {
|
||
|
if (!results) {
|
||
|
throw new Error("Should have found a keypair for " + opts.subject);
|
||
|
}
|
||
|
if (!results.privateKeyPem && !results.privateKeyJwk) {
|
||
|
throw new Error("Should have received privateKeyPem and/or privateKeyJwk, but got neither: "
|
||
|
+ JSON.stringify(results));
|
||
|
}
|
||
|
if (results.privateKeyPem) {
|
||
|
if (domainCert.privkey.replace(/\r\n/g, '\n').trim() !== results.privateKeyPem.replace(/\r\n/g, '\n').trim()) {
|
||
|
throw new Error("privateKeyPem should be the same string PEM as was set");
|
||
|
}
|
||
|
}
|
||
|
if (results.privateKeyJwk) {
|
||
|
if (Object.keys(domainKey.privateKeyJwk).sort().join(' ')
|
||
|
!== Object.keys(results.privateKeyJwk).sort().join(' ')
|
||
|
) {
|
||
|
throw new Error("privateKeyJwk should be the same string Jwk as was set");
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
}).then(function () {
|
||
|
var opts = { account: {}, email: email, subject: subject, certificate: {} };
|
||
|
return checkAccountKey(opts).then(function (results) {
|
||
|
if (!results) {
|
||
|
throw new Error("Should have found a keypair for " + opts.email);
|
||
|
}
|
||
|
if (!results.privateKeyPem && !results.privateKeyJwk) {
|
||
|
throw new Error("Should have received privateKeyPem and/or privateKeyJwk, but got neither: "
|
||
|
+ JSON.stringify(results));
|
||
|
}
|
||
|
if (results.privateKeyPem) {
|
||
|
if (accountKey.privateKeyPem.replace(/\r\n/g, '\n').trim()
|
||
|
!== results.privateKeyPem.replace(/\r\n/g, '\n').trim()
|
||
|
) {
|
||
|
throw new Error("privateKeyPem should be the same string PEM as was set");
|
||
|
}
|
||
|
}
|
||
|
if (results.privateKeyJwk) {
|
||
|
var akeys = Object.keys(accountKey.privateKeyJwk).sort().join(' ');
|
||
|
var bkeys = Object.keys(results.privateKeyJwk).sort().join(' ');
|
||
|
if (akeys !== bkeys) {
|
||
|
throw new Error("privateKeyJwk should be the same string Jwk as was set."
|
||
|
+ "\nExpected: " + akeys + "\nActual: " + bkeys);
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
};
|