isEnabled()) { Log::warning('Intune write gate is disabled — write operation proceeding without RBAC verification.', [ 'tenant_id' => $tenant->getKey(), 'operation_type' => $operationType, ]); return; } $status = $tenant->rbac_status; if ($status === null || $status === 'not_configured') { throw new ProviderAccessHardeningRequired( tenantId: (int) $tenant->getKey(), operationType: $operationType, reasonCode: 'intune_rbac.not_configured', reasonMessage: 'Intune RBAC is not configured for this tenant. Configure RBAC before performing write operations.', ); } if (in_array($status, ['degraded', 'failed', 'error', 'missing', 'partial'], true)) { throw new ProviderAccessHardeningRequired( tenantId: (int) $tenant->getKey(), operationType: $operationType, reasonCode: 'intune_rbac.unhealthy', reasonMessage: sprintf( 'Intune RBAC status is "%s". Resolve RBAC issues before performing write operations.', $status, ), ); } if ($this->isStale($tenant)) { throw new ProviderAccessHardeningRequired( tenantId: (int) $tenant->getKey(), operationType: $operationType, reasonCode: 'intune_rbac.stale', reasonMessage: 'Intune RBAC health check is outdated. Run a fresh health check before performing write operations.', ); } } public function wouldBlock(Tenant $tenant): bool { try { $this->evaluate($tenant, 'ui_check'); return false; } catch (ProviderAccessHardeningRequired) { return true; } } private function isEnabled(): bool { return (bool) config('tenantpilot.hardening.intune_write_gate.enabled', true); } private function isStale(Tenant $tenant): bool { $lastCheckedAt = $tenant->rbac_last_checked_at; if ($lastCheckedAt === null) { return true; } $thresholdHours = (int) config('tenantpilot.hardening.intune_write_gate.freshness_threshold_hours', 24); return $lastCheckedAt->diffInHours(now()) >= $thresholdHours; } }