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-27 16:55:50 +02:00
import datetime
2021-07-03 16:19:42 +02:00
import re
2021-07-11 13:03:13 +02:00
from xml . etree import ElementTree
2021-06-28 10:45:00 +02:00
2021-08-01 10:16:16 +02:00
import flask
2021-10-16 23:22:03 +02:00
from flask import g , url_for , request , current_app , flash
2021-08-28 16:01:41 +02:00
from flask import redirect , render_template
2021-08-22 13:24:36 +02:00
2021-06-28 10:45:00 +02:00
from flask_login import current_user
2021-10-16 23:22:03 +02:00
from wtforms import HiddenField , PasswordField , StringField , SubmitField
from wtforms . validators import DataRequired , Email , ValidationError , EqualTo
2021-06-26 21:57:54 +02:00
2021-07-01 18:54:07 +02:00
from app import db
2021-10-16 23:22:03 +02:00
from app . api . auth import verify_password
2021-08-28 16:01:41 +02:00
from app . auth . forms import DeactivateUserForm
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-10-15 19:17:40 +02:00
from app . auth . models import is_valid_password
2021-10-15 15:12:46 +02:00
from app . email import send_email
2021-08-13 09:31:49 +02:00
from app . models import Departement
2021-06-26 21:57:54 +02:00
from app . decorators import (
2021-08-13 00:34:58 +02:00
scodoc ,
2021-06-26 21:57:54 +02:00
scodoc7func ,
permission_required ,
)
2021-08-22 13:24:36 +02:00
from app . scodoc import html_sco_header , sco_import_users , sco_excel
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-07-11 13:03:13 +02:00
from app . scodoc import sco_xml
2021-08-29 19:57:32 +02:00
from app import log
2021-07-03 16:19:42 +02:00
from app . scodoc . sco_exceptions import AccessDenied , ScoValueError
2021-10-15 15:12:46 +02:00
from app . scodoc . sco_import_users import generate_password
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-10-16 23:22:03 +02:00
from flask_wtf import FlaskForm
_ = lambda x : x # sans babel
_l = _
class ChangePasswordForm ( FlaskForm ) :
user_name = HiddenField ( )
old_password = PasswordField ( _l ( " Ancien mot de passe " ) )
new_password = PasswordField ( _l ( " Nouveau mot de passe " ) )
bis_password = PasswordField (
_l ( " Répéter " ) ,
validators = [
EqualTo (
" new_password " ,
message = " Les deux saisies sont " " différentes, recommencez " ,
) ,
] ,
)
email = StringField ( _l ( " Email " ) , validators = [ DataRequired ( ) , Email ( ) ] )
submit = SubmitField ( _l ( " Modifier " ) )
def validate_email ( self , email ) :
user = User . query . filter_by ( email = email . data ) . first ( )
if user is not None and self . user_name . data != user . user_name :
raise ValidationError ( _ ( " Please choose a different email address. " ) )
def validate_new_password ( self , new_password ) :
if new_password . data != " " and not is_valid_password ( new_password . data ) :
raise ValidationError ( f " Mot de passe trop simple, recommencez " )
def validate_old_password ( self , old_password ) :
if not verify_password ( self . user_name . data , old_password . data ) :
raise ValidationError ( " Ancien mot de passe incorrect, recommenccez " )
2021-06-26 21:57:54 +02:00
@bp.route ( " / " )
@bp.route ( " /index_html " )
2021-08-13 00:34:58 +02:00
@scodoc
2021-06-26 21:57:54 +02:00
@permission_required ( Permission . ScoUsersView )
2021-08-21 00:24:51 +02:00
@scodoc7func
2021-09-27 10:20:10 +02:00
def index_html ( all_depts = False , with_inactives = False , format = " html " ) :
2021-06-26 21:57:54 +02:00
return sco_users . index_html (
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 " )
2021-08-13 00:34:58 +02:00
@scodoc
2021-06-27 12:11:39 +02:00
@permission_required ( Permission . ScoUsersView )
2021-08-21 00:24:51 +02:00
@scodoc7func
2021-09-27 10:20:10 +02:00
def user_info ( user_name , format = " json " ) :
2021-08-22 13:24:36 +02:00
info = sco_users . user_info ( user_name )
2021-09-21 15:53:33 +02:00
return scu . sendResult ( info , name = " user " , format = format )
2021-06-27 12:11:39 +02:00
2021-06-28 10:45:00 +02:00
@bp.route ( " /create_user_form " , methods = [ " GET " , " POST " ] )
2021-08-13 00:34:58 +02:00
@scodoc
2021-06-28 10:45:00 +02:00
@permission_required ( Permission . ScoUsersAdmin )
2021-08-21 00:24:51 +02:00
@scodoc7func
2021-09-27 10:20:10 +02:00
def create_user_form ( user_name = None , edit = 0 , all_roles = 1 ) :
2021-09-13 23:06:42 +02:00
" form. création ou edition utilisateur "
2021-06-28 10:45:00 +02:00
auth_dept = current_user . dept
initvalues = { }
edit = int ( edit )
2021-09-13 23:06:42 +02:00
all_roles = int ( all_roles )
2021-10-15 15:34:10 +02:00
H = [
html_sco_header . sco_header (
bodyOnLoad = " init_tf_form( ' ' ) " ,
javascripts = [ " js/user_form.js " ] ,
)
]
2021-07-29 10:19:00 +02:00
F = html_sco_header . sco_footer ( )
2021-06-28 10:45:00 +02:00
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-09-13 23:06:42 +02:00
if all_roles :
# tous sauf SuperAdmin
standard_roles = [
r
for r in Role . query . all ( )
if r . permissions != Permission . ALL_PERMISSIONS [ 0 ]
]
else :
# Les rôles standards créés à l'initialisation de ScoDoc:
standard_roles = [
Role . get_named_role ( r ) for r in ( " Ens " , " Secr " , " Admin " , " RespPe " )
]
# Départements auxquels ont peut associer des rôles via ce dialogue:
2021-07-03 16:19:42 +02:00
# 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-08-13 09:31:49 +02:00
dept_ids = [ d . acronym for d in Departement . query . all ( ) ]
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 " ] = [ ]
2021-07-27 16:55:50 +02:00
if " date_expiration " in initvalues :
2021-08-09 14:29:03 +02:00
initvalues [ " date_expiration " ] = (
u . date_expiration . strftime ( " %d / % m/ % Y " ) if u . date_expiration else " "
)
2021-07-27 16:55:50 +02:00
initvalues [ " status " ] = " " if u . active else " old "
2021-07-03 16:19:42 +02:00
orig_roles = { # set des roles existants avant édition
UserRole . role_dept_from_string ( role_dept )
for role_dept in initvalues [ " roles " ]
2021-09-29 10:27:49 +02:00
if role_dept
2021-07-03 16:19:42 +02:00
}
if not initvalues [ " active " ] :
editable_roles_set = set ( ) # can't change roles of a disabled user
2021-09-28 16:20:15 +02:00
editable_roles_strings = {
r . name + " _ " + ( dept or " " ) for ( r , dept ) in editable_roles_set
}
orig_roles_strings = { r . name + " _ " + ( dept or " " ) 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 ) )
2021-10-01 23:48:11 +02:00
displayed_roles . sort ( key = lambda x : ( x [ 1 ] or " " , x [ 0 ] . name or " " ) )
2021-09-28 16:20:15 +02:00
displayed_roles_strings = [
r . name + " _ " + ( dept or " " ) for ( r , dept ) in displayed_roles
2021-07-03 16:19:42 +02:00
]
2021-09-28 16:20:15 +02:00
displayed_roles_labels = [ f " { dept } : { r . name } " 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 ,
2021-10-15 15:12:46 +02:00
" explanation " : " nom utilisé pour la connexion. Doit être unique parmi tous les utilisateurs. "
2021-10-15 15:34:10 +02:00
" Lettres ou chiffres uniquement. " ,
2021-10-15 15:12:46 +02:00
} ,
) ,
( " formsemestre_id " , { " input_type " : " hidden " } ) ,
2021-06-28 10:45:00 +02:00
(
2021-09-11 22:46:37 +02:00
" password " ,
2021-06-28 10:45:00 +02:00
{
" title " : " Mot de passe " ,
" input_type " : " password " ,
" size " : 14 ,
2021-08-28 16:01:41 +02:00
" allow_null " : True ,
" explanation " : " optionnel, l ' utilisateur pourra le saisir avec son mail " ,
2021-06-28 10:45:00 +02:00
} ,
) ,
(
2021-09-11 22:46:37 +02:00
" password2 " ,
2021-06-28 10:45:00 +02:00
{
" title " : " Confirmer mot de passe " ,
" input_type " : " password " ,
" size " : 14 ,
2021-08-28 16:01:41 +02:00
" allow_null " : True ,
2021-06-28 10:45:00 +02:00
} ,
) ,
]
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 " ,
2021-08-28 16:01:41 +02:00
" explanation " : " requis, doit fonctionner " ,
2021-06-28 10:45:00 +02:00
" size " : 20 ,
2021-08-28 16:01:41 +02:00
" allow_null " : False ,
2021-06-28 10:45:00 +02:00
} ,
)
]
2021-10-15 19:17:40 +02:00
if not edit : # options création utilisateur
descr + = [
(
" welcome " ,
{
" title " : " Message d ' accueil " ,
" input_type " : " checkbox " ,
" explanation " : " Envoie un mail d ' accueil à l ' utilisateur. " ,
" labels " : ( " " , ) ,
" allowed_values " : ( " 1 " , ) ,
" default " : " 1 " ,
} ,
) ,
(
" reset_password " ,
{
" title " : " " ,
" input_type " : " checkbox " ,
" explanation " : " indiquer par mail de changer le mot de passe initial " ,
" labels " : ( " " , ) ,
" allowed_values " : ( " 1 " , ) ,
" default " : " 1 " ,
# "attributes": ["style='margin-left:20pt'"],
} ,
) ,
]
2021-06-28 10:45:00 +02:00
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 "
2021-10-10 21:09:27 +02:00
% auth_dept ,
2021-06-28 10:45:00 +02:00
} ,
)
)
else :
descr . append (
(
" d " ,
{
" input_type " : " separator " ,
" title " : " L ' utilisateur sera crée dans le département %s "
2021-10-10 21:09:27 +02:00
% auth_dept ,
2021-06-28 10:45:00 +02:00
} ,
)
)
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 " , ) ,
} ,
) ,
]
2021-09-28 09:14:04 +02:00
vals = scu . get_request_args ( )
2021-09-27 10:20:10 +02:00
if " tf_submitted " in vals and not " roles " in vals :
vals [ " roles " ] = [ ]
if " tf_submitted " in vals :
2021-06-28 10:45:00 +02:00
# Ajoute roles existants mais non modifiables (disabled dans le form)
2021-09-27 10:20:10 +02:00
vals [ " roles " ] = list (
set ( vals [ " roles " ] ) . union ( orig_roles_strings - editable_roles_strings )
2021-06-28 10:45:00 +02:00
)
tf = TrivialFormulator (
2021-09-18 10:10:02 +02:00
request . base_url ,
2021-09-27 10:20:10 +02:00
vals ,
2021-06-28 10:45:00 +02:00
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-31 18:01:10 +02:00
return flask . 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-09-27 10:20:10 +02:00
if " edit " in vals :
edit = int ( vals [ " edit " ] )
2021-06-28 10:45:00 +02:00
else :
edit = 0
try :
force = int ( vals [ " force " ] [ 0 ] )
2021-07-27 16:55:50 +02:00
except ( IndexError , ValueError , TypeError ) :
2021-06-28 10:45:00 +02:00
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
2021-10-10 10:52:06 +02:00
ok , msg = sco_users . check_modif_user (
edit ,
2021-10-13 16:32:43 +02:00
enforce_optionals = not force ,
2021-10-10 10:52:06 +02:00
user_name = user_name ,
nom = vals [ " nom " ] ,
prenom = vals [ " prenom " ] ,
email = vals [ " email " ] ,
2021-10-15 15:34:10 +02:00
dept = vals . get ( " dept " , auth_dept ) ,
2021-10-10 10:52:06 +02:00
roles = vals [ " roles " ] ,
)
if not ok :
2021-10-10 21:09:27 +02:00
H . append ( tf_error_message ( msg ) )
2021-10-10 10:52:06 +02:00
return " \n " . join ( H ) + " \n " + tf [ 1 ] + F
2021-06-28 10:45:00 +02:00
2021-10-07 23:00:02 +02:00
if " date_expiration " in vals :
try :
if vals [ " date_expiration " ] :
vals [ " date_expiration " ] = datetime . datetime . strptime (
vals [ " date_expiration " ] , " %d / % m/ % Y "
)
2021-10-16 07:10:55 +02:00
if vals [ " date_expiration " ] < datetime . datetime . now ( ) :
H . append ( tf_error_message ( " date expiration passée " ) )
return " \n " . join ( H ) + " \n " + tf [ 1 ] + F
2021-10-07 23:00:02 +02:00
else :
vals [ " date_expiration " ] = None
except ValueError :
H . append ( tf_error_message ( " date expiration invalide " ) )
return " \n " . join ( H ) + " \n " + tf [ 1 ] + F
2021-09-11 22:46:37 +02:00
if edit : # modif utilisateur (mais pas password ni user_name !)
2021-07-09 13:45:10 +02:00
if ( not can_choose_dept ) and " dept " in vals :
2021-06-28 10:45:00 +02:00
del vals [ " dept " ]
2021-09-11 22:46:37 +02:00
if " password " in vals :
del vals [ " passwordd " ]
2021-07-09 13:45:10 +02:00
if " date_modif_passwd " in vals :
2021-06-28 10:45:00 +02:00
del vals [ " date_modif_passwd " ]
2021-07-09 13:45:10 +02:00
if " user_name " in vals :
2021-06-28 10:45:00 +02:00
del vals [ " user_name " ]
2021-07-09 13:45:10 +02:00
if ( current_user . user_name == user_name ) and " status " in vals :
2021-06-28 10:45:00 +02:00
del vals [ " status " ] # no one can't change its own status
2021-07-27 16:55:50 +02:00
if " status " in vals :
vals [ " active " ] = vals [ " status " ] == " "
2021-06-28 10:45:00 +02:00
# 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-07-31 18:01:10 +02:00
return flask . 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
2021-08-22 00:17:47 +02:00
vals [ " roles_string " ] = " , " . join ( vals [ " roles " ] )
2021-06-28 10:45:00 +02:00
# 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
2021-10-15 15:12:46 +02:00
# Traitement initial (mode) : 3 cas
# A: envoi de welcome + procedure de reset
# B: envoi de welcome seulement (mot de passe saisie dans le formulaire)
2021-10-15 19:17:40 +02:00
# C: Aucun envoi (mot de passe saisi dans le formulaire)
2021-10-15 15:34:10 +02:00
if vals [ " welcome:list " ] == " 1 " :
if vals [ " reset_password:list " ] == " 1 " :
mode = " A "
2021-10-15 15:12:46 +02:00
else :
2021-10-15 15:34:10 +02:00
mode = " B "
2021-10-15 15:12:46 +02:00
else :
2021-10-15 15:34:10 +02:00
mode = " C "
2021-10-15 15:12:46 +02:00
2021-06-28 10:45:00 +02:00
# check passwords
2021-10-15 15:34:10 +02:00
if mode == " A " :
vals [ " password " ] = generate_password ( )
2021-10-15 15:12:46 +02:00
else :
if vals [ " password " ] :
if vals [ " password " ] != vals [ " password2 " ] :
msg = tf_error_message (
""" Les deux mots de passes ne correspondent pas ! """
)
return " \n " . join ( H ) + msg + " \n " + tf [ 1 ] + F
2021-10-15 19:17:40 +02:00
if not is_valid_password ( vals [ " password " ] ) :
2021-10-15 15:12:46 +02:00
msg = tf_error_message (
""" Mot de passe trop simple, recommencez ! """
)
return " \n " . join ( H ) + msg + " \n " + tf [ 1 ] + F
2021-06-28 10:45:00 +02:00
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 ( )
2021-10-15 15:12:46 +02:00
# envoi éventuel d'un message
2021-10-15 15:34:10 +02:00
if mode == " A " or mode == " B " :
if mode == " A " :
2021-10-15 15:12:46 +02:00
token = u . get_reset_password_token ( )
else :
token = None
send_email (
2021-10-15 19:17:40 +02:00
" [ScoDoc] Création de votre compte " ,
2021-10-15 15:12:46 +02:00
sender = current_app . config [ " ADMINS " ] [ 0 ] ,
recipients = [ u . email ] ,
text_body = render_template ( " email/welcome.txt " , user = u , token = token ) ,
2021-10-15 15:34:10 +02:00
html_body = render_template (
" email/welcome.html " , user = u , token = token
) ,
2021-10-15 15:12:46 +02:00
)
2021-07-31 18:01:10 +02:00
return flask . redirect (
2021-08-28 16:01:41 +02:00
url_for (
" users.user_info_page " ,
scodoc_dept = g . scodoc_dept ,
user_name = user_name ,
head_message = " Nouvel utilisateur créé " ,
)
2021-07-03 16:19:42 +02:00
)
2021-06-26 21:57:54 +02:00
2021-08-22 13:24:36 +02:00
@bp.route ( " /import_users_generate_excel_sample " )
@scodoc
@permission_required ( Permission . ScoUsersAdmin )
@scodoc7func
2021-09-27 10:20:10 +02:00
def import_users_generate_excel_sample ( ) :
2021-08-22 13:24:36 +02:00
" une feuille excel pour importation utilisateurs "
data = sco_import_users . generate_excel_sample ( )
2021-09-21 22:19:08 +02:00
return scu . send_file ( data , " ImportUtilisateurs " , scu . XLSX_SUFFIX , scu . XLSX_MIMETYPE )
2021-08-22 13:24:36 +02:00
@bp.route ( " /import_users_form " , methods = [ " GET " , " POST " ] )
@scodoc
@permission_required ( Permission . ScoUsersAdmin )
@scodoc7func
2021-09-27 10:20:10 +02:00
def import_users_form ( ) :
2021-08-22 13:24:36 +02:00
""" Import utilisateurs depuis feuille Excel """
head = html_sco_header . sco_header ( page_title = " Import utilisateurs " )
H = [
head ,
""" <h2>Téléchargement d ' une nouvelle liste d ' utilisateurs</h2>
< p style = " color: red " > A utiliser pour importer de < b > nouveaux < / b > utilisateurs ( enseignants ou secrétaires )
< / p >
< p >
L ' opération se déroule en deux étapes. Dans un premier temps,
vous téléchargez une feuille Excel type . Vous devez remplir
cette feuille , une ligne décrivant chaque utilisateur . Ensuite ,
vous indiquez le nom de votre fichier dans la case " Fichier Excel "
ci - dessous , et cliquez sur " Télécharger " pour envoyer au serveur
votre liste .
< / p >
""" ,
]
help = """ <p class= " help " >
Lors de la creation des utilisateurs , les opérations suivantes sont effectuées :
< / p >
< ol class = " help " >
< li > vérification des données ; < / li >
< li > génération d ' un mot de passe alétoire pour chaque utilisateur;</li>
< li > création de chaque utilisateur ; < / li >
< li > envoi à chaque utilisateur de son < b > mot de passe initial par mail < / b > . < / li >
< / ol > """
H . append (
""" <ol><li><a class= " stdlink " href= " import_users_generate_excel_sample " >
Obtenir la feuille excel à remplir < / a > < / li > < li > """
)
F = html_sco_header . sco_footer ( )
tf = TrivialFormulator (
2021-09-18 10:10:02 +02:00
request . base_url ,
2021-09-27 16:42:14 +02:00
scu . get_request_args ( ) ,
2021-08-22 13:24:36 +02:00
(
(
" xlsfile " ,
{ " title " : " Fichier Excel: " , " input_type " : " file " , " size " : 40 } ,
) ,
2021-10-13 16:32:43 +02:00
(
" force " ,
{
" title " : " Ignorer les avertissements " ,
" input_type " : " checkbox " ,
" explanation " : " passer outre les avertissements (homonymes, etc) " ,
" labels " : ( " " , ) ,
" allowed_values " : ( " 1 " , ) ,
} ,
) ,
2021-08-22 13:24:36 +02:00
( " formsemestre_id " , { " input_type " : " hidden " } ) ,
) ,
submitlabel = " Télécharger " ,
)
if tf [ 0 ] == 0 :
return " \n " . join ( H ) + tf [ 1 ] + " </li></ol> " + help + F
elif tf [ 0 ] == - 1 :
2021-09-18 13:42:19 +02:00
return flask . redirect ( url_for ( " scolar.index_html " , docodc_dept = g . scodoc_dept ) )
2021-08-22 13:24:36 +02:00
else :
# IMPORT
2021-10-13 16:32:43 +02:00
ok , diag , nb_created = sco_import_users . import_excel_file (
tf [ 2 ] [ " xlsfile " ] , tf [ 2 ] [ " force " ]
)
2021-08-22 19:17:43 +02:00
H = [ html_sco_header . sco_header ( page_title = " Import utilisateurs " ) ]
H . append ( " <ul> " )
for d in diag :
H . append ( " <li> %s </li> " % d )
H . append ( " </ul> " )
2021-08-22 13:24:36 +02:00
if ok :
2021-10-13 15:27:19 +02:00
dest = url_for ( " users.index_html " , scodoc_dept = g . scodoc_dept , all_depts = 1 )
2021-08-22 19:17:43 +02:00
H . append ( " <p>Ok, Import terminé ( %s utilisateurs créés)!</p> " % nb_created )
2021-08-22 13:24:36 +02:00
H . append ( ' <p><a class= " stdlink " href= " %s " >Continuer</a></p> ' % dest )
else :
dest = url_for ( " users.import_users_form " , scodoc_dept = g . scodoc_dept )
H . append ( " <p>Erreur, importation annulée !</p> " )
H . append ( ' <p><a class= " stdlink " href= " %s " >Continuer</a></p> ' % dest )
return " \n " . join ( H ) + html_sco_header . sco_footer ( )
2021-06-26 21:57:54 +02:00
2021-06-27 12:11:39 +02:00
@bp.route ( " /user_info_page " )
2021-08-13 00:34:58 +02:00
@scodoc
2021-06-28 10:45:00 +02:00
@permission_required ( Permission . ScoUsersView )
2021-08-21 00:24:51 +02:00
@scodoc7func
2021-08-22 00:17:47 +02:00
def user_info_page ( user_name ) :
return sco_users . user_info_page ( user_name = user_name )
2021-06-28 10:45:00 +02:00
@bp.route ( " /get_user_list_xml " )
2021-08-13 00:34:58 +02:00
@scodoc
2021-06-28 10:45:00 +02:00
@permission_required ( Permission . ScoView )
2021-08-21 00:24:51 +02:00
@scodoc7func
2021-09-27 10:20:10 +02:00
def get_user_list_xml ( dept = None , start = " " , limit = 25 ) :
2021-06-28 10:45:00 +02:00
""" Returns XML list of users with name (nomplogin) starting with start.
2021-09-03 18:17:43 +02:00
Used for forms auto - completion .
"""
# suggère seulement seulement les utilisateurs actifs:
2021-06-28 10:45:00 +02:00
userlist = sco_users . get_user_list ( dept = dept )
2021-07-12 11:54:04 +02:00
start = scu . suppress_accents ( start ) . lower ( )
2021-06-28 10:45:00 +02:00
# TODO : à refaire avec une requete SQL #py3
# (et en json)
userlist = [
user
for user in userlist
2021-08-21 00:24:51 +02:00
if scu . suppress_accents ( ( user . nom or " " ) . lower ( ) ) . startswith ( start )
2021-06-28 10:45:00 +02:00
]
2021-07-11 13:03:13 +02:00
doc = ElementTree . Element ( " results " )
2021-06-28 10:45:00 +02:00
for user in userlist [ : limit ] :
2021-07-12 23:34:18 +02:00
x_rs = ElementTree . Element ( " rs " , id = str ( user . id ) , info = " " )
2021-07-11 13:03:13 +02:00
x_rs . text = user . get_nomplogin ( )
doc . append ( x_rs )
2021-09-27 10:20:10 +02:00
data = sco_xml . XML_HEADER + ElementTree . tostring ( doc ) . decode ( scu . SCO_ENCODING )
return scu . send_file ( data , mime = scu . XML_MIMETYPE )
2021-07-01 18:54:07 +02:00
2021-10-16 23:22:03 +02:00
@bp.route ( " /form_change_password " , methods = [ " GET " , " POST " ] )
2021-08-13 00:34:58 +02:00
@scodoc
2021-07-01 18:54:07 +02:00
@permission_required ( Permission . ScoView )
2021-08-21 00:24:51 +02:00
@scodoc7func
2021-09-27 10:20:10 +02:00
def form_change_password ( user_name = None ) :
2021-07-01 18:54:07 +02:00
""" 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 :
2021-10-16 23:22:03 +02:00
user = current_user
2021-07-01 18:54:07 +02:00
else :
2021-10-16 23:22:03 +02:00
user = User . query . filter_by ( user_name = user_name ) . first ( )
2021-07-01 18:54:07 +02:00
# check access
2021-10-16 23:22:03 +02:00
if not can_handle_passwd ( user ) :
return " \n " . join (
[
html_sco_header . sco_header ( user_check = False ) ,
" <p>Vous n ' avez pas la permission de changer ce mot de passe</p> " ,
html_sco_header . sco_footer ( ) ,
]
2021-07-01 18:54:07 +02:00
)
2021-10-16 23:22:03 +02:00
form = ChangePasswordForm ( user_name = user . user_name , email = user . email )
if form . validate_on_submit ( ) :
messages = [ ]
if form . new_password . data != " " : # change password
user . set_password ( form . new_password . data )
messages . append ( " Mot de passe modifié " )
if form . email . data != user . email : # change email
user . email = form . email . data
messages . append ( " Adresse email modifiée " )
db . session . commit ( )
flash ( " \n " . join ( messages ) )
return render_template ( " auth/change_password.html " , form = form )
2021-07-01 18:54:07 +02:00
@bp.route ( " /change_password " , methods = [ " POST " ] )
2021-08-13 00:34:58 +02:00
@scodoc
2021-07-01 18:54:07 +02:00
@permission_required ( Permission . ScoView )
2021-08-21 00:24:51 +02:00
@scodoc7func
2021-09-27 10:20:10 +02:00
def change_password ( user_name , password , password2 ) :
2021-07-01 18:54:07 +02:00
" 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 (
2021-09-18 13:42:19 +02:00
" change_password: access denied (authuser= %s , user_name= %s ) "
% ( current_user , user_name )
2021-07-01 18:54:07 +02:00
)
raise AccessDenied ( " vous n ' avez pas la permission de changer ce mot de passe " )
H = [ ]
2021-07-29 10:19:00 +02:00
F = html_sco_header . sco_footer ( )
2021-07-01 18:54:07 +02:00
# 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 :
2021-10-15 19:17:40 +02:00
if not is_valid_password ( password ) :
2021-07-01 18:54:07 +02:00
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 (
2021-10-10 21:09:27 +02:00
""" <?xml version= " 1.0 " encoding= " %s " ?>
2021-07-01 18:54:07 +02:00
< ! 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 >
"""
2021-10-10 21:09:27 +02:00
% ( scu . SCO_ENCODING , scu . SCO_ENCODING )
+ " \n " . join ( H )
+ ' <a href= " %s " class= " stdlink " >Continuer</a></body></html> '
% scu . ScoURL ( )
2021-07-01 18:54:07 +02:00
)
2021-07-29 16:31:15 +02:00
return html_sco_header . sco_header ( ) + " \n " . join ( H ) + F
2021-08-28 16:01:41 +02:00
@bp.route ( " /toggle_active_user/<user_name> " , methods = [ " GET " , " POST " ] )
@scodoc
@permission_required ( Permission . ScoUsersAdmin )
def toggle_active_user ( user_name : str = None ) :
""" Change active status of a user account """
u = User . query . filter_by ( user_name = user_name ) . first ( )
if not u :
raise ScoValueError ( " invalid user_name " )
form = DeactivateUserForm ( )
if (
2021-10-10 21:09:27 +02:00
request . method == " POST " and form . cancel . data
2021-08-28 16:01:41 +02:00
) : # if cancel button is clicked, the form.cancel.data will be True
# flash
return redirect ( url_for ( " users.index_html " , scodoc_dept = g . scodoc_dept ) )
if form . validate_on_submit ( ) :
u . active = not u . active
db . session . add ( u )
db . session . commit ( )
return redirect ( url_for ( " users.index_html " , scodoc_dept = g . scodoc_dept ) )
return render_template ( " auth/toogle_active_user.html " , form = form , u = u )