Laravel Gate for authorization check - php

I used Gate for laravel authorization where A user have One role and one role have Multiple Permissions. I used the following method for checking role has permission or not. But it doesn't work properly means user may have chance to access one route though I have given him multiple permissions through roles.
See the scenario is suppose admin can see list of users as well can access test route. But in my case admin can see list of users but he can't access the test route. Though I have given him the access in permission table.
Can anyone suggest what's the problem here?
public function boot(GateContract $gate)
{
$this->registerPolicies();
foreach ($this->getPermissions() as $permission) {
$gate->define($permission->name, function ($user) use ($permission) {
return $user->role->id === $permission->role_id;
});
}
}
public function getPermissions()
{
return Permission::with('role')->get();
}
In controller Code :
public function test(){
if(Gate::allows('test')){
echo "This is Test";
}
return redirect('/');
}

Related

Laravel 8: Call to undefined method Model::contains()

I'm using Laravel 8 for my project and in this project and I have created a custom Middleware called Admin that goes like this:
public function handle(Request $request, Closure $next)
{
if (Auth::user()->isAdmin()) {
return $next($request);
}
return redirect('/');
}
And I tried applying it like this:
Route::middleware('admin')->group(function () {
Route::get('test', function () { return "User is authenticated and is an admin."; });
});
And on Kernel.php at $middlewareGroups section:
'admin' => [
'auth',
\App\Http\Middleware\Admin::class
],
So I called the isAdmin() at User Model which simply checks if the role of the user is correct or not:
public function role()
{
return $this->belongsTo(Role::class);
}
public function isAdmin()
{
return $this->role->contains('slug', 'super-admin');
}
But now the problem is, when I go to /test uri, it does not redirect me to ('/') uri and shows me this error:
BadMethodCallException
Call to undefined method App\Models\Role::contains()
So what is going wrong here? How can I fix this issue?
Note that the relationship between roles and users table is One To Many. That's why I wrote role() instead of roles() because each user has only one role.
When you use a belongsTo relationship, laravel will return the model directly and not a collection of models.
When you call:
$this->role
What you get is either null (if the relationship does not exist) or a Role model. You can write your isAdmin function accordingly:
public function isAdmin()
{
return empty($this->role) ? false : $this->role->slug === 'super-admin';
}
You probably copied the code from an example with a Many to many relationship: in that case Laravel will return a collection of models and you can invoke collection methods on it, such as contains
could we see a snip of your Role Model.

Laravel 8 returns 403 UNAUTHORIZED ACCESS

I'm working with Laravel 8, and I have a resource Controller named Profile. And within this Controller, users can edit their user profile information.
So at the edit method, I put this:
public function edit(User $user)
{
$this->authorize('edit-user', $user);
return view('editprofile', compact('user'));
}
And users can access editprofile blade by this button:
Edit Profile
But now the problem is, it returns this:
403 THIS ACTION IS UNAUTHORIZED.
However I am already logged in and I want to access my own profile.edit and not other users!
So if you know why am I getting this error, please let me know, I would really appreciate any idea or suggestion from you guys...
Thanks.
And here is also my route:
Route::middleware('auth')->group(function() {
Route::resource('profile' , ProfileController::class);
});
UDPATE 1:
Please change the followings
Route parameter names is "profile"
So, the controller method parameter must be the same "$profile".
public function edit (Request $request, User $profile) {
Gate::allows('edit-user', $profile);
// do the logic
}
i had the same error. i changed
public function authorize()
{
return false;
}
to
public function authorize()
{
return true;
}
in App\Http\Requests\StoreProductRequest

Laravel Middleware is not work properly for Check Permission

I'm developing a Laravel ACL System. In my ACL I'm grant the permissions via HasPermission Middleware, in my middleware can't check any permission it's always executed the redirect()->back() method.
Here is my code Sample.
class HasPermission
{
public function handle($request, Closure $next,...$permissions)
{
// $permissions = explode(',', $permissions);
//dd($permissions);
foreach($permissions as $permission){
if (Auth::user()->can($permission)) {
return $next($request);
}
}
return redirect()->back();
}
}
My Controller.
function __construct()
{
$this->middleware('auth');
$this->middleware('HasPermission:Role-Read,Role-Delete')->only('userEdit');
$this->middleware('can:Role-Update')->only('userEdit');
}
This. Auth::user()->can($permission) is not work properly. What will be the solutions for this problems.
try:
Auth::user()->can('field you want to update',$permission);

Laravel: How to throw 403 if the user enter the ID manually in the route?

