Files
CD73/patches.d/scribe-backend.patch

537 lines
22 KiB
Diff
Raw Normal View History

diff --git usr/bin/importation_scribe usr/bin/importation_scribe
index 0db4982..889dc03 100755
2025-08-19 12:13:37 +02:00
--- usr/bin/importation_scribe
+++ usr/bin/importation_scribe
@@ -42,20 +42,22 @@ choix de la source de données et imports
2025-08-18 11:30:59 +02:00
- personnels administratifs
- comptes invités
"""
import sys
from os import environ, getcwd, chdir
from os.path import isfile, dirname
2025-08-18 09:39:27 +02:00
from pyeole.process import system_out, system_code
from scribe.storage import init_store
from scribe.eoleldap import Ldap
2025-08-19 11:04:10 +02:00
+from scribe.eoleuser import User
2025-08-27 14:15:43 +02:00
+from scribe.enseignants import Enseignant
from scribe.ldapconf import SUPPORT_ETAB
2025-08-18 09:39:27 +02:00
from scribe.eoletools import nscd_start, nscd_stop
from scribe.parsing import sconet, aaf, be1d, scribecsv2
from scribe.importation import preferences, writer, config
2025-08-18 11:30:59 +02:00
from scribe.importation import log
#______________________________________________________________________________
# utilitaires de manipulation de la console
class OutOfRange(Exception):
@@ -198,21 +200,23 @@ def ask_administratifs_prefs():
ask_prefs(preferences.get_administratifs_prefs())
def ask_invites_prefs():
"""
préférences comptes invités
"""
print("*************************")
print("** Préférences Invités **")
print("*************************")
print()
- ask_prefs(preferences.get_invites_prefs())
+ prefs = preferences.get_invites_prefs()
+ if not isfile(prefs):
+ ask_prefs(preferences.get_invites_prefs())
#______________________________________________________________________________
class Console:
"""
Questions à utiliser en mode console
"""
def __init__(self):
@@ -455,36 +459,48 @@ class Console:
def do_write_enseignant(self):
"""
écriture des enseignants
2025-08-18 11:30:59 +02:00
"""
log.add_lock()
log.debuglog("Arrêt de LSC...", title=True)
nscd_stop()
connexion = Ldap()
connexion.connect()
if SUPPORT_ETAB:
+ enseignant = Enseignant()
+ enseignant.ldap_admin = connexion
2025-08-18 09:39:27 +02:00
prefs = preferences.get_enseignants_prefs()
etab = prefs.get_default('etab')
etab_prefix = prefs.get_default('etab_prefix')
+ old_logins = connexion._search(f'(&(objectClass=eolegroupe)(type=etablissement)(cn={etab}))', ['memberUid'])
+ if old_logins:
2025-08-27 14:21:20 +02:00
+ old_logins = [ol for ol in old_logins[0][1].get('memberUid', []) if enseignant._is_enseignant(ol)]
2025-08-18 09:39:27 +02:00
else:
etab = None
etab_prefix = ''
2025-08-18 11:30:59 +02:00
if self.import_type != 'maj':
writer.purge_equipes(connexion=connexion, etab=etab)
writer.verify_classe(store=self.store, connexion=connexion,
etab_prefix=etab_prefix)
writer.write_matiere(store=self.store, connexion=connexion,
2025-08-18 09:39:27 +02:00
etab=etab, etab_prefix=etab_prefix)
writer.verify_option(store=self.store, connexion=connexion,
etab_prefix=etab_prefix)
- writer.write_enseignant(store=self.store, connexion=connexion,
+ logins = writer.write_enseignant(store=self.store, connexion=connexion,
etab=etab)
+ if SUPPORT_ETAB and self.import_type == 'annu':
2025-08-19 11:04:10 +02:00
+ user = User()
2025-08-20 11:38:27 +02:00
+ user.has_ftp = True
2025-08-19 11:04:10 +02:00
+ user.ldap_admin = connexion
2025-08-27 14:26:40 +02:00
+ obsolete_logins = [login for login in old_logins if login not in logins]
2025-08-19 11:04:10 +02:00
+ for obsolete_login in obsolete_logins:
2025-08-20 11:18:57 +02:00
+ user._quit_etab(obsolete_login, etab=etab)
2025-08-18 09:39:27 +02:00
if self.data_type in ['sconet', 'aaf']:
writer.write_service(store=self.store, connexion=connexion,
etab=etab, etab_prefix=etab_prefix)
2025-08-18 11:30:59 +02:00
writer.write_administratif(store=self.store, connexion=connexion,
etab=etab)
writer.write_samba(connexion)
connexion.close()
log.debuglog("Démarrage de LSC...", title=True)
nscd_start()
log.del_lock()
2025-08-19 11:04:10 +02:00
diff --git usr/lib/python3/dist-packages/scribe/enseignants.py usr/lib/python3/dist-packages/scribe/enseignants.py
index 69f3411..97cd0df 100644
2025-08-19 11:04:10 +02:00
--- usr/lib/python3/dist-packages/scribe/enseignants.py
+++ usr/lib/python3/dist-packages/scribe/enseignants.py
2025-08-19 15:20:28 +02:00
@@ -12,21 +12,21 @@
from os import system, makedirs
from os.path import join, isdir
from ldap import MOD_REPLACE, modlist
from datetime import datetime
import time
from fichier.quota import set_quota
from fichier.acl import set_owner, clear_acl, set_user_acl, copy_default_acl
from scribe.ldapconf import SMB_SERVEUR, HOME_PATH, MAIL_DOMAIN, PROF_FILTER, \
AD_HOME_PATH, ADMIN_GROUPS, USER_FILTER, num_etab, BRANCHE_GROUP_ETAB
from scribe.eoletools import fixdate, create_ad_home, \
-to_list, not_empty, format_current_date, replace_cars, format_old_date
+to_list, not_empty, format_current_date, replace_cars, format_old_date, launch_smbldap_tool
from scribe.eoleuser import User, gen_common_attrs, send_first_mail, \
gen_profil, gen_common_devdir, gen_radius_attrs
class Enseignant(User):
"""
classe pour la gestion des enseignants Scribe
"""
_type = 'personnels'
profil = 'enseignant'
@@ -145,38 +145,31 @@ class Enseignant(User):
2025-08-19 11:04:10 +02:00
rep = join(AD_HOME_PATH, login)
# répertoire supérieur
clear_acl(rep)
set_user_acl(rep, login, 'rwx')
copy_default_acl(rep)
# chown pour la prise en compte des quotas
set_owner(perso, login)
if 'quota' in args:
set_quota(login, args['quota'])
- def _change_etab(self, user, old_etab, new_etab):
+ def _change_etab(self, user, new_etab):
old_dn = self.get_user_dn(user)
new_dn = self.get_user_dn(user, force_etab=new_etab)
#copie de l'utilisateur + suppression
uidfilter = "(&%s(uid=%s))" % (USER_FILTER, user)
cur_ldif = self.ldap_admin._search_one(uidfilter)
2025-08-19 11:04:10 +02:00
- self._desinscription(user, old_etab, sync=False)
- self._desinscription(user, 'profs-' + old_etab, sync=False)
- for grp in self._get_user_groups(user, old_etab):
- self._desinscription(user, grp, sync=False)
self.ldap_admin._delete(old_dn)
#Suppression du cache
self.cache_etab['login'].pop(user)
self.ldap_admin._add(new_dn, modlist.addModlist(cur_ldif))
- #inscription dans le groupe du nouvel etablissement
- self._inscription(user, new_etab, sync=False, etab=num_etab)
- self._inscription(user, 'profs-' + new_etab, sync=False, etab=new_etab)
2025-08-19 11:04:10 +02:00
-
+
2025-08-19 11:04:10 +02:00
def _update(self, login, **args):
"""
Mise à niveau Enseignant via l'extraction
"""
2025-08-28 16:17:22 +02:00
if 'etab' in args:
2025-08-20 09:11:40 +02:00
user_dn = self.get_user_dn(login, force_etab=args.get('etab')) #USER_DN % dict(uid=login, _type=self._type)
2025-08-19 11:04:10 +02:00
else:
user_dn = self.get_user_dn(login) #USER_DN % dict(uid=login, _type=self._type)
datas = []
@@ -196,21 +189,21 @@ class Enseignant(User):
2025-08-19 11:04:10 +02:00
datas.append((MOD_REPLACE, 'ENTAuxEnsCategoDiscipline', args['disciplines']))
datas.append((MOD_REPLACE, 'sn', args['nom']))
datas.append((MOD_REPLACE, 'givenName', args['prenom']))
datas.append((MOD_REPLACE, 'cn', "%(prenom)s %(nom)s" % args ))
datas.append((MOD_REPLACE, 'displayName', "%(prenom)s %(nom)s" % args ))
datas.append((MOD_REPLACE, 'gecos', replace_cars("%(prenom)s %(nom)s" % args) ))
datas.append((MOD_REPLACE, 'LastUpdate', format_current_date()))
self.ldap_admin._modify(user_dn, datas)
if not_empty(args, 'groups'):
groups = to_list(args['groups'])
- old_groups = self._get_user_groups(login)
+ old_groups = self._get_user_groups(login, etab=args.get('etab'))
for group in groups:
if group not in old_groups:
self._inscription(login, group, sync=False)
self._gen_ftpdir(login)
self._gen_groupesdir(login)
def _Upgrade(self, login):
"""
Mise à niveau d'un compte enseignant
"""
diff --git usr/lib/python3/dist-packages/scribe/eoleldap.py usr/lib/python3/dist-packages/scribe/eoleldap.py
index 45ec338..dac52b3 100644
--- usr/lib/python3/dist-packages/scribe/eoleldap.py
+++ usr/lib/python3/dist-packages/scribe/eoleldap.py
@@ -8,21 +8,21 @@
# eoleldap.py
#
# librairie pour la connexion à un serveur ldap
#
###########################################################################
"""
Librairie Ldap pour Scribe
"""
import sys
from .ldapconf import SUFFIX, ROOT_DN, USER_FILTER, GROUP_FILTER, SHARE_FILTER, \
- SUPPORT_ETAB, ldap_server, ldap_passwd, num_etab, BRANCHE_GROUP_ETAB, LDAP_MODE, acad
+ SUPPORT_ETAB, ldap_server, ldap_passwd, num_etab, BRANCHE_GROUP_ETAB, BRANCHE_ETAB, LDAP_MODE, acad
from scribe.errors import LdapExistingGroup, LdapExistingUser, \
SystemExistingUser, NiveauNotFound
from .eoletools import to_list
import ldap
from ldap import SCOPE_ONELEVEL
def is_system_user(user):
"""
indique si le login proposé est déjà un utilisateur système
2025-08-19 11:04:10 +02:00
@@ -267,29 +267,27 @@ class _LdapEntry(object):
res = res['memberUid']
res.sort()
return res
else:
return []
2025-08-19 11:04:10 +02:00
def _get_user_groups(self, login, etab=None):
"""
2025-08-19 11:04:10 +02:00
renvoit la liste des groupes d'un utilisateur
"""
+ if etab:
2025-08-19 11:04:10 +02:00
+ suffix = BRANCHE_ETAB % {'etab': etab}
+ else:
2025-08-19 11:04:10 +02:00
+ suffix = None
res = self.ldap_admin._search("(&%s(memberUid=%s))" % (
- GROUP_FILTER, login), 'cn')
- groups = []
- for group in res:
- if etab is not None:
- grp_etab = group[0].split(',ou=')[-3]
- if etab != grp_etab:
- continue
- groups.append(group[1]['cn'][0])
+ GROUP_FILTER, login), 'cn', suffix=suffix)
+ groups = [group[1]['cn'][0] for group in res]
groups.sort()
return groups
2025-08-19 11:04:10 +02:00
def _get_users(self, filtre='', attrs=['uid']):
"""
2025-08-19 11:04:10 +02:00
recherche d'utilisateurs
"""
2025-08-19 11:04:10 +02:00
users = []
res = self.ldap_admin._search("(&%s%s)" % (USER_FILTER, filtre), attrs)
for user in res:
@@ -572,21 +570,31 @@ elif LDAP_MODE == 'openldap':
récupère le numéro d'etab du cache ou requète LDAP
"""
if not SUPPORT_ETAB:
return None
if not multi_etabs and name in self.cache_etab[_type]:
return self.cache_etab[_type][name]
need_close = False
if self.ldap_admin.connexion == None:
self.ldap_admin.connect()
need_close = True
- dn = self.ldap_admin._search(ldap_filter, [attr])[0][0]
+ try:
+ if _type == 'group':
2025-08-27 15:47:40 +02:00
+ attr = 'cn'
+ result = self.ldap_admin._search(ldap_filter, [attr])[0]
+ if _type == 'login':
2025-08-27 15:47:40 +02:00
+ etab = [cn[1]['cn'][0] for cn in self.ldap_admin._search(f'(&(objectClass=eolegroupe)(type=Etablissement)(memberUid={name}))', ['cn'])]
+ if not etab:
+ return None
+ else:
+ etab = [result[0].split(',ou=')[-3]]
+ if not multi_etabs:
+ etab = etab[0]
+ except:
+ etab = None
if need_close:
self.ldap_admin.close()
- etab = dn.split(',ou=')[-3]
- if multi_etabs:
- etab = [etab]
- else:
+ if not multi_etabs:
self.cache_etab[_type][name] = etab
return etab
else:
raise Exception('Unknown mode')
2025-08-19 11:04:10 +02:00
diff --git usr/lib/python3/dist-packages/scribe/eoleuser.py usr/lib/python3/dist-packages/scribe/eoleuser.py
index 05569fd..f8b4ab3 100644
2025-08-19 11:04:10 +02:00
--- usr/lib/python3/dist-packages/scribe/eoleuser.py
+++ usr/lib/python3/dist-packages/scribe/eoleuser.py
@@ -361,25 +361,27 @@ class User(LdapEntry):
def _inscription(self, login, groupe, sync=True, etab=None, check_etab=True, touch=True):
"""
Inscription d'un utilisateur à un groupe
"""
etabgroup = None
if SUPPORT_ETAB:
etabgroup = self.get_etab_from_group(groupe)
if check_etab:
# vérification de la cohérence
if etab is None:
- etabuser = self.get_etab(login)
+ etabuser = self.get_etab(login, multi_etabs=True)
else:
#on force l'établissement de l'utilisateur (#4827)
+ if not isinstance(etab, list):
+ etab = [etab]
etabuser = etab
- if etabgroup != etabuser:
+ if etabgroup not in etabuser:
raise Exception("L'utilisateur {0} ne fait pas partie du même établissement que le groupe {1} : {2} - {3}".format(login, groupe, etabuser, etabgroup))
if not self.has_ftp:
return True
grp = Group()
grp.ldap_admin = self.ldap_admin
gtype = grp.c_get_group_type(groupe)
if self.profil != 'eleve' and gtype in ['Classe', 'Niveau', 'Option']:
# groupes réservés élèves
return True
if self.profil not in ['enseignant', 'administratif'] and gtype in ['Matiere', 'Equipe']:
@@ -410,20 +412,32 @@ class User(LdapEntry):
2025-08-19 11:04:10 +02:00
grp = Group()
grp.ldap_admin = self.ldap_admin
if touch:
grp._touch(groupe)
# cas eleve + option
if sync:
self._gen_ftpdir(login)
self._gen_groupesdir(login)
return True
2025-08-19 11:04:10 +02:00
+ def _quit_etab(self, user, etab):
+ self._desinscription(user, etab, sync=True)
2025-08-19 11:04:10 +02:00
+ for grp in self._get_user_groups(user, etab):
+ self._desinscription(user, grp, sync=True)
+ grp = Group()
+ divcods = [dv for dv in self.get_attr(user, 'Divcod')
+ if grp.get_etab_from_group(dv) != etab]
+ self.set_attr(user, 'Divcod', divcods)
+
+
+
2025-08-19 11:04:10 +02:00
+
def _gen_ftpdir(self, login):
"""
2025-08-19 11:04:10 +02:00
Gestion du répertoire "/home/adhomes/<login>/.ftp"
"""
2025-08-19 11:04:10 +02:00
homedir = join(AD_HOME_PATH, login)
ftpdir = join(homedir, '.ftp')
if isdir(ftpdir):
rmtree(ftpdir)
makedirs(ftpdir, 0o500)
system('/bin/chown %s %s' % (login, ftpdir))
@@ -594,27 +608,27 @@ class User(LdapEntry):
2025-08-19 11:04:10 +02:00
authldap = Ldap(binddn=user_dn,
passwd=password)
try:
authldap.connect()
authldap.close()
return True
2025-08-19 11:04:10 +02:00
except:
authldap.close()
return False
2025-08-19 11:04:10 +02:00
- def get_user_groups(self, login):
+ def get_user_groups(self, login, etab=None):
"""
2025-08-19 11:04:10 +02:00
renvoie la liste des groupes d'un utilisateur
avec connexion ldap
"""
self.ldap_admin.connect()
2025-08-19 11:04:10 +02:00
- res = self._get_user_groups(login)
+ res = self._get_user_groups(login, etab=etab)
self.ldap_admin.close()
return res
2025-08-19 11:04:10 +02:00
def _touch(self, login):
"""
2025-08-19 11:04:10 +02:00
Mise à jour de l'attribut LastUpdate
"""
2025-08-19 11:04:10 +02:00
self._set_attr(login, 'LastUpdate', tool.format_current_date())
2025-08-18 14:10:41 +02:00
2025-08-19 11:04:10 +02:00
def _get_ead_type(self, login):
@@ -850,22 +864,27 @@ class User(LdapEntry):
return num_etab
def get_user_dn(self, login, force_etab=None):
if LDAP_MODE == 'ad':
if self.ldap_admin.connexion is None:
self.ldap_admin.connect()
data = self.ldap_admin.connexion.search_s(SUFFIX, SCOPE_SUBTREE,
"(&%s(uid=%s))" % (USER_FILTER, login), ['cn'])
if data[0][0]:
return data[0][0]
- if force_etab is None:
- etab = self.get_etab(login)
- else:
- etab = force_etab
- return USER_DN % dict(uid=login, _type=self._type, etab=etab)
+ elif LDAP_MODE == 'openldap':
+ if force_etab is None:
+ if self.ldap_admin.connexion is None:
+ self.ldap_admin.connect()
+ data = self.ldap_admin.connexion.search_s(SUFFIX, SCOPE_SUBTREE,
+ "(&%s(uid=%s))" % (USER_FILTER, login), ['cn'])
+ if data[0][0]:
+ return data[0][0]
+ else:
+ return USER_DN % dict(uid=login, _type=self._type, etab=force_etab)
class Machine(User):
"""
classe pour les comptes machine
"""
pass
2025-07-31 16:03:18 +02:00
diff --git usr/lib/python3/dist-packages/scribe/importation/writer.py usr/lib/python3/dist-packages/scribe/importation/writer.py
index 34ce0fb..14d939e 100644
2025-07-31 16:03:18 +02:00
--- usr/lib/python3/dist-packages/scribe/importation/writer.py
+++ usr/lib/python3/dist-packages/scribe/importation/writer.py
@@ -6,21 +6,21 @@
# eole@ac-dijon.fr
###########################################################################
"""
écriture dans le ldap des données relationnelles
"""
from os.path import join
from creole.eosfunc import gen_random
from scribe.storage import Niveau, Classe, Eleve, Responsable, Enseignant, \
Administratif, EnsClasse, Matiere, Groupe, JointureClasseEnseignant, \
Service, Invite, JointureGroupeEleve, JointureGroupeEnseignant
-from scribe.ldapconf import CIVILITES, EOLE_AD, MIN_PASSWORD_CLASS, GROUP_PATH, LDAP_MODE, FORCED_PASSWORD_MODIFICATION_ALLOWED
+from scribe.ldapconf import CIVILITES, EOLE_AD, MIN_PASSWORD_CLASS, GROUP_PATH, LDAP_MODE, FORCED_PASSWORD_MODIFICATION_ALLOWED, num_etab
from scribe.eoletools import ok_groupe, gen_login, clean_date, \
gen_strong_password, lsc_sync
from scribe.errors import BadLogin
from scribe.eolegroup import Group
from scribe.eoleshare import Share
from scribe.eleves import Eleve as LdapEleve
from scribe.responsables import Responsable as LdapResponsable
from scribe.enseignants import Enseignant as LdapEnseignant
from scribe.administratifs import Administratif as LdapAdministratif
from scribe.autres import Autre as LdapAutre
@@ -834,23 +834,28 @@ def _maj_enseignant(enseignant, user, login, etab, administratif=False):
enseignant : store.Enseignant()
user : eoleuser.Enseignant()
2025-08-18 11:30:59 +02:00
login : uid de l'utilisateur dans ldap
administratif : personnel administratif avec un compte enseignant
"""
log.log.debug("maj de %s" % login)
classe = []
groups = []
# attention : des administratifs peuvent avoir un compte enseignant
if isinstance(enseignant, Enseignant):
- old_etab = user.get_etab(login)
- if old_etab != etab:
- user._change_etab(login, old_etab, etab)
+ current_dn = user.get_user_dn(login)
+ new_dn = user.get_user_dn(login, force_etab=etab)
+ if current_dn != new_dn:
2025-08-20 09:11:40 +02:00
+ user._change_etab(login, etab)
+ #inscription dans le groupe du nouvel etablissement si nécessaire
+ if etab not in user.get_etab(login, multi_etabs=True):
+ user._inscription(login, etab, sync=False, etab=num_etab)
+ user._inscription(login, 'profs-' + etab, sync=False, etab=etab)
for joint in enseignant.get_classes():
groups.append('profs-%s' % str(joint.classe.nom))
if joint.profprincipal:
2025-08-18 11:30:59 +02:00
classe.append(str(joint.classe.nom))
for matiere in enseignant.get_matieres():
groups.append(str(matiere.nom))
for option in enseignant.get_groupes():
groups.append('profs-%s' % str(option.nom))
disciplines = eval(enseignant.disciplines)
else:
@@ -885,20 +890,21 @@ def write_enseignant(store, connexion, etab=None, current_ead_user=config.DEFAUL
2025-08-19 11:19:07 +02:00
log.write_header(config.ENS_HEADER, config.ENS_INFO)
user = LdapEnseignant()
user.ldap_admin = connexion
prefs = preferences.get_enseignants_prefs()
quota = prefs.get_default('quota')
if FORCED_PASSWORD_MODIFICATION_ALLOWED:
change_pwd = prefs.get_default('change_pwd') == 'oui'
else:
change_pwd = False
new_passwords = []
+ logins = []
for enseignant in store.query(Enseignant):
if enseignant.force_login:
# login forcé
if user._is_enseignant(str(enseignant.force_login)):
login = str(enseignant.force_login)
else:
login = ''
else:
login = _enseignant_exists(enseignant, user)
if login != '':
@@ -909,29 +915,31 @@ def write_enseignant(store, connexion, etab=None, current_ead_user=config.DEFAUL
2025-08-19 11:19:07 +02:00
if str(enseignant.nom) == '' or str(enseignant.prenom) == '':
log.infolog("Enseignant n°%s invalide" % str(enseignant.int_id))
continue
try:
login = _new_enseignant(enseignant, user, prefs, etab=etab, new_passwords=new_passwords)
except BadLogin as message:
log.infolog(str(message))
continue
# enregistrement du login attribué
enseignant.login = str(login)
+ logins.append(enseignant.login)
num += 1
if num % config.DEBUG_NUM == 0:
log.debuglog("%d enseignants traités..." % num)
if EOLE_AD:
_sync_passwords(user, new_passwords, change_pwd=change_pwd)
_create_dirs(user, quota, new_passwords)
log.infolog("TOTAL : %d enseignants" % num)
if num != 0:
log.copy_info(config.ENS_INFO, user=current_ead_user)
+ return logins
# -------------------- administratifs -------------------- #
def _new_administratif(administratif, user, prefs, etab=None, new_passwords=[]):
"""
traitement d'un nouvel administratif (création)
administratif : store.Administratif()
user : eoleuser.Administratif()
"""