fix(rls): close 4 dev↔prod RLS gaps in cron/jobs (hole #7 Phase B)

Found by docs/audit/2026-05-23-rls-gap-audit.md. Each touched an RLS-protected
table on the default connection in cron/queue context (no tenant GUC) — crash or
silent misbehaviour on prod (crm_app_user, not BYPASSRLS), hidden on dev (superuser).

- RemindersDispatchDue (Pattern B): gather pending via pgsql_supplier, then
  per-reminder DB::transaction + SET LOCAL app.current_tenant_id (isolation kept).
- ReportsCleanupExpired (Pattern A): SaaS-admin cron → report_jobs + pd_processing_log
  via pgsql_supplier (BYPASSRLS).
- GenerateReportJob (Pattern B): +readonly int $tenantId ctor param, wrap handle()
  in DB::transaction + SET LOCAL; both ReportJobController dispatch sites updated.
- ProcessWebhookJob::failed (Pattern A): failed_webhook_jobs insert via pgsql_supplier
  → webhook failures now logged, incidents:watch-failures can see them.

Tests +SharesSupplierPdo trait. 118 passed / 0 failed. My 5 src files pass larastan
isolated (0 errors).
This commit is contained in:
Дмитрий
2026-05-23 10:16:46 +03:00
parent 0539951d6b
commit fb4e711b4a
8 changed files with 126 additions and 83 deletions
@@ -14,6 +14,7 @@ use App\Models\WebhookDedupKey;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Tests\Concerns\SharesSupplierPdo;
/**
* Тесты ProcessWebhookJob двустадийный dedup v8.6 (CTO-17).
@@ -25,8 +26,12 @@ use Illuminate\Support\Str;
* NB: Job::handle() сам открывает DB::transaction. DatabaseTransactions
* trait оборачивает каждый тест в outer-транзакцию Laravel-PG-driver
* корректно обрабатывает nested через savepoints.
*
* SharesSupplierPdo: failed() now inserts via pgsql_supplier (BYPASSRLS)
* share PDO so DatabaseTransactions cross-connection visibility works on dev.
*/
uses(DatabaseTransactions::class);
uses(SharesSupplierPdo::class);
function makePayload(int $vid = 432176649, ?int $time = null): array
{