Permissions & Contrôle d'Accès
Ce document décrit le système de permissions de StageConnect : les rôles utilisateurs, les droits associés, et comment ils sont appliqués dans le backend.
Rôles Utilisateurs (UserType)
Le backend définit 6 rôles dans l'enum UserType (app/core/config.py). Ces rôles sont encodés dans chaque JWT dans le claim user_type.
| Rôle JWT | Code | Interface | Rôle métier |
|---|---|---|---|
STUDENT | UserType.STUDENT | Discover ou Career | Étudiant L1/L2 ou L3/M |
UNIVERSITY_ADMIN | UserType.UNIVERSITY_ADMIN | Career | Responsable pédagogique université |
ACADEMIC_SUPERVISOR | UserType.ACADEMIC_SUPERVISOR | Career (portail encadrant) | Encadreur académique |
COMPANY | UserType.COMPANY | Career | Représentant entreprise |
COMPANY_MENTOR | UserType.COMPANY_MENTOR | Career (portail encadrant) | Maître de stage |
PLATFORM_ADMIN | UserType.PLATFORM_ADMIN | Admin | Administrateur plateforme |
Dans la base de données (users.user_type), les valeurs sont différentes : STUDENT, ACADEMIC_STAFF, COMPANY_STAFF, ADMIN, SCHOOL_STAFF. Ces valeurs DB sont ensuite mappées vers les rôles JWT lors de l'authentification. Dans le code Python, toujours utiliser UserType.UNIVERSITY_ADMIN etc., pas les valeurs brutes de la DB.
Dépendances FastAPI par Rôle
Le fichier app/utils/dependencies.py expose des dépendances prêtes à l'emploi :
# Accès étudiant uniquement
current_user = Depends(get_current_student)
# Accès admin université uniquement
current_user = Depends(get_current_university_admin)
# Accès encadreur académique uniquement
current_user = Depends(get_current_academic_supervisor)
# Accès entreprise uniquement
current_user = Depends(get_current_company_user)
# Accès maître de stage uniquement
current_user = Depends(get_current_company_mentor)
# Accès admin plateforme uniquement
current_user = Depends(get_current_platform_admin)
Multi-rôles
# Utiliser require_user_types() pour les endpoints multi-rôles
from app.utils.dependencies import require_user_types
from app.core.config import UserType
@router.get("/shared-endpoint")
async def shared_endpoint(
current_user = Depends(require_user_types([
UserType.UNIVERSITY_ADMIN,
UserType.PLATFORM_ADMIN
]))
):
...
Matrice des Droits par Rôle
Étudiants
| Action | L1/L2 (Discover) | L3/M (Career) |
|---|---|---|
| Voir les offres de stage (liste) | ✅ | ✅ |
| Voir le détail d'une offre | ❌ | ✅ |
| Candidater à une offre | ❌ | ✅ |
| Retirer une candidature | ❌ | ✅ |
| Modifier son profil (basique) | ✅ | ✅ |
| Uploader un CV | ❌ | ✅ |
| Proposer un thème de stage | ❌ | ✅ |
| Créer un binôme | ❌ | ✅ |
| Consulter sa convention | ❌ | ✅ |
| Soumettre un rapport | ❌ | ✅ |
La séparation L1/L2 vs L3/M est actuellement gérée côté frontend par l'application (Discover vs Career). Le backend applique les permissions par UserType.STUDENT sans distinction de niveau. La distinction de niveau académique est basée sur students.academic_year en DB et vérifiée dans les services métier concernés.
Université
| Action | UNIVERSITY_ADMIN |
|---|---|
| Voir les étudiants de son université | ✅ |
| Valider les thèmes de stage | ✅ |
| Assigner des encadreurs académiques | ✅ |
| Gérer les conventions de stage | ✅ |
| Programmer les soutenances | ✅ |
| Voir les statistiques université | ✅ |
| Accéder aux données d'autres universités | ❌ |
Entreprise
| Action | COMPANY | COMPANY_MENTOR |
|---|---|---|
| Publier des offres | ✅ | ❌ |
| Voir les candidatures reçues | ✅ | ❌ |
| Voir les stagiaires en cours | ✅ | ✅ (siens uniquement) |
| Évaluer un stagiaire | ❌ | ✅ |
| Gérer les maîtres de stage | ✅ | ❌ |
| Voir le profil des candidats | ✅ | ✅ |
Admin Plateforme
| Action | PLATFORM_ADMIN |
|---|---|
| Lire toutes les données | ✅ |
| Modifier les utilisateurs | ✅ |
| Voir les logs d'activité | ✅ |
| Gérer les plans tarifaires | ✅ |
| Accéder aux rapports de bugs | ✅ |
| Suspendre un compte | ✅ |
Décorateurs de Sécurité
Le fichier app/utils/decorators.py fournit des décorateurs pour vérifications rapides :
from app.utils.decorators import require_platform_admin, require_university_admin, require_company_access
@router.delete("/users/{user_id}")
@require_platform_admin
async def delete_user(user_id: str, current_user = Depends(get_current_user)):
...
Comment Ajouter un Nouveau Endpoint Protégé
from fastapi import APIRouter, Depends
from app.utils.dependencies import get_current_student
router = APIRouter()
@router.get("/my-protected-endpoint")
async def my_endpoint(current_user = Depends(get_current_student)):
"""
Endpoint accessible uniquement aux étudiants.
current_user.user_type == UserType.STUDENT est garanti.
"""
student_id = current_user.id
...
Règles de Sécurité
Les permissions ne doivent JAMAIS être gérées côté frontend. Le frontend peut adapter son UI (masquer/afficher des boutons), mais le backend doit toujours refuser les requêtes non autorisées — indépendamment de ce que le frontend affiche.
- Toujours utiliser une dépendance : Chaque endpoint protégé doit avoir
Depends(...)avec le bon rôle. - Isolation des données : Un
UNIVERSITY_ADMINne peut accéder qu'aux données de son université (filtrer paruniversity_id). - Pas de confiance au JWT pour les données : Le JWT dit qui vous êtes, pas quelles données vous pouvez voir — vérifier sur chaque requête.
Fichiers de Référence
| Fichier | Contenu |
|---|---|
app/core/config.py | Définition de UserType enum |
app/utils/dependencies.py | Dépendances FastAPI par rôle |
app/utils/decorators.py | Décorateurs de sécurité |
app/utils/auth.py | verify_token(), create_tokens() |