251 lines
9.0 KiB
PHP
251 lines
9.0 KiB
PHP
<?php
|
|
|
|
use App\Models\AdminRole;
|
|
use App\Models\AdminUser;
|
|
use App\Models\AgentNode;
|
|
use App\Lottery\ErrorCode;
|
|
use Illuminate\Support\Facades\DB;
|
|
use App\Support\PlatformSystemRoles;
|
|
use Illuminate\Support\Facades\Hash;
|
|
use App\Services\Agent\AgentNodeService;
|
|
use App\Support\InvalidPlatformAgentRoleCleanup;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
function makePlatformRoleBoundaryToken(string $username): string
|
|
{
|
|
$admin = AdminUser::query()->create([
|
|
'username' => $username,
|
|
'name' => 'Role Boundary Manager',
|
|
'email' => null,
|
|
'password' => Hash::make('secret-strong'),
|
|
'status' => 0,
|
|
]);
|
|
$role = AdminRole::query()->create([
|
|
'slug' => 'boundary_'.$username,
|
|
'name' => 'Boundary Manager',
|
|
'scope_type' => AdminRole::SCOPE_SYSTEM,
|
|
]);
|
|
$role->syncLegacyPermissionSlugs(['prd.admin_user.manage', 'prd.admin_role.manage']);
|
|
$admin->roles()->sync([
|
|
(int) $role->id => [
|
|
'site_id' => AdminUser::requireDefaultAdminSiteId(),
|
|
'granted_at' => now(),
|
|
],
|
|
]);
|
|
|
|
return $admin->createToken('test', ['*'], now()->addDay())->plainTextToken;
|
|
}
|
|
|
|
function createPlatformRoleBoundaryAgent(string $username): AdminUser
|
|
{
|
|
$siteId = AdminUser::requireDefaultAdminSiteId();
|
|
$root = AgentNode::query()
|
|
->where('admin_site_id', $siteId)
|
|
->where('depth', 0)
|
|
->firstOrFail();
|
|
$super = AdminUser::query()->create([
|
|
'username' => 'super_'.$username,
|
|
'name' => 'Super',
|
|
'email' => null,
|
|
'password' => Hash::make('secret-strong'),
|
|
'status' => 0,
|
|
]);
|
|
grantSuperAdminRole($super);
|
|
|
|
$node = app(AgentNodeService::class)->createChild($super, [
|
|
'parent_id' => (int) $root->id,
|
|
'code' => 'node-'.$username,
|
|
'name' => 'Agent '.$username,
|
|
'username' => $username,
|
|
'password' => 'secret-strong',
|
|
]);
|
|
|
|
$adminUserId = DB::table('admin_user_agents')
|
|
->where('agent_node_id', $node->id)
|
|
->where('is_primary', true)
|
|
->value('admin_user_id');
|
|
|
|
return AdminUser::query()->findOrFail((int) $adminUserId);
|
|
}
|
|
|
|
test('permission catalog keeps all roles but only exposes platform assignable roles', function (): void {
|
|
$token = makePlatformRoleBoundaryToken('catalog_boundary');
|
|
|
|
$data = $this->withHeader('Authorization', 'Bearer '.$token)
|
|
->getJson('/api/v1/admin/admin-user-permission-catalog')
|
|
->assertOk()
|
|
->json('data');
|
|
|
|
expect(collect($data['roles'])->pluck('slug')->all())
|
|
->toContain(PlatformSystemRoles::SLUG_AGENT, PlatformSystemRoles::SLUG_SUPER_ADMIN);
|
|
expect(collect($data['assignable_roles'])->pluck('slug')->all())
|
|
->not->toContain(PlatformSystemRoles::SLUG_AGENT, PlatformSystemRoles::SLUG_SUPER_ADMIN);
|
|
});
|
|
|
|
test('platform account create and role update reject the agent role', function (): void {
|
|
$token = makePlatformRoleBoundaryToken('assignment_boundary');
|
|
$siteId = AdminUser::requireDefaultAdminSiteId();
|
|
|
|
$this->withHeader('Authorization', 'Bearer '.$token)
|
|
->withHeader('X-Locale', 'zh')
|
|
->postJson('/api/v1/admin/admin-users', [
|
|
'username' => 'illegal_agent_platform',
|
|
'nickname' => 'Illegal Agent Platform',
|
|
'email' => null,
|
|
'password' => 'secret-strong',
|
|
'status' => 0,
|
|
'admin_site_id' => $siteId,
|
|
'role_slugs' => [PlatformSystemRoles::SLUG_AGENT],
|
|
])
|
|
->assertStatus(422)
|
|
->assertJsonPath('code', ErrorCode::ValidationFailed->value)
|
|
->assertJsonPath('msg', '平台账号不能分配“代理”角色;请在“代理管理”中创建或绑定代理账号。');
|
|
|
|
expect(AdminUser::query()->where('username', 'illegal_agent_platform')->exists())->toBeFalse();
|
|
|
|
$target = AdminUser::query()->create([
|
|
'username' => 'platform_role_target',
|
|
'name' => 'Platform Role Target',
|
|
'email' => null,
|
|
'password' => Hash::make('secret-strong'),
|
|
'status' => 0,
|
|
]);
|
|
|
|
$this->withHeader('Authorization', 'Bearer '.$token)
|
|
->putJson('/api/v1/admin/admin-users/'.$target->id.'/roles', [
|
|
'admin_site_id' => $siteId,
|
|
'role_slugs' => [PlatformSystemRoles::SLUG_AGENT],
|
|
])
|
|
->assertStatus(422)
|
|
->assertJsonPath('code', ErrorCode::ValidationFailed->value);
|
|
});
|
|
|
|
test('agent creation keeps its agent role and cleanup removes only unbound platform assignments', function (): void {
|
|
$agentUser = createPlatformRoleBoundaryAgent('legal_agent_boundary');
|
|
$agentRole = AdminRole::query()
|
|
->where('scope_type', AdminRole::SCOPE_SYSTEM)
|
|
->where('slug', PlatformSystemRoles::SLUG_AGENT)
|
|
->firstOrFail();
|
|
$siteId = AdminUser::requireDefaultAdminSiteId();
|
|
|
|
expect(DB::table('admin_user_agents')->where('admin_user_id', $agentUser->id)->exists())->toBeTrue();
|
|
expect(DB::table('admin_user_site_roles')
|
|
->where('admin_user_id', $agentUser->id)
|
|
->where('role_id', $agentRole->id)
|
|
->exists())->toBeTrue();
|
|
|
|
$illegalUser = AdminUser::query()->create([
|
|
'username' => 'unbound_agent_role',
|
|
'name' => 'Unbound Agent Role',
|
|
'email' => null,
|
|
'password' => Hash::make('secret-strong'),
|
|
'status' => 0,
|
|
]);
|
|
DB::table('admin_user_site_roles')->insert([
|
|
'admin_user_id' => $illegalUser->id,
|
|
'site_id' => $siteId,
|
|
'role_id' => $agentRole->id,
|
|
'granted_at' => now(),
|
|
]);
|
|
|
|
$otherSiteId = (int) DB::table('admin_sites')->insertGetId([
|
|
'code' => 'boundary-other-site',
|
|
'name' => 'Boundary Other Site',
|
|
'is_default' => false,
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
]);
|
|
DB::table('admin_user_site_roles')->insert([
|
|
'admin_user_id' => $agentUser->id,
|
|
'site_id' => $otherSiteId,
|
|
'role_id' => $agentRole->id,
|
|
'granted_at' => now(),
|
|
]);
|
|
$ordinaryRole = AdminRole::query()->create([
|
|
'slug' => 'cross_site_ordinary_role',
|
|
'name' => 'Cross Site Ordinary Role',
|
|
'scope_type' => AdminRole::SCOPE_SYSTEM,
|
|
]);
|
|
DB::table('admin_user_site_roles')->insert([
|
|
'admin_user_id' => $agentUser->id,
|
|
'site_id' => $otherSiteId,
|
|
'role_id' => $ordinaryRole->id,
|
|
'granted_at' => now(),
|
|
]);
|
|
|
|
expect(InvalidPlatformAgentRoleCleanup::run())->toBe(2);
|
|
expect(DB::table('admin_user_site_roles')
|
|
->where('admin_user_id', $illegalUser->id)
|
|
->where('role_id', $agentRole->id)
|
|
->exists())->toBeFalse();
|
|
expect(DB::table('admin_user_site_roles')
|
|
->where('admin_user_id', $agentUser->id)
|
|
->where('site_id', $siteId)
|
|
->where('role_id', $agentRole->id)
|
|
->exists())->toBeTrue();
|
|
expect(DB::table('admin_user_site_roles')
|
|
->where('admin_user_id', $agentUser->id)
|
|
->where('site_id', $otherSiteId)
|
|
->where('role_id', $agentRole->id)
|
|
->exists())->toBeFalse();
|
|
expect(DB::table('admin_user_site_roles')
|
|
->where('admin_user_id', $agentUser->id)
|
|
->where('site_id', $otherSiteId)
|
|
->where('role_id', $ordinaryRole->id)
|
|
->exists())->toBeTrue();
|
|
expect(AdminUser::query()->whereKey($illegalUser->id)->exists())->toBeTrue();
|
|
});
|
|
|
|
test('role counts classify platform and agent accounts and role filter never leaks agents', function (): void {
|
|
$token = makePlatformRoleBoundaryToken('count_boundary');
|
|
$siteId = AdminUser::requireDefaultAdminSiteId();
|
|
$role = AdminRole::query()->create([
|
|
'slug' => 'counted_role',
|
|
'name' => 'Counted Role',
|
|
'scope_type' => AdminRole::SCOPE_SYSTEM,
|
|
]);
|
|
$platformUser = AdminUser::query()->create([
|
|
'username' => 'counted_platform',
|
|
'name' => 'Counted Platform',
|
|
'email' => null,
|
|
'password' => Hash::make('secret-strong'),
|
|
'status' => 0,
|
|
]);
|
|
$platformUser->roles()->sync([
|
|
(int) $role->id => [
|
|
'site_id' => $siteId,
|
|
'granted_at' => now(),
|
|
],
|
|
]);
|
|
$agentUser = createPlatformRoleBoundaryAgent('counted_agent');
|
|
DB::table('admin_user_site_roles')->insert([
|
|
'admin_user_id' => $agentUser->id,
|
|
'site_id' => $siteId,
|
|
'role_id' => $role->id,
|
|
'granted_at' => now(),
|
|
]);
|
|
|
|
$roleRow = collect($this->withHeader('Authorization', 'Bearer '.$token)
|
|
->getJson('/api/v1/admin/admin-roles')
|
|
->assertOk()
|
|
->json('data.items'))
|
|
->firstWhere('slug', 'counted_role');
|
|
|
|
expect($roleRow)
|
|
->not->toBeNull()
|
|
->and($roleRow['platform_user_count'])->toBe(1)
|
|
->and($roleRow['agent_user_count'])->toBe(1)
|
|
->and($roleRow['user_count'])->toBe(2);
|
|
|
|
$items = $this->withHeader('Authorization', 'Bearer '.$token)
|
|
->getJson('/api/v1/admin/admin-users?role_slug=counted_role')
|
|
->assertOk()
|
|
->json('data.items');
|
|
|
|
expect(collect($items)->pluck('username')->all())
|
|
->toBe(['counted_platform'])
|
|
->not->toContain('counted_agent');
|
|
});
|