fix: billing tenant isolation, invoice status validation, SMTP password masking

H2: list_invoices now passes tenant_id (from current_user) to get_invoices;
    get_invoices applies WHERE tenant_id filter for non-global-admin users;
    get_invoice_endpoint returns 404 when tenant mismatch for non-admins.

H3: InvoiceStatusUpdate.status changed to Literal["draft","sent","paid","cancelled"]
    for schema-level validation; guard also added in update_invoice_status service.

H5: _settings_to_out masks smtp_password as "***" when set, "" when empty;
    update_settings skips writing when value is the "***" sentinel.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-07-22 13:40:59 +02:00
co-authored by Claude Sonnet 4.6
parent a11d2fb1e7
commit b69190dd86
5 changed files with 25 additions and 5 deletions
+4
View File
@@ -283,6 +283,8 @@ async def get_invoices(
.offset(skip)
.limit(limit)
)
if tenant_id is not None:
q = q.where(Invoice.tenant_id == tenant_id)
result = await db.execute(q)
return list(result.scalars().all())
@@ -297,6 +299,8 @@ async def get_invoice(db: AsyncSession, invoice_id: uuid.UUID) -> Invoice | None
async def update_invoice_status(db: AsyncSession, invoice_id: uuid.UUID, status: str) -> Invoice | None:
if status not in VALID_STATUSES:
raise ValueError(f"Invalid status '{status}'. Must be one of: {', '.join(sorted(VALID_STATUSES))}")
invoice = await get_invoice(db, invoice_id)
if not invoice:
return None