This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
breakpilot-pwa/backend/session/protected_routes.py
BreakPilot Dev 19855efacc
Some checks failed
Tests / Go Tests (push) Has been cancelled
Tests / Python Tests (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / Go Lint (push) Has been cancelled
Tests / Python Lint (push) Has been cancelled
Tests / Security Scan (push) Has been cancelled
Tests / All Checks Passed (push) Has been cancelled
Security Scanning / Secret Scanning (push) Has been cancelled
Security Scanning / Dependency Vulnerability Scan (push) Has been cancelled
Security Scanning / Go Security Scan (push) Has been cancelled
Security Scanning / Python Security Scan (push) Has been cancelled
Security Scanning / Node.js Security Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
CI/CD Pipeline / Go Tests (push) Has been cancelled
CI/CD Pipeline / Python Tests (push) Has been cancelled
CI/CD Pipeline / Website Tests (push) Has been cancelled
CI/CD Pipeline / Linting (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Docker Build & Push (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / CI Summary (push) Has been cancelled
ci/woodpecker/manual/build-ci-image Pipeline was successful
ci/woodpecker/manual/main Pipeline failed
feat: BreakPilot PWA - Full codebase (clean push without large binaries)
All services: admin-v2, studio-v2, website, ai-compliance-sdk,
consent-service, klausur-service, voice-service, and infrastructure.
Large PDFs and compiled binaries excluded via .gitignore.
2026-02-11 13:25:58 +01:00

390 lines
10 KiB
Python

"""
Protected Routes Example
Shows how to structure routes under /api/protected with session-based auth.
Route structure:
/api/auth/ - Public (login, register, logout)
/api/public/ - Public (health, docs)
/api/protected/ - Authenticated (all users)
/api/protected/employee/ - Employees only
/api/protected/customer/ - Customers only
/api/admin/ - Admins only
"""
from fastapi import APIRouter, Depends, HTTPException
from typing import List, Optional
from .session_store import Session, UserType
from .session_middleware import get_current_session, get_optional_session
from .rbac_middleware import (
require_employee,
require_customer,
require_permission,
require_role,
require_any_role,
)
# =============================================
# Router Setup
# =============================================
# Protected routes - require authentication
protected_router = APIRouter(prefix="/api/protected", tags=["Protected"])
# Employee-only routes
employee_router = APIRouter(prefix="/api/protected/employee", tags=["Employee"])
# Customer-only routes
customer_router = APIRouter(prefix="/api/protected/customer", tags=["Customer"])
# Admin routes
admin_router = APIRouter(prefix="/api/admin", tags=["Admin"])
# =============================================
# Protected Routes (All Authenticated Users)
# =============================================
@protected_router.get("/profile")
async def get_profile(session: Session = Depends(get_current_session)):
"""Get current user's profile."""
return {
"user_id": session.user_id,
"email": session.email,
"user_type": session.user_type.value,
"roles": session.roles,
"permissions": session.permissions,
"tenant_id": session.tenant_id,
}
@protected_router.get("/notifications")
async def get_notifications(session: Session = Depends(get_current_session)):
"""Get user's notifications."""
# TODO: Implement actual notification fetching
return {
"notifications": [],
"unread_count": 0,
}
@protected_router.post("/logout")
async def logout(session: Session = Depends(get_current_session)):
"""Logout current session."""
from .session_store import get_session_store
store = await get_session_store()
await store.revoke_session(session.session_id)
return {"message": "Logged out successfully"}
@protected_router.post("/logout-all")
async def logout_all(session: Session = Depends(get_current_session)):
"""Logout from all devices."""
from .session_store import get_session_store
store = await get_session_store()
count = await store.revoke_all_user_sessions(session.user_id)
return {"message": f"Logged out from {count} sessions"}
@protected_router.get("/sessions")
async def get_active_sessions(session: Session = Depends(get_current_session)):
"""Get all active sessions for current user."""
from .session_store import get_session_store
store = await get_session_store()
sessions = await store.get_active_sessions(session.user_id)
return {
"sessions": [
{
"session_id": s.session_id,
"ip_address": s.ip_address,
"user_agent": s.user_agent,
"created_at": s.created_at.isoformat() if s.created_at else None,
"last_activity_at": s.last_activity_at.isoformat() if s.last_activity_at else None,
"is_current": s.session_id == session.session_id,
}
for s in sessions
]
}
# =============================================
# Employee Routes
# =============================================
@employee_router.get("/dashboard")
async def employee_dashboard(session: Session = Depends(require_employee)):
"""Employee dashboard with overview data."""
return {
"user_type": "employee",
"email": session.email,
"roles": session.roles,
"widgets": [
{"type": "today_classes", "title": "Heutige Stunden"},
{"type": "pending_corrections", "title": "Ausstehende Korrekturen"},
{"type": "absent_students", "title": "Abwesende Schueler"},
],
}
@employee_router.get("/grades")
async def get_grades(
class_id: Optional[str] = None,
session: Session = Depends(require_permission("grades:read"))
):
"""Get grades (employee only, requires grades:read permission)."""
# TODO: Implement actual grade fetching
return {
"grades": [],
"class_id": class_id,
}
@employee_router.post("/grades")
async def create_grade(
grade_data: dict,
session: Session = Depends(require_permission("grades:write"))
):
"""Create a new grade (requires grades:write permission)."""
# TODO: Implement grade creation
return {"message": "Grade created"}
@employee_router.get("/attendance")
async def get_attendance(
date: Optional[str] = None,
class_id: Optional[str] = None,
session: Session = Depends(require_permission("attendance:read"))
):
"""Get attendance records."""
return {
"attendance": [],
"date": date,
"class_id": class_id,
}
@employee_router.post("/attendance")
async def mark_attendance(
attendance_data: dict,
session: Session = Depends(require_permission("attendance:write"))
):
"""Mark student attendance."""
return {"message": "Attendance recorded"}
@employee_router.get("/students")
async def get_students(
class_id: Optional[str] = None,
session: Session = Depends(require_permission("students:read"))
):
"""Get student list."""
return {
"students": [],
"class_id": class_id,
}
@employee_router.get("/corrections")
async def get_corrections(
session: Session = Depends(require_permission("corrections:read"))
):
"""Get pending corrections."""
return {
"corrections": [],
"pending_count": 0,
}
# =============================================
# Customer Routes
# =============================================
@customer_router.get("/dashboard")
async def customer_dashboard(session: Session = Depends(require_customer)):
"""Customer dashboard."""
return {
"user_type": "customer",
"email": session.email,
"widgets": [
{"type": "my_children", "title": "Meine Kinder"},
{"type": "upcoming_meetings", "title": "Anstehende Termine"},
{"type": "recent_grades", "title": "Aktuelle Noten"},
],
}
@customer_router.get("/my-children")
async def get_my_children(
session: Session = Depends(require_permission("children:read"))
):
"""Get parent's children."""
# TODO: Implement actual children fetching
return {
"children": [],
}
@customer_router.get("/my-grades")
async def get_my_grades(
session: Session = Depends(require_permission("own_grades:read"))
):
"""Get student's own grades."""
return {
"grades": [],
"average": None,
}
@customer_router.get("/my-attendance")
async def get_my_attendance(
session: Session = Depends(require_permission("own_attendance:read"))
):
"""Get student's own attendance."""
return {
"attendance_records": [],
"absence_count": 0,
}
@customer_router.get("/children/{child_id}/grades")
async def get_child_grades(
child_id: str,
session: Session = Depends(require_permission("children:grades:read"))
):
"""Get child's grades (for parents)."""
# TODO: Verify parent-child relationship
return {
"child_id": child_id,
"grades": [],
}
@customer_router.get("/appointments")
async def get_appointments(session: Session = Depends(require_customer)):
"""Get upcoming appointments/meetings."""
return {
"appointments": [],
}
@customer_router.post("/appointments/{slot_id}/book")
async def book_appointment(
slot_id: str,
session: Session = Depends(require_permission("meetings:join"))
):
"""Book a parent meeting slot."""
return {
"message": "Appointment booked",
"slot_id": slot_id,
}
# =============================================
# Admin Routes
# =============================================
@admin_router.get("/users")
async def list_users(
page: int = 1,
limit: int = 50,
session: Session = Depends(require_permission("users:read"))
):
"""List all users (admin only)."""
return {
"users": [],
"total": 0,
"page": page,
"limit": limit,
}
@admin_router.get("/users/{user_id}")
async def get_user(
user_id: str,
session: Session = Depends(require_permission("users:read"))
):
"""Get user details."""
return {
"user_id": user_id,
"email": None,
"roles": [],
}
@admin_router.put("/users/{user_id}/roles")
async def update_user_roles(
user_id: str,
roles: List[str],
session: Session = Depends(require_permission("users:manage"))
):
"""Update user roles (admin only)."""
return {
"message": "Roles updated",
"user_id": user_id,
"roles": roles,
}
@admin_router.get("/audit-log")
async def get_audit_log(
page: int = 1,
limit: int = 100,
session: Session = Depends(require_permission("audit:read"))
):
"""Get audit log entries."""
return {
"entries": [],
"total": 0,
"page": page,
"limit": limit,
}
@admin_router.get("/rbac/roles")
async def list_roles(
session: Session = Depends(require_permission("rbac:read"))
):
"""List all RBAC roles."""
return {
"roles": [],
}
@admin_router.get("/rbac/permissions")
async def list_permissions(
session: Session = Depends(require_permission("rbac:read"))
):
"""List all permissions."""
from .rbac_middleware import EMPLOYEE_PERMISSIONS, CUSTOMER_PERMISSIONS, ADMIN_PERMISSIONS
return {
"employee_permissions": EMPLOYEE_PERMISSIONS,
"customer_permissions": CUSTOMER_PERMISSIONS,
"admin_permissions": ADMIN_PERMISSIONS,
}
# =============================================
# Router Registration Helper
# =============================================
def register_protected_routes(app):
"""
Register all protected route routers with FastAPI app.
Usage:
from session.protected_routes import register_protected_routes
register_protected_routes(app)
"""
app.include_router(protected_router)
app.include_router(employee_router)
app.include_router(customer_router)
app.include_router(admin_router)