2019-11-05 04:03:21 +00:00
|
|
|
"use strict";
|
2019-10-31 06:07:33 +00:00
|
|
|
|
2019-11-05 04:03:21 +00:00
|
|
|
var request = require("@root/request");
|
|
|
|
|
|
|
|
// For most tests
|
|
|
|
var siteSubject = "xx.com";
|
|
|
|
var siteAltname = "www.foo.xx.com";
|
|
|
|
var siteWildname = "*.xx.com";
|
|
|
|
var siteMatch = "foo.xx.com";
|
|
|
|
var domains = [siteSubject, siteAltname, siteWildname];
|
|
|
|
|
|
|
|
// Similar, but non-matching subjects
|
|
|
|
var noExistWild = "*.foo.xx.com";
|
|
|
|
var noExistAlt = "bar.xx.com";
|
|
|
|
|
|
|
|
// For wildcard-as-subject test
|
|
|
|
var siteWildnameNet = "*.xx.net";
|
|
|
|
var siteMatchNet = "foo.xx.net";
|
2019-10-31 06:07:33 +00:00
|
|
|
|
|
|
|
module.exports.test = async function(pkg, config) {
|
2019-11-05 04:03:21 +00:00
|
|
|
if ("function" !== typeof pkg.create) {
|
|
|
|
throw new Error(
|
|
|
|
"must have a create function that accepts a single options object"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
var features = {
|
|
|
|
altnames: false,
|
|
|
|
wildcard: false,
|
|
|
|
renewal: false
|
|
|
|
};
|
|
|
|
var manager = pkg.create(config);
|
|
|
|
var initVal;
|
|
|
|
|
|
|
|
if (manager.init) {
|
|
|
|
initVal = await manager.init({
|
|
|
|
request: request
|
|
|
|
});
|
|
|
|
if (!initVal && initVal !== null) {
|
|
|
|
console.warn(
|
|
|
|
"WARN: `init()` returned `undefined`, but should return `null`"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
console.info("PASS: init(deps)");
|
|
|
|
|
|
|
|
await manager.set({
|
|
|
|
subject: siteSubject,
|
|
|
|
altnames: domains
|
|
|
|
});
|
|
|
|
var site = await manager.get({
|
|
|
|
servername: siteSubject
|
|
|
|
// *.com is an invalid wildname
|
|
|
|
});
|
|
|
|
if (!site || site.subject !== siteSubject) {
|
|
|
|
throw new Error(
|
|
|
|
"set({ subject: '" +
|
|
|
|
siteSubject +
|
|
|
|
"'}), but could not `get()` or `find()` it"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Test for altname support
|
|
|
|
//
|
|
|
|
site = await get({
|
|
|
|
servername: siteAltname,
|
|
|
|
wildname: untame(siteAltname)
|
|
|
|
});
|
|
|
|
if (site) {
|
|
|
|
if (site.subject !== siteSubject) {
|
|
|
|
throw new Error("found incorrect site");
|
|
|
|
}
|
|
|
|
features.altnames = true;
|
|
|
|
} else {
|
|
|
|
console.warn("WARN: Does not support altnames.");
|
|
|
|
console.warn(
|
|
|
|
" (searched for %s but did not find site '%s')",
|
|
|
|
siteAltname,
|
|
|
|
domains.join(" ")
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Test for wildcard support
|
|
|
|
//
|
|
|
|
if (features.altnames) {
|
|
|
|
// Set the wildcard as an altname
|
|
|
|
site = await get({
|
|
|
|
servername: siteMatch,
|
|
|
|
wildname: siteWildname
|
|
|
|
});
|
|
|
|
if (site) {
|
|
|
|
if (site.subject !== siteSubject) {
|
|
|
|
throw new Error(
|
|
|
|
"found %s when looking for %s",
|
|
|
|
site.subject,
|
|
|
|
siteSubject
|
|
|
|
);
|
|
|
|
}
|
|
|
|
features.wildcard = true;
|
|
|
|
} else {
|
|
|
|
console.warn("WARN: Does not support wildcard domains.");
|
|
|
|
console.warn(
|
|
|
|
" (searched for %s but did not find site %s)",
|
|
|
|
siteMatch,
|
|
|
|
siteSubject
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Set the wildcard as the subject
|
|
|
|
await manager.set({
|
|
|
|
subject: siteWildnameNet,
|
|
|
|
altnames: [siteWildnameNet]
|
|
|
|
});
|
|
|
|
site = await get({
|
|
|
|
servername: siteMatchNet,
|
|
|
|
wildname: siteWildnameNet
|
|
|
|
});
|
|
|
|
if (site) {
|
|
|
|
if (site.subject !== siteWildnameNet) {
|
|
|
|
throw new Error("found incorrect site");
|
|
|
|
}
|
|
|
|
features.wildcard = true;
|
|
|
|
} else {
|
|
|
|
if (features.wildcard) {
|
|
|
|
throw new Error(
|
|
|
|
"searched for wildcard subject " +
|
|
|
|
siteWildnameNet +
|
|
|
|
" but did not find it"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if (!features.altnames) {
|
|
|
|
console.warn(
|
|
|
|
"WARN: Does not support wildcard domains as certificate subjects."
|
|
|
|
);
|
|
|
|
console.warn(
|
|
|
|
" (searched for %s as %s but did not find site %s)",
|
|
|
|
siteMatchNet,
|
|
|
|
siteWildnameNet,
|
|
|
|
siteWildnameNet
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
await remove({ subject: siteWildnameNet });
|
|
|
|
|
|
|
|
var wasSet = false;
|
|
|
|
if (manager.find) {
|
|
|
|
await manager.find({}).then(function(results) {
|
|
|
|
if (!results.length) {
|
|
|
|
//console.error(results);
|
|
|
|
throw new Error("should have found all managed sites");
|
|
|
|
}
|
|
|
|
wasSet = results.some(function(site) {
|
|
|
|
return site.subject === siteSubject;
|
|
|
|
});
|
|
|
|
if (!wasSet) {
|
|
|
|
throw new Error("should have found " + siteSubject);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (manager.get) {
|
|
|
|
await manager.get({ servername: siteSubject }).then(function(site) {
|
|
|
|
if (!site || site.subject !== siteSubject) {
|
|
|
|
throw new Error("should have found " + siteSubject);
|
|
|
|
}
|
|
|
|
wasSet = true;
|
|
|
|
});
|
|
|
|
if (features.altnames) {
|
|
|
|
wasSet = false;
|
|
|
|
await manager.get({ servername: siteAltname }).then(function(site) {
|
|
|
|
if (!site || site.subject !== siteSubject) {
|
|
|
|
throw new Error("should have found " + siteAltname);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
await manager
|
|
|
|
.get({ servername: siteMatch, wildname: siteWildname })
|
|
|
|
.then(function(site) {
|
|
|
|
if (!site || site.subject !== siteSubject) {
|
|
|
|
throw new Error(
|
|
|
|
"did not find " +
|
|
|
|
siteMatch +
|
|
|
|
", which matches " +
|
|
|
|
siteWildname
|
|
|
|
);
|
|
|
|
}
|
|
|
|
wasSet = true;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
console.info("PASS: get({ servername, wildname })");
|
|
|
|
} else {
|
|
|
|
console.info("[skip] get({ servername, wildname }) not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wasSet) {
|
|
|
|
console.info("PASS: set({ subject })");
|
|
|
|
} else {
|
|
|
|
throw new Error("neither `get()` nor `find()` was implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (manager.find) {
|
|
|
|
await manager.find({ subject: siteAltname }).then(function(results) {
|
|
|
|
if (results.length) {
|
|
|
|
console.error(results);
|
|
|
|
throw new Error(
|
|
|
|
"shouldn't find what doesn't exist, exactly, by subject"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
await manager
|
|
|
|
.find({ servernames: [siteAltname], altnames: [siteAltname] })
|
|
|
|
.then(function(results) {
|
|
|
|
if (!results.length) {
|
|
|
|
console.error(results);
|
|
|
|
throw new Error("should have found sites matching altname");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
console.info("PASS: find({ servernames, renewBefore })");
|
|
|
|
} else {
|
|
|
|
console.info(
|
|
|
|
"[skip] find({ servernames, renewBefore }) not implemented"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
await remove({ subject: noExistWild }).then(function(result) {
|
|
|
|
if (result) {
|
|
|
|
console.error(siteWildname, result);
|
|
|
|
throw new Error(
|
|
|
|
"should not return prior object when deleting non-existing wildcard domain: " +
|
|
|
|
noExistWild
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
await remove({ subject: noExistAlt }).then(function(result) {
|
|
|
|
if (result) {
|
|
|
|
throw new Error(
|
|
|
|
"should not return prior object when deleting non-existing site: " +
|
|
|
|
noExistAlt
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
await remove({ subject: siteWildname }).then(function(result) {
|
|
|
|
if (result) {
|
|
|
|
throw new Error("should not delete by wildname: " + siteWildname);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
await remove({ subject: siteAltname }).then(function(result) {
|
|
|
|
if (result) {
|
|
|
|
throw new Error("should not delete by altname: " + siteAltname);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
await remove({ subject: siteSubject }).then(function(result) {
|
|
|
|
if (!result || !result.subject || !result.altnames) {
|
|
|
|
throw new Error("should return prior object when deleting site");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if (!manager.remove) {
|
|
|
|
console.info(
|
|
|
|
"[skip] remove() not implemented - using set({ deletedAt }) instead"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
await manager.set({ subject: siteSubject, altnames: domains.slice(0, 2) });
|
|
|
|
if (manager.find) {
|
|
|
|
await manager
|
|
|
|
.find({ servernames: [noExistWild], altnames: [noExistWild] })
|
|
|
|
.then(function(results) {
|
|
|
|
if (results.length) {
|
|
|
|
console.error(results);
|
|
|
|
throw new Error(
|
|
|
|
"should only find an exact (literal) wildcard match"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
await remove({ subject: siteSubject }).then(function(result) {
|
|
|
|
if (!result || !result.subject || !result.altnames) {
|
|
|
|
console.error(
|
|
|
|
"Could not find",
|
|
|
|
siteSubject,
|
|
|
|
"to delete it:",
|
|
|
|
result
|
|
|
|
);
|
|
|
|
throw new Error("should return prior object when deleting site");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if (manager.find) {
|
|
|
|
await manager
|
|
|
|
.find({ servernames: domains, altnames: domains })
|
|
|
|
.then(function(results) {
|
|
|
|
if (results.length) {
|
|
|
|
console.error(results);
|
|
|
|
throw new Error("should not find() deleted sites");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
await get({ servername: siteAltname }).then(function(result) {
|
|
|
|
if (result) {
|
|
|
|
console.error(result);
|
|
|
|
throw new Error("should not get() deleted sites");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
console.info("PASS: remove({ subject })");
|
|
|
|
|
|
|
|
var originalInput = {
|
|
|
|
serverKeyType: "RSA-2048",
|
|
|
|
accountKeyType: "P-256",
|
|
|
|
subscriberEmail: "jon@example.com",
|
|
|
|
agreeToTerms: true,
|
|
|
|
store: { module: "/path/to/store-module", foo: "foo" },
|
|
|
|
challenges: {
|
|
|
|
"http-01": { module: "/path/to/http-01-module", bar: "bar" },
|
|
|
|
"dns-01": { module: "/path/to/dns-01-module", baz: "baz" },
|
|
|
|
"tls-alpn-01": {
|
|
|
|
module: "/path/to/tls-alpn-01-module",
|
|
|
|
qux: "quux"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
customerEmail: "jane@example.com"
|
|
|
|
};
|
|
|
|
//var backup = JSON.parse(JSON.stringify(originalInput));
|
|
|
|
var configUpdate = {
|
|
|
|
renewOffset: "45d",
|
|
|
|
renewStagger: "12h",
|
|
|
|
subscriberEmail: "pat@example.com"
|
|
|
|
};
|
|
|
|
|
|
|
|
var internalConfig;
|
|
|
|
if (manager.defaults) {
|
|
|
|
await manager.defaults().then(function(result) {
|
|
|
|
internalConfig = result;
|
|
|
|
if (!result) {
|
|
|
|
throw new Error(
|
|
|
|
"should at least return an empty object, perhaps one with some defaults set"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
await manager.defaults(originalInput).then(function(result) {
|
|
|
|
// can't say much... what _should_ this return?
|
|
|
|
// probably nothing? or maybe the full config object?
|
|
|
|
if (internalConfig === result) {
|
|
|
|
console.warn(
|
|
|
|
"WARN: should return a new copy, not the same internal object"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if (originalInput === result) {
|
|
|
|
console.warn(
|
|
|
|
"WARN: should probably return a copy, not the original input"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
await manager.defaults().then(function(result) {
|
|
|
|
if (originalInput === result) {
|
|
|
|
console.warn(
|
|
|
|
"WARN: should probably return a copy, not the prior input"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
await manager.defaults(configUpdate).then(function() {
|
|
|
|
if (originalInput.renewOffset) {
|
|
|
|
console.warn("WARN: should probably modify the prior input");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
console.info("PASS: defaults(conf)");
|
|
|
|
|
|
|
|
await manager.defaults().then(function(result) {
|
|
|
|
if (!result.subscriberEmail || !result.renewOffset) {
|
|
|
|
throw new Error("should merge config values together");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
console.info("PASS: defaults()");
|
|
|
|
} else {
|
|
|
|
console.info(
|
|
|
|
"[skip] defaults({ store, challenges, ... }) not implemented"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
features.renewal = !!manager.find;
|
|
|
|
var featureNames = {
|
|
|
|
altnames: "Multiple Domains per Certificate",
|
|
|
|
wildcard:
|
|
|
|
"Wildcard Certificates" +
|
|
|
|
(features.altnames ? "" : " (subject only)"),
|
|
|
|
renewal: "Fully Automatic Renewal"
|
|
|
|
};
|
|
|
|
return Object.keys(features).map(function(k) {
|
|
|
|
return {
|
|
|
|
name: k,
|
|
|
|
description: featureNames[k],
|
|
|
|
supported: features[k]
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
|
|
|
function get(opts) {
|
|
|
|
if (manager.get) {
|
|
|
|
opts.servername = opts.servername || opts.subject;
|
|
|
|
delete opts.subject;
|
|
|
|
return manager.get(opts);
|
|
|
|
} else {
|
|
|
|
return manager.find(opts);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function remove(opts) {
|
|
|
|
if (manager.remove) {
|
|
|
|
return manager.remove(opts);
|
|
|
|
} else {
|
|
|
|
return get(opts).then(function(site) {
|
|
|
|
// get matches servername, but remove should only match subject
|
|
|
|
if (site && site.subject === opts.servername) {
|
|
|
|
site.deletedAt = Date.now();
|
|
|
|
return manager.set(site).then(function() {
|
|
|
|
return site;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function untame(str) {
|
|
|
|
return (
|
|
|
|
"*." +
|
|
|
|
str
|
|
|
|
.split(".")
|
|
|
|
.slice(1)
|
|
|
|
.join(".")
|
|
|
|
);
|
|
|
|
}
|
2019-10-31 06:07:33 +00:00
|
|
|
};
|