diff --git a/.travis.yml b/.travis.yml index dd8d548c..14cf1c41 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,14 @@ -language: php - -php: - - 5.4 - - 5.5 - - 5.6 - - hhvm - -before_script: - - travis_retry composer self-update - - travis_retry composer install --no-interaction --prefer-source --dev - -script: - - vendor/bin/phpunit --verbose +language: php + +php: + - 5.4 + - 5.5 + - 5.6 + - hhvm + +before_script: + - travis_retry composer self-update + - travis_retry composer install --no-interaction --prefer-source --dev + +script: + - vendor/bin/phpunit --verbose diff --git a/composer.json b/composer.json index 6579795d..38429a01 100644 --- a/composer.json +++ b/composer.json @@ -1,62 +1,62 @@ -{ - "name": "zizaco/entrust", - "description": "This package provides a flexible way to add Role-based Permissions to Laravel 4", - "keywords": ["laravel","illuminate","auth","roles"], - "license": "MIT", - "authors": [ - { - "name": "Zizaco Zizuini", - "email": "zizaco@gmail.com" - }, - { - "name": "Andrew Elkins", - "homepage": "http://www.andrewelkins.com" - } - ], - "require": { - "php": ">=5.4.0", - "illuminate/support": "~4.0", - "laravelbook/ardent": "~2.4" - }, - "require-dev": { - "phpunit/phpunit": "~4.0", - "mockery/mockery": "~0.8", - "illuminate/database": "~4.0", - "league/factory-muffin": "~1.5" - }, - "repositories": [ - { - "type": "package", - "package": { - "name": "laravelbook/ardent", - "version": "2.4.2", - "source": { - "type": "git", - "url": "https://github.com/bexarcreativeinc/ardent.git", - "reference": "master" - }, - "autoload": { - "psr-0": { - "LaravelBook\\Ardent": "src/" - } - } - } - } - ], - "suggest": { - "zizaco/confide":"Confide is an authentication solution for Laravel 4 that couples very well with Entrust" - }, - "autoload": { - "classmap": [ - "src/commands" - ], - "psr-0": { - "Zizaco\\Entrust": "src/" - } - }, - "extra": { - "branch-alias": { - "dev-master": "1.2-dev" - } - } -} +{ + "name": "zizaco/entrust", + "description": "This package provides a flexible way to add Role-based Permissions to Laravel 4", + "keywords": ["laravel","illuminate","auth","roles"], + "license": "MIT", + "authors": [ + { + "name": "Zizaco Zizuini", + "email": "zizaco@gmail.com" + }, + { + "name": "Andrew Elkins", + "homepage": "http://www.andrewelkins.com" + } + ], + "require": { + "php": ">=5.4.0", + "illuminate/support": "~4.0", + "laravelbook/ardent": "~2.4" + }, + "require-dev": { + "phpunit/phpunit": "~4.0", + "mockery/mockery": "~0.8", + "illuminate/database": "~4.0", + "league/factory-muffin": "~1.5" + }, + "repositories": [ + { + "type": "package", + "package": { + "name": "laravelbook/ardent", + "version": "2.4.2", + "source": { + "type": "git", + "url": "https://github.com/bexarcreativeinc/ardent.git", + "reference": "master" + }, + "autoload": { + "psr-0": { + "LaravelBook\\Ardent": "src/" + } + } + } + } + ], + "suggest": { + "zizaco/confide":"Confide is an authentication solution for Laravel 4 that couples very well with Entrust" + }, + "autoload": { + "classmap": [ + "src/commands" + ], + "psr-4": { + "Zizaco\\Entrust\\": "src/Entrust/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + } +} diff --git a/phpunit.xml b/phpunit.xml index 71adedca..3347b75b 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,18 +1,18 @@ - - - - - ./tests/ - - - + + + + + ./tests/ + + + diff --git a/src/Zizaco/Entrust/Entrust.php b/src/Entrust/Entrust.php similarity index 93% rename from src/Zizaco/Entrust/Entrust.php rename to src/Entrust/Entrust.php index d0973265..1ec49724 100644 --- a/src/Zizaco/Entrust/Entrust.php +++ b/src/Entrust/Entrust.php @@ -10,7 +10,7 @@ class Entrust * * @var Illuminate\Foundation\Application */ - public $_app; + public $app; /** * Create a new confide instance. @@ -20,7 +20,7 @@ class Entrust */ public function __construct($app) { - $this->_app = $app; + $this->app = $app; } /** @@ -80,7 +80,7 @@ public function can( $permission ) */ public function user() { - return $this->_app['auth']->user(); + return $this->app->auth->user(); } /** @@ -126,11 +126,11 @@ public function routeNeedsRole( $route, $roles, $result = null, $cumulative=true } // Same as Route::filter, registers a new filter - $this->_app['router']->filter($filter_name, $result); + $this->app->router->filter($filter_name, $result); // Same as Route::when, assigns a route pattern to the // previously created filter. - $this->_app['router']->when( $route, $filter_name ); + $this->app->router->when( $route, $filter_name ); } /** @@ -178,11 +178,11 @@ public function routeNeedsPermission( $route, $permissions, $result = null, $cum } // Same as Route::filter, registers a new filter - $this->_app['router']->filter($filter_name, $result); + $this->app->router->filter($filter_name, $result); // Same as Route::when, assigns a route pattern to the // previously created filter. - $this->_app['router']->when( $route, $filter_name ); + $this->app->router->when( $route, $filter_name ); } /** @@ -242,10 +242,10 @@ public function routeNeedsRoleOrPermission( $route, $roles, $permissions, $resul } // Same as Route::filter, registers a new filter - $this->_app['router']->filter($filter_name, $result); + $this->app->router->filter($filter_name, $result); // Same as Route::when, assigns a route pattern to the // previously created filter. - $this->_app['router']->when( $route, $filter_name ); + $this->app->router->when( $route, $filter_name ); } } diff --git a/src/Zizaco/Entrust/EntrustFacade.php b/src/Entrust/EntrustFacade.php similarity index 100% rename from src/Zizaco/Entrust/EntrustFacade.php rename to src/Entrust/EntrustFacade.php diff --git a/src/Zizaco/Entrust/EntrustPermission.php b/src/Entrust/EntrustPermission.php similarity index 100% rename from src/Zizaco/Entrust/EntrustPermission.php rename to src/Entrust/EntrustPermission.php diff --git a/src/Zizaco/Entrust/EntrustRole.php b/src/Entrust/EntrustRole.php similarity index 100% rename from src/Zizaco/Entrust/EntrustRole.php rename to src/Entrust/EntrustRole.php diff --git a/src/Zizaco/Entrust/EntrustServiceProvider.php b/src/Entrust/EntrustServiceProvider.php similarity index 94% rename from src/Zizaco/Entrust/EntrustServiceProvider.php rename to src/Entrust/EntrustServiceProvider.php index 9d2c9d83..014985e8 100644 --- a/src/Zizaco/Entrust/EntrustServiceProvider.php +++ b/src/Entrust/EntrustServiceProvider.php @@ -1,65 +1,65 @@ -package('zizaco/entrust'); - } - - /** - * Register the service provider. - * - * @return void - */ - public function register() - { - $this->registerEntrust(); - - $this->registerCommands(); - } - - /** - * Register the application bindings. - * - * @return void - */ - private function registerEntrust() - { - $this->app->bind('entrust', function($app) - { - return new Entrust($app); - }); - } - - /** - * Register the artisan commands. - * - * @return void - */ - private function registerCommands() - { - $this->app['command.entrust.migration'] = $this->app->share(function($app) - { - return new MigrationCommand($app); - }); - - $this->commands( - 'command.entrust.migration' - ); - } -} +package('zizaco/entrust'); + } + + /** + * Register the service provider. + * + * @return void + */ + public function register() + { + $this->registerEntrust(); + + $this->registerCommands(); + } + + /** + * Register the application bindings. + * + * @return void + */ + private function registerEntrust() + { + $this->app->bind('entrust', function($app) + { + return new Entrust($app); + }); + } + + /** + * Register the artisan commands. + * + * @return void + */ + private function registerCommands() + { + $this->app['command.entrust.migration'] = $this->app->share(function($app) + { + return new MigrationCommand($app); + }); + + $this->commands( + 'command.entrust.migration' + ); + } +} diff --git a/src/Zizaco/Entrust/HasRole.php b/src/Entrust/HasRole.php similarity index 100% rename from src/Zizaco/Entrust/HasRole.php rename to src/Entrust/HasRole.php diff --git a/tests/EntrustTest.php b/tests/EntrustTest.php index 4528767a..b71759d8 100644 --- a/tests/EntrustTest.php +++ b/tests/EntrustTest.php @@ -1,114 +1,710 @@ shouldReceive('user') + ->andReturn($user) + ->twice()->ordered(); + + $entrust->shouldReceive('user') + ->andReturn(false) + ->once()->ordered(); + + $user->shouldReceive('hasRole') + ->with('UserRole') + ->andReturn(true) + ->once(); + + $user->shouldReceive('hasRole') + ->with('NonUserRole') + ->andReturn(false) + ->once(); + + /* + |------------------------------------------------------------ + | Assertion + |------------------------------------------------------------ + */ + $this->assertTrue($entrust->hasRole('UserRole')); + $this->assertFalse($entrust->hasRole('NonUserRole')); + $this->assertFalse($entrust->hasRole('AnyRole')); + } + + public function testCan() + { + /* + |------------------------------------------------------------ + | Set + |------------------------------------------------------------ + */ + $app = new stdClass(); + $entrust = m::mock('Zizaco\Entrust\Entrust[user]', [$app]); + $user = m::mock('_mockedUser'); + + /* + |------------------------------------------------------------ + | Expectation + |------------------------------------------------------------ + */ + $entrust->shouldReceive('user') + ->andReturn($user) + ->twice()->ordered(); + + $entrust->shouldReceive('user') + ->andReturn(false) + ->once()->ordered(); + $user->shouldReceive('can') - ->with( 'manage_a' ) - ->once() - ->andReturn( true ); - - // Permission manage b as false + ->with('user_can') + ->andReturn(true) + ->once(); + $user->shouldReceive('can') - ->with( 'manage_b' ) - ->once() - ->andReturn( false ); - - $entrust = new Entrust( $this->mockAppWithCurrentUser( $user ) ); + ->with('user_cannot') + ->andReturn(false) + ->once(); + + /* + |------------------------------------------------------------ + | Assertion + |------------------------------------------------------------ + */ + $this->assertTrue($entrust->can('user_can')); + $this->assertFalse($entrust->can('user_cannot')); + $this->assertFalse($entrust->can('any_permission')); + } - // Check if user 'can' - $this->assertTrue( $entrust->can('manage_a') ); - $this->assertFalse( $entrust->can('manage_b') ); + public function testShouldGetUser() + { + /* + |------------------------------------------------------------ + | Set + |------------------------------------------------------------ + */ + $app = new stdClass(); + $app->auth = m::mock('Auth'); + $entrust = new Entrust($app); + $user = m::mock('_mockedUser'); + + /* + |------------------------------------------------------------ + | Expectation + |------------------------------------------------------------ + */ + $app->auth->shouldReceive('user') + ->andReturn($user) + ->once(); + + /* + |------------------------------------------------------------ + | Assertion + |------------------------------------------------------------ + */ + $this->assertSame($user, $entrust->user()); } + + public function testRouteNeedsRole() + { + /* + |------------------------------------------------------------ + | Set + |------------------------------------------------------------ + */ + $app = new stdClass(); + $app->router = m::mock('Route'); + $entrust = new Entrust($app); + + $route = 'route'; + $oneRole = 'RoleA'; + $manyRole = ['RoleA', 'RoleB', 'RoleC']; + $emptyClosure = function () {}; + + $oneRoleFilterName = $this->makeFilterName($route, [$oneRole]); + $manyRoleFilterName = $this->makeFilterName($route, $manyRole); - public function testEntrustHasRole() + /* + |------------------------------------------------------------ + | Expectation + |------------------------------------------------------------ + */ + $app->router->shouldReceive('filter') + ->with(m::anyOf($oneRoleFilterName, $manyRoleFilterName), m::type('Closure')) + ->twice()->ordered(); + $app->router->shouldReceive('filter') + ->with(m::anyOf($oneRoleFilterName, $manyRoleFilterName), m::mustBe($emptyClosure)) + ->twice()->ordered(); + + $app->router->shouldReceive('when') + ->with($route, m::anyOf($oneRoleFilterName, $manyRoleFilterName)) + ->times(4); + + /* + |------------------------------------------------------------ + | Assertion + |------------------------------------------------------------ + */ + $entrust->routeNeedsRole($route, $oneRole); + $entrust->routeNeedsRole($route, $manyRole); + $entrust->routeNeedsRole($route, $oneRole, $emptyClosure); + $entrust->routeNeedsRole($route, $manyRole, $emptyClosure); + } + + public function testFilterGeneratedByRouteNeedsRole() { - // Current user - $user = m::mock( 'User' ); + /* + |------------------------------------------------------------ + | Set + |------------------------------------------------------------ + */ + $app = new stdClass(); + $app->router = m::mock('Route'); + $entrust = m::mock('Zizaco\Entrust\Entrust[hasRole]', [$app]); + $facadeApp = m::mock('_mockedApplication'); + Facade::setFacadeApplication($facadeApp); + + $route = 'route'; + $userRoleA = 'UserRoleA'; + $userRoleB = 'UserRoleB'; + $nonUserRoleA = 'NonUserRoleA'; + $nonUserRoleB = 'NonUserRoleB'; + $nonUserRoles = [$nonUserRoleA, $nonUserRoleB]; + $customResponse = new stdClass(); + + $roles = []; + $isPassedCustomResponse = false; + $isCumulative = false; - // Permission manage a as true - $user->shouldReceive('hasRole') - ->with( 'AdminA' ) - ->once() - ->andReturn( true ); + $callFilterAndAssert = function ($filter) use ( + $nonUserRoles, + $customResponse, + &$roles, + &$isPassedCustomResponse, + &$isCumulative + ) { + if (!($filter instanceof Closure)) { + return false; + } + + $result = null; + $numAgainst = count(array_intersect($nonUserRoles, $roles)); + $numTotal = count($roles); + $isUserAuthorized = !($numAgainst > 0 && ($isCumulative || $numAgainst === $numTotal)); - // Permission manage b as false - $user->shouldReceive('hasRole') - ->with( 'AdminB' ) - ->once() - ->andReturn( false ); + try { + $result = $filter(); + } catch(Exception $e) { + $this->assertSame('abort', $e->getMessage()); + $this->assertFalse($isPassedCustomResponse); + $this->assertFalse($isUserAuthorized); + return true; + } + + if ($isUserAuthorized) { + $this->assertNull($result); + } else { + $this->assertSame($customResponse, $result); + } - $entrust = new Entrust( $this->mockAppWithCurrentUser( $user ) ); + return true; + }; + + $runTestCase = function ( + array $caseRoles, + $result = null, + $cumulative = true + ) use ( + $entrust, + $route, + &$roles, + &$isPassedCustomResponse, + &$isCumulative + ) { + list($roles, $isPassedCustomResponse, $isCumulative) + = [$caseRoles, !is_null($result), $cumulative]; + $entrust->routeNeedsRole( + $route, + $caseRoles, + $result, + $cumulative + ); + }; + + /* + |------------------------------------------------------------ + | Expectation + |------------------------------------------------------------ + */ + $app->router->shouldReceive('filter') + ->with(m::type('string'), m::on($callFilterAndAssert)); + $app->router->shouldReceive('when') + ->with($route, m::type('string')); + + $facadeApp->shouldReceive('abort') + ->with(403)->andThrow('Exception', 'abort'); - // Check if user 'can' - $this->assertTrue( $entrust->hasRole('AdminA') ); - $this->assertFalse( $entrust->hasRole('AdminB') ); + $entrust->shouldReceive('hasRole') + ->with(m::anyOf($userRoleA, $userRoleB)) + ->andReturn(true); + $entrust->shouldReceive('hasRole') + ->with(m::anyOf($nonUserRoleA, $nonUserRoleB)) + ->andReturn(false); + + /* + |------------------------------------------------------------ + | Assertion + |------------------------------------------------------------ + */ + // Case: User has both roles. + $runTestCase([$userRoleA, $userRoleB]); + $runTestCase([$userRoleA, $userRoleB], null, false); + $runTestCase([$userRoleA, $userRoleB], $customResponse); + $runTestCase([$userRoleA, $userRoleB], $customResponse, false); + + // Case: User lacks a role. + $runTestCase([$nonUserRoleA, $userRoleB]); + $runTestCase([$nonUserRoleA, $userRoleB], null, false); + $runTestCase([$nonUserRoleA, $userRoleB], $customResponse); + $runTestCase([$nonUserRoleA, $userRoleB], $customResponse, false); + + // Case: User lacks both roles. + $runTestCase([$nonUserRoleA, $nonUserRoleB]); + $runTestCase([$nonUserRoleA, $nonUserRoleB], null, false); + $runTestCase([$nonUserRoleA, $nonUserRoleB], $customResponse); + $runTestCase([$nonUserRoleA, $nonUserRoleB], $customResponse, false); } - public function testGetUser() + public function testRouteNeedsPermission() { - // Current user - $user = m::mock( 'User' ); + /* + |------------------------------------------------------------ + | Set + |------------------------------------------------------------ + */ + $app = new stdClass(); + $app->router = m::mock('Route'); + $entrust = new Entrust($app); + + $route = 'route'; + $onePerm = 'can_a'; + $manyPerm = ['can_a', 'can_b', 'can_c']; + $emptyClosure = function () {}; - $entrust = new Entrust( $this->mockAppWithCurrentUser( $user ) ); + $onePermFilterName = $this->makeFilterName($route, [$onePerm]); + $manyPermFilterName = $this->makeFilterName($route, $manyPerm); - // Check the returned user - $this->assertEquals( $entrust->user(), $user ); - } + /* + |------------------------------------------------------------ + | Expectation + |------------------------------------------------------------ + */ + $app->router->shouldReceive('filter') + ->with(m::anyOf($onePermFilterName, $manyPermFilterName), m::type('Closure')) + ->twice()->ordered(); + $app->router->shouldReceive('filter') + ->with(m::anyOf($onePermFilterName, $manyPermFilterName), m::mustBe($emptyClosure)) + ->twice()->ordered(); + + $app->router->shouldReceive('when') + ->with($route, m::anyOf($onePermFilterName, $manyPermFilterName)) + ->times(4); - public function testFilterRoutesNeedRole() + /* + |------------------------------------------------------------ + | Assertion + |------------------------------------------------------------ + */ + $entrust->routeNeedsPermission($route, $onePerm); + $entrust->routeNeedsPermission($route, $manyPerm); + $entrust->routeNeedsPermission($route, $onePerm, $emptyClosure); + $entrust->routeNeedsPermission($route, $manyPerm, $emptyClosure); + } + + public function testFilterGeneratedByRouteNeedsPermission() { - $router = m::mock( 'Router' ); - $router->shouldReceive('filter') - ->once(); - $router->shouldReceive('when') - ->once(); + /* + |------------------------------------------------------------ + | Set + |------------------------------------------------------------ + */ + $app = new stdClass(); + $app->router = m::mock('Route'); + $entrust = m::mock('Zizaco\Entrust\Entrust[can]', [$app]); + $facadeApp = m::mock('_mockedApplication'); + Facade::setFacadeApplication($facadeApp); + + $route = 'route'; + $userPermA = 'user_can_a'; + $userPermB = 'user_can_b'; + $nonUserPermA = 'user_cannot_a'; + $nonUserPermB = 'user_cannot_b'; + $nonUserPerms = [$nonUserPermA, $nonUserPermB]; + $customResponse = new stdClass(); + + $perms = []; + $isPassedCustomResponse = false; + $isCumulative = false; - $app = array('router'=>$router); + $callFilterAndAssert = function ($filter) use ( + $nonUserPerms, + $customResponse, + &$perms, + &$isPassedCustomResponse, + &$isCumulative + ) { + if (!($filter instanceof Closure)) { + return false; + } + + $result = null; + $numAgainst = count(array_intersect($nonUserPerms, $perms)); + $numTotal = count($perms); + $isUserAuthorized = !($numAgainst > 0 && ($isCumulative || $numAgainst === $numTotal)); - $entrust = new Entrust( $app ); + try { + $result = $filter(); + } catch(Exception $e) { + $this->assertSame('abort', $e->getMessage()); + $this->assertFalse($isPassedCustomResponse); + $this->assertFalse($isUserAuthorized); + return true; + } - $entrust->routeNeedsRole('admin','Admin'); - } + if ($isUserAuthorized) { + $this->assertNull($result); + } else { + $this->assertSame($customResponse, $result); + } - public function testFilterRoutesNeedPermission() + return true; + }; + + $runTestCase = function ( + array $casePerms, + $result = null, + $cumulative = true + ) use ( + $entrust, + $route, + &$perms, + &$isPassedCustomResponse, + &$isCumulative + ) { + list($perms, $isPassedCustomResponse, $isCumulative) + = [$casePerms, !is_null($result), $cumulative]; + $entrust->routeNeedsPermission( + $route, + $casePerms, + $result, + $cumulative + ); + }; + + /* + |------------------------------------------------------------ + | Expectation + |------------------------------------------------------------ + */ + $app->router->shouldReceive('filter') + ->with(m::type('string'), m::on($callFilterAndAssert)); + $app->router->shouldReceive('when') + ->with($route, m::type('string')); + + $facadeApp->shouldReceive('abort') + ->with(403)->andThrow('Exception', 'abort'); + + $entrust->shouldReceive('can') + ->with(m::anyOf($userPermA, $userPermB)) + ->andReturn(true); + $entrust->shouldReceive('can') + ->with(m::anyOf($nonUserPermA, $nonUserPermB)) + ->andReturn(false); + + /* + |------------------------------------------------------------ + | Assertion + |------------------------------------------------------------ + */ + // Case: User has both permissions. + $runTestCase([$userPermA, $userPermB]); + $runTestCase([$userPermA, $userPermB], null, false); + $runTestCase([$userPermA, $userPermB], $customResponse); + $runTestCase([$userPermA, $userPermB], $customResponse, false); + + + // Case: User lacks a permission. + $runTestCase([$nonUserPermA, $userPermB]); + $runTestCase([$nonUserPermA, $userPermB], null, false); + $runTestCase([$nonUserPermA, $userPermB], $customResponse, false); + $runTestCase([$nonUserPermA, $userPermB], $customResponse); + + // Case: User lacks both permissions. + $runTestCase([$nonUserPermA, $nonUserPermB]); + $runTestCase([$nonUserPermA, $nonUserPermB], null, false); + $runTestCase([$nonUserPermA, $nonUserPermB], $customResponse, false); + $runTestCase([$nonUserPermA, $nonUserPermB], $customResponse); + } + + public function testRouteNeedsRoleOrPermission() { - $router = m::mock( 'Router' ); - $router->shouldReceive('filter') - ->once(); - $router->shouldReceive('when') - ->once(); + /* + |------------------------------------------------------------ + | Set + |------------------------------------------------------------ + */ + $app = new stdClass(); + $app->router = m::mock('Route'); + $entrust = new Entrust($app); + + $route = 'route'; + $oneRole = 'RoleA'; + $manyRole = ['RoleA', 'RoleB', 'RoleC']; + $onePerm = 'can_a'; + $manyPerm = ['can_a', 'can_b', 'can_c']; + $emptyClosure = function () {}; - $app = array('router'=>$router); + $oneRoleOnePermFilterName = $this->makeFilterName($route, [$oneRole], [$onePerm]); + $oneRoleManyPermFilterName = $this->makeFilterName($route, [$oneRole], $manyPerm); + $manyRoleOnePermFilterName = $this->makeFilterName($route, $manyRole, [$onePerm]); + $manyRoleManyPermFilterName = $this->makeFilterName($route, $manyRole, $manyPerm); - $entrust = new Entrust( $app ); + /* + |------------------------------------------------------------ + | Expectation + |------------------------------------------------------------ + */ + $app->router->shouldReceive('filter') + ->with( + m::anyOf( + $oneRoleOnePermFilterName, + $oneRoleManyPermFilterName, + $manyRoleOnePermFilterName, + $manyRoleManyPermFilterName + ), + m::type('Closure') + ) + ->times(4)->ordered(); + $app->router->shouldReceive('filter') + ->with( + m::anyOf( + $oneRoleOnePermFilterName, + $oneRoleManyPermFilterName, + $manyRoleOnePermFilterName, + $manyRoleManyPermFilterName + ), + m::mustBe($emptyClosure) + ) + ->times(4)->ordered(); - $entrust->routeNeedsPermission('admin','manage_users'); - } + $app->router->shouldReceive('when') + ->with( + $route, + m::anyOf( + $oneRoleOnePermFilterName, + $oneRoleManyPermFilterName, + $manyRoleOnePermFilterName, + $manyRoleManyPermFilterName + ) + ) + ->times(8); - private function mockAppWithCurrentUser( $user ) + /* + |------------------------------------------------------------ + | Assertion + |------------------------------------------------------------ + */ + $entrust->routeNeedsRoleOrPermission($route, $oneRole, $onePerm); + $entrust->routeNeedsRoleOrPermission($route, $oneRole, $manyPerm); + $entrust->routeNeedsRoleOrPermission($route, $manyRole, $onePerm); + $entrust->routeNeedsRoleOrPermission($route, $manyRole, $manyPerm); + + $entrust->routeNeedsRoleOrPermission($route, $oneRole, $onePerm, $emptyClosure); + $entrust->routeNeedsRoleOrPermission($route, $oneRole, $manyPerm, $emptyClosure); + $entrust->routeNeedsRoleOrPermission($route, $manyRole, $onePerm, $emptyClosure); + $entrust->routeNeedsRoleOrPermission($route, $manyRole, $manyPerm, $emptyClosure); + } + + public function testFilterGeneratedByRouteNeedsRoleOrPermission() { - // Mock app - $app = array( 'auth' => m::mock( 'Auth' ) ); + /* + |------------------------------------------------------------ + | Set + |------------------------------------------------------------ + */ + $app = new stdClass(); + $app->router = m::mock('Route'); + $entrust = m::mock('Zizaco\Entrust\Entrust[hasRole,can]', [$app]); + $facadeApp = m::mock('_mockedApplication'); + Facade::setFacadeApplication($facadeApp); + + $route = 'route'; + $userRoleA = 'UserRoleA'; + $userRoleB = 'UserRoleB'; + $userPermA = 'user_can_a'; + $userPermB = 'user_can_b'; + $nonUserRoleA = 'NonUserRoleA'; + $nonUserRoleB = 'NonUserRoleB'; + $nonUserPermA = 'user_cannot_a'; + $nonUserPermB = 'user_cannot_b'; + $nonUserRolesPerms = [$nonUserRoleA, $nonUserRoleB, $nonUserPermA, $nonUserPermB]; + $customResponse = new stdClass(); + + $roles = []; + $perms = []; + $isPassedCustomResponse = false; + $isCumulative = false; + + $callFilterAndAssert = function ($filter) use ( + $nonUserRolesPerms, + $customResponse, + &$roles, + &$perms, + &$isPassedCustomResponse, + &$isCumulative + ) { + if (!($filter instanceof Closure)) { + return false; + } + + $result = null; + $numAgainst = count(array_intersect($nonUserRolesPerms, array_merge($roles, $perms))); + $numTotal = count(array_merge($roles, $perms)); + $isUserAuthorized = !($numAgainst > 0 && ($isCumulative || $numAgainst === $numTotal)); + + try { + $result = $filter(); + } catch(Exception $e) { + $this->assertSame('abort', $e->getMessage()); + $this->assertFalse($isPassedCustomResponse); + $this->assertFalse($isUserAuthorized); + return true; + } - // Return current user within Auth mock - $app['auth']->shouldReceive('user') - ->andReturn( $user ); + if ($isUserAuthorized) { + $this->assertNull($result); + } else { + $this->assertSame($customResponse, $result); + } - return $app; + return true; + }; + + $runTestCase = function ( + array $caseRoles, + array $casePerms, + $result = null, + $cumulative = false + ) use ( + $entrust, + $route, + &$roles, + &$perms, + &$isPassedCustomResponse, + &$isCumulative + ) { + list($roles, $perms, $isPassedCustomResponse, $isCumulative) + = [$caseRoles, $casePerms, !is_null($result), $cumulative]; + $entrust->routeNeedsRoleOrPermission( + $route, + $caseRoles, + $casePerms, + $result, + $cumulative + ); + }; + + /* + |------------------------------------------------------------ + | Expectation + |------------------------------------------------------------ + */ + $app->router->shouldReceive('filter') + ->with(m::type('string'), m::on($callFilterAndAssert)); + $app->router->shouldReceive('when') + ->with($route, m::type('string')); + + $facadeApp->shouldReceive('abort') + ->with(403)->andThrow('Exception', 'abort'); + + $entrust->shouldReceive('hasRole') + ->with(m::anyOf($userRoleA, $userRoleB)) + ->andReturn(true); + $entrust->shouldReceive('hasRole') + ->with(m::anyOf($nonUserRoleA, $nonUserRoleB)) + ->andReturn(false); + $entrust->shouldReceive('can') + ->with(m::anyOf($userPermA, $userPermB)) + ->andReturn(true); + $entrust->shouldReceive('can') + ->with(m::anyOf($nonUserPermA, $nonUserPermB)) + ->andReturn(false); + + /* + |------------------------------------------------------------ + | Assertion + |------------------------------------------------------------ + */ + // Case: User has everything. + $runTestCase([$userRoleA, $userRoleB], [$userPermA, $userPermB]); + $runTestCase([$userRoleA, $userRoleB], [$userPermA, $userPermB], null, true); + $runTestCase([$userRoleA, $userRoleB], [$userPermA, $userPermB], $customResponse, true); + $runTestCase([$userRoleA, $userRoleB], [$userPermA, $userPermB], $customResponse); + + // Case: User lacks a role. + $runTestCase([$nonUserRoleA, $userRoleB], [$userPermA, $userPermB]); + $runTestCase([$nonUserRoleA, $userRoleB], [$userPermA, $userPermB], null, true); + $runTestCase([$nonUserRoleA, $userRoleB], [$userPermA, $userPermB], $customResponse, true); + $runTestCase([$nonUserRoleA, $userRoleB], [$userPermA, $userPermB], $customResponse); + + // Case: User lacks a permission. + $runTestCase([$userRoleA, $userRoleB], [$nonUserPermA, $userPermB]); + $runTestCase([$userRoleA, $userRoleB], [$nonUserPermA, $userPermB], null, true); + $runTestCase([$userRoleA, $userRoleB], [$nonUserPermA, $userPermB], $customResponse, true); + $runTestCase([$userRoleA, $userRoleB], [$nonUserPermA, $userPermB], $customResponse); + + // Case: User lacks a role and a permission. + $runTestCase([$nonUserRoleA, $userRoleB], [$nonUserPermA, $userPermB]); + $runTestCase([$nonUserRoleA, $userRoleB], [$nonUserPermA, $userPermB], null, true); + $runTestCase([$nonUserRoleA, $userRoleB], [$nonUserPermA, $userPermB], $customResponse, true); + $runTestCase([$nonUserRoleA, $userRoleB], [$nonUserPermA, $userPermB], $customResponse); + + // Case: User lacks everything. + $runTestCase([$nonUserRoleA, $nonUserRoleB], [$nonUserPermA, $nonUserPermB]); + $runTestCase([$nonUserRoleA, $nonUserRoleB], [$nonUserPermA, $nonUserPermB], null, true); + $runTestCase([$nonUserRoleA, $nonUserRoleB], [$nonUserPermA, $nonUserPermB], $customResponse, true); + $runTestCase([$nonUserRoleA, $nonUserRoleB], [$nonUserPermA, $nonUserPermB], $customResponse); + } + + protected function makeFilterName($route, array $roles, array $permissions = null) + { + if (is_null($permissions)) { + return implode('_', $roles) . '_' . substr(md5($route), 0, 6); + } else { + return implode('_', $roles) . '_' . implode('_', $permissions) . '_' . substr(md5($route), 0, 6); + } } }