TenantAtlas/specs/128-rbac-baseline-compare/research.md
2026-03-09 19:43:13 +01:00

58 lines
5.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Research — Intune RBAC Baseline Compare & Findings v1
## Decision: Add explicit baseline-compare support metadata on foundation types
**Rationale**: `BaselineProfileResource::foundationTypeOptions()` currently lists all foundation types from `InventoryPolicyTypeMeta::foundations()`, which would make every configured foundation implicitly selectable for baseline compare. The spec requires intentional, type-level baseline eligibility metadata so only `intuneRoleDefinition` becomes eligible and `intuneRoleAssignment` stays excluded.
**Alternatives considered**:
- Filter foundation options with hard-coded type checks in Filament resource code: rejected because it hides eligibility rules in UI code and does not create a reusable source of truth.
- Treat every foundation as baseline-eligible by default: rejected because the spec explicitly disallows broad foundation rollout.
## Decision: Reuse the existing RBAC Graph contracts and inventory/version evidence from Spec 127
**Rationale**: `config/graph_contracts.php` already contains inventory-grade contracts for `intuneRoleDefinition` and `intuneRoleAssignment`, and Spec 127 already established tenant-scoped inventory plus immutable `PolicyVersion` evidence for Role Definitions. The compare feature can therefore stay DB-first and does not need new live Graph calls for baseline rendering or run detail.
**Alternatives considered**:
- Add new RBAC-specific Graph contracts for compare: rejected because current contracts already include the fields needed for normalized Role Definition diffs.
- Re-hydrate live Graph payloads during compare: rejected because it would violate the existing DB-first compare and monitoring model and introduce misleading drift when live state is unavailable.
## Decision: Use Role Definition ID as the compare identity for this foundation type
**Rationale**: Current baseline capture and compare use display-name-derived `BaselineSubjectKey` matching. That is adequate for some policy surfaces but does not meet the spec requirement for Role Definition identity, where a delete-and-recreate with a new ID is meaningful drift. The capture and compare pipeline needs a targeted identity-model extension for `intuneRoleDefinition` so baseline items and current inventory match by stable external ID.
**Alternatives considered**:
- Keep display-name matching and treat renamed duplicates as ambiguous: rejected because it would hide recreated roles and violate the specs identity model.
- Match by a composite of display name plus built-in/custom state: rejected because the object identity still changes when a role is recreated.
## Decision: Reuse `IntuneRoleDefinitionNormalizer::flattenForDiff()` as the normalized compare surface
**Rationale**: The existing normalizer already produces deterministic, order-insensitive permission-block output and readable summary blocks for Role Definitions. Reusing that normalization path keeps diff semantics aligned with the inventory and version-display model from Spec 127 and avoids a second RBAC diff representation.
**Alternatives considered**:
- Hash raw snapshot payloads directly: rejected because transport noise and ordering differences would create unstable findings.
- Build a separate RBAC-only diff normalizer: rejected because the existing normalizer already captures the governance-relevant fields required by the spec.
## Decision: Keep unified baseline.compare finding lifecycle and recurrence behavior
**Rationale**: `CompareBaselineToTenantJob::upsertFindings()` already provides profile-scoped recurrence keys, idempotent `times_seen` updates, reopen behavior, and baseline auto-close support. The RBAC feature should plug into that same lifecycle so findings behave consistently with the rest of the drift engine.
**Alternatives considered**:
- Introduce an RBAC-specific finding table or source lifecycle: rejected because it would duplicate the compare engine and break existing findings surfaces.
- Make recurrence snapshot-scoped for RBAC only: rejected because the current implementation and tests are profile-scoped, and the new spec only requires baseline profile participation in the fingerprint inputs.
## Decision: Extend the existing evidence contract instead of inventing an RBAC-only UI model
**Rationale**: Existing baseline compare evidence already carries summary kind, provenance, baseline and current version references, and diff-compatible structures consumed by findings and run-detail surfaces. RBAC compare should extend those contracts with an RBAC-specific summary kind and before/after normalized evidence so existing UI surfaces can render the new findings without a dedicated page.
**Alternatives considered**:
- Render RBAC changes from ad-hoc JSON blobs in the UI: rejected because it would bypass the unified finding evidence contract and create fragile presentation logic.
- Create a new RBAC findings screen: rejected because the spec explicitly requires compatibility with existing baseline, drift, and findings surfaces.
## Decision: Reuse existing coverage-guard and partial-success semantics for RBAC safe degradation
**Rationale**: `CompareBaselineToTenantJob` already suppresses findings for uncovered types and records partial-success outcomes with coverage reason codes. RBAC compare should use the same mechanism so missing provider or permission coverage produces a clear run/report warning without emitting false drift.
**Alternatives considered**:
- Emit missing findings when current RBAC data is unavailable: rejected because it would create false drift.
- Fail the entire compare run hard on any RBAC coverage gap: rejected because the current engine already supports safer partial-success semantics with explicit suppression reasons.