TenantAtlas/specs/112-list-expand-parity/data-model.md
ahmido 32c3a64147 feat(112): LIST $expand parity + Entra principal names (#136)
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
2026-02-25 23:54:20 +00:00

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 $select fields
  • allowed_expand (string[]): allowlisted $expand expansions (exact-match)

Query options (caller input)

Options are passed as an array $options into GraphClientInterface::listPolicies($policyType, $options). Relevant keys:

  • select (string|string[]): optional $select input
  • expand (string|string[]): optional $expand input
  • filter (string): optional $filter
  • top (int): optional $top
  • platform (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 $select and $expand are sent as comma-separated strings
  • warnings (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

  • $expand values must match allowed_expand exactly.
  • $expand normalization: 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.