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]);

}

 Lalu coba pada browser


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.



Previous Post
No Comment
Add Comment
comment url