#!/usr/bin/env python # -*- coding: utf-8 -*- # # ATTRIBUTS.PY-- Description des attributs ldap # # Copyright (C) 2010 Cr@ns # Author: Antoine Durand-Gasselin # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # * Neither the name of the Cr@ns nor the names of its contributors may # be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import re from unicodedata import normalize def normalize_and_validate(attr, vals, ctxt): """Vérifie que attr peut se voir attribuer les valeurs vals""" a = eval('%s()' % attr) new_vals = a.normalize(vals, ctxt) a.validate(new_vals, ctxt) return new_vals def validate_name(value, more_chars=""): """Valide un nom: ie un unicode qui contient lettres, espaces et apostrophes, et éventuellement des caractères additionnels""" return re.match("[A-Za-z][-' A-Za-z%s]*" % more_chars, normalize('NFKD', value).encode('ASCII', 'ignore')) def validate_mac(value): """Vérifie qu'une adresse mac est valide""" return True def attrify(val, attr, ldif, ctxt_check = True): """Transforme un n'importe quoi en 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[attr](val, ldif, ctxt_check) class Attr(object): legend = "Human-readable description of attribute" singlevalue = None optional = None def __init__(self, val, ldif, ctxt_check): """Crée un nouvel objet représentant un attribut. val: valeur de l'attribut ldif: objet contenant l'attribut (permet de faire les validations sur l'environnement) ctxt_check: effectue les validations """ self.value = None assert isinstance(val, unicode) self.parse_value(val) if ctxt_check: self.validate(ldif) def parse_value(self, val): """Transforme l'attribut pour travailler avec notre validateur""" self.value = val def __str__(self): unicode.encode(unicode(self), 'utf-8') def validate(self, ldif): """validates: vérifie déjà que ce qu'on a rentré est parsable""" own_values = ldif[self.__class__.__name__] self._check_cardinality(own_values) self._check_uniqueness() self._check_users_restrictions(own_values) 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(u'%s doit avoir au maximum une valeur (affecte %s)' % self.__class__, values) if not self.optional and len(values) == 0: raise ValueError('%s doit avoir au moins une valeur' % self.__class__) def _check_type(self, values): """Vérifie que les valeurs ont le bon type (nom est un mot, tel est un nombre, etc...)""" for val in values: assert isinstance(val, unicode) def _check_uniqueness(self): """Vérifie l'unicité dans la base de la valeur (mailAlias, chbre, etc...)""" pass def _check_values(self, values): """Vérifie que les valeurs sont valides (typiquement chbre)""" pass def _check_users_restrictions(self, values): """Vérifie les restrictions supplémentaires imposées selon les niveaux de droits (<= 3 mailAlias, pas de mac identiques, etc...)""" pass def can_modify(self): """Vérifie si l'attribut est modifiable""" return False class objectClass(Attr): singlevalue = False optional = False legend = "entité" class nom(Attr): singlevalue = True optional = False legend = "Nom" def _check_type(self, values): return [ validate_name(v) for v in values] def normalize(self, values, uldif): return [ v.strip().capitalize() for v in values ] class prenom(Attr): singlevalue = True optional = False legend = u"Prénom" def _check_type(self, values): return [ validate_name(v) for v in values ] def normalize(self, values, uldif): return [ values.strip().capitalize() for v in values ] class tel(Attr): singlevalue = True optional = False legend = u"Téléphone" def normalize(self, value, uldif): return value # XXX - To implement class paiement(Attr): singlevallue = False optional = True legend = u"Paiement" def normalize(self, values, uldif): return values # XXX - To implement class carteEtudiant(Attr): singlevalue = False optional = True legend = u"Carte d'étudiant" class mailAlias(Attr): singlevalue = False optional = True legend = u"Alias mail" class canonicalAlias(Attr): singlevalue = True optional = False legend = u"Alias mail canonique" class etudes(Attr): singlevalue = False optional = False legend = u"Études" class chbre(Attr): singlevalue = True optional = False legend = u"Chambre sur le campus" class droits(Attr): singlevalue = False optional = True legend = u"Droits sur les serveurs" class solde(Attr): singlevalue = True optional = True legend = u"Solde d'impression" class host(Attr): singlevalue = True optional = False hname = legend = u"Nom d'hôte" class macAddress(Attr): singlevalue = True optional = False legend = u"Adresse physique de la carte réseau" hname = "Adresse MAC" class ipHostNumber(Attr): singlevalue = True optional = False legend = u"Adresse IPv4 de la machine" hname = "IPv4" class mid(Attr): singlevalue = True optional = False legend = "Identifiant de machine" class hostAlias(Attr): singlevalue = False optional = True legend = u'Alias de nom de machine' class ipsec(Attr): singlevalue = False optional = True legend = u'Clef wifi' class puissance(Attr): singlevalue = True optional = True legend = u"puissance d'émission pour les bornes wifi" class canal(Attr): singlevalue = True optional = True legend = u'Canal d\'émission de la borne' class portTCPout(Attr): singlevalue = False optional = True legend = u'Port TCP ouvert vers l\'extérieur' class portTCPin(Attr): singlevalue = False optional = True legend = u"Port TCP ouvert depuis l'extérieur" class portUDPout(Attr): singlevalue = False optional = True legend = u"Port UDP ouvert vers l'extérieur" class portUDPin(Attr): singlevalue = False optional = True legend = u"Port UDP ouvert depuis l'extérieur" class prise(Attr): singlevalue = True optional = True legend = u"Prise sur laquelle est branchée la machine" class cid(Attr): singlevalue = True optional = True legend = u"Identifiant du club" class responsable(Attr): singlevalue = True optional = True legend = u"Responsable du club" class blacklist(Attr): singlevalue = False optional = True legend = u"Blackliste" class historique(Attr): singlevalue = False optional = True legend = u"Historique de l'objet" CRANS_ATTRIBUTES= { 'objectClass' : objectClass, 'nom' : nom, 'prenom' : prenom, 'tel' : tel, 'paiement' : paiement, 'carteEtudiant' : carteEtudiant, 'mailAlias' : mailAlias, 'canonicalAlias' : canonicalAlias, 'etudes' : etudes, 'chbre' : chbre, 'droits' : droits, 'solde' : solde, 'mid' : mid, 'hostAlias' : hostAlias, 'ipsec' : ipsec, 'puissance' : puissance, 'canal' : canal, 'portTCPout' : portTCPout, 'portTCPin' : portTCPin, 'portUDPout' : portUDPout, 'portUDPin' : portUDPin, 'prise' : prise, 'cid' : cid, 'responsable' : responsable, 'blacklist' : blacklist, 'historique' : historique }