diff --git a/src/cloudron.js b/src/cloudron.js index d8e1b8630..9595bbd4b 100644 --- a/src/cloudron.js +++ b/src/cloudron.js @@ -11,8 +11,6 @@ exports = module.exports = { getConfig: getConfig, getStatus: getStatus, - setCertificate: setCertificate, - sendHeartbeat: sendHeartbeat, update: update, @@ -51,8 +49,7 @@ var apps = require('./apps.js'), util = require('util'), webhooks = require('./webhooks.js'); -var RELOAD_NGINX_CMD = path.join(__dirname, 'scripts/reloadnginx.sh'), - REBOOT_CMD = path.join(__dirname, 'scripts/reboot.sh'), +var REBOOT_CMD = path.join(__dirname, 'scripts/reboot.sh'), BACKUP_BOX_CMD = path.join(__dirname, 'scripts/backupbox.sh'), BACKUP_SWAP_CMD = path.join(__dirname, 'scripts/backupswap.sh'), INSTALLER_UPDATE_URL = 'http://127.0.0.1:2020/api/v1/installer/update'; @@ -344,28 +341,6 @@ function addDnsRecords() { }); } -function setCertificate(certificate, key, callback) { - assert.strictEqual(typeof certificate, 'string'); - assert.strictEqual(typeof key, 'string'); - assert.strictEqual(typeof callback, 'function'); - - debug('Updating certificates'); - - if (!safe.fs.writeFileSync(path.join(paths.NGINX_CERT_DIR, 'host.cert'), certificate)) { - return callback(new CloudronError(CloudronError.INTERNAL_ERROR, safe.error.message)); - } - - if (!safe.fs.writeFileSync(path.join(paths.NGINX_CERT_DIR, 'host.key'), key)) { - return callback(new CloudronError(CloudronError.INTERNAL_ERROR, safe.error.message)); - } - - shell.sudo('setCertificate', [ RELOAD_NGINX_CMD ], function (error) { - if (error) return callback(new CloudronError(CloudronError.INTERNAL_ERROR, error)); - - return callback(null); - }); -} - function reboot(callback) { shell.sudo('reboot', [ REBOOT_CMD ], callback); } diff --git a/src/routes/cloudron.js b/src/routes/cloudron.js index 77cfb8ecf..8c40ea298 100644 --- a/src/routes/cloudron.js +++ b/src/routes/cloudron.js @@ -11,7 +11,6 @@ exports = module.exports = { getConfig: getConfig, update: update, migrate: migrate, - setCertificate: setCertificate, feedback: feedback }; @@ -25,7 +24,6 @@ var assert = require('assert'), HttpError = require('connect-lastmile').HttpError, HttpSuccess = require('connect-lastmile').HttpSuccess, superagent = require('superagent'), - safe = require('safetydance'), updateChecker = require('../updatechecker.js'); /** @@ -140,23 +138,6 @@ function migrate(req, res, next) { }); } -function setCertificate(req, res, next) { - assert.strictEqual(typeof req.files, 'object'); - - if (!req.files.certificate) return next(new HttpError(400, 'certificate must be provided')); - var certificate = safe.fs.readFileSync(req.files.certificate.path, 'utf8'); - - if (!req.files.key) return next(new HttpError(400, 'key must be provided')); - var key = safe.fs.readFileSync(req.files.key.path, 'utf8'); - - cloudron.setCertificate(certificate, key, function (error) { - if (error && error.reason === CloudronError.BAD_FIELD) return next(new HttpError(400, error.message)); - if (error) return next(new HttpError(500, error)); - - next(new HttpSuccess(202, {})); - }); -} - function feedback(req, res, next) { assert.strictEqual(typeof req.user, 'object'); diff --git a/src/routes/settings.js b/src/routes/settings.js index 52f7df674..eda729d19 100644 --- a/src/routes/settings.js +++ b/src/routes/settings.js @@ -13,7 +13,9 @@ exports = module.exports = { setCloudronAvatar: setCloudronAvatar, getDnsConfig: getDnsConfig, - setDnsConfig: setDnsConfig + setDnsConfig: setDnsConfig, + + setCertificate: setCertificate }; var assert = require('assert'), @@ -109,3 +111,19 @@ function setDnsConfig(req, res, next) { next(new HttpSuccess(200)); }); } + +function setCertificate(req, res, next) { + assert.strictEqual(typeof req.files, 'object'); + + if (!req.files.certificate) return next(new HttpError(400, 'certificate must be provided')); + var certificate = safe.fs.readFileSync(req.files.certificate.path, 'utf8'); + + if (!req.files.key) return next(new HttpError(400, 'key must be provided')); + var key = safe.fs.readFileSync(req.files.key.path, 'utf8'); + + settings.setCertificate(certificate, key, function (error) { + if (error) return next(new HttpError(500, error)); + + next(new HttpSuccess(202, {})); + }); +} diff --git a/src/routes/test/cloudron-test.js b/src/routes/test/cloudron-test.js index c56126091..5653cd566 100644 --- a/src/routes/test/cloudron-test.js +++ b/src/routes/test/cloudron-test.js @@ -167,101 +167,6 @@ describe('Cloudron', function () { }); }); - describe('Certificates API', function () { - var certFile, keyFile; - - before(function (done) { - certFile = path.join(os.tmpdir(), 'host.cert'); - fs.writeFileSync(certFile, 'test certificate'); - - keyFile = path.join(os.tmpdir(), 'host.key'); - fs.writeFileSync(keyFile, 'test key'); - - async.series([ - setup, - - function (callback) { - var scope1 = nock(config.apiServerOrigin()).get('/api/v1/boxes/' + config.fqdn() + '/setup/verify?setupToken=somesetuptoken').reply(200, {}); - var scope2 = nock(config.apiServerOrigin()).post('/api/v1/boxes/' + config.fqdn() + '/setup/done?setupToken=somesetuptoken').reply(201, {}); - - request.post(SERVER_URL + '/api/v1/cloudron/activate') - .query({ setupToken: 'somesetuptoken' }) - .send({ username: USERNAME, password: PASSWORD, email: EMAIL }) - .end(function (error, result) { - expect(error).to.not.be.ok(); - expect(result).to.be.ok(); - expect(scope1.isDone()).to.be.ok(); - expect(scope2.isDone()).to.be.ok(); - - // stash token for further use - token = result.body.token; - - callback(); - }); - }, - ], done); - }); - - after(function (done) { - fs.unlinkSync(certFile); - fs.unlinkSync(keyFile); - - cleanup(done); - }); - - it('cannot set certificate without token', function (done) { - request.post(SERVER_URL + '/api/v1/cloudron/certificate') - .end(function (error, result) { - expect(error).to.not.be.ok(); - expect(result.statusCode).to.equal(401); - done(); - }); - }); - - it('cannot set certificate without certificate', function (done) { - request.post(SERVER_URL + '/api/v1/cloudron/certificate') - .query({ access_token: token }) - .attach('key', keyFile, 'key') - .end(function (error, result) { - expect(error).to.not.be.ok(); - expect(result.statusCode).to.equal(400); - done(); - }); - }); - - it('cannot set certificate without key', function (done) { - request.post(SERVER_URL + '/api/v1/cloudron/certificate') - .query({ access_token: token }) - .attach('certificate', certFile, 'certificate') - .end(function (error, result) { - expect(error).to.not.be.ok(); - expect(result.statusCode).to.equal(400); - done(); - }); - }); - - it('can set certificate', function (done) { - request.post(SERVER_URL + '/api/v1/cloudron/certificate') - .query({ access_token: token }) - .attach('key', keyFile, 'key') - .attach('certificate', certFile, 'certificate') - .end(function (error, result) { - expect(error).to.not.be.ok(); - expect(result.statusCode).to.equal(202); - done(); - }); - }); - - it('did set the certificate', function (done) { - var cert = fs.readFileSync(path.join(paths.NGINX_CERT_DIR, 'host.cert')); - expect(cert).to.eql(fs.readFileSync(certFile)); - - var key = fs.readFileSync(path.join(paths.NGINX_CERT_DIR, 'host.key')); - expect(key).to.eql(fs.readFileSync(keyFile)); - done(); - }); - }); - describe('get config', function () { before(function (done) { async.series([ diff --git a/src/routes/test/settings-test.js b/src/routes/test/settings-test.js index 010d8654e..d27a5eb18 100644 --- a/src/routes/test/settings-test.js +++ b/src/routes/test/settings-test.js @@ -11,6 +11,7 @@ var appdb = require('../../appdb.js'), config = require('../../config.js'), database = require('../../database.js'), expect = require('expect.js'), + path = require('path'), paths = require('../../paths.js'), request = require('superagent'), server = require('../../server.js'), @@ -270,5 +271,75 @@ describe('Settings API', function () { }); }); }); + + describe('Certificates API', function () { + var certFile, keyFile; + + before(function () { + certFile = '/tmp/host.cert'; + fs.writeFileSync(certFile, 'test certificate'); + + keyFile = '/tmp/host.key'; + fs.writeFileSync(keyFile, 'test key'); + }); + + after(function () { + fs.unlinkSync(certFile); + fs.unlinkSync(keyFile); + }); + + it('cannot set certificate without token', function (done) { + request.post(SERVER_URL + '/api/v1/settings/certificate') + .end(function (error, result) { + expect(error).to.not.be.ok(); + expect(result.statusCode).to.equal(401); + done(); + }); + }); + + it('cannot set certificate without certificate', function (done) { + request.post(SERVER_URL + '/api/v1/settings/certificate') + .query({ access_token: token }) + .attach('key', keyFile, 'key') + .end(function (error, result) { + expect(error).to.not.be.ok(); + expect(result.statusCode).to.equal(400); + done(); + }); + }); + + it('cannot set certificate without key', function (done) { + request.post(SERVER_URL + '/api/v1/settings/certificate') + .query({ access_token: token }) + .attach('certificate', certFile, 'certificate') + .end(function (error, result) { + expect(error).to.not.be.ok(); + expect(result.statusCode).to.equal(400); + done(); + }); + }); + + it('can set certificate', function (done) { + request.post(SERVER_URL + '/api/v1/settings/certificate') + .query({ access_token: token }) + .attach('key', keyFile, 'key') + .attach('certificate', certFile, 'certificate') + .end(function (error, result) { + expect(error).to.not.be.ok(); + expect(result.statusCode).to.equal(202); + done(); + }); + }); + + it('did set the certificate', function (done) { + var cert = fs.readFileSync(path.join(paths.NGINX_CERT_DIR, 'host.cert')); + expect(cert).to.eql(fs.readFileSync(certFile)); + + var key = fs.readFileSync(path.join(paths.NGINX_CERT_DIR, 'host.key')); + expect(key).to.eql(fs.readFileSync(keyFile)); + + done(); + }); + }); }); diff --git a/src/server.js b/src/server.js index d77729321..6e7b07434 100644 --- a/src/server.js +++ b/src/server.js @@ -95,7 +95,6 @@ function initializeExpressSync() { router.post('/api/v1/cloudron/update', rootScope, routes.user.requireAdmin, routes.user.verifyPassword, routes.cloudron.update); router.post('/api/v1/cloudron/reboot', rootScope, routes.cloudron.reboot); router.post('/api/v1/cloudron/migrate', rootScope, routes.user.requireAdmin, routes.user.verifyPassword, routes.cloudron.migrate); - router.post('/api/v1/cloudron/certificate', rootScope, multipart, routes.cloudron.setCertificate); router.get ('/api/v1/cloudron/graphs', rootScope, routes.graphs.getGraphs); // feedback @@ -163,6 +162,7 @@ function initializeExpressSync() { router.post('/api/v1/settings/cloudron_avatar', settingsScope, multipart, routes.settings.setCloudronAvatar); router.get ('/api/v1/settings/dns_config', settingsScope, routes.settings.getDnsConfig); router.post('/api/v1/settings/dns_config', settingsScope, routes.settings.setDnsConfig); + router.post('/api/v1/settings/certificate', settingsScope, multipart, routes.settings.setCertificate); // backup routes router.get ('/api/v1/backups', settingsScope, routes.backups.get); diff --git a/src/settings.js b/src/settings.js index 74947755a..ecc82991c 100644 --- a/src/settings.js +++ b/src/settings.js @@ -26,6 +26,8 @@ exports = module.exports = { getDefaultSync: getDefaultSync, getAll: getAll, + setCertificate: setCertificate, + AUTOUPDATE_PATTERN_KEY: 'autoupdate_pattern', TIME_ZONE_KEY: 'time_zone', CLOUDRON_NAME_KEY: 'cloudron_name', @@ -39,9 +41,11 @@ var assert = require('assert'), config = require('./config.js'), CronJob = require('cron').CronJob, DatabaseError = require('./databaseerror.js'), + path = require('path'), paths = require('./paths.js'), safe = require('safetydance'), settingsdb = require('./settingsdb.js'), + shell = require('./shell.js'), util = require('util'), _ = require('underscore'); @@ -56,6 +60,8 @@ var gDefaults = (function () { return result; })(); +var RELOAD_NGINX_CMD = path.join(__dirname, 'scripts/reloadnginx.sh'); + if (config.TEST) { // avoid noisy warnings during npm test exports.events.setMaxListeners(100); @@ -264,3 +270,23 @@ function getAll(callback) { callback(null, result); }); } + +function setCertificate(certificate, key, callback) { + assert.strictEqual(typeof certificate, 'string'); + assert.strictEqual(typeof key, 'string'); + assert.strictEqual(typeof callback, 'function'); + + if (!safe.fs.writeFileSync(path.join(paths.NGINX_CERT_DIR, 'host.cert'), certificate)) { + return callback(new SettingsError(SettingsError.INTERNAL_ERROR, safe.error.message)); + } + + if (!safe.fs.writeFileSync(path.join(paths.NGINX_CERT_DIR, 'host.key'), key)) { + return callback(new SettingsError(SettingsError.INTERNAL_ERROR, safe.error.message)); + } + + shell.sudo('setCertificate', [ RELOAD_NGINX_CMD ], function (error) { + if (error) return callback(new SettingsError(SettingsError.INTERNAL_ERROR, error)); + + return callback(null); + }); +}