package mailboxconfig import ( "context" "errors" "testing" ) // TestTenantScoping_ForeignKnownIDNeverAccessible ist Teil der // geforderten Pflichtprüfung "Stichprobenprüfung mehrerer Speicherpfade // auf Mandantentrennung" (QA-04): dieser Speicherpfad ist besonders // sensibel (verschlüsselte Postfach-Zugangsdaten). Anders als die // Tenant-Scoping-Tests aus ING-10 (unterschiedliche Server-/Store- // Instanzen je Mandant) prüft dieser Test den REALISTISCHSTEN // Angriffsfall für eine geteilte Datenbank mit tenant_slug-Spalte: // Mandant B kennt/errät eine gültige, echte ID aus Mandant As Zeile // (z. B. durch fortlaufende IDs) und versucht direkt darauf // zuzugreifen — muss an der tenant_slug-Prüfung in der WHERE-Klausel // scheitern, nicht an einem geratenen falschen Wert. func TestTenantScoping_ForeignKnownIDNeverAccessible(t *testing.T) { store := setupStore(t) ctx := context.Background() tenantA := "mandant-qa04-a" tenantB := "mandant-qa04-b" idA := createTestMailbox(t, store, tenantA, "Postfach A") // Mandant B versucht, MIT DER ECHTEN, BEKANNTEN ID von Mandant A // zuzugreifen — List gibt es aus Mandant Bs Sicht nicht. listB, err := store.List(ctx, tenantB) if err != nil { t.Fatalf("List mandant b: %v", err) } for _, mb := range listB { if mb.ID == idA { t.Fatalf("mandant b sieht mandant As postfach in seiner eigenen liste") } } // Direkter Zugriff mit fremder, aber echter ID: GetDecryptedPassword. _, err = store.GetDecryptedPassword(ctx, tenantB, idA) if !errors.Is(err, ErrNotFound) { t.Fatalf("GetDecryptedPassword mit fremder mandant-b-anfrage auf mandant-a-id hätte ErrNotFound liefern müssen, habe: %v", err) } // Direkter Zugriff mit fremder, aber echter ID: Update. err = store.Update(ctx, tenantB, idA, UpdateInput{ IMAPHost: "boese.example.com", IMAPPort: 993, FolderSelection: []string{"INBOX"}, IntervalSeconds: 60, }) if !errors.Is(err, ErrNotFound) { t.Fatalf("Update mit fremder mandant-b-anfrage auf mandant-a-id hätte ErrNotFound liefern müssen, habe: %v", err) } // Direkter Zugriff mit fremder, aber echter ID: Delete. err = store.Delete(ctx, tenantB, idA) if !errors.Is(err, ErrNotFound) { t.Fatalf("Delete mit fremder mandant-b-anfrage auf mandant-a-id hätte ErrNotFound liefern müssen, habe: %v", err) } // Kontrolle: Mandant As eigenes Postfach ist von alledem UNBERÜHRT // (kein erfolgreicher Update/Delete-Seiteneffekt trotz Ablehnung). stillA, err := store.List(ctx, tenantA) if err != nil { t.Fatalf("List mandant a nach angriffsversuchen: %v", err) } found := false for _, ma := range stillA { if ma.ID == idA { found = true if ma.IMAPHost == "boese.example.com" { t.Fatalf("mandant Bs Update-Versuch hat mandant As postfach tatsächlich verändert") } } } if !found { t.Fatalf("mandant As postfach ist nach den angriffsversuchen verschwunden") } }