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-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
from app . auth . models import Permission
from app . auth . models import User
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-06-26 21:57:54 +02:00
from app . views import users_bp as bp
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 " )
initvalues = sco_users . _user_list ( user_name )
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
# Noms de roles pouvant etre attribues aux utilisateurs via ce dialogue
# si pas SuperAdmin, restreint aux rôles EnsX, SecrX, AdminX
#
editable_roles = Role . query . all ( )
if is_super_admin :
log ( " create_user_form called by %s (super admin) " % ( current_user . user_name , ) )
else :
editable_roles = [
r for r in editable_roles if r . name in { u " Ens " , u " Secr " , u " Admin " }
]
#
if not edit :
submitlabel = " Créer utilisateur "
orig_roles = set ( )
else :
submitlabel = " Modifier utilisateur "
initvalues [ " roles " ] = initvalues [ " roles " ] . split ( " , " ) or [ ]
orig_roles = set ( initvalues [ " roles " ] )
if initvalues [ " status " ] == " old " :
editable_roles = set ( ) # can't change roles of a disabled user
# add existing user roles
displayed_roles = list ( editable_roles . union ( orig_roles ) )
displayed_roles . sort ( )
disabled_roles = { } # pour desactiver les roles que l'on ne peut pas editer
for i in range ( len ( displayed_roles ) ) :
if displayed_roles [ i ] not in editable_roles :
disabled_roles [ i ] = True
# log('create_user_form: displayed_roles=%s' % displayed_roles)
descr = [
( " edit " , { " input_type " : " hidden " , " default " : edit } ) ,
( " nom " , { " title " : " Nom " , " size " : 20 , " allow_null " : False } ) ,
( " prenom " , { " title " : " Prénom " , " size " : 20 , " allow_null " : False } ) ,
]
if auth_name != user_name : # no one can't change its own status
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 " ] } ,
) ,
( " user_id " , { " input_type " : " hidden " , " default " : initvalues [ " user_id " ] } ) ,
]
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 ,
" allowed_values " : displayed_roles ,
" 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)
# orig_roles - editable_roles
REQUEST . form [ " roles " ] = list (
set ( REQUEST . form [ " roles " ] ) . union ( orig_roles - editable_roles )
)
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 :
return REQUEST . RESPONSE . redirect ( context . UsersURL ( ) )
else :
vals = tf [ 2 ]
roles = set ( vals [ " roles " ] ) . intersection ( editable_roles )
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
users = _user_list ( user_name )
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 :
ok , msg = context . _check_modif_user (
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
if edit : # modif utilisateur (mais pas passwd)
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 " ]
if ( auth_name == user_name ) and vals . has_key ( " status " ) :
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:
for role in orig_roles :
if role and not role in editable_roles :
roles . add ( role )
vals [ " roles " ] = " , " . join ( roles )
# ok, edit
log ( " sco_users: editing %s by %s " % ( user_name , auth_name ) )
# log('sco_users: previous_values=%s' % initvalues)
# log('sco_users: new_values=%s' % vals)
context . _user_edit ( user_name , vals )
return REQUEST . RESPONSE . redirect (
" userinfo?user_name= %s &head_message=Utilisateur %s modifié "
% ( 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
log ( " sco_users: new_user %s by %s " % ( vals [ " user_name " ] , auth_name ) )
context . create_user ( vals , REQUEST = REQUEST )
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 )