196 lines
7.5 KiB
Python
196 lines
7.5 KiB
Python
"""
|
|
Core Routes - Authentification
|
|
GMAO Collège
|
|
"""
|
|
from flask import Blueprint, render_template, redirect, url_for, request, flash
|
|
from flask_login import login_user, logout_user, login_required, current_user
|
|
from ...extensions import db
|
|
from ..models.user import User
|
|
from ..forms import LoginForm, UserForm, PasswordForm
|
|
from ...constants import ROLES
|
|
from ..models.rbac import Role
|
|
from ..services.rbac import replace_user_roles
|
|
from ..authorization import has_permission
|
|
|
|
auth_bp = Blueprint('auth', __name__)
|
|
|
|
|
|
@auth_bp.route('/login', methods=['GET', 'POST'])
|
|
def login():
|
|
"""Page de connexion."""
|
|
if current_user.is_authenticated:
|
|
return redirect(url_for('dashboard.index'))
|
|
|
|
form = LoginForm()
|
|
if form.validate_on_submit():
|
|
user = User.query.filter_by(username=form.username.data).first()
|
|
if user and user.check_password(form.password.data):
|
|
if not user.is_active:
|
|
flash("Votre compte est désactivé.", "danger")
|
|
return render_template("auth/login.html", form=form)
|
|
login_user(user)
|
|
flash(f"Bienvenue, {user.username} !", "success")
|
|
next_page = request.args.get('next', '')
|
|
if not next_page.startswith('/') or next_page.startswith('//'):
|
|
next_page = ''
|
|
return redirect(next_page or url_for('dashboard.index'))
|
|
flash("Nom d'utilisateur ou mot de passe incorrect.", "danger")
|
|
|
|
return render_template('auth/login.html', form=form)
|
|
|
|
|
|
@auth_bp.route('/logout')
|
|
@login_required
|
|
def logout():
|
|
"""Déconnexion."""
|
|
logout_user()
|
|
flash("Vous avez été déconnecté.", "info")
|
|
return redirect(url_for('auth.login'))
|
|
|
|
|
|
@auth_bp.route('/users')
|
|
@login_required
|
|
def list_users():
|
|
"""Liste des utilisateurs."""
|
|
if not has_permission('user.view', current_user):
|
|
flash("Accès réservé aux administrateurs.", "danger")
|
|
return redirect(url_for('dashboard.index'))
|
|
|
|
users = User.query.order_by(User.full_name).all()
|
|
return render_template('auth/users.html', users=users)
|
|
|
|
|
|
@auth_bp.route('/users/new', methods=['GET', 'POST'])
|
|
@login_required
|
|
def create_user():
|
|
"""Créer un utilisateur."""
|
|
if not has_permission('user.create', current_user):
|
|
flash("Accès réservé aux administrateurs.", "danger")
|
|
return redirect(url_for('dashboard.index'))
|
|
|
|
form = UserForm()
|
|
if form.validate_on_submit():
|
|
selected_role_slugs = request.form.getlist('role_ids') or ([form.role.data] if form.role.data else [])
|
|
user = User(
|
|
username=form.username.data,
|
|
email=form.email.data,
|
|
full_name=form.full_name.data,
|
|
role=form.role.data,
|
|
is_active=form.is_active.data,
|
|
)
|
|
if not form.password.data:
|
|
form.password.errors.append("Un mot de passe initial est obligatoire.")
|
|
return render_template('auth/user_form.html', form=form, title="Nouvel utilisateur"), 400
|
|
user.set_password(form.password.data)
|
|
|
|
db.session.add(user)
|
|
db.session.flush()
|
|
selected_roles = Role.query.filter(Role.slug.in_(selected_role_slugs), Role.is_active.is_(True)).all() if selected_role_slugs else []
|
|
replace_user_roles(user, [role.id for role in selected_roles], assigned_by=current_user)
|
|
db.session.commit()
|
|
flash(f"Utilisateur {user.full_name} créé.", "success")
|
|
return redirect(url_for('auth.list_users'))
|
|
|
|
return render_template('auth/user_form.html', form=form, title="Nouvel utilisateur", roles_list=Role.query.filter_by(is_active=True).order_by(Role.name).all())
|
|
|
|
|
|
@auth_bp.route('/profile')
|
|
@login_required
|
|
def profile():
|
|
"""Profil utilisateur."""
|
|
return render_template('auth/profile.html', user=current_user)
|
|
|
|
|
|
@auth_bp.route('/users/<int:id>/edit', methods=['GET', 'POST'])
|
|
@login_required
|
|
def edit_user(id):
|
|
"""Modifier un utilisateur."""
|
|
if not has_permission('user.edit', current_user):
|
|
flash("Accès réservé aux administrateurs.", "danger")
|
|
return redirect(url_for('dashboard.index'))
|
|
|
|
user = User.query.get_or_404(id)
|
|
form = UserForm(obj=user)
|
|
|
|
if form.validate_on_submit():
|
|
selected_role_slugs = request.form.getlist('role_ids') or ([form.role.data] if form.role.data else [])
|
|
user.username = form.username.data
|
|
user.email = form.email.data
|
|
user.full_name = form.full_name.data
|
|
user.is_active = form.is_active.data
|
|
|
|
if form.password.data:
|
|
user.set_password(form.password.data)
|
|
selected_roles = Role.query.filter(Role.slug.in_(selected_role_slugs), Role.is_active.is_(True)).all() if selected_role_slugs else []
|
|
replace_user_roles(user, [role.id for role in selected_roles], assigned_by=current_user)
|
|
db.session.commit()
|
|
flash(f"Utilisateur {user.full_name} modifié.", "success")
|
|
return redirect(url_for('auth.list_users'))
|
|
|
|
return render_template('auth/user_form.html', form=form, title="Modifier l'utilisateur", roles_list=Role.query.filter_by(is_active=True).order_by(Role.name).all())
|
|
|
|
|
|
@auth_bp.route('/change-password', methods=['GET', 'POST'])
|
|
@login_required
|
|
def change_own_password():
|
|
"""Changement de mot de passe."""
|
|
form = PasswordForm()
|
|
|
|
if form.validate_on_submit():
|
|
if not current_user.check_password(form.current_password.data):
|
|
flash("Mot de passe actuel incorrect.", "danger")
|
|
else:
|
|
current_user.set_password(form.new_password.data)
|
|
db.session.commit()
|
|
flash("Mot de passe modifié avec succès.", "success")
|
|
return redirect(url_for('dashboard.index'))
|
|
|
|
return render_template('auth/change_own_password.html', form=form)
|
|
|
|
|
|
@auth_bp.route('/users/<int:id>/delete', methods=['POST'])
|
|
@login_required
|
|
def delete_user(id):
|
|
"""Supprimer un utilisateur."""
|
|
if not has_permission('user.delete', current_user):
|
|
flash("Accès réservé aux administrateurs.", "danger")
|
|
return redirect(url_for('dashboard.index'))
|
|
|
|
user = User.query.get_or_404(id)
|
|
if user.id == current_user.id:
|
|
flash("Vous ne pouvez pas supprimer votre propre compte.", "danger")
|
|
else:
|
|
db.session.delete(user)
|
|
db.session.commit()
|
|
flash(f"Utilisateur {user.username} supprimé.", "success")
|
|
|
|
return redirect(url_for('auth.list_users'))
|
|
|
|
|
|
@auth_bp.route('/users/<int:id>/password', methods=['GET', 'POST'])
|
|
@login_required
|
|
def change_user_password(id):
|
|
"""Changer le mot de passe d'un utilisateur (admin only)."""
|
|
if not has_permission('user.edit', current_user):
|
|
flash("Accès réservé aux administrateurs.", "danger")
|
|
return redirect(url_for('dashboard.index'))
|
|
|
|
user = User.query.get_or_404(id)
|
|
|
|
if request.method == 'POST':
|
|
new_password = request.form.get('new_password')
|
|
confirm_password = request.form.get('confirm_password')
|
|
|
|
if not new_password:
|
|
flash("Le mot de passe est requis.", "danger")
|
|
elif new_password != confirm_password:
|
|
flash("Les mots de passe ne correspondent pas.", "danger")
|
|
elif len(new_password) < 12:
|
|
flash("Le mot de passe doit contenir au moins 12 caractères.", "danger")
|
|
else:
|
|
user.set_password(new_password)
|
|
db.session.commit()
|
|
flash(f"Mot de passe de {user.username} modifié.", "success")
|
|
return redirect(url_for('auth.list_users'))
|
|
|
|
return render_template('auth/change_user_password.html', user=user)
|