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
84 lines
14 KiB
Markdown
84 lines
14 KiB
Markdown
# Feature Specification: Tenant RBAC v1 (Entra Login + Suite-Tenant Memberships)
|
||
|
||
**Feature Branch**: `062-tenant-rbac-v1`
|
||
**Created**: 2026-01-25
|
||
**Status**: Draft
|
||
**Input**: User description: "# Feature 062 — Tenant RBAC v1 (Entra Login + Suite-Tenant Memberships + Optional Group/AppRole Mapping + Break-glass) **Stack:** Laravel 12 · PHP 8.4 · Filament v5 · Livewire v4 · Postgres **Scope:** Best-practice, scalable authorization for a multi-tenant SaaS/MSP suite: - Authentication via Microsoft Entra ID (OIDC) - Authorization via **suite-tenant** memberships in TenantAtlas (SoT) - Optional automation: Entra Groups / App Roles → memberships - Platform break-glass local superadmin (operator-safe, audited) **Goal:** Enable MSPs and customers to manage who can do what per Suite Tenant (customer + env), without mixing Microsoft tenant admin concepts into TenantAtlas authorization. --- ## 0. Key Concepts (avoid confusion) ### K-001 — Two different “tenants” 1) **Suite Tenant (TenantAtlas Tenant)**: a customer/environment container inside your app (e.g., “Customer A – PROD”, “Customer A – DEV”). 2) **Microsoft Tenant (Entra/Intune tenant)**: the target platform you connect to (Entra tenant ID GUID). TenantAtlas RBAC controls who can use TenantAtlas features per **Suite Tenant**. Microsoft (Entra/Intune) RBAC controls what the app is technically able to read/write. --- ## 1. Principles (Constitution-aligned) ### RBAC-001 — Capabilities-first, not role checks Feature code MUST check **capabilities/permissions** (Gates/Policies), never `if role == X`. Roles are a mapping layer only. ### RBAC-002 — Suite Tenant is the authorization scope All permissions are evaluated within the current Suite Tenant context. ### RBAC-003 — Audited changes All membership/role/permission changes must write an AuditLog entry (no secrets, no PII dumps). ### RBAC-004 — Safe defaults If a user is not a member of a Suite Tenant: deny access (404/403), no “implicit member”. ### RBAC-005 — Break-glass exists A local platform superadmin account exists to recover access when Entra integration fails. --- ## 2. Goals 1) Allow adding/removing members per Suite Tenant and setting their role via UI. 2) Support MSP reality: same person can have different roles across different Suite Tenants. 3) Keep authorization stable even if Entra groups change (source recorded). 4) Allow optional Entra group/app-role mapping to auto-provision memberships. 5) Support future fine-grained permissions without refactoring all features. --- ## 3. Non-Goals (v1) - Full local user management for all users (passwords etc.) — only break-glass. - Cross-tenant global RBAC (platform-wide “auditor across all tenants”) beyond superadmin. - Impersonation (future). - Per-resource row-level permissions beyond tenant scope (future). - Full SCIM provisioning (future). --- # 4. Role Model & Capabilities ## 4.1 Canonical roles (v1) - `owner` - `manager` - `operator` - `readonly` ## 4.2 Canonical capabilities (v1 baseline) Define these high-level permissions (more can be added later): **General** - `tenant.view` - `tenant.manage` (edit tenant metadata) **Providers (from Feature 061)** - `provider.view` - `provider.manage` (connections + credentials) - `provider.run` (check/snapshot/sync) **Operations** - `ops.view` (Monitoring → Operations) - `ops.run` (start ops) **Inventory** - `inventory.view` - `inventory.run` (run sync) **Policies** - `policy.view` - `policy.run` (sync, bulk ops) - `policy.restore` (if applicable) **Backups/Restore** - `backup.view` - `backup.run` - `restore.view` - `restore.execute` (high-risk) **Drift/Findings** - `drift.view` - `drift.run` ## 4.3 Role → capability mapping (v1 defaults) **Owner** - all capabilities for the Suite Tenant **Manager** - all except the highest-risk destructive actions (if you want a stricter split) - recommended v1: same as owner except `restore.execute` (optional) **Operator** - view + run operational tasks - cannot manage providers/credentials or tenant config - cannot execute destructive ops unless explicitly granted later **Readonly** - view-only across all tenant features ### Minimum enforced mapping for Provider v1 (must match Feature 061) - Owner/Manager: `provider.view`, `provider.manage`, `provider.run` - Operator: `provider.view`, `provider.run` - Readonly: `provider.view` --- # 5. Data Model ## 5.1 users Store Entra identity as stable keys: - `entra_tenant_id` (tid) - `entra_object_id` (oid) - `email` (optional/nullable; do not rely on it as identity) - `name` - timestamps Unique index: - `(entra_tenant_id, entra_object_id)`. ## 5.2 tenant_memberships (Suite Tenant authorization SoT) - `id` (uuid) - `tenant_id` (fk to suite tenant) - `user_id` (fk) - `role` (enum string) - `source` (`manual | entra_group | entra_app_role | break_glass`) - `source_ref` (nullable string; e.g., group id/app role id) - `created_by_user_id` (nullable; for manual changes) - `timestamps` Constraints: - unique `(tenant_id, user_id)` - index `(tenant_id, role)` ## 5.3 tenant_role_mappings (optional automation) Allows automatic provisioning based on Entra groups/app roles. - `id` (uuid) - `tenant_id` (fk) - `mapping_type` (`entra_group | entra_app_role`) - `external_id` (group GUID or appRole string) - `role` (enum string) - `is_enabled` (bool) - timestamps Constraints: - unique `(tenant_id, mapping_type, external_id)` ## 5.4 audit_logs (existing) Used for membership changes; must support: - actor - tenant - action_id - target - before/after (redacted) - timestamp --- # 6. Authentication & Provisioning ## 6.1 Entra login (OIDC) On successful login: 1) Upsert `users` by `(entra_tenant_id, entra_object_id)` 2) Determine accessible Suite Tenants by memberships 3) Set current tenant context via Filament tenant selection ## 6.2 Membership sources and precedence (v1) Membership evaluation per Suite Tenant: 1) Manual membership (`source=manual`) takes precedence over automated sources. 2) If no manual membership exists: - apply Entra mapping rules (groups/app roles) if configured. 3) If still no membership: deny. Record the source: - If created by mapping: `source=entra_group/app_role`, `source_ref=<id>` ## 6.3 Entra group overage handling (v2-ready) v1 may rely on claims if available; if group claims are not present: - v1: treat mapping as optional; show “group overage not supported yet” - v2: add Graph call to resolve group membership (OperationRun-backed admin job, not render-time) --- # 7. Authorization (Policies/Gates) ## 7.1 Capability engine Implement a central `CapabilityResolver`: - input: user, tenant, membership role, optional overrides - output: `can($capability)` boolean All feature policies call `can()`. Forbidden: - direct role comparisons in feature code ## 7.2 Tenant scoping enforcement Every Filament resource query MUST scope to the current Suite Tenant and MUST enforce membership: - Non-members see 404 (deny-as-not-found) on tenant resources. --- # 8. UI Requirements (Filament) ## 8.1 Tenant “Members” management Add to **Settings → Tenants**: - Tab/Relation Manager: **Members** - Search existing users (by email/name) - Add member (select user) + role - Edit role - Remove member Permissions: - only Owner/Manager with `tenant.manage` can manage members - Operator/Readonly can view member list only if `tenant.view` (optional) Every change writes AuditLog. ## 8.2 Role mapping configuration (optional) Tenant detail page: - “Role Mappings” section - Add mapping: group/app-role → role - enable/disable mapping - show last sync attempt (if you add later) v1 can ship with UI but without group-membership resolution (if you choose). ## 8.3 Break-glass admin UX When logged in as break-glass: - Show a persistent banner “Break-glass account” - All actions are audited with source `break_glass` --- # 9. Break-glass Local Superadmin (Platform) ## 9.1 Purpose Allow platform operator to recover access if Entra login/mapping breaks. ## 9.2 Minimal implementation (v1) - local user table entry flagged `is_platform_superadmin = true` - password-based auth (local) OR separate guard - platform superadmin can: - view/manage all Suite Tenants - manage memberships - manage provider connections - must be audited and clearly indicated Constraints: - only one/few accounts - not used for day-to-day operations --- # 10. Tests & Guardrails ## 10.1 RBAC tests (required) - Membership required: non-member cannot access tenant resources (deny-as-not-found) - Role mapping: - Owner/Manager can manage members - Operator cannot manage credentials (ties into Feature 061) - Readonly cannot start operations ## 10.2 Capability resolver tests - role → capability mapping is stable - adding a new capability won’t break existing roles (safe defaults) ## 10.3 Audit tests - membership changes create audit log records - no secrets/PII in audit before/after ## 10.4 UI tests (Filament) - Members relation manager visible only to authorized roles - Role dropdown reflects canonical roles --- # 11. Migration & Backward Compatibility - Introduce membership enforcement gradually: - v1: create a default owner membership for the creator of a tenant (migration/seed) - existing dev tenants: backfill membership for current admin user - Ensure no tenant becomes inaccessible after deployment: - platform superadmin can always recover --- # 12. Task Plan (high level) ## Phase 0 — Discovery - Identify existing “tenant scoping” and any implicit access assumptions. - Locate current auth flow and where to hook Entra identity upsert. ## Phase 1 — Schema - `tenant_memberships` - `tenant_role_mappings` (optional) - user identity fields (`entra_tenant_id`, `entra_object_id`) if not present ## Phase 2 — Capability resolver + gates - Implement `CapabilityResolver` - Register Gates: `provider.manage`, `provider.run`, etc. ## Phase 3 — UI - Tenant Members manager (CRUD) - Optional role mapping UI ## Phase 4 — Break-glass - platform_superadmin account + banner + audit ## Phase 5 — Tests - RBAC + audit + UI tests --- # 13. Acceptance Criteria (DoD) 1) Users authenticate via Entra; user identities are stored by stable `(tid, oid)`. 2) A user’s access is determined by tenant_memberships for the current Suite Tenant. 3) Owner/Manager can add/remove members and set roles via UI; changes are audited. 4) Operator/Readonly restrictions are enforced across provider management and operations. 5) Optional: Entra group/app-role mappings can auto-provision memberships (at least config + data model). 6) Break-glass platform superadmin exists and can recover access; actions are audited and bannered. 7) No feature code uses direct role checks; only capabilities/gates/policies.
|
||
|
||
---
|
||
|
||
## Addendum — Bootstrap & Admin Responsibilities
|
||
|
||
### Context
|
||
TenantAtlas operates across two distinct administrative planes:
|
||
- **Microsoft tenant administration (Entra/Intune)** governs what the platform is technically allowed to do in the target Microsoft tenant (consent, permissions, RBAC assignments).
|
||
- **TenantAtlas administration (Suite Tenant RBAC)** governs who is allowed to use TenantAtlas features for a given Suite Tenant (memberships, roles, operational actions).
|
||
|
||
These roles may be held by the same person in small orgs, but must not be assumed to be the same in enterprise/MSP environments.
|
||
|
||
### FR-013 Admin Planes Are Distinct (No Assumptions)
|
||
The system MUST NOT assume that:
|
||
- a Microsoft tenant admin is automatically a TenantAtlas admin, or
|
||
- a TenantAtlas admin is automatically a Microsoft tenant admin.
|
||
|
||
TenantAtlas permissions are determined solely by `tenant_memberships` (and optional mappings), independent of Microsoft directory roles.
|
||
|
||
**Acceptance:**
|
||
- A user can be a TenantAtlas Owner/Manager without holding any Microsoft admin role.
|
||
- A Microsoft admin can grant consent without being a TenantAtlas member (unless explicitly added).
|
||
|
||
### FR-014 Bootstrap Owner Assignment (First-Admin Rule)
|
||
Each Suite Tenant MUST always have at least one Owner-capable administrator to prevent lockout.
|
||
|
||
#### FR-014a Default Bootstrap Rule (v1)
|
||
On Suite Tenant creation, the creator is assigned as `owner` membership (source: `manual`, created_by = creator).
|
||
|
||
#### FR-014b Platform Recovery Rule (Break-glass)
|
||
A platform superadmin MUST be able to assign/change the initial owner membership in case:
|
||
- the creator no longer exists,
|
||
- Entra login/mapping breaks,
|
||
- the tenant was imported.
|
||
|
||
#### FR-014c Non-Removable Last Owner Guard
|
||
The system MUST prevent removing or demoting the last remaining `owner` membership for a Suite Tenant.
|
||
|
||
**Acceptance:**
|
||
- Attempting to remove the last Owner fails with a clear message.
|
||
- Platform superadmin can recover by adding a new Owner first.
|
||
|
||
### FR-015 TenantAtlas Admin Responsibilities (v1)
|
||
TenantAtlas admins (Owner/Manager) are responsible for:
|
||
- managing Suite Tenant members and roles (tenant_memberships),
|
||
- approving who can run operations and manage providers inside TenantAtlas,
|
||
- reviewing audit logs for membership/role changes.
|
||
|
||
Microsoft tenant admins are responsible for:
|
||
- granting admin consent for the app,
|
||
- ensuring required Graph permissions are granted,
|
||
- maintaining any Microsoft-side RBAC/group prerequisites.
|
||
|
||
**Acceptance:**
|
||
- The UI communicates this split (e.g., small help text near membership management and provider setup links).
|
||
|
||
### FR-016 Audit Requirements for Bootstrap & Membership Management
|
||
All bootstrap and membership management actions MUST be audited with canonical action_ids:
|
||
- `tenant_membership.add`
|
||
- `tenant_membership.role_change`
|
||
- `tenant_membership.remove`
|
||
- `tenant_membership.bootstrap_assign` (initial owner assignment)
|
||
- `tenant_membership.bootstrap_recover` (platform superadmin recovery)
|
||
|
||
Audit entries MUST include:
|
||
- actor id
|
||
- suite tenant id
|
||
- target user id/email (minimal)
|
||
- before/after role (redacted)
|
||
- timestamp
|
||
|
||
No secrets, no tokens, no Microsoft credentials.
|
||
|
||
### UI Requirements (Bootstrap-related)
|
||
- Tenant creation flow MUST result in an Owner membership being present (visible in Tenant → Members).
|
||
- Tenant → Members UI MUST show an “Owner” role and enforce “last owner cannot be removed/demoted”.
|
||
- Platform superadmin UI MUST support adding an Owner membership if none exists or recovery is required.
|