Laravel - Authorization
Authorization
Implementasi Relationship Antara Model User dan BlogPost
Saat ini di table blog_post kita sudah memiliki column
user_id, namun model yang kita buat sebelumnya belum di implementasikan relationship. Sekarang kita akan
mengimplementasikan relationship di model User dan BlogPost. User has-many
BlogPost dan BlogPost belongs-to User. Implemetasi nya nanti seperti sintak
dibawah ini buka file app/Models/BlogPost.php, tambahkan code dibawah ini
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class BlogPost extends Model
{
use SoftDeletes;
protected $fillable = ['title', 'content'];
use HasFactory;
public function comments()
{
return $this->hasMany('App\Models\Comments');
}
public function user()
{
return $this->belongsTo('App\Models\User');
}
protected static function booted()
{
static::deleting(function (BlogPost $post){
$post->comments()->delete();
});
static::restoring(function (BlogPost $post){
$post->comments()->restore();
});
}
}
Buka file app/Models/User.php, tambahkan code dibawah ini.
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
public function blogPosts(){
return $this->hasMany(App\Models\BlogPost::class);
}
Buka file resources/views/posts/partials/post.blade.php,
tambahkan code dibawah ini.
<h3>
<a href="{{ route('posts.show', ['post'=> $post->id])}}">
{{ $post['title'] }}
</a>
</h3>
<p class="text-muted">
Ditambahkan pada {{ $post->created_at->diffForHumans() }}
oleh {{ $post->user->name }}
</p>
@if($post->comments->count())
<p> {{ $post->comments->count() }} Komentar </p>
@else
<p> Belum ada komentar </p>
@endif
Buka file resources/views/posts/show.blade.php, tambahkan
code dibawah ini.
<div class="row">
<div class="col-md-12">
<p>
<h2> {{ $post['title'] }}</h2>
{{ $post['content'] }}
@if (now()->diffInMinutes($post['created_at']) < 5)
<b>(new)</b>
@endif
</p>
<p class="text-muted">
Ditambahkan pada {{ $post->created_at->diffForHumans() }}
oleh {{ $post->user->name }}
</p>
</div>
<div class="col-md-12">
<hr>
<h5> Comments </h5>
@forelse ($post->comments as $key => $comment)
<div class="row">
<div class="col-md-8">
<p> {{ $comment->content }}
<br><small> {{ $comment->created_at }}</small>
</p>
</div>
</div>
@empty
No Comments Found !
@endforelse
</div>
</div>
Lalu buka tampilan blog post dah show blog post maka akan
sperti dibawah ini
Implementasi Update Post Hanya Oleh User Yang Meng-Create Post Tersebut
Menggunakan Laravel Gate
KIta akan meng-authorize user yang melakukan update post.
Misalkan BlogPost A di create oleh User A, maka hanya User A yang bisa
melakukan update BlogPost A. Buka file
app/Providers/AuthServiceProvider.php, tambahkan code dibawah ini.
public function boot()
{
$this->registerPolicies();
Gate::define('update-post', function($user, $blogPost){
return $user->id == $blogPost->user_id;
});
}
Buka file App/Http/Controller/PostsController.php tambahkan
sintak dibawah ini
use App\Http\Requests\StorePost as RequestsStorePost;
use App\Models\BlogPost;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(RequestsStorePost $request, $id)
{
$post = BlogPost::findOrFail($id);
//Implementasi Authorazation
if(Gate::denies('update-post', $post)){
abort('403', 'Anda tidak diizinkan untuk melakukan update post ini!')
}
$validated = $request->validated();
$post->fill($validated);
$post->save();
$request->session()->flash('status', 'Blog Post Telah Berhasil diupdate !');
return redirect()->route('posts.show', ['post'=> $post->id]);
}
Sekarang coba login dengan user1@mail.com (atau user lain),
kemudian edit salah satu blog post yang di create oleh user lain, maka ketika
tombol update di klik akan muncul error 403 forbidden.
Sekarang kita implementasikan juga authorization untuk
function edit(). Buka file app/Http/Controllers/PostsController.php, tambahkan
code dibawah ini.
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
$post = BlogPost::findOrFail($id);
//Implementasi Authorazation
if(Gate::denies('update-post', $post)){
abort('403', 'Anda tidak diizinkan untuk melakukan update post ini!');
}
return view('posts.edit', ['post'=> $post]);
}
Cara Lain Untuk Meng-Authorize Menggunakan authorize() method
Sekarang kita akan mengimplementasikan authorization untuk
delete function. Buka file app/Providers/AuthServiceProvider.php, tambahkan
code dibawah ini
public function boot()
{
$this->registerPolicies();
Gate::define('update-post', function($user, $blogPost) {
return $user->id == $blogPost->user_id;
});
Gate::define('delete-post', function($user, $blogPost) {
return $user->id == $blogPost->user_id;
});
}
Buka file app/Http/Controllers/PostsController.php,
tambahkan code dibawah ini.
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
$post = BlogPost::findOrFail($id);
$this->authorize('delete-post', $post);
$post->delete();
session()->flash('status', 'Blog Post Telah Berhasil dihapus !');
return redirect()->route('posts.index');
}
Silahkan coba delete Post yang di create oleh user lain Untuk
mengetahui lebih detail mengenai Gate di laravel, bisa kunjungi halaman ini Laravel Gates
Implementasi SuperUser Dan Override Permission
Kita akan mengimplementasikan superuser, yang mana user ini
bisa override permission atau atuthorization yang ada. Untuk implementasi
superuser kita akan menambahkan column is_admin ketable users. Buat migration
baru dengan menjalankan command dibawah ini.
php
artisan make:migration AddIsAdminToUsersTable
Buka file
database/migrations/[yyyy_mm_dd_time]_add_is_admin_to_users_table.php, dan
tambahkan code dibawah ini.
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddIsAdminToUsersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->boolean('is_admin')->default(false);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('is_admin');
});
}
}
Jalan migration command.
php
artisan migrate
Buka file database/factories/UserFactory.php, tambahkan code
dibawah ini.
<?php
namespace Database\Factories;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
class UserFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = User::class;
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
'name' => $this->faker->name,
'email' => $this->faker->unique()->safeEmail,
'email_verified_at' => now(),
'password' => bcrypt('password'), // password
'remember_token' => Str::random(10),
'is_admin' =>false
];
}
public function userSatu()
{
return $this->state([
'name' => "User 1",
'email' => "user1@mail.com",
'email_verified_at' => now(),
'password' => bcrypt('password'),
'remember_token' => Str::random(10),
'is_admin' =>true
]);
}
}
Kita melakukan settingan bahwa user1@mail.com adalah
superuser. 7. Jalankan command db refresh dan seed.
php
artisan migrate:refresh –seed
Buka file app/Providers/AuthServiceProvider.php, dan
tambahkan code dibawah ini
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
Gate::define('update-post', function($user, $blogPost) {
return $user->id == $blogPost->user_id;
});
Gate::define('delete-post', function($user, $blogPost) {
return $user->id == $blogPost->user_id;
});
Gate::before(function ($user, $ability){
if($user->is_admin){
return true;
}
});
}
Sekarang coba login dengan user1 @mail.com kemudian edit
atau delete blog post yang di create oleh lain.
Code diatas digunakan untuk override semua Gate atau
permission. Untuk override Gate tertentu, kita bisa mengubahnya menjadi seperti
ini.
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
Gate::define('update-post', function($user, $blogPost) {
return $user->id == $blogPost->user_id;
});
Gate::define('delete-post', function($user, $blogPost) {
return $user->id == $blogPost->user_id;
});
Gate::before(function ($user, $ability){
// Untuk ovveride semua permission
// if($user->is_admin){
// return true;
// }
// Untuk override Gate Update-post saja
if($user->is_admin && in_array(($ability, ['update-post']))){
return true;
}
});
}
Buka file app/Http/Controllers/PostsController.php, dan
tambahkan code dibawah ini.
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
$post = BlogPost::findOrFail($id);
// $this->authorize('delete-post', $post);
if(Gate::denies('delete-post', $post)){
abort('403', 'Anda tidak diizinkan untuk melakukan update post ini!');
}
$post->delete();
session()->flash('status', 'Blog Post Telah Berhasil dihapus !');
return redirect()->route('posts.index');
}
Sekarang super user hanya bisa melakukan update saja tidak
bisa melakukan delete
Implementasi Authorization Menggunakan Laravel Policy
Salah satu kekurang authorization menggunakan Gate adalah
code nya harus ditulis dalam satu file yakni app/Providers/AuthServiceProvider.php.
Ketika aplikasi menjadi besar, maka satu
file tersebut akan menjadi susah untuk di manage. Salah satu solusi dan best practice
di laravel untuk menghandle masalah ini adalah dengan menggunakan Laravel
Policy Untuk lebih detail, bisa kunjungi halaman ini Laravel
Policies Kita akan implementasi Policy untuk model BlogPost. Create policy
file dengan menjalankan command di bawah ini
php
artisan make:policy BlogPostPolicy --model=BlogPost
Command di atas akan generate file
app/Policies/BlogPostPolicy.php. Sekarang kita akan memindahkan logic
permission untuk BlogPost dari file app/Providers/AuthServiceProvider.php ke
file app/Policies/BlogPostPolicy.php. Buka file
app/Policies/BlogPostPolicy.php, dan tambahkan code dibawah ini
/**
* Determine whether the user can update the model.
*
* @param \App\Models\User $user
* @param \App\Models\BlogPost $blogPost
* @return mixed
*/
public function update(User $user, BlogPost $blogPost)
{
return $user->id == $blogPost->user_id;
}
/**
* Determine whether the user can delete the model.
*
* @param \App\Models\User $user
* @param \App\Models\BlogPost $blogPost
* @return mixed
*/
public function delete(User $user, BlogPost $blogPost)
{
return $user->id == $blogPost->user_id;
}
Buka file app/Providers/AuthServiceProvider.php, kemudian
ubah code nya menjadi seperti ini
public function boot()
{
$this->registerPolicies();
Gate::define('posts.update', 'App\Policies\BlogPostPolicy@update');
Gate::define('posts.delete', 'App\Policies\BlogPostPolicy@delete');
}
.Pada file controller
app/Http/Controllers/PostsController.php, ubah code dibawah ini menjadi
-
Gate::denies('update-post', $post) → Gate::denies('posts.update',
$post)
-
Gate::denies('delete-post', $post) →
Gate::denies('posts.delete', $post)
Coba untuk melakukan edit atau delete post, maka permission
nya tetap akan berjalan dengan baik. untuk menyederhanakan code pada file
app/Providers/AuthServiceProvider.php, maka kita bisa mengubah kode menjadi
seperti dibawah ini
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
Gate::resource('posts', 'App\Policies\BlogPostPolicy');
}
Implementasi Authorization Pada View
Saat ini yang kita authorize adalah sisi controller.
Sekarang kita akan implementasi authorization di sisi view. Kita akan meng-hide button Edit dan Delete di
halaman http://localhost:8000/posts. Buka file
app/Providers/AuthServiceProvider.php, tambahkan code di bawah ini
**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
'App\Models\BlogPost' => 'App\Policies\BlogPostPolicy',
// 'App\Models\Model' => 'App\Policies\ModelPolicy',
];
Buka file resources/views/posts/partials/post.blade.php,
ubah code nya menjadi seperti ini
<p>
<h3>
<a href="{{ route('posts.show', ['post'=> $post->id])}}">
{{ $post['title'] }}
</a>
</h3>
<p class="text-muted">
Ditambahkan pada {{ $post->created_at->diffForHumans() }}
oleh {{ $post->user->name }}
</p>
@if($post->comments->count())
<p> {{ $post->comments->count() }} Komentar </p>
@else
<p> Belum ada komentar </p>
@endif
<div class="mb-3">
<form action="{{ route('posts.destroy', ['post'=> $post->id] ) }}" method="post">
@csrf
@method('DELETE')
@can('update', $post)
<a href="{{ route('posts.edit', ['post'=> $post->id]) }}" class="btn btn-primary"> Edit </a>
@endcan
@can('delete', $post)
<input type="submit" value="Delete!" class="btn btn-primary">
@endcan
</form>
</div>
</p>
Sekarang coba buka halaman /posts, maka
ada beberapa post yang tombol Edit dan Delete nya tidak muncul.