diff --git a/gestion/gest_crans_lc.py b/gestion/gest_crans_lc.py index 5ae0aa6b..a740c8f9 100755 --- a/gestion/gest_crans_lc.py +++ b/gestion/gest_crans_lc.py @@ -24,7 +24,7 @@ import tempfile import collections sys.path.append('/usr/scripts/') from pythondialog import Dialog -from pythondialog import DialogError +from pythondialog import DialogError, DialogTerminatedBySignal from OpenSSL import crypto, SSL from gestion.cert_utils import createCertRequest @@ -205,9 +205,14 @@ class GestCrans(object): def check_ldap(self): self.check_ldap_last = time.time() - # On ouvre une connexion lc_ldap - self.conn = lc_ldap.shortcuts.lc_ldap_admin() - #self.conn.current_login = 'totocrans' + + # S'il y a --test dans les argument, on utilise la base de test + if '--test' in sys.argv[1:]: + self.conn = lc_ldap.shortcuts.lc_ldap_test() + else: + # On ouvre une connexion lc_ldap + self.conn = lc_ldap.shortcuts.lc_ldap_admin() + # On vérifie que l'utilisateur système existe dans ldap (pour la gestion des droits) luser=self.conn.search(u'(&(uid=%s)(objectClass=cransAccount))' % self.conn.current_login) if not luser: @@ -216,6 +221,25 @@ class GestCrans(object): self.conn.droits = [str(d) for d in luser[0]['droits']] self.conn.dn = luser[0].dn + # Si un nom d'utilisateur est donné sur la ligne de commande + # et qu'on a les droits nounou, on l'utilise + if sys.argv[1:] and attributs.nounou in self.conn.droits: + for u in sys.argv[1:]: + luser=self.conn.search(u'(&(uid=%s)(objectClass=cransAccount))' % u) + if luser: + self.conn.current_login = u + self.conn.droits = [str(d) for d in luser[0]['droits']] + self.conn.dn = luser[0].dn + break + a = attributs + allowed_right = [a.cableur, a.tresorier, a.bureau, a.nounou, a.imprimeur] + for droit in allowed_right: + if droit in self.conn.droits: + break + else: + sys.stderr.write(u"%s ne possède aucun des droits :\n * %s\nnécessaire à utiliser ce programme\n" % (self.conn.current_login, '\n * '.join(allowed_right))) + sys.exit(1) + _dialog = None @property def dialog(self): @@ -237,14 +261,19 @@ class GestCrans(object): signal.signal(signal.SIGINT, signal.SIG_IGN) # Pas de Ctrl-C except KeyboardInterrupt: signal.signal(signal.SIGINT, signal.SIG_IGN) # Pas de Ctrl-C - ctrlC = True - finally: - if ctrlC: + raise Continue(cancel_cont) + except DialogTerminatedBySignal as e: + signal.signal(signal.SIGINT, signal.SIG_IGN) # Pas de Ctrl-C + if e[1] == 11: + self.dialog.msgbox("La fenêtre dialog à été fermée par une erreur de segmentation", timeout=self.timeout, title="Erreur rencontrée", width=73, height=10) raise Continue(cancel_cont) - elif ret: + else: + raise + finally: + if ret: return ret else: - EnvironmentError("Ni Ctrl+C ni ret ?!? c'est pas possible ") + EnvironmentError("Pas de ret ?!? c'est pas possible ") @tailcaller def handle_dialog_result(self, code, output, cancel_cont, error_cont, codes_todo=[]): @@ -396,9 +425,9 @@ class GestCrans(object): debut = None fin = None comm = None - tag, bl = self.edit_blacklist_select(obj, title, cont) + tag, bl = self.edit_blacklist_select(obj, title, cont(**{update_obj:obj})) if bl_type is None and tag == 'new': - bl['type'] = self.edit_blacklist_type(title, self_cont) + bl['type'] = self.edit_blacklist_type(title, self_cont(obj=obj)) elif tag == 'new': bl['type'] = bl_type @@ -424,23 +453,23 @@ class GestCrans(object): obj['blacklist'].append(bl) obj.save() # On s'en va en mettant à jour dans la continuation la valeur de obj - raise Continue(cont(**{update_obj:obj})) + raise Continue(self_cont(bl=None, obj=obj)) # On propage les Continue except self.error_to_raise: raise # En cas d'une autre erreur, on l'affiche et on retourne except (Exception, ldap.OBJECT_CLASS_VIOLATION) as e: self.dialog.msgbox("%s" % unicode_of_Error(e), timeout=self.timeout, title="Erreur rencontrée", width=73) - raise Continue(self_cont) + raise Continue(self_cont(obj=obj)) else: - raise Continue(self_cont(bl=None)) + raise Continue(self_cont(bl=None, obj=obj)) # Cas de l'édition d'une blacklist else: if debut is None: # Mettre un warning pour éditer (seulement quand debut vaut None pour ne pas le répéter à chaque fois que l'on revient en arrière par la suite if not self.confirm_item(item=attributs.attrify(bl, 'blacklist', self.conn), title="Éditer la blackliste ?"): - raise Continue(self_cont(bl=None)) + raise Continue(self_cont(bl=None, obj=obj)) debut = time.localtime(bl['debut']) debut_tuple = self.get_timestamp(title=title, text="Choisir le début de la blacklist", cont=self_cont(bl=bl, tag=tag, debut=None, fin=None, comm=None), day=debut.tm_mday, @@ -477,7 +506,7 @@ class GestCrans(object): obj['blacklist'][int(tag)]=bl obj.save() # On s'en va en mettant à jour dans la continuation la valeur de obj - raise Continue(cont(**{update_obj:obj})) + raise Continue(self_cont(bl=None, obj=obj)) # On propage les Continue except self.error_to_raise: raise @@ -486,7 +515,7 @@ class GestCrans(object): self.dialog.msgbox("%s" % unicode_of_Error(e), timeout=self.timeout, title="Erreur rencontrée", width=73) raise Continue(self_cont) else: - raise Continue(self_cont(bl=None)) + raise Continue(self_cont(bl=None, obj=obj)) @@ -1094,13 +1123,15 @@ les valeurs valident sont : ) @tailcaller - def get_password(self, cont, confirm=True): + def get_password(self, cont, confirm=True, title="Choix d'un mot de passe", **kwargs): def todo(self_cont, cont): - (code, pass1) = self.dialog.passwordbox("Entrez un mot de passe", title="Choix d'un mot de passe") + (code, pass1) = self.dialog.passwordbox("Entrez un mot de passe", title=title, timeout=self.timeout, **kwargs) if code != self.dialog.DIALOG_OK: raise Continue(cont) + elif not pass1: + raise ValueError("Mot de pass vide !") if confirm: - (code, pass2) = self.dialog.passwordbox("Comfirmer le mot de passe", title="Choix d'un mot de passe") + (code, pass2) = self.dialog.passwordbox("Comfirmer le mot de passe", timeout=self.timeout, title=title, **kwargs) if code != self.dialog.DIALOG_OK: raise Continue(self_cont) if pass1 != pass2: @@ -1146,7 +1177,7 @@ les valeurs valident sont : else: self.dialog.msgbox("Vous aller être inviter à entrez un mot de passe. Ce mot de passe est utilisé pour chiffrer la clef privée qui va être générée dans la base de donnée du crans.\n\nCe mot de passe n'est pas conservé, sous quelque forme que se soit par le crans.\nAussi, en cas de perte, la clef privée deviendrait inutilisable.\n Pensez à le sauvegarder quelque part", title="Génération d'une clée privée", - width=70, + width=70, timeout=self.timeout, height=12) passphrase = self.get_password(cont) self.dialog.infobox("Génération d'une clef privée RSA de taille %s en cours.\nMerci de patienter" % size) @@ -1161,14 +1192,14 @@ les valeurs valident sont : with self.conn.search(dn=certificat.dn, scope=0, mode='rw')[0] as certificat: certificat.private(pem, encrypted=True) certificat.save() - self.dialog.msgbox("Clef privée bien ajouté", title="Ajout d'une clef privée") + self.dialog.msgbox("Clef privée bien ajouté", timeout=self.timeout, title="Ajout d'une clef privée") raise Continue(cont(certificat=certificat, machine=certificat.machine())) else: with self.conn.newCertificat(machine.dn, {}) as certificat: certificat['hostCert']=unicode(machine['host'][0]) certificat.private(pem, encrypted=True) certificat.create() - self.dialog.msgbox("Clef privée créée avec succès", title="Création d'une clef privée") + self.dialog.msgbox("Clef privée créée avec succès", timeout=self.timeout, title="Création d'une clef privée") raise Continue(cont(certificat=certificat, machine=certificat.machine())) if imp: @@ -1205,17 +1236,6 @@ les valeurs valident sont : def gen_csr(self, certificat, cont): - def box(text): - fp, path = tempfile.mkstemp() - os.write(fp, text) - os.close(fp) - self.dialog.textbox(filename=path, height=0, width=0, - backtitle="Appuyez sur CTRL+MAJ+V pour coller", - title="Récupération d'un certificat", - no_mouse=True,) - os.remove(path) - return - def todo(certificat, self_cont, cont): if certificat['encrypted']: if "machineCrans" in certificat.machine()["objectClass"]: @@ -1224,7 +1244,7 @@ les valeurs valident sont : self.dialog.msgbox("Mercie de fournir le mot de passe chiffrant la clef privée.\nIl a été choisis lors de la création de la clef.", title="Génération d'un CSR", width=70, - height=10) + height=10, timeout=self.timeout) passphrase = self.get_password(cont, confirm=False) else: passphrase = None @@ -1236,7 +1256,7 @@ les valeurs valident sont : pkey = crypto.load_privatekey(crypto.FILETYPE_PEM, str(certificat['privatekey'][0])) except crypto.Error as e: if len(e.message) > 2 and len(e.message[2]) > 2 and e.message[2][2] == 'bad password read': - self.dialog.msgbox("Mauvais mot de passe") + self.dialog.msgbox("Mauvais mot de passe", timeout=self.timeout) raise Continue(self_cont) else: raise @@ -1256,7 +1276,7 @@ les valeurs valident sont : certificat['csr']=unicode(csr) certificat.save() self.handle_dialog(cont, box, csr) - if self.dialog.yesno("Remplacer le certificat actuel ?") == self.dialog.DIALOG_OK: + if self.dialog.yesno("Remplacer le certificat actuel ?", timeout=self.timeout) == self.dialog.DIALOG_OK: return self.create_certificat(certificat=certificat, cont=cont(certificat=certificat)) else: raise Continue(cont(certificat=certificat)) @@ -1278,7 +1298,7 @@ les valeurs valident sont : self.dialog.textbox(filename=path, height=0, width=0, backtitle="Appuyez sur CTRL+MAJ+V pour coller", title="Récupération d'un certificat", - no_mouse=True,) + no_mouse=True, timeout=self.timeout,) os.remove(path) return if privatekey: @@ -1343,6 +1363,21 @@ les valeurs valident sont : elif certificat_index == 'csr': raise Continue(TailCall(self.create_csr, machine=machine, cont=self_cont)) certificat = machine.certificats()[certificat_index] + a = attributs + menu_droits = { + 'Hostname':[a.parent, a.nounou], + 'AddPrivateKey':[a.parent, a.nounou], + 'AddCertificate':[a.parent, a.nounou], + 'SetCertificate':[a.parent, a.nounou], + 'TLSA':[a.parent, a.nounou], + 'Autre':[a.nounou], + 'GetPriv':[a.parent, a.nounou], + 'GetCert':[a.parent, a.nounou, a.cableur], + 'GetCSR':[a.parent, a.nounou, a.cableur], + 'GenCSR':[a.parent, a.nounou], + 'Remarque':[a.parent, a.nounou, a.cableur], + 'Supprimer':[a.parent, a.nounou], + } menu = { 'Hostname' : {'text':"Noms d'hôte utilisant le certificat", "help":'Il doivent être inclus dans les host et hostAlias de la machine parente', "attribut":attributs.hostCert}, 'AddPrivateKey' : {'text': 'Ajouter la clef privée', 'help':'La clef doit être obligatoirement chiffrée avant envoi', "callback":TailCall(self.create_privatekey, imp=True)}, @@ -1400,7 +1435,7 @@ les valeurs valident sont : scrollbar=True, cancel_label="Retour", backtitle=u"Vous êtes connecté en tant que %s" % self.conn.current_login, - choices=[(key, menu[key]['text'], menu[key]['help']) for key in menu_order]) + choices=[(key, menu[key]['text'], menu[key]['help']) for key in menu_order if self.has_right(menu_droits[key], certificat)]) def todo(tag, menu, certificat, self_cont): if not tag in menu_order: @@ -1425,11 +1460,25 @@ les valeurs valident sont : @tailcaller def edit_certificat_select(self, machine, title, cont): """Permet de choisir un certificat existant ou nouveau d'une machine""" + a = attributs + menu_droits = { + 'new':[a.parent, a.nounou], + 'priv':[a.parent, a.nounou], + 'csr':[a.parent, a.nounou], + } + menu = { + 'new':'Ajouter un nouveau certificat', + 'priv':'Générer une nouvelle clef privée', + 'csr':'Ajouter une nouvelle requête de certificat', + } + menu_order = ['new', 'priv', 'csr'] + menu_special = ['new', 'priv', 'csr'] def box(default_item=None): index=0 - choices = [('new', 'Ajouter un nouveau certificat')] - choices.append(('priv', 'Générer une nouvelle clef privée')) - choices.append(('csr', 'Ajouter une nouvelle requête de certificat')) + choices = [] + for key in menu_order: + if self.has_right(menu[key], machine): + choices.append((key, menu[key])) for cert in machine.certificats(): if cert['info']: item = str(cert['info'][0]) @@ -1479,6 +1528,10 @@ les valeurs valident sont : """Juste un raccourci vers edit_attributs spécifique aux adherents""" return self.edit_attributs(obj=adherent, update_obj='adherent', attr=attr, title="Modification de %s %s" % (adherent['prenom'][0], adherent['nom'][0]), cont=cont) + def modif_proprio_attributs(self, proprio, attr, cont): + """Juste un raccourci vers edit_attributs spécifique aux proprios""" + return self.edit_attributs(obj=proprio, update_obj='proprio', attr=attr, title="Modification de %s %s" % (proprio.get('prenom', [''])[0], proprio['nom'][0]), cont=cont) + def modif_certificat_attributs(self, certificat, attr, cont): """Juste un raccourci vers edit_attributs spécifique aux certificats""" return self.edit_attributs(obj=certificat, update_obj='certificat', attr=attr, title="Modification d'un certificat de la machine %s" % certificat.machine()['host'][0], cont=cont) @@ -1513,6 +1566,17 @@ les valeurs valident sont : if machine is None: machine = self.select(["machineFixe", "machineWifi", "machineCrans", "borneWifi"], "Recherche d'une machine pour modification", cont=cont) a = attributs + menu_droits = { + 'Information' : [a.parent, a.cableur, a.nounou], + 'Autre': [a.parent, a.cableur, a.nounou], + 'Blackliste':[a.cableur, a.nounou], + 'Certificat': [a.parent, a.cableur, a.nounou], + 'Exemption' : [a.nounou], + 'Alias' : [a.parent, a.cableur, a.nounou], + 'Remarques' : [a.cableur, a.nounou], + 'SshKey' : [a.parent, a.nounou], + 'Supprimer' : [a.parent, a.cableur, a.nounou], + } menu = { 'Information' : {'text' : "Modifier le nom de machine, l'IP, adresse MAC", "callback":self.machine_information}, 'Autre' : {'text' : "Modifier les attribut booléen comme dsnIpv6", "callback":self.modif_machine_boolean}, @@ -1522,8 +1586,9 @@ les valeurs valident sont : 'Alias' : {'text': 'Créer ou supprimer un alias de la machine', 'attribut':attributs.hostAlias}, 'Remarques' : {'text':'Ajouter ou supprimer une remarque de la machine', 'attribut':attributs.info}, 'SshKey' : {'text':'Ajouter ou supprimer une clef ssh pour la machine', 'attribut':attributs.sshFingerprint}, + 'Supprimer' : {'text':'Supprimer la machine', 'callback':self.delete_machine}, } - menu_order = ['Information', 'Blackliste', 'Certificat', 'Alias', 'Exemption', 'SshKey', 'Autre', 'Remarques'] + menu_order = ['Information', 'Blackliste', 'Certificat', 'Alias', 'Exemption', 'SshKey', 'Autre', 'Remarques', 'Supprimer'] def box(default_item=None): return self.dialog.menu( "Que souhaitez vous modifier ?", @@ -1537,7 +1602,7 @@ les valeurs valident sont : scrollbar=True, cancel_label="Retour", backtitle=u"Vous êtes connecté en tant que %s" % self.conn.current_login, - choices=[(key, menu[key]['text']) for key in menu_order]) + choices=[(key, menu[key]['text']) for key in menu_order if self.has_right(menu_droits[key], machine)]) def todo(tag, menu, machine, cont_ret): if not tag in menu_order: @@ -1562,12 +1627,22 @@ les valeurs valident sont : ) def create_machine_proprio(self, cont, proprio, tag=None): + a = attributs + menu_droits = { + 'Fixe' : [a.soi, a.cableur, a.nounou], + 'Wifi' : [a.soi, a.cableur, a.nounou], + } menu = { 'Fixe' : {'text' : "Machine filaire", 'objectClass':'machineFixe', 'realm':'adherents'}, 'Wifi' : {'text': 'Machine sans fil', 'objectClass':'machineWifi', 'realm':'wifi-adh'}, } menu_order = ['Fixe', 'Wifi'] if isinstance(proprio, objets.AssociationCrans): + menu_droits.update({ + 'Fixe' : [a.nounou], + 'Wifi' : [a.nounou], + 'Adm' : [a.nounou], + }) menu.update({ 'Fixe' : {'text' : "Ajouter un serveur sur le vlan adherent", 'objectClass':'machineCrans', 'realm':'serveurs'}, 'Wifi' : {'text': 'Ajouter une borne WiFi sur le vlan wifi', 'objectClass':'borneWifi', 'realm':'bornes'}, @@ -1587,7 +1662,7 @@ les valeurs valident sont : scrollbar=True, cancel_label="Retour", backtitle=u"Vous êtes connecté en tant que %s" % self.conn.current_login, - choices=[(key, menu[key]['text']) for key in menu_order]) + choices=[(key, menu[key]['text']) for key in menu_order if self.has_right(menu_droits[key], proprio)]) def todo(tag, menu, proprio, self_cont, cont): if not tag in menu_order: @@ -1644,30 +1719,53 @@ les valeurs valident sont : codes_todo=[([self.dialog.DIALOG_OK], todo, [machine])] ) - def modif_adherent(self, cont, adherent=None, tag=None): + def modif_adherent_blacklist(self, adherent, cont): + """Raccourci vers edit_blacklist spécifique aux adherent""" + return self.edit_blacklist(obj=adherent, title="Éditions des blacklist de %s %s" % (adherent['prenom'][0], adherent['nom'][0]), update_obj='adherent', cont=cont) + + + def modif_adherent(self, cont, adherent=None, proprio=None, tag=None): if adherent is None: adherent = self.select(["adherent"], "Recherche d'un adhérent pour modification", cont=cont) a = attributs + menu_droits = { + 'Administratif' : [a.cableur, a.nounou], + 'Personnel':[a.cableur, a.nounou, a.soi], + 'Études':[a.nounou, a.soi, a.cableur], + 'Chambre':[a.cableur, a.nounou], + 'Compte':[a.cableur, a.nounou], + 'GPGFingerprint' : [a.nounou, a.soi], + 'Remarques' : [a.cableur, a.nounou], + 'Droits':[a.nounou, a.bureau], + 'Blackliste':[a.cableur, a.nounou], + 'Vente':[a.cableur, a.nounou], + 'Supprimer':[a.nounou, a.bureau], + } menu = { 'Administratif' : {'text' : "Adhésion, carte étudiant, chartes", "callback":self.adherent_administratif}, 'Personnel' : {'text' : "Nom, prénom, téléphone... (ajouter l'age ?)", 'callback':self.adherent_personnel}, 'Études' : {'text' : "Étude en cours (perso, je pense que c'est à supprimer)", "callback":self.adherent_etudes}, 'Chambre' : {'text' : 'Déménagement', "callback":self.adherent_chambre}, 'Compte' : {'text' : "Gestion du compte crans", "adherent":"proprio", "callback":self.proprio_compte, 'help':"Création/Suppression/Activation/Désactivation du compte, gestion des alias mails crans du compte"}, - #'Mail' :{'text' : "adresse mail de contact (alternative si compte crans)", "adherent":"proprio", "callback":self.proprio_mail}, - #'Alias' : {'text': 'Créer ou supprimer un alias de la machine', 'attribut':attributs.mailAlias}, 'GPGFingerprint' : {'text':'Ajouter ou supprimer une empeinte GPG', 'attribut':attributs.gpgFingerprint}, 'Remarques' : {'text':'Ajouter ou supprimer une remarque de la machine', 'attribut':attributs.info}, 'Droits' : {'text':"Modifier les droits alloués à cet adhérent", "callback":self.adherent_droits}, - 'Blackliste' : {'text': 'Modifier les blacklist de la machine', "adherent":"proprio", 'callback':self.modif_proprio_blacklist}, + 'Blackliste' : {'text': 'Modifier les blacklist de la machine', 'callback':self.modif_adherent_blacklist}, 'Vente' : {'text':"Chargement solde crans, vente de cable ou adaptateur ethernet ou autre", "adherent":"proprio", "callback":self.proprio_vente}, + 'Supprimer' : {'text':"Supprimer l'adhérent de la base de donnée", 'callback':self.delete_adherent}, } - menu_order = ['Administratif', 'Personnel', 'Études', 'Chambre', 'Compte', 'GPGFingerprint', 'Remarques', 'Blackliste', 'Vente'] + menu_order = ['Administratif', 'Personnel', 'Études', 'Chambre', 'Compte'] menu_compte_crans = ['Droits'] + medu_end = ['GPGFingerprint', 'Remarques', 'Blackliste', 'Vente', 'Supprimer'] if "cransAccount" in adherent['objectClass']: menu_order.extend(menu_compte_crans) + menu_order.extend(medu_end) def box(default_item=None): + choices = [] + for key in menu_order: + if self.has_right(menu_droits[key], adherent): + choices.append((key, menu[key]['text'], menu[key].get('help', ""))) return self.dialog.menu( "Que souhaitez vous modifier ?", width=0, @@ -1680,7 +1778,7 @@ les valeurs valident sont : scrollbar=True, cancel_label="Retour", backtitle=u"Vous êtes connecté en tant que %s" % self.conn.current_login, - choices=[(key, menu[key]['text'], menu[key].get('help', "")) for key in menu_order]) + choices=choices) def todo(tag, menu, adherent, cont_ret): if not tag in menu_order: @@ -1708,22 +1806,24 @@ les valeurs valident sont : self.dialog.msgbox("todo", width=0, height=0) return cont - def adherent_personnel(self, cont, adherent=None, uldif={}, fields_values=None, make_compte_crans=None, force_create=False): + def adherent_personnel(self, cont, adherent=None, fields_attrs={}, make_compte_crans=None, force_create=False): """ Permet d'éditer les nom, prénom et téléphone d'un adhérent, ou de créer un adhérent. Il faut encore trouver un moyen de récupérer des valeurs pour les attributs mail et chbre """ a = attributs - # Quel sont les attributs ldap dont on veut afficher et la taille du champs d'édition correspondant + # Quel sont les attributs ldap dont on veut afficher et + # la taille du champs d'édition correspondant to_display = [(a.nom, 30), (a.prenom, 30), (a.tel, 30), (a.mail, 30)] + non_empty = [a.nom, a.prenom, a.tel] input_type = {'default':0} # Quel séparateur on utilise pour les champs multivalué separateur = ' ' def box(make_compte_crans): - if force_create and adherent is None and fields_values and make_compte_crans is not None: - return (self.dialog.DIALOG_OK, [t[1] for t in fields_values], make_compte_crans) + if force_create and adherent is None and fields_attrs and make_compte_crans is not None: + return (self.dialog.DIALOG_OK, [fields_attrs[a] for a,l in to_display], make_compte_crans) if adherent: attrs = dict((k,[str(a) for a in at]) for k,at in adherent.items()) if 'cransAccount' in adherent['objectClass']: @@ -1732,20 +1832,25 @@ les valeurs valident sont : else: attrs = {} if make_compte_crans is None: - if self.dialog.yesno("Crééra-t-on un compte crans à l'utilisateur ?", title="Création d'un adhérent", width=50) == self.dialog.DIALOG_OK: + if self.dialog.yesno("Crééra-t-on un compte crans à l'utilisateur ?", timeout=self.timeout, title="Création d'un adhérent", width=50) == self.dialog.DIALOG_OK: input_type[attributs.mail] = 2 make_compte_crans = True to_display.append((attributs.mailExt, 30)) else: make_compte_crans = False - fields = [("%s :" % a.legend, separateur.join(attrs.get(a.ldap_name, [a.default] if a.default else [])), l+1, l, input_type.get(a, input_type['default'])) for a,l in to_display] + fields = [( + "%s %s:" % (a.legend, '(optionnel) ' if a.optional else ''), + fields_attrs.get(a, separateur.join(attrs.get(a.ldap_name, [a.default] if a.default else []))), + l+1, l, + input_type.get(a, input_type['default']) + ) for a,l in to_display] (code, tags) = self.dialog.form( text="", timeout=self.timeout, height=0, width=0, form_height=0, - fields=fields_values if fields_values else fields, + fields=fields, title="Création d'un adhérent" if adherent is None else "Édition des informations de %s %s" % (adherent['prenom'][0], adherent['nom'][0]), backtitle="Gestion des adhérents du Crans") return (code, tags, make_compte_crans) @@ -1758,24 +1863,34 @@ les valeurs valident sont : adherent.save() return adherent - def create_adherent(attrs, make_compte_crans, force_create, uldif, self_cont, cont): + def create_adherent(attrs, make_compte_crans, force_create, self_cont, cont): if not force_create: items = self.conn.search("(&(prenom=%s)(nom=%s))" % (attrs['prenom'], attrs['nom'])) if items: newadherent = self.select_one(items, title="Choisir un adhérant existant", text="Des adhérent avec les même noms et prénoms existent déjà, en utiliser un ?\n(Annuler pour continuer la création)", cont=self_cont(make_compte_crans=make_compte_crans, force_create=True)) raise Continue(cont(adherent=newadherent)) - with self.conn.newAdherent(uldif) as adherent: + with self.conn.newAdherent({}) as adherent: + delay={} for (key, values) in attrs.items(): - adherent[key]=values - # Si compte crans à créer, on le crée - if make_compte_crans: - self.dialog.msgbox("todo", width=0, height=0) - return None - # Sinon, on récupère la chambre + try: + adherent[key]=values + # En cas d'erreur, on a peut être besoin du compte crans + except ValueError: + delay[key]=values + print delay + # on récupère la chambre adherent = self.adherent_chambre_campus(success_cont=None, cont=self_cont(make_compte_crans=make_compte_crans), adherent=adherent, create=True) # Si c'est EXT, on demande une adresse complète if 'EXT' in adherent['chbre']: adherent = self.adherent_chambre_ext(keep_machine=True, keep_compte=True, success_cont=None, cont=self_cont(make_compte_crans=make_compte_crans), adherent=adherent, create=True) + # Si compte crans à créer, on le crée. + # On le met en dernier pour éviter de faire entrez plusieurs fois son mdp à l'adhérent + # en cas d'erreur de la part du cableur + if make_compte_crans: + adherent = self.proprio_compte_create(proprio=adherent, cont=self_cont(make_compte_crans=None, force_create=False, adherent=None), update_obj='adherent', return_obj=True) + # On réeaffecte les attributs de tout à l'heure + for (key, values) in delay.items(): + adherent[key]=values # On confirme la création if self.confirm_item(adherent, title="Créer l'adhérent suivant ?"): adherent.validate_changes() @@ -1784,10 +1899,12 @@ les valeurs valident sont : adherent = None return adherent - def todo(to_display, tags, adherent, uldif, separateur, make_compte_crans, force_create, cont, self_cont): + def todo(to_display, non_empty, tags, adherent, separateur, make_compte_crans, force_create, self_cont, cont): attrs = {} # On traite les valeurs reçues for ((a,l),values) in zip(to_display, tags): + if not values and a in non_empty: + raise ValueError("%s ne devrait pas être vide" % a.legend) values = unicode(values, 'utf-8') # Si le champs n'est pas single value, on utilise separateur pour découper # et on ne garde que les valeurs non vides @@ -1797,7 +1914,7 @@ les valeurs valident sont : if adherent: adherent = modif_adherent(adherent, attrs) else: - adherent = create_adherent(attrs, make_compte_crans, force_create, uldif, self_cont, cont) + adherent = create_adherent(attrs, make_compte_crans, force_create, self_cont, cont) raise Continue(cont(adherent=adherent)) @@ -1806,15 +1923,15 @@ les valeurs valident sont : # On prépare les fiels à afficher à l'utilisateur si une erreure à lieu # pendant le traitement des donnée (on n'éfface pas ce qui a déjà été entré # c'est au cableur de corriger ou d'annuler - fields_values = [("%s :" % a.legend, values, l) for ((a,l),values) in zip(to_display, tags)] - retry_cont = TailCall(self.adherent_personnel, adherent=adherent, cont=cont, uldif=uldif, fields_values=fields_values) + fields_attrs = dict((a, values) for ((a,l),values) in zip(to_display, tags)) + retry_cont = TailCall(self.adherent_personnel, adherent=adherent, cont=cont, fields_attrs=fields_attrs) return self.handle_dialog_result( code=code, output=tags, cancel_cont=cont, error_cont=retry_cont, - codes_todo=[([self.dialog.DIALOG_OK], todo, [to_display, tags, adherent, uldif, separateur, make_compte_crans, force_create, cont, retry_cont])] + codes_todo=[([self.dialog.DIALOG_OK], todo, [to_display, non_empty, tags, adherent, separateur, make_compte_crans, force_create, retry_cont, cont])] ) def adherent_etudes(self, adherent, cont): @@ -1828,7 +1945,7 @@ les valeurs valident sont : "chambre ?", title="%s de %s %s" % ("Création" if create else "Déménagement", adherent['prenom'][0], adherent["nom"][0]), cancel_label="Retour", - width=50, + width=50, timeout=self.timeout, ) def expulse_squatteur(adherent, chbre): @@ -1900,7 +2017,7 @@ les valeurs valident sont : if not create: if self.dialog.yesno("changer l'adresse de l'adhérent pour %s ?" % ", ".join([o for o in output if o]), title=u"Déménagement de %s %s" % (adherent['prenom'][0], adherent["nom"][0]), - defaultno=True) == self.dialog.DIALOG_OK: + defaultno=True, timeout=self.timeout) == self.dialog.DIALOG_OK: with self.conn.search(dn=adherent.dn, scope=0, mode='rw')[0] as adherent: adherent['postalAddress']=[unicode(pa, 'utf-8') for pa in output] adherent['chbre']=u'EXT' @@ -1944,7 +2061,7 @@ les valeurs valident sont : if adherent.get('solde', [0])[0] > 0: self.dialog.msgbox("Solde de l'adhérent %s€ strictement positif, impossible de supprimer le compte\nRepasser le solde à 0€ pour supprimer le compte." % adherent.get('solde', [0])[0], title=u"Déménagement de %s %s" % (adherent['prenom'][0], adherent["nom"][0]), - width=50) + width=50, timeout=self.timeout) raise Continue(cont) elif self.confirm_item( item=adherent, @@ -1958,10 +2075,14 @@ les valeurs valident sont : machine.delete() adherent['chbre']=u'EXT' adherent.save() - ### TODO SUpression du compte crans - self.dialog.msgbox("todo Supression du compte crans, lc_ldap ne sais pas encore faire", width=0, height=0) - self.display_item(item=adherent, title="Adhérent déménégé hors campus, machines et compte crans supprimées") - raise Continue(success_cont(adherent=adherent)) + # On supprime le compte crans (on essaye) + def post_deletion(proprio, cont): + if not "cransAccount" in proprio['objectClass']: + self.display_item(item=adherent, title="Adhérent déménégé hors campus, machines et compte crans supprimées") + else: + self.display_item(item=adherent, title="Adhérent déménégé hors campus, machines supprimées et compte crans concervé") + raise Continue(cont(adherent=proprio)) + self.proprio_compte_delete(proprio=adherent, cont=TailCall(post_deletion, proprio=adherent, cont=success_cont(adherent=adherent)), force=True) else: raise Continue(cont) else: @@ -1978,6 +2099,13 @@ les valeurs valident sont : ) def adherent_chambre(self, adherent, cont, default_item=None): + a = attributs + menu_droits = { + '0' : [a.cableur, a.nounou], + '1' : [a.cableur, a.nounou], + '2' : [a.cableur, a.nounou], + '3' : [a.cableur, a.nounou], + } menu = { "0": {'text':"Déménagement sur le campus", 'callback':self.adherent_chambre_campus, 'help': "Déménagement vers une chambre sur le campus, on ne demande que le bâtiment et la chambre"}, "1": {'text':"Déménagement à l'extérieur en conservant les machines", "callback": TailCall(self.adherent_chambre_ext, keep_machine=True, keep_compte=True), "help": "Pour concerver ses machines, il faut donner un adresse postale complète"}, @@ -1998,7 +2126,7 @@ les valeurs valident sont : scrollbar=True, cancel_label="Retour", backtitle=u"Vous êtes connecté en tant que %s" % self.conn.current_login, - choices=[(k, menu[k]['text'], menu[k]['help']) for k in menu_order]) + choices=[(k, menu[k]['text'], menu[k]['help']) for k in menu_order if self.has_right(menu_droits[key], adherent)]) def todo(tag, menu, adherent, self_cont, cont): if not tag in menu_order: @@ -2016,16 +2144,20 @@ les valeurs valident sont : codes_todo=[([self.dialog.DIALOG_OK], todo, [tag, menu, adherent, self_cont, cont])] ) - def proprio_compte_create(self, proprio, cont, warning=True, guess_login=True, guess_pass=0): - def todo(proprio, warning, guess_login, guess_pass, self_cont, cont): + @tailcaller + def proprio_compte_create(self, proprio, cont, warning=True, guess_login=True, guess_pass=0, return_obj=False, update_obj='proprio'): + def box_warning(warning, proprio, cont): # Affiche-t-on le warning sur la consutation de l'adresse crans if warning: if self.dialog.yesno( text="\Zr\Z1AVERTISSEMENT :\Zn \nL'adhérent devra impérativement consulter l'adresse mail associée\n\n\n\ZnContinuer ?", title="Création du compte de %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]), defaultno=True, - colors=True) != self.dialog.DIALOG_OK: + width=70, + colors=True, timeout=self.timeout) != self.dialog.DIALOG_OK: raise Continue(cont) + + def get_login(guess_login, guess_pass, proprio, self_cont, cont): # Essaye-t-on de deviner le login à utiliser if not guess_login: (code, login) = self.dialog.inputbox( @@ -2033,7 +2165,7 @@ les valeurs valident sont : title="Choix du login pour %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]), init=str(proprio['nom'][0]).lower(), width=60, - height=10) + height=10, timeout=self.timeout) if code != self.dialog.DIALOG_OK: raise Continue(cont) else: @@ -2045,36 +2177,62 @@ les valeurs valident sont : # Si toutes les manières ont échoués, la prochaine fois, ça on n'essaye pas de deviner else: raise Continue(self_cont(warning=False, guess_login=False, guess_pass=2)) - with self.conn.search(dn=proprio.dn, scope=0, mode='rw')[0] as proprio: - try: - proprio.compte(login=unicode(login, 'utf-8')) - except ValueError: - # Il y a eu une erreur, si on essaye de deviner, on essaye la manière suivante - if guess_login: - raise Continue(self_cont(warning=False, guess_login=True, guess_pass=guess_pass+1)) - # Sinon on propage l'erreur pour l'afficher à l'utilisateur - else: - raise - self.dialog.msgbox( - text="Le compte ne sera créé que lors de l'enregistrement des données\n\nL'adresse mail de l'adhérent est : %s\nL'adhérent possède également l'alias :\n%s\n" % (proprio['mail'][0], proprio['canonicalAlias'][0]), - title="Création du compte de %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]), - width=75, - height=12, - ) - if not self.confirm_item(item=proprio, title="Création du compte crans pour l'adhérent ?"): - raise Continue(cont) + return login + + def create_compte(proprio, login, guess_login, self_cont, cont): + try: + proprio.compte(login=unicode(login, 'utf-8')) + except ValueError: + # Il y a eu une erreur, si on essaye de deviner, on essaye la manière suivante + if guess_login: + raise Continue(self_cont(warning=False, guess_login=True, guess_pass=guess_pass+1)) + # Sinon on propage l'erreur pour l'afficher à l'utilisateur else: - proprio.save() - self.dialog.msgbox( - text="Compte créé avec succès.", - title="Création du compte de %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]), - ) - if self.dialog.yesno("Attribuer un mot de passe maintenant ?", - title="Création du compte de %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]), - ) == self.dialog.DIALOG_OK: - return self.proprio_compte_password(proprio=proprio, cont=cont(proprio=proprio)) + raise + self.dialog.msgbox( + text="Le compte ne sera créé que lors de l'enregistrement des données\n\nL'adresse mail de l'adhérent est : %s\nL'adhérent possède également l'alias :\n%s\n" % (proprio['mail'][0], proprio['canonicalAlias'][0]), + title="Création du compte de %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]), + width=75, + height=12, timeout=self.timeout, + ) + return proprio + + @tailcaller + def set_password(proprio, update_obj, cont): + if self.dialog.yesno("Attribuer un mot de passe maintenant ?", + title="Création du compte de %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]), + timeout=self.timeout + ) == self.dialog.DIALOG_OK: + #return self.proprio_compte_password(proprio=proprio, return_obj=return_obj, cont=cont(**{update_obj:proprio})) + proprio = self.proprio_compte_password(proprio=proprio, return_obj=True, cont=TailCall(set_password, proprio, update_obj, cont)) + if return_obj: + return proprio + else: + raise Continue(cont(**{update_obj:proprio})) + elif return_obj: + return proprio + else: + raise Continue(cont(**{update_obj:proprio})) + + def todo(proprio, warning, guess_login, guess_pass, return_obj, self_cont, cont): + box_warning(warning, proprio, cont) + login = get_login(guess_login, guess_pass, proprio, self_cont, cont) + if return_obj: + proprio = create_compte(proprio, login, guess_login, self_cont, cont) + return set_password(proprio, update_obj, cont) + else: + with self.conn.search(dn=proprio.dn, scope=0, mode='rw')[0] as proprio: + proprio = create_compte(proprio, login, guess_login, self_cont, cont) + if not self.confirm_item(item=proprio, title="Création du compte crans pour l'adhérent ?"): + raise Continue(cont) else: - raise Continue(cont(proprio=proprio)) + proprio.save() + self.dialog.msgbox( + text="Compte créé avec succès.", + title="Création du compte de %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]), + timeout=self.timeout + ) + return set_password(proprio, update_obj, cont) self_cont = TailCall(self.proprio_compte_create, proprio=proprio, cont=cont, warning=warning, guess_login=guess_login, guess_pass=guess_pass) return self.handle_dialog_result( @@ -2082,61 +2240,63 @@ les valeurs valident sont : output="", cancel_cont=cont, error_cont=self_cont, - codes_todo=[([self.dialog.DIALOG_OK], todo, [proprio, warning, guess_login, guess_pass, self_cont, cont])] + codes_todo=[([self.dialog.DIALOG_OK], todo, [proprio, warning, guess_login, guess_pass, return_obj, self_cont, cont])] ) - def proprio_compte_password(self, proprio, cont): - def box(): - return self.dialog.passwordform(text="Remplacer le mot de passe", - height=15, width=54, form_height=7, - fields=[("Mot de passe", "", 10, 20), - ("Confirmation", "", 10, 20)], - title="Choix du mot de passe pour %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]), - backtitle="Le mot de passe doit être assez difficile") + @tailcaller + def proprio_compte_password(self, proprio, cont, return_obj=False): - def todo(passwords, proprio, self_cont, cont): - (good, msg) = checkpass(passwords[0], dialog=True) + def test_password(password, self_cont): + (good, msg) = checkpass(password, dialog=True) if not good: self.dialog.msgbox( msg, title="Erreur dans le mot de passe de %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]), - colors=True) - raise Continue(self_cont) - elif passwords[0] != passwords[1]: - self.dialog.msgbox( - "Les deux mots de passes ne sont pas identiques", - title="Erreur dans le mot de passe de %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]), - colors=True) + colors=True, + width=70, timeout=self.timeout) raise Continue(self_cont) else: - with self.conn.search(dn=proprio.dn, scope=0, mode='rw')[0] as proprio: - proprio['userPassword']=unicode(lc_utils.hash_password(passwords[0])) - proprio.save() - self.dialog.msgbox( - "Mot de passe changé avec succès", - title="Choix du mot de passe pour %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]), - ) - raise Continue(cont(proprio=proprio)) - (code, passwords) = self.handle_dialog(cont, box) + return True + def todo(passwords, proprio, return_obj, self_cont, cont): + password = self.get_password(cont=cont, + title="Choix du mot de passe pour %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]), + backtitle="Le mot de passe doit être assez difficile") + + if test_password(password, self_cont): + if return_obj: + proprio['userPassword']=unicode(lc_utils.hash_password(password)) + return proprio + else: + with self.conn.search(dn=proprio.dn, scope=0, mode='rw')[0] as proprio: + proprio['userPassword']=unicode(lc_utils.hash_password(password)) + proprio.save() + self.dialog.msgbox( + "Mot de passe changé avec succès", + title="Choix du mot de passe pour %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]), + width=70, timeout=self.timeout + ) + raise Continue(cont(proprio=proprio)) + #(code, passwords) = self.handle_dialog(cont, box) + (code, passwords) = (self.dialog.DIALOG_OK, "") self_cont = TailCall(self.proprio_compte_password, proprio=proprio, cont=cont) return self.handle_dialog_result( code=code, output=passwords, cancel_cont=cont, error_cont=self_cont, - codes_todo=[([self.dialog.DIALOG_OK], todo, [passwords, proprio, self_cont, cont])] + codes_todo=[([self.dialog.DIALOG_OK], todo, [passwords, proprio, return_obj, self_cont, cont])] ) - - def proprio_compte_delete(self, proprio, cont): + @tailcaller + def proprio_compte_delete(self, proprio, cont, force=False): """Permet la suppression du compte crans d'un proprio""" def todo(proprio, self_cont, cont): - if self.confirm_item(item=proprio, title="Voulez vous vraiement supprimer le compte de %s %s ?" % (proprio.get('prenom', [''])[0], proprio["nom"][0]), defaultno=True): + if force or self.confirm_item(item=proprio, title="Voulez vous vraiement supprimer le compte de %s %s ?" % (proprio.get('prenom', [''])[0], proprio["nom"][0]), defaultno=True): (code, mail) = self.dialog.inputbox( text="Il faut choisir une nouvelle adresse de contact.\n(On regarde s'il y a une adresse optionnel)", title="Choix d'une adresse de contact pour %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]), init=str(proprio.get("mailExt", [""])[0]), - width=50) + width=50, timeout=self.timeout) if not code == self.dialog.DIALOG_OK: raise Continue(cont) elif not mail: @@ -2149,7 +2309,7 @@ les valeurs valident sont : else: raise Continue(cont) - self_cont = TailCall(self.proprio_compte_delete, proprio=proprio, cont=cont) + self_cont = TailCall(self.proprio_compte_delete, proprio=proprio, cont=cont, force=force) return self.handle_dialog_result( code=self.dialog.DIALOG_OK, output="", @@ -2167,14 +2327,61 @@ les valeurs valident sont : proprio.save() raise Continue(cont(proprio=proprio)) - def proprio_compte_shell(self, proprio, cont): - self.dialog.msgbox("todo", width=0, height=0) - return cont + def proprio_compte_shell(self, proprio, cont, choices_values=None): + shell = os.path.basename(str(proprio['loginShell'][0])).lower() + shells = config.shells_gest_crans + shells_order = config.shells_gest_crans_order + def box(): + return self.dialog.radiolist( + text="", + height=0, width=0, list_height=0, + choices=choices_values if choices_values else [(s, shells[s]['desc'], 1 if s == shell else 0) for s in shells_order], + title="Shell de %s %s" % (proprio.get('prenom', [""])[0], proprio['nom'][0]), + timeout=self.timeout + ) + def todo(output, shell, shells, proprio, self_cont, cont): + loginShell = shells[output]['path'] + if shell != output: + with self.conn.search(dn=proprio.dn, scope=0, mode='rw')[0] as proprio: + proprio['loginShell']=unicode(loginShell) + proprio.save() + self.dialog.msgbox("Shell modifié avec succès.\nLa modification peut prendre une quainzaine de minute avant d'être effective.", + title="Shell de %s %s" % (proprio.get('prenom', [""])[0], proprio['nom'][0]), + width=50, timeout=self.timeout, + ) + raise Continue(cont(proprio=proprio)) + + (code, output) = self.handle_dialog(cont, box) + self_cont = TailCall(self.proprio_compte_shell, proprio=proprio, cont=cont, choices_values=[(s, shells[s]['desc'], 1 if s == output else 0) for s in shells_order]) + return self.handle_dialog_result( + code=code, + output=output, + cancel_cont=cont, + error_cont=self_cont, + codes_todo=[([self.dialog.DIALOG_OK], todo, [output, shell, shells, proprio, self_cont, cont])] + ) def proprio_compte(self, proprio, cont, default_item=None): has_compte = 'cransAccount' in proprio['objectClass'] disabled_compte = has_compte and 0 in proprio['shadowExpire'] - menu = {} + a = attributs + menu_droits = { + "Password": [a.cableur, a.nounou], + 'MailAlias': [a.cableur, a.nounou], + "Activer" : [a.nounou], + "Désactiver" : [a.nounou], + "Créer" : [a.cableur, a.nounou], + "Supprimer" : [a.cableur, a.nounou], + } + menu = { + "Password" : {"text":"Changer le mot de passe du compte", "help":"", "callback":self.proprio_compte_password}, + 'MailAlias' : {'text': 'Créer ou supprimer des alias mail', "help":"", 'attribut':attributs.mailAlias}, + "Shell" : {"text" : "Changer le shell de cet utilisateur", "help":'', "callback":self.proprio_compte_shell}, + "Activer" : {"text" : "Activer le compte pour la connexion mail/serveur", "help":"Permet d'autoriser les connexions smtp, imap, ssh, etc… avec le compte", "callback":TailCall(self.proprio_compte_etat, disable=False)}, + "Désactiver" : {"text" : "Désactiver le compte pour la connexion mail/serveur", "help":"Permet d'interdire les connexions smtp, imap, ssh, etc… avec le compte", "callback":TailCall(self.proprio_compte_etat, disable=True)}, + "Créer" : {"text": "Créer un compte", "help":'', "callback":self.proprio_compte_create}, + "Supprimer" : {"text": "Supprimer le compte", "help":"Le home sera archivé dans le cimetière", "callback":self.proprio_compte_delete}, + } menu_order = [] tag_translate = { "Créer":"Password", @@ -2183,21 +2390,16 @@ les valeurs valident sont : "Activer":"Désactiver", "Désactiver":"Activer", "Shell":"Shell", + 'MailAlias':'MailAlias', '':'', } if has_compte: - menu["Password"] = {"text":"Changer le mot de passe du compte", "help":"", "callback":self.proprio_compte_password} - menu["Shell"] = {"text" : "Changer le shell de cet utilisateur", "help":'', "callback":self.proprio_compte_shell} - menu["Supprimer"] = {"text": "Supprimer le compte", "help":"Le home sera archivé dans le cimetière", "callback":self.proprio_compte_delete} - menu_order.extend(["Password", "Shell", "Supprimer"]) if disabled_compte: - menu["Activer"] = {"text" : "Activer le compte pour la connexion mail/serveur", "help":"Permet d'autoriser les connexions smtp, imap, ssh, etc… avec le compte", "callback":TailCall(self.proprio_compte_etat, disable=False)} - menu_order.insert(1, "Activer") + menu_order.append("Activer") else: - menu["Désactiver"] = {"text" : "Désactiver le compte pour la connexion mail/serveur", "help":"Permet d'interdire les connexions smtp, imap, ssh, etc… avec le compte", "callback":TailCall(self.proprio_compte_etat, disable=True)} - menu_order.insert(1, "Désactiver") + menu_order.append("Désactiver") + menu_order.extend(['MailAlias', "Shell", "Password", "Supprimer"]) else: - menu["Créer"] = {"text": "Créer un compte", "help":'', "callback":self.proprio_compte_create} menu_order.append("Créer") def box(default_item=None): return self.dialog.menu( @@ -2212,16 +2414,21 @@ les valeurs valident sont : scrollbar=True, cancel_label="Retour", backtitle=u"Vous êtes connecté en tant que %s" % self.conn.current_login, - choices=[(k, menu[k]['text'], menu[k]['help']) for k in menu_order]) + choices=[(k, menu[k]['text'], menu[k]['help']) for k in menu_order if self.has_right(menu_droits[key], proprio)]) def todo(tag, menu, proprio, self_cont): if not tag in menu_order: raise Continue(self_cont) - else: + elif 'callback' in menu[tag]: raise Continue(TailCall(menu[tag]['callback'], cont=self_cont, proprio=proprio)) + elif 'attribut' in menu[tag]: + raise Continue(TailCall(self.modif_proprio_attributs, proprio=proprio, cont=self_cont, attr=menu[tag]['attribut'].ldap_name)) + else: + raise EnvironmentError("Il n'y a ni champ 'attribut' ni 'callback' pour le tag %s" % tag) + (code, tag) = self.handle_dialog(cont, box, default_item) - self_cont = TailCall(self.proprio_compte, proprio=proprio, cont=cont, default_item=tag_translate[tag]) + self_cont = TailCall(self.proprio_compte, proprio=proprio, cont=cont, default_item=tag_translate.get(tag, tag)) return self.handle_dialog_result( code=code, output=tag, @@ -2230,10 +2437,6 @@ les valeurs valident sont : codes_todo=[([self.dialog.DIALOG_OK], todo, [tag, menu, proprio, self_cont])] ) - def proprio_mail(self, proprio, cont): - self.dialog.msgbox("todo", width=0, height=0) - return cont - def adherent_droits(self, adherent, cont, choices_values=None): def box(): return self.dialog.checklist( @@ -2241,23 +2444,10 @@ les valeurs valident sont : height=0, width=0, list_height=0, choices=choices_values if choices_values else [(droit, "", 1 if droit in adherent["droits"] else 0) for droit in attributs.TOUS_DROITS], title="Droits de %s %s" % (adherent['prenom'][0], adherent['nom'][0]), + timeout=self.timeout ) def todo(droits, adherent, self_cont, cont): - """if attributs.nounou in self.conn.droits: - droits_modifiable = attributs.DROITS_SUPERVISEUR[attributs.nounou] - elif attributs.bureau in self.conn.droits: - droits_modifiable = attributs.DROITS_SUPERVISEUR[attributs.bureau] - else: - droits_modifiable = [] - new_droits = [str(d) for d in droits if d not in adherent['droits']] - del_droits = [str(d) for d in adherent['droits'] if d not in droits] - modif_droit = set(new_droits+del_droits) - modif_interdite = [d for d in modif_droit if d not in droits_modifiable] - if modif_interdite: - self.dialog.msgbox("Vous n'avez pas le droits de modifier les droits %s, seulement les droits '%s'" % (", ".join(modif_interdite), ", ".join(droits_modifiable)), - title="Droits de %s %s" % (adherent['prenom'][0], adherent['nom'][0])) - raise Continue(self_cont) - else:""" + # Les vérifications de sécurité sont faites dans lc_ldap with self.conn.search(dn=adherent.dn, scope=0, mode='rw')[0] as adherent: adherent['droits']=[unicode(d) for d in droits] adherent.save() @@ -2274,6 +2464,7 @@ les valeurs valident sont : error_cont=self_cont, codes_todo=[([self.dialog.DIALOG_OK], todo, [droits, adherent, self_cont, cont])] ) + def modif_proprio_blacklist(self, proprio, cont): self.dialog.msgbox("todo", width=0, height=0) return cont @@ -2339,31 +2530,49 @@ les valeurs valident sont : associationCrans = self.conn.search(dn="ou=data,dc=crans,dc=org", scope=0)[0] return self.create_machine_proprio(cont=cont, proprio=associationCrans) + def has_right(self, list, obj=None): + if obj: + droits = obj.rights() + else: + droits = self.conn.droits + for d in list: + if d in droits: + return True + return False + + @tailcaller def menu_principal(self, tag=None, machine=None, proprio=None): """Menu principal de l'application affiché au lancement""" + a = attributs + menu_droits = { + 'default' : [a.cableur, a.nounou], + 'aKM' : [a.nounou], + } menu = { - 'aA' : {'text':"Inscrire un nouvel adhérent", 'callback': self.create_adherent}, + 'aA' : {'text':"Inscrire un nouvel adhérent", 'callback': self.create_adherent,}, 'mA' : {'text':"Modifier l'inscription d'un adhérent", 'callback': self.modif_adherent, 'help':"Changer la chambre, la remarque, la section, la carte d'étudiant ou précâbler."}, 'aMA': {'text':"Ajouter une machine à un adhérent", 'callback': self.create_machine_adherent}, - 'dA' : {'text':"Détruire un adhérent", 'callback': self.delete_adherent, 'help':"Suppression de l'adhérent ainsi que de ses machines"}, + #'dA' : {'text':"Détruire un adhérent", 'callback': self.delete_adherent, 'help':"Suppression de l'adhérent ainsi que de ses machines"}, 'mM' : {'text':"Modifier une machine existante", 'callback': self.modif_machine, 'help':"Changer le nom ou la MAC d'une machine."}, - 'dM' : {'text':"Détruire une machine", 'callback': self.delete_machine}, + #'dM' : {'text':"Détruire une machine", 'callback': self.delete_machine}, 'aC' : {'text':"Inscrire un nouveau club", 'callback': self.create_club}, 'mC' : {'text':"Modifier un club", 'callback': self.modif_club}, 'aMC': {'text':"Ajouter une machine à un club", 'callback': self.create_machine_club}, - 'dC' : {'text':"Détruire un club", 'callback': self.delete_club}, + #'dC' : {'text':"Détruire un club", 'callback': self.delete_club}, 'aKM': {'text':"Ajouter une machine à l'association", 'callback': self.create_machine_crans}, - '' : {'text':"---------------------------------------",'callback': None}, + '' : {'text':"---------------------------------------",'callback': None}, } ### Les clef qui n'existe pas sont toute renvoyé sur la clef '' - menu_order = ["aA", "mA", "aMA", "dA", "", "mM", "dM", " ", "aC", "mC", "aMC", "dC", " ", "aKM"] + #menu_order = ["aA", "mA", "aMA", "dA", "", "mM", "dM", " ", "aC", "mC", "aMC", "dC", " ", "aKM"] + #menu_order = ["aA", "mA", "aMA", "", "mM", " ", "aC", "mC", "aMC", " ", "aKM"] + menu_order = ["aA", "mA", "aMA", "", "mM", "", "aC", "mC", "aMC", "", "aKM"] if machine and not proprio: proprio = machine.proprio() if isinstance(proprio, objets.AssociationCrans): proprio = None if machine or proprio: - menu_order = [' '] + menu_order + menu_order = [''] + menu_order if machine: menu_machine = { 'mMc' : { @@ -2407,6 +2616,14 @@ les valeurs valident sont : menu.update(menu_proprio) menu_order = menu_proprio_order + menu_order def box(default_item=None): + choices = [] + for key in menu_order: + if self.has_right(menu_droits.get(key, menu_droits['default'])): + choices.append((key, menu[key]['text'], menu[key].get('help', ""))) + while choices[-1][0] == '': + choices=choices[:-1] + while choices[0][0] == '': + choices=choices[1:] return self.dialog.menu( "Que souhaitez vous faire ?", width=0, @@ -2419,10 +2636,10 @@ les valeurs valident sont : timeout=self.timeout, cancel_label="Quitter", backtitle=u"Vous êtes connecté en tant que %s" % self.conn.current_login, - choices=[(key, menu.get(key, menu[''])['text'], menu.get(key, menu['']).get('help', "")) for key in menu_order]) + choices=choices) - self_cont = TailCall(self.menu_principal, tag=tag, proprio=proprio, machine=machine) (code, tag) = self.handle_dialog(TailCall(handle_exit_code, self.dialog, self.dialog.DIALOG_ESC), box, tag) + self_cont = TailCall(self.menu_principal, tag=tag, proprio=proprio, machine=machine) callback = menu.get(tag, menu[''])['callback'] if handle_exit_code(self.dialog, code) and callback: return TailCall(callback, cont=TailCall(self.menu_principal, tag=tag, machine=machine, proprio=proprio))