2014-01-31 16:53:27 -06:00
/*globals describe, beforeEach, afterEach, before, it*/
2014-02-02 00:07:39 -06:00
var fs = require ( 'fs' ) ,
path = require ( 'path' ) ,
EventEmitter = require ( 'events' ) . EventEmitter ,
should = require ( 'should' ) ,
sinon = require ( 'sinon' ) ,
_ = require ( 'lodash' ) ,
2014-02-11 19:04:13 -06:00
when = require ( 'when' ) ,
2014-02-02 00:07:39 -06:00
helpers = require ( '../../server/helpers' ) ,
filters = require ( '../../server/filters' ) ,
2014-04-21 19:22:13 -05:00
api = require ( '../../server/api' ) ,
2014-01-31 16:53:27 -06:00
// Stuff we are testing
2014-04-21 19:22:13 -05:00
AppProxy = require ( '../../server/apps/proxy' ) ,
AppSandbox = require ( '../../server/apps/sandbox' ) ,
2014-02-11 19:04:13 -06:00
AppDependencies = require ( '../../server/apps/dependencies' ) ,
2014-04-21 19:22:13 -05:00
AppPermissions = require ( '../../server/apps/permissions' ) ;
2014-01-31 16:53:27 -06:00
describe ( 'Apps' , function ( ) {
var sandbox ,
fakeApi ;
beforeEach ( function ( ) {
sandbox = sinon . sandbox . create ( ) ;
fakeApi = {
posts : {
browse : sandbox . stub ( ) ,
read : sandbox . stub ( ) ,
edit : sandbox . stub ( ) ,
add : sandbox . stub ( ) ,
destroy : sandbox . stub ( )
} ,
users : {
browse : sandbox . stub ( ) ,
read : sandbox . stub ( ) ,
edit : sandbox . stub ( )
} ,
tags : {
all : sandbox . stub ( )
} ,
notifications : {
destroy : sandbox . stub ( ) ,
add : sandbox . stub ( )
} ,
settings : {
browse : sandbox . stub ( ) ,
read : sandbox . stub ( ) ,
add : sandbox . stub ( )
}
} ;
} ) ;
afterEach ( function ( ) {
sandbox . restore ( ) ;
} ) ;
describe ( 'Proxy' , function ( ) {
2014-04-21 19:22:13 -05:00
it ( 'requires a name to be passed' , function ( ) {
function makeWithoutName ( ) {
return new AppProxy ( { } ) ;
}
makeWithoutName . should . throw ( 'Must provide an app name for api context' ) ;
} ) ;
it ( 'requires permissions to be passed' , function ( ) {
function makeWithoutPerms ( ) {
return new AppProxy ( {
name : 'NoPerms'
} ) ;
}
makeWithoutPerms . should . throw ( 'Must provide app permissions' ) ;
} ) ;
2014-01-31 16:53:27 -06:00
it ( 'creates a ghost proxy' , function ( ) {
2014-04-21 19:22:13 -05:00
var appProxy = new AppProxy ( {
name : 'TestApp' ,
permissions : {
filters : [ 'prePostRender' ] ,
helpers : [ 'myTestHelper' ] ,
posts : [ 'browse' , 'read' , 'edit' , 'add' , 'delete' ]
}
} ) ;
2014-01-31 16:53:27 -06:00
should . exist ( appProxy . filters ) ;
2014-02-10 00:17:28 +01:00
should . exist ( appProxy . filters . register ) ;
2014-02-10 12:44:07 +00:00
should . exist ( appProxy . filters . deregister ) ;
2014-01-31 16:53:27 -06:00
should . exist ( appProxy . helpers ) ;
2014-02-10 00:17:28 +01:00
should . exist ( appProxy . helpers . register ) ;
should . exist ( appProxy . helpers . registerAsync ) ;
2014-01-31 16:53:27 -06:00
should . exist ( appProxy . api ) ;
should . exist ( appProxy . api . posts ) ;
2014-04-21 19:22:13 -05:00
should . exist ( appProxy . api . posts . browse ) ;
should . exist ( appProxy . api . posts . read ) ;
should . exist ( appProxy . api . posts . edit ) ;
should . exist ( appProxy . api . posts . add ) ;
should . exist ( appProxy . api . posts . destroy ) ;
2014-01-31 16:53:27 -06:00
should . not . exist ( appProxy . api . users ) ;
should . exist ( appProxy . api . tags ) ;
2014-04-21 19:22:13 -05:00
should . exist ( appProxy . api . tags . browse ) ;
2014-01-31 16:53:27 -06:00
should . exist ( appProxy . api . notifications ) ;
2014-04-21 19:22:13 -05:00
should . exist ( appProxy . api . notifications . browse ) ;
should . exist ( appProxy . api . notifications . add ) ;
should . exist ( appProxy . api . notifications . destroy ) ;
2014-01-31 16:53:27 -06:00
should . exist ( appProxy . api . settings ) ;
2014-04-21 19:22:13 -05:00
should . exist ( appProxy . api . settings . browse ) ;
should . exist ( appProxy . api . settings . read ) ;
should . exist ( appProxy . api . settings . edit ) ;
} ) ;
it ( 'allows filter registration with permission' , function ( done ) {
var registerSpy = sandbox . spy ( filters , 'registerFilter' ) ;
var appProxy = new AppProxy ( {
name : 'TestApp' ,
permissions : {
filters : [ 'testFilter' ] ,
helpers : [ 'myTestHelper' ] ,
posts : [ 'browse' , 'read' , 'edit' , 'add' , 'delete' ]
}
} ) ;
var fakePosts = [ { id : 0 } , { id : 1 } ] ;
var filterStub = sandbox . spy ( function ( val ) {
return val ;
} ) ;
appProxy . filters . register ( 'testFilter' , 5 , filterStub ) ;
registerSpy . called . should . equal ( true ) ;
filterStub . called . should . equal ( false ) ;
filters . doFilter ( 'testFilter' , fakePosts )
. then ( function ( ) {
filterStub . called . should . equal ( true ) ;
appProxy . filters . deregister ( 'testFilter' , 5 , filterStub ) ;
done ( ) ;
} )
2014-05-05 21:58:58 +01:00
. catch ( done ) ;
2014-04-21 19:22:13 -05:00
} ) ;
it ( 'does not allow filter registration without permission' , function ( ) {
var registerSpy = sandbox . spy ( filters , 'registerFilter' ) ;
var appProxy = new AppProxy ( {
name : 'TestApp' ,
permissions : {
filters : [ 'prePostRender' ] ,
helpers : [ 'myTestHelper' ] ,
posts : [ 'browse' , 'read' , 'edit' , 'add' , 'delete' ]
}
} ) ;
var filterStub = sandbox . stub ( ) . returns ( 'test result' ) ;
function registerFilterWithoutPermission ( ) {
appProxy . filters . register ( 'superSecretFilter' , 5 , filterStub ) ;
}
registerFilterWithoutPermission . should . throw ( 'The App "TestApp" attempted to perform an action or access a resource (filters.superSecretFilter) without permission.' ) ;
registerSpy . called . should . equal ( false ) ;
} ) ;
it ( 'allows filter deregistration with permission' , function ( done ) {
var registerSpy = sandbox . spy ( filters , 'deregisterFilter' ) ;
var appProxy = new AppProxy ( {
name : 'TestApp' ,
permissions : {
filters : [ 'prePostsRender' ] ,
helpers : [ 'myTestHelper' ] ,
posts : [ 'browse' , 'read' , 'edit' , 'add' , 'delete' ]
}
} ) ;
var fakePosts = [ { id : 0 } , { id : 1 } ] ;
var filterStub = sandbox . stub ( ) . returns ( fakePosts ) ;
appProxy . filters . deregister ( 'prePostsRender' , 5 , filterStub ) ;
registerSpy . called . should . equal ( true ) ;
filterStub . called . should . equal ( false ) ;
filters . doFilter ( 'prePostsRender' , fakePosts )
. then ( function ( ) {
filterStub . called . should . equal ( false ) ;
done ( ) ;
} )
2014-05-05 21:58:58 +01:00
. catch ( done ) ;
2014-04-21 19:22:13 -05:00
} ) ;
it ( 'does not allow filter deregistration without permission' , function ( ) {
var registerSpy = sandbox . spy ( filters , 'deregisterFilter' ) ;
var appProxy = new AppProxy ( {
name : 'TestApp' ,
permissions : {
filters : [ 'prePostRender' ] ,
helpers : [ 'myTestHelper' ] ,
posts : [ 'browse' , 'read' , 'edit' , 'add' , 'delete' ]
}
} ) ;
var filterStub = sandbox . stub ( ) . returns ( 'test result' ) ;
function deregisterFilterWithoutPermission ( ) {
appProxy . filters . deregister ( 'superSecretFilter' , 5 , filterStub ) ;
}
deregisterFilterWithoutPermission . should . throw ( 'The App "TestApp" attempted to perform an action or access a resource (filters.superSecretFilter) without permission.' ) ;
registerSpy . called . should . equal ( false ) ;
} ) ;
it ( 'allows helper registration with permission' , function ( ) {
var registerSpy = sandbox . spy ( helpers , 'registerThemeHelper' ) ;
var appProxy = new AppProxy ( {
name : 'TestApp' ,
permissions : {
filters : [ 'prePostRender' ] ,
helpers : [ 'myTestHelper' ] ,
posts : [ 'browse' , 'read' , 'edit' , 'add' , 'delete' ]
}
} ) ;
appProxy . helpers . register ( 'myTestHelper' , sandbox . stub ( ) . returns ( 'test result' ) ) ;
registerSpy . called . should . equal ( true ) ;
} ) ;
it ( 'does not allow helper registration without permission' , function ( ) {
var registerSpy = sandbox . spy ( helpers , 'registerThemeHelper' ) ;
var appProxy = new AppProxy ( {
name : 'TestApp' ,
permissions : {
filters : [ 'prePostRender' ] ,
helpers : [ 'myTestHelper' ] ,
posts : [ 'browse' , 'read' , 'edit' , 'add' , 'delete' ]
}
} ) ;
function registerWithoutPermissions ( ) {
appProxy . helpers . register ( 'otherHelper' , sandbox . stub ( ) . returns ( 'test result' ) ) ;
}
registerWithoutPermissions . should . throw ( 'The App "TestApp" attempted to perform an action or access a resource (helpers.otherHelper) without permission.' ) ;
registerSpy . called . should . equal ( false ) ;
2014-01-31 16:53:27 -06:00
} ) ;
} ) ;
2014-02-06 11:58:58 +00:00
2014-01-31 16:53:27 -06:00
describe ( 'Sandbox' , function ( ) {
it ( 'loads apps in a sandbox' , function ( ) {
var appBox = new AppSandbox ( ) ,
appPath = path . resolve ( _ _dirname , '..' , 'utils' , 'fixtures' , 'app' , 'good.js' ) ,
GoodApp ,
2014-04-21 19:22:13 -05:00
appProxy = new AppProxy ( {
name : 'TestApp' ,
permissions : { }
} ) ,
2014-01-31 16:53:27 -06:00
app ;
GoodApp = appBox . loadApp ( appPath ) ;
should . exist ( GoodApp ) ;
app = new GoodApp ( appProxy ) ;
app . install ( appProxy ) ;
app . app . something . should . equal ( 42 ) ;
app . app . util . util ( ) . should . equal ( 42 ) ;
app . app . nested . other . should . equal ( 42 ) ;
app . app . path . should . equal ( appPath ) ;
} ) ;
it ( 'does not allow apps to require blacklisted modules at top level' , function ( ) {
var appBox = new AppSandbox ( ) ,
badAppPath = path . join ( _ _dirname , '..' , 'utils' , 'fixtures' , 'app' , 'badtop.js' ) ,
BadApp ,
app ,
loadApp = function ( ) {
appBox . loadApp ( badAppPath ) ;
} ;
loadApp . should . throw ( 'Unsafe App require: knex' ) ;
} ) ;
it ( 'does not allow apps to require blacklisted modules at install' , function ( ) {
var appBox = new AppSandbox ( ) ,
badAppPath = path . join ( _ _dirname , '..' , 'utils' , 'fixtures' , 'app' , 'badinstall.js' ) ,
BadApp ,
2014-04-21 19:22:13 -05:00
appProxy = new AppProxy ( {
name : 'TestApp' ,
permissions : { }
} ) ,
2014-01-31 16:53:27 -06:00
app ,
installApp = function ( ) {
app . install ( appProxy ) ;
} ;
BadApp = appBox . loadApp ( badAppPath ) ;
app = new BadApp ( appProxy ) ;
installApp . should . throw ( 'Unsafe App require: knex' ) ;
} ) ;
it ( 'does not allow apps to require blacklisted modules from other requires' , function ( ) {
var appBox = new AppSandbox ( ) ,
badAppPath = path . join ( _ _dirname , '..' , 'utils' , 'fixtures' , 'app' , 'badrequire.js' ) ,
BadApp ,
app ,
loadApp = function ( ) {
BadApp = appBox . loadApp ( badAppPath ) ;
} ;
loadApp . should . throw ( 'Unsafe App require: knex' ) ;
} ) ;
it ( 'does not allow apps to require modules relatively outside their directory' , function ( ) {
var appBox = new AppSandbox ( ) ,
badAppPath = path . join ( _ _dirname , '..' , 'utils' , 'fixtures' , 'app' , 'badoutside.js' ) ,
BadApp ,
app ,
loadApp = function ( ) {
BadApp = appBox . loadApp ( badAppPath ) ;
} ;
2014-02-06 11:58:58 +00:00
loadApp . should . throw ( /^Unsafe App require[\w\W]*example$/ ) ;
2014-01-31 16:53:27 -06:00
} ) ;
} ) ;
2014-02-02 00:07:39 -06:00
describe ( 'Dependencies' , function ( ) {
it ( 'can install by package.json' , function ( done ) {
var deps = new AppDependencies ( process . cwd ( ) ) ,
fakeEmitter = new EventEmitter ( ) ;
deps . spawnCommand = sandbox . stub ( ) . returns ( fakeEmitter ) ;
deps . install ( ) . then ( function ( ) {
deps . spawnCommand . calledWith ( 'npm' ) . should . equal ( true ) ;
done ( ) ;
2014-05-05 21:58:58 +01:00
} ) . catch ( done ) ;
2014-02-02 00:07:39 -06:00
_ . delay ( function ( ) {
fakeEmitter . emit ( 'exit' ) ;
} , 30 ) ;
} ) ;
it ( 'does not install when no package.json' , function ( done ) {
var deps = new AppDependencies ( _ _dirname ) ,
fakeEmitter = new EventEmitter ( ) ;
deps . spawnCommand = sandbox . stub ( ) . returns ( fakeEmitter ) ;
deps . install ( ) . then ( function ( ) {
deps . spawnCommand . called . should . equal ( false ) ;
done ( ) ;
2014-05-05 21:58:58 +01:00
} ) . catch ( done ) ;
2014-02-02 00:07:39 -06:00
_ . defer ( function ( ) {
fakeEmitter . emit ( 'exit' ) ;
} ) ;
} ) ;
} ) ;
2014-02-11 19:04:13 -06:00
describe ( 'Permissions' , function ( ) {
var noGhostPackageJson = {
"name" : "myapp" ,
"version" : "0.0.1" ,
"description" : "My example app" ,
"main" : "index.js" ,
"scripts" : {
"test" : "echo \"Error: no test specified\" && exit 1"
} ,
"author" : "Ghost" ,
"license" : "MIT" ,
"dependencies" : {
"ghost-app" : "0.0.1"
}
} ,
validGhostPackageJson = {
"name" : "myapp" ,
"version" : "0.0.1" ,
"description" : "My example app" ,
"main" : "index.js" ,
"scripts" : {
"test" : "echo \"Error: no test specified\" && exit 1"
} ,
"author" : "Ghost" ,
"license" : "MIT" ,
"dependencies" : {
"ghost-app" : "0.0.1"
} ,
"ghost" : {
"permissions" : {
"posts" : [ "browse" , "read" , "edit" , "add" , "delete" ] ,
"users" : [ "browse" , "read" , "edit" , "add" , "delete" ] ,
"settings" : [ "browse" , "read" , "edit" , "add" , "delete" ]
}
}
} ;
it ( 'has default permissions to read and browse posts' , function ( ) {
should . exist ( AppPermissions . DefaultPermissions ) ;
should . exist ( AppPermissions . DefaultPermissions . posts ) ;
AppPermissions . DefaultPermissions . posts . should . contain ( 'browse' ) ;
AppPermissions . DefaultPermissions . posts . should . contain ( 'read' ) ;
// Make it hurt to add more so additional checks are added here
_ . keys ( AppPermissions . DefaultPermissions ) . length . should . equal ( 1 ) ;
} ) ;
it ( 'uses default permissions if no package.json' , function ( done ) {
var perms = new AppPermissions ( "test" ) ;
// No package.json in this directory
sandbox . stub ( perms , "checkPackageContentsExists" ) . returns ( when . resolve ( false ) ) ;
perms . read ( ) . then ( function ( readPerms ) {
should . exist ( readPerms ) ;
readPerms . should . equal ( AppPermissions . DefaultPermissions ) ;
done ( ) ;
2014-05-05 21:58:58 +01:00
} ) . catch ( done ) ;
2014-02-11 19:04:13 -06:00
} ) ;
it ( 'uses default permissions if no ghost object in package.json' , function ( done ) {
var perms = new AppPermissions ( "test" ) ,
noGhostPackageJsonContents = JSON . stringify ( noGhostPackageJson , null , 2 ) ;
// package.json IS in this directory
sandbox . stub ( perms , "checkPackageContentsExists" ) . returns ( when . resolve ( true ) ) ;
// no ghost property on package
sandbox . stub ( perms , "getPackageContents" ) . returns ( when . resolve ( noGhostPackageJsonContents ) ) ;
perms . read ( ) . then ( function ( readPerms ) {
should . exist ( readPerms ) ;
readPerms . should . equal ( AppPermissions . DefaultPermissions ) ;
done ( ) ;
2014-05-05 21:58:58 +01:00
} ) . catch ( done ) ;
2014-02-11 19:04:13 -06:00
} ) ;
it ( 'rejects when reading malformed package.json' , function ( done ) {
var perms = new AppPermissions ( "test" ) ;
// package.json IS in this directory
sandbox . stub ( perms , "checkPackageContentsExists" ) . returns ( when . resolve ( true ) ) ;
// malformed JSON on package
sandbox . stub ( perms , "getPackageContents" ) . returns ( when . reject ( new Error ( 'package.json file is malformed' ) ) ) ;
perms . read ( ) . then ( function ( readPerms ) {
done ( new Error ( 'should not resolve' ) ) ;
2014-05-05 21:58:58 +01:00
} ) . catch ( function ( err ) {
err . message . should . equal ( 'package.json file is malformed' ) ;
2014-02-11 19:04:13 -06:00
done ( ) ;
} ) ;
} ) ;
it ( 'reads from package.json in root of app directory' , function ( done ) {
var perms = new AppPermissions ( "test" ) ,
validGhostPackageJsonContents = validGhostPackageJson ;
// package.json IS in this directory
sandbox . stub ( perms , "checkPackageContentsExists" ) . returns ( when . resolve ( true ) ) ;
// valid ghost property on package
sandbox . stub ( perms , "getPackageContents" ) . returns ( when . resolve ( validGhostPackageJsonContents ) ) ;
perms . read ( ) . then ( function ( readPerms ) {
should . exist ( readPerms ) ;
readPerms . should . not . equal ( AppPermissions . DefaultPermissions ) ;
should . exist ( readPerms . posts ) ;
readPerms . posts . length . should . equal ( 5 ) ;
should . exist ( readPerms . users ) ;
readPerms . users . length . should . equal ( 5 ) ;
should . exist ( readPerms . settings ) ;
readPerms . settings . length . should . equal ( 5 ) ;
_ . keys ( readPerms ) . length . should . equal ( 3 ) ;
done ( ) ;
2014-05-05 21:58:58 +01:00
} ) . catch ( done ) ;
2014-02-11 19:04:13 -06:00
} ) ;
} ) ;
2014-02-10 00:17:28 +01:00
} ) ;