Files
fotospiel-app/app/Http/Middleware/TenantIsolation.php

66 lines
1.8 KiB
PHP

<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class TenantIsolation
{
/**
* Handle an incoming request.
*/
public function handle(Request $request, Closure $next)
{
$tenantId = $request->attributes->get('tenant_id');
if (!$tenantId) {
return response()->json(['error' => 'Tenant ID not found in token'], 401);
}
// Get the tenant from request (query param, route param, or header)
$requestTenantId = $this->getTenantIdFromRequest($request);
if ($requestTenantId && $requestTenantId != $tenantId) {
return response()->json(['error' => 'Tenant isolation violation'], 403);
}
// Set tenant context for query scoping
$connection = DB::connection();
if (in_array($connection->getDriverName(), ['mysql', 'mariadb'])) {
$connection->statement('SET @tenant_id = ?', [$tenantId]);
}
// Add tenant context to request for easy access in controllers
$request->attributes->set('current_tenant_id', $tenantId);
return $next($request);
}
/**
* Extract tenant ID from request
*/
private function getTenantIdFromRequest(Request $request): ?int
{
// 1. Route parameter (e.g., /api/v1/tenant/123/events)
if ($request->route('tenant')) {
return (int) $request->route('tenant');
}
// 2. Query parameter (e.g., ?tenant_id=123)
if ($request->query('tenant_id')) {
return (int) $request->query('tenant_id');
}
// 3. Header (X-Tenant-ID)
if ($request->header('X-Tenant-ID')) {
return (int) $request->header('X-Tenant-ID');
}
// 4. For tenant-specific resources, use token tenant_id
return null;
}
}