TenantAtlas/specs/127-rbac-inventory-backup/data-model.md
ahmido c6e7591d19 feat: add Intune RBAC inventory and backup support (#155)
## 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
2026-03-09 10:40:51 +00:00

200 lines
7.6 KiB
Markdown

# Data Model — Intune RBAC Inventory & Backup v1 (127)
## Entities
### Foundation Type Metadata
Config-defined support metadata for RBAC foundations.
- Source: `config/tenantpilot.php`
- New keys:
- `intuneRoleDefinition`
- `intuneRoleAssignment`
- Required attributes:
- `type`
- `label`
- `category = RBAC`
- `platform = all`
- `endpoint`
- `backup`
- `restore = preview-only`
- `risk`
### Graph Contract Definition
Config-defined Graph fetch contract for inventory-grade RBAC capture.
- Source: `config/graph_contracts.php`
- New contract keys:
- `intuneRoleDefinition`
- `intuneRoleAssignment`
- Required fields:
- `resource`
- `allowed_select`
- `allowed_expand`
- `type_family` when available
- optional `hydration` or follow-up fetch hints if full assignment fidelity requires them
- Compatibility rule:
- Existing `directoryRoleDefinitions` and `rbacRoleAssignment` keys remain unchanged for current health/onboarding flows.
### InventoryItem (tenant-owned observed state)
Represents the latest observed RBAC inventory row per tenant and external object.
- Existing model/table: `InventoryItem`
- Ownership:
- `workspace_id` NOT NULL
- `tenant_id` NOT NULL
- Identity:
- unique by `tenant_id + policy_type + external_id`
- Relevant fields for RBAC:
- `policy_type` = `intuneRoleDefinition` or `intuneRoleAssignment`
- `external_id`
- `display_name`
- `category = RBAC`
- `platform = all`
- `meta_jsonb` sanitized, metadata-only
- `last_seen_at`
- `last_seen_operation_run_id`
- `meta_jsonb` for Role Definitions should include only safe observed metadata such as:
- OData type
- built-in versus custom state
- summary counts or identifiers needed for inventory surfaces
- `meta_jsonb` for Role Assignments should include only safe observed metadata such as:
- linked role definition identifier or display name if available
- assignment target counts or scope counts
- warnings and sanitized support-state metadata
### Inventory Coverage Payload
Canonical coverage status written into `OperationRun.context.inventory.coverage.foundation_types`.
- Existing source: `App\Support\Inventory\InventoryCoverage`
- New keys:
- `foundation_types.intuneRoleDefinition`
- `foundation_types.intuneRoleAssignment`
- Per-type fields:
- `status` in `succeeded | failed | skipped`
- optional `item_count`
- optional `error_code`
### BackupItem (tenant-owned immutable RBAC payload snapshot)
Represents a captured RBAC foundation snapshot inside a backup set.
- Existing model/table: `BackupItem`
- Ownership:
- `tenant_id` NOT NULL
- workspace derived via tenant relationship
- RBAC foundation shape:
- `policy_id` = synthetic tenant-scoped policy anchor for `intuneRoleDefinition` and `intuneRoleAssignment`
- `policy_version_id` = immutable snapshot row created or reused for the captured RBAC payload
- `policy_type` = `intuneRoleDefinition` or `intuneRoleAssignment`
- `policy_identifier` = source Graph object id
- `payload` = full immutable RBAC payload
- `metadata.displayName`
- `metadata.kind`
- `metadata.graph.resource`
- additional metadata warnings or capture details
### Policy / PolicyVersion linkage for RBAC foundations
RBAC foundations reuse the existing policy-version review surfaces by creating synthetic policy anchors.
- Existing models/tables: `Policy`, `PolicyVersion`
- RBAC anchor shape:
- `Policy.external_id` = RBAC source Graph object id
- `Policy.policy_type` = `intuneRoleDefinition` or `intuneRoleAssignment`
- `Policy.metadata.foundation_anchor = true`
- `Policy.metadata.capture_mode = immutable_backup`
- RBAC version shape:
- `PolicyVersion.snapshot` = immutable RBAC payload
- `PolicyVersion.capture_purpose = backup`
- identical protected snapshots are reused for the same synthetic policy instead of creating duplicate versions
### Normalized RBAC Snapshot View Model
Human-readable representation produced for backup/version display and future diff-safe reuse.
#### Role Definition normalized fields
- `name`
- `description`
- `is_built_in`
- `role_permissions`
- normalized permission blocks
- ordered for diff stability
- optional warnings when payload is incomplete or partially expanded
#### Role Assignment normalized fields
- `assignment_name`
- `role_definition`
- preferred: readable name + stable id
- `members`
- readable names when known, ids as fallback
- `scope_members`
- readable names when known, ids as fallback
- `resource_scopes`
- stable ordered values
- optional warnings when referenced objects are unresolved
### Verification Check Row
Provider or tenant verification result describing RBAC permission readiness.
- Existing source pattern: `TenantPermissionCheckClusters`
- Relevant fields:
- `key`
- `title`
- `status = pass | warn | fail`
- `severity`
- `blocking`
- `reason_code`
- `message`
- `evidence[]`
- `next_steps[]`
- RBAC v1 target behavior:
- Missing `DeviceManagementRBAC.Read.All` produces a clear warning or failure reason with actionable next steps.
- The rest of the inventory pipeline remains able to complete partially.
## Relationships
- A `Tenant` has many `InventoryItem` rows, including RBAC foundation rows.
- A `Tenant` has many `BackupSet` rows.
- A `BackupSet` has many `BackupItem` rows, including RBAC foundation payload snapshots.
- A synthetic RBAC `Policy` has many immutable `PolicyVersion` rows and can be referenced by RBAC `BackupItem` rows.
- An `OperationRun` for inventory sync carries coverage status for RBAC foundation types in `context.inventory.coverage.foundation_types`.
- Verification rows and provider reason codes describe permission posture for the same tenant and workspace scope.
## Invariants
- `intuneRoleDefinition` and `intuneRoleAssignment` are separate foundation types everywhere: config, contracts, inventory, backup, coverage, and normalization.
- Inventory rows remain metadata-only and tenant-scoped.
- Full RBAC payloads are stored only in immutable backup/version artifacts.
- Restore mode for both RBAC foundation types is always `preview-only` in v1.
- Non-members must not be able to infer RBAC object existence across workspace or tenant boundaries.
- Missing `DeviceManagementRBAC.Read.All` must produce a stable reason path rather than an opaque exception.
- Role Definitions and Role Assignments must normalize in a stable, ordered, diff-safe manner.
## State Transitions
### Inventory coverage state
- `skipped`
- when foundations are excluded from a run or a type is not attempted
- `failed`
- when the RBAC fetch for that type fails or returns a permission/problem reason
- `succeeded`
- when the RBAC type is fetched and inventory rows are updated successfully
### Backup capture state for RBAC foundations
- not present in backup set
- created as immutable `BackupItem` plus synthetic `Policy` / `PolicyVersion` linkage for version-detail review
- restored from soft-delete if already present in the same backup set and previously archived
### Verification state for RBAC permission readiness
- `pass`
- all required RBAC read permissions are present
- `warn`
- delegated refresh or adjacent non-blocking permission posture issue exists
- `fail`
- required application permission is missing or permission fetch errored in a blocking way
## Validation Rules
- Foundation type config rows must include deterministic `restore` and `risk` metadata.
- RBAC Graph contracts must only request fields allowed by the contract registry.
- Inventory updates require non-empty Graph `id` values.
- Backup foundation snapshots require non-empty `source_id` values.
- Normalizers must preserve identifiers when readable expansions are absent.