domain: split the config and wellknown routes
we want to add more stuff to the UI like the jitsi URL
This commit is contained in:
+20
-8
@@ -4,7 +4,8 @@ module.exports = exports = {
|
||||
add,
|
||||
get,
|
||||
list,
|
||||
update,
|
||||
setConfig,
|
||||
setWellKnown,
|
||||
del,
|
||||
clear,
|
||||
|
||||
@@ -189,7 +190,7 @@ async function list() {
|
||||
return results;
|
||||
}
|
||||
|
||||
async function update(domain, data, auditSource) {
|
||||
async function setConfig(domain, data, auditSource) {
|
||||
assert.strictEqual(typeof domain, 'string');
|
||||
assert.strictEqual(typeof data.zoneName, 'string');
|
||||
assert.strictEqual(typeof data.provider, 'string');
|
||||
@@ -198,7 +199,7 @@ async function update(domain, data, auditSource) {
|
||||
assert.strictEqual(typeof data.tlsConfig, 'object');
|
||||
assert.strictEqual(typeof auditSource, 'object');
|
||||
|
||||
let { zoneName, provider, config, fallbackCertificate, tlsConfig, wellKnown } = data;
|
||||
let { zoneName, provider, config, fallbackCertificate, tlsConfig } = data;
|
||||
let error;
|
||||
|
||||
if (settings.isDemo() && (domain === settings.dashboardDomain())) throw new BoxError(BoxError.CONFLICT, 'Not allowed in demo mode');
|
||||
@@ -218,9 +219,6 @@ async function update(domain, data, auditSource) {
|
||||
error = validateTlsConfig(tlsConfig, provider);
|
||||
if (error) throw error;
|
||||
|
||||
error = validateWellKnown(wellKnown, provider);
|
||||
if (error) throw error;
|
||||
|
||||
if (provider === domainObject.provider) api(provider).injectPrivateFields(config, domainObject.config);
|
||||
|
||||
const result = await verifyDnsConfig(config, domain, zoneName, provider);
|
||||
@@ -231,14 +229,13 @@ async function update(domain, data, auditSource) {
|
||||
zoneName,
|
||||
provider,
|
||||
tlsConfig,
|
||||
wellKnown,
|
||||
};
|
||||
|
||||
if (fallbackCertificate) newData.fallbackCertificate = fallbackCertificate;
|
||||
|
||||
let args = [ ], fields = [ ];
|
||||
for (const k in newData) {
|
||||
if (k === 'config' || k === 'tlsConfig' || k === 'wellKnown' || k === 'fallbackCertificate') { // json fields
|
||||
if (k === 'config' || k === 'tlsConfig' || k === 'fallbackCertificate') { // json fields
|
||||
fields.push(`${k}Json = ?`);
|
||||
args.push(JSON.stringify(newData[k]));
|
||||
} else {
|
||||
@@ -259,6 +256,21 @@ async function update(domain, data, auditSource) {
|
||||
eventlog.add(eventlog.ACTION_DOMAIN_UPDATE, auditSource, { domain, zoneName, provider });
|
||||
}
|
||||
|
||||
async function setWellKnown(domain, wellKnown, auditSource) {
|
||||
assert.strictEqual(typeof domain, 'string');
|
||||
assert.strictEqual(typeof wellKnown, 'object');
|
||||
assert.strictEqual(typeof auditSource, 'object');
|
||||
|
||||
let error = validateWellKnown(wellKnown);
|
||||
if (error) throw error;
|
||||
|
||||
[error] = await safe(database.query('UPDATE domains SET wellKnownJson = ? WHERE domain=?', [ JSON.stringify(wellKnown), domain ]));
|
||||
if (error && error.reason === BoxError.NOT_FOUND) throw new BoxError(BoxError.NOT_FOUND, 'Domain not found');
|
||||
if (error) throw new BoxError(BoxError.DATABASE_ERROR, error);
|
||||
|
||||
eventlog.add(eventlog.ACTION_DOMAIN_UPDATE, auditSource, { domain, wellKnown });
|
||||
}
|
||||
|
||||
async function del(domain, auditSource) {
|
||||
assert.strictEqual(typeof domain, 'string');
|
||||
assert.strictEqual(typeof auditSource, 'object');
|
||||
|
||||
+21
-13
@@ -4,7 +4,8 @@ exports = module.exports = {
|
||||
add,
|
||||
get,
|
||||
list,
|
||||
update,
|
||||
setConfig,
|
||||
setWellKnown,
|
||||
del,
|
||||
|
||||
checkDnsRecords,
|
||||
@@ -76,7 +77,7 @@ async function list(req, res, next) {
|
||||
next(new HttpSuccess(200, { domains: results }));
|
||||
}
|
||||
|
||||
async function update(req, res, next) {
|
||||
async function setConfig(req, res, next) {
|
||||
assert.strictEqual(typeof req.params.domain, 'string');
|
||||
assert.strictEqual(typeof req.body, 'object');
|
||||
|
||||
@@ -98,26 +99,33 @@ async function update(req, res, next) {
|
||||
if (!req.body.tlsConfig.provider || typeof req.body.tlsConfig.provider !== 'string') return next(new HttpError(400, 'tlsConfig.provider must be a string'));
|
||||
}
|
||||
|
||||
if ('wellKnown' in req.body) {
|
||||
if (typeof req.body.wellKnown !== 'object') return next(new HttpError(400, 'wellKnown must be an object'));
|
||||
if (req.body.wellKnown) {
|
||||
if (Object.keys(req.body.wellKnown).some(k => typeof req.body.wellKnown[k] !== 'string')) return next(new HttpError(400, 'wellKnown is a map of strings'));
|
||||
}
|
||||
}
|
||||
|
||||
// some DNS providers like DigitalOcean take a really long time to verify credentials (https://github.com/expressjs/timeout/issues/26)
|
||||
req.clearTimeout();
|
||||
|
||||
let data = {
|
||||
const data = {
|
||||
zoneName: req.body.zoneName || '',
|
||||
provider: req.body.provider,
|
||||
config: req.body.config,
|
||||
fallbackCertificate: req.body.fallbackCertificate || null,
|
||||
tlsConfig: req.body.tlsConfig || { provider: 'letsencrypt-prod' },
|
||||
wellKnown: req.body.wellKnown || null
|
||||
tlsConfig: req.body.tlsConfig || { provider: 'letsencrypt-prod' }
|
||||
};
|
||||
|
||||
const [error] = await safe(domains.update(req.params.domain, data, AuditSource.fromRequest(req)));
|
||||
const [error] = await safe(domains.setConfig(req.params.domain, data, AuditSource.fromRequest(req)));
|
||||
if (error) return next(BoxError.toHttpError(error));
|
||||
|
||||
next(new HttpSuccess(204, {}));
|
||||
}
|
||||
|
||||
async function setWellKnown(req, res, next) {
|
||||
assert.strictEqual(typeof req.params.domain, 'string');
|
||||
assert.strictEqual(typeof req.body, 'object');
|
||||
|
||||
if (typeof req.body.wellKnown !== 'object') return next(new HttpError(400, 'wellKnown must be an object'));
|
||||
if (req.body.wellKnown) {
|
||||
if (Object.keys(req.body.wellKnown).some(k => typeof req.body.wellKnown[k] !== 'string')) return next(new HttpError(400, 'wellKnown is a map of strings'));
|
||||
}
|
||||
|
||||
const [error] = await safe(domains.setWellKnown(req.params.domain, req.body.wellKnown || null, AuditSource.fromRequest(req)));
|
||||
if (error) return next(BoxError.toHttpError(error));
|
||||
|
||||
next(new HttpSuccess(204, {}));
|
||||
|
||||
@@ -152,6 +152,40 @@ describe('Domains API', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('update', function () {
|
||||
it('config fails for non-existing domain', async function () {
|
||||
const response = await superagent.post(`${serverUrl}/api/v1/domains/whatever/update`)
|
||||
.query({ access_token: owner.token })
|
||||
.ok(() => true);
|
||||
|
||||
expect(response.statusCode).to.equal(404);
|
||||
});
|
||||
|
||||
it('config succeeds', async function () {
|
||||
const response = await superagent.post(`${serverUrl}/api/v1/domains/${DOMAIN_0.domain}/config`)
|
||||
.query({ access_token: owner.token })
|
||||
.send(DOMAIN_0);
|
||||
|
||||
expect(response.statusCode).to.equal(204);
|
||||
});
|
||||
|
||||
it('wellknown succeeds', async function () {
|
||||
const response = await superagent.post(`${serverUrl}/api/v1/domains/${DOMAIN_0.domain}/wellknown`)
|
||||
.query({ access_token: owner.token })
|
||||
.send({ wellKnown: null });
|
||||
|
||||
expect(response.statusCode).to.equal(204);
|
||||
});
|
||||
|
||||
it('wellknown succeeds', async function () {
|
||||
const response = await superagent.post(`${serverUrl}/api/v1/domains/${DOMAIN_0.domain}/wellknown`)
|
||||
.query({ access_token: owner.token })
|
||||
.send({ wellKnown: { service: 'some.service' } });
|
||||
|
||||
expect(response.statusCode).to.equal(204);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Certificates API', function () {
|
||||
let validCert0, validKey0, // example.com
|
||||
validCert1, validKey1; // *.example.com
|
||||
@@ -170,7 +204,7 @@ describe('Domains API', function () {
|
||||
let d = Object.assign({}, DOMAIN_0);
|
||||
d.fallbackCertificate = { key: validKey1 };
|
||||
|
||||
const response = await superagent.put(`${serverUrl}/api/v1/domains/${DOMAIN_0.domain}`)
|
||||
const response = await superagent.post(`${serverUrl}/api/v1/domains/${DOMAIN_0.domain}/config`)
|
||||
.query({ access_token: owner.token })
|
||||
.send(d)
|
||||
.ok(() => true);
|
||||
@@ -182,7 +216,7 @@ describe('Domains API', function () {
|
||||
let d = Object.assign({}, DOMAIN_0);
|
||||
d.fallbackCertificate = { cert: validCert1 };
|
||||
|
||||
const response = await superagent.put(`${serverUrl}/api/v1/domains/${DOMAIN_0.domain}`)
|
||||
const response = await superagent.post(`${serverUrl}/api/v1/domains/${DOMAIN_0.domain}/config`)
|
||||
.query({ access_token: owner.token })
|
||||
.send(d)
|
||||
.ok(() => true);
|
||||
@@ -194,7 +228,7 @@ describe('Domains API', function () {
|
||||
let d = Object.assign({}, DOMAIN_0);
|
||||
d.fallbackCertificate = { cert: 1234, key: validKey1 };
|
||||
|
||||
const response = await superagent.put(`${serverUrl}/api/v1/domains/${DOMAIN_0.domain}`)
|
||||
const response = await superagent.post(`${serverUrl}/api/v1/domains/${DOMAIN_0.domain}/config`)
|
||||
.query({ access_token: owner.token })
|
||||
.send(d)
|
||||
.ok(() => true);
|
||||
@@ -206,7 +240,7 @@ describe('Domains API', function () {
|
||||
let d = Object.assign({}, DOMAIN_0);
|
||||
d.fallbackCertificate = { cert: validCert1, key: true };
|
||||
|
||||
const response = await superagent.put(`${serverUrl}/api/v1/domains/${DOMAIN_0.domain}`)
|
||||
const response = await superagent.post(`${serverUrl}/api/v1/domains/${DOMAIN_0.domain}/config`)
|
||||
.query({ access_token: owner.token })
|
||||
.send(d)
|
||||
.ok(() => true);
|
||||
@@ -218,7 +252,7 @@ describe('Domains API', function () {
|
||||
let d = Object.assign({}, DOMAIN_0);
|
||||
d.fallbackCertificate = { cert: validCert0, key: validKey0 };
|
||||
|
||||
const response = await superagent.put(`${serverUrl}/api/v1/domains/${DOMAIN_0.domain}`)
|
||||
const response = await superagent.post(`${serverUrl}/api/v1/domains/${DOMAIN_0.domain}/config`)
|
||||
.query({ access_token: owner.token })
|
||||
.send(d)
|
||||
.ok(() => true);
|
||||
@@ -230,7 +264,7 @@ describe('Domains API', function () {
|
||||
let d = Object.assign({}, DOMAIN_0);
|
||||
d.fallbackCertificate = { cert: validCert1, key: validKey1 };
|
||||
|
||||
const response = await superagent.put(`${serverUrl}/api/v1/domains/${DOMAIN_0.domain}`)
|
||||
const response = await superagent.post(`${serverUrl}/api/v1/domains/${DOMAIN_0.domain}/config`)
|
||||
.query({ access_token: owner.token })
|
||||
.send(d);
|
||||
|
||||
|
||||
+2
-1
@@ -313,7 +313,8 @@ function initializeExpressSync() {
|
||||
router.post('/api/v1/domains', json, token, authorizeAdmin, routes.domains.add);
|
||||
router.get ('/api/v1/domains', token, routes.domains.list);
|
||||
router.get ('/api/v1/domains/:domain', token, authorizeAdmin, routes.domains.get); // this is manage scope because it returns non-restricted fields
|
||||
router.put ('/api/v1/domains/:domain', json, token, authorizeAdmin, routes.domains.update);
|
||||
router.post('/api/v1/domains/:domain/config', json, token, authorizeAdmin, routes.domains.setConfig);
|
||||
router.post('/api/v1/domains/:domain/wellknown', json, token, authorizeAdmin, routes.domains.setWellKnown);
|
||||
router.del ('/api/v1/domains/:domain', token, authorizeAdmin, routes.domains.del);
|
||||
router.get ('/api/v1/domains/:domain/dns_check', token, authorizeAdmin, routes.domains.checkDnsRecords);
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ describe('dns provider', function () {
|
||||
domainCopy.provider = 'noop';
|
||||
domainCopy.config = {};
|
||||
|
||||
await domains.update(domainCopy.domain, domainCopy, auditSource);
|
||||
await domains.setConfig(domainCopy.domain, domainCopy, auditSource);
|
||||
});
|
||||
|
||||
it('upsert succeeds', async function () {
|
||||
@@ -55,7 +55,7 @@ describe('dns provider', function () {
|
||||
token: TOKEN
|
||||
};
|
||||
|
||||
await domains.update(domainCopy.domain, domainCopy, auditSource);
|
||||
await domains.setConfig(domainCopy.domain, domainCopy, auditSource);
|
||||
});
|
||||
|
||||
it('upsert non-existing record succeeds', async function () {
|
||||
@@ -292,7 +292,7 @@ describe('dns provider', function () {
|
||||
apiSecret: SECRET
|
||||
};
|
||||
|
||||
await domains.update(domainCopy.domain, domainCopy, auditSource);
|
||||
await domains.setConfig(domainCopy.domain, domainCopy, auditSource);
|
||||
});
|
||||
|
||||
it('upsert record succeeds', async function () {
|
||||
@@ -367,7 +367,7 @@ describe('dns provider', function () {
|
||||
token: TOKEN
|
||||
};
|
||||
|
||||
await domains.update(domainCopy.domain, domainCopy, auditSource);
|
||||
await domains.setConfig(domainCopy.domain, domainCopy, auditSource);
|
||||
});
|
||||
|
||||
it('upsert record succeeds', async function () {
|
||||
@@ -430,7 +430,7 @@ describe('dns provider', function () {
|
||||
token: TOKEN
|
||||
};
|
||||
|
||||
await domains.update(domainCopy.domain, domainCopy, auditSource);
|
||||
await domains.setConfig(domainCopy.domain, domainCopy, auditSource);
|
||||
});
|
||||
|
||||
it('upsert record succeeds', async function () {
|
||||
@@ -530,7 +530,7 @@ describe('dns provider', function () {
|
||||
token: token
|
||||
};
|
||||
|
||||
await domains.update(domainCopy.domain, domainCopy, auditSource);
|
||||
await domains.setConfig(domainCopy.domain, domainCopy, auditSource);
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
@@ -942,7 +942,7 @@ describe('dns provider', function () {
|
||||
AWS._originalRoute53 = AWS.Route53;
|
||||
AWS.Route53 = Route53Mock;
|
||||
|
||||
await domains.update(domainCopy.domain, domainCopy, auditSource);
|
||||
await domains.setConfig(domainCopy.domain, domainCopy, auditSource);
|
||||
});
|
||||
|
||||
after(function () {
|
||||
@@ -1078,7 +1078,7 @@ describe('dns provider', function () {
|
||||
_OriginalGCDNS = GCDNS.prototype.getZones;
|
||||
GCDNS.prototype.getZones = mockery(zoneQueue);
|
||||
|
||||
await domains.update(domainCopy.domain, domainCopy, auditSource);
|
||||
await domains.setConfig(domainCopy.domain, domainCopy, auditSource);
|
||||
});
|
||||
|
||||
after(function () {
|
||||
|
||||
@@ -52,12 +52,12 @@ describe('Domains', function () {
|
||||
expect(result).to.be(null);
|
||||
});
|
||||
|
||||
it('can update domain', async function () {
|
||||
it('can set domain config', async function () {
|
||||
const newConfig = {};
|
||||
const newTlsConfig = { provider: 'letsencrypt-staging' };
|
||||
const newDomain = Object.assign({}, DOMAIN_0, { config: newConfig, tlsConfig: newTlsConfig });
|
||||
|
||||
await domains.update(DOMAIN_0.domain, newDomain, auditSource);
|
||||
await domains.setConfig(DOMAIN_0.domain, newDomain, auditSource);
|
||||
|
||||
const result = await domains.get(DOMAIN_0.domain);
|
||||
expect(result.domain).to.equal(DOMAIN_0.domain);
|
||||
@@ -70,6 +70,16 @@ describe('Domains', function () {
|
||||
DOMAIN_0.tlsConfig = newTlsConfig;
|
||||
});
|
||||
|
||||
it('can set domain wellknown', async function () {
|
||||
await domains.setWellKnown(DOMAIN_0.domain, { service: 'some.service' }, auditSource);
|
||||
let result = await domains.get(DOMAIN_0.domain);
|
||||
expect(result.wellKnown).to.eql({ service: 'some.service' });
|
||||
|
||||
await domains.setWellKnown(DOMAIN_0.domain, null, auditSource);
|
||||
result = await domains.get(DOMAIN_0.domain);
|
||||
expect(result.wellKnown).to.eql(null);
|
||||
});
|
||||
|
||||
it('can get all domains', async function () {
|
||||
const result = await domains.list();
|
||||
expect(result.length).to.equal(2);
|
||||
|
||||
@@ -143,7 +143,7 @@ describe('Reverse Proxy', function () {
|
||||
before(async function () {
|
||||
domainCopy.tlsConfig = { provider: 'letsencrypt-prod' };
|
||||
|
||||
await domains.update(domainCopy.domain, domainCopy, auditSource);
|
||||
await domains.setConfig(domainCopy.domain, domainCopy, auditSource);
|
||||
});
|
||||
|
||||
it('returns prod acme in prod cloudron', async function () {
|
||||
@@ -157,7 +157,7 @@ describe('Reverse Proxy', function () {
|
||||
before(async function () {
|
||||
domainCopy.tlsConfig = { provider: 'letsencrypt-staging' };
|
||||
|
||||
await domains.update(domainCopy.domain, domainCopy, auditSource);
|
||||
await domains.setConfig(domainCopy.domain, domainCopy, auditSource);
|
||||
});
|
||||
|
||||
it('returns staging acme in prod cloudron', async function () {
|
||||
@@ -171,7 +171,7 @@ describe('Reverse Proxy', function () {
|
||||
before(async function () {
|
||||
domainCopy.tlsConfig = { provider: 'fallback' };
|
||||
|
||||
await domains.update(domainCopy.domain, domainCopy, auditSource);
|
||||
await domains.setConfig(domainCopy.domain, domainCopy, auditSource);
|
||||
});
|
||||
|
||||
it('configure nginx correctly', async function () {
|
||||
|
||||
Reference in New Issue
Block a user