Building an app (Blog/posts).
Where only auth users can edit their post(which ofcourse belongs to them only).
For example, Post with an id of 15 belongs to particular user, so if he edits it, the route will be like this
http://localhost:8000/post/15/edit
this is correct.
But when the user enters any other post ID(which doesn't belongs to him) in the route, it shows
http://localhost:8000/post/16/edit
ErrorException (E_NOTICE)
Trying to get property 'user_id' of non-object
How to show unauthorised page in this case?
This is the postController
public function edit($id)
{
$post = Post::find($id);
if(Auth::user()->id == $post->user_id){
return view('post-edit',compact('post'));
}else {
return redirect()->route('home');
}
}
The following code checks if the post exist (which is why you are getting the error Trying to get property 'user_id' of non-object, because it doesn't exist), and then checks if it belongs to the user in the same condition. If it's not valid it aborts with a 403 UNAUTHORIZED error code.
public function edit($id)
{
$post = Post::find($id);
if (empty($post) || Auth::id() != $post->user_id) {
abort(403);
}
else {
return view('post-edit',compact('post'));
}
}
Here is a better version that checks if a post exist, with the specified ID, but also with the right user and throws an exception otherwise:
public function edit($id)
{
$post = Post::whereHas('user', function ($q) {
$q->where('users.id', Auth::id());
})->findOrFail($id);
return view('post-edit',compact('post'));
}
A third version, on the same idea as the 2nd one, but simpler:
public function edit($id)
{
$post = Post::where('user_id', Auth::id())->findOrFail($id);
return view('post-edit',compact('post'));
}
use laravel authorization policy to authorize users.
php artisan make:policy PostPolicy --model=Post
This command will create PostPolicy.php in app\policies dir.
now you'll have to register the policy in AuthServiceProvider. So first add use statements of your policy and model for example.
use App\Post;
use App\Policies\PostPolicy;
then find protected $policies and in that array register your policy. Model followed by policy.
protected $policies = [
Post::class => PostPolicy::class,
];
Now in your Policy that we generated using artisan command. will hold all CRUD related methods. each of them accepts two parameters one is User and second is the model you want to authorize except create method. note that you can modify create or other methods to accept more parameters. it's upto you.
Now for example in your policy let's build logic for update method.
/**
* Determine if the given post can be updated by the user.
*
* #param \App\User $user
* #param \App\Post $post
* #return bool
*/
public function update(User $user, Post $post)
{
return $user->id === $post->user_id;
}
As you can see return Boolean here. you can customize methods as you want. Next in your controller method. where you want to authorize user simply add
public function update(Post $post)
{
$this->authorize('update', $post);
// then your logic here.
}
For create authorization you just pass pass empty class
$this->authorize('create', Post::class);
It accepts two parameters one is authorization method name and second is model.It automatically get's authenticated user and authorize user. if not authorized then throws Illuminate\Auth\Access\AuthorizationException which is 403.
Also if you need to modify the 403 error view you'll need to create 403 blade in
resources/views/errors/403.blade.php
Everything is well documented in laravel doc.
Extra tip if you are going to use some Boolean datatype value for returned from database as tinyint which are 1 or 0. for example
public function view(User $user, Post $post)
{
if(! $post->isPrivate) {
return true;
}
return $user->id === $post->user_id;
}
then make sure to cast that value to Boolean in model to return as true or false. because it was not working for when i deployed my application on shared hosting. Later i found that it was returning as a string. also the version of the database was old.

how to get the roles and permissions dinamically - Laravel

I'm trying to implement roles and permissions for my laravel API. I installed the package:
https://yajrabox.com/docs/laravel-acl/3.0/introduction
It would be great if someone could explain to me how it works, all I want to do is get the permission when the user hits one API route.
I don't want to set the middleware in every route, because I'm going to do several routes and it would be a pain to set middleware every time, I want do it dynamically.
I tried to do it myself but it's not working. This is my code in Authserviceprovider:
public function boot(GateContract $gate)
{
$this->registerPolicies();
Passport::routes();
Passport::tokensExpireIn(Carbon::now()->addDays(15));
Passport::refreshTokensExpireIn(Carbon::now()->addDays(30));
$permissions = Permission::with('roles')->get();
foreach ($permissions as $permission)
{
$gate->define($permission->name, function (User $user) use ($permission) {
return $user->hasPermission($permission);
});
}
}
I'm doing like this: https://github.com/laracasts/laravel-5-roles-and-permissions-demo/tree/master/app
You can use middleware within your web.php / api.php file such as my example (web.php) below:
Route::group(['middleware' => ['verified']], function () {
Route::get('/', 'HomeController#index')->name('home');
});
As my example shows, this will check an account is verified before allowing it to view '/'
Updated
This is almost irrelevant to the question above but as the Op asked a secondary question within the comments to my answer: here is my middleware code to show the Op how the middleware will function:
public function handle($request, Closure $next)
{
$verified = Auth::user();
if ($verified->verified == 0)
{
Auth::logout();
Session::flash('error', "$verified->username, your email address hasn't been verified yet therefore you're unable sign in.");
return Redirect('/login');
}
return $next($request);
}

Categories