Moe's avatar
Level 6

Policy on create

With the code below i will check if a user has the rights to create a new post object, but even when I return TRUE the link will not show up. What am I doing wrong?

view:

@can('create-post')
    <a>new post</a>
@endcan

PostPolicy.php

<?php

namespace App\Policies;

class PostPolicy
{
    public function create()
    {
        return TRUE;
    }
}

AuthServiceProvider:

<?php

namespace App\Providers;

use Illuminate\Contracts\Auth\Access\Gate as GateContract;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var array
     */
    protected $policies = [
        Post::class => PostPolicy::class,
    ];

    /**
     * Register any application authentication / authorization services.
     *
     * @param  \Illuminate\Contracts\Auth\Access\Gate  $gate
     * @return void
     */
    public function boot(GateContract $gate)
    {
        parent::registerPolicies($gate);
    }
}
````

0 likes
10 replies
Matt.H's avatar

Hi.

Same problem for me today. A Policy is registered for a specific Model (in AuthServiceProvider). Laravel needs to know the model which Policy is for.

I think you can do something like that (with your code) :

<?php

namespace App\Policies;

class PostPolicy
{
    public function create(User $user)
    {
        return TRUE;
    }
}
@can('create', Post::class)
    <a>new post</a>
@endcan
2 likes
jrdavidson's avatar

Do you not have to do the following in your AuthServiceProvider still.

use App\Post
1 like
mtpultz's avatar

Did anyone figure this out? I seem to be having a similar problem, but using the store action. I can return true and it still fails with a 403.

stevenobird's avatar

If you want to do access

$model->can('create-post')

or in blade

@can('create-post')

you'll need to define that rule name in your AuthServiceProvider class:

$gate->define('create-post', function($user) {
    return true; //$user>isAdmin() or whatever...
});

If you define rules in Policy classes, like here:

use App\Post;
use App\User;

class PostPolicy
{
    
    public function update(User $user, Post $post)
    {
        // $user->isAdmin() or ...
        // $user->id == $post->user_id;
        return true; 
    }

}

... that makes only sence when you can pass in or inject an existing model - so an already created post works.

In Blade (while $post was passed through your Controller):

@can('update', $post)

.. in Controller:

$post = Post::findOrFail($id);
$user->can('update', $post);

So my conclusion is:

  • If you want to use policies, just use them for existing models and their mutators (update, delete)
  • If you want to create authorization rules for creation of models, you'll have to stick with first sample and create that rule in your AuthServiceProvider.

// EDIT: I should read how old this thread is... @mtpultz how did you define your rules?

3 likes
veuge0592's avatar

Thanks @twaileit, I was wondering how to make creation policies, your solution of placing them in AuthServiceController worked for me, but I think it could get messy if happens that you need to restrict the creation in several models...

mostafaznv's avatar

Just define create (or index) policy in your policy class (postsPolicy.php for me)

public function create(User $user,  $post){
    return $user->isAdmin();
}

and pass your model class to authorize function:

$this->authorize('create', posts::class);

or

you can define a policy in AuthServiceProvider.php :

public function boot(GateContract $gate) {
    $this->registerPolicies($gate);

    $gate->define('isSuperUser', function ($user) { 
        return $user->super_user;
    });
}

and call it on everywhere without any model class:

if (Gate::allows('superadmin')){
    return "allow";
}
else{
    return "deny";
}
2 likes
Jarrid's avatar

Wish I had found this a few hours ago ;)..mostafaznv's post worked for me. However, I did not need to update my create function on the policy to accept the $post variable - just adding the posts::class worked.

1 like
woodydrn's avatar

I know this is a long time ago, but in Laravel 7.x you can use:

@can('create', App\Post::class)

Remember the "App" part, else it does not work within Blade.

If you are in a controller App is not needed:

$user->can('create', Post::class)

Please or to participate in this conversation.