artisan('lottery:admin-auth-sync')->assertExitCode(0); }); test('role with only agent role view slug does not receive manage slugs in profile', function (): void { $admin = AdminUser::query()->create([ 'username' => 'agent_role_view_only', 'name' => 'View Only', 'email' => null, 'password' => Hash::make('secret-strong'), 'status' => 0, ]); $role = AdminRole::query()->create([ 'slug' => 'agent_role_view_only', 'name' => 'Agent role view only', ]); $role->syncLegacyPermissionSlugs(['prd.agent.role.view']); $siteId = AdminUser::defaultAdminSiteId(); $admin->roles()->sync([ (int) $role->id => ['site_id' => $siteId, 'granted_at' => now()], ]); $profile = AdminAuthProfile::fromAdmin($admin->fresh()); expect($profile['permissions'])->toContain('prd.agent.role.view') ->and($profile['permissions'])->toContain('prd.agent.view') ->not->toContain('prd.agent.role.manage') ->not->toContain('prd.agent.user.manage') ->not->toContain('prd.agent.manage'); }); test('role with only agent role manage can create roles but not agent nodes', function (): void { $siteId = (int) DB::table('admin_sites')->where('is_default', true)->value('id'); $rootId = (int) DB::table('agent_nodes')->where('admin_site_id', $siteId)->where('depth', 0)->value('id'); $super = AdminUser::query()->create([ 'username' => 'granularity_super', 'name' => 'Super', 'email' => null, 'password' => Hash::make('secret-strong'), 'status' => 0, ]); grantSuperAdminRole($super); $service = app(\App\Services\Agent\AgentNodeService::class); $branch = $service->createChild($super, [ 'parent_id' => $rootId, 'code' => 'granularity-branch', 'name' => 'Granularity Branch', ]); $admin = AdminUser::query()->create([ 'username' => 'agent_role_manage_only', 'name' => 'Role Manage', 'email' => null, 'password' => Hash::make('secret-strong'), 'status' => 0, ]); $role = AdminRole::query()->create([ 'slug' => 'agent_role_manage_only', 'name' => 'Agent role manage only', ]); $role->syncLegacyPermissionSlugs(['prd.agent.role.manage']); $siteId = (int) $branch->admin_site_id; DB::table('admin_user_site_roles')->insert([ 'admin_user_id' => $admin->id, 'site_id' => $siteId, 'role_id' => $role->id, 'granted_at' => now(), ]); DB::table('admin_user_agents')->insert([ 'admin_user_id' => $admin->id, 'agent_node_id' => $branch->id, 'is_primary' => true, 'granted_at' => now(), ]); DB::table('admin_user_agent_roles')->insert([ 'admin_user_id' => $admin->id, 'agent_node_id' => $branch->id, 'role_id' => $role->id, 'granted_at' => now(), ]); $token = $admin->createToken('test', ['*'], now()->addDay())->plainTextToken; $this->withHeader('Authorization', 'Bearer '.$token) ->postJson('/api/v1/admin/agent-nodes/'.$branch->id.'/roles', [ 'slug' => 'ops_role_only', 'name' => 'Ops Role', 'permission_slugs' => ['prd.agent.role.view'], ]) ->assertOk(); $this->withHeader('Authorization', 'Bearer '.$token) ->postJson('/api/v1/admin/agent-nodes', [ 'parent_id' => $branch->id, 'code' => 'should-fail', 'name' => 'Should Fail', ]) ->assertForbidden(); }); test('role with only agent node manage cannot create roles or admin users', function (): void { $siteId = (int) DB::table('admin_sites')->where('is_default', true)->value('id'); $rootId = (int) DB::table('agent_nodes')->where('admin_site_id', $siteId)->where('depth', 0)->value('id'); $super = AdminUser::query()->create([ 'username' => 'granularity_super_node', 'name' => 'Super', 'email' => null, 'password' => Hash::make('secret-strong'), 'status' => 0, ]); grantSuperAdminRole($super); $service = app(\App\Services\Agent\AgentNodeService::class); $branch = $service->createChild($super, [ 'parent_id' => $rootId, 'code' => 'granularity-node-only', 'name' => 'Node Only Branch', ]); $admin = AdminUser::query()->create([ 'username' => 'agent_node_manage_only', 'name' => 'Node Manage', 'email' => null, 'password' => Hash::make('secret-strong'), 'status' => 0, ]); $role = AdminRole::query()->create([ 'slug' => 'agent_node_manage_only', 'name' => 'Agent node manage only', ]); $role->syncLegacyPermissionSlugs(['prd.agent.manage']); $siteId = (int) $branch->admin_site_id; DB::table('admin_user_site_roles')->insert([ 'admin_user_id' => $admin->id, 'site_id' => $siteId, 'role_id' => $role->id, 'granted_at' => now(), ]); DB::table('admin_user_agents')->insert([ 'admin_user_id' => $admin->id, 'agent_node_id' => $branch->id, 'is_primary' => true, 'granted_at' => now(), ]); DB::table('admin_user_agent_roles')->insert([ 'admin_user_id' => $admin->id, 'agent_node_id' => $branch->id, 'role_id' => $role->id, 'granted_at' => now(), ]); $token = $admin->createToken('test', ['*'], now()->addDay())->plainTextToken; $this->withHeader('Authorization', 'Bearer '.$token) ->postJson('/api/v1/admin/agent-nodes/'.$branch->id.'/roles', [ 'slug' => 'should_fail_role', 'name' => 'Should Fail Role', 'permission_slugs' => ['prd.agent.role.view'], ]) ->assertForbidden(); $this->withHeader('Authorization', 'Bearer '.$token) ->postJson('/api/v1/admin/agent-nodes/'.$branch->id.'/admin-users', [ 'username' => 'should_fail_user', 'name' => 'Should Fail User', 'password' => 'secret-strong-2', 'role_ids' => [], ]) ->assertForbidden(); }); test('agent-bound admin with only site role bindings still receives manage slugs in profile', function (): void { $siteId = (int) DB::table('admin_sites')->where('is_default', true)->value('id'); $rootId = (int) DB::table('agent_nodes')->where('admin_site_id', $siteId)->where('depth', 0)->value('id'); $super = AdminUser::query()->create([ 'username' => 'granularity_super_site_fallback', 'name' => 'Super', 'email' => null, 'password' => Hash::make('secret-strong'), 'status' => 0, ]); grantSuperAdminRole($super); $service = app(\App\Services\Agent\AgentNodeService::class); $branch = $service->createChild($super, [ 'parent_id' => $rootId, 'code' => 'site-fallback-branch', 'name' => 'Site Fallback Branch', ]); $role = AdminRole::query()->create([ 'slug' => 'agent_role_manage_site_only', 'name' => 'Agent role manage site bind', 'scope_type' => \App\Models\AdminRole::SCOPE_AGENT, 'owner_agent_id' => $branch->id, ]); $role->syncLegacyPermissionSlugs(['prd.agent.role.manage']); $admin = AdminUser::query()->create([ 'username' => 'agent_site_roles_only', 'name' => 'Site Roles Only', 'email' => null, 'password' => Hash::make('secret-strong'), 'status' => 0, ]); DB::table('admin_user_agents')->insert([ 'admin_user_id' => $admin->id, 'agent_node_id' => $branch->id, 'is_primary' => true, 'granted_at' => now(), ]); DB::table('admin_user_site_roles')->insert([ 'admin_user_id' => $admin->id, 'site_id' => $siteId, 'role_id' => $role->id, 'granted_at' => now(), ]); $profile = AdminAuthProfile::fromAdmin($admin->fresh()); expect($profile['permissions'])->toContain('prd.agent.role.manage'); expect(DB::table('admin_user_agent_roles')->where('admin_user_id', $admin->id)->count())->toBe(0); });