laravel 9 undefined variable $role - php

Hello i have try to do permisssion and role in laravel 9 but i have a error .
User model
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable implements MustVerifyEmail
{
use HasApiTokens, HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
'secretword',
];
/**
* The attributes that should be hidden for serialization.
*
* #var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast.
*
* #var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
public function roles()
{
return $this
->belongsToMany('App\Role')
->withTimestamps();
}
public function users()
{
return $this
->belongsToMany('App\User')
->withTimestamps();
}
public function authorizeRoles($roles)
{
if ($this->hasAnyRole($roles)) {
return true;
}
abort(401, 'This action is unauthorized.');
}
public function hasAnyRole($roles)
{
if (is_array($roles)) {
foreach ($roles as $role) {
if ($this->hasRole($role)) {
return true;
}
}
} else {
if ($this->hasRole($roles)) {
return true;
}
}
return false;
}
public function hasRole($role)
{
if ($this->roles()->where('name', $role)->first()) {
return true;
}
return false;
}
};
Role model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Role extends Model
{
use HasFactory;
}
AdminController
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class AdminController extends Controller
{
public function __construct()
{
$this->middleware('auth');
$this->middleware('role:ROLE_ADMIN');
}
public function index()
{
return view('admin.home');
}
}
SuperAdminController
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class SuperAdminController extends Controller
{
public function __construct()
{
$this->middleware('auth');
$this->middleware('role:ROLE_SUPERADMIN');
}
public function index()
{
return view('superadmin.home');
}
}
web.php
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\PostController;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('/', function () {
return view('auth/login');
});
Auth::routes(['verify' => true]);
Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');
Route::get('/admin', [App\Http\Controllers\AdminController::class, 'index']);
Route::get('/superadmin', [App\Http\Controllers\SuperAdminController::class, 'index']);
Route::resource('posts', PostController::class);
app / Http / Middleware / CheckRole.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class CheckRole
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* #return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next)
{
if (! $request->user()->hasRole($role)) {
abort(401, 'This action is unauthorized.');
}
return $next($request);
}
}
I try to do permissions and roles in laravel 9.And when i want to go /admin i have this error and show me this file i want to know how can i resolve this error for all good work?

You need pass and accept the parameter of role, in middleware.
Your middleware
public function handle(Request $request, Closure $next, string $role)
If you are using route for middleware then
Route::resource(...)->middleware('middlewareName:roleName');
If controller being used
$this->middleware(\App\Http\Middleware\CheckRole::class.':roleName')
To send argument to the middleware, use : after the middleware name. To send multiple argument, separate the arguments with ,.

I strongly recommend using Policy classes for this. Then you can simply use the can middleware.
More info here: https://laravel.com/docs/9.x/authorization#creating-policies
Your models basically would have their own policy classes per model, then you can fine tune exactly what users can do based on what their role is.
No need to reinvent the wheel here.

Related

Laravel showing {"message":"Unauthenticated."} when accessing api routes

I have an Vue + Laravel application. Here I am using laravel API routes to get the data. For the API auth I am using JWT from this tutorial: https://blog.logrocket.com/implementing-jwt-authentication-laravel-9/
Now I am getting:
{"message":"Unauthenticated."}
error message whne I access to this API route: http://localhost:3000/api/countries
Country.php (Model)
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Country extends Model
{
use HasFactory;
protected $table = 'countries';
protected $primaryKey = 'id_country';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'country_name' ,
'code' ,
];
}
CountryController.php (Controller)
<?php
namespace App\Http\Controllers;
use App\Models\Country;
use Illuminate\Http\Request;
class CountryController extends Controller
{
public function __construct()
{
$this->middleware('auth:api');
}
/**
* #return \Illuminate\Http\JsonResponse
*/
public function countries()
{
$country = Country::get();
return response()->json($country, 200);
}
}
api.php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Auth\AuthController;
use App\Http\Controllers\CountryController;
Route::controller(CountryController::class)->group(function () {
Route::get('countries', 'countries')->name("countries");
});
Can you tell me what is wrong here and how can I solve it?

Laravel policy return unauthorized

