## 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
7.6 KiB
7.6 KiB
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:
intuneRoleDefinitionintuneRoleAssignment
- Required attributes:
typelabelcategory = RBACplatform = allendpointbackuprestore = preview-onlyrisk
Graph Contract Definition
Config-defined Graph fetch contract for inventory-grade RBAC capture.
- Source:
config/graph_contracts.php - New contract keys:
intuneRoleDefinitionintuneRoleAssignment
- Required fields:
resourceallowed_selectallowed_expandtype_familywhen available- optional
hydrationor follow-up fetch hints if full assignment fidelity requires them
- Compatibility rule:
- Existing
directoryRoleDefinitionsandrbacRoleAssignmentkeys remain unchanged for current health/onboarding flows.
- Existing
InventoryItem (tenant-owned observed state)
Represents the latest observed RBAC inventory row per tenant and external object.
- Existing model/table:
InventoryItem - Ownership:
workspace_idNOT NULLtenant_idNOT NULL
- Identity:
- unique by
tenant_id + policy_type + external_id
- unique by
- Relevant fields for RBAC:
policy_type=intuneRoleDefinitionorintuneRoleAssignmentexternal_iddisplay_namecategory = RBACplatform = allmeta_jsonbsanitized, metadata-onlylast_seen_atlast_seen_operation_run_id
meta_jsonbfor 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_jsonbfor 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.intuneRoleDefinitionfoundation_types.intuneRoleAssignment
- Per-type fields:
statusinsucceeded | 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_idNOT NULL- workspace derived via tenant relationship
- RBAC foundation shape:
policy_id= synthetic tenant-scoped policy anchor forintuneRoleDefinitionandintuneRoleAssignmentpolicy_version_id= immutable snapshot row created or reused for the captured RBAC payloadpolicy_type=intuneRoleDefinitionorintuneRoleAssignmentpolicy_identifier= source Graph object idpayload= full immutable RBAC payloadmetadata.displayNamemetadata.kindmetadata.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 idPolicy.policy_type=intuneRoleDefinitionorintuneRoleAssignmentPolicy.metadata.foundation_anchor = truePolicy.metadata.capture_mode = immutable_backup
- RBAC version shape:
PolicyVersion.snapshot= immutable RBAC payloadPolicyVersion.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
namedescriptionis_built_inrole_permissions- normalized permission blocks
- ordered for diff stability
- optional warnings when payload is incomplete or partially expanded
Role Assignment normalized fields
assignment_namerole_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:
keytitlestatus = pass | warn | failseverityblockingreason_codemessageevidence[]next_steps[]
- RBAC v1 target behavior:
- Missing
DeviceManagementRBAC.Read.Allproduces a clear warning or failure reason with actionable next steps. - The rest of the inventory pipeline remains able to complete partially.
- Missing
Relationships
- A
Tenanthas manyInventoryItemrows, including RBAC foundation rows. - A
Tenanthas manyBackupSetrows. - A
BackupSethas manyBackupItemrows, including RBAC foundation payload snapshots. - A synthetic RBAC
Policyhas many immutablePolicyVersionrows and can be referenced by RBACBackupItemrows. - An
OperationRunfor inventory sync carries coverage status for RBAC foundation types incontext.inventory.coverage.foundation_types. - Verification rows and provider reason codes describe permission posture for the same tenant and workspace scope.
Invariants
intuneRoleDefinitionandintuneRoleAssignmentare 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-onlyin v1. - Non-members must not be able to infer RBAC object existence across workspace or tenant boundaries.
- Missing
DeviceManagementRBAC.Read.Allmust 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
BackupItemplus syntheticPolicy/PolicyVersionlinkage 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
restoreandriskmetadata. - RBAC Graph contracts must only request fields allowed by the contract registry.
- Inventory updates require non-empty Graph
idvalues. - Backup foundation snapshots require non-empty
source_idvalues. - Normalizers must preserve identifiers when readable expansions are absent.