Code source de services.api_ihm.src.auth.routes

"""
Version: 1.2.0 (2023-12-20)
Auteur: Kahina et franck - Groupe 2
Commit: bigmoletos@yopmail.com

Routes d'authentification pour l'application de qualité de l'air.

Ce module gère l'ensemble des fonctionnalités d'authentification et
d'autorisation de l'application, notamment:
- Connexion et déconnexion des utilisateurs
- Inscription de nouveaux utilisateurs
- Gestion des utilisateurs par l'administrateur

Architecture:
------------
1. Authentification:
   - Connexion sécurisée
   - Gestion des sessions
   - Protection des routes

2. Gestion utilisateurs:
   - Inscription avec validation
   - Administration des comptes
   - Suppression sécurisée

3. Sécurité:
   - Hachage des mots de passe
   - Contrôle des accès
   - Protection CSRF

Routes:
-------
    /login : Connexion utilisateur
        - GET: Affiche le formulaire
        - POST: Traite la connexion

    /register : Inscription
        - GET: Affiche le formulaire
        - POST: Crée le compte

    /logout : Déconnexion
        - GET: Termine la session

    /admin : Interface admin
        - GET: Liste les utilisateurs
        - Accès restreint aux admins

Dépendances:
-----------
    - flask : Framework web
    - flask_login : Gestion authentification
    - database.models : Modèles SQLAlchemy
"""

from flask import Blueprint, render_template, redirect, url_for, flash, request
from flask_login import login_user, logout_user, login_required, current_user
from database.models import User, db
import logging

logger = logging.getLogger(__name__)

auth_bp = Blueprint('auth', __name__)


