2021-06-26 21:57:54 +02:00
# -*- mode: python -*-
# -*- coding: utf-8 -*-
##############################################################################
#
# ScoDoc
#
# Copyright (c) 1999 - 2021 Emmanuel Viennet. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Emmanuel Viennet emmanuel.viennet@viennet.net
#
##############################################################################
"""
Module users : interface gestion utilisateurs
ré - écriture pour Flask ScoDoc7 / ZScoUsers . py
Vues s ' appuyant sur auth et sco_users
Emmanuel Viennet , 2021
"""
2021-07-03 16:19:42 +02:00
import re
2021-06-28 10:45:00 +02:00
import jaxml
2021-06-26 21:57:54 +02:00
from flask import g
2021-06-27 12:11:39 +02:00
from flask import current_app , request
2021-06-28 10:45:00 +02:00
from flask_login import current_user
2021-06-26 21:57:54 +02:00
2021-07-01 18:54:07 +02:00
from app import db
2021-06-26 21:57:54 +02:00
from app . auth . models import Permission
from app . auth . models import User
2021-07-03 16:19:42 +02:00
from app . auth . models import Role
from app . auth . models import UserRole
2021-06-26 21:57:54 +02:00
from app . decorators import (
scodoc7func ,
ScoDoc7Context ,
permission_required ,
admin_required ,
login_required ,
2021-06-27 12:11:39 +02:00
ZRequest ,
2021-06-26 21:57:54 +02:00
)
2021-06-28 10:45:00 +02:00
from app . scodoc import html_sco_header
2021-06-26 21:57:54 +02:00
from app . scodoc import sco_users
2021-06-27 12:11:39 +02:00
from app . scodoc import sco_utils as scu
2021-06-28 10:45:00 +02:00
from app . scodoc . notes_log import log
2021-07-03 16:19:42 +02:00
from app . scodoc . sco_exceptions import AccessDenied , ScoValueError
2021-07-01 18:54:07 +02:00
from app . scodoc . sco_permissions_check import can_handle_passwd
2021-07-03 16:19:42 +02:00
from app . scodoc . TrivialFormulator import TrivialFormulator , tf_error_message
2021-06-26 21:57:54 +02:00
from app . views import users_bp as bp
2021-07-03 16:19:42 +02:00
from scodoc_manager import sco_mgr
2021-06-26 21:57:54 +02:00
context = ScoDoc7Context ( " users " ) # sco8
@bp.route ( " / " )
@bp.route ( " /index_html " )
@permission_required ( Permission . ScoUsersView )
@scodoc7func ( context )
2021-06-27 12:11:39 +02:00
def index_html ( context , REQUEST , all_depts = False , with_inactives = False , format = " html " ) :
2021-06-26 21:57:54 +02:00
return sco_users . index_html (
context ,
REQUEST = REQUEST ,
all_depts = all_depts ,
2021-06-27 12:11:39 +02:00
with_inactives = with_inactives ,
2021-06-26 21:57:54 +02:00
format = format ,
)
2021-06-27 12:11:39 +02:00
@bp.route ( " /user_info " )
@permission_required ( Permission . ScoUsersView )
@scodoc7func ( context )
def user_info ( user_name , format = " json " , REQUEST = None ) :
info = sco_users . user_info ( user_name = user_name )
return scu . sendResult ( REQUEST , info , name = " user " , format = format )
2021-06-28 10:45:00 +02:00
@bp.route ( " /create_user_form " , methods = [ " GET " , " POST " ] )
@permission_required ( Permission . ScoUsersAdmin )
@scodoc7func ( context )
def create_user_form ( context , REQUEST , user_name = None , edit = 0 ) :
" form. creation ou edit utilisateur "
auth_dept = current_user . dept
initvalues = { }
edit = int ( edit )
H = [ html_sco_header . sco_header ( context , REQUEST , bodyOnLoad = " init_tf_form( ' ' ) " ) ]
F = html_sco_header . sco_footer ( context , REQUEST )
if edit :
if not user_name :
raise ValueError ( " missing argument: user_name " )
2021-07-03 16:19:42 +02:00
u = User . query . filter_by ( user_name = user_name ) . first ( )
if not u :
raise ScoValueError ( " utilisateur inexistant " )
initvalues = u . to_dict ( )
2021-06-28 10:45:00 +02:00
H . append ( " <h2>Modification de l ' utilisateur %s </h2> " % user_name )
else :
H . append ( " <h2>Création d ' un utilisateur</h2> " )
is_super_admin = False
if current_user . has_permission ( Permission . ScoSuperAdmin , g . scodoc_dept ) :
H . append ( """ <p class= " warning " >Vous êtes super administrateur !</p> """ )
is_super_admin = True
2021-07-03 16:19:42 +02:00
# Les rôles standards créés à l'initialisation de ScoDoc:
standard_roles = [ Role . get_named_role ( r ) for r in ( u " Ens " , u " Secr " , u " Admin " ) ]
# Rôles pouvant etre attribués aux utilisateurs via ce dialogue:
# si SuperAdmin, tous les rôles standards dans tous les départements
# sinon, les départements dans lesquels l'utilisateur a le droit
2021-06-28 10:45:00 +02:00
if is_super_admin :
log ( " create_user_form called by %s (super admin) " % ( current_user . user_name , ) )
2021-07-03 16:19:42 +02:00
dept_ids = sco_mgr . get_dept_ids ( )
2021-06-28 10:45:00 +02:00
else :
2021-07-03 16:19:42 +02:00
# Si on n'est pas SuperAdmin, liste les départements dans lesquels on a la
# permission ScoUsersAdmin
dept_ids = sorted (
set (
[
x . dept
for x in UserRole . query . filter_by ( user = current_user )
if x . role . has_permission ( Permission . ScoUsersAdmin ) and x . dept
]
)
)
editable_roles_set = { ( r , dept ) for r in standard_roles for dept in dept_ids }
2021-06-28 10:45:00 +02:00
#
if not edit :
submitlabel = " Créer utilisateur "
orig_roles = set ( )
else :
submitlabel = " Modifier utilisateur "
2021-07-03 16:19:42 +02:00
if " roles_string " in initvalues :
initvalues [ " roles " ] = initvalues [ " roles_string " ] . split ( " , " )
else :
initvalues [ " roles " ] = [ ]
orig_roles = { # set des roles existants avant édition
UserRole . role_dept_from_string ( role_dept )
for role_dept in initvalues [ " roles " ]
}
if not initvalues [ " active " ] :
editable_roles_set = set ( ) # can't change roles of a disabled user
editable_roles_strings = { r . name + " _ " + dept for ( r , dept ) in editable_roles_set }
orig_roles_strings = { r . name + " _ " + dept for ( r , dept ) in orig_roles }
2021-06-28 10:45:00 +02:00
# add existing user roles
2021-07-03 16:19:42 +02:00
displayed_roles = list ( editable_roles_set . union ( orig_roles ) )
displayed_roles . sort ( key = lambda x : ( x [ 1 ] , x [ 0 ] . name ) )
displayed_roles_strings = [ r . name + " _ " + dept for ( r , dept ) in displayed_roles ]
displayed_roles_labels = [
" {dept} : {r.name} " . format ( dept = dept , r = r ) for ( r , dept ) in displayed_roles
]
2021-06-28 10:45:00 +02:00
disabled_roles = { } # pour desactiver les roles que l'on ne peut pas editer
2021-07-03 16:19:42 +02:00
for i in range ( len ( displayed_roles_strings ) ) :
if displayed_roles_strings [ i ] not in editable_roles_strings :
2021-06-28 10:45:00 +02:00
disabled_roles [ i ] = True
descr = [
( " edit " , { " input_type " : " hidden " , " default " : edit } ) ,
( " nom " , { " title " : " Nom " , " size " : 20 , " allow_null " : False } ) ,
( " prenom " , { " title " : " Prénom " , " size " : 20 , " allow_null " : False } ) ,
]
2021-07-03 16:19:42 +02:00
if current_user . user_name != user_name :
# no one can change its own status
2021-06-28 10:45:00 +02:00
descr . append (
(
" status " ,
{
" title " : " Statut " ,
" input_type " : " radio " ,
" labels " : ( " actif " , " ancien " ) ,
" allowed_values " : ( " " , " old " ) ,
} ,
)
)
if not edit :
descr + = [
(
" user_name " ,
{
" title " : " Pseudo (login) " ,
" size " : 20 ,
" allow_null " : False ,
" explanation " : " nom utilisé pour la connexion. Doit être unique parmi tous les utilisateurs. " ,
} ,
) ,
(
" passwd " ,
{
" title " : " Mot de passe " ,
" input_type " : " password " ,
" size " : 14 ,
" allow_null " : False ,
} ,
) ,
(
" passwd2 " ,
{
" title " : " Confirmer mot de passe " ,
" input_type " : " password " ,
" size " : 14 ,
" allow_null " : False ,
} ,
) ,
]
else :
descr + = [
(
" user_name " ,
{ " input_type " : " hidden " , " default " : initvalues [ " user_name " ] } ,
) ,
2021-07-03 16:19:42 +02:00
( " user_name " , { " input_type " : " hidden " , " default " : initvalues [ " user_name " ] } ) ,
2021-06-28 10:45:00 +02:00
]
descr + = [
(
" email " ,
{
" title " : " e-mail " ,
" input_type " : " text " ,
" explanation " : " vivement recommandé: utilisé pour contacter l ' utilisateur " ,
" size " : 20 ,
" allow_null " : True ,
} ,
)
]
if not auth_dept :
# si auth n'a pas de departement (admin global)
# propose de choisir le dept du nouvel utilisateur
# sinon, il sera créé dans le même département que auth
descr . append (
(
" dept " ,
{
" title " : " Département " ,
" input_type " : " text " ,
" size " : 12 ,
" allow_null " : True ,
" explanation " : """ département d \' appartenance de l \' utilisateur (s ' il s ' agit d ' un administrateur, laisser vide si vous voulez qu ' il puisse créer des utilisateurs dans d ' autres départements) """ ,
} ,
)
)
can_choose_dept = True
else :
can_choose_dept = False
if edit :
descr . append (
(
" d " ,
{
" input_type " : " separator " ,
" title " : " L ' utilisateur appartient au département %s "
% auth_dept ,
} ,
)
)
else :
descr . append (
(
" d " ,
{
" input_type " : " separator " ,
" title " : " L ' utilisateur sera crée dans le département %s "
% auth_dept ,
} ,
)
)
descr + = [
(
" date_expiration " ,
{
" title " : " Date d ' expiration " , # j/m/a
" input_type " : " date " ,
" explanation " : " j/m/a, laisser vide si pas de limite " ,
" size " : 9 ,
" allow_null " : True ,
} ,
) ,
(
" roles " ,
{
" title " : " Rôles " ,
" input_type " : " checkbox " ,
" vertical " : True ,
2021-07-03 16:19:42 +02:00
" labels " : displayed_roles_labels ,
" allowed_values " : displayed_roles_strings ,
2021-06-28 10:45:00 +02:00
" disabled_items " : disabled_roles ,
} ,
) ,
(
" force " ,
{
" title " : " Ignorer les avertissements " ,
" input_type " : " checkbox " ,
" explanation " : " passer outre les avertissements (homonymes, etc) " ,
" labels " : ( " " , ) ,
" allowed_values " : ( " 1 " , ) ,
} ,
) ,
]
if " tf-submitted " in REQUEST . form and not " roles " in REQUEST . form :
REQUEST . form [ " roles " ] = [ ]
if " tf-submitted " in REQUEST . form :
# Ajoute roles existants mais non modifiables (disabled dans le form)
REQUEST . form [ " roles " ] = list (
2021-07-03 16:19:42 +02:00
set ( REQUEST . form [ " roles " ] ) . union (
orig_roles_strings - editable_roles_strings
)
2021-06-28 10:45:00 +02:00
)
tf = TrivialFormulator (
REQUEST . URL0 ,
REQUEST . form ,
descr ,
initvalues = initvalues ,
submitlabel = submitlabel ,
cancelbutton = " Annuler " ,
)
if tf [ 0 ] == 0 :
return " \n " . join ( H ) + " \n " + tf [ 1 ] + F
elif tf [ 0 ] == - 1 :
2021-07-03 23:35:32 +02:00
return REQUEST . RESPONSE . redirect ( scu . UsersURL ( ) )
2021-06-28 10:45:00 +02:00
else :
vals = tf [ 2 ]
2021-07-03 16:19:42 +02:00
roles = set ( vals [ " roles " ] ) . intersection ( editable_roles_strings )
2021-06-28 10:45:00 +02:00
if REQUEST . form . has_key ( " edit " ) :
edit = int ( REQUEST . form [ " edit " ] )
else :
edit = 0
try :
force = int ( vals [ " force " ] [ 0 ] )
except :
force = 0
if edit :
user_name = initvalues [ " user_name " ]
else :
user_name = vals [ " user_name " ]
# ce login existe ?
err = None
2021-07-03 16:19:42 +02:00
users = sco_users . _user_list ( user_name )
2021-06-28 10:45:00 +02:00
if edit and not users : # safety net, le user_name ne devrait pas changer
err = " identifiant %s inexistant " % user_name
if not edit and users :
err = " identifiant %s déjà utilisé " % user_name
if err :
H . append ( tf_error_message ( """ Erreur: %s """ % err ) )
return " \n " . join ( H ) + " \n " + tf [ 1 ] + F
if not force :
2021-07-03 16:19:42 +02:00
ok , msg = sco_users . check_modif_user (
2021-06-28 10:45:00 +02:00
edit ,
user_name = user_name ,
nom = vals [ " nom " ] ,
prenom = vals [ " prenom " ] ,
email = vals [ " email " ] ,
roles = vals [ " roles " ] ,
)
if not ok :
H . append (
tf_error_message (
""" Attention: %s (vous pouvez forcer l ' opération en cochant " <em>Ignorer les avertissements</em> " en bas de page) """
% msg
)
)
return " \n " . join ( H ) + " \n " + tf [ 1 ] + F
2021-07-03 16:19:42 +02:00
if edit : # modif utilisateur (mais pas passwd ni user_name !)
2021-06-28 10:45:00 +02:00
if ( not can_choose_dept ) and vals . has_key ( " dept " ) :
del vals [ " dept " ]
if vals . has_key ( " passwd " ) :
del vals [ " passwd " ]
if vals . has_key ( " date_modif_passwd " ) :
del vals [ " date_modif_passwd " ]
if vals . has_key ( " user_name " ) :
del vals [ " user_name " ]
2021-07-03 16:19:42 +02:00
if ( current_user . user_name == user_name ) and vals . has_key ( " status " ) :
2021-06-28 10:45:00 +02:00
del vals [ " status " ] # no one can't change its own status
# traitement des roles: ne doit pas affecter les roles
# que l'on en controle pas:
2021-07-03 16:19:42 +02:00
for role in orig_roles_strings : # { "Ens_RT", "Secr_CJ", ... }
if role and not role in editable_roles_strings :
2021-06-28 10:45:00 +02:00
roles . add ( role )
2021-07-03 16:19:42 +02:00
vals [ " roles_string " ] = " , " . join ( roles )
2021-06-28 10:45:00 +02:00
# ok, edit
2021-07-03 16:19:42 +02:00
log ( " sco_users: editing %s by %s " % ( user_name , current_user . user_name ) )
log ( " sco_users: previous_values= %s " % initvalues )
log ( " sco_users: new_values= %s " % vals )
sco_users . user_edit ( user_name , vals )
2021-06-28 10:45:00 +02:00
return REQUEST . RESPONSE . redirect (
2021-07-03 16:19:42 +02:00
" user_info_page?user_name= %s &head_message=Utilisateur %s modifié "
2021-06-28 10:45:00 +02:00
% ( user_name , user_name )
)
else : # creation utilisateur
vals [ " roles " ] = " , " . join ( vals [ " roles " ] )
# check identifiant
if not re . match ( r " ^[a-zA-Z0-9@ \\ \ -_ \\ \ .]+$ " , vals [ " user_name " ] ) :
msg = tf_error_message (
" identifiant invalide (pas d ' accents ni de caractères spéciaux) "
)
return " \n " . join ( H ) + msg + " \n " + tf [ 1 ] + F
# check passwords
if vals [ " passwd " ] != vals [ " passwd2 " ] :
msg = tf_error_message (
""" Les deux mots de passes ne correspondent pas ! """
)
return " \n " . join ( H ) + msg + " \n " + tf [ 1 ] + F
if not sco_users . is_valid_password ( vals [ " passwd " ] ) :
msg = tf_error_message ( """ Mot de passe trop simple, recommencez ! """ )
return " \n " . join ( H ) + msg + " \n " + tf [ 1 ] + F
if not can_choose_dept :
vals [ " dept " ] = auth_dept
# ok, go
2021-07-03 16:19:42 +02:00
log (
" sco_users: new_user %s by %s "
% ( vals [ " user_name " ] , current_user . user_name )
)
u = User ( )
u . from_dict ( vals , new_user = True )
db . session . add ( u )
db . session . commit ( )
return REQUEST . RESPONSE . redirect (
" user_info_page?user_name= %s &head_message=Nouvel utilisateur créé "
% ( user_name )
)
2021-06-26 21:57:54 +02:00
@bp.route ( " /import_users_form " )
def import_users_form ( ) :
raise NotImplementedError ( )
2021-06-27 12:11:39 +02:00
@bp.route ( " /user_info_page " )
2021-06-28 10:45:00 +02:00
@permission_required ( Permission . ScoUsersView )
2021-06-27 12:11:39 +02:00
@scodoc7func ( context )
def user_info_page ( user_name , REQUEST = None ) :
return sco_users . user_info_page ( context , user_name = user_name , REQUEST = REQUEST )
2021-06-28 10:45:00 +02:00
@bp.route ( " /get_user_list_xml " )
@permission_required ( Permission . ScoView )
@scodoc7func ( context )
def get_user_list_xml ( context , dept = None , start = " " , limit = 25 , REQUEST = None ) :
""" Returns XML list of users with name (nomplogin) starting with start.
Used for forms auto - completion . """
userlist = sco_users . get_user_list ( dept = dept )
start = scu . suppression_diacritics ( unicode ( start , " utf-8 " ) )
start = scu . strlower ( str ( start ) )
# TODO : à refaire avec une requete SQL #py3
# (et en json)
userlist = [
user
for user in userlist
if scu . suppress_accents ( scu . strlower ( user . nom ) ) . startswith ( start )
]
if REQUEST :
REQUEST . RESPONSE . setHeader ( " content-type " , scu . XML_MIMETYPE )
doc = jaxml . XML_document ( encoding = scu . SCO_ENCODING )
doc . results ( )
for user in userlist [ : limit ] :
doc . _push ( )
doc . rs ( user [ " nomplogin " ] , id = user [ " user_id " ] , info = " " )
doc . _pop ( )
return repr ( doc )
2021-07-01 18:54:07 +02:00
@bp.route ( " /form_change_password " )
@permission_required ( Permission . ScoView )
@scodoc7func ( context )
def form_change_password ( REQUEST , user_name = None ) :
""" Formulaire de changement mot de passe de l ' utilisateur user_name.
Un utilisateur peut toujours changer son propre mot de passe .
"""
if not user_name :
u = current_user
else :
u = User . query . filter_by ( user_name = user_name ) . first ( )
H = [ html_sco_header . sco_header ( context , REQUEST , user_check = False ) ]
F = html_sco_header . sco_footer ( context , REQUEST )
# check access
if not can_handle_passwd ( u ) :
return (
" \n " . join ( H )
+ " <p>Vous n ' avez pas la permission de changer ce mot de passe</p> "
+ F
)
#
H . append (
""" <h2>Changement du mot de passe de <font color= " red " > %(nomplogin)s </font></h2>
< p >
< form action = " change_password " method = " post " > < table >
< tr > < td > Nouveau mot de passe : < / td > < td > < input type = " password " size = " 14 " name = " password " / > < / td > < / tr >
< tr > < td > Confirmation : < / td > < td > < input type = " password " size = " 14 " name = " password2 " / > < / td > < / tr >
< / table >
< input type = " hidden " value = " %(user_name)s " name = " user_name " >
< input type = " submit " value = " Changer " >
< / p >
< p > Vous pouvez aussi : < a class = " stdlink " href = " reset_password_form?user_name= %(user_name)s " > renvoyer un mot de passe aléatoire temporaire par mail à l ' utilisateur</a>
"""
% { " nomplogin " : u . get_nomplogin ( ) , " user_name " : user_name }
)
return " \n " . join ( H ) + F
@bp.route ( " /change_password " , methods = [ " POST " ] )
@permission_required ( Permission . ScoView )
@scodoc7func ( context )
def change_password ( user_name , password , password2 , REQUEST ) :
" Change the password for user given by user_name "
u = User . query . filter_by ( user_name = user_name ) . first ( )
# Check access permission
if not can_handle_passwd ( u ) :
# access denied
log (
" change_password: access denied (authuser= %s , user_name= %s , ip= %s ) "
% ( REQUEST . AUTHENTICATED_USER , user_name , REQUEST . REMOTE_ADDR )
)
raise AccessDenied ( " vous n ' avez pas la permission de changer ce mot de passe " )
H = [ ]
F = html_sco_header . sco_footer ( context , REQUEST )
# check password
if password != password2 :
H . append (
""" <p>Les deux mots de passes saisis sont différents !</p>
< p > < a href = " form_change_password?user_name= %s " class = " stdlink " > Recommencer < / a > < / p > """
% user_name
)
else :
if not sco_users . is_valid_password ( password ) :
H . append (
""" <p><b>ce mot de passe n \' est pas assez compliqué !</b><br/>(oui, il faut un mot de passe vraiment compliqué !)</p>
< p > < a href = " form_change_password?user_name= %s " class = " stdlink " > Recommencer < / a > < / p >
"""
% user_name
)
else :
# ok, strong password
db . session . add ( u )
u . set_password ( password )
db . session . commit ( )
#
# ici page simplifiee car on peut ne plus avoir
# le droit d'acceder aux feuilles de style
H . append (
" <h2>Changement effectué !</h2><p>Ne notez pas ce mot de passe, mais mémorisez le !</p><p>Rappel: il est <b>interdit</b> de communiquer son mot de passe à un tiers, même si c ' est un collègue de confiance !</p><p><b>Si vous n ' êtes pas administrateur, le système va vous redemander votre login et nouveau mot de passe au prochain accès.</b></p> "
)
return (
""" <?xml version= " 1.0 " encoding= " %s " ?>
< ! DOCTYPE html PUBLIC " -//W3C//DTD XHTML 1.0 Transitional//EN " " http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd " >
< html >
< head >
< title > Mot de passe changé < / title >
< meta http - equiv = " Content-Type " content = " text/html; charset= %s " / >
< body > < h1 > Mot de passe changé ! < / h1 >
"""
% ( scu . SCO_ENCODING , scu . SCO_ENCODING )
+ " \n " . join ( H )
+ ' <a href= " %s " class= " stdlink " >Continuer</a></body></html> '
% scu . ScoURL ( )
)
return html_sco_header . sco_header ( context , REQUEST ) + " \n " . join ( H ) + F
2021-07-02 14:12:33 +02:00
@bp.route ( " /delete_user_form " , methods = [ " GET " , " POST " ] )
@permission_required ( Permission . ScoUsersAdmin )
@scodoc7func ( context )
def delete_user_form ( REQUEST , user_name , dialog_confirmed = False ) :
" delete user "
u = User . query . filter_by ( user_name = user_name ) . first ( )
# Check access permission
if not can_handle_passwd ( u ) :
# access denied (or non existent user)
return (
html_sco_header . sco_header ( context , REQUEST , user_check = False )
+ " <p>Vous n ' avez pas la permission de supprimer cet utilisateur</p> "
+ html_sco_header . sco_footer ( context , REQUEST )
)
if not dialog_confirmed :
return scu . confirm_dialog (
context ,
""" <h2>Confirmer la suppression de l \' utilisateur %s ?</h2>
< p class = " warning " > En général , il est < b > déconseillé de supprimer un utilisateur < / b > , son
identité étant référencé dans les modules de formation . N ' utilisez
cette fonction qu ' en cas d ' erreur ( création de doublons , etc ) .
< / p >
"""
% user_name ,
dest_url = " " ,
REQUEST = REQUEST ,
cancel_url = scu . UsersURL ( ) ,
parameters = { " user_name " : user_name } ,
)
db . session . delete ( u )
db . session . commit ( )
return REQUEST . RESPONSE . redirect (
scu . UsersURL ( ) + r " ?head_message=Utilisateur %20s upprimé "
)