'use strict'; exports = module.exports = { add, get, list, setConfig, setWellKnown, del, checkDnsRecords, syncDnsRecords, }; const assert = require('assert'), AuditSource = require('../auditsource.js'), BoxError = require('../boxerror.js'), dns = require('../dns.js'), domains = require('../domains.js'), HttpError = require('connect-lastmile').HttpError, HttpSuccess = require('connect-lastmile').HttpSuccess, safe = require('safetydance'); async function add(req, res, next) { assert.strictEqual(typeof req.body, 'object'); if (typeof req.body.domain !== 'string') return next(new HttpError(400, 'domain must be a string')); if (typeof req.body.provider !== 'string') return next(new HttpError(400, 'provider must be a string')); if (!req.body.config || typeof req.body.config !== 'object') return next(new HttpError(400, 'config must be an object')); if ('wildcard' in req.body.config && typeof req.body.config.wildcard !== 'boolean') return next(new HttpError(400, 'wildcard must be a boolean')); if ('zoneName' in req.body && typeof req.body.zoneName !== 'string') return next(new HttpError(400, 'zoneName must be a string')); if ('fallbackCertificate' in req.body && typeof req.body.fallbackCertificate !== 'object') return next(new HttpError(400, 'fallbackCertificate must be a object with cert and key strings')); if (req.body.fallbackCertificate) { let fallbackCertificate = req.body.fallbackCertificate; if (!fallbackCertificate.cert || typeof fallbackCertificate.cert !== 'string') return next(new HttpError(400, 'fallbackCertificate.cert must be a string')); if (!fallbackCertificate.key || typeof fallbackCertificate.key !== 'string') return next(new HttpError(400, 'fallbackCertificate.key must be a string')); } if ('tlsConfig' in req.body) { if (!req.body.tlsConfig || typeof req.body.tlsConfig !== 'object') return next(new HttpError(400, 'tlsConfig must be a object with a provider string property')); if (!req.body.tlsConfig.provider || typeof req.body.tlsConfig.provider !== 'string') return next(new HttpError(400, 'tlsConfig.provider must be a string')); } // some DNS providers like DigitalOcean take a really long time to verify credentials (https://github.com/expressjs/timeout/issues/26) req.clearTimeout(); let 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' } }; const [error] = await safe(domains.add(req.body.domain, data, AuditSource.fromRequest(req))); if (error) return next(BoxError.toHttpError(error)); next(new HttpSuccess(201, {})); } async function get(req, res, next) { assert.strictEqual(typeof req.params.domain, 'string'); const [error, result] = await safe(domains.get(req.params.domain)); if (error) return next(BoxError.toHttpError(error)); if (!result) return next(new HttpError(404, 'Domain not found')); next(new HttpSuccess(200, domains.removePrivateFields(result))); } async function list(req, res, next) { let [error, results] = await safe(domains.list()); if (error) return next(new HttpError(500, error)); results = results.map(domains.removeRestrictedFields); next(new HttpSuccess(200, { domains: results })); } async function setConfig(req, res, next) { assert.strictEqual(typeof req.params.domain, 'string'); assert.strictEqual(typeof req.body, 'object'); if (typeof req.body.provider !== 'string') return next(new HttpError(400, 'provider must be an object')); if (!req.body.config || typeof req.body.config !== 'object') return next(new HttpError(400, 'config must be an object')); if ('wildcard' in req.body.config && typeof req.body.config.wildcard !== 'boolean') return next(new HttpError(400, 'wildcard must be a boolean')); if ('zoneName' in req.body && typeof req.body.zoneName !== 'string') return next(new HttpError(400, 'zoneName must be a string')); if ('fallbackCertificate' in req.body && typeof req.body.fallbackCertificate !== 'object') return next(new HttpError(400, 'fallbackCertificate must be a object with cert and key strings')); if (req.body.fallbackCertificate) { let fallbackCertificate = req.body.fallbackCertificate; if (!fallbackCertificate.cert || typeof fallbackCertificate.cert !== 'string') return next(new HttpError(400, 'fallbackCertificate.cert must be a string')); if (!fallbackCertificate.key || typeof fallbackCertificate.key !== 'string') return next(new HttpError(400, 'fallbackCertificate.key must be a string')); } if ('tlsConfig' in req.body) { if (!req.body.tlsConfig || typeof req.body.tlsConfig !== 'object') return next(new HttpError(400, 'tlsConfig must be a object with a provider string property')); if (!req.body.tlsConfig.provider || typeof req.body.tlsConfig.provider !== 'string') return next(new HttpError(400, 'tlsConfig.provider must be a string')); } // some DNS providers like DigitalOcean take a really long time to verify credentials (https://github.com/expressjs/timeout/issues/26) req.clearTimeout(); 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' } }; 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, {})); } async function del(req, res, next) { assert.strictEqual(typeof req.params.domain, 'string'); const [error] = await safe(domains.del(req.params.domain, AuditSource.fromRequest(req))); if (error) return next(BoxError.toHttpError(error)); next(new HttpSuccess(204)); } async function checkDnsRecords(req, res, next) { assert.strictEqual(typeof req.params.domain, 'string'); if (!('subdomain' in req.query)) return next(new HttpError(400, 'subdomain is required')); // some DNS providers like DigitalOcean take a really long time to verify credentials (https://github.com/expressjs/timeout/issues/26) req.clearTimeout(); const [error, result] = await safe(dns.checkDnsRecords(req.query.subdomain, req.params.domain)); if (error && error.reason === BoxError.ACCESS_DENIED) return next(new HttpSuccess(200, { error: { reason: error.reason, message: error.message }})); if (error) return next(BoxError.toHttpError(error)); next(new HttpSuccess(200, { needsOverwrite: result.needsOverwrite })); } async function syncDnsRecords(req, res, next) { assert.strictEqual(typeof req.body, 'object'); if ('domain' in req.body && typeof req.body.domain !== 'string') return next(new HttpError(400, 'domain must be a string')); if ('type' in req.body && typeof req.body.type !== 'string') return next(new HttpError(400, 'type must be a string')); const [error, taskId] = await safe(dns.startSyncDnsRecords(req.body)); if (error && error.reason === BoxError.ACCESS_DENIED) return next(new HttpSuccess(200, { error: { reason: error.reason, message: error.message }})); if (error) return next(BoxError.toHttpError(error)); next(new HttpSuccess(201, { taskId })); }