I am trying to implement policies in my project. All tries have proven unsuccessful despite following documentation to the letter. And also read numerous posts on SO about it and other media. I did as described in docs, but nonetheless it doesn't work. What gives?
In AuthServiceProvider:
<?php
namespace App\Providers;
use App\User;
use App\Job;
use App\Policies\JobPolicy;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* #var array
*/
protected $policies = [
'App\Job' => 'App\Policies\JobPolicy',
//Job::class => JobPolicy::class,
];
/**
* Register any authentication / authorization services.
*
* #return void
*/
public function boot()
{
$this->registerPolicies();
//
}
}
In policy:
<?php
namespace App\Policies;
use App\Job;
use App\User;
use Illuminate\Auth\Access\HandlesAuthorization;
class JobPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view any jobs.
*
* #param \App\User $user
* #return mixed
*/
public function viewAny(User $user,Job $job)
{
//return (($user->isAdmin() || $user->isModerator() || $user->isUser()) && $user->status==1);
//return ($user->isMod());
return true;
}
In controller:
public function index()
{
$this->authorize('viewAny', User::class, Job::class);
return view("jobs.index");
}
My User model:
<?php
namespace App;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use App\Role;
class User extends Authenticatable
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',"role_id"
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
public function role(){
return $this->belongsTo("App\Role", "role_id");
}
public function isMod()
{
$user = User::find(auth()->user()->id);
$role = $user->role()->first()->name;
if($role==="job board moderator"){
return true;
}
else{
return false;
}
}
}
And Job model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use App\User;
class Job extends Model
{
protected $fillable = [
"title", "description", "email"
];
public function user(){
return $this->belongsTo("App\User","user_id");
}
}
In policy:
public function viewAny(User $user)
{
return true;
}
In controller:
public function index()
{
$this->authorize('viewAny', Job::class);
return view("jobs.index");
}
The way to call a model policy method changes depending on the number of parameters it has.
No object
/* In policy*/
public function viewAny(User $user)
/* In controller */
$this->authorize('viewAny', Job::class)`
1 object
/* In policy*/
public function view(User $user, Job $job)
/* In controller */
$this->authorize('view', $job)
More than 1 object
/* In policy*/
public function view(User $user, Job $job, AnotherModel $model)
/* In controller */
$this->authorize('view', [$job, $model])
Source: https://laravel.com/docs/5.8/authorization#creating-policies

Laravel middleware with users

I'm trying to set up middleware to my laravel blog and I have a problem with following situation.
I want to allow access to certain routes but only to specific users (not roles).
For example... 'index', 'create', 'store' and 'show' routes are available for everyone with admin, globalmod or moderator role. To 'destroy' route can access only admin.
Problem is with 'edit' and 'update' routes. To these routes I want to give access only to 'admin' users and to user who created that blog post.
This code is working propertly for roles but I don't know how to set it up for specific user.
App\User.php
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable {
use Notifiable;
protected $fillable = [
'name', 'email', 'password',
];
protected $hidden = [
'password', 'remember_token',
];
public function roles() {
return $this->belongsToMany('App\Role');
}
public function authorizeRoles($roles) {
if (is_array($roles)) {
return $this->hasAnyRole($roles) ||
abort(401, 'This action is unauthorized.');
}
return $this->hasRole($roles) ||
abort(401, 'This action is unauthorized.');
}
public function hasAnyRole($roles) {
return null !== $this->roles()->whereIn('slug', $roles)->first();
}
public function hasRole($role) {
return null !== $this->roles()->where('slug', $role)->first();
}
}
App\Http\Middleware\RolesMiddleware.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class RolesMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next, ... $roles) {
if(!Auth::check()) {
return redirect()->route('login')->with('attention', 'You have no access');
}
$user = Auth::user();
foreach($roles as $role) {
if($user->hasRole($role)) {
return $next($request);
}
}
return redirect()->back()->with('attention', 'You have no access');
}
}
App\Http\Kernel.php
protected $routeMiddleware = [
...
'role' => \App\Http\Middleware\RolesMiddleware::class,
];
App\Http\Controllers\BlogController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Blog;
class BlogsController extends Controller
{
public function __construct() {
$this->middleware('role:admin', array('only' => 'destroy'));
$this->middleware('role:admin,globalmod,moderator', array('only' => array('index', 'create', 'store', 'show')));
}
public function index() {
...
}
public function create() {
...
}
public function store(Request $request) {
...
}
public function show($id) {
...
}
public function edit($id) {
...
}
public function update(Request $request, $id) {
...
}
public function destroy($id) {
...
}
}
For example, I need something like this
$this->middleware('role:admin OR $blog->author_id == Auth::user()->id',
array('only' => 'edit', 'update'));
Look at https://laravel.com/docs/5.6/authorization#gates you can define rules/policies for you model.
It can be achieved by custom middleware but Laravel policies and gates is way to go.
try something like this
Route::group(['middleware' => ['role:Admin']], function () {
//define routes here... Ex. below
Route::get('/', 'HomeController#index');
/* for application to get sale price */
}

Laravel 5.4, Login page keep redirecting

I'm having an issue where a login page keeps redirecting me back to login instead of going to the HomePage.
My Routes file:
Route::group(['middleware' => ['auth']], function () {
Route::get('/', function () {
return redirect()->route('dash');
});
Route::get('dash', 'DashController#index')->name('dash');
});
Route::get('login', ['as' => 'login', 'uses' => 'LDAPAuthController#index']);
Route::post('login', 'LDAPAuthController#login');
Everything else is set to its normal default setting:
RedirectIfAuthenticated Middleware:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param string|null $guard
* #return mixed
*/
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->check()) {
return redirect('/home');
}
return $next($request);
}
}
My login controller:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Input;
use Illuminate\Http\Request;
use App\Services\CakePHPHash;
use App\Users;
use Redirect;
use Auth;
use View;
class LDAPAuthController extends Controller {
public function login(Request $request) {
$user = Users::where('email', strtolower($request->email))->first();
$hash = new CakePHPHash;
if(($hash->hash($request->password, null, true)) !== $user->password) {
return back()->with('error', 'Username and/or password are incorrect.');
}
Auth::login($user);
return Redirect::to('/');
}
public function logout() {
Auth::logout();
Session::flush();
return back()->with('error', 'You just logged out.');
}
public function index(Request $request) {
return View::make('auth.login');
}
}
My Sample Dash Controller:
<?php
namespace App\Http\Controllers;
use \App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Input;
use Auth;
class DashController extends Controller {
public function index() {
return view('Home.home');
}
}
I simplified my Users model down to just changing primary and table name:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Session;
use Auth;
class Users extends Authenticatable
{
protected $table = 'users';
protected $primaryKey = 'user_id';
public function getRememberToken()
{
return null; // not supported
}
public function setRememberToken($value)
{
// not supported
}
public function getRememberTokenName()
{
return null; // not supported
}
/**
* Overrides the method to ignore the remember token.
*/
public function setAttribute($key, $value)
{
$isRememberTokenAttribute = $key == $this->getRememberTokenName();
if (!$isRememberTokenAttribute)
{
parent::setAttribute($key, $value);
}
}
}
This isn't the first I'll be making an App in Laravel, but this is giving me headaches unlike before. Another piece of info I'm concerned about is that the $user gives out the right user and user_id, but the login_web_ session is giving out a different one (not the right user_id) from debugbar. Auth::check() returns true though.

Dynamic Allow access to different permission rules - ACL LARAVEL

The admin user can view all posts on the homepage and access the update page for each post
$gate->before(function(User $user, $ability)
{
if ($user->hasAnyRoles('adm') )
return true;
});
This is ok
But I do not know what I am doing wrong in the policies because other users can see all posts at home page but can not access the post update page that they have created, access is denied.
The permissions are dynamic, I have the tables in the DB:
permissions
permission_role
posts
roles
role_user
users
Home Controller
<?php
namespace App\Http\Controllers;
use App\Http\Requests;
use Illuminate\Http\Request;
use App\Post;
use Gate;
class HomeController extends Controller
{
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('auth');
}
/**
* Show the application dashboard.
*
* #return \Illuminate\Http\Response
*/
public function index(Post $post)
{
$posts = $post->all();
return view('home', compact('posts'));
}
public function update($idPost)
{
$post = Post::find($idPost);
if(Gate::denies('update-post', $post))
abort(403);
return view('update-post', compact('post'));
}
}
class AuthServiceProvider
<?php
namespace App\Providers;
use Illuminate\Contracts\Auth\Access\Gate as GateContract;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use App\Post;
use App\User;
use App\Permission;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* #var array
*/
protected $policies = [
];
/**
* Register any application authentication / authorization services.
*
* #param \Illuminate\Contracts\Auth\Access\Gate $gate
* #return void
*/
public function boot(GateContract $gate)
{
$this->registerPolicies($gate);
$permissions = Permission::with('roles')->get();
foreach( $permissions as $permission)
{
$gate->define($permission->name, function(User $user) use ($permission){
return $user->hasPermission($permission);
});
}
$gate->before(function(User $user, $ability)
{
if ($user->hasAnyRoles('adm') )
return true;
});
}
}
model User
<?php
namespace App;
use Illuminate\Foundation\Auth\User as Authenticatable;
use App\Permission;
class User extends Authenticatable
{
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
public function roles()
{
return $this->belongsToMany(\App\Role::class);
}
public function hasPermission(Permission $permission)
{
return $this->hasAnyRoles($permission->roles);
}
public function hasAnyRoles($roles)
{
if(is_array($roles) || is_object($roles)){
foreach($roles as $role){
return $roles->intersect($this->roles)->count();
}
}
return $this->roles->contains('name', $roles);
}
}
model Role
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Role extends Model
{
public function permissions()
{
return $this->belongsToMany(\App\Permission::class);
}
}
model Permission
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Permission extends Model
{
public function roles()
{
return $this->belongsToMany(\App\Role::class);
}
}
view home.blade.php
#extends('layouts.app')
#section('content')
<div class="container">
#forelse($posts as $post)
#can('view_post', $post)
<h2>{{$post->title}}</h2>
<p>{{$post->description}}</p><br>
<p>Autor: {{$post->user->name}}</p>
Editar
#endcan
<hr>
#empty
<p>No posts registered</p>
#endforelse
</div>
#endsection
view update-post.blade.php
#extends('layouts.app')
#section('content')
#can('edit_post', $post)
<div class="container">
<h2>{{$post->title}}</h2>
<p>{{$post->description}}</p><br>
<p>Autor: {{$post->user->name}}</p>
#endcan
#endsection
create a policy class:
php artisan make:policy PostPolicy --model=Post
than in your AuthServiceProvider register the policy:
protected $policies = [
Post::class => PostPolicy::class,
];
than in PostPolicy update method do this:
public function update(User $user, Post $post)
{
return $user->id === $post->user_id;
}
you made a mistake in your home controller.
if(Gate::denies('update-post', $post))
{
abort(403);
}
or you can do the same in a single line:
$this->authorize('update-post', $post);
or using can middleware:
if ($user->can('update-post', $post)) {
return view('update-post', compact('post'));
}

Categories