diff --git a/gestion/config.py b/gestion/config.py index 77db46c2..59652dab 100644 --- a/gestion/config.py +++ b/gestion/config.py @@ -37,6 +37,40 @@ club_login_shell='/usr/bin/rssh' # Longueur maximale d'un login maxlen_login=15 +# Quels droits donnent l'appartenance à quel groupe Unix ? +droits_groupes = {'adm' : [u'Nounou'], + 'respbats' : [u'Imprimeur', u'Cableur', u'Nounou'], + 'moderateurs' : [u'Moderateur'], + 'disconnect' : [u'Bureau'], + 'imprimeurs' : [u'Imprimeur', u'Nounou', u'Tresorier'], + 'bureau' : [u'Bureau'], + 'webadm' : [u'WebMaster'], + 'webradio' : [u'WebRadio'], + } + +####### Les ML +# Le + devant un nom de ML indique une synchronisation +# ML <-> fonction partielle : il n'y a pas d'effacement automatique +# des abonnés si le droit est retiré +droits_mailing_listes = {'roots' : [ u'Nounou', u'Apprenti'], + 'mailman' : [ u'Nounou'], + '+nounou' : [ u'Nounou', u'Apprenti'], + 'respbats' : [ u'Cableur', u'Nounou', u'Bureau'], + '+moderateurs' : [ u'Moderateur', u'Bureau'], + '+disconnect' : [ u'Nounou', u'Bureau'], + '+impression' : [ u'Imprimeur'], + 'bureau' : [u'Bureau'], + 'tresorier' : [u'Tresorier'], + + '+federez' : [u'Bureau', u'Apprenti', u'Nounou'], + '+install-party' : [u'Bureau', u'Apprenti', u'Nounou'], + + '+dsi-crans' : [u'Nounou', u'Bureau'], + '+crous-crans' : [u'Nounou', u'Bureau'], + + '+wrc' : [u'WebRadio'], + } + ## Répertoire de stockage des objets détruits cimetiere = '/home/cimetiere' diff --git a/gestion/gen_confs/droits.py b/gestion/gen_confs/droits.py index 41271ec2..e19fc0f6 100644 --- a/gestion/gen_confs/droits.py +++ b/gestion/gen_confs/droits.py @@ -1,10 +1,12 @@ #! /usr/bin/env python -# -*- coding: iso-8859-15 -*- +# -*- coding: utf-8 -*- import sys, signal, os +import getopt # Imports pour LDAP sys.path.append('/usr/scripts/gestion') +from config import droits_groupes, droits_mailing_listes from gen_confs import gen_config, anim, cprint, OK, ERREUR from ldap_crans import crans_ldap, preattr, ldap, CransLdap, strip_accents @@ -14,73 +16,39 @@ try: from Mailman import Utils from Mailman.UserDesc import UserDesc except: - # Machine sans mailman, les ML ne seront pas reconfigurées + # Machine sans mailman, les ML ne seront pas reconfigurées pass class droits(gen_config) : def restart(s) : - # Rien à faire + # Rien à faire pass - + def __str__(s): return "droits" class droits_ldap(CransLdap, droits): ####### Les groupes base_group_dn = 'ou=Group,dc=crans,dc=org' - - # Quels droits donnent l'appartenance à quel groupe Unix ? - groupes = {'adm' : [u'Nounou'], - 'respbats' : [u'Imprimeur', u'Cableur', u'Nounou'], - 'moderateurs' : [u'Moderateur'], - 'disconnect' : [u'Bureau'], - 'imprimeurs' : [u'Imprimeur', u'Nounou', u'Tresorier'], - 'bureau' : [u'Bureau'], - 'webadm' : [u'WebMaster'], - 'webradio' : [u'WebRadio'], - } - ####### Les ML - # Le + devant un nom de ML indique une synchronisation - # ML <-> fonction partielle : il n'y a pas d'effacement - # des abonnés si le droit est retiré - mailing_listes = {'roots' : [ u'Nounou', u'Apprenti'], - 'mailman' : [ u'Nounou'], - '+nounou' : [ u'Nounou', u'Apprenti'], - 'respbats' : [ u'Cableur', u'Nounou', u'Bureau'], - '+moderateurs' : [ u'Moderateur', u'Bureau'], - '+disconnect' : [ u'Nounou', u'Bureau'], - '+impression' : [ u'Imprimeur'], - 'bureau' : [u'Bureau'], - 'tresorier' : [u'Tresorier'], - - '+federez' : [u'Bureau', u'Apprenti', u'Nounou'], - '+install-party' : [u'Bureau', u'Apprenti', u'Nounou'], - - '+dsi-crans' : [u'Nounou', u'Bureau'], - '+crous-crans' : [u'Nounou', u'Bureau'], - - '+wrc' : [u'WebRadio'], - } - def build_group(self) : """ Reconstruit les groupes dans la base LDAP """ - self.anim.iter = len( self.groupes.keys() ) - for group, fonctions in self.groupes.items() : + self.anim.iter = len(droits_groupes.keys() ) + for group, fonctions in droits_groupes.items() : self.anim.cycle() - # Qui doit être dans ce groupe ? + # Qui doit être dans ce groupe ? res = [] for f in fonctions : res += self.search('droits=%s' % f)['adherent'] - # Récupération de la constitution du groupe actuel + # Récupération de la constitution du groupe actuel dn = 'cn=%s,%s' % (group, self.base_group_dn) data = self.conn.search_s(dn ,0,'objectClass=posixGroup')[0][1] init_data = data.copy() - + # Supression de tout les membres data['memberUid'] = [] - + # Ajout des bonnes personnes for adher in res : if not adher.droitsGeles(): @@ -96,7 +64,7 @@ class droits_ldap(CransLdap, droits): # Ajout de michel et webradio data['memberUid'].append('michel') data['memberUid'].append('webradio') - + # Sauvegarde modlist = ldap.modlist.modifyModlist(init_data,data) self.conn.modify_s(dn,modlist) @@ -105,32 +73,57 @@ class droits_ldap(CransLdap, droits): """ Donne la liste des membres actifs """ for adh in self.search('droits=%s&chbre!=EXT' % poste)['adherent'] : print "%s %s" % (adh.nom(), adh.prenom()) - - def sync_ML(self) : - self.anim.iter = len( self.mailing_listes.keys() ) - for ML, fonctions in self.mailing_listes.items() : - self.anim.cycle() + + def config_ML(self, mode='autosync', args=None) : + """ Reconfigure les MLs. + mode désigne le mode de fonctionnement : + * autosync est le mode par défaut, il resynchronise les MLs avec les + droits + * forcedel fait comme autosync, en demandant un args du type + [('ml1', 'all'), ('ml2', 'adresse_mail1')] + et supprime les adresses mail concernées de la mailing-liste, + 'all' pour toutes les MLs ne correspondant pas à des gens avec + des droits (seulement si elles ne seront pas réinscrites + automatiquement) + * getunsync retourne les gens qui devraient être inscrits/désinscrits + de chaque ML + args désigne l'argument au mode choisi.""" + + try: + self.anim.iter = len(droits_mailing_listes.keys()) + except: + pass + + if mode == 'getunsync': + unsync = {} + + for ML, fonctions in droits_mailing_listes.items() : + try: + self.anim.cycle() + except: + pass + if ML[0] == '+' : ML = ML[1:] only_add = 1 else : only_add = 0 - - # Instance correspondant à la ML + + # Instance correspondant à la ML mlist = MailList.MailList(ML) self.mlist_to_unlock = mlist - - # Qui doit être dans cette ML ? + + # Qui doit être dans cette ML ? res = [] for f in fonctions : res += self.search('droits=%s' % f)['adherent'] - - # Liste des personnes déja inscrites + + # Liste des personnes déja inscrites deja_inscrits = {} # { email en miniscules : email avec case n'importe comment } for addr in mlist.getMemberCPAddresses(mlist.getMembers()): deja_inscrits[addr.lower()] = addr - - # Mails à ajouter + + # Mails à ajouter to_add = [] mail_traite = [] for adher in res : @@ -142,24 +135,40 @@ class droits_ldap(CransLdap, droits): # Visiblement pas inscrit to_add.append([ mail, adher.Nom() ]) else : - # L'adhérent est déja inscrit + # L'adhérent est déja inscrit deja_inscrits.pop(mail) - - # Ajout - for mail, nom in to_add : - pw = Utils.MakeRandomPassword() - userdesc = UserDesc(mail, strip_accents(nom), pw) - mlist.ApprovedAddMember(userdesc) - if not only_add : - # Supression des personnes inscritees en trop - for mail in deja_inscrits.values() : - mlist.ApprovedDeleteMember(mail) - - mlist.Save() + + if mode == 'autosync' or mode == 'forcedel': + # Ajout + for mail, nom in to_add : + pw = Utils.MakeRandomPassword() + userdesc = UserDesc(mail, strip_accents(nom), pw) + mlist.ApprovedAddMember(userdesc) + if not only_add or mode == 'forcedel': + # Supression des personnes inscritees en trop + if not only_add or (mode == 'forcedel' and args != None + and (ML, 'all') in args): + for mail in deja_inscrits.values() : + mlist.ApprovedDeleteMember(mail) + else: + # Suppression des mails demandés + for mail in deja_inscrits.values() : + if (ML, mail) in args: + mlist.ApprovedDeleteMember(mail) + + mlist.Save() + elif mode == 'getunsync': + if to_add or deja_inscrits: + unsync[ML] = (only_add, map(lambda (x,y): y, to_add), + deja_inscrits.values()) + mlist.Unlock() self.mlist_to_unlock = None - - def gen_conf(self) : + + if mode == 'getunsync': + return unsync + + def gen_conf(self): self.anim = anim('\tconfiguration groupes') try: self.build_group() @@ -168,46 +177,64 @@ class droits_ldap(CransLdap, droits): except: self.anim.reinit() print ERREUR - if self.debug : + if self.debug : import traceback traceback.print_exc() - + self.anim = anim('\tconfiguration ML Crans') try: - self.sync_ML() + self.config_ML() self.anim.reinit() print OK except: self.anim.reinit() print ERREUR - if self.debug : + if self.debug : import traceback traceback.print_exc() try: - # Au cas où... + # Au cas où... self.mlist_to_unlock.Unlock() except : pass +class desabonner_ml(droits_ldap): + def gen_conf(self): + self.anim = anim('\tdesabonnement MLs Crans') + try: + to_del = [] + for arg in self.args: + (mail, ml) = arg.split('$') + to_del.append((ml, "%s@crans.org" % mail)) + self.config_ML(mode='forcedel', args=to_del) + self.anim.reinit() + print OK + except: + self.anim.reinit() + print ERREUR + if self.debug : + import traceback + traceback.print_exc() + class droits_openbsd(droits) : def build_master_passwd_group(self) : - """Reconstruit les entrées à ajouter dans master.passwd + """Reconstruit les entrées à ajouter dans master.passwd - Reconstruit également /etc/group pour le group wheel + Reconstruit également /etc/group pour le group wheel """ master = "" group = "wheel:*:0:root" self.anim.iter = 2 for fonction in ("Nounou", "Apprenti"): self.anim.cycle() - # Qui doit être dans ce groupe ? + # Qui doit être dans ce groupe ? res = crans_ldap().search('droits=%s' % fonction)['adherent'] for a in res: if fonction == "Nounou": - # On rajoute à /etc/group + # On rajoute à /etc/group group = "%s,%s" % (group, a._data['uid'][0]) #On conserve le shell - #Il faut ôter les emplacements spécifique à linux (type + #Il faut ôter les emplacements spécifique à linux (type #/usr/bin/zsh) if "zsh" in a._data['loginShell'][0]: shell = "/bin/zsh" @@ -225,26 +252,26 @@ class droits_openbsd(droits) : shell) group = "%s\n" % group - # On va réécrire /etc/master.passwd + # On va réécrire /etc/master.passwd # cf man master.passwd fichier = file("/etc/master.passwd") for line in fichier: if line.split(":")[4].strip() != "ldap": master = "%s%s" % (line,master) fichier.close() - # On va écrire par-dessus + # On va écrire par-dessus fichier = file("/etc/master.passwd", "w") fichier.write(master) fichier.close() os.system("pwd_mkdb -p /etc/master.passwd") - # On réécrit /etc/group + # On réécrit /etc/group fichier = file("/etc/group") for line in fichier: if line.split(":")[0].strip() != "wheel": group = "%s%s" % (group,line) fichier.close() - # On va réécrire par-dessus + # On va réécrire par-dessus fichier = file("/etc/group", "w") fichier.write(group) fichier.close() @@ -262,16 +289,52 @@ class droits_openbsd(droits) : import traceback traceback.print_exc() +def usage(): + print "%s [options] [arguments]" % sys.argv[0] + print "Options :" + print " * -h : afficher cette aide" + print " * -d ml1:mail1,mail2 ml2:all : enlever des adresses mail" + print " * -p droit1 droit2 : afficher les membres ayant tels droits" + print " * -u : affiche les MLs avec des gens à inscrire / enlever" if __name__ == '__main__' : - if '-h' in sys.argv or '--help' in sys.argv or len(sys.argv) == 1 : - print "%s " % sys.argv[0].split('/')[-1].split('.')[0] - print "Génération de la liste des personnes avec le(s) droit(s) donné(s)." - sys.exit(255) - - cl = droits() - for arg in sys.argv[1:] : - titre = "%s : " % arg - print titre - print "-" * len (titre) - cl.print_liste(unicode(arg,'iso-8859-1')) + try: + opts, args = getopt.getopt(sys.argv[1:], "dhpu", []) + except: + usage() + sys.exit() + + if len(opts) == 0 or '-h' in sys.argv[1:]: + usage() + sys.exit() + + cl = droits_ldap() + for o, a in opts: + if o == '-d': + # Suppression d'adresses mail + to_del = [] + for arg in args: + if ':' in arg: + [ml, mails] = arg.split(':', 1) + to_del.extend(map(lambda m: (ml, m), mails.split(','))) + print "Suppression forcée : %s ..." % str(to_del) + cl.config_ML(mode='forcedel', args=to_del) + + elif o == '-p': + # Affichage des membres ayant certains droits + for arg in args: + titre = "%s : " % arg + print titre + print "-" * len (titre) + cl.print_liste(arg) + + elif o == '-u': + # Affichage des MLs non synchronisées + print "MLs non synchronisées :" + unsync = cl.config_ML(mode='getunsync') + for ml in unsync.keys(): + (only_add, to_add, to_del) = unsync[ml] + print u"%s: À ajouter: %s / À enlever: %s%s" % (ml, + ', '.join(to_add), ','.join(to_del), + u" (ne seront pas enlevées automatiquement)" + if to_add == 1 else "") diff --git a/gestion/gen_confs/generate.py b/gestion/gen_confs/generate.py index 72d17dc5..27d764c2 100644 --- a/gestion/gen_confs/generate.py +++ b/gestion/gen_confs/generate.py @@ -40,7 +40,7 @@ make_lock('auto_generate', 'Big lock', nowait=1) class base_reconfigure: __service_develop = { 'macip': [ 'rouge-macip', 'zamok-macip', 'sable-macip', 'komaz-macip', 'gordon-macip' ], - 'droits': [ 'rouge-droits', 'ragnarok-droits' ], +# 'droits': [ 'rouge-droits', 'ragnarok-droits' ], 'blacklist_upload': [ 'sable-blacklist_upload', 'komaz-blacklist' ], 'blacklist_p2p': [ 'sable-blacklist_p2p', 'komaz-blacklist' ], 'blacklist_autodisc_upload': [ 'sable-blacklist_autodisc_upload', 'komaz-blacklist'], @@ -141,6 +141,12 @@ class rouge(base_reconfigure): from gen_confs.droits import droits_ldap self._do(droits_ldap()) + def desabonner_ml(self, args): + from gen_confs.droits import desabonner_ml + d = desabonner_ml() + d.args = args + self._do(d) + def ML_ens(self, mails): from adherents import ML_ens self._do(ML_ens(mails)) diff --git a/gestion/gest_crans.py b/gestion/gest_crans.py index 8eb4298a..2076a876 100644 --- a/gestion/gest_crans.py +++ b/gestion/gest_crans.py @@ -564,13 +564,64 @@ def set_droits(adher): annul, result = dialog(arg) if annul: return 1 - - # Traitement - # Dans le cas ou l'utilisateur qui modiifie n'est pas nounou + ### Traitement + # Dans le cas où l'utilisateur qui modifie n'est pas nounou, on ajoute les + # droits critiques que l'adhérent possède déjà + if not isadm: + for key in [d for d in droits_critiques if key in adher.droits()]: + result.append(key.encode("ISO-8859-15")) + + # On regarde les MLs auxquelles l'adhérent était inscrit grâce à ses + # droits et auxquelles il ne sera pas désabonné + # (MLs à débonnement manuel) + droits_mls = config.droits_mailing_listes + + # On nettoie la sortie de dialog + new = [] + for droit in result: + droit = droit.strip() + if droit == '': continue + if droit not in droits_possibles: + raise ValueError(u'Droit %s incorrect' % droit) + new.append(droit) + + # Droits supprimés + diff = [droit for droit in adher.droits() + if droit not in new] + + # Droits supprimés qui abonnaient à une ML + old_mls = [] + for droit in diff: + old_mls.extend([m for m in droits_mls + if m[0] == '+' and droit in droits_mls[m] + and m not in old_mls]) + + # MLs pour lesquelles un autre droit abonne encore + for droit in new: + for ml in [m for m in old_mls if droit in droits_mls[m]]: + old_mls.pop(ml) + + # Choix des MLs auxquelles débonner + if old_mls: + arg = u'--title "Mailing-listes dont désabonner %s" ' % adher.Nom() + arg += u'--separate-output ' + arg += u'--checklist "%s a été abonné automatiquement' % adher.Nom() + arg += u' aux MLs suivantes lorsqu\'il a obtenu ses droits." 0 0 0 ' + for ml in old_mls: + arg += u'"%s" " " "" ' % ml[1:] + annulmls, resultmls = dialog(arg) + if annulmls: resultmls = [] + del_mls = [] + for resml in resultmls: + if resml == '': continue + del_mls.append(resml.strip()) + + if del_mls: + db.services_to_restart('desabonner_ml', + map(lambda m: "%s$%s" % (adher.mail(), m), resultmls)) + + # On modifie ! if not isadm: - for key in droits_critiques: - if key in adher.droits(): - result.append(key.encode("ISO-8859-15")) adher.droits(result, light=True) else: adher.droits(result) diff --git a/gestion/ldap_crans.py b/gestion/ldap_crans.py index 0eddd459..1e9fa7db 100644 --- a/gestion/ldap_crans.py +++ b/gestion/ldap_crans.py @@ -2319,11 +2319,11 @@ class Adherent(BaseProprietaire): new = [] for droit in droits: + droit = droit.strip() if droit == '': continue - droit = unicode(droit.strip(), 'iso-8859-15') if droit not in droits_possibles: raise ValueError(u'Droit %s incorrect' % droit) - new.append(droit.encode('utf-8')) + new.append(droit) ancien = self._data.get('droits', []) # On envoie les mails de "bienvenue" pour chaque nouveau droit