TenantAtlas/specs/088-remove-tenant-graphoptions-legacy/research.md

3.6 KiB

Research — Remove Legacy Tenant Graph Options

Goal

Remove usage of the deprecated tenant-based Graph options accessor ($tenant->graphOptions() / Tenant::graphOptions()) and make provider-based resolution (ProviderConnectionResolver + ProviderGateway) the single source of truth for Microsoft Graph configuration.

Decisions

Decision: Provider connection is canonical for Graph options

  • Chosen: Resolve ProviderConnection via App\Services\Providers\ProviderConnectionResolver::resolveDefault($tenant, 'microsoft'), then derive options via App\Services\Providers\ProviderGateway::graphOptions($connection, $overrides).
  • Rationale: Existing services already follow this path (e.g. onboarding/health); it centralizes credential storage and validation.
  • Alternatives considered:
    • Keep reading tenant columns (Tenant.app_client_id / Tenant.app_client_secret) as fallback — rejected (explicitly forbidden; introduces mixed credential sources).

Decision: Kill-switch behavior for Tenant::graphOptions()

  • Chosen: Keep method for now, but make it throw a clear exception (fail-fast).
  • Rationale: Ensures any missed call site fails deterministically in CI/runtime.
  • Alternatives considered:
    • Delete method outright — rejected (higher risk / more disruptive API break).

Decision: Guardrail scope and matching rules

  • Chosen: Add a CI guard test that scans app/ only for these forbidden patterns:
    • \$tenant->graphOptions(
    • Tenant::graphOptions(
  • Rationale: Matches the agreed scope and avoids false positives on unrelated graphOptions(...) methods (e.g. ProviderGateway::graphOptions).
  • Alternatives considered:
    • Scan for all ->graphOptions( — rejected (would flag many legitimate methods).
    • Scan tests/ too — rejected (would create noisy failures during refactors).

Current Code Findings (Call Sites)

Deprecated tenant accessor call sites (must be removed)

These are current app/ references to $tenant->graphOptions():

  • app/Services/AssignmentRestoreService.php
  • app/Services/AssignmentBackupService.php
  • app/Services/Intune/RestoreRiskChecker.php
  • app/Services/Intune/TenantPermissionService.php
  • app/Services/Intune/VersionService.php
  • app/Services/Intune/FoundationMappingService.php
  • app/Services/Intune/PolicyCaptureOrchestrator.php
  • app/Services/Intune/FoundationSnapshotService.php
  • app/Services/Intune/TenantConfigService.php (wraps + returns $tenant->graphOptions())
  • app/Services/Intune/ConfigurationPolicyTemplateResolver.php
  • app/Services/Directory/EntraGroupSyncService.php
  • app/Services/Directory/RoleDefinitionsSyncService.php
  • app/Services/Graph/AssignmentFilterResolver.php

Canonical provider-based examples to copy

  • app/Services/Intune/RbacOnboardingService.php
  • app/Services/Intune/RbacHealthService.php
  • app/Services/Intune/RestoreService.php
  • app/Services/Intune/PolicySnapshotService.php

Source of legacy accessor

  • app/Models/Tenant.php defines the deprecated graphOptions() method.
$resolution = $this->providerConnections()->resolveDefault($tenant, 'microsoft');

if (! $resolution->resolved || ! $resolution->connection instanceof ProviderConnection) {
    // Fail fast with actionable reason.
}

$connection = $resolution->connection;
$graphOptions = $this->providerGateway()->graphOptions($connection, $overrides);

Notes / Non-goals

  • No database schema changes (tenant credential columns stay for now).
  • No changes to Graph contract registry (config/graph_contracts.php) are required for this feature; this work changes configuration sourcing only.