Skip to content

Enforce tenant isolation

Tenant scope is a query and write constraint, not a side effect of authorization or a scoped binding. The app looks up the Order through its tenant before it charges it.

Before you begin

Run composer run reference:test:tenant and review tenant isolation.

Scope the subject in the UseCase

examples/reference-app/tests/Tenant/TenantIsolationTest.php — A cross-tenant write is denied independently of authorization.

php
<?php

namespace Tests\Tenant;

use App\Pulsar\Domain\Orders\Models\Order;
use App\Pulsar\Services\Client\Modules\Orders\UseCases\ChargeOrder;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class TenantIsolationTest extends TestCase
{
    use RefreshDatabase;

    // #region tenant-isolation-test
    public function test_tenant_scope_denies_a_cross_tenant_write_independently_of_policy(): void
    {
        $order = Order::query()->create(['tenant_id' => 'tenant-a', 'reference' => 'tenant-order', 'amount_cents' => 100, 'status' => 'pending']);
        $this->expectException(ModelNotFoundException::class);
        app(ChargeOrder::class)->execute($order->id, 'tenant-b', 'cross-tenant');
    }
    // #endregion
}

Verify and troubleshoot

The tenant test requests an otherwise valid Order under another tenant and receives no subject. Keep this negative check separate from Policy tests; an authorized actor can still be in the wrong tenant.