From 180c17baedba9a9d6ab66b4bff51a912a9392161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Mon, 8 Apr 2013 00:12:00 +0200 Subject: [PATCH] =?UTF-8?q?[global]=20On=20jette=20ctxt=5Fcheck,=20et=20le?= =?UTF-8?q?s=20ldifs=20qui=20tra=C3=AEnent=20partout,=20qu'on=20remplace.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Les ldifs sont remplacés par un pointeur vers l'objet propriétaire des attributs * Le test de cardinalité est fait lors du setitem dans AttrsDict * Le test d'unicité dans le setitem d'un CransLdapObject. Le setitem vérifie également qu'on affecte pas des attributs non désirés à un objet --- attributs.py | 304 ++++++++++++++++++++++++++------------------------- lc_ldap.py | 38 +++---- 2 files changed, 172 insertions(+), 170 deletions(-) diff --git a/attributs.py b/attributs.py index 5cd7d3e..7054303 100644 --- a/attributs.py +++ b/attributs.py @@ -75,28 +75,34 @@ DROITS_SUPERVISEUR = { nounou : TOUS_DROITS, bureau : DROITS_FAIBLES + [bureau, tresorier], } +class SingleValueError(ValueError): + pass -def attrify(val, attr, ldif, conn, ctxt_check = True): +class UniquenessError(EnvironmentError): + pass + +class OptionalError(EnvironmentError): + pass + +def attrify(val, attr, conn, Parent=None): """Transforme un n'importe quoi en :py:class:`Attr`. Doit effectuer les normalisations et sanity check si un str ou un unicode est passé en argument. Devrait insulter en cas de potentiel problème d'encodage. - Doit effectuer les vérifications de contexte dans le ``ldif`` si - ``ctxt_check`` est vrai""" + """ if isinstance(val, Attr): return val else: - return CRANS_ATTRIBUTES.get(attr, Attr)(val, ldif, conn, ctxt_check) + return CRANS_ATTRIBUTES.get(attr, Attr)(val, conn, Parent) class AttrsDict(dict) : - def __init__(self, conn, ldif = {}, check_ctxt = True): + def __init__(self, conn, ldif={}, Parent=None): super(AttrsDict, self).__init__(ldif) self._conn = conn - self._ldif = ldif - self._check_ctxt = check_ctxt + self._parent = Parent def __getitem__(self, attr): values = super(AttrsDict, self).__getitem__(attr) @@ -105,12 +111,23 @@ class AttrsDict(dict) : output = [] for val in values: if not isinstance(val, Attr): - output.append(attrify(unicode(val, 'utf-8'), attr, self._ldif, self._conn, self._check_ctxt)) + output.append(attrify(unicode(val, 'utf-8'), attr, self._conn, self._parent)) else: output.append(val) self[attr] = output return output + def __setitem__(self, attr, values): + # ne devrait par arriver + if not isinstance(values, list): + values = [ values ] + if self._parent.mode in ['w', 'rw']: + if CRANS_ATTRIBUTES.get(attr, Attr).singlevalue and len(values) > 1: + raise SingleValueError("L'attribut %s doit être monovalué.") + super(AttrsDict, self).__setitem__(attr, values) + else: + super(AttrsDict, self).__setitem__(attr, values) + def get(self, value, default_value): try: return self[value] @@ -124,36 +141,33 @@ class Attr(object): * ``val`` : valeur de l'attribut * ``ldif`` : objet contenant l'attribut (permet de faire les validations sur l'environnement) - * ``ctxt_check`` : effectue les validations """ legend = "Human-readable description of attribute" - singlevalue = None - optional = None + singlevalue = False + optional = True conn = None unique = False - can_modify = [nounou] """La liste des droits qui suffisent à avoir le droit de modifier la valeur""" + can_modify = [nounou] - can_view = [nounou, apprenti, soi, parent, respo] """Qui peut voir l'attribut. Par défaut, les Nounous et les Apprentis peuvent tout voir. Par transparence, et par utilité, on autorise par défaut l'adhérent à voir les données le concernant.""" + can_view = [nounou, apprenti, soi, parent, respo] - category = 'other' """Catégorie de l'attribut (pour affichage futur)""" + category = 'other' - def __init__(self, val, ldif, conn, ctxt_check): + def __init__(self, val, conn, Parent): """Crée un nouvel objet représentant un attribut. """ - self.ctxt_check=ctxt_check - #self.value = None + self.value = None self.conn = conn assert isinstance(val, unicode) - self.parse_value(val, ldif) - if ctxt_check: - self.validate(ldif) + self.parent = Parent + self.parse_value(val) - def parse_value(self, val, ldif): + def parse_value(self, val): """Transforme l'attribut pour travailler avec notre validateur Le ldif est en dépendance car à certains endroits, il peut servir (par exemple, pour l'ipv6, ou l'ipv4...""" @@ -167,37 +181,16 @@ class Attr(object): assert isinstance(self.value, unicode) return self.value - def validate(self, ldif): - """validates: - vérifie déjà que ce qu'on a rentré est parsable""" - if not self.__class__.__name__ in CRANS_ATTRIBUTES: - raise ValueError('Attribut %s inconnu' % self.__class__.__name__) - - # On utilise un ldif parce qu'il faut avoir un contexte - # pour savoir si l'attribut qu'on est en train de créer - # est légitime. - own_values = ldif.get(self.__class__.__name__, []) - self._check_cardinality(own_values) - self._check_uniqueness() - - def _check_cardinality(self, values): - """Vérifie qu'il y a un nombre correct de valeur =1, <=1, {0,1}, - etc...""" - if self.singlevalue and len(values) > 1: - raise ValueError('%s doit avoir au maximum une valeur (affecte %s)' % (self.__class__, values)) - # A priori, ça sert à rien, on fait du for val in values, si la liste - # est vide, le type d'attribut généré n'est pas pris en compte. - if not self.optional and len(values) == 0: - raise ValueError('%s doit avoir au moins une valeur' % self.__class__) - - def _check_uniqueness(self): + def check_uniqueness(self, liste_exclue): """Vérifie l'unicité dans la base de la valeur (``mailAlias``, ``chbre``, etc...)""" attr = self.__class__.__name__ + if str(self) in liste_exclue: + return if self.unique: res = self.conn.search('%s=%s' % (attr, str(self))) if res: - raise ValueError("%s déjà existant" % attr, [r.dn for r in res]) + raise UniquenessError("%s déjà existant" % attr, [r.dn for r in res]) def is_modifiable(self, liste_droits): """ @@ -210,13 +203,13 @@ class objectClass(Attr): optional = False legend = "entité" - can_modify = [] """ Personne ne doit modifier de classe """ + can_modify = [] - can_view = [nounou, apprenti] """ Internal purpose (et à fin pédagogique) """ + can_view = [nounou, apprenti] - def parse_value(self, val, ldif): + def parse_value(self, val): if val not in [ 'top', 'organizationalUnit', 'posixAccount', 'shadowAccount', 'proprio', 'adherent', 'club', 'machine', 'machineCrans', 'borneWifi', 'machineWifi', 'machineFixe', @@ -227,7 +220,7 @@ class objectClass(Attr): class intAttr(Attr): - def parse_value(self, val, ldif): + def parse_value(self, val): if int(val) <= 0: raise ValueError("Valeur entière invalide : %r" % val) self.value = int(val) @@ -236,7 +229,7 @@ class intAttr(Attr): return unicode(self.value) class boolAttr(Attr): - def parse_value(self, val, ldif): + def parse_value(self, val): if val.lower() in [u'true', u'ok']: self.value = True elif val.lower() == u'false': @@ -256,13 +249,13 @@ class aid(intAttr): category = 'id' unique = True - can_modify = [] """ Personne ne devrait modifier un attribut d'identification """ + can_modify = [] - can_view = [nounou, apprenti, 'Cableurs'] + can_view = [nounou, apprenti, cableur] - def parse_value(self, val, ldif): - self.value = int(val) + def parse_value(self, aid): + self.value = int(aid) class uid(Attr): singlevalue = True @@ -278,11 +271,14 @@ class nom(Attr): category = 'perso' can_modify = [nounou, cableur] - def parse_value(self, val, ldif): - if u'club' in ldif['objectClass']: - self.value = validate_name(val,"0123456789\[\]") + def parse_value(self, nom): + if self.parent != None: + if u'club' in self.parent['objectClass']: + self.value = validate_name(nom,"0123456789\[\]") + else: + self.value = validate_name(nom) else: - self.value = validate_name(val) + self.value = validate_name(nom) class prenom(Attr): singlevalue = True @@ -291,8 +287,8 @@ class prenom(Attr): category = 'perso' can_modify = [nounou, cableur] - def parse_value(self, val, ldif): - self.value = validate_name(val) + def parse_value(self, prenom): + self.value = validate_name(prenom) class compteWiki(Attr): singlevalue = False @@ -301,8 +297,8 @@ class compteWiki(Attr): category = 'perso' can_modify = [nounou, cableur, soi] - def parse_value(self, val, ldif): - self.value = validate_name(val) + def parse_value(self, compte): + self.value = validate_name(compte) # TODO: validate with mdp for user definition here ? class tel(Attr): @@ -312,19 +308,20 @@ class tel(Attr): category = 'perso' can_modify = [soi, nounou, cableur] - def parse_value(self, val, ldif): - self.value = format_tel(val) + def parse_value(self, tel): + self.value = format_tel(tel) if len(self.value) == 0: - raise ValueError("Numéro de téléphone invalide (%r)" % val) + raise ValueError("Numéro de téléphone invalide (%r)" % tel) class yearAttr(intAttr): singlevalue = False - optional= True + optional = True - def parse_value(self, val, ldif): - if int(val) < 1998: - raise ValueError("Année invalide (%r)" % val) - self.value = int(val) + def parse_value(self, annee): + annee = int(annee) + if annee < 1998: + raise ValueError("Année invalide (%r)" % annee) + self.value = annee class paiement(yearAttr): legend = u"Paiement" @@ -348,8 +345,10 @@ class mail(Attr): can_modify = [soi, nounou, cableur] category = 'mail' - def _check_uniqueness(self): + def check_uniqueness(self, liste_exclue): attr = self.__class__.__name__ + if str(self) in liste_exclue: + return if attr in ["mailAlias", "canonicalAlias", 'mail']: mail = str(self).split('@', 1)[0] @@ -364,7 +363,7 @@ class mail(Attr): if res: raise ValueError("Le mail %s est déjà pris." % (str(self))) - def parse_value(self, mail, ldif): + def parse_value(self, mail): if not re.match('^[-_.0-9A-Za-z]+@([A-Za-z0-9]{1}[A-Za-z0-9-_]+[.])+[a-z]{2,6}$', mail): raise ValueError("Adresse mail invalide (%s)" % mail) self.value = mail @@ -377,11 +376,11 @@ class canonicalAlias(mail): legend = u"Alias mail canonique" category = 'mail' - def parse_value(self, val, ldif): - val = u".".join([ a.capitalize() for a in val.split(u'.', 1) ]) - if not re.match('^[-_.0-9A-Za-z]+@([A-Za-z0-9]{1}[A-Za-z0-9-_]+[.])+[a-z]{2,6}$', val): - raise ValueError("Alias mail invalide (%s)" % val) - self.value = val + def parse_value(self, mail): + mail = u".".join([ a.capitalize() for a in mail.split(u'.', 1) ]) + if not re.match('^[-_.0-9A-Za-z]+@([A-Za-z0-9]{1}[A-Za-z0-9-_]+[.])+[a-z]{2,6}$', mail): + raise ValueError("Alias mail invalide (%s)" % mail) + self.value = mail class mailAlias(mail): singlevalue = False @@ -391,11 +390,11 @@ class mailAlias(mail): can_modify = [soi, cableur, nounou] category = 'mail' - def parse_value(self, val, ldif): - val = val.lower() - if not re.match('^[-_.0-9A-Za-z]+@([A-Za-z0-9]{2}[A-Za-z0-9-_]+[.])+[a-z]{2,6}$', val): - raise ValueError("Alias mail invalide (%r)" % val) - self.value = val + def parse_value(self, mail): + mail = mail.lower() + if not re.match('^[-_.0-9A-Za-z]+@([A-Za-z0-9]{2}[A-Za-z0-9-_]+[.])+[a-z]{2,6}$', mail): + raise ValueError("Alias mail invalide (%r)" % mail) + self.value = mail class mailInvalide(boolAttr): optional = True @@ -418,10 +417,9 @@ class etudes(Attr): can_modify = [soi, cableur, nounou] category = 'perso' - def parse_value(self, val, ldif): + def parse_value(self, etudes): # who cares - self.value = val - + self.value = etudes class chbre(Attr): singlevalue = True @@ -431,26 +429,26 @@ class chbre(Attr): can_modify = [soi, cableur, nounou] category = 'perso' - def parse_value(self, val, ldif): - if self.ctxt_check: # Si ce n'est pas la peine de vérifier, on ne vérifie pas - if u'club' in ldif['objectClass']: - if val in annuaires_pg.locaux_clubs(): - self.value = val + def parse_value(self, chambre): + if self.parent != None: + if u'club' in self.parent['objectClass']: + if chambre in annuaires_pg.locaux_clubs(): + self.value = chambre return else: - raise ValueError("Club devrait etre en XclN, pas en %r" % val) + raise ValueError("Club devrait etre en XclN, pas en %r" % chambre) - if val in (u"EXT", u"????"): - self.value = val - return + if chambre in (u"EXT", u"????"): + self.value = chambre + return - try: - annuaires_pg.chbre_prises(val[0], val[1:]) - except NameError: - import annuaires_pg_test - annuaires_pg_test.chbre_prises(val[0], val[1:]) + try: + annuaires_pg.chbre_prises(chambre[0], chambre[1:]) + except NameError: + import annuaires_pg_test + annuaires_pg_test.chbre_prises(chambre[0], chambre[1:]) - self.value = val + self.value = chambre class droits(Attr): singlevalue = False @@ -459,10 +457,10 @@ class droits(Attr): can_modify = [nounou, bureau] #ne sert à rien ici, mais c'est tout à fait exceptionnel category = 'perso' - def parse_value(self, val, ldif): + def parse_value(self, droits): # if val.lower() not in [i.lower() for i in TOUS_DROITS]: # raise ValueError("Ces droits n'existent pas (%r)" % val) - self.value = val.capitalize() + self.value = droits.capitalize() def is_modifiable(self, liste_droits): """ @@ -481,21 +479,21 @@ class solde(Attr): legend = u"Solde d'impression" can_modify = [imprimeur, nounou, tresorier] - def parse_value(self, solde, ldif): + def parse_value(self, solde): # on évite les dépassements, sauf si on nous dit de ne pas vérifier - if self.ctxt_check and not (float(solde) >= config.impression.decouvert and float(solde) <= 1024.): + if not (float(solde) >= config.impression.decouvert and float(solde) <= 1024.): raise ValueError("Solde invalide: %r" % solde) self.value = solde class dnsAttr(Attr): category = 'dns' - def parse_value(self, dns, ldif): - dns = dns.lower() - name, net = dns.split('.', 1) - if self.ctxt_check and (net not in ['adm.crans.org', 'crans.org', 'wifi.crans.org'] or + def parse_value(self, val): + val = val.lower() + name, net = val.split('.', 1) + if (net not in ['adm.crans.org', 'crans.org', 'wifi.crans.org'] or not re.match('[a-z][-_a-z0-9]+', name)): - raise ValueError("Nom d'hote invalide %r" % dns) - self.value = dns + raise ValueError("Nom d'hote invalide %r" % val) + self.value = val class host(dnsAttr): singlevalue = True @@ -504,8 +502,10 @@ class host(dnsAttr): can_modify = [parent, nounou, cableur] category = 'base_tech' - def _check_uniqueness(self): + def check_uniqueness(self, liste_exclue): attr = self.__class__.__name__ + if str(self) in liste_exclue: + return if attr in ["host", "hostAlias"]: res = self.conn.search('(|(host=%s)(hostAlias=%s))' % ((str(self),)*2)) if res: @@ -527,13 +527,13 @@ class macAddress(Attr): category = 'base_tech' can_modify = [parent, nounou, cableur] - def parse_value(self, mac, ldif): + def parse_value(self, mac): self.value = format_mac(mac) + # XXX self.parent['ip6HostNumber'] = cequ'ilfaut def __unicode__(self): return unicode(self.value).lower() - class ipHostNumber(Attr): singlevalue = True optional = True @@ -543,9 +543,9 @@ class ipHostNumber(Attr): category = 'base_tech' can_modify = [nounou] - def parse_value(self, ip, ldif): + def parse_value(self, ip): if ip == '': - ip = ip4_of_rid(ldif['rid'][0]) + ip = ip4_of_rid(self.parent['rid'][0]) self.value = netaddr.ip.IPAddress(ip) def __unicode__(self): @@ -560,8 +560,8 @@ class ip6HostNumber(Attr): category = 'base_tech' can_modify = [nounou] - def parse_value(self, val, ldif): - ip = ip6_of_mac(ldif['macAddress'][0], int(ldif['rid'][0])) + def parse_value(self, ip6): + ip = ip6_of_mac(self.parent['macAddress'][0], int(self.parent['rid'][0])) self.value = netaddr.ip.IPAddress(ip) def __unicode__(self): @@ -574,7 +574,7 @@ class mid(Attr): legend = u"Identifiant de machine" category = 'id' - def parse_value(self, mid, ldif): + def parse_value(self, mid): self.value = int(mid) def __unicode__(self): @@ -588,7 +588,7 @@ class rid(Attr): category = 'id' can_modify = [nounou] - def parse_value(self, rid, ldif): + def parse_value(self, rid): rid = int(rid) # On veut éviter les rid qui recoupent les ipv4 finissant par @@ -644,11 +644,11 @@ class positionBorne(Attr): singlevalue = True optional = True - def parse_value(self, val, ldif): - if val == u'N/A N/A': + def parse_value(self, pos): + if pos == u'N/A N/A': self.value = u"N/A°N N/A°E" else: - lat, lon = val.split(" ") + lat, lon = pos.split(" ") self.value = u"%f°N %f°E" % (float(lat), float(lon)) class nvram(Attr): @@ -656,8 +656,9 @@ class nvram(Attr): optional = True can_modify = [nounou] - def parse_value(self, val, ldif): - pass + def parse_value(self, nvr): + # XXX - on fait quoi ici ? + self.value = nvr class portAttr(Attr): singlevalue = False @@ -666,7 +667,7 @@ class portAttr(Attr): category = 'firewall' can_modify = [nounou] - def parse_value(self, port, ldif): + def parse_value(self, port): if ":" in port: a,b = port.split(":", 1) if a: @@ -694,7 +695,6 @@ class portAttr(Attr): else: return unicode(self.value[0]) - class portTCPout(portAttr): legend = u'Port TCP ouvert vers l\'extérieur' @@ -724,8 +724,8 @@ class prise(Attr): category = 'base_tech' can_modify = [nounou] - def parse_value(self, prise, ldif): - ### Tu es une Nounou, je te fais confiance + def parse_value(self, prise): + ### Tu es Beau, je te fais confiance self.value = prise class cid(intAttr): @@ -735,7 +735,7 @@ class cid(intAttr): legend = u"Identifiant du club" category = 'id' - def parse_value(self, val, ldif): + def parse_value(self, val): self.value = int(val) class responsable(Attr): @@ -745,7 +745,7 @@ class responsable(Attr): category = 'perso' can_modify = [cableur, nounou] - def nonefunction(self, val): + def nonefunction(self, resp): """ Just... do... nothing. L'idée est qu'on initialise self.value à None @@ -755,6 +755,9 @@ class responsable(Attr): pass def get_respo(self): + """Méthode spéciale, pour aller avec + property. On génère le respo quand + c'est nécessaire, pas avant.""" if hasattr(self, "_value"): return self._value else: @@ -765,7 +768,7 @@ class responsable(Attr): self._value = res return res - def parse_value(self, resp, ldif): + def parse_value(self, resp): self.__value = resp value = property(get_respo, nonefunction) @@ -779,7 +782,7 @@ class imprimeurClub(Attr): category = "perso" can_modify = [cableur, nounou] - def nonefunction(self, val): + def nonefunction(self, imprimeur): """ Just... do... nothing. L'idée est qu'on initialise self.value à None @@ -799,8 +802,8 @@ class imprimeurClub(Attr): self._value = res return res - def parse_value(self, val, ldif): - self.__value = val + def parse_value(self, imprimeur): + self.__value = imprimeur value = property(get_imprimeur, nonefunction) @@ -814,8 +817,8 @@ class blacklist(Attr): category = 'info' can_modify = [nounou] - def parse_value(self, bl, ldif): - bl_debut, bl_fin, bl_type, bl_comm = bl.split('$') + def parse_value(self, blacklist): + bl_debut, bl_fin, bl_type, bl_comm = blacklist.split('$') now = time.time() self.value = { 'debut' : bl_debut if bl_debut == '-' else int (bl_debut), 'fin' : bl_fin if bl_fin == '-' else int(bl_fin), @@ -860,10 +863,10 @@ class charteMA(Attr): can_modify = [nounou, bureau] category = 'perso' - def parse_value(self, signed, ldif): - if signed.upper() not in ["TRUE", "FALSE"]: - raise ValueError("La charte MA est soit TRUE ou FALSE, pas %r" % signed) - self.value = signed.upper() + def parse_value(self, charteSignee): + if charteSignee.upper() not in ["TRUE", "FALSE"]: + raise ValueError("La charte MA est soit TRUE ou FALSE, pas %r" % charteSignee) + self.value = charteSignee.upper() class homeDirectory(Attr): singlevalue=True @@ -871,13 +874,12 @@ class homeDirectory(Attr): unique = True legend="Le chemin du home de l'adhérent" - def parse_value(self, home, ldif): - uid = ldif['uid'][0] + def parse_value(self, home): + uid = str(self.parent['uid'][0]) if uid.startswith('club-'): uid = uid.split('-',1)[1] if home != u'/home/%s' % uid and home != u'/home/club/%s' % uid: - raise ValueError("Le répertoire personnel n'est pas bon: %r (devrait être %r ou %r)" % - (home, '/home/%s' % ldif['uid'][0], '/home/club/%s' % ldif['uid'][0])) + raise ValueError("Le répertoire personnel n'est pas bon: %r (devrait être %r ou %r)" % (home, '/home/%s' % self.parent['uid'][0], '/home/club/%s' % self.parent['uid'][0])) self.value = home class loginShell(Attr): @@ -886,10 +888,10 @@ class loginShell(Attr): legend = "Le shell de l'adherent" can_modify = [soi, nounou, cableur] - def parse_value(self, shell, ldif): + def parse_value(self, shell): #with open('/etc/shells') as f: # shells = [ l.strip() for l in f.readlines() if not l.startswith('#') ] - shells=['/bin/csh', + shells = ['/bin/csh', '/bin/sh', '/usr/bin/es', '/usr/bin/ksh', @@ -912,7 +914,7 @@ class loginShell(Attr): '/bin/false', '/bin//zsh' ''] - if self.ctxt_check and (shell not in shells): + if shell not in shells: raise ValueError("Shell %r invalide" % shell) self.value = shell @@ -935,7 +937,7 @@ class gecos(Attr): legend = "Le gecos" category = 'id' - def parse_value(self, gecos, ldif): + def parse_value(self, gecos): self.value = gecos class sshFingerprint(Attr): @@ -976,7 +978,7 @@ class controle(Attr): can_modify = [tresorier, nounou] category = 'perso' - def parse_value(self, ctrl, ldif): + def parse_value(self, ctrl): if ctrl not in [u"", u"c", u"p", u"cp", u"pc"]: raise ValueError("control peut prendre les valeurs [c][p]") if ctrl == u'cp': @@ -1008,7 +1010,7 @@ MACHINE_ATTRS = [ host, macAddress, hostAlias, ipHostNumber, portTCPout, portTCPin, portUDPout, portUDPin ] -CRANS_ATTRIBUTES= { +CRANS_ATTRIBUTES = { 'objectClass' : objectClass, 'cn' : cn, 'dn' : dn, diff --git a/lc_ldap.py b/lc_ldap.py index eea588c..dd809d5 100644 --- a/lc_ldap.py +++ b/lc_ldap.py @@ -104,7 +104,7 @@ def ldif_to_uldif(ldif): uldif[attr] = [ unicode(val, 'utf-8') for val in vals ] return uldif -def ldif_to_cldif(ldif, conn, check_ctxt = True): +def ldif_to_cldif(ldif, conn): """Transforme un dictionnaire renvoyé par python-ldap, en un dictionnaire dont les valeurs sont des instances de Attr Lorsqu'on récupère le ldif de la base ldap, on n'a pas besoin @@ -112,7 +112,7 @@ def ldif_to_cldif(ldif, conn, check_ctxt = True): """ cldif = {} for attr, vals in ldif.items(): - cldif[attr] = [ attributs.attrify(val, attr, ldif, conn, check_ctxt) for val in vals] + cldif[attr] = [ attributs.attrify(val, attr, ldif, conn) for val in vals] return cldif def cldif_to_ldif(cldif): @@ -325,7 +325,7 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object): continue else: # On crée l'attribut associé, pour parser sa valeur. - my_id = attributs.attrify(unicode(i), attr, {}, self, False) + my_id = attributs.attrify(unicode(i), attr, self, False, None) if my_id.value != i: continue else: @@ -456,20 +456,20 @@ class CransLdapObject(object): raise TypeError("conn doit être une instance de lc_ldap") self.conn = conn - self.attrs = attributs.AttrsDict(conn) # Contient un dico uldif qui doit représenter ce qui + self.attrs = attributs.AttrsDict(conn, Parent=self) # Contient un dico uldif qui doit représenter ce qui # est dans la base. On attrify paresseusement au moment où on utilise un attribut self._modifs = None # C'est là qu'on met les modifications - + self._modifs_unchecked = [] self.dn = dn orig = {} if ldif: if dn != base_dn: # new_cransldapobject ne donne pas de ldif formaté et utilise un ldif non formaté, donc on formate - self.attrs = attributs.AttrsDict(self.conn, ldif, check_ctxt = False) + self.attrs = attributs.AttrsDict(self.conn, ldif, Parent=self) else: self.attrs = ldif - self._modifs = attributs.AttrsDict(self.conn, ldif, check_ctxt = False) + self._modifs = attributs.AttrsDict(self.conn, ldif, Parent=self) orig = ldif elif dn != base_dn: @@ -480,12 +480,12 @@ class CransLdapObject(object): # L'objet sortant de la base ldap, on ne fait pas de vérifications sur # l'état des données. - self.attrs = attributs.AttrsDict(self.conn, res_attrs, check_ctxt = False) + self.attrs = attributs.AttrsDict(self.conn, res_attrs, Parent=self) # Pour test en cas de mode w ou rw orig = res[0][1] - self._modifs = attributs.AttrsDict(self.conn, res[0][1], check_ctxt = False) + self._modifs = attributs.AttrsDict(self.conn, res[0][1], Parent=self) if mode in ['w', 'rw']: if not self.may_be(modified, self.conn.droits + self.conn._check_parent(dn) + self.conn._check_self(dn)): @@ -550,7 +550,7 @@ class CransLdapObject(object): raise EnvironmentError("Impossible de modifier l'objet, peut-être n'existe-t-il pas ?") # Vérification des modifications - self.attrs = attributs.AttrsDict(self.conn, self.conn.search_s(self.dn, 0)[0][1], check_ctxt=False) + self.attrs = attributs.AttrsDict(self.conn, self.conn.search_s(self.dn, 0)[0][1], Parent=self) differences = [] # On fait les différences entre les deux dicos for attr in set(self.attrs.keys()).union(set(self._modifs.keys())): @@ -607,6 +607,8 @@ class CransLdapObject(object): # Quand on est pas en mode d'écriture, ça plante. if self.mode not in ['w', 'rw']: raise ValueError("Objet en lecture seule") + if not self.has_key(attr): + raise ValueError("L'objet que vous modifiez n'a pas d'attribut %s" % (attr)) # Les valeurs sont nécessairement stockées en liste if not isinstance(values, list): values = [ values ] @@ -616,12 +618,9 @@ class CransLdapObject(object): # (on peut pas utiliser self._modifs, car il ne faut # faire le changement que si on peut) - # Sans le dict(), les deux pointent vers le même truc. - ldif = dict(self._modifs) - # On doit encoder, car _modifs est un ensemble de données brutes - # comme tout ldif - ldif[attr] = [ value.encode('utf-8') for value in values ] - attrs_before_verif = [ attributs.attrify(val, attr, ldif, self.conn) for val in values ] + attrs_before_verif = [ attributs.attrify(val, attr, self.conn, Parent=self) for val in values ] + for attribut in attrs_before_verif: + attribut.check_uniqueness([str(content) for content in self.attrs[attr]]) # On groupe les attributs précédents, et les nouveaux mixed_attrs = attrs_before_verif + self.attrs[attr] @@ -950,7 +949,8 @@ class facture(CransLdapObject): } attribs = [attributs.fid, attributs.modePaiement, attributs.recuPaiement] -class service(CransLdapObject): pass - -class lock(CransLdapObject): pass +class service(CransLdapObject): + pass +class lock(CransLdapObject): + pass