create([ 'username' => $username, 'name' => 'Tester', 'email' => null, 'password' => Hash::make('secret-strong'), 'status' => 0, ]); $role = AdminRole::query()->create([ 'slug' => 'role_'.$username, 'name' => 'Role '.$username, ]); foreach ($permissionSlugs as $slug) { $permission = AdminPermission::query()->firstOrCreate( ['slug' => $slug], ['name' => $slug], ); $role->permissions()->syncWithoutDetaching([(int) $permission->id]); } $admin->roles()->syncWithoutDetaching([(int) $role->id]); return $admin->createToken('test', ['*'], now()->addDay())->plainTextToken; } test('admin user permission apis require rbac permission', function (): void { AdminPermission::query()->create(['slug' => 'prd.admin_user.manage', 'name' => 'admin manage']); $token = makeAdminWithPermissions('rbac_viewer', ['prd.report.player']); $this->withHeader('Authorization', 'Bearer '.$token) ->getJson('/api/v1/admin/admin-users') ->assertForbidden() ->assertJsonPath('code', ErrorCode::AdminForbidden->value); }); test('admin can list users and sync direct permissions', function (): void { $manage = AdminPermission::query()->create(['slug' => 'prd.admin_user.manage', 'name' => 'admin manage']); $report = AdminPermission::query()->create(['slug' => 'prd.report.player', 'name' => 'report player']); $draw = AdminPermission::query()->create(['slug' => 'prd.draw_result.view', 'name' => 'draw view']); $token = makeAdminWithPermissions('rbac_manager', ['prd.admin_user.manage']); $target = AdminUser::query()->create([ 'username' => 'target_user', 'name' => 'Target User', 'email' => 'target@example.com', 'password' => Hash::make('secret-strong'), 'status' => 0, ]); $targetRole = AdminRole::query()->create(['slug' => 'target_role', 'name' => 'Target Role']); $targetRole->permissions()->sync([(int) $draw->id]); $target->roles()->sync([(int) $targetRole->id]); $this->withHeader('Authorization', 'Bearer '.$token) ->getJson('/api/v1/admin/admin-user-permission-catalog') ->assertOk() ->assertJsonPath('code', ErrorCode::Success->value) ->assertJsonPath('data.permissions.0.slug', 'prd.admin_user.manage'); $this->withHeader('Authorization', 'Bearer '.$token) ->getJson('/api/v1/admin/admin-users?keyword=target') ->assertOk() ->assertJsonPath('code', ErrorCode::Success->value) ->assertJsonPath('data.items.0.username', 'target_user') ->assertJsonPath('data.items.0.roles.0', 'target_role'); $this->withHeader('Authorization', 'Bearer '.$token) ->putJson('/api/v1/admin/admin-users/'.$target->id.'/permissions', [ 'permission_slugs' => [$report->slug], ]) ->assertOk() ->assertJsonPath('code', ErrorCode::Success->value) ->assertJsonPath('data.direct_permissions.0', 'prd.report.player'); expect( $target->fresh()->permissions()->pluck('slug')->sort()->values()->all() )->toBe([$report->slug]); $list = $this->withHeader('Authorization', 'Bearer '.$token) ->getJson('/api/v1/admin/admin-users?keyword=target') ->assertOk() ->json('data.items.0.effective_permissions'); expect($list)->toContain($draw->slug); expect($list)->toContain($report->slug); expect($manage->slug)->toBe('prd.admin_user.manage'); });