elemes/services/teacher_auth.py

56 lines
2.1 KiB
Python

"""Helper auth guru — dipakai lintas blueprint (student_management, content_editor)."""
import os
from flask import request
from services import repositories
from services.token_service import validate_token
def teacher_from_cookie():
"""Cek cepat is_teacher tanpa query user_id (dipakai student_management.py,
yang endpoint-nya tidak butuh atribusi user_id)."""
token = request.cookies.get("student_token", "")
if not token:
return None
info = validate_token(token)
if not info or not info.get("is_teacher"):
return None
return info
def teacher_user_from_cookie(db):
"""Resolve objek User (role='teacher') dari cookie, atau None.
Dipakai endpoint yang butuh user_id sungguhan (mis. content_editor)."""
token = request.cookies.get("student_token", "")
if not token:
return None
user = repositories.get_user_by_token(db, token)
if not user or user.role != "teacher":
return None
return user
def check_origin() -> bool:
"""Known limitation: fail-open bila header Origin tidak ada.
Request tanpa header Origin dianggap sah (return True). Ini pola lama yang
sudah dipakai sebelum ini (student_management.py) dan bukan regresi.
Risiko diflaskan oleh cookie student_token yang SameSite=Lax di routes/auth.py
— browser modern tidak mengirim cookie ini pada request state-changing
(POST/PATCH/DELETE) cross-site.
Namun karena content_editor blueprint kini memiliki akses tulis/hapus langsung
ke filesystem (bukan hanya baca data siswa), kebijakan fail-open ini menjadi
lebih berisiko. Jika Origin header tidak ada, request dianggap berasal dari
klien same-origin (seperti curl, internal service call, atau browser yang
menyederhanakan header). Jika Origin ada, wajib match terhadap ORIGIN env.
"""
allowed = os.environ.get("ORIGIN", "").strip()
if not allowed or allowed == "*":
return True
origin = request.headers.get("Origin", "")
if not origin:
return True # fail-open: rasa tidak adanya header Origin
return origin in {allowed, allowed.rstrip("/")}