diff --git a/src/routes/settings.js b/src/routes/settings.js index 253671804..1a094fb1f 100644 --- a/src/routes/settings.js +++ b/src/routes/settings.js @@ -186,9 +186,14 @@ function setAppstoreConfig(req, res, next) { settings.setAppstoreConfig(options, function (error) { if (error && error.reason === SettingsError.BAD_FIELD) return next(new HttpError(400, error.message)); + if (error && error.reason === SettingsError.EXTERNAL_ERROR) return next(new HttpError(406, error.message)); if (error) return next(new HttpError(500, error)); - next(new HttpSuccess(200)); + settings.getAppstoreConfig(function (error, result) { + if (error) return next(new HttpError(500, error)); + + next(new HttpSuccess(202, result)); + }); }); } diff --git a/src/routes/test/settings-test.js b/src/routes/test/settings-test.js index fbdb2e5c7..0ec48e0f5 100644 --- a/src/routes/test/settings-test.js +++ b/src/routes/test/settings-test.js @@ -372,5 +372,112 @@ describe('Settings API', function () { }); }); }); -}); + describe('appstore_config', function () { + it('get appstore_config fails', function (done) { + superagent.get(SERVER_URL + '/api/v1/settings/appstore_config') + .query({ access_token: token }) + .end(function (err, res) { + expect(res.statusCode).to.equal(200); + expect(res.body).to.eql({}); + done(); + }); + }); + + it('cannot set without data', function (done) { + superagent.post(SERVER_URL + '/api/v1/settings/appstore_config') + .query({ access_token: token }) + .end(function (err, res) { + expect(res.statusCode).to.equal(400); + done(); + }); + }); + + it('set fails with wrong appstore token', function (done) { + var scope = nock(config.apiServerOrigin()).post('/api/v1/users/nebulon/cloudrons?accessToken=sometoken').reply(401); + + superagent.post(SERVER_URL + '/api/v1/settings/appstore_config') + .query({ access_token: token }) + .send({ userId: 'nebulon', token: 'sometoken' }) + .end(function (err, res) { + expect(scope.isDone()).to.be.ok(); + expect(res.statusCode).to.equal(406); + expect(res.body.message).to.equal('invalid appstore token'); + + done(); + }); + }); + + it('set succeeds for unknown cloudron', function (done) { + var scope = nock(config.apiServerOrigin()).post('/api/v1/users/nebulon/cloudrons?accessToken=sometoken').reply(201, { cloudron: { id: 'cloudron0' }}); + + superagent.post(SERVER_URL + '/api/v1/settings/appstore_config') + .query({ access_token: token }) + .send({ userId: 'nebulon', token: 'sometoken' }) + .end(function (err, res) { + expect(scope.isDone()).to.be.ok(); + expect(res.statusCode).to.equal(202); + expect(res.body).to.eql({ userId: 'nebulon', token: 'sometoken', cloudronId: 'cloudron0' }); + + done(); + }); + }); + + it('set fails with wrong appstore user', function (done) { + var scope = nock(config.apiServerOrigin()).get('/api/v1/users/nebulon/cloudrons/cloudron0?accessToken=sometoken').reply(403); + + superagent.post(SERVER_URL + '/api/v1/settings/appstore_config') + .query({ access_token: token }) + .send({ userId: 'nebulon', token: 'sometoken' }) + .end(function (err, res) { + expect(scope.isDone()).to.be.ok(); + expect(res.statusCode).to.equal(406); + expect(res.body.message).to.equal('wrong user'); + + done(); + }); + }); + + it('get succeeds', function (done) { + superagent.get(SERVER_URL + '/api/v1/settings/appstore_config') + .query({ access_token: token }) + .end(function (err, res) { + expect(res.statusCode).to.equal(200); + expect(res.body).to.eql({ userId: 'nebulon', token: 'sometoken', cloudronId: 'cloudron0' }); + done(); + }); + }); + + it('set succeeds with cloudronId', function (done) { + var scope = nock(config.apiServerOrigin()).get('/api/v1/users/nebulon/cloudrons/cloudron0?accessToken=someothertoken').reply(200, { cloudron: { id: 'cloudron0' }}); + + superagent.post(SERVER_URL + '/api/v1/settings/appstore_config') + .query({ access_token: token }) + .send({ userId: 'nebulon', token: 'someothertoken' }) + .end(function (err, res) { + expect(scope.isDone()).to.be.ok(); + expect(res.statusCode).to.equal(202); + expect(res.body).to.eql({ userId: 'nebulon', token: 'someothertoken', cloudronId: 'cloudron0' }); + + done(); + }); + }); + + it('set succeeds with cloudronId but unkown one (reregister)', function (done) { + var scope0 = nock(config.apiServerOrigin()).get('/api/v1/users/nebulon/cloudrons/cloudron0?accessToken=someothertoken').reply(404); + var scope1 = nock(config.apiServerOrigin()).post('/api/v1/users/nebulon/cloudrons?accessToken=someothertoken').reply(201, { cloudron: { id: 'cloudron1' }}); + + superagent.post(SERVER_URL + '/api/v1/settings/appstore_config') + .query({ access_token: token }) + .send({ userId: 'nebulon', token: 'someothertoken' }) + .end(function (err, res) { + expect(scope0.isDone()).to.be.ok(); + expect(scope1.isDone()).to.be.ok(); + expect(res.statusCode).to.equal(202); + expect(res.body).to.eql({ userId: 'nebulon', token: 'someothertoken', cloudronId: 'cloudron1' }); + + done(); + }); + }); + }); +}); diff --git a/src/settings.js b/src/settings.js index f0e376d79..eaf418df7 100644 --- a/src/settings.js +++ b/src/settings.js @@ -61,6 +61,7 @@ var assert = require('assert'), safe = require('safetydance'), settingsdb = require('./settingsdb.js'), SubdomainError = require('./subdomains.js').SubdomainError, + superagent = require('superagent'), sysinfo = require('./sysinfo.js'), util = require('util'), _ = require('underscore'); @@ -105,6 +106,7 @@ function SettingsError(reason, errorOrMessage) { } util.inherits(SettingsError, Error); SettingsError.INTERNAL_ERROR = 'Internal Error'; +SettingsError.EXTERNAL_ERROR = 'External Error'; SettingsError.NOT_FOUND = 'Not Found'; SettingsError.BAD_FIELD = 'Bad Field'; @@ -425,13 +427,59 @@ function setAppstoreConfig(appstoreConfig, callback) { assert.strictEqual(typeof appstoreConfig, 'object'); assert.strictEqual(typeof callback, 'function'); - settingsdb.set(exports.APPSTORE_CONFIG_KEY, JSON.stringify(appstoreConfig), function (error) { - if (error) return callback(new SettingsError(SettingsError.INTERNAL_ERROR, error)); + getAppstoreConfig(function (error, oldConfig) { + if (error) return callback(error); - exports.events.emit(exports.APPSTORE_CONFIG_KEY, appstoreConfig); + var cloudronId = oldConfig.cloudronId; - callback(null); + function setNewConfig() { + var data = { + userId: appstoreConfig.userId, + token: appstoreConfig.token, + cloudronId: cloudronId + }; + + settingsdb.set(exports.APPSTORE_CONFIG_KEY, JSON.stringify(data), function (error) { + if (error) return callback(new SettingsError(SettingsError.INTERNAL_ERROR, error)); + + exports.events.emit(exports.APPSTORE_CONFIG_KEY, appstoreConfig); + + callback(null); + }); + } + + function registerCloudron() { + const url = config.apiServerOrigin() + '/api/v1/users/' + appstoreConfig.userId + '/cloudrons'; + const data = { + domain: config.fqdn() + }; + + superagent.post(url).send(data).query({ accessToken: appstoreConfig.token }).end(function (error, result) { + if (error && !error.response) return callback(new SettingsError(SettingsError.EXTERNAL_ERROR, error.message)); + if (result.statusCode === 401) return callback(new SettingsError(SettingsError.EXTERNAL_ERROR, 'invalid appstore token')); + if (result.statusCode !== 201) return callback(new SettingsError(SettingsError.EXTERNAL_ERROR, 'unable to register cloudron')); + + cloudronId = result.body.cloudron.id; + + setNewConfig(); + }); + } + + if (!cloudronId) return registerCloudron(); + + // verify that cloudron belongs to this user + const url = config.apiServerOrigin() + '/api/v1/users/' + appstoreConfig.userId + '/cloudrons/' + oldConfig.cloudronId; + superagent.get(url).query({ accessToken: appstoreConfig.token }).end(function (error, result) { + if (error && !error.response) return callback(new SettingsError(SettingsError.EXTERNAL_ERROR, error.message)); + if (result.statusCode === 401) return callback(new SettingsError(SettingsError.EXTERNAL_ERROR, 'invalid appstore token')); + if (result.statusCode === 403) return callback(new SettingsError(SettingsError.EXTERNAL_ERROR, 'wrong user')); + if (result.statusCode === 404) return registerCloudron(); + if (result.statusCode !== 200) return callback(new SettingsError(SettingsError.EXTERNAL_ERROR, 'unknown error')); + + setNewConfig(); + }); }); + } function getDefaultSync(name) {