373 lines
17 KiB
Python
373 lines
17 KiB
Python
#!/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)]
|
|
|
|
to_display_port = [(a.portTCPout, 50), (a.portTCPin, 50), (a.portUDPout, 50),
|
|
(a.portUDPin, 50)]
|
|
|
|
to_display_borne = [(a.canal, 10), (a.hotspot, 10), (a.puissance, 10), (a.positionBorne, 50), (a.nvram, 10)]
|
|
|
|
# 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, realm):
|
|
# Si c'est une machine wifi, host doit finir par wifi.crans.org
|
|
if "machineWifi" == objectClass or 'borneWifi' == objectClass or realm == 'bornes':
|
|
hostend = ".wifi.crans.org"
|
|
# Si c'est une machine wifi, host doit finir par crans.org
|
|
elif "machineFixe" == objectClass or realm == 'serveurs':
|
|
hostend = ".crans.org"
|
|
# Si l'object class est machineCrans, pas de vérification
|
|
elif "machineCrans" == objectClass:
|
|
if realm == 'adm':
|
|
hostend = ".adm.crans.org"
|
|
if not '.' in host:
|
|
host = host + hostend
|
|
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, realm)
|
|
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]
|
|
|
|
if self.has_right(a.nounou, proprio):
|
|
to_display += to_display_port
|
|
|
|
# Les bornes wifi ont un to_display différent
|
|
if objectClass == 'borneWifi':
|
|
to_display += to_display_borne
|
|
|
|
(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.nounou],
|
|
'Certificat': [a.parent, 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 = ['Wifi','Fixe']
|
|
|
|
if not bool(proprio.get('droits', False)) and isinstance(proprio, objets.adherent):
|
|
for machine in proprio.machines():
|
|
if isinstance(machine, objets.machineFixe):
|
|
menu_order.remove('Fixe')
|
|
break
|
|
|
|
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])]
|
|
)
|
|
|