Skip to content

Testing Authority rules

Tortue Torche edited this page Apr 22, 2014 · 47 revisions

It can be difficult to thoroughly test user permissions at the functional/integration level because there are often many branching possibilities. Since AuthorityController handles all permission logic in a single AuthorityController config file this makes it easy to have a solid set of unit test for complete coverage.

The can() method can be called directly on any Authority (like you would in the controller or view) so it is easy to test permission logic.

// user should only destroy projects which he owns
public function testUserCanOnlyDestroyProjectsWhichHeOwns()
{
    // Disables mass assignment exceptions from being thrown from model inserts and updates
    Eloquent::unguard();

    $user = User::create([
                'email'     => 'user@localhost',
                'name'  => 'User',
                'password'  => Hash::make('password'),
            ]);
    $authority = App::make('authority');
    $authority->setCurrentUser($user);

    // You can set the 'allow' rule directly in the test or fallback to the rules defined in the Authority config file
    $rulesCount = $authority->getRules()->count();
    $authority->allow('destroy', 'Project', function($self, $project) {
        return $self->user()->id === $project->user_id;
    });
    $this->assertGreaterThan($rulesCount, $authority->getRules()->count());

    $this->assertTrue($authority->can('destroy', new Project(['user_id' => $user->id])));
    $this->assertTrue($authority->cannot('destroy', new Project));

    // Renables any ability to throw mass assignment exceptions
    Eloquent::reguard();
}

Controller Testing

If you want to test authorization functionality at the controller level one option is to log-in the user who has the appropriate permissions.

// Disables mass assignment exceptions from being thrown from model inserts and updates
Eloquent::unguard();

$user = User::create([
            'email'     => 'admin@localhost',
            'name'  => 'Administrator',
            'password'  => Hash::make('password'),
        ]); // I recommend a factory for this
$roleAdmin = Role::where('name', 'admin')->firstOrFail(); // Or Role::create(['name' => 'admin']);
$user->roles()->attach($roleAdmin->id);
Auth::login($user); // log in user however you like, alternatively stub Auth::guest() and $yourControllerInstance->getCurrentUser() method
$this->action('GET', "ProjectsController@index");
$this->assertViewHas('projects'); // render the view with the 'projects' variable, since it should have access

// Renables any ability to throw mass assignment exceptions
Eloquent::reguard();

Alternatively, if you want to test the controller behavior independently from what is inside the AuthorityController config file, it is easy to stub out the authority with any behavior you want. You need to install the Mockery package. Then you can use the code below (keep in mind that this code is for the ProjectsController class):

<?php
// app/tests/controllers/ProjectsControllerTest.php
use Mockery as m;

class ProjectsControllerTest extends TestCase
{
    public function setUp()
    {
        parent::setUp();
        Route::enableFilters();
        $this->app['authority'] = new Efficiently\AuthorityController\Authority(Auth::user());
        $this->authority = App::make('authority');
    }

    public function tearDown()
    {
        parent::tearDown();
        m::close();
    }

    // render index if have read authority on project
    public function testRenderIndexIfHaveReadAuthorityOnProject()
    {
        $this->authority->allow('read', 'Project');
        $response = $this->action('GET', "ProjectsController@index");
        $view = $response->original;
        $this->assertEquals($view->getName(), 'projects.index');
        $this->assertViewHas('projects');
    }

    //...
}

If you have very complex permissions it can lead to many branching possibilities. If these are all tested in the controller layer then it can lead to slow and bloated tests. Instead I recommend keeping controller authorization tests light and testing the authorization functionality more thoroughly in the Authority config file through unit tests as shown at the top.

Additional Docs

Clone this wiki locally