diff --git a/migrations/20190125221223-domains-add-locked.js b/migrations/20190125221223-domains-add-locked.js new file mode 100644 index 000000000..503d77a6f --- /dev/null +++ b/migrations/20190125221223-domains-add-locked.js @@ -0,0 +1,17 @@ +'use strict'; + +exports.up = function(db, callback) { + db.runSql('ALTER TABLE domains ADD COLUMN locked BOOLEAN DEFAULT 0', function (error) { + if (error) return callback(error); + + callback(); + }); +}; + +exports.down = function(db, callback) { + db.runSql('ALTER TABLE domains DROP COLUMN locked', function (error) { + if (error) console.error(error); + callback(error); + }); +}; + diff --git a/src/domaindb.js b/src/domaindb.js index de7904a20..d766dc1ef 100644 --- a/src/domaindb.js +++ b/src/domaindb.js @@ -16,7 +16,7 @@ var assert = require('assert'), DatabaseError = require('./databaseerror'), safe = require('safetydance'); -var DOMAINS_FIELDS = [ 'domain', 'zoneName', 'provider', 'configJson', 'tlsConfigJson' ].join(','); +var DOMAINS_FIELDS = [ 'domain', 'zoneName', 'provider', 'configJson', 'tlsConfigJson', 'locked' ].join(','); function postProcess(data) { data.config = safe.JSON.parse(data.configJson); @@ -24,6 +24,8 @@ function postProcess(data) { delete data.configJson; delete data.tlsConfigJson; + data.locked = !!data.locked; // make it bool + return data; } diff --git a/src/domains.js b/src/domains.js index 8323e28e1..463eca0d4 100644 --- a/src/domains.js +++ b/src/domains.js @@ -7,7 +7,6 @@ module.exports = exports = { update: update, del: del, clear: clear, - isLocked: isLocked, fqdn: fqdn, getName: getName, @@ -244,10 +243,6 @@ function add(domain, data, auditSource, callback) { }); } -function isLocked(domain) { - return domain === config.adminDomain() && config.edition() === 'hostingprovider'; -} - function get(domain, callback) { assert.strictEqual(typeof domain, 'string'); assert.strictEqual(typeof callback, 'function'); @@ -257,8 +252,6 @@ function get(domain, callback) { if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new DomainsError(DomainsError.NOT_FOUND)); if (error) return callback(new DomainsError(DomainsError.INTERNAL_ERROR, error)); - result.locked = isLocked(domain); - reverseProxy.getFallbackCertificate(domain, function (error, bundle) { if (error && error.reason !== ReverseProxyError.NOT_FOUND) return callback(new DomainsError(DomainsError.INTERNAL_ERROR, error)); @@ -280,8 +273,6 @@ function getAll(callback) { domaindb.getAll(function (error, result) { if (error) return callback(new DomainsError(DomainsError.INTERNAL_ERROR, error)); - result.forEach(function (r) { r.locked = isLocked(r.domain); }); - return callback(null, result); }); } diff --git a/src/routes/domains.js b/src/routes/domains.js index 2ec34a40e..9cabdee49 100644 --- a/src/routes/domains.js +++ b/src/routes/domains.js @@ -21,13 +21,17 @@ function auditSource(req) { return { ip: ip, username: req.user ? req.user.username : null, userId: req.user ? req.user.id : null }; } -// this code exists for the hosting provider edition function verifyDomainLock(req, res, next) { assert.strictEqual(typeof req.params.domain, 'string'); - if (domains.isLocked(req.params.domain)) return next(new HttpError(423, 'This domain is locked')); + domains.get(req.params.domain, function (error, domain) { + if (error && error.reason === DomainsError.NOT_FOUND) return next(new HttpError(404, 'No such domain')); + if (error) return next(new HttpError(500, error)); - next(); + if (domain.locked) return next(new HttpError(423, 'This domain is locked')); + + next(); + }); } function add(req, res, next) { diff --git a/src/routes/test/domains-test.js b/src/routes/test/domains-test.js index 1cd1c9d4a..b3764ed5c 100644 --- a/src/routes/test/domains-test.js +++ b/src/routes/test/domains-test.js @@ -9,6 +9,7 @@ var async = require('async'), child_process = require('child_process'), config = require('../../config.js'), database = require('../../database.js'), + domaindb = require('../../domaindb.js'), expect = require('expect.js'), fs = require('fs'), path = require('path'), @@ -216,9 +217,53 @@ describe('Domains API', function () { }); }); + describe('locked', function () { + before(function (done) { + domaindb.update(DOMAIN_0.domain, { locked: true }, done); + }); + + after(function (done) { + domaindb.update(DOMAIN_0.domain, { locked: false }, done); + }); + + it('can list the domains', function (done) { + superagent.get(SERVER_URL + '/api/v1/domains') + .query({ access_token: token }) + .end(function (error, result) { + expect(result.statusCode).to.equal(200); + expect(result.body.domains).to.be.an(Array); + expect(result.body.domains.length).to.equal(2); + + expect(result.body.domains[0].domain).to.equal(DOMAIN_0.domain); + expect(result.body.domains[1].domain).to.equal(DOMAIN_1.domain); + + done(); + }); + }); + + it('cannot get locked domain', function (done) { + superagent.get(SERVER_URL + '/api/v1/domains/' + DOMAIN_0.domain) + .query({ access_token: token }) + .end(function (error, result) { + expect(result.statusCode).to.equal(423); + done(); + }); + }); + + it('cannot delete locked domain', function (done) { + superagent.delete(SERVER_URL + '/api/v1/domains/' + DOMAIN_0.domain) + .query({ access_token: token }) + .send({ password: PASSWORD }) + .end(function (error, result) { + expect(result.statusCode).to.equal(423); + done(); + }); + }); + }); + describe('delete', function () { it('fails without password', function (done) { - superagent.delete(SERVER_URL + '/api/v1/domains/' + DOMAIN_0.domain + DOMAIN_0.domain) + superagent.delete(SERVER_URL + '/api/v1/domains/' + DOMAIN_0.domain) .query({ access_token: token }) .end(function (error, result) { expect(result.statusCode).to.equal(400); @@ -228,7 +273,7 @@ describe('Domains API', function () { }); it('fails with wrong password', function (done) { - superagent.delete(SERVER_URL + '/api/v1/domains/' + DOMAIN_0.domain + DOMAIN_0.domain) + superagent.delete(SERVER_URL + '/api/v1/domains/' + DOMAIN_0.domain) .query({ access_token: token }) .send({ password: PASSWORD + PASSWORD }) .end(function (error, result) {