Laravel can't create policy for user model - php

Can' create Policy for User model.
I created Policy like this
php artisan make:policy UserPolicy --model=User
Got UserPolicy.php with CRUD actions.
Then inside AuthServiceProvider.php I added
protected $policies = [
// 'App\Model' => 'App\Policies\ModelPolicy',
User::class => UserPolicy::class,
];
But nothing happens. As I understand generated Policy for User model by default returning false on every action, I even explicitly added this to UserPolicy class:
public function create(User $user)
{
return false;
}
Still can create user.
Later I will need to check if the user trying to edit his own post or not. Everything should be forbidden for non-admin users except editing own profile (model).
I must be missing something obvious.
UPDATE:
If I put
$this->authorize('create', $user);
In UsersController create method, it will invoke create method Policy, so it seams that something is wrong with
...
use App\Policies\UserPolicy;
use App\User;
...
protected $policies = [
// 'App\Model' => 'App\Policies\ModelPolicy',
User::class => UserPolicy::class,
];
inside AuthServiceProvider.

You can write this code for User Policy
in UserPolicy.php :
public function update(User $user, User $model)
{
return $user->id === $model->id;
}
For example by this code you can update just your own profile.

You need to put this in you function in controller
$this->authorize('create',$user)

Put below lines in your controller function $this->authorizeForUser($currentUser,'create', User::class)

Related

Why does my Laravel policy always return false?

The code for the policy is here:
class userOwnedClassPolicy
{
use HandlesAuthorization;
...
public function create(User $user)
{
return ($user->userType == 'teacher');
}
...
}
This policy is registered thusly in the AuthServiceProvider.php file:
class AuthServiceProvider extends ServiceProvider
{
//Map models to authorization policies.
protected $policies = [
App\Models\classMember::class => App\Policies\classMemberPolicy::class,
App\Models\evaluation::class => App\Policies\evaluationPolicy::class,
App\Models\group::class => App\Policies\groupPolicy::class,
App\Models\groupMember::class => App\Policies\groupMemberPolicy::class,
App\Models\sharedClass::class => App\Policies\sharedClassPolicy::class,
App\Models\slg::class => App\Policies\slgPolicy::class,
App\Models\spreadsheet::class => App\Policies\spreadsheetPolicy::class,
App\Models\spreadsheetValue::class => App\Policies\spreadsheetValuePolicy::class,
App\Models\teacher::class => App\Policies\teacherPolicy::class,
App\Models\test::class => App\Policies\testPolicy::class,
App\Models\userOwnedClass::class => App\Policies\userOwnedClassPolicy::class
];
public function boot()
{
$this->registerPolicies();
}
}
(I have tried registering the policies using strings of the file paths as well, but this accomplishes nothing.)
The relevant section of controller code is here:
class ClassController extends Controller
{
...
public function store(Request $postReq)
{
$this->authorize('create', Auth::user());
userOwnedClass::create([
'name' => $postReq->input('className'),
'ownerId' => Auth::user()->id
]);
}
...
}
I have tried substituting the code in the policy's create method with return true, but even that fails. What have I done wrong, and why does the controller always return a 403 error when called?
As you created policy userOwnedClassPolicy and set it for userOwnedClass model in AuthServiceProvider here:
App\Models\userOwnedClass::class => App\Policies\userOwnedClassPolicy::class
you cannot just run policy method:
$this->authorize('create', Auth::user());
When you run this line above, you tell - check create method for policy for \App\Models\User object, but you don't have any policy created for this model.
So in this case you should run it like so:
$this->authorize('create', \App\Models\userOwnedClass::class);
Then Laravel will know that it should run create method from userOwnedClassPolicy policy and it will automatically pass currently authenticated user into $user variable in policy method.

Laravel resource toArray() not working properly

I've created a new API resource called UserResource:
class UserResource extends JsonResource
{
public function toArray($request)
{
/** #var User $this */
return [
'first_name' => $this->first_name,
'last_name' => $this->last_name,
'email_verified_at' => $this->email_verified_at,
];
}
}
And then I'm trying to get current user object and pass it to the resource:
class UserController extends Controller
{
public function index(Request $request)
{
/** #var User $user */
$user = $request->user();
return new UserResource($user);
}
}
But it always throws an exception Trying to get property 'first_name' of non-object. $user variable contains current user.
I'm using Xdebug and I checked that the resource contains formatted json in $this->resource, but not current user's model:
So why? Laravel's documentation says that I'll be able to get current resource (User model in my case) in $this->resource parameter, but it does not work. Any ideas?
UPDATE: here is break point for xbedug in UserResource:
It seems strange to me that you get the User object from the index's request and I reckon that somethings probably wrong there.
Normally, the index method is used to return a listing of all instances of the resp. model, e.g. something like
public function index() {
return UserResource::collection(User::all());
}
Try to return a user object (as resource) which you are sure exists
$user = Users::findOrFail(1);
return new UserResource($user);
and see if the error still persists. If it does not, something is wrong with the data you pass in the request.
I had a look at the JsonResource Class. Look at the contructor:
public function __construct($resource)
{
$this->resource = $resource;
}
Seems like the $user is saved to $this->resource. So you have to change your return statement to:
return [
'first_name' => $this->resource->first_name,
'last_name' => $this->resource->last_name,
'email_verified_at' => $this->resource->email_verified_at,
];
Does that work?
As #Clément Baconnier said in comments to the question, the right way to fix it is reinstalling vendor folder. The problem is that project has been created via Laravel new command from local with php 7.2, but there's php 7.4 in container.

