From 1693a56e43216eff555f215b9d897f77d3157cef Mon Sep 17 00:00:00 2001 From: Moritz Kowalski Date: Tue, 13 May 2025 15:51:25 +0200 Subject: [PATCH] More database handling and admin interface --- auth/oidc.py | 20 +++++++++- auth/webauthn.py | 86 ------------------------------------------- db/models.py | 15 +++++++- main.py | 40 +++++++++++++++++--- templates/base.html | 23 +++++++++++- templates/index.html | 10 ++++- test.db | Bin 12288 -> 12288 bytes 7 files changed, 97 insertions(+), 97 deletions(-) diff --git a/auth/oidc.py b/auth/oidc.py index 70c1408..aef1650 100644 --- a/auth/oidc.py +++ b/auth/oidc.py @@ -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") diff --git a/auth/webauthn.py b/auth/webauthn.py index a44247d..e69de29 100644 --- a/auth/webauthn.py +++ b/auth/webauthn.py @@ -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) diff --git a/db/models.py b/db/models.py index c2f66cc..c1d2842 100644 --- a/db/models.py +++ b/db/models.py @@ -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(): diff --git a/main.py b/main.py index 142a8fb..d1b7947 100644 --- a/main.py +++ b/main.py @@ -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) diff --git a/templates/base.html b/templates/base.html index 88f79c8..7ca5eb0 100644 --- a/templates/base.html +++ b/templates/base.html @@ -9,11 +9,32 @@

Meine Beispielseite

{% if user %} -

Angemeldet als {{ user.preferred_username }} ({{ user.groups }}) – Logout

+

Angemeldet als {{ user.preferred_username }}{% if 'Fachschaft Admins' in user.groups %} (Admin){% endif %} – Logout

{% endif %}
{% block content %}{% endblock %} + {% if user %} + {% if 'Fachschaft' in user.groups %} +

Du bist Teil der Fachschaft Informatik.

+ {% endif %} + {% if 'Fachschaft Admins' in user.groups %} +

Admin Interface

+

Users in database:

+ + +

Set user money:

+
+ + + +
+ {% endif %} + {% endif %}
diff --git a/templates/index.html b/templates/index.html index 4ac2188..813cfa7 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,6 +1,14 @@ {% extends "base.html" %} {% block title %}Startseite{% endblock %} {% block content %} -

Willkommen, {{ user.username }}!

+

Willkommen, {{ user.name }}!

Dies ist eine einfache geschützte Seite.

+

Aktueller Stand:

+

Du hast {{ db_user.money / 100 }} Euro.

+{% if 'Fachschaft' in user.groups %} +

Getränk abziehen

+
+ +
+{% endif %} {% endblock %} diff --git a/test.db b/test.db index afdc2608f499469d590778ece66b5447a09bda5d..059b2b49334be55621c86715b09e104cb97fe54f 100644 GIT binary patch delta 195 zcmZojXh@hK%@{IK##u0gK`(C?F9QPu6Tc_}e-Xdv#=@n1lhyfSxf;0{*~P`h85>I{ z_wpy|=jP|7Rw{VzoB*~P`h85@Hq r*YGDw1O$2dItE25c)La_Xk?~na%qBeF$ioHRJg-Gu|bZF3xx{+Z~PXP