- Added standard Laravel directory structure and configuration. - Included Svelte and Tailwind configuration for the admin interface. - Added core PHPUnit and testing scripts.
263 lines
9.8 KiB
PHP
263 lines
9.8 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\Permission;
|
|
use App\Models\Role;
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Tests\TestCase;
|
|
|
|
class RolePermissionTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
public function test_custom_role_with_theme_permissions_can_access_admin_dashboard(): void
|
|
{
|
|
// 1. Seed permissions
|
|
$this->seed(\Database\Seeders\PermissionSeeder::class);
|
|
|
|
// 2. Create the 'Theme Manager' role
|
|
$role = Role::create([
|
|
'name' => 'Theme Manager',
|
|
'slug' => 'theme-manager',
|
|
'description' => 'Can manage themes',
|
|
]);
|
|
|
|
// 3. Assign theme related permissions
|
|
$themePermissions = Permission::whereIn('slug', [
|
|
'view-themes',
|
|
'activate-themes',
|
|
'upload-themes',
|
|
'edit-themes'
|
|
])->pluck('id');
|
|
|
|
$role->permissions()->sync($themePermissions);
|
|
|
|
// 4. Create user and assign the role
|
|
$user = User::factory()->create([
|
|
'email' => 'themes@siteweaver.test'
|
|
]);
|
|
$user->roles()->attach($role);
|
|
|
|
// 5. Try to access the admin dashboard
|
|
$adminPath = env('ADMIN_PATH', 'loom');
|
|
$response = $this->actingAs($user)->get('/' . $adminPath);
|
|
$response->assertStatus(200);
|
|
|
|
// 6. Try to access the themes index
|
|
$response = $this->actingAs($user)->get('/' . $adminPath . '/themes');
|
|
$response->assertStatus(200);
|
|
|
|
// 7. Try to access the theme editor
|
|
$response = $this->actingAs($user)->get('/' . $adminPath . '/themes/editor');
|
|
$response->assertStatus(200);
|
|
|
|
// 8. Try to access pages (should fail as they don't have view-pages)
|
|
$response = $this->actingAs($user)->get('/' . $adminPath . '/pages');
|
|
$response->assertStatus(403);
|
|
}
|
|
|
|
public function test_editor_can_access_dashboard_and_pages(): void
|
|
{
|
|
$this->seed(\Database\Seeders\PermissionSeeder::class);
|
|
$this->seed(\Database\Seeders\RoleSeeder::class);
|
|
|
|
$user = User::factory()->create();
|
|
$user->roles()->attach(Role::where('slug', 'editor')->first());
|
|
|
|
$adminPath = env('ADMIN_PATH', 'loom');
|
|
|
|
$response = $this->actingAs($user)->get('/' . $adminPath);
|
|
$response->assertStatus(200);
|
|
|
|
$response = $this->actingAs($user)->get('/' . $adminPath . '/pages');
|
|
$response->assertStatus(200);
|
|
|
|
// Editor cannot view users
|
|
$response = $this->actingAs($user)->get('/' . $adminPath . '/users');
|
|
$response->assertStatus(403);
|
|
}
|
|
|
|
public function test_admin_has_global_access_via_middleware_bypass(): void
|
|
{
|
|
$this->seed(\Database\Seeders\PermissionSeeder::class);
|
|
$this->seed(\Database\Seeders\RoleSeeder::class);
|
|
|
|
// Create an admin user - note that in the seeder, 'admin' role might have permissions,
|
|
// but we want to test the bypass specifically.
|
|
$user = User::factory()->create();
|
|
$user->roles()->attach(Role::where('slug', 'admin')->first());
|
|
|
|
$adminPath = env('ADMIN_PATH', 'loom');
|
|
|
|
// Admin can access everything
|
|
$this->actingAs($user)->get('/' . $adminPath)->assertStatus(200);
|
|
$this->actingAs($user)->get('/' . $adminPath . '/pages')->assertStatus(200);
|
|
$this->actingAs($user)->get('/' . $adminPath . '/users')->assertStatus(200);
|
|
$this->actingAs($user)->get('/' . $adminPath . '/roles')->assertStatus(200);
|
|
$this->actingAs($user)->get('/' . $adminPath . '/analytics')->assertStatus(200);
|
|
}
|
|
|
|
public function test_editor_loses_access_when_permission_is_removed(): void
|
|
{
|
|
$this->seed(\Database\Seeders\PermissionSeeder::class);
|
|
$this->seed(\Database\Seeders\RoleSeeder::class);
|
|
|
|
$user = User::factory()->create();
|
|
$role = Role::where('slug', 'editor')->first();
|
|
$user->roles()->attach($role);
|
|
|
|
$adminPath = env('ADMIN_PATH', 'loom');
|
|
|
|
// Initially has access
|
|
$this->actingAs($user)->get('/' . $adminPath . '/pages')->assertStatus(200);
|
|
|
|
// Remove the permission from the role
|
|
$permission = Permission::where('slug', 'view-pages')->first();
|
|
$role->permissions()->detach($permission->id);
|
|
|
|
// Now should be forbidden (403)
|
|
$this->actingAs($user)->get('/' . $adminPath . '/pages')->assertStatus(403);
|
|
}
|
|
|
|
public function test_navigation_and_dashboard_items_hidden_based_on_permissions(): void
|
|
{
|
|
$this->seed(\Database\Seeders\PermissionSeeder::class);
|
|
|
|
// Create a role with ONLY view-pages permission
|
|
$role = Role::create([
|
|
'name' => 'Page Viewer',
|
|
'slug' => 'page-viewer',
|
|
]);
|
|
$permission = Permission::where('slug', 'view-pages')->first();
|
|
$role->permissions()->attach($permission->id);
|
|
|
|
$user = User::factory()->create();
|
|
$user->roles()->attach($role);
|
|
|
|
$adminPath = env('ADMIN_PATH', 'loom');
|
|
|
|
$response = $this->actingAs($user)->get('/' . $adminPath);
|
|
$response->assertStatus(200);
|
|
|
|
// Verify "Pages" is visible in navigation
|
|
$response->assertSee('Pages');
|
|
$response->assertSee('file icon');
|
|
|
|
// Verify "Users", "Themes", "Backups", "Navigation" are NOT visible in navigation
|
|
$response->assertDontSee('Users');
|
|
$response->assertDontSee('Themes');
|
|
$response->assertDontSee('Backups');
|
|
$response->assertDontSee('Navigation');
|
|
|
|
// Verify Dashboard cards visibility (Svelte component data)
|
|
// Check for the data-permissions attribute in the dashboard view
|
|
$response->assertSee('data-permissions');
|
|
$response->assertSee('view-pages', false);
|
|
$response->assertSee('true', false);
|
|
$response->assertSee('view-themes', false);
|
|
$response->assertSee('false', false);
|
|
}
|
|
|
|
public function test_admin_sees_all_cards_in_dashboard_data(): void
|
|
{
|
|
$this->seed(\Database\Seeders\PermissionSeeder::class);
|
|
$this->seed(\Database\Seeders\RoleSeeder::class);
|
|
|
|
$user = User::factory()->create();
|
|
$user->roles()->attach(Role::where('slug', 'admin')->first());
|
|
|
|
$adminPath = env('ADMIN_PATH', 'loom');
|
|
|
|
$response = $this->actingAs($user)->get('/' . $adminPath);
|
|
$response->assertStatus(200);
|
|
|
|
// All permissions should be true for admin in data-permissions attribute
|
|
$response->assertSee('data-permissions');
|
|
$response->assertSee('"view-pages":true', false);
|
|
$response->assertSee('"view-themes":true', false);
|
|
$response->assertSee('"view-users":true', false);
|
|
$response->assertSee('"view-roles":true', false);
|
|
$response->assertSee('"manage-settings":true', false);
|
|
}
|
|
|
|
public function test_media_manager_buttons_visibility_based_on_permissions(): void
|
|
{
|
|
$this->seed(\Database\Seeders\PermissionSeeder::class);
|
|
|
|
// Create a role with view-media but NO upload-media or delete-media
|
|
$role = Role::create([
|
|
'name' => 'Media Viewer',
|
|
'slug' => 'media-viewer',
|
|
]);
|
|
$role->permissions()->attach(Permission::where('slug', 'view-media')->first()->id);
|
|
|
|
$user = User::factory()->create();
|
|
$user->roles()->attach($role);
|
|
|
|
$adminPath = env('ADMIN_PATH', 'loom');
|
|
|
|
$response = $this->actingAs($user)->get('/' . $adminPath . '/media');
|
|
$response->assertStatus(200);
|
|
|
|
// Verify "upload-media" is false in Svelte component data
|
|
$response->assertSee('"upload-media":false', false);
|
|
$response->assertSee('"delete-media":false', false);
|
|
$response->assertSee('"update-media":false', false);
|
|
|
|
// Verify "view-media" is true
|
|
$response->assertSee('"view-media":true', false);
|
|
|
|
// Create a role with upload-media
|
|
$role2 = Role::create([
|
|
'name' => 'Media Creator',
|
|
'slug' => 'media-creator',
|
|
]);
|
|
$role2->permissions()->attach(Permission::where('slug', 'view-media')->first()->id);
|
|
$role2->permissions()->attach(Permission::where('slug', 'upload-media')->first()->id);
|
|
|
|
$user2 = User::factory()->create();
|
|
$user2->roles()->attach($role2);
|
|
|
|
$response = $this->actingAs($user2)->get('/' . $adminPath . '/media');
|
|
$response->assertStatus(200);
|
|
|
|
$response->assertSee('"upload-media":true', false);
|
|
$response->assertSee('"delete-media":false', false);
|
|
}
|
|
|
|
public function test_page_editor_passes_media_permissions_to_picker(): void
|
|
{
|
|
$this->seed(\Database\Seeders\PermissionSeeder::class);
|
|
|
|
$role = Role::create([
|
|
'name' => 'Page Editor Limited',
|
|
'slug' => 'page-editor-limited',
|
|
]);
|
|
$role->permissions()->attach(Permission::where('slug', 'view-pages')->first()->id);
|
|
$role->permissions()->attach(Permission::where('slug', 'create-pages')->first()->id);
|
|
$role->permissions()->attach(Permission::where('slug', 'edit-pages')->first()->id);
|
|
// NO media permissions
|
|
|
|
$user = User::factory()->create();
|
|
$user->roles()->attach($role);
|
|
|
|
$adminPath = env('ADMIN_PATH', 'loom');
|
|
|
|
$response = $this->actingAs($user)->get('/' . $adminPath . '/pages/create');
|
|
$response->assertStatus(200);
|
|
|
|
// Verify "upload-media" is false in data-permissions
|
|
$response->assertSee('data-permissions');
|
|
$response->assertSee('"upload-media":false', false);
|
|
|
|
// Grant upload-media
|
|
$role->permissions()->attach(Permission::where('slug', 'upload-media')->first()->id);
|
|
|
|
$response = $this->actingAs($user)->get('/' . $adminPath . '/pages/create');
|
|
$response->assertStatus(200);
|
|
$response->assertSee('"upload-media":true', false);
|
|
}
|
|
}
|