From 84777603029e5506087a83df83a5abcf656df85f Mon Sep 17 00:00:00 2001 From: Valentin Samir Date: Sat, 22 Nov 2014 12:54:14 +0100 Subject: [PATCH] =?UTF-8?q?[attributs,=20objets]=20Une=20fonction=20pour?= =?UTF-8?q?=20g=C3=A9n=C3=A9rer=20automatiquement=20une=20ligne=20d'histor?= =?UTF-8?q?ique?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Elle s'appelle history_gen. Il faut l'appeler explicitement pour le moment, pour éviter de mettre des lignes en double vu que jusqu'à maintenant, historique était fait à la main. Il y a 4 niveaux d'historique pour les attributs : * full on loggue toutes les modifications pour un singlevalue : nom (Durant -> Dupond") pour les autres : mailAlias+toto@free.fr-titi@orange.com * partial, comme full sauf qu'on limite la longeur de chaque valeur d'attribut à au plus 15 caractères * info, on signalute juste que l'attribut attribut a été créer, supprimer ou modifier: * None, on n'ajoute pas de ligne (par exemple pour l'historique lui même, on le loggue pas ses modifications) Ajoutez en d'autre si vous pensez à des trucs cools --- attributs.py | 9 ++++++ objets.py | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/attributs.py b/attributs.py index c5ca1c5..7bbd0de 100644 --- a/attributs.py +++ b/attributs.py @@ -256,6 +256,7 @@ class Attr(object): conn = None unique = False concurrent = True + historique = "full" # valeurs possibles "full", "partial", "info", None unique_exclue = [] #: Le nom de l'attribut dans le schéma LDAP ldap_name = None @@ -1022,6 +1023,7 @@ class ipsec(Attr): category = 'wifi' ldap_name = "ipsec" can_modify = [nounou, parent] + historique = "info" default = u'auto' def parse_value(self, val): @@ -1313,6 +1315,7 @@ class historique(Attr): ldap_name = "historique" # Il faudrait faire quelque chose pour que soi, parent, cableur ne puissent que ajouter de nouvelles lignes can_modify = [nounou, soi, parent, cableur] + historique = None # Thanks to 20-100, on va devoir gérer deux cas FORMAT = "%d/%m/%Y %H:%M:%S" @@ -1334,6 +1337,7 @@ class info(Attr): category = 'info' can_modify = [nounou, cableur, bureau] ldap_name = "info" + historique = "info" @crans_attribute class homepageAlias(Attr): @@ -1427,6 +1431,7 @@ class userPassword(Attr): category = '' ldap_name = "userPassword" can_modify = [nounou, bureau, cableur] + historique = "info" def is_modifiable(self, liste_droits): """ @@ -1452,6 +1457,7 @@ class sshFingerprint(Attr): can_modify = [parent, nounou] ldap_name = "sshFingerprint" python_type = dict + historique = "partial" def parse_value(self, key): if isinstance(key, self.python_type): @@ -1692,6 +1698,7 @@ class privatekey(Attr): python_type = str can_modify = [parent, nounou] legend = "Clef privée" + historique = "info" @crans_attribute class csr(Attr): @@ -1699,6 +1706,7 @@ class csr(Attr): python_type = str can_modify = [parent, nounou] legend = "requête de signature de certificat" + historique = "info" def _format_cert(self, csr): import OpenSSL @@ -1728,6 +1736,7 @@ class certificat(Attr): python_type = str can_modify = [parent, nounou] legend = "Certificat" + historique = "info" def __init__(self, *args, **kwargs): super(certificat, self).__init__(*args, **kwargs) diff --git a/objets.py b/objets.py index e875244..a161b90 100644 --- a/objets.py +++ b/objets.py @@ -299,9 +299,91 @@ class CransLdapObject(object): assert isinstance(chain, unicode) new_line = u"%s, %s : %s" % (time.strftime(attributs.historique.FORMAT), login, chain) - # Attention, le __setitem__ est surchargé, mais pas .append sur l'historique self["historique"] = self.get("historique", []) + [new_line] + def history_gen(self, attr=None, login=None): + "Génère une ligne d'historique pour l'arribut attr ou une ligne par attributs pour l'objet courant" + if attr is None: + for attr in self.attrs.keys(): + self.history_gen(attr) + def partial_name(name, max_len=14, start=7, end=7): + if len(name) > max_len: + return "%s…%s" % (name[:start], name[-end:]) + else: + return name + if login is None: + login = self.conn.current_login + if isinstance(attr, str) or isinstance(attr, unicode): + attr = attributs.AttributeFactory.get(attr) + elif isinstance(attr, Attr): + attr = type(attr) + elif issubclass(attr, Attr): + pass + else: + raise ValueError("%r ne correspont pas a un attribut" % attr) + if not attr.historique: + return + if attr.historique not in ["full", "partial", "info"]: + raise ValueError("Format d'historique %s inconnu" % attr.historique) + old_values = self.attrs[attr.ldap_name] + new_values = self._modifs[attr.ldap_name] + if old_values == new_values: + return + comm = None + if attr.singlevalue: + # modification + if old_values and new_values: + if attr.historique == "full": + comm = u"%s (%s -> %s)" % (attr.ldap_name, old_values[0], new_values[0]) + elif attr.historique == "partial": + old = partial_name(str(old_values[0])) + new = partial_name(str(new_values[0])) + comm = u"%s (%s -> %s)" % (attr.ldap_name, old, new) + elif attr.historique == "info": + comm = u"%s" % attr.ldap_name + # création + elif not old_values and new_values: + if attr.historique == "info": + comm = u"+%s" % attr.ldap_name + elif attr.historique in ["full", "partial"]: + new = str(new_values[0]) + if attr.historique == "partial": + new = partial_name(new) + comm = u"%s+%s" % (attr.ldap_name, new) + # suppréssion + elif not new_values and old_values: + if attr.historique == "info": + comm = u"-%s" % attr.ldap_name + elif attr.historique in ["full", "partial"]: + old = str(old_values[0]) + if attr.historique == "partial": + old = partial_name(old) + comm = u"%s-%s" % (attr.ldap_name, old) + else: + added = [] + deleted = [] + if attr.historique == "partial": + append = lambda x: partial_name(str(x)) + else: + append = lambda x: str(x) + for a in new_values: + if not a in old_values: + added.append(append(a)) + for a in old_values: + if not a in new_values: + deleted.append(append(a)) + if attr.historique == "info": + comm = u"%s%s%s" % ('+' if added else "", '-' if deleted else "", attr.ldap_name) + elif attr.historique in ["full", "partial"]: + comm = u"%s%s%s%s%s" % (attr.ldap_name, '+' if added else "", '+'.join(added), '-' if deleted else "", '-'.join(deleted)) + + if comm: + new_line = u"%s, %s : %s" % (time.strftime(attributs.historique.FORMAT), login, comm) + if not new_line in self["historique"]: + self["historique"].append(new_line) + else: + raise ValueError("impossible de générer l'historique pour %s %s %s" % (attr, old_values, new_values)) + def _check_optionnal(self, comment): """Vérifie que les attributs qui ne sont pas optionnels sont effectivement peuplés.""" objet = self.ldap_name