update template, add tests
This commit is contained in:
parent
a0bda23683
commit
00157ab870
44
index.js
44
index.js
|
@ -9,20 +9,27 @@ module.exports.create = function (options) {
|
||||||
|
|
||||||
|
|
||||||
var accounts = {
|
var accounts = {
|
||||||
checkKeypair: function (opts, cb) {
|
|
||||||
// opts.email // optional
|
|
||||||
// opts.accountId // optional
|
|
||||||
|
|
||||||
// check db and return null or keypair object with one of privateKeyPem or privateKeyJwk
|
// Accounts
|
||||||
cb(null, { privateKeyPem: '...', privateKeyJwk: {} });
|
setKeypair: function (opts, keypair, cb) {
|
||||||
}
|
|
||||||
, setKeypair: function (opts, keypair, cb) {
|
|
||||||
// opts.email // optional
|
// opts.email // optional
|
||||||
// opts.accountId // optional
|
// opts.accountId // optional
|
||||||
|
|
||||||
// SAVE to db (as PEM and/or JWK) and index each domain in domains to this keypair
|
// SAVE to db (as PEM and/or JWK) and index each domain in domains to this keypair
|
||||||
cb(null, keypair);
|
cb(null, keypair);
|
||||||
}
|
}
|
||||||
|
// Accounts
|
||||||
|
, checkKeypair: function (opts, cb) {
|
||||||
|
// opts.email // optional
|
||||||
|
// opts.accountId // optional
|
||||||
|
|
||||||
|
// check db and return null or keypair object with one of privateKeyPem or privateKeyJwk
|
||||||
|
cb(null, { privateKeyPem: '...', privateKeyJwk: {} });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Accounts
|
||||||
, check: function (opts, cb) {
|
, check: function (opts, cb) {
|
||||||
// opts.email // optional
|
// opts.email // optional
|
||||||
// opts.accountId // optional
|
// opts.accountId // optional
|
||||||
|
@ -31,6 +38,7 @@ module.exports.create = function (options) {
|
||||||
// return account from db if it exists, otherwise null
|
// return account from db if it exists, otherwise null
|
||||||
cb(null, { id: '...', keypair: { privateKeyJwk: {} }, domains: [] });
|
cb(null, { id: '...', keypair: { privateKeyJwk: {} }, domains: [] });
|
||||||
}
|
}
|
||||||
|
// Accounts
|
||||||
, set: function (opts, reg, cb) {
|
, set: function (opts, reg, cb) {
|
||||||
// opts.email
|
// opts.email
|
||||||
// reg.keypair
|
// reg.keypair
|
||||||
|
@ -39,23 +47,31 @@ module.exports.create = function (options) {
|
||||||
|
|
||||||
cb(null, { id: '...', email: opts.email, keypair: reg.keypair, receipt: reg.receipt });
|
cb(null, { id: '...', email: opts.email, keypair: reg.keypair, receipt: reg.receipt });
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var certificates = {
|
var certificates = {
|
||||||
checkKeypair: function (opts, cb) {
|
|
||||||
// opts.domains
|
|
||||||
|
|
||||||
// check db and return null or keypair object with one of privateKeyPem or privateKeyJwk
|
// Certificates
|
||||||
cb(null, { privateKeyPem: '...', privateKeyJwk: {} });
|
setKeypair: function (opts, keypair, cb) {
|
||||||
}
|
|
||||||
, setKeypair: function (opts, keypair, cb) {
|
|
||||||
// opts.domains
|
// opts.domains
|
||||||
|
|
||||||
// SAVE to db (as PEM and/or JWK) and index each domain in domains to this keypair
|
// SAVE to db (as PEM and/or JWK) and index each domain in domains to this keypair
|
||||||
cb(null, keypair);
|
cb(null, keypair);
|
||||||
}
|
}
|
||||||
|
// Certificates
|
||||||
|
, checkKeypair: function (opts, cb) {
|
||||||
|
// opts.domains
|
||||||
|
|
||||||
|
// check db and return null or keypair object with one of privateKeyPem or privateKeyJwk
|
||||||
|
cb(null, { privateKeyPem: '...', privateKeyJwk: {} });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Certificates
|
||||||
, check: function (opts, cb) {
|
, check: function (opts, cb) {
|
||||||
// You will be provided one of these (which should be tried in this order)
|
// You will be provided one of these (which should be tried in this order)
|
||||||
// opts.domains
|
// opts.domains
|
||||||
|
@ -67,6 +83,7 @@ module.exports.create = function (options) {
|
||||||
// (otherwise they will be read from the cert itself later)
|
// (otherwise they will be read from the cert itself later)
|
||||||
cb(null, { privkey: 'PEM', cert: 'PEM', chain: 'PEM', domains: [], accountId: '...' });
|
cb(null, { privkey: 'PEM', cert: 'PEM', chain: 'PEM', domains: [], accountId: '...' });
|
||||||
}
|
}
|
||||||
|
// Certificates
|
||||||
, set: function (opts, pems, cb) {
|
, set: function (opts, pems, cb) {
|
||||||
// opts.domains
|
// opts.domains
|
||||||
// opts.email // optional
|
// opts.email // optional
|
||||||
|
@ -79,6 +96,7 @@ module.exports.create = function (options) {
|
||||||
// SAVE to the database, index the email address, the accountId, and alias the domains
|
// SAVE to the database, index the email address, the accountId, and alias the domains
|
||||||
cb(null, pems);
|
cb(null, pems);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,352 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var PromiseA = require('bluebird');
|
||||||
|
var leStore = PromiseA.promisifyAll(require('../').create({
|
||||||
|
debug: true
|
||||||
|
}));
|
||||||
|
leStore.accounts = PromiseA.promisifyAll(leStore.accounts);
|
||||||
|
leStore.certificates = PromiseA.promisifyAll(leStore.certificates);
|
||||||
|
|
||||||
|
// fixtures
|
||||||
|
var doesntExist = {
|
||||||
|
email: 'e@gmail.co'
|
||||||
|
, accountId: 'eee'
|
||||||
|
};
|
||||||
|
var goodGuy = {
|
||||||
|
email: 'goodguy@gmail.com'
|
||||||
|
, keypair: {
|
||||||
|
privateKeyPem: 'PRIVKEY.PEM', privateKeyJwk: { e: 'EXPO', n: 'MODULO' }
|
||||||
|
, publicKeyPem: 'PUBKEY.PEM'/*, publicKeyJwk: would be reduntdant */
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var tests = [
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// SANITY CHECKS
|
||||||
|
//
|
||||||
|
|
||||||
|
// SANITY test that an unregistered email returns no results
|
||||||
|
function () {
|
||||||
|
return leStore.accounts.checkKeypairAsync({
|
||||||
|
email: doesntExist.email
|
||||||
|
}).then(function (keypair) {
|
||||||
|
if (null !== keypair) {
|
||||||
|
throw new Error("Should return `null` when keypair does not exist by `email`.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// SANITY test that an unregistered account id returns no results
|
||||||
|
, function () {
|
||||||
|
return leStore.accounts.checkAsync({
|
||||||
|
accountId: doesntExist.accountId
|
||||||
|
}).then(function (account) {
|
||||||
|
if (null !== account) {
|
||||||
|
throw new Error("Should return `null` when account does not exist by `accountId`.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// SANITY test that an unregistered email returns no results
|
||||||
|
, function () {
|
||||||
|
return leStore.accounts.checkAsync({
|
||||||
|
email: doesntExist.email
|
||||||
|
}).then(function (account) {
|
||||||
|
if (null !== account) {
|
||||||
|
throw new Error("Should return `null` when account does not exist by `accountId`.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Creating Account Keypairs
|
||||||
|
//
|
||||||
|
|
||||||
|
// Register a private key to an email
|
||||||
|
// and make sure agreeTos remains falsey
|
||||||
|
, function () {
|
||||||
|
return leStore.accounts.setKeypairAsync(goodGuy, goodGuy.keypair);
|
||||||
|
}
|
||||||
|
, function () {
|
||||||
|
return leStore.accounts.checkKeypairAsync({
|
||||||
|
email: goodGuy.email
|
||||||
|
}).then(function (keypair) {
|
||||||
|
|
||||||
|
if (!keypair) {
|
||||||
|
throw new Error("should return saved keypair");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (goodGuy.keypair.privateKeyPem !== keypair.privateKeyPem) {
|
||||||
|
if (keypair.privateKeyJwk) {
|
||||||
|
throw new Error("Error in test itself (not your fault). TODO: implement checking privateKeyJwk.");
|
||||||
|
}
|
||||||
|
throw new Error("agreeTos should return false or null because it was not set.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Creating Accounts
|
||||||
|
//
|
||||||
|
|
||||||
|
// create a new account
|
||||||
|
, function () {
|
||||||
|
var account = {
|
||||||
|
receipt: {}
|
||||||
|
, agreeTos: true
|
||||||
|
};
|
||||||
|
|
||||||
|
return leStore.accounts.setAsync(goodGuy, account).then(function (account) {
|
||||||
|
if (!account || !account.id || !account.email) {
|
||||||
|
throw new Error('accounts.set should return the object with its new `id` attached');
|
||||||
|
}
|
||||||
|
|
||||||
|
goodGuy.accountId = account.id;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// get by account id
|
||||||
|
, function () {
|
||||||
|
return leStore.accounts.checkAsync({
|
||||||
|
accountId: goodGuy.accountId
|
||||||
|
}).then(function (account) {
|
||||||
|
|
||||||
|
if (!account) {
|
||||||
|
throw new Error("Did not find account.");
|
||||||
|
}
|
||||||
|
else if (!account.keypair) {
|
||||||
|
throw new Error("Account did not have a keypair.");
|
||||||
|
}
|
||||||
|
else if (goodGuy.keypair.privateKeyPem !== account.keypair.privateKeyPem) {
|
||||||
|
if (account.keypair.privateKeyJwk) {
|
||||||
|
throw new Error("Error in test itself (not your fault). TODO: implement checking privateKeyJwk.");
|
||||||
|
}
|
||||||
|
throw new Error("agreeTos should return false or null because it was not set.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!account.email) {
|
||||||
|
throw new Error("should have returned email");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!account.agreeTos) {
|
||||||
|
throw new Error("should have returned agreeTos");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!account.receipt) {
|
||||||
|
throw new Error("should have returned receipt");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// get by email
|
||||||
|
, function () {
|
||||||
|
return leStore.accounts.checkAsync({
|
||||||
|
email: goodGuy.email
|
||||||
|
}).then(function (account) {
|
||||||
|
|
||||||
|
if (goodGuy.keypair.privateKeyPem !== account.keypair.privateKeyPem) {
|
||||||
|
if (account.keypair.privateKeyJwk) {
|
||||||
|
throw new Error("Error in test itself (not your fault). TODO: implement checking privateKeyJwk.");
|
||||||
|
}
|
||||||
|
throw new Error("agreeTos should return false or null because it was not set.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!account.email) {
|
||||||
|
throw new Error("should have returned email");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!account.agreeTos) {
|
||||||
|
throw new Error("should have returned agreeTos");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!account.receipt) {
|
||||||
|
throw new Error("should have returned receipt");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that id and accountId are ignored
|
||||||
|
// and that arbitrary keys are stored
|
||||||
|
, function () {
|
||||||
|
var rnd = require('crypto').randomBytes(8).toString('hex');
|
||||||
|
var opts = {
|
||||||
|
accountId: '_account_id'
|
||||||
|
, id: '__account_id'
|
||||||
|
, email: 'john.doe@gmail.com'
|
||||||
|
, agreeTos: 'TOS_URL'
|
||||||
|
};
|
||||||
|
var account = {
|
||||||
|
keypair: { privateKeyJwk: {}, privateKeyPem: 'PEM2', publicKeyPem: 'PUBPEM2' }
|
||||||
|
, receipt: {}
|
||||||
|
};
|
||||||
|
account[rnd] = rnd;
|
||||||
|
return leStore.accounts.setKeypairAsync(opts, account.keypair).then(function () {
|
||||||
|
return leStore.accounts.setAsync(opts, account).then(function (account) {
|
||||||
|
|
||||||
|
if ('_account_id' === account.id || '__account_id' === account.id) {
|
||||||
|
throw new Error("Should create `id` deterministically from email or public key, not the given `accountId` or `id`.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('john.doe@gmail.com' !== account.email) {
|
||||||
|
throw new Error("Should return the same email that was stored.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('TOS_URL' !== account.agreeTos) {
|
||||||
|
throw new Error("Should return the same string for the tosUrl in agreeTos as was stored.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('PEM2' !== account.keypair.privateKeyPem) {
|
||||||
|
throw new Error("Should return the same privateKey that was stored.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rnd !== account[rnd]) {
|
||||||
|
throw new Error("Should save and restore arbitrary keys.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// test lots of stuff
|
||||||
|
, function () {
|
||||||
|
return leStore.accounts.checkAsync({
|
||||||
|
accountId: goodGuy.accountId
|
||||||
|
}).then(function (account) {
|
||||||
|
if (!account
|
||||||
|
|| !account.agreeTos
|
||||||
|
|| account.email !== goodGuy.email
|
||||||
|
|| goodGuy.keypair.privateKeyPem !== account.keypair.privateKeyPem
|
||||||
|
) {
|
||||||
|
throw new Error("Should return the same account that was saved when retrieved using `accountId`.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
, function () {
|
||||||
|
return leStore.accounts.checkAsync({
|
||||||
|
email: goodGuy.email
|
||||||
|
}).then(function (account) {
|
||||||
|
if (!account
|
||||||
|
|| !account.agreeTos
|
||||||
|
|| account.email !== goodGuy.email
|
||||||
|
|| goodGuy.keypair.privateKeyPem !== account.keypair.privateKeyPem
|
||||||
|
) {
|
||||||
|
throw new Error("Should return the same account that was saved when retrieved using `accountId`.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Save a cert
|
||||||
|
//
|
||||||
|
, function () {
|
||||||
|
var certOpts = {
|
||||||
|
domains: [ 'example.com', 'www.example.com', 'foo.net', 'bar.foo.net' ]
|
||||||
|
, email: goodGuy.email
|
||||||
|
, certs: {
|
||||||
|
cert: 'CERT_A.PEM'
|
||||||
|
, privkey: 'PRIVKEY_A.PEM'
|
||||||
|
, chain: 'CHAIN_A.PEM'
|
||||||
|
// TODO issuedAt, expiresAt?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return leStore.certificates.setAsync(certOpts, certOpts.certs);
|
||||||
|
}
|
||||||
|
// and another
|
||||||
|
, function () {
|
||||||
|
var certOpts = {
|
||||||
|
domains: [ 'foo.com', 'www.foo.com', 'baz.net', 'bar.baz.net' ]
|
||||||
|
, accountId: goodGuy.accountId
|
||||||
|
, certs: {
|
||||||
|
cert: 'CERT_B.PEM'
|
||||||
|
, privkey: 'PRIVKEY_B.PEM'
|
||||||
|
, chain: 'CHAIN_B.PEM'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return leStore.certificates.setAsync(certOpts, certOpts.certs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// basic test (set by email)
|
||||||
|
, function () {
|
||||||
|
var certOpts = {
|
||||||
|
domains: [ 'example.com' ]
|
||||||
|
};
|
||||||
|
return leStore.certificates.checkAsync(certOpts).then(function (certs) {
|
||||||
|
if (!certs || certs.privkey !== 'PRIVKEY_A.PEM') {
|
||||||
|
throw new Error("should have correct certs for example.com (set by email)");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// basic test (set by accountId)
|
||||||
|
, function () {
|
||||||
|
var certOpts = {
|
||||||
|
domains: [ 'example.com' ]
|
||||||
|
};
|
||||||
|
return leStore.certificates.checkAsync(certOpts).then(function (certs) {
|
||||||
|
if (!certs || certs.privkey !== 'PRIVKEY_A.PEM') {
|
||||||
|
throw new Error("should have correct certs for example.com (set by email)");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// altnames test
|
||||||
|
, function () {
|
||||||
|
var certOpts = {
|
||||||
|
domains: [ 'bar.foo.net' ]
|
||||||
|
};
|
||||||
|
return leStore.certificates.checkAsync(certOpts).then(function (certs) {
|
||||||
|
if (!certs || certs.privkey !== 'PRIVKEY_A.PEM') {
|
||||||
|
throw new Error("should have correct certs for bar.foo.net (one of the example.com altnames)");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// altnames test
|
||||||
|
, function () {
|
||||||
|
var certOpts = {
|
||||||
|
domains: [ 'baz.net' ]
|
||||||
|
};
|
||||||
|
return leStore.certificates.checkAsync(certOpts).then(function (certs) {
|
||||||
|
if (!certs || certs.privkey !== 'PRIVKEY_B.PEM') {
|
||||||
|
throw new Error("should have correct certs for baz.net (one of the foo.com altnames)");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
var arr = tests.slice(0);
|
||||||
|
|
||||||
|
function run() {
|
||||||
|
var test = tests.shift();
|
||||||
|
if (!test) {
|
||||||
|
console.info('All tests passed');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
test().then(run, function (err) {
|
||||||
|
var index = arr.length - tests.length - 1;
|
||||||
|
console.error('');
|
||||||
|
console.error(arr[index].toString());
|
||||||
|
console.error('');
|
||||||
|
console.error(err.stack);
|
||||||
|
console.error('');
|
||||||
|
console.error('Failed Test #' + index);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
run();
|
|
@ -0,0 +1,3 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
throw new Error("Tests not implemented");
|
Loading…
Reference in New Issue