diff --git a/core/server/api/configuration.js b/core/server/api/configuration.js new file mode 100644 index 0000000000..8fc6e72ef3 --- /dev/null +++ b/core/server/api/configuration.js @@ -0,0 +1,78 @@ +// # Configuration API +// RESTful API for browsing the configuration +var _ = require('lodash'), + canThis = require('../permissions').canThis, + config = require('../config'), + errors = require('../errors'), + parsePackageJson = require('../require-tree').parsePackageJson, + Promise = require('bluebird'), + + configuration; + +function getValidKeys() { + var validKeys = { + 'fileStorage': config.fileStorage === false ? false : true, + 'apps': config.apps || false, + 'version': false, + 'environment': process.env.NODE_ENV, + 'database': config.database.client, + 'mail': _.isObject(config.mail) ? config.mail.transport : '', + 'blogUrl': config.url + }; + + return parsePackageJson('package.json').then(function (json) { + validKeys.version = json.version; + return validKeys; + }); +} + +/** + * ## Configuration API Methods + * + * **See:** [API Methods](index.js.html#api%20methods) + */ +configuration = { + + /** + * ### Browse + * Fetch all configuration keys + * @returns {Promise(Configurations)} + */ + browse: function browse(options) { + return canThis(options.context).browse.configuration().then(function () { + return getValidKeys().then(function (result) { + return { 'configuration': _.map(result, function (value, key) { + return { + key: key, + value: value + }; + })}; + }); + }, function () { + return Promise.reject(new errors.NoPermissionError('You do not have permission to browse the configuration.')); + }); + }, + + /** + * ### Read + * + */ + read: function read(options) { + return canThis(options.context).read.configuration().then(function () { + return getValidKeys().then(function (result) { + if (_.has(result, options.key)) { + return { 'configuration': [{ + key: options.key, + value: result[options.key] + }]}; + } else { + return Promise.reject(new errors.NotFoundError('Invalid key')); + } + }); + }, function () { + return Promise.reject(new errors.NoPermissionError('You do not have permission to read the configuration.')); + }); + } +}; + +module.exports = configuration; diff --git a/core/server/api/index.js b/core/server/api/index.js index 359af6c31f..2741659c22 100644 --- a/core/server/api/index.js +++ b/core/server/api/index.js @@ -8,6 +8,7 @@ var _ = require('lodash'), Promise = require('bluebird'), config = require('../config'), // Include Endpoints + configuration = require('./configuration'), db = require('./db'), mail = require('./mail'), notifications = require('./notifications'), @@ -278,6 +279,7 @@ module.exports = { init: init, http: http, // API Endpoints + configuration: configuration, db: db, mail: mail, notifications: notifications, diff --git a/core/server/data/fixtures/permissions/permissions.json b/core/server/data/fixtures/permissions/permissions.json index a13b5276d5..df9341c82a 100644 --- a/core/server/data/fixtures/permissions/permissions.json +++ b/core/server/data/fixtures/permissions/permissions.json @@ -1,5 +1,15 @@ { "permissions": { + "configuration": [ + { + "name": "Browse configuration", + "action_type": "browse" + }, + { + "name": "Read configuration", + "action_type": "read" + } + ], "db": [ { "name": "Export database", @@ -143,6 +153,7 @@ }, "permissions_roles": { "Administrator": { + "configuration": "all", "db": "all", "mail": "all", "notification": "all", @@ -155,6 +166,7 @@ "role": "all" }, "Editor": { + "configuration": "all", "post": "all", "setting": ["browse", "read"], "slug": "all", @@ -164,6 +176,7 @@ "role": "all" }, "Author": { + "configuration": "all", "post": ["browse", "read", "add"], "setting": ["browse", "read"], "slug": "all", diff --git a/core/server/routes/api.js b/core/server/routes/api.js index 67a60150cf..f35e4eab39 100644 --- a/core/server/routes/api.js +++ b/core/server/routes/api.js @@ -8,6 +8,10 @@ apiRoutes = function (middleware) { // alias delete with del router.del = router.delete; + // ## Configuration + router.get('/configuration', api.http(api.configuration.browse)); + router.get('/configuration/:key', api.http(api.configuration.read)); + // ## Posts router.get('/posts', api.http(api.posts.browse)); router.post('/posts', api.http(api.posts.add)); diff --git a/core/test/integration/api/api_configuration_spec.js b/core/test/integration/api/api_configuration_spec.js new file mode 100644 index 0000000000..62644492b7 --- /dev/null +++ b/core/test/integration/api/api_configuration_spec.js @@ -0,0 +1,75 @@ +/*globals describe, before, beforeEach, afterEach, it */ +/*jshint expr:true*/ +var testUtils = require('../../utils'), + should = require('should'), + sinon = require('sinon'), + + + rewire = require('rewire'), + _ = require('lodash'), + config = rewire('../../../server/config'), + + // Stuff we are testing + ConfigurationAPI = rewire('../../../server/api/configuration'); + +describe('Configuration API', function () { + var newConfig = { + 'fileStorage': true, + 'apps': true, + 'version': '0.5.0', + 'environment': process.env.NODE_ENV, + 'database': { + 'client': 'mysql' + }, + 'mail': { + 'transport': 'SMTP' + }, + 'blogUrl': 'http://local.tryghost.org' + }; + + // Keep the DB clean + before(testUtils.teardown); + afterEach(testUtils.teardown); + + beforeEach(testUtils.setup('users','users:roles', 'perms:user', 'perms:role', 'perms:configuration', 'perms:init')); + + should.exist(ConfigurationAPI); + + it('can browse config', function (done) { + var existingConfig = ConfigurationAPI.__get__('config'), + updatedConfig = _.extend(config, newConfig); + config.set(updatedConfig); + ConfigurationAPI.__set__('config', updatedConfig); + + ConfigurationAPI.browse(testUtils.context.owner).then(function (response) { + should.exist(response); + should.exist(response.configuration); + testUtils.API.checkResponse(response.configuration[0], 'configuration'); + /*jshint unused:false */ + done(); + }).catch(function (error) { + console.log(JSON.stringify(error)); + done(); + }).catch(done); + }); + + it('can read config', function (done) { + var existingConfig = ConfigurationAPI.__get__('config'), + updatedConfig = _.extend(config, newConfig); + config.set(updatedConfig); + ConfigurationAPI.__set__('config', updatedConfig); + + ConfigurationAPI.read(_.extend(testUtils.context.owner, { key: 'database' })).then(function (response) { + should.exist(response); + should.exist(response.configuration); + testUtils.API.checkResponse(response.configuration[0], 'configuration'); + response.configuration[0].key.should.equal('database'); + response.configuration[0].value.should.equal('mysql'); + /*jshint unused:false */ + done(); + }).catch(function (error) { + console.log(JSON.stringify(error)); + done(); + }).catch(done); + }); +}); \ No newline at end of file diff --git a/core/test/utils/api.js b/core/test/utils/api.js index 7880b79c74..19a66aa16b 100644 --- a/core/test/utils/api.js +++ b/core/test/utils/api.js @@ -6,6 +6,7 @@ var url = require('url'), port = config.server.port, schema = 'http://', expectedProperties = { + configuration: ['key', 'value'], posts: ['posts', 'meta'], users: ['users', 'meta'], roles: ['roles'],