[docs] @auth_bp.route('/login', methods=['GET', 'POST']) def login(): """ Gère la connexion des utilisateurs à l'application. Cette route: 1. Affiche le formulaire de connexion (GET) 2. Authentifie l'utilisateur (POST) 3. Crée la session utilisateur Methods: ------- GET: Affiche le formulaire de connexion POST: Traite les informations de connexion Parameters: ---------- username : str Nom d'utilisateur (via form) password : str Mot de passe (via form) Returns: ------- GET: Template: login.html POST: Redirect: Page d'accueil si succès Template: login.html avec erreur si échec Version: 1.0.0 (2024-03-19 15:35) """ if request.method == 'POST': username = request.form['username'] user = User.query.filter_by(username=username).first() if user and user.check_password(request.form['password']): login_user(user) return redirect(url_for('main.index')) flash('Nom d’utilisateur ou mot de passe incorrect') return render_template('login.html')
[docs] @auth_bp.route('/register', methods=['GET', 'POST']) def register(): """ Gère l'inscription des nouveaux utilisateurs. Cette route: 1. Affiche le formulaire d'inscription (GET) 2. Valide les données soumises (POST) 3. Crée le compte utilisateur Methods: ------- GET: Affiche le formulaire d'inscription POST: Traite la création du compte Parameters: ---------- username : str Nom d'utilisateur unique (via form) email : str Email unique (via form) password : str Mot de passe (via form) Returns: ------- GET: Template: register.html POST: Redirect: Page de connexion si succès Template: register.html avec erreur si échec Notes: ----- - Vérifie l'unicité du username - Vérifie l'unicité de l'email - Hash le mot de passe - Gère les erreurs de BD Version: 1.0.0 (2024-03-19 15:35) """ if request.method == 'POST': # Vérifier si l'utilisateur existe déjà existing_user = User.query.filter_by( username=request.form['username']).first() if existing_user: flash('Ce nom d\'utilisateur est déjà pris', 'warning') return render_template('register.html') # Vérifier si l'email existe déjà existing_email = User.query.filter_by( email=request.form['email']).first() if existing_email: flash('Cet email est déjà utilisé', 'warning') return render_template('register.html') # Vérifier la complexité du mot de passe password = request.form['password'] if len(set(password)) < 4: flash( 'Le mot de passe doit contenir au moins 4 caractères différents', 'warning') return render_template('register.html') user = User(username=request.form['username'], email=request.form['email']) user.set_password(request.form['password']) try: db.session.add(user) db.session.commit() flash( f'Inscription réussie ! Compte créé avec le nom d\'utilisateur "{user.username}" et l\'email "{user.email}". Vous pouvez maintenant vous connecter.', 'success') return redirect(url_for('auth.login')) except Exception as e: db.session.rollback() logger.error(f"Erreur lors de l'inscription : {str(e)}") flash('Une erreur est survenue lors de l\'inscription', 'danger') return render_template('register.html') return render_template('register.html')
[docs] @auth_bp.route('/logout') @login_required def logout(): """ Déconnecte l'utilisateur actuel. Cette route: 1. Termine la session utilisateur 2. Nettoie les cookies 3. Redirige vers la page de connexion Decorators: ---------- login_required: Assure qu'un utilisateur est connecté Returns: ------- Redirect: Page de connexion """ logout_user() return redirect(url_for('auth.login'))
[docs] @auth_bp.route('/admin') @login_required def admin(): """ Affiche l'interface d'administration des utilisateurs. Cette route: 1. Vérifie les droits d'administration 2. Liste tous les utilisateurs 3. Permet la gestion des comptes Decorators: ---------- login_required: Assure qu'un utilisateur est connecté Returns: ------- Template: admin.html avec liste des utilisateurs Redirect: Page d'accueil si non admin Notes: ----- - Accès restreint aux administrateurs - Affiche tous les utilisateurs - Permet ajout/suppression """ if not current_user.is_admin: flash('Accès non autorisé') return redirect(url_for('main.index')) users = User.query.all() return render_template('admin.html', users=users)
[docs] @auth_bp.route('/add_user', methods=['POST']) @login_required def add_user(): """ Ajoute un nouvel utilisateur via l'interface admin. """ if not current_user.is_admin: return {'error': 'Accès non autorisé'}, 403 try: # Vérifier si l'utilisateur existe déjà existing_user = User.query.filter_by( username=request.form['username']).first() if existing_user: flash('Ce nom d\'utilisateur est déjà pris', 'warning') return redirect(url_for('auth.admin')) # Vérifier si l'email existe déjà existing_email = User.query.filter_by( email=request.form['email']).first() if existing_email: flash('Cet email est déjà utilisé', 'warning') return redirect(url_for('auth.admin')) # Vérifier la complexité du mot de passe password = request.form['password'] if len(set(password)) < 4: flash( 'Le mot de passe doit contenir au moins 4 caractères différents', 'warning') return redirect(url_for('auth.admin')) # Créer le nouvel utilisateur user = User(username=request.form['username'], email=request.form['email']) user.set_password(request.form['password']) db.session.add(user) db.session.commit() flash('Utilisateur ajouté avec succès', 'success') return redirect(url_for('auth.admin')) except Exception as e: db.session.rollback() logger.error(f"Erreur lors de l'ajout de l'utilisateur : {str(e)}") flash('Une erreur est survenue lors de l\'ajout de l\'utilisateur', 'danger') return redirect(url_for('auth.admin'))
[docs] @auth_bp.route('/delete_user/<int:user_id>', methods=['DELETE']) @login_required def delete_user(user_id): """ Supprime un utilisateur via l'interface admin. Cette route: 1. Vérifie les droits d'administration 2. Valide l'existence de l'utilisateur 3. Supprime le compte et ses données Decorators: ---------- login_required: Assure qu'un utilisateur est connecté Parameters: ---------- user_id : int ID de l'utilisateur à supprimer Returns: ------- JSON: Message de succès (200) Erreur 403 si non admin Erreur 404 si utilisateur non trouvé Erreur 400 si tentative de suppression de soi-même Notes: ----- - Accès restreint aux administrateurs - Empêche l'auto-suppression - Suppression en cascade des données """ if not current_user.is_admin: return {'error': 'Accès non autorisé'}, 403 user = User.query.get_or_404(user_id) if user.id == current_user.id: return {'error': 'Impossible de supprimer votre propre compte'}, 400 db.session.delete(user) db.session.commit() return {'message': 'Utilisateur supprimé'}, 200