5
Manajemen User, Role, dan Permission
Badaso menyimpan model aksesnya di tiga tabel utama, users, roles, dan permissions, lalu menghubungkannya lewat dua tabel pivot: user_roles dan role_permissions. Kelima tabel itu ditangani oleh lima controller terpisah yang masing-masing mengekspos pola browse, read, add, edit, delete, dan deleteMultiple. Middleware config('badaso.middleware.check_permission') menjaga setiap route dengan sebuah permission key, misalnya browse_users untuk BadasoUserController@browse src/Routes/api.php:95-1341. Mekanisme middleware itu sendiri dibahas di Autentikasi dan Otorisasi, sedangkan jejak perubahan tiap aksi dibahas di Media, Notifikasi, dan Activity Log.
Bentuk relasi antar tabel
Relasi Eloquent sejalan dengan skema migrasi. User::roles adalah belongsToMany ke Role melalui tabel user_roles src/Models/User.php:88-912, dan Role::permissions adalah belongsToMany ke Permission melalui role_permissions src/Models/Role.php:41-443. Kedua tabel pivot menyimpan foreign key user_id, role_id, permission_id beserta timestamps, dan keduanya memakai onDelete('cascade') sehingga menghapus user atau role ikut membersihkan baris pivotnya src/Migrations/2020_11_18_015852_create_user_roles.php:17-244 src/Migrations/2020_11_18_020028_create_role_permissions.php:17-245.
erDiagram
users ||--o{ user_roles : "pivot"
roles ||--o{ user_roles : "pivot"
roles ||--o{ role_permissions : "pivot"
permissions ||--o{ role_permissions : "pivot"
users ||--o{ user_verifications : "token"
users {
bigint id PK
string name
string username
string email
string password
string avatar
datetime email_verified_at
}
roles {
bigint id PK
string name
string display_name
string description
}
permissions {
bigint id PK
string key
string table_name
string description
string always_allow
string is_public
string roles_can_see_all_data
string field_identify_related_user
}
user_roles {
bigint id PK
bigint user_id FK
bigint role_id FK
}
role_permissions {
bigint id PK
bigint role_id FK
bigint permission_id FK
}
user_verifications {
bigint id PK
bigint user_id FK
string verification_token
datetime expired_at
integer count_incorrect
}
%% fig: Relasi tabel user, role, dan permission
%% desc: Dua tabel pivot menghubungkan users ke roles dan roles ke permissions; user_verifications menyimpan token verifikasi per user.Tabel user_verifications melengkapi skema ini dengan kolom verification_token yang unique, expired_at, dan count_incorrect src/Migrations/2021_03_09_064445_create_user_verifications.php:16-236. Model UserVerification memetakan kolom itu sebagai fillable dan menyediakan relasi belongsTo(User::class) src/Models/UserVerification.php:25-357.
Endpoint per resource
Kelima grup route memakai awalan yang sama dengan nama tabelnya, dan hampir semua handler memiliki permission key sendiri. Perhatikan bahwa grup user-roles dan role-permissions memakai key gabungan, bukan key per method src/Routes/api.php:95-1341.
| Method | Path | Handler | permission key |
|---|---|---|---|
| GET | /users/ | BadasoUserController@browse | browse_users |
| GET | /users/read | BadasoUserController@read | read_users |
| PUT | /users/edit | BadasoUserController@edit | edit_users |
| POST | /users/add | BadasoUserController@add | add_users |
| DELETE | /users/delete | BadasoUserController@delete | delete_users |
| DELETE | /users/delete-multiple | BadasoUserController@deleteMultiple | delete_users |
| GET | /roles/ | BadasoRoleController@browse | browse_roles |
| PUT | /roles/edit | BadasoRoleController@edit | edit_roles |
| DELETE | /roles/delete-multiple | BadasoRoleController@deleteMultiple | delete_roles |
| GET | /permissions/ | BadasoPermissionController@browse | browse_permissions |
| PUT | /permissions/edit | BadasoPermissionController@edit | edit_permissions |
| DELETE | /permissions/delete-multiple | BadasoPermissionController@deleteMultiple | delete_permissions |
| GET | /user-roles/ | BadasoUserRoleController@browseByUser | browse_user_role |
| GET | /user-roles/all | BadasoUserRoleController@browse | browse_user_role |
| GET | /user-roles/all-role | BadasoUserRoleController@browseAllRole | browse_user_role |
| POST | /user-roles/add-edit | BadasoUserRoleController@addOrEdit | add_or_edit_user_role |
| GET | /role-permissions/ | BadasoRolePermissionController@browseByRole | browse_role_permission |
| GET | /role-permissions/all-permission | BadasoRolePermissionController@browseAllPermission | browse_role_permission |
| POST | /role-permissions/add-edit | BadasoRolePermissionController@addOrEdit | add_or_edit_role_permission |
Sinkronisasi role milik user
BadasoUserRoleController::addOrEdit menerima user_id dan array roles, lalu menyamakan isi pivot dengan daftar itu: setiap role yang dikirim disimpan lewat UserRole::firstOrCreate, dan baris yang tidak lagi ada di daftar dihapus lewat whereNotIn('role_id', $stored_roles) src/Controllers/BadasoUserRoleController.php:82-1228. Model UserRole menyimpan user_id dan role_id sebagai fillable dan menyediakan relasi role() serta user() untuk memuat keduanya src/Models/UserRole.php:25-389.
graph TD
A["POST /user-roles/add-edit"] --> B["validasi user_id"]
B --> C["User::find"]
C --> D["loop roles"]
D --> E["Role::find"]
E --> F["UserRole::firstOrCreate"]
F --> G["stored_roles[]"]
G --> H["whereNotIn role_id"]
H --> I["UserRole::find->delete"]
C --> J["activity User created or updated"]
style A fill:#f59e0b,color:#000
style F fill:#2d7d46,color:#fff
style G fill:#8b5cf6,color:#fff
style I fill:#dc2626,color:#fff
style J fill:#336791,color:#fff
%% fig: Alur addOrEdit pada user_roles
%% desc: Role yang dikirim disimpan dengan firstOrCreate, lalu baris pivot yang tidak ada di daftar dihapus.⚠️ Handler ini bersifat mengganti, bukan menambah. Bila
rolesdikirim kosong,$stored_rolestetap kosong danwhereNotInmencocokkan semua baris milik user itu, sehingga seluruh role user tersebut dicabut src/Controllers/BadasoUserRoleController.php:82-1228. Handler juga tidak membungkus prosesnya denganDB::beginTransaction, berbeda dari controller user, role, dan permission.
Untuk keperluan tampilan, browseAllRole tidak memakai relasi Eloquent melainkan query SQL mentah yang menyusun tabel roles dengan LEFT JOIN ke user_roles dan menambahkan kolom selected bernilai 1 atau 0 untuk user yang sedang dibuka src/Controllers/BadasoUserRoleController.php:54-8010. Nama tabel di kedua sisi query memakai config('badaso.database.prefix'), jadi awalan tabel harus konsisten dengan konfigurasi.
Sinkronisasi permission milik role
BadasoRolePermissionController::addOrEdit adalah padanan langsung dari handler sebelumnya pada pivot role_permissions: permissions wajib diisi, tiap permission disimpan lewat RolePermission::firstOrCreate, dan baris yang tidak lagi dikirim dihapus dengan whereNotIn('permission_id', $stored_permissions) src/Controllers/BadasoRolePermissionController.php:78-12111. Model RolePermission memakai fillable berisi id, role_id, dan permission_id, dengan relasi role() dan permission() src/Models/RolePermission.php:25-3912.
⚠️ Berbeda dari
BadasoUserRoleController@addOrEdit, handler ini memvalidasipermissionssebagairequired. Request tanpa field itu gagal validasi, bukan diam-diam mengosongkan permission role src/Controllers/BadasoRolePermissionController.php:78-12111.
Daftar permission untuk satu role diambil browseAllPermission dengan pola query yang sama seperti browseAllRole, hanya saja tabel yang di-join adalah permissions dan role_permissions src/Controllers/BadasoRolePermissionController.php:50-7613.
Permission yang lahir dari nama tabel
Selain dibuat manual lewat BadasoPermissionController@add, permission dibuat otomatis oleh Permission::generateFor. Static method itu hanya bekerja bila belum ada permission dengan table_name tersebut, lalu membuat lima permission browse_, read_, edit_, add_, dan delete_ yang digabung dengan nama tabel, ditambah maintenance_ bila argumen $is_maintenance benar. Setiap permission baru langsung dihubungkan ke role administrator yang dicari dengan firstOrFail src/Models/Permission.php:27-5714.
graph LR
S["Permission::generateFor"] --> Q{"permission untuk table_name sudah ada?"}
Q -- belum --> P["firstOrCreate browse_ read_ edit_ add_ delete_"]
P --> M["maintenance_ bila is_maintenance"]
M --> R["Role::where name administrator"]
R --> RP["RolePermission role_id + permission_id"]
Q -- sudah --> X["tidak ada perubahan"]
style S fill:#f59e0b,color:#000
style P fill:#2d7d46,color:#fff
style RP fill:#336791,color:#fff
style X fill:#8b5cf6,color:#fff
%% fig: Pembuatan permission otomatis per tabel
%% desc: generateFor membuat lima permission standar (plus maintenance) dan mengaitkannya ke role administrator.Kebalikannya, Permission::removeFrom menghapus semua permission yang table_name-nya cocok beserta baris RolePermission terkait src/Models/Permission.php:59-6515. generateForTableCRUD menyapu seluruh DataType dan mengisi roles_can_see_all_data dengan ["administrator"] serta field_identify_related_user dengan user_id bila kedua kolom masih null src/Models/Permission.php:101-11416. Pemanggilan generateFor dari alur CRUD dibahas di CRUD Generator dan Content Manager.
Pengelolaan user
BadasoUserController@add memvalidasi email, name, username, phone, dan gender sebagai wajib, dengan username dibatasi alpha_num dan harus unik. Password selalu di-hash memakai Hash::make, dan email_verified_at hanya diisi bila request mengirim email_verified src/Controllers/BadasoUserController.php:104-14817. Pada edit, password hanya ditimpa bila field password benar-benar berisi nilai src/Controllers/BadasoUserController.php:75-8018. Kolom yang boleh diisi massal didefinisikan di $fillable model User src/Models/User.php:34-4519, sementara password dan remember_token disembunyikan dari serialisasi lewat $hidden src/Models/User.php:52-5520.
Saat menghapus, BadasoUserController@delete memanggil handleDeleteFile($user->avatar) dari trait FileHandler sebelum baris user dihapus, sehingga berkas avatar tidak tertinggal src/Controllers/BadasoUserController.php:150-18021. deleteMultiple mengulang langkah yang sama untuk setiap id pada parameter ids yang dipisah koma src/Controllers/BadasoUserController.php:150-18021.
⚠️
deleteMultiplemencatat activity memakai variabel$userhasil iterasi terakhir, bukan kumpulan nama yang sudah dihimpun di$user_name, sehingga log hanya mewakili satu user src/Controllers/BadasoUserController.php:150-18021.
Nama sebagai kunci unik
Role dan permission memakai nama sebagai identitas. Validasi edit pada role menolak name yang sudah dipakai role lain lewat aturan unique dengan pengecualian id yang sedang diedit, dan menyimpan name, display_name, serta description src/Controllers/BadasoRoleController.php:43-8222. Pada permission, key menjadi kolom unik, sedangkan always_allow, is_public, table_name, roles_can_see_all_data, dan field_identify_related_user melengkapi aturan aksesnya src/Controllers/BadasoPermissionController.php:43-8323.
⚠️ Mengubah
namesebuah role memutus asumsi kode lain yang mencari role berdasarkan nama, sepertiRole::where('name', 'administrator')diPermission::generateFor. Ganti nama role bawaan hanya setelah memastikan tidak ada pencarian berbasis nama yang tertinggal src/Models/Permission.php:27-5714.
Ringkasan berkas
| Berkas | Peran |
|---|---|
BadasoUserController.php | CRUD user, hash password, penghapusan avatar |
BadasoRoleController.php | CRUD role dengan name unik |
BadasoPermissionController.php | CRUD permission dengan key unik |
BadasoUserRoleController.php | Sinkronisasi pivot user_roles, daftar role per user |
BadasoRolePermissionController.php | Sinkronisasi pivot role_permissions, daftar permission per role |
User.php, Role.php, Permission.php | Relasi belongsToMany dan pembuatan permission otomatis |
UserRole.php, RolePermission.php, UserVerification.php | Model pivot dan token verifikasi |