lc_ldap/attributs.py

1100 lines
31 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# ATTRIBUTS.PY-- Description des attributs ldap
#
# Copyright (C) 2010-2013 Cr@ns <roots@crans.org>
# Authors: Antoine Durand-Gasselin <adg@crans.org>
# Nicolas Dandrimont <olasd@crans.org>
# Valentin Samir <samir@crans.org>
# Vincent Le Gallic <legallic@crans.org>
# Pierre-Elliott Bécue <becue@crans.org>
#
# 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 <COPYRIGHT
# HOLDER> 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
import sys
import netaddr
import time
from unicodedata import normalize
from crans_utils import format_tel, format_mac, mailexist, validate_name, ip4_of_rid, ip6_of_mac
sys.path.append("/usr/scripts/gestion")
import config
import config.impression
import annuaires_pg
import smtplib
### SMTP
smtpserv = "smtp.crans.org"
### Les droits
# en cas de typo, l'appel d'une variable plante, on préfèrera donc les utiliser en lieu et place
# des chaînes de caractères
nounou = u"Nounou"
cableur = u"Cableur"
apprenti = u"Apprenti"
tresorier = u"Tresorier"
bureau = u"Bureau"
imprimeur = u"Imprimeur"
moderateur = u"Moderateur"
multimachines = u"Multimachines"
webmaster = u"Webmaster"
webradio = u"Webradio"
parent = u"parent"
soi = u"soi"
respo = u"responsable"
TOUS_DROITS = [nounou, apprenti, bureau, tresorier, imprimeur, moderateur, multimachines, cableur, webmaster]
DROITS_ELEVES = [nounou, bureau]
DROITS_MOYEN = [apprenti, moderateur]
DROITS_FAIBLES = [cableur, imprimeur, multimachines]
DROITS_SUPERVISEUR = { nounou : TOUS_DROITS,
bureau : DROITS_FAIBLES + [bureau, tresorier],
}
class SingleValueError(ValueError):
pass
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.
"""
if isinstance(val, Attr):
return val
else:
return CRANS_ATTRIBUTES.get(attr, Attr)(val, conn, Parent)
class AttrsDict(dict) :
def __init__(self, conn, ldif={}, Parent=None):
super(AttrsDict, self).__init__(ldif)
self._conn = conn
self._parent = Parent
def __getitem__(self, attr):
values = super(AttrsDict, self).__getitem__(attr)
if not isinstance(values, list):
values = [ values ]
output = []
for val in values:
if not isinstance(val, Attr):
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]
except KeyError:
return default_value
class Attr(object):
"""Objet représentant un attribut.
**Paramètres :**
* ``val`` : valeur de l'attribut
* ``ldif`` : objet contenant l'attribut (permet de faire les validations sur l'environnement)
"""
legend = "Human-readable description of attribute"
singlevalue = False
optional = True
conn = None
unique = False
"""La liste des droits qui suffisent à avoir le droit de modifier la valeur"""
can_modify = [nounou]
"""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]
"""Catégorie de l'attribut (pour affichage futur)"""
category = 'other'
def __init__(self, val, conn, Parent):
"""Crée un nouvel objet représentant un attribut. """
self.value = None
self.conn = conn
assert isinstance(val, unicode)
self.parent = Parent
self.parse_value(val)
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..."""
self.value = val
def __str__(self):
return self.__unicode__().encode('utf-8')
def __unicode__(self):
# XXX - Vérifier que cette méthode produit un objet parsable
assert isinstance(self.value, unicode)
return self.value
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 UniquenessError("%s déjà existant" % attr, [r.dn for r in res])
def is_modifiable(self, liste_droits):
"""
L'attribut est-il modifiable par un des droits dans liste_droits ?
"""
return not set(liste_droits).isdisjoint(self.can_modify)
class objectClass(Attr):
singlevalue = False
optional = False
legend = "entité"
""" Personne ne doit modifier de classe """
can_modify = []
""" Internal purpose (et à fin pédagogique) """
can_view = [nounou, apprenti]
def parse_value(self, val):
if val not in [ 'top', 'organizationalUnit', 'posixAccount', 'shadowAccount',
'proprio', 'adherent', 'club', 'machine', 'machineCrans',
'borneWifi', 'machineWifi', 'machineFixe',
'cransAccount', 'service', 'facture', 'freeMid' ]:
raise ValueError("Pourquoi insérer un objectClass=%r ?" % val)
else:
self.value = unicode(val)
class intAttr(Attr):
def parse_value(self, val):
if int(val) <= 0:
raise ValueError("Valeur entière invalide : %r" % val)
self.value = int(val)
def __unicode__(self):
return unicode(self.value)
class boolAttr(Attr):
def parse_value(self, val):
if val.lower() in [u'true', u'ok']:
self.value = True
elif val.lower() == u'false':
self.value = False
elif isinstance(val, bool):
self.value = val
else:
raise ValueError("%r doit être un booléen !" % val)
def __unicode__(self):
return unicode(self.value).upper()
class aid(intAttr):
singlevalue = True
optional = True
legend = u"Identifiant de l'adhérent"
category = 'id'
unique = True
""" Personne ne devrait modifier un attribut d'identification """
can_modify = []
can_view = [nounou, apprenti, cableur]
def parse_value(self, aid):
self.value = int(aid)
class uid(Attr):
singlevalue = True
option = False
legend = u"L'identifiant canonique de l'adhérent"
category = 'perso'
unique = True
class nom(Attr):
singlevalue = True
optional = False
legend = "Nom"
category = 'perso'
can_modify = [nounou, cableur]
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(nom)
class prenom(Attr):
singlevalue = True
optional = False
legend = u"Prénom"
category = 'perso'
can_modify = [nounou, cableur]
def parse_value(self, prenom):
self.value = validate_name(prenom)
class compteWiki(Attr):
singlevalue = False
optional = True
legend = u"Compte WiKi"
category = 'perso'
can_modify = [nounou, cableur, soi]
def parse_value(self, compte):
self.value = validate_name(compte)
# TODO: validate with mdp for user definition here ?
class tel(Attr):
singlevalue = True
optional = False
legend = u"Téléphone"
category = 'perso'
can_modify = [soi, nounou, cableur]
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)" % tel)
class yearAttr(intAttr):
singlevalue = False
optional = True
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"
can_modify = [cableur, nounou, tresorier]
category = 'perso'
class carteEtudiant(yearAttr):
legend = u"Carte d'étudiant"
category = 'perso'
can_modify = [cableur, nounou, tresorier]
class derniereConnexion(intAttr):
legend = u"Dernière connexion"
can_modify = []
class mail(Attr):
singlevalue = True
optional = False
unique = True
legend = "Le mail de l'adhérent"
can_modify = [soi, nounou, cableur]
category = 'mail'
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]
try:
smtp = smtplib.SMTP(smtpserv)
smtp.putcmd("vrfy", mail)
res = smtp.getreply()[0] in [250, 252]
smtp.close()
except:
raise ValueError(u'Serveur de mail injoignable')
if res:
raise ValueError("Le mail %s est déjà pris." % (str(self)))
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
class canonicalAlias(mail):
singlevalue = True
optional = False
unique = True
legend = u"Alias mail canonique"
category = 'mail'
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
optional = True
unique = True
legend = u"Alias mail"
can_modify = [soi, cableur, nounou]
category = 'mail'
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 mailExt(mail):
singlevalue = False
optional = True
unique = True
legend = u"Mail externe"
can_modify = [soi, cableur, nounou]
category = 'mail'
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("Mail externe invalide (%r)" % mail)
self.value = mail
class mailInvalide(boolAttr):
optional = True
legend = u"Mail invalide"
can_modify = [bureau, nounou]
class contourneGreylist(boolAttr):
optionnal = True
legend = u"Contourner la greylist"
category = 'mail'
can_modify = [soi]
def __unicode__(self):
return u"OK"
class etudes(Attr):
singlevalue = False
optional = False
legend = u"Études"
can_modify = [soi, cableur, nounou]
category = 'perso'
def parse_value(self, etudes):
# who cares
self.value = etudes
class chbre(Attr):
singlevalue = True
optional = False
unique = True
legend = u"Chambre sur le campus"
can_modify = [soi, cableur, nounou]
category = 'perso'
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" % chambre)
if chambre in (u"EXT", u"????"):
self.value = chambre
return
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 = chambre
class droits(Attr):
singlevalue = False
optional = True
legend = u"Droits sur les serveurs"
can_modify = [nounou, bureau] #ne sert à rien ici, mais c'est tout à fait exceptionnel
category = 'perso'
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 = droits.capitalize()
def is_modifiable(self, liste_droits):
"""
Le droit est-il modifiable par un des droits dans liste_droits ?
"""
modifiables = set()
for i in liste_droits:
modifiables.add(DROITS_SUPERVISEUR.get(i, []))
modifiables = list(modifiables)
return self.value in modifiables
class solde(Attr):
singlevalue = True
optional = True
legend = u"Solde d'impression"
can_modify = [imprimeur, nounou, tresorier]
def parse_value(self, solde):
# on évite les dépassements, sauf si on nous dit de ne pas vérifier
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, 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" % val)
self.value = val
class host(dnsAttr):
singlevalue = True
optional = False
hname = legend = u"Nom d'hôte"
can_modify = [parent, nounou, cableur]
category = 'base_tech'
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:
raise ValueError("Hôte déjà existant", [r.dn for r in res])
class hostAlias(host):
singlevalue = False
unique = True
optional = True
legend = u'Alias de nom de machine'
can_modify = [nounou, cableur]
class macAddress(Attr):
singlevalue = True
optional = False
legend = u"Adresse physique de la carte réseau"
hname = "Adresse MAC"
category = 'base_tech'
can_modify = [parent, nounou, cableur]
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
unique = True
legend = u"Adresse IPv4 de la machine"
hname = "IPv4"
category = 'base_tech'
can_modify = [nounou]
def parse_value(self, ip):
if ip == '<automatique>':
ip = ip4_of_rid(self.parent['rid'][0])
self.value = netaddr.ip.IPAddress(ip)
def __unicode__(self):
return unicode(self.value)
class ip6HostNumber(Attr):
singlevalue = True
optional = True
unique = True
legend = u"Adresse IPv6 de la machine"
hname = "IPv6"
category = 'base_tech'
can_modify = [nounou]
def parse_value(self, ip6):
ip = ip6_of_mac(str(self.parent['macAddress'][0]), int(str(self.parent['rid'][0])))
self.value = netaddr.ip.IPAddress(ip)
def __unicode__(self):
return unicode(self.value)
class mid(Attr):
singlevalue = True
optional = False
unique = True
legend = u"Identifiant de machine"
category = 'id'
def parse_value(self, mid):
self.value = int(mid)
def __unicode__(self):
return unicode(self.value)
class rid(Attr):
singlevalue = True
optional = False
unique = True
legend = u"Identifiant réseau de machine"
category = 'id'
can_modify = [nounou]
def parse_value(self, rid):
rid = int(rid)
# On veut éviter les rid qui recoupent les ipv4 finissant par
# .0 ou .255
plages = [xrange(config.rid[a][0], config.rid[a][1]+1) for a in config.rid.keys() if ('v6' not in a) and ('special' not in a)]
for plage in plages:
if rid in plage:
if rid % 256 == 0 or rid % 256 == 255:
rid = -1
break
else:
continue
else:
continue
self.value = rid
def __unicode__(self):
return unicode(self.value)
class ipsec(Attr):
singlevalue = False
optional = True
legend = u'Clef wifi'
category = 'wifi'
class puissance(Attr):
singlevalue = True
optional = True
legend = u"puissance d'émission pour les bornes wifi"
category = 'wifi'
can_modify = [nounou]
class canal(intAttr):
singlevalue = True
optional = True
legend = u'Canal d\'émission de la borne'
category = 'wifi'
can_modify = [nounou]
class hotspot(boolAttr):
singlevalue = True
optional = True
legend = u'Hotspot'
category = 'wifi'
can_modify = [nounou]
class positionBorne(Attr):
legend = u"Position de la borne"
category = "wifi"
can_modify = [nounou]
singlevalue = True
optional = True
def parse_value(self, pos):
if pos == u'N/A N/A':
self.value = u"N/A°N N/A°E"
else:
lat, lon = pos.split(" ")
self.value = u"%f°N %f°E" % (float(lat), float(lon))
class nvram(Attr):
legend = u"Configuration speciale"
optional = True
can_modify = [nounou]
def parse_value(self, nvr):
# XXX - on fait quoi ici ?
self.value = nvr
class portAttr(Attr):
singlevalue = False
optional = True
legend = u'Ouverture de port'
category = 'firewall'
can_modify = [nounou]
def parse_value(self, port):
if ":" in port:
a,b = port.split(":", 1)
if a:
if int(a) <0 or int(a)> 65535:
raise ValueError("Port invalide: %r" % port)
else:
a = 0
if b:
if int(a) <0 or int(a)> 65535:
raise ValueError("Port invalide: %r" % port)
else:
b = 65535
self.value = [int(a), int(b)]
else:
if int(port) <0 or int(port)> 65535:
raise ValueError("Port invalide: %r" % port)
self.value = [int(port)]
def __unicode__(self):
if len(self.value) > 1:
a, b = self.value
a = '' if a == 0 else str(a)
b = '' if b == 65535 else str(b)
return unicode('%s:%s' % (a, b))
else:
return unicode(self.value[0])
class portTCPout(portAttr):
legend = u'Port TCP ouvert vers l\'extérieur'
class portTCPin(portAttr):
legend = u"Port TCP ouvert depuis l'extérieur"
class portUDPout(portAttr):
legend = u"Port UDP ouvert vers l'extérieur"
class portUDPin(portAttr):
legend = u"Port UDP ouvert depuis l'extérieur"
class exempt(Attr):
legend = u"Exemption vers une IP"
class nombrePrises(intAttr):
legend = u"Nombre de prises ethernet de la machine"
singlevalue = True
optional = True
categoriy = 'base_tech'
can_modify = [nounou]
class prise(Attr):
singlevalue = True
optional = True
legend = u"Prise sur laquelle est branchée la machine"
category = 'base_tech'
can_modify = [nounou]
def parse_value(self, prise):
### Tu es Beau, je te fais confiance
self.value = prise
class cid(intAttr):
singlevalue = True
optional = True
unique = True
legend = u"Identifiant du club"
category = 'id'
def parse_value(self, val):
self.value = int(val)
class responsable(Attr):
singlevalue = True
optional = True
legend = u"Responsable du club"
category = 'perso'
can_modify = [cableur, nounou]
def nonefunction(self, resp):
"""
Just... do... nothing.
L'idée est qu'on initialise self.value à None
dans Attr. Simplement ici, self.value est une property,
donc il faut une fonction pour l'attribution.
"""
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:
try:
res = self.conn.search('aid=%s' % self.__value)[0]
except IndexError:
raise ValueError("L'adherent %s n'existe pas ou plus" % (self.__value))
self._value = res
return res
def parse_value(self, resp):
self.__value = resp
value = property(get_respo, nonefunction)
def __unicode__(self):
return self.__value
class imprimeurClub(Attr):
optional = True
legend = u"Imprimeur du club"
category = "perso"
can_modify = [cableur, nounou]
def nonefunction(self, imprimeur):
"""
Just... do... nothing.
L'idée est qu'on initialise self.value à None
dans Attr. Simplement ici, self.value est une property,
donc il faut une fonction pour l'attribution.
"""
pass
def get_imprimeur(self):
if hasattr(self, "_value"):
return self._value
else:
try:
res = self.conn.search('aid=%s' % int(self.__value))[0]
except IndexError:
raise ValueError("L'adhérent aid=%r n'existe pas ou plus" % self.__value)
self._value = res
return res
def parse_value(self, imprimeur):
self.__value = imprimeur
value = property(get_imprimeur, nonefunction)
def __unicode__(self):
return unicode(self.value['aid'][0])
class blacklist(Attr):
singlevalue = False
optional = True
legend = u"Blackliste"
category = 'info'
can_modify = [nounou]
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),
'type' : bl_type,
'comm' : bl_comm,
'actif' : (bl_debut == '-' or int(bl_debut) < now) and (bl_fin == '-' or int(bl_fin) > now) }
def is_actif(self):
return self.value['actif']
def terminer(self):
self.value['fin'] = int(max(self.value['debut'], time.time() - 60))
self.value['actif'] = False
def __unicode__(self):
return u'%(debut)s$%(fin)s$%(type)s$%(comm)s' % self.value
class historique(Attr):
singlevalue = False
optional = True
legend = u"Historique de l'objet"
category = 'info'
class info(Attr):
singlevalue = False
optional = True
legend = u"Quelques informations"
category = 'info'
can_modify = [nounou, cableur, bureau]
class homepageAlias(Attr):
singlevalue = True
optional = True
legend = u'Un alias pour la page personnelle'
can_modify = [nounou, cableur]
category = 'webpage'
class charteMA(Attr):
singlevalue = True
optional = True
legend= "Signale si l'adhérent a signé la charte de membres actifs"
can_modify = [nounou, bureau]
category = 'perso'
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
optional = True
unique = True
legend="Le chemin du home de l'adhérent"
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' % self.parent['uid'][0], '/home/club/%s' % self.parent['uid'][0]))
self.value = home
class loginShell(Attr):
singlevalue = True
optional = True
legend = "Le shell de l'adherent"
can_modify = [soi, nounou, cableur]
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',
'/bin/sh',
'/usr/bin/es',
'/usr/bin/ksh',
'/bin/ksh',
'/usr/bin/rc',
'/usr/bin/tcsh',
'/bin/tcsh',
'/usr/bin/esh',
'/bin/bash',
'/bin/rbash',
'/bin/zsh',
'/usr/bin/zsh',
'/usr/bin/screen',
'/bin/dash',
'/usr/bin/rssh',
'/usr/local/bin/disconnect_shell',
'/usr/scripts/surveillance/disconnect_shell',
'/usr/local/bin/badPassSh',
'/usr/bin/passwd',
'/bin/false',
'/bin//zsh'
'']
if shell not in shells:
raise ValueError("Shell %r invalide" % shell)
self.value = shell
class uidNumber(intAttr):
singlevalue = True
optional = True
unique = True
legend = "L'uid du compte de l'adherent"
category = 'id'
class gidNumber(intAttr):
singlevalue = True
optional = True
legend = "Le gid du compte de l'adhérent"
category = 'id'
class gecos(Attr):
singlevalue = True
optional = True
legend = "Le gecos"
category = 'id'
def parse_value(self, gecos):
self.value = gecos
class sshFingerprint(Attr):
singlevalue = False
optional = True
legend = "Clef ssh de la machine"
can_modify = [parent, nounou]
class gpgFingerprint(Attr):
singlevalue = False
optional = True
unique = True
legend = "Clef gpg d'un adhérent"
can_modify = [soi, nounou]
class cn(Attr):
singlevalue = True
optional = False
category = 'id'
class dn(Attr):
singlevalue = True
optional = False
unique = True
category = 'id'
class postalAddress(Attr):
singlevalue = False
optional = True
can_modify = [soi, cableur, nounou, bureau]
legend = u"Adresse"
category = 'perso'
class controle(Attr):
singlevalue = True
optional = False
legend = u"Contrôle"
can_modify = [tresorier, nounou]
category = 'perso'
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':
self.value = u'pc'
else:
self.value = ctrl
class fid(intAttr):
legend = u"Id de facture"
category = 'factures'
optional = False
singlevalue = True
class modePaiement(Attr):
legend = u"Mode de paiement"
category = 'factures'
optional = False
singlevalue = True
class recuPaiement(Attr):
pass
class dnsIpv6(boolAttr):
pass
class machineAlias(boolAttr):
pass
### Les classes
ADHERENT_ATTRS = [ nom, prenom, tel, chbre, postalAddress, mail, uid,
canonicalAlias, mailAlias, etudes, paiement,
solde, carteEtudiant, droits, loginShell, blacklist ]
MACHINE_ATTRS = [ host, macAddress, hostAlias, ipHostNumber, portTCPout,
portTCPin, portUDPout, portUDPin ]
CRANS_ATTRIBUTES = {
'objectClass' : objectClass,
'cn' : cn,
'dn' : dn,
'aid': aid,
'uid': uid,
'nom' : nom,
'prenom' : prenom,
'compteWiki' : compteWiki,
'tel' : tel,
'paiement' : paiement,
'controle': controle,
'derniereConnexion' : derniereConnexion,
'carteEtudiant' : carteEtudiant,
'mailAlias' : mailAlias,
'canonicalAlias' : canonicalAlias,
'etudes' : etudes,
'chbre' : chbre,
'droits' : droits,
'solde' : solde,
'gpgFingerprint' : gpgFingerprint,
'mid' : mid,
'rid' : rid,
'host' : host,
'sshFingerprint' : sshFingerprint,
'macAddress': macAddress,
'ipHostNumber': ipHostNumber,
'ip6HostNumber': ip6HostNumber,
'hostAlias' : hostAlias,
'ipsec' : ipsec,
'puissance' : puissance,
'canal' : canal,
'portTCPout' : portTCPout,
'portTCPin' : portTCPin,
'portUDPout' : portUDPout,
'portUDPin' : portUDPin,
'exempt' : exempt,
'prise' : prise,
'nombrePrises' : nombrePrises,
'hotspot' : hotspot,
'mailInvalide' : mailInvalide,
'positionBorne' : positionBorne,
'nvram' : nvram,
'contourneGreylist' : contourneGreylist,
'imprimeurClub' : imprimeurClub,
'fid' : fid,
'dnsIpv6' : dnsIpv6,
'machineAlias' : machineAlias,
'modePaiement' : modePaiement,
'recuPaiement' : recuPaiement,
'cid' : cid,
'responsable' : responsable,
'blacklist' : blacklist,
'historique' : historique,
'info': info,
'homepageAlias': homepageAlias,
'charteMA': charteMA,
'mail' : mail,
'postalAddress': postalAddress,
# {posix,shadow}Account
'homeDirectory': homeDirectory,
'loginShell': loginShell,
'uidNumber': uidNumber,
'gecos': gecos,
'gidNumber': gidNumber
}