user(); if (! $user) { return $this->unauthorizedResponse('Unauthenticated request.'); } $accessToken = $user->currentAccessToken(); if (! $accessToken instanceof PersonalAccessToken) { return $this->unauthorizedResponse('Missing personal access token context.'); } if (! in_array($user->role, $this->allowedRoles(), true)) { return $this->forbiddenResponse($this->forbiddenRoleMessage()); } if (! $this->hasRequiredAbilities($accessToken, $user)) { return $this->forbiddenResponse($this->abilityErrorMessage()); } /** @var Tenant|null $tenant */ $tenant = $user->tenant; if (! $tenant && $user->role === 'super_admin') { $requestedTenantId = $this->resolveRequestedTenantId($request); if ($requestedTenantId !== null) { $tenant = Tenant::query()->find($requestedTenantId); } } if (! $tenant && $user->role !== 'super_admin') { return $this->forbiddenResponse('Tenant context missing for user.'); } if ($tenant) { $request->attributes->set('tenant_id', $tenant->id); $request->attributes->set('tenant', $tenant); } elseif ($user->role === 'super_admin') { $requestedTenantId = $this->resolveRequestedTenantId($request); if ($requestedTenantId !== null) { $request->attributes->set('tenant_id', $requestedTenantId); } } $request->attributes->set('sanctum_token_id', $accessToken->id); Auth::shouldUse('sanctum'); return $next($request); } private function unauthorizedResponse(string $message): JsonResponse { return ApiError::response( 'unauthenticated', 'Unauthenticated', $message, Response::HTTP_UNAUTHORIZED ); } private function forbiddenResponse(string $message): JsonResponse { return ApiError::response( 'tenant_admin_only', 'Forbidden', $message, Response::HTTP_FORBIDDEN ); } /** * @return array */ protected function allowedRoles(): array { return ['tenant_admin', 'super_admin', 'admin']; } protected function forbiddenRoleMessage(): string { return 'Only tenant administrators may access this resource.'; } protected function requiredAbilitiesForUser(User $user): array { return ['tenant-admin']; } protected function abilityErrorMessage(): string { return 'Access token does not include the tenant-admin ability.'; } protected function hasRequiredAbilities(PersonalAccessToken $accessToken, User $user): bool { foreach ($this->requiredAbilitiesForUser($user) as $ability) { if (! $accessToken->can($ability)) { return false; } } return true; } private function resolveRequestedTenantId(Request $request): ?int { $routeTenant = $request->route('tenant'); if (is_numeric($routeTenant)) { return (int) $routeTenant; } $queryTenant = $request->query('tenant_id'); if (is_numeric($queryTenant)) { return (int) $queryTenant; } $headerTenant = $request->header('X-Tenant-ID'); if (is_numeric($headerTenant)) { return (int) $headerTenant; } return null; } }