[config,{gest,ldap}_crans,gen_confs/] Modifications systeme droits

* gest_crans.py :: Dialogue permettant de desabonner les gens des MLs lorsqu'on
leur enleve des droits (les gens ne sont pas desabonnes automatiquement de
certaines MLs a l'enlevement des droits, il propose de le faire quand meme)
=> gest_crans.complexite++
 * config.py :: Deplacement des definitions droits <-> {MLs, groupes unix}
depuis droits.py
 * droits.py :: Ajout de fonctionnalites :
  * -d pour supprimer certaines adresses mail de certaines MLs
  * -u pour afficher les MLs inscrites non-automatiquement a certaines MLs
 * ldap_crans.py :: Les droits ne contiennent plus d'accents depuis un moment

darcs-hash:20100319001432-ddb99-f0d337d161b8d95e6b6300cd53efbae8b9a7e834.gz
This commit is contained in:
Michel Blockelet 2010-03-19 01:14:32 +01:00
parent 61302647c9
commit 392006237d
5 changed files with 261 additions and 107 deletions

View file

@ -37,6 +37,40 @@ club_login_shell='/usr/bin/rssh'
# Longueur maximale d'un login # Longueur maximale d'un login
maxlen_login=15 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 ## Répertoire de stockage des objets détruits
cimetiere = '/home/cimetiere' cimetiere = '/home/cimetiere'

View file

@ -1,10 +1,12 @@
#! /usr/bin/env python #! /usr/bin/env python
# -*- coding: iso-8859-15 -*- # -*- coding: utf-8 -*-
import sys, signal, os import sys, signal, os
import getopt
# Imports pour LDAP # Imports pour LDAP
sys.path.append('/usr/scripts/gestion') 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 gen_confs import gen_config, anim, cprint, OK, ERREUR
from ldap_crans import crans_ldap, preattr, ldap, CransLdap, strip_accents from ldap_crans import crans_ldap, preattr, ldap, CransLdap, strip_accents
@ -14,73 +16,39 @@ try:
from Mailman import Utils from Mailman import Utils
from Mailman.UserDesc import UserDesc from Mailman.UserDesc import UserDesc
except: except:
# Machine sans mailman, les ML ne seront pas reconfigurées # Machine sans mailman, les ML ne seront pas reconfigurées
pass pass
class droits(gen_config) : class droits(gen_config) :
def restart(s) : def restart(s) :
# Rien à faire # Rien à faire
pass pass
def __str__(s): def __str__(s):
return "droits" return "droits"
class droits_ldap(CransLdap, droits): class droits_ldap(CransLdap, droits):
####### Les groupes ####### Les groupes
base_group_dn = 'ou=Group,dc=crans,dc=org' 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) : def build_group(self) :
""" Reconstruit les groupes dans la base LDAP """ """ Reconstruit les groupes dans la base LDAP """
self.anim.iter = len( self.groupes.keys() ) self.anim.iter = len(droits_groupes.keys() )
for group, fonctions in self.groupes.items() : for group, fonctions in droits_groupes.items() :
self.anim.cycle() self.anim.cycle()
# Qui doit être dans ce groupe ? # Qui doit être dans ce groupe ?
res = [] res = []
for f in fonctions : for f in fonctions :
res += self.search('droits=%s' % f)['adherent'] 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) dn = 'cn=%s,%s' % (group, self.base_group_dn)
data = self.conn.search_s(dn ,0,'objectClass=posixGroup')[0][1] data = self.conn.search_s(dn ,0,'objectClass=posixGroup')[0][1]
init_data = data.copy() init_data = data.copy()
# Supression de tout les membres # Supression de tout les membres
data['memberUid'] = [] data['memberUid'] = []
# Ajout des bonnes personnes # Ajout des bonnes personnes
for adher in res : for adher in res :
if not adher.droitsGeles(): if not adher.droitsGeles():
@ -96,7 +64,7 @@ class droits_ldap(CransLdap, droits):
# Ajout de michel et webradio # Ajout de michel et webradio
data['memberUid'].append('michel') data['memberUid'].append('michel')
data['memberUid'].append('webradio') data['memberUid'].append('webradio')
# Sauvegarde # Sauvegarde
modlist = ldap.modlist.modifyModlist(init_data,data) modlist = ldap.modlist.modifyModlist(init_data,data)
self.conn.modify_s(dn,modlist) self.conn.modify_s(dn,modlist)
@ -105,32 +73,57 @@ class droits_ldap(CransLdap, droits):
""" Donne la liste des membres actifs """ """ Donne la liste des membres actifs """
for adh in self.search('droits=%s&chbre!=EXT' % poste)['adherent'] : for adh in self.search('droits=%s&chbre!=EXT' % poste)['adherent'] :
print "%s %s" % (adh.nom(), adh.prenom()) print "%s %s" % (adh.nom(), adh.prenom())
def sync_ML(self) : def config_ML(self, mode='autosync', args=None) :
self.anim.iter = len( self.mailing_listes.keys() ) """ Reconfigure les MLs.
for ML, fonctions in self.mailing_listes.items() : mode désigne le mode de fonctionnement :
self.anim.cycle() * 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] == '+' : if ML[0] == '+' :
ML = ML[1:] ML = ML[1:]
only_add = 1 only_add = 1
else : else :
only_add = 0 only_add = 0
# Instance correspondant à la ML # Instance correspondant à la ML
mlist = MailList.MailList(ML) mlist = MailList.MailList(ML)
self.mlist_to_unlock = mlist self.mlist_to_unlock = mlist
# Qui doit être dans cette ML ? # Qui doit être dans cette ML ?
res = [] res = []
for f in fonctions : for f in fonctions :
res += self.search('droits=%s' % f)['adherent'] 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 } deja_inscrits = {} # { email en miniscules : email avec case n'importe comment }
for addr in mlist.getMemberCPAddresses(mlist.getMembers()): for addr in mlist.getMemberCPAddresses(mlist.getMembers()):
deja_inscrits[addr.lower()] = addr deja_inscrits[addr.lower()] = addr
# Mails à ajouter # Mails à ajouter
to_add = [] to_add = []
mail_traite = [] mail_traite = []
for adher in res : for adher in res :
@ -142,24 +135,40 @@ class droits_ldap(CransLdap, droits):
# Visiblement pas inscrit # Visiblement pas inscrit
to_add.append([ mail, adher.Nom() ]) to_add.append([ mail, adher.Nom() ])
else : else :
# L'adhérent est déja inscrit # L'adhérent est déja inscrit
deja_inscrits.pop(mail) deja_inscrits.pop(mail)
# Ajout if mode == 'autosync' or mode == 'forcedel':
for mail, nom in to_add : # Ajout
pw = Utils.MakeRandomPassword() for mail, nom in to_add :
userdesc = UserDesc(mail, strip_accents(nom), pw) pw = Utils.MakeRandomPassword()
mlist.ApprovedAddMember(userdesc) userdesc = UserDesc(mail, strip_accents(nom), pw)
if not only_add : mlist.ApprovedAddMember(userdesc)
# Supression des personnes inscritees en trop if not only_add or mode == 'forcedel':
for mail in deja_inscrits.values() : # Supression des personnes inscritees en trop
mlist.ApprovedDeleteMember(mail) if not only_add or (mode == 'forcedel' and args != None
and (ML, 'all') in args):
mlist.Save() 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() mlist.Unlock()
self.mlist_to_unlock = None self.mlist_to_unlock = None
def gen_conf(self) : if mode == 'getunsync':
return unsync
def gen_conf(self):
self.anim = anim('\tconfiguration groupes') self.anim = anim('\tconfiguration groupes')
try: try:
self.build_group() self.build_group()
@ -168,46 +177,64 @@ class droits_ldap(CransLdap, droits):
except: except:
self.anim.reinit() self.anim.reinit()
print ERREUR print ERREUR
if self.debug : if self.debug :
import traceback import traceback
traceback.print_exc() traceback.print_exc()
self.anim = anim('\tconfiguration ML Crans') self.anim = anim('\tconfiguration ML Crans')
try: try:
self.sync_ML() self.config_ML()
self.anim.reinit() self.anim.reinit()
print OK print OK
except: except:
self.anim.reinit() self.anim.reinit()
print ERREUR print ERREUR
if self.debug : if self.debug :
import traceback import traceback
traceback.print_exc() traceback.print_exc()
try: try:
# Au cas où... # Au cas où...
self.mlist_to_unlock.Unlock() self.mlist_to_unlock.Unlock()
except : except :
pass 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) : class droits_openbsd(droits) :
def build_master_passwd_group(self) : 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 = "" master = ""
group = "wheel:*:0:root" group = "wheel:*:0:root"
self.anim.iter = 2 self.anim.iter = 2
for fonction in ("Nounou", "Apprenti"): for fonction in ("Nounou", "Apprenti"):
self.anim.cycle() self.anim.cycle()
# Qui doit être dans ce groupe ? # Qui doit être dans ce groupe ?
res = crans_ldap().search('droits=%s' % fonction)['adherent'] res = crans_ldap().search('droits=%s' % fonction)['adherent']
for a in res: for a in res:
if fonction == "Nounou": if fonction == "Nounou":
# On rajoute à /etc/group # On rajoute à /etc/group
group = "%s,%s" % (group, a._data['uid'][0]) group = "%s,%s" % (group, a._data['uid'][0])
#On conserve le shell #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) #/usr/bin/zsh)
if "zsh" in a._data['loginShell'][0]: if "zsh" in a._data['loginShell'][0]:
shell = "/bin/zsh" shell = "/bin/zsh"
@ -225,26 +252,26 @@ class droits_openbsd(droits) :
shell) shell)
group = "%s\n" % group group = "%s\n" % group
# On va réécrire /etc/master.passwd # On va réécrire /etc/master.passwd
# cf man master.passwd # cf man master.passwd
fichier = file("/etc/master.passwd") fichier = file("/etc/master.passwd")
for line in fichier: for line in fichier:
if line.split(":")[4].strip() != "ldap": if line.split(":")[4].strip() != "ldap":
master = "%s%s" % (line,master) master = "%s%s" % (line,master)
fichier.close() fichier.close()
# On va écrire par-dessus # On va écrire par-dessus
fichier = file("/etc/master.passwd", "w") fichier = file("/etc/master.passwd", "w")
fichier.write(master) fichier.write(master)
fichier.close() fichier.close()
os.system("pwd_mkdb -p /etc/master.passwd") os.system("pwd_mkdb -p /etc/master.passwd")
# On réécrit /etc/group # On réécrit /etc/group
fichier = file("/etc/group") fichier = file("/etc/group")
for line in fichier: for line in fichier:
if line.split(":")[0].strip() != "wheel": if line.split(":")[0].strip() != "wheel":
group = "%s%s" % (group,line) group = "%s%s" % (group,line)
fichier.close() fichier.close()
# On va réécrire par-dessus # On va réécrire par-dessus
fichier = file("/etc/group", "w") fichier = file("/etc/group", "w")
fichier.write(group) fichier.write(group)
fichier.close() fichier.close()
@ -262,16 +289,52 @@ class droits_openbsd(droits) :
import traceback import traceback
traceback.print_exc() 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 __name__ == '__main__' :
if '-h' in sys.argv or '--help' in sys.argv or len(sys.argv) == 1 : try:
print "%s <switch>" % sys.argv[0].split('/')[-1].split('.')[0] opts, args = getopt.getopt(sys.argv[1:], "dhpu", [])
print "Génération de la liste des personnes avec le(s) droit(s) donné(s)." except:
sys.exit(255) usage()
sys.exit()
cl = droits()
for arg in sys.argv[1:] : if len(opts) == 0 or '-h' in sys.argv[1:]:
titre = "%s : " % arg usage()
print titre sys.exit()
print "-" * len (titre)
cl.print_liste(unicode(arg,'iso-8859-1')) 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 "")

View file

@ -40,7 +40,7 @@ make_lock('auto_generate', 'Big lock', nowait=1)
class base_reconfigure: class base_reconfigure:
__service_develop = { __service_develop = {
'macip': [ 'rouge-macip', 'zamok-macip', 'sable-macip', 'komaz-macip', 'gordon-macip' ], '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_upload': [ 'sable-blacklist_upload', 'komaz-blacklist' ],
'blacklist_p2p': [ 'sable-blacklist_p2p', 'komaz-blacklist' ], 'blacklist_p2p': [ 'sable-blacklist_p2p', 'komaz-blacklist' ],
'blacklist_autodisc_upload': [ 'sable-blacklist_autodisc_upload', '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 from gen_confs.droits import droits_ldap
self._do(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): def ML_ens(self, mails):
from adherents import ML_ens from adherents import ML_ens
self._do(ML_ens(mails)) self._do(ML_ens(mails))

View file

@ -564,13 +564,64 @@ def set_droits(adher):
annul, result = dialog(arg) annul, result = dialog(arg)
if annul: return 1 if annul: return 1
### Traitement
# Traitement # Dans le cas où l'utilisateur qui modifie n'est pas nounou, on ajoute les
# Dans le cas ou l'utilisateur qui modiifie n'est pas nounou # 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: 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) adher.droits(result, light=True)
else: else:
adher.droits(result) adher.droits(result)

View file

@ -2319,11 +2319,11 @@ class Adherent(BaseProprietaire):
new = [] new = []
for droit in droits: for droit in droits:
droit = droit.strip()
if droit == '': continue if droit == '': continue
droit = unicode(droit.strip(), 'iso-8859-15')
if droit not in droits_possibles: if droit not in droits_possibles:
raise ValueError(u'Droit %s incorrect' % droit) raise ValueError(u'Droit %s incorrect' % droit)
new.append(droit.encode('utf-8')) new.append(droit)
ancien = self._data.get('droits', []) ancien = self._data.get('droits', [])
# On envoie les mails de "bienvenue" pour chaque nouveau droit # On envoie les mails de "bienvenue" pour chaque nouveau droit