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'));
}
Related
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.
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
I m new to laravel i want to display all the posts on a view with the name of the user who created the post.. I m trying to do that but I m getting the error of "Trying to get property 'name' of non-object " ..
Any kind of help will be appreciated Thank you
User.php
class User extends Authenticatable
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
public function Posts(){
return $this->hasMany('\App\Post');
}
protected $fillable = [
'name', 'email', 'password','verifyToken','dob',
];
Post.php
class Post extends Model
{
public function Users(){
return $this->belongsTo('\App\User');
}
protected $fillable = [
'title','body','thumbnail','user_id',
];
PostController.php
class PostController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
$posts=Post::paginate(5);
return view('\home2',compact('posts'));
}
web.php
Route::get('/home2', 'PostController#index')->name('home');
Home2.blade.php
#foreach($posts as $post)
<div class="text-center">
<h1 align="center">{{$post->title }}</h1>
<h4 align="center">{{$post->body}}</h4>
<p>{{ $post->created_at->diffForHumans() }}</p>
<p> {{$post->Users->name}}</p>
Eager load the users:
class PostController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
$posts=Post::with('users')->paginate(5);
return view('\home2',compact('posts'));
}
Then on the blade:
<p> {{$post->user->name}}</p>
Update your relationship classes
from:
public function Posts(){
return $this->hasMany('\App\Post');
}
to
public function posts(){
return $this->hasMany(Post::class);
}
Import at the top use App\Post;
and
public function user(){
return $this->belongsTo(User::class);
}
Import at the top use App\User;
Your models should look like this.
User.php
public function posts(){
return $this->hasMany('App\Post');
}
Post.php
public function user(){
return $this->belongsTo('App\User','user_id');
}
then your blade code should be like this,
<p> {{$post->user->name}}</p>
I try to get the number of logged user posts count so it shows to logged user for example how many post or comment they published so far:
What I have so far is author_id column in posts table which will refer to user id
This is my Post model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
public function author() {
return $this->belongsTo('App\User');
}
}
and this is my User model:
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable;
protected $table = 'users';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'role_id',
'email',
'password',
'name',
'avatar',
'remember_token',
'created_at',
'updated_at'
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
public function posts()
{
return $this->hasMany('Post', 'author_id');
}
}
This is my PostController
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Post;
use App\Auth;
use App\User;
class PostController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
$posts = Post::orderBy('created_at', 'desc')->paginate(15);
$countTodayOrders = Post::whereRaw('Date(created_at) = CURDATE()')->count();
return view('theme.index', compact('posts', 'countTodayOrders'));
}
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function single($slug) {
$post = Post::where('slug', '=', $slug)->first();
$countTodayOrders = Post::whereRaw('Date(created_at) = CURDATE()')->count();
return view('theme.single', compact('post', 'countTodayOrders'));
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(Request $request)
{
//
}
}
Anyone knows what is my mistake and how to fix it?
Well i figure it out :)
i ignored all model roles and directly used controller inserted data from Database and compare it with user id if it's true (same) will return the number.
here is the code for you if you need it PostController:
public function index()
{
$posts = Post::orderBy('created_at', 'desc')->paginate(15);
$countTodayOrders = Post::whereRaw('Date(created_at) = CURDATE()')->count();
$postscount = DB::table('posts')
->whereExists(function ($query) {
$query->select(DB::raw(1))
->from('users')
->whereRaw('author_id = id');
})
->get();
return view('theme.index', compact('posts', 'countTodayOrders', 'postscount'));
}
I was following a tutorial on laracast about easy auth (Easy Auth), but there were some gaps on the video, i had to declare
use Auth;
to be able to get the current user, however, when i save the article i get this error
FatalErrorException in ArticleController.php line 42:
Call to undefined method Illuminate\Database\Eloquent\Collection::save()
where the corresponding code in my ArticleController is
public function store(ArticleRequest $request)
{
$article = new Article($request->all());
Auth::user()->articles->save($article);
return redirect('blog');
}
My Article model:
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
use Carbon\Carbon;
class Article extends Model {
protected $fillable = [
'title',
'body',
'published_at',
'user_id'
];
protected $dates = ['published_at'];
public function scopePublished ($query)
{
$query->where('published_at', '<=', Carbon::now());
}
public function scopeUnpublished ($query)
{
$query->where('published_at', '>', Carbon::now());
}
public function setPublishedAtAttribute($date)
{
$this->attributes['published_at'] = Carbon::parse($date);
}
public function user()
{
return $this-> belongsTo('App\User');
}
}
My User model
<?php namespace App;
use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
class User extends Model implements AuthenticatableContract, CanResetPasswordContract {
use Authenticatable, CanResetPassword;
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'users';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = ['name', 'email', 'password'];
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = ['password', 'remember_token'];
public function articles()
{
return $this-> hasMany('App\Article');
}
}
try with this
Auth::user()->articles()->save($article);
store action
public function store(ArticleRequest $request)
{
$article = new Article($request->all());
Auth::user()->articles()->save($article);
return redirect('blog');
}