TenantAtlas/specs/128-rbac-baseline-compare/plan.md
ahmido ef41c9193a feat: add Intune RBAC baseline compare support (#156)
## Summary
- add Intune RBAC Role Definition baseline scope support, capture references, compare classification, findings evidence, and landing/detail UI labels
- keep Intune Role Assignments explicitly excluded from baseline compare scope, summaries, findings, and restore messaging
- add focused Pest coverage for baseline scope selection, capture, compare behavior, recurrence, isolation, findings rendering, inventory anchoring, and RBAC summaries

## Verification
- `vendor/bin/sail bin pint --dirty --format agent`
- `vendor/bin/sail artisan test --compact tests/Unit/Inventory/InventoryPolicyTypeMetaBaselineSupportTest.php tests/Unit/Baselines/BaselinePolicyVersionResolverTest.php tests/Unit/Baselines/BaselineScopeTest.php tests/Unit/IntuneRoleDefinitionNormalizerTest.php tests/Feature/Baselines/BaselineCaptureRbacRoleDefinitionsTest.php tests/Feature/Baselines/BaselineCompareRbacRoleDefinitionsTest.php tests/Feature/Baselines/BaselineCompareDriftEvidenceContractRbacTest.php tests/Feature/Baselines/BaselineCompareCoverageGuardTest.php tests/Feature/Baselines/BaselineCompareCrossTenantMatchTest.php tests/Feature/Baselines/BaselineCompareFindingRecurrenceKeyTest.php tests/Feature/Baselines/BaselineCompareWhyNoFindingsReasonCodeTest.php tests/Feature/Filament/BaselineProfileFoundationScopeTest.php tests/Feature/Filament/BaselineSnapshotRbacRoleDefinitionsTest.php tests/Feature/Filament/BaselineCompareLandingRbacLabelsTest.php tests/Feature/Filament/FindingViewRbacEvidenceTest.php tests/Feature/Findings/FindingRecurrenceTest.php tests/Feature/Findings/DriftStaleAutoResolveTest.php tests/Feature/Inventory/InventorySyncButtonTest.php tests/Feature/Inventory/InventorySyncServiceTest.php tests/Feature/RunAuthorizationTenantIsolationTest.php`
- result: `71 passed (467 assertions)`

## Filament / Platform Notes
- Livewire compliance: unchanged and compatible with Livewire v4.0+
- Provider registration: no panel/provider changes; `bootstrap/providers.php` remains the registration location
- Global search: no new globally searchable resource added; existing global search behavior is unchanged
- Destructive actions: no new destructive actions introduced; existing confirmed actions remain unchanged
- Assets: no new Filament assets introduced; deploy asset handling remains unchanged, including `php artisan filament:assets`
- Testing plan covered: baseline profile scope, snapshot detail, compare job, findings recurrence, findings detail, compare landing labels, inventory sync anchoring, and tenant isolation

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #156
2026-03-09 18:49:20 +00:00

16 KiB
Raw Permalink Blame History

Implementation Plan: Intune RBAC Baseline Compare & Findings v1

Branch: feat/128-rbac-baseline-compare | Date: 2026-03-09 | Spec: specs/128-rbac-baseline-compare/spec.md Input: Feature specification from specs/128-rbac-baseline-compare/spec.md

Summary

Extend the existing baseline capture and compare engine to support one additional foundation type, intuneRoleDefinition, by introducing explicit baseline-support metadata, capturing Role Definition baseline references from the existing RBAC version history, and adding an ID-based normalized compare path that emits unified baseline.compare findings with RBAC-specific severity, evidence, and summaries. Keep intuneRoleAssignment explicitly excluded from baseline scope, compare, and findings.

Technical Context

Language/Version: PHP 8.4
Primary Dependencies: Laravel 12, Filament v5, Livewire v4
Storage: PostgreSQL via Laravel Sail
Testing: Pest v4 on PHPUnit 12
Target Platform: Dockerized Laravel web application (Sail)
Project Type: Web application
Performance Goals: Baseline capture and compare stay chunked and DB-first, avoid UI-time Graph calls, and remain bounded by covered in-scope subjects
Constraints: Existing Ops-UX OperationRun contract, numeric-only summary_counts, deny-as-not-found tenant/workspace isolation, no RBAC write path, and deterministic compare/finding identity
Scale/Scope: Workspace-owned baseline profiles and snapshots plus tenant-scoped compare runs and findings across potentially large in-scope baseline subject sets

Constitution Check

GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.

  • Inventory-first: PASS — current state remains InventoryItem plus existing RBAC PolicyVersion evidence from Spec 127; baseline snapshots stay immutable.
  • Read/write separation: PASS — this release is read-only governance analysis; no restore or Graph write behavior is added.
  • Graph contract path: PASS — existing intuneRoleDefinition and intuneRoleAssignment contracts already exist in config/graph_contracts.php; compare and UI rendering remain DB-only at runtime.
  • Deterministic capabilities: PASS — scope eligibility can be derived from config metadata and helper filters with tests; no raw capability strings are needed.
  • RBAC-UX planes and isolation: PASS — workspace baseline management stays in the tenant/admin plane, compare and findings stay tenant-context, and cross-plane behavior is unchanged.
  • Workspace isolation: PASS — baseline profiles and snapshots remain workspace-owned; workspace membership enforcement remains unchanged.
  • Tenant isolation: PASS — compare runs, current inventory, and findings remain tenant-owned and deny-as-not-found for non-members.
  • Destructive confirmation: PASS — no new destructive RBAC action is introduced; existing baseline archive/capture surfaces keep their current confirmation semantics.
  • Global search: PASS — no new searchable resource is added, so the existing global-search rules are unaffected.
  • Run observability: PASS — baseline capture and compare already use OperationRun and queued jobs; this release only extends their scope and result payloads.
  • Ops-UX 3-surface feedback: PASS — baseline start surfaces already use queued-only toasts and canonical run links.
  • Ops-UX lifecycle: PASS — OperationRun transitions remain service-owned through OperationRunService.
  • Ops-UX summary counts: PASS — RBAC-specific counts can live in context.baseline_compare while summary_counts stay numeric-only.
  • Ops-UX guards: PASS — existing baseline compare guard tests can be extended for the new foundation type.
  • Ops-UX system runs: PASS — unchanged; initiator-null behavior remains handled by the existing operation framework.
  • Data minimization: PASS — inventory remains metadata-only; baseline evidence reuses existing version references and normalized snapshots without introducing secret-bearing payloads.
  • Badge semantics: PASS — severity and label additions can use the centralized badge and renderer system.
  • Filament Action Surface Contract: PASS — only existing Baseline Profile, Baseline Snapshot, Baseline Compare, and Findings surfaces are extended; no new resource or unreviewed action surface is introduced.
  • Filament UX-001: PASS — the feature only adjusts scope options, summaries, and evidence blocks inside established sectioned forms and detail screens.
  • Filament v5 / Livewire v4 compliance: PASS — no version or API changes are introduced.
  • Provider registration (bootstrap/providers.php): PASS — no new providers or panels are introduced.
  • Global search resource rule: PASS — no new globally searchable resource is added.
  • Asset strategy: PASS — no new panel or shared assets are needed; filament:assets deployment behavior is unchanged.

Project Structure

Documentation (this feature)

specs/128-rbac-baseline-compare/
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── contracts/
│   └── openapi.yaml
├── checklists/
│   └── requirements.md
└── tasks.md

Source Code (repository root)

app/
├── Filament/
│   ├── Pages/
│   │   └── BaselineCompareLanding.php
│   └── Resources/
│       ├── BaselineProfileResource.php
│       └── BaselineSnapshotResource.php
├── Jobs/
│   ├── CaptureBaselineSnapshotJob.php
│   └── CompareBaselineToTenantJob.php
├── Models/
│   ├── BaselineProfile.php
│   ├── BaselineSnapshot.php
│   ├── BaselineSnapshotItem.php
│   ├── Finding.php
│   ├── InventoryItem.php
│   └── PolicyVersion.php
├── Services/
│   ├── Baselines/
│   │   ├── BaselineCaptureService.php
│   │   ├── BaselineCompareService.php
│   │   ├── BaselineAutoCloseService.php
│   │   ├── BaselineSnapshotIdentity.php
│   │   └── Evidence/
│   ├── Intune/
│   │   ├── IntuneRoleDefinitionNormalizer.php
│   │   └── PolicyNormalizer.php
│   └── Inventory/
│       └── InventorySyncService.php
├── Support/
│   ├── Baselines/
│   │   ├── BaselineCompareReasonCode.php
│   │   ├── BaselineScope.php
│   │   └── BaselineSubjectKey.php
│   ├── Badges/
│   └── Inventory/
│       └── InventoryPolicyTypeMeta.php
config/
├── graph_contracts.php
└── tenantpilot.php
tests/
├── Feature/
│   ├── Baselines/
│   ├── Findings/
│   ├── Filament/
│   └── Inventory/
└── Unit/
    └── IntuneRoleDefinitionNormalizerTest.php

Structure Decision: Keep all work in the existing Laravel application. The feature is a targeted extension of current baseline capture/compare services, baseline Filament surfaces, config-driven type metadata, and existing baseline/findings tests.

Complexity Tracking

No constitution violations are required for this feature.

Phase 0 — Outline & Research (DONE)

Outputs:

  • specs/128-rbac-baseline-compare/research.md

Key findings captured:

  • Baseline scope already supports foundation_types, but the current UI options list every configured foundation type without an explicit baseline-support gate.
  • Baseline capture already queries scope->allTypes() and can include foundations, but current subject identity is display-name-derived via BaselineSubjectKey, which is not acceptable for Role Definition compare.
  • Compare and finding upsert infrastructure already supports unified lifecycle, evidence provenance, coverage guards, and recurrence logic; this feature should plug into those seams rather than inventing a separate RBAC compare engine.

Phase 1 — Design & Contracts (DONE)

Outputs:

  • specs/128-rbac-baseline-compare/data-model.md
  • specs/128-rbac-baseline-compare/contracts/openapi.yaml
  • specs/128-rbac-baseline-compare/quickstart.md

Design highlights:

  • Add explicit baseline-compare metadata on foundation types so intuneRoleDefinition is opt-in supported and intuneRoleAssignment remains explicitly unsupported.
  • Extend baseline capture and compare with a Role Definition ID identity strategy while leaving the existing display-name subject-key flow in place for previously supported policy types.
  • Reuse IntuneRoleDefinitionNormalizer::flattenForDiff() for normalized governance diffs and feed the result into the existing baseline evidence and finding contracts.

Phase 1 — Agent Context Update (DONE)

Run:

  • .specify/scripts/bash/update-agent-context.sh copilot

Phase 2 — Implementation Plan

Step 1 — Add explicit baseline-support metadata for foundation types

Goal: implement FR-128-01 through FR-128-04.

Changes:

  • Extend config/tenantpilot.php foundation-type rows with explicit baseline-compare support metadata instead of implicitly treating every foundation as baseline-eligible.
  • Mark intuneRoleDefinition as supported for baseline compare and intuneRoleAssignment as unsupported.
  • Add helper accessors in App\Support\Inventory\InventoryPolicyTypeMeta for baseline-supported foundations so scope selection, summaries, and tests read from one canonical source.
  • Update BaselineProfileResource::foundationTypeOptions() and related infolist formatting to show only eligible foundation types while making the RBAC Role Definition label explicit.

Tests:

  • Add or update focused tests proving intuneRoleDefinition is baseline-supported and intuneRoleAssignment is excluded.
  • Add or update baseline profile selection tests proving Role Definitions can be chosen and Assignments cannot leak into the saved scope.

Step 2 — Introduce Role Definition ID identity for baseline capture and compare

Goal: implement FR-128-05 through FR-128-10.

Changes:

  • Extend the baseline subject identity model so intuneRoleDefinition can use stable external ID matching instead of the current display-name-derived BaselineSubjectKey flow.
  • Update CaptureBaselineSnapshotJob::collectInventorySubjects() and buildSnapshotItems() to preserve the tenant Role Definition ID, a workspace-safe external reference, and an explicit identity marker for Role Definition baseline items.
  • Update compare-side loaders in CompareBaselineToTenantJob so Role Definitions are loaded and matched by Role Definition ID while existing policy types keep their current behavior.
  • Ensure delete-and-recreate with a new ID resolves to missing + unexpected, not a silent rename match.

Tests:

  • Add capture tests proving baseline snapshot items for intuneRoleDefinition keep evidence-ready references and exclude intuneRoleAssignment.
  • Add compare tests proving same-name/different-ID Role Definitions do not match and instead produce missing/unexpected outcomes.

Step 3 — Add normalized RBAC Role Definition diffing and classification

Goal: implement FR-128-11 through FR-128-19.

Changes:

  • Introduce a narrow Role Definition compare helper that uses IntuneRoleDefinitionNormalizer::flattenForDiff() as the governance-normalized diff surface.
  • Define classification logic for unchanged, modified, missing, and unexpected Role Definitions.
  • Split modified Role Definition diffs into metadata-only versus permission-impacting changes so severity can map to Low versus High.
  • Reuse existing coverage-guard and evidence-gap handling so provider or permission issues suppress false findings instead of inventing RBAC-only failure semantics.

Tests:

  • Add normalized diff tests that prove ordering noise in permission blocks is ignored.
  • Add compare classification tests for unchanged, modified, missing, and unexpected Role Definitions.
  • Add severity tests proving permission changes are High, missing is High, unexpected is Medium, and metadata-only is Low.

Step 4 — Extend finding evidence, fingerprints, and run summaries for RBAC

Goal: implement FR-128-18 through FR-128-26.

Changes:

  • Implement the intuneRoleDefinition finding fingerprint composition explicitly in the compare job so the stable fingerprint includes baseline profile scope, Role Definition identity, change kind, and normalized diff fingerprint inputs.
  • Reuse baseline compare finding upsert and recurrence behavior, keeping fingerprints recurrence-stable and profile-scoped while adding intuneRoleDefinition-specific diff fingerprints as evidence inputs.
  • Extend the evidence contract builder to emit an RBAC-specific summary.kind, readable before/after normalized evidence, baseline and current version references, and built-in/custom visibility.
  • Extend compare-run context with an RBAC Role Definition summary bucket: total compared, unchanged, modified, missing, and unexpected.
  • Update label and presentation helpers so findings and run detail surfaces identify these records as Intune RBAC Role Definition drift, not generic policy drift.

Tests:

  • Add or update evidence contract tests for modified, missing, and unexpected RBAC Role Definition findings.
  • Add fingerprint/idempotency tests for repeated identical compare runs and recurrence tests for resolved-then-reappearing RBAC drift.
  • Add summary serialization tests for the RBAC run-level counts.

Step 5 — Extend existing Filament surfaces without introducing RBAC restore semantics

Goal: implement FR-128-24 through FR-128-29 and the UX-001 layout and UI Action Matrix constraints already defined in the spec and constitution.

Changes:

  • Update existing baseline profile, baseline snapshot, compare landing or run detail, and findings detail surfaces to surface Role Definition scope, summary counts, RBAC-specific wording, and readable evidence blocks.
  • Keep action surfaces unchanged except for the new scope option and evidence presentation; no new destructive or restore actions are introduced.
  • Use existing badge and tag renderers for any new severity or compare-state display values.
  • Ensure no screen text implies Role Assignment coverage or executable RBAC restore.

Tests:

  • Add or update Filament tests asserting the baseline profile scope picker shows Intune Role Definition and not Intune Role Assignment.
  • Add or update UI tests for compare landing and finding detail labels so RBAC findings are clearly labeled, show readable evidence, and do not imply restore support.

Step 6 — Preserve safe degradation, auditability, and isolation semantics

Goal: implement FR-128-28 through FR-128-30 and the failure-path test requirements.

Changes:

  • Reuse compare coverage and evidence-gap reason codes for RBAC so unavailable current-state data results in warning or partial-success outcomes instead of false drift.
  • Ensure RBAC compare audit events and OperationRun.context capture effective scope, RBAC compare counts, and suppression reasons without adding non-canonical summary_counts keys.
  • Confirm workspace and tenant scoping on compare queries, finding upserts, and UI read paths.

Tests:

  • Add isolation coverage ensuring one tenants Role Definition baseline items and findings cannot match another tenants current state.
  • Add failure-path tests proving provider or permission gaps emit zero false RBAC findings.
  • Keep existing baseline compare coverage-guard, run-authorization, and stale auto-close tests passing.

Post-design Constitution Re-check

Expected: PASS.

  • Livewire v4.0+ compliance: unchanged and preserved because no new Filament panel or Livewire version changes are introduced.
  • Provider registration location: unchanged; no new providers or panel registration changes outside bootstrap/providers.php.
  • Globally searchable resources: unchanged; no new Resource is added, so no new Edit/View global-search requirement applies.
  • Destructive actions: unchanged; existing baseline archive actions remain confirmed and no new destructive RBAC action is added.
  • Asset strategy: unchanged; no new assets are introduced, so the existing deploy-time php artisan filament:assets behavior remains sufficient.
  • Testing plan: extend focused Pest coverage for baseline eligibility, scope selection, capture references, compare classification, evidence, severity, fingerprinting, assignment exclusion, isolation, failure paths, and unchanged baseline behavior for existing supported types.