#!/bin/bash /usr/scripts/python.sh # -*- coding: utf-8 -*- u""" Copyright (C) Valentin Samir Licence : GPLv3 """ import sys if '/usr/scripts' not in sys.path: sys.path.append('/usr/scripts') import lc_ldap.objets as objets import lc_ldap.attributs as attributs import certificat import blacklist from CPS import TailCall, tailcaller, Continue class Dialog(certificat.Dialog, blacklist.Dialog): def machine_information(self, cont, machine=None, objectClass=None, proprio=None, realm=None, fields_values=None): """ Permet de modifier une machine si elle est fournit par le paramètre machine sinon, crée une machine à partir de proprio, objectClass et realm. Si on ne fait qu'éditer une machine, proprio, objectClass et realm sont ignoré D'une machinère générale, il faudrait mettre ici tous les attributs single value et les multivalué que l'on peut simplement représenter de façon textuelle avec un séparateur. Pour le moment il y a : * host * macAddress * ipHostNumber * port(TCP|UDP)(in|out) """ a = attributs # Quel sont les attributs ldap dont on veut afficher et la taille du champs d'édition correspondant to_display = [(a.host, 30), (a.macAddress, 17), (a.ipHostNumber, 15), (a.portTCPout, 50), (a.portTCPin, 50), (a.portUDPout, 50), (a.portUDPin, 50) ] # Quel séparateur on utilise pour les champs multivalué separateur = ' ' def box(): if machine: attrs = dict((k,[str(a) for a in at]) for k,at in machine.items()) else: attrs = {} fields = [("%s :" % a.legend, separateur.join(attrs.get(a.ldap_name, [a.default] if a.default else [])), l+1, l) for a,l in to_display] return self.dialog.form( text="", timeout=self.timeout, height=0, width=0, form_height=0, fields=fields_values if fields_values else fields, title="Paramètres machine", backtitle="Gestion des machines du Crans") def check_host(host, objectClass): # Si c'est une machine wifi, host doit finir par wifi.crans.org if "machineWifi" == objectClass or 'borneWifi' == objectClass: hostend = ".wifi.crans.org" # Si c'est une machine wifi, host doit finir par crans.org elif "machineFixe" == objectClass: hostend = ".crans.org" # Si l'object class est machineCrans, pas de vérification elif "machineCrans" == objectClass: return host # Sinon, libre à chachun d'ajouter d'autres objectClass ou de filtrer # plus finement fonction des droits de self.conn.droits else: raise ValueError("La machine n'est ni une machine fixe, ni une machine wifi mais %s ?!?" % objectClass) if not host.endswith(hostend) and not '.' in host: host = host + hostend elif host.endswith(hostend) and '.' in host[:-len(hostend)]: raise ValueError("Nom d'hôte invalide, devrait finir par %s et être sans point dans la première partie" % hostend) elif not host.endswith(hostend) and '.' in host: raise ValueError("Nom d'hôte invalide, devrait finir par %s et être sans point dans la première partie" % hostend) return host def modif_machine(machine, attrs): with self.conn.search(dn=machine.dn, scope=0, mode='rw')[0] as machine: for (key, values) in attrs.items(): machine[key]=values machine.validate_changes() machine.history_gen() machine.save() return machine def create_machine(proprio, realm, attrs): # Dans ce cas, on a besoin d'un proprio et d'un realm pour déterminer le rid if proprio is None or realm is None: raise EnvironmentError("On essaye de créer une machine mais proprio ou realm vaut None") ldif = { 'macAddress': ['%s' % attrs['macAddress']], 'host': ['%s' % attrs['host']] } with self.conn.newMachine(proprio.dn, realm, ldif) as machine: for (key, values) in attrs.items(): machine[key]=values if attributs.ipsec in machine.attribs: machine[attributs.ipsec.ldap_name]=attributs.ipsec.default machine.validate_changes() if self.confirm_item(machine, "Voulez vous vraiement créer cette machine ?"): machine.create() self.display_item(machine, "La machine à bien été créée", ipsec=True) return machine else: raise Continue(cont) def todo(to_display, tags, objectClass, machine, proprio, realm, separateur, cont): attrs = {} # On traite les valeurs reçues for ((a,l),values) in zip(to_display, tags): 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 if not a.singlevalue: values = [v for v in values.split(separateur) if v] # Pour host, on fait quelques vérification de syntaxe if a.ldap_name == 'host': attrs[a.ldap_name]=check_host(values, objectClass) else: attrs[a.ldap_name]=values # Soit on édite une machine existante if machine: machine = modif_machine(machine, attrs) # Soit on crée une nouvelle machine else: machine = create_machine(proprio, realm, attrs) raise Continue(cont(machine=machine)) if machine: objectClass = machine["objectClass"][0] (code, tags) = self.handle_dialog(cont, box) # 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.machine_information, machine=machine, cont=cont, objectClass=objectClass, proprio=proprio, realm=realm, fields_values=fields_values) 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, objectClass, machine, proprio, realm, separateur, cont])] ) def modif_machine_blacklist(self, machine, cont): """Raccourci vers edit_blacklist spécifique aux machines""" return self.edit_blacklist(obj=machine, title="Éditions des blacklist de la machine %s" % machine['host'][0], update_obj='machine', cont=cont) def modif_machine_attributs(self, machine, attr, cont): """Juste un raccourci vers edit_attributs spécifique aux machines""" return self.edit_attributs(obj=machine, update_obj='machine', attr=attr, title="Modification de la machine %s" % machine['host'][0], cont=cont) def modif_machine_boolean(self, machine, cont): """Juste un raccourci vers edit_boolean_attributs spécifique aux machines""" a = attributs attribs = [a.dnsIpv6] return self.edit_boolean_attributs( obj=machine, attribs=attribs, title="Édition des attributs booléen de la machine %s" % machine['host'][0], update_obj='machine', cont=cont) def modif_machine(self, cont, machine=None, tag=None): """ Permet d'éditer une machine. Si fournie en paramètre on éditer en place, sinon, on en cherche une dans la base ldap """ 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}, 'Blackliste' : {'text': 'Modifier les blacklist de la machine', 'callback':self.modif_machine_blacklist}, 'Certificat' : {'text': 'Modifier les certificats de la machine', 'callback':self.modif_machine_certificat}, 'Exemption' : {'text':"Modifier la liste d'exemption d'upload de la machine", 'attribut':attributs.exempt}, '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', 'Supprimer'] def box(default_item=None): return self.dialog.menu( "Que souhaitez vous modifier ?", width=0, height=0, menu_height=0, timeout=self.timeout, item_help=0, default_item=str(default_item), title="Modification de %s" % machine['host'][0], scrollbar=True, cancel_label="Retour", backtitle=self._connected_as(), 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: raise Continue(cont_ret) else: if 'callback' in menu[tag]: raise Continue(TailCall(menu[tag]['callback'], machine=machine, cont=cont_ret)) elif 'attribut' in menu[tag]: raise Continue(TailCall(self.modif_machine_attributs, machine=machine, cont=cont_ret, 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, tag) cont_ret = TailCall(self.modif_machine, cont=cont, machine=machine, tag=tag) return self.handle_dialog_result( code=code, output=tag, cancel_cont=cont(machine=machine), error_cont=cont_ret, codes_todo=[([self.dialog.DIALOG_OK], todo, [tag, menu, machine, cont_ret])] ) def create_machine_proprio(self, cont, proprio, tag=None): """Permet d'ajouter une machine à un proprio (adherent, club ou AssociationCrans)""" 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'}, 'Adm' : {'text' : "Ajouter un serveur sur le vlan adm", "objectClass":"machineCrans", 'realm':'adm'}, }) menu_order.append('Adm') def box(default_item=None): return self.dialog.menu( "Type de Machine ?", width=0, height=0, menu_height=0, item_help=0, timeout=self.timeout, default_item=str(default_item), title="Création de machines", scrollbar=True, cancel_label="Retour", backtitle=self._connected_as(), 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: raise Continue(self_cont) else: return self.machine_information( cont=cont, machine=None, objectClass=menu[tag]['objectClass'], proprio=proprio, realm=menu[tag]['realm'] ) (code, tag) = self.handle_dialog(cont, box, tag) cont = cont(proprio=None) if isinstance(proprio, objets.AssociationCrans) else cont(proprio=proprio) self_cont = TailCall(self.create_machine_proprio, cont=cont, proprio=proprio, tag=tag) return self.handle_dialog_result( code=code, output=tag, cancel_cont=cont, error_cont=self_cont, codes_todo=[([self.dialog.DIALOG_OK], todo, [tag, menu, proprio, self_cont, cont])] ) def create_machine_adherent(self, cont, adherent=None): """ Permet d'ajouter une machine à un adhérent. On affiche un menu pour choisir le type de machine (juste filaire et wifi pour le moment) """ if adherent is None: adherent = self.select(["adherent"], "Recherche d'un adhérent pour lui ajouter une machine", cont=cont) return self.create_machine_proprio(cont=cont, proprio=adherent) def create_machine_club(self, cont, club=None): """ Permet d'ajouter une machine à un club. On affiche un menu pour choisir le type de machine (juste filaire et wifi pour le moment) """ if club is None: club = self.select(["club"], "Recherche d'un club pour lui ajouter une machine", cont=cont) return self.create_machine_proprio(cont=cont, proprio=club) def create_machine_crans(self, cont): """Permet l'ajout d'une machine à l'association""" associationCrans = self.conn.search(dn="ou=data,dc=crans,dc=org", scope=0)[0] return self.create_machine_proprio(cont=cont, proprio=associationCrans) def delete_machine(self, cont, machine=None): """Permet la suppression d'une machine de la base ldap""" if machine is None: machine = self.select(["machineFixe", "machineWifi", "machineCrans", "borneWifi"], "Recherche d'une machine pour supression", cont=cont) def todo(machine): if self.confirm_item(item=machine, title="Voulez vous vraiement supprimer la machine ?", defaultno=True): with self.conn.search(dn=machine.dn, scope=0, mode='rw')[0] as machine: machine.delete() self.dialog.msgbox("La machine a bien été supprimée", timeout=self.timeout, title="Suppression d'une machine") raise Continue(cont(machine=None)) else: raise Continue(cont) return self.handle_dialog_result( code=self.dialog.DIALOG_OK, output="", cancel_cont=cont, error_cont=cont, codes_todo=[([self.dialog.DIALOG_OK], todo, [machine])] )