fix: Kontolöschung schlug mit 500 fehl (bytes-vs-str, psycopg-Eigenart)
Live bei der Verifikation gefunden: konto_loeschen() lud das Zielkonto über get_by_email(ziel['email']) nach - der email-Wert kam aus fetch_benutzer_fuer_organisation()'s sync-Verbindungspfad, wo psycopg TEXT-Spalten teils als bytes statt str liefert (dasselbe bereits bekannte, nicht deterministische Verhalten wie in _row_to_user(), siehe dortiger Kommentar) - die async get_by_email()-Abfrage bekam dadurch einen bytes-Parameter und scheiterte mit 'operator does not exist: text = bytea'. Zwei Fixes: konto_loeschen() nutzt jetzt user_manager.get(id) statt des E-Mail-Umwegs (nur async-Pfad, umgeht das Problem strukturell). fetch_benutzer_fuer_organisation() bekommt zusätzlich denselben defensiven bytes-Decode wie _row_to_user(), damit das nicht an anderer Stelle erneut zuschlägt. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
ec11de1ee9
commit
9265d47916
19
auth.py
19
auth.py
@ -211,16 +211,25 @@ def fetch_benutzer_fuer_organisation(organization_id: str) -> list[dict]:
|
|||||||
""",
|
""",
|
||||||
(organization_id,),
|
(organization_id,),
|
||||||
)
|
)
|
||||||
return [
|
ergebnis = []
|
||||||
{
|
for benutzer_id, email, is_superuser, ist_organisationsadmin, created_at in cur.fetchall():
|
||||||
|
# psycopg liefert TEXT-Spalten hier teils als bytes zurück,
|
||||||
|
# nicht deterministisch (dasselbe bekannte Verhalten wie in
|
||||||
|
# _row_to_user()) - live gefunden, als ein daraus
|
||||||
|
# weitergereichter bytes-Wert eine spätere SQL-Abfrage mit
|
||||||
|
# "operator does not exist: text = bytea" zum Absturz brachte.
|
||||||
|
if isinstance(email, bytes):
|
||||||
|
email = email.decode("utf-8")
|
||||||
|
|
||||||
|
ergebnis.append({
|
||||||
"id": str(benutzer_id),
|
"id": str(benutzer_id),
|
||||||
"email": email,
|
"email": email,
|
||||||
"is_superuser": is_superuser,
|
"is_superuser": is_superuser,
|
||||||
"ist_organisationsadmin": ist_organisationsadmin,
|
"ist_organisationsadmin": ist_organisationsadmin,
|
||||||
"created_at": created_at,
|
"created_at": created_at,
|
||||||
}
|
})
|
||||||
for benutzer_id, email, is_superuser, ist_organisationsadmin, created_at in cur.fetchall()
|
|
||||||
]
|
return ergebnis
|
||||||
|
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|||||||
@ -101,7 +101,14 @@ async def konto_loeschen(
|
|||||||
if ziel is None:
|
if ziel is None:
|
||||||
raise HTTPException(status_code=404, detail="Konto wurde nicht gefunden.")
|
raise HTTPException(status_code=404, detail="Konto wurde nicht gefunden.")
|
||||||
|
|
||||||
ziel_user = await user_manager.get_by_email(ziel["email"])
|
# Über die ID statt per E-Mail nachladen: get_by_email() lief über den
|
||||||
|
# sync-Verbindungspfad von fetch_benutzer_fuer_organisation() zurück in
|
||||||
|
# eine async-Abfrage - psycopg liefert TEXT-Spalten dort teils als bytes
|
||||||
|
# zurück (dasselbe bekannte Verhalten wie bei _row_to_user()), was hier
|
||||||
|
# zu "operator does not exist: text = bytea" führte (live gefunden bei
|
||||||
|
# der Verifikation). user_manager.get(id) nutzt ausschließlich den
|
||||||
|
# async-Pfad (UserDatabase.get(), mit korrektem Decode in _row_to_user()).
|
||||||
|
ziel_user = await user_manager.get(user_manager.parse_id(konto_id))
|
||||||
await user_manager.delete(ziel_user)
|
await user_manager.delete(ziel_user)
|
||||||
|
|
||||||
return RedirectResponse("/organisation", status_code=303)
|
return RedirectResponse("/organisation", status_code=303)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user