guard('jwt')->login($user); } protected function authHeaders(User $user): array { return ['Authorization' => 'Bearer ' . $this->getAuthToken($user)]; } protected function createTestUser(string $role_name, array $overrides = []): User { $role = Role::query()->where('name', $role_name)->firstOrFail(); $suffix = bin2hex(random_bytes(4)); return User::query()->create(array_merge([ 'username' => 'integ_perm_' . $suffix, 'email' => 'integ_perm_' . $suffix . '@example.com', 'password' => 'Pass_' . $suffix, 'status' => 1, 'role_id' => $role->id, ], $overrides)); } /** * 为指定角色授权指定路由的路由组 * * @return array{group: RouteGroup, old_group_id: ?int} 用于清理 */ protected function authorizeRouteGroup(int $role_id, Route $route): array { $group = RouteGroup::query()->create([ 'name' => 'integ_test_' . uniqid(), 'label' => '集成测试路由组', ]); $old_group_id = $route->group_id; $route->group_id = $group->id; $route->save(); Db::table('role_route_groups')->insert([ 'role_id' => $role_id, 'group_id' => $group->id, ]); return ['group' => $group, 'old_group_id' => $old_group_id]; } /** * 清理路由组授权 */ protected function cleanupRouteGroup(int $role_id, Route $route, array $auth_data): void { Db::table('role_route_groups') ->where('role_id', $role_id) ->where('group_id', $auth_data['group']->id) ->delete(); $route->group_id = $auth_data['old_group_id']; $route->save(); $auth_data['group']->delete(); } // ========== 测试用例 ========== public function test_admin_full_access(): void { $user = $this->createTestUser('administrator'); // administrator 可访问全部 UserController 端点 $response = $this->get('/api/v1/users', [], $this->authHeaders($user)); $response->assertStatus(200); $response->assertJsonPath('code', 0); // 参数化路由 $response = $this->get('/api/v1/users/1', [], $this->authHeaders($user)); $this->assertContains($response->getStatusCode(), [200, 404]); } public function test_developer_with_group_auth_access(): void { $user = $this->createTestUser('developer'); $route = Route::query()->where('method', 'GET')->where('path', '/api/v1/users')->first(); if (!$route) { $this->markTestSkipped('routes 表中无 GET /api/v1/users 路由'); } $auth_data = $this->authorizeRouteGroup($user->role_id, $route); try { $response = $this->get('/api/v1/users', [], $this->authHeaders($user)); $response->assertStatus(200); } finally { $this->cleanupRouteGroup($user->role_id, $route, $auth_data); } } public function test_accessor_without_auth_denied(): void { $user = $this->createTestUser('accessor'); // accessor 无任何路由授权 → 403 $response = $this->get('/api/v1/users', [], $this->authHeaders($user)); $response->assertStatus(403); } public function test_accessor_with_override_access(): void { $user = $this->createTestUser('accessor'); $route = Route::query()->where('method', 'GET')->where('path', '/api/v1/users')->first(); if (!$route) { $this->markTestSkipped('routes 表中无 GET /api/v1/users 路由'); } // 添加 store scope 以通过数据范围检查 $store = Store::query()->first(); if ($store) { UserDataScope::query()->create([ 'user_id' => $user->id, 'scope_type' => 'store', 'scope_id' => $store->id, ]); } // 设置 override 允许访问 RoleRouteOverride::query()->create([ 'role_id' => $user->role_id, 'route_id' => $route->id, 'allowed' => true, ]); try { $response = $this->get('/api/v1/users', [], $this->authHeaders($user)); $response->assertStatus(200); } finally { RoleRouteOverride::query() ->where('role_id', $user->role_id) ->where('route_id', $route->id) ->delete(); UserDataScope::query()->where('user_id', $user->id)->delete(); } } public function test_override_deny_overrides_group(): void { $user = $this->createTestUser('developer'); $route = Route::query()->where('method', 'GET')->where('path', '/api/v1/users')->first(); if (!$route) { $this->markTestSkipped('routes 表中无 GET /api/v1/users 路由'); } // 授权路由组 $auth_data = $this->authorizeRouteGroup($user->role_id, $route); // 设置 override 为拒绝(优先级高于 group) RoleRouteOverride::query()->create([ 'role_id' => $user->role_id, 'route_id' => $route->id, 'allowed' => false, ]); try { $response = $this->get('/api/v1/users', [], $this->authHeaders($user)); $response->assertStatus(403); } finally { RoleRouteOverride::query() ->where('role_id', $user->role_id) ->where('route_id', $route->id) ->delete(); $this->cleanupRouteGroup($user->role_id, $route, $auth_data); } } public function test_developer_scope_param_validation(): void { $user = $this->createTestUser('developer'); $route = Route::query()->where('method', 'GET')->where('path', '/api/v1/users')->first(); if (!$route) { $this->markTestSkipped('routes 表中无 GET /api/v1/users 路由'); } // 授权路由访问 $auth_data = $this->authorizeRouteGroup($user->role_id, $route); try { // 传不属于自己的 platform_id → 403 $response = $this->get('/api/v1/users', ['platform_id' => 999999], $this->authHeaders($user)); $response->assertStatus(403); } finally { $this->cleanupRouteGroup($user->role_id, $route, $auth_data); } } // ========== 边缘场景 ========== public function test_accessor_with_invalid_store_id_denied(): void { $user = $this->createTestUser('accessor'); $route = Route::query()->where('method', 'GET')->where('path', '/api/v1/users')->first(); if (!$route) { $this->markTestSkipped('routes 表中无 GET /api/v1/users 路由'); } // 授权路由访问 RoleRouteOverride::query()->create([ 'role_id' => $user->role_id, 'route_id' => $route->id, 'allowed' => true, ]); // 添加 store scope(store_id=1),然后传不在 bitmap 中的 store_id $store = Store::query()->first(); if (!$store) { $this->markTestSkipped('stores 表中无数据'); } UserDataScope::query()->create([ 'user_id' => $user->id, 'scope_type' => 'store', 'scope_id' => $store->id, ]); try { // 传不属于自己的 store_id → 403 $response = $this->get('/api/v1/users', ['store_id' => 999999], $this->authHeaders($user)); $response->assertStatus(403); } finally { RoleRouteOverride::query() ->where('role_id', $user->role_id) ->where('route_id', $route->id) ->delete(); UserDataScope::query()->where('user_id', $user->id)->delete(); } } public function test_accessor_with_invalid_company_id_denied(): void { $user = $this->createTestUser('accessor'); $route = Route::query()->where('method', 'GET')->where('path', '/api/v1/users')->first(); if (!$route) { $this->markTestSkipped('routes 表中无 GET /api/v1/users 路由'); } RoleRouteOverride::query()->create([ 'role_id' => $user->role_id, 'route_id' => $route->id, 'allowed' => true, ]); $store = Store::query()->first(); if (!$store) { $this->markTestSkipped('stores 表中无数据'); } UserDataScope::query()->create([ 'user_id' => $user->id, 'scope_type' => 'store', 'scope_id' => $store->id, ]); try { // 传不属于自己的 company_id → 403 $response = $this->get('/api/v1/users', ['company_id' => 999999], $this->authHeaders($user)); $response->assertStatus(403); } finally { RoleRouteOverride::query() ->where('role_id', $user->role_id) ->where('route_id', $route->id) ->delete(); UserDataScope::query()->where('user_id', $user->id)->delete(); } } public function test_accessor_with_no_scope_data_gets_empty_results(): void { $user = $this->createTestUser('accessor'); $route = Route::query()->where('method', 'GET')->where('path', '/api/v1/users')->first(); if (!$route) { $this->markTestSkipped('routes 表中无 GET /api/v1/users 路由'); } // 授权路由访问但不设置任何 scope 数据 RoleRouteOverride::query()->create([ 'role_id' => $user->role_id, 'route_id' => $route->id, 'allowed' => true, ]); try { // accessor 无 scope 数据 → bitmap 为空 → scope_ids=[] → 200 但无数据 $response = $this->get('/api/v1/users', [], $this->authHeaders($user)); $response->assertStatus(200); $response->assertJsonPath('code', 0); } finally { RoleRouteOverride::query() ->where('role_id', $user->role_id) ->where('route_id', $route->id) ->delete(); } } public function test_developer_with_invalid_store_id_denied(): void { $user = $this->createTestUser('developer'); $route = Route::query()->where('method', 'GET')->where('path', '/api/v1/users')->first(); if (!$route) { $this->markTestSkipped('routes 表中无 GET /api/v1/users 路由'); } $auth_data = $this->authorizeRouteGroup($user->role_id, $route); try { // developer 传不属于自己的 store_id → 403 $response = $this->get('/api/v1/users', ['store_id' => 999999], $this->authHeaders($user)); $response->assertStatus(403); } finally { $this->cleanupRouteGroup($user->role_id, $route, $auth_data); } } public function test_developer_with_no_platforms_gets_empty_scope(): void { // 创建 developer 用户但不关联任何 platform $user = $this->createTestUser('developer'); $route = Route::query()->where('method', 'GET')->where('path', '/api/v1/users')->first(); if (!$route) { $this->markTestSkipped('routes 表中无 GET /api/v1/users 路由'); } $auth_data = $this->authorizeRouteGroup($user->role_id, $route); try { // developer 无维护平台 → platform_ids=[] → scope_ids=[] → 200 但无数据 $response = $this->get('/api/v1/users', [], $this->authHeaders($user)); $response->assertStatus(200); $response->assertJsonPath('code', 0); } finally { $this->cleanupRouteGroup($user->role_id, $route, $auth_data); } } public function test_ungrouped_route_denied_for_non_admin(): void { $user = $this->createTestUser('developer'); $route = Route::query()->where('method', 'GET')->where('path', '/api/v1/users')->first(); if (!$route) { $this->markTestSkipped('routes 表中无 GET /api/v1/users 路由'); } // 确保路由未分组且无 override $old_group_id = $route->group_id; $route->group_id = null; $route->save(); try { // 路由已注册但未分组、无 override → 403 $response = $this->get('/api/v1/users', [], $this->authHeaders($user)); $response->assertStatus(403); } finally { $route->group_id = $old_group_id; $route->save(); } } }