Tutorial Portal Berita - Level 3: Autentikasi & Otorisasi π
Selamat datang di Level 3! Setelah berhasil menyiapkan struktur data di Level 2, sekarang kita akan fokus pada implementasi sistem autentikasi dan otorisasi. Kita akan memanfaatkan fitur Laravolt secara penuh, termasuk kemampuannya untuk menyinkronkan permission kustom yang kita definisikan melalui Enum standar.
Tujuan Level 3:
- Memahami bagaimana Laravolt menangani autentikasi dasar.
- Menyiapkan sistem ACL (Access Control List) dengan peran: Admin, Writer, dan Member.
- Mendefinisikan hak akses (permissions) kustom aplikasi menggunakan file Enum
app/Enums/Permission.php
yang disediakan/diharapkan oleh Laravolt. - Membuat seeders untuk mengisi data awal peran, hak akses (yang disinkronkan melalui
laravolt:sync-permission
), dan pengguna default dengan perannya masing-masing. - Mengimplementasikan policies untuk model
Post
danComment
. - Mendaftarkan dan menggunakan policies dalam aplikasi.
Mari kita amankan aplikasi kita!
1. Autentikasi dengan Laravolt (Tinjauan Singkat)
Seperti yang telah kita ketahui, php artisan laravolt:install
telah menyiapkan infrastruktur autentikasi dasar. Ini mencakup rute, views, dan logika untuk login, registrasi, lupa password, dll.
2. Mendefinisikan Peran (Roles)
Peran utama dalam aplikasi kita adalah:
- admin: (nama di database:
admin
) - Writer: (nama di database:
Writer
) - Member: (nama di database:
Member
)
Peran Guest (Tamu) adalah pengguna anonim yang hak aksesnya diatur di level rute atau controller.
3. Mendefinisikan Hak Akses (Permissions) Kustom dengan app/Enums/Permission.php
Laravolt mempermudah kita dalam mendefinisikan dan mengelola hak akses.
-
Paket Enum Bawaan: Laravolt sudah menyertakan paket
bensampo/laravel-enum
(atau yang setara), jadi kita tidak perlu menginstalnya secara manual. -
File Enum Permission Kustom: Saat proses
php artisan laravolt:install
, Laravolt biasanya sudah mempublikasikan atau menyiapkan mekanisme untuk custom permissions, umumnya melalui fileapp/Enums/Permission.php
. File inilah yang akan dibaca oleh perintahlaravolt:sync-permission
untuk mengetahui permissions apa saja yang spesifik untuk aplikasi kita, di luar permissions sistem internal Laravolt.Pastikan file
app/Enums/Permission.php
ada. Jika tidak ada atau Anda ingin memodifikasinya, inilah tempat Anda mendefinisikan semua hak akses yang dibutuhkan aplikasi Anda. Gunakan definisi Enum yang telah Anda siapkan:// app/Enums/Permission.php <?php namespace App\Enums; use BenSampo\Enum\Enum; // Diasumsikan Laravolt menggunakan ini atau yang kompatibel final class Permission extends Enum { // Akses dasbor const DASHBOARD_VIEW = 'dashboard.view'; // Manajemen pos const POST_VIEW = 'post.view'; const POST_CREATE = 'post.create'; const POST_EDIT = 'post.edit'; const POST_DELETE = 'post.delete'; const POST_PUBLISH = 'post.publish'; // Manajemen topik const TOPIC_VIEW = 'topic.view'; const TOPIC_CREATE = 'topic.create'; const TOPIC_EDIT = 'topic.edit'; const TOPIC_DELETE = 'topic.delete'; // Manajemen komentar const COMMENT_VIEW = 'comment.view'; const COMMENT_CREATE = 'comment.create'; const COMMENT_EDIT = 'comment.edit'; const COMMENT_DELETE = 'comment.delete'; const COMMENT_MODERATE = 'comment.moderate'; // Manajemen pengguna const USER_VIEW = 'user.view'; const USER_CREATE = 'user.create'; const USER_EDIT = 'user.edit'; const USER_DELETE = 'user.delete'; }
Dengan mendefinisikan permissions di sini,
laravolt:sync-permission
akan secara otomatis memasukkannya ke dalam database.
4. Membuat Seeder untuk Peran dan Hak Akses (AclSeeder
)
Seeder ini akan membuat peran dan kemudian, setelah menjalankan sinkronisasi permission Laravolt, memberikan permissions yang relevan kepada setiap peran.
-
Buat
AclSeeder
:php artisan make:seeder AclSeeder
-
Isi
AclSeeder.php
(sesuai kerangka Anda, dengan penekanan pada alur Laravolt):<?php namespace Database\Seeders; use App\Enums\Permission as AppPermissionEnum; // Enum kustom kita use Illuminate\Database\Seeder; use Illuminate\Support\Facades\Artisan; use Laravolt\Platform\Models\Role; use Laravolt\Platform\Models\Permission as LaravoltPermission; class AclSeeder extends Seeder { public function run(): void { // Membuat Roles $adminRole = Role::firstOrCreate(['name' => 'admin']); $writerRole = Role::firstOrCreate(['name' => 'Writer']); $memberRole = Role::firstOrCreate(['name' => 'Member']); // Jalankan perintah Laravolt untuk menyinkronkan permissions. // Ini akan membaca permissions sistem Laravolt dan permissions dari app/Enums/Permission.php // lalu memasukkannya ke tabel `acl_permissions`. Artisan::call('laravolt:sync-permission'); // Memberikan semua izin kepada admin // Menggunakan wildcard '*' adalah cara singkat jika didukung sistem permission Laravolt (Spatie). // Kode Anda: $admin->syncPermission(['*'] + LaravoltPermission::all()->pluck('name')->toArray()); // Opsi lebih bersih jika '*' didukung: $adminRole->syncPermissions(['*']); // Opsi paling aman jika ingin semua yang ada di DB: $allDbPermissions = LaravoltPermission::all()->pluck('name')->toArray(); $adminRole->syncPermissions(['*'] + $allDbPermissions); // Memberikan izin tertentu kepada peran Penulis dari Enum kita $writerPermissions = [ AppPermissionEnum::DASHBOARD_VIEW, AppPermissionEnum::POST_VIEW, AppPermissionEnum::POST_CREATE, AppPermissionEnum::POST_EDIT, AppPermissionEnum::POST_DELETE, AppPermissionEnum::POST_PUBLISH, AppPermissionEnum::COMMENT_VIEW, AppPermissionEnum::COMMENT_CREATE, AppPermissionEnum::COMMENT_EDIT, AppPermissionEnum::COMMENT_DELETE, ]; $writerRole->syncPermissions($writerPermissions); // Langsung dari Enum, karena sync-permission sudah memastikan ada di DB // Memberikan izin tertentu kepada peran Anggota dari Enum kita $memberPermissions = [ AppPermissionEnum::COMMENT_VIEW, AppPermissionEnum::COMMENT_CREATE, AppPermissionEnum::COMMENT_EDIT, AppPermissionEnum::COMMENT_DELETE, AppPermissionEnum::POST_VIEW, ]; $memberRole->syncPermissions($memberPermissions); // Langsung dari Enum } }
Poin Penting:
Artisan::call('laravolt:sync-permission');
adalah kunci. Ini akan mengisi tabelpermissions
dengan semua permission yang diketahui Laravolt, termasuk yang dariapp/Enums/Permission.php
Anda.- Setelah sinkronisasi, kita bisa langsung menggunakan nilai dari
AppPermissionEnum
untuksyncPermissions
pada roles, karena kita berasumsi permissions tersebut sudah ada di database.
5. Membuat Seeder Pengguna Default (UserSeeder
)
Langkah ini tetap sama. Seeder ini membuat pengguna awal untuk setiap peran.
<?php
namespace Database\Seeders;
use App\Models\User;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Hash;
class UserSeeder extends Seeder
{
public function run(): void
{
Artisan::call('laravolt:admin Administrator [email protected] secret');
$writer = User::firstOrCreate(
['email' => '[email protected]'],
[
'name' => 'Content Writer',
'password' => Hash::make('secret'),
'status' => 'ACTIVE',
'email_verified_at' => now(),
]
);
$writer->assignRole('Writer');
$member = User::firstOrCreate(
['email' => '[email protected]'],
[
'name' => 'Regular Member',
'password' => Hash::make('password'),
'status' => 'ACTIVE',
'email_verified_at' => now(),
]
);
$member->assignRole('Member');
}
}
6. Memperbarui DatabaseSeeder.php
Pastikan urutan pemanggilan seeder benar.
// database/seeders/DatabaseSeeder.php
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
public function run(): void
{
$this->command->info('Memulai DatabaseSeeder utama...');
$this->call([
ContentSeeder::class, // Opsional, untuk data dummy dari Level 2
AclSeeder::class, // Roles dibuat, permissions disinkronkan & diassign
UserSeeder::class, // Users dibuat & roles diassign
]);
$this->command->info('Semua seeder utama telah dijalankan.');
}
}
7. Mengimplementasikan Policies
Policies adalah kelas yang mengatur logika otorisasi untuk tindakan pada model tertentu.
-
Buat File Policies:
php artisan make:policy PostPolicy --model=Post php artisan make:policy CommentPolicy --model=Comment # Opsional: php artisan make:policy TopicPolicy --model=Topic # Opsional: php artisan make:policy UserPolicy --model=User
-
Implementasikan
PostPolicy
(app/Policies/PostPolicy.php
):<?php namespace App\Policies; use App\Enums\Permission as AppPermissionEnum; // Ganti nama Enum Anda use App\Models\Post; use App\Models\User; class PostPolicy { /** * Perform pre-authorization checks. */ public function before(User $user, string $ability): bool|null { if ($user->hasRole('admin')) { // Pastikan nama role 'admin' konsisten return true; } return null; } public function viewAny(User $user): bool { return $user->can(AppPermissionEnum::POST_VIEW); } public function view(User $user, Post $post): bool { if ($user->can(AppPermissionEnum::POST_VIEW)) { if ($post->status === 'draft' && $post->created_by !== $user->id) { return false; // Hanya pemilik (atau admin via before()) yang bisa lihat draft orang lain } return true; } return false; } public function create(User $user): bool { return $user->can(AppPermissionEnum::POST_CREATE); } public function update(User $user, Post $post): bool { return $user->can(AppPermissionEnum::POST_EDIT) && $post->created_by === $user->id; } public function delete(User $user, Post $post): bool { return $user->can(AppPermissionEnum::POST_DELETE) && $post->created_by === $user->id; } public function publish(User $user, Post $post): bool { return $user->can(AppPermissionEnum::POST_PUBLISH); // Logika kepemilikan bisa ditambahkan jika perlu } public function restore(User $user, Post $post): bool { return $user->can(AppPermissionEnum::POST_EDIT) && $post->created_by === $user->id; // Contoh } public function forceDelete(User $user, Post $post): bool { return false; // Biasanya hanya admin (sudah dihandle oleh before()) } }
-
Implementasikan
CommentPolicy
(app/Policies/CommentPolicy.php
):<?php namespace App\Policies; use App\Enums\Permission as AppPermissionEnum; use App\Models\Comment; use App\Models\User; class CommentPolicy { public function before(User $user, string $ability): bool|null { // Admin dengan izin moderasi bisa melakukan banyak hal terkait komentar if ($user->hasRole('admin') && $user->can(AppPermissionEnum::COMMENT_MODERATE)) { return true; } return null; } public function viewAny(User $user): bool { return $user->can(AppPermissionEnum::COMMENT_VIEW); } public function view(User $user, Comment $comment): bool { return $user->can(AppPermissionEnum::COMMENT_VIEW); } public function create(User $user): bool { return $user->can(AppPermissionEnum::COMMENT_CREATE); } public function update(User $user, Comment $comment): bool { // Hanya pemilik komentar yang bisa update, atau moderator (via before()) return $user->can(AppPermissionEnum::COMMENT_EDIT) && $comment->created_by === $user->id; } public function delete(User $user, Comment $comment): bool { // Hanya pemilik komentar yang bisa delete, atau moderator (via before()) return $user->can(AppPermissionEnum::COMMENT_DELETE) && $comment->created_by === $user->id; } }
PENTING: Pastikan kolom
created_by
ada di modelPost
danComment
(dari Level 2, kita menggunakancreated_by
sebagai foreign key keusers.id
). Jika Anda menggunakanuser_id
, sesuaikan nama kolomnya di policy.
8. Mendaftarkan Policies
Daftarkan policies Anda di app/Providers/AuthServiceProvider.php
:
<?php
namespace App\Providers;
use App\Models\Comment;
use App\Models\Post;
use App\Policies\CommentPolicy;
use App\Policies\PostPolicy;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* The model to policy mappings for the application.
*
* @var array<class-string, class-string>
*/
protected $policies = [
Post::class => PostPolicy::class,
Comment::class => CommentPolicy::class,
];
/**
* Register any authentication / authorization services.
*/
public function boot(): void
{
$this->registerPolicies();
}
}
9. Menjalankan Migrasi dan Seeders
Sekarang, jalankan migrasi dan semua seeder Anda:
php artisan migrate:fresh --seed
Ini akan menghapus semua data lama, membuat ulang struktur tabel, dan mengisi data dari seeder.
Verifikasi:
- Login sebagai
[email protected]
(password:secret
) di panel admin Laravolt. - Login sebagai
[email protected]
(password:secret
). - Login sebagai
[email protected]
(password:password
). - Cek tabel database:
users
,acl_roles
,acl_permissions
,acl_permission_role
,acl_role_user
untuk memastikan data sudah terisi dengan benar.
10. Menggunakan Otorisasi dalam Aplikasi (Contoh)
Setelah policies terdaftar, Anda bisa menggunakannya:
Di Controller:
// Contoh di PostController untuk method store()
public function store(Request $request)
{
$this->authorize('create', Post::class); // Memanggil PostPolicy@create
// ... logika membuat post
}
// Contoh di PostController untuk method update()
public function update(Request $request, Post $post)
{
$this->authorize('update', $post); // Memanggil PostPolicy@update
// ... logika memperbarui post
}
Di Blade Views:
{{-- Tombol edit post hanya untuk yang berhak --}}
@can('update', $post)
<a href="{{ route('posts.edit', $post->slug) }}">Edit Post</a>
@endcan
{{-- Tombol buat post baru --}}
@can('create', App\Models\Post::class)
<a href="{{ route('posts.create') }}">Buat Post Baru</a>
@endcan
{{-- Berdasarkan permission langsung --}}
@if(auth()->check() && auth()->user()->can(App\Enums\Permission::COMMENT_MODERATE))
<a href="#">Panel Moderasi Komentar</a>
@endif
{{-- Mengecek role --}}
@if(auth()->check() && auth()->user()->hasRole('Writer'))
<p>Selamat datang, Penulis!</p>
@endif
Selamat! π Anda telah berhasil menyelesaikan Level 3. Aplikasi Anda kini memiliki sistem autentikasi yang berfungsi dan dasar-dasar otorisasi melalui peran, hak akses, dan policies. Pengguna default untuk setiap peran juga sudah siap digunakan.
Di Level 4, kita akan mulai membangun fungsionalitas di Panel Admin, dimulai dengan Manajemen Topik. Kita akan menggunakan fitur AutoCRUD dari Laravolt untuk mempercepat pengembangan. Sampai jumpa di level berikutnya!