Implements LIST `$expand` parity with GET by forwarding caller-provided, contract-allowlisted expands. Key changes: - Entra Admin Roles scan now requests `expand=principal` for role assignments so `principal.displayName` can render. - `$expand` normalization/sanitization: top-level comma split (commas inside balanced parentheses preserved), trim, dedupe, allowlist exact match, caps (max 10 tokens, max 200 chars/token). - Diagnostics when expands are removed/truncated (non-prod warning, production low-noise). Tests: - Adds/extends unit coverage for Graph contract sanitization, list request shaping, and the EntraAdminRolesReportService. Spec artifacts included under `specs/112-list-expand-parity/`. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #136
1.9 KiB
1.9 KiB
Data Model — Graph Contracts: LIST $expand Parity Fix
Summary
No database schema changes. This feature changes how list queries are constructed and sanitized for Microsoft Graph.
Key entities & shapes
Graph contract (config/graph_contracts.php)
Per policyType contract keys relevant to this feature:
resource(string): Graph resource path (e.g.directory/roleAssignments)allowed_select(string[]): allowlisted$selectfieldsallowed_expand(string[]): allowlisted$expandexpansions (exact-match)
Query options (caller input)
Options are passed as an array $options into GraphClientInterface::listPolicies($policyType, $options).
Relevant keys:
select(string|string[]): optional$selectinputexpand(string|string[]): optional$expandinputfilter(string): optional$filtertop(int): optional$topplatform(string): optional platform filter for contract-specific endpoints- plus tenant/auth context from
MicrosoftGraphOptionsResolver
Sanitized query (outbound)
GraphContractRegistry::sanitizeQuery($policyType, $query) returns:
query(array): sanitized query map, where$selectand$expandare sent as comma-separated stringswarnings(string[]): human-readable warnings for capability safety
Entra role assignment record
entraRoleAssignments list response item is expected to include:
roleDefinitionId(string)principalId(string)principal(object, when expanded)displayName(string)@odata.type(string)
State transitions
None. This is a read-only integration-layer capability change.
Validation rules
$expandvalues must matchallowed_expandexactly.$expandnormalization: trim, split on top-level commas only (string input; do not split inside balanced parentheses), drop empty, dedupe.- Safety caps: max 10 allowed expands; max 200 chars per token.