update hak akses role: view-point-audit terkunci 'Khusus Admin' (badge + kunci di UI, proteksi di backend update()) + test

This commit is contained in:
Sari
2026-08-03 09:51:28 +07:00
parent 156841a4e7
commit 465be127ec
3 changed files with 97 additions and 8 deletions
@@ -11,6 +11,13 @@ use Illuminate\View\View;
class PermissionController extends Controller
{
/**
* Permission yang TIDAK bisa diubah via halaman ini — khusus admin.
*/
public const ADMIN_ONLY_PERMISSIONS = [
'view-point-audit',
];
public function index(): View
{
$permissions = DB::table('permissions')
@@ -29,7 +36,9 @@ class PermissionController extends Controller
->toArray();
}
return view('settings.permissions', compact('permissions', 'roles', 'rolePermissions'));
$adminOnlyKeys = self::ADMIN_ONLY_PERMISSIONS;
return view('settings.permissions', compact('permissions', 'roles', 'rolePermissions', 'adminOnlyKeys'));
}
public function update(Request $request): RedirectResponse
@@ -46,10 +55,27 @@ class PermissionController extends Controller
if (! empty($data['permissions'])) {
foreach ($data['permissions'] as $role => $permissionIds) {
// Permission khusus admin tidak boleh diberikan ke role lain
if ($role !== 'admin') {
$permissionIds = array_filter($permissionIds, function ($pid) {
$pid = (int) $pid;
if (! $pid) {
return false;
}
$key = DB::table('permissions')->where('id', $pid)->value('key');
return ! in_array($key, self::ADMIN_ONLY_PERMISSIONS, true);
});
}
foreach ($permissionIds as $permissionId) {
$permissionId = (int) $permissionId;
if (! $permissionId) {
continue;
}
DB::table('role_permissions')->insert([
'role' => $role,
'permission_id' => (int) $permissionId,
'permission_id' => $permissionId,
'created_at' => $now,
'updated_at' => $now,
]);
+14 -6
View File
@@ -96,11 +96,18 @@
<tr class="hover:bg-gradient-to-r hover:from-blue-50/30 hover:to-white transition-all duration-100 group">
<td class="px-5 py-3.5">
<div class="flex items-center gap-3">
<div class="w-8 h-8 rounded-lg bg-gray-100 flex items-center justify-center group-hover:bg-blue-100 transition-all duration-200">
<i class="fa-solid fa-check text-xs text-gray-400 group-hover:text-blue-500 transition-colors"></i>
<div class="w-8 h-8 rounded-lg {{ in_array($perm->key, $adminOnlyKeys) ? 'bg-amber-50' : 'bg-gray-100' }} flex items-center justify-center group-hover:bg-blue-100 transition-all duration-200">
<i class="fa-solid {{ in_array($perm->key, $adminOnlyKeys) ? 'fa-crown text-amber-500' : 'fa-check text-xs text-gray-400 group-hover:text-blue-500' }} transition-colors"></i>
</div>
<div>
<div class="text-sm font-semibold text-gray-800">{{ $perm->label }}</div>
<div class="text-sm font-semibold text-gray-800 flex items-center gap-2">
{{ $perm->label }}
@if (in_array($perm->key, $adminOnlyKeys))
<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[10px] font-bold bg-amber-50 text-amber-700 border border-amber-200">
<i class="fa-solid fa-lock text-[9px]"></i> Khusus Admin
</span>
@endif
</div>
<div class="text-[10px] text-gray-400 font-mono mt-0.5">{{ $perm->key }}</div>
</div>
</div>
@@ -109,11 +116,12 @@
@php
$checked = in_array($perm->id, $rolePermissions[$role] ?? []);
$disabled = ($role === 'admin');
$adminOnly = in_array($perm->key, $adminOnlyKeys);
@endphp
<td class="text-center px-3 py-3.5">
@if($disabled)
<div class="inline-flex items-center justify-center w-9 h-9 rounded-lg bg-blue-50 border border-blue-200">
<i class="fa-solid fa-lock text-xs text-blue-400"></i>
@if($disabled || $adminOnly)
<div class="inline-flex items-center justify-center w-9 h-9 rounded-lg {{ $adminOnly ? 'bg-amber-50 border border-amber-200' : 'bg-blue-50 border border-blue-200' }}">
<i class="fa-solid {{ $adminOnly ? 'fa-crown' : 'fa-lock' }} text-xs {{ $adminOnly ? 'text-amber-500' : 'text-blue-400' }}"></i>
</div>
@else
<input type="checkbox"
@@ -0,0 +1,55 @@
<?php
namespace Tests\Feature;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class PermissionManagementTest extends TestCase
{
use RefreshDatabase;
protected function setUp(): void
{
parent::setUp();
$this->admin = User::factory()->create(['roles' => ['admin']]);
}
public function test_permission_khusus_admin_tidak_bisa_diberikan_ke_role_lain(): void
{
$auditId = DB::table('permissions')->where('key', 'view-point-audit')->value('id');
$normalId = DB::table('permissions')->where('key', 'input-violations')->value('id');
$this->actingAs($this->admin)
->post('/settings/permissions', [
'permissions' => [
'bk' => [$auditId, $normalId],
'ketua_tim' => [$auditId],
],
])
->assertRedirect()
->assertSessionHas('success');
// view-point-audit TIDAK boleh jatuh ke role non-admin
$this->assertDatabaseMissing('role_permissions', ['role' => 'bk', 'permission_id' => $auditId]);
$this->assertDatabaseMissing('role_permissions', ['role' => 'ketua_tim', 'permission_id' => $auditId]);
// permission normal tetap tersimpan
$this->assertDatabaseHas('role_permissions', ['role' => 'bk', 'permission_id' => $normalId]);
// admin selalu dapat semua
$this->assertDatabaseHas('role_permissions', ['role' => 'admin', 'permission_id' => $auditId]);
}
public function test_halaman_hak_akses_menampilkan_permission_khusus_admin(): void
{
$response = $this->actingAs($this->admin)
->get('/settings/permissions')
->assertOk()
->assertSee('Hak Akses Role')
->assertSee('view-point-audit')
->assertSee('Khusus Admin');
}
}