TenantAtlas/specs/062-tenant-rbac-v1/tasks.md
ahmido eef85af990 062-tenant-rbac-v1 (#74)
Kurzbeschreibung

Implementiert Tenant RBAC v1 (specs/062-tenant-rbac-v1): tenant_memberships, Capability registry/resolver, gates, Filament RelationManager für Tenant→Members, Last‑Owner‑Guard, bootstrap assign/recover (break‑glass), Audit-Logging.
Wichtige Änderungen

Migration: create_tenant_memberships_table (T004) — ausgeführt
Models/Services: TenantMembership, Capabilities, RoleCapabilityMap, CapabilityResolver (T008–T013)
Auth: Gates registriert in AuthServiceProvider.php (T011)
Filament: RelationManager unter Settings → Tenants (Members CRUD + Last‑Owner‑Guard) (T017–T018)
Break‑glass: lokale platform superadmin + persistent banner + bootstrap_recover action (T024–T026)
Audit: Audit‑Einträge für membership actions mit canonical action_ids (T022)
Tests: neue/aktualisierte Feature- und Unit‑Tests (siehe Test‑Abschnitt)
Migrations / Deploy

Run migrations: vendor/bin/sail artisan migrate
Keine neuen Panel‑Assets registriert (kein php artisan filament:assets nötig)
Wenn Frontend nicht sichtbar: vendor/bin/sail npm run dev oder vendor/bin/sail npm run build
Tests (geprüft / neu)

Fokus-Suite ausgeführt für Tenant RBAC (T031).
Neu / aktualisiert:
CapabilitiesRegistryTest
CapabilityResolverTest
TenantSwitcherScopeTest
TenantRouteDenyAsNotFoundTest
TenantMembershipCrudTest
LastOwnerGuardTest
TenantBootstrapAssignTest
MembershipAuditLogTest
BreakGlassRecoveryTest
Befehl zum lokalen Ausführen (minimal): vendor/bin/sail artisan test tests/Feature/TenantRBAC --stop-on-failure
Filament / Sicherheits‑Contract (erforderliche Punkte)

Livewire v4.0+ compliance: bestätigt (Filament v5 target).
Provider registration: keine neue Panel‑Provider-Änderung; falls nötig: providers.php (Laravel 11+).
Globale Suche: keine neuen Ressourcen für Global Search hinzugefügt; vorhandene Ressourcen behalten Edit/View‑Pages unverändert.
Destructive actions: tenant_membership.remove und role‑demote sind destruktive — implemented via Action::make(...)->action(...)->requiresConfirmation() + policy checks.
Asset strategy: keine globalen Assets; on‑demand/load as before. Deployment: filament:assets nicht erforderlich für diese PR.
Testing plan: Livewire/Filament Komponenten + actions abgedeckt — RelationManager CRUD, Last‑Owner‑Guard, BreakGlassRecovery, CapabilityResolver/Registry, Tenant switcher + deny‑as‑not‑found route tests.
Offene/optionale Punkte

T005/T028/T029 (tenant_role_mappings migration + UI + Tests) sind optional und noch nicht umgesetzt.
Checklist (aus tasks.md)

 T001–T003 Discovery
 T004, T006–T007 Migrations (T005 optional)
 T008–T013 Models/Capabilities/Gates
 T014–T016 Tenant isolation & route enforcement
 T017–T021 Membership UI + bootstrap flows
 T022–T023 Audit logging + tests
 T024–T027 Break‑glass flows & tests
 T005, T028, T029 Optional mappings
 T030–T031 Formatting + focused tests
Migration / Test commands to run locally

vendor/bin/sail up -d
vendor/bin/sail artisan migrate
vendor/bin/sail artisan tinker (falls manuell Benutzer/Flags setzen)
vendor/bin/sail artisan test tests/Feature/TenantRBAC --stop-on-failure
Wenn du einen PR‑Titel und Labels willst, schlage ich vor:

Title: feat(062): Tenant RBAC v1 — memberships, capability resolver, break‑glass recovery
Labels: feature, tests, migration

Co-authored-by: Ahmed Darrazi <ahmeddarrazi@MacBookPro.fritz.box>
Reviewed-on: #74
2026-01-25 15:27:39 +00:00

6.0 KiB

Actionable Tasks for Tenant RBAC v1

Feature: Tenant RBAC v1
Branch: 062-tenant-rbac-v1
Plan: specs/062-tenant-rbac-v1/plan.md

This task list is dependency-ordered and test-driven. It implements:

  • Entra (OIDC) identity (no Entra credentials stored)
  • Suite-tenant authorization via tenant_memberships (SoT)
  • Capabilities-first gates/policies (no role checks in feature code)
  • Tenant switcher + direct route enforcement (non-members = 404)
  • Audit logging with canonical action_ids
  • Break-glass platform superadmin recovery

Phase 0 — Discovery / Fit Check

  • T001 [P] Confirm existing auth entrypoints (where OIDC callback/upsert happens) and Filament tenancy resolver (where current tenant is set).
  • T002 [P] Confirm existing User / Tenant models and current schema (do NOT create duplicates). Identify required columns for Entra identity: entra_tenant_id (tid), entra_object_id (oid).
  • T003 [P] Identify existing AuditLog service/model and how to write audit entries (target format + redaction).

Phase 1 — Schema (RBAC source of truth)

  • T004 Create migration create_tenant_memberships_table with:
    • tenant_id, user_id, role (owner|manager|operator|readonly)
    • source (manual|entra_group|entra_app_role|break_glass)
    • source_ref (nullable)
    • created_by_user_id (nullable)
    • unique (tenant_id, user_id) and index (tenant_id, role)
  • T005 (Optional, but supported) Create migration create_tenant_role_mappings_table with:
    • tenant_id, mapping_type (entra_group|entra_app_role), external_id, role, is_enabled
    • unique (tenant_id, mapping_type, external_id)
  • T006 Add/adjust users columns if missing: entra_tenant_id (tid), entra_object_id (oid) + unique index (entra_tenant_id, entra_object_id).
  • T007 Run migrations.

Phase 2 — Models + Capability Registry (capabilities-first)

  • T008 Create app/Support/Auth/Capabilities.php as the canonical allowlist (constants/enum) of capability strings.
  • T009 Create app/Services/Auth/RoleCapabilityMap.php (single source of truth) mapping roles → capabilities.
  • T010 Create app/Services/Auth/CapabilityResolver.php:
    • resolves membership for (user, tenant) once per request (no N+1)
    • answers can($capability) using the registry + map
  • T011 Register Gates in app/Providers/AuthServiceProvider.php using CapabilityResolver (no direct role checks).
  • T012 Add model TenantMembership and (if used) TenantRoleMapping with relationships:
    • Tenant::memberships(), User::tenantMemberships()
  • T013 Unit tests:
    • CapabilitiesRegistryTest: role map only references registry entries
    • CapabilityResolverTest: Owner/Manager/Operator/Readonly mapping works and is deterministic

Phase 3 — Tenant Isolation (switcher + deny-as-not-found)

  • T014 Enforce tenant switcher scoping: only tenants with a membership are listable/selectable for a user.
  • T015 Enforce route-level deny-as-not-found:
    • direct access to /t/{tenant} and tenant-scoped resources returns 404 when user is not a member.
    • member without capability returns 403.
  • T016 Feature tests:
    • TenantSwitcherScopeTest: only membership tenants appear
    • TenantRouteDenyAsNotFoundTest: non-member gets 404 for direct URL

Phase 4 — Suite Tenant Membership Management UI (Tenant → Members)

  • T017 Add a Filament Relation Manager (or equivalent) under Settings → Tenants to manage memberships:
    • list members + role
    • add member (select existing user) + role
    • edit member role
    • remove member
  • T018 Implement Last Owner Guard:
    • prevent removing/demoting last owner membership (clear UI message)
  • T019 Implement Bootstrap assign:
    • on tenant creation, creator becomes Owner (action_id tenant_membership.bootstrap_assign)
  • T020 Implement Bootstrap recover (platform superadmin path):
    • add/assign Owner when needed (action_id tenant_membership.bootstrap_recover)
  • T021 Feature tests:
    • TenantMembershipCrudTest
    • LastOwnerGuardTest
    • TenantBootstrapAssignTest

Phase 5 — Audit Logging (canonical action_ids)

  • T022 Add audit logging for membership and mapping changes with canonical action_ids:

    • tenant_membership.add
    • tenant_membership.role_change
    • tenant_membership.remove
    • tenant_membership.bootstrap_assign
    • tenant_membership.bootstrap_recover
    • tenant_role_mapping.create|update|delete (if mappings are enabled)

    Audit entries must be redacted (no secrets; minimal identity data).

  • T023 Feature test MembershipAuditLogTest ensures audit entries are written on add/change/remove and contain no sensitive fields.


Phase 6 — Break-glass Platform Superadmin (recovery)

  • T024 Implement (or confirm existing) local platform superadmin authentication separate from Entra users.
  • T025 Add a persistent UI banner when authenticated as break-glass.
  • T026 Ensure platform superadmin can manage memberships across all tenants for recovery (at least to add an Owner).
  • T027 Feature test BreakGlassRecoveryTest:
    • can assign owner to tenant
    • actions are audited with bootstrap_recover

Phase 7 — Optional: Entra Mapping (deferred execution in v1)

  • T028 (Optional) Add UI to manage tenant_role_mappings (no Graph calls for resolution in v1).
  • T029 (Optional) Test that mapping records are tenant-scoped and audited on create/update/delete.

Phase 8 — Quality Gates

  • T030 Run formatting: ./vendor/bin/sail php ./vendor/bin/pint --dirty
  • T031 Run focused tests: ./vendor/bin/sail artisan test tests/Feature/TenantRBAC --stop-on-failure

Notes / Guardrails

  • Non-member access = 404 (deny-as-not-found). Member without capability = 403.
  • No feature code may use role == ... checks. Always gates/capabilities.
  • Do not add any render-time Graph calls (group/app-role resolution is deferred unless explicitly scheduled as a job in a later feature).