More database handling and admin interface

This commit is contained in:
2025-05-13 15:51:25 +02:00
parent d1adfe9f93
commit 1693a56e43
7 changed files with 97 additions and 97 deletions

View File

@@ -3,6 +3,8 @@ from fastapi.responses import RedirectResponse
from authlib.integrations.starlette_client import OAuth
from starlette.requests import Request
from db.models import User, SessionLocal
from dotenv import load_dotenv
import os
@@ -40,7 +42,7 @@ oauth.register(
@router.get("/login/oidc")
async def login(request: Request):
auth0_client = oauth.create_client("auth0")
redirect_uri = os.getenv("OIDC_REDIRECT_URI")
redirect_uri = os.getenv("OIDC_REDIRECT_URL")
return await auth0_client.authorize_redirect(request, redirect_uri)
@router.route("/authorize")
@@ -55,6 +57,22 @@ async def authorize(request: Request):
# save user info in session
request.session["user"] = profile
# check if user is already in the database
db = SessionLocal()
user_db = db.query(User).filter(User.username == profile["preferred_username"]).first()
if not user_db:
print("Create User in DB")
user_db = User(
username=profile["preferred_username"],
role="user" # Default role
)
db.add(user_db)
db.commit()
db.refresh(user_db)
db.close()
print("User in DB:", user_db)
return RedirectResponse(url="/", status_code=303)
@router.get("/logout")

View File

@@ -1,86 +0,0 @@
from fastapi import APIRouter, Request, Response, HTTPException
from fastapi.responses import JSONResponse, RedirectResponse
from webauthn import (
generate_authentication_options,
verify_authentication_response,
)
from webauthn import (
generate_registration_options,
verify_registration_response,
options_to_json,
base64url_to_bytes,
)
from webauthn.helpers.cose import COSEAlgorithmIdentifier
from webauthn.helpers.structs import (
AttestationConveyancePreference,
AuthenticatorAttachment,
AuthenticatorSelectionCriteria,
PublicKeyCredentialDescriptor,
PublicKeyCredentialHint,
ResidentKeyRequirement,
)
import os
router = APIRouter()
# Simulierte Userdatenbank (nur zum Testen!)
fake_users = {
"admin@example.com": {
"id": b"user-id-in-bytes",
"credential_id": b"credential-id-in-bytes",
"public_key": b"public-key-in-bytes",
"sign_count": 0
}
}
RP_ID = "localhost" # Oder deine Domain bei Produktivbetrieb
ORIGIN = "http://localhost:8000"
@router.get("/login/webauthn/start")
async def start_webauthn(request: Request):
email = "admin@example.com" # Hardcoded Demo-User
if email not in fake_users:
raise HTTPException(status_code=404, detail="User nicht gefunden")
user = fake_users[email]
options = PublicKeyCredentialRequestOptions(
challenge=os.urandom(32),
rp_id=RP_ID,
allow_credentials=[...],
timeout=60000,
)
# Speichere Challenge für später
request.session["challenge"] = options.challenge
return JSONResponse(content=options.model_dump())
@router.post("/login/webauthn/finish")
async def finish_webauthn(request: Request):
body = await request.json()
email = "admin@example.com" # Again, Demo-User
if email not in fake_users:
raise HTTPException(status_code=404, detail="User nicht gefunden")
user = fake_users[email]
try:
verified_auth = verify_authentication_response(
credential=AuthenticationCredential.parse_obj(body),
expected_challenge=request.session.get("challenge"),
expected_rp_id=RP_ID,
expected_origin=ORIGIN,
credential_public_key=user["public_key"],
credential_current_sign_count=user["sign_count"],
credential_id=user["credential_id"]
)
# Erfolg setze Session
request.session["user"] = email
return RedirectResponse(url="/", status_code=303)
except Exception as e:
return JSONResponse({"detail": f"WebAuthn fehlgeschlagen: {str(e)}"}, status_code=400)

View File

@@ -1,5 +1,5 @@
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import declarative_base, sessionmaker
from sqlalchemy import Column, Integer, String, Float
from sqlalchemy.orm import declarative_base, sessionmaker, Session
from sqlalchemy import create_engine
from fastapi import Depends
@@ -17,6 +17,17 @@ class User(Base):
id = Column(Integer, primary_key=True)
username = Column(String, unique=True, index=True)
role = Column(String) # z.B. "admin" oder "user"
money = Column(Float, default=0) # money of user
def create_user(db: Session, username: str, role: str):
db_user = User(username=username, role=role)
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
def get_user(db: Session, user_id: int):
return db.query(User).filter(User.id == user_id).first()
def get_db():

40
main.py
View File

@@ -4,18 +4,18 @@ from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from starlette.middleware.sessions import SessionMiddleware
from db.models import Base, engine, SessionLocal, get_db, User
from db.models import Base, engine, get_db, User
from auth.session import get_current_user, login_user, logout_user
from auth.session import get_current_user
from auth import webauthn, oidc
from auth import oidc
import uvicorn
from sqlalchemy.orm import Session
app = FastAPI()
app.add_middleware(SessionMiddleware, secret_key="my_secret_key")
app.include_router(webauthn.router)
app.include_router(oidc.router)
app.mount("/static", StaticFiles(directory="static"), name="static")
@@ -25,14 +25,42 @@ templates = Jinja2Templates(directory="templates")
Base.metadata.create_all(bind=engine)
@app.get("/", response_class=HTMLResponse)
def home(request: Request, user: User = Depends(get_current_user)):
def home(request: Request, user: User = Depends(get_current_user), db: Session = Depends(get_db)):
if not user:
return RedirectResponse(url="/login", status_code=303)
return templates.TemplateResponse("index.html", {"request": request, "user": user})
db_user = db.query(User).filter_by(username=user["preferred_username"]).first()
if not db_user:
raise HTTPException(status_code=404, detail="User nicht gefunden")
users = None
if "Fachschaft Admins" in user["groups"]:
users = db.query(User).all()
return templates.TemplateResponse("index.html", {"request": request, "user": user, "users": users, "db_user": db_user})
@app.get("/login", response_class=HTMLResponse)
def login_form(request: Request):
return templates.TemplateResponse("login.html", {"request": request})
@app.post("/set_money")
def set_money(request: Request, username: str = Form(...), money: float = Form(...), db: Session = Depends(get_db), user: User = Depends(get_current_user)):
if not user or "Fachschaft Admins" not in user["groups"]:
raise HTTPException(status_code=403, detail="Nicht erlaubt")
db_user = db.query(User).filter_by(username=username).first()
if not db_user:
raise HTTPException(status_code=404, detail="User nicht gefunden")
db_user.money = money*100
db.commit()
return RedirectResponse(url="/", status_code=303)
@app.post("/drink")
def drink(request: Request, db: Session = Depends(get_db), user: User = Depends(get_current_user)):
if not user or "Fachschaft" not in user["groups"]:
raise HTTPException(status_code=403, detail="Nicht erlaubt")
db_user = db.query(User).filter_by(username=user["preferred_username"]).first()
if not db_user:
raise HTTPException(status_code=404, detail="User nicht gefunden")
db_user.money -= 100
db.commit()
return RedirectResponse(url="/", status_code=303)
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)