Laravel authorizeResource "This action is unauthorized."

I have a model called BusinessClient in the namespace App/BaseData. For this model I've created a Policy with the command php artisan make:policy BaseData/BusinessClientPolicy --model=BusinessClient and registerd it in th e AuthServiceProvider.php file.
protected $policies = [
// Base Data
BusinessClient::class => BusinessClientPolicy::class,
];
The viewmethod from this policy looks like this
public function view(User $user, BusinessClient $businessClient)
{
return true;
}
To authorize my resource I called in the constructor of my apiResource Controller the authorizeResource method.
public function __construct(Request $request)
{
$this->middleware('auth:api');
$this->middleware('verified');
$this->authorizeResource(BusinessClient::class, 'business_client');
}
My problem is that I alwas get a 401 Forbiddenresponse when I try to call the show method from my controller. What is going wrong with my code?

Laravel 5.5: Authorization with pivot table

I have four table users, groups, posts, and group_user. Users can follow different groups. The group_user table is for many-to-many relationship.
Every post belongs to a group and a user. I want that a user can only post a post on a group if he follows that group. I can easily check using if statement that whether a user follows that group or not. But how can I authorize the user for posting using policies.
Create a policy:php artisan make:policy.
Register policy:
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* #var array
*/
protected $policies = [
Post::class => PostPolicy::class,
];
In your policy the logic, for instance:
public function create(User $user, Post $post)
{
$groupsOfUser = $user->groups()->get();
foreach ($groupsOfUser as $group) {
if($group->id == request('groupId'))return true;
}
}
And in your Controller:
public function store(Post $post, $groupId)
{
$this->authorize('create', $post);
Post::create([
'user_id' =>auth()->id(),
'group_id' => $groupId,
'title' => 'sometitle',
]);
}
And i have tested it with route:
Route::get('/post/{groupId}', 'PostController#store');
But you may be getting groupId via input, but you get the idea.
There doesn't seem to be any good answers for this so I'll share what I did to help out anyone looking for this in the future.
This was tested in laravel 5.8 but I think it will work at least a few versions back.
First create a pivot model
use Illuminate\Database\Eloquent\Relations\Pivot;
class GroupUser extends Pivot {}
Update your groups relationship in the User model
public function groups() {
return $this->belongsToMany(Group::class)
->using(GroupUser::class);
}
And update your users relationship in the Group model
public function users() {
return $this->belongsToMany(User::class)
->using(GroupUser::class);
}
Create your GroupUserPolicy class
use Illuminate\Auth\Access\HandlesAuthorization;
class GroupUserPolicy {
use HandlesAuthorization;
public function update(User $user, GroupUser $pivot) {
// check permissions
}
// additional authorization methods
}
And link them up in the AuthServiceProvider if not using auto discover
protected $policies = [
GroupUser::class => GroupUserPolicy::class,
// ...
];
Then when you want to show or update a pivot, in a controller for example, then you can just pass the pivot to the authorize check
$group = $user->groups()->where('group_id', $groupId)->firstOrFail();
$this->authorize('update', $group->pivot);
// or ...
foreach ($user->groups as $group) {
$this->authorize('update', $group->pivot);
}
There is a complex solution for Access Control List. Read about Zizaco Entrust Library. Here you can set permissions for each route in your system. By that routing and permission you can prepare few groups.
Topic is hard but realy worth to implement:
https://github.com/Zizaco/entrust

Not working laravel Policy for my UsersUpdate

I have
(1/1) HttpException
This action is unauthorized.
I think all should work fine and I have done it right but maybe not.
My controller method:
public function update(Request $request, Users $uzytkownik)
{
$this->authorize('update', $uzytkownik);
return 1;
}
UsersPolicy that is in App\Policies\:
<?php
namespace App\Policies;
use App\Models\Users;
use Illuminate\Auth\Access\HandlesAuthorization;
class UsersPolicy
{
use HandlesAuthorization;
public function update(Users $user)
{
return true;
// return $user->login === auth()->login;
}
}
And in AuthServiceProvider:
protected $policies = [
'App\Model' => 'App\Policies\ModelPolicy',
'App\Models\Users' => 'App\Policies\UsersPolicy',
];
My Users model lays in App\Models\
When I cut $this->authorize('update', $uzytkownik); this line from controller everything works fine and I see '1', when I add it again HttpException.
What do I have wrong here? Thinking and Thinking, looking, I don't see anything bad here.
please make sure that your route is under auth middlware like this :
Route::group(['middleware' => 'auth'], function () {
// ur update route here
});
or in ur controller constructor like this :
public function __construct()
{
$this->middleware('auth');
}
and also like #Laerte said your update policy method should have another parameter of type user which is the user you want to edit, like this :
public function update(Users $userLoggedIn, Users $uzytkownik)
{
return true;
}
In your Policy, you have to add two parameters: The first one is the user logged in, and the second is the actual parameter. Try this in the Policy:
public function update(Users $userLoggedIn, $user)
{
return true;
}

Categories