TenantAtlas/specs/066-rbac-ui-enforcement-helper/contracts/ui-enforcement.md
2026-01-30 17:49:05 +01:00

2.7 KiB

Contract: UiEnforcement v2 (RBAC UI Enforcement Helper)

Branch: 066-rbac-ui-enforcement-helper-v2
Date: 2026-01-30
Spec: /Users/ahmeddarrazi/Documents/projects/TenantAtlas-066-rbac-ui-enforcement-helper-v2/specs/066-rbac-ui-enforcement-helper/spec.md

Scope

  • Tenant plane only (/admin/t/{tenant}).
  • Platform plane (/system) is out of scope for v2.

RBAC-UX Contract (summary)

  1. Non-member
  • Actions are hidden and cannot execute.
  • Direct access/execution attempts must deny-as-not-found (404) where reachable.
  1. Member without capability
  • Action is visible but disabled.
  • Disabled tooltip is standardized via UiTooltips (default copy is defined in the feature spec).
  • Disabled action cannot execute (Filament no-op is acceptable).
  1. Member with capability
  • Action is enabled and executes.
  • Destructive/high-impact actions require confirmation (->requiresConfirmation()).

Mixed visibility composition (business + RBAC)

preserveVisibility()

Contract

  • The helper MUST NOT write ->visible(...) or ->hidden(...).
  • The helper MAY still set disabled state + tooltip + confirmation + server-side guard.

Restriction

  • Allowed only on tenant-scoped surfaces where routing already denies non-members (404).
  • Forbidden for record-scoped / cross-tenant lists (must not leak discoverability).

andVisibleWhen(callable $businessVisible)

Contract

  • The helper combines business visibility AND RBAC visibility.
  • If business visibility is false, the action is not visible (even for authorized members).

andHiddenWhen(callable $businessHidden)

Contract

  • The helper combines business-hidden OR RBAC-hidden semantics.

Tenant context providers

tenantFromFilament() (default)

  • Uses Filament tenant context (Filament::getTenant() or equivalent).
  • Intended for tenant-scoped pages/actions.

tenantFromRecord()

  • Treats $record as the tenant.
  • Intended for record-scoped surfaces like TenantResource row actions.

tenantFrom(callable $resolver)

  • Maps a record (or selection) to a tenant instance.
  • Intended for “record → tenant” mapping surfaces.

Bulk preflight authorization

Default behavior (v2)

  • All-or-nothing authorization: if any selected record is unauthorized, the bulk action is disabled and cannot execute.
  • Business eligibility is handled separately by the action implementation (e.g., skip inactive/archived records with clear feedback).

Preflight hooks

  • Custom: preflightSelection(callable $preflight)
  • Built-ins:
    • preflightByTenantMembership()
    • preflightByCapability()

Performance

  • Preflight should use set-based DB queries where feasible; avoid N+1 membership checks.