View File

@@ -9,11 +9,32 @@
<header>
<h1>Meine Beispielseite</h1>
{% if user %}
<p>Angemeldet als {{ user.preferred_username }} ({{ user.groups }}) <a href="/logout">Logout</a></p>
<p>Angemeldet als {{ user.preferred_username }}{% if 'Fachschaft Admins' in user.groups %} (Admin){% endif %} <a href="/logout">Logout</a></p>
{% endif %}
</header>
<main>
{% block content %}{% endblock %}
{% if user %}
{% if 'Fachschaft' in user.groups %}
<p>Du bist Teil der Fachschaft Informatik.</p>
{% endif %}
{% if 'Fachschaft Admins' in user.groups %}
<h2>Admin Interface</h2>
<p>Users in database:</p>
<ul>
{% for db_user in users %}
<li>{{ db_user.username }} ({{ db_user.role }}) - {{ db_user.money / 100 }}</li>
{% endfor %}
</ul>
<p>Set user money:</p>
<form method="post" action="/set_money">
<input type="text" name="username" placeholder="Username" required>
<input type="number" name="money" placeholder="Money" step="0.01" required>
<button type="submit">Set Money</button>
</form>
{% endif %}
{% endif %}
</main>
</body>
</html>

View File

@@ -1,6 +1,14 @@
{% extends "base.html" %}
{% block title %}Startseite{% endblock %}
{% block content %}
<h2>Willkommen, {{ user.username }}!</h2>
<h2>Willkommen, {{ user.name }}!</h2>
<p>Dies ist eine einfache geschützte Seite.</p>
<h3>Aktueller Stand:</h3>
<p>Du hast {{ db_user.money / 100 }} Euro.</p>
{% if 'Fachschaft' in user.groups %}
<h3>Getränk abziehen</h3>
<form method="post" action="/drink">
<button type="submit">Abziehen</button>
</form>
{% endif %}
{% endblock %}

BIN
test.db

Binary file not shown.