TenantAtlas/specs/076-permissions-enterprise-ui/research.md

99 lines
6.3 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 — Spec 076 (Permissions Enterprise UI)
## Decisions
### 1) Build a dedicated tenant-scoped Filament Page
- Decision: Implement a new Filament Page under the tenant panel route (`/admin/t/{tenant}/...`) for the “Required permissions” enterprise remediation UX.
- Rationale:
- The existing `TenantResource` infolist entry is a raw list; Spec 076 requires a two-layer remediation layout (overview + matrix) and feature grouping.
- A dedicated Page can provide operator-first UX without bloating the tenant detail resource.
- Alternatives considered:
- Extending the existing `TenantResource` view: rejected because it couples a complex remediation UI to a general-purpose resource view and makes verification deep-linking/clustering harder.
### 2) Use stored + config-based data only at render time (DB-only render)
- Decision: The page loads required permissions from `config('intune_permissions.permissions')` and granted/missing statuses from the `tenant_permissions` table via `TenantPermissionService::compare($tenant, persist: false, liveCheck: false, useConfiguredStub: false)`.
- Rationale:
- Satisfies FR-076-008 (no external network calls during page view).
- Reuses existing data normalization and status modeling.
- Alternatives considered:
- Calling Graph on page view (`liveCheck: true`): rejected (explicitly out of scope and violates DB-only render).
### 3) Authorization semantics: non-member 404, member missing capability 403
- Decision:
- Non-member tenant access remains deny-as-not-found (404) via the Admin panel middleware (`DenyNonMemberTenantAccess`).
- Page access is capability-gated via `Page::canAccess()` using `Capabilities::TENANT_VIEW`.
- Rationale:
- Matches Constitution RBAC-UX-002 and RBAC-UX-003.
- Ensures correct semantics for both initial request and Livewire requests.
- Alternatives considered:
- Enforcing capability only in `mount()` with custom `abort(...)`: rejected because `canAccess()` is the consistent Filament entry-point gate and keeps nav hiding in sync.
### 4) Badge semantics: use centralized domains only
- Decision:
- Per-permission badges: `BadgeDomain::TenantPermissionStatus`.
- Overall status badge: `BadgeDomain::VerificationReportOverall` with values from `VerificationReportOverall`.
- Rationale:
- Constitution BADGE-001 requires centralized semantic mapping.
### 5) Filters/search implementation: server-side Livewire state, in-memory filtering
- Decision: Represent filter/search state as Livewire properties on the Page and filter the already-loaded permission array in-memory.
- Rationale:
- Dataset size is small (config-defined permissions), so in-memory filtering is fast and stable.
- Enables programmatic tests for filtering/copy payload generation without relying on browser JS.
- Alternatives considered:
- Filament `Tables` with query-backed filters: rejected as unnecessary complexity for a config-driven list.
- Pure client-side Alpine filtering: rejected due to weaker automated testability.
### 6) Copy-to-clipboard: “copy payload modal” + robust clipboard fallback
- Decision: Implement copy actions that open a modal (or inline panel) containing the exact newline-separated payload; a “Copy” button uses the existing robust Alpine clipboard fallback pattern.
- Rationale:
- Clipboard APIs are browser-only; Livewire actions cannot write directly to clipboard.
- Reuses proven fallback approach in `resources/views/filament/partials/json-viewer.blade.php`.
- Makes copy output auditable/visible before copying (enterprise-friendly).
- Alternatives considered:
- Attempting server-side copy: not possible.
### 7) Verify-step clustering: emit clustered checks in `verification_report`
- Decision:
- Extend the queued verification job (`ProviderConnectionHealthCheckJob`) to write a verification report that includes the existing connection check plus 56 permission cluster checks.
- Update the onboarding wizard Verify step to render `OperationRun.context.verification_report` (via `VerificationReportViewer`) and show checks issues-first.
- Rationale:
- The project already has a report schema (`VerificationReportSchema`) and writer (`VerificationReportWriter`).
- Clustering in the report keeps the experience consistent across the wizard and operation-run detail views.
- Alternatives considered:
- Cluster in Blade only: rejected because it does not affect summary/overall and can drift between views.
### 8) Enterprise correctness: refresh Observed permissions during the verification run
- Decision:
- The queued verification run (Operation Run) attempts a live Graph refresh for Observed permissions and persists it to `tenant_permissions`.
- Viewer surfaces (Required Permissions page, onboarding Verify step, operation run viewer) remain DB-only at render time.
- If the refresh fails (429/network), permission clusters degrade to warnings with retry guidance and MUST NOT assert “missing permissions” based on stale/empty inventory.
- Rationale:
- Prevents false “missing” findings when the stored inventory is empty/stale.
- Keeps all external calls in the queued run, maintaining the DB-only render rule.
## Open questions resolved (NEEDS CLARIFICATION → decision)
### “Enabled features” for impact summary
- Decision: In Spec 076 scope, treat “enabled features” as the feature tags present in `config('intune_permissions.permissions')`.
- Rationale: There is no current per-tenant feature-enable registry in the codebase; feature tags already exist and are deterministic.
- Future upgrade path: If/when tenant-specific enablement exists, compute relevance by intersecting enabled features with permission feature tags.
## Check cluster proposal (stable keys)
Target: 57 checks; issues-first.
- `provider.connection.check` (existing)
- `permissions.admin_consent` (overall admin consent / application permissions missing)
- `permissions.directory_groups`
- `permissions.intune_configuration`
- `permissions.intune_apps`
- `permissions.intune_rbac_assignments`
- `permissions.scripts_remediations` (optional / skip when irrelevant)
Each permission-derived check:
- Pass: no missing permissions in its mapped set
- Fail/Blocked: any missing required permission in its set
- Skip: cluster mapped permissions set is empty (or feature not relevant)
- Next step: “Open required permissions” deep link to the new page (optionally pre-filtered by Feature).