[global] On jette ctxt_check, et les ldifs qui traînent partout, qu'on remplace.

* 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
This commit is contained in:
Pierre-Elliott Bécue 2013-04-08 00:12:00 +02:00
parent 60ea18d180
commit 180c17baed
2 changed files with 172 additions and 170 deletions

View file

@ -75,28 +75,34 @@ DROITS_SUPERVISEUR = { nounou : TOUS_DROITS,
bureau : DROITS_FAIBLES + [bureau, tresorier], 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`. """Transforme un n'importe quoi en :py:class:`Attr`.
Doit effectuer les normalisations et sanity check si un str ou un Doit effectuer les normalisations et sanity check si un str ou un
unicode est passé en argument. unicode est passé en argument.
Devrait insulter en cas de potentiel problème d'encodage. 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): if isinstance(val, Attr):
return val return val
else: else:
return CRANS_ATTRIBUTES.get(attr, Attr)(val, ldif, conn, ctxt_check) return CRANS_ATTRIBUTES.get(attr, Attr)(val, conn, Parent)
class AttrsDict(dict) : class AttrsDict(dict) :
def __init__(self, conn, ldif = {}, check_ctxt = True): def __init__(self, conn, ldif={}, Parent=None):
super(AttrsDict, self).__init__(ldif) super(AttrsDict, self).__init__(ldif)
self._conn = conn self._conn = conn
self._ldif = ldif self._parent = Parent
self._check_ctxt = check_ctxt
def __getitem__(self, attr): def __getitem__(self, attr):
values = super(AttrsDict, self).__getitem__(attr) values = super(AttrsDict, self).__getitem__(attr)
@ -105,12 +111,23 @@ class AttrsDict(dict) :
output = [] output = []
for val in values: for val in values:
if not isinstance(val, Attr): 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: else:
output.append(val) output.append(val)
self[attr] = output self[attr] = output
return 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): def get(self, value, default_value):
try: try:
return self[value] return self[value]
@ -124,36 +141,33 @@ class Attr(object):
* ``val`` : valeur de l'attribut * ``val`` : valeur de l'attribut
* ``ldif`` : objet contenant l'attribut (permet de faire les validations sur l'environnement) * ``ldif`` : objet contenant l'attribut (permet de faire les validations sur l'environnement)
* ``ctxt_check`` : effectue les validations
""" """
legend = "Human-readable description of attribute" legend = "Human-readable description of attribute"
singlevalue = None singlevalue = False
optional = None optional = True
conn = None conn = None
unique = False unique = False
can_modify = [nounou]
"""La liste des droits qui suffisent à avoir le droit de modifier la valeur""" """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 """Qui peut voir l'attribut. Par défaut, les Nounous et les Apprentis
peuvent tout voir. Par transparence, et par utilité, on autorise par peuvent tout voir. Par transparence, et par utilité, on autorise par
défaut l'adhérent à voir les données le concernant.""" 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)""" """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. """ """Crée un nouvel objet représentant un attribut. """
self.ctxt_check=ctxt_check self.value = None
#self.value = None
self.conn = conn self.conn = conn
assert isinstance(val, unicode) assert isinstance(val, unicode)
self.parse_value(val, ldif) self.parent = Parent
if ctxt_check: self.parse_value(val)
self.validate(ldif)
def parse_value(self, val, ldif): def parse_value(self, val):
"""Transforme l'attribut pour travailler avec notre validateur """Transforme l'attribut pour travailler avec notre validateur
Le ldif est en dépendance car à certains endroits, il peut servir Le ldif est en dépendance car à certains endroits, il peut servir
(par exemple, pour l'ipv6, ou l'ipv4...""" (par exemple, pour l'ipv6, ou l'ipv4..."""
@ -167,37 +181,16 @@ class Attr(object):
assert isinstance(self.value, unicode) assert isinstance(self.value, unicode)
return self.value return self.value
def validate(self, ldif): def check_uniqueness(self, liste_exclue):
"""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):
"""Vérifie l'unicité dans la base de la valeur (``mailAlias``, ``chbre``, """Vérifie l'unicité dans la base de la valeur (``mailAlias``, ``chbre``,
etc...)""" etc...)"""
attr = self.__class__.__name__ attr = self.__class__.__name__
if str(self) in liste_exclue:
return
if self.unique: if self.unique:
res = self.conn.search('%s=%s' % (attr, str(self))) res = self.conn.search('%s=%s' % (attr, str(self)))
if res: 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): def is_modifiable(self, liste_droits):
""" """
@ -210,13 +203,13 @@ class objectClass(Attr):
optional = False optional = False
legend = "entité" legend = "entité"
can_modify = []
""" Personne ne doit modifier de classe """ """ Personne ne doit modifier de classe """
can_modify = []
can_view = [nounou, apprenti]
""" Internal purpose (et à fin pédagogique) """ """ 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', if val not in [ 'top', 'organizationalUnit', 'posixAccount', 'shadowAccount',
'proprio', 'adherent', 'club', 'machine', 'machineCrans', 'proprio', 'adherent', 'club', 'machine', 'machineCrans',
'borneWifi', 'machineWifi', 'machineFixe', 'borneWifi', 'machineWifi', 'machineFixe',
@ -227,7 +220,7 @@ class objectClass(Attr):
class intAttr(Attr): class intAttr(Attr):
def parse_value(self, val, ldif): def parse_value(self, val):
if int(val) <= 0: if int(val) <= 0:
raise ValueError("Valeur entière invalide : %r" % val) raise ValueError("Valeur entière invalide : %r" % val)
self.value = int(val) self.value = int(val)
@ -236,7 +229,7 @@ class intAttr(Attr):
return unicode(self.value) return unicode(self.value)
class boolAttr(Attr): class boolAttr(Attr):
def parse_value(self, val, ldif): def parse_value(self, val):
if val.lower() in [u'true', u'ok']: if val.lower() in [u'true', u'ok']:
self.value = True self.value = True
elif val.lower() == u'false': elif val.lower() == u'false':
@ -256,13 +249,13 @@ class aid(intAttr):
category = 'id' category = 'id'
unique = True unique = True
can_modify = []
""" Personne ne devrait modifier un attribut d'identification """ """ 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): def parse_value(self, aid):
self.value = int(val) self.value = int(aid)
class uid(Attr): class uid(Attr):
singlevalue = True singlevalue = True
@ -278,11 +271,14 @@ class nom(Attr):
category = 'perso' category = 'perso'
can_modify = [nounou, cableur] can_modify = [nounou, cableur]
def parse_value(self, val, ldif): def parse_value(self, nom):
if u'club' in ldif['objectClass']: if self.parent != None:
self.value = validate_name(val,"0123456789\[\]") if u'club' in self.parent['objectClass']:
self.value = validate_name(nom,"0123456789\[\]")
else: else:
self.value = validate_name(val) self.value = validate_name(nom)
else:
self.value = validate_name(nom)
class prenom(Attr): class prenom(Attr):
singlevalue = True singlevalue = True
@ -291,8 +287,8 @@ class prenom(Attr):
category = 'perso' category = 'perso'
can_modify = [nounou, cableur] can_modify = [nounou, cableur]
def parse_value(self, val, ldif): def parse_value(self, prenom):
self.value = validate_name(val) self.value = validate_name(prenom)
class compteWiki(Attr): class compteWiki(Attr):
singlevalue = False singlevalue = False
@ -301,8 +297,8 @@ class compteWiki(Attr):
category = 'perso' category = 'perso'
can_modify = [nounou, cableur, soi] can_modify = [nounou, cableur, soi]
def parse_value(self, val, ldif): def parse_value(self, compte):
self.value = validate_name(val) self.value = validate_name(compte)
# TODO: validate with mdp for user definition here ? # TODO: validate with mdp for user definition here ?
class tel(Attr): class tel(Attr):
@ -312,19 +308,20 @@ class tel(Attr):
category = 'perso' category = 'perso'
can_modify = [soi, nounou, cableur] can_modify = [soi, nounou, cableur]
def parse_value(self, val, ldif): def parse_value(self, tel):
self.value = format_tel(val) self.value = format_tel(tel)
if len(self.value) == 0: 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): class yearAttr(intAttr):
singlevalue = False singlevalue = False
optional = True optional = True
def parse_value(self, val, ldif): def parse_value(self, annee):
if int(val) < 1998: annee = int(annee)
raise ValueError("Année invalide (%r)" % val) if annee < 1998:
self.value = int(val) raise ValueError("Année invalide (%r)" % annee)
self.value = annee
class paiement(yearAttr): class paiement(yearAttr):
legend = u"Paiement" legend = u"Paiement"
@ -348,8 +345,10 @@ class mail(Attr):
can_modify = [soi, nounou, cableur] can_modify = [soi, nounou, cableur]
category = 'mail' category = 'mail'
def _check_uniqueness(self): def check_uniqueness(self, liste_exclue):
attr = self.__class__.__name__ attr = self.__class__.__name__
if str(self) in liste_exclue:
return
if attr in ["mailAlias", "canonicalAlias", 'mail']: if attr in ["mailAlias", "canonicalAlias", 'mail']:
mail = str(self).split('@', 1)[0] mail = str(self).split('@', 1)[0]
@ -364,7 +363,7 @@ class mail(Attr):
if res: if res:
raise ValueError("Le mail %s est déjà pris." % (str(self))) 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): 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) raise ValueError("Adresse mail invalide (%s)" % mail)
self.value = mail self.value = mail
@ -377,11 +376,11 @@ class canonicalAlias(mail):
legend = u"Alias mail canonique" legend = u"Alias mail canonique"
category = 'mail' category = 'mail'
def parse_value(self, val, ldif): def parse_value(self, mail):
val = u".".join([ a.capitalize() for a in val.split(u'.', 1) ]) 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}$', val): 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)" % val) raise ValueError("Alias mail invalide (%s)" % mail)
self.value = val self.value = mail
class mailAlias(mail): class mailAlias(mail):
singlevalue = False singlevalue = False
@ -391,11 +390,11 @@ class mailAlias(mail):
can_modify = [soi, cableur, nounou] can_modify = [soi, cableur, nounou]
category = 'mail' category = 'mail'
def parse_value(self, val, ldif): def parse_value(self, mail):
val = val.lower() mail = mail.lower()
if not re.match('^[-_.0-9A-Za-z]+@([A-Za-z0-9]{2}[A-Za-z0-9-_]+[.])+[a-z]{2,6}$', val): 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)" % val) raise ValueError("Alias mail invalide (%r)" % mail)
self.value = val self.value = mail
class mailInvalide(boolAttr): class mailInvalide(boolAttr):
optional = True optional = True
@ -418,10 +417,9 @@ class etudes(Attr):
can_modify = [soi, cableur, nounou] can_modify = [soi, cableur, nounou]
category = 'perso' category = 'perso'
def parse_value(self, val, ldif): def parse_value(self, etudes):
# who cares # who cares
self.value = val self.value = etudes
class chbre(Attr): class chbre(Attr):
singlevalue = True singlevalue = True
@ -431,26 +429,26 @@ class chbre(Attr):
can_modify = [soi, cableur, nounou] can_modify = [soi, cableur, nounou]
category = 'perso' category = 'perso'
def parse_value(self, val, ldif): def parse_value(self, chambre):
if self.ctxt_check: # Si ce n'est pas la peine de vérifier, on ne vérifie pas if self.parent != None:
if u'club' in ldif['objectClass']: if u'club' in self.parent['objectClass']:
if val in annuaires_pg.locaux_clubs(): if chambre in annuaires_pg.locaux_clubs():
self.value = val self.value = chambre
return return
else: 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"????"): if chambre in (u"EXT", u"????"):
self.value = val self.value = chambre
return return
try: try:
annuaires_pg.chbre_prises(val[0], val[1:]) annuaires_pg.chbre_prises(chambre[0], chambre[1:])
except NameError: except NameError:
import annuaires_pg_test import annuaires_pg_test
annuaires_pg_test.chbre_prises(val[0], val[1:]) annuaires_pg_test.chbre_prises(chambre[0], chambre[1:])
self.value = val self.value = chambre
class droits(Attr): class droits(Attr):
singlevalue = False singlevalue = False
@ -459,10 +457,10 @@ class droits(Attr):
can_modify = [nounou, bureau] #ne sert à rien ici, mais c'est tout à fait exceptionnel can_modify = [nounou, bureau] #ne sert à rien ici, mais c'est tout à fait exceptionnel
category = 'perso' 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]: # if val.lower() not in [i.lower() for i in TOUS_DROITS]:
# raise ValueError("Ces droits n'existent pas (%r)" % val) # raise ValueError("Ces droits n'existent pas (%r)" % val)
self.value = val.capitalize() self.value = droits.capitalize()
def is_modifiable(self, liste_droits): def is_modifiable(self, liste_droits):
""" """
@ -481,21 +479,21 @@ class solde(Attr):
legend = u"Solde d'impression" legend = u"Solde d'impression"
can_modify = [imprimeur, nounou, tresorier] 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 # 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) raise ValueError("Solde invalide: %r" % solde)
self.value = solde self.value = solde
class dnsAttr(Attr): class dnsAttr(Attr):
category = 'dns' category = 'dns'
def parse_value(self, dns, ldif): def parse_value(self, val):
dns = dns.lower() val = val.lower()
name, net = dns.split('.', 1) name, net = val.split('.', 1)
if self.ctxt_check and (net not in ['adm.crans.org', 'crans.org', 'wifi.crans.org'] or if (net not in ['adm.crans.org', 'crans.org', 'wifi.crans.org'] or
not re.match('[a-z][-_a-z0-9]+', name)): not re.match('[a-z][-_a-z0-9]+', name)):
raise ValueError("Nom d'hote invalide %r" % dns) raise ValueError("Nom d'hote invalide %r" % val)
self.value = dns self.value = val
class host(dnsAttr): class host(dnsAttr):
singlevalue = True singlevalue = True
@ -504,8 +502,10 @@ class host(dnsAttr):
can_modify = [parent, nounou, cableur] can_modify = [parent, nounou, cableur]
category = 'base_tech' category = 'base_tech'
def _check_uniqueness(self): def check_uniqueness(self, liste_exclue):
attr = self.__class__.__name__ attr = self.__class__.__name__
if str(self) in liste_exclue:
return
if attr in ["host", "hostAlias"]: if attr in ["host", "hostAlias"]:
res = self.conn.search('(|(host=%s)(hostAlias=%s))' % ((str(self),)*2)) res = self.conn.search('(|(host=%s)(hostAlias=%s))' % ((str(self),)*2))
if res: if res:
@ -527,13 +527,13 @@ class macAddress(Attr):
category = 'base_tech' category = 'base_tech'
can_modify = [parent, nounou, cableur] can_modify = [parent, nounou, cableur]
def parse_value(self, mac, ldif): def parse_value(self, mac):
self.value = format_mac(mac) self.value = format_mac(mac)
# XXX self.parent['ip6HostNumber'] = cequ'ilfaut
def __unicode__(self): def __unicode__(self):
return unicode(self.value).lower() return unicode(self.value).lower()
class ipHostNumber(Attr): class ipHostNumber(Attr):
singlevalue = True singlevalue = True
optional = True optional = True
@ -543,9 +543,9 @@ class ipHostNumber(Attr):
category = 'base_tech' category = 'base_tech'
can_modify = [nounou] can_modify = [nounou]
def parse_value(self, ip, ldif): def parse_value(self, ip):
if ip == '<automatique>': if ip == '<automatique>':
ip = ip4_of_rid(ldif['rid'][0]) ip = ip4_of_rid(self.parent['rid'][0])
self.value = netaddr.ip.IPAddress(ip) self.value = netaddr.ip.IPAddress(ip)
def __unicode__(self): def __unicode__(self):
@ -560,8 +560,8 @@ class ip6HostNumber(Attr):
category = 'base_tech' category = 'base_tech'
can_modify = [nounou] can_modify = [nounou]
def parse_value(self, val, ldif): def parse_value(self, ip6):
ip = ip6_of_mac(ldif['macAddress'][0], int(ldif['rid'][0])) ip = ip6_of_mac(self.parent['macAddress'][0], int(self.parent['rid'][0]))
self.value = netaddr.ip.IPAddress(ip) self.value = netaddr.ip.IPAddress(ip)
def __unicode__(self): def __unicode__(self):
@ -574,7 +574,7 @@ class mid(Attr):
legend = u"Identifiant de machine" legend = u"Identifiant de machine"
category = 'id' category = 'id'
def parse_value(self, mid, ldif): def parse_value(self, mid):
self.value = int(mid) self.value = int(mid)
def __unicode__(self): def __unicode__(self):
@ -588,7 +588,7 @@ class rid(Attr):
category = 'id' category = 'id'
can_modify = [nounou] can_modify = [nounou]
def parse_value(self, rid, ldif): def parse_value(self, rid):
rid = int(rid) rid = int(rid)
# On veut éviter les rid qui recoupent les ipv4 finissant par # On veut éviter les rid qui recoupent les ipv4 finissant par
@ -644,11 +644,11 @@ class positionBorne(Attr):
singlevalue = True singlevalue = True
optional = True optional = True
def parse_value(self, val, ldif): def parse_value(self, pos):
if val == u'N/A N/A': if pos == u'N/A N/A':
self.value = u"N/A°N N/A°E" self.value = u"N/A°N N/A°E"
else: else:
lat, lon = val.split(" ") lat, lon = pos.split(" ")
self.value = u"%f°N %f°E" % (float(lat), float(lon)) self.value = u"%f°N %f°E" % (float(lat), float(lon))
class nvram(Attr): class nvram(Attr):
@ -656,8 +656,9 @@ class nvram(Attr):
optional = True optional = True
can_modify = [nounou] can_modify = [nounou]
def parse_value(self, val, ldif): def parse_value(self, nvr):
pass # XXX - on fait quoi ici ?
self.value = nvr
class portAttr(Attr): class portAttr(Attr):
singlevalue = False singlevalue = False
@ -666,7 +667,7 @@ class portAttr(Attr):
category = 'firewall' category = 'firewall'
can_modify = [nounou] can_modify = [nounou]
def parse_value(self, port, ldif): def parse_value(self, port):
if ":" in port: if ":" in port:
a,b = port.split(":", 1) a,b = port.split(":", 1)
if a: if a:
@ -694,7 +695,6 @@ class portAttr(Attr):
else: else:
return unicode(self.value[0]) return unicode(self.value[0])
class portTCPout(portAttr): class portTCPout(portAttr):
legend = u'Port TCP ouvert vers l\'extérieur' legend = u'Port TCP ouvert vers l\'extérieur'
@ -724,8 +724,8 @@ class prise(Attr):
category = 'base_tech' category = 'base_tech'
can_modify = [nounou] can_modify = [nounou]
def parse_value(self, prise, ldif): def parse_value(self, prise):
### Tu es une Nounou, je te fais confiance ### Tu es Beau, je te fais confiance
self.value = prise self.value = prise
class cid(intAttr): class cid(intAttr):
@ -735,7 +735,7 @@ class cid(intAttr):
legend = u"Identifiant du club" legend = u"Identifiant du club"
category = 'id' category = 'id'
def parse_value(self, val, ldif): def parse_value(self, val):
self.value = int(val) self.value = int(val)
class responsable(Attr): class responsable(Attr):
@ -745,7 +745,7 @@ class responsable(Attr):
category = 'perso' category = 'perso'
can_modify = [cableur, nounou] can_modify = [cableur, nounou]
def nonefunction(self, val): def nonefunction(self, resp):
""" """
Just... do... nothing. Just... do... nothing.
L'idée est qu'on initialise self.value à None L'idée est qu'on initialise self.value à None
@ -755,6 +755,9 @@ class responsable(Attr):
pass pass
def get_respo(self): 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"): if hasattr(self, "_value"):
return self._value return self._value
else: else:
@ -765,7 +768,7 @@ class responsable(Attr):
self._value = res self._value = res
return res return res
def parse_value(self, resp, ldif): def parse_value(self, resp):
self.__value = resp self.__value = resp
value = property(get_respo, nonefunction) value = property(get_respo, nonefunction)
@ -779,7 +782,7 @@ class imprimeurClub(Attr):
category = "perso" category = "perso"
can_modify = [cableur, nounou] can_modify = [cableur, nounou]
def nonefunction(self, val): def nonefunction(self, imprimeur):
""" """
Just... do... nothing. Just... do... nothing.
L'idée est qu'on initialise self.value à None L'idée est qu'on initialise self.value à None
@ -799,8 +802,8 @@ class imprimeurClub(Attr):
self._value = res self._value = res
return res return res
def parse_value(self, val, ldif): def parse_value(self, imprimeur):
self.__value = val self.__value = imprimeur
value = property(get_imprimeur, nonefunction) value = property(get_imprimeur, nonefunction)
@ -814,8 +817,8 @@ class blacklist(Attr):
category = 'info' category = 'info'
can_modify = [nounou] can_modify = [nounou]
def parse_value(self, bl, ldif): def parse_value(self, blacklist):
bl_debut, bl_fin, bl_type, bl_comm = bl.split('$') bl_debut, bl_fin, bl_type, bl_comm = blacklist.split('$')
now = time.time() now = time.time()
self.value = { 'debut' : bl_debut if bl_debut == '-' else int (bl_debut), self.value = { 'debut' : bl_debut if bl_debut == '-' else int (bl_debut),
'fin' : bl_fin if bl_fin == '-' else int(bl_fin), 'fin' : bl_fin if bl_fin == '-' else int(bl_fin),
@ -860,10 +863,10 @@ class charteMA(Attr):
can_modify = [nounou, bureau] can_modify = [nounou, bureau]
category = 'perso' category = 'perso'
def parse_value(self, signed, ldif): def parse_value(self, charteSignee):
if signed.upper() not in ["TRUE", "FALSE"]: if charteSignee.upper() not in ["TRUE", "FALSE"]:
raise ValueError("La charte MA est soit TRUE ou FALSE, pas %r" % signed) raise ValueError("La charte MA est soit TRUE ou FALSE, pas %r" % charteSignee)
self.value = signed.upper() self.value = charteSignee.upper()
class homeDirectory(Attr): class homeDirectory(Attr):
singlevalue=True singlevalue=True
@ -871,13 +874,12 @@ class homeDirectory(Attr):
unique = True unique = True
legend="Le chemin du home de l'adhérent" legend="Le chemin du home de l'adhérent"
def parse_value(self, home, ldif): def parse_value(self, home):
uid = ldif['uid'][0] uid = str(self.parent['uid'][0])
if uid.startswith('club-'): if uid.startswith('club-'):
uid = uid.split('-',1)[1] uid = uid.split('-',1)[1]
if home != u'/home/%s' % uid and home != u'/home/club/%s' % uid: 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)" % 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]))
(home, '/home/%s' % ldif['uid'][0], '/home/club/%s' % ldif['uid'][0]))
self.value = home self.value = home
class loginShell(Attr): class loginShell(Attr):
@ -886,7 +888,7 @@ class loginShell(Attr):
legend = "Le shell de l'adherent" legend = "Le shell de l'adherent"
can_modify = [soi, nounou, cableur] can_modify = [soi, nounou, cableur]
def parse_value(self, shell, ldif): def parse_value(self, shell):
#with open('/etc/shells') as f: #with open('/etc/shells') as f:
# shells = [ l.strip() for l in f.readlines() if not l.startswith('#') ] # shells = [ l.strip() for l in f.readlines() if not l.startswith('#') ]
shells = ['/bin/csh', shells = ['/bin/csh',
@ -912,7 +914,7 @@ class loginShell(Attr):
'/bin/false', '/bin/false',
'/bin//zsh' '/bin//zsh'
''] '']
if self.ctxt_check and (shell not in shells): if shell not in shells:
raise ValueError("Shell %r invalide" % shell) raise ValueError("Shell %r invalide" % shell)
self.value = shell self.value = shell
@ -935,7 +937,7 @@ class gecos(Attr):
legend = "Le gecos" legend = "Le gecos"
category = 'id' category = 'id'
def parse_value(self, gecos, ldif): def parse_value(self, gecos):
self.value = gecos self.value = gecos
class sshFingerprint(Attr): class sshFingerprint(Attr):
@ -976,7 +978,7 @@ class controle(Attr):
can_modify = [tresorier, nounou] can_modify = [tresorier, nounou]
category = 'perso' 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"]: if ctrl not in [u"", u"c", u"p", u"cp", u"pc"]:
raise ValueError("control peut prendre les valeurs [c][p]") raise ValueError("control peut prendre les valeurs [c][p]")
if ctrl == u'cp': if ctrl == u'cp':

View file

@ -104,7 +104,7 @@ def ldif_to_uldif(ldif):
uldif[attr] = [ unicode(val, 'utf-8') for val in vals ] uldif[attr] = [ unicode(val, 'utf-8') for val in vals ]
return uldif 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 """Transforme un dictionnaire renvoyé par python-ldap, en
un dictionnaire dont les valeurs sont des instances de Attr 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 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 = {} cldif = {}
for attr, vals in ldif.items(): 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 return cldif
def cldif_to_ldif(cldif): def cldif_to_ldif(cldif):
@ -325,7 +325,7 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object):
continue continue
else: else:
# On crée l'attribut associé, pour parser sa valeur. # 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: if my_id.value != i:
continue continue
else: else:
@ -456,20 +456,20 @@ class CransLdapObject(object):
raise TypeError("conn doit être une instance de lc_ldap") raise TypeError("conn doit être une instance de lc_ldap")
self.conn = conn 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 # 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 = None # C'est là qu'on met les modifications
self._modifs_unchecked = []
self.dn = dn self.dn = dn
orig = {} orig = {}
if ldif: if ldif:
if dn != base_dn: # new_cransldapobject ne donne pas de ldif formaté et utilise un ldif non formaté, donc on formate 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: else:
self.attrs = ldif self.attrs = ldif
self._modifs = attributs.AttrsDict(self.conn, ldif, check_ctxt = False) self._modifs = attributs.AttrsDict(self.conn, ldif, Parent=self)
orig = ldif orig = ldif
elif dn != base_dn: 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'objet sortant de la base ldap, on ne fait pas de vérifications sur
# l'état des données. # 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 # Pour test en cas de mode w ou rw
orig = res[0][1] 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 mode in ['w', 'rw']:
if not self.may_be(modified, self.conn.droits + self.conn._check_parent(dn) + self.conn._check_self(dn)): 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 ?") raise EnvironmentError("Impossible de modifier l'objet, peut-être n'existe-t-il pas ?")
# Vérification des modifications # 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 = [] differences = []
# On fait les différences entre les deux dicos # On fait les différences entre les deux dicos
for attr in set(self.attrs.keys()).union(set(self._modifs.keys())): 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. # Quand on est pas en mode d'écriture, ça plante.
if self.mode not in ['w', 'rw']: if self.mode not in ['w', 'rw']:
raise ValueError("Objet en lecture seule") 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 # Les valeurs sont nécessairement stockées en liste
if not isinstance(values, list): if not isinstance(values, list):
values = [ values ] values = [ values ]
@ -616,12 +618,9 @@ class CransLdapObject(object):
# (on peut pas utiliser self._modifs, car il ne faut # (on peut pas utiliser self._modifs, car il ne faut
# faire le changement que si on peut) # faire le changement que si on peut)
# Sans le dict(), les deux pointent vers le même truc. attrs_before_verif = [ attributs.attrify(val, attr, self.conn, Parent=self) for val in values ]
ldif = dict(self._modifs) for attribut in attrs_before_verif:
# On doit encoder, car _modifs est un ensemble de données brutes attribut.check_uniqueness([str(content) for content in self.attrs[attr]])
# 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 ]
# On groupe les attributs précédents, et les nouveaux # On groupe les attributs précédents, et les nouveaux
mixed_attrs = attrs_before_verif + self.attrs[attr] mixed_attrs = attrs_before_verif + self.attrs[attr]
@ -950,7 +949,8 @@ class facture(CransLdapObject):
} }
attribs = [attributs.fid, attributs.modePaiement, attributs.recuPaiement] attribs = [attributs.fid, attributs.modePaiement, attributs.recuPaiement]
class service(CransLdapObject): pass class service(CransLdapObject):
pass
class lock(CransLdapObject): pass
class lock(CransLdapObject):
pass