Ersetzt die handgestrickte bcrypt+Session-Auth durch fastapi-users (CookieTransport+JWTStrategy statt Starlette-Session, custom psycopg3-async-Adapter statt SQLAlchemy). Bestehende bcrypt-Hashes bleiben gültig und werden beim nächsten Login automatisch auf Argon2 angehoben (pwdlib-Default-PasswordHelper). ist_admin wird zu is_superuser, damit fastapi-users' eingebaute Semantik direkt nutzbar ist - require_admin/get_current_user bleiben als Wrapper unter gleichem Namen, alle Router-Dateien bis auf geraete.py (dict- zu attribut-Zugriff auf user.organization_id) unverändert. Lokal end-to-end gegen eine Wegwerf-Postgres-Instanz (podman) verifiziert: Migration, Bestandskonto-Login mit altem Passwort samt Argon2-Upgrade, Admin-Kontoanlage über beide Aufrufstellen (create_user.py und admin_kunden.py), 403 für Nicht-Admins, Logout. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
61 lines
1.9 KiB
Python
61 lines
1.9 KiB
Python
"""
|
|
Manuelle Konto-Anlage für die Kundenplattform (kein Self-Service in v1, siehe
|
|
ADR-0011). Beispiel:
|
|
|
|
KUNDENPLATTFORM_DATABASE_URL=... .venv/bin/python scripts/create_user.py \\
|
|
--organization-id 11111111-1111-4111-8111-111111111111 \\
|
|
--email admin@schule-beispiel.de
|
|
"""
|
|
|
|
import argparse
|
|
import asyncio
|
|
import getpass
|
|
import os
|
|
import sys
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
from auth import UserCreate, UserDatabase, UserManager # noqa: E402
|
|
from db import get_async_database_connection # noqa: E402
|
|
|
|
|
|
async def konto_anlegen(organization_id: str, email: str, password: str, admin: bool):
|
|
async with await get_async_database_connection() as connection:
|
|
user_manager = UserManager(UserDatabase(connection))
|
|
await user_manager.create(
|
|
UserCreate(
|
|
email=email,
|
|
password=password,
|
|
organization_id=organization_id,
|
|
is_superuser=admin,
|
|
)
|
|
)
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--organization-id", required=True)
|
|
parser.add_argument("--email", required=True)
|
|
parser.add_argument(
|
|
"--admin", action="store_true",
|
|
help="Konto bekommt organisationsübergreifenden Admin-Zugriff (/admin/...).",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
if not os.environ.get("KUNDENPLATTFORM_DATABASE_URL"):
|
|
sys.exit("KUNDENPLATTFORM_DATABASE_URL ist nicht gesetzt.")
|
|
|
|
password = getpass.getpass("Passwort: ")
|
|
password_confirm = getpass.getpass("Passwort (Wiederholung): ")
|
|
|
|
if password != password_confirm:
|
|
sys.exit("Passwörter stimmen nicht überein.")
|
|
|
|
asyncio.run(konto_anlegen(args.organization_id, args.email, password, args.admin))
|
|
|
|
print(f"Konto für {args.email} angelegt{' (Admin)' if args.admin else ''}.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|