"""
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