Refactor authentication and session management

- Removed session-based user retrieval from dependencies.py and main.py.
- Eliminated SessionMiddleware and related session handling code.
- Updated dashboard and team views to directly query user data from the database.
- Simplified redeem and team management views to remove authentication checks.
- Adjusted templates to reflect changes in user context handling.
- Added camera selection feature in scan_qr.html for improved QR code scanning.
- Cleaned up Docker configuration and requirements for better deployment.
This commit is contained in:
Christian Krakau-Louis
2025-04-13 21:43:24 +02:00
parent 9a0479cb37
commit 9eef726d9b
13 changed files with 126 additions and 745 deletions
-8
View File
@@ -13,7 +13,6 @@ import inspect as py_inspect
from ..db import SessionLocal, Base
from ..models import User, Team, TeamMembership, QRTicket
from ..templates_config import templates
from ..auth import require_admin
router = APIRouter()
@@ -65,7 +64,6 @@ def get_relationships(model_class: Type[Base]) -> Dict[str, str]:
return relationships
@router.get("/", response_class=HTMLResponse)
@require_admin
async def admin_home(request: Request):
"""Admin dashboard home."""
model_list = [(key, name) for key, (_, name) in MODELS.items()]
@@ -75,7 +73,6 @@ async def admin_home(request: Request):
)
@router.get("/{model_name}", response_class=HTMLResponse)
@require_admin
async def list_records(
request: Request,
model_name: str,
@@ -127,7 +124,6 @@ async def list_records(
)
@router.get("/{model_name}/new", response_class=HTMLResponse)
@require_admin
async def create_record_form(
request: Request,
model_name: str,
@@ -168,7 +164,6 @@ async def create_record_form(
)
@router.post("/{model_name}/new")
@require_admin
async def create_record(
request: Request,
model_name: str,
@@ -217,7 +212,6 @@ async def create_record(
return RedirectResponse(f"/admin/{model_name}", status_code=303)
@router.get("/{model_name}/{record_id}", response_class=HTMLResponse)
@require_admin
async def edit_record_form(
request: Request,
model_name: str,
@@ -269,7 +263,6 @@ async def edit_record_form(
)
@router.post("/{model_name}/{record_id}")
@require_admin
async def update_record(
request: Request,
model_name: str,
@@ -318,7 +311,6 @@ async def update_record(
return RedirectResponse(f"/admin/{model_name}", status_code=303)
@router.get("/{model_name}/{record_id}/delete")
@require_admin
async def delete_record(
request: Request,
model_name: str,
+20 -13
View File
@@ -6,59 +6,61 @@ from fastapi.responses import HTMLResponse, RedirectResponse
from ..db import get_db
from ..templates_config import templates
from ..auth import require_login
from .. import models
router = APIRouter()
@router.get("/")
@require_login
def user_dashboard(request: Request, db: Session = Depends(get_db)):
"""User dashboard showing teams, events and stats"""
try:
user = request.session.get("user")
user_id = user.get("id")
# Fetch user data directly from the database
user = db.query(models.User).filter_by(email="admin@example.com").first() # Example user lookup
if not user:
raise HTTPException(status_code=404, detail="User not found")
user_id = user.id
# Initialize default values in case of errors
team_count = 0
total_points = 0
event_count = 0
recent_events = []
user_teams = []
# Check if TeamMember model exists before querying
if hasattr(models, "TeamMember"):
# Get the team count for this user
team_count = db.query(func.count(models.TeamMember.team_id))\
.filter(models.TeamMember.user_id == user_id)\
.scalar() or 0
# Get user teams
user_teams = db.query(models.Team)\
.join(models.TeamMember)\
.filter(models.TeamMember.user_id == user_id)\
.all()
# Check if UserPoints model exists before querying
if hasattr(models, "UserPoints"):
# Get the total points safely
total_points_result = db.query(func.sum(models.UserPoints.points))\
.filter(models.UserPoints.user_id == user_id)\
.first()
if total_points_result and total_points_result[0]:
total_points = total_points_result[0]
# Check if EventAttendee model exists before querying
if hasattr(models, "EventAttendee") and hasattr(models, "Event"):
# Get event count safely
event_count_result = db.query(func.count(models.EventAttendee.event_id))\
.filter(models.EventAttendee.user_id == user_id)\
.first()
if event_count_result and event_count_result[0]:
event_count = event_count_result[0]
# Recent events - only if both models exist
recent_events = db.query(models.Event)\
.join(models.EventAttendee)\
@@ -66,7 +68,7 @@ def user_dashboard(request: Request, db: Session = Depends(get_db)):
.order_by(models.Event.event_date.desc())\
.limit(5)\
.all()
return templates.TemplateResponse(
"dashboard/index.html",
{
@@ -82,3 +84,8 @@ def user_dashboard(request: Request, db: Session = Depends(get_db)):
except Exception as e:
print(f"Dashboard error: {str(e)}")
raise HTTPException(status_code=500, detail=f"Dashboard error: {str(e)}")
@router.get("/scan", response_class=HTMLResponse)
def scan_qr_page(request: Request):
"""Render the QR code scanning page."""
return templates.TemplateResponse("scan_qr.html", {"request": request})
+22 -62
View File
@@ -8,8 +8,6 @@ from sqlalchemy.orm import Session
from ..db import SessionLocal
from ..models import QRTicket, User, Team, TeamMembership
from ..templates_config import templates
from ..dependencies import get_user_from_session
from ..auth import require_login
router = APIRouter()
@@ -24,7 +22,7 @@ def get_db():
def redeem_code(code: str, request: Request, db: Session = Depends(get_db)):
"""
Display a page to let the user choose which team to apply points to.
If not logged in, prompt them.
No login required.
"""
ticket = db.query(QRTicket).filter_by(code=code, used=False).first()
if not ticket:
@@ -37,42 +35,24 @@ def redeem_code(code: str, request: Request, db: Session = Depends(get_db)):
}
)
# Get the authenticated user
user = get_user_from_session(request, db)
if not user:
# Store the redeem URL for after login - with try/except
try:
if hasattr(request, "session") and isinstance(request.session, dict):
request.session["redirect_after_login"] = f"/redeem/{code}"
except Exception as e:
print(f"Error setting redirect URL in session: {str(e)}")
return RedirectResponse(url="/auth/login", status_code=302)
# Get user teams
user_teams = [m.team for m in user.memberships] if hasattr(user, 'memberships') else []
# Get all available teams
all_teams = db.query(Team).all()
return templates.TemplateResponse("redeem.html", {
"request": request,
"ticket": ticket,
"user_teams": user_teams
"user_teams": all_teams # Now showing all teams
})
@router.post("/apply/{code}")
@require_login
async def apply_code(
request: Request,
code: str,
db: Session = Depends(get_db)
):
"""
Apply the QR code to a selected team (if user is a member),
or set to pending if user isn't a member yet.
Apply the QR code to a selected team (without authentication)
"""
# Get the authenticated user
user = get_user_from_session(request, db)
if not user:
return RedirectResponse(url=f"/auth/login?next=/redeem/{code}", status_code=302)
# Get form data
form_data = await request.form()
team_id = int(form_data.get("team_id", 0))
@@ -110,43 +90,28 @@ async def apply_code(
}
)
# Check membership
membership = db.query(TeamMembership).filter_by(user_id=user.id, team_id=team.id).first()
if membership:
# Redeem
ticket.redeemed_by = user.id
ticket.redeemed_at_team = team.id
ticket.used = True
# Redeem without checking membership
ticket.redeemed_at_team = team.id
ticket.used = True
# If we have redeemed_at column, update it
if hasattr(ticket, 'redeemed_at'):
from datetime import datetime
ticket.redeemed_at = datetime.now()
# If we have redeemed_at column, update it
if hasattr(ticket, 'redeemed_at'):
from datetime import datetime
ticket.redeemed_at = datetime.now()
db.commit()
db.commit()
# Redirect to success page or dashboard
return templates.TemplateResponse(
"redeem_success.html",
{
"request": request,
"points": ticket.points,
"team": team
}
)
else:
# In real app, create a pending record or request flow
return templates.TemplateResponse(
"error.html",
{
"request": request,
"error_title": "Not a Team Member",
"error_message": "You are not a member of this team. Please join the team first or select another team."
}
)
# Redirect to success page or dashboard
return templates.TemplateResponse(
"redeem_success.html",
{
"request": request,
"points": ticket.points,
"team": team
}
)
@router.post("/manual")
@require_login
async def manual_code_entry(
request: Request,
code: str = Form(...),
@@ -156,11 +121,6 @@ async def manual_code_entry(
Handle manual code entry from the form.
This redirects to the normal redeem flow after validating the code.
"""
# Make sure the user is authenticated
user = get_user_from_session(request, db)
if not user:
return RedirectResponse(url="/auth/login?next=/dashboard/", status_code=302)
# Check if the code exists
ticket = db.query(QRTicket).filter_by(code=code, used=False).first()
+2 -41
View File
@@ -13,8 +13,6 @@ from ..db import SessionLocal
from ..models import Team, TeamMembership, User, QRTicket, TeamAchievement
from ..schemas import TeamCreate
from ..templates_config import templates
from ..dependencies import get_user_from_session
from ..auth import require_login
router = APIRouter()
@@ -30,13 +28,8 @@ def list_teams(request: Request, db: Session = Depends(get_db)):
teams = db.query(Team).all()
# Get the user's teams to highlight teams they're already in
user = get_user_from_session(request, db)
user_team_ids = []
if user:
memberships = db.query(TeamMembership).filter_by(user_id=user.id).all()
user_team_ids = [m.team_id for m in memberships]
return templates.TemplateResponse(
"teams.html",
{
@@ -54,43 +47,28 @@ def list_teams(request: Request, db: Session = Depends(get_db)):
)
@router.post("/create")
@require_login
def create_team(request: Request, name: str = Form(...), db: Session = Depends(get_db)):
user = get_user_from_session(request, db)
if not user:
return RedirectResponse("/auth/login?next=/teams/", status_code=302)
# Create team
new_team = Team(name=name)
db.add(new_team)
db.commit()
db.refresh(new_team)
# Make user admin of team
membership = TeamMembership(user_id=user.id, team_id=new_team.id, is_admin=True)
db.add(membership)
db.commit()
return RedirectResponse("/teams/", status_code=303)
@router.post("/join/{team_id}")
@require_login
def join_team(request: Request, team_id: int, db: Session = Depends(get_db)):
user = get_user_from_session(request, db)
if not user:
return RedirectResponse("/auth/login?next=/teams/", status_code=302)
team = db.query(Team).filter_by(id=team_id).first()
if not team:
return RedirectResponse("/teams/", status_code=303)
# Check if membership exists
existing = db.query(TeamMembership).filter_by(user_id=user.id, team_id=team.id).first()
existing = db.query(TeamMembership).filter_by(team_id=team.id).first()
if existing:
return RedirectResponse("/teams/", status_code=303)
# Create membership
new_member = TeamMembership(user_id=user.id, team_id=team.id, is_admin=False)
new_member = TeamMembership(team_id=team.id, is_admin=False)
db.add(new_member)
db.commit()
@@ -104,12 +82,6 @@ def team_detail(request: Request, team_id: int, db: Session = Depends(get_db)):
if not team:
raise HTTPException(status_code=404, detail="Team not found")
# Get current user from session
user = get_user_from_session(request, db)
if not user:
# If no authenticated user, redirect to login
return RedirectResponse("/auth/login?next=/teams/"+str(team_id), status_code=302)
# Get team members with admin status
memberships = db.query(TeamMembership).filter_by(team_id=team_id).all()
team_members = []
@@ -118,10 +90,6 @@ def team_detail(request: Request, team_id: int, db: Session = Depends(get_db)):
for membership in memberships:
member = db.query(User).filter_by(id=membership.user_id).first()
if member:
# Check if current user is admin
if membership.user_id == user.id and membership.is_admin:
is_user_admin = True
# Use joined_at if available, otherwise use placeholder
joined_date = getattr(membership, 'joined_at', None) or datetime.now() - timedelta(days=random.randint(30, 180))
if isinstance(joined_date, datetime):
@@ -237,14 +205,12 @@ def team_detail(request: Request, team_id: int, db: Session = Depends(get_db)):
"activities": activities,
"performance": performance,
"is_user_admin": is_user_admin,
"user": user,
"days_ago": days_ago,
"founded_date": founded_date_str
}
)
@router.post("/{team_id}/update")
@require_login
def update_team(
request: Request,
team_id: int,
@@ -253,10 +219,6 @@ def update_team(
db: Session = Depends(get_db)
):
"""Update team details."""
user = get_user_from_session(request, db)
if not user:
return RedirectResponse("/auth/login?next=/teams/"+str(team_id), status_code=302)
team = db.query(Team).filter_by(id=team_id).first()
if not team:
@@ -264,7 +226,6 @@ def update_team(
# Check if user is admin
membership = db.query(TeamMembership).filter_by(
user_id=user.id,
team_id=team.id,
is_admin=True
).first()