More database handling and admin interface
This commit is contained in:
20
auth/oidc.py
20
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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
15
db/models.py
15
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():
|
||||
|
||||
40
main.py
40
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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
Reference in New Issue
Block a user