Multi-Tenant: X-Tenant-Id Header

Tenant izolasyonu nasıl çalışır, RLS güvenlik mantığı, header zorunluluğu.

Tenant nedir?

Qando çok kiracılı (multi-tenant) bir platformdur. Her workspace bir tenant'tır. Veriler tenant bazında izole edilir — bir tenant'ın verisi başka tenant'a sızmaz.

Header zorunluluğu

Her korunan API isteğine X-Tenant-Id header'ı eklenmelidir:

GET /api/products
Authorization: Bearer <token>
X-Tenant-Id: <tenantId>

Bu header eklenmezse:

{ "error": "X-Tenant-Id header zorunlu" }

400 Bad Request döner.

Tenant ID nereden alınır?

Login yanıtındaki user.tenants[] dizisinden:

{
  "user": {
    "tenants": [
      { "tenantId": "abc-123", "name": "Ana Mağaza", "role": "OWNER" },
      { "tenantId": "def-456", "name": "Şube", "role": "STAFF" }
    ]
  }
}

Kullanıcı birden fazla tenant'ın üyesi olabilir; her API isteğinde hangisi için olduğunu belirtin.

Erişim kontrolü

Backend her isteği şu şekilde kontrol eder:

  1. Token geçerli mi? (JWT validasyonu)
  2. Token sahibi bu tenant'a üye mi? (memberships tablosu kontrolü)
  3. Tenant statüsü erişime izin veriyor mu? (SUSPENDED ise sadece-okuma)
  4. Geçerse istek işlenir; tenant context'i set edilir.

Row Level Security (RLS)

Veri tabanı seviyesinde PostgreSQL Row Level Security ile ek güvenlik katmanı:

  • Her korunan tabloda tenant_id kolonu vardır
  • PostgreSQL RLS policy'si app.current_tenant_id session değişkeniyle filtreleme yapar
  • Backend kodunda her sorgu öncesi SET LOCAL app.current_tenant_id yazılır

Bu sayede:

  • Backend kodda bir bug olsa bile DB seviyesi tenant izolasyonu bypass edilemez
  • Cross-tenant veri sızıntısı engellenir

Tenant değiştirme

UI'da workspace selector ile tenant değiştirildiğinde:

  • Kullanıcı arabirimde aktif tenant değişir
  • Bir sonraki API isteğinde yeni tenant ID gönderilir
  • Backend yeni context'le çalışır

API consumer olarak siz de:

  • Hangi tenant için istek atacağınızı kararlaştırın
  • Header'ı doğru tenantId ile doldurun

Yanlış tenant durumu

Eğer kullanıcı üye olmadığı bir tenant ID gönderirse:

HTTP 403 Forbidden
{
  "error": "Bu workspace'e erişiminiz yok"
}

Sistem context'i

Cron job'lar ve sistem servisleri kullanıcı context'inde değil sistem context'inde çalışır:

  • Tüm tenant'ların verisini görebilir (cross-tenant okuma)
  • app.bypass_rls=on session değişkeniyle RLS bypass edilir
  • Tipik kullanım: pazaryeri sipariş sync (her tenant'ın bağlantısı için sync çalışır)

Bu mekanizma sadece sunucu içinden kullanılır; API consumer olarak siz buna erişemezsiniz.

Yaygın sorunlar

  • 403 Forbidden → tenantId yanlış veya üye değilsiniz
  • 400 Bad Request → header tamamen eksik
  • Veri görünmüyor → tenantId doğru ama tenant statüsü SUSPENDED olabilir

Sonraki adım