diff --git usr/bin/importation_scribe usr/bin/importation_scribe index 0db4982..31f8cee 100755 --- usr/bin/importation_scribe +++ usr/bin/importation_scribe @@ -42,20 +42,22 @@ choix de la source de données et imports - personnels administratifs - comptes invités """ import sys from os import environ, getcwd, chdir from os.path import isfile, dirname from pyeole.process import system_out, system_code from scribe.storage import init_store from scribe.eoleldap import Ldap +from scribe.eoleuser import User +from scribe.enseignants import Enseignant from scribe.ldapconf import SUPPORT_ETAB from scribe.eoletools import nscd_start, nscd_stop from scribe.parsing import sconet, aaf, be1d, scribecsv2 from scribe.importation import preferences, writer, config 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 """ 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.connect() 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: + old_logins = [ol for ol in old_logins[0][1].get('memberUid', []) if enseignant._is_enseignant(ol)] else: etab = None etab_prefix = '' 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, 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': + user = User() + user.has_ftp = True + user.ldap_admin.connect() + obsolete_logins = [login for login in old_logins if login not in logins] + for obsolete_login in obsolete_logins: + user._quit_etab(obsolete_login, etab=etab) if self.data_type in ['sconet', 'aaf']: writer.write_service(store=self.store, connexion=connexion, etab=etab, etab_prefix=etab_prefix) 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() diff --git usr/lib/python3/dist-packages/scribe/enseignants.py usr/lib/python3/dist-packages/scribe/enseignants.py index 69f3411..07fa33a 100644 --- usr/lib/python3/dist-packages/scribe/enseignants.py +++ usr/lib/python3/dist-packages/scribe/enseignants.py @@ -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,33 @@ class Enseignant(User): 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) - 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) + if user in self.cache_etab['login']: + self.cache_etab['login'].pop(user) + #Ajout de l’utilisateur 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) - + def _update(self, login, **args): """ Mise à niveau Enseignant via l'extraction """ if 'etab' in args: user_dn = self.get_user_dn(login, force_etab=args.get('etab')) #USER_DN % dict(uid=login, _type=self._type) else: user_dn = self.get_user_dn(login) #USER_DN % dict(uid=login, _type=self._type) datas = [] @@ -196,21 +191,21 @@ class Enseignant(User): 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..a3054ee 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 @@ -267,29 +267,27 @@ class _LdapEntry(object): res = res['memberUid'] res.sort() return res else: return [] def _get_user_groups(self, login, etab=None): """ renvoit la liste des groupes d'un utilisateur """ + if etab: + suffix = BRANCHE_ETAB % {'etab': etab} + else: + 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 def _get_users(self, filtre='', attrs=['uid']): """ recherche d'utilisateurs """ users = [] res = self.ldap_admin._search("(&%s%s)" % (USER_FILTER, filtre), attrs) for user in res: @@ -566,27 +564,47 @@ elif LDAP_MODE == 'openldap': ret.append((key, sub)) return ret class LdapEntry(_LdapEntry): def _get_etab(self, _type, name, ldap_filter, attr, multi_etabs=False): """ 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: + if self.ldap_admin.connexion is None: self.ldap_admin.connect() need_close = True - dn = self.ldap_admin._search(ldap_filter, [attr])[0][0] - if need_close: - self.ldap_admin.close() - etab = dn.split(',ou=')[-3] - if multi_etabs: - etab = [etab] - else: + + try: + if _type == 'login': + etab = [cn[1]['cn'][0] for cn in self.ldap_admin._search(f'(&(objectClass=eolegroupe)(type=Etablissement)(memberUid={name}))', ['cn'])] + else: + if _type == 'group': + attr = 'cn' + result = self.ldap_admin._search(ldap_filter, [attr])[0] + etab = [result[0].split(',ou=')[-3]] + + if not multi_etabs: + if len(etab) == 0: + etab = None + else: + etab = etab[0] + + except BaseException: + etab = None + finally: + if need_close: + self.ldap_admin.close() + + if not multi_etabs: self.cache_etab[_type][name] = etab + return etab + else: raise Exception('Unknown mode') diff --git usr/lib/python3/dist-packages/scribe/eoleuser.py usr/lib/python3/dist-packages/scribe/eoleuser.py index 05569fd..f8b4ab3 100644 --- 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): 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 + def _quit_etab(self, user, etab): + self._desinscription(user, etab, sync=True) + 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) + + + + def _gen_ftpdir(self, login): """ Gestion du répertoire "/home/adhomes//.ftp" """ 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): authldap = Ldap(binddn=user_dn, passwd=password) try: authldap.connect() authldap.close() return True except: authldap.close() return False - def get_user_groups(self, login): + def get_user_groups(self, login, etab=None): """ renvoie la liste des groupes d'un utilisateur avec connexion ldap """ self.ldap_admin.connect() - res = self._get_user_groups(login) + res = self._get_user_groups(login, etab=etab) self.ldap_admin.close() return res def _touch(self, login): """ Mise à jour de l'attribut LastUpdate """ self._set_attr(login, 'LastUpdate', tool.format_current_date()) 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 diff --git usr/lib/python3/dist-packages/scribe/importation/writer.py usr/lib/python3/dist-packages/scribe/importation/writer.py index 34ce0fb..14d939e 100644 --- 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() 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: + 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: 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 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 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() """