fix(sec): Cross-Tenant-IDOR bei IMAP-Konten schließen

domain_admin sah und konnte IMAP-Konten (inkl. Credentials) fremder
Tenants auflisten, löschen, synchronisieren und umkonfigurieren, da
Store.List() für Admins ungefiltert alle Konten lieferte und die
Einzelhandler nur den Owner, nicht den Tenant prüften.

- Store.List() filtert jetzt nach tenant_id, außer für superadmin
- Store.Create() setzt tenant_id beim Anlegen
- Alle Einzelhandler (delete/start-import/progress/sync/update)
  prüfen zusätzlich tenantAccessAllowed()
This commit is contained in:
sysops
2026-06-12 23:26:31 +02:00
parent d07e65021f
commit 730099d2aa
2 changed files with 45 additions and 8 deletions
+12 -7
View File
@@ -139,10 +139,10 @@ func (s *Store) Create(ctx context.Context, acc Account, password string) (*Acco
}
row := s.pool.QueryRow(ctx, `
INSERT INTO imap_accounts (owner, name, host, port, tls, username, password_enc, excluded_folders)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
INSERT INTO imap_accounts (owner, name, host, port, tls, username, password_enc, excluded_folders, tenant_id)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
RETURNING id, created_at`,
acc.Owner, acc.Name, acc.Host, acc.Port, acc.TLS, acc.Username, enc, acc.ExcludedFolders,
acc.Owner, acc.Name, acc.Host, acc.Port, acc.TLS, acc.Username, enc, acc.ExcludedFolders, acc.TenantID,
)
if err := row.Scan(&acc.ID, &acc.CreatedAt); err != nil {
@@ -180,16 +180,21 @@ func scanRow(row scanner) (Account, error) {
return a, err
}
// List returns IMAP accounts. Admins see all accounts; regular users see only their own.
func (s *Store) List(ctx context.Context, owner string, isAdmin bool) ([]Account, error) {
// List returns IMAP accounts. Superadmins (tenantID == nil) see all accounts;
// other admins (tenantID != nil) see all accounts within their own tenant;
// regular users see only their own accounts.
func (s *Store) List(ctx context.Context, owner string, isAdmin bool, tenantID *int64) ([]Account, error) {
var rows pgx.Rows
var err error
q := `SELECT` + selectColumns + `FROM imap_accounts`
if isAdmin {
switch {
case isAdmin && tenantID == nil:
rows, err = s.pool.Query(ctx, q+` ORDER BY id`)
} else {
case isAdmin:
rows, err = s.pool.Query(ctx, q+` WHERE tenant_id = $1 ORDER BY id`, *tenantID)
default:
rows, err = s.pool.Query(ctx, q+` WHERE owner = $1 ORDER BY id`, owner)
}
if err != nil {