116 lines
3.7 KiB
Markdown
116 lines
3.7 KiB
Markdown
# Super Admin — Tenant Access Design
|
|
|
|
**Date:** 2026-06-29
|
|
**Status:** Approved
|
|
**Scope:** Bug fix (TenantMiddleware schema name) + feature (super admin impersonation with session restore)
|
|
|
|
---
|
|
|
|
## Problem
|
|
|
|
1. **Bug:** `TenantMiddleware` constructs schema name as `"tenant_<uuid-with-hyphens>"` but schemas are named `tenant_<uuid-with-underscores>`. All tenant user requests fail with SET search_path error. Independent of the feature — must always be fixed.
|
|
|
|
2. **Feature gap:** Super admin can list and create tenants but cannot access or manage data within individual tenant workspaces.
|
|
|
|
---
|
|
|
|
## Solution
|
|
|
|
### Bug Fix — TenantMiddleware
|
|
|
|
Replace raw UUID concatenation with normalized schema name (hyphens → underscores):
|
|
|
|
```go
|
|
// Before
|
|
schema := `"tenant_` + claims.TenantID + `"`
|
|
|
|
// After
|
|
schema := `"tenant_` + strings.ReplaceAll(claims.TenantID, "-", "_") + `"`
|
|
```
|
|
|
|
### Feature — Tenant Impersonation with Session Restore
|
|
|
|
**Flow:**
|
|
1. Super admin clicks "Gerir" on any tenant row in TenantsPage
|
|
2. Frontend calls `POST /api/v1/admin/tenants/:id/access`
|
|
3. Backend validates tenant (exists + active), generates a 15-min access token with `tenantID=<id>` and `role="tenant_admin"`
|
|
4. Frontend saves current session to `previousSession` in authStore, sets new token+user
|
|
5. Navigates to `/app` — super admin now sees the tenant workspace
|
|
6. AppLayout shows a banner: `[TechXCar Admin] A gerir: <tenant name> [← Voltar ao painel]`
|
|
7. Clicking "Voltar" restores the previous session and navigates to `/admin`
|
|
|
|
---
|
|
|
|
## Backend
|
|
|
|
### New endpoint
|
|
|
|
`POST /api/v1/admin/tenants/:id/access`
|
|
- Auth: `RequireAuth` + `RequireRole("super_admin")`
|
|
- Validates: tenant ID format, tenant exists, tenant status = "active"
|
|
- Returns: `{ "data": { "access_token": "...", "tenant": { "id", "name", "slug" } }, "error": null }`
|
|
- Token: standard 15-min access token, `userID = super_admin_id`, `tenantID = tenant.ID`, `role = "tenant_admin"`
|
|
- No new refresh token — the impersonation session is access-token only
|
|
|
|
### Route registration
|
|
|
|
Added to `tenant.RegisterRoutes` under the existing `admin` group:
|
|
```
|
|
POST /api/v1/admin/tenants/:id/access
|
|
```
|
|
|
|
---
|
|
|
|
## Frontend
|
|
|
|
### authStore changes
|
|
|
|
New fields:
|
|
```typescript
|
|
previousSession?: { token: string; user: AuthUser }
|
|
```
|
|
|
|
New actions:
|
|
- `impersonateTenant(token, user)` — saves current `{ token, user }` to `previousSession`, sets new token+user
|
|
- `restoreSession()` — restores `previousSession` into token+user, clears `previousSession`
|
|
|
|
`previousSession` is NOT persisted to localStorage (impersonation does not survive page refresh — intentional).
|
|
|
|
### TenantsPage
|
|
|
|
Each tenant row gains a "Gerir" button (secondary/outline variant).
|
|
|
|
On click:
|
|
1. POST `/admin/tenants/:id/access`
|
|
2. On success: `impersonateTenant(data.access_token, { ...data.tenant, role: 'tenant_admin' })`
|
|
3. Invalidate TanStack Query cache (avoid stale super-admin-scoped data)
|
|
4. Navigate to `/app`
|
|
|
|
### AppLayout
|
|
|
|
When `authStore.previousSession` is defined, render a fixed banner at the top:
|
|
|
|
```
|
|
[TechXCar Admin] A gerir: <user.name> [← Voltar ao painel]
|
|
```
|
|
|
|
- Background: amber/yellow to visually distinguish from normal tenant UI
|
|
- "Voltar" button: calls `restoreSession()`, navigates to `/admin`
|
|
- Banner height: ~40px; main content padding-top adjusts accordingly
|
|
|
|
---
|
|
|
|
## Error Handling
|
|
|
|
- Tenant not found or not active → 404 "oficina não encontrada ou inativa"
|
|
- `impersonateTenant` failure (network) → show error toast, do not change session
|
|
- Restoring session never fails (purely client-side state)
|
|
|
|
---
|
|
|
|
## Out of Scope
|
|
|
|
- Refresh token for impersonation sessions (15-min window is intentional)
|
|
- Audit log of impersonation events (future)
|
|
- Super admin creating/editing data within tenant as themselves (they appear as tenant_admin)
|