[gest_crans_lc] Gestion des certificats++, gestion des Ctrl-c

This commit is contained in:
Valentin Samir 2014-03-17 11:24:18 +01:00
parent a8a1767844
commit 227389ae73

View file

@ -15,6 +15,7 @@ Licence : GPLv3
import os import os
import sys import sys
import ssl
import time import time
import ldap import ldap
import signal import signal
@ -24,7 +25,9 @@ import collections
sys.path.append('/usr/scripts/') sys.path.append('/usr/scripts/')
from pythondialog import Dialog from pythondialog import Dialog
from pythondialog import DialogError from pythondialog import DialogError
from OpenSSL import crypto, SSL
from gestion.cert_utils import createCertRequest
from gestion.affich_tools import get_screen_size, coul from gestion.affich_tools import get_screen_size, coul
from gestion.chgpass import checkpass from gestion.chgpass import checkpass
import gestion.config as config import gestion.config as config
@ -37,8 +40,9 @@ import lc_ldap.crans_utils as lc_utils
from lc_ldap.attributs import UniquenessError from lc_ldap.attributs import UniquenessError
import gestion.secrets_new as secrets
debugf=None debugf=None
debug_enable = True debug_enable = False
def mydebug(txt): def mydebug(txt):
global debugf, debug_enable global debugf, debug_enable
@ -166,7 +170,7 @@ def handle_exit_code(d, code):
os.system('clear') os.system('clear')
sys.exit(0) sys.exit(0)
else: else:
msg = "Vous avez appuyer sur ESC dans la dernière fenêtre de dialogue.\n\n" \ msg = "Vous avez appuyer sur ESC ou CTRL+C dans la dernière fenêtre de dialogue.\n\n" \
"Voulez vous quitter le programme ?" "Voulez vous quitter le programme ?"
if d.yesno(msg, width=60) == d.DIALOG_OK: if d.yesno(msg, width=60) == d.DIALOG_OK:
os.system('clear') os.system('clear')
@ -175,6 +179,8 @@ def handle_exit_code(d, code):
else: else:
return True # code est d.DIALOG_OK return True # code est d.DIALOG_OK
def raiseKeyboardInterrupt(x, y):
raise KeyboardInterrupt()
class GestCrans(object): class GestCrans(object):
"""Interface de gestion des machines et des adhérents du crans, version lc_ldap""" """Interface de gestion des machines et des adhérents du crans, version lc_ldap"""
@ -188,7 +194,6 @@ class GestCrans(object):
return ret return ret
def __init__(self): def __init__(self):
if not debug_enable:
signal.signal(signal.SIGINT, signal.SIG_IGN) signal.signal(signal.SIGINT, signal.SIG_IGN)
# On initialise le moteur de rendu en spécifiant qu'on va faire du dialog # On initialise le moteur de rendu en spécifiant qu'on va faire du dialog
printing.template(dialog=True) printing.template(dialog=True)
@ -202,7 +207,7 @@ class GestCrans(object):
self.check_ldap_last = time.time() self.check_ldap_last = time.time()
# On ouvre une connexion lc_ldap # On ouvre une connexion lc_ldap
self.conn = lc_ldap.shortcuts.lc_ldap_admin() self.conn = lc_ldap.shortcuts.lc_ldap_admin()
self.conn.current_login = 'totocrans' #self.conn.current_login = 'totocrans'
# On vérifie que l'utilisateur système existe dans ldap (pour la gestion des droits) # 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) luser=self.conn.search(u'(&(uid=%s)(objectClass=cransAccount))' % self.conn.current_login)
if not luser: if not luser:
@ -222,6 +227,26 @@ class GestCrans(object):
self.dialog_last_access = time.time() self.dialog_last_access = time.time()
return self._dialog return self._dialog
@tailcaller
def handle_dialog(self, cancel_cont, box, *args):
ctrlC=False
ret=None
try:
signal.signal(signal.SIGINT, raiseKeyboardInterrupt) # Ctrl-C
ret = box(*args)
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)
elif ret:
return ret
else:
EnvironmentError("Ni Ctrl+C ni ret ?!? c'est pas possible ")
@tailcaller
def handle_dialog_result(self, code, output, cancel_cont, error_cont, codes_todo=[]): def handle_dialog_result(self, code, output, cancel_cont, error_cont, codes_todo=[]):
""" codes_todo = [(code, todo, todo_args)]""" """ codes_todo = [(code, todo, todo_args)]"""
# Si on a appuyé sur annulé ou ESC, on s'en va via la continuation donnée en argument # Si on a appuyé sur annulé ou ESC, on s'en va via la continuation donnée en argument
@ -232,15 +257,23 @@ class GestCrans(object):
for (codes, todo, todo_args) in codes_todo: for (codes, todo, todo_args) in codes_todo:
if code in codes: if code in codes:
try: try:
signal.signal(signal.SIGINT, raiseKeyboardInterrupt) # Ctrl-C
# On effectue ce qu'il y a a faire dans todo # On effectue ce qu'il y a a faire dans todo
return todo(*todo_args) ret = todo(*todo_args)
signal.signal(signal.SIGINT, signal.SIG_IGN) # Pas de Ctrl-C
return ret
# On propage les Continue # On propage les Continue
except self.error_to_raise: except self.error_to_raise:
signal.signal(signal.SIGINT, signal.SIG_IGN) # Pas de Ctrl-C
raise raise
# En cas d'une autre erreur, on l'affiche et on retourne au menu d'édition # En cas d'une autre erreur, on l'affiche et on retourne au menu d'édition
except (Exception, ldap.OBJECT_CLASS_VIOLATION) as e: except (Exception, ldap.OBJECT_CLASS_VIOLATION) as e:
signal.signal(signal.SIGINT, signal.SIG_IGN) # Pas de Ctrl-C
self.dialog.msgbox("%s" % unicode_of_Error(e), timeout=self.timeout,title="Erreur rencontrée", width=73, height=10) self.dialog.msgbox("%s" % unicode_of_Error(e), timeout=self.timeout,title="Erreur rencontrée", width=73, height=10)
raise Continue(error_cont) raise Continue(error_cont)
except KeyboardInterrupt:
signal.signal(signal.SIGINT, signal.SIG_IGN) # Pas de Ctrl-C
raise Continue(cancel_cont)
# En cas de code de retour dialog non attendu, on prévient et on retourne au menu d'édition # En cas de code de retour dialog non attendu, on prévient et on retourne au menu d'édition
self.dialog.msgbox("Le code de retour dialog est %s, c'est étrange" % code, timeout=self.timeout, title="Erreur rencontrée", width=73, height=10) self.dialog.msgbox("Le code de retour dialog est %s, c'est étrange" % code, timeout=self.timeout, title="Erreur rencontrée", width=73, height=10)
@ -282,7 +315,7 @@ class GestCrans(object):
return tag, bl return tag, bl
(code, tag) = box() (code, tag) = self.handle_dialog(cont, box)
retry_cont = TailCall(self.edit_blacklist_select, obj=obj, title=title, cont=cont) retry_cont = TailCall(self.edit_blacklist_select, obj=obj, title=title, cont=cont)
return self.handle_dialog_result( return self.handle_dialog_result(
code=code, code=code,
@ -319,7 +352,7 @@ class GestCrans(object):
self.dialog.msgbox("%s n'est pas une blacklist" % tag, timeout=self.timeout, title="Erreur rencontrée", width=73, height=10) self.dialog.msgbox("%s n'est pas une blacklist" % tag, timeout=self.timeout, title="Erreur rencontrée", width=73, height=10)
raise Continue(retry_cont) raise Continue(retry_cont)
(code, tag) = box() (code, tag) = self.handle_dialog(cont, box)
return self.handle_dialog_result( return self.handle_dialog_result(
code=code, code=code,
output=tag, output=tag,
@ -544,7 +577,7 @@ class GestCrans(object):
if values is None: if values is None:
values = [str(a) for a in obj[attr]] values = [str(a) for a in obj[attr]]
retry_cont = TailCall(self.edit_attributs, obj=obj, attr=attr, title=title, update_obj=update_obj, cont=cont, values=values) retry_cont = TailCall(self.edit_attributs, obj=obj, attr=attr, title=title, update_obj=update_obj, cont=cont, values=values)
(code, output) = box(values, tag) (code, output) = self.handle_dialog(cont, box, values, tag)
return self.handle_dialog_result( return self.handle_dialog_result(
code=code, code=code,
@ -605,7 +638,7 @@ class GestCrans(object):
else: # empty selection else: # empty selection
return (code, []) return (code, [])
(code, dialog_values) = box() (code, dialog_values) = self.handle_dialog(cont, box)
# Si il a appuyé sur annuler ou sur escape, on saute sur la continuation # Si il a appuyé sur annuler ou sur escape, on saute sur la continuation
if code in (self.dialog.DIALOG_CANCEL, self.dialog.DIALOG_ESC): if code in (self.dialog.DIALOG_CANCEL, self.dialog.DIALOG_ESC):
raise Continue(cont) raise Continue(cont)
@ -724,7 +757,7 @@ class GestCrans(object):
else: else:
raise Continue(cont) raise Continue(cont)
(code, tag) = box(items, default_item) (code, tag) = self.handle_dialog(cont, box, items, default_item)
retry_cont = TailCall(self.select_one, items=items, title=title, default_item=tag, cont=cont) retry_cont = TailCall(self.select_one, items=items, title=title, default_item=tag, cont=cont)
return self.handle_dialog_result( return self.handle_dialog_result(
@ -885,7 +918,7 @@ class GestCrans(object):
machine.save() machine.save()
return machine return machine
def create_machine(prorio, realm, attrs): def create_machine(proprio, realm, attrs):
# Dans ce cas, on a besoin d'un proprio et d'un realm pour déterminer le rid # 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: if proprio is None or realm is None:
raise EnvironmentError("On essaye de créer une machine mais proprio ou realm vaut None") raise EnvironmentError("On essaye de créer une machine mais proprio ou realm vaut None")
@ -925,14 +958,14 @@ class GestCrans(object):
machine = modif_machine(machine, attrs) machine = modif_machine(machine, attrs)
# Soit on crée une nouvelle machine # Soit on crée une nouvelle machine
else: else:
machine = create_machine(prorio, realm, attrs) machine = create_machine(proprio, realm, attrs)
raise Continue(cont(machine=machine)) raise Continue(cont(machine=machine))
if machine: if machine:
objectClass = machine["objectClass"][0] objectClass = machine["objectClass"][0]
(code, tags) = box() (code, tags) = self.handle_dialog(cont, box)
# On prépare les fiels à afficher à l'utilisateur si une erreure à lieu # 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é # pendant le traitement des donnée (on n'éfface pas ce qui a déjà été entré
@ -1002,7 +1035,7 @@ les valeurs valident sont :
certificat.save() certificat.save()
raise Continue(cont(certificat=certificat)) raise Continue(cont(certificat=certificat))
(code, output) = box(values) (code, output) = self.handle_dialog(cont, box, values)
values = dict(zip([form[k]['ldap_name'] for k in form_order], output)) values = dict(zip([form[k]['ldap_name'] for k in form_order], output))
fields_values = [("%s : " % k, values.get(form[k]['ldap_name'], ""), form[k]['len'] + 1, form[k]['len']) for k in form_order] fields_values = [("%s : " % k, values.get(form[k]['ldap_name'], ""), form[k]['len'] + 1, form[k]['len']) for k in form_order]
self_cont=TailCall(self.certificat_tlsa, certificat=certificat, cont=cont, values=fields_values) self_cont=TailCall(self.certificat_tlsa, certificat=certificat, cont=cont, values=fields_values)
@ -1014,8 +1047,10 @@ les valeurs valident sont :
codes_todo=[([self.dialog.DIALOG_OK], todo, [form, values, certificat, cont])] codes_todo=[([self.dialog.DIALOG_OK], todo, [form, values, certificat, cont])]
) )
def create_certificat(self, machine, cont): def create_certificat(self, cont, machine=None, certificat=None):
"""Permet d'ajouter un certificat à une machine à partir du PEM du certificat""" """Permet d'ajouter un certificat à une machine à partir du PEM du certificat"""
if machine is None and certificat is None:
raise EnvironmentError("Il faut fournir au moins une machine ou un certificat")
# input multiline en utilisant un editbox # input multiline en utilisant un editbox
def box(): def box():
fp, path = tempfile.mkstemp() fp, path = tempfile.mkstemp()
@ -1032,20 +1067,116 @@ les valeurs valident sont :
else: else:
return code, None return code, None
def todo(machine, cont): def todo(machine, certificat, cont):
if certificat:
with self.conn.search(dn=certificat.dn, scope=0, mode='rw')[0] as certificat:
certificat['certificat'] = unicode(pem, 'utf-8')
certificat.save()
else:
with self.conn.newCertificat(machine.dn, {}) as certificat: with self.conn.newCertificat(machine.dn, {}) as certificat:
certificat['certificat'] = unicode(pem, 'utf-8') certificat['certificat'] = unicode(pem, 'utf-8')
certificat.create() certificat.create()
raise Continue(cont(certificat=certificat, machine=certificat.machine())) raise Continue(cont(certificat=certificat, machine=certificat.machine()))
(code, pem) = box() (code, pem) = self.handle_dialog(cont, box)
self_cont = TailCall(self.create_certificat, machine=machine, cont=cont) self_cont = TailCall(self.create_certificat, machine=machine, certificat=certificat, cont=cont)
return self.handle_dialog_result( return self.handle_dialog_result(
code=code, code=code,
output=pem, output=pem,
cancel_cont=cont, cancel_cont=cont,
error_cont=self_cont, error_cont=self_cont,
codes_todo=[([self.dialog.DIALOG_OK], todo, [machine, cont])] codes_todo=[([self.dialog.DIALOG_OK], todo, [machine, certificat, cont])]
)
@tailcaller
def get_password(self, cont, confirm=True):
def todo(self_cont, cont):
(code, pass1) = self.dialog.passwordbox("Entrez un mot de passe", title="Choix d'un mot de passe")
if code != self.dialog.DIALOG_OK:
raise Continue(cont)
if confirm:
(code, pass2) = self.dialog.passwordbox("Comfirmer le mot de passe", title="Choix d'un mot de passe")
if code != self.dialog.DIALOG_OK:
raise Continue(self_cont)
if pass1 != pass2:
raise ValueError("Les deux mots de passe ne concordent pas")
return pass1
self_cont = TailCall(self.get_password, cont=cont)
return self.handle_dialog_result(
code=self.dialog.DIALOG_OK,
output="",
cancel_cont=cont,
error_cont=self_cont,
codes_todo=[([self.dialog.DIALOG_OK], todo, [self_cont, cont])]
)
def create_privatekey(self, cont, machine=None, certificat=None, imp=False, size=4096):
"""Permet de générer ou importer une clef privée à une machine"""
if machine is None and certificat is None:
raise EnvironmentError("Il faut fournir au moins une machine ou un certificat")
# input multiline en utilisant un editbox
def box():
fp, path = tempfile.mkstemp()
os.close(fp)
cmd = ['--editbox', path, "0", "0"]
(code, output) = self.dialog._perform(*(cmd,),
no_mouse=True, # On désactive la sourie sinon dialog segfault si on clic
backtitle="Appuyez sur CTRL+MAJ+V pour coller",
timeout=self.timeout,
title="Création d'un certificat, entrez le PEM du certificat")
os.remove(path)
if code == self.dialog.DIALOG_OK:
return code, output
else:
return code, None
def todo(machine, certificat, pem, imp, size, cont):
if not imp:
if not machine:
machine=certificat.machine()
if "machineCrans" in machine['objectClass']:
passphrase = secrets.get('privatekey_passphrase')
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,
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)
pem = crypto.PKey()
pem.generate_key(crypto.TYPE_RSA, size)
pem = crypto.dump_privatekey(crypto.FILETYPE_PEM, pem, "des3", passphrase)
elif not pem.startswith("-----BEGIN ENCRYPTED PRIVATE KEY-----"):
raise ValueError("On n'accepte que des clef chiffrée PKCS#8 en PEM. Donc la clef doit commencer par -----BEGIN ENCRYPTED PRIVATE KEY-----")
if certificat:
if "privatekey" in certificat:
raise ValueError("Il y a déjà une clef privée, merci d'annuler")
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")
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")
raise Continue(cont(certificat=certificat, machine=certificat.machine()))
if imp:
(code, pem) = self.handle_dialog(cont, box)
else:
(code, pem) = (self.dialog.DIALOG_OK, "")
self_cont = TailCall(self.create_privatekey, machine=machine, certificat=certificat, cont=cont, imp=imp, size=size)
return self.handle_dialog_result(
code=code,
output=pem,
cancel_cont=cont,
error_cont=self_cont,
codes_todo=[([self.dialog.DIALOG_OK], todo, [machine, certificat, pem, imp, size, cont])]
) )
def delete_certificat(self, certificat, cont): def delete_certificat(self, certificat, cont):
@ -1068,6 +1199,133 @@ 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"]:
passphrase = secrets.get('privatekey_passphrase')
else:
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)
passphrase = self.get_password(cont, confirm=False)
else:
passphrase = None
try:
if passphrase:
pkey = crypto.load_privatekey(crypto.FILETYPE_PEM, str(certificat['privatekey'][0]), passphrase)
else:
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")
raise Continue(self_cont)
else:
raise
req = createCertRequest(pkey,
digest="sha1",
subjectAltName=[str(host) for host in certificat['hostCert'][1:]],
C=u"FR",
ST=u"Ile de France",
L=u"Cachan",
O=u"Association Cachan Réseaux A Normal SUP (C.R.A.N.S)",
OU=u"Crans",
CN=unicode(certificat['hostCert'][0]),
)
csr = crypto.dump_certificate_request(crypto.FILETYPE_PEM, req)
with self.conn.search(dn=certificat.dn, scope=0, mode='rw')[0] as certificat:
certificat['csr']=unicode(csr)
certificat.save()
self.handle_dialog(cont, box, csr)
if self.dialog.yesno("Remplacer le certificat actuel ?") == self.dialog.DIALOG_OK:
return self.create_certificat(certificat=certificat, cont=cont(certificat=certificat))
else:
raise Continue(cont(certificat=certificat))
self_cont = TailCall(self.gen_csr, certificat=certificat, cont=cont)
return self.handle_dialog_result(
code=self.dialog.DIALOG_OK,
output="",
cancel_cont=cont,
error_cont=self_cont,
codes_todo=[([self.dialog.DIALOG_OK], todo, [certificat, self_cont, cont])]
)
def get_certificat(self, certificat, cont, privatekey=False, csr=False):
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
if privatekey:
self.handle_dialog(cont, box, unicode(certificat['privatekey'][0]))
elif csr:
self.handle_dialog(cont, box, unicode(certificat['csr'][0]))
else:
self.handle_dialog(cont, box, unicode(ssl.DER_cert_to_PEM_cert(str(certificat['certificat'][0]))))
raise Continue(cont)
def create_csr(self, cont, machine=None, certificat=None):
"""Permet d'ajouter un csr à une machine à partir du PEM du csr"""
if machine is None and certificat is None:
raise EnvironmentError("Il faut fournir au moins une machine ou un certificat")
# input multiline en utilisant un editbox
def box():
fp, path = tempfile.mkstemp()
os.close(fp)
cmd = ['--editbox', path, "0", "0"]
(code, output) = self.dialog._perform(*(cmd,),
no_mouse=True, # On désactive la sourie sinon dialog segfault si on clic
backtitle="Appuyez sur CTRL+MAJ+V pour coller",
timeout=self.timeout,
title="Création d'un certificat, entrez le PEM du certificat")
os.remove(path)
if code == self.dialog.DIALOG_OK:
return code, output
else:
return code, None
def todo(machine, certificat, pem, cont):
if certificat:
with self.conn.search(dn=certificat.dn, scope=0, mode='rw')[0] as certificat:
certificat['csr'] = unicode(pem, 'utf-8')
certificat.save()
else:
with self.conn.newCertificat(machine.dn, {}) as certificat:
certificat['hostCert']=unicode(machine['host'][0])
certificat['csr'] = unicode(pem, 'utf-8')
certificat.create()
raise Continue(cont(certificat=certificat, machine=certificat.machine()))
(code, pem) = self.handle_dialog(cont, box)
self_cont = TailCall(self.create_csr, machine=machine, certificat=certificat, cont=cont)
return self.handle_dialog_result(
code=code,
output=pem,
cancel_cont=cont,
error_cont=self_cont,
codes_todo=[([self.dialog.DIALOG_OK], todo, [machine, certificat, pem, cont])]
)
def modif_machine_certificat(self, machine, cont, tag=None, certificat=None): def modif_machine_certificat(self, machine, cont, tag=None, certificat=None):
"""Permet l'édition d'un certificat d'une machine""" """Permet l'édition d'un certificat d'une machine"""
self_cont = TailCall(self.modif_machine_certificat, machine=machine, cont=cont, certificat=certificat) self_cont = TailCall(self.modif_machine_certificat, machine=machine, cont=cont, certificat=certificat)
@ -1075,33 +1333,69 @@ les valeurs valident sont :
certificat_index = self.edit_certificat_select(machine=machine, title="Modification des certificats de %s" % machine['host'][0], cont=cont) certificat_index = self.edit_certificat_select(machine=machine, title="Modification des certificats de %s" % machine['host'][0], cont=cont)
if certificat_index == 'new': if certificat_index == 'new':
raise Continue(TailCall(self.create_certificat, machine=machine, cont=self_cont)) raise Continue(TailCall(self.create_certificat, machine=machine, cont=self_cont))
elif certificat_index == 'priv':
raise Continue(TailCall(self.create_privatekey, machine=machine, cont=self_cont))
elif certificat_index == 'csr':
raise Continue(TailCall(self.create_csr, machine=machine, cont=self_cont))
certificat = machine.certificats()[certificat_index] certificat = machine.certificats()[certificat_index]
menu = { menu = {
'Hostname' : {'text':"Noms d'hôte utilisant le certificat", "attribut":attributs.hostCert}, '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},
'TLSA' : {'text':"Paramètres pour les champs dns TLSA", "callback":self.certificat_tlsa}, 'AddPrivateKey' : {'text': 'Ajouter la clef privée', 'help':'La clef doit être obligatoirement chiffrée avant envoi', "callback":TailCall(self.create_privatekey, imp=True)},
'Autre': {'text' : "Modifier les attribut booléen comme revocked", "callback":self.modif_certificat_boolean}, 'AddCertificate' : {'text': 'Ajouter un certificat X509', 'help':'', "callback":self.create_certificat},
'Supprimer' : {'text' : "Supprimer le certificat", "callback":self.delete_certificat}, 'SetCertificate' : {'text': 'Remplacer le certificat X509', 'help':'', "callback":self.create_certificat},
'TLSA' : {'text':"Paramètres pour les champs dns TLSA", 'help':'Permet de configurer DANE pour le certificat X509', "callback":self.certificat_tlsa},
'Autre': {'text' : "Modifier les attribut booléen comme revocked", 'help':'', "callback":self.modif_certificat_boolean},
'GetPriv' : {'text' : 'Récupérer la clef privée', 'help':"Affiche la clef privée telle qu'elle est dans la base de donnée, c'est à dire chiffrée", 'callback':TailCall(self.get_certificat, privatekey=True)},
'GetCert' : {'text' : 'Récupérer le certificat', 'help':"Affiche le certificat au format PEM", 'callback':self.get_certificat},
'GetCSR' : {'text' : 'Récupérer la requête de signature de certificat', 'help':"Affiche le CSR au format PEM", 'callback':TailCall(self.get_certificat, csr=True)},
'GenCSR' : {'text' : 'Générer un CSR, puis remplacer le certificat', 'help':'Généré à partir de la clef privée. Les noms (CN et subjectAltName) sont pris à partir de Hostname (attribut hostCert)', "callback":self.gen_csr},
'Remarque' : {'text': 'Mettre des remarques', 'help':'La première apparait dans la liste des certificats', 'attribut':attributs.info},
'Supprimer' : {'text' : "Supprimer le certificat", 'help':'', "callback":self.delete_certificat},
} }
menu_order = ['Hostname', 'TLSA', 'Autre', 'Supprimer'] if "privateKey" in certificat["objectClass"]:
menu
menu_order = ['Hostname']
if not "privateKey" in certificat['objectClass']:
menu_order.append('AddPrivateKey')
if not "x509Cert" in certificat['objectClass']:
menu_order.extend([ 'AddCertificate'])
if "x509Cert" in certificat['objectClass']:
menu_order.extend(['TLSA', 'Autre', 'GetCert'])
if certificat['csr']:
menu_order.extend(['GetCSR'])
if "privateKey" in certificat['objectClass']:
if attributs.nounou in self.conn.droits or machine.dn.startswith(self.conn.dn):
menu_order.extend(['GetPriv'])
menu_order.extend(['GenCSR'])
menu_order.extend(['Remarque', 'Supprimer'])
def box(default_item=None): def box(default_item=None):
return self.dialog.menu( text="Certificat de %s, xid=%s :\n" % (certificat['hostCert'][0], certificat['xid'][0])
"Paramètres pour le certificat N°0x%X émis par %s, valable du %s au %s" % ( if "x509Cert" in certificat['objectClass']:
text += " * Certificat N°0x%X émis par %s, valable du %s au %s\n" % (
int(str(certificat['serialNumber'][0])), int(str(certificat['serialNumber'][0])),
certificat['issuerCN'][0], certificat['issuerCN'][0],
time.strftime("%d/%m/%Y", time.localtime(int(certificat['start'][0]))), time.strftime("%d/%m/%Y", time.localtime(int(certificat['start'][0]))),
time.strftime("%d/%m/%Y", time.localtime(int(certificat['end'][0]))) time.strftime("%d/%m/%Y", time.localtime(int(certificat['end'][0])))
), )
if "privateKey" in certificat['objectClass']:
text += " * Clef privée\n"
if certificat['csr']:
text += " * Requête de signature de certificat\n"
if certificat['info']:
text += str(certificat['info'][0])
return self.dialog.menu(
text,
width=0, width=0,
height=0, height=0,
menu_height=0, menu_height=0,
item_help=0, item_help=1,
timeout=self.timeout, timeout=self.timeout,
default_item=str(default_item), default_item=str(default_item),
title="Modification des certificats de %s" % certificat.machine()['host'][0], title="Modification des certificats de %s" % certificat.machine()['host'][0],
scrollbar=True, scrollbar=True,
cancel_label="Retour", cancel_label="Retour",
backtitle=u"Vous êtes connecté en tant que %s" % self.conn.current_login, 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'], menu[key]['help']) for key in menu_order])
def todo(tag, menu, certificat, self_cont): def todo(tag, menu, certificat, self_cont):
if not tag in menu_order: if not tag in menu_order:
@ -1113,8 +1407,8 @@ les valeurs valident sont :
raise Continue(TailCall(self.modif_certificat_attributs, certificat=certificat, cont=self_cont(certificat=certificat, tag=tag), attr=menu[tag]['attribut'].ldap_name)) raise Continue(TailCall(self.modif_certificat_attributs, certificat=certificat, cont=self_cont(certificat=certificat, tag=tag), attr=menu[tag]['attribut'].ldap_name))
else: else:
raise EnvironmentError("Il n'y a ni champ 'attribut' ni 'callback' pour le tag %s" % tag) raise EnvironmentError("Il n'y a ni champ 'attribut' ni 'callback' pour le tag %s" % tag)
(code, tag) = box(tag) (code, tag) = self.handle_dialog(cont, box, tag)
cancel_cont = cont(machine=machine) if certificat is None else self_cont(certificat=None) cancel_cont = cont(machine=machine) if certificat is None else self_cont(machine=certificat.machine(), certificat=None)
return self.handle_dialog_result( return self.handle_dialog_result(
code=code, code=code,
output=tag, output=tag,
@ -1129,8 +1423,18 @@ les valeurs valident sont :
def box(default_item=None): def box(default_item=None):
index=0 index=0
choices = [('new', 'Ajouter un nouveau certificat')] 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'))
for cert in machine.certificats(): for cert in machine.certificats():
choices.append((str(index), "Emit par %s pour %s du %s au %s" % (cert['issuerCN'][0], ', '.join(str(cn) for cn in cert['hostCert']), time.strftime("%d/%m/%Y", time.localtime(int(cert['start'][0]))), time.strftime("%d/%m/%Y", time.localtime(int(cert['end'][0])))))) if cert['info']:
item = str(cert['info'][0])
elif "x509Cert" in cert['objectClass']:
item = "Emit par %s pour %s du %s au %s" % (cert['issuerCN'][0], ', '.join(str(cn) for cn in cert['hostCert']), time.strftime("%d/%m/%Y", time.localtime(int(cert['start'][0]))), time.strftime("%d/%m/%Y", time.localtime(int(cert['end'][0]))))
elif "privateKey" in cert['objectClass']:
item = "Clef privée de %s, xid=%s" % (cert['hostCert'][0], cert['xid'][0])
elif cert['csr']:
item = "Requête de signature de certificat pour %s, xid=%s" % (cert['hostCert'][0], cert['xid'][0])
choices.append((str(index), item))
index+=1 index+=1
return self.dialog.menu( return self.dialog.menu(
"Modifier ou ajouter un certificat ?", "Modifier ou ajouter un certificat ?",
@ -1147,17 +1451,17 @@ les valeurs valident sont :
choices=choices) choices=choices)
def todo(tag): def todo(tag):
if tag == 'new': if tag in ['new', 'priv', 'csr']:
return tag return tag
else: else:
return int(tag) return int(tag)
(code, tag) = box() (code, tag) = self.handle_dialog(cont, box)
retry_cont = TailCall(self.edit_certificat_select, machine=machine, title=title, cont=cont) retry_cont = TailCall(self.edit_certificat_select, machine=machine, title=title, cont=cont)
return self.handle_dialog_result( return self.handle_dialog_result(
code=code, code=code,
output=tag, output=tag,
cancel_cont=cont, cancel_cont=cont(machine=machine),
error_cont=retry_cont, error_cont=retry_cont,
codes_todo=[([self.dialog.DIALOG_OK], todo, [tag])] codes_todo=[([self.dialog.DIALOG_OK], todo, [tag])]
) )
@ -1241,7 +1545,7 @@ les valeurs valident sont :
else: else:
raise EnvironmentError("Il n'y a ni champ 'attribut' ni 'callback' pour le tag %s" % tag) raise EnvironmentError("Il n'y a ni champ 'attribut' ni 'callback' pour le tag %s" % tag)
(code, tag) = box(tag) (code, tag) = self.handle_dialog(cont, box, tag)
cont_ret = TailCall(self.modif_machine, cont=cont, machine=machine, tag=tag) cont_ret = TailCall(self.modif_machine, cont=cont, machine=machine, tag=tag)
return self.handle_dialog_result( return self.handle_dialog_result(
@ -1280,19 +1584,19 @@ les valeurs valident sont :
backtitle=u"Vous êtes connecté en tant que %s" % self.conn.current_login, 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])
def todo(tag, menu, proprio, self_cont): def todo(tag, menu, proprio, self_cont, cont):
if not tag in menu_order: if not tag in menu_order:
raise Continue(self_cont) raise Continue(self_cont)
else: else:
return self.machine_information( return self.machine_information(
cont=self_cont, cont=cont,
machine=None, machine=None,
objectClass=menu[tag]['objectClass'], objectClass=menu[tag]['objectClass'],
proprio=proprio, proprio=proprio,
realm=menu[tag]['realm'] realm=menu[tag]['realm']
) )
(code, tag) = box(tag) (code, tag) = self.handle_dialog(cont, box, tag)
cont = cont(proprio=None) if isinstance(proprio, objets.AssociationCrans) else cont(proprio=proprio) 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) self_cont = TailCall(self.create_machine_proprio, cont=cont, proprio=proprio, tag=tag)
@ -1301,7 +1605,7 @@ les valeurs valident sont :
output=tag, output=tag,
cancel_cont=cont, cancel_cont=cont,
error_cont=self_cont, error_cont=self_cont,
codes_todo=[([self.dialog.DIALOG_OK], todo, [tag, menu, proprio, self_cont])] codes_todo=[([self.dialog.DIALOG_OK], todo, [tag, menu, proprio, self_cont, cont])]
) )
def create_machine_adherent(self, cont, adherent=None): def create_machine_adherent(self, cont, adherent=None):
@ -1384,7 +1688,7 @@ les valeurs valident sont :
else: else:
raise EnvironmentError("Il n'y a ni champ 'attribut' ni 'callback' pour le tag %s" % tag) raise EnvironmentError("Il n'y a ni champ 'attribut' ni 'callback' pour le tag %s" % tag)
(code, tag) = box(tag) (code, tag) = self.handle_dialog(cont, box, tag)
cont_ret = TailCall(self.modif_adherent, cont=cont, adherent=adherent, tag=tag) cont_ret = TailCall(self.modif_adherent, cont=cont, adherent=adherent, tag=tag)
return self.handle_dialog_result( return self.handle_dialog_result(
@ -1492,7 +1796,7 @@ les valeurs valident sont :
raise Continue(cont(adherent=adherent)) raise Continue(cont(adherent=adherent))
(code, tags, make_compte_crans) = box(make_compte_crans) (code, tags, make_compte_crans) = self.handle_dialog(cont, box, make_compte_crans)
# On prépare les fiels à afficher à l'utilisateur si une erreure à lieu # 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é # pendant le traitement des donnée (on n'éfface pas ce qui a déjà été entré
@ -1563,7 +1867,7 @@ les valeurs valident sont :
self.display_item(item=adherent, title="Adhérent déménagé dans la chambre %s" % output) self.display_item(item=adherent, title="Adhérent déménagé dans la chambre %s" % output)
raise Continue(success_cont(adherent=adherent)) raise Continue(success_cont(adherent=adherent))
(code, output) = box() (code, output) = self.handle_dialog(cont, box)
self_cont = TailCall(self.adherent_chambre_campus, adherent=adherent, success_cont=success_cont, cont=cont) self_cont = TailCall(self.adherent_chambre_campus, adherent=adherent, success_cont=success_cont, cont=cont)
return self.handle_dialog_result( return self.handle_dialog_result(
code=code, code=code,
@ -1658,7 +1962,7 @@ les valeurs valident sont :
else: else:
raise EnvironmentError("Impossible, on a fait tous les cas, python est buggué") raise EnvironmentError("Impossible, on a fait tous les cas, python est buggué")
(code, output) = box() (code, output) = self.handle_dialog(cont, box)
self_cont = TailCall(self.adherent_chambre_ext, adherent=adherent, keep_machine=keep_machine, keep_compte=keep_compte, success_cont=success_cont, cont=cont) self_cont = TailCall(self.adherent_chambre_ext, adherent=adherent, keep_machine=keep_machine, keep_compte=keep_compte, success_cont=success_cont, cont=cont)
return self.handle_dialog_result( return self.handle_dialog_result(
code=code, code=code,
@ -1697,7 +2001,7 @@ les valeurs valident sont :
else: else:
raise Continue(TailCall(menu[tag]['callback'], cont=self_cont, success_cont=cont, adherent=adherent)) raise Continue(TailCall(menu[tag]['callback'], cont=self_cont, success_cont=cont, adherent=adherent))
(code, tag) = box(default_item) (code, tag) = self.handle_dialog(cont, box, default_item)
self_cont = TailCall(self.adherent_chambre, adherent=adherent, cont=cont, default_item=tag) self_cont = TailCall(self.adherent_chambre, adherent=adherent, cont=cont, default_item=tag)
return self.handle_dialog_result( return self.handle_dialog_result(
code=code, code=code,
@ -1808,7 +2112,7 @@ les valeurs valident sont :
title="Choix du mot de passe pour %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]), title="Choix du mot de passe pour %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]),
) )
raise Continue(cont(proprio=proprio)) raise Continue(cont(proprio=proprio))
(code, passwords) = box() (code, passwords) = self.handle_dialog(cont, box)
self_cont = TailCall(self.proprio_compte_password, proprio=proprio, cont=cont) self_cont = TailCall(self.proprio_compte_password, proprio=proprio, cont=cont)
return self.handle_dialog_result( return self.handle_dialog_result(
code=code, code=code,
@ -1911,7 +2215,7 @@ les valeurs valident sont :
else: else:
raise Continue(TailCall(menu[tag]['callback'], cont=self_cont, proprio=proprio)) raise Continue(TailCall(menu[tag]['callback'], cont=self_cont, proprio=proprio))
(code, tag) = box(default_item) (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[tag])
return self.handle_dialog_result( return self.handle_dialog_result(
code=code, code=code,
@ -1956,7 +2260,7 @@ les valeurs valident sont :
self.check_ldap() self.check_ldap()
raise Continue(cont(adherent=adherent)) raise Continue(cont(adherent=adherent))
(code, droits) = box() (code, droits) = self.handle_dialog(cont, box)
self_cont = TailCall(self.adherent_droits, adherent=adherent, cont=cont, choices_values=[(d, "", 1 if d in droits else 0) for d in attributs.TOUS_DROITS]) self_cont = TailCall(self.adherent_droits, adherent=adherent, cont=cont, choices_values=[(d, "", 1 if d in droits else 0) for d in attributs.TOUS_DROITS])
return self.handle_dialog_result( return self.handle_dialog_result(
code=code, code=code,
@ -2112,12 +2416,13 @@ les valeurs valident sont :
backtitle=u"Vous êtes connecté en tant que %s" % self.conn.current_login, 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=[(key, menu.get(key, menu[''])['text'], menu.get(key, menu['']).get('help', "")) for key in menu_order])
(code, tag) = box(tag) 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)
callback = menu.get(tag, menu[''])['callback'] callback = menu.get(tag, menu[''])['callback']
if handle_exit_code(self.dialog, code) and callback: if handle_exit_code(self.dialog, code) and callback:
return TailCall(callback, cont=TailCall(self.menu_principal, tag=tag, machine=machine, proprio=proprio)) return TailCall(callback, cont=TailCall(self.menu_principal, tag=tag, machine=machine, proprio=proprio))
else: else:
return TailCall(self.menu_principal, tag=tag, proprio=proprio, machine=machine) return self_cont
def main(gc, cont=None): def main(gc, cont=None):
while True: while True: