Skip to content

Authority bypassed in tests #14

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
arthurkirkosa opened this issue Sep 1, 2014 · 22 comments
Closed

Authority bypassed in tests #14

arthurkirkosa opened this issue Sep 1, 2014 · 22 comments

Comments

@arthurkirkosa
Copy link

I'm doing some functional testing on an entity (Company) in my app and though I have the rules set correctly in config.php (they work ok in borwser) they are ignored when testing them with Codeception

Am I doing something wrong?

Using latest laravel with latest codeception.

@tortuetorche
Copy link
Contributor

Hi Arthur,

they are ignored when testing them with Codeception

In functional tests or acceptance tests ?

Edit: Sorry you mention functional tests, I read too fast 😉

Maybe the Route filters are disabled, try this:

Route::enableFilters();

Or configure the Laravel4 module of Codeception to enable filters which are false by default, here the doc.

Cheers,
Tortue Torche

@tortuetorche
Copy link
Contributor

You can also read this Wiki page about tests with Authority Controller.

@arthurkirkosa
Copy link
Author

Route::enableFilters() did the trick for a filter that did not apply for me, but the authority ones still fail for another entity (Document)

$document = $I->haveADocument();

$authority = App::make('authority');
$authority->setCurrentUser($user);
$rulesCount = $authority->getRules()->count();
$I->assertTrue($authority->cannot('read', $document));

$I->amOnPage('/documents/' . $document->id);

I still am able to go to that page when doing tests, though the about assert returns true

<?php

use BusinessMastery\System\Documents\Document;
use BusinessMastery\System\Documents\Repositories\DocumentRepository;
use BusinessMastery\System\ContentModules\Repository;

class DocumentsController extends \BaseController {

    /**
     * @var DocumentRepository
     */
    protected $repo;

    /**
     * @var Repository
     */
    protected $contentRepo;

    /**
     * Authenticated user
     *
     * @var BusinessMastery\System\Users\User
     */
    protected $user;

    public function __construct(DocumentRepository $repo, Repository $contentRepo)
    {
        $this->beforeFilter('auth');

        $this->repo        = $repo;
        $this->contentRepo = $contentRepo;
        $this->user        = Auth::user();

        $this->loadAndAuthorizeResource([
            'class' => 'BusinessMastery\\System\\Documents\\Document'
        ]);
    }

    /**
     * Display a listing of the resource.
     *
     * @return Response
     */
    public function index()
    {
        return View::make('documents.list');
    }


    /**
     * Show the form for creating a new resource.
     *
     * @return Response
     */
    public function create()
    {
        try
        {
            $document = $this->execute('BusinessMastery\System\Documents\UseCases\CreateDocumentCommand');

            return Redirect::route('documents.edit', $document->id);
        }
        catch (Exception $e)
        {
            return Redirect::route('dashboard')->withError($e->getMessage());
        }
    }

@tortuetorche
Copy link
Contributor

You means, this rule fails?

$I->assertTrue($authority->cannot('read', $document));

Is $rulesCount greater than 0 ?

$rulesCount = $authority->getRules()->count();

Do you have a show action in your controller for this test?

$I->amOnPage('/documents/' . $document->id);

Can you post your app/config/packages/efficiently/authority-controller/config.php file, please ?

@arthurkirkosa
Copy link
Author

$rulesCount is 18

<?php

use BusinessMastery\System\Documents\Document;
use BusinessMastery\System\Documents\Repositories\DocumentRepository;
use BusinessMastery\System\ContentModules\Repository;

class DocumentsController extends \BaseController {

    /**
     * @var DocumentRepository
     */
    protected $repo;

    /**
     * @var Repository
     */
    protected $contentRepo;

    /**
     * Authenticated user
     *
     * @var BusinessMastery\System\Users\User
     */
    protected $user;

    public function __construct(DocumentRepository $repo, Repository $contentRepo)
    {
        $this->beforeFilter('auth');

        $this->repo        = $repo;
        $this->contentRepo = $contentRepo;
        $this->user        = Auth::user();

        $this->loadAndAuthorizeResource([
            'class' => 'BusinessMastery\\System\\Documents\\Document'
        ]);
    }

    /**
     * Display a listing of the resource.
     *
     * @return Response
     */
    public function index()
    {
        return View::make('documents.list');
    }


    /**
     * Show the form for creating a new resource.
     *
     * @return Response
     */
    public function create()
    {
        try
        {
            $document = $this->execute('BusinessMastery\System\Documents\UseCases\CreateDocumentCommand');

            return Redirect::route('documents.edit', $document->id);
        }
        catch (Exception $e)
        {
            return Redirect::route('dashboard')->withError($e->getMessage());
        }
    }


    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return Response
     */
    public function show($id)
    {
        $document = $this->repo->findOrFail($id, [
            'comments' => function($q)
            {
                $q->with('commenter');
            },
            'author'
        ]);

        $comments = $document->comments->groupBy('content_id');
        $contents = $this->repo->content($id, $document);

        return View::make('documents.show', compact('document', 'comments', 'contents'));
    }


    /**
     * Show the form for editing the specified resource.
     *
     * @param  int  $id
     * @return Response
     */
    public function edit($id)
    {
        $document = $this->repo->fetchForEdit($id, $this->user->id);

        if ($document->id != $id)
        {
            return Redirect::route('documents.edit', $document->id);
        }

        $contents = $this->repo->content($id, $document);
        $blanks   = $this->contentRepo->loadBlank($document->parent_id ?: $id);

        return View::make('documents.edit', compact('document', 'contents', 'blanks'));
    }

    /**
     * @param string $type
     * @return int
     */
    private function parseType($type)
    {
        return str_replace(
            ['strategic', 'principles'],
            [Document::TYPE_STRATEGIC_OBJECTIVE, Document::TYPE_OPERATING_PRINCIPLES],
            $type
        );
    }

    /**
     * Edit strategic objective and operating principles
     *
     * @param string $type
     * @return Response
     */
    public function editSpecial($type)
    {
        $document = $this->repo->editSpecial($this->parseType($type));

        return Redirect::route('documents.edit', $document->id);
    }

    /**
     * View strategic objective and operating principles
     *
     * @param string $type
     * @return Response
     */
    public function viewSpecial($type)
    {
        $document = $this->repo->getSpecial($this->parseType($type));

        if ($document)
        {
            $comments = $document->comments->groupBy('content_id');
            $contents = $this->repo->content($document->id, $document);

            return View::make('documents.show', compact('document', 'comments', 'contents'));
        }

        if (Auth::user()->isValidator())
        {
            return Redirect::route('documents.specialedit', $type);
        }

        return Redirect::route('dashboard')->withError(
            'Acest document nu a fost creat inca'
        );
    }


    /**
     * Update the specified resource in storage.
     *
     * @param  int  $id
     * @return Response
     */
    public function update($id)
    {
        Input::merge(['id' => $id]);
        $data = Input::only(['id', 'title', 'description', 'field']);

        try
        {
            $this->execute('BusinessMastery\System\Documents\UseCases\SaveDraftCommand', $data);

            return Request::ajax()
                ? Response::json(['success' => 'Documentul a fost salvat cu succes'])
                : Redirect::route('documents.edit', $id)->withSuccess('Documentul a fost salvat cu succes');
        }
        catch (Exception $e)
        {
            return Request::ajax()
                ? Response::json(['error' => 'A aparut o eroare la salvarea documentului'], 400)
                : Redirect::route('documents.edit', $id)->withError('A aparut o eroare la salvarea documentului');
        }
    }


    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return Response
     */
    public function destroy($id)
    {
        //
    }

    /**
     * Ask for review
     *
     * @param int $id
     * @return Response
     */
    public function askForReview($id)
    {
        try
        {
            $document = $this->execute(
                'BusinessMastery\System\Documents\UseCases\RequestReviewCommand',
                [
                    'id'  => $id,
                    'ids' => Input::get('ids', [])
                ]
            );

            return Redirect::route('documents.show', $id);
        }
        catch (Exception $e)
        {
            return Redirect::route('documents.edit', $id)->withError('A aparut o eroare la cererea review-ului');
        }
    }

    /**
     * Publish document
     *
     * @param int $id
     * @return Response
     */
    public function publish($id)
    {
        try
        {
            $document = $this->execute(
                'BusinessMastery\System\Documents\UseCases\PublishDocumentCommand',
                ['id' => $id]
            );

            return Redirect::route('documents.show', $document->id);
        }
        catch (Exception $e)
        {
            return Redirect::route('documents.show', $id)->withError('A aparut o eroare la publicarea acestui document');
        }
    }

    /**
     * Reviewers can approve a document
     *
     * @param  int $id
     * @return Response
     */
    public function approve($id)
    {
        $document = $this->execute(
            'BusinessMastery\System\Users\UseCases\ApproveDocumentCommand',
            ['id' => $id]
        );

        return Redirect::route('documents.show', $id);
    }

    /**
     * Upload an attachment
     *
     * @param int $docId
     * @return Response
     */
    public function storeAttachment($docId)
    {
        try
        {
            if (class_exists('\Debugbar'))
            {
                \Debugbar::disable();
            }

            $fileName = $this->execute(
                'BusinessMastery\System\Documents\UseCases\AttachFileCommand', [
                    'documentId' => $docId,
                    'file'       => Input::file('file'),
                ]
            );

            return json_encode([
                'success' => true,
                'url'     => asset($fileName),
            ]);
        }
        catch (Exception $e)
        {
            return json_encode([
                'success' => false,
                'error'   => $e->getMessage(),
            ]);
        }

    }

    /**
     * Destroy an attachment
     *
     * @param int $docId
     * @return Response
     */
    public function destroyAttachment($docId)
    {
        //
    }

    /**
     * Document revision history
     *
     * @param int $id
     * @return Response
     */
    public function revisions($id)
    {
        $revisions = $this->repo->revisions($id);

        return View::make('documents.revisions', compact('revisions'));
    }

}
<?php

return [

    'initialize' => function($authority) {

        $authority->addAlias('moderate', ['read', 'update', 'delete']);

        $user = Auth::guest() ? new User : $authority->getCurrentUser();

        /**
         * Allow owners to access all resources
         * Owner != Admin
         */
        if ($user->hasRole('owner'))
        {
            $authority->allow('manage', 'all');
        }

        /**
         * Special permissions for validators
         */
        if ($user->isValidator())
        {
            $authority->allow('editSpecial', 'all');
        }

        $authority->allow('viewSpecial', 'BusinessMastery\\System\\Documents\\Document');

        /**
         * For each company admin
         */
        if ($user->isAdmin())
        {
            $authority->allow(
                'manage',
                'BusinessMastery\\System\\Companies\\Company',
                function($self, $company)
                {
                    return $company->id == $self->getCurrentUser()->company_id;
                }
            );

            $authority->allow(
                'manage',
                'BusinessMastery\\System\\Departments\\Department',
                function($self, $department)
                {
                    if ($department->id)
                    {
                        return $department->company_id == $self->getCurrentUser()->company_id;
                    }

                    return true;
                }
            );

            $authority->allow(
                'manage',
                'BusinessMastery\\System\\Invoices\\Invoice',
                function($self, $invoice)
                {
                    return $invoice->company_id == $self->getCurrentUser()->company_id;
                }
            );

            $authority->allow(
                'manage',
                'BusinessMastery\\System\\Users\\User',
                function($self, $user)
                {
                    return $user->company_id == $self->getCurrentUser()->company_id;
                }
            );
        }

        /**
         * Allow correct users to view departments
         */
        $authority->allow(
            'read',
            'BusinessMastery\\System\\Departments\\Department',
            function($self, $department)
            {
                $user = $self->getCurrentUser();

                /**
                 * Get the list of departments
                 * the user has access to
                 *
                 * @var array
                 */
                $ids = $user->departments()
                    ->wherePivot('permission', '>', 0)
                    ->get()
                    ->lists('id');

                /**
                 * Get the list of folders
                 * that are in the above departments
                 *
                 * @var array
                 */
                $folderIds = \DB::table('departments')
                    ->whereIn('parent_id', $ids)
                    ->lists('id');

                $ids = array_merge($ids, $folderIds);

                return in_array($department->id, $ids);
            }
        );

        /**
         * Allow users to view documents
         * if they have view permission
         */
        $authority->allow(
            ['read', 'revisions'],
            'BusinessMastery\\System\\Documents\\Document',
            function($self, $document)
            {
                /**
                 * Everyone can view the strategic
                 * objective and operating principles
                 */
                if (is_null($document->department_id))
                {
                    return true;
                }

                $user = $self->getCurrentUser();

                return $user->present()->can('view', $document->department_id);
            }
        );

        /**
         * Allow reviewers to read documents they review
         */
        $authority->allow(
            'create',
            'BusinessMastery\\System\\Documents\\Document',
            function($self, $document)
            {
                $user = $self->getCurrentUser();

                $department = Input::get('department');

                return $user->present()->can('edit', $department);
            }
        );

        /**
         * Allow users to create documents
         * or manage them if they own it
         */
        $authority->allow(
            ['edit', 'update', 'askForReview'],
            'BusinessMastery\\System\\Documents\\Document',
            function($self, $document)
            {
                $user = $self->getCurrentUser();

                /** If we are the owner */
                if ($document->author_id == $user->id)
                {
                    return true;
                }

                return false;
            }
        );

        /**
         * Allow reviewers to read documents they review
         */
        $authority->allow(
            'read',
            'BusinessMastery\\System\\Documents\\Document',
            function($self, $document)
            {
                $user = $self->getCurrentUser();

                return in_array($document->id, $user->reviews->lists('id'));
            }
        );

        /**
         * Allow reviewers to approve documents they review
         * and haven't approved
         */
        $authority->allow(
            'approve',
            'BusinessMastery\\System\\Documents\\Document',
            function($self, $document)
            {
                $user = $self->getCurrentUser();

                $pivots = array_pluck($user->reviews->toArray(), 'pivot');

                $rules = [
                    'user_id'     => $user->id,
                    'document_id' => $document->id,
                    'approved'    => 0
                ];

                $count = 0;

                /** Increment count if we find a row that matches the rules */
                foreach($pivots as $pivot) {
                    if (array_intersect($pivot, $rules) == $rules) return true;
                }

                return false;
            }
        );

        /**
         * Only the company validator cand publish/validate
         * a document
         */
        $authority->allow(
            'publish',
            'BusinessMastery\\System\\Documents\\Document',
            function($self, $document)
            {
                $user = $self->getCurrentUser();

                return $user->isValidator();
            }
        );

        /**
         * Allow all users to comment
         */
        $authority->allow('create', 'BusinessMastery\\System\\Comments\\Comment');

        /**
         * If the comment is private
         * allow the person who created it
         * and the one to whoom it's private for
         * to view it
         * else
         * allow all to view the comment
         */
        $authority->allow(
            'read',
            'BusinessMastery\\System\\Comments\\Comment',
            function($self, $comment)
            {
                $user = $self->getCurrentUser();

                if ($comment->private_for) {
                    return $comment->private_for == $user->id || $comment->user_id == $user->id;
                }

                return true;
            }
        );

        $authority->allow(
            'delete',
            'BusinessMastery\\System\\Comments\\Comment',
            function($self, $comment)
            {
                $user = $self->getCurrentUser();

                return $comment->user_id == $user->id;
            }
        );

        /**
         * Get permissions stored in DB
         */
        foreach($user->permissions as $permission)
        {
            if ($permission->type == 'allow')
            {
                $authority->allow($permission->action, $permission->resource);
            }
            else
            {
                $authority->deny($permission->action, $permission->resource);
            }
        }

        if ( ! $user->companyActive())
        {
            // general ruleset
            $authority->deny(['edit','store','create','update','delete'], 'all');

            $authority->deny('read', [
                'BusinessMastery\\System\\Documents\\Document',
                'BusinessMastery\\System\\Departments\\Department'
            ]);

            // ruleset for users
            $authority->deny(['invite', 'saveInvite'], 'all');
        }

    }

];

@tortuetorche
Copy link
Contributor

Wow, this is huge!

$document = $I->haveADocument();

Is an existing record or a new instance of BusinessMastery\\System\\Documents\\Document ?

$user->companyActive();

Returns true or false ?

The best thing you can do is to ensure your assertion works in the Laravel console (php artisan tinker), you can read this Wiki page.
Then if the result isn't the same as Codeception, I'll investigate, with my own apps, why the rules are bypassed...

@arthurkirkosa
Copy link
Author

/**
     * Create a dummy Document
     *
     * @param  array $overrides
     * @return Document
     */
    public function haveADocument($overrides = [])
    {
        return $this->have('BusinessMastery\System\Documents\Document', $overrides);
    }

/**
     * Create a new Model
     *
     * @param  mixed $model
     * @param  array $overrides
     * @return mixed
     */
    public function have($model, $overrides = [])
    {
        return TestDummy::create($model, $overrides);
    }

It will create a new record in the db.

$document = $I->haveADocument();

$authority = App::make('authority');
$authority->setCurrentUser($user);
$rulesCount = $authority->getRules()->count();
dd($document);

* I have a document
class BusinessMastery\System\Documents\Document#5501 (24) {
  protected $table =>
  string(9) "documents"
  protected $fillable =>

Company active returns true.

$ php artisan tinker
[1] > $user = BusinessMastery\System\Users\User::find(1);
// object(BusinessMastery\System\Users\User)(
//   'messages' => array(
//     'oldpassword.passcheck' => 'Parola veche este gresita'
//   ),
//   'incrementing' => true,
//   'timestamps' => true,
//   'exists' => true
// )
[2] > $user->[2] > $user->companyActive();
// true
[3] > $document = BusinessMastery\System\Documents\Document::find(2);
// object(BusinessMastery\System\Documents\Document)(
//   'incrementing' => true,
//   'timestamps' => true,
//   'exists' => true
// )
[4] > Auth::login($user);
// NULL
[5] > $authority = App::make('authority');
// object(Efficiently\AuthorityController\Authority)(
//
// )
[6] > $authority->can('show', $document);
// false
[7] >

@arthurkirkosa
Copy link
Author

If you want I can give you access to my bitbucket repo to test the actual app.

@tortuetorche
Copy link
Contributor

OK, I've done some tests.

Can you try to add this line of code Auth::login($user); before $authority = App::make('authority'); and remove this one $authority->setCurrentUser($user); ?

@arthurkirkosa
Copy link
Author

Same thing.

This is my test.

<?php

$I = new FunctionalTester($scenario);
$I->am('a BMS admin');
$I->wantTo('see that I can access only what I\'m allowed to');

$user = $I->signInAsAdmin();
$name = $user->present()->name;
$company = $user->company;

Route::enableFilters();

/** What I CAN access */
#Profile Page
$I->amOnPage('/profile');
$I->see($user->last_name);
$I->see($user->email);
$I->seeCheckboxIsChecked('input[type="checkbox"][name="is_admin"]');
$I->see('Privilegii Administrative');

#Company Page
$I->amOnPage('/company');
$I->see($company->name);
$I->dontSeeOptionIsSelected('select[name="validator_id"]', 'Nimeni');
$I->dontSeeOptionIsSelected('select[name="validator_id"]', $name);
$I->selectOption('validator_id', $name);
$I->click('Salveaza', '.btn');
$I->seeCurrentUrlEquals('/company');
$I->seeOptionIsSelected('select[name="validator_id"]', $name);
$I->selectOption('validator_id', 'Nimeni');
$I->click('Salveaza', '.btn');
$I->seeCurrentUrlEquals('/company');
$I->dontSeeOptionIsSelected('select[name="validator_id"]', $name);

#Company Fiscal Page
$I->amOnPage('/company');
$I->click('Editeaza datele fiscale', '.btn');
$I->seeCurrentUrlEquals('/company/fiscal');
$I->see('Denumire Firma');

#Users
$I->amOnPage('/users');
$I->see($name);
$I->see('admin');
$I->click('Adauga utilizator');
$I->seeCurrentUrlEquals('/invite');

#Departments
$I->amOnPage('/departments');
$I->click('Adauga departament');
$I->seeCurrentUrlEquals('/departments/create');
$I->fillField('name', 'Departament Instant');
$I->click('Salveaza', '.btn');
$I->seeCurrentUrlEquals('/departments');
$I->see('Departament Instant');
$I->amOnPage('/dashboard');
$I->dontSee('Departament Instant');

// $I->amOnPage('/profile');
// $I->see('Departament Instant');
// $I->dontSeeCheckboxIsChecked('#can-view-departament-instant');
// $I->checkOption('//*[@id="can-view-departament-instant"]');
// $I->click('#can-view-departament-instant');
// $I->click('Actualizare date', '.btn');
// $I->seeCurrentUrlEquals('/profile');
/** Fake view on first department */
// $user->departments()->attach($company->departments->first()->id, ['permission' => 1]);

#Invoices
$I->amOnPage('/invoices');
$I->see('Pentru');

#Dashboard
$I->amOnPage('/dashboard');
$I->see('Adauga departament nou');
$I->click('Adauga departament nou', '.btn');
$I->seeCurrentUrlEquals('/departments/create');

$I->amOnPage('/dashboard');
$I->click('Obiectivul Strategic', '.btn');
$I->seeCurrentUrlEquals('/dashboard');
$I->see('Acest document nu a fost creat inca');

$I->amOnPage('/dashboard');
$I->click('Principii de Operare', '.btn');
$I->seeCurrentUrlEquals('/dashboard');
$I->see('Acest document nu a fost creat inca');

#Documents
$I->amOnPage('/documents/create');
$I->seeCurrentUrlEquals('/dashboard');
$I->see('Unable to map input to command: department');
// $I->see('You are not authorized to access this page.');
$I->amOnPage('/documents/create?department=' . $company->departments->first()->id);
// $I->seeCurrentUrlEquals('/dashboard');
// $I->see('You are not authorized to access this page.');

$document = $I->haveADocument();

Auth::login($user);
$authority = App::make('authority');
$authority->setCurrentUser($user);
$rulesCount = $authority->getRules()->count();
$I->assertTrue($authority->cannot('read', $document));

$I->amOnPage('/documents/' . $document->id);
$I->seeCurrentUrlEquals('/dashboard');
$I->see('You are not authorized to access this page.');

Auth::logout();

FunctionalHelper.php

<?php namespace Codeception\Module;

use Carbon\Carbon;
use Laracasts\TestDummy\Factory as TestDummy;

class FunctionalHelper extends \Codeception\Module {

    /**
     * Sign in a User
     *
     * @param  boolean $isAdmin
     * @return User
     */
    public function signIn($isAdmin = false)
    {
        $I = $this->getModule('Laravel4');

        /**
         * Defaults
         */
        $first_name = 'Gigi';
        $email      = '[email protected]';
        $password   = 'secret';
        $is_admin   = 0;

        if ($isAdmin)
        {
            $is_admin = 1;
        }

        /**
         * Create a dummy company
         *
         * @var Company
         */
        $company = $this->haveACompany([
            'name'          => 'Dummy Company',
            'max_users'     => 5,
            'trial_ends_at' => Carbon::now()->addDays(30)
        ]);

        $company_id = $company->id;

        /**
         * Create dummy user
         *
         * @var User
         */
        $user = $this->haveAnAccount(compact(
            'first_name', 'email', 'password', 'is_admin', 'company_id'
        ));

        $I->amOnPage('/login');

        $I->fillField('email', $email);
        $I->fillField('password', $password);
        $I->click('Autentificare', '.btn');

        return $user;
    }

    /**
     * Sign in a User as a company admin
     *
     * @return User
     */
    public function signInAsAdmin()
    {
        $user = $this->signIn(true);

        $I = $this->getModule('Laravel4');

        /**
         * Verify that the current user is
         * an admin
         */
        $I->seeRecord('users', [
            'email'      => '[email protected]',
            'is_admin'   => '1'
        ]);

        return $user;
    }

    /**
     * Sign in a User as a site owner
     *
     * @return User
     */
    public function signInAsOwner()
    {
        $I = $this->getModule('Laravel4');

        $user = $this->haveAnAccount([
            'email' => '[email protected]'
        ]);

        $I->seeRecord('users', [
            'email' => '[email protected]'
        ]);

        $role = $this->haveARole([
            'name' => 'owner'
        ]);

        $user->roles()->attach($role);

        $I->seeRecord('role_user', [
            'user_id' => $user->id,
            'role_id' => $role->id
        ]);

        return $user;
    }

    /**
     * Register a new user as admin
     * and create the new company
     *
     * @param  array $overrides
     * @return void
     */
    public function signUp($overrides = [])
    {
        $I = $this->getModule('Laravel4');

        $I->amOnPage('/');
        $I->click('Inregistrare');
        $I->seeCurrentUrlEquals('/register');

        $first_name   = isset($overrides['first_name'])   ? $overrides['first_name']   : 'Gigi';
        $last_name    = isset($overrides['last_name'])    ? $overrides['last_name']    : 'Lanseta';
        $email        = isset($overrides['email'])        ? $overrides['email']        : '[email protected]';
        $password     = isset($overrides['password'])     ? $overrides['password']     : 'secret';
        $phone        = isset($overrides['phone'])        ? $overrides['phone']        : '0746278379';
        $company_name = isset($overrides['company_name']) ? $overrides['company_name'] : 'Dummy Company';
        $company_size = isset($overrides['company_size']) ? $overrides['company_size'] : '6-25';

        $I->fillField('first_name', $first_name);
        $I->fillField('last_name', $last_name);
        $I->fillField('email', $email);
        $I->fillField('password', $password);
        $I->fillField('phone', $phone);
        $I->fillField('company_name', $company_name);
        $I->selectOption('company_size', $company_size);
        $I->click('Inregistrare', '.btn');
    }

    /**
     * Update a profile
     *
     * @param  array $overrides
     * @return void
     */
    public function updateProfile($overrides = [])
    {
        $I = $this->getModule('Laravel4');

        $I->amOnPage('/profile');

        $first_name = isset($overrides['first_name']) ? $overrides['first_name'] : 'Gigi';
        $last_name  = isset($overrides['last_name'])  ? $overrides['last_name']  : 'Lanseta';
        $title      = isset($overrides['title'])      ? $overrides['title']      : 'CTO';
        $is_admin   = isset($overrides['is_admin'])   ? $overrides['is_admin']   : true;

        $I->fillField('first_name', $first_name);
        $I->fillField('last_name', $last_name);
        $I->fillField('title', $title);

        if ($is_admin)
        {
            $I->checkOption('input[name=is_admin]');
        }

        $I->click('Actualizare date', '.btn');
    }

    /**
     * Create a new Department
     *
     * @param  array $overrides
     * @return void
     */
    public function createDepartment($overrides = [])
    {
        $I = $this->getModule('Laravel4');

        $I->amOnPage('/departments');

        $I->click('Adauga departament');

        $I->amOnPage('/departments/create');

        $name      = isset($overrides['name'])      ? $overrides['name']      : 'Departament Magic';
        $parent_id = isset($overrides['parent_id']) ? $overrides['parent_id'] : null;

        $I->fillField('name', $name);

        if ($parent_id)
        {
            $I->selectOption('parent_id', $parent_id);
        }

        $I->click('Salveaza', '.btn');
    }

    /**
     * Create a plan
     *
     * @param  array $overrides
     * @return void
     */
    public function createPlan($overrides = [])
    {
        $I = $this->getModule('Laravel4');

        $I->amOnPage('/admin/plans');

        $I->click('Adauga plan');

        $I->amOnPage('/admin/plans/create');

        $name = isset($overrides['name'])
            ? $overrides['name']
            : 'Planul Magic';
        $max_users = isset($overrides['max_users'])
            ? $overrides['max_users']
            : '11';
        $price = isset($overrides['price'])
            ? $overrides['price']
            : '3100';
        $extra_user_price = isset($overrides['extra_user_price'])
            ? $overrides['extra_user_price']
            : '31';
        $months = isset($overrides['months'])
            ? $overrides['months']
            : '6';

        $I->fillField('name', $name);
        $I->fillField('max_users', $max_users);
        $I->fillField('price', $price);
        $I->fillField('price', $price);
        $I->fillField('extra_user_price', $extra_user_price);
        $I->fillField('months', $months);

        $I->click('Creare', '.btn');
    }

    /**
     * Create a dummy User
     *
     * @param  array $overrides
     * @return User
     */
    public function haveAnAccount($overrides = [])
    {
        return $this->have('BusinessMastery\System\Users\User', $overrides);
    }

    /**
     * Create a dummy Department
     *
     * @param  array $overrides
     * @return Department
     */
    public function haveADepartment($overrides = [])
    {
        return $this->have('BusinessMastery\System\Departments\Department', $overrides);
    }

    /**
     * Create a dummy Document
     *
     * @param  array $overrides
     * @return Document
     */
    public function haveADocument($overrides = [])
    {
        return $this->have('BusinessMastery\System\Documents\Document', $overrides);
    }

    /**
     * Create a dummy Company
     *
     * @param  array $overrides
     * @return Company
     */
    public function haveACompany($overrides = [])
    {
        return $this->have('BusinessMastery\System\Companies\Company', $overrides);
    }

    /**
     * Create a dummy Role
     *
     * @param  array $overrides
     * @return Role
     */
    public function haveARole($overrides = [])
    {
        return $this->have('BusinessMastery\System\Roles\Role', $overrides);
    }

    /**
     * Create a new Model
     *
     * @param  mixed $model
     * @param  array $overrides
     * @return mixed
     */
    public function have($model, $overrides = [])
    {
        return TestDummy::create($model, $overrides);
    }

}

@tortuetorche
Copy link
Contributor

Can you replace these lines:

Auth::login($user);
$authority = App::make('authority');
$authority->setCurrentUser($user);

By theses ones:

Auth::login($user);//Not sure if it's necessary to keep this line because you already do `$user = $I->signInAsAdmin();`
$authority = new \Efficiently\AuthorityController\Authority($user);
$authorityConfig = Config::get('authority-controller::initialize');
$authorityConfig($authority);
App::instance('authority', $authority);//useful if you call the Authority facade `Authority::can()`

@arthurkirkosa
Copy link
Author

Nope :(

Interested in testing the actual code?

@tortuetorche
Copy link
Contributor

You have a dummy Document instance. With the code TestDummy::create($model, $overrides);
Is it stored in the database or it's just an object ?

@arthurkirkosa
Copy link
Author

It will store it in the db. https://github.com/laracasts/TestDummy

@tortuetorche
Copy link
Contributor

OK, if you want you can send me your code.
I'm going to send you an email...

@arthurkirkosa
Copy link
Author

I have it stored in bitbucket.org, do you have an account there?

@tortuetorche
Copy link
Contributor

Codeception functional tests have some pitfalls:

In functional testing unlike the traditional way, PHP application does not stop after it finished processing a request. As all requests run in one memory container they are not isolated. So if you see that your tests are mysteriously failing when they shouldn't - try to execute a single test. This will check if tests were isolated during run. Because it's really easy to spoil environment as all tests are run in shared memory. Keep your memory clean, avoid memory leaks and clean global and static variables.

Source: http://codeception.com/docs/05-FunctionalTests#Shared-Memory

Authority-Controller uses Laravel events to inject authorizations into the controllers.
And with the functional tests of Codeception, who uses shared memory, the events of previous requests are still attached to controllers (e.g. DocumentsController)...
So your functional tests have some collision events issue!

As a workaround, we can remove all previous Authority-Controller events.

@arthurkirkosa For your functional test, it looks like this:

Route::enableFilters();
//...
$document = $I->haveADocument();

// Remove old Authority Controller events
$events = app('events');
$listeners = get_property($events, 'listeners');
foreach ($listeners as $eventName => $listener) {
    if (starts_with($eventName, "router.filter: controller.")) {
        $events->forget($eventName);
    }
}

$authority = App::make('authority');
$I->assertTrue($authority->cannot('read', $document));

try {
    $I->amOnPage("/documents/".$document->id);
} catch (Efficiently\AuthorityController\Exceptions\AccessDenied $e) {
    // NOTE: App::error() doesn't seem to work with Codeception so we need this try catch
    // See: http://laravel.io/forum/03-23-2014-codeception-laravel-and-exceptions
    $message = $e->getMessage();
    Session::flash('error', $message);
    $I->amOnPage('/dashboard');
}

$I->seeCurrentUrlEquals('/dashboard');
$I->see('You are not authorized to access this page.');

@tortuetorche
Copy link
Contributor

Maybe we can override amOnAction(), amOnPage() and amOnRoute() methods in the Laravel4 module of Codeception via a helper module.

Create a app/tests/_support/FunctionalHelper.php file with the following code:

<?php namespace Codeception\Module;

class FunctionalHelper extends \Codeception\Module
{
    //..
    public function amOnPage($page)
    {
        // Remove old Authority Controller events
        $events = app('events');
        $listeners = get_property($events, 'listeners');
        foreach ($listeners as $eventName => $listener) {
            if (starts_with($eventName, "router.filter: controller.")) {
                $events->forget($eventName);
            }
        }

        return $this->getModule('Laravel4')->amOnPage($page);
    }
    //...
}

Then require this helper module in your app/tests/functional.suite.yml:

class_name: FunctionalTester
modules:
    enabled: [Laravel4, Filesystem, FunctionalHelper, Asserts]

Finally run this command in a terminal:

codecept build

tortuetorche pushed a commit that referenced this issue Sep 2, 2014
… all its subclasses.

Useful for functional tests with Codeception (see issue #14).

Remove all Authority-Controller events of every Controllers:

BaseController::flushAuthorityEvents('*');

Remove all Authority-Controller events of ProjectsController:

ProjectsController::flushAuthorityEvents();
@tortuetorche
Copy link
Contributor

You can read this Wiki page who explains how to run Codeception functional tests with Authority-Controller.

NB: You need the lastest version of Authority-Controller.

@arthurkirkosa
Copy link
Author

@tortuetorche I've switched to "minimum-stability": "stable" and this changes are not included in 1.2.3, can you make a new tag please

@tortuetorche
Copy link
Contributor

You're right, I forgot to tag these changes.

@tortuetorche
Copy link
Contributor

Done: https://packagist.org/packages/efficiently/authority-controller#1.2.4
You can update the package, like this:
composer update efficiently/authority-controller

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants