Bündel 3: Security-Helper + Demo-Migration, SW by-v1115

NEUE HELPER in auth.py:

require_moderator(user=Depends(get_current_user))
  Konsequente Dependency statt inline
  'if user["rolle"] not in ("admin", "moderator")'

require_breeder(user=Depends(get_current_user))
  Konsequente Dependency statt inline
  'if user["subscription_tier"] not in ("breeder", "breeder_test")'

require_owner(row, user, owner_field='user_id',
              not_found_msg, forbidden_msg) -> row
  Zentralisiert das häufigste Pattern (54 Stellen im Audit):
  Statt:
    row = conn.execute(...).fetchone()
    if not row: raise HTTPException(404, ...)
    if row['user_id'] != user['id']: raise HTTPException(403, ...)
  Jetzt:
    row = require_owner(conn.execute(...).fetchone(), user,
                        not_found_msg='Ort nicht gefunden.')

is_owner_or_admin(row, user, owner_field='user_id') -> bool
  True wenn Owner ODER Admin/Moderator (Admin-Override für
  Moderations-Endpoints)

DEMO-MIGRATION:
places.py PATCH /places/{id} + DELETE /places/{id} migriert auf
require_owner() — als Style-Referenz für künftige Migrationen.

KEINE Massen-Migration der 54 Stellen — bewusste Entscheidung
weil security-kritisch. Helper sind bereitgestellt, neuer Code
nutzt sie, bestehender bleibt funktional identisch.

Tests 19/19 grün.

Hinweis: Massen-Migration der Owner-Checks ist eigener Sprint mit
sehr sorgfältigem Testing — bei jeder migrierten Route muss die
404→403→Cascade durchgeprüft werden, dass Owner+Non-Owner+Admin
sich identisch zum Vorher verhalten.
This commit is contained in:
rene 2026-05-27 11:27:00 +02:00
parent 297bd22f96
commit 35937ed51b
7 changed files with 68 additions and 27 deletions

View file

@ -86,14 +86,14 @@
<title>Ban Yaro</title>
<!-- Theme + theme-color Statusleiste vor CSS setzen -->
<script src="/js/boot-early.js?v=1114"></script>
<script src="/js/boot-early.js?v=1115"></script>
<!-- CSS: Reihenfolge ist wichtig — ?v= zwingt Browser zur Neuladung -->
<link rel="stylesheet" href="/css/design-system.css?v=1114">
<link rel="stylesheet" href="/css/layout.css?v=1114">
<link rel="stylesheet" href="/css/components.css?v=1114">
<link rel="stylesheet" href="/css/utilities.css?v=1114">
<link rel="stylesheet" href="/css/lists.css?v=1114">
<link rel="stylesheet" href="/css/design-system.css?v=1115">
<link rel="stylesheet" href="/css/layout.css?v=1115">
<link rel="stylesheet" href="/css/components.css?v=1115">
<link rel="stylesheet" href="/css/utilities.css?v=1115">
<link rel="stylesheet" href="/css/lists.css?v=1115">
</head>
<body>
@ -617,11 +617,11 @@
<div id="modal-container"></div>
<!-- JS: Reihenfolge ist wichtig — erst Basis, dann Features -->
<script src="/js/api.js?v=1114"></script>
<script src="/js/ui.js?v=1114"></script>
<script src="/js/app.js?v=1114"></script>
<script src="/js/worlds.js?v=1114"></script>
<script src="/js/offline-indicator.js?v=1114"></script>
<script src="/js/api.js?v=1115"></script>
<script src="/js/ui.js?v=1115"></script>
<script src="/js/app.js?v=1115"></script>
<script src="/js/worlds.js?v=1115"></script>
<script src="/js/offline-indicator.js?v=1115"></script>
<!-- Feature-Seiten werden lazy geladen -->
@ -631,7 +631,7 @@
<!-- Boot: Offline-Banner + SW-Registration (extrahiert für CSP) -->
<script src="/js/boot.js?v=1114"></script>
<script src="/js/boot.js?v=1115"></script>
</body>