# -*- coding: UTF-8 -* """ auth.routes.py """ import flask from flask import current_app, flash, render_template from flask import redirect, url_for, request from flask_login import login_user, current_user from sqlalchemy import func from app import db from app.auth import bp, cas, logic from app.auth.forms import ( CASUsersImportConfigForm, LoginForm, ResetPasswordForm, ResetPasswordRequestForm, UserCreationForm, ) from app.auth.models import Role, User, invalid_user_name from app.auth.email import send_password_reset_email from app.decorators import admin_required from app.models.config import ScoDocSiteConfig from app.scodoc import sco_utils as scu _ = lambda x: x # sans babel _l = _ def _login_form(): """le formulaire de login, avec un lien CAS s'il est configuré.""" form = LoginForm() if form.validate_on_submit(): # note: ceci est la première requête SQL déclenchée par un utilisateur arrivant if invalid_user_name(form.user_name.data): user = None else: user = User.query.filter_by(user_name=form.user_name.data).first() if user is None or not user.check_password(form.password.data): current_app.logger.info("login: invalid (%s)", form.user_name.data) flash(_("Nom ou mot de passe invalide")) return redirect(url_for("auth.login")) login_user(user, remember=form.remember_me.data) current_app.logger.info("login: success (%s)", form.user_name.data) return form.redirect("scodoc.index") return render_template( "auth/login.j2", title=_("Sign In"), form=form, is_cas_enabled=ScoDocSiteConfig.is_cas_enabled(), ) @bp.route("/login", methods=["GET", "POST"]) def login(): """ScoDoc Login form Si paramètre cas_force, redirige vers le CAS. """ if current_user.is_authenticated: return redirect(url_for("scodoc.index")) if ScoDocSiteConfig.get("cas_force"): current_app.logger.info("login: forcing CAS") return redirect(url_for("cas.login")) return _login_form() @bp.route("/login_scodoc", methods=["GET", "POST"]) def login_scodoc(): """ScoDoc Login form. Formulaire login, sans redirection immédiate sur CAS si ce dernier est configuré. Sans CAS, ce formulaire est identique à /login """ if current_user.is_authenticated: return redirect(url_for("scodoc.index")) return _login_form() @bp.route("/logout") def logout() -> flask.Response: "Logout a scodoc user. If CAS session, logout from CAS. Redirect." return logic.logout() @bp.route("/create_user", methods=["GET", "POST"]) @admin_required def create_user(): "Form creating new user" form = UserCreationForm() if form.validate_on_submit(): user = User(user_name=form.user_name.data, email=form.email.data) user.set_password(form.password.data) db.session.add(user) db.session.commit() flash(f"Utilisateur {user.user_name} créé") return redirect(url_for("scodoc.index")) return render_template("auth/register.j2", title="Création utilisateur", form=form) @bp.route("/reset_password_request", methods=["GET", "POST"]) def reset_password_request(): """Form demande renvoi de mot de passe par mail Si l'utilisateur est déjà authentifié, le renvoie simplement sur la page d'accueil. """ if current_user.is_authenticated: return redirect(url_for("scodoc.index")) form = ResetPasswordRequestForm() if form.validate_on_submit(): users = User.query.filter( func.lower(User.email) == func.lower(form.email.data) ).all() if len(users) == 1: send_password_reset_email(users[0]) elif len(users) > 1: current_app.logger.info( f"reset_password_request: multiple users with email '{form.email.data}' (ignoring)" ) else: current_app.logger.info( f"reset_password_request: for unkown user '{form.email.data}'" ) flash( _("Voir les instructions envoyées par mail (pensez à regarder vos spams)") ) return redirect(url_for("auth.login")) return render_template( "auth/reset_password_request.j2", title=_("Reset Password"), form=form, is_cas_enabled=ScoDocSiteConfig.is_cas_enabled(), ) @bp.route("/reset_password/<token>", methods=["GET", "POST"]) def reset_password(token): "Reset password après demande par mail" if current_user.is_authenticated: return redirect(url_for("scodoc.index")) user: User = User.verify_reset_password_token(token) if user is None: return redirect(url_for("scodoc.index")) form = ResetPasswordForm() if form.validate_on_submit(): user.set_password(form.password.data) db.session.commit() flash(_("Votre mot de passe a été changé.")) return redirect(url_for("auth.login")) return render_template("auth/reset_password.j2", form=form, user=user) @bp.route("/reset_standard_roles_permissions", methods=["GET", "POST"]) @admin_required def reset_standard_roles_permissions(): "Réinitialise (recrée au besoin) les rôles standards de ScoDoc et leurs permissions" Role.reset_standard_roles_permissions() flash("rôles standards réinitialisés !") return redirect(url_for("scodoc.configuration")) @bp.route("/cas_users_generate_excel_sample") @admin_required def cas_users_generate_excel_sample(): "une feuille excel pour importation config CAS" data = cas.cas_users_generate_excel_sample() return scu.send_file(data, "ImportConfigCAS", scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE) @bp.route("/cas_users_import_config", methods=["GET", "POST"]) @admin_required def cas_users_import_config(): """Import utilisateurs depuis feuille Excel""" form = CASUsersImportConfigForm() if form.validate_on_submit(): if form.cancel.data: # cancel button return redirect(url_for("scodoc.configuration")) datafile = request.files[form.user_config_file.name] nb_modif = cas.cas_users_import_excel_file(datafile) current_app.logger.info(f"cas_users_import_config: {nb_modif} comptes modifiés") flash(f"Config. CAS de {nb_modif} comptes modifiée.") return redirect(url_for("scodoc.configuration")) return render_template( "auth/cas_users_import_config.j2", title=_("Importation configuration CAS utilisateurs"), form=form, ) return