Policies & Authorization — Laravel

Autentikasi (siapa kamu) vs Autorisasi (apa yang boleh kamu lakukan) adalah dua hal berbeda. Laravel punya 2 mekanisme utama untuk autorisasi: Gates (simple…

Autentikasi menjawab "kamu siapa", otorisasi menjawab "boleh gak kamu melakukan ini pada data ini". Di Laravel, aturan otorisasi untuk satu model ditulis di satu class bernama Policy. Bayangin kartu kamar hotel: resepsionis sudah tahu kamu siapa (autentikasi), tapi kartumu cuma membuka kamar 207 milikmu. Petugas kebersihan boleh masuk kamar mana pun untuk bersih-bersih, tapi gak boleh mengubah barangmu. Manajer punya kartu master yang membuka semua pintu.

🧪 Tebakan cepat: tombol Edit di ulasan Budi disembunyikan dari Ani dengan @can. Ani mengirim PUT /reviews/7 langsung lewat curl, dan controller-nya gak memanggil Gate::authorize(). Apa yang terjadi: 403, 404, atau ulasan Budi berhasil diubah Ani? Tebak dulu, jawabannya ada di bagian "@can cuma menyembunyikan tombol".

Aturannya: ulasan buku di Perpustakaan

Anggota boleh menulis ulasan buku (model Review, tabel reviews dengan kolom user_id). Aturannya:

Siapa yang login sudah diurus middleware auth (lesson Middleware): tamu yang membuka halaman ber-auth diarahkan ke /login. Policy bekerja sesudahnya. Konsep role dan permission secara umum ada di lesson Role-based Access Control (RBAC) dan RBAC vs ABAC: Implementasi (track Auth).

Policy pertamamu

php artisan make:policy ReviewPolicy --model=Review
<?php
// app/Policies/ReviewPolicy.php
namespace App\Policies;

use App\Models\Review;
use App\Models\User;

class ReviewPolicy
{
    public function before(User $user, string $ability): ?bool
    {
        if ($user->is_super_admin) {
            return true; // kartu master: boleh semua
        }

        return null; // lanjut ke method aksinya
    }

    public function viewAny(?User $user): bool
    {
        return true; // tamu juga boleh membaca daftar
    }

    public function update(User $user, Review $review): bool
    {
        return $user->id === $review->user_id;
    }

    public function delete(User $user, Review $review): bool
    {
        return $user->id === $review->user_id || $user->is_admin;
    }
}

Begini hasilnya untuk ulasan #7 milik Budi (id 1):

User before() update delete
Budi, penulisnya null 1 === 1, boleh boleh
Ani (id 2), anggota lain null 2 === 1, ditolak ditolak
Rina, pustakawan null ditolak boleh, karena is_admin
Pak Dodi, kepala true boleh, update() gak dipanggil boleh
Tamu gak dipanggil ditolak ditolak

Tiga tempat memakai policy

// 1. Di controller: lempar 403 kalau gak boleh
public function update(UpdateReviewRequest $request, Review $review)
{
    Gate::authorize('update', $review); // memanggil ReviewPolicy::update()

    $review->update($request->validated());

    return redirect()->route('reviews.index');
}
{{-- 2. Di Blade: tampilkan tombol cuma kalau boleh --}}
@can('update', $review)
    <a href="{{ route('reviews.edit', $review) }}">Edit</a>
@endcan
// 3. Di kode biasa: dapat true atau false, tanpa melempar apa-apa
if (Gate::allows('delete', $review)) {
    // ...
}

@can cuma menyembunyikan tombol

@can mengatur apa yang terlihat. Request tetap bisa dikirim tanpa tombol. Ani sudah login, jadi dia punya cookie session dan token CSRF sendiri, dan bisa mengirim PUT lewat curl, Postman, atau devtools browser. Kalau controller gak memanggil Gate::authorize(), PUT /reviews/7 dari Ani diterima, dan ulasan Budi berhasil diubah Ani. Itu jawaban tebakannya.

Jadi selalu cek di server (controller, route, atau FormRequest authorize()), dan pakai @can cuma supaya tampilannya rapi.

Gate untuk aturan tanpa model

Gak semua aturan menyangkut satu record. "Cuma pustakawan yang boleh membuka laporan bulanan" ditulis sebagai Gate di AppServiceProvider::boot():

Gate::define('view-reports', fn (User $user) => $user->is_admin);

// lalu di mana saja
Gate::authorize('view-reports');

Aturan tentang satu model (ulasan, peminjaman): Policy. Aturan tentang fitur: Gate.

⚠️ Jebakan yang sering ketemu

TL;DR: Policy berisi aturan "boleh gak" untuk satu model: php artisan make:policy ReviewPolicy --model=Review, satu method per aksi yang mengembalikan true atau false, biasanya dengan $user->id === $review->user_id. before() jalan duluan: true boleh semua, false tolak semua, null lanjut. Pakai Gate::authorize('update', $review) atau $this->authorize(...) (butuh trait AuthorizesRequests) di server untuk menjawab 403 | This action is unauthorized., @can di Blade untuk menyembunyikan tombol, dan Gate::allows() untuk sekadar bertanya. @can saja gak melindungi apa-apa: request langsung tetap lolos kalau server gak mengecek.

Yang akan kamu pelajari