Minor internal apps improvements

refs #6589

- add internalAppsPath as a proper config path
- middleware/routes will be setup for any internal apps which have the function
- this should be refactored into some sort of proper hooks system as part of apps
- internal apps get permission to do anything the proxy allows
This commit is contained in:
Hannah Wolfe 2016-04-14 18:32:43 +01:00
parent 9a674018b0
commit c5eda57f1e
8 changed files with 67 additions and 15 deletions

View File

@ -17,7 +17,7 @@ function isInternalApp(name) {
// Get the full path to an app by name
function getAppAbsolutePath(name) {
if (isInternalApp(name)) {
return path.join(config.paths.corePath, '/server/apps/', name);
return path.join(config.paths.internalAppPath, name);
}
return path.join(config.paths.appPath, name);
@ -49,7 +49,8 @@ function getAppByName(name, permissions) {
var AppClass = loadApp(getAppRelativePath(name), isInternalApp(name)),
appProxy = new AppProxy({
name: name,
permissions: permissions
permissions: permissions,
internal: isInternalApp(name)
}),
app;

View File

@ -1,4 +1,3 @@
var fs = require('fs'),
Promise = require('bluebird'),
path = require('path'),

View File

@ -5,7 +5,7 @@ var _ = require('lodash'),
i18n = require('../i18n'),
generateProxyFunctions;
generateProxyFunctions = function (name, permissions) {
generateProxyFunctions = function (name, permissions, isInternal) {
var getPermission = function (perm) {
return permissions[perm];
},
@ -21,6 +21,11 @@ generateProxyFunctions = function (name, permissions) {
});
},
runIfPermissionToMethod = function (perm, method, wrappedFunc, context, args) {
// internal apps get all permissions
if (isInternal) {
return wrappedFunc.apply(context, args);
}
var permValue = getPermissionToMethod(perm, method);
if (!permValue) {
@ -92,7 +97,7 @@ function AppProxy(options) {
throw new Error(i18n.t('errors.apps.mustProvideAppPermissions.error'));
}
_.extend(this, generateProxyFunctions(options.name, options.permissions));
_.extend(this, generateProxyFunctions(options.name, options.permissions, options.internal));
}
module.exports = AppProxy;

View File

@ -169,6 +169,7 @@ ConfigManager.prototype.set = function (config) {
themePath: path.resolve(contentPath, 'themes'),
appPath: path.resolve(contentPath, 'apps'),
imagesPath: path.resolve(contentPath, 'images'),
internalAppPath: path.join(corePath, '/server/apps/'),
imagesRelPath: 'content/images',
adminViews: path.join(corePath, '/server/views/'),
@ -190,7 +191,8 @@ ConfigManager.prototype.set = function (config) {
author: 'author',
page: 'page',
preview: 'p',
private: 'private'
private: 'private',
subscribe: 'subscribe'
},
internalApps: ['private-blogging'],
slugs: {

View File

@ -25,7 +25,6 @@ var bodyParser = require('body-parser'),
themeHandler = require('./theme-handler'),
uncapitalise = require('./uncapitalise'),
cors = require('./cors'),
privateBlogging = require('../apps/private-blogging'),
ClientPasswordStrategy = require('passport-oauth2-client-password').Strategy,
BearerStrategy = require('passport-http-bearer').Strategy,
@ -110,8 +109,14 @@ setupMiddleware = function setupMiddleware(blogApp, adminApp) {
// Theme only config
blogApp.use(staticTheme());
// setup middleware for private blogs
privateBlogging.setupMiddleware(blogApp);
// setup middleware for internal apps
// @TODO: refactor this to be a proper app middleware hook for internal & external apps
config.internalApps.forEach(function (appName) {
var app = require(path.join(config.paths.internalAppPath, appName));
if (app.hasOwnProperty('setupMiddleware')) {
app.setupMiddleware(blogApp);
}
});
// Serve sitemap.xsl file
blogApp.use(serveSharedFile('sitemap.xsl', 'text/xsl', utils.ONE_DAY_S));

View File

@ -1,9 +1,9 @@
var frontend = require('../controllers/frontend'),
channels = require('../controllers/frontend/channels'),
var express = require('express'),
path = require('path'),
config = require('../config'),
express = require('express'),
frontend = require('../controllers/frontend'),
channels = require('../controllers/frontend/channels'),
utils = require('../utils'),
privateBlogging = require('../apps/private-blogging'),
frontendRoutes;
@ -34,8 +34,14 @@ frontendRoutes = function frontendRoutes() {
// Default
router.get('*', frontend.single);
// @TODO: this can be removed once the proper app route hooks have been set up.
privateBlogging.setupRoutes(router);
// setup routes for internal apps
// @TODO: refactor this to be a proper app route hook for internal & external apps
config.internalApps.forEach(function (appName) {
var app = require(path.join(config.paths.internalAppPath, appName));
if (app.hasOwnProperty('setupRoutes')) {
app.setupRoutes(router);
}
});
return router;
};

View File

@ -254,6 +254,24 @@ describe('Apps', function () {
registerSpy.called.should.equal(false);
});
it('does allow INTERNAL app to register helper without permission', function () {
var registerSpy = sandbox.spy(helpers, 'registerThemeHelper'),
appProxy = new AppProxy({
name: 'TestApp',
permissions: {},
internal: true
});
function registerWithoutPermissions() {
appProxy.helpers.register('otherHelper', sandbox.stub().returns('test result'));
}
registerWithoutPermissions.should.not.throw('The App "TestApp" attempted to perform an action or access a ' +
'resource (helpers.otherHelper) without permission.');
registerSpy.called.should.equal(true);
});
});
describe('Sandbox', function () {
@ -332,6 +350,21 @@ describe('Apps', function () {
loadApp.should.throw(/^Unsafe App require[\w\W]*example$/);
});
it('does allow INTERNAL apps to require modules relatively outside their directory', function () {
var appBox = new AppSandbox({internal: true}),
badAppPath = path.join(__dirname, '..', 'utils', 'fixtures', 'app', 'badoutside.js'),
InternalApp,
loadApp = function () {
InternalApp = appBox.loadApp(badAppPath);
};
InternalApp = appBox.loadApp(badAppPath);
loadApp.should.not.throw(/^Unsafe App require[\w\W]*example$/);
InternalApp.should.be.a.Function();
});
});
describe('Dependencies', function () {

View File

@ -72,6 +72,7 @@ describe('Config', function () {
'corePath',
'themePath',
'appPath',
'internalAppPath',
'imagesPath',
'imagesRelPath',
'adminViews',