ScoDoc/sco_import_users.py

237 lines
7.6 KiB
Python

# -*- mode: python -*-
# -*- coding: utf-8 -*-
##############################################################################
#
# Gestion scolarite IUT
#
# 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
#
##############################################################################
"""Import d'utilisateurs via fichier Excel
"""
from email.MIMEMultipart import ( # pylint: disable=no-name-in-module,import-error
MIMEMultipart,
)
from email.MIMEText import MIMEText # pylint: disable=no-name-in-module,import-error
from email.MIMEBase import MIMEBase # pylint: disable=no-name-in-module,import-error
from email.Header import Header # pylint: disable=no-name-in-module,import-error
from email import Encoders # pylint: disable=no-name-in-module,import-error
import notesdb as ndb
import sco_utils as scu
from notes_log import log
from TrivialFormulator import TrivialFormulator, TF
import sco_news
import sco_excel
from sco_exceptions import AccessDenied, ScoValueError, ScoException
TITLES = ("user_name", "nom", "prenom", "email", "roles", "dept")
def generate_excel_sample():
"""generates an excel document suitable to import users"""
style = sco_excel.Excel_MakeStyle(bold=True)
titles = TITLES
titlesStyles = [style] * len(titles)
return sco_excel.Excel_SimpleTable(
titles=titles, titlesStyles=titlesStyles, SheetName="Utilisateurs ScoDoc"
)
def import_excel_file(datafile, REQUEST=None, context=None):
"Create users from Excel file"
authuser = REQUEST.AUTHENTICATED_USER
auth_name = str(authuser)
authuser_info = context._user_list(args={"user_name": auth_name})
zope_roles = authuser.getRolesInContext(context)
if not authuser_info and not ("Manager" in zope_roles):
# not admin, and not in database
raise AccessDenied("invalid user (%s)" % auth_name)
if authuser_info:
auth_dept = authuser_info[0]["dept"]
else:
auth_dept = ""
log("sco_import_users.import_excel_file by %s" % auth_name)
exceldata = datafile.read()
if not exceldata:
raise ScoValueError("Ficher excel vide ou invalide")
_, data = sco_excel.Excel_to_list(exceldata)
if not data: # probably a bug
raise ScoException("import_excel_file: empty file !")
# 1- --- check title line
fs = [scu.strlower(scu.stripquotes(s)) for s in data[0]]
log("excel: fs='%s'\ndata=%s" % (str(fs), str(data)))
# check cols
cols = {}.fromkeys(TITLES)
unknown = []
for tit in fs:
if not cols.has_key(tit):
unknown.append(tit)
else:
del cols[tit]
if cols or unknown:
raise ScoValueError(
"colonnes incorrectes (on attend %d, et non %d) <br/> (colonnes manquantes: %s, colonnes invalides: %s)"
% (len(TITLES), len(fs), cols.keys(), unknown)
)
# ok, same titles...
U = []
for line in data[1:]:
d = {}
for i in range(len(fs)):
d[fs[i]] = line[i]
U.append(d)
return import_users(U, auth_dept=auth_dept, context=context)
def import_users(U, auth_dept="", context=None):
"""Import des utilisateurs:
Pour chaque utilisateur à créer:
- vérifier données
- générer mot de passe aléatoire
- créer utilisateur et mettre le mot de passe
- envoyer mot de passe par mail
En cas d'erreur: supprimer tous les utilisateurs que l'on vient de créer.
"""
created = [] # liste de uid créés
try:
for u in U:
ok, msg = context._check_modif_user(
0,
user_name=u["user_name"],
nom=u["nom"],
prenom=u["prenom"],
email=u["email"],
roles=u["roles"],
)
if not ok:
raise ScoValueError(
"données invalides pour %s: %s" % (u["user_name"], msg)
)
u["passwd"] = generate_password()
# si auth_dept, crée tous les utilisateurs dans ce departement
if auth_dept:
u["dept"] = auth_dept
#
context.create_user(u.copy())
created.append(u["user_name"])
except:
log("import_users: exception: deleting %s" % str(created))
# delete created users
for user_name in created:
context._user_delete(user_name)
raise # re-raise exception
for u in U:
mail_password(u, context=context)
return "ok"
# --------- Génération du mot de passe initial -----------
# Adapté de http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/440564
# Alphabet tres simple pour des mots de passe simples...
import getpass, random, sha, string, md5, time, base64
ALPHABET = r"""ABCDEFGHIJKLMNPQRSTUVWXYZ123456789123456789AEIOU"""
PASSLEN = 6
RNG = random.Random(time.time())
def generate_password():
"""This function creates a pseudo random number generator object, seeded with
the cryptographic hash of the passString. The contents of the character set
is then shuffled and a selection of passLength words is made from this list.
This selection is returned as the generated password."""
l = list(ALPHABET) # make this mutable so that we can shuffle the characters
RNG.shuffle(l) # shuffle the character set
# pick up only a subset from the available characters:
return "".join(RNG.sample(l, PASSLEN))
def mail_password(u, context=None, reset=False):
"Send password by email"
if not u["email"]:
return
u["url"] = context.ScoURL()
txt = (
"""
Bonjour %(prenom)s %(nom)s,
"""
% u
)
if reset:
txt += (
"""
votre mot de passe ScoDoc a été ré-initialisé.
Le nouveau mot de passe est: %(passwd)s
Votre nom d'utilisateur est %(user_name)s
Vous devrez changer ce mot de passe lors de votre première connexion
sur %(url)s
"""
% u
)
else:
txt += (
"""
vous avez été déclaré comme utilisateur du logiciel de gestion de scolarité ScoDoc.
Votre nom d'utilisateur est %(user_name)s
Votre mot de passe est: %(passwd)s
Le logiciel est accessible sur: %(url)s
Vous êtes invité à changer ce mot de passe au plus vite (cliquez sur
votre nom en haut à gauche de la page d'accueil).
"""
% u
)
txt += (
"""
ScoDoc est un logiciel libre développé à l'Université Paris 13 par Emmanuel Viennet.
Pour plus d'informations sur ce logiciel, voir %s
"""
% scu.SCO_WEBSITE
)
msg = MIMEMultipart()
if reset:
msg["Subject"] = Header("Mot de passe ScoDoc", scu.SCO_ENCODING)
else:
msg["Subject"] = Header("Votre accès ScoDoc", scu.SCO_ENCODING)
msg["From"] = context.get_preference("email_from_addr")
msg["To"] = u["email"]
msg.epilogue = ""
txt = MIMEText(txt, "plain", scu.SCO_ENCODING)
msg.attach(txt)
context.sendEmail(msg)