## Summary - add Intune RBAC role definitions and role assignments as foundation-backed inventory, backup, and versioned snapshot types - add RBAC-specific normalization, coverage, permission-warning handling, and preview-only restore safety behavior across existing Filament and service surfaces - add spec 127 artifacts, contracts, audits, and focused regression coverage for inventory, backup, versioning, verification, and authorization behavior ## Testing - `vendor/bin/sail bin pint --dirty --format agent` - `vendor/bin/sail artisan test --compact tests/Feature/Inventory/InventorySyncServiceTest.php tests/Feature/Filament/InventoryCoverageTableTest.php tests/Feature/FoundationBackupTest.php tests/Feature/Filament/RestoreExecutionTest.php tests/Feature/RestoreUnknownPolicyTypeSafetyTest.php tests/Unit/GraphContractRegistryTest.php tests/Unit/FoundationSnapshotServiceTest.php tests/Feature/Verification/IntuneRbacPermissionCoverageTest.php tests/Unit/IntuneRoleDefinitionNormalizerTest.php tests/Unit/IntuneRoleAssignmentNormalizerTest.php` ## Notes - tasks in `specs/127-rbac-inventory-backup/tasks.md` are complete except `T041`, which is the documented manual QA validation step Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #155
83 lines
9.0 KiB
Markdown
83 lines
9.0 KiB
Markdown
# Research — Intune RBAC Inventory & Backup v1 (127)
|
||
|
||
## Decisions
|
||
|
||
### Decision 1 — Register RBAC as two new foundation types
|
||
- **Chosen**: Add `intuneRoleDefinition` and `intuneRoleAssignment` to `config/tenantpilot.php` under `foundation_types`, with category `RBAC`, deterministic risk metadata, and `restore: preview-only`.
|
||
- **Rationale**: Existing foundation support is config-driven. `InventorySyncService`, `BackupService`, `InventoryPolicyTypeMeta`, `BackupItem::isFoundation()`, and Filament coverage/backup surfaces already derive classification and restore/risk semantics from `config('tenantpilot.foundation_types')`.
|
||
- **Alternatives considered**:
|
||
- Model RBAC as supported policy types rather than foundations. Rejected because the product and existing architecture already distinguish governance foundations from policy families.
|
||
- Merge definitions and assignments into a single RBAC type. Rejected because payloads, risks, and future compare/restore semantics differ materially.
|
||
|
||
### Decision 2 — Use new inventory-grade Graph contract keys for RBAC
|
||
- **Chosen**: Introduce inventory-grade contract keys named `intuneRoleDefinition` and `intuneRoleAssignment` in `config/graph_contracts.php`, while leaving `directoryRoleDefinitions` and `rbacRoleAssignment` intact for current health/onboarding usage.
|
||
- **Rationale**: The repo already contains narrowly scoped RBAC keys used by onboarding and health services. `GraphContractRegistry::directoryRoleDefinitionsListPath()` and `GraphContractRegistry::rbacRoleAssignmentListPath()` are referenced by `RbacOnboardingService` and `RbacHealthService`. Reusing those keys for full-fidelity snapshot capture would silently change behavior in existing flows.
|
||
- **Alternatives considered**:
|
||
- Expand the existing keys in place. Rejected because it couples inventory fidelity requirements to onboarding and health-check behavior, increasing regression risk.
|
||
- Hardcode RBAC endpoints in feature services. Rejected because it violates the constitution’s single contract path to Graph.
|
||
|
||
### Decision 3 — Reuse existing inventory sync persistence through `InventoryItem`
|
||
- **Chosen**: Keep RBAC inventory in `InventoryItem` rows created by `App\Services\Inventory\InventorySyncService`, using sanitized `meta_jsonb` only.
|
||
- **Rationale**: Existing foundation inventory already works this way. `InventorySyncService` merges `supported_policy_types` and `foundation_types`, fetches by configured type, and stores a tenant-scoped `InventoryItem` keyed by `tenant_id + policy_type + external_id`. Existing tests already verify this behavior for `roleScopeTag`.
|
||
- **Alternatives considered**:
|
||
- Add dedicated RBAC inventory tables. Rejected because the repo already has a generic inventory store and the feature does not require custom query semantics.
|
||
- Persist full RBAC payloads in inventory. Rejected because the constitution requires metadata-only inventory and immutable payload storage elsewhere.
|
||
|
||
### Decision 4 — Reuse foundation backup capture for immutable RBAC payloads
|
||
- **Chosen**: Reuse `App\Services\Intune\FoundationSnapshotService` and `App\Services\Intune\BackupService::captureFoundations()` for RBAC payload capture, but create synthetic tenant-scoped `Policy` anchors plus immutable `PolicyVersion` rows for `intuneRoleDefinition` and `intuneRoleAssignment`.
|
||
- **Rationale**: RBAC still enters the system through the existing foundation capture path and `include_foundations`, but linked `PolicyVersion` rows let backup-set detail and policy-version detail reuse the same immutable review surface. This preserves foundation semantics without introducing a separate RBAC snapshot model.
|
||
- **Alternatives considered**:
|
||
- Keep RBAC backup items permanently detached from `PolicyVersion`. Rejected after implementation because it would leave backup-set detail and version detail on different immutable paths and make readable RBAC history inconsistent with the product goal.
|
||
- Create a separate RBAC snapshot model. Rejected because it duplicates current foundation backup behavior and would fragment restore/preview surfaces.
|
||
|
||
### Decision 5 — Add dedicated RBAC normalizers through the existing policy normalizer registry
|
||
- **Chosen**: Implement `IntuneRoleDefinitionNormalizer` and `IntuneRoleAssignmentNormalizer` as `PolicyTypeNormalizer` implementations and register them through the `policy-type-normalizers` tag in `AppServiceProvider`.
|
||
- **Rationale**: `PolicyNormalizer` already resolves type-specific normalizers this way. This keeps RBAC readable in version and backup surfaces while preserving the repo’s normalization strategy for future diffing.
|
||
- **Alternatives considered**:
|
||
- Use the default normalizer only. Rejected because raw RBAC payloads are not sufficiently readable for governance and audit use.
|
||
- Add ad-hoc formatting inside Filament screens. Rejected because it would bypass the centralized normalization path and make future diffs inconsistent.
|
||
|
||
### Decision 6 — Handle missing `DeviceManagementRBAC.Read.All` as a verification warning and capture failure reason
|
||
- **Chosen**: Reuse existing provider reason and verification cluster patterns so missing RBAC read permission results in a clear warning or non-blocking failure reason rather than a pipeline crash.
|
||
- **Rationale**: The repo already has soft-permission patterns in `TenantPermissionCheckClusters`, `ProviderReasonCodes`, `RunFailureSanitizer`, and fail-soft Graph helper code such as `ScopeTagResolver`. Existing RBAC health and onboarding services also route reason codes through `ProviderReasonCodes`.
|
||
- **Alternatives considered**:
|
||
- Let Graph 403 bubble as a generic exception. Rejected because it produces opaque failures and weak operator guidance.
|
||
- Mark missing delegated or application permission as hard-blocking for all inventory. Rejected because the spec explicitly requires graceful handling and partial continuity for unrelated inventory types.
|
||
|
||
### Decision 7 — Preserve preview-only restore by configuration, not special-case UI logic
|
||
- **Chosen**: Rely on existing restore-mode evaluation through config metadata and current restore-risk checks rather than adding RBAC-only branches.
|
||
- **Rationale**: `InventoryPolicyTypeMeta`, `RestoreRiskChecker`, `RestoreService`, and badge tests already treat `preview-only` as a first-class mode. Adding the new foundation types with `restore: preview-only` lets existing restore guards and UI labels work consistently.
|
||
- **Alternatives considered**:
|
||
- Add RBAC-specific UI suppression logic. Rejected because it is redundant and risks diverging from the canonical restore-mode rules.
|
||
|
||
### Decision 8 — Treat Entra admin role reporting as a normalization reference, not a storage model
|
||
- **Chosen**: Reuse payload-shaping ideas from `EntraAdminRolesReportService` for readable role-definition/assignment summaries, but keep Intune RBAC data inside the inventory/foundation architecture.
|
||
- **Rationale**: The Entra reporting code already demonstrates stable role/assignment mapping and fingerprint-friendly sorting, which is useful for readable RBAC normalization. However, it is a separate feature and not a replacement for tenant inventory or backup storage.
|
||
- **Alternatives considered**:
|
||
- Reuse Entra role reporting models directly. Rejected because the domain is different and the feature scope is Intune RBAC foundations, not Entra directory roles.
|
||
|
||
## Resolved Unknowns
|
||
|
||
- **Unknown**: Whether existing RBAC contract keys could be reused safely.
|
||
- **Resolved**: No. Existing keys are already consumed by RBAC onboarding and health flows and should stay backward compatible.
|
||
|
||
- **Unknown**: Whether foundations already had an immutable full-payload path.
|
||
- **Resolved**: Yes. `FoundationSnapshotService` + `BackupService::captureFoundations()` already persists full foundation payloads as `BackupItem` rows.
|
||
|
||
- **Unknown**: Whether inventory stores full payloads or sanitized metadata.
|
||
- **Resolved**: Inventory stores sanitized metadata only in `InventoryItem.meta_jsonb`; full payloads belong in backup/version artifacts.
|
||
|
||
- **Unknown**: Whether preview-only restore needs a new specialized guard.
|
||
- **Resolved**: No. Existing config-driven restore-mode evaluation already supports `preview-only` across coverage and restore paths.
|
||
|
||
## Repo Evidence
|
||
|
||
- Foundation registration baseline: `config/tenantpilot.php`
|
||
- Existing RBAC contract keys: `config/graph_contracts.php`
|
||
- Contract helper usage in onboarding and health: `app/Services/Graph/GraphContractRegistry.php`, `app/Services/Intune/RbacOnboardingService.php`, `app/Services/Intune/RbacHealthService.php`
|
||
- Inventory persistence path: `app/Services/Inventory/InventorySyncService.php`
|
||
- Coverage payload model: `app/Support/Inventory/InventoryCoverage.php`
|
||
- Foundation payload backup path: `app/Services/Intune/FoundationSnapshotService.php`, `app/Services/Intune/BackupService.php`
|
||
- Normalizer registry: `app/Services/Intune/PolicyNormalizer.php`, `app/Providers/AppServiceProvider.php`
|
||
- Permission-warning patterns: `app/Support/Verification/TenantPermissionCheckClusters.php`, `app/Support/Providers/ProviderReasonCodes.php`, `app/Services/Graph/ScopeTagResolver.